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