/
platform_windows.c
959 lines (782 loc) · 28.1 KB
1
/*
2
* Windows support routines for PhysicsFS.
3
*
4
* Please see the file LICENSE.txt in the source's root directory.
5
*
6
* This file written by Ryan C. Gordon, and made sane by Gregory S. Read.
7
8
*/
9
#define __PHYSICSFS_INTERNAL__
10
#include "physfs_internal.h"
11
12
#ifdef PHYSFS_PLATFORM_WINDOWS
13
#ifndef PHYSFS_PLATFORM_WINRT
14
15
/* Forcibly disable UNICODE macro, since we manage this ourselves. */
16
17
18
19
#ifdef UNICODE
#undef UNICODE
#endif
20
21
22
/* !!! FIXME: maybe clean out the "allocator" macro, eventually. */
#undef allocator /* apparently Windows 10 SDK conflicts here. */
23
#define WIN32_LEAN_AND_MEAN 1
24
#include <windows.h>
25
#include <userenv.h>
26
#include <shlobj.h>
27
#include <dbt.h>
28
#include <errno.h>
29
#include <ctype.h>
30
#include <time.h>
31
32
33
34
35
36
#ifdef allocator /* apparently Windows 10 SDK conflicts here. */
#undef allocator
#endif
#define allocator __PHYSFS_AllocatorHooks
37
38
#define LOWORDER_UINT64(pos) ((PHYSFS_uint32) (pos & 0xFFFFFFFF))
#define HIGHORDER_UINT64(pos) ((PHYSFS_uint32) ((pos >> 32) & 0xFFFFFFFF))
39
40
41
42
43
44
45
/*
* Users without the platform SDK don't have this defined. The original docs
* for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should
* work as desired.
*/
#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF
46
47
48
/* just in case... */
#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
49
50
/* Not defined before the Vista SDK. */
51
#define PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT 0x400
52
53
54
#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C
55
#define UTF8_TO_UNICODE_STACK(w_assignto, str) { \
56
57
58
if (str == NULL) \
w_assignto = NULL; \
else { \
59
const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \
60
61
w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \
if (w_assignto != NULL) \
62
PHYSFS_utf8ToUtf16(str, (PHYSFS_uint16 *) w_assignto, len); \
63
64
} \
} \
65
66
/* Note this counts WCHARs, not codepoints! */
67
68
69
70
71
static PHYSFS_uint64 wStrLen(const WCHAR *wstr)
{
PHYSFS_uint64 len = 0;
while (*(wstr++))
len++;
72
return len;
73
74
75
76
77
78
79
80
81
82
} /* wStrLen */
static char *unicodeToUtf8Heap(const WCHAR *w_str)
{
char *retval = NULL;
if (w_str != NULL)
{
void *ptr = NULL;
const PHYSFS_uint64 len = (wStrLen(w_str) * 4) + 1;
retval = allocator.Malloc(len);
83
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
84
PHYSFS_utf8FromUtf16((const PHYSFS_uint16 *) w_str, retval, len);
85
86
87
88
ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */
if (ptr != NULL)
retval = (char *) ptr;
} /* if */
89
return retval;
90
91
} /* unicodeToUtf8Heap */
92
/* !!! FIXME: do we really need readonly? If not, do we need this struct? */
93
94
95
96
typedef struct
{
HANDLE handle;
int readonly;
97
} WinApiFile;
98
99
100
101
102
static HANDLE detectCDThreadHandle = NULL;
static HWND detectCDHwnd = 0;
static volatile int initialDiscDetectionComplete = 0;
static volatile DWORD drivesWithMediaBitmap = 0;
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
static PHYSFS_ErrorCode errcodeFromWinApiError(const DWORD err)
{
/*
* win32 error codes are sort of a tricky thing; Microsoft intentionally
* doesn't list which ones a given API might trigger, there are several
* with overlapping and unclear meanings...and there's 16 thousand of
* them in Windows 7. It looks like the ones we care about are in the
* first 500, but I can't say this list is perfect; we might miss
* important values or misinterpret others.
*
* Don't treat this list as anything other than a work in progress.
*/
switch (err)
118
{
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
case ERROR_SUCCESS: return PHYSFS_ERR_OK;
case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
case ERROR_NETWORK_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
case ERROR_NOT_READY: return PHYSFS_ERR_IO;
case ERROR_CRC: return PHYSFS_ERR_IO;
case ERROR_SEEK: return PHYSFS_ERR_IO;
case ERROR_SECTOR_NOT_FOUND: return PHYSFS_ERR_IO;
case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_IO;
case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO;
case ERROR_READ_FAULT: return PHYSFS_ERR_IO;
case ERROR_DEV_NOT_EXIST: return PHYSFS_ERR_IO;
/* !!! FIXME: ?? case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP; */
case ERROR_BUFFER_OVERFLOW: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_INVALID_NAME: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_BAD_PATHNAME: return PHYSFS_ERR_BAD_FILENAME;
case ERROR_DIRECTORY: return PHYSFS_ERR_BAD_FILENAME;
135
136
137
138
case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
case ERROR_DELETE_PENDING: return PHYSFS_ERR_NOT_FOUND;
case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NOT_FOUND;
139
140
case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
141
/* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; */
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* !!! FIXME: ?? case EISDIR: return PHYSFS_ERR_NOT_A_FILE; */
case ERROR_WRITE_PROTECT: return PHYSFS_ERR_READ_ONLY;
case ERROR_LOCK_VIOLATION: return PHYSFS_ERR_BUSY;
case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_BUSY;
case ERROR_CURRENT_DIRECTORY: return PHYSFS_ERR_BUSY;
case ERROR_DRIVE_LOCKED: return PHYSFS_ERR_BUSY;
case ERROR_PATH_BUSY: return PHYSFS_ERR_BUSY;
case ERROR_BUSY: return PHYSFS_ERR_BUSY;
case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
case ERROR_OUTOFMEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
case ERROR_DIR_NOT_EMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
default: return PHYSFS_ERR_OS_ERROR;
} /* switch */
} /* errcodeFromWinApiError */
static inline PHYSFS_ErrorCode errcodeFromWinApi(void)
{
return errcodeFromWinApiError(GetLastError());
} /* errcodeFromWinApi */
161
162
163
typedef BOOL (WINAPI *fnSTEM)(DWORD, LPDWORD b);
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
static DWORD pollDiscDrives(void)
{
/* Try to use SetThreadErrorMode(), which showed up in Windows 7. */
HANDLE lib = LoadLibraryA("kernel32.dll");
fnSTEM stem = NULL;
char drive[4] = { 'x', ':', '\\', '\0' };
DWORD oldErrorMode = 0;
DWORD drives = 0;
DWORD i;
if (lib)
stem = (fnSTEM) GetProcAddress(lib, "SetThreadErrorMode");
if (stem)
stem(SEM_FAILCRITICALERRORS, &oldErrorMode);
else
oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
182
183
184
185
186
187
188
189
/* Do detection. This may block if a disc is spinning up. */
for (i = 'A'; i <= 'Z'; i++)
{
DWORD tmp = 0;
drive[0] = (char) i;
if (GetDriveTypeA(drive) != DRIVE_CDROM)
continue;
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/* If this function succeeds, there's media in the drive */
if (GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0))
drives |= (1 << (i - 'A'));
} /* for */
if (stem)
stem(oldErrorMode, NULL);
else
SetErrorMode(oldErrorMode);
if (lib)
FreeLibrary(lib);
return drives;
} /* pollDiscDrives */
static LRESULT CALLBACK detectCDWndProc(HWND hwnd, UINT msg,
WPARAM wp, LPARAM lparam)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR) lparam;
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME) lparam;
const int removed = (wp == DBT_DEVICEREMOVECOMPLETE);
if (msg == WM_DESTROY)
return 0;
else if ((msg != WM_DEVICECHANGE) ||
((wp != DBT_DEVICEARRIVAL) && (wp != DBT_DEVICEREMOVECOMPLETE)) ||
(lpdb->dbch_devicetype != DBT_DEVTYP_VOLUME) ||
((lpdbv->dbcv_flags & DBTF_MEDIA) == 0))
{
return DefWindowProcW(hwnd, msg, wp, lparam);
} /* else if */
if (removed)
drivesWithMediaBitmap &= ~lpdbv->dbcv_unitmask;
else
drivesWithMediaBitmap |= lpdbv->dbcv_unitmask;
return TRUE;
} /* detectCDWndProc */
static DWORD WINAPI detectCDThread(LPVOID lpParameter)
{
const char *classname = "PhysicsFSDetectCDCatcher";
const char *winname = "PhysicsFSDetectCDMsgWindow";
HINSTANCE hInstance = GetModuleHandleW(NULL);
ATOM class_atom = 0;
WNDCLASSEXA wce;
MSG msg;
memset(&wce, '\0', sizeof (wce));
wce.cbSize = sizeof (wce);
wce.lpfnWndProc = detectCDWndProc;
wce.lpszClassName = classname;
wce.hInstance = hInstance;
class_atom = RegisterClassExA(&wce);
if (class_atom == 0)
{
initialDiscDetectionComplete = 1; /* let main thread go on. */
return 0;
} /* if */
detectCDHwnd = CreateWindowExA(0, classname, winname, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
if (detectCDHwnd == NULL)
{
initialDiscDetectionComplete = 1; /* let main thread go on. */
UnregisterClassA(classname, hInstance);
return 0;
} /* if */
/* We'll get events when discs come and go from now on. */
/* Do initial detection, possibly blocking awhile... */
drivesWithMediaBitmap = pollDiscDrives();
initialDiscDetectionComplete = 1; /* let main thread go on. */
do
{
const BOOL rc = GetMessageW(&msg, detectCDHwnd, 0, 0);
if ((rc == 0) || (rc == -1))
break; /* don't care if WM_QUIT or error break this loop. */
TranslateMessage(&msg);
DispatchMessageW(&msg);
} while (1);
/* we've been asked to quit. */
DestroyWindow(detectCDHwnd);
do
{
const BOOL rc = GetMessage(&msg, detectCDHwnd, 0, 0);
if ((rc == 0) || (rc == -1))
break;
TranslateMessage(&msg);
DispatchMessageW(&msg);
} while (1);
UnregisterClassA(classname, hInstance);
return 0;
} /* detectCDThread */
297
298
299
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
300
{
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
char drive_str[4] = { 'x', ':', '\\', '\0' };
DWORD drives = 0;
DWORD i;
/*
* If you poll a drive while a user is inserting a disc, the OS will
* block this thread until the drive has spun up. So we swallow the risk
* once for initial detection, and spin a thread that will get device
* events thereafter, for apps that use this interface to poll for
* disc insertion.
*/
if (!detectCDThreadHandle)
{
initialDiscDetectionComplete = 0;
detectCDThreadHandle = CreateThread(NULL,0,detectCDThread,NULL,0,NULL);
if (detectCDThreadHandle == NULL)
return; /* oh well. */
while (!initialDiscDetectionComplete)
Sleep(50);
} /* if */
drives = drivesWithMediaBitmap; /* whatever the thread has seen, we take. */
for (i = 'A'; i <= 'Z'; i++)
325
{
326
327
328
if (drives & (1 << (i - 'A')))
{
drive_str[0] = (char) i;
329
cb(data, drive_str);
330
} /* if */
331
} /* for */
332
} /* __PHYSFS_platformDetectAvailableCDs */
333
334
335
336
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
337
338
339
DWORD buflen = 64;
LPWSTR modpath = NULL;
char *retval = NULL;
340
341
342
343
344
345
346
347
348
while (1)
{
DWORD rc;
void *ptr;
if ( (ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) == NULL )
{
allocator.Free(modpath);
349
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
350
351
352
353
354
355
356
} /* if */
modpath = (LPWSTR) ptr;
rc = GetModuleFileNameW(NULL, modpath, buflen);
if (rc == 0)
{
allocator.Free(modpath);
357
BAIL(errcodeFromWinApi(), NULL);
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
} /* if */
if (rc < buflen)
{
buflen = rc;
break;
} /* if */
buflen *= 2;
} /* while */
if (buflen > 0) /* just in case... */
{
WCHAR *ptr = (modpath + buflen) - 1;
while (ptr != modpath)
{
if (*ptr == '\\')
break;
ptr--;
} /* while */
if ((ptr == modpath) && (*ptr != '\\'))
380
PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR); /* oh well. */
381
382
else
{
383
*(ptr+1) = '\0'; /* chop off filename. */
384
385
386
387
388
389
retval = unicodeToUtf8Heap(modpath);
} /* else */
} /* else */
allocator.Free(modpath);
return retval; /* w00t. */
390
391
392
} /* __PHYSFS_platformCalcBaseDir */
393
394
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
395
396
397
398
399
400
401
/*
* Vista and later has a new API for this, but SHGetFolderPath works there,
* and apparently just wraps the new API. This is the new way to do it:
*
* SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE,
* NULL, &wszPath);
*/
402
403
WCHAR path[MAX_PATH];
404
405
406
407
char *utf8 = NULL;
size_t len = 0;
char *retval = NULL;
408
409
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
NULL, 0, path)))
410
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
411
412
utf8 = unicodeToUtf8Heap(path);
413
BAIL_IF_ERRPASS(!utf8, NULL);
414
len = strlen(utf8) + strlen(org) + strlen(app) + 4;
415
416
417
418
retval = allocator.Malloc(len);
if (!retval)
{
allocator.Free(utf8);
419
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
420
421
} /* if */
422
snprintf(retval, len, "%s\\%s\\%s\\", utf8, org, app);
423
allocator.Free(utf8);
424
return retval;
425
426
427
} /* __PHYSFS_platformCalcPrefDir */
428
char *__PHYSFS_platformCalcUserDir(void)
429
{
430
431
432
433
434
435
436
typedef BOOL (WINAPI *fnGetUserProfDirW)(HANDLE, LPWSTR, LPDWORD);
fnGetUserProfDirW pGetDir = NULL;
HANDLE lib = NULL;
HANDLE accessToken = NULL; /* Security handle to process */
char *retval = NULL;
lib = LoadLibraryA("userenv.dll");
437
BAIL_IF(!lib, errcodeFromWinApi(), NULL);
438
pGetDir=(fnGetUserProfDirW) GetProcAddress(lib,"GetUserProfileDirectoryW");
439
GOTO_IF(!pGetDir, errcodeFromWinApi(), done);
440
441
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &accessToken))
442
GOTO(errcodeFromWinApi(), done);
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
else
{
DWORD psize = 0;
WCHAR dummy = 0;
LPWSTR wstr = NULL;
BOOL rc = 0;
/*
* Should fail. Will write the size of the profile path in
* psize. Also note that the second parameter can't be
* NULL or the function fails.
*/
rc = pGetDir(accessToken, &dummy, &psize);
assert(!rc); /* !!! FIXME: handle this gracefully. */
(void) rc;
/* Allocate memory for the profile directory */
460
wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR));
461
462
463
if (wstr != NULL)
{
if (pGetDir(accessToken, wstr, &psize))
464
465
466
467
468
469
470
{
/* Make sure it ends in a dirsep. We allocated +1 for this. */
if (wstr[psize - 2] != '\\')
{
wstr[psize - 1] = '\\';
wstr[psize - 0] = '\0';
} /* if */
471
retval = unicodeToUtf8Heap(wstr);
472
} /* if */
473
474
475
476
477
478
479
480
481
482
__PHYSFS_smallFree(wstr);
} /* if */
CloseHandle(accessToken);
} /* if */
done:
FreeLibrary(lib);
return retval; /* We made it: hit the showers. */
} /* __PHYSFS_platformCalcUserDir */
483
484
485
void *__PHYSFS_platformGetThreadID(void)
486
{
487
return ( (void *) ((size_t) GetCurrentThreadId()) );
488
489
} /* __PHYSFS_platformGetThreadID */
490
void __PHYSFS_platformEnumerateFiles(const char *dirname,
491
492
PHYSFS_EnumFilesCallback callback,
const char *origdir,
493
void *callbackdata)
494
{
495
496
HANDLE dir = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW entw;
497
size_t len = strlen(dirname);
498
499
char *searchPath = NULL;
WCHAR *wSearchPath = NULL;
500
501
/* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
502
503
searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
if (searchPath == NULL)
504
return;
505
506
/* Copy current dirname */
507
strcpy(searchPath, dirname);
508
509
/* if there's no '\\' at the end of the path, stick one in there. */
510
if (searchPath[len - 1] != '\\')
511
{
512
513
searchPath[len++] = '\\';
searchPath[len] = '\0';
514
515
} /* if */
516
/* Append the "*" to the end of the string */
517
strcat(searchPath, "*");
518
519
UTF8_TO_UNICODE_STACK(wSearchPath, searchPath);
520
if (!wSearchPath)
521
return; /* oh well. */
522
523
dir = FindFirstFileW(wSearchPath, &entw);
524
525
526
527
528
__PHYSFS_smallFree(wSearchPath);
__PHYSFS_smallFree(searchPath);
if (dir == INVALID_HANDLE_VALUE)
return;
529
530
do
531
{
532
533
const WCHAR *fn = entw.cFileName;
char *utf8;
534
535
536
537
538
539
540
541
if ((fn[0] == '.') && (fn[1] == '\0'))
continue;
if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
continue;
utf8 = unicodeToUtf8Heap(fn);
if (utf8 != NULL)
542
{
543
544
545
546
callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
} /* if */
} while (FindNextFileW(dir, &entw) != 0);
547
548
549
550
551
552
553
FindClose(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
554
555
WCHAR *wpath;
DWORD rc;
556
UTF8_TO_UNICODE_STACK(wpath, path);
557
rc = CreateDirectoryW(wpath, NULL);
558
__PHYSFS_smallFree(wpath);
559
BAIL_IF(rc == 0, errcodeFromWinApi(), 0);
560
return 1;
561
562
} /* __PHYSFS_platformMkDir */
563
564
565
int __PHYSFS_platformInit(void)
{
566
return 1; /* It's all good */
567
} /* __PHYSFS_platformInit */
568
569
570
571
int __PHYSFS_platformDeinit(void)
{
572
if (detectCDThreadHandle)
573
574
575
576
577
578
579
580
581
{
if (detectCDHwnd)
PostMessageW(detectCDHwnd, WM_QUIT, 0, 0);
CloseHandle(detectCDThreadHandle);
detectCDThreadHandle = NULL;
initialDiscDetectionComplete = 0;
drivesWithMediaBitmap = 0;
} /* if */
582
return 1; /* It's all good */
583
584
585
586
587
} /* __PHYSFS_platformDeinit */
static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
{
588
HANDLE fileh;
589
590
WinApiFile *retval;
WCHAR *wfname;
591
592
593
UTF8_TO_UNICODE_STACK(wfname, fname);
BAIL_IF(!wfname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
594
595
fileh = CreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
596
__PHYSFS_smallFree(wfname);
597
598
BAIL_IF(fileh == INVALID_HANDLE_VALUE,errcodeFromWinApi(), NULL);
599
600
retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile));
601
if (!retval)
602
{
603
CloseHandle(fileh);
604
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
605
} /* if */
606
607
retval->readonly = rdonly;
608
retval->handle = fileh;
609
return retval;
610
611
} /* doOpen */
612
613
614
void *__PHYSFS_platformOpenRead(const char *filename)
{
615
return doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1);
616
} /* __PHYSFS_platformOpenRead */
617
618
619
620
void *__PHYSFS_platformOpenWrite(const char *filename)
{
621
return doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0);
622
} /* __PHYSFS_platformOpenWrite */
623
624
625
626
void *__PHYSFS_platformOpenAppend(const char *filename)
{
627
628
629
void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
if (retval != NULL)
{
630
HANDLE h = ((WinApiFile *) retval)->handle;
631
632
DWORD rc = SetFilePointer(h, 0, NULL, FILE_END);
if (rc == PHYSFS_INVALID_SET_FILE_POINTER)
633
{
634
const PHYSFS_ErrorCode err = errcodeFromWinApi();
635
CloseHandle(h);
636
allocator.Free(retval);
637
BAIL(err, NULL);
638
639
640
} /* if */
} /* if */
641
return retval;
642
} /* __PHYSFS_platformOpenAppend */
643
644
645
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
646
{
647
HANDLE Handle = ((WinApiFile *) opaque)->handle;
648
PHYSFS_sint64 totalRead = 0;
649
650
if (!__PHYSFS_ui64FitsAddressSpace(len))
651
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
652
653
654
655
656
657
while (len > 0)
{
const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
DWORD numRead = 0;
if (!ReadFile(Handle, buf, thislen, &numRead, NULL))
658
BAIL(errcodeFromWinApi(), -1);
659
660
661
662
663
664
665
len -= (PHYSFS_uint64) numRead;
totalRead += (PHYSFS_sint64) numRead;
if (numRead != thislen)
break;
} /* while */
return totalRead;
666
667
} /* __PHYSFS_platformRead */
668
669
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
670
PHYSFS_uint64 len)
671
{
672
HANDLE Handle = ((WinApiFile *) opaque)->handle;
673
PHYSFS_sint64 totalWritten = 0;
674
675
if (!__PHYSFS_ui64FitsAddressSpace(len))
676
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
677
678
679
680
681
682
while (len > 0)
{
const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
DWORD numWritten = 0;
if (!WriteFile(Handle, buffer, thislen, &numWritten, NULL))
683
BAIL(errcodeFromWinApi(), -1);
684
685
686
687
688
689
690
len -= (PHYSFS_uint64) numWritten;
totalWritten += (PHYSFS_sint64) numWritten;
if (numWritten != thislen)
break;
} /* while */
return totalWritten;
691
692
} /* __PHYSFS_platformWrite */
693
694
695
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
696
HANDLE Handle = ((WinApiFile *) opaque)->handle;
697
698
LONG HighOrderPos;
PLONG pHighOrderPos;
699
DWORD rc;
700
701
702
703
/* Get the high order 32-bits of the position */
HighOrderPos = HIGHORDER_UINT64(pos);
704
705
706
707
708
709
/*
* MSDN: "If you do not need the high-order 32 bits, this
* pointer must be set to NULL."
*/
pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL;
710
/* Move pointer "pos" count from start of file */
711
rc = SetFilePointer(Handle, LOWORDER_UINT64(pos),
712
pHighOrderPos, FILE_BEGIN);
713
714
715
716
if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
{
717
BAIL(errcodeFromWinApi(), 0);
718
719
} /* if */
720
return 1; /* No error occured */
721
} /* __PHYSFS_platformSeek */
722
723
724
725
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
726
HANDLE Handle = ((WinApiFile *) opaque)->handle;
727
LONG HighPos = 0;
728
DWORD LowPos;
729
730
731
PHYSFS_sint64 retval;
/* Get current position */
732
LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
733
734
if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
735
{
736
BAIL(errcodeFromWinApi(), -1);
737
} /* if */
738
739
740
else
{
/* Combine the high/low order to create the 64-bit position value */
741
742
743
744
retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
assert(retval >= 0);
} /* else */
745
return retval;
746
} /* __PHYSFS_platformTell */
747
748
749
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
750
{
751
HANDLE Handle = ((WinApiFile *) opaque)->handle;
752
753
DWORD SizeHigh;
DWORD SizeLow;
754
755
PHYSFS_sint64 retval;
756
SizeLow = GetFileSize(Handle, &SizeHigh);
757
758
if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
759
{
760
BAIL(errcodeFromWinApi(), -1);
761
} /* if */
762
763
764
else
{
/* Combine the high/low order to create the 64-bit position value */
765
766
retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
assert(retval >= 0);
767
} /* else */
768
769
return retval;
770
771
} /* __PHYSFS_platformFileLength */
772
773
774
int __PHYSFS_platformFlush(void *opaque)
{
775
WinApiFile *fh = ((WinApiFile *) opaque);
776
if (!fh->readonly)
777
BAIL_IF(!FlushFileBuffers(fh->handle), errcodeFromWinApi(), 0);
778
779
return 1;
780
} /* __PHYSFS_platformFlush */
781
782
783
void __PHYSFS_platformClose(void *opaque)
784
{
785
HANDLE Handle = ((WinApiFile *) opaque)->handle;
786
(void) CloseHandle(Handle); /* ignore errors. You should have flushed! */
787
allocator.Free(opaque);
788
} /* __PHYSFS_platformClose */
789
790
791
static int doPlatformDelete(LPWSTR wpath)
792
{
793
794
const int isdir = (GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY);
const BOOL rc = (isdir) ? RemoveDirectoryW(wpath) : DeleteFileW(wpath);
795
BAIL_IF(!rc, errcodeFromWinApi(), 0);
796
return 1; /* if you made it here, it worked. */
797
798
799
800
801
802
} /* doPlatformDelete */
int __PHYSFS_platformDelete(const char *path)
{
int retval = 0;
803
LPWSTR wpath = NULL;
804
805
UTF8_TO_UNICODE_STACK(wpath, path);
BAIL_IF(!wpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
806
807
retval = doPlatformDelete(wpath);
__PHYSFS_smallFree(wpath);
808
return retval;
809
} /* __PHYSFS_platformDelete */
810
811
812
813
void *__PHYSFS_platformCreateMutex(void)
{
814
815
LPCRITICAL_SECTION lpcs;
lpcs = (LPCRITICAL_SECTION) allocator.Malloc(sizeof (CRITICAL_SECTION));
816
BAIL_IF(!lpcs, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
817
818
InitializeCriticalSection(lpcs);
return lpcs;
819
820
} /* __PHYSFS_platformCreateMutex */
821
822
823
void __PHYSFS_platformDestroyMutex(void *mutex)
{
824
825
DeleteCriticalSection((LPCRITICAL_SECTION) mutex);
allocator.Free(mutex);
826
827
} /* __PHYSFS_platformDestroyMutex */
828
829
830
int __PHYSFS_platformGrabMutex(void *mutex)
{
831
832
EnterCriticalSection((LPCRITICAL_SECTION) mutex);
return 1;
833
} /* __PHYSFS_platformGrabMutex */
834
835
836
837
void __PHYSFS_platformReleaseMutex(void *mutex)
{
838
LeaveCriticalSection((LPCRITICAL_SECTION) mutex);
839
840
} /* __PHYSFS_platformReleaseMutex */
841
842
static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft)
843
844
845
{
SYSTEMTIME st_utc;
SYSTEMTIME st_localtz;
846
847
848
TIME_ZONE_INFORMATION tzi;
DWORD tzid;
PHYSFS_sint64 retval;
849
struct tm tm;
850
BOOL rc;
851
852
BAIL_IF(!FileTimeToSystemTime(ft, &st_utc), errcodeFromWinApi(), -1);
853
tzid = GetTimeZoneInformation(&tzi);
854
BAIL_IF(tzid == TIME_ZONE_ID_INVALID, errcodeFromWinApi(), -1);
855
rc = SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz);
856
BAIL_IF(!rc, errcodeFromWinApi(), -1);
857
858
/* Convert to a format that mktime() can grok... */
859
860
861
862
863
864
tm.tm_sec = st_localtz.wSecond;
tm.tm_min = st_localtz.wMinute;
tm.tm_hour = st_localtz.wHour;
tm.tm_mday = st_localtz.wDay;
tm.tm_mon = st_localtz.wMonth - 1;
tm.tm_year = st_localtz.wYear - 1900;
865
tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
866
867
tm.tm_yday = -1;
tm.tm_isdst = -1;
868
869
870
/* Convert to a format PhysicsFS can grok... */
retval = (PHYSFS_sint64) mktime(&tm);
871
BAIL_IF(retval == -1, PHYSFS_ERR_OS_ERROR, -1);
872
return retval;
873
874
} /* FileTimeToPhysfsTime */
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
/* check for symlinks. These exist in NTFS 3.1 (WinXP), even though
they aren't really available to userspace before Vista. I wonder
what would happen if you put an NTFS disk with a symlink on it
into an XP machine, though; would this flag get set?
NTFS symlinks are a form of "reparse point" (junction, volume mount,
etc), so if the REPARSE_POINT attribute is set, check for the symlink
tag thereafter. This assumes you already read in the file attributes. */
static int isSymlink(const WCHAR *wpath, const DWORD attr)
{
WIN32_FIND_DATAW w32dw;
HANDLE h;
if ((attr & PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT) == 0)
return 0; /* not a reparse point? Definitely not a symlink. */
h = FindFirstFileW(wpath, &w32dw);
if (h == INVALID_HANDLE_VALUE)
return 0; /* ...maybe the file just vanished...? */
FindClose(h);
896
return (w32dw.dwReserved0 == PHYSFS_IO_REPARSE_TAG_SYMLINK);
897
898
899
} /* isSymlink */
900
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
901
902
903
{
WIN32_FILE_ATTRIBUTE_DATA winstat;
WCHAR *wstr = NULL;
904
DWORD err = 0;
905
BOOL rc = 0;
906
int issymlink = 0;
907
908
909
UTF8_TO_UNICODE_STACK(wstr, filename);
BAIL_IF(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0);
910
rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat);
911
912
913
914
915
916
if (!rc)
err = GetLastError();
else /* check for symlink while wstr is still available */
issymlink = isSymlink(wstr, winstat.dwFileAttributes);
917
__PHYSFS_smallFree(wstr);
918
BAIL_IF(!rc, errcodeFromWinApiError(err), 0);
919
920
921
922
st->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime);
st->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime);
st->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime);
923
924
if (issymlink)
925
{
926
st->filetype = PHYSFS_FILETYPE_SYMLINK;
927
st->filesize = 0;
928
929
} /* if */
930
931
932
933
934
935
936
else if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
st->filesize = 0;
} /* else if */
else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE))
937
{
938
st->filetype = PHYSFS_FILETYPE_OTHER;
939
/* !!! FIXME: don't rely on this */
940
st->filesize = 0;
941
942
943
944
} /* else if */
else
{
945
946
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow;
947
948
} /* else */
949
st->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
950
951
return 1;
952
953
} /* __PHYSFS_platformStat */
954
#endif /* PHYSFS_PLATFORM_WINDOWS */
955
#endif /* PHYSFS_PLATFORM_WINRT */
956
957
/* end of platform_windows.c ... */