Add the ability to add individual files exefs with mod loader (#1766)
Co-authored-by: Ac_K <Acoustik666@gmail.com>
This commit is contained in:
parent
9a808fe484
commit
fb0db32338
2 changed files with 92 additions and 43 deletions
|
@ -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<NsoExecutable> nsos = new List<NsoExecutable>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<NsoExecutable> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue