physfs_platform_windows.c 31 KB

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