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);
        }
Beispiel #2
0
        /// <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);
        }