From e4c9af176bf0cf6085564a5d397b2578ab296211 Mon Sep 17 00:00:00 2001 From: Eladash <18193363+elad335@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:54:11 +0200 Subject: [PATCH] fixup hdd1 cache --- rpcs3/Emu/Cell/Modules/cellSysCache.cpp | 45 +++++++++++++++++++------ rpcs3/Emu/System.cpp | 13 +++++-- rpcs3/Loader/TAR.cpp | 31 ++++++++++++++--- 3 files changed, 71 insertions(+), 18 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSysCache.cpp b/rpcs3/Emu/Cell/Modules/cellSysCache.cpp index 382c305e32..6f0a309220 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysCache.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysCache.cpp @@ -51,10 +51,15 @@ struct syscache_info const auto lock = init.init(); // Extract cache id from path - cache_id = Emu.hdd1; - if (cache_id.back() == '/') - cache_id.resize(cache_id.size() - 1); - cache_id = cache_id.substr(cache_id.find_last_of('/') + 1); + std::string_view id = Emu.hdd1; + id = id.substr(0, id.find_last_not_of(fs::delim) + 1); + id = id.substr(id.find_last_of(fs::delim) + 1); + cache_id = std::string{id}; + + if (!Emu.DeserialManager() && !fs::write_file(get_syscache_state_corruption_indicator_file_path(Emu.hdd1), fs::write_new)) + { + fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", Emu.hdd1, fs::g_tls_error); + } cellSysutil.success("Retained cache from parent process: %s", Emu.hdd1); return; @@ -67,13 +72,17 @@ struct syscache_info { if (entry.is_directory && entry.name.starts_with(prefix)) { - if (fs::is_file(get_syscache_state_corruption_indicator_file_path(cache_root + '/' + entry.name))) + cache_id = vfs::unescape(entry.name); + + if (fs::is_file(get_syscache_state_corruption_indicator_file_path(cache_root + '/' + cache_id))) { // State is not complete - break; + clear(true); + cache_id.clear(); + continue; } - cache_id = std::move(entry.name); + cellSysutil.notice("Retained cache from past data: %s", cache_root + '/' + cache_id); break; } } @@ -172,13 +181,22 @@ error_code cellSysCacheMount(vm::ptr param) auto& cache = g_fxo->get(); - if (!param || (param->cacheId[0] && sysutil_check_name_string(param->cacheId, 1, CELL_SYSCACHE_ID_SIZE) != 0)) + if (!param) + { + return CELL_SYSCACHE_ERROR_PARAM; + } + + std::string cache_name; + + ensure(vm::read_string(param.ptr(&CellSysCacheParam::cacheId).addr(), sizeof(param->cacheId), cache_name), "Access violation"); + + if (!cache_name.empty() && sysutil_check_name_string(cache_name.data(), 1, CELL_SYSCACHE_ID_SIZE) != 0) { return CELL_SYSCACHE_ERROR_PARAM; } // Full virtualized cache id (with title id included) - std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + param->cacheId); + std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + cache_name); // Full path to virtual cache root (/dev_hdd1) std::string new_path = cache.cache_root + cache_id + '/'; @@ -194,7 +212,7 @@ error_code cellSysCacheMount(vm::ptr param) std::lock_guard lock0(g_mp_sys_dev_hdd1.mutex); // Check if can reuse existing cache (won't if cache id is an empty string or cache is damaged/incomplete) - if (param->cacheId[0] && cache_id == cache.cache_id && !fs::is_file(get_syscache_state_corruption_indicator_file_path(cache.cache_root + cache_id))) + if (!cache_name.empty() && cache_id == cache.cache_id) { // Isn't mounted yet on first call to cellSysCacheMount if (vfs::mount("/dev_hdd1", new_path)) @@ -204,7 +222,7 @@ error_code cellSysCacheMount(vm::ptr param) return not_an_error(CELL_SYSCACHE_RET_OK_RELAYED); } - const bool can_create = cache.cache_id != cache_id; + const bool can_create = cache.cache_id != cache_id || !cache.cache_id.empty(); if (!cache.cache_id.empty()) { @@ -231,6 +249,11 @@ error_code cellSysCacheMount(vm::ptr param) } } + if (!fs::write_file(get_syscache_state_corruption_indicator_file_path(new_path), fs::write_new)) + { + fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", new_path, fs::g_tls_error); + } + if (vfs::mount("/dev_hdd1", new_path)) g_fxo->get().add("/dev_hdd1", &g_mp_sys_dev_hdd1); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 3efc1a097d..bcc9008d89 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -91,6 +91,8 @@ extern void signal_system_cache_can_stay(); fs::file make_file_view(const fs::file& file, u64 offset, u64 size); +extern std::string get_syscache_state_corruption_indicator_file_path(std::string_view dir_path); + fs::file g_tty; atomic_t g_tty_size{0}; std::array, 16> g_tty_input; @@ -1103,7 +1105,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, } } - auto load_tar = [&](const std::string& path) + auto load_tar = [&](const std::string& path, const std::string& special_file) { const usz size = m_ar->pop(); const usz max_data_size = m_ar->get_size(utils::add_saturate(size, m_ar->pos)); @@ -1115,6 +1117,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, fs::remove_all(path, size == 0); + if (!special_file.empty()) + { + fs::write_file(special_file, fs::write_new); + } + if (size) { m_ar->breathe(true); @@ -1134,7 +1141,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, if (!hdd1.empty()) { hdd1 = rpcs3::utils::get_hdd1_dir() + "caches/" + hdd1 + "/"; - load_tar(hdd1); + load_tar(hdd1, get_syscache_state_corruption_indicator_file_path(hdd1)); } for (const std::string hdd0_game = rpcs3::utils::get_hdd0_dir() + "game/";;) @@ -1152,7 +1159,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, fmt::throw_exception("HDD0 deserialization failed: Invalid directory name: %s, ar=%s", dirname.substr(0, CELL_GAME_DIRNAME_SIZE + 1), *m_ar); } - load_tar(hdd0_game + game_data); + load_tar(hdd0_game + game_data, ""); } // Reserved area diff --git a/rpcs3/Loader/TAR.cpp b/rpcs3/Loader/TAR.cpp index b2d3cbcdc4..3fcbd27f5c 100644 --- a/rpcs3/Loader/TAR.cpp +++ b/rpcs3/Loader/TAR.cpp @@ -232,6 +232,9 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) const TARHeader& header = iter->second.second; const std::string& name = iter->first; + // Backwards compatibility measure + const bool should_ignore = name.find(reinterpret_cast(u8"$")) != umax; + std::string result = name; if (!prefix_path.empty()) @@ -291,7 +294,16 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) std::unique_ptr file_data = get_file(name); - fs::file file(result, fs::rewrite); + fs::file file; + + if (should_ignore) + { + file = fs::make_stream>(); + } + else + { + file.open(result, fs::rewrite); + } if (file && file_data) { @@ -301,7 +313,7 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) if (unread_size == 0) { - file.write(filedata_span.data(), filedata_span.size()); + file.write(filedata_span.data(), should_ignore ? 0 : filedata_span.size()); continue; } @@ -310,7 +322,7 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) if (usz read_size = filedata_span.size() - unread_size) { ensure(file_data->try_read(filedata_span.first(read_size)) == 0); - file.write(filedata_span.data(), read_size); + file.write(filedata_span.data(), should_ignore ? 0 : read_size); } break; @@ -318,6 +330,7 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) file.close(); + file_data->seek_pos(m_ar_tar_start + largest_offset, true); if (!m_file) @@ -327,6 +340,11 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) m_ar->m_max_data = restore_limit; } + if (should_ignore) + { + break; + } + if (mtime != umax && !fs::utime(result, atime, mtime)) { tar_log.error("TAR Loader: fs::utime failed on %s (%s)", result, fs::g_tls_error); @@ -351,6 +369,11 @@ bool tar_object::extract(std::string prefix_path, bool is_vfs) case '5': { + if (should_ignore) + { + break; + } + if (!fs::create_path(result)) { tar_log.error("TAR Loader: failed to create directory %s (%s)", name, fs::g_tls_error); @@ -576,7 +599,7 @@ void tar_object::save_directory(const std::string& target_path, utils::serial& a { exists = true; - if (entry.name.find_first_not_of('.') == umax) + if (entry.name.find_first_not_of('.') == umax || entry.name.starts_with(reinterpret_cast(u8"$"))) { continue; }