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 System.Reflection;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||||
|
using static Ryujinx.HLE.HOS.ModLoader;
|
||||||
using ApplicationId = LibHac.Ncm.ApplicationId;
|
using ApplicationId = LibHac.Ncm.ApplicationId;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
|
@ -30,7 +31,22 @@ namespace Ryujinx.HLE.HOS
|
||||||
public class ApplicationLoader
|
public class ApplicationLoader
|
||||||
{
|
{
|
||||||
// Binaries from exefs are loaded into mem in this order. Do not change.
|
// 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 Switch _device;
|
||||||
private readonly ContentManager _contentManager;
|
private readonly ContentManager _contentManager;
|
||||||
|
@ -463,37 +479,48 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
metaData ??= ReadNpdm(codeFs);
|
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; // file doesn't exist, skip
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// 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);
|
_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.");
|
Logger.Warning?.Print(LogClass.Ptc, $"Detected exefs modifications. PPTC disabled.");
|
||||||
}
|
}
|
||||||
|
@ -501,7 +528,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
Graphics.Gpu.GraphicsConfig.TitleId = TitleIdText;
|
||||||
_device.Gpu.HostInitalized.Set();
|
_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);
|
ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, executables: programs);
|
||||||
}
|
}
|
||||||
|
@ -657,4 +684,4 @@ namespace Ryujinx.HLE.HOS
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Ryujinx.HLE.Loaders.Npdm;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
|
@ -381,66 +382,87 @@ namespace Ryujinx.HLE.HOS
|
||||||
return true;
|
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)
|
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;
|
var exeMods = mods.ExefsDirs;
|
||||||
|
|
||||||
BitVector32 stubs = new BitVector32();
|
|
||||||
BitVector32 repls = new BitVector32();
|
|
||||||
|
|
||||||
foreach (var mod in exeMods)
|
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 = ApplicationLoader.ExeFsPrefixes[i];
|
||||||
var nsoName = nso.Name;
|
|
||||||
|
|
||||||
FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName));
|
FileInfo nsoFile = new FileInfo(Path.Combine(mod.Path.FullName, nsoName));
|
||||||
if (nsoFile.Exists)
|
if (nsoFile.Exists)
|
||||||
{
|
{
|
||||||
if (repls[1 << i])
|
if (modLoadResult.Replaces[1 << i])
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ModLoader, $"Multiple replacements to '{nsoName}'");
|
Logger.Warning?.Print(LogClass.ModLoader, $"Multiple replacements to '{nsoName}'");
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
repls[1 << i] = true;
|
modLoadResult.Replaces[1 << i] = true;
|
||||||
|
|
||||||
nsos[i] = new NsoExecutable(nsoFile.OpenRead().AsStorage(), nsoName);
|
nsos[i] = new NsoExecutable(nsoFile.OpenRead().AsStorage(), nsoName);
|
||||||
Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced");
|
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;
|
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");
|
Logger.Info?.Print(LogClass.ModLoader, $" NSO '{nsos[i].Name}' stubbed");
|
||||||
nsos.RemoveAt(i);
|
nsos[i] = null;
|
||||||
replaced = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return replaced;
|
return modLoadResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ApplyNroPatches(NroExecutable nro)
|
internal void ApplyNroPatches(NroExecutable nro)
|
||||||
|
@ -542,4 +564,4 @@ namespace Ryujinx.HLE.HOS
|
||||||
return count > 0;
|
return count > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue