From 9078bb9854cff3f77ac28a15dffff06bb31b3755 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 29 Oct 2018 16:08:03 -0400 Subject: [PATCH 1/3] bis_factory: Add getter for mod dump root for a title ID Equates to yuzu_dir/dump// --- src/core/file_sys/bis_factory.cpp | 9 ++++++- src/core/file_sys/bis_factory.h | 4 ++- .../hle/service/filesystem/filesystem.cpp | 25 ++++++++++++++++--- src/core/hle/service/filesystem/filesystem.h | 1 + 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 76a2b7e86..e29f70b3a 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -8,8 +8,9 @@ namespace FileSys { -BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) +BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_) : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), + dump_root(std::move(dump_root_)), sysnand_cache(std::make_unique<RegisteredCache>( GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), usrnand_cache(std::make_unique<RegisteredCache>( @@ -32,4 +33,10 @@ VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); } +VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const { + if (title_id == 0) + return nullptr; + return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); +} + } // namespace FileSys diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 364d309bd..453c11ad2 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -17,17 +17,19 @@ class RegisteredCache; /// registered caches. class BISFactory { public: - explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); + explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); ~BISFactory(); RegisteredCache* GetSystemNANDContents() const; RegisteredCache* GetUserNANDContents() const; VirtualDir GetModificationLoadRoot(u64 title_id) const; + VirtualDir GetModificationDumpRoot(u64 title_id) const; private: VirtualDir nand_root; VirtualDir load_root; + VirtualDir dump_root; std::unique_ptr<RegisteredCache> sysnand_cache; std::unique_ptr<RegisteredCache> usrnand_cache; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index e32a7c48e..234a8687b 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -360,6 +360,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { return bis_factory->GetModificationLoadRoot(title_id); } +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { + LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetModificationDumpRoot(title_id); +} + void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; @@ -373,13 +382,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { FileSys::Mode::ReadWrite); auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), FileSys::Mode::ReadWrite); + auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), + FileSys::Mode::ReadWrite); - if (bis_factory == nullptr) - bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); - if (save_data_factory == nullptr) + if (bis_factory == nullptr) { + bis_factory = + std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory); + } + + if (save_data_factory == nullptr) { save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); - if (sdmc_factory == nullptr) + } + + if (sdmc_factory == nullptr) { sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory)); + } } void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6ca5c5636..b18652a68 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -54,6 +54,7 @@ FileSys::RegisteredCache* GetUserNANDContents(); FileSys::RegisteredCache* GetSDMCContents(); FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); +FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function // above is called. From 48eb3742b9f84108538e2a41019aa9d5d47f7352 Mon Sep 17 00:00:00 2001 From: Zach Hilman <zachhilman@gmail.com> Date: Mon, 29 Oct 2018 16:09:08 -0400 Subject: [PATCH 2/3] settings: Add setting to control NSO dumping Also adds UI option in Debug > Dump section, with the idea later things to be dumped (i.e. other game data or textures, etc) will use the same group box. --- src/core/settings.h | 1 + src/yuzu/configuration/config.cpp | 2 ++ src/yuzu/configuration/configure_debug.cpp | 2 ++ src/yuzu/configuration/configure_debug.ui | 21 ++++++++++++++++++++- src/yuzu_cmd/config.cpp | 1 + src/yuzu_cmd/default_ini.h | 2 ++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/core/settings.h b/src/core/settings.h index b5aeff29b..0af9dd416 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -157,6 +157,7 @@ struct Values { bool use_gdbstub; u16 gdbstub_port; std::string program_args; + bool dump_nso; // WebService bool enable_telemetry; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index d4fd60a73..b85011cd6 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -145,6 +145,7 @@ void Config::ReadValues() { Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool(); Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString(); + Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); qt_config->endGroup(); qt_config->beginGroup("WebService"); @@ -283,6 +284,7 @@ void Config::SaveValues() { qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub); qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args)); + qt_config->setValue("dump_nso", Settings::values.dump_nso); qt_config->endGroup(); qt_config->beginGroup("WebService"); diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 9e765fc93..fd5876b41 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() { ui->toggle_console->setChecked(UISettings::values.show_console); ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); + ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); } void ConfigureDebug::applyConfiguration() { @@ -42,6 +43,7 @@ void ConfigureDebug::applyConfiguration() { UISettings::values.show_console = ui->toggle_console->isChecked(); Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); + Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index ff4987604..9c5b702f8 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>300</height> + <height>357</height> </rect> </property> <property name="windowTitle"> @@ -129,6 +129,25 @@ </layout> </widget> </item> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>Dump</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QCheckBox" name="dump_decompressed_nso"> + <property name="whatsThis"> + <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> + </property> + <property name="text"> + <string>Dump Decompressed NSOs</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index b456266a6..f20b3a1b3 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -141,6 +141,7 @@ void Config::ReadValues() { Settings::values.gdbstub_port = static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); + Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); // Web Service Settings::values.enable_telemetry = diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index e0b223cd6..d83498358 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -201,6 +201,8 @@ log_filter = *:Trace # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 +# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them +dump_nso=false [WebService] # Whether or not to enable telemetry From 1c0226815d453acfb5e1fd766765b43fd447c15c Mon Sep 17 00:00:00 2001 From: Zach Hilman <zachhilman@gmail.com> Date: Mon, 29 Oct 2018 16:10:16 -0400 Subject: [PATCH 3/3] patch_manager: Add support for dumping decompressed NSOs When enabled in settings, PatchNSO will dump the unmodified NSO that it was passed to a file named <build id>.nso in the dump root for the current title ID. --- src/core/file_sys/patch_manager.cpp | 13 +++++++++++++ src/core/loader/nso.cpp | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index cb457b987..3a1623ca0 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -19,6 +19,7 @@ #include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "core/settings.h" namespace FileSys { @@ -119,6 +120,18 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { const auto build_id_raw = Common::HexArrayToString(header.build_id); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); + if (Settings::values.dump_nso) { + LOG_INFO(Loader, "Dumping NSO for build_id={}, title_id={:016X}", build_id, title_id); + const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); + if (dump_dir != nullptr) { + const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); + const auto file = nso_dir->CreateFile(fmt::format("{}.nso", build_id)); + + file->Resize(nso.size()); + file->WriteBytes(nso); + } + } + LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 68efca5c0..aaf006309 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -154,7 +154,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAd program_image.resize(image_size); // Apply patches if necessary - if (pm && pm->HasNSOPatch(nso_header.build_id)) { + if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { std::vector<u8> pi_header(program_image.size() + 0x100); std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());