physfs_platform_windows.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  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. /* !!! FIXME: ?? case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP; */
  175. case ERROR_BUFFER_OVERFLOW: return PHYSFS_ERR_BAD_FILENAME;
  176. case ERROR_INVALID_NAME: return PHYSFS_ERR_BAD_FILENAME;
  177. case ERROR_BAD_PATHNAME: return PHYSFS_ERR_BAD_FILENAME;
  178. case ERROR_DIRECTORY: return PHYSFS_ERR_BAD_FILENAME;
  179. case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
  180. case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
  181. case ERROR_DELETE_PENDING: return PHYSFS_ERR_NOT_FOUND;
  182. case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NOT_FOUND;
  183. case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
  184. case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
  185. /* !!! FIXME: ?? case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; */
  186. /* !!! FIXME: ?? case EISDIR: return PHYSFS_ERR_NOT_A_FILE; */
  187. case ERROR_WRITE_PROTECT: return PHYSFS_ERR_READ_ONLY;
  188. case ERROR_LOCK_VIOLATION: return PHYSFS_ERR_BUSY;
  189. case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_BUSY;
  190. case ERROR_CURRENT_DIRECTORY: return PHYSFS_ERR_BUSY;
  191. case ERROR_DRIVE_LOCKED: return PHYSFS_ERR_BUSY;
  192. case ERROR_PATH_BUSY: return PHYSFS_ERR_BUSY;
  193. case ERROR_BUSY: return PHYSFS_ERR_BUSY;
  194. case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
  195. case ERROR_OUTOFMEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
  196. case ERROR_DIR_NOT_EMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
  197. default: return PHYSFS_ERR_OS_ERROR;
  198. } /* switch */
  199. } /* errcodeFromWinApiError */
  200. static inline PHYSFS_ErrorCode errcodeFromWinApi(void)
  201. {
  202. return errcodeFromWinApiError(GetLastError());
  203. } /* errcodeFromWinApi */
  204. #if defined(PHYSFS_NO_CDROM_SUPPORT)
  205. #define detectAvailableCDs(cb, data)
  206. #define deinitCDThread()
  207. #else
  208. static HANDLE detectCDThreadHandle = NULL;
  209. static HWND detectCDHwnd = NULL;
  210. static volatile DWORD drivesWithMediaBitmap = 0;
  211. typedef BOOL (WINAPI *fnSTEM)(DWORD, LPDWORD b);
  212. static DWORD pollDiscDrives(void)
  213. {
  214. /* Try to use SetThreadErrorMode(), which showed up in Windows 7. */
  215. HANDLE lib = LoadLibraryA("kernel32.dll");
  216. fnSTEM stem = NULL;
  217. char drive[4] = { 'x', ':', '\\', '\0' };
  218. DWORD oldErrorMode = 0;
  219. DWORD drives = 0;
  220. DWORD i;
  221. if (lib)
  222. stem = (fnSTEM) GetProcAddress(lib, "SetThreadErrorMode");
  223. if (stem)
  224. stem(SEM_FAILCRITICALERRORS, &oldErrorMode);
  225. else
  226. oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  227. /* Do detection. This may block if a disc is spinning up. */
  228. for (i = 'A'; i <= 'Z'; i++)
  229. {
  230. DWORD tmp = 0;
  231. drive[0] = (char) i;
  232. if (GetDriveTypeA(drive) != DRIVE_CDROM)
  233. continue;
  234. /* If this function succeeds, there's media in the drive */
  235. if (GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0))
  236. drives |= (1 << (i - 'A'));
  237. } /* for */
  238. if (stem)
  239. stem(oldErrorMode, NULL);
  240. else
  241. SetErrorMode(oldErrorMode);
  242. if (lib)
  243. FreeLibrary(lib);
  244. return drives;
  245. } /* pollDiscDrives */
  246. static LRESULT CALLBACK detectCDWndProc(HWND hwnd, UINT msg,
  247. WPARAM wp, LPARAM lparam)
  248. {
  249. PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR) lparam;
  250. PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME) lparam;
  251. const int removed = (wp == DBT_DEVICEREMOVECOMPLETE);
  252. if (msg == WM_DESTROY)
  253. return 0;
  254. else if ((msg != WM_DEVICECHANGE) ||
  255. ((wp != DBT_DEVICEARRIVAL) && (wp != DBT_DEVICEREMOVECOMPLETE)) ||
  256. (lpdb->dbch_devicetype != DBT_DEVTYP_VOLUME) ||
  257. ((lpdbv->dbcv_flags & DBTF_MEDIA) == 0))
  258. {
  259. return DefWindowProcW(hwnd, msg, wp, lparam);
  260. } /* else if */
  261. if (removed)
  262. drivesWithMediaBitmap &= ~lpdbv->dbcv_unitmask;
  263. else
  264. drivesWithMediaBitmap |= lpdbv->dbcv_unitmask;
  265. return TRUE;
  266. } /* detectCDWndProc */
  267. static DWORD WINAPI detectCDThread(LPVOID arg)
  268. {
  269. HANDLE initialDiscDetectionComplete = *((HANDLE *) arg);
  270. const char *classname = "PhysicsFSDetectCDCatcher";
  271. const char *winname = "PhysicsFSDetectCDMsgWindow";
  272. HINSTANCE hInstance = GetModuleHandleW(NULL);
  273. ATOM class_atom = 0;
  274. WNDCLASSEXA wce;
  275. MSG msg;
  276. memset(&wce, '\0', sizeof (wce));
  277. wce.cbSize = sizeof (wce);
  278. wce.lpfnWndProc = detectCDWndProc;
  279. wce.lpszClassName = classname;
  280. wce.hInstance = hInstance;
  281. class_atom = RegisterClassExA(&wce);
  282. if (class_atom == 0)
  283. {
  284. SetEvent(initialDiscDetectionComplete); /* let main thread go on. */
  285. return 0;
  286. } /* if */
  287. detectCDHwnd = CreateWindowExA(0, classname, winname, WS_OVERLAPPEDWINDOW,
  288. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  289. CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
  290. if (detectCDHwnd == NULL)
  291. {
  292. SetEvent(initialDiscDetectionComplete); /* let main thread go on. */
  293. UnregisterClassA(classname, hInstance);
  294. return 0;
  295. } /* if */
  296. /* We'll get events when discs come and go from now on. */
  297. /* Do initial detection, possibly blocking awhile... */
  298. drivesWithMediaBitmap = pollDiscDrives();
  299. SetEvent(initialDiscDetectionComplete); /* let main thread go on. */
  300. do
  301. {
  302. const BOOL rc = GetMessageW(&msg, detectCDHwnd, 0, 0);
  303. if ((rc == 0) || (rc == -1))
  304. break; /* don't care if WM_QUIT or error break this loop. */
  305. TranslateMessage(&msg);
  306. DispatchMessageW(&msg);
  307. } while (1);
  308. /* we've been asked to quit. */
  309. DestroyWindow(detectCDHwnd);
  310. UnregisterClassA(classname, hInstance);
  311. return 0;
  312. } /* detectCDThread */
  313. static void detectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  314. {
  315. char drive_str[4] = { 'x', ':', '\\', '\0' };
  316. DWORD drives = 0;
  317. DWORD i;
  318. /*
  319. * If you poll a drive while a user is inserting a disc, the OS will
  320. * block this thread until the drive has spun up. So we swallow the risk
  321. * once for initial detection, and spin a thread that will get device
  322. * events thereafter, for apps that use this interface to poll for
  323. * disc insertion.
  324. */
  325. if (!detectCDThreadHandle)
  326. {
  327. HANDLE initialDetectDone = CreateEvent(NULL, TRUE, FALSE, NULL);
  328. if (!initialDetectDone)
  329. return; /* oh well. */
  330. detectCDThreadHandle = CreateThread(NULL, 0, detectCDThread,
  331. &initialDetectDone, 0, NULL);
  332. if (detectCDThreadHandle)
  333. WaitForSingleObject(initialDetectDone, INFINITE);
  334. CloseHandle(initialDetectDone);
  335. if (!detectCDThreadHandle)
  336. return; /* oh well. */
  337. } /* if */
  338. drives = drivesWithMediaBitmap; /* whatever the thread has seen, we take. */
  339. for (i = 'A'; i <= 'Z'; i++)
  340. {
  341. if (drives & (1 << (i - 'A')))
  342. {
  343. drive_str[0] = (char) i;
  344. cb(data, drive_str);
  345. } /* if */
  346. } /* for */
  347. } /* detectAvailableCDs */
  348. static void deinitCDThread(void)
  349. {
  350. if (detectCDThreadHandle)
  351. {
  352. if (detectCDHwnd)
  353. PostMessageW(detectCDHwnd, WM_QUIT, 0, 0);
  354. CloseHandle(detectCDThreadHandle);
  355. detectCDThreadHandle = NULL;
  356. drivesWithMediaBitmap = 0;
  357. } /* if */
  358. } /* deinitCDThread */
  359. #endif
  360. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  361. {
  362. detectAvailableCDs(cb, data);
  363. } /* __PHYSFS_platformDetectAvailableCDs */
  364. #ifdef PHYSFS_PLATFORM_WINRT
  365. static char *calcDirAppendSep(const WCHAR *wdir)
  366. {
  367. size_t len;
  368. void *ptr;
  369. char *retval;
  370. BAIL_IF(!wdir, errcodeFromWinApi(), NULL);
  371. retval = unicodeToUtf8Heap(wdir);
  372. BAIL_IF_ERRPASS(!retval, NULL);
  373. len = strlen(retval);
  374. ptr = allocator.Realloc(retval, len + 2);
  375. if (!ptr)
  376. {
  377. allocator.Free(retval);
  378. BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  379. } /* if */
  380. retval = (char *) ptr;
  381. retval[len] = '\\';
  382. retval[len+1] = '\0';
  383. return retval;
  384. } /* calcDirAppendSep */
  385. #endif
  386. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  387. {
  388. #ifdef PHYSFS_PLATFORM_WINRT
  389. return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcBaseDir());
  390. #else
  391. char *retval = NULL;
  392. DWORD buflen = 64;
  393. LPWSTR modpath = NULL;
  394. while (1)
  395. {
  396. DWORD rc;
  397. void *ptr;
  398. if ( (ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) == NULL )
  399. {
  400. allocator.Free(modpath);
  401. BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  402. } /* if */
  403. modpath = (LPWSTR) ptr;
  404. rc = GetModuleFileNameW(NULL, modpath, buflen);
  405. if (rc == 0)
  406. {
  407. allocator.Free(modpath);
  408. BAIL(errcodeFromWinApi(), NULL);
  409. } /* if */
  410. if (rc < buflen)
  411. {
  412. buflen = rc;
  413. break;
  414. } /* if */
  415. buflen *= 2;
  416. } /* while */
  417. if (buflen > 0) /* just in case... */
  418. {
  419. WCHAR *ptr = (modpath + buflen) - 1;
  420. while (ptr != modpath)
  421. {
  422. if (*ptr == '\\')
  423. break;
  424. ptr--;
  425. } /* while */
  426. if ((ptr == modpath) && (*ptr != '\\'))
  427. PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR); /* oh well. */
  428. else
  429. {
  430. *(ptr+1) = '\0'; /* chop off filename. */
  431. retval = unicodeToUtf8Heap(modpath);
  432. } /* else */
  433. } /* else */
  434. allocator.Free(modpath);
  435. return retval; /* w00t. */
  436. #endif
  437. } /* __PHYSFS_platformCalcBaseDir */
  438. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  439. {
  440. #ifdef PHYSFS_PLATFORM_WINRT
  441. return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcPrefDir());
  442. #else
  443. /*
  444. * Vista and later has a new API for this, but SHGetFolderPath works there,
  445. * and apparently just wraps the new API. This is the new way to do it:
  446. *
  447. * SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE,
  448. * NULL, &wszPath);
  449. */
  450. WCHAR path[MAX_PATH];
  451. char *utf8 = NULL;
  452. size_t len = 0;
  453. char *retval = NULL;
  454. if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
  455. NULL, 0, path)))
  456. BAIL(PHYSFS_ERR_OS_ERROR, NULL);
  457. utf8 = unicodeToUtf8Heap(path);
  458. BAIL_IF_ERRPASS(!utf8, NULL);
  459. len = strlen(utf8) + strlen(org) + strlen(app) + 4;
  460. retval = allocator.Malloc(len);
  461. if (!retval)
  462. {
  463. allocator.Free(utf8);
  464. BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  465. } /* if */
  466. snprintf(retval, len, "%s\\%s\\%s\\", utf8, org, app);
  467. allocator.Free(utf8);
  468. return retval;
  469. #endif
  470. } /* __PHYSFS_platformCalcPrefDir */
  471. char *__PHYSFS_platformCalcUserDir(void)
  472. {
  473. #ifdef PHYSFS_PLATFORM_WINRT
  474. return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcPrefDir());
  475. #else
  476. typedef BOOL (WINAPI *fnGetUserProfDirW)(HANDLE, LPWSTR, LPDWORD);
  477. fnGetUserProfDirW pGetDir = NULL;
  478. HANDLE lib = NULL;
  479. HANDLE accessToken = NULL; /* Security handle to process */
  480. char *retval = NULL;
  481. lib = LoadLibraryA("userenv.dll");
  482. BAIL_IF(!lib, errcodeFromWinApi(), NULL);
  483. pGetDir=(fnGetUserProfDirW) GetProcAddress(lib,"GetUserProfileDirectoryW");
  484. GOTO_IF(!pGetDir, errcodeFromWinApi(), done);
  485. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &accessToken))
  486. GOTO(errcodeFromWinApi(), done);
  487. else
  488. {
  489. DWORD psize = 0;
  490. WCHAR dummy = 0;
  491. LPWSTR wstr = NULL;
  492. BOOL rc = 0;
  493. /*
  494. * Should fail. Will write the size of the profile path in
  495. * psize. Also note that the second parameter can't be
  496. * NULL or the function fails.
  497. */
  498. rc = pGetDir(accessToken, &dummy, &psize);
  499. GOTO_IF(rc, PHYSFS_ERR_OS_ERROR, done); /* should have failed! */
  500. /* Allocate memory for the profile directory */
  501. wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR));
  502. if (wstr != NULL)
  503. {
  504. if (pGetDir(accessToken, wstr, &psize))
  505. {
  506. /* Make sure it ends in a dirsep. We allocated +1 for this. */
  507. if (wstr[psize - 2] != '\\')
  508. {
  509. wstr[psize - 1] = '\\';
  510. wstr[psize - 0] = '\0';
  511. } /* if */
  512. retval = unicodeToUtf8Heap(wstr);
  513. } /* if */
  514. __PHYSFS_smallFree(wstr);
  515. } /* if */
  516. } /* if */
  517. done:
  518. if (accessToken)
  519. CloseHandle(accessToken);
  520. FreeLibrary(lib);
  521. return retval; /* We made it: hit the showers. */
  522. #endif
  523. } /* __PHYSFS_platformCalcUserDir */
  524. int __PHYSFS_platformInit(void)
  525. {
  526. return 1; /* It's all good */
  527. } /* __PHYSFS_platformInit */
  528. void __PHYSFS_platformDeinit(void)
  529. {
  530. deinitCDThread();
  531. } /* __PHYSFS_platformDeinit */
  532. void *__PHYSFS_platformGetThreadID(void)
  533. {
  534. return ( (void *) ((size_t) GetCurrentThreadId()) );
  535. } /* __PHYSFS_platformGetThreadID */
  536. void __PHYSFS_platformEnumerateFiles(const char *dirname,
  537. PHYSFS_EnumFilesCallback callback,
  538. const char *origdir,
  539. void *callbackdata)
  540. {
  541. HANDLE dir = INVALID_HANDLE_VALUE;
  542. WIN32_FIND_DATAW entw;
  543. size_t len = strlen(dirname);
  544. char *searchPath = NULL;
  545. WCHAR *wSearchPath = NULL;
  546. /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
  547. searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
  548. if (searchPath == NULL)
  549. return;
  550. /* Copy current dirname */
  551. strcpy(searchPath, dirname);
  552. /* if there's no '\\' at the end of the path, stick one in there. */
  553. if (searchPath[len - 1] != '\\')
  554. {
  555. searchPath[len++] = '\\';
  556. searchPath[len] = '\0';
  557. } /* if */
  558. /* Append the "*" to the end of the string */
  559. strcat(searchPath, "*");
  560. UTF8_TO_UNICODE_STACK(wSearchPath, searchPath);
  561. if (!wSearchPath)
  562. return; /* oh well. */
  563. dir = winFindFirstFileW(wSearchPath, &entw);
  564. __PHYSFS_smallFree(wSearchPath);
  565. __PHYSFS_smallFree(searchPath);
  566. if (dir == INVALID_HANDLE_VALUE)
  567. return;
  568. do
  569. {
  570. const WCHAR *fn = entw.cFileName;
  571. char *utf8;
  572. if ((fn[0] == '.') && (fn[1] == '\0'))
  573. continue;
  574. if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
  575. continue;
  576. utf8 = unicodeToUtf8Heap(fn);
  577. if (utf8 != NULL)
  578. {
  579. callback(callbackdata, origdir, utf8);
  580. allocator.Free(utf8);
  581. } /* if */
  582. } while (FindNextFileW(dir, &entw) != 0);
  583. FindClose(dir);
  584. } /* __PHYSFS_platformEnumerateFiles */
  585. int __PHYSFS_platformMkDir(const char *path)
  586. {
  587. WCHAR *wpath;
  588. DWORD rc;
  589. UTF8_TO_UNICODE_STACK(wpath, path);
  590. rc = CreateDirectoryW(wpath, NULL);
  591. __PHYSFS_smallFree(wpath);
  592. BAIL_IF(rc == 0, errcodeFromWinApi(), 0);
  593. return 1;
  594. } /* __PHYSFS_platformMkDir */
  595. static HANDLE doOpen(const char *fname, DWORD mode, DWORD creation)
  596. {
  597. HANDLE fileh;
  598. WCHAR *wfname;
  599. UTF8_TO_UNICODE_STACK(wfname, fname);
  600. BAIL_IF(!wfname, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  601. fileh = winCreateFileW(wfname, mode, creation);
  602. __PHYSFS_smallFree(wfname);
  603. if (fileh == INVALID_HANDLE_VALUE)
  604. BAIL(errcodeFromWinApi(), INVALID_HANDLE_VALUE);
  605. return fileh;
  606. } /* doOpen */
  607. void *__PHYSFS_platformOpenRead(const char *filename)
  608. {
  609. HANDLE h = doOpen(filename, GENERIC_READ, OPEN_EXISTING);
  610. return (h == INVALID_HANDLE_VALUE) ? NULL : (void *) h;
  611. } /* __PHYSFS_platformOpenRead */
  612. void *__PHYSFS_platformOpenWrite(const char *filename)
  613. {
  614. HANDLE h = doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS);
  615. return (h == INVALID_HANDLE_VALUE) ? NULL : (void *) h;
  616. } /* __PHYSFS_platformOpenWrite */
  617. void *__PHYSFS_platformOpenAppend(const char *filename)
  618. {
  619. HANDLE h = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS);
  620. BAIL_IF_ERRPASS(h == INVALID_HANDLE_VALUE, NULL);
  621. if (!winSetFilePointer(h, 0, NULL, FILE_END))
  622. {
  623. const PHYSFS_ErrorCode err = errcodeFromWinApi();
  624. CloseHandle(h);
  625. BAIL(err, NULL);
  626. } /* if */
  627. return (void *) h;
  628. } /* __PHYSFS_platformOpenAppend */
  629. PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
  630. {
  631. HANDLE h = (HANDLE) opaque;
  632. PHYSFS_sint64 totalRead = 0;
  633. if (!__PHYSFS_ui64FitsAddressSpace(len))
  634. BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
  635. while (len > 0)
  636. {
  637. const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
  638. DWORD numRead = 0;
  639. if (!ReadFile(h, buf, thislen, &numRead, NULL))
  640. BAIL(errcodeFromWinApi(), -1);
  641. len -= (PHYSFS_uint64) numRead;
  642. totalRead += (PHYSFS_sint64) numRead;
  643. if (numRead != thislen)
  644. break;
  645. } /* while */
  646. return totalRead;
  647. } /* __PHYSFS_platformRead */
  648. PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
  649. PHYSFS_uint64 len)
  650. {
  651. HANDLE h = (HANDLE) opaque;
  652. PHYSFS_sint64 totalWritten = 0;
  653. if (!__PHYSFS_ui64FitsAddressSpace(len))
  654. BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
  655. while (len > 0)
  656. {
  657. const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len;
  658. DWORD numWritten = 0;
  659. if (!WriteFile(h, buffer, thislen, &numWritten, NULL))
  660. BAIL(errcodeFromWinApi(), -1);
  661. len -= (PHYSFS_uint64) numWritten;
  662. totalWritten += (PHYSFS_sint64) numWritten;
  663. if (numWritten != thislen)
  664. break;
  665. } /* while */
  666. return totalWritten;
  667. } /* __PHYSFS_platformWrite */
  668. int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
  669. {
  670. HANDLE h = (HANDLE) opaque;
  671. const PHYSFS_sint64 spos = (PHYSFS_sint64) pos;
  672. BAIL_IF(!winSetFilePointer(h,spos,NULL,FILE_BEGIN), errcodeFromWinApi(), 0);
  673. return 1; /* No error occured */
  674. } /* __PHYSFS_platformSeek */
  675. PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
  676. {
  677. HANDLE h = (HANDLE) opaque;
  678. PHYSFS_sint64 pos = 0;
  679. BAIL_IF(!winSetFilePointer(h,0,&pos,FILE_CURRENT), errcodeFromWinApi(), -1);
  680. return pos;
  681. } /* __PHYSFS_platformTell */
  682. PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
  683. {
  684. HANDLE h = (HANDLE) opaque;
  685. const PHYSFS_sint64 retval = winGetFileSize(h);
  686. BAIL_IF(retval < 0, errcodeFromWinApi(), -1);
  687. return retval;
  688. } /* __PHYSFS_platformFileLength */
  689. int __PHYSFS_platformFlush(void *opaque)
  690. {
  691. HANDLE h = (HANDLE) opaque;
  692. BAIL_IF(!FlushFileBuffers(h), errcodeFromWinApi(), 0);
  693. return 1;
  694. } /* __PHYSFS_platformFlush */
  695. void __PHYSFS_platformClose(void *opaque)
  696. {
  697. HANDLE h = (HANDLE) opaque;
  698. (void) CloseHandle(h); /* ignore errors. You should have flushed! */
  699. } /* __PHYSFS_platformClose */
  700. static int doPlatformDelete(LPWSTR wpath)
  701. {
  702. WIN32_FILE_ATTRIBUTE_DATA info;
  703. if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &info))
  704. BAIL(errcodeFromWinApi(), 0);
  705. else
  706. {
  707. const int isdir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  708. const BOOL rc = isdir ? RemoveDirectoryW(wpath) : DeleteFileW(wpath);
  709. BAIL_IF(!rc, errcodeFromWinApi(), 0);
  710. } /* else */
  711. return 1; /* if you made it here, it worked. */
  712. } /* doPlatformDelete */
  713. int __PHYSFS_platformDelete(const char *path)
  714. {
  715. int retval = 0;
  716. LPWSTR wpath = NULL;
  717. UTF8_TO_UNICODE_STACK(wpath, path);
  718. BAIL_IF(!wpath, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  719. retval = doPlatformDelete(wpath);
  720. __PHYSFS_smallFree(wpath);
  721. return retval;
  722. } /* __PHYSFS_platformDelete */
  723. void *__PHYSFS_platformCreateMutex(void)
  724. {
  725. LPCRITICAL_SECTION lpcs;
  726. lpcs = (LPCRITICAL_SECTION) allocator.Malloc(sizeof (CRITICAL_SECTION));
  727. BAIL_IF(!lpcs, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  728. if (!winInitializeCriticalSection(lpcs))
  729. {
  730. allocator.Free(lpcs);
  731. BAIL(errcodeFromWinApi(), NULL);
  732. } /* if */
  733. return lpcs;
  734. } /* __PHYSFS_platformCreateMutex */
  735. void __PHYSFS_platformDestroyMutex(void *mutex)
  736. {
  737. DeleteCriticalSection((LPCRITICAL_SECTION) mutex);
  738. allocator.Free(mutex);
  739. } /* __PHYSFS_platformDestroyMutex */
  740. int __PHYSFS_platformGrabMutex(void *mutex)
  741. {
  742. EnterCriticalSection((LPCRITICAL_SECTION) mutex);
  743. return 1;
  744. } /* __PHYSFS_platformGrabMutex */
  745. void __PHYSFS_platformReleaseMutex(void *mutex)
  746. {
  747. LeaveCriticalSection((LPCRITICAL_SECTION) mutex);
  748. } /* __PHYSFS_platformReleaseMutex */
  749. static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft)
  750. {
  751. SYSTEMTIME st_utc;
  752. SYSTEMTIME st_localtz;
  753. TIME_ZONE_INFORMATION tzi;
  754. DWORD tzid;
  755. PHYSFS_sint64 retval;
  756. struct tm tm;
  757. BOOL rc;
  758. BAIL_IF(!FileTimeToSystemTime(ft, &st_utc), errcodeFromWinApi(), -1);
  759. tzid = GetTimeZoneInformation(&tzi);
  760. BAIL_IF(tzid == TIME_ZONE_ID_INVALID, errcodeFromWinApi(), -1);
  761. rc = SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz);
  762. BAIL_IF(!rc, errcodeFromWinApi(), -1);
  763. /* Convert to a format that mktime() can grok... */
  764. tm.tm_sec = st_localtz.wSecond;
  765. tm.tm_min = st_localtz.wMinute;
  766. tm.tm_hour = st_localtz.wHour;
  767. tm.tm_mday = st_localtz.wDay;
  768. tm.tm_mon = st_localtz.wMonth - 1;
  769. tm.tm_year = st_localtz.wYear - 1900;
  770. tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
  771. tm.tm_yday = -1;
  772. tm.tm_isdst = -1;
  773. /* Convert to a format PhysicsFS can grok... */
  774. retval = (PHYSFS_sint64) mktime(&tm);
  775. BAIL_IF(retval == -1, PHYSFS_ERR_OS_ERROR, -1);
  776. return retval;
  777. } /* FileTimeToPhysfsTime */
  778. /* check for symlinks. These exist in NTFS 3.1 (WinXP), even though
  779. they aren't really available to userspace before Vista. I wonder
  780. what would happen if you put an NTFS disk with a symlink on it
  781. into an XP machine, though; would this flag get set?
  782. NTFS symlinks are a form of "reparse point" (junction, volume mount,
  783. etc), so if the REPARSE_POINT attribute is set, check for the symlink
  784. tag thereafter. This assumes you already read in the file attributes. */
  785. static int isSymlink(const WCHAR *wpath, const DWORD attr)
  786. {
  787. WIN32_FIND_DATAW w32dw;
  788. HANDLE h;
  789. if ((attr & PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT) == 0)
  790. return 0; /* not a reparse point? Definitely not a symlink. */
  791. h = winFindFirstFileW(wpath, &w32dw);
  792. if (h == INVALID_HANDLE_VALUE)
  793. return 0; /* ...maybe the file just vanished...? */
  794. FindClose(h);
  795. return (w32dw.dwReserved0 == PHYSFS_IO_REPARSE_TAG_SYMLINK);
  796. } /* isSymlink */
  797. int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st)
  798. {
  799. WIN32_FILE_ATTRIBUTE_DATA winstat;
  800. WCHAR *wstr = NULL;
  801. DWORD err = 0;
  802. BOOL rc = 0;
  803. int issymlink = 0;
  804. UTF8_TO_UNICODE_STACK(wstr, filename);
  805. BAIL_IF(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0);
  806. rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat);
  807. if (!rc)
  808. err = GetLastError();
  809. else /* check for symlink while wstr is still available */
  810. issymlink = isSymlink(wstr, winstat.dwFileAttributes);
  811. __PHYSFS_smallFree(wstr);
  812. BAIL_IF(!rc, errcodeFromWinApiError(err), 0);
  813. st->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime);
  814. st->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime);
  815. st->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime);
  816. if (issymlink)
  817. {
  818. st->filetype = PHYSFS_FILETYPE_SYMLINK;
  819. st->filesize = 0;
  820. } /* if */
  821. else if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  822. {
  823. st->filetype = PHYSFS_FILETYPE_DIRECTORY;
  824. st->filesize = 0;
  825. } /* else if */
  826. else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE))
  827. {
  828. st->filetype = PHYSFS_FILETYPE_OTHER;
  829. st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow;
  830. } /* else if */
  831. else
  832. {
  833. st->filetype = PHYSFS_FILETYPE_REGULAR;
  834. st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow;
  835. } /* else */
  836. st->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0);
  837. return 1;
  838. } /* __PHYSFS_platformStat */
  839. #endif /* PHYSFS_PLATFORM_WINDOWS */
  840. /* end of physfs_platform_windows.c ... */