/
platform_windows.c
932 lines (755 loc) · 27.7 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
10
11
12
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_WINDOWS
13
14
/* Forcibly disable UNICODE macro, since we manage this ourselves. */
15
16
17
18
#ifdef UNICODE
#undef UNICODE
#endif
19
#define WIN32_LEAN_AND_MEAN 1
20
#include <windows.h>
21
#include <userenv.h>
22
#include <dbt.h>
23
#include <errno.h>
24
#include <ctype.h>
25
#include <time.h>
26
27
28
#include "physfs_internal.h"
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
43
44
45
/* Not defined before the Vista SDK. */
#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C
46
47
48
49
#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \
if (str == NULL) \
w_assignto = NULL; \
else { \
50
const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \
51
52
w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \
if (w_assignto != NULL) \
53
PHYSFS_utf8ToUtf16(str, (PHYSFS_uint16 *) w_assignto, len); \
54
55
} \
} \
56
57
/* Note this counts WCHARs, not codepoints! */
58
59
60
61
62
static PHYSFS_uint64 wStrLen(const WCHAR *wstr)
{
PHYSFS_uint64 len = 0;
while (*(wstr++))
len++;
63
return len;
64
65
66
67
68
69
70
71
72
73
} /* 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);
74
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
75
PHYSFS_utf8FromUtf16((const PHYSFS_uint16 *) w_str, retval, len);
76
77
78
79
ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */
if (ptr != NULL)
retval = (char *) ptr;
} /* if */
80
return retval;
81
82
} /* unicodeToUtf8Heap */
83
/* !!! FIXME: do we really need readonly? If not, do we need this struct? */
84
85
86
87
typedef struct
{
HANDLE handle;
int readonly;
88
} WinApiFile;
89
90
91
static char *userDir = NULL;
92
static HANDLE libUserEnv = NULL;
93
94
95
96
static HANDLE detectCDThreadHandle = NULL;
static HWND detectCDHwnd = 0;
static volatile int initialDiscDetectionComplete = 0;
static volatile DWORD drivesWithMediaBitmap = 0;
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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)
112
{
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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_DISK_RESOURCES_EXHAUSTED: return PHYSFS_ERR_NO_SPACE;
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;
case ERROR_DATA_CHECKSUM_ERROR: 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;
case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NO_SUCH_PATH;
case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NO_SUCH_PATH;
case ERROR_DELETE_PENDING: return PHYSFS_ERR_NO_SUCH_PATH;
case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NO_SUCH_PATH;
case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
/* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH; */
/* !!! 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 */
157
158
159
160
/*
161
162
163
164
* On success, module-scope variable (userDir) will have a pointer to
* a malloc()'d string of the user's profile dir, and a non-zero value is
* returned. If we can't determine the profile dir, (userDir) will
* be NULL, and zero is returned.
165
*/
166
static int determineUserDir(void)
167
{
168
169
170
171
172
typedef BOOL (WINAPI *fnGetUserProfDirW)(HANDLE, LPWSTR, LPDWORD);
fnGetUserProfDirW pGetDir = NULL;
HANDLE accessToken = NULL; /* Security handle to process */
173
if (userDir != NULL)
174
return 1; /* already good to go. */
175
176
177
pGetDir = (fnGetUserProfDirW)
GetProcAddress(libUserEnv, "GetUserProfileDirectoryW");
178
BAIL_IF_MACRO(pGetDir == NULL, errcodeFromWinApi(), 0);
179
180
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &accessToken))
181
BAIL_MACRO(errcodeFromWinApi(), 0);
182
else
183
{
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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 */
wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR));
if (wstr != NULL)
201
{
202
203
204
if (pGetDir(accessToken, wstr, &psize))
userDir = unicodeToUtf8Heap(wstr);
__PHYSFS_smallFree(wstr);
205
} /* if */
206
207
CloseHandle(accessToken);
208
209
} /* if */
210
return 1; /* We made it: hit the showers. */
211
} /* determineUserDir */
212
213
214
typedef BOOL (WINAPI *fnSTEM)(DWORD, LPDWORD b);
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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);
233
234
235
236
237
238
239
240
/* 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;
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
297
298
299
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/* 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 */
348
349
350
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
351
{
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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++)
376
{
377
378
379
if (drives & (1 << (i - 'A')))
{
drive_str[0] = (char) i;
380
cb(data, drive_str);
381
} /* if */
382
} /* for */
383
} /* __PHYSFS_platformDetectAvailableCDs */
384
385
386
387
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
388
389
390
DWORD buflen = 64;
LPWSTR modpath = NULL;
char *retval = NULL;
391
392
393
394
395
396
397
398
399
while (1)
{
DWORD rc;
void *ptr;
if ( (ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) == NULL )
{
allocator.Free(modpath);
400
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
401
402
403
404
405
406
407
} /* if */
modpath = (LPWSTR) ptr;
rc = GetModuleFileNameW(NULL, modpath, buflen);
if (rc == 0)
{
allocator.Free(modpath);
408
BAIL_MACRO(errcodeFromWinApi(), NULL);
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
} /* 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 != '\\'))
__PHYSFS_setError(ERR_GETMODFN_NO_DIR);
else
{
*(ptr + 1) = '\0'; /* chop off filename. */
retval = unicodeToUtf8Heap(modpath);
} /* else */
} /* else */
allocator.Free(modpath);
return retval; /* w00t. */
441
442
443
444
445
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformGetUserName(void)
{
446
DWORD bufsize = 0;
447
448
char *retval = NULL;
449
if (GetUserNameW(NULL, &bufsize) == 0) /* This SHOULD fail. */
450
{
451
LPWSTR wbuf = (LPWSTR) __PHYSFS_smallAlloc(bufsize * sizeof (WCHAR));
452
BAIL_IF_MACRO(!wbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
453
if (GetUserNameW(wbuf, &bufsize) == 0) /* ?! */
454
__PHYSFS_setError(errcodeFromWinApi());
455
456
457
else
retval = unicodeToUtf8Heap(wbuf);
__PHYSFS_smallFree(wbuf);
458
459
} /* if */
460
return retval;
461
462
463
464
465
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void)
{
466
char *retval = (char *) allocator.Malloc(strlen(userDir) + 1);
467
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
468
strcpy(retval, userDir); /* calculated at init time. */
469
return retval;
470
471
472
} /* __PHYSFS_platformGetUserDir */
473
void *__PHYSFS_platformGetThreadID(void)
474
{
475
return ( (void *) ((size_t) GetCurrentThreadId()) );
476
477
478
} /* __PHYSFS_platformGetThreadID */
479
static int isSymlinkAttrs(const DWORD attr, const DWORD tag)
480
{
481
482
return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) &&
(tag == PHYSFS_IO_REPARSE_TAG_SYMLINK) );
483
484
485
} /* isSymlinkAttrs */
486
487
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
488
489
PHYSFS_EnumFilesCallback callback,
const char *origdir,
490
void *callbackdata)
491
{
492
493
HANDLE dir = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAW entw;
494
size_t len = strlen(dirname);
495
496
char *searchPath = NULL;
WCHAR *wSearchPath = NULL;
497
498
/* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
499
500
searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
if (searchPath == NULL)
501
return;
502
503
/* Copy current dirname */
504
strcpy(searchPath, dirname);
505
506
/* if there's no '\\' at the end of the path, stick one in there. */
507
if (searchPath[len - 1] != '\\')
508
{
509
510
searchPath[len++] = '\\';
searchPath[len] = '\0';
511
512
} /* if */
513
/* Append the "*" to the end of the string */
514
strcat(searchPath, "*");
515
516
UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath);
517
if (!wSearchPath)
518
return; /* oh well. */
519
520
dir = FindFirstFileW(wSearchPath, &entw);
521
522
523
524
525
__PHYSFS_smallFree(wSearchPath);
__PHYSFS_smallFree(searchPath);
if (dir == INVALID_HANDLE_VALUE)
return;
526
527
do
528
{
529
530
531
532
const DWORD attr = entw.dwFileAttributes;
const DWORD tag = entw.dwReserved0;
const WCHAR *fn = entw.cFileName;
char *utf8;
533
534
535
536
537
538
539
540
541
542
if ((fn[0] == '.') && (fn[1] == '\0'))
continue;
if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
continue;
if ((omitSymLinks) && (isSymlinkAttrs(attr, tag)))
continue;
utf8 = unicodeToUtf8Heap(fn);
if (utf8 != NULL)
543
{
544
545
546
547
callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
} /* if */
} while (FindNextFileW(dir, &entw) != 0);
548
549
550
551
552
553
554
FindClose(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
555
556
557
WCHAR *wpath;
DWORD rc;
UTF8_TO_UNICODE_STACK_MACRO(wpath, path);
558
rc = CreateDirectoryW(wpath, NULL);
559
__PHYSFS_smallFree(wpath);
560
BAIL_IF_MACRO(rc == 0, errcodeFromWinApi(), 0);
561
return 1;
562
563
} /* __PHYSFS_platformMkDir */
564
565
566
int __PHYSFS_platformInit(void)
{
567
libUserEnv = LoadLibraryA("userenv.dll");
568
BAIL_IF_MACRO(libUserEnv == NULL, errcodeFromWinApi(), 0);
569
570
/* !!! FIXME: why do we precalculate this? */
571
BAIL_IF_MACRO(!determineUserDir(), ERRPASS, 0);
572
return 1; /* It's all good */
573
} /* __PHYSFS_platformInit */
574
575
576
577
int __PHYSFS_platformDeinit(void)
{
578
if (detectCDThreadHandle)
579
580
581
582
583
584
585
586
587
{
if (detectCDHwnd)
PostMessageW(detectCDHwnd, WM_QUIT, 0, 0);
CloseHandle(detectCDThreadHandle);
detectCDThreadHandle = NULL;
initialDiscDetectionComplete = 0;
drivesWithMediaBitmap = 0;
} /* if */
588
589
590
if (libUserEnv)
FreeLibrary(libUserEnv);
libUserEnv = NULL;
591
592
allocator.Free(userDir);
userDir = NULL;
593
return 1; /* It's all good */
594
595
596
597
598
} /* __PHYSFS_platformDeinit */
static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
{
599
HANDLE fileh;
600
601
WinApiFile *retval;
WCHAR *wfname;
602
603
UTF8_TO_UNICODE_STACK_MACRO(wfname, fname);
604
BAIL_IF_MACRO(!wfname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
605
606
fileh = CreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
607
__PHYSFS_smallFree(wfname);
608
609
BAIL_IF_MACRO(fileh == INVALID_HANDLE_VALUE,errcodeFromWinApi(), NULL);
610
611
retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile));
612
if (!retval)
613
{
614
CloseHandle(fileh);
615
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
616
} /* if */
617
618
retval->readonly = rdonly;
619
retval->handle = fileh;
620
return retval;
621
622
} /* doOpen */
623
624
625
void *__PHYSFS_platformOpenRead(const char *filename)
{
626
return doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1);
627
} /* __PHYSFS_platformOpenRead */
628
629
630
631
void *__PHYSFS_platformOpenWrite(const char *filename)
{
632
return doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0);
633
} /* __PHYSFS_platformOpenWrite */
634
635
636
637
void *__PHYSFS_platformOpenAppend(const char *filename)
{
638
639
640
void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
if (retval != NULL)
{
641
HANDLE h = ((WinApiFile *) retval)->handle;
642
643
DWORD rc = SetFilePointer(h, 0, NULL, FILE_END);
if (rc == PHYSFS_INVALID_SET_FILE_POINTER)
644
{
645
const PHYSFS_ErrorCode err = errcodeFromWinApi();
646
CloseHandle(h);
647
allocator.Free(retval);
648
649
650
651
BAIL_MACRO(err, NULL);
} /* if */
} /* if */
652
return retval;
653
} /* __PHYSFS_platformOpenAppend */
654
655
656
/* !!! FIXME: this function fails if len > 0xFFFFFFFF. */
657
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
658
{
659
HANDLE Handle = ((WinApiFile *) opaque)->handle;
660
DWORD CountOfBytesRead = 0;
661
662
663
664
665
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
else if(!ReadFile(Handle, buf, (DWORD) len, &CountOfBytesRead, NULL))
BAIL_MACRO(errcodeFromWinApi(), -1);
666
667
return (PHYSFS_sint64) CountOfBytesRead;
668
669
} /* __PHYSFS_platformRead */
670
671
/* !!! FIXME: this function fails if len > 0xFFFFFFFF. */
672
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
673
PHYSFS_uint64 len)
674
{
675
HANDLE Handle = ((WinApiFile *) opaque)->handle;
676
DWORD CountOfBytesWritten = 0;
677
678
679
680
681
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
else if(!WriteFile(Handle, buffer, (DWORD) len, &CountOfBytesWritten, NULL))
BAIL_MACRO(errcodeFromWinApi(), -1);
682
683
return (PHYSFS_sint64) CountOfBytesWritten;
684
685
} /* __PHYSFS_platformWrite */
686
687
688
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
689
HANDLE Handle = ((WinApiFile *) opaque)->handle;
690
691
LONG HighOrderPos;
PLONG pHighOrderPos;
692
DWORD rc;
693
694
695
696
/* Get the high order 32-bits of the position */
HighOrderPos = HIGHORDER_UINT64(pos);
697
698
699
700
701
702
703
704
705
706
707
708
709
710
/*
* MSDN: "If you do not need the high-order 32 bits, this
* pointer must be set to NULL."
*/
pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL;
/*
* !!! FIXME: MSDN: "Windows Me/98/95: If the pointer
* !!! FIXME: lpDistanceToMoveHigh is not NULL, then it must
* !!! FIXME: point to either 0, INVALID_SET_FILE_POINTER, or
* !!! FIXME: the sign extension of the value of lDistanceToMove.
* !!! FIXME: Any other value will be rejected."
*/
711
/* Move pointer "pos" count from start of file */
712
rc = SetFilePointer(Handle, LOWORDER_UINT64(pos),
713
pHighOrderPos, FILE_BEGIN);
714
715
716
717
if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
{
718
BAIL_MACRO(errcodeFromWinApi(), 0);
719
720
} /* if */
721
return 1; /* No error occured */
722
} /* __PHYSFS_platformSeek */
723
724
725
726
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
727
HANDLE Handle = ((WinApiFile *) opaque)->handle;
728
LONG HighPos = 0;
729
DWORD LowPos;
730
731
732
PHYSFS_sint64 retval;
/* Get current position */
733
LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
734
735
if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
736
{
737
BAIL_MACRO(errcodeFromWinApi(), -1);
738
} /* if */
739
740
741
else
{
/* Combine the high/low order to create the 64-bit position value */
742
743
744
745
retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
assert(retval >= 0);
} /* else */
746
return retval;
747
} /* __PHYSFS_platformTell */
748
749
750
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
751
{
752
HANDLE Handle = ((WinApiFile *) opaque)->handle;
753
754
DWORD SizeHigh;
DWORD SizeLow;
755
756
PHYSFS_sint64 retval;
757
SizeLow = GetFileSize(Handle, &SizeHigh);
758
759
if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) &&
(GetLastError() != NO_ERROR) )
760
{
761
BAIL_MACRO(errcodeFromWinApi(), -1);
762
} /* if */
763
764
765
else
{
/* Combine the high/low order to create the 64-bit position value */
766
767
retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
assert(retval >= 0);
768
} /* else */
769
770
return retval;
771
772
} /* __PHYSFS_platformFileLength */
773
774
775
int __PHYSFS_platformFlush(void *opaque)
{
776
WinApiFile *fh = ((WinApiFile *) opaque);
777
if (!fh->readonly)
778
BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), errcodeFromWinApi(), 0);
779
780
return 1;
781
} /* __PHYSFS_platformFlush */
782
783
784
void __PHYSFS_platformClose(void *opaque)
785
{
786
HANDLE Handle = ((WinApiFile *) opaque)->handle;
787
(void) CloseHandle(Handle); /* ignore errors. You should have flushed! */
788
allocator.Free(opaque);
789
} /* __PHYSFS_platformClose */
790
791
792
static int doPlatformDelete(LPWSTR wpath)
793
{
794
795
const int isdir = (GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY);
const BOOL rc = (isdir) ? RemoveDirectoryW(wpath) : DeleteFileW(wpath);
796
BAIL_IF_MACRO(!rc, errcodeFromWinApi(), 0);
797
return 1; /* if you made it here, it worked. */
798
799
800
801
802
803
} /* doPlatformDelete */
int __PHYSFS_platformDelete(const char *path)
{
int retval = 0;
804
LPWSTR wpath = NULL;
805
UTF8_TO_UNICODE_STACK_MACRO(wpath, path);
806
BAIL_IF_MACRO(!wpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
807
808
retval = doPlatformDelete(wpath);
__PHYSFS_smallFree(wpath);
809
return retval;
810
} /* __PHYSFS_platformDelete */
811
812
813
814
815
816
817
/*
* !!! FIXME: why aren't we using Critical Sections instead of Mutexes?
* !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are
* !!! FIXME: mutexes for threads in a single process and are faster.
*/
818
819
void *__PHYSFS_platformCreateMutex(void)
{
820
return ((void *) CreateMutex(NULL, FALSE, NULL));
821
822
} /* __PHYSFS_platformCreateMutex */
823
824
825
void __PHYSFS_platformDestroyMutex(void *mutex)
{
826
827
828
CloseHandle((HANDLE) mutex);
} /* __PHYSFS_platformDestroyMutex */
829
830
831
int __PHYSFS_platformGrabMutex(void *mutex)
{
832
return (WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED);
833
} /* __PHYSFS_platformGrabMutex */
834
835
836
837
void __PHYSFS_platformReleaseMutex(void *mutex)
{
838
839
840
ReleaseMutex((HANDLE) mutex);
} /* __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_MACRO(!FileTimeToSystemTime(ft, &st_utc), errcodeFromWinApi(), -1);
853
tzid = GetTimeZoneInformation(&tzi);
854
BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, errcodeFromWinApi(), -1);
855
rc = SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz);
856
BAIL_IF_MACRO(!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_MACRO(retval == -1, PHYSFS_ERR_OS_ERROR, -1);
872
return retval;
873
874
} /* FileTimeToPhysfsTime */
875
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *stat)
876
877
878
{
WIN32_FILE_ATTRIBUTE_DATA winstat;
WCHAR *wstr = NULL;
879
DWORD err = 0;
880
881
882
BOOL rc = 0;
UTF8_TO_UNICODE_STACK_MACRO(wstr, filename);
883
BAIL_IF_MACRO(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0);
884
885
886
rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat);
err = (!rc) ? GetLastError() : 0;
*exists = ((err != ERROR_FILE_NOT_FOUND) && (err != ERROR_PATH_NOT_FOUND));
887
__PHYSFS_smallFree(wstr);
888
BAIL_IF_MACRO(!rc, errcodeFromWinApiError(err), 0);
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
stat->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime);
stat->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime);
stat->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime);
if(winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
stat->filesize = 0;
} /* if */
else if(winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE))
{
/* !!! FIXME: what are reparse points? */
stat->filetype = PHYSFS_FILETYPE_OTHER;
/* !!! FIXME: don't rely on this */
stat->filesize = 0;
} /* else if */
/* !!! FIXME: check for symlinks on Vista. */
else
{
stat->filetype = PHYSFS_FILETYPE_REGULAR;
913
stat->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow;
914
915
916
917
} /* else */
stat->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
918
return 1;
919
920
921
} /* __PHYSFS_platformStat */
922
/* !!! FIXME: Don't use C runtime for allocators? */
923
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
924
{
925
return 0; /* just use malloc() and friends. */
926
} /* __PHYSFS_platformSetDefaultAllocator */
927
928
#endif /* PHYSFS_PLATFORM_WINDOWS */
929
930
/* end of windows.c ... */