Skip to content

Commit

Permalink
Improved dynamic loader and initial Unicode work in windows.c ...
Browse files Browse the repository at this point in the history
  • Loading branch information
icculus committed Mar 25, 2007
1 parent 71e6a8d commit dc0d272
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 125 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Expand Up @@ -2,6 +2,7 @@
* CHANGELOG.
*/

03252007 - Improved dynamic loader and initial Unicode work in windows.c ...
03242007 - Replaced BeOS semaphores with BLockers for the mutex implementation.
It's much simpler, it has "benaphores" built in behind the scenes
for faster performance, and it's recursive...also, we were
Expand Down
266 changes: 141 additions & 125 deletions platform/windows.c
Expand Up @@ -26,20 +26,25 @@
#define HIGHORDER_UINT64(pos) (PHYSFS_uint32) \
(((pos & 0xFFFFFFFF00000000) >> 32) & 0x00000000FFFFFFFF)

/*
* 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.
*/
#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF

/* !!! FIXME: unicode version. */
/* GetUserProfileDirectory() is only available on >= NT4 (no 9x/ME systems!) */
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETUSERPROFILEDIR) (
HANDLE hToken,
LPTSTR lpProfileDir,
LPDWORD lpcchSize);
/* just in case... */
#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF

/* !!! 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);
#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
{
Expand All @@ -48,20 +53,83 @@ typedef struct
} win32file;

const char *__PHYSFS_platformDirSeparator = "\\";
static LPFNGETFILEATTRIBUTESEX pGetFileAttributesEx = NULL;


/* 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;

/*
* 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.
*/
#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF

/* just in case... */
#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
/* 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 */


/*
Expand Down Expand Up @@ -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
* use the physfs base dir for the user dir, too.
*
Expand All @@ -186,72 +254,59 @@ static char *getExePath(const char *argv0)
*/
static int determineUserDir(void)
{
DWORD psize = 0;
char dummy[1];
BOOL rc = 0;
HANDLE processHandle; /* Current process handle */
HANDLE accessToken = NULL; /* Security handle to process */
LPFNGETUSERPROFILEDIR GetUserProfileDirectory;
HMODULE lib;

assert(userDir == NULL);
if (userDir != NULL)
return(1); /* already good to go. */

/*
* 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
* 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.
*/

lib = LoadLibrary("userenv.dll");
if (lib)
if (pGetUserProfileDirectoryW != NULL)
{
/* !!! FIXME: unicode version. */
GetUserProfileDirectory = (LPFNGETUSERPROFILEDIR)
GetProcAddress(lib, "GetUserProfileDirectoryA");
if (GetUserProfileDirectory)
HANDLE accessToken = NULL; /* Security handle to process */
HANDLE processHandle = GetCurrentProcess();
if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
{
processHandle = GetCurrentProcess();
if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
DWORD psize = 0;
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)
{
/*
* 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)
if (pGetUserProfileDirectoryW(accessToken, wstr, &psize))
{
/* !!! FIXME: unicode version. */
if (GetUserProfileDirectory(accessToken, userDir, &psize))
{
/* !!! FIXME: convert to UTF-8. */
} /* if */
else
{
allocator.Free(userDir);
userDir = NULL;
} /* else */
} /* else */
} /* if */

CloseHandle(accessToken);
const PHYSFS_uint64 buflen = psize * 6;
userDir = (char *) allocator.Malloc(buflen);
if (userDir != NULL)
PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) wstr, userDir, buflen);
/* !!! FIXME: shrink allocation... */
} /* if */
__PHYSFS_smallFree(wstr);
} /* else */
} /* if */

FreeLibrary(lib);
CloseHandle(accessToken);
} /* if */

if (userDir == NULL) /* couldn't get profile for some reason. */
{
/* Might just be a non-NT system; resort to the basedir. */
userDir = getExePath(NULL);
BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */
/* !!! FIXME: convert to UTF-8. */
} /* if */

return(1); /* We made it: hit the showers. */
Expand Down Expand Up @@ -445,6 +500,8 @@ void __PHYSFS_platformEnumerateFiles(const char *dirname,
continue;

callback(callbackdata, origdir, ent.cFileName);

/* !!! FIXME: unicode version. */
} while (FindNextFile(dir, &ent) != 0);

FindClose(dir);
Expand Down Expand Up @@ -622,71 +679,30 @@ static int getOSInfo(void)
} /* 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)
{
BAIL_IF_MACRO(!findApiSymbols(), NULL, 0);
BAIL_IF_MACRO(!getOSInfo(), NULL, 0);
BAIL_IF_MACRO(!loadLibraries(), NULL, 0);
BAIL_IF_MACRO(!determineUserDir(), NULL, 0);

return(1); /* It's all good */
} /* __PHYSFS_platformInit */


int __PHYSFS_platformDeinit(void)
{
if (userDir != NULL)
{
allocator.Free(userDir);
userDir = NULL;
} /* if */
HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL };
int i;

allocator.Free(userDir);
userDir = NULL;

if (libKernel32)
for (i = 0; libs[i] != NULL; i++)
{
FreeLibrary(libKernel32);
libKernel32 = NULL;
} /* if */
const HANDLE lib = *(libs[i]);
if (lib)
FreeLibrary(lib);
*(libs[i]) = NULL;
} /* for */

return(1); /* It's all good */
} /* __PHYSFS_platformDeinit */
Expand Down Expand Up @@ -1054,10 +1070,10 @@ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
memset(&attrData, '\0', sizeof (attrData));

/* GetFileAttributesEx didn't show up until Win98 and NT4. */
if (pGetFileAttributesEx != NULL)
if (pGetFileAttributesExA != NULL)
{
/* !!! FIXME: unicode version. */
if (pGetFileAttributesEx(fname, GetFileExInfoStandard, &attrData))
if (pGetFileAttributesExA(fname, GetFileExInfoStandard, &attrData))
{
/* 0 return value indicates an error or not supported */
if ( (attrData.ftLastWriteTime.dwHighDateTime != 0) ||
Expand Down

0 comments on commit dc0d272

Please sign in to comment.