platform/win32.c
changeset 312 4996c48861db
parent 310 f8bca4a93fd5
child 313 fccebd616595
--- a/platform/win32.c	Sat Jun 29 19:16:00 2002 +0000
+++ b/platform/win32.c	Sat Jun 29 22:05:12 2002 +0000
@@ -13,6 +13,8 @@
 #include <windows.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <errno.h>
 #include <ctype.h>
 #include <time.h>
 #include <assert.h>
@@ -20,26 +22,51 @@
 #define __PHYSICSFS_INTERNAL__
 #include "physfs_internal.h"
 
-#ifndef _MSC_VER /* for Cygwin, etc. */
-#define _alloca alloca
+#ifdef _MSC_VER /* for Cygwin, etc. */
+#define alloca _alloca
 #endif
 
 #define LOWORDER_UINT64(pos)       (PHYSFS_uint32)(pos & 0x00000000FFFFFFFF)
 #define HIGHORDER_UINT64(pos)      (PHYSFS_uint32)(pos & 0xFFFFFFFF00000000)
 
-const char *__PHYSFS_platformDirSeparator = "\\";
+/* GetUserProfileDirectory() is only available on >= NT4 (no 9x/ME systems!) */
+typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETUSERPROFILEDIR) (
+      HANDLE hToken,
+      LPTSTR lpProfileDir,
+      LPDWORD lpcchSize);
 
-static int runningNT = 0;               /* TRUE if NT derived OS */
-static OSVERSIONINFO OSVersionInfo;     /* Information about the OS */
-static char *ProfileDirectory = NULL;   /* User profile folder */
+/* GetFileAttributesEx() is only available on >= Win98 or WinNT4 ... */
+typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETFILEATTRIBUTESEX) (
+      LPCTSTR lpFileName,
+      GET_FILEEX_INFO_LEVELS fInfoLevelId,
+      LPVOID lpFileInformation);
+
+typedef struct
+{
+    HANDLE handle;
+    int readonly;
+} win32file;
 
-/* Users without the platform SDK don't have this defined.  The original docs
-   for SetFilePointer() just said to compare with 0xFFFFFFF, so this should
-   work as desired */
+const char *__PHYSFS_platformDirSeparator = "\\";
+static LPFNGETFILEATTRIBUTESEX pGetFileAttributesEx = NULL;
+static HANDLE libKernel32 = NULL;
+static char *userDir = NULL;
+
+/*
+ * Users without the platform SDK don't have this defined.  The original docs
+ *  for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should
+ *  work as desired
+ */
 #ifndef INVALID_SET_FILE_POINTER
-#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
+#  define INVALID_SET_FILE_POINTER  0xFFFFFFFF
 #endif
 
+/* just in case... */
+#ifndef INVALID_FILE_ATTRIBUTES
+#  define INVALID_FILE_ATTRIBUTES   0xFFFFFFFF
+#endif
+
+
 /*
  * Figure out what the last failing Win32 API call was, and
  *  generate a human-readable string for the error message.
@@ -50,6 +77,7 @@
 static const char *win32strerror(void)
 {
     static TCHAR msgbuf[255];
+    TCHAR *ptr = msgbuf;
 
     FormatMessage(
         FORMAT_MESSAGE_FROM_SYSTEM |
@@ -62,120 +90,181 @@
         NULL 
     );
 
+        /* chop off newlines. */
+    for (ptr = msgbuf; *ptr; ptr++)
+    {
+        if ((*ptr == '\n') || (*ptr == '\r'))
+        {
+            *ptr = ' ';
+            break;
+        } /* if */
+    } /* for */
+
     return((const char *) msgbuf);
 } /* win32strerror */
 
 
-/*
- * Uninitialize any NT specific stuff done in doNTInit().
- *
- * Return zero if there was a catastrophic failure and non-zero otherwise.
- */
-static int doNTDeinit(void)
+static char *getExePath(const char *argv0)
 {
-    /* nothing NT-specific to deinit at this point. */
-    return 1;  /* It's all good */
-} /* doNTDeinit */
+    DWORD buflen;
+    int success = 0;
+    char *ptr = NULL;
+    char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
+
+    BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+    retval[0] = '\0';
+    buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1);
+    if (buflen <= 0)
+        __PHYSFS_setError(win32strerror());
+    else
+    {
+        retval[buflen] = '\0';  /* does API always null-terminate this? */
+
+            /* make sure the string was not truncated. */
+        if (__PHYSFS_platformStricmp(&retval[buflen - 4], ".exe") != 0)
+            __PHYSFS_setError("WIN32: GetModuleFileName() got truncated.");
+        else
+        {
+            ptr = strrchr(retval, '\\');
+            if (ptr == NULL)
+                __PHYSFS_setError("WIN32: GetModuleFileName() had no dir.");
+            else
+            {
+                *(ptr + 1) = '\0';  /* chop off filename. */
+                success = 1;
+            } /* else */
+        } /* else */
+    } /* else */
+
+    /* if any part of the previous approach failed, try SearchPath()... */
+
+    if (!success)
+    {
+        if (argv0 == NULL)
+            __PHYSFS_setError("WIN32: argv0 is NULL.");
+        else
+        {
+            buflen = SearchPath(NULL, argv0, NULL, MAX_PATH+1, retval, &ptr);
+            if (buflen == 0)
+                __PHYSFS_setError(win32strerror());
+            else if (buflen > MAX_PATH)
+                __PHYSFS_setError("Win32: SearchPath() got truncated.");
+            else
+                success = 1;
+        } /* else */
+    } /* if */
+
+    if (!success)
+    {
+        free(retval);
+        return(NULL);  /* physfs error message will be set, above. */
+    } /* if */
+
+    /* free up the bytes we didn't actually use. */
+    ptr = (char *) realloc(retval, strlen(retval) + 1);
+    if (ptr != NULL)
+        retval = ptr;
+
+    return(retval);   /* w00t. */
+} /* getExePath */
 
 
-typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETUSERPROFILEDIR) (
-      HANDLE hToken,
-      LPTSTR lpProfileDir,
-      LPDWORD lpcchSize);
-   
 /*
- * Initialize any NT specific stuff.  This includes any OS based on NT.
+ * Try to make use of GetUserProfileDirectory(), which isn't available on
+ *  some common variants of Win32. If we can't use this, we just punt and
+ *  use the physfs base dir for the user dir, too.
  *
- * Return zero if there was a catastrophic failure and non-zero otherwise.
+ * On success, module-scope variable (userDir) will have a pointer to
+ *  a malloc()'d string of the user's profile dir, and a non-zero value is
+ *  returned. If we can't determine the profile dir, (userDir) will
+ *  be NULL, and zero is returned.
  */
-static int doNTInit(void)
+static int determineUserDir(void)
 {
-    DWORD pathsize = 0;
+    DWORD psize = 0;
     char dummy[1];
     BOOL rc = 0;
-    HANDLE ProcessHandle = NULL;     /* Current process handle */
-    HANDLE AccessTokenHandle = NULL; /* Security handle to process */
-    LPFNGETUSERPROFILEDIR GetUserProfileDirectory = NULL;
-    HMODULE lib = NULL;
-    const char *err = NULL;
+    HANDLE processHandle;            /* Current process handle */
+    HANDLE accessToken = NULL;       /* Security handle to process */
+    LPFNGETUSERPROFILEDIR GetUserProfileDirectory;
+    HMODULE lib;
+
+    assert(userDir == NULL);
 
-    /* Hooray for spaghetti code! */
+    /*
+     * GetUserProfileDirectory() is only available on NT 4.0 and later.
+     *  This means Win95/98/ME (and CE?) users have to do without, so for
+     *  them, we'll default to the base directory when we can't get the
+     *  function pointer.
+     */
 
     lib = LoadLibrary("userenv.dll");
-    if (!lib)
-        goto ntinit_failed;
-
-    /* !!! FIXME: Handle Unicode? */
-    GetUserProfileDirectory = (LPFNGETUSERPROFILEDIR)
+    if (lib)
+    {
+        /* !!! FIXME: Handle Unicode? */
+        GetUserProfileDirectory = (LPFNGETUSERPROFILEDIR)
                               GetProcAddress(lib, "GetUserProfileDirectoryA");
-    if (!GetUserProfileDirectory)
-        goto ntinit_failed;
-
-    /* Create a process handle associated with the current process ID */
-    ProcessHandle = GetCurrentProcess();
+        if (GetUserProfileDirectory)
+        {
+            processHandle = GetCurrentProcess();
+            if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
+            {
+                /*
+                 * Should fail. Will write the size of the profile path in
+                 *  psize. Also note that the second parameter can't be
+                 *  NULL or the function fails.
+                 */
+                rc = GetUserProfileDirectory(accessToken, dummy, &psize);
+                assert(!rc);  /* success?! */
 
-    /* Create a process access token handle */
-    if(!OpenProcessToken(ProcessHandle, TOKEN_QUERY, &AccessTokenHandle))
-        goto ntinit_failed; /* we need that token to get the profile dir. */
+                /* Allocate memory for the profile directory */
+                userDir = (char *) malloc(psize);
+                if (userDir != NULL)
+                {
+                    if (!GetUserProfileDirectory(accessToken, userDir, &psize))
+                    {
+                        free(userDir);
+                        userDir = NULL;
+                    } /* if */
+                } /* else */
+            } /* if */
 
-    /* Should fail.  Will write the size of the profile path in pathsize */
-    /* Second parameter can't be NULL or the function fails. */
-    rc = GetUserProfileDirectory(AccessTokenHandle, dummy, &pathsize);
-    assert(!rc);  /* success?! */
+            CloseHandle(accessToken);
+        } /* if */
 
-    /* Allocate memory for the profile directory */
-    ProfileDirectory = (char *) malloc(pathsize);
-    if (ProfileDirectory == NULL)
-    {
-        err = ERR_OUT_OF_MEMORY;
-        goto ntinit_failed;
+        FreeLibrary(lib);
     } /* if */
 
-    /* Try to get the profile directory */
-    if(!GetUserProfileDirectory(AccessTokenHandle, ProfileDirectory, &pathsize))
-        goto ntinit_failed;
-
-    goto ntinit_succeeded;  /* We made it: hit the showers. */
-
-ntinit_failed:
-    if (err == NULL) /* set an error string if we haven't yet. */
-        __PHYSFS_setError(win32strerror());
-
-    if (ProfileDirectory != NULL)
+    if (userDir == NULL)  /* couldn't get profile for some reason. */
     {
-        free(ProfileDirectory);
-        ProfileDirectory = NULL;
+        /* Might just be a non-NT system; resort to the basedir. */
+        userDir = getExePath(NULL);
+        BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */
     } /* if */
 
-    /* drop through and clean up the rest of the stuff... */
+    return(1);  /* We made it: hit the showers. */
+} /* determineUserDir */
 
-ntinit_succeeded:
-    if (lib != NULL)
-        FreeLibrary(lib);
-
-    if (AccessTokenHandle != NULL)
-        CloseHandle(AccessTokenHandle);
 
-    return ((err == NULL) ? 1 : 0);
-} /* doNTInit */
-
-static BOOL MediaInDrive(const char *DriveLetter)
+static BOOL mediaInDrive(const char *driveLetter)
 {
-    UINT OldErrorMode;
-    DWORD DummyValue;
-    BOOL ReturnValue;
+    UINT oldErrorMode;
+    DWORD dummyValue;
+    BOOL returnValue;
 
     /* Prevent windows warning message to appear when checking media size */
-    OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+    oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
     
     /* If this function succeeds, there's media in the drive */
-    ReturnValue = GetDiskFreeSpace(DriveLetter, &DummyValue, &DummyValue, &DummyValue, &DummyValue);
+    returnValue = GetDiskFreeSpace(driveLetter, &dummyValue, &dummyValue, &dummyValue, &dummyValue);
 
     /* Revert back to old windows error handler */
-    SetErrorMode(OldErrorMode);
+    SetErrorMode(oldErrorMode);
 
-    return ReturnValue;
-} /* MediaInDrive */
+    return(returnValue);
+} /* mediaInDrive */
+
 
 char **__PHYSFS_platformDetectAvailableCDs(void)
 {
@@ -185,16 +274,16 @@
 
     for (drive_str[0] = 'A'; drive_str[0] <= 'Z'; drive_str[0]++)
     {
-        if (GetDriveType(drive_str) == DRIVE_CDROM && MediaInDrive(drive_str))
+        if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str))
         {
-            char **tmp = realloc(retval, sizeof (char *) * cd_count + 1);
+            char **tmp = realloc(retval, sizeof (char *) * (cd_count + 1));
             if (tmp)
             {
                 retval = tmp;
-                retval[cd_count-1] = (char *) malloc(4);
-                if (retval[cd_count-1])
+                retval[cd_count - 1] = (char *) malloc(4);
+                if (retval[cd_count - 1])
                 {
-                    strcpy(retval[cd_count-1], drive_str);
+                    strcpy(retval[cd_count - 1], drive_str);
                     cd_count++;
                 } /* if */
             } /* if */
@@ -206,51 +295,6 @@
 } /* __PHYSFS_detectAvailableCDs */
 
 
-static char *getExePath(const char *argv0)
-{
-    char *filepart = NULL;
-    char *retval;
-	DWORD buflen;
-		
-	retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
-	BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
-    buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1);
-	if (buflen == 0)
-    {
-        const char *err = win32strerror();
-        free(retval);
-        BAIL_MACRO(err, NULL);
-    } /* if */
-
-    retval[buflen] = '\0';  /* does API always null-terminate the string? */
-
-        /* make sure the string was not truncated. */
-    if (__PHYSFS_platformStricmp(&retval[buflen - 4], ".exe") == 0)
-    {
-        char *ptr = strrchr(retval, '\\');
-        if (ptr != NULL)
-        {
-            *(ptr + 1) = '\0';  /* chop off filename. */
-
-            /* free up the bytes we didn't actually use. */            
-            ptr = (char *) realloc(retval, strlen(retval) + 1);
-            if (ptr != NULL)
-			    retval = ptr;
-
-            return(retval);
-        } /* if */
-    } /* if */
-
-    /* if any part of the previous approach failed, try SearchPath()... */
-    buflen = SearchPath(NULL, argv0, NULL, buflen, NULL, NULL);
-    retval = (char *) realloc(retval, buflen);
-    BAIL_IF_MACRO(!retval, ERR_OUT_OF_MEMORY, NULL);
-    SearchPath(NULL, argv0, NULL, buflen, retval, &filepart);
-
-    return(retval);
-} /* getExePath */
-
-
 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
 {
     if (strchr(argv0, '\\') != NULL)   /* default behaviour can handle this. */
@@ -283,16 +327,16 @@
 
 char *__PHYSFS_platformGetUserDir(void)
 {
-    char *retval = (char *) malloc(strlen(ProfileDirectory) + 1);
+    char *retval = (char *) malloc(strlen(userDir) + 1);
     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
-    strcpy(retval, ProfileDirectory); /* calculated at init time. */
-	return retval;
+    strcpy(retval, userDir); /* calculated at init time. */
+    return(retval);
 } /* __PHYSFS_platformGetUserDir */
 
 
 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
 {
-    return((PHYSFS_uint64)GetCurrentThreadId());
+    return((PHYSFS_uint64) GetCurrentThreadId());
 } /* __PHYSFS_platformGetThreadID */
 
 
@@ -323,7 +367,7 @@
 
 int __PHYSFS_platformExists(const char *fname)
 {
-    return(GetFileAttributes(fname) != 0xffffffff);
+    return(GetFileAttributes(fname) != INVALID_FILE_ATTRIBUTES);
 } /* __PHYSFS_platformExists */
 
 
@@ -387,7 +431,7 @@
     size_t len = strlen(dirname);
 
     /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
-    SearchPath = (char *) _alloca(len + 3);
+    SearchPath = (char *) alloca(len + 3);
     BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL);
 
     /* Copy current dirname */
@@ -587,108 +631,151 @@
 } /* __PHYSFS_platformMkDir */
 
 
-/* 
- * Get OS info and save it.
+/*
+ * Get OS info and save the important parts.
  *
  * Returns non-zero if successful, otherwise it returns zero on failure.
  */
-int getOSInfo(void)
+static int getOSInfo(void)
 {
-    /* Get OS info */
+#if 0  /* we don't actually use this at the moment, but may in the future. */
+    OSVERSIONINFO OSVersionInfo;     /* Information about the OS */
     OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);
     BAIL_IF_MACRO(!GetVersionEx(&OSVersionInfo), win32strerror(), 0);
 
     /* Set to TRUE if we are runnign a WinNT based OS 4.0 or greater */
-    runningNT = (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
-        (OSVersionInfo.dwMajorVersion > 3);
+    runningNT = ((OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
+                 (OSVersionInfo.dwMajorVersion >= 4));
+#endif
+
+    return(1);
+} /* getOSInfo */
+
 
-    return 1;
-}
+/*
+ * Some things we want/need are in external DLLs that may or may not be
+ *  available, based on the operating system, etc. This function loads those
+ *  libraries and hunts down the needed pointers.
+ *
+ * Libraries that are one-shot deals, or better loaded as needed, are loaded
+ *  elsewhere (see determineUserDir()).
+ *
+ * Returns zero if a needed library couldn't load, non-zero if we have enough
+ *  to go on (which means some useful but non-crucial libraries may _NOT_ be
+ *  loaded; check the related module-scope variables).
+ */
+static int loadLibraries(void)
+{
+    /* !!! FIXME: Make this table driven? */
+    int allNeededLibrariesLoaded = 1;  /* flip to zero as needed. */
+
+    libKernel32 = LoadLibrary("kernel32.dll");
+    if (libKernel32)
+    {
+        pGetFileAttributesEx = (LPFNGETFILEATTRIBUTESEX)
+                          GetProcAddress(libKernel32, "GetFileAttributesExA");
+    } /* if */
+
+    /* add other DLLs here... */
+
+
+    /* see if there's any reason to keep kernel32.dll around... */
+    if (libKernel32)
+    {
+        if ((pGetFileAttributesEx == NULL) /* && (somethingElse == NULL) */ )
+        {
+            FreeLibrary(libKernel32);
+            libKernel32 = NULL;
+        } /* if */
+    } /* if */
+
+    return(allNeededLibrariesLoaded);
+} /* loadLibraries */
+
 
 int __PHYSFS_platformInit(void)
 {
     BAIL_IF_MACRO(!getOSInfo(), NULL, 0);
+    BAIL_IF_MACRO(!loadLibraries(), NULL, 0);
+    BAIL_IF_MACRO(!determineUserDir(), NULL, 0);
 
-    /* If running an NT system (NT/Win2k/XP, etc...) */
-    if(runningNT)
-    {
-        BAIL_IF_MACRO(!doNTInit(), NULL, 0);
-    } /* if */
-    else
-    {
-        /* Profile directory is the exe path on 95/98/ME systems. */
-    	ProfileDirectory = getExePath(NULL);
-        BAIL_IF_MACRO(ProfileDirectory == NULL, win32strerror(), 0);
-    } /* else */
+    return(1);  /* It's all good */
+} /* __PHYSFS_platformInit */
 
-    return 1;  /* It's all good */
-}
 
 int __PHYSFS_platformDeinit(void)
 {
-    if (runningNT)
+    if (userDir != NULL)
     {
-        BAIL_IF_MACRO(!doNTDeinit(), NULL, 0);
+        free(userDir);
+        userDir = NULL;
     } /* if */
 
-    if (ProfileDirectory != NULL)
+    return(1); /* It's all good */
+} /* __PHYSFS_platformDeinit */
+
+
+static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
+{
+    HANDLE fileHandle;
+    win32file *retval;
+
+    fileHandle = CreateFile(fname, mode, FILE_SHARE_READ, NULL,
+                            creation, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    BAIL_IF_MACRO(fileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
+
+    retval = malloc(sizeof (win32file));
+    if (retval == NULL)
     {
-        free(ProfileDirectory);
-        ProfileDirectory = NULL;
+        CloseHandle(fileHandle);
+        BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
     } /* if */
 
-    return 1; /* It's all good */
-}
+    retval->readonly = rdonly;
+    retval->handle = fileHandle;
+    return(retval);
+} /* doOpen */
+
 
 void *__PHYSFS_platformOpenRead(const char *filename)
 {
-    HANDLE FileHandle;
-    
-    /* Open an existing file for read only. File can be opened by others
-       who request read access on the file only. */
-    FileHandle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, 
-        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1));
+} /* __PHYSFS_platformOpenRead */
 
-    BAIL_IF_MACRO(FileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
-    return (void *)FileHandle;
-}
 
 void *__PHYSFS_platformOpenWrite(const char *filename)
 {
-    HANDLE FileHandle;
-    
-    /* Open an existing file for write only.  File can be opened by others
-       who request read access to the file only */
-    FileHandle = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, 
-        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0));
+} /* __PHYSFS_platformOpenWrite */
 
-    BAIL_IF_MACRO(FileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
-    return (void *)FileHandle;
-}
 
 void *__PHYSFS_platformOpenAppend(const char *filename)
 {
-    HANDLE FileHandle;
-    
-    /* Open an existing file for appending only.  File can be opened by others
-       who request read access to the file only. */
-    FileHandle = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, 
-        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
+    if (retval != NULL)
+    {
+        HANDLE h = ((win32file *) retval)->handle;
+        if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+        {
+            const char *err = win32strerror();
+            CloseHandle(h);
+            free(retval);
+            BAIL_MACRO(err, NULL);
+        } /* if */
+    } /* if */
 
-    BAIL_IF_MACRO(FileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
-    return (void *)FileHandle;
-}
+    return(retval);
+} /* __PHYSFS_platformOpenAppend */
+
 
 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
                                     PHYSFS_uint32 size, PHYSFS_uint32 count)
 {
-    HANDLE FileHandle;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
     DWORD CountOfBytesRead;
     PHYSFS_sint64 retval;
 
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
-
     /* Read data from the file */
     /*!!! - uint32 might be a greater # than DWORD */
     if(!ReadFile(FileHandle, buffer, count * size, &CountOfBytesRead, NULL))
@@ -702,19 +789,17 @@
         retval = CountOfBytesRead / size;
     } /* else */
 
-    return retval;
-}
+    return(retval);
+} /* __PHYSFS_platformRead */
+
 
 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
                                      PHYSFS_uint32 size, PHYSFS_uint32 count)
 {
-    HANDLE FileHandle;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
     DWORD CountOfBytesWritten;
     PHYSFS_sint64 retval;
 
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
-
     /* Read data from the file */
     /*!!! - uint32 might be a greater # than DWORD */
     if(!WriteFile(FileHandle, buffer, count * size, &CountOfBytesWritten, NULL))
@@ -728,248 +813,258 @@
         retval = CountOfBytesWritten / size;
     } /* else */
 
-    return retval;
-}
+    return(retval);
+} /* __PHYSFS_platformWrite */
+
 
 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
 {
-    HANDLE FileHandle;
-    int retval;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
     DWORD HighOrderPos;
-
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
+    DWORD rc;
 
     /* Get the high order 32-bits of the position */
     HighOrderPos = HIGHORDER_UINT64(pos);
 
     /*!!! SetFilePointer needs a signed 64-bit value. */
     /* Move pointer "pos" count from start of file */
-    if((SetFilePointer(FileHandle, LOWORDER_UINT64(pos), &HighOrderPos, FILE_BEGIN)
-        == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
-    {
-        /* An error occured.  Set the error to GetLastError */
-        __PHYSFS_setError(win32strerror());
+    rc = SetFilePointer(FileHandle, LOWORDER_UINT64(pos),
+                        &HighOrderPos, FILE_BEGIN);
 
-        retval = 0;
-    }
-    else
-    {
-        /* No error occured */
-        retval = 1;
-    }
+    if ((rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+        BAIL_MACRO(win32strerror(), 0);
 
-    return retval;
-}
+    return(1);  /* No error occured */
+} /* __PHYSFS_platformSeek */
+
 
 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
 {
-    HANDLE FileHandle;
-    DWORD HighOrderPos = 0; 
-    DWORD LowOrderPos;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
+    DWORD HighPos = 0;
+    DWORD LowPos;
     PHYSFS_sint64 retval;
 
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
-
     /* Get current position */
-    if(((LowOrderPos = SetFilePointer(FileHandle, 0, &HighOrderPos, FILE_CURRENT))
-        == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+    LowPos = SetFilePointer(FileHandle, 0, &HighPos, FILE_CURRENT);
+    if ((LowPos == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
     {
-        /* Set the error to GetLastError */
-        __PHYSFS_setError(win32strerror());
-        /* We errored out */
-        retval = 0;
-    }
+        BAIL_MACRO(win32strerror(), 0);
+    } /* if */
     else
     {
         /* Combine the high/low order to create the 64-bit position value */
-        retval = HighOrderPos;
-        retval = retval << 32;
-        retval |= LowOrderPos;
-    }
+        retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
+        assert(retval >= 0);
+    } /* else */
+
+    return(retval);
+} /* __PHYSFS_platformTell */
 
-    /*!!! Can't find a file pointer routine?!?!?!!?!?*/
-    return retval;
-}
 
-PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle)
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
 {
-    HANDLE FileHandle;
-    DWORD FileSizeHigh;
-    DWORD FileSizeLow;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
+    DWORD SizeHigh;
+    DWORD SizeLow;
     PHYSFS_sint64 retval;
 
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)handle;
-
-    /* Get the file size.  Condition evaluates to TRUE if an error occured */
-    if(((FileSizeLow = GetFileSize(FileHandle, &FileSizeHigh))
-        == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+    SizeLow = GetFileSize(FileHandle, &SizeHigh);
+    if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
     {
         BAIL_MACRO(win32strerror(), -1);
     } /* if */
     else
     {
         /* Combine the high/low order to create the 64-bit position value */
-        retval = FileSizeHigh;
-        retval = retval << 32;
-        retval |= FileSizeLow;
+        retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
+        assert(retval >= 0);
     } /* else */
 
-    return retval;
-}
+    return(retval);
+} /* __PHYSFS_platformFileLength */
+
 
 int __PHYSFS_platformEOF(void *opaque)
 {
-    HANDLE FileHandle;
     PHYSFS_sint64 FilePosition;
     int retval = 0;
 
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
-
     /* Get the current position in the file */
-    if((FilePosition = __PHYSFS_platformTell(opaque)) != 0)
+    if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0)
     {
         /* Non-zero if EOF is equal to the file length */
         retval = FilePosition == __PHYSFS_platformFileLength(opaque);
-    }
+    } /* if */
 
-    return retval;
-}
+    return(retval);
+} /* __PHYSFS_platformEOF */
+
 
 int __PHYSFS_platformFlush(void *opaque)
 {
-    HANDLE FileHandle;
-    int retval;
-
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
+    win32file *fh = ((win32file *) opaque);
+    if (!fh->readonly)
+        BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), win32strerror(), 0);
 
-    /* Close the file */
-    if(!(retval = FlushFileBuffers(FileHandle)))
-    {
-        /* Set the error to GetLastError */
-        __PHYSFS_setError(win32strerror());
-    }
+    return(1);
+} /* __PHYSFS_platformFlush */
 
-    return retval;
-}
 
 int __PHYSFS_platformClose(void *opaque)
 {
-    HANDLE FileHandle;
-    int retval;
-
-    /* Cast the generic handle to a Win32 handle */
-    FileHandle = (HANDLE)opaque;
+    HANDLE FileHandle = ((win32file *) opaque)->handle;
+    BAIL_IF_MACRO(!CloseHandle(FileHandle), win32strerror(), 0);
+    free(opaque);
+    return(1);
+} /* __PHYSFS_platformClose */
 
-    /* Close the file */
-    if(!(retval = CloseHandle(FileHandle)))
-    {
-        /* Set the error to GetLastError */
-        __PHYSFS_setError(win32strerror());
-    }
-
-    return retval;
-}
 
 int __PHYSFS_platformDelete(const char *path)
 {
-    int retval;
-
     /* If filename is a folder */
-    if(GetFileAttributes(path) == FILE_ATTRIBUTE_DIRECTORY)
+    if (GetFileAttributes(path) == FILE_ATTRIBUTE_DIRECTORY)
     {
-        retval = RemoveDirectory(path);
-    }
+        BAIL_IF_MACRO(!RemoveDirectory(path), win32strerror(), 0);
+    } /* if */
     else
     {
-        retval = DeleteFile(path);
-    }
+        BAIL_IF_MACRO(!DeleteFile(path), win32strerror(), 0);
+    } /* else */
 
-    if(!retval)
-    {
-            /* Set the error to GetLastError */
-        __PHYSFS_setError(win32strerror());
-    }
+    return(1);  /* if you got here, it worked. */
+} /* __PHYSFS_platformDelete */
 
-    return retval;
-}
 
 void *__PHYSFS_platformCreateMutex(void)
 {
-    return (void *)CreateMutex(NULL, FALSE, NULL);
-}
+    return((void *) CreateMutex(NULL, FALSE, NULL));
+} /* __PHYSFS_platformCreateMutex */
+
 
 void __PHYSFS_platformDestroyMutex(void *mutex)
 {
-    CloseHandle((HANDLE)mutex);
-}
+    CloseHandle((HANDLE) mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
 
 int __PHYSFS_platformGrabMutex(void *mutex)
 {
-    int retval;
+    return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED);
+} /* __PHYSFS_platformGrabMutex */
 
-    if(WaitForSingleObject((HANDLE)mutex, INFINITE) == WAIT_FAILED)
-    {
-        /* Our wait failed for some unknown reason */
-        retval = 0;
-    }
-    else
-    {
-        /* Good to go */
-        retval = 1;
-    }
-
-    return retval;
-}
 
 void __PHYSFS_platformReleaseMutex(void *mutex)
 {
-    ReleaseMutex((HANDLE)mutex);
-}
+    ReleaseMutex((HANDLE) mutex);
+} /* __PHYSFS_platformReleaseMutex */
 
-static time_t FileTimeToTimeT(FILETIME *ft)
+
+static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft)
 {
     SYSTEMTIME st_utc;
     SYSTEMTIME st_localtz;
-    TIME_ZONE_INFORMATION TimeZoneInfo;
+    TIME_ZONE_INFORMATION tzi;
+    DWORD tzid;
+    PHYSFS_sint64 retval;
     struct tm tm;
 
-    FileTimeToSystemTime(ft, &st_utc);
-    GetTimeZoneInformation(&TimeZoneInfo);
-    SystemTimeToTzSpecificLocalTime(&TimeZoneInfo, &st_utc, &st_localtz);
+    BAIL_IF_MACRO(!FileTimeToSystemTime(ft, &st_utc), win32strerror(), -1);
+    tzid = GetTimeZoneInformation(&tzi);
+    BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, win32strerror(), -1);
+
+        /* (This API is unsupported and fails on non-NT systems. */
+    if (!SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz))
+    {
+        /* do it by hand. Grumble... */
+        ULARGE_INTEGER ui64;
+        FILETIME new_ft;
+        ui64.LowPart = ft->dwLowDateTime;
+        ui64.HighPart = ft->dwHighDateTime;
+
+        if (tzid == TIME_ZONE_ID_STANDARD)
+            tzi.Bias += tzi.StandardBias;
+        else if (tzid == TIME_ZONE_ID_DAYLIGHT)
+            tzi.Bias += tzi.DaylightBias;
 
+        /* convert from minutes to 100-nanosecond increments... */
+        #if 0 /* For compilers that puke on 64-bit math. */
+            /* goddamn this is inefficient... */
+            while (tzi.Bias > 0)
+            {
+                DWORD tmp = ui64.LowPart - 60000000;
+                if ((ui64.LowPart < tmp) && (tmp > 60000000))
+                    ui64.HighPart--;
+                ui64.LowPart = tmp;
+                tzi.Bias--;
+            } /* while */
+
+            while (tzi.Bias < 0)
+            {
+                DWORD tmp = ui64.LowPart + 60000000;
+                if ((ui64.LowPart > tmp) && (tmp < 60000000))
+                    ui64.HighPart++;
+                ui64.LowPart = tmp;
+                tzi.Bias++;
+            } /* while */
+        #else
+            ui64.QuadPart -= (((LONGLONG) tzi.Bias) * (600000000));
+        #endif
+
+        /* Move it back into a FILETIME structure... */
+        new_ft.dwLowDateTime = ui64.LowPart;
+        new_ft.dwHighDateTime = ui64.HighPart;
+
+        /* Convert to something human-readable... */
+        if (!FileTimeToSystemTime(&new_ft, &st_localtz))
+            BAIL_MACRO(win32strerror(), -1);
+    } /* if */
+
+    /* Convert to a format that mktime() can grok... */
     tm.tm_sec = st_localtz.wSecond;
     tm.tm_min = st_localtz.wMinute;
     tm.tm_hour = st_localtz.wHour;
     tm.tm_mday = st_localtz.wDay;
     tm.tm_mon = st_localtz.wMonth - 1;
     tm.tm_year = st_localtz.wYear - 1900;
-    tm.tm_wday = st_localtz.wDayOfWeek;
+    tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
     tm.tm_yday = -1;
     tm.tm_isdst = -1;
-    return mktime(&tm);
-} /* FileTimeToTimeT */
+
+    /* Convert to a format PhysicsFS can grok... */
+    retval = (PHYSFS_sint64) mktime(&tm);
+    BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
+    return(retval);
+} /* FileTimeToPhysfsTime */
+
 
 PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
 {
-    WIN32_FILE_ATTRIBUTE_DATA AttributeData;
+    PHYSFS_sint64 retval = -1;
+    WIN32_FILE_ATTRIBUTE_DATA attrData;
+    memset(&attrData, '\0', sizeof (attrData));
 
-    GetFileAttributesEx(fname, GetFileExInfoStandard, &AttributeData);
-    /* 0 return value indicates an error or not supported */
-    if(AttributeData.ftLastWriteTime.dwHighDateTime == 0 &&
-        AttributeData.ftLastWriteTime.dwLowDateTime == 0)
+    if (pGetFileAttributesEx != NULL)
     {
-        /* Return error */
-        BAIL_MACRO(win32strerror(), -1);
-    }
+        if (pGetFileAttributesEx(fname, GetFileExInfoStandard, &attrData))
+        {
+            /* 0 return value indicates an error or not supported */
+            if ( (attrData.ftLastWriteTime.dwHighDateTime != 0) ||
+                 (attrData.ftLastWriteTime.dwLowDateTime != 0) )
+            {
+                retval = FileTimeToPhysfsTime(&attrData.ftLastWriteTime);
+            } /* if */
+        } /* if */
+    } /* if */
 
-    /* Return UNIX time_t version of last write time */
-    return (PHYSFS_sint64)FileTimeToTimeT(&AttributeData.ftLastWriteTime);
-    /*return (PHYSFS_sint64)FileTimeToTimeT(&AttributeData.ftCreationTime);*/
+    if (retval == -1)  /* try a fallback... */
+    {
+        /* !!! FIXME: uhh...? */
+    } /* if */
+
+    return(retval);
+
+    /*return(FileTimeToPhysfsTime(&attrData.ftCreationTime));*/
 } /* __PHYSFS_platformGetLastModTime */
 
 /* end of win32.c ... */