Use libhac for loading NSOs and KIPs

This commit is contained in:
EliseZeroTwo 2020-03-26 14:58:19 +01:00
parent 0dd38028cb
commit 0c762455cc
4 changed files with 53 additions and 190 deletions

View file

@ -271,7 +271,7 @@ namespace Ryujinx.HLE.HOS
public void LoadKip(string kipFile)
{
using (FileStream fs = new FileStream(kipFile, FileMode.Open))
using (IStorage fs = new LocalStorage(kipFile, FileAccess.Read))
{
ProgramLoader.LoadKernelInitalProcess(this, new KernelInitialProcess(fs));
}
@ -544,8 +544,8 @@ namespace Ryujinx.HLE.HOS
Logger.PrintInfo(LogClass.Loader, $"Loading {file.Name}...");
codeFs.OpenFile(out IFile nsoFile, file.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
NxStaticObject staticObject = new NxStaticObject(nsoFile.AsStream());
NxStaticObject staticObject = new NxStaticObject(nsoFile.AsStorage());
staticObjects.Add(staticObject);
}
@ -569,12 +569,12 @@ namespace Ryujinx.HLE.HOS
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
FileStream input = new FileStream(filePath, FileMode.Open);
IExecutable staticObject;
if (isNro)
{
FileStream input = new FileStream(filePath, FileMode.Open);
NxRelocatableObject obj = new NxRelocatableObject(input);
staticObject = obj;
@ -648,7 +648,7 @@ namespace Ryujinx.HLE.HOS
}
else
{
staticObject = new NxStaticObject(input);
staticObject = new NxStaticObject(new LocalStorage(filePath, FileAccess.Read));
}
ContentManager.LoadEntries(Device);

View file

@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Npdm;
using LibHac;
namespace Ryujinx.HLE.HOS
{
@ -288,7 +289,6 @@ namespace Ryujinx.HLE.HOS
}
size = BitUtils.AlignUp(size, KMemoryManager.PageSize);
return process.MemoryManager.SetProcessMemoryPermission(address, size, permission);
}
@ -306,6 +306,7 @@ namespace Ryujinx.HLE.HOS
return result;
}
return SetProcessMemoryPermission(dataStart, end - dataStart, MemoryPermission.ReadAndWrite);
}
}

View file

@ -1,43 +1,46 @@
using Ryujinx.HLE.Loaders.Compression;
using System.IO;
using LibHac;
using LibHac.Fs;
namespace Ryujinx.HLE.Loaders.Executables
{
class KernelInitialProcess : IExecutable
{
public string Name { get; private set; }
Kip kip;
public string Name => kip.Header.Name;
public ulong TitleId { get; private set; }
public ulong TitleId => kip.Header.TitleId;
public int ProcessCategory { get; private set; }
public int ProcessCategory => kip.Header.ProcessCategory;
public byte MainThreadPriority { get; private set; }
public byte DefaultProcessorId { get; private set; }
public byte MainThreadPriority => kip.Header.MainThreadPriority;
public byte DefaultProcessorId => kip.Header.DefaultCore;
public bool Is64Bits => (kip.Header.Flags & 0x08) != 0;
public bool Addr39Bits => (kip.Header.Flags & 0x10) != 0;
public bool IsService => (kip.Header.Flags & 0x20) != 0;
public bool Is64Bits { get; private set; }
public bool Addr39Bits { get; private set; }
public bool IsService { get; private set; }
public byte[] Text { get; private set; }
public byte[] Ro { get; private set; }
public byte[] Ro { get; private set; }
public byte[] Data { get; private set; }
public int TextOffset { get; private set; }
public int RoOffset { get; private set; }
public int DataOffset { get; private set; }
public int BssOffset { get; private set; }
public int BssSize { get; private set; }
public int TextOffset => kip.Header.Sections[0].OutOffset;
public int RoOffset => kip.Header.Sections[1].OutOffset;
public int DataOffset => kip.Header.Sections[2].OutOffset;
public int BssOffset => kip.Header.Sections[3].OutOffset;
public int BssSize => kip.Header.Sections[3].DecompressedSize;
public int MainThreadStackSize { get; private set; }
public int[] Capabilities { get; private set; }
public int MainThreadStackSize => kip.Header.Sections[1].Attribute;
public int[] Capabilities { get; set; }
private struct SegmentHeader
{
public int Offset { get; private set; }
public int Offset { get; private set; }
public int DecompressedSize { get; private set; }
public int CompressedSize { get; private set; }
public int Attribute { get; private set; }
public int CompressedSize { get; private set; }
public int Attribute { get; private set; }
public SegmentHeader(
int offset,
@ -45,103 +48,28 @@ namespace Ryujinx.HLE.Loaders.Executables
int compressedSize,
int attribute)
{
Offset = offset;
Offset = offset;
DecompressedSize = decompressedSize;
CompressedSize = compressedSize;
Attribute = attribute;
CompressedSize = compressedSize;
Attribute = attribute;
}
}
public KernelInitialProcess(Stream input)
public KernelInitialProcess(IStorage inStorage)
{
BinaryReader reader = new BinaryReader(input);
string magic = ReadString(reader, 4);
if (magic != "KIP1")
{
}
Name = ReadString(reader, 12);
TitleId = reader.ReadUInt64();
ProcessCategory = reader.ReadInt32();
MainThreadPriority = reader.ReadByte();
DefaultProcessorId = reader.ReadByte();
byte reserved = reader.ReadByte();
byte flags = reader.ReadByte();
Is64Bits = (flags & 0x08) != 0;
Addr39Bits = (flags & 0x10) != 0;
IsService = (flags & 0x20) != 0;
SegmentHeader[] segments = new SegmentHeader[6];
for (int index = 0; index < segments.Length; index++)
{
segments[index] = new SegmentHeader(
reader.ReadInt32(),
reader.ReadInt32(),
reader.ReadInt32(),
reader.ReadInt32());
}
TextOffset = segments[0].Offset;
RoOffset = segments[1].Offset;
DataOffset = segments[2].Offset;
BssOffset = segments[3].Offset;
BssSize = segments[3].DecompressedSize;
MainThreadStackSize = segments[1].Attribute;
kip = new Kip(inStorage);
Capabilities = new int[32];
for (int index = 0; index < Capabilities.Length; index++)
{
Capabilities[index] = reader.ReadInt32();
Capabilities[index] = System.BitConverter.ToInt32(kip.Header.Capabilities, index * 4);
}
input.Seek(0x100, SeekOrigin.Begin);
Text = ReadSegment(segments[0], input);
Ro = ReadSegment(segments[1], input);
Data = ReadSegment(segments[2], input);
Text = kip.DecompressSection(0);
Ro = kip.DecompressSection(1);
Data = kip.DecompressSection(2);
}
private byte[] ReadSegment(SegmentHeader header, Stream input)
{
byte[] data = new byte[header.DecompressedSize];
input.Read(data, 0, header.CompressedSize);
BackwardsLz.DecompressInPlace(data, header.CompressedSize);
return data;
}
private static string ReadString(BinaryReader reader, int maxSize)
{
string value = string.Empty;
for (int index = 0; index < maxSize; index++)
{
char chr = (char)reader.ReadByte();
if (chr == '\0')
{
reader.BaseStream.Seek(maxSize - index - 1, SeekOrigin.Current);
break;
}
value += chr;
}
return value;
}
}
}

View file

@ -1,21 +1,24 @@
using Ryujinx.HLE.Loaders.Compression;
using System;
using System.IO;
using LibHac.Fs;
using LibHac;
namespace Ryujinx.HLE.Loaders.Executables
{
class NxStaticObject : IExecutable
{
Nso nso;
public byte[] Text { get; private set; }
public byte[] Ro { get; private set; }
public byte[] Data { get; private set; }
public int TextOffset { get; private set; }
public int RoOffset { get; private set; }
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public int TextOffset => (int)nso.Sections[0].MemoryOffset;
public int RoOffset => (int)nso.Sections[1].MemoryOffset;
public int DataOffset => (int)nso.Sections[2].MemoryOffset;
public int BssOffset => DataOffset + Data.Length;
public int BssSize => (int)nso.BssSize;
[Flags]
private enum NsoFlags
@ -28,82 +31,13 @@ namespace Ryujinx.HLE.Loaders.Executables
HasDataHash = 1 << 5
}
public NxStaticObject(Stream input)
public NxStaticObject(IStorage inStorage)
{
BinaryReader reader = new BinaryReader(input);
input.Seek(0, SeekOrigin.Begin);
int nsoMagic = reader.ReadInt32();
int version = reader.ReadInt32();
int reserved = reader.ReadInt32();
int flagsMsk = reader.ReadInt32();
int textOffset = reader.ReadInt32();
int textMemOffset = reader.ReadInt32();
int textDecSize = reader.ReadInt32();
int modNameOffset = reader.ReadInt32();
int roOffset = reader.ReadInt32();
int roMemOffset = reader.ReadInt32();
int roDecSize = reader.ReadInt32();
int modNameSize = reader.ReadInt32();
int dataOffset = reader.ReadInt32();
int dataMemOffset = reader.ReadInt32();
int dataDecSize = reader.ReadInt32();
int bssSize = reader.ReadInt32();
byte[] buildId = reader.ReadBytes(0x20);
int textSize = reader.ReadInt32();
int roSize = reader.ReadInt32();
int dataSize = reader.ReadInt32();
input.Seek(0x24, SeekOrigin.Current);
int dynStrOffset = reader.ReadInt32();
int dynStrSize = reader.ReadInt32();
int dynSymOffset = reader.ReadInt32();
int dynSymSize = reader.ReadInt32();
byte[] textHash = reader.ReadBytes(0x20);
byte[] roHash = reader.ReadBytes(0x20);
byte[] dataHash = reader.ReadBytes(0x20);
NsoFlags flags = (NsoFlags)flagsMsk;
TextOffset = textMemOffset;
RoOffset = roMemOffset;
DataOffset = dataMemOffset;
BssSize = bssSize;
// Text segment
input.Seek(textOffset, SeekOrigin.Begin);
Text = reader.ReadBytes(textSize);
if (flags.HasFlag(NsoFlags.IsTextCompressed) && textSize != 0)
{
Text = Lz4.Decompress(Text, textDecSize);
}
// Read-only data segment
input.Seek(roOffset, SeekOrigin.Begin);
Ro = reader.ReadBytes(roSize);
if (flags.HasFlag(NsoFlags.IsRoCompressed) && roSize != 0)
{
Ro = Lz4.Decompress(Ro, roDecSize);
}
// Data segment
input.Seek(dataOffset, SeekOrigin.Begin);
Data = reader.ReadBytes(dataSize);
if (flags.HasFlag(NsoFlags.IsDataCompressed) && dataSize != 0)
{
Data = Lz4.Decompress(Data, dataDecSize);
}
nso = new Nso(inStorage);
Text = nso.Sections[0].DecompressSection();
Ro = nso.Sections[1].DecompressSection();
Data = nso.Sections[2].DecompressSection();
}
}
}