Improved dynamic loader and initial Unicode work in windows.c ...

This commit is contained in:
Ryan C. Gordon 2007-03-25 05:17:08 +00:00
parent 71e6a8dc3f
commit dc0d27228c
2 changed files with 147 additions and 130 deletions

View File

@ -2,6 +2,7 @@
* CHANGELOG. * CHANGELOG.
*/ */
03252007 - Improved dynamic loader and initial Unicode work in windows.c ...
03242007 - Replaced BeOS semaphores with BLockers for the mutex implementation. 03242007 - Replaced BeOS semaphores with BLockers for the mutex implementation.
It's much simpler, it has "benaphores" built in behind the scenes It's much simpler, it has "benaphores" built in behind the scenes
for faster performance, and it's recursive...also, we were for faster performance, and it's recursive...also, we were

View File

@ -26,32 +26,6 @@
#define HIGHORDER_UINT64(pos) (PHYSFS_uint32) \ #define HIGHORDER_UINT64(pos) (PHYSFS_uint32) \
(((pos & 0xFFFFFFFF00000000) >> 32) & 0x00000000FFFFFFFF) (((pos & 0xFFFFFFFF00000000) >> 32) & 0x00000000FFFFFFFF)
/* !!! FIXME: unicode version. */
/* GetUserProfileDirectory() is only available on >= NT4 (no 9x/ME systems!) */
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETUSERPROFILEDIR) (
HANDLE hToken,
LPTSTR lpProfileDir,
LPDWORD lpcchSize);
/* !!! FIXME: unicode version. */
/* 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;
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 * Users without the platform SDK don't have this defined. The original docs
* for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should * for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should
@ -62,6 +36,100 @@ static char *userDir = NULL;
/* just in case... */ /* just in case... */
#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF #define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \
if (str == NULL) \
w_assignto = NULL; \
else { \
const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \
w_assignto = (char *) __PHYSFS_smallAlloc(len); \
PHYSFS_uc2fromutf8(str, (PHYSFS_uint16 *) w_assignto, len); \
} \
} \
typedef struct
{
HANDLE handle;
int readonly;
} win32file;
const char *__PHYSFS_platformDirSeparator = "\\";
/* pointers for APIs that may not exist on some Windows versions... */
static HANDLE libKernel32 = NULL;
static HANDLE libUserEnv = NULL;
static HANDLE libAdvApi32 = NULL;
static DWORD (WINAPI *pGetModuleFileNameA)(HMODULE, LPCH, DWORD);
static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD);
static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD);
static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR);
static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW);
static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW);
static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR);
static BOOL (WINAPI *pDeleteFileW)(LPCWSTR);
static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR);
static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES);
static BOOL (WINAPI *pGetFileAttributesExA)
(LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
static BOOL (WINAPI *pGetFileAttributesExW)
(LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
static DWORD (WINAPI *pFormatMessageW)
(DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *);
static DWORD (WINAPI *pSearchPathW)
(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPWSTR, LPWSTR *);
static HANDLE (WINAPI *pCreateFileW)
(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
static char *userDir = NULL;
/* A blatant abuse of pointer casting... */
static void symLookup(HMODULE dll, void **addr, const char *sym)
{
*addr = GetProcAddress(dll, sym);
} /* symLookup */
static int findApiSymbols(void)
{
HMODULE dll = NULL;
#define LOOKUP(x) { symLookup(dll, (void **) &p##x, #x); }
dll = libUserEnv = LoadLibrary("userenv.dll");
if (dll != NULL)
LOOKUP(GetUserProfileDirectoryW);
/* !!! FIXME: what do they call advapi32.dll on Win64? */
dll = libAdvApi32 = LoadLibrary("advapi32.dll");
if (dll != NULL)
LOOKUP(GetUserNameW);
/* !!! FIXME: what do they call kernel32.dll on Win64? */
dll = libKernel32 = LoadLibrary("kernel32.dll");
if (dll != NULL)
{
LOOKUP(GetModuleFileNameA);
LOOKUP(GetModuleFileNameW);
LOOKUP(FormatMessageW);
LOOKUP(FindFirstFileW);
LOOKUP(FindNextFileW);
LOOKUP(GetFileAttributesW);
LOOKUP(GetFileAttributesExA);
LOOKUP(GetFileAttributesExW);
LOOKUP(GetCurrentDirectoryW);
LOOKUP(CreateDirectoryW);
LOOKUP(RemoveDirectoryW);
LOOKUP(CreateFileW);
LOOKUP(DeleteFileW);
LOOKUP(SearchPathW);
} /* if */
#undef LOOKUP
return(1);
} /* findApiSymbols */
/* /*
@ -175,7 +243,7 @@ static char *getExePath(const char *argv0)
/* /*
* Try to make use of GetUserProfileDirectory(), which isn't available on * Try to make use of GetUserProfileDirectoryW(), which isn't available on
* some common variants of Win32. If we can't use this, we just punt and * 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. * use the physfs base dir for the user dir, too.
* *
@ -186,64 +254,52 @@ static char *getExePath(const char *argv0)
*/ */
static int determineUserDir(void) static int determineUserDir(void)
{ {
DWORD psize = 0; if (userDir != NULL)
char dummy[1]; return(1); /* already good to go. */
BOOL rc = 0;
HANDLE processHandle; /* Current process handle */
HANDLE accessToken = NULL; /* Security handle to process */
LPFNGETUSERPROFILEDIR GetUserProfileDirectory;
HMODULE lib;
assert(userDir == NULL);
/* /*
* GetUserProfileDirectory() is only available on NT 4.0 and later. * GetUserProfileDirectoryW() is only available on NT 4.0 and later.
* This means Win95/98/ME (and CE?) users have to do without, so for * 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 * them, we'll default to the base directory when we can't get the
* function pointer. * function pointer. Since this is originally an NT API, we don't
* offer a non-Unicode fallback.
*/ */
if (pGetUserProfileDirectoryW != NULL)
lib = LoadLibrary("userenv.dll");
if (lib)
{ {
/* !!! FIXME: unicode version. */ HANDLE accessToken = NULL; /* Security handle to process */
GetUserProfileDirectory = (LPFNGETUSERPROFILEDIR) HANDLE processHandle = GetCurrentProcess();
GetProcAddress(lib, "GetUserProfileDirectoryA"); if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
if (GetUserProfileDirectory)
{ {
processHandle = GetCurrentProcess(); DWORD psize = 0;
if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken)) WCHAR dummy = 0;
LPWSTR wstr = NULL;
BOOL rc = 0;
/*
* 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 = pGetUserProfileDirectoryW(accessToken, &dummy, &psize);
assert(!rc); /* !!! FIXME: handle this gracefully. */
/* Allocate memory for the profile directory */
wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR));
if (wstr != NULL)
{ {
/* if (pGetUserProfileDirectoryW(accessToken, wstr, &psize))
* 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.
*/
/* !!! FIXME: unicode version. */
rc = GetUserProfileDirectory(accessToken, dummy, &psize);
assert(!rc); /* success?! */
/* Allocate memory for the profile directory */
userDir = (char *) allocator.Malloc(psize);
if (userDir != NULL)
{ {
/* !!! FIXME: unicode version. */ const PHYSFS_uint64 buflen = psize * 6;
if (GetUserProfileDirectory(accessToken, userDir, &psize)) userDir = (char *) allocator.Malloc(buflen);
{ if (userDir != NULL)
/* !!! FIXME: convert to UTF-8. */ PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) wstr, userDir, buflen);
} /* if */ /* !!! FIXME: shrink allocation... */
else } /* if */
{ __PHYSFS_smallFree(wstr);
allocator.Free(userDir); } /* else */
userDir = NULL;
} /* else */
} /* else */
} /* if */
CloseHandle(accessToken);
} /* if */ } /* if */
FreeLibrary(lib); CloseHandle(accessToken);
} /* if */ } /* if */
if (userDir == NULL) /* couldn't get profile for some reason. */ if (userDir == NULL) /* couldn't get profile for some reason. */
@ -251,7 +307,6 @@ static int determineUserDir(void)
/* Might just be a non-NT system; resort to the basedir. */ /* Might just be a non-NT system; resort to the basedir. */
userDir = getExePath(NULL); userDir = getExePath(NULL);
BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */ BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */
/* !!! FIXME: convert to UTF-8. */
} /* if */ } /* if */
return(1); /* We made it: hit the showers. */ return(1); /* We made it: hit the showers. */
@ -445,6 +500,8 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname,
continue; continue;
callback(callbackdata, origdir, ent.cFileName); callback(callbackdata, origdir, ent.cFileName);
/* !!! FIXME: unicode version. */
} while (FindNextFile(dir, &ent) != 0); } while (FindNextFile(dir, &ent) != 0);
FindClose(dir); FindClose(dir);
@ -622,71 +679,30 @@ static int getOSInfo(void)
} /* getOSInfo */ } /* getOSInfo */
/*
* 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)
{
/* If this get unwieldy, make it 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) int __PHYSFS_platformInit(void)
{ {
BAIL_IF_MACRO(!findApiSymbols(), NULL, 0);
BAIL_IF_MACRO(!getOSInfo(), NULL, 0); BAIL_IF_MACRO(!getOSInfo(), NULL, 0);
BAIL_IF_MACRO(!loadLibraries(), NULL, 0);
BAIL_IF_MACRO(!determineUserDir(), NULL, 0); BAIL_IF_MACRO(!determineUserDir(), NULL, 0);
return(1); /* It's all good */ return(1); /* It's all good */
} /* __PHYSFS_platformInit */ } /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void) int __PHYSFS_platformDeinit(void)
{ {
if (userDir != NULL) HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL };
{ int i;
allocator.Free(userDir);
userDir = NULL;
} /* if */
if (libKernel32) allocator.Free(userDir);
userDir = NULL;
for (i = 0; libs[i] != NULL; i++)
{ {
FreeLibrary(libKernel32); const HANDLE lib = *(libs[i]);
libKernel32 = NULL; if (lib)
} /* if */ FreeLibrary(lib);
*(libs[i]) = NULL;
} /* for */
return(1); /* It's all good */ return(1); /* It's all good */
} /* __PHYSFS_platformDeinit */ } /* __PHYSFS_platformDeinit */
@ -1054,10 +1070,10 @@ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
memset(&attrData, '\0', sizeof (attrData)); memset(&attrData, '\0', sizeof (attrData));
/* GetFileAttributesEx didn't show up until Win98 and NT4. */ /* GetFileAttributesEx didn't show up until Win98 and NT4. */
if (pGetFileAttributesEx != NULL) if (pGetFileAttributesExA != NULL)
{ {
/* !!! FIXME: unicode version. */ /* !!! FIXME: unicode version. */
if (pGetFileAttributesEx(fname, GetFileExInfoStandard, &attrData)) if (pGetFileAttributesExA(fname, GetFileExInfoStandard, &attrData))
{ {
/* 0 return value indicates an error or not supported */ /* 0 return value indicates an error or not supported */
if ( (attrData.ftLastWriteTime.dwHighDateTime != 0) || if ( (attrData.ftLastWriteTime.dwHighDateTime != 0) ||