physfs_platform_windows.c 30 KB

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