mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Merge pull request #11 from shadowninja108/master
Add partial NSO support
This commit is contained in:
commit
b5c7f14d55
3 changed files with 231 additions and 7 deletions
|
@ -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
78
libhac/Lz4.cs
Normal 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
114
libhac/Nso.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue