Add XCI and NCA loading support
This commit is contained in:
parent
76a3172f17
commit
70f6514716
5 changed files with 179 additions and 3 deletions
|
@ -7,6 +7,8 @@ using Ryujinx.HLE.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using LibHac;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
|
@ -30,6 +32,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
internal KEvent VsyncEvent { get; private set; }
|
internal KEvent VsyncEvent { get; private set; }
|
||||||
|
|
||||||
|
internal Keyset Keyset { get; private set; }
|
||||||
|
|
||||||
public Horizon(Switch Device)
|
public Horizon(Switch Device)
|
||||||
{
|
{
|
||||||
this.Device = Device;
|
this.Device = Device;
|
||||||
|
@ -52,6 +56,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
Font = new SharedFontManager(Device, FontSharedMem.PA);
|
Font = new SharedFontManager(Device, FontSharedMem.PA);
|
||||||
|
|
||||||
VsyncEvent = new KEvent();
|
VsyncEvent = new KEvent();
|
||||||
|
|
||||||
|
LoadDefaultKeyset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
|
@ -119,6 +125,118 @@ namespace Ryujinx.HLE.HOS
|
||||||
MainProcess.Run();
|
MainProcess.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadXci(string xciFile)
|
||||||
|
{
|
||||||
|
var file = new FileStream(xciFile, FileMode.Open, FileAccess.Read);
|
||||||
|
var xci = new Xci(Keyset, file);
|
||||||
|
var nca = GetXciMainNca(xci);
|
||||||
|
LoadNca(nca);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Nca GetXciMainNca(Xci xci)
|
||||||
|
{
|
||||||
|
if (xci.SecurePartition == null)
|
||||||
|
{
|
||||||
|
Device.Log.PrintError(LogClass.Loader, "Could not find XCI secure partition");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nca mainNca = null;
|
||||||
|
|
||||||
|
foreach (var fileEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca")))
|
||||||
|
{
|
||||||
|
var ncaStream = xci.SecurePartition.OpenFile(fileEntry);
|
||||||
|
var nca = new Nca(Keyset, ncaStream, true);
|
||||||
|
|
||||||
|
if (nca.Header.ContentType == ContentType.Program)
|
||||||
|
{
|
||||||
|
mainNca = nca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainNca;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadNca(string ncaFile)
|
||||||
|
{
|
||||||
|
var file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
|
||||||
|
var nca = new Nca(Keyset, file, true);
|
||||||
|
LoadNca(nca);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadNca(Nca nca)
|
||||||
|
{
|
||||||
|
NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x.Type == SectionType.Romfs);
|
||||||
|
NcaSection exefsSection = nca.Sections.FirstOrDefault(x => x.IsExefs);
|
||||||
|
|
||||||
|
if (exefsSection == null)
|
||||||
|
{
|
||||||
|
Device.Log.PrintError(LogClass.Loader, "No ExeFS found in NCA");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (romfsSection == null)
|
||||||
|
{
|
||||||
|
Device.Log.PrintError(LogClass.Loader, "No RomFS found in NCA");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var romfsStream = nca.OpenSection(romfsSection.SectionNum, false);
|
||||||
|
Device.FileSystem.SetRomFs(romfsStream);
|
||||||
|
|
||||||
|
var exefsStream = nca.OpenSection(exefsSection.SectionNum, false);
|
||||||
|
var exefs = new Pfs(exefsStream);
|
||||||
|
|
||||||
|
Npdm MetaData = null;
|
||||||
|
|
||||||
|
if (exefs.FileExists("main.npdm"))
|
||||||
|
{
|
||||||
|
Device.Log.PrintInfo(LogClass.Loader, "Loading main.npdm...");
|
||||||
|
MetaData = new Npdm(exefs.OpenFile("main.npdm"));
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Process MainProcess = MakeProcess(MetaData);
|
||||||
|
|
||||||
|
void LoadNso(string filename)
|
||||||
|
{
|
||||||
|
foreach (var File in exefs.Files.Where(x => x.Name.StartsWith(filename)))
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(File.Name) != string.Empty)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device.Log.PrintInfo(LogClass.Loader, $"Loading {filename}...");
|
||||||
|
|
||||||
|
string Name = Path.GetFileNameWithoutExtension(File.Name);
|
||||||
|
|
||||||
|
Nso Program = new Nso(exefs.OpenFile(File), Name);
|
||||||
|
|
||||||
|
MainProcess.LoadProgram(Program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MainProcess.MetaData.Is64Bits)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("32-bit titles are unsupported!");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadNso("rtld");
|
||||||
|
|
||||||
|
MainProcess.SetEmptyArgs();
|
||||||
|
|
||||||
|
LoadNso("main");
|
||||||
|
LoadNso("subsdk");
|
||||||
|
LoadNso("sdk");
|
||||||
|
|
||||||
|
MainProcess.Run();
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FilePath)
|
public void LoadProgram(string FilePath)
|
||||||
{
|
{
|
||||||
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
||||||
|
@ -156,6 +274,35 @@ namespace Ryujinx.HLE.HOS
|
||||||
MainProcess.Run(IsNro);
|
MainProcess.Run(IsNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadDefaultKeyset()
|
||||||
|
{
|
||||||
|
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
|
var homeKeyFile = Path.Combine(home, ".switch", "prod.keys");
|
||||||
|
var homeTitleKeyFile = Path.Combine(home, ".switch", "title.keys");
|
||||||
|
var homeConsoleKeyFile = Path.Combine(home, ".switch", "console.keys");
|
||||||
|
|
||||||
|
string keyFile = null;
|
||||||
|
string titleKeyFile = null;
|
||||||
|
string consoleKeyFile = null;
|
||||||
|
|
||||||
|
if (File.Exists(homeKeyFile))
|
||||||
|
{
|
||||||
|
keyFile = homeKeyFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(homeTitleKeyFile))
|
||||||
|
{
|
||||||
|
titleKeyFile = homeTitleKeyFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(homeConsoleKeyFile))
|
||||||
|
{
|
||||||
|
consoleKeyFile = homeConsoleKeyFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Keyset = ExternalKeys.ReadKeyFile(keyFile, titleKeyFile, consoleKeyFile);
|
||||||
|
}
|
||||||
|
|
||||||
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
||||||
|
|
||||||
private Process MakeProcess(Npdm MetaData = null)
|
private Process MakeProcess(Npdm MetaData = null)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
<ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
|
||||||
|
<PackageReference Include="LibHac" Version="0.1.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -61,6 +61,16 @@ namespace Ryujinx.HLE
|
||||||
System.LoadCart(ExeFsDir, RomFsFile);
|
System.LoadCart(ExeFsDir, RomFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadXci(string xciFile)
|
||||||
|
{
|
||||||
|
System.LoadXci(xciFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadNca(string ncaFile)
|
||||||
|
{
|
||||||
|
System.LoadNca(ncaFile);
|
||||||
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FileName)
|
public void LoadProgram(string FileName)
|
||||||
{
|
{
|
||||||
System.LoadProgram(FileName);
|
System.LoadProgram(FileName);
|
||||||
|
|
|
@ -17,6 +17,12 @@ namespace Ryujinx.HLE
|
||||||
RomFs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
RomFs = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetRomFs(Stream romfsStream)
|
||||||
|
{
|
||||||
|
RomFs?.Close();
|
||||||
|
RomFs = romfsStream;
|
||||||
|
}
|
||||||
|
|
||||||
public string GetFullPath(string BasePath, string FileName)
|
public string GetFullPath(string BasePath, string FileName)
|
||||||
{
|
{
|
||||||
if (FileName.StartsWith("//"))
|
if (FileName.StartsWith("//"))
|
||||||
|
|
|
@ -50,9 +50,21 @@ namespace Ryujinx
|
||||||
}
|
}
|
||||||
else if (File.Exists(args[0]))
|
else if (File.Exists(args[0]))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Loading as homebrew.");
|
switch (Path.GetExtension(args[0]))
|
||||||
|
{
|
||||||
Device.LoadProgram(args[0]);
|
case ".xci":
|
||||||
|
Console.WriteLine("Loading as XCI.");
|
||||||
|
Device.LoadXci(args[0]);
|
||||||
|
break;
|
||||||
|
case ".nca":
|
||||||
|
Console.WriteLine("Loading as NCA.");
|
||||||
|
Device.LoadNca(args[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Console.WriteLine("Loading as homebrew.");
|
||||||
|
Device.LoadProgram(args[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue