From fb0db323385047afa179d3fcf1d5eee627eedfdc Mon Sep 17 00:00:00 2001 From: Somebody Whoisbored <13044396+shadowninja108@users.noreply.github.com> Date: Tue, 29 Dec 2020 12:54:32 -0700 Subject: [PATCH] Add the ability to add individual files exefs with mod loader (#1766) Co-authored-by: Ac_K --- Ryujinx.HLE/HOS/ApplicationLoader.cs | 71 +++++++++++++++++++--------- Ryujinx.HLE/HOS/ModLoader.cs | 64 +++++++++++++++++-------- 2 files changed, 92 insertions(+), 43 deletions(-) diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs index 9bf9a7bf..2c4708d7 100644 --- a/Ryujinx.HLE/HOS/ApplicationLoader.cs +++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Reflection; using static LibHac.Fs.ApplicationSaveDataManagement; +using static Ryujinx.HLE.HOS.ModLoader; using ApplicationId = LibHac.Ncm.ApplicationId; namespace Ryujinx.HLE.HOS @@ -30,7 +31,22 @@ namespace Ryujinx.HLE.HOS public class ApplicationLoader { // Binaries from exefs are loaded into mem in this order. Do not change. - private static readonly string[] ExeFsPrefixes = { "rtld", "main", "subsdk*", "sdk" }; + internal static readonly string[] ExeFsPrefixes = + { + "rtld", + "main", + "subsdk0", + "subsdk1", + "subsdk2", + "subsdk3", + "subsdk4", + "subsdk5", + "subsdk6", + "subsdk7", + "subsdk8", + "subsdk9", + "sdk" + }; private readonly Switch _device; private readonly ContentManager _contentManager; @@ -463,37 +479,48 @@ namespace Ryujinx.HLE.HOS metaData ??= ReadNpdm(codeFs); - List nsos = new List(); + NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length]; - foreach (string exePrefix in ExeFsPrefixes) // Load binaries with standard prefixes + for(int i = 0; i < nsos.Length; i++) { - foreach (DirectoryEntryEx file in codeFs.EnumerateEntries("/", exePrefix)) + string name = ExeFsPrefixes[i]; + + if (!codeFs.FileExists(name)) { - if (Path.GetExtension(file.Name) != string.Empty) - { - continue; - } - - Logger.Info?.Print(LogClass.Loader, $"Loading {file.Name}..."); - - codeFs.OpenFile(out IFile nsoFile, file.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - NsoExecutable nso = new NsoExecutable(nsoFile.AsStorage(), file.Name); - - nsos.Add(nso); + continue; // file doesn't exist, skip } + + Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); + + codeFs.OpenFile(out IFile nsoFile, $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + nsos[i] = new NsoExecutable(nsoFile.AsStorage(), name); } // ExeFs file replacements - bool modified = _fileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); + ModLoadResult modLoadResult = _fileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); - NsoExecutable[] programs = nsos.ToArray(); + // collect the nsos, ignoring ones that aren't used + NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); - modified |= _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); + // take the npdm from mods if present + if (modLoadResult.Npdm != null) + { + metaData = modLoadResult.Npdm; + } + + bool hasPatches = _fileSystem.ModLoader.ApplyNsoPatches(TitleId, programs); _contentManager.LoadEntries(_device); - if (_device.System.EnablePtc && modified) + bool usePtc = _device.System.EnablePtc; + + // don't use PTC if exefs files have been replaced + usePtc &= !modLoadResult.Modified; + // don't use PTC if exefs files have been patched + usePtc &= !hasPatches; + + if (_device.System.EnablePtc && !usePtc) { Logger.Warning?.Print(LogClass.Ptc, $"Detected exefs modifications. PPTC disabled."); } @@ -501,7 +528,7 @@ namespace Ryujinx.HLE.HOS Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText; _device.Gpu.HostInitalized.Set(); - Ptc.Initialize(TitleIdText, DisplayVersion, _device.System.EnablePtc && !modified); + Ptc.Initialize(TitleIdText, DisplayVersion, usePtc); ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, executables: programs); } @@ -657,4 +684,4 @@ namespace Ryujinx.HLE.HOS return resultCode; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs index 3d701525..d2f80ebd 100644 --- a/Ryujinx.HLE/HOS/ModLoader.cs +++ b/Ryujinx.HLE/HOS/ModLoader.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.IO; +using Ryujinx.HLE.Loaders.Npdm; namespace Ryujinx.HLE.HOS { @@ -381,66 +382,87 @@ namespace Ryujinx.HLE.HOS return true; } - internal bool ApplyExefsMods(ulong titleId, List nsos) + public struct ModLoadResult { + public BitVector32 Stubs; + public BitVector32 Replaces; + public Npdm Npdm; + + public bool Modified => (Stubs.Data | Replaces.Data) != 0; + } + + internal ModLoadResult ApplyExefsMods(ulong titleId, NsoExecutable[] nsos) + { + ModLoadResult modLoadResult = new ModLoadResult + { + Stubs = new BitVector32(), + Replaces = new BitVector32() + }; + if (!AppMods.TryGetValue(titleId, out ModCache mods) || mods.ExefsDirs.Count == 0) { - return false; + return modLoadResult; } - bool replaced = false; - if (nsos.Count > 32) + if (nsos.Length != ApplicationLoader.ExeFsPrefixes.Length) { - throw new ArgumentOutOfRangeException("NSO Count is more than 32"); + throw new ArgumentOutOfRangeException("NSO Count is incorrect"); } var exeMods = mods.ExefsDirs; - BitVector32 stubs = new BitVector32(); - BitVector32 repls = new BitVector32(); - foreach (var mod in exeMods) { - for (int i = 0; i < nsos.Count; ++i) + for (int i = 0; i < ApplicationLoader.ExeFsPrefixes.Length; ++i) { - var nso = nsos[i]; - var nsoName = nso.Name; + var nsoName = ApplicationLoader.ExeFsPrefixes[i]; FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName)); if (nsoFile.Exists) { - if (repls[1 << i]) + if (modLoadResult.Replaces[1 << i]) { Logger.Warning?.Print(LogClass.ModLoader, $"Multiple replacements to '{nsoName}'"); + continue; } - repls[1 << i] = true; + modLoadResult.Replaces[1 << i] = true; nsos[i] = new NsoExecutable(nsoFile.OpenRead().AsStorage(), nsoName); Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced"); + } - replaced = true; + modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); + } + + FileInfo npdmFile = new FileInfo(Path.Combine(mod.Path.FullName, "main.npdm")); + if(npdmFile.Exists) + { + if(modLoadResult.Npdm != null) + { + Logger.Warning?.Print(LogClass.ModLoader, "Multiple replacements to 'main.npdm'"); continue; } - stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension)); + modLoadResult.Npdm = new Npdm(npdmFile.OpenRead()); + + Logger.Info?.Print(LogClass.ModLoader, $"main.npdm replaced"); } } - for (int i = nsos.Count - 1; i >= 0; --i) + for (int i = ApplicationLoader.ExeFsPrefixes.Length - 1; i >= 0; --i) { - if (stubs[1 << i] && !repls[1 << i]) // Prioritizes replacements over stubs + if (modLoadResult.Stubs[1 << i] && !modLoadResult.Replaces[1 << i]) // Prioritizes replacements over stubs { Logger.Info?.Print(LogClass.ModLoader, $" NSO '{nsos[i].Name}' stubbed"); - nsos.RemoveAt(i); - replaced = true; + nsos[i] = null; } } - return replaced; + return modLoadResult; } internal void ApplyNroPatches(NroExecutable nro) @@ -542,4 +564,4 @@ namespace Ryujinx.HLE.HOS return count > 0; } } -} \ No newline at end of file +}