physfs_platform_windows.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  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. #ifdef PHYSFS_PLATFORM_WINRT
  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. #ifdef PHYSFS_PLATFORM_WINRT
  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. #ifdef PHYSFS_PLATFORM_WINRT
  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. #ifdef PHYSFS_PLATFORM_WINRT
  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. #ifdef PHYSFS_PLATFORM_WINRT
  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.
  494. */
  495. rc = pGetDir(accessToken, NULL, &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. PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
  534. PHYSFS_EnumerateCallback callback,
  535. const char *origdir, void *callbackdata)
  536. {
  537. PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
  538. HANDLE dir = INVALID_HANDLE_VALUE;
  539. WIN32_FIND_DATAW entw;
  540. size_t len = strlen(dirname);
  541. char *searchPath = NULL;
  542. WCHAR *wSearchPath = NULL;
  543. /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
  544. searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
  545. BAIL_IF(!searchPath, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
  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, PHYSFS_ENUM_ERROR);
  559. dir = winFindFirstFileW(wSearchPath, &entw);
  560. __PHYSFS_smallFree(wSearchPath);
  561. BAIL_IF(dir==INVALID_HANDLE_VALUE, errcodeFromWinApi(), PHYSFS_ENUM_ERROR);
  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 == PHYSFS_ENUM_ERROR)
  579. PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
  580. } /* else */
  581. } while ((retval == PHYSFS_ENUM_OK) && (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, const int follow)
  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 = !follow && 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 ... */