public void AlwaysCompressCorrectly() { using var imgStream = File.OpenRead("G:\\KH2.IMG"); new Img(imgStream, File.OpenRead("G:\\KH2.IDX").Using(Idx.Read), true) .Entries.ToList() .Where(x => x.IsCompressed) .Where(x => !x.IsStreamed) .Select(x => new { Name = IdxName.Lookup(x), Entry = x, Data = imgStream .SetPosition(x.Offset * 0x800) .ReadBytes((x.BlockLength + 1) * 0x800) }) .ToList() .AsParallel() .WithDegreeOfParallelism(16) .ForAll(x => { new MemoryStream(Img.Decompress(x.Data)).Using(stream => { Helpers.AssertStream(stream, inStream => { var compressedData = Img.Compress(inStream.ReadAllBytes()); var decompressedData = Img.Decompress(compressedData); return(new MemoryStream(decompressedData)); }); }); }); }
public void CompressEquallyOrBetter(string fileName, int expectLength) => File.OpenRead($"kh2/res/{fileName}.dec").Using(stream => { var compress = Img.Compress(stream.ReadAllBytes()); if (compress.Length > expectLength) { throw new XunitException($"Compressed file is {compress.Length} byte, but the official one is {expectLength}.\n" + "The compression algorithm is not performant enough."); } });
public void CompressAtLEastEquallyOrMore(string fileName) => File.OpenRead($"kh2/res/{fileName}.dec").Using(stream => { const int CompressionGrace = 6; const int OriginalCompressedSizeLength = 0x167 + CompressionGrace; var compress = Img.Compress(stream.ReadAllBytes()); if (compress.Length > OriginalCompressedSizeLength) { throw new XunitException($"Compressed file is {compress.Length} byte, but the official one is {OriginalCompressedSizeLength}.\n" + "The compression algorithm is not performant enough."); } });
public void CompressChunkSimple() { const int HeaderSize = 5; var dec = new byte[0x29]; var cmp = Img.Compress(dec); var expected = new byte[] { 0x25, 0x01, 0x01, 0x00 }; Assert.Equal(expected.Length, cmp.Length - HeaderSize); Assert.Equal(expected, cmp.Take(cmp.Length - HeaderSize)); }
public void CompressCorrectlyWhenKeyIsFoundInTheData() => new MemoryStream(Enumerable.Range(0, 0x100).Select(x => (byte)x).ToArray()).Using(stream => { Helpers.AssertStream(stream, inStream => new MemoryStream(Img.Decompress(Img.Compress(inStream.ReadAllBytes())))); });
public void CompressCorrectly(string fileName) => File.OpenRead($"kh2/res/{fileName}.dec").Using(stream => { Helpers.AssertStream(stream, inStream => new MemoryStream(Img.Decompress(Img.Compress(inStream.ReadAllBytes())))); });
protected int OnExecute(CommandLineApplication app) { const long EsitmatedMaximumImgFileSize = 4L * 1024 * 1024 * 1024; // 4GB const int EsitmatedMaximumIdxFileSize = 600 * 1024; // 600KB const int EstimatedMaximumIdxEntryAmountToBeValid = EsitmatedMaximumIdxFileSize / 0x10 - 4; using var isoStream = File.Open(InputIso, FileMode.Open, FileAccess.ReadWrite); if (IdxIsoBlock == -1 || ImgIsoBlock == -1) { const int needleLength = 0x0B; var imgNeedle = new byte[needleLength] { 0x01, 0x09, 0x4B, 0x48, 0x32, 0x2E, 0x49, 0x4D, 0x47, 0x3B, 0x31 }; var idxNeedle = new byte[needleLength] { 0x01, 0x09, 0x4B, 0x48, 0x32, 0x2E, 0x49, 0x44, 0x58, 0x3B, 0x31 }; const uint basePosition = 0x105 * 0x800; for (int i = 0; i < 0x500; i++) { isoStream.Position = basePosition + i; var hayRead = isoStream.ReadBytes(needleLength); var idxCmp = hayRead.SequenceEqual(idxNeedle); var imgCmp = hayRead.SequenceEqual(imgNeedle); if (imgCmp || idxCmp) { isoStream.Position -= 0x24; var blockStack = isoStream.ReadBytes(0x04); var blockCorrect = new byte[0x04] { blockStack[3], blockStack[2], blockStack[1], blockStack[0] }; if (idxCmp && IdxIsoBlock == -1) { IdxIsoBlock = BitConverter.ToInt32(blockCorrect); } else if (imgCmp && ImgIsoBlock == -1) { ImgIsoBlock = BitConverter.ToInt32(blockCorrect); } } } if (IdxIsoBlock == -1 || ImgIsoBlock == -1) { throw new IOException("Could not determine the LBA Offsets of KH2.IDX or KH2.IMG, is this ISO valid?"); } } var idxStream = OpenIsoSubStream(isoStream, IdxIsoBlock, EsitmatedMaximumIdxFileSize); using var imgStream = OpenIsoSubStream(isoStream, ImgIsoBlock, EsitmatedMaximumImgFileSize); var idxEntryCount = idxStream.ReadInt32(); if (idxEntryCount > EstimatedMaximumIdxEntryAmountToBeValid) { throw new CustomException("There is a high chance that the IDX block is not valid, therefore the injection will terminate to avoid corruption."); } var idxEntries = Idx.Read(idxStream.SetPosition(0)); var entry = idxEntries.FirstOrDefault(x => x.Hash32 == Idx.GetHash32(FilePath) && x.Hash16 == Idx.GetHash16(FilePath)); if (entry == null) { idxStream = GetIdxStreamWhichContainsTargetedFile(idxEntries, imgStream, FilePath); if (idxStream == null) { throw new CustomException($"The file {FilePath} has not been found inside the KH2.IDX, therefore the injection will terminate."); } idxEntries = Idx.Read(idxStream.SetPosition(0)); entry = idxEntries.FirstOrDefault(x => x.Hash32 == Idx.GetHash32(FilePath) && x.Hash16 == Idx.GetHash16(FilePath)); } var inputData = File.ReadAllBytes(InputFile); var decompressedLength = inputData.Length; if (Uncompressed == false) { inputData = Img.Compress(inputData); } var blockCountRequired = (inputData.Length + 0x7ff) / 0x800 - 1; if (blockCountRequired > entry.BlockLength) { throw new CustomException($"The file to inject is too big: the actual is {inputData.Length} but the maximum allowed is {GetLength(entry.BlockLength)}."); } imgStream.SetPosition(GetOffset(entry.Offset)); // Clean completely the content of the previous file to not mess up the decompression imgStream.Write(new byte[GetLength(entry.BlockLength)]); imgStream.SetPosition(GetOffset(entry.Offset)); imgStream.Write(inputData); entry.IsCompressed = !Uncompressed; entry.Length = decompressedLength; // we are intentionally not patching entry.BlockLength because it would not allow to insert back bigger files. Idx.Write(idxStream.SetPosition(0), idxEntries); return(0); }
protected int OnExecute(CommandLineApplication app) { const long EsitmatedMaximumImgFileSize = 4L * 1024 * 1024 * 1024; // 4GB const int EsitmatedMaximumIdxFileSize = 600 * 1024; // 600KB const int EstimatedMaximumIdxEntryAmountToBeValid = EsitmatedMaximumIdxFileSize / 0x10 - 4; using var isoStream = File.Open(InputIso, FileMode.Open, FileAccess.ReadWrite); if (IdxIsoBlock == -1 || ImgIsoBlock == -1) { var bufferedStream = new BufferedStream(isoStream); IdxIsoBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IDX;1"); ImgIsoBlock = IsoUtility.GetFileOffset(bufferedStream, "KH2.IMG;1"); if (IdxIsoBlock == -1 || ImgIsoBlock == -1) { throw new IOException("Could not determine the LBA Offsets of KH2.IDX or KH2.IMG, is this ISO valid?"); } } var idxStream = OpenIsoSubStream(isoStream, IdxIsoBlock, EsitmatedMaximumIdxFileSize); using var imgStream = OpenIsoSubStream(isoStream, ImgIsoBlock, EsitmatedMaximumImgFileSize); var idxEntryCount = idxStream.ReadInt32(); if (idxEntryCount > EstimatedMaximumIdxEntryAmountToBeValid) { throw new CustomException("There is a high chance that the IDX block is not valid, therefore the injection will terminate to avoid corruption."); } var idxEntries = Idx.Read(idxStream.SetPosition(0)); var entry = idxEntries.FirstOrDefault(x => x.Hash32 == Idx.GetHash32(FilePath) && x.Hash16 == Idx.GetHash16(FilePath)); if (entry == null) { idxStream = GetIdxStreamWhichContainsTargetedFile(idxEntries, imgStream, FilePath); if (idxStream == null) { throw new CustomException($"The file {FilePath} has not been found inside the KH2.IDX, therefore the injection will terminate."); } idxEntries = Idx.Read(idxStream.SetPosition(0)); entry = idxEntries.FirstOrDefault(x => x.Hash32 == Idx.GetHash32(FilePath) && x.Hash16 == Idx.GetHash16(FilePath)); } var inputData = File.ReadAllBytes(InputFile); var decompressedLength = inputData.Length; if (Uncompressed == false) { inputData = Img.Compress(inputData); } var blockCountRequired = (inputData.Length + 0x7ff) / 0x800 - 1; if (blockCountRequired > entry.BlockLength) { throw new CustomException($"The file to inject is too big: the actual is {inputData.Length} but the maximum allowed is {GetLength(entry.BlockLength)}."); } imgStream.SetPosition(GetOffset(entry.Offset)); // Clean completely the content of the previous file to not mess up the decompression imgStream.Write(new byte[GetLength(entry.BlockLength)]); imgStream.SetPosition(GetOffset(entry.Offset)); imgStream.Write(inputData); entry.IsCompressed = !Uncompressed; entry.Length = decompressedLength; // we are intentionally not patching entry.BlockLength because it would not allow to insert back bigger files. Idx.Write(idxStream.SetPosition(0), idxEntries); return(0); }