LibHac/libhac/Nso.cs

115 lines
4.1 KiB
C#
Raw Normal View History

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