diff --git a/rpcs3/Crypto/unpkg.cpp b/rpcs3/Crypto/unpkg.cpp index 40a7112d3f..63821189e4 100644 --- a/rpcs3/Crypto/unpkg.cpp +++ b/rpcs3/Crypto/unpkg.cpp @@ -689,20 +689,16 @@ package_error package_reader::check_target_app_version() const return package_error::app_version; } -bool package_reader::fill_data(std::map& all_install_entries) +bool package_reader::set_install_path() { if (!m_is_valid) { return false; } - m_install_entries.clear(); m_install_path.clear(); - m_bootable_file_path.clear(); - m_entry_indexer = 0; - m_written_bytes = 0; - // Get full path and create the directory + // Get full path std::string dir = rpcs3::utils::get_hdd0_dir(); // Based on https://www.psdevwiki.com/ps3/PKG_files#ContentType @@ -739,13 +735,27 @@ bool package_reader::fill_data(std::map& all_instal // If false, an existing directory is being overwritten: cannot cancel the operation m_was_null = !fs::is_dir(dir); - if (!fs::create_path(dir)) + m_install_path = dir; + return true; +} + +bool package_reader::fill_data(std::map& all_install_entries) +{ + if (!m_is_valid) { - pkg_log.error("Could not create the installation directory %s", dir); return false; } - m_install_path = dir; + if (!fs::create_path(m_install_path)) + { + pkg_log.error("Could not create the installation directory %s (error=%s)", m_install_path, fs::g_tls_error); + return false; + } + + m_install_entries.clear(); + m_bootable_file_path.clear(); + m_entry_indexer = 0; + m_written_bytes = 0; if (!decrypt_data()) { @@ -772,7 +782,7 @@ bool package_reader::fill_data(std::map& all_instal decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()); const std::string name{reinterpret_cast(m_bufs.back().get()), entry.name_size}; - std::string path = dir + vfs::escape(name); + std::string path = m_install_path + vfs::escape(name); const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS); @@ -831,7 +841,7 @@ bool package_reader::fill_data(std::map& all_instal if (num_failures != 0) { - pkg_log.error("Package installation failed: %s", dir); + pkg_log.error("Package installation failed: %s", m_install_path); return false; } @@ -988,29 +998,53 @@ void package_reader::extract_worker(thread_key thread_data_key) } } -bool package_reader::extract_data(std::deque& readers, std::deque& bootable_paths) +package_error package_reader::extract_data(std::deque& readers, std::deque& bootable_paths) { - std::map all_install_entries; + package_error error = package_error::no_error; + usz num_failures = 0; + // Set paths first in order to know if the install dir was empty before starting any installations. + // This will also allow us to remove all the new packages in one path at once if any of them fail. for (package_reader& reader : readers) { - if (!reader.fill_data(all_install_entries)) + reader.m_result = result::not_started; + + if (!reader.set_install_path()) { - return false; + error = package_error::other; + reader.m_result = result::error; + break; } } - usz num_failures = 0; - for (package_reader& reader : readers) { + // Use a seperate map for each reader. We need to check if the target app version exists for each package in sequence. + std::map all_install_entries; + + if (error == package_error::no_error) + { + // Check if this package is allowed to be installed on top of the existing data + error = reader.check_target_app_version(); + } + + if (error == package_error::no_error) + { + reader.m_result = result::started; + + // Parse the files to be installed and create all paths. + if (!reader.fill_data(all_install_entries)) + { + error = package_error::other; + } + } + reader.m_bufs.resize(std::min(utils::get_thread_count(), reader.m_install_entries.size())); - reader.m_num_failures = 0; - reader.m_result = result::not_started; + reader.m_num_failures = error == package_error::no_error ? 0 : 1; atomic_t thread_indexer = 0; - named_thread_group workers("PKG Installer "sv, std::max(reader.m_bufs.size(), 1) - 1, [&]() + named_thread_group workers("PKG Installer "sv, std::max(::narrow(reader.m_bufs.size()), 1) - 1, [&]() { reader.extract_worker(thread_key{thread_indexer++}); }); @@ -1067,7 +1101,12 @@ bool package_reader::extract_data(std::deque& readers, std::dequ bootable_paths.emplace_back(std::move(reader.m_bootable_file_path)); } - return num_failures == 0; + if (num_failures > 0) + { + error = package_error::other; + } + + return error; } void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode) diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index 4ad248d46a..03a1b13008 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -335,6 +335,7 @@ public: enum result { not_started, + started, success, aborted, aborted_cleaned, @@ -344,7 +345,7 @@ public: bool is_valid() const { return m_is_valid; } package_error check_target_app_version() const; - static bool extract_data(std::deque& readers, std::deque& bootable_paths); + static package_error extract_data(std::deque& readers, std::deque& bootable_paths); psf::registry get_psf() const { return m_psf; } result get_result() const { return m_result; }; @@ -359,6 +360,7 @@ private: bool decrypt_data(); void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set); u64 archive_read(void* data_ptr, u64 num_bytes); + bool set_install_path(); bool fill_data(std::map& all_install_entries); std::span archive_read_block(u64 offset, void* data_ptr, u64 num_bytes); std::span decrypt(u64 offset, u64 size, const uchar* key, thread_key thread_data_key = {0}); diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index 1cd7d62aac..6e2d35740f 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -92,8 +92,8 @@ namespace rpcs3::utils named_thread worker("PKG Installer", [&] { std::deque bootables; - - return package_reader::extract_data(reader, bootables); + const package_error error = package_reader::extract_data(reader, bootables); + return error == package_error::no_error; }); // Wait for the completion diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 739a3d9a6a..3ea1a3f7d7 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -944,10 +944,9 @@ void main_window::HandlePackageInstallation(QStringList file_paths) std::deque readers; - for (usz i = 0; error == package_error::no_error && i < packages.size(); i++) + for (const compat::package_info& info : packages) { - readers.emplace_back(sstr(packages[i].path)); - error = readers.back().check_target_app_version(); + readers.emplace_back(sstr(info.path)); } std::deque bootable_paths; @@ -955,15 +954,8 @@ void main_window::HandlePackageInstallation(QStringList file_paths) // Run PKG unpacking asynchronously named_thread worker("PKG Installer", [&readers, &error, &bootable_paths] { - if (error == package_error::no_error) - { - if (package_reader::extract_data(readers, bootable_paths)) - { - return true; - } - } - - return false; + error = package_reader::extract_data(readers, bootable_paths); + return error == package_error::no_error; }); pdlg.show(); @@ -1021,6 +1013,7 @@ void main_window::HandlePackageInstallation(QStringList file_paths) break; } case package_reader::result::not_started: + case package_reader::result::started: case package_reader::result::aborted_cleaned: { gui_log.notice("Aborted installation of %s (title_id=%s, title=%s, version=%s).", sstr(package.path), sstr(package.title_id), sstr(package.title), sstr(package.version)); @@ -1146,6 +1139,7 @@ void main_window::HandlePackageInstallation(QStringList file_paths) { case package_reader::result::success: case package_reader::result::not_started: + case package_reader::result::started: case package_reader::result::aborted: case package_reader::result::aborted_cleaned: break;