From 4b9e89744506f81ac4a0f48e5233fa8f984158cd Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Tue, 10 Dec 2024 18:56:46 +0000 Subject: [PATCH] OSX: Fix base/pref/config paths on Tiger --- .editorconfig | 3 + Packaging/apple/Info.plist | 2 +- Source/CMakeLists.txt | 4 + Source/init.cpp | 2 +- Source/platform/macos_sdl1/SDL_filesystem.m | 138 ++++++++++++++++++++ Source/utils/paths.cpp | 21 +++ Source/utils/sdl2_to_1_2_backports.cpp | 11 +- Source/utils/sdl2_to_1_2_backports.h | 4 +- 8 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 Source/platform/macos_sdl1/SDL_filesystem.m diff --git a/.editorconfig b/.editorconfig index b0e4e46f18f..f53afb140e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -56,6 +56,9 @@ end_of_line = lf [*.txt] end_of_line = crlf +[*.plist] +end_of_line = lf + [AppRun] end_of_line = lf diff --git a/Packaging/apple/Info.plist b/Packaging/apple/Info.plist index 91e32a9810c..52754cc115c 100644 --- a/Packaging/apple/Info.plist +++ b/Packaging/apple/Info.plist @@ -39,7 +39,7 @@ NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} SDL_FILESYSTEM_BASE_DIR_TYPE - parent + resource NSSupportsAutomaticGraphicsSwitching UIApplicationSupportsIndirectInputEvents diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index d9bc5b8e666..78e02279cd2 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -184,6 +184,10 @@ endif() if(USE_SDL1) list(APPEND libdevilutionx_SRCS utils/sdl2_to_1_2_backports.cpp) + if(APPLE) + enable_language(OBJC) + list(APPEND libdevilutionx_SRCS platform/macos_sdl1/SDL_filesystem.m) + endif() endif() if(NOT DISABLE_DEMOMODE) diff --git a/Source/init.cpp b/Source/init.cpp index eb214c8f297..4e6fab1f5c1 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -128,7 +128,7 @@ std::vector GetMPQSearchPaths() if (paths[0] == paths[1] || (paths.size() == 3 && (paths[0] == paths[2] || paths[1] == paths[2]))) paths.pop_back(); -#if defined(__unix__) && !defined(__ANDROID__) +#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__) // `XDG_DATA_HOME` is usually the root path of `paths::PrefPath()`, so we only // add `XDG_DATA_DIRS`. const char *xdgDataDirs = std::getenv("XDG_DATA_DIRS"); diff --git a/Source/platform/macos_sdl1/SDL_filesystem.m b/Source/platform/macos_sdl1/SDL_filesystem.m new file mode 100644 index 00000000000..1bf4ed6e93d --- /dev/null +++ b/Source/platform/macos_sdl1/SDL_filesystem.m @@ -0,0 +1,138 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + An altered version based on: + https://github.com/libsdl-org/SDL/blob/3c142abcb2b0b0ad7e08b096ea8d9a1a1e1af1ef/src/filesystem/cocoa/SDL_sysfilesystem.m + + Modifications: + 1. Changes to compile with gcc (@autoreleasepool -> NSAutoreleasePool) + 2. Targets SDL-1.2 rather than SDL2 (SDL_InvalidParamError -> SDL_SetError) +*/ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* System dependent filesystem routines */ + +#include +#include +#include + +#include + +char *SDL_GetBasePath(void) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSBundle *bundle = [NSBundle mainBundle]; + const char *baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String]; + const char *base = NULL; + char *retval = NULL; + + if (baseType == NULL) { + baseType = "resource"; + } + if (SDL_strcasecmp(baseType, "bundle") == 0) { + base = [[bundle bundlePath] fileSystemRepresentation]; + } else if (SDL_strcasecmp(baseType, "parent") == 0) { + base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation]; + } else { + /* this returns the exedir for non-bundled and the resourceDir for bundled apps */ + base = [[bundle resourcePath] fileSystemRepresentation]; + } + + if (base) { + const size_t len = SDL_strlen(base) + 2; + retval = (char *)SDL_malloc(len); + if (retval == NULL) { + SDL_OutOfMemory(); + } else { + SDL_snprintf(retval, len, "%s/", base); + } + } + + [pool drain]; + return retval; +} + +char *SDL_GetPrefPath(const char *org, const char *app) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + char *retval = NULL; + NSArray *array; + + if (!app) { + SDL_SetError("SDL_GetPrefPath: app argument cannot be null"); + return NULL; + } + if (!org) { + org = ""; + } + +#if !TARGET_OS_TV + array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); +#else + /* tvOS does not have persistent local storage! + * The only place on-device where we can store data is + * a cache directory that the OS can empty at any time. + * + * It's therefore very likely that save data will be erased + * between sessions. If you want your app's save data to + * actually stick around, you'll need to use iCloud storage. + */ + { + static SDL_bool shown = SDL_FALSE; + if (!shown) { + shown = SDL_TRUE; + SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "tvOS does not have persistent local storage! Use iCloud storage if you want your data to persist between sessions.\n"); + } + } + + array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); +#endif /* !TARGET_OS_TV */ + + if ([array count] > 0) { /* we only want the first item in the list. */ + NSString *str = [array objectAtIndex:0]; + const char *base = [str fileSystemRepresentation]; + if (base) { + const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; + retval = (char *)SDL_malloc(len); + if (retval == NULL) { + SDL_OutOfMemory(); + } else { + char *ptr; + if (*org) { + SDL_snprintf(retval, len, "%s/%s/%s/", base, org, app); + } else { + SDL_snprintf(retval, len, "%s/%s/", base, app); + } + for (ptr = retval + 1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + mkdir(retval, 0700); + *ptr = '/'; + } + } + mkdir(retval, 0700); + } + } + } + + [pool drain]; + return retval; +} + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/Source/utils/paths.cpp b/Source/utils/paths.cpp index e80343635ab..9635ac3e382 100644 --- a/Source/utils/paths.cpp +++ b/Source/utils/paths.cpp @@ -1,5 +1,9 @@ #include "utils/paths.h" +#include +#include +#include + #include #include "appfat.h" @@ -120,6 +124,23 @@ const std::string &AssetsPath() assetsPath.emplace("D:\\assets\\"); #elif defined(__3DS__) || defined(__SWITCH__) assetsPath.emplace("romfs:/"); +#elif defined(__APPLE__) && defined(USE_SDL1) + // In `Info.plist` we have + // + // SDL_FILESYSTEM_BASE_DIR_TYPE + // resource + // + // This means `SDL_GetBasePath()` returns exedir for non-bundled + // and the `app_dir.app/Resources/` for bundles. + // + // Our built-in resources are directly in the `devilutionx.app/Resources` directory + // and are normally looked up via a relative lookup in `FindAsset`. + // In SDL2, this is implemented by calling `SDL_OpenFPFromBundleOrFallback` + // from `SDL_RWFromFile` but SDL1 doesn't do it, so we set the directory explicitly. + // + // Note that SDL3 reverts to SDL1 behaviour! + // https://github.com/libsdl-org/SDL/blob/962268ca21ed10b9cee31198c22681099293f20a/docs/README-migration.md?plain=1#L1623 + assetsPath.emplace(FromSDL(SDL_GetBasePath())); #else assetsPath.emplace(FromSDL(SDL_GetBasePath()) + ("assets" DIRECTORY_SEPARATOR_STR)); #endif diff --git a/Source/utils/sdl2_to_1_2_backports.cpp b/Source/utils/sdl2_to_1_2_backports.cpp index 7e4879de240..4d42a7e24d0 100644 --- a/Source/utils/sdl2_to_1_2_backports.cpp +++ b/Source/utils/sdl2_to_1_2_backports.cpp @@ -538,7 +538,7 @@ int WIN_SetError(const char *prefix) } // namespace -char *SDL_GetBasePath(void) +extern "C" char *SDL_GetBasePath(void) { // From sdl2-2.0.9/src/filesystem/windows/SDL_sysfilesystem.c @@ -605,7 +605,7 @@ char *SDL_GetBasePath(void) return retval; } -char *SDL_GetPrefPath(const char *org, const char *app) +extern "C" char *SDL_GetPrefPath(const char *org, const char *app) { // From sdl2-2.0.9/src/filesystem/windows/SDL_sysfilesystem.c @@ -693,7 +693,8 @@ char *SDL_GetPrefPath(const char *org, const char *app) return retval; } -#else +// For Apple, definitions are in Source/platform/macos_sdl1/SDL_filesystem.m +#elif !defined(__APPLE__) namespace { #if !defined(__QNXNTO__) && !defined(__amigaos__) @@ -730,7 +731,7 @@ char *readSymLink(const char *path) #endif } // namespace -char *SDL_GetBasePath() +extern "C" char *SDL_GetBasePath() { // From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c @@ -831,7 +832,7 @@ char *SDL_GetBasePath() return retval; } -char *SDL_GetPrefPath(const char *org, const char *app) +extern "C" char *SDL_GetPrefPath(const char *org, const char *app) { // From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c /* diff --git a/Source/utils/sdl2_to_1_2_backports.h b/Source/utils/sdl2_to_1_2_backports.h index 93a5ac94960..9653b3ae8e1 100644 --- a/Source/utils/sdl2_to_1_2_backports.h +++ b/Source/utils/sdl2_to_1_2_backports.h @@ -319,5 +319,5 @@ int SDL_BlitScaled(SDL_Surface *src, SDL_Rect *srcrect, Sint64 SDL_RWsize(SDL_RWops *context); -char *SDL_GetBasePath(); -char *SDL_GetPrefPath(const char *org, const char *app); +extern "C" char *SDL_GetBasePath(); +extern "C" char *SDL_GetPrefPath(const char *org, const char *app);