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