/
platform_windows.c
951 lines (776 loc) · 27.8 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
#define WIN32_LEAN_AND_MEAN 1
21
#include <windows.h>
22
#include <userenv.h>
23
#include <shlobj.h>
24
#include <dbt.h>
25
#include <errno.h>
26
#include <ctype.h>
27
#include <time.h>
28
29
30
#define LOWORDER_UINT64(pos) ((PHYSFS_uint32) (pos & 0xFFFFFFFF))
#define HIGHORDER_UINT64(pos) ((PHYSFS_uint32) ((pos >> 32) & 0xFFFFFFFF))
31
32
33
34
35
36
37
/*
* 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
38
39
40
/* just in case... */
#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
41
42
/* Not defined before the Vista SDK. */
43
#define PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT 0x400
44
45
46
#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C
47
#define UTF8_TO_UNICODE_STACK(w_assignto, str) { \
48
49
50
if (str == NULL) \
w_assignto = NULL; \
else { \
51
const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \
52
53
w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \
if (w_assignto != NULL) \
54
PHYSFS_utf8ToUtf16(str, (PHYSFS_uint16 *) w_assignto, len); \
55
56
} \
} \
57
58
/* Note this counts WCHARs, not codepoints! */
59
60
61
62
63
static PHYSFS_uint64 wStrLen(const WCHAR *wstr)
{
PHYSFS_uint64 len = 0;
while (*(wstr++))
len++;
64
return len;
65
66
67
68
69
70
71
72
73
74
} /* 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);
75
BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
76
PHYSFS_utf8FromUtf16((const PHYSFS_uint16 *) w_str, retval, len);
77
78
79
80
ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */
if (ptr != NULL)
retval = (char *) ptr;
} /* if */
81
return retval;
82
83
} /* unicodeToUtf8Heap */
84
/* !!! FIXME: do we really need readonly? If not, do we need this struct? */
85
86
87
88
typedef struct
{
HANDLE handle;
int readonly;
89
} WinApiFile;
90
91
92
93
94
static HANDLE detectCDThreadHandle = NULL;
static HWND detectCDHwnd = 0;
static volatile int initialDiscDetectionComplete = 0;
static volatile DWORD drivesWithMediaBitmap = 0;
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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)
110
{
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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;
127
128
129
130
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;
131
132
case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
133
/* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; */
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* !!! 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 */
153
154
155
typedef BOOL (WINAPI *fnSTEM)(DWORD, LPDWORD b);
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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);
174
175
176
177
178
179
180
181
/* 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;
182
183
184
185
186
187
188
189
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
/* 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 */
289
290
291
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
292
{
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
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++)
317
{
318
319
320
if (drives & (1 << (i - 'A')))
{
drive_str[0] = (char) i;
321
cb(data, drive_str);
322
} /* if */
323
} /* for */
324
} /* __PHYSFS_platformDetectAvailableCDs */
325
326
327
328
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
329
330
331
DWORD buflen = 64;
LPWSTR modpath = NULL;
char *retval = NULL;
332
333
334
335
336
337
338
339
340
while (1)
{
DWORD rc;
void *ptr;
if ( (ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) == NULL )
{
allocator.Free(modpath);
341
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
342
343
344
345
346
347
348
} /* if */
modpath = (LPWSTR) ptr;
rc = GetModuleFileNameW(NULL, modpath, buflen);
if (rc == 0)
{
allocator.Free(modpath);
349
BAIL(errcodeFromWinApi(), NULL);
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
} /* 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 != '\\'))
372
PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR); /* oh well. */
373
374
else
{
375
*(ptr+1) = '\0'; /* chop off filename. */
376
377
378
379
380
381
retval = unicodeToUtf8Heap(modpath);
} /* else */
} /* else */
allocator.Free(modpath);
return retval; /* w00t. */
382
383
384
} /* __PHYSFS_platformCalcBaseDir */
385
386
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
387
388
389
390
391
392
393
/*
* 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);
*/
394
395
WCHAR path[MAX_PATH];
396
397
398
399
char *utf8 = NULL;
size_t len = 0;
char *retval = NULL;
400
401
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
NULL, 0, path)))
402
BAIL(PHYSFS_ERR_OS_ERROR, NULL);
403
404
utf8 = unicodeToUtf8Heap(path);
405
BAIL_IF_ERRPASS(!utf8, NULL);
406
len = strlen(utf8) + strlen(org) + strlen(app) + 4;
407
408
409
410
retval = allocator.Malloc(len);
if (!retval)
{
allocator.Free(utf8);
411
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
412
413
} /* if */
414
snprintf(retval, len, "%s\\%s\\%s\\", utf8, org, app);
415
allocator.Free(utf8);
416
return retval;
417
418
419
} /* __PHYSFS_platformCalcPrefDir */
420
char *__PHYSFS_platformCalcUserDir(void)
421
{
422
423
424
425
426
427
428
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");
429
BAIL_IF(!lib, errcodeFromWinApi(), NULL);
430
pGetDir=(fnGetUserProfDirW) GetProcAddress(lib,"GetUserProfileDirectoryW");
431
GOTO_IF(!pGetDir, errcodeFromWinApi(), done);
432
433
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &accessToken))
434
GOTO(errcodeFromWinApi(), done);
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
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 */
452
wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR));
453
454
455
if (wstr != NULL)
{
if (pGetDir(accessToken, wstr, &psize))
456
457
458
459
460
461
462
{
/* Make sure it ends in a dirsep. We allocated +1 for this. */
if (wstr[psize - 2] != '\\')
{
wstr[psize - 1] = '\\';
wstr[psize - 0] = '\0';
} /* if */
463
retval = unicodeToUtf8Heap(wstr);
464
} /* if */
465
466
467
468
469
470
471
472
473
474
__PHYSFS_smallFree(wstr);
} /* if */
CloseHandle(accessToken);
} /* if */
done:
FreeLibrary(lib);
return retval; /* We made it: hit the showers. */
} /* __PHYSFS_platformCalcUserDir */
475
476
477
void *__PHYSFS_platformGetThreadID(void)
478
{
479
return ( (void *) ((size_t) GetCurrentThreadId()) );
480
481
} /* __PHYSFS_platformGetThreadID */
482
void __PHYSFS_platformEnumerateFiles(const char *dirname,
483
484
PHYSFS_EnumFilesCallback callback,
const char *origdir,
485
void *callbackdata)
486
{
487
488
HANDLE dir = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW entw;
489
size_t len = strlen(dirname);
490
491
char *searchPath = NULL;
WCHAR *wSearchPath = NULL;
492
493
/* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
494
495
searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
if (searchPath == NULL)
496
return;
497
498
/* Copy current dirname */
499
strcpy(searchPath, dirname);
500
501
/* if there's no '\\' at the end of the path, stick one in there. */
502
if (searchPath[len - 1] != '\\')
503
{
504
505
searchPath[len++] = '\\';
searchPath[len] = '\0';
506
507
} /* if */
508
/* Append the "*" to the end of the string */
509
strcat(searchPath, "*");
510
511
UTF8_TO_UNICODE_STACK(wSearchPath, searchPath);
512
if (!wSearchPath)
513
return; /* oh well. */
514
515
dir = FindFirstFileW(wSearchPath, &entw);
516
517
518
519
520
__PHYSFS_smallFree(wSearchPath);
__PHYSFS_smallFree(searchPath);
if (dir == INVALID_HANDLE_VALUE)
return;
521
522
do
523
{
524
525
const WCHAR *fn = entw.cFileName;
char *utf8;
526
527
528
529
530
531
532
533
if ((fn[0] == '.') && (fn[1] == '\0'))
continue;
if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
continue;
utf8 = unicodeToUtf8Heap(fn);
if (utf8 != NULL)
534
{
535
536
537
538
callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
} /* if */
} while (FindNextFileW(dir, &entw) != 0);
539
540
541
542
543
544
545
FindClose(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
546
547
WCHAR *wpath;
DWORD rc;
548
UTF8_TO_UNICODE_STACK(wpath, path);
549
rc = CreateDirectoryW(wpath, NULL);
550
__PHYSFS_smallFree(wpath);
551
BAIL_IF(rc == 0, errcodeFromWinApi(), 0);
552
return 1;
553
554
} /* __PHYSFS_platformMkDir */
555
556
557
int __PHYSFS_platformInit(void)
{
558
return 1; /* It's all good */
559
} /* __PHYSFS_platformInit */
560
561
562
563
int __PHYSFS_platformDeinit(void)
{
564
if (detectCDThreadHandle)
565
566
567
568
569
570
571
572
573
{
if (detectCDHwnd)
PostMessageW(detectCDHwnd, WM_QUIT, 0, 0);
CloseHandle(detectCDThreadHandle);
detectCDThreadHandle = NULL;
initialDiscDetectionComplete = 0;
drivesWithMediaBitmap = 0;
} /* if */
574
return 1; /* It's all good */
575
576
577
578
579
} /* __PHYSFS_platformDeinit */
static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
{
580
HANDLE fileh;
581
582
WinApiFile *retval;
WCHAR *wfname;
583
584
585
UTF8_TO_UNICODE_STACK(wfname, fname);
BAIL_IF(!wfname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
586
587
fileh = CreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
588
__PHYSFS_smallFree(wfname);
589
590
BAIL_IF(fileh == INVALID_HANDLE_VALUE,errcodeFromWinApi(), NULL);
591
592
retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile));
593
if (!retval)
594
{
595
CloseHandle(fileh);
596
BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
597
} /* if */
598
599
retval->readonly = rdonly;
600
retval->handle = fileh;
601
return retval;
602
603
} /* doOpen */
604
605
606
void *__PHYSFS_platformOpenRead(const char *filename)
{
607
return doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1);
608
} /* __PHYSFS_platformOpenRead */
609
610
611
612
void *__PHYSFS_platformOpenWrite(const char *filename)
{
613
return doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0);
614
} /* __PHYSFS_platformOpenWrite */
615
616
617
618
void *__PHYSFS_platformOpenAppend(const char *filename)
{
619
620
621
void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
if (retval != NULL)
{
622
HANDLE h = ((WinApiFile *) retval)->handle;
623
624
DWORD rc = SetFilePointer(h, 0, NULL, FILE_END);
if (rc == PHYSFS_INVALID_SET_FILE_POINTER)
625
{
626
const PHYSFS_ErrorCode err = errcodeFromWinApi();
627
CloseHandle(h);
628
allocator.Free(retval);
629
BAIL(err, NULL);
630
631
632
} /* if */
} /* if */
633
return retval;
634
} /* __PHYSFS_platformOpenAppend */
635
636
637
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
638
{
639
HANDLE Handle = ((WinApiFile *) opaque)->handle;
640
PHYSFS_sint64 totalRead = 0;
641
642
if (!__PHYSFS_ui64FitsAddressSpace(len))
643
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
644
645
646
647
648
649
while (len > 0)
{
const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
DWORD numRead = 0;
if (!ReadFile(Handle, buf, thislen, &numRead, NULL))
650
BAIL(errcodeFromWinApi(), -1);
651
652
653
654
655
656
657
len -= (PHYSFS_uint64) numRead;
totalRead += (PHYSFS_sint64) numRead;
if (numRead != thislen)
break;
} /* while */
return totalRead;
658
659
} /* __PHYSFS_platformRead */
660
661
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
662
PHYSFS_uint64 len)
663
{
664
HANDLE Handle = ((WinApiFile *) opaque)->handle;
665
PHYSFS_sint64 totalWritten = 0;
666
667
if (!__PHYSFS_ui64FitsAddressSpace(len))
668
BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
669
670
671
672
673
674
while (len > 0)
{
const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
DWORD numWritten = 0;
if (!WriteFile(Handle, buffer, thislen, &numWritten, NULL))
675
BAIL(errcodeFromWinApi(), -1);
676
677
678
679
680
681
682
len -= (PHYSFS_uint64) numWritten;
totalWritten += (PHYSFS_sint64) numWritten;
if (numWritten != thislen)
break;
} /* while */
return totalWritten;
683
684
} /* __PHYSFS_platformWrite */
685
686
687
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
688
HANDLE Handle = ((WinApiFile *) opaque)->handle;
689
690
LONG HighOrderPos;
PLONG pHighOrderPos;
691
DWORD rc;
692
693
694
695
/* Get the high order 32-bits of the position */
HighOrderPos = HIGHORDER_UINT64(pos);
696
697
698
699
700
701
/*
* MSDN: "If you do not need the high-order 32 bits, this
* pointer must be set to NULL."
*/
pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL;
702
/* Move pointer "pos" count from start of file */
703
rc = SetFilePointer(Handle, LOWORDER_UINT64(pos),
704
pHighOrderPos, FILE_BEGIN);
705
706
707
708
if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
{
709
BAIL(errcodeFromWinApi(), 0);
710
711
} /* if */
712
return 1; /* No error occured */
713
} /* __PHYSFS_platformSeek */
714
715
716
717
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
718
HANDLE Handle = ((WinApiFile *) opaque)->handle;
719
LONG HighPos = 0;
720
DWORD LowPos;
721
722
723
PHYSFS_sint64 retval;
/* Get current position */
724
LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
725
726
if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
727
{
728
BAIL(errcodeFromWinApi(), -1);
729
} /* if */
730
731
732
else
{
/* Combine the high/low order to create the 64-bit position value */
733
734
735
736
retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
assert(retval >= 0);
} /* else */
737
return retval;
738
} /* __PHYSFS_platformTell */
739
740
741
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
742
{
743
HANDLE Handle = ((WinApiFile *) opaque)->handle;
744
745
DWORD SizeHigh;
DWORD SizeLow;
746
747
PHYSFS_sint64 retval;
748
SizeLow = GetFileSize(Handle, &SizeHigh);
749
750
if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
751
{
752
BAIL(errcodeFromWinApi(), -1);
753
} /* if */
754
755
756
else
{
/* Combine the high/low order to create the 64-bit position value */
757
758
retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
assert(retval >= 0);
759
} /* else */
760
761
return retval;
762
763
} /* __PHYSFS_platformFileLength */
764
765
766
int __PHYSFS_platformFlush(void *opaque)
{
767
WinApiFile *fh = ((WinApiFile *) opaque);
768
if (!fh->readonly)
769
BAIL_IF(!FlushFileBuffers(fh->handle), errcodeFromWinApi(), 0);
770
771
return 1;
772
} /* __PHYSFS_platformFlush */
773
774
775
void __PHYSFS_platformClose(void *opaque)
776
{
777
HANDLE Handle = ((WinApiFile *) opaque)->handle;
778
(void) CloseHandle(Handle); /* ignore errors. You should have flushed! */
779
allocator.Free(opaque);
780
} /* __PHYSFS_platformClose */
781
782
783
static int doPlatformDelete(LPWSTR wpath)
784
{
785
786
const int isdir = (GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY);
const BOOL rc = (isdir) ? RemoveDirectoryW(wpath) : DeleteFileW(wpath);
787
BAIL_IF(!rc, errcodeFromWinApi(), 0);
788
return 1; /* if you made it here, it worked. */
789
790
791
792
793
794
} /* doPlatformDelete */
int __PHYSFS_platformDelete(const char *path)
{
int retval = 0;
795
LPWSTR wpath = NULL;
796
797
UTF8_TO_UNICODE_STACK(wpath, path);
BAIL_IF(!wpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
798
799
retval = doPlatformDelete(wpath);
__PHYSFS_smallFree(wpath);
800
return retval;
801
} /* __PHYSFS_platformDelete */
802
803
804
805
void *__PHYSFS_platformCreateMutex(void)
{
806
807
LPCRITICAL_SECTION lpcs;
lpcs = (LPCRITICAL_SECTION) allocator.Malloc(sizeof (CRITICAL_SECTION));
808
BAIL_IF(!lpcs, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
809
810
InitializeCriticalSection(lpcs);
return lpcs;
811
812
} /* __PHYSFS_platformCreateMutex */
813
814
815
void __PHYSFS_platformDestroyMutex(void *mutex)
{
816
817
DeleteCriticalSection((LPCRITICAL_SECTION) mutex);
allocator.Free(mutex);
818
819
} /* __PHYSFS_platformDestroyMutex */
820
821
822
int __PHYSFS_platformGrabMutex(void *mutex)
{
823
824
EnterCriticalSection((LPCRITICAL_SECTION) mutex);
return 1;
825
} /* __PHYSFS_platformGrabMutex */
826
827
828
829
void __PHYSFS_platformReleaseMutex(void *mutex)
{
830
LeaveCriticalSection((LPCRITICAL_SECTION) mutex);
831
832
} /* __PHYSFS_platformReleaseMutex */
833
834
static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft)
835
836
837
{
SYSTEMTIME st_utc;
SYSTEMTIME st_localtz;
838
839
840
TIME_ZONE_INFORMATION tzi;
DWORD tzid;
PHYSFS_sint64 retval;
841
struct tm tm;
842
BOOL rc;
843
844
BAIL_IF(!FileTimeToSystemTime(ft, &st_utc), errcodeFromWinApi(), -1);
845
tzid = GetTimeZoneInformation(&tzi);
846
BAIL_IF(tzid == TIME_ZONE_ID_INVALID, errcodeFromWinApi(), -1);
847
rc = SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz);
848
BAIL_IF(!rc, errcodeFromWinApi(), -1);
849
850
/* Convert to a format that mktime() can grok... */
851
852
853
854
855
856
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;
857
tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
858
859
tm.tm_yday = -1;
tm.tm_isdst = -1;
860
861
862
/* Convert to a format PhysicsFS can grok... */
retval = (PHYSFS_sint64) mktime(&tm);
863
BAIL_IF(retval == -1, PHYSFS_ERR_OS_ERROR, -1);
864
return retval;
865
866
} /* FileTimeToPhysfsTime */
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
/* 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);
888
return (w32dw.dwReserved0 == PHYSFS_IO_REPARSE_TAG_SYMLINK);
889
890
891
} /* isSymlink */
892
int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
893
894
895
{
WIN32_FILE_ATTRIBUTE_DATA winstat;
WCHAR *wstr = NULL;
896
DWORD err = 0;
897
BOOL rc = 0;
898
int issymlink = 0;
899
900
901
UTF8_TO_UNICODE_STACK(wstr, filename);
BAIL_IF(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0);
902
rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat);
903
904
905
906
907
908
if (!rc)
err = GetLastError();
else /* check for symlink while wstr is still available */
issymlink = isSymlink(wstr, winstat.dwFileAttributes);
909
__PHYSFS_smallFree(wstr);
910
BAIL_IF(!rc, errcodeFromWinApiError(err), 0);
911
912
913
914
st->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime);
st->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime);
st->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime);
915
916
if (issymlink)
917
{
918
st->filetype = PHYSFS_FILETYPE_SYMLINK;
919
st->filesize = 0;
920
921
} /* if */
922
923
924
925
926
927
928
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))
929
{
930
st->filetype = PHYSFS_FILETYPE_OTHER;
931
/* !!! FIXME: don't rely on this */
932
st->filesize = 0;
933
934
935
936
} /* else if */
else
{
937
938
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow;
939
940
} /* else */
941
st->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
942
943
return 1;
944
945
} /* __PHYSFS_platformStat */
946
#endif /* PHYSFS_PLATFORM_WINDOWS */
947
#endif /* PHYSFS_PLATFORM_WINRT */
948
949
/* end of windows.c ... */