public byte[] Decompress(byte[] compressedInput, int uncompressedSize) { KrakenDecoder decoder = new KrakenDecoder(); byte[] decompressedBuffer = new byte[uncompressedSize]; int remainingBytes = uncompressedSize; int sourceLength = compressedInput.Length; int destinationOffset = 0; fixed(byte *decompressedBufferPtr = decompressedBuffer) fixed(byte *compressedInputPtr = compressedInput) fixed(byte *scratchPtr = decoder.Scratch) { byte *sourceStart = compressedInputPtr; byte *decompressBufferStart = decompressedBufferPtr; while (remainingBytes != 0) { if (!DecodeStep(decoder, decompressedBufferPtr, destinationOffset, remainingBytes, sourceStart, sourceLength, scratchPtr)) { throw new DecoderException($"Failed DecodeStep method"); } sourceStart += decoder.SourceUsed; sourceLength -= decoder.SourceUsed; destinationOffset += decoder.DestinationUsed; remainingBytes -= decoder.DestinationUsed; } } return(decompressedBuffer); }
/// <summary> /// Decompression for the <paramref name="compressedInput"/>. /// </summary> /// <param name="compressedInput"></param> /// <param name="uncompressedSize"></param> /// <returns>Decompressed byte[]</returns> public ReadOnlyMemory <byte> Decompress(ReadOnlySpan <byte> compressedInput, int uncompressedSize) { using var decoder = new KrakenDecoder(); var decompressedBuffer = new byte[uncompressedSize]; var remainingBytes = uncompressedSize; var sourceLength = compressedInput.Length; var destinationOffset = 0; fixed(byte *decompressedBufferPtr = decompressedBuffer) fixed(byte *compressedInputPtr = compressedInput) fixed(byte *scratchPtr = decoder.Scratch) { var sourceStart = compressedInputPtr; var decompressBufferStart = decompressedBufferPtr; while (remainingBytes != 0) { if (!DecodeStep(decoder, decompressedBufferPtr, destinationOffset, remainingBytes, sourceStart, sourceLength, scratchPtr)) { throw new DecoderException($"Failed DecodeStep method"); } sourceStart += decoder.SourceUsed; sourceLength -= decoder.SourceUsed; destinationOffset += decoder.DestinationUsed; remainingBytes -= decoder.DestinationUsed; } } return(decompressedBuffer); }
private bool DecodeStep(KrakenDecoder decoder, byte *destination, int destinationOffset, int remainingDestinationBytes, byte *source, int sourceBytesleft, byte *scratch) { byte *sourceIn = source; byte *sourceEnd = source + sourceBytesleft; if ((destinationOffset & 0x3FFFF) == 0) { decoder.Header = ParseHeader(source); source += 2; } //Only need Mermaid for Fortnite //"Oodle initializing compressor with Mermaid, level Normal, SpaceSpeed tradeoff 256" bool isKrakenDecoder = decoder.Header.DecoderType == DecoderTypes.Mermaid; int destinationBytesLeft = Math.Min(isKrakenDecoder ? 0x40000 : 0x4000, remainingDestinationBytes); if (decoder.Header.Uncompressed) { if (sourceEnd - source < destinationBytesLeft) { throw new DecoderException($"DecodeStep: sourceEnd - source ({sourceEnd - source }) < destinationBytesLeft ({destinationBytesLeft})"); } //throw new NotImplementedException($"memmove(dst_start + offset, src, dst_bytes_left);"); Buffer.MemoryCopy(source, destination + destinationOffset, sourceBytesleft, sourceBytesleft); decoder.SourceUsed = (int)((source - sourceIn) + destinationBytesLeft); decoder.DestinationUsed = destinationBytesLeft; return(true); } KrakenQuantumHeader quantumHeader; if (isKrakenDecoder) { quantumHeader = ParseQuantumHeader(source, decoder.Header.UseChecksums, out int bytesRead); source += bytesRead; } else { throw new DecoderException($"Decoder type {decoder.Header.DecoderType} not supported"); } if (source > sourceEnd) { throw new DecoderException($"Index out of range of source array"); } // Too few bytes in buffer to make any progress? if (sourceEnd - source < quantumHeader.CompressedSize) { decoder.SourceUsed = 0; decoder.DestinationUsed = 0; return(true); } if (quantumHeader.CompressedSize > remainingDestinationBytes) { throw new DecoderException($"Invalid compression size CompressedSize > RemainingDestinationLength. {quantumHeader.CompressedSize} > {remainingDestinationBytes}"); } if (quantumHeader.CompressedSize == 0) { if (quantumHeader.WholeMatchDistance != 0) { if (quantumHeader.WholeMatchDistance > destinationOffset) { throw new DecoderException($"WholeMatchDistance > destinationOffset. {quantumHeader.WholeMatchDistance} > {destinationOffset}"); } throw new NotImplementedException($"Kraken_CopyWholeMatch(dst_start + offset, qhdr.whole_match_distance, dst_bytes_left);"); } else { uint val = quantumHeader.Checksum; Buffer.MemoryCopy(&val, destination + destinationOffset, destinationBytesLeft, destinationBytesLeft); } decoder.SourceUsed = (int)(source - sourceIn); decoder.DestinationUsed = destinationBytesLeft; return(true); } if (decoder.Header.UseChecksums) { uint checksum = GetCrc(source, quantumHeader.CompressedSize) & 0xFFFFFF; if (checksum != quantumHeader.Checksum) { throw new DecoderException($"Invalid checksum. Found {checksum} need {quantumHeader.Checksum}"); } } if (quantumHeader.CompressedSize == destinationBytesLeft) { decoder.SourceUsed = (int)((source - sourceIn) + destinationBytesLeft); decoder.DestinationUsed = destinationBytesLeft; throw new NotImplementedException($"memmove(dst_start + offset, src, dst_bytes_left);"); } int numBytes; switch (decoder.Header.DecoderType) { case DecoderTypes.Mermaid: numBytes = MermaidDecodeQuantum(destination + destinationOffset, destination + destinationOffset + destinationBytesLeft, destination, source, source + quantumHeader.CompressedSize, scratch, decoder.ScratchSize); break; default: throw new DecoderException($"Decoder type {decoder.Header.DecoderType} currently not supported"); } if (numBytes != quantumHeader.CompressedSize) { throw new DecoderException($"Invalid number of bytes decompressed. {numBytes} != {quantumHeader.CompressedSize}"); } decoder.SourceUsed = (int)(source - sourceIn) + numBytes; decoder.DestinationUsed = destinationBytesLeft; return(true); }