Merge pull request #11 from shadowninja108/master

Add partial NSO support
This commit is contained in:
Alex Barney 2018-09-23 20:22:39 -06:00 committed by GitHub
commit b5c7f14d55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 231 additions and 7 deletions

View file

@ -269,9 +269,10 @@ namespace LibHac
public static class ExternalKeys public static class ExternalKeys
{ {
private const int TitleKeySize = 0x10; private const int TitleKeySize = 0x10;
private static readonly Dictionary<string, KeyValue> CommonKeyDict;
private static readonly Dictionary<string, KeyValue> UniqueKeyDict; public static readonly Dictionary<string, KeyValue> CommonKeyDict;
private static readonly Dictionary<string, KeyValue> AllKeyDict; public static readonly Dictionary<string, KeyValue> UniqueKeyDict;
public static readonly Dictionary<string, KeyValue> AllKeyDict;
static ExternalKeys() static ExternalKeys()
{ {
@ -393,12 +394,12 @@ namespace LibHac
} }
} }
public static string PrintKeys(Keyset keyset) public static string PrintKeys(Keyset keyset, Dictionary<string, KeyValue> dict)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
int maxNameLength = CommonKeyDict.Values.Max(x => x.Name.Length); int maxNameLength = dict.Values.Max(x => x.Name.Length);
foreach (KeyValue keySlot in CommonKeyDict.Values.OrderBy(x => x.Name)) foreach (KeyValue keySlot in dict.Values.OrderBy(x => x.Name))
{ {
byte[] key = keySlot.GetKey(keyset); byte[] key = keySlot.GetKey(keyset);
if (key.IsEmpty()) continue; if (key.IsEmpty()) continue;
@ -410,6 +411,37 @@ namespace LibHac
return sb.ToString(); return sb.ToString();
} }
public static string PrintCommonKeys(Keyset keyset)
{
return PrintKeys(keyset, CommonKeyDict);
}
public static string PrintUniqueKeys(Keyset keyset)
{
return PrintKeys(keyset, UniqueKeyDict);
}
public static string PrintAllKeys(Keyset keyset)
{
return PrintKeys(keyset, AllKeyDict);
}
public static string PrintTitleKeys(Keyset keyset)
{
var sb = new StringBuilder();
int maxNameLength = keyset.TitleKeys.Values.Max(x => x.Length);
foreach (KeyValuePair<byte[], byte[]> kv in keyset.TitleKeys)
{
byte[] key = kv.Key;
byte[] value = kv.Value;
var line = $"{key.ToHexString().PadRight(maxNameLength)} = {value.ToHexString()}";
sb.AppendLine(line);
}
return sb.ToString();
}
private static List<KeyValue> CreateCommonKeyList() private static List<KeyValue> CreateCommonKeyList()
{ {
var keys = new List<KeyValue> var keys = new List<KeyValue>
@ -489,7 +521,7 @@ namespace LibHac
return keys; return keys;
} }
private class KeyValue public class KeyValue
{ {
public readonly string Name; public readonly string Name;
public readonly int Size; public readonly int Size;

78
libhac/Lz4.cs Normal file
View file

@ -0,0 +1,78 @@
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 chunck
LitCount = GetLength(LitCount);
Buffer.BlockCopy(Cmp, CmpPos, Dec, DecPos, LitCount);
CmpPos += LitCount;
DecPos += LitCount;
if (CmpPos >= Cmp.Length)
{
break;
}
//Copy compressed chunck
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;
}
}
}

114
libhac/Nso.cs Normal file
View file

@ -0,0 +1,114 @@
using System.Collections;
using System.IO;
using LibHac.Streams;
using Ryujinx.HLE.Loaders.Compression;
namespace LibHac
{
public class Nso
{
public NsoSection[] Sections { get; }
public RodataRelativeExtent[] RodataRelativeExtents { get; }
public uint BssSize { get; }
public byte[] BuildId { get; } = new byte[0x20];
private SharedStreamSource StreamSource { get; }
public Nso(Stream stream)
{
StreamSource = new SharedStreamSource(stream);
BinaryReader reader = new BinaryReader(StreamSource.CreateStream());
if (reader.ReadAscii(4) != "NSO0")
throw new InvalidDataException("NSO magic is incorrect!");
reader.ReadUInt32(); // Version
reader.ReadUInt32(); // Reserved/Unused
BitArray flags = new BitArray(new[] { (int)reader.ReadUInt32() });
NsoSection textSection = new NsoSection(StreamSource);
NsoSection rodataSection = new NsoSection(StreamSource);
NsoSection dataSection = new NsoSection(StreamSource);
textSection.IsCompressed = flags[0];
rodataSection.IsCompressed = flags[1];
dataSection.IsCompressed = flags[2];
textSection.CheckHash = flags[3];
rodataSection.CheckHash = flags[4];
dataSection.CheckHash = flags[5];
textSection.ReadSegmentHeader(reader);
reader.ReadUInt32(); // Module offset (TODO)
rodataSection.ReadSegmentHeader(reader);
reader.ReadUInt32(); // Module file size
dataSection.ReadSegmentHeader(reader);
BssSize = reader.ReadUInt32();
reader.Read(BuildId, 0, 0x20);
textSection.CompressedSize = reader.ReadUInt32();
rodataSection.CompressedSize = reader.ReadUInt32();
dataSection.CompressedSize = reader.ReadUInt32();
reader.ReadBytes(0x1C); // Padding
RodataRelativeExtents = new[]
{
new RodataRelativeExtent(reader), new RodataRelativeExtent(reader), new RodataRelativeExtent(reader)
};
reader.Read(textSection.Hash, 0, 0x20);
reader.Read(rodataSection.Hash, 0, 0x20);
reader.Read(dataSection.Hash, 0, 0x20);
Sections = new[] { textSection, rodataSection, dataSection };
reader.Close();
}
public class NsoSection
{
private SharedStreamSource StreamSource { get; }
public bool IsCompressed { get; set; }
public bool CheckHash { get; set; }
public uint FileOffset { get; set; }
public uint MemoryOffset { get; set; }
public uint DecompressedSize { get; set; }
public uint CompressedSize { get; set; }
public byte[] Hash { get; } = new byte[0x20];
public NsoSection(SharedStreamSource streamSource)
{
StreamSource = streamSource;
}
public Stream OpenSection()
{
return StreamSource.CreateStream(FileOffset, CompressedSize);
}
public byte[] DecompressSection()
{
byte[] compressed = new byte[CompressedSize];
OpenSection().Read(compressed, 0, (int)CompressedSize);
if (IsCompressed)
return Lz4.Decompress(compressed, (int)DecompressedSize);
else
return compressed;
}
internal void ReadSegmentHeader(BinaryReader reader)
{
FileOffset = reader.ReadUInt32();
MemoryOffset = reader.ReadUInt32();
DecompressedSize = reader.ReadUInt32();
}
}
public class RodataRelativeExtent
{
public uint RegionRodataOffset { get; }
public uint RegionSize { get; }
public RodataRelativeExtent(BinaryReader reader)
{
RegionRodataOffset = reader.ReadUInt32();
RegionSize = reader.ReadUInt32();
}
}
}
}