diff options
Diffstat (limited to 'xdg.diff')
-rw-r--r-- | xdg.diff | 845 |
1 files changed, 845 insertions, 0 deletions
diff --git a/xdg.diff b/xdg.diff new file mode 100644 index 000000000000..04f2e665c29e --- /dev/null +++ b/xdg.diff @@ -0,0 +1,845 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index e9f7458..9135c62 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -58,6 +58,13 @@ option(ENABLE_OPENGL "Enables the OpenGL backend" ON) + option(ENABLE_VULKAN "Enables the Vulkan backend" ON) + option(ENABLE_DISCORD_RPC "Enables the Discord Rich Presence feature" ON) + ++if (UNIX) ++ option(ENABLE_XDG_DIRS "Enable storage according to XDG specification" ON) ++endif() ++if (SYSTEM_DATA_PATH) ++ add_compile_definitions(SYSTEM_DATA_PATH=\"${SYSTEM_DATA_PATH}\") ++endif() ++ + # input backends + if (WIN32) + option(ENABLE_XINPUT "Enables the usage of XInput" ON) +@@ -133,6 +140,10 @@ if (ENABLE_CUBEB) + add_compile_definitions("HAS_CUBEB=1") + endif() + ++if (ENABLE_XDG_DIRS) ++ add_compile_definitions(XDG) ++endif() ++ + add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL) + + find_package(ZArchive) +diff --git a/src/Cafe/Account/Account.cpp b/src/Cafe/Account/Account.cpp +index f370c81..55fc824 100644 +--- a/src/Cafe/Account/Account.cpp ++++ b/src/Cafe/Account/Account.cpp +@@ -424,7 +424,7 @@ OnlineValidator Account::ValidateOnlineFiles() const + { + OnlineValidator result{}; + +- const auto otp = ActiveSettings::GetPath("otp.bin"); ++ const auto otp = ActiveSettings::GetDataPath("otp.bin"); + if (!fs::exists(otp)) + result.otp = OnlineValidator::FileState::Missing; + else if (fs::file_size(otp) != 1024) +@@ -432,7 +432,7 @@ OnlineValidator Account::ValidateOnlineFiles() const + else + result.otp = OnlineValidator::FileState::Ok; + +- const auto seeprom = ActiveSettings::GetPath("seeprom.bin"); ++ const auto seeprom = ActiveSettings::GetDataPath("seeprom.bin"); + if (!fs::exists(seeprom)) + result.seeprom = OnlineValidator::FileState::Missing; + else if (fs::file_size(seeprom) != 512) +diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp +index bfa5d82..50f46d1 100644 +--- a/src/Cafe/CafeSystem.cpp ++++ b/src/Cafe/CafeSystem.cpp +@@ -289,7 +289,7 @@ uint32 loadSharedData() + for (sint32 i = 0; i < sizeof(shareddataDef) / sizeof(shareddataDef[0]); i++) + { + bool existsInMLC = fs::exists(ActiveSettings::GetMlcPath(shareddataDef[i].mlcPath)); +- bool existsInResources = fs::exists(ActiveSettings::GetPath(shareddataDef[i].resourcePath)); ++ bool existsInResources = fs::exists(ActiveSettings::GetSystemDataPath(shareddataDef[i].resourcePath)); + + if (!existsInMLC && !existsInResources) + { +@@ -314,7 +314,7 @@ uint32 loadSharedData() + // alternatively fall back to our shared fonts + if (!fontFile) + { +- path = ActiveSettings::GetPath(shareddataDef[i].resourcePath); ++ path = ActiveSettings::GetSystemDataPath(shareddataDef[i].resourcePath); + fontFile = FileStream::openFile2(path); + } + if (!fontFile) +@@ -340,7 +340,7 @@ uint32 loadSharedData() + return memory_getVirtualOffsetFromPointer(dataWritePtr); + } + // alternative method: load RAM dump +- const auto path = ActiveSettings::GetPath("shareddata.bin"); ++ const auto path = ActiveSettings::GetSystemDataPath("shareddata.bin"); + FileStream* ramDumpFile = FileStream::openFile2(path); + if (ramDumpFile) + { +diff --git a/src/Cafe/Filesystem/FST/KeyCache.cpp b/src/Cafe/Filesystem/FST/KeyCache.cpp +index 7fedc3d..124611f 100644 +--- a/src/Cafe/Filesystem/FST/KeyCache.cpp ++++ b/src/Cafe/Filesystem/FST/KeyCache.cpp +@@ -59,7 +59,7 @@ void KeyCache_Prepare() + sKeyCachePrepared = true; + g_keyCache.clear(); + // load keys +- auto keysPath = ActiveSettings::GetPath("keys.txt"); ++ auto keysPath = ActiveSettings::GetConfigPath("keys.txt"); + FileStream* fs_keys = FileStream::openFile2(keysPath); + if( !fs_keys ) + { +diff --git a/src/Cafe/GameProfile/GameProfile.cpp b/src/Cafe/GameProfile/GameProfile.cpp +index d3e930e..85de1f4 100644 +--- a/src/Cafe/GameProfile/GameProfile.cpp ++++ b/src/Cafe/GameProfile/GameProfile.cpp +@@ -180,12 +180,12 @@ void gameProfile_load() + + bool GameProfile::Load(uint64_t title_id) + { +- auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id); ++ auto gameProfilePath = ActiveSettings::GetConfigPath("gameProfiles/{:016x}.ini", title_id); + + std::optional<std::vector<uint8>> profileContents = FileStream::LoadIntoMemory(gameProfilePath); + if (!profileContents) + { +- gameProfilePath = ActiveSettings::GetPath("gameProfiles/default/{:016x}.ini", title_id); ++ gameProfilePath = ActiveSettings::GetSystemDataPath("gameProfiles/default/{:016x}.ini", title_id); + profileContents = FileStream::LoadIntoMemory(gameProfilePath); + if (!profileContents) + return false; +@@ -276,7 +276,7 @@ bool GameProfile::Load(uint64_t title_id) + + void GameProfile::Save(uint64_t title_id) + { +- auto gameProfilePath = ActiveSettings::GetPath("gameProfiles/{:016x}.ini", title_id); ++ auto gameProfilePath = ActiveSettings::GetConfigPath("gameProfiles/{:016x}.ini", title_id); + FileStream* fs = FileStream::createFile2(gameProfilePath); + if (!fs) + { +diff --git a/src/Cafe/GraphicPack/GraphicPack2.cpp b/src/Cafe/GraphicPack/GraphicPack2.cpp +index 41e33a1..38e52bb 100644 +--- a/src/Cafe/GraphicPack/GraphicPack2.cpp ++++ b/src/Cafe/GraphicPack/GraphicPack2.cpp +@@ -63,7 +63,7 @@ void GraphicPack2::LoadGraphicPack(fs::path graphicPackPath) + void GraphicPack2::LoadAll() + { + std::error_code ec; +- fs::path basePath = ActiveSettings::GetPath("graphicPacks"); ++ fs::path basePath = ActiveSettings::GetDataPath("graphicPacks"); + for (fs::recursive_directory_iterator it(basePath, ec); it != end(it); ++it) + { + if (!it->is_directory(ec)) +diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +index ebf425b..7ad99e4 100644 +--- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp ++++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +@@ -197,17 +197,17 @@ void LatteShaderCache_load() + LatteShaderCache_initCompileQueue(); + // create directories + std::error_code ec; +- fs::create_directories(ActiveSettings::GetPath("shaderCache/transferable"), ec); +- fs::create_directories(ActiveSettings::GetPath("shaderCache/precompiled"), ec); ++ fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); ++ fs::create_directories(ActiveSettings::GetCachePath("shaderCache/precompiled"), ec); + // initialize renderer specific caches + if (g_renderer->GetType() == RendererAPI::Vulkan) + RendererShaderVk::ShaderCacheLoading_begin(cacheTitleId); + else if (g_renderer->GetType() == RendererAPI::OpenGL) + RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); + // get cache file name +- const auto pathGeneric = ActiveSettings::GetPath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); +- const auto pathGenericPre1_25_0 = ActiveSettings::GetPath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 +- const auto pathGenericPre1_16_0 = ActiveSettings::GetPath("shaderCache/transferable/{:08x}.bin", CafeSystem::GetRPXHashBase()); // before 1.16.0 ++ const auto pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); ++ const auto pathGenericPre1_25_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 ++ const auto pathGenericPre1_16_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:08x}.bin", CafeSystem::GetRPXHashBase()); // before 1.16.0 + + LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0); + // calculate extraVersion for transferable and precompiled shader cache +diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp +index e9e86f5..dc088ae 100644 +--- a/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp ++++ b/src/Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp +@@ -279,7 +279,7 @@ void RendererShaderGL::ShaderCacheLoading_begin(uint64 cacheTitleId) + { + const uint32 cacheMagic = GeneratePrecompiledCacheId(); + const std::string cacheFilename = fmt::format("{:016x}_gl.bin", cacheTitleId); +- const std::wstring cachePath = ActiveSettings::GetPath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); ++ const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); + g_programBinaryCache = FileCache::Open(cachePath, true, cacheMagic); + if (g_programBinaryCache == nullptr) + cemuLog_log(LogType::Force, "Unable to open OpenGL precompiled cache {}", cacheFilename); +diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.cpp b/src/Cafe/HW/Latte/Renderer/Renderer.cpp +index c7f7b81..366ce54 100644 +--- a/src/Cafe/HW/Latte/Renderer/Renderer.cpp ++++ b/src/Cafe/HW/Latte/Renderer/Renderer.cpp +@@ -133,7 +133,7 @@ void Renderer::SaveScreenshot(const std::vector<uint8>& rgb_data, int width, int + // save to png file + if (save_screenshot) + { +- fs::path screendir = ActiveSettings::GetPath("screenshots"); ++ fs::path screendir = ActiveSettings::GetDataPath("screenshots"); + if (!fs::exists(screendir)) + fs::create_directory(screendir); + +diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +index 7af5204..703c92d 100644 +--- a/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp ++++ b/src/Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp +@@ -456,7 +456,7 @@ void RendererShaderVk::ShaderCacheLoading_begin(uint64 cacheTitleId) + } + uint32 spirvCacheMagic = GeneratePrecompiledCacheId(); + const std::string cacheFilename = fmt::format("{:016x}_spirv.bin", cacheTitleId); +- const std::wstring cachePath = ActiveSettings::GetPath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); ++ const std::wstring cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename).generic_wstring(); + s_spirvCache = FileCache::Open(cachePath, true, spirvCacheMagic); + if (s_spirvCache == nullptr) + cemuLog_log(LogType::Force, "Unable to open SPIR-V cache {}", cacheFilename); +diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp +index 50d5a25..b86b0a5 100644 +--- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp ++++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp +@@ -32,8 +32,8 @@ VulkanPipelineStableCache& VulkanPipelineStableCache::GetInstance() + uint32 VulkanPipelineStableCache::BeginLoading(uint64 cacheTitleId) + { + std::error_code ec; +- fs::create_directories(ActiveSettings::GetPath("shaderCache/transferable"), ec); +- const auto pathCacheFile = ActiveSettings::GetPath("shaderCache/transferable/{:016x}_vkpipeline.bin", cacheTitleId); ++ fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); ++ const auto pathCacheFile = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_vkpipeline.bin", cacheTitleId); + + // init cache loader state + g_vkCacheState.pipelineLoadIndex = 0; +diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +index 7c81a3c..8cf1f12 100644 +--- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp ++++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +@@ -2305,7 +2305,7 @@ void VulkanRenderer::WaitCommandBufferFinished(uint64 commandBufferId) + + void VulkanRenderer::PipelineCacheSaveThread(size_t cache_size) + { +- const auto dir = ActiveSettings::GetPath("shaderCache/driver/vk"); ++ const auto dir = ActiveSettings::GetCachePath("shaderCache/driver/vk"); + if (!fs::exists(dir)) + { + try +@@ -2382,7 +2382,7 @@ void VulkanRenderer::PipelineCacheSaveThread(size_t cache_size) + void VulkanRenderer::CreatePipelineCache() + { + std::vector<uint8_t> cacheData; +- const auto dir = ActiveSettings::GetPath("shaderCache/driver/vk"); ++ const auto dir = ActiveSettings::GetCachePath("shaderCache/driver/vk"); + if (fs::exists(dir)) + { + const auto filename = dir / fmt::format("{:016x}.bin", CafeSystem::GetForegroundTitleId()); +diff --git a/src/Cafe/HW/MMU/MMU.cpp b/src/Cafe/HW/MMU/MMU.cpp +index 87bf572..10d69ef 100644 +--- a/src/Cafe/HW/MMU/MMU.cpp ++++ b/src/Cafe/HW/MMU/MMU.cpp +@@ -409,7 +409,7 @@ void memory_writeDumpFile(uint32 startAddr, uint32 size, const fs::path& path) + void memory_createDump() + { + const uint32 pageSize = MemMapper::GetPageSize(); +- fs::path path = ActiveSettings::GetPath("dump/ramDump{:}", (uint32)time(nullptr)); ++ fs::path path = ActiveSettings::GetDataPath("dump/ramDump{:}", (uint32)time(nullptr)); + fs::create_directories(path); + + for (auto& itr : g_mmuRanges) +diff --git a/src/Cafe/IOSU/legacy/iosu_crypto.cpp b/src/Cafe/IOSU/legacy/iosu_crypto.cpp +index 0433995..ef30a2b 100644 +--- a/src/Cafe/IOSU/legacy/iosu_crypto.cpp ++++ b/src/Cafe/IOSU/legacy/iosu_crypto.cpp +@@ -563,7 +563,7 @@ void iosuCrypto_loadSSLCertificates() + void iosuCrypto_init() + { + // load OTP dump +- if (std::ifstream otp_file(ActiveSettings::GetPath("otp.bin"), std::ifstream::in | std::ios::binary); otp_file.is_open()) ++ if (std::ifstream otp_file(ActiveSettings::GetDataPath("otp.bin"), std::ifstream::in | std::ios::binary); otp_file.is_open()) + { + otp_file.seekg(0, std::ifstream::end); + const auto length = otp_file.tellg(); +@@ -586,7 +586,7 @@ void iosuCrypto_init() + hasOtpMem = false; + } + +- if (std::ifstream seeprom_file(ActiveSettings::GetPath("seeprom.bin"), std::ifstream::in | std::ios::binary); seeprom_file.is_open()) ++ if (std::ifstream seeprom_file(ActiveSettings::GetDataPath("seeprom.bin"), std::ifstream::in | std::ios::binary); seeprom_file.is_open()) + { + seeprom_file.seekg(0, std::ifstream::end); + const auto length = seeprom_file.tellg(); +@@ -630,13 +630,13 @@ sint32 iosuCrypt_checkRequirementsForOnlineMode(std::wstring& additionalErrorInf + { + std::error_code ec; + // check if otp.bin is present +- const auto otp_file = ActiveSettings::GetPath("otp.bin"); ++ const auto otp_file = ActiveSettings::GetDataPath("otp.bin"); + if(!fs::exists(otp_file, ec)) + return IOS_CRYPTO_ONLINE_REQ_OTP_MISSING; + if(fs::file_size(otp_file, ec) != 1024) + return IOS_CRYPTO_ONLINE_REQ_OTP_CORRUPTED; + // check if seeprom.bin is present +- const auto seeprom_file = ActiveSettings::GetPath("seeprom.bin"); ++ const auto seeprom_file = ActiveSettings::GetDataPath("seeprom.bin"); + if (!fs::exists(seeprom_file, ec)) + return IOS_CRYPTO_ONLINE_REQ_SEEPROM_MISSING; + if (fs::file_size(seeprom_file, ec) != 512) +diff --git a/src/Cafe/OS/RPL/rpl.cpp b/src/Cafe/OS/RPL/rpl.cpp +index 90f9225..40c8e42 100644 +--- a/src/Cafe/OS/RPL/rpl.cpp ++++ b/src/Cafe/OS/RPL/rpl.cpp +@@ -2122,7 +2122,7 @@ void RPLLoader_LoadDependency(rplDependency_t* dependency) + // attempt to load rpl from Cemu's /cafeLibs/ directory + if (ActiveSettings::LoadSharedLibrariesEnabled()) + { +- const auto filePath = ActiveSettings::GetPath("cafeLibs/{}", dependency->filepath); ++ const auto filePath = ActiveSettings::GetDataPath("cafeLibs/{}", dependency->filepath); + auto fileData = FileStream::LoadIntoMemory(filePath); + if (fileData) + { +diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +index 3d13fdb..fb72ca8 100644 +--- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp ++++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +@@ -107,7 +107,7 @@ namespace coreinit + return; + + std::error_code ec; +- const auto path = ActiveSettings::GetPath("sdcard/"); ++ const auto path = ActiveSettings::GetDataPath("sdcard/"); + fs::create_directories(path, ec); + FSCDeviceHostFS_Mount("/vol/external01", path.generic_wstring().c_str() , FSC_PRIORITY_BASE); + +@@ -140,7 +140,7 @@ namespace coreinit + return FS_RESULT::ERR_PLACEHOLDER; + + std::error_code ec; +- const auto path = ActiveSettings::GetPath("sdcard/"); ++ const auto path = ActiveSettings::GetDataPath("sdcard/"); + fs::create_directories(path, ec); + if (!FSCDeviceHostFS_Mount(mountPathOut, path.generic_wstring().c_str(), FSC_PRIORITY_BASE)) + return FS_RESULT::ERR_PLACEHOLDER; +diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp +index f58e798..6d403de 100644 +--- a/src/Cemu/Logging/CemuLogging.cpp ++++ b/src/Cemu/Logging/CemuLogging.cpp +@@ -98,7 +98,7 @@ void cemuLog_createLogFile(bool triggeredByCrash) + if (LogContext.file_stream.is_open()) + return; + +- const auto path = ActiveSettings::GetPath("log.txt"); ++ const auto path = ActiveSettings::GetDataPath("log.txt"); + LogContext.file_stream.open(path, std::ios::out); + if (LogContext.file_stream.fail()) + { +diff --git a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp +index 25dca26..9f38cdd 100644 +--- a/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp ++++ b/src/Common/ExceptionHandler/ExceptionHandler_win32.cpp +@@ -61,7 +61,7 @@ bool CreateMiniDump(CrashDump dump, EXCEPTION_POINTERS* pep) + if (dump == CrashDump::Disabled) + return true; + +- fs::path p = ActiveSettings::GetPath("crashdump"); ++ fs::path p = ActiveSettings::GetDataPath("crashdump"); + + std::error_code ec; + fs::create_directories(p, ec); +@@ -356,11 +356,11 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context) + const auto temp_time = std::chrono::system_clock::to_time_t(now); + const auto& time = *std::gmtime(&temp_time); + +- fs::path p = ActiveSettings::GetPath("crashdump"); ++ fs::path p = ActiveSettings::GetDataPath("crashdump"); + p /= fmt::format("log_{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}.txt", 1900 + time.tm_year, time.tm_mon + 1, time.tm_mday, time.tm_year, time.tm_hour, time.tm_min, time.tm_sec); + + std::error_code ec; +- fs::copy_file(ActiveSettings::GetPath("log.txt"), p, ec); ++ fs::copy_file(ActiveSettings::GetDataPath("log.txt"), p, ec); + } + + exit(0); +diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp +index c7ff4fe..23bc364 100644 +--- a/src/config/ActiveSettings.cpp ++++ b/src/config/ActiveSettings.cpp +@@ -18,7 +18,7 @@ void ActiveSettings::LoadOnce() + s_path = s_full_path.parent_path(); + s_filename = s_full_path.filename(); + +- g_config.SetFilename(GetPath("settings.xml").generic_wstring()); ++ g_config.SetFilename(GetConfigPath("settings.xml").generic_wstring()); + g_config.Load(); + + std::wstring additionalErrorInfo; +@@ -220,8 +220,59 @@ fs::path ActiveSettings::GetMlcPath() + return GetDefaultMLCPath(); + } + +-fs::path ActiveSettings::GetDefaultMLCPath() ++#ifdef XDG ++std::string ActiveSettings::GetXDGPath(const char* envVar, const std::string& defaultValue) ++{ ++ auto raw_value = std::getenv(envVar); ++ return !raw_value || std::strlen(raw_value) == 0 ? defaultValue : std::string(raw_value); ++} ++#endif ++ ++fs::path ActiveSettings::GetConfigPath() ++{ ++#ifdef XDG ++ auto config_home = GetXDGPath("XDG_CONFIG_HOME", fmt::format("{}/.config", std::getenv("HOME"))); ++ auto dir = fs::path(fmt::format("{}/cemu", config_home)); ++ if (!fs::exists(dir)) ++ fs::create_directories(dir); ++ return dir; ++#endif ++ return s_full_path; ++} ++ ++fs::path ActiveSettings::GetCachePath() ++{ ++#ifdef XDG ++ auto config_home = GetXDGPath("XDG_CACHE_HOME", fmt::format("{}/.cache", std::getenv("HOME"))); ++ auto dir = fs::path(fmt::format("{}/cemu", config_home)); ++ if (!fs::exists(dir)) ++ fs::create_directories(dir); ++ return dir; ++#endif ++ return s_full_path; ++} ++ ++fs::path ActiveSettings::GetDataPath() ++{ ++#ifdef XDG ++ auto config_home = GetXDGPath("XDG_DATA_HOME", fmt::format("{}/.local/share", std::getenv("HOME"))); ++ auto dir = fs::path(fmt::format("{}/cemu", config_home)); ++ if (!fs::exists(dir)) ++ fs::create_directories(dir); ++ return dir; ++#endif ++ return s_full_path; ++} ++ ++fs::path ActiveSettings::GetSystemDataPath() + { +- return GetPath("mlc01"); ++#ifdef SYSTEM_DATA_PATH ++ return fs::path(SYSTEM_DATA_PATH); ++#endif ++ return s_full_path; + } + ++fs::path ActiveSettings::GetDefaultMLCPath() ++{ ++ return GetDataPath("mlc01"); ++} +diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h +index 95ceae1..c47ebeb 100644 +--- a/src/config/ActiveSettings.h ++++ b/src/config/ActiveSettings.h +@@ -13,6 +13,11 @@ public: + [[nodiscard]] static fs::path GetFilename() { return s_filename; } + + [[nodiscard]] static fs::path GetMlcPath(); ++ [[nodiscard]] static fs::path GetCachePath(); ++ [[nodiscard]] static fs::path GetConfigPath(); ++ [[nodiscard]] static fs::path GetDataPath(); ++ [[nodiscard]] static fs::path GetSystemDataPath(); ++ [[nodiscard]] static std::string GetXDGPath(const char* envVar, const std::string& defaultValue); + + [[nodiscard]] static fs::path GetPath(std::string_view p) + { +@@ -57,6 +62,62 @@ public: + return GetMlcPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); + } + ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetCachePath(std::string_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetCachePath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetCachePath(std::wstring_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetCachePath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetConfigPath(std::string_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetConfigPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetConfigPath(std::wstring_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetConfigPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetDataPath(std::string_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetDataPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template <typename ...TArgs> ++ [[nodiscard]] static fs::path GetDataPath(std::wstring_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetDataPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template<typename ...TArgs> ++ [[nodiscard]] static fs::path GetSystemDataPath(std::string_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetSystemDataPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ ++ template<typename ...TArgs> ++ [[nodiscard]] static fs::path GetSystemDataPath(std::wstring_view format, TArgs&&... args) ++ { ++ cemu_assert_debug(format.empty() || (format[0] != L'/' && format[0] != L'\\')); ++ return GetSystemDataPath() / fmt::format(fmt::runtime(format), std::forward<TArgs>(args)...); ++ } ++ + // get mlc path to default cemu root dir/mlc01 + [[nodiscard]] static fs::path GetDefaultMLCPath(); + +@@ -126,4 +187,3 @@ private: + + inline static bool s_has_required_online_files = false; + }; +- +diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp +index e78b30b..b1a72b0 100644 +--- a/src/gui/CemuApp.cpp ++++ b/src/gui/CemuApp.cpp +@@ -115,7 +115,7 @@ bool CemuApp::OnInit() + + Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this); + +- if (!TestWriteAccess(ActiveSettings::GetPath())) ++ if (!TestWriteAccess(ActiveSettings::GetConfigPath())) + wxMessageBox(_("Cemu can't write to its directory.\nPlease move it to a different location or run Cemu as administrator!"), _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); + + auto& config = GetConfig(); +@@ -183,7 +183,7 @@ int CemuApp::FilterEvent(wxEvent& event) + + std::vector<const wxLanguageInfo*> CemuApp::GetAvailableLanguages() + { +- const auto path = ActiveSettings::GetPath("resources"); ++ const auto path = ActiveSettings::GetSystemDataPath("resources"); + if (!exists(path)) + return {}; + +@@ -308,11 +308,15 @@ void CemuApp::CreateDefaultFiles(bool first_start) + // cemu directories + try + { +- const auto controllerProfileFolder = GetCemuPath(L"controllerProfiles").ToStdWstring(); ++ const auto controllerProfileFolder = ActiveSettings::GetConfigPath(L"controllerProfiles").generic_wstring(); + if (!fs::exists(controllerProfileFolder)) + fs::create_directories(controllerProfileFolder); + +- const auto memorySearcherFolder = GetCemuPath(L"memorySearcher").ToStdWstring(); ++ const auto gameProfileFolder = ActiveSettings::GetConfigPath(L"gameProfiles").generic_wstring(); ++ if (!fs::exists(gameProfileFolder)) ++ fs::create_directories(gameProfileFolder); ++ ++ const auto memorySearcherFolder = ActiveSettings::GetConfigPath(L"memorySearcher").generic_wstring(); + if (!fs::exists(memorySearcherFolder)) + fs::create_directories(memorySearcherFolder); + } +@@ -399,4 +403,3 @@ void CemuApp::ActivateApp(wxActivateEvent& event) + event.Skip(); + } + +- +diff --git a/src/gui/ChecksumTool.cpp b/src/gui/ChecksumTool.cpp +index 74422c9..9787ffd 100644 +--- a/src/gui/ChecksumTool.cpp ++++ b/src/gui/ChecksumTool.cpp +@@ -133,7 +133,7 @@ ChecksumTool::ChecksumTool(wxWindow* parent, wxTitleManagerList::TitleEntry& ent + const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); + const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); + +- const auto checksum_path = ActiveSettings::GetPath("resources/checksums/{}", default_file); ++ const auto checksum_path = ActiveSettings::GetDataPath("resources/checksums/{}", default_file); + if (exists(checksum_path)) + m_verify_online->Enable(); + } +@@ -185,7 +185,7 @@ void ChecksumTool::LoadOnlineData() const + + std::string latest_commit; + +- const auto checksum_path = ActiveSettings::GetPath("resources/checksums"); ++ const auto checksum_path = ActiveSettings::GetDataPath("resources/checksums"); + if (exists(checksum_path)) + { + std::string current_commit; +@@ -593,7 +593,7 @@ void ChecksumTool::OnVerifyOnline(wxCommandEvent& event) + const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); + const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); + +- const auto checksum_path = ActiveSettings::GetPath("resources/checksums/{}", default_file); ++ const auto checksum_path = ActiveSettings::GetDataPath("resources/checksums/{}", default_file); + if(!exists(checksum_path)) + return; + +diff --git a/src/gui/DownloadGraphicPacksWindow.cpp b/src/gui/DownloadGraphicPacksWindow.cpp +index 28e5bf6..6811b99 100644 +--- a/src/gui/DownloadGraphicPacksWindow.cpp ++++ b/src/gui/DownloadGraphicPacksWindow.cpp +@@ -65,7 +65,7 @@ bool DownloadGraphicPacksWindow::curlDownloadFile(const char *url, curlDownloadF + bool checkGraphicPackDownloadedVersion(const char* nameVersion, bool& hasVersionFile) + { + hasVersionFile = false; +- const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/version.txt"); ++ const auto path = ActiveSettings::GetDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); + std::unique_ptr<FileStream> file(FileStream::openFile2(path)); + + std::string versionInFile; +@@ -78,7 +78,7 @@ bool checkGraphicPackDownloadedVersion(const char* nameVersion, bool& hasVersion + + void createGraphicPackDownloadedVersionFile(const char* nameVersion) + { +- const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/version.txt"); ++ const auto path = ActiveSettings::GetDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); + std::unique_ptr<FileStream> file(FileStream::createFile2(path)); + if (file) + file->writeString(nameVersion); +@@ -90,7 +90,7 @@ void createGraphicPackDownloadedVersionFile(const char* nameVersion) + + void deleteDownloadedGraphicPacks() + { +- const auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks"); ++ const auto path = ActiveSettings::GetDataPath("graphicPacks/downloadedGraphicPacks"); + std::error_code er; + if (!fs::exists(path, er)) + return; +@@ -238,7 +238,7 @@ void DownloadGraphicPacksWindow::UpdateThread() + return; + } + +- auto path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks"); ++ auto path = ActiveSettings::GetDataPath("graphicPacks/downloadedGraphicPacks"); + std::error_code er; + //fs::remove_all(path, er); -> Don't delete the whole folder and recreate it immediately afterwards because sometimes it just fails + deleteDownloadedGraphicPacks(); +@@ -258,7 +258,7 @@ void DownloadGraphicPacksWindow::UpdateThread() + std::strstr(sb.name, "..\\") != nullptr) + continue; // bad path + +- path = ActiveSettings::GetPath("graphicPacks/downloadedGraphicPacks/{}", sb.name); ++ path = ActiveSettings::GetDataPath("graphicPacks/downloadedGraphicPacks/{}", sb.name); + + size_t sbNameLen = strlen(sb.name); + if(sbNameLen == 0) +diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp +index 8f26f81..2fb052e 100644 +--- a/src/gui/MainWindow.cpp ++++ b/src/gui/MainWindow.cpp +@@ -997,7 +997,7 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event) + { + try + { +- const auto path = CemuApp::GetCemuPath(L"dump\\curl").ToStdWstring(); ++ const auto path = ActiveSettings::GetDataPath(L"dump/curl").generic_wstring(); + fs::create_directories(path); + } + catch (const std::exception& ex) +@@ -1054,8 +1054,8 @@ void MainWindow::OnDebugDumpUsedTextures(wxCommandEvent& event) + try + { + // create directory +- const auto path = CemuApp::GetCemuPath(L"dump\\textures"); +- fs::create_directories(path.ToStdWstring()); ++ const auto path = ActiveSettings::GetDataPath(L"dump/textures"); ++ fs::create_directories(path.generic_wstring()); + } + catch (const std::exception& ex) + { +@@ -1075,8 +1075,8 @@ void MainWindow::OnDebugDumpUsedShaders(wxCommandEvent& event) + try + { + // create directory +- const auto path = CemuApp::GetCemuPath(L"dump\\shaders"); +- fs::create_directories(path.ToStdWstring()); ++ const auto path = ActiveSettings::GetDataPath(L"dump/shaders"); ++ fs::create_directories(path.generic_wstring()); + } + catch (const std::exception & ex) + { +diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/MemorySearcherTool.cpp +index c7c6c73..3ecfff5 100644 +--- a/src/gui/MemorySearcherTool.cpp ++++ b/src/gui/MemorySearcherTool.cpp +@@ -270,7 +270,7 @@ void MemorySearcherTool::OnFilter(wxCommandEvent& event) + + void MemorySearcherTool::Load() + { +- const auto memorySearcherPath = ActiveSettings::GetPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); ++ const auto memorySearcherPath = ActiveSettings::GetConfigPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); + auto memSearcherIniContents = FileStream::LoadIntoMemory(memorySearcherPath); + if (!memSearcherIniContents) + return; +@@ -322,7 +322,7 @@ void MemorySearcherTool::Load() + + void MemorySearcherTool::Save() + { +- const auto memorySearcherPath = ActiveSettings::GetPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); ++ const auto memorySearcherPath = ActiveSettings::GetConfigPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); + FileStream* fs = FileStream::createFile2(memorySearcherPath); + if (fs) + { +diff --git a/src/gui/debugger/DebuggerWindow2.cpp b/src/gui/debugger/DebuggerWindow2.cpp +index cca9bfb..e31031d 100644 +--- a/src/gui/debugger/DebuggerWindow2.cpp ++++ b/src/gui/debugger/DebuggerWindow2.cpp +@@ -272,7 +272,7 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) + { + this->wxWindowBase::SetBackgroundColour(*wxWHITE); + +- const auto file = ActiveSettings::GetPath("debugger/config.xml"); ++ const auto file = ActiveSettings::GetConfigPath("debugger/config.xml"); + m_config.SetFilename(file.generic_wstring()); + m_config.Load(); + +@@ -472,7 +472,7 @@ bool DebuggerWindow2::Show(bool show) + std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const + { + if (module_name.empty() || crc_hash == 0) return std::wstring(); +- return ActiveSettings::GetPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); ++ return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); + } + + void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event) +diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/input/InputSettings2.cpp +index 5793cbd..e48d2a2 100644 +--- a/src/gui/input/InputSettings2.cpp ++++ b/src/gui/input/InputSettings2.cpp +@@ -669,10 +669,10 @@ void InputSettings2::on_profile_delete(wxCommandEvent& event) + } + try + { +- const fs::path old_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}.txt", selection)); ++ const fs::path old_path = ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/{}.txt", selection)); + fs::remove(old_path); + +- const fs::path path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}.xml", selection)); ++ const fs::path path = ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/{}.xml", selection)); + fs::remove(path); + + profile_names->ChangeValue(kDefaultProfileName); +diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp +index 4ae43ce..852c918 100644 +--- a/src/input/InputManager.cpp ++++ b/src/input/InputManager.cpp +@@ -76,9 +76,9 @@ bool InputManager::load(size_t player_index, std::string_view filename) + { + fs::path file_path; + if (filename.empty()) +- file_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}", player_index)); ++ file_path = ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/controller{}", player_index)); + else +- file_path = ActiveSettings::GetPath(fmt::format("controllerProfiles/{}", filename)); ++ file_path = ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/{}", filename)); + + auto old_file = file_path; + old_file.replace_extension(".txt"); // test .txt extension +@@ -448,7 +448,7 @@ bool InputManager::save(size_t player_index, std::string_view filename) + if (!emulated_controller) + return false; + +- fs::path file_path = ActiveSettings::GetPath("controllerProfiles"); ++ fs::path file_path = ActiveSettings::GetConfigPath("controllerProfiles"); + fs::create_directories(file_path); + + const auto is_default_file = filename.empty(); +@@ -664,8 +664,8 @@ EmulatedControllerPtr InputManager::delete_controller(size_t player_index, bool + if(delete_profile) + { + std::error_code ec{}; +- fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); +- fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); ++ fs::remove(ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); ++ fs::remove(ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); + } + + return result; +@@ -680,8 +680,8 @@ EmulatedControllerPtr InputManager::delete_controller(size_t player_index, bool + controller = {}; + + std::error_code ec{}; +- fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); +- fs::remove(ActiveSettings::GetPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); ++ fs::remove(ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/controller{}.xml", player_index)), ec); ++ fs::remove(ActiveSettings::GetConfigPath(fmt::format("controllerProfiles/controller{}.txt", player_index)), ec); + + return result; + } +@@ -782,7 +782,7 @@ void InputManager::apply_game_profile() + + std::vector<std::string> InputManager::get_profiles() + { +- const auto path = ActiveSettings::GetPath("controllerProfiles"); ++ const auto path = ActiveSettings::GetConfigPath("controllerProfiles"); + if (!exists(path)) + return {}; + +diff --git a/src/main.cpp b/src/main.cpp +index f94b276..fb8baec 100644 +--- a/src/main.cpp ++++ b/src/main.cpp +@@ -160,7 +160,7 @@ void _putenvSafe(const char* c) + void reconfigureGLDrivers() + { + // reconfigure GL drivers to store +- const fs::path nvCacheDir = ActiveSettings::GetPath("shaderCache/driver/nvidia/"); ++ const fs::path nvCacheDir = ActiveSettings::GetCachePath("shaderCache/driver/nvidia/"); + + std::error_code err; + fs::create_directories(nvCacheDir, err); +@@ -242,7 +242,7 @@ void unitTests() + + int mainEmulatorHLE() + { +- if (!TestWriteAccess(ActiveSettings::GetPath())) ++ if (!TestWriteAccess(ActiveSettings::GetConfigPath())) + wxMessageBox("Cemu doesn't have write access to it's own directory.\nPlease move it to a different location or run Cemu as administrator!", "Warning", wxOK|wxICON_ERROR); // todo - different error messages per OS + LatteOverlay_init(); + // run a couple of tests if in non-release mode +@@ -264,7 +264,7 @@ int mainEmulatorHLE() + // init Cafe system (todo - the stuff above should be part of this too) + CafeSystem::Initialize(); + // init title list +- CafeTitleList::Initialize(ActiveSettings::GetPath("title_list_cache.xml")); ++ CafeTitleList::Initialize(ActiveSettings::GetCachePath("title_list_cache.xml")); + for (auto& it : GetConfig().game_paths) + CafeTitleList::AddScanPath(it); + fs::path mlcPath = ActiveSettings::GetMlcPath(); +diff --git a/src/util/libusbWrapper/libusbWrapper.cpp b/src/util/libusbWrapper/libusbWrapper.cpp +index e1d7298..6e498c8 100644 +--- a/src/util/libusbWrapper/libusbWrapper.cpp ++++ b/src/util/libusbWrapper/libusbWrapper.cpp +@@ -18,7 +18,7 @@ void libusbWrapper::init() + m_module = LoadLibraryW(L"libusb-1.0.dll"); + if (!m_module) + { +- const auto path = ActiveSettings::GetPath("resources/libusb-1.0.dll"); ++ const auto path = ActiveSettings::GetSystemDataPath("resources/libusb-1.0.dll"); + m_module = LoadLibraryW(path.generic_wstring().c_str()); + if (!m_module) + { |