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
|
||||
{
|
||||
private const int TitleKeySize = 0x10;
|
||||
private static readonly Dictionary<string, KeyValue> CommonKeyDict;
|
||||
private static readonly Dictionary<string, KeyValue> UniqueKeyDict;
|
||||
private static readonly Dictionary<string, KeyValue> AllKeyDict;
|
||||
|
||||
public static readonly Dictionary<string, KeyValue> CommonKeyDict;
|
||||
public static readonly Dictionary<string, KeyValue> UniqueKeyDict;
|
||||
public static readonly Dictionary<string, KeyValue> AllKeyDict;
|
||||
|
||||
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();
|
||||
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);
|
||||
if (key.IsEmpty()) continue;
|
||||
|
@ -410,6 +411,37 @@ namespace LibHac
|
|||
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()
|
||||
{
|
||||
var keys = new List<KeyValue>
|
||||
|
@ -489,7 +521,7 @@ namespace LibHac
|
|||
return keys;
|
||||
}
|
||||
|
||||
private class KeyValue
|
||||
public class KeyValue
|
||||
{
|
||||
public readonly string Name;
|
||||
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