/* * Win32 support routines for PhysicsFS. * * Please see the file LICENSE in the source's root directory. * * This file written by Ryan C. Gordon. */ #if (defined __STRICT_ANSI__) #define __PHYSFS_DOING_STRICT_ANSI__ #endif #include #include #include #include #define __PHYSICSFS_INTERNAL__ #include "physfs_internal.h" const char *__PHYSFS_platformDirSeparator = "\\"; static const char *win32strerror(void) { static TCHAR msgbuf[255]; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ msgbuf, sizeof (msgbuf) / sizeof (TCHAR), NULL ); return((const char *) msgbuf); } /* win32strerror */ 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) { 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) { DWORD buflen = 0; char *retval = NULL; char *filepart = NULL; if (strchr(argv0, '\\') != NULL) /* default behaviour can handle this. */ return(NULL); retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1)); buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1); 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. */ retval = (char *) realloc(retval, strlen(retval) + 1); if (retval != NULL) 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); } /* __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) /* ?! */ { free(retval); retval = NULL; } /* if */ } /* if */ return((char *) retval); } /* __PHYSFS_platformGetUserName */ static char *copyEnvironmentVariable(const char *varname) { const char *envr = getenv(varname); char *retval = NULL; if (envr != NULL) { retval = malloc(strlen(envr) + 1); if (retval != NULL) strcpy(retval, envr); } /* if */ return(retval); } /* copyEnvironmentVariable */ char *__PHYSFS_platformGetUserDir(void) { char *home = copyEnvironmentVariable("HOME"); const char *homedrive = getenv("HOMEDRIVE"); const char *homepath = getenv("HOMEPATH"); if (home != NULL) return(home); if ((homedrive != NULL) && (homepath != NULL)) { char *retval = (char *) malloc(strlen(homedrive)+strlen(homepath)+2); if (retval != NULL) { strcpy(retval, homedrive); if ((homepath[0] != '\\') && (homedrive[strlen(homedrive)-1] != '\\')) { strcat(retval, "\\"); } /* if */ strcat(retval, homepath); return(retval); } /* if */ } /* if */ return(NULL); /* fall through to default rules. */ } /* __PHYSFS_platformGetUserDir */ int __PHYSFS_platformGetThreadID(void) { return((int) GetCurrentThreadId()); } /* __PHYSFS_platformGetThreadID */ /* ...make this Cygwin AND Visual C friendly... */ int __PHYSFS_platformStricmp(const char *x, const char *y) { 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); } /* __PHYSFS_platformStricmp */ int __PHYSFS_platformExists(const char *fname) { return(GetFileAttributes(fname) != 0xffffffff); } /* __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; LinkedStringList *l = NULL; LinkedStringList *prev = NULL; HANDLE dir; WIN32_FIND_DATA ent; dir = FindFirstFile(dirname, &ent); BAIL_IF_MACRO(dir == INVALID_HANDLE_VALUE, win32strerror(), NULL); for (; FindNextFile(dir, &ent) != 0; ) { if (strcmp(ent.cFileName, ".") == 0) continue; if (strcmp(ent.cFileName, "..") == 0) continue; l = (LinkedStringList *) malloc(sizeof (LinkedStringList)); if (l == NULL) break; l->str = (char *) malloc(strlen(ent.cFileName) + 1); if (l->str == NULL) { free(l); break; } /* if */ strcpy(l->str, ent.cFileName); if (retval == NULL) retval = l; else prev->next = l; prev = l; l->next = NULL; } /* while */ FindClose(dir); return(retval); } /* __PHYSFS_platformEnumerateFiles */ int __PHYSFS_platformFileLength(FILE *handle) { fpos_t curpos; int retval; fgetpos(handle, &curpos); fseek(handle, 0, SEEK_END); retval = ftell(handle); fsetpos(handle, &curpos); return(retval); } /* __PHYSFS_platformFileLength */ 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] == '\\')) { BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL); strcpy(retval, path); } /* if */ 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] = '\\'; strcpy(retval + 3, path + 2); } /* else */ } /* else */ } /* if */ else /* no drive letter specified. */ { if (path[0] == '\\') /* absolute path. */ { retval[0] = currentDir[0]; retval[1] = ':'; strcpy(retval + 2, path); } /* if */ else { strcpy(retval, currentDir); strcat(retval, path); } /* else */ } /* else */ free(currentDir); } /* else */ /* (whew.) Ok, now take out "." and ".." path entries... */ p = retval; while ( (p = strstr(p, "\\.")) != NULL) { /* it's a "." entry that doesn't end the string. */ if (p[2] == '\\') memmove(p + 1, p + 3, strlen(p + 3) + 1); /* it's a "." entry that ends the string. */ else if (p[2] == '\0') p[0] = '\0'; /* it's a ".." entry. */ else if (p[2] == '.') { char *prevEntry = p - 1; while ((prevEntry != retval) && (*prevEntry != '\\')) prevEntry--; if (prevEntry == retval) /* make it look like a "." entry. */ memmove(p + 1, p + 2, strlen(p + 2) + 1); else { if (p[3] != '\0') /* doesn't end string. */ *prevEntry = '\0'; else /* ends string. */ memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1); p = prevEntry; } /* else */ } /* else if */ else { p++; /* look past current char. */ } /* else */ } /* while */ /* shrink the retval's memory block if possible... */ p = (char *) realloc(retval, strlen(retval) + 1); if (p != NULL) retval = p; return(retval); } /* __PHYSFS_platformRealPath */ int __PHYSFS_platformMkDir(const char *path) { DWORD rc = CreateDirectory(path, NULL); BAIL_IF_MACRO(rc == 0, win32strerror(), 0); return(1); } /* __PHYSFS_platformMkDir */ /* end of win32.c ... */