Просмотр исходного кода

Merge pull request #1747 from DarkLordZach/exefs-lfs

patch_manager: Add support for applying LayeredFS patches to ExeFS
bunnei 7 лет назад
Родитель
Сommit
67ff974387

+ 47 - 2
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<const char*, 14> 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);
@@ -57,6 +62,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
@@ -70,6 +84,30 @@ 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<VirtualDir> 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;
 }
 
@@ -314,18 +352,25 @@ std::map<std::string, std::string, std::less<>> 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");

+ 1 - 0
src/core/settings.h

@@ -403,6 +403,7 @@ struct Values {
     bool use_gdbstub;
     u16 gdbstub_port;
     std::string program_args;
+    bool dump_exefs;
     bool dump_nso;
 
     // WebService

+ 2 - 0
src/yuzu/configuration/config.cpp

@@ -432,6 +432,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();
 
@@ -638,6 +639,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();
 

+ 2 - 0
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;

+ 10 - 0
src/yuzu/configuration/configure_debug.ui

@@ -145,6 +145,16 @@
         </property>
        </widget>
       </item>
+      <item>
+       <widget class="QCheckBox" name="dump_exefs">
+        <property name="whatsThis">
+         <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
+        </property>
+        <property name="text">
+         <string>Dump ExeFS</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>

+ 1 - 0
src/yuzu_cmd/config.cpp

@@ -366,6 +366,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_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
     Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
 
     // Web Service

+ 2 - 0
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