Use libhac for loading NSO and KIP (#1047)
* Use libhac for loading NSOs and KIPs * Fix formatting * Refactor KIP and NSO executables for libhac * Fix up formatting * Remove Ryujinx.HLE.Loaders.Compression * Remove reference to Ryujinx.HLE.Loaders.Compression in NxStaticObject.cs * Remove reference to Ryujinx.HLE.Loaders.Compression in KernelInitialProcess.cs * Rename classes in Ryujinx.HLE.Loaders.Executables * Fix space alignment * Fix up formatting
This commit is contained in:
parent
468d8f841f
commit
dc144d2e19
11 changed files with 89 additions and 466 deletions
|
@ -32,7 +32,7 @@ using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
|
using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
|
||||||
using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
|
using NsoExecutable = Ryujinx.HLE.Loaders.Executables.NsoExecutable;
|
||||||
|
|
||||||
using static LibHac.Fs.ApplicationSaveDataManagement;
|
using static LibHac.Fs.ApplicationSaveDataManagement;
|
||||||
|
|
||||||
|
@ -271,9 +271,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public void LoadKip(string kipFile)
|
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));
|
ProgramLoader.LoadKernelInitalProcess(this, new KipExecutable(fs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,7 +545,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
codeFs.OpenFile(out IFile nsoFile, file.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
codeFs.OpenFile(out IFile nsoFile, file.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
NxStaticObject staticObject = new NxStaticObject(nsoFile.AsStream());
|
NsoExecutable staticObject = new NsoExecutable(nsoFile.AsStorage());
|
||||||
|
|
||||||
staticObjects.Add(staticObject);
|
staticObjects.Add(staticObject);
|
||||||
}
|
}
|
||||||
|
@ -569,13 +569,13 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
|
||||||
|
|
||||||
FileStream input = new FileStream(filePath, FileMode.Open);
|
|
||||||
|
|
||||||
IExecutable staticObject;
|
IExecutable staticObject;
|
||||||
|
|
||||||
if (isNro)
|
if (isNro)
|
||||||
{
|
{
|
||||||
NxRelocatableObject obj = new NxRelocatableObject(input);
|
FileStream input = new FileStream(filePath, FileMode.Open);
|
||||||
|
NroExecutable obj = new NroExecutable(input);
|
||||||
staticObject = obj;
|
staticObject = obj;
|
||||||
|
|
||||||
// homebrew NRO can actually have some data after the actual NRO
|
// homebrew NRO can actually have some data after the actual NRO
|
||||||
|
@ -648,7 +648,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
staticObject = new NxStaticObject(input);
|
staticObject = new NsoExecutable(new LocalStorage(filePath, FileAccess.Read));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentManager.LoadEntries(Device);
|
ContentManager.LoadEntries(Device);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using LibHac;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
@ -17,7 +18,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
private const int ArgsDataSize = 0x9000;
|
private const int ArgsDataSize = 0x9000;
|
||||||
private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
|
private const int ArgsTotalSize = ArgsHeaderSize + ArgsDataSize;
|
||||||
|
|
||||||
public static bool LoadKernelInitalProcess(Horizon system, KernelInitialProcess kip)
|
public static bool LoadKernelInitalProcess(Horizon system, KipExecutable kip)
|
||||||
{
|
{
|
||||||
int endOffset = kip.DataOffset + kip.Data.Length;
|
int endOffset = kip.DataOffset + kip.Data.Length;
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
int codePagesCount = codeSize / KMemoryManager.PageSize;
|
int codePagesCount = codeSize / KMemoryManager.PageSize;
|
||||||
|
|
||||||
ulong codeBaseAddress = kip.Addr39Bits ? 0x8000000UL : 0x200000UL;
|
ulong codeBaseAddress = (kip.Header.Flags & 0x10) != 0 ? 0x8000000UL : 0x200000UL;
|
||||||
|
|
||||||
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
|
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
|
||||||
|
|
||||||
|
@ -43,27 +44,27 @@ namespace Ryujinx.HLE.HOS
|
||||||
mmuFlags |= 0x20;
|
mmuFlags |= 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kip.Addr39Bits)
|
if ((kip.Header.Flags & 0x10) != 0)
|
||||||
{
|
{
|
||||||
mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
|
mmuFlags |= (int)AddressSpaceType.Addr39Bits << 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kip.Is64Bits)
|
if ((kip.Header.Flags & 0x08) != 0)
|
||||||
{
|
{
|
||||||
mmuFlags |= 1;
|
mmuFlags |= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessCreationInfo creationInfo = new ProcessCreationInfo(
|
ProcessCreationInfo creationInfo = new ProcessCreationInfo(
|
||||||
kip.Name,
|
kip.Header.Name,
|
||||||
kip.ProcessCategory,
|
kip.Header.ProcessCategory,
|
||||||
kip.TitleId,
|
kip.Header.TitleId,
|
||||||
codeAddress,
|
codeAddress,
|
||||||
codePagesCount,
|
codePagesCount,
|
||||||
mmuFlags,
|
mmuFlags,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
MemoryRegion memoryRegion = kip.IsService
|
MemoryRegion memoryRegion = (kip.Header.Flags & 0x20) != 0
|
||||||
? MemoryRegion.Service
|
? MemoryRegion.Service
|
||||||
: MemoryRegion.Application;
|
: MemoryRegion.Application;
|
||||||
|
|
||||||
|
@ -103,9 +104,9 @@ namespace Ryujinx.HLE.HOS
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.DefaultCpuCore = kip.DefaultProcessorId;
|
process.DefaultCpuCore = kip.Header.DefaultCore;
|
||||||
|
|
||||||
result = process.Start(kip.MainThreadPriority, (ulong)kip.MainThreadStackSize);
|
result = process.Start(kip.Header.MainThreadPriority, (ulong)kip.Header.Sections[1].Attribute);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
|
|
||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|
||||||
NxRelocatableObject executable = new NxRelocatableObject(stream, nroAddress, bssAddress);
|
NroExecutable executable = new NroExecutable(stream, nroAddress, bssAddress);
|
||||||
|
|
||||||
// check if everything is page align.
|
// check if everything is page align.
|
||||||
if ((executable.Text.Length & 0xFFF) != 0 || (executable.Ro.Length & 0xFFF) != 0 ||
|
if ((executable.Text.Length & 0xFFF) != 0 || (executable.Ro.Length & 0xFFF) != 0 ||
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
{
|
{
|
||||||
class NroInfo
|
class NroInfo
|
||||||
{
|
{
|
||||||
public NxRelocatableObject Executable { get; private set; }
|
public NroExecutable Executable { get; private set; }
|
||||||
|
|
||||||
public byte[] Hash { get; private set; }
|
public byte[] Hash { get; private set; }
|
||||||
public ulong NroAddress { get; private set; }
|
public ulong NroAddress { get; private set; }
|
||||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
||||||
public ulong NroMappedAddress { get; set; }
|
public ulong NroMappedAddress { get; set; }
|
||||||
|
|
||||||
public NroInfo(
|
public NroInfo(
|
||||||
NxRelocatableObject executable,
|
NroExecutable executable,
|
||||||
byte[] hash,
|
byte[] hash,
|
||||||
ulong nroAddress,
|
ulong nroAddress,
|
||||||
ulong nroSize,
|
ulong nroSize,
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Compression
|
|
||||||
{
|
|
||||||
static class BackwardsLz
|
|
||||||
{
|
|
||||||
private class BackwardsReader
|
|
||||||
{
|
|
||||||
private byte[] _data;
|
|
||||||
|
|
||||||
private int _position;
|
|
||||||
|
|
||||||
public int Position => _position;
|
|
||||||
|
|
||||||
public BackwardsReader(byte[] data, int end)
|
|
||||||
{
|
|
||||||
_data = data;
|
|
||||||
_position = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SeekCurrent(int offset)
|
|
||||||
{
|
|
||||||
_position += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte ReadByte()
|
|
||||||
{
|
|
||||||
return _data[--_position];
|
|
||||||
}
|
|
||||||
|
|
||||||
public short ReadInt16()
|
|
||||||
{
|
|
||||||
return (short)((ReadByte() << 8) | (ReadByte() << 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ReadInt32()
|
|
||||||
{
|
|
||||||
return ((ReadByte() << 24) |
|
|
||||||
(ReadByte() << 16) |
|
|
||||||
(ReadByte() << 8) |
|
|
||||||
(ReadByte() << 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void DecompressInPlace(byte[] buffer, int headerEnd)
|
|
||||||
{
|
|
||||||
BackwardsReader reader = new BackwardsReader(buffer, headerEnd);
|
|
||||||
|
|
||||||
int additionalDecLength = reader.ReadInt32();
|
|
||||||
int startOffset = reader.ReadInt32();
|
|
||||||
int compressedLength = reader.ReadInt32();
|
|
||||||
|
|
||||||
reader.SeekCurrent(12 - startOffset);
|
|
||||||
|
|
||||||
int decBase = headerEnd - compressedLength;
|
|
||||||
|
|
||||||
int decPos = compressedLength + additionalDecLength;
|
|
||||||
|
|
||||||
byte mask = 0;
|
|
||||||
byte header = 0;
|
|
||||||
|
|
||||||
while (decPos > 0)
|
|
||||||
{
|
|
||||||
if ((mask >>= 1) == 0)
|
|
||||||
{
|
|
||||||
header = reader.ReadByte();
|
|
||||||
mask = 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((header & mask) == 0)
|
|
||||||
{
|
|
||||||
buffer[decBase + --decPos] = reader.ReadByte();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ushort pair = (ushort)reader.ReadInt16();
|
|
||||||
|
|
||||||
int length = (pair >> 12) + 3;
|
|
||||||
int position = (pair & 0xfff) + 3;
|
|
||||||
|
|
||||||
if (length > decPos)
|
|
||||||
{
|
|
||||||
length = decPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
decPos -= length;
|
|
||||||
|
|
||||||
int dstPos = decBase + decPos;
|
|
||||||
|
|
||||||
if (length <= position)
|
|
||||||
{
|
|
||||||
int srcPos = dstPos + position;
|
|
||||||
|
|
||||||
Buffer.BlockCopy(buffer, srcPos, buffer, dstPos, length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int offset = 0; offset < length; offset++)
|
|
||||||
{
|
|
||||||
buffer[dstPos + offset] = buffer[dstPos + position + offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Compression
|
|
||||||
{
|
|
||||||
static class Lz4
|
|
||||||
{
|
|
||||||
public static byte[] Decompress(byte[] cmp, int decLength)
|
|
||||||
{
|
|
||||||
byte[] dec = new byte[decLength];
|
|
||||||
|
|
||||||
int cmpPos = 0;
|
|
||||||
int decPos = 0;
|
|
||||||
|
|
||||||
int GetLength(int length)
|
|
||||||
{
|
|
||||||
byte sum;
|
|
||||||
|
|
||||||
if (length == 0xf)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
length += (sum = cmp[cmpPos++]);
|
|
||||||
}
|
|
||||||
while (sum == 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
byte token = cmp[cmpPos++];
|
|
||||||
|
|
||||||
int encCount = (token >> 0) & 0xf;
|
|
||||||
int litCount = (token >> 4) & 0xf;
|
|
||||||
|
|
||||||
// Copy literal chunk
|
|
||||||
litCount = GetLength(litCount);
|
|
||||||
|
|
||||||
Buffer.BlockCopy(cmp, cmpPos, dec, decPos, litCount);
|
|
||||||
|
|
||||||
cmpPos += litCount;
|
|
||||||
decPos += litCount;
|
|
||||||
|
|
||||||
if (cmpPos >= cmp.Length)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy compressed chunk
|
|
||||||
int back = cmp[cmpPos++] << 0 |
|
|
||||||
cmp[cmpPos++] << 8;
|
|
||||||
|
|
||||||
encCount = GetLength(encCount) + 4;
|
|
||||||
|
|
||||||
int encPos = decPos - back;
|
|
||||||
|
|
||||||
if (encCount <= back)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(dec, encPos, dec, decPos, encCount);
|
|
||||||
|
|
||||||
decPos += encCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (encCount-- > 0)
|
|
||||||
{
|
|
||||||
dec[decPos++] = dec[encPos++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (cmpPos < cmp.Length &&
|
|
||||||
decPos < dec.Length);
|
|
||||||
|
|
||||||
return dec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
using Ryujinx.HLE.Loaders.Compression;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
|
||||||
{
|
|
||||||
class KernelInitialProcess : IExecutable
|
|
||||||
{
|
|
||||||
public string Name { get; private set; }
|
|
||||||
|
|
||||||
public ulong TitleId { get; private set; }
|
|
||||||
|
|
||||||
public int ProcessCategory { get; private set; }
|
|
||||||
|
|
||||||
public byte MainThreadPriority { get; private set; }
|
|
||||||
public byte DefaultProcessorId { get; private set; }
|
|
||||||
|
|
||||||
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[] 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 MainThreadStackSize { get; private set; }
|
|
||||||
|
|
||||||
public int[] Capabilities { get; private set; }
|
|
||||||
|
|
||||||
private struct SegmentHeader
|
|
||||||
{
|
|
||||||
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 SegmentHeader(
|
|
||||||
int offset,
|
|
||||||
int decompressedSize,
|
|
||||||
int compressedSize,
|
|
||||||
int attribute)
|
|
||||||
{
|
|
||||||
Offset = offset;
|
|
||||||
DecompressedSize = decompressedSize;
|
|
||||||
CompressedSize = compressedSize;
|
|
||||||
Attribute = attribute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public KernelInitialProcess(Stream input)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
Capabilities = new int[32];
|
|
||||||
|
|
||||||
for (int index = 0; index < Capabilities.Length; index++)
|
|
||||||
{
|
|
||||||
Capabilities[index] = reader.ReadInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
input.Seek(0x100, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
Text = ReadSegment(segments[0], input);
|
|
||||||
Ro = ReadSegment(segments[1], input);
|
|
||||||
Data = ReadSegment(segments[2], input);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
35
Ryujinx.HLE/Loaders/Executables/KipExecutable.cs
Normal file
35
Ryujinx.HLE/Loaders/Executables/KipExecutable.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
{
|
||||||
|
class KipExecutable : Kip, IExecutable
|
||||||
|
{
|
||||||
|
public byte[] Text { get; }
|
||||||
|
public byte[] Ro { get; }
|
||||||
|
public byte[] Data { get; }
|
||||||
|
|
||||||
|
public int TextOffset => Header.Sections[0].OutOffset;
|
||||||
|
public int RoOffset => Header.Sections[1].OutOffset;
|
||||||
|
public int DataOffset => Header.Sections[2].OutOffset;
|
||||||
|
public int BssOffset => Header.Sections[3].OutOffset;
|
||||||
|
public int BssSize => Header.Sections[3].DecompressedSize;
|
||||||
|
|
||||||
|
public int[] Capabilities { get; }
|
||||||
|
|
||||||
|
public KipExecutable(IStorage inStorage) : base(inStorage)
|
||||||
|
{
|
||||||
|
Capabilities = new int[32];
|
||||||
|
|
||||||
|
for (int index = 0; index < Capabilities.Length; index++)
|
||||||
|
{
|
||||||
|
Capabilities[index] = System.BitConverter.ToInt32(Header.Capabilities, index * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text = DecompressSection(0);
|
||||||
|
Ro = DecompressSection(1);
|
||||||
|
Data = DecompressSection(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class NxRelocatableObject : IExecutable
|
class NroExecutable : IExecutable
|
||||||
{
|
{
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] Ro { get; private set; }
|
public byte[] Ro { get; private set; }
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
public ulong SourceAddress { get; private set; }
|
public ulong SourceAddress { get; private set; }
|
||||||
public ulong BssAddress { get; private set; }
|
public ulong BssAddress { get; private set; }
|
||||||
|
|
||||||
public NxRelocatableObject(Stream input, ulong sourceAddress = 0, ulong bssAddress = 0)
|
public NroExecutable(Stream input, ulong sourceAddress = 0, ulong bssAddress = 0)
|
||||||
{
|
{
|
||||||
SourceAddress = sourceAddress;
|
SourceAddress = sourceAddress;
|
||||||
BssAddress = bssAddress;
|
BssAddress = bssAddress;
|
28
Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs
Normal file
28
Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.Loaders.Executables
|
||||||
|
{
|
||||||
|
class NsoExecutable : Nso, IExecutable
|
||||||
|
{
|
||||||
|
public byte[] Text { get; }
|
||||||
|
public byte[] Ro { get; }
|
||||||
|
public byte[] Data { get; }
|
||||||
|
|
||||||
|
public int TextOffset => (int)Sections[0].MemoryOffset;
|
||||||
|
public int RoOffset => (int)Sections[1].MemoryOffset;
|
||||||
|
public int DataOffset => (int)Sections[2].MemoryOffset;
|
||||||
|
public int BssOffset => DataOffset + Data.Length;
|
||||||
|
|
||||||
|
public new int BssSize => (int)base.BssSize;
|
||||||
|
|
||||||
|
public NsoExecutable(IStorage inStorage) : base(inStorage)
|
||||||
|
{
|
||||||
|
Text = Sections[0].DecompressSection();
|
||||||
|
Ro = Sections[1].DecompressSection();
|
||||||
|
Data = Sections[2].DecompressSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,109 +0,0 @@
|
||||||
using Ryujinx.HLE.Loaders.Compression;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Executables
|
|
||||||
{
|
|
||||||
class NxStaticObject : IExecutable
|
|
||||||
{
|
|
||||||
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 BssOffset => DataOffset + Data.Length;
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
private enum NsoFlags
|
|
||||||
{
|
|
||||||
IsTextCompressed = 1 << 0,
|
|
||||||
IsRoCompressed = 1 << 1,
|
|
||||||
IsDataCompressed = 1 << 2,
|
|
||||||
HasTextHash = 1 << 3,
|
|
||||||
HasRoHash = 1 << 4,
|
|
||||||
HasDataHash = 1 << 5
|
|
||||||
}
|
|
||||||
|
|
||||||
public NxStaticObject(Stream input)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in a new issue