From e694833e0cb176a11b3d1298b71da656b307fd08 Mon Sep 17 00:00:00 2001 From: shadowninja108 Date: Sat, 22 Sep 2018 21:46:15 -0700 Subject: [PATCH] Switched to Ryujinx's implementation of LZ4 Fixed stream handling in Nso.cs --- LibHac/LibHac.csproj | 1 - libhac/Lz4.cs | 78 ++++++++++++++++++++++++++++++++++++++++++++ libhac/Nso.cs | 37 ++++++++++++++------- 3 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 libhac/Lz4.cs diff --git a/LibHac/LibHac.csproj b/LibHac/LibHac.csproj index 3f4cd941..59228d90 100644 --- a/LibHac/LibHac.csproj +++ b/LibHac/LibHac.csproj @@ -27,7 +27,6 @@ - diff --git a/libhac/Lz4.cs b/libhac/Lz4.cs new file mode 100644 index 00000000..cfb49551 --- /dev/null +++ b/libhac/Lz4.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/libhac/Nso.cs b/libhac/Nso.cs index 8d983c32..11da2a6b 100644 --- a/libhac/Nso.cs +++ b/libhac/Nso.cs @@ -1,5 +1,5 @@ using LibHac.Streams; -using LZ4; +using Ryujinx.HLE.Loaders.Compression; using System; using System.Collections; using System.Collections.Generic; @@ -15,19 +15,20 @@ namespace LibHac public uint BssSize; public byte[] BuildID = new byte[0x20]; - private BinaryReader reader; + private SharedStreamSource StreamSource; public Nso(Stream stream) { - reader = new BinaryReader(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[] { (int) reader.ReadUInt32() }); - NsoSection textSection = new NsoSection(stream); - NsoSection rodataSection = new NsoSection(stream); - NsoSection dataSection = new NsoSection(stream); + NsoSection textSection = new NsoSection(StreamSource); + NsoSection rodataSection = new NsoSection(StreamSource); + NsoSection dataSection = new NsoSection(StreamSource); textSection.IsCompressed = flags[0]; textSection.CheckHash = flags[3]; rodataSection.IsCompressed = flags[1]; @@ -56,26 +57,31 @@ namespace LibHac reader.Read(dataSection.Hash, 0, 0x20); Sections = new NsoSection[] {textSection, rodataSection, dataSection }; + reader.Close(); } public void ReadSegmentHeader(NsoSection section) { + BinaryReader reader = new BinaryReader(StreamSource.CreateStream()); section.FileOffset = reader.ReadUInt32(); section.MemoryOffset = reader.ReadUInt32(); - section.DecompressedSize = reader.ReadUInt32(); + section.DecompressedSize = reader.ReadUInt32(); + reader.Close(); } public RodataRelativeExtent ReadRodataRelativeExtent() { + BinaryReader reader = new BinaryReader(StreamSource.CreateStream()); RodataRelativeExtent extent = new RodataRelativeExtent(); extent.RegionRodataOffset = reader.ReadUInt32(); extent.RegionSize = reader.ReadUInt32(); + reader.Close(); return extent; } public class NsoSection { - private Stream Stream; + private readonly SharedStreamSource StreamSource; public bool IsCompressed, CheckHash; @@ -85,19 +91,26 @@ namespace LibHac CompressedSize; public byte[] Hash = new byte[0x20]; - public NsoSection(Stream stream) + public NsoSection(SharedStreamSource streamSource) { - Stream = stream; + StreamSource = streamSource; } public Stream OpenCompressedStream() { - return new SubStream(Stream, FileOffset, CompressedSize); + return StreamSource.CreateStream(FileOffset, CompressedSize); } public Stream OpenDecompressedStream() { - return new LZ4Stream(OpenCompressedStream(), LZ4StreamMode.Decompress); + return new MemoryStream(Decompress()); + } + + public byte[] Decompress() + { + byte[] compressed = new byte[CompressedSize]; + OpenCompressedStream().Read(compressed, 0, (int) CompressedSize); + return Lz4.Decompress(compressed, (int) DecompressedSize); } }