From 17d8e25cbfd0c8937c8791bc1941af85f9541666 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 20 Nov 2018 17:49:09 -0500 Subject: [PATCH 1/3] settings: Add option to dump ExeFS of games upon launch When enabled, all exefs(es) will be copied to yuzu/dump//exefs. --- src/core/file_sys/patch_manager.cpp | 9 +++++++++ src/core/settings.h | 1 + src/yuzu/configuration/config.cpp | 2 ++ src/yuzu/configuration/configure_debug.cpp | 2 ++ src/yuzu/configuration/configure_debug.ui | 10 ++++++++++ src/yuzu_cmd/config.cpp | 1 + src/yuzu_cmd/default_ini.h | 2 ++ 7 files changed, 27 insertions(+) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 8d062eb3e..f56b1c773 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -57,6 +57,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { if (exefs == nullptr) return exefs; + if (Settings::values.dump_exefs) { + LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); + const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); + if (dump_dir != nullptr) { + const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); + VfsRawCopyD(exefs, exefs_dir); + } + } + const auto installed = Service::FileSystem::GetUnionContents(); // Game Updates diff --git a/src/core/settings.h b/src/core/settings.h index e424479f2..9767bc162 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -159,6 +159,7 @@ struct Values { bool use_gdbstub; u16 gdbstub_port; std::string program_args; + bool dump_exefs; bool dump_nso; // WebService diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index be69fb831..e750cbe7d 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -153,6 +153,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_exefs = qt_config->value("dump_exefs", false).toBool(); Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool(); qt_config->endGroup(); @@ -297,6 +298,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_exefs", Settings::values.dump_exefs); qt_config->setValue("dump_nso", Settings::values.dump_nso); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index fd5876b41..aa7de7b54 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_exefs->setChecked(Settings::values.dump_exefs); ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); } @@ -43,6 +44,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_exefs = ui->dump_exefs->isChecked(); Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); Debugger::ToggleConsole(); Log::Filter filter; diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 9c5b702f8..f51f8702b 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -145,6 +145,16 @@ + + + + When checked, any game that yuzu loads will have its ExeFS dumped + + + Dump ExeFS + + + diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 9cc409fd5..b443bfba7 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -148,6 +148,7 @@ void Config::ReadValues() { Settings::values.gdbstub_port = static_cast(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); + Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); // Web Service diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index ecf625e7b..d73669f36 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -206,6 +206,8 @@ log_filter = *:Trace # Port for listening to GDB connections. use_gdbstub=false gdbstub_port=24689 +# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them +dump_exefs=false # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them dump_nso=false From da6d4cde567695d22fefc152d2d913f54e5ece68 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 20 Nov 2018 17:51:00 -0500 Subject: [PATCH 2/3] patch_manager: Apply LayeredExeFS patches This will scan the /exefs dir for all files and then layer those on top of the game's exefs and use this as the new exefs. This allows for overriding of the compressed NSOs or adding new files. This does use the same dir as IPS/IPSwitch patch, but since the loader will not look for those they are ignored. --- src/core/file_sys/patch_manager.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index f56b1c773..ccc4f3061 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -79,6 +79,31 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { exefs = update->GetExeFS(); } + // LayeredExeFS + const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); + if (load_dir != nullptr && load_dir->GetSize() > 0) { + + auto patch_dirs = load_dir->GetSubdirectories(); + std::sort( + patch_dirs.begin(), patch_dirs.end(), + [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); + + std::vector layers; + layers.reserve(patch_dirs.size() + 1); + for (const auto& subdir : patch_dirs) { + auto exefs_dir = subdir->GetSubdirectory("exefs"); + if (exefs_dir != nullptr) + layers.push_back(std::move(exefs_dir)); + } + layers.push_back(exefs); + + auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); + if (layered != nullptr) { + LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); + exefs = std::move(layered); + } + } + return exefs; } From 54e74b3572d3f61fdbaf4f2ddb8b54e2c06a5ab8 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 20 Nov 2018 19:22:26 -0500 Subject: [PATCH 3/3] patch_manager: Show LayeredExeFS patch in add-ons column The decision was made to name them LayeredExeFS instead of just LayeredFS to differentiate from normal RomFS-based mods. The name may be long/unweildy, but conveys the meaning well. --- src/core/file_sys/patch_manager.cpp | 17 ++++++++++++++--- src/yuzu/configuration/configure_debug.ui | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index ccc4f3061..e8df08724 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -26,6 +26,11 @@ namespace FileSys { constexpr u64 SINGLE_BYTE_MODULUS = 0x100; constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; +constexpr std::array EXEFS_FILE_NAMES{ + "main", "main.npdm", "rtld", "sdk", "subsdk0", "subsdk1", "subsdk2", + "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", +}; + struct NSOBuildHeader { u32_le magic; INSERT_PADDING_BYTES(0x3C); @@ -82,7 +87,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // LayeredExeFS const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); if (load_dir != nullptr && load_dir->GetSize() > 0) { - auto patch_dirs = load_dir->GetSubdirectories(); std::sort( patch_dirs.begin(), patch_dirs.end(), @@ -348,18 +352,25 @@ std::map> PatchManager::GetPatchVersionNam if (IsDirValidAndNonEmpty(exefs_dir)) { bool ips = false; bool ipswitch = false; + bool layeredfs = false; for (const auto& file : exefs_dir->GetFiles()) { - if (file->GetExtension() == "ips") + if (file->GetExtension() == "ips") { ips = true; - else if (file->GetExtension() == "pchtxt") + } else if (file->GetExtension() == "pchtxt") { ipswitch = true; + } else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(), + file->GetName()) != EXEFS_FILE_NAMES.end()) { + layeredfs = true; + } } if (ips) AppendCommaIfNotEmpty(types, "IPS"); if (ipswitch) AppendCommaIfNotEmpty(types, "IPSwitch"); + if (layeredfs) + AppendCommaIfNotEmpty(types, "LayeredExeFS"); } if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) AppendCommaIfNotEmpty(types, "LayeredFS"); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index f51f8702b..758a92335 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -148,7 +148,7 @@ - When checked, any game that yuzu loads will have its ExeFS dumped + When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory. Dump ExeFS