mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
Virtualize cellSysCache
Allows caches from multiple games to coexist. Also change the way of handling cache IDs (file-less).
This commit is contained in:
parent
508ffcb775
commit
03b9ee27c5
@ -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
|
||||
|
144
rpcs3/Emu/Cell/Modules/cellSysCache.cpp
Normal file
144
rpcs3/Emu/Cell/Modules/cellSysCache.cpp
Normal file
@ -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<CellSysCacheError>::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<syscache_info>();
|
||||
|
||||
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<CellSysCacheParam> param)
|
||||
{
|
||||
cellSysutil.notice("cellSysCacheMount(param=*0x%x)", param);
|
||||
|
||||
const auto cache = g_fxo->get<syscache_info>();
|
||||
|
||||
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);
|
||||
}
|
@ -70,22 +70,6 @@ extern void sysutil_send_system_cmd(u64 status, u64 param)
|
||||
}
|
||||
}
|
||||
|
||||
struct syscache
|
||||
{
|
||||
atomic_t<bool> 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<CellSysutilLang>::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<syscache>();
|
||||
|
||||
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<CellSysCacheParam> param)
|
||||
{
|
||||
cellSysutil.warning("cellSysCacheMount(param=*0x%x)", param);
|
||||
|
||||
const auto cache = g_fxo->get<syscache>();
|
||||
|
||||
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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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));
|
||||
|
@ -239,7 +239,7 @@ std::string vfs::get(std::string_view vpath, std::vector<std::string>* 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)
|
||||
|
@ -13,7 +13,7 @@ namespace vfs
|
||||
std::string get(std::string_view vpath, std::vector<std::string>* 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);
|
||||
|
@ -239,6 +239,7 @@
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSync.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSync2.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysconf.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysCache.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysmodule.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysutil.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysutilAp.cpp" />
|
||||
|
@ -521,6 +521,9 @@
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysconf.cpp">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysCache.cpp">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\Modules\cellSysmodule.cpp">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClCompile>
|
||||
|
@ -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))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user