From 73b472befa182332466aa64db7d9e8823df5a3ee Mon Sep 17 00:00:00 2001 From: shadowninja108 Date: Sat, 22 Sep 2018 20:19:27 -0700 Subject: [PATCH] Add partial NSO support --- LibHac/LibHac.csproj | 1 + libhac/Nso.cs | 113 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 libhac/Nso.cs diff --git a/LibHac/LibHac.csproj b/LibHac/LibHac.csproj index 59228d90..3f4cd941 100644 --- a/LibHac/LibHac.csproj +++ b/LibHac/LibHac.csproj @@ -27,6 +27,7 @@ + diff --git a/libhac/Nso.cs b/libhac/Nso.cs new file mode 100644 index 00000000..8d983c32 --- /dev/null +++ b/libhac/Nso.cs @@ -0,0 +1,113 @@ +using LibHac.Streams; +using LZ4; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace LibHac +{ + public class Nso + { + public NsoSection[] Sections; + public RodataRelativeExtent[] RodataRelativeExtents; + public uint BssSize; + public byte[] BuildID = new byte[0x20]; + + private BinaryReader reader; + + public Nso(Stream stream) + { + reader = new BinaryReader(stream); + 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[] { (int) reader.ReadUInt32() }); + NsoSection textSection = new NsoSection(stream); + NsoSection rodataSection = new NsoSection(stream); + NsoSection dataSection = new NsoSection(stream); + textSection.IsCompressed = flags[0]; + textSection.CheckHash = flags[3]; + rodataSection.IsCompressed = flags[1]; + rodataSection.CheckHash = flags[4]; + dataSection.IsCompressed = flags[2]; + dataSection.CheckHash = flags[5]; + + ReadSegmentHeader(textSection); + reader.ReadUInt32(); // Module offset (TODO) + ReadSegmentHeader(rodataSection); + reader.ReadUInt32(); // Module file size + ReadSegmentHeader(dataSection); + 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 RodataRelativeExtent[] + { + ReadRodataRelativeExtent(), ReadRodataRelativeExtent(), ReadRodataRelativeExtent() + }; + + reader.Read(textSection.Hash, 0, 0x20); + reader.Read(rodataSection.Hash, 0, 0x20); + reader.Read(dataSection.Hash, 0, 0x20); + + Sections = new NsoSection[] {textSection, rodataSection, dataSection }; + } + + public void ReadSegmentHeader(NsoSection section) + { + section.FileOffset = reader.ReadUInt32(); + section.MemoryOffset = reader.ReadUInt32(); + section.DecompressedSize = reader.ReadUInt32(); + } + + public RodataRelativeExtent ReadRodataRelativeExtent() + { + RodataRelativeExtent extent = new RodataRelativeExtent(); + extent.RegionRodataOffset = reader.ReadUInt32(); + extent.RegionSize = reader.ReadUInt32(); + return extent; + } + + public class NsoSection + { + private Stream Stream; + + public bool IsCompressed, + CheckHash; + public uint FileOffset, + MemoryOffset, + DecompressedSize, + CompressedSize; + public byte[] Hash = new byte[0x20]; + + public NsoSection(Stream stream) + { + Stream = stream; + } + + public Stream OpenCompressedStream() + { + return new SubStream(Stream, FileOffset, CompressedSize); + } + + public Stream OpenDecompressedStream() + { + return new LZ4Stream(OpenCompressedStream(), LZ4StreamMode.Decompress); + } + } + + public class RodataRelativeExtent + { + public uint + RegionRodataOffset, + RegionSize; + } + + + } +}