diff --git a/CMakeLists.txt b/CMakeLists.txt index e87cbb84..a12b897a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ # cmake_minimum_required(VERSION 3.15) +option(PORTABLE_INSTALL "Portable Installation" ON) + project(RMG) find_package(Git REQUIRED) diff --git a/Source/RMG-Core/CMakeLists.txt b/Source/RMG-Core/CMakeLists.txt index 6e3d64b2..50861575 100644 --- a/Source/RMG-Core/CMakeLists.txt +++ b/Source/RMG-Core/CMakeLists.txt @@ -18,8 +18,9 @@ set(RMG_CORE_SOURCES Settings/Settings.cpp SpeedLimiter.cpp RomSettings.cpp - RomHeader.cpp + Directories.cpp Screenshot.cpp + RomHeader.cpp Emulation.cpp SaveState.cpp Callback.cpp @@ -42,6 +43,10 @@ else() ) endif() +if (PORTABLE_INSTALL) +add_definitions(-DPORTABLE_INSTALL) +endif(PORTABLE_INSTALL) + add_library(RMG-Core STATIC ${RMG_CORE_SOURCES}) if(UNIX) @@ -49,7 +54,7 @@ if(UNIX) endif(UNIX) if(WIN32) - target_link_libraries(RMG-Core wsock32 ws2_32) + target_link_libraries(RMG-Core wsock32 ws2_32) endif(WIN32) target_link_libraries(RMG-Core diff --git a/Source/RMG-Core/Core.cpp b/Source/RMG-Core/Core.cpp index 51610646..77e237e6 100644 --- a/Source/RMG-Core/Core.cpp +++ b/Source/RMG-Core/Core.cpp @@ -27,7 +27,7 @@ static osal_dynlib_lib_handle l_CoreLibHandle; std::string find_core_lib(void) { - for (const auto& entry : std::filesystem::recursive_directory_iterator("Core")) + for (const auto& entry : std::filesystem::recursive_directory_iterator(CoreGetCoreDirectory())) { std::string path = entry.path().string(); if (path.ends_with(OSAL_DYNLIB_LIB_EXT_STR)) @@ -44,19 +44,7 @@ bool config_override_user_dirs(void) std::string error; m64p_error ret; - std::string dataDir; - std::string cacheDir; - bool overrideUserDirs; - dataDir = CoreSettingsGetStringValue(SettingsID::Core_UserDataDirOverride); - cacheDir = CoreSettingsGetStringValue(SettingsID::Core_UserCacheDirOverride); - overrideUserDirs = CoreSettingsGetBoolValue(SettingsID::Core_OverrideUserDirs); - - if (!overrideUserDirs) - { - return true; - } - - ret = m64p::Config.OverrideUserPaths(dataDir.c_str(), cacheDir.c_str()); + ret = m64p::Config.OverrideUserPaths(CoreGetUserDataDirectory().c_str(), CoreGetUserCacheDirectory().c_str()); if (ret != M64ERR_SUCCESS) { error = "config_override_user_dirs m64p::Config.OverrideUserPaths() Failed: "; @@ -130,6 +118,12 @@ bool CoreInit(void) return false; } + ret = CoreCreateDirectories(); + if (!ret) + { + return false; + } + return true; } diff --git a/Source/RMG-Core/Core.hpp b/Source/RMG-Core/Core.hpp index f1dcca6b..16c7aa74 100644 --- a/Source/RMG-Core/Core.hpp +++ b/Source/RMG-Core/Core.hpp @@ -13,6 +13,7 @@ #include "Settings/Settings.hpp" #include "SpeedLimiter.hpp" #include "RomSettings.hpp" +#include "Directories.hpp" #include "Screenshot.hpp" #include "Emulation.hpp" #include "SaveState.hpp" diff --git a/Source/RMG-Core/Directories.cpp b/Source/RMG-Core/Directories.cpp new file mode 100644 index 00000000..5b7f5ca2 --- /dev/null +++ b/Source/RMG-Core/Directories.cpp @@ -0,0 +1,173 @@ +/* + * Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG + * Copyright (C) 2020 Rosalie Wanders + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "Directories.hpp" +#include "Error.hpp" + +#include +#include +#include + +// +// Local Functions +// + +static std::string get_var_directory(std::string var, std::string append, std::string fallbackVar, std::string fallbackAppend) +{ + std::string directory; + + const char* env = std::getenv(var.c_str()); + if (env == nullptr) + { + env = std::getenv(fallbackVar.c_str()); + if (env == nullptr) + { + std::cerr << "get_var_directory: fallbackVar: $" << fallbackVar << " cannot be nullptr!" << std::endl; + std::terminate(); + } + directory = env; + directory += fallbackAppend; + } + else + { + directory = env; + directory += append; + } + + return directory; +} + +// +// Exported Functions +// + +bool CoreCreateDirectories(void) +{ + std::string error; + + std::string directories[] = + { +#ifdef PORTABLE_INSTALL + CoreGetCoreDirectory(), + CoreGetPluginDirectory(), + CoreGetSharedDataDirectory(), +#endif // PORTABLE_INSTALL + CoreGetUserConfigDirectory(), + CoreGetUserDataDirectory(), + CoreGetUserCacheDirectory(), + }; + + for (const auto& dir : directories) + { + try + { + std::filesystem::create_directories(dir); + } + catch (...) + { + error = "CoreCreateDirectories Failed: cannot create the '"; + error += dir; + error += "' directory!"; + CoreSetError(error); + return false; + } + } + + return true; +} + +std::string CoreGetCoreDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Core"; +#else // Not Portable +#ifdef _WIN32 + directory = "Core"; +#else // Unix + directory = "/usr/local/lib/RMG/Core/"; +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} + +std::string CoreGetPluginDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Plugin"; +#else // Not Portable +#ifdef _WIN32 + directory = "Plugin"; +#else // Unix + directory = "/usr/local/lib/RMG/Plugin/"; +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} + +std::string CoreGetUserConfigDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Config"; +#else // Not Portable +#ifdef _WIN32 + directory = get_var_directory("APPDATA", "/RMG/Config", "HOME", "/RMG/Config"); +#else // Unix + directory = get_var_directory("XDG_CONFIG_HOME", "/RMG", "HOME", "/.config/RMG"); +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} + +std::string CoreGetUserDataDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Data"; +#else // Not Portable +#ifdef _WIN32 + directory = get_var_directory("APPDATA", "/RMG/Data", "HOME", "/RMG/Data"); +#else // Unix + directory = get_var_directory("XDG_DATA_HOME", "/RMG", "HOME", "/.local/share/RMG"); +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} + +std::string CoreGetUserCacheDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Cache"; +#else // Not Portable +#ifdef _WIN32 + directory = get_var_directory("APPDATA", "/RMG/Cache", "HOME", "/RMG/Cache"); +#else // Unix + directory = get_var_directory("XDG_CACHE_HOME", "/RMG", "HOME", "/.cache/RMG"); +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} + +std::string CoreGetSharedDataDirectory(void) +{ + std::string directory; +#ifdef PORTABLE_INSTALL + directory = "Data"; +#else // Not Portable +#ifdef _WIN32 + directory = "Data"; +#else // Unix + directory = "/usr/share/RMG/"; +#endif // _WIN32 +#endif // PORTABLE_INSTALL + return directory; +} diff --git a/Source/RMG-Core/Directories.hpp b/Source/RMG-Core/Directories.hpp new file mode 100644 index 00000000..934f5c2c --- /dev/null +++ b/Source/RMG-Core/Directories.hpp @@ -0,0 +1,45 @@ +/* + * Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG + * Copyright (C) 2020 Rosalie Wanders + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef DIRECTORIES_HPP +#define DIRECTORIES_HPP + +#include + +// tries to create the needed directories, +// returns false when failed +bool CoreCreateDirectories(void); + +// returns the core directory where +// the mupen64plus library should be +// located +std::string CoreGetCoreDirectory(void); + +// returns the plugin directory where +// plugins should be located +std::string CoreGetPluginDirectory(void); + +// returns the user config directory where +// configuration files should be located +std::string CoreGetUserConfigDirectory(void); + +// returns the user data directory where +// data files should be located +std::string CoreGetUserDataDirectory(void); + +// returns the user cache directory where +// cache files should be located +std::string CoreGetUserCacheDirectory(void); + +// returns the shared data directory where +// shared data files should be located +std::string CoreGetSharedDataDirectory(void); + + +#endif // DIRECTORIES_HPP diff --git a/Source/RMG-Core/Plugins.cpp b/Source/RMG-Core/Plugins.cpp index a6e318b8..2ddd0012 100644 --- a/Source/RMG-Core/Plugins.cpp +++ b/Source/RMG-Core/Plugins.cpp @@ -8,6 +8,7 @@ * along with this program. If not, see . */ #include "Plugins.hpp" +#include "Directories.hpp" #include "Error.hpp" #include "Emulation.hpp" #include "RomSettings.hpp" @@ -216,7 +217,7 @@ std::vector CoreGetAllPlugins(void) osal_dynlib_lib_handle handle; m64p::PluginApi plugin; - for (const auto& entry : std::filesystem::recursive_directory_iterator("Plugin")) + for (const auto& entry : std::filesystem::recursive_directory_iterator(CoreGetPluginDirectory())) { std::string path = entry.path().string(); if (!entry.is_directory() && diff --git a/Source/RMG-Core/Settings/Settings.cpp b/Source/RMG-Core/Settings/Settings.cpp index 81ddfd3e..e82cbdbf 100644 --- a/Source/RMG-Core/Settings/Settings.cpp +++ b/Source/RMG-Core/Settings/Settings.cpp @@ -9,6 +9,7 @@ */ #include "Settings.hpp" +#include "Directories.hpp" #include "m64p/Api.hpp" #include "Error.hpp" #include "m64p/api/m64p_types.h" @@ -221,7 +222,7 @@ static l_Setting get_setting(SettingsID settingId) setting = {SETTING_SECTION_M64P, "SaveSRAMPath", "Save/Game", "", true}; break; case SettingsID::Core_SharedDataPath: - setting = {SETTING_SECTION_M64P, "SharedDataPath", "Data", "", true}; + setting = {SETTING_SECTION_M64P, "SharedDataPath", CoreGetSharedDataDirectory().c_str(), "", true}; break; case SettingsID::Core_64DD_RomFile: diff --git a/Source/Script/Build.sh b/Source/Script/Build.sh index e88fa21e..f5bd36a2 100755 --- a/Source/Script/Build.sh +++ b/Source/Script/Build.sh @@ -27,7 +27,7 @@ then generator="MSYS Makefiles" fi -cmake -S "$toplvl_dir" -B "$build_dir" -DCMAKE_BUILD_TYPE="$build_config" -G "$generator" +cmake -S "$toplvl_dir" -B "$build_dir" -DCMAKE_BUILD_TYPE="$build_config" -DPORTABLE_INSTALL="${RMG_PORTABLE:-ON}" -G "$generator" -DCMAKE_EXPORT_COMPILE_COMMANDS=1 make install DESTDIR="$toplvl_dir" -j$threads