diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 1091e1832f..19ec166bae 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -99,6 +99,7 @@ target_sources(rpcs3_emu PRIVATE # Loader target_sources(rpcs3_emu PRIVATE ../Loader/ELF.cpp + ../Loader/mself.cpp ../Loader/PSF.cpp ../Loader/PUP.cpp ../Loader/TAR.cpp diff --git a/rpcs3/Loader/mself.cpp b/rpcs3/Loader/mself.cpp new file mode 100644 index 0000000000..1b56c8c9aa --- /dev/null +++ b/rpcs3/Loader/mself.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" + +#include "Utilities/File.h" +#include "util/logs.hpp" +#include "Emu/VFS.h" + +#include "mself.hpp" + +LOG_CHANNEL(mself_log, "MSELF"); + +bool extract_mself(const std::string& file, const std::string& extract_to) +{ + fs::file mself(file); + + mself_log.notice("Extracting MSELF file '%s' to directory '%s'...", file, extract_to); + + if (!mself) + { + mself_log.error("Error opening MSELF file '%s' (%s)", file, fs::g_tls_error); + return false; + } + + mself_header hdr{}; + + if (!mself.read(hdr)) + { + mself_log.error("Error reading MSELF header, file is too small. (size=0x%x)", mself.size()); + return false; + } + + const u64 mself_size = mself.size(); + const u32 hdr_count = hdr.get_count(mself_size); + + if (!hdr_count) + { + mself_log.error("Provided file is not an MSELF"); + return false; + } + + std::vector recs(hdr_count); + + if (!mself.read(recs)) + { + mself_log.error("Error extracting MSELF records"); + return false; + } + + std::vector buffer; + + for (const mself_record& rec : recs) + { + const std::string name = vfs::escape(rec.name); + + const u64 pos = rec.get_pos(mself_size); + + if (!pos) + { + mself_log.error("Error extracting %s from MSELF", name); + return false; + } + + buffer.resize(rec.size); + mself.seek(pos); + mself.read(buffer.data(), rec.size); + + if (!fs::write_file(extract_to + name, fs::rewrite, buffer)) + { + mself_log.error("Error creating %s (%s)", extract_to + name, fs::g_tls_error); + return false; + } + + mself_log.success("Extracted '%s' to '%s'", name, extract_to + name); + } + + mself_log.success("Extraction complete!"); + return true; +} diff --git a/rpcs3/Loader/mself.hpp b/rpcs3/Loader/mself.hpp index 637b377d7f..5d2bf0691b 100644 --- a/rpcs3/Loader/mself.hpp +++ b/rpcs3/Loader/mself.hpp @@ -3,6 +3,23 @@ #include "util/types.hpp" #include "util/endian.hpp" +struct mself_record +{ + char name[0x20]; + be_t off; + be_t size; + u8 reserved[0x10]; + + u64 get_pos(u64 file_size) const + { + // Fast sanity check + if (off < file_size && file_size - off >= size) [[likely]] + return off; + + return 0; + } +}; + struct mself_header { nse_t magic; // "MSF\x00" @@ -12,10 +29,10 @@ struct mself_header be_t header_size; // ??? u8 reserved[0x28]; - u32 get_count(u64 file_size) + u32 get_count(u64 file_size) const { // Fast sanity check - if (magic != "MSF"_u32 || ver != u32{1} || this->size != file_size) [[unlikely]] + if (magic != "MSF"_u32 || ver != u32{1} || (file_size - sizeof(mself_header)) / sizeof(mself_record) < count || this->size != file_size) [[unlikely]] return 0; return count; @@ -24,21 +41,6 @@ struct mself_header CHECK_SIZE(mself_header, 0x40); -struct mself_record -{ - char name[0x20]; - be_t off; - be_t size; - u8 reserved[0x10]; - - u64 get_pos(u64 file_size) - { - // Fast sanity check - if (off < file_size && off + size <= file_size) [[likely]] - return off; - - return 0; - } -}; - CHECK_SIZE(mself_record, 0x40); + +bool extract_mself(const std::string& file, const std::string& extract_to); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 13c246b6dd..e48c9c2a1f 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -422,6 +422,7 @@ + @@ -483,6 +484,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 4f0d3f676d..43019c7a57 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -776,6 +776,9 @@ Loader + + Loader + Emu @@ -1912,6 +1915,9 @@ Emu\GPU\RSX\Common + + Loader + diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 91b6b56201..513cc746bb 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -129,6 +129,7 @@ namespace gui const gui_save fd_decrypt_sprx = gui_save(main_window, "lastExplorePathSPRX", ""); const gui_save fd_cg_disasm = gui_save(main_window, "lastExplorePathCGD", ""); const gui_save fd_log_viewer = gui_save(main_window, "lastExplorePathLOG", ""); + const gui_save fd_ext_mself = gui_save(main_window, "lastExplorePathExMSELF", ""); const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false); const gui_save mw_logger = gui_save(main_window, "loggerVisible", true); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index bbf577363e..5170d71c0b 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -41,6 +41,7 @@ #include "rpcs3_version.h" #include "Emu/System.h" #include "Emu/IdManager.h" +#include "Emu/VFS.h" #include "Emu/system_config.h" #include "Crypto/unpkg.h" @@ -49,6 +50,7 @@ #include "Loader/PUP.h" #include "Loader/TAR.h" +#include "Loader/mself.hpp" #include "Utilities/Thread.h" #include "util/sysinfo.hpp" @@ -769,6 +771,25 @@ void main_window::HandlePackageInstallation(QStringList file_paths) } } +void main_window::ExtractMSELF() +{ + const QString path_last_mself = m_gui_settings->GetValue(gui::fd_ext_mself).toString(); + QString file_path = QFileDialog::getOpenFileName(this, tr("Select MSELF To extract"), path_last_mself, tr("All mself files (*.mself);;All files (*.*)")); + + if (file_path.isEmpty()) + { + return; + } + + QString dir = QFileDialog::getExistingDirectory(this, tr("Extraction Directory"), QString{}, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (!dir.isEmpty()) + { + m_gui_settings->SetValue(gui::fd_ext_mself, QFileInfo(file_path).path()); + extract_mself(sstr(file_path), sstr(dir) + '/'); + } +} + void main_window::InstallPup(QString file_path) { if (file_path.isEmpty()) @@ -1919,6 +1940,8 @@ void main_window::CreateConnects() connect(ui->toolsDecryptSprxLibsAct, &QAction::triggered, this, &main_window::DecryptSPRXLibraries); + connect(ui->toolsExtractMSELFAct, &QAction::triggered, this, &main_window::ExtractMSELF); + connect(ui->showDebuggerAct, &QAction::triggered, [this](bool checked) { checked ? m_debugger_frame->show() : m_debugger_frame->hide(); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index bd24bf8865..e3287dd9a9 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -146,6 +146,8 @@ private: void InstallPup(QString filePath = ""); void HandlePupInstallation(QString file_path = ""); + void ExtractMSELF(); + drop_type IsValidFile(const QMimeData& md, QStringList* drop_paths = nullptr); void AddGamesFromDir(const QString& path); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 4b57e140dc..49543fca4e 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -256,6 +256,7 @@ + @@ -626,6 +627,11 @@ Decrypt PS3 Binaries + + + Extract MSELF + + true