diff --git a/source/lib/sysdep/os/osx/osx.cpp b/source/lib/sysdep/os/osx/osx.cpp index b3fd13a7c7..9161a4da2d 100644 --- a/source/lib/sysdep/os/osx/osx.cpp +++ b/source/lib/sysdep/os/osx/osx.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2012 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -26,6 +26,7 @@ #include "lib/sysdep/sysdep.h" #include "lib/sysdep/gfx.h" +#include "osx_bundle.h" #include #include @@ -109,25 +110,26 @@ Status GetMonitorSize(int* xres, int* yres, int* bpp, int* freq) OsPath sys_ExecutablePathname() { - static char name[PATH_MAX]; - static bool init = false; - if ( !init ) + OsPath path; + + // On OS X we might be a bundle, return the bundle path as the executable name, + // i.e. /path/to/0ad.app instead of /path/to/0ad.app/Contents/MacOS/pyrogenesis + if (osx_IsAppBundleValid()) + { + path = osx_GetBundlePath(); + ENSURE(!path.empty()); + } + else { - init = true; char temp[PATH_MAX]; u32 size = PATH_MAX; - if (_NSGetExecutablePath( temp, &size )) - return OsPath(); - realpath(temp, name); + if (_NSGetExecutablePath(temp, &size) == 0) + { + char name[PATH_MAX]; + realpath(temp, name); + path = OsPath(name); + } } - - // On OS X, we might be in a bundle. In this case set its name as our name. - char* app = strstr(name, ".app"); - if (app) { - // Remove everything after the .app - *(app + strlen(".app")) = '\0'; - debug_printf(L"app bundle name: %hs\n", name); - } - - return name; + + return path; } diff --git a/source/lib/sysdep/os/osx/osx_bundle.h b/source/lib/sysdep/os/osx/osx_bundle.h new file mode 100644 index 0000000000..bac6b951e4 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_bundle.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2012 Wildfire Games + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef OSX_BUNDLE_H +#define OSX_BUNDLE_H + +/** + * @file + * C++ interface to Cocoa implementation for getting bundle information + */ + +/** + * Check if app is running in a valid bundle + * + * @return true if valid bundle reference was found matching identifier + * property "com.wildfiregames.0ad" + */ +bool osx_IsAppBundleValid(); + +/** + * Get the system path to the bundle itself + * + * @return string containing POSIX-style path in UTF-8 encoding, + * else empty string if an error occurred. + */ +std::string osx_GetBundlePath(); + +/** + * Get the system path to the bundle's Resources directory + * + * @return string containing POSIX-style path in UTF-8 encoding, + * else empty string if an error occurred. + */ +std::string osx_GetBundleResourcesPath(); + +/** + * Get the system path to the bundle's Frameworks directory + * + * @return string containing POSIX-style path in UTF-8 encoding, + * else empty string if an error occurred. + */ +std::string osx_GetBundleFrameworksPath(); + +#endif // OSX_BUNDLE_H diff --git a/source/lib/sysdep/os/osx/osx_bundle.mm b/source/lib/sysdep/os/osx/osx_bundle.mm new file mode 100644 index 0000000000..876f601113 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_bundle.mm @@ -0,0 +1,116 @@ +/* Copyright (c) 2012 Wildfire Games + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import + +#import "osx_bundle.h" + +#define STRINGIZE2(id) # id +#define STRINGIZE(id) STRINGIZE2(id) + +// Pass the bundle identifier string as a build option +#ifdef BUNDLE_IDENTIFIER +static const char* BUNDLE_ID_STR = STRINGIZE(BUNDLE_IDENTIFIER); +#else +static const char* BUNDLE_ID_STR = ""; +#endif + + +bool osx_IsAppBundleValid() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // Check for the existence of bundle with correct identifier property + // (can't just use mainBundle because that can return a bundle reference + // even for a loose binary!) + NSBundle *bundle = [NSBundle bundleWithIdentifier: [NSString stringWithUTF8String: BUNDLE_ID_STR]]; + + [pool drain]; + return bundle != nil; +} + +std::string osx_GetBundlePath() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + std::string path; + + NSBundle *bundle = [NSBundle bundleWithIdentifier: [NSString stringWithUTF8String: BUNDLE_ID_STR]]; + if (bundle != nil) + { + // Retrieve NSURL and convert to POSIX path, then get C-string + // encoded as UTF-8, and use it to construct std::string + // NSURL:path "If the receiver does not conform to RFC 1808, returns nil." + NSString *pathStr = [[bundle bundleURL] path]; + if (pathStr != nil) + { + path = std::string([pathStr UTF8String]); + } + } + + [pool drain]; + return path; +} + +std::string osx_GetBundleResourcesPath() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + std::string path; + + NSBundle *bundle = [NSBundle bundleWithIdentifier: [NSString stringWithUTF8String: BUNDLE_ID_STR]]; + if (bundle != nil) + { + // Retrieve NSURL and convert to POSIX path, then get C-string + // encoded as UTF-8, and use it to construct std::string + // NSURL:path "If the receiver does not conform to RFC 1808, returns nil." + NSString *pathStr = [[bundle resourceURL] path]; + if (pathStr != nil) + { + path = std::string([pathStr UTF8String]); + } + } + + [pool drain]; + return path; +} + +std::string osx_GetBundleFrameworksPath() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + std::string path; + + NSBundle *bundle = [NSBundle bundleWithIdentifier: [NSString stringWithUTF8String: BUNDLE_ID_STR]]; + if (bundle != nil) + { + // Retrieve NSURL and convert to POSIX path, then get C-string + // encoded as UTF-8, and use it to construct std::string + // NSURL:path "If the receiver does not conform to RFC 1808, returns nil." + NSString *pathStr = [[bundle privateFrameworksURL] path]; + if (pathStr != nil) + { + path = std::string([pathStr UTF8String]); + } + } + + [pool drain]; + return path; +} diff --git a/source/lib/sysdep/os/osx/osx_paths.h b/source/lib/sysdep/os/osx/osx_paths.h new file mode 100644 index 0000000000..067688a6a7 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_paths.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2012 Wildfire Games + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef OSX_PATHS_H +#define OSX_PATHS_H + +/** + * @file + * C++ interface to Cocoa implementation for retrieving standard OS X paths + */ + +/** + * Get the user's Application Support path (typically ~/Library/Application Support) + * + * @return string containing POSIX-style path in UTF-8 encoding, + * else empty string if an error occurred. + */ +std::string osx_GetAppSupportPath(); + +/** + * Get the user's Caches path (typically ~/Library/Caches) + * + * @return string containing POSIX-style path in UTF-8 encoding, + * else empty string if an error occurred. + */ +std::string osx_GetCachesPath(); + +#endif // OSX_PATHS_H diff --git a/source/lib/sysdep/os/osx/osx_paths.mm b/source/lib/sysdep/os/osx/osx_paths.mm new file mode 100644 index 0000000000..8ba78a58e1 --- /dev/null +++ b/source/lib/sysdep/os/osx/osx_paths.mm @@ -0,0 +1,58 @@ +/* Copyright (c) 2012 Wildfire Games + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import + +#import "osx_paths.h" + +// Helper function +static std::string getUserDirectoryPath(NSSearchPathDirectory directory) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + std::string result; + + // Returns array of NSURL objects which are preferred for file paths + NSArray* paths = [[NSFileManager defaultManager] URLsForDirectory:directory inDomains:NSUserDomainMask]; + if ([paths count] > 0) + { + // Retrieve first NSURL and convert to POSIX path, then get C-string + // encoded as UTF-8, and use it to construct std::string + // NSURL:path "If the receiver does not conform to RFC 1808, returns nil." + NSString* pathStr = [[paths objectAtIndex:0] path]; + if (pathStr != nil) + result = std::string([pathStr UTF8String]); + } + + [pool drain]; + return result; +} + +std::string osx_GetAppSupportPath() +{ + return getUserDirectoryPath(NSApplicationSupportDirectory); +} + +std::string osx_GetCachesPath() +{ + return getUserDirectoryPath(NSCachesDirectory); +} diff --git a/source/lib/sysdep/os/win/wutil.cpp b/source/lib/sysdep/os/win/wutil.cpp index 1b55d1c853..30004bba4d 100644 --- a/source/lib/sysdep/os/win/wutil.cpp +++ b/source/lib/sysdep/os/win/wutil.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2012 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -251,7 +251,9 @@ bool wutil_HasCommandLineArgument(const wchar_t* arg) // (NB: wutil_Init is called before static ctors => use placement new) static OsPath* systemPath; static OsPath* executablePath; -static OsPath* appdataPath; +static OsPath* localAppdataPath; +static OsPath* roamingAppdataPath; +static OsPath* personalPath; const OsPath& wutil_SystemPath() { @@ -263,11 +265,33 @@ const OsPath& wutil_ExecutablePath() return *executablePath; } -const OsPath& wutil_AppdataPath() +const OsPath& wutil_LocalAppdataPath() { - return *appdataPath; + return *localAppdataPath; } +const OsPath& wutil_RoamingAppdataPath() +{ + return *roamingAppdataPath; +} + +const OsPath& wutil_PersonalPath() +{ + return *personalPath; +} + +// Helper to avoid duplicating this setup +static OsPath* GetFolderPath(int csidl) +{ + HWND hwnd = 0; // ignored unless a dial-up connection is needed to access the folder + HANDLE token = 0; + wchar_t path[MAX_PATH]; // mandated by SHGetFolderPathW + const HRESULT ret = SHGetFolderPathW(hwnd, csidl, token, 0, path); + ENSURE(SUCCEEDED(ret)); + if(GetLastError() == ERROR_NO_TOKEN) // avoid polluting last error + SetLastError(0); + return new(wutil_Allocate(sizeof(OsPath))) OsPath(path); +} static void GetDirectories() { @@ -286,17 +310,14 @@ static void GetDirectories() // executable's directory executablePath = new(wutil_Allocate(sizeof(OsPath))) OsPath(sys_ExecutablePathname().Parent()); - // application data - { - HWND hwnd = 0; // ignored unless a dial-up connection is needed to access the folder - HANDLE token = 0; - wchar_t path[MAX_PATH]; // mandated by SHGetFolderPathW - const HRESULT ret = SHGetFolderPathW(hwnd, CSIDL_APPDATA, token, 0, path); - ENSURE(SUCCEEDED(ret)); - if(GetLastError() == ERROR_NO_TOKEN) // avoid polluting last error - SetLastError(0); - appdataPath = new(wutil_Allocate(sizeof(OsPath))) OsPath(path); - } + // roaming application data + roamingAppdataPath = GetFolderPath(CSIDL_APPDATA); + + // local application data + localAppdataPath = GetFolderPath(CSIDL_LOCAL_APPDATA); + + // my documents + personalPath = GetFolderPath(CSIDL_PERSONAL); } @@ -306,8 +327,12 @@ static void FreeDirectories() wutil_Free(systemPath); executablePath->~OsPath(); wutil_Free(executablePath); - appdataPath->~OsPath(); - wutil_Free(appdataPath); + localAppdataPath->~OsPath(); + wutil_Free(localAppdataPath); + roamingAppdataPath->~OsPath(); + wutil_Free(roamingAppdataPath); + personalPath->~OsPath(); + wutil_Free(personalPath); } diff --git a/source/lib/sysdep/os/win/wutil.h b/source/lib/sysdep/os/win/wutil.h index cf49e533af..0cedf219e8 100644 --- a/source/lib/sysdep/os/win/wutil.h +++ b/source/lib/sysdep/os/win/wutil.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2012 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -159,7 +159,9 @@ extern bool wutil_HasCommandLineArgument(const wchar_t* arg); extern const OsPath& wutil_SystemPath(); extern const OsPath& wutil_ExecutablePath(); -extern const OsPath& wutil_AppdataPath(); +extern const OsPath& wutil_LocalAppdataPath(); +extern const OsPath& wutil_RoamingAppdataPath(); +extern const OsPath& wutil_PersonalPath(); //----------------------------------------------------------------------------- diff --git a/source/ps/DllLoader.cpp b/source/ps/DllLoader.cpp index 1b52cace15..9d79dc4660 100644 --- a/source/ps/DllLoader.cpp +++ b/source/ps/DllLoader.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,6 +25,10 @@ #include "ps/CLogger.h" #include "ps/GameSetup/Config.h" +#if OS_MACOSX +# include "lib/sysdep/os/osx/osx_bundle.h" +#endif + static void* const HANDLE_UNAVAILABLE = (void*)-1; // directory to search for libraries (optionally set by --libdir at build-time, @@ -70,8 +74,23 @@ static CStr extensions[] = { static CStr GenerateFilename(const CStr& name, const CStr& suffix, const CStr& extension) { CStr n; + if (!g_Libdir.empty()) n = g_Libdir + "/"; + +#if OS_MACOSX + // On OS X, we might be in a bundle in which case the lib directory is ../Frameworks + // relative to the binary, so we use a helper function to get the system path + if (osx_IsAppBundleValid()) + { + CStr frameworksPath = osx_GetBundleFrameworksPath(); + if (!frameworksPath.empty()) + { + n = frameworksPath + "/"; + } + } +#endif + n += prefix + name + suffix + extension; return n; } diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 7bc37366e1..a58c82efd8 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -453,8 +453,8 @@ static void InitVfs(const CmdLineArgs& args) const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); - g_VFS->Mount(L"screenshots/", paths.Data()/"screenshots"/""); - g_VFS->Mount(L"saves/", paths.Data()/"saves"/"", VFS_MOUNT_WATCH); + g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/""); + g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"", VFS_MOUNT_WATCH); const OsPath readonlyConfig = paths.RData()/"config"/""; g_VFS->Mount(L"config/", readonlyConfig); if(readonlyConfig != paths.Config()) diff --git a/source/ps/GameSetup/Paths.cpp b/source/ps/GameSetup/Paths.cpp index 20e4cc01f6..1bad0f2129 100644 --- a/source/ps/GameSetup/Paths.cpp +++ b/source/ps/GameSetup/Paths.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,7 +22,10 @@ #include "lib/sysdep/sysdep.h" // sys_get_executable_name #include "lib/sysdep/filesystem.h" // wrealpath #if OS_WIN -# include "lib/sysdep/os/win/wutil.h" // wutil_AppdataPath +# include "lib/sysdep/os/win/wutil.h" // wutil_*Path +#elif OS_MACOSX +# include "lib/sysdep/os/osx/osx_paths.h" +# include "lib/sysdep/os/osx/osx_bundle.h" #endif #include "ps/CLogger.h" @@ -31,47 +34,120 @@ Paths::Paths(const CmdLineArgs& args) { m_root = Root(args.GetArg0()); -#ifdef INSTALLED_DATADIR - m_rdata = OsPath(STRINGIZE(INSTALLED_DATADIR))/""; -#else - m_rdata = m_root/"data"/""; -#endif + m_rdata = RootData(args.GetArg0()); const char* subdirectoryName = args.Has("writableRoot")? 0 : "0ad"; - // everything is a subdirectory of the root if(!subdirectoryName) { - m_data = m_rdata; - m_config = m_data/"config"/""; - m_cache = m_data/"cache"/""; - m_logs = m_root/"logs"/""; + // Note: if writableRoot option is passed to the game, then + // all the data is a subdirectory of the root + m_gameData = m_rdata; + m_userData = m_gameData; + m_config = m_gameData / "config"/""; + m_cache = m_gameData / "cache"/""; + m_logs = m_root / "logs"/""; } - else + else // OS-specific path handling { + #if OS_ANDROID + const OsPath appdata = OsPath("/sdcard/0ad/appdata"); - m_data = appdata/"data"/""; + + // We don't make the game vs. user data distinction on Android + m_gameData = appdata/"data"/""; + m_userData = m_gameData; m_config = appdata/"config"/""; m_cache = appdata/"cache"/""; m_logs = appdata/"logs"/""; + #elif OS_WIN - const OsPath appdata = wutil_AppdataPath() / subdirectoryName/""; - m_data = appdata/"data"/""; - m_config = appdata/"config"/""; - m_cache = appdata/"cache"/""; - m_logs = appdata/"logs"/""; -#else + + /* For reasoning behind our Windows paths, see the discussion here: + * http://www.wildfiregames.com/forum/index.php?showtopic=14759 + * + * Summary: + * 1. Local appdata: for bulky unfriendly data like the cache, + * which can be recreated if deleted; doesn't need backing up. + * 2. Roaming appdata: for slightly less unfriendly data like config + * files that might theoretically be shared between different + * machines on a domain. + * 3. Personal / My Documents: for data explicitly created by the user, + * and which should be visible and easily accessed. We use a non- + * localized My Games subfolder for improved organization. + */ + + // %localappdata%/0ad/ + const OsPath localAppdata = wutil_LocalAppdataPath() / subdirectoryName/""; + // %appdata%/0ad/ + const OsPath roamingAppData = wutil_RoamingAppdataPath() / subdirectoryName/""; + // My Documents/My Games/0ad/ + const OsPath personalData = wutil_PersonalPath() / "My Games" / subdirectoryName/""; + + m_cache = localAppdata / "cache"/""; + m_gameData = roamingAppData / "data"/""; + m_userData = personalData/""; + m_config = roamingAppData / "config"/""; + m_logs = localAppdata / "logs"/""; + +#elif OS_MACOSX + + /* For reasoning behind our OS X paths, see the discussion here: + * http://www.wildfiregames.com/forum/index.php?showtopic=15511 + * + * Summary: + * 1. Application Support: most data associated with the app + * should be stored here, with few exceptions (e.g. temporary + * data, cached data, and managed media files). + * 2. Caches: used for non-critial app data that can be easily + * regenerated if this directory is deleted. It is not + * included in backups by default. + * + * Note: the paths returned by osx_Get*Path are not guaranteed to exist, + * but that's OK since we always create them on demand. + */ + + // We probably want to use the same subdirectoryName regardless + // of whether running a bundle or from SVN. Apple recommends using + // company name, bundle name or bundle identifier. + OsPath appSupportPath; // ~/Library/Application Support/0ad + OsPath cachePath; // ~/Library/Caches/0ad + + { + std::string path = osx_GetAppSupportPath(); + ENSURE(!path.empty()); + appSupportPath = OsPath(path) / subdirectoryName; + } + { + std::string path = osx_GetCachesPath(); + ENSURE(!path.empty()); + cachePath = OsPath(path) / subdirectoryName; + } + + // We don't make the game vs. user data distinction on OS X + m_gameData = appSupportPath / "data"/""; + m_userData = m_gameData; + m_cache = cachePath/""; + m_config = appSupportPath / "config"/""; + m_logs = appSupportPath / "logs"/""; + +#else // OS_UNIX + const char* envHome = getenv("HOME"); ENSURE(envHome); const OsPath home(envHome); const OsPath xdgData = XDG_Path("XDG_DATA_HOME", home, home/".local/share/") / subdirectoryName; const OsPath xdgConfig = XDG_Path("XDG_CONFIG_HOME", home, home/".config/" ) / subdirectoryName; const OsPath xdgCache = XDG_Path("XDG_CACHE_HOME", home, home/".cache/" ) / subdirectoryName; - m_data = xdgData/""; + + // We don't make the game vs. user data distinction on Unix + m_gameData = xdgData/""; + m_userData = m_gameData; m_cache = xdgCache/""; - m_config = xdgConfig/"config"/""; - m_logs = xdgConfig/"logs"/""; + m_config = xdgConfig / "config"/""; + m_logs = xdgConfig / "logs"/""; + #endif } } @@ -107,6 +183,31 @@ Paths::Paths(const CmdLineArgs& args) #endif } +/*static*/ OsPath Paths::RootData(const OsPath& argv0) +{ + +#ifdef INSTALLED_DATADIR + UNUSED2(argv0); + return OsPath(STRINGIZE(INSTALLED_DATADIR))/""; +#else + +# if OS_MACOSX + if (osx_IsAppBundleValid()) + { + debug_printf(L"Valid app bundle detected\n"); + + std::string resourcesPath = osx_GetBundleResourcesPath(); + // Ensure we have a valid resources path + ENSURE(!resourcesPath.empty()); + + return OsPath(resourcesPath)/"data"/""; + } +# endif // OS_MACOSX + + return Root(argv0)/"data"/""; + +#endif // INSTALLED_DATADIR +} /*static*/ OsPath Paths::XDG_Path(const char* envname, const OsPath& home, const OsPath& defaultPath) { diff --git a/source/ps/GameSetup/Paths.h b/source/ps/GameSetup/Paths.h index 1dcdd9ccf5..e21bb2703e 100644 --- a/source/ps/GameSetup/Paths.h +++ b/source/ps/GameSetup/Paths.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2009 Wildfire Games. +/* Copyright (C) 2012 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -21,36 +21,67 @@ #include "lib/os_path.h" #include "CmdLineArgs.h" +/** + * Wrapper class for OS paths used by the game + */ class Paths { public: Paths(const CmdLineArgs& args); + /** + * Returns the game's root directory + */ const OsPath& Root() const { return m_root; } + /** + * Returns directory for read-only data installed with the game + */ const OsPath& RData() const { return m_rdata; } - const OsPath& Data() const + /** + * Returns directory for game-managed data and mods + */ + const OsPath& GameData() const { - return m_data; + return m_gameData; } + /** + * Returns directory for user-created data + * Only things created in response to an explicit user action should go here. + * (note: only Windows currently treats this differently than GameData) + */ + const OsPath& UserData() const + { + return m_userData; + } + + /** + * Returns config file directory + */ const OsPath& Config() const { return m_config; } + /** + * Returns cache directory + */ const OsPath& Cache() const { return m_cache; } + /** + * Returns logs directory + */ const OsPath& Logs() const { return m_logs; @@ -58,6 +89,7 @@ public: private: static OsPath Root(const OsPath& argv0); + static OsPath RootData(const OsPath& argv0); static OsPath XDG_Path(const char* envname, const OsPath& home, const OsPath& defaultPath); // read-only directories, fixed paths relative to executable @@ -65,7 +97,8 @@ private: OsPath m_rdata; // writable directories - OsPath m_data; + OsPath m_gameData; + OsPath m_userData; OsPath m_config; OsPath m_cache; OsPath m_logs; // special-cased in single-root-folder installations diff --git a/source/tools/dist/0ad.nsi b/source/tools/dist/0ad.nsi index d82f34f18e..5b857b576d 100644 --- a/source/tools/dist/0ad.nsi +++ b/source/tools/dist/0ad.nsi @@ -179,8 +179,8 @@ Section "Uninstall" RMDir "$INSTDIR" - RMDir /r "$APPDATA\0ad\cache" - RMDir /r "$APPDATA\0ad\logs" + RMDir /r "$LOCALAPPDATA\0ad\cache" + RMDir /r "$LOCALAPPDATA\0ad\logs" ; leave the other directories (screenshots, config files, etc) !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder