diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index c19a0a776a..b9910349e9 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -246,6 +246,7 @@ target_sources(rpcs3_emu PRIVATE Cell/Modules/cellSync2.cpp Cell/Modules/cellSync.cpp Cell/Modules/cellSysconf.cpp + Cell/Modules/cellSysCache.cpp Cell/Modules/cellSysmodule.cpp Cell/Modules/cellSysutilAp.cpp Cell/Modules/cellSysutilAvc2.cpp diff --git a/rpcs3/Emu/Cell/Modules/cellSysCache.cpp b/rpcs3/Emu/Cell/Modules/cellSysCache.cpp new file mode 100644 index 0000000000..d077ff6213 --- /dev/null +++ b/rpcs3/Emu/Cell/Modules/cellSysCache.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "Emu/System.h" +#include "Emu/IdManager.h" +#include "Emu/Cell/PPUModule.h" + +#include "cellSysutil.h" +#include "util/init_mutex.hpp" +#include "Utilities/StrUtil.h" + +extern logs::channel cellSysutil; + +template<> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto error) + { + switch (error) + { + STR_CASE(CELL_SYSCACHE_ERROR_ACCESS_ERROR); + STR_CASE(CELL_SYSCACHE_ERROR_INTERNAL); + STR_CASE(CELL_SYSCACHE_ERROR_NOTMOUNTED); + STR_CASE(CELL_SYSCACHE_ERROR_PARAM); + } + + return unknown; + }); +} + +struct syscache_info +{ + const std::string cache_root = Emu.GetHdd1Dir() + "/caches/"; + + stx::init_mutex init; + + std::string cache_id; + + syscache_info() noexcept + { + // Find existing cache at startup + const std::string prefix = Emu.GetTitleID() + '_'; + + for (auto&& entry : fs::dir(cache_root)) + { + if (entry.is_directory && entry.name.size() >= prefix.size() && entry.name.compare(0, prefix.size(), prefix) == 0) + { + cache_id = std::move(entry.name); + break; + } + } + } + + void clear(bool remove_root) noexcept + { + // Clear cache + if (!vfs::host::remove_all(cache_root + cache_id, cache_root, remove_root)) + { + cellSysutil.fatal("cellSysCache: failed to clear cache directory '%s%s' (%s)", cache_root, cache_id, fs::g_tls_error); + } + + if (!remove_root) + { + // Recreate /cache subdirectory if necessary + fs::create_path(cache_root + cache_id + "/cache"); + } + } +}; + +error_code cellSysCacheClear() +{ + cellSysutil.notice("cellSysCacheClear()"); + + const auto cache = g_fxo->get(); + + const auto lock = cache->init.access(); + + if (!lock) + { + return CELL_SYSCACHE_ERROR_NOTMOUNTED; + } + + // Clear existing cache + if (!cache->cache_id.empty()) + { + cache->clear(false); + } + + return not_an_error(CELL_SYSCACHE_RET_OK_CLEARED); +} + +error_code cellSysCacheMount(vm::ptr param) +{ + cellSysutil.notice("cellSysCacheMount(param=*0x%x)", param); + + const auto cache = g_fxo->get(); + + if (!param || !std::memchr(param->cacheId, '\0', CELL_SYSCACHE_ID_SIZE)) + { + return CELL_SYSCACHE_ERROR_PARAM; + } + + // Full virtualized cache id (with title id included) + std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + param->cacheId, true); + + // Full path to virtual cache root (/dev_hdd1) + std::string new_path = cache->cache_root + cache_id + '/'; + + // Set fixed VFS path + strcpy_trunc(param->getCachePath, "/dev_hdd1/cache"); + + // Lock pseudo-mutex + const auto lock = cache->init.init_always([&] + { + }); + + // Check if can reuse existing cache (won't if cache id is an empty string) + if (param->cacheId[0] && cache_id == cache->cache_id && fs::is_dir(new_path + "cache")) + { + // Isn't mounted yet on first call to cellSysCacheMount + vfs::mount("/dev_hdd1", new_path); + + cellSysutil.success("Mounted existing cache at %s", new_path); + return not_an_error(CELL_SYSCACHE_RET_OK_RELAYED); + } + + // Clear existing cache + if (!cache->cache_id.empty()) + { + cache->clear(true); + } + + // Set new cache id + cache->cache_id = std::move(cache_id); + fs::create_path(new_path + "cache"); + vfs::mount("/dev_hdd1", new_path); + + return not_an_error(CELL_SYSCACHE_RET_OK_CLEARED); +} + + +extern void cellSysutil_SysCache_init() +{ + REG_FUNC(cellSysutil, cellSysCacheMount); + REG_FUNC(cellSysutil, cellSysCacheClear); +} diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index 0ac71eb87e..cfc3ffd6d6 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -70,22 +70,6 @@ extern void sysutil_send_system_cmd(u64 status, u64 param) } } -struct syscache -{ - atomic_t mounted = false; - std::string cache_id; - shared_mutex mtx; - - ~syscache() - { - // Check if mounted and cache_id is different than already written empty value - if (!cache_id.empty()) - { - fs::write_file(fs::get_cache_dir() + "/cache/cacheId", fs::rewrite, cache_id); - } - } -}; - template <> void fmt_class_string::format(std::string& out, u64 arg) { @@ -319,89 +303,6 @@ s32 cellSysutilUnregisterCallback(u32 slot) return CELL_OK; } -const std::string cache_path = "/dev_hdd1/cache"; - -s32 cellSysCacheClear() -{ - cellSysutil.warning("cellSysCacheClear()"); - - const auto cache = g_fxo->get(); - - if (!cache->mounted) - { - return CELL_SYSCACHE_ERROR_NOTMOUNTED; - } - - if (!vfs::host::remove_all(vfs::get(cache_path), Emu.GetHdd1Dir(), false)) - { - cellSysutil.error("cellSysCacheClear(): failed to clear directory '%s' (%s)", cache_path, fs::g_tls_error); - return CELL_SYSCACHE_ERROR_ACCESS_ERROR; - } - - return CELL_SYSCACHE_RET_OK_CLEARED; -} - -s32 cellSysCacheMount(vm::ptr param) -{ - cellSysutil.warning("cellSysCacheMount(param=*0x%x)", param); - - const auto cache = g_fxo->get(); - - if (!param || !memchr(param->cacheId, '\0', CELL_SYSCACHE_ID_SIZE)) - { - return CELL_SYSCACHE_ERROR_PARAM; - } - - std::string cache_id = param->cacheId; - strcpy_trunc(param->getCachePath, cache_path); - - cellSysutil.notice("cellSysCacheMount: cache id=%s", cache_id); - - std::lock_guard lock(cache->mtx); - - if (!cache->mounted.exchange(true)) - { - // Get last cache ID, lasts between application boots - fs::file last_id(fs::get_cache_dir() + "/cache/cacheId", fs::read + fs::write + fs::create); - const auto id_size = last_id.size(); - - // Compare specified ID with old one (if size is 0 clear unconditionally) - const bool relayed = id_size && id_size == cache_id.size() && [&]() - { - char buf[CELL_SYSCACHE_ID_SIZE - 1]; - last_id.read(buf, id_size); - return memcmp(buf, cache_id.c_str(), id_size) == 0; - }(); - - // Protection against rpcs3 crash (syscache dtor wasn't called) - // Clear cacheId (clear cache on next startup) - last_id.trunc(0); - - if (relayed) - { - cache->cache_id = std::move(cache_id); - return CELL_SYSCACHE_RET_OK_RELAYED; - } - } - else - { - // If null term specified at start it must be cleared uncondionally - if (!cache_id.empty() && cache->cache_id == cache_id) - { - return CELL_SYSCACHE_RET_OK_RELAYED; - } - } - - // Set new cache ID (clear previous) - if (!vfs::host::remove_all(vfs::get(cache_path), Emu.GetHdd1Dir(), false)) - { - cellSysutil.error("cellSysCacheMount(): failed to clear directory '%s' (%s)", cache_path, fs::g_tls_error); - } - - cache->cache_id = std::move(cache_id); - return CELL_SYSCACHE_RET_OK_CLEARED; -} - bool g_bgm_playback_enabled = true; s32 cellSysutilEnableBgmPlayback() @@ -762,6 +663,7 @@ extern void cellSysutil_SysutilAvc_init(); extern void cellSysutil_WebBrowser_init(); extern void cellSysutil_AudioOut_init(); extern void cellSysutil_VideoOut_init(); +extern void cellSysutil_SysCache_init(); DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []() { @@ -775,6 +677,7 @@ DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []() cellSysutil_WebBrowser_init(); // cellWebBrowser, cellWebComponent functions cellSysutil_AudioOut_init(); // cellAudioOut functions cellSysutil_VideoOut_init(); // cellVideoOut functions + cellSysutil_SysCache_init(); // cellSysCache functions REG_FUNC(cellSysutil, _cellSysutilGetSystemParamInt); REG_FUNC(cellSysutil, cellSysutilGetSystemParamInt); @@ -792,9 +695,6 @@ DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []() REG_FUNC(cellSysutil, cellSysutilDisableBgmPlaybackEx); REG_FUNC(cellSysutil, cellSysutilSetBgmPlaybackExtraParam); - REG_FUNC(cellSysutil, cellSysCacheMount); - REG_FUNC(cellSysutil, cellSysCacheClear); - REG_FUNC(cellSysutil, cellSysutilRegisterCallbackDispatcher); REG_FUNC(cellSysutil, cellSysutilUnregisterCallbackDispatcher); REG_FUNC(cellSysutil, cellSysutilPacketRead); diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.h b/rpcs3/Emu/Cell/Modules/cellSysutil.h index 55e8d9762f..6015e50b2b 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.h +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.h @@ -148,7 +148,10 @@ enum CELL_SYSCACHE_ID_SIZE = 32, CELL_SYSCACHE_PATH_MAX = 1055, +}; +enum CellSysCacheError : u32 +{ CELL_SYSCACHE_ERROR_ACCESS_ERROR = 0x8002bc01, // I don't think we need this CELL_SYSCACHE_ERROR_INTERNAL = 0x8002bc02, // Not really useful, if we run out of HDD space sys_fs should handle that diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index cd5ee2cbbb..eefdf9bbbe 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -378,8 +378,7 @@ void Emulator::Init() make_path_verbose(dev_hdd0 + "disc/"); make_path_verbose(dev_hdd0 + "savedata/"); make_path_verbose(dev_hdd0 + "savedata/vmc/"); - make_path_verbose(dev_hdd1 + "cache/"); - make_path_verbose(dev_hdd1 + "game/"); + make_path_verbose(dev_hdd1 + "caches/"); } // Fixup savedata @@ -653,7 +652,7 @@ bool Emulator::BootRsxCapture(const std::string& path) void Emulator::LimitCacheSize() { - const std::string cache_location = Emulator::GetHdd1Dir() + "/cache"; + const std::string cache_location = Emulator::GetHdd1Dir() + "/caches"; if (!fs::is_dir(cache_location)) { LOG_WARNING(GENERAL, "Cache does not exist (%s)", cache_location); @@ -1066,7 +1065,6 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa // Mount default relative path to non-existent directory vfs::mount("/dev_hdd0", fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", emu_dir)); - vfs::mount("/dev_hdd1", fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", emu_dir)); vfs::mount("/dev_flash", g_cfg.vfs.get_dev_flash()); vfs::mount("/dev_usb", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir)); vfs::mount("/dev_usb000", fmt::replace_all(g_cfg.vfs.dev_usb000, "$(EmulatorDir)", emu_dir)); diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index c49a9020d2..fdb20f901f 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -239,7 +239,7 @@ std::string vfs::get(std::string_view vpath, std::vector* out_dir) return std::string{result_base} + vfs::escape(fmt::merge(result, "/")); } -std::string vfs::escape(std::string_view path) +std::string vfs::escape(std::string_view path, bool escape_slash) { std::string result; result.reserve(path.size()); @@ -331,6 +331,17 @@ std::string vfs::escape(std::string_view path) result += u8"*"; break; } + case '/': + { + if (escape_slash) + { + result += u8"/"; + break; + } + + result += c; + break; + } case char{u8"!"[0]}: { // Escape full-width characters 0xFF01..0xFF5e with ! (0xFF01) diff --git a/rpcs3/Emu/VFS.h b/rpcs3/Emu/VFS.h index 9a12e2c493..06c931ed0e 100644 --- a/rpcs3/Emu/VFS.h +++ b/rpcs3/Emu/VFS.h @@ -13,7 +13,7 @@ namespace vfs std::string get(std::string_view vpath, std::vector* out_dir = nullptr); // Escape VFS path by replacing non-portable characters with surrogates - std::string escape(std::string_view path); + std::string escape(std::string_view path, bool escape_slash = false); // Invert escape operation std::string unescape(std::string_view path); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index ed2a4af04a..affda9bd70 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -239,6 +239,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index b6712e7e23..eb5d9219d9 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -521,6 +521,9 @@ Emu\Cell\Modules + + Emu\Cell\Modules + Emu\Cell\Modules diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 58146e01db..09fb6d3d74 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1704,7 +1704,7 @@ void main_window::SetIconSizeActions(int idx) void main_window::RemoveDiskCache() { - std::string cacheDir = Emulator::GetHdd1Dir() + "/cache"; + std::string cacheDir = Emulator::GetHdd1Dir() + "/caches"; if (fs::is_dir(cacheDir) && fs::remove_all(cacheDir, false)) {