📄 win32.c
字号:
/* * Win32 support routines for PhysicsFS. * * Please see the file LICENSE in the source's root directory. * * This file written by Ryan C. Gordon, and made sane by Gregory S. Read. */#if HAVE_CONFIG_H# include <config.h>#endif#ifdef WIN32#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <ctype.h>#include <time.h>#define __PHYSICSFS_INTERNAL__#include "physfs_internal.h"#if (defined _MSC_VER) #define alloca(x) _alloca(x)#elif (defined MINGW) /* scary...hopefully this is okay. */ #define alloca(x) __builtin_alloca(x) #endif#define LOWORDER_UINT64(pos) (PHYSFS_uint32)(pos & 0x00000000FFFFFFFF)#define HIGHORDER_UINT64(pos) (PHYSFS_uint32)(pos & 0xFFFFFFFF00000000)/* GetUserProfileDirectory() is only available on >= NT4 (no 9x/ME systems!) */typedef BOOL (STDMETHODCALLTYPE FAR * LPFNGETUSERPROFILEDIR) ( HANDLE hToken, LPTSTR lpProfileDir, LPDWORD lpcchSize);/* 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 * 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#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. * * The return value is a static buffer that is overwritten with * each call to this function. */static const char *win32strerror(void){ static TCHAR msgbuf[255]; TCHAR *ptr = msgbuf; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ msgbuf, sizeof (msgbuf) / sizeof (TCHAR), NULL ); /* chop off newlines. */ for (ptr = msgbuf; *ptr; ptr++) { if ((*ptr == '\n') || (*ptr == '\r')) { *ptr = ' '; break; } /* if */ } /* for */ return((const char *) msgbuf);} /* win32strerror */static char *getExePath(const char *argv0){ 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(ERR_GETMODFN_TRUNC); else { ptr = strrchr(retval, '\\'); if (ptr == NULL) __PHYSFS_setError(ERR_GETMODFN_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(ERR_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(ERR_SEARCHPATH_TRUNC); 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 *//* * 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. * * 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 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); /* * 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) { /* !!! FIXME: Handle Unicode? */ GetUserProfileDirectory = (LPFNGETUSERPROFILEDIR) GetProcAddress(lib, "GetUserProfileDirectoryA"); 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?! */ /* 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 */ CloseHandle(accessToken); } /* if */ FreeLibrary(lib); } /* 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?! */ } /* if */ return(1); /* We made it: hit the showers. */} /* determineUserDir */static BOOL mediaInDrive(const char *drive){ UINT oldErrorMode; DWORD tmp; BOOL retval; /* Prevent windows warning message to appear when checking media size */ oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); /* If this function succeeds, there's media in the drive */ retval = GetVolumeInformation(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0); /* Revert back to old windows error handler */ SetErrorMode(oldErrorMode); return(retval);} /* mediaInDrive */char **__PHYSFS_platformDetectAvailableCDs(void){ char **retval = (char **) malloc(sizeof (char *)); int cd_count = 1; /* We count the NULL entry. */ char drive_str[4] = "x:\\"; for (drive_str[0] = 'A'; drive_str[0] <= 'Z'; drive_str[0]++) { if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str)) { 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]) { strcpy(retval[cd_count - 1], drive_str); cd_count++; } /* if */ } /* if */ } /* if */ } /* for */ retval[cd_count - 1] = NULL; return(retval);} /* __PHYSFS_detectAvailableCDs */char *__PHYSFS_platformCalcBaseDir(const char *argv0){ if ((argv0 != NULL) && (strchr(argv0, '\\') != NULL)) return(NULL); /* default behaviour can handle this. */ return(getExePath(argv0));} /* __PHYSFS_platformCalcBaseDir */char *__PHYSFS_platformGetUserName(void){ DWORD bufsize = 0; LPTSTR retval = NULL; if (GetUserName(NULL, &bufsize) == 0) /* This SHOULD fail. */ { retval = (LPTSTR) malloc(bufsize); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); if (GetUserName(retval, &bufsize) == 0) /* ?! */ { __PHYSFS_setError(win32strerror()); free(retval); retval = NULL; } /* if */ } /* if */ return((char *) retval);} /* __PHYSFS_platformGetUserName */char *__PHYSFS_platformGetUserDir(void){ char *retval = (char *) malloc(strlen(userDir) + 1); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); strcpy(retval, userDir); /* calculated at init time. */ return(retval);} /* __PHYSFS_platformGetUserDir */PHYSFS_uint64 __PHYSFS_platformGetThreadID(void){ return((PHYSFS_uint64) GetCurrentThreadId());} /* __PHYSFS_platformGetThreadID *//* ...make this Cygwin AND Visual C friendly... */int __PHYSFS_platformStricmp(const char *x, const char *y){#if (defined _MSC_VER) return(stricmp(x, y));#else int ux, uy; do { ux = toupper((int) *x); uy = toupper((int) *y); if (ux > uy) return(1); else if (ux < uy) return(-1); x++; y++; } while ((ux) && (uy)); return(0);#endif} /* __PHYSFS_platformStricmp */int __PHYSFS_platformExists(const char *fname){ BAIL_IF_MACRO(GetFileAttributes(fname) == INVALID_FILE_ATTRIBUTES, win32strerror(), 0); return(1);} /* __PHYSFS_platformExists */int __PHYSFS_platformIsSymLink(const char *fname){ return(0); /* no symlinks on win32. */} /* __PHYSFS_platformIsSymlink */int __PHYSFS_platformIsDirectory(const char *fname){ return((GetFileAttributes(fname) & FILE_ATTRIBUTE_DIRECTORY) != 0);} /* __PHYSFS_platformIsDirectory */char *__PHYSFS_platformCvtToDependent(const char *prepend, const char *dirName, const char *append){ int len = ((prepend) ? strlen(prepend) : 0) + ((append) ? strlen(append) : 0) + strlen(dirName) + 1; char *retval = malloc(len); char *p; BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); if (prepend) strcpy(retval, prepend); else retval[0] = '\0'; strcat(retval, dirName); if (append) strcat(retval, append); for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/')) *p = '\\'; return(retval);} /* __PHYSFS_platformCvtToDependent *//* Much like my college days, try to sleep for 10 milliseconds at a time... */void __PHYSFS_platformTimeslice(void){ Sleep(10);} /* __PHYSFS_platformTimeslice */LinkedStringList *__PHYSFS_platformEnumerateFiles(const char *dirname, int omitSymLinks){ LinkedStringList *retval = NULL, *p = NULL; HANDLE dir; WIN32_FIND_DATA ent; char *SearchPath; size_t len = strlen(dirname); /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ SearchPath = (char *) alloca(len + 3); BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL); /* Copy current dirname */ strcpy(SearchPath, dirname); /* if there's no '\\' at the end of the path, stick one in there. */ if (SearchPath[len - 1] != '\\') { SearchPath[len++] = '\\'; SearchPath[len] = '\0'; } /* if */ /* Append the "*" to the end of the string */ strcat(SearchPath, "*"); dir = FindFirstFile(SearchPath, &ent); BAIL_IF_MACRO(dir == INVALID_HANDLE_VALUE, win32strerror(), NULL); do { if (strcmp(ent.cFileName, ".") == 0) continue; if (strcmp(ent.cFileName, "..") == 0) continue; retval = __PHYSFS_addToLinkedStringList(retval, &p, ent.cFileName, -1); } while (FindNextFile(dir, &ent) != 0); FindClose(dir); return(retval);} /* __PHYSFS_platformEnumerateFiles */char *__PHYSFS_platformCurrentDir(void){ LPTSTR retval; DWORD buflen = 0; buflen = GetCurrentDirectory(buflen, NULL); retval = (LPTSTR) malloc(sizeof (TCHAR) * (buflen + 2)); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); GetCurrentDirectory(buflen, retval); if (retval[buflen - 2] != '\\') strcat(retval, "\\"); return((char *) retval);} /* __PHYSFS_platformCurrentDir *//* this could probably use a cleanup. */char *__PHYSFS_platformRealPath(const char *path){ char *retval = NULL; char *p = NULL; BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL); BAIL_IF_MACRO(*path == '\0', ERR_INVALID_ARGUMENT, NULL); retval = (char *) malloc(MAX_PATH); BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); /* * If in \\server\path format, it's already an absolute path. * We'll need to check for "." and ".." dirs, though, just in case. */ if ((path[0] == '\\') && (path[1] == '\\')) strcpy(retval, path); else { char *currentDir = __PHYSFS_platformCurrentDir(); if (currentDir == NULL) { free(retval); BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL); } /* if */ if (path[1] == ':') /* drive letter specified? */ { /* * Apparently, "D:mypath" is the same as "D:\\mypath" if * D: is not the current drive. However, if D: is the * current drive, then "D:mypath" is a relative path. Ugh. */ if (path[2] == '\\') /* maybe an absolute path? */ strcpy(retval, path); else /* definitely an absolute path. */ { if (path[0] == currentDir[0]) /* current drive; relative. */ { strcpy(retval, currentDir); strcat(retval, path + 2); } /* if */ else /* not current drive; absolute. */ { retval[0] = path[0]; retval[1] = ':'; retval[2] = '\\';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -