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 size_t 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. int __PHYSFS_platformEnumerate(const char *dirname,
  534. PHYSFS_EnumerateCallback callback,
  535. const char *origdir, void *callbackdata)
  536. {
  537. HANDLE dir = INVALID_HANDLE_VALUE;
  538. WIN32_FIND_DATAW entw;
  539. size_t len = strlen(dirname);
  540. char *searchPath = NULL;
  541. WCHAR *wSearchPath = NULL;
  542. int retval = 1;
  543. /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
  544. searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
  545. BAIL_IF(!searchPath, PHYSFS_ERR_OUT_OF_MEMORY, -1);
  546. /* Copy current dirname */
  547. strcpy(searchPath, dirname);
  548. /* if there's no '\\' at the end of the path, stick one in there. */
  549. if (searchPath[len - 1] != '\\')
  550. {
  551. searchPath[len++] = '\\';
  552. searchPath[len] = '\0';
  553. } /* if */
  554. /* Append the "*" to the end of the string */
  555. strcat(searchPath, "*");
  556. UTF8_TO_UNICODE_STACK(wSearchPath, searchPath);
  557. __PHYSFS_smallFree(searchPath);
  558. BAIL_IF_ERRPASS(!wSearchPath, -1);
  559. dir = winFindFirstFileW(wSearchPath, &entw);
  560. __PHYSFS_smallFree(wSearchPath);
  561. BAIL_IF(dir == INVALID_HANDLE_VALUE, errcodeFromWinApi(), -1);
  562. do
  563. {
  564. const WCHAR *fn = entw.cFileName;
  565. char *utf8;
  566. if (fn[0] == '.') /* ignore "." and ".." */
  567. {
  568. if ((fn[1] == '\0') || ((fn[1] == '.') && (fn[2] == '\0')))
  569. continue;
  570. } /* if */
  571. utf8 = unicodeToUtf8Heap(fn);
  572. if (utf8 == NULL)
  573. retval = -1;
  574. else
  575. {
  576. retval = callback(callbackdata, origdir, utf8);
  577. allocator.Free(utf8);
  578. if (retval == -1)
  579. PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
  580. } /* else */
  581. } while ((retval == 1) && (FindNextFileW(dir, &entw) != 0));
  582. FindClose(dir);
  583. return retval;
  584. } /* __PHYSFS_platformEnumerate */
  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 ... */