/
platform_windows.c
1439 lines (1177 loc) · 42.6 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
15
16
17
18
/* Forcibly disable UNICODE, since we manage this ourselves. */
#ifdef UNICODE
#undef UNICODE
#endif
19
20
#include <windows.h>
#include <stdio.h>
21
#include <stdlib.h>
22
23
#include <string.h>
#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
53
w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \
if (w_assignto != NULL) \
PHYSFS_utf8ToUcs2(str, (PHYSFS_uint16 *) w_assignto, len); \
54
55
} \
} \
56
57
58
59
60
61
static PHYSFS_uint64 wStrLen(const WCHAR *wstr)
{
PHYSFS_uint64 len = 0;
while (*(wstr++))
len++;
62
return len;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
} /* 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);
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) w_str, retval, len);
ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */
if (ptr != NULL)
retval = (char *) ptr;
} /* if */
79
return retval;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
} /* unicodeToUtf8Heap */
static char *codepageToUtf8Heap(const char *cpstr)
{
char *retval = NULL;
if (cpstr != NULL)
{
const int len = (int) (strlen(cpstr) + 1);
WCHAR *wbuf = (WCHAR *) __PHYSFS_smallAlloc(len * sizeof (WCHAR));
BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, len, wbuf, len);
retval = (char *) allocator.Malloc(len * 4);
if (retval == NULL)
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
else
PHYSFS_utf8FromUcs2(wbuf, retval, len * 4);
__PHYSFS_smallFree(wbuf);
} /* if */
99
return retval;
100
101
102
} /* codepageToUtf8Heap */
103
104
105
106
typedef struct
{
HANDLE handle;
int readonly;
107
} WinApiFile;
108
109
110
static char *userDir = NULL;
111
static int osHasUnicode = 0;
112
113
114
/* pointers for APIs that may not exist on some Windows versions... */
115
static HANDLE libKernel32 = NULL;
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
static HANDLE libUserEnv = NULL;
static HANDLE libAdvApi32 = NULL;
static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD);
static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD);
static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR);
static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW);
static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW);
static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR);
static BOOL (WINAPI *pDeleteFileW)(LPCWSTR);
static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR);
static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES);
static BOOL (WINAPI *pGetFileAttributesExA)
(LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
static BOOL (WINAPI *pGetFileAttributesExW)
(LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
static DWORD (WINAPI *pFormatMessageW)
(DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *);
static HANDLE (WINAPI *pCreateFileW)
(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Fallbacks for missing Unicode functions on Win95/98/ME. These are filled
* into the function pointers if looking up the real Unicode entry points
* in the system DLLs fails, so they're never used on WinNT/XP/Vista/etc.
* They make an earnest effort to convert to/from UTF-8 and UCS-2 to
* the user's current codepage.
*/
static BOOL WINAPI fallbackGetUserNameW(LPWSTR buf, LPDWORD len)
{
const DWORD cplen = *len;
char *cpstr = __PHYSFS_smallAlloc(cplen);
BOOL retval = GetUserNameA(cpstr, len);
if (buf != NULL)
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, cplen, buf, *len);
__PHYSFS_smallFree(cpstr);
154
return retval;
155
156
157
158
159
160
161
162
163
164
165
166
167
} /* fallbackGetUserNameW */
static DWORD WINAPI fallbackFormatMessageW(DWORD dwFlags, LPCVOID lpSource,
DWORD dwMessageId, DWORD dwLangId,
LPWSTR lpBuf, DWORD nSize,
va_list *Arguments)
{
char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize);
DWORD retval = FormatMessageA(dwFlags, lpSource, dwMessageId, dwLangId,
cpbuf, nSize, Arguments);
if (retval > 0)
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize);
__PHYSFS_smallFree(cpbuf);
168
return retval;
169
170
171
172
173
174
175
176
177
178
} /* fallbackFormatMessageW */
static DWORD WINAPI fallbackGetModuleFileNameW(HMODULE hMod, LPWCH lpBuf,
DWORD nSize)
{
char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize);
DWORD retval = GetModuleFileNameA(hMod, cpbuf, nSize);
if (retval > 0)
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize);
__PHYSFS_smallFree(cpbuf);
179
return retval;
180
181
182
183
184
185
186
187
188
189
} /* fallbackGetModuleFileNameW */
static DWORD WINAPI fallbackGetFileAttributesW(LPCWSTR fname)
{
DWORD retval = 0;
const int buflen = (int) (wStrLen(fname) + 1);
char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
retval = GetFileAttributesA(cpstr);
__PHYSFS_smallFree(cpstr);
190
return retval;
191
192
193
194
195
196
197
198
199
200
201
202
203
204
} /* fallbackGetFileAttributesW */
static DWORD WINAPI fallbackGetCurrentDirectoryW(DWORD buflen, LPWSTR buf)
{
DWORD retval = 0;
char *cpbuf = NULL;
if (buf != NULL)
cpbuf = (char *) __PHYSFS_smallAlloc(buflen);
retval = GetCurrentDirectoryA(buflen, cpbuf);
if (cpbuf != NULL)
{
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,buf,buflen);
__PHYSFS_smallFree(cpbuf);
} /* if */
205
return retval;
206
207
208
209
210
211
212
213
214
215
} /* fallbackGetCurrentDirectoryW */
static BOOL WINAPI fallbackRemoveDirectoryW(LPCWSTR dname)
{
BOOL retval = 0;
const int buflen = (int) (wStrLen(dname) + 1);
char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL);
retval = RemoveDirectoryA(cpstr);
__PHYSFS_smallFree(cpstr);
216
return retval;
217
218
219
220
221
222
223
224
225
226
227
} /* fallbackRemoveDirectoryW */
static BOOL WINAPI fallbackCreateDirectoryW(LPCWSTR dname,
LPSECURITY_ATTRIBUTES attr)
{
BOOL retval = 0;
const int buflen = (int) (wStrLen(dname) + 1);
char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL);
retval = CreateDirectoryA(cpstr, attr);
__PHYSFS_smallFree(cpstr);
228
return retval;
229
230
231
232
233
234
235
236
237
238
} /* fallbackCreateDirectoryW */
static BOOL WINAPI fallbackDeleteFileW(LPCWSTR fname)
{
BOOL retval = 0;
const int buflen = (int) (wStrLen(fname) + 1);
char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
retval = DeleteFileA(cpstr);
__PHYSFS_smallFree(cpstr);
239
return retval;
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
} /* fallbackDeleteFileW */
static HANDLE WINAPI fallbackCreateFileW(LPCWSTR fname,
DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttrs,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttrs, HANDLE hTemplFile)
{
HANDLE retval;
const int buflen = (int) (wStrLen(fname) + 1);
char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
retval = CreateFileA(cpstr, dwDesiredAccess, dwShareMode, lpSecurityAttrs,
dwCreationDisposition, dwFlagsAndAttrs, hTemplFile);
__PHYSFS_smallFree(cpstr);
255
return retval;
256
} /* fallbackCreateFileW */
257
258
259
#if (PHYSFS_MINIMUM_GCC_VERSION(3,3))
260
261
262
263
264
265
266
267
typedef FARPROC __attribute__((__may_alias__)) PHYSFS_FARPROC;
#else
typedef FARPROC PHYSFS_FARPROC;
#endif
static void symLookup(HMODULE dll, PHYSFS_FARPROC *addr, const char *sym,
int reallyLook, PHYSFS_FARPROC fallback)
268
{
269
270
271
272
273
PHYSFS_FARPROC proc;
proc = (PHYSFS_FARPROC) ((reallyLook) ? GetProcAddress(dll, sym) : NULL);
if (proc == NULL)
proc = fallback; /* may also be NULL. */
*addr = proc;
274
275
276
277
278
279
280
} /* symLookup */
static int findApiSymbols(void)
{
HMODULE dll = NULL;
281
282
283
284
285
286
#define LOOKUP_NOFALLBACK(x, reallyLook) \
symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, reallyLook, NULL)
#define LOOKUP(x, reallyLook) \
symLookup(dll, (PHYSFS_FARPROC *) &p##x, #x, \
reallyLook, (PHYSFS_FARPROC) fallback##x)
287
288
289
290
/* Apparently Win9x HAS the Unicode entry points, they just don't WORK. */
/* ...so don't look them up unless we're on NT+. (see osHasUnicode.) */
291
dll = libUserEnv = LoadLibraryA("userenv.dll");
292
if (dll != NULL)
293
LOOKUP_NOFALLBACK(GetUserProfileDirectoryW, osHasUnicode);
294
295
dll = libAdvApi32 = LoadLibraryA("advapi32.dll");
296
if (dll != NULL)
297
LOOKUP(GetUserNameW, osHasUnicode);
298
299
dll = libKernel32 = LoadLibraryA("kernel32.dll");
300
301
if (dll != NULL)
{
302
303
304
305
306
307
308
309
310
311
312
313
LOOKUP_NOFALLBACK(GetFileAttributesExA, 1);
LOOKUP_NOFALLBACK(GetFileAttributesExW, osHasUnicode);
LOOKUP_NOFALLBACK(FindFirstFileW, osHasUnicode);
LOOKUP_NOFALLBACK(FindNextFileW, osHasUnicode);
LOOKUP(GetModuleFileNameW, osHasUnicode);
LOOKUP(FormatMessageW, osHasUnicode);
LOOKUP(GetFileAttributesW, osHasUnicode);
LOOKUP(GetCurrentDirectoryW, osHasUnicode);
LOOKUP(CreateDirectoryW, osHasUnicode);
LOOKUP(RemoveDirectoryW, osHasUnicode);
LOOKUP(CreateFileW, osHasUnicode);
LOOKUP(DeleteFileW, osHasUnicode);
314
} /* if */
315
316
#undef LOOKUP_NOFALLBACK
317
318
#undef LOOKUP
319
return 1;
320
} /* findApiSymbols */
321
322
323
324
325
const char *__PHYSFS_platformDirSeparator = "\\";
326
/*
327
* Figure out what the last failing Windows API call was, and
328
329
330
331
332
* generate a human-readable string for the error message.
*
* The return value is a static buffer that is overwritten with
* each call to this function.
*/
333
static const char *winApiStrError(void)
334
{
335
336
337
338
339
340
341
342
343
344
static char utf8buf[255];
WCHAR msgbuf[255];
WCHAR *ptr;
DWORD rc = pFormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msgbuf, __PHYSFS_ARRAYLEN(msgbuf),
NULL);
345
346
347
348
if (rc == 0)
msgbuf[0] = '\0'; /* oh well. */
349
/* chop off newlines. */
350
351
352
353
for (ptr = msgbuf; *ptr; ptr++)
{
if ((*ptr == '\n') || (*ptr == '\r'))
{
354
*ptr = '\0';
355
356
357
358
break;
} /* if */
} /* for */
359
360
/* may truncate, but oh well. */
PHYSFS_utf8FromUcs2((PHYSFS_uint16 *) msgbuf, utf8buf, sizeof (utf8buf));
361
return ((const char *) utf8buf);
362
} /* winApiStrError */
363
364
365
static char *getExePath(void)
366
{
367
368
369
DWORD buflen = 64;
LPWSTR modpath = NULL;
char *retval = NULL;
370
371
while (1)
372
{
373
374
DWORD rc;
void *ptr;
375
376
if ( !(ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) )
377
{
378
379
380
381
allocator.Free(modpath);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
modpath = (LPWSTR) ptr;
382
383
384
385
386
rc = pGetModuleFileNameW(NULL, modpath, buflen);
if (rc == 0)
{
allocator.Free(modpath);
387
BAIL_MACRO(winApiStrError(), NULL);
388
} /* if */
389
390
391
392
393
394
395
396
397
398
399
if (rc < buflen)
{
buflen = rc;
break;
} /* if */
buflen *= 2;
} /* while */
if (buflen > 0) /* just in case... */
400
{
401
402
403
404
405
406
407
408
409
410
WCHAR *ptr = (modpath + buflen) - 1;
while (ptr != modpath)
{
if (*ptr == '\\')
break;
ptr--;
} /* while */
if ((ptr == modpath) && (*ptr != '\\'))
__PHYSFS_setError(ERR_GETMODFN_NO_DIR);
411
412
else
{
413
*(ptr + 1) = '\0'; /* chop off filename. */
414
retval = unicodeToUtf8Heap(modpath);
415
} /* else */
416
417
} /* else */
allocator.Free(modpath);
418
419
return retval; /* w00t. */
420
} /* getExePath */
421
422
423
/*
424
* Try to make use of GetUserProfileDirectoryW(), which isn't available on
425
426
* some common variants of Win32. If we can't use this, we just punt and
* use the physfs base dir for the user dir, too.
427
*
428
429
430
431
* 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.
432
*/
433
static int determineUserDir(void)
434
{
435
if (userDir != NULL)
436
return 1; /* already good to go. */
437
438
/*
439
* GetUserProfileDirectoryW() is only available on NT 4.0 and later.
440
441
* This means Win95/98/ME (and CE?) users have to do without, so for
* them, we'll default to the base directory when we can't get the
442
443
* function pointer. Since this is originally an NT API, we don't
* offer a non-Unicode fallback.
444
*/
445
if (pGetUserProfileDirectoryW != NULL)
446
{
447
448
449
HANDLE accessToken = NULL; /* Security handle to process */
HANDLE processHandle = GetCurrentProcess();
if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
450
{
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
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 = pGetUserProfileDirectoryW(accessToken, &dummy, &psize);
assert(!rc); /* !!! FIXME: handle this gracefully. */
/* Allocate memory for the profile directory */
wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR));
if (wstr != NULL)
467
{
468
if (pGetUserProfileDirectoryW(accessToken, wstr, &psize))
469
userDir = unicodeToUtf8Heap(wstr);
470
471
__PHYSFS_smallFree(wstr);
} /* else */
472
} /* if */
473
474
CloseHandle(accessToken);
475
476
} /* if */
477
if (userDir == NULL) /* couldn't get profile for some reason. */
478
{
479
/* Might just be a non-NT system; resort to the basedir. */
480
userDir = getExePath();
481
BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */
482
} /* if */
483
484
return 1; /* We made it: hit the showers. */
485
} /* determineUserDir */
486
487
488
static BOOL mediaInDrive(const char *drive)
489
{
490
UINT oldErrorMode;
491
492
DWORD tmp;
BOOL retval;
493
494
/* Prevent windows warning message appearing when checking media size */
495
oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
496
497
/* If this function succeeds, there's media in the drive */
498
retval = GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0);
499
500
/* Revert back to old windows error handler */
501
502
SetErrorMode(oldErrorMode);
503
return retval;
504
} /* mediaInDrive */
505
506
507
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
508
{
509
510
/* !!! FIXME: Can CD drives be non-drive letter paths? */
/* !!! FIXME: (so can they be Unicode paths?) */
511
char drive_str[4] = "x:\\";
512
513
char ch;
for (ch = 'A'; ch <= 'Z'; ch++)
514
{
515
drive_str[0] = ch;
516
if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str))
517
cb(data, drive_str);
518
} /* for */
519
} /* __PHYSFS_platformDetectAvailableCDs */
520
521
522
523
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
524
if ((argv0 != NULL) && (strchr(argv0, '\\') != NULL))
525
return NULL; /* default behaviour can handle this. */
526
527
return getExePath();
528
529
530
531
532
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformGetUserName(void)
{
533
DWORD bufsize = 0;
534
535
536
char *retval = NULL;
if (pGetUserNameW(NULL, &bufsize) == 0) /* This SHOULD fail. */
537
{
538
539
540
541
542
543
544
LPWSTR wbuf = (LPWSTR) __PHYSFS_smallAlloc(bufsize * sizeof (WCHAR));
BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
if (pGetUserNameW(wbuf, &bufsize) == 0) /* ?! */
__PHYSFS_setError(winApiStrError());
else
retval = unicodeToUtf8Heap(wbuf);
__PHYSFS_smallFree(wbuf);
545
546
} /* if */
547
return retval;
548
549
550
551
552
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void)
{
553
char *retval = (char *) allocator.Malloc(strlen(userDir) + 1);
554
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
555
strcpy(retval, userDir); /* calculated at init time. */
556
return retval;
557
558
559
} /* __PHYSFS_platformGetUserDir */
560
void *__PHYSFS_platformGetThreadID(void)
561
{
562
return ( (void *) ((size_t) GetCurrentThreadId()) );
563
564
565
} /* __PHYSFS_platformGetThreadID */
566
static int doPlatformExists(LPWSTR wpath)
567
{
568
569
BAIL_IF_MACRO
(
570
571
pGetFileAttributesW(wpath) == PHYSFS_INVALID_FILE_ATTRIBUTES,
winApiStrError(), 0
572
);
573
return 1;
574
575
576
577
578
579
580
581
582
583
584
} /* doPlatformExists */
int __PHYSFS_platformExists(const char *fname)
{
int retval = 0;
LPWSTR wpath;
UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
retval = doPlatformExists(wpath);
__PHYSFS_smallFree(wpath);
585
return retval;
586
587
588
} /* __PHYSFS_platformExists */
589
static int isSymlinkAttrs(const DWORD attr, const DWORD tag)
590
{
591
592
return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) &&
(tag == PHYSFS_IO_REPARSE_TAG_SYMLINK) );
593
594
595
} /* isSymlinkAttrs */
596
597
int __PHYSFS_platformIsSymLink(const char *fname)
{
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
/* !!! FIXME:
* Windows Vista can have NTFS symlinks. Can older Windows releases have
* them when talking to a network file server? What happens when you
* mount a NTFS partition on XP that was plugged into a Vista install
* that made a symlink?
*/
int retval = 0;
LPWSTR wpath;
HANDLE dir;
WIN32_FIND_DATAW entw;
/* no unicode entry points? Probably no symlinks. */
BAIL_IF_MACRO(pFindFirstFileW == NULL, NULL, 0);
UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
/* !!! FIXME: filter wildcard chars? */
dir = pFindFirstFileW(wpath, &entw);
if (dir != INVALID_HANDLE_VALUE)
{
retval = isSymlinkAttrs(entw.dwFileAttributes, entw.dwReserved0);
FindClose(dir);
} /* if */
__PHYSFS_smallFree(wpath);
625
return retval;
626
627
628
629
630
} /* __PHYSFS_platformIsSymlink */
int __PHYSFS_platformIsDirectory(const char *fname)
{
631
632
633
634
635
636
int retval = 0;
LPWSTR wpath;
UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
retval = ((pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) != 0);
__PHYSFS_smallFree(wpath);
637
return retval;
638
639
640
641
642
643
644
645
646
647
} /* __PHYSFS_platformIsDirectory */
char *__PHYSFS_platformCvtToDependent(const char *prepend,
const char *dirName,
const char *append)
{
int len = ((prepend) ? strlen(prepend) : 0) +
((append) ? strlen(append) : 0) +
strlen(dirName) + 1;
648
char *retval = (char *) allocator.Malloc(len);
649
char *p;
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
if (prepend)
strcpy(retval, prepend);
else
retval[0] = '\0';
strcat(retval, dirName);
if (append)
strcat(retval, append);
for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
*p = '\\';
666
return retval;
667
668
669
} /* __PHYSFS_platformCvtToDependent */
670
671
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
672
673
PHYSFS_EnumFilesCallback callback,
const char *origdir,
674
void *callbackdata)
675
{
676
677
const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL);
HANDLE dir = INVALID_HANDLE_VALUE;
678
WIN32_FIND_DATA ent;
679
WIN32_FIND_DATAW entw;
680
size_t len = strlen(dirname);
681
682
683
char *searchPath = NULL;
WCHAR *wSearchPath = NULL;
char *utf8 = NULL;
684
685
/* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
686
687
searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
if (searchPath == NULL)
688
return;
689
690
/* Copy current dirname */
691
strcpy(searchPath, dirname);
692
693
/* if there's no '\\' at the end of the path, stick one in there. */
694
if (searchPath[len - 1] != '\\')
695
{
696
697
searchPath[len++] = '\\';
searchPath[len] = '\0';
698
699
} /* if */
700
/* Append the "*" to the end of the string */
701
strcat(searchPath, "*");
702
703
704
705
UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath);
if (wSearchPath == NULL)
return; /* oh well. */
706
707
708
709
if (unicode)
dir = pFindFirstFileW(wSearchPath, &entw);
else
710
{
711
712
713
714
715
716
717
718
719
const int len = (int) (wStrLen(wSearchPath) + 1);
char *cp = (char *) __PHYSFS_smallAlloc(len);
if (cp != NULL)
{
WideCharToMultiByte(CP_ACP, 0, wSearchPath, len, cp, len, 0, 0);
dir = FindFirstFileA(cp, &ent);
__PHYSFS_smallFree(cp);
} /* if */
} /* else */
720
721
722
723
724
__PHYSFS_smallFree(wSearchPath);
__PHYSFS_smallFree(searchPath);
if (dir == INVALID_HANDLE_VALUE)
return;
725
726
727
728
729
if (unicode)
{
do
{
730
const DWORD attr = entw.dwFileAttributes;
731
const DWORD tag = entw.dwReserved0;
732
733
734
735
736
const WCHAR *fn = entw.cFileName;
if ((fn[0] == '.') && (fn[1] == '\0'))
continue;
if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
continue;
737
738
if ((omitSymLinks) && (isSymlinkAttrs(attr, tag)))
continue;
739
740
utf8 = unicodeToUtf8Heap(fn);
741
742
743
744
745
746
747
if (utf8 != NULL)
{
callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
} /* if */
} while (pFindNextFileW(dir, &entw) != 0);
} /* if */
748
749
750
751
752
else /* ANSI fallback. */
{
do
{
753
const DWORD attr = ent.dwFileAttributes;
754
const DWORD tag = ent.dwReserved0;
755
756
757
758
759
const char *fn = ent.cFileName;
if ((fn[0] == '.') && (fn[1] == '\0'))
continue;
if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
continue;
760
761
if ((omitSymLinks) && (isSymlinkAttrs(attr, tag)))
continue;
762
763
utf8 = codepageToUtf8Heap(fn);
764
765
766
767
768
769
770
if (utf8 != NULL)
{
callback(callbackdata, origdir, utf8);
allocator.Free(utf8);
} /* if */
} while (FindNextFileA(dir, &ent) != 0);
} /* else */
771
772
773
774
775
776
777
FindClose(dir);
} /* __PHYSFS_platformEnumerateFiles */
char *__PHYSFS_platformCurrentDir(void)
{
778
779
char *retval = NULL;
WCHAR *wbuf = NULL;
780
781
DWORD buflen = 0;
782
783
784
785
buflen = pGetCurrentDirectoryW(buflen, NULL);
wbuf = (WCHAR *) __PHYSFS_smallAlloc((buflen + 2) * sizeof (WCHAR));
BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
pGetCurrentDirectoryW(buflen, wbuf);
786
787
788
789
790
791
792
793
if (wbuf[buflen - 2] == '\\')
wbuf[buflen-1] = '\0'; /* just in case... */
else
{
wbuf[buflen - 1] = '\\';
wbuf[buflen] = '\0';
} /* else */
794
795
796
retval = unicodeToUtf8Heap(wbuf);
__PHYSFS_smallFree(wbuf);
797
return retval;
798
799
800
} /* __PHYSFS_platformCurrentDir */
801
/* this could probably use a cleanup. */
802
803
char *__PHYSFS_platformRealPath(const char *path)
{
804
805
/* !!! FIXME: this should return NULL if (path) doesn't exist? */
/* !!! FIXME: Need to handle symlinks in Vista... */
806
/* !!! FIXME: try GetFullPathName() instead? */
807
/* this function should be UTF-8 clean. */
808
809
810
811
812
813
char *retval = NULL;
char *p = NULL;
BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(*path == '\0', ERR_INVALID_ARGUMENT, NULL);
814
retval = (char *) allocator.Malloc(MAX_PATH);
815
816
817
818
819
820
821
822
823
824
825
826
827
828
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
/*
* If in \\server\path format, it's already an absolute path.
* We'll need to check for "." and ".." dirs, though, just in case.
*/
if ((path[0] == '\\') && (path[1] == '\\'))
strcpy(retval, path);
else
{
char *currentDir = __PHYSFS_platformCurrentDir();
if (currentDir == NULL)
{
829
allocator.Free(retval);
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
if (path[1] == ':') /* drive letter specified? */
{
/*
* Apparently, "D:mypath" is the same as "D:\\mypath" if
* D: is not the current drive. However, if D: is the
* current drive, then "D:mypath" is a relative path. Ugh.
*/
if (path[2] == '\\') /* maybe an absolute path? */
strcpy(retval, path);
else /* definitely an absolute path. */
{
if (path[0] == currentDir[0]) /* current drive; relative. */
{
strcpy(retval, currentDir);
strcat(retval, path + 2);
} /* if */
else /* not current drive; absolute. */
{
retval[0] = path[0];
retval[1] = ':';
retval[2] = '\\';
strcpy(retval + 3, path + 2);
} /* else */
} /* else */
} /* if */
else /* no drive letter specified. */
{
if (path[0] == '\\') /* absolute path. */
{
retval[0] = currentDir[0];
retval[1] = ':';
strcpy(retval + 2, path);
} /* if */
else
{
strcpy(retval, currentDir);
strcat(retval, path);
} /* else */
} /* else */
875
allocator.Free(currentDir);
876
877
878
879
880
881
882
} /* else */
/* (whew.) Ok, now take out "." and ".." path entries... */
p = retval;
while ( (p = strstr(p, "\\.")) != NULL)
{
883
/* it's a "." entry that doesn't end the string. */
884
885
886
if (p[2] == '\\')
memmove(p + 1, p + 3, strlen(p + 3) + 1);
887
/* it's a "." entry that ends the string. */
888
889
890
else if (p[2] == '\0')
p[0] = '\0';
891
/* it's a ".." entry. */
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
else if (p[2] == '.')
{
char *prevEntry = p - 1;
while ((prevEntry != retval) && (*prevEntry != '\\'))
prevEntry--;
if (prevEntry == retval) /* make it look like a "." entry. */
memmove(p + 1, p + 2, strlen(p + 2) + 1);
else
{
if (p[3] != '\0') /* doesn't end string. */
*prevEntry = '\0';
else /* ends string. */
memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1);
p = prevEntry;
} /* else */
} /* else if */
else
{
p++; /* look past current char. */
} /* else */
} /* while */
917
/* shrink the retval's memory block if possible... */
918
p = (char *) allocator.Realloc(retval, strlen(retval) + 1);
919
920
921
if (p != NULL)
retval = p;
922
return retval;
923
924
925
926
927
} /* __PHYSFS_platformRealPath */
int __PHYSFS_platformMkDir(const char *path)
{
928
929
930
931
932
933
WCHAR *wpath;
DWORD rc;
UTF8_TO_UNICODE_STACK_MACRO(wpath, path);
rc = pCreateDirectoryW(wpath, NULL);
__PHYSFS_smallFree(wpath);
BAIL_IF_MACRO(rc == 0, winApiStrError(), 0);
934
return 1;
935
936
} /* __PHYSFS_platformMkDir */
937
938
939
940
941
942
/*
* Get OS info and save the important parts.
*
* Returns non-zero if successful, otherwise it returns zero on failure.
*/
943
944
945
946
947
948
static int getOSInfo(void)
{
OSVERSIONINFO osVerInfo; /* Information about the OS */
osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
BAIL_IF_MACRO(!GetVersionEx(&osVerInfo), winApiStrError(), 0);
osHasUnicode = (osVerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
949
return 1;
950
} /* getOSInfo */
951
952
953
954
int __PHYSFS_platformInit(void)
{
955
BAIL_IF_MACRO(!getOSInfo(), NULL, 0);
956
BAIL_IF_MACRO(!findApiSymbols(), NULL, 0);
957
BAIL_IF_MACRO(!determineUserDir(), NULL, 0);
958
return 1; /* It's all good */
959
} /* __PHYSFS_platformInit */
960
961
962
963
int __PHYSFS_platformDeinit(void)
{
964
965
966
967
968
HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL };
int i;
allocator.Free(userDir);
userDir = NULL;
969
970
for (i = 0; libs[i] != NULL; i++)
971
{
972
973
974
975
976
const HANDLE lib = *(libs[i]);
if (lib)
FreeLibrary(lib);
*(libs[i]) = NULL;
} /* for */
977
978
return 1; /* It's all good */
979
980
981
982
983
984
} /* __PHYSFS_platformDeinit */
static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
{
HANDLE fileHandle;
985
986
WinApiFile *retval;
WCHAR *wfname;
987
988
989
UTF8_TO_UNICODE_STACK_MACRO(wfname, fname);
BAIL_IF_MACRO(wfname == NULL, ERR_OUT_OF_MEMORY, NULL);
990
991
fileHandle = pCreateFileW(wfname, mode, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
992
__PHYSFS_smallFree(wfname);
993
994
995
BAIL_IF_MACRO
(
996
fileHandle == INVALID_HANDLE_VALUE,
997
winApiStrError(), NULL
998
);
999
1000
retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile));