From 0b91499ee0c69e21f10ae1fa1fbf37f784a12230 Mon Sep 17 00:00:00 2001 From: hendricks266 Date: Mon, 8 Dec 2014 04:31:57 +0000 Subject: [PATCH] Mac OS X: Add detection of the Steam and GOG releases of Duke 3D and the Steam release of NAM. DONT_BUILD. git-svn-id: https://svn.eduke32.com/eduke32@4801 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/build/include/compat.h | 3 +- polymer/eduke32/build/include/osxbits.h | 5 + polymer/eduke32/build/src/compat.c | 100 +++++++---- polymer/eduke32/build/src/osxbits.m | 89 +++++++++ polymer/eduke32/source/common.c | 230 ++++++++++++++++++++++-- 5 files changed, 376 insertions(+), 51 deletions(-) diff --git a/polymer/eduke32/build/include/compat.h b/polymer/eduke32/build/include/compat.h index 32a043663..33e323f8d 100644 --- a/polymer/eduke32/build/include/compat.h +++ b/polymer/eduke32/build/include/compat.h @@ -761,7 +761,8 @@ char *Bgetenv(const char *name); #endif char *Bgethomedir(void); -char *Bgetsupportdir(int32_t global); +char *Bgetsupportdir(void); +char *Bgetappdir(void); uint32_t Bgetsysmemsize(void); int32_t Bcorrectfilename(char *filename, int32_t removefn); int32_t Bcanonicalisefilename(char *filename, int32_t removefn); diff --git a/polymer/eduke32/build/include/osxbits.h b/polymer/eduke32/build/include/osxbits.h index 3790b7824..c4a25b5f9 100644 --- a/polymer/eduke32/build/include/osxbits.h +++ b/polymer/eduke32/build/include/osxbits.h @@ -9,6 +9,11 @@ extern "C" { int32_t osx_msgbox(const char *name, const char *msg); int32_t osx_ynbox(const char *name, const char *msg); +char *osx_gethomedir(void); +char *osx_getsupportdir(void); +char *osx_getappdir(void); +char *osx_getapplicationsdir(void); + #ifdef __cplusplus } #endif diff --git a/polymer/eduke32/build/src/compat.c b/polymer/eduke32/build/src/compat.c index 63fe08e4c..4a9c61c6c 100644 --- a/polymer/eduke32/build/src/compat.c +++ b/polymer/eduke32/build/src/compat.c @@ -17,6 +17,8 @@ #ifdef _WIN32 # include # include +#elif __APPLE__ +# include "osxbits.h" #endif #if defined(_MSC_VER) @@ -25,6 +27,13 @@ # include #endif +#if defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include // for dirname() +#endif +#if defined(__FreeBSD__) +# include // for sysctl() to get path to executable +#endif + #include "baselayer.h" ////////// PANICKING ALLOCATION FUNCTIONS ////////// @@ -393,22 +402,8 @@ char *Bgethomedir(void) if (loaded) FreeLibrary(hShell32); return NULL; -#elif defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3 - FSRef ref; - CFStringRef str; - CFURLRef base; - char *s; - - if (FSFindFolder(kUserDomain, kVolumeRootFolderType, kDontCreateFolder, &ref) < 0) return NULL; - base = CFURLCreateFromFSRef(NULL, &ref); - if (!base) return NULL; - str = CFURLCopyFileSystemPath(base, kCFURLPOSIXPathStyle); - CFRelease(base); - if (!str) return NULL; - s = (char *)CFStringGetCStringPtr(str,CFStringGetSystemEncoding()); - if (s) s = Bstrdup(s); - CFRelease(str); - return s; +#elif defined __APPLE__ + return osx_gethomedir(); #elif defined(GEKKO) // return current drive's name char *drv, cwd[BMAX_PATH] = {0}; @@ -424,32 +419,63 @@ char *Bgethomedir(void) #endif } -char *Bgetsupportdir(int32_t global) +char *Bgetsupportdir(void) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3_ - UNREFERENCED_PARAMETER(global); - return Bgethomedir(); +#if defined __APPLE__ + return osx_getsupportdir(); #else - FSRef ref; - CFStringRef str; - CFURLRef base; - char *s; - - if (FSFindFolder(global ? kLocalDomain : kUserDomain, - kApplicationSupportFolderType, - kDontCreateFolder, &ref) < 0) return NULL; - base = CFURLCreateFromFSRef(NULL, &ref); - if (!base) return NULL; - str = CFURLCopyFileSystemPath(base, kCFURLPOSIXPathStyle); - CFRelease(base); - if (!str) return NULL; - s = (char *)CFStringGetCStringPtr(str,CFStringGetSystemEncoding()); - if (s) s = Bstrdup(s); - CFRelease(str); - return s; + return Bgethomedir(); #endif } +char *Bgetappdir(void) +{ + char *dir = NULL; + +#ifdef _WIN32 + TCHAR appdir[MAX_PATH]; + + if (GetModuleFileName(NULL, appdir, MAX_PATH) > 0) { + // trim off the filename + char *slash = strrchr(appdir, '\\'); + if (slash) slash[0] = 0; + dir = strdup(appdir); + } + +#elif defined __APPLE__ + dir = osx_getappdir(); +#elif defined(__linux) || defined(__NetBSD__) || defined(__OpenBSD__) + char buf[PATH_MAX] = {0}; + char buf2[PATH_MAX] = {0}; +# ifdef __linux + snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid()); +# else // the BSDs.. except for FreeBSD which has a sysctl + snprintf(buf, sizeof(buf), "/proc/%d/file", getpid()); +# endif + int len = readlink(buf, buf2, sizeof(buf2)); + if (len != -1) { + // remove executable name with dirname(3) + // on Linux, dirname() will modify buf2 (cutting off executable name) and return it + // on FreeBSD it seems to use some internal buffer instead.. anyway, just strdup() + dir = strdup(dirname(buf2)); + } +#elif defined(__FreeBSD__) + // the sysctl should also work when /proc/ is not mounted (which seems to + // be common on FreeBSD), so use it.. + char buf[PATH_MAX] = {0}; + int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t len = sizeof(buf)-1; + int ret = sysctl(name, sizeof(name)/sizeof(name[0]), buf, &len, NULL, 0); + if(ret == 0 && buf[0] != '\0') { + // again, remove executable name with dirname() + // on FreeBSD dirname() seems to use some internal buffer + dir = strdup(dirname(buf)); + } +#endif + + return dir; +} + int32_t Bcorrectfilename(char *filename, int32_t removefn) { char *fn; diff --git a/polymer/eduke32/build/src/osxbits.m b/polymer/eduke32/build/src/osxbits.m index 7d3aa2b9f..a138b3a89 100644 --- a/polymer/eduke32/build/src/osxbits.m +++ b/polymer/eduke32/build/src/osxbits.m @@ -58,3 +58,92 @@ int osx_ynbox(const char *name, const char *msg) return r; } + +char *osx_gethomedir(void) +{ + NSString *path = NSHomeDirectory(); + const char *Cpath = [path UTF8String]; + char *returnpath = NULL; + + if (Cpath) + returnpath = Bstrdup(Cpath); + + [path release]; + + return returnpath; +} + +char *osx_getsupportdir(void) +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + char *returnpath = NULL; + + if ([paths count] > 0) + { + const char *Cpath = [[paths objectAtIndex:0] UTF8String]; + + if (Cpath) + returnpath = Bstrdup(Cpath); + } + + [paths release]; + + return returnpath; +} + +char *osx_getappdir(void) +{ + CFBundleRef mainBundle; + CFURLRef resUrl, fullUrl; + CFStringRef str; + const char *s; + char *dir = NULL; + + mainBundle = CFBundleGetMainBundle(); + if (!mainBundle) { + return NULL; + } + + resUrl = CFBundleCopyResourcesDirectoryURL(mainBundle); + CFRelease(mainBundle); + if (!resUrl) { + return NULL; + } + fullUrl = CFURLCopyAbsoluteURL(resUrl); + if (fullUrl) { + CFRelease(resUrl); + resUrl = fullUrl; + } + + str = CFURLCopyFileSystemPath(resUrl, kCFURLPOSIXPathStyle); + CFRelease(resUrl); + if (!str) { + return NULL; + } + + s = CFStringGetCStringPtr(str, CFStringGetSystemEncoding()); + if (s) { + dir = strdup(s); + } + CFRelease(str); + + return dir; +} + +char *osx_getapplicationsdir(void) +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSLocalDomainMask, YES); + char *returnpath = NULL; + + if ([paths count] > 0) + { + const char *Cpath = [[paths objectAtIndex:0] UTF8String]; + + if (Cpath) + returnpath = Bstrdup(Cpath); + } + + [paths release]; + + return returnpath; +} diff --git a/polymer/eduke32/source/common.c b/polymer/eduke32/source/common.c index a9d8e9252..27e2797df 100644 --- a/polymer/eduke32/source/common.c +++ b/polymer/eduke32/source/common.c @@ -15,6 +15,8 @@ # ifndef KEY_WOW64_32KEY # define KEY_WOW64_32KEY 0x0200 # endif +#elif defined __APPLE__ +# include "osxbits.h" #endif #include "common.h" @@ -292,16 +294,14 @@ void G_ExtInit(void) { char cwd[BMAX_PATH]; - if (getcwd(cwd,BMAX_PATH)) - { -#if defined(__APPLE__) - /* Dirty hack on OS X to also look for gamedata inside the application bundle - rhoenie 08/08 */ - char seekinappcontainer[BMAX_PATH]; - Bsnprintf(seekinappcontainer,sizeof(seekinappcontainer),"%s/EDuke32.app/", cwd); - addsearchpath(seekinappcontainer); +#ifdef __APPLE__ + char *appdir = Bgetappdir(); + addsearchpath(appdir); + Bfree(appdir); #endif + + if (getcwd(cwd,BMAX_PATH) && Bstrcmp(cwd,"/") != 0) addsearchpath(cwd); - } if (CommandPaths) { @@ -337,8 +337,6 @@ void G_ExtInit(void) Bsnprintf(cwd,sizeof(cwd),"%s/" #if defined(_WIN32) "EDuke32 Settings" -#elif defined(__APPLE__) - "Library/Application Support/EDuke32" #elif defined(GEKKO) "apps/eduke32" #else @@ -605,6 +603,195 @@ static void G_LoadAddon(void) } } +#ifdef __APPLE__ +static void G_AddSteamPathsApple(const char *basepath) +{ + char buf[BMAX_PATH]; + + Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot", basepath); + addsearchpath(buf); + + Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/dc", basepath); + addsearchpath(buf); + + Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/nw", basepath); + addsearchpath(buf); + + Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/vacation", basepath); + addsearchpath(buf); + + Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Nam/Nam.app/Contents/Resources/Nam.boxer/C.harddisk/NAM", basepath); + addsearchpath(buf); +} + +// A bare-bones "parser" for Valve's KeyValues VDF format. +// There is no guarantee this will function properly with ill-formed files. +static void KeyValues_SkipWhitespace(char **vdfbuf, char * const vdfbufend) +{ + while (((*vdfbuf)[0] == ' ' || (*vdfbuf)[0] == '\n' || (*vdfbuf)[0] == '\r' || (*vdfbuf)[0] == '\t' || (*vdfbuf)[0] == '\0') && *vdfbuf < vdfbufend) + (*vdfbuf)++; + + // comments + if ((*vdfbuf) + 2 < vdfbufend && (*vdfbuf)[0] == '/' && (*vdfbuf)[1] == '/') + { + while ((*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && *vdfbuf < vdfbufend) + (*vdfbuf)++; + + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); + } +} +static void KeyValues_SkipToEndOfQuotedToken(char **vdfbuf, char * const vdfbufend) +{ + (*vdfbuf)++; + while ((*vdfbuf)[0] != '\"' && (*vdfbuf)[-1] != '\\' && *vdfbuf < vdfbufend) + (*vdfbuf)++; +} +static void KeyValues_SkipToEndOfUnquotedToken(char **vdfbuf, char * const vdfbufend) +{ + while ((*vdfbuf)[0] != ' ' && (*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && (*vdfbuf)[0] != '\t' && (*vdfbuf)[0] != '\0' && *vdfbuf < vdfbufend) + (*vdfbuf)++; +} +static void KeyValues_SkipNextWhatever(char **vdfbuf, char * const vdfbufend) +{ + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); + + if (*vdfbuf == vdfbufend) + return; + + if ((*vdfbuf)[0] == '{') + { + (*vdfbuf)++; + do + { + KeyValues_SkipNextWhatever(vdfbuf, vdfbufend); + } + while ((*vdfbuf)[0] != '}'); + (*vdfbuf)++; + } + else if ((*vdfbuf)[0] == '\"') + KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend); + else if ((*vdfbuf)[0] != '}') + KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend); + + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); +} +static char* KeyValues_NormalizeToken(char **vdfbuf, char * const vdfbufend) +{ + char *token = *vdfbuf; + + if ((*vdfbuf)[0] == '\"' && *vdfbuf < vdfbufend) + { + token++; + + KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend); + (*vdfbuf)[0] = '\0'; + + // account for escape sequences + char *writeseeker = token, *readseeker = token; + while (readseeker <= *vdfbuf) + { + if (readseeker[0] == '\\') + readseeker++; + + writeseeker[0] = readseeker[0]; + + writeseeker++; + readseeker++; + } + + return token; + } + + KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend); + (*vdfbuf)[0] = '\0'; + + return token; +} +static void KeyValues_FindKey(char **vdfbuf, char * const vdfbufend, const char *token) +{ + char *ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend); + if (token != NULL) // pass in NULL to find the next key instead of a specific one + while (Bstrcmp(ParentKey, token) != 0 && *vdfbuf < vdfbufend) + { + KeyValues_SkipNextWhatever(vdfbuf, vdfbufend); + ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend); + } + + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); +} +static int32_t KeyValues_FindParentKey(char **vdfbuf, char * const vdfbufend, const char *token) +{ + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); + + // end of scope + if ((*vdfbuf)[0] == '}') + return 0; + + KeyValues_FindKey(vdfbuf, vdfbufend, token); + + // ignore the wrong type + while ((*vdfbuf)[0] != '{' && *vdfbuf < vdfbufend) + { + KeyValues_SkipNextWhatever(vdfbuf, vdfbufend); + KeyValues_FindKey(vdfbuf, vdfbufend, token); + } + + if (*vdfbuf == vdfbufend) + return 0; + + return 1; +} +static char* KeyValues_FindKeyValue(char **vdfbuf, char * const vdfbufend, const char *token) +{ + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); + + // end of scope + if ((*vdfbuf)[0] == '}') + return NULL; + + KeyValues_FindKey(vdfbuf, vdfbufend, token); + + // ignore the wrong type + while ((*vdfbuf)[0] == '{' && *vdfbuf < vdfbufend) + { + KeyValues_SkipNextWhatever(vdfbuf, vdfbufend); + KeyValues_FindKey(vdfbuf, vdfbufend, token); + } + + KeyValues_SkipWhitespace(vdfbuf, vdfbufend); + + if (*vdfbuf == vdfbufend) + return NULL; + + return KeyValues_NormalizeToken(vdfbuf, vdfbufend); +} + +static void G_ParseSteamKeyValuesForPaths(const char *vdf) +{ + int32_t fd = Bopen(vdf, BO_RDONLY); + int32_t size = Bfilelength(fd); + char *vdfbufstart, *vdfbuf, *vdfbufend; + + if (size <= 0) + return; + + vdfbufstart = vdfbuf = (char*)Bmalloc(size); + size = (int32_t)Bread(fd, vdfbuf, size); + Bclose(fd); + vdfbufend = vdfbuf + size; + + if (KeyValues_FindParentKey(&vdfbuf, vdfbufend, "LibraryFolders")) + { + char *result; + vdfbuf++; + while ((result = KeyValues_FindKeyValue(&vdfbuf, vdfbufend, NULL)) != NULL) + G_AddSteamPathsApple(result); + } + + Bfree(vdfbufstart); +} +#endif + void G_AddSearchPaths(void) { #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) @@ -613,10 +800,27 @@ void G_AddSearchPaths(void) addsearchpath("/usr/share/games/eduke32"); addsearchpath("/usr/local/share/games/eduke32"); #elif defined(__APPLE__) - addsearchpath("/Library/Application Support/JFDuke3D"); - addsearchpath("/Library/Application Support/EDuke32"); + char buf[BMAX_PATH]; + char *applications = osx_getapplicationsdir(); + char *support = Bgetsupportdir(); + + Bsnprintf(buf, sizeof(buf), "%s/Steam", support); + G_AddSteamPathsApple(buf); + + Bsnprintf(buf, sizeof(buf), "%s/Steam/steamapps/libraryfolders.vdf", support); + G_ParseSteamKeyValuesForPaths(buf); + + Bsnprintf(buf, sizeof(buf), "%s/Duke Nukem 3D.app/Contents/Resources/Duke Nukem 3D.boxer/C.harddisk", applications); + addsearchpath(buf); + + Bsnprintf(buf, sizeof(buf), "%s/JFDuke3D", support); + addsearchpath(buf); + Bsnprintf(buf, sizeof(buf), "%s/EDuke32", support); + addsearchpath(buf); + + Bfree(applications); + Bfree(support); #elif defined (_WIN32) - // detect Steam and GOG versions of Duke3D char buf[BMAX_PATH]; const char* instpath;