Merge pull request #1747 from DarkLordZach/exefs-lfs
patch_manager: Add support for applying LayeredFS patches to ExeFS
This commit is contained in:
commit
67ff974387
|
@ -26,6 +26,11 @@ namespace FileSys {
|
||||||
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
|
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
|
||||||
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
|
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 {
|
struct NSOBuildHeader {
|
||||||
u32_le magic;
|
u32_le magic;
|
||||||
INSERT_PADDING_BYTES(0x3C);
|
INSERT_PADDING_BYTES(0x3C);
|
||||||
|
@ -57,6 +62,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
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();
|
const auto installed = Service::FileSystem::GetUnionContents();
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
|
@ -70,6 +84,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
exefs = update->GetExeFS();
|
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;
|
return exefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,18 +352,25 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||||
if (IsDirValidAndNonEmpty(exefs_dir)) {
|
if (IsDirValidAndNonEmpty(exefs_dir)) {
|
||||||
bool ips = false;
|
bool ips = false;
|
||||||
bool ipswitch = false;
|
bool ipswitch = false;
|
||||||
|
bool layeredfs = false;
|
||||||
|
|
||||||
for (const auto& file : exefs_dir->GetFiles()) {
|
for (const auto& file : exefs_dir->GetFiles()) {
|
||||||
if (file->GetExtension() == "ips")
|
if (file->GetExtension() == "ips") {
|
||||||
ips = true;
|
ips = true;
|
||||||
else if (file->GetExtension() == "pchtxt")
|
} else if (file->GetExtension() == "pchtxt") {
|
||||||
ipswitch = true;
|
ipswitch = true;
|
||||||
|
} else if (std::find(EXEFS_FILE_NAMES.begin(), EXEFS_FILE_NAMES.end(),
|
||||||
|
file->GetName()) != EXEFS_FILE_NAMES.end()) {
|
||||||
|
layeredfs = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ips)
|
if (ips)
|
||||||
AppendCommaIfNotEmpty(types, "IPS");
|
AppendCommaIfNotEmpty(types, "IPS");
|
||||||
if (ipswitch)
|
if (ipswitch)
|
||||||
AppendCommaIfNotEmpty(types, "IPSwitch");
|
AppendCommaIfNotEmpty(types, "IPSwitch");
|
||||||
|
if (layeredfs)
|
||||||
|
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||||
}
|
}
|
||||||
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
|
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
|
||||||
AppendCommaIfNotEmpty(types, "LayeredFS");
|
AppendCommaIfNotEmpty(types, "LayeredFS");
|
||||||
|
|
|
@ -403,6 +403,7 @@ struct Values {
|
||||||
bool use_gdbstub;
|
bool use_gdbstub;
|
||||||
u16 gdbstub_port;
|
u16 gdbstub_port;
|
||||||
std::string program_args;
|
std::string program_args;
|
||||||
|
bool dump_exefs;
|
||||||
bool dump_nso;
|
bool dump_nso;
|
||||||
|
|
||||||
// WebService
|
// WebService
|
||||||
|
|
|
@ -432,6 +432,7 @@ void Config::ReadValues() {
|
||||||
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
|
||||||
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
|
||||||
Settings::values.program_args = qt_config->value("program_args", "").toString().toStdString();
|
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();
|
Settings::values.dump_nso = qt_config->value("dump_nso", false).toBool();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
@ -638,6 +639,7 @@ void Config::SaveValues() {
|
||||||
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
|
||||||
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
|
||||||
qt_config->setValue("program_args", QString::fromStdString(Settings::values.program_args));
|
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->setValue("dump_nso", Settings::values.dump_nso);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ void ConfigureDebug::setConfiguration() {
|
||||||
ui->toggle_console->setChecked(UISettings::values.show_console);
|
ui->toggle_console->setChecked(UISettings::values.show_console);
|
||||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
|
||||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
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);
|
ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ void ConfigureDebug::applyConfiguration() {
|
||||||
UISettings::values.show_console = ui->toggle_console->isChecked();
|
UISettings::values.show_console = ui->toggle_console->isChecked();
|
||||||
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
|
||||||
Settings::values.program_args = ui->homebrew_args_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();
|
Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
|
||||||
Debugger::ToggleConsole();
|
Debugger::ToggleConsole();
|
||||||
Log::Filter filter;
|
Log::Filter filter;
|
||||||
|
|
|
@ -145,6 +145,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -366,6 +366,7 @@ void Config::ReadValues() {
|
||||||
Settings::values.gdbstub_port =
|
Settings::values.gdbstub_port =
|
||||||
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
|
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
|
||||||
Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
|
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);
|
Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
|
||||||
|
|
||||||
// Web Service
|
// Web Service
|
||||||
|
|
|
@ -206,6 +206,8 @@ log_filter = *:Trace
|
||||||
# Port for listening to GDB connections.
|
# Port for listening to GDB connections.
|
||||||
use_gdbstub=false
|
use_gdbstub=false
|
||||||
gdbstub_port=24689
|
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
|
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
|
||||||
dump_nso=false
|
dump_nso=false
|
||||||
|
|
||||||
|
|
Reference in New Issue