private bool MermaidProcessLzRuns(int mode, byte *source, byte *sourceEnd, byte *destination, int destinationSize, long offset, byte *destinationEnd, MermaidLzTable *lz)
        {
            int   iteration        = 0;
            byte *destinationStart = destination - offset;
            int   savedDist        = -8;
            byte *sourceCurrent    = null;

            for (iteration = 0; iteration != 2; iteration++)
            {
                int destinationSizeCurrent = destinationSize;

                destinationSizeCurrent = destinationSizeCurrent > 0x10000 ? 0x10000 : destinationSizeCurrent;

                if (iteration == 0)
                {
                    lz->Offset32Stream    = lz->Offset32Stream1;
                    lz->Offset32StreamEnd = lz->Offset32Stream1 + lz->Offset32Stream1Size * 4;
                    lz->CmdStreamEnd      = lz->CmdStream + lz->CmdStream2Offsets;
                }
                else
                {
                    lz->Offset32Stream    = lz->Offset32Stream2;
                    lz->Offset32StreamEnd = lz->Offset32Stream2 + lz->Offset32Stream2Size * 4;
                    lz->CmdStreamEnd      = lz->CmdStream + lz->CmdStream2OffsetsEnd;

                    lz->CmdStream += lz->CmdStream2Offsets;
                }

                if (mode == 0)
                {
                    throw new NotImplementedException($"MermaidProcessLzRuns: Mode 0 not implemented currently");
                }
                else
                {
                    sourceCurrent = MermaidMode1(destination, destinationSizeCurrent, destinationEnd, destinationStart, sourceEnd, lz, &savedDist, (offset == 0) && (iteration == 0) ? 8 : 0);
                }

                destination     += destinationSizeCurrent;
                destinationSize -= destinationSizeCurrent;

                if (destinationSize == 0)
                {
                    break;
                }
            }

            if (sourceCurrent != sourceEnd)
            {
                throw new DecoderException($"MermaidProcessLzRuns: Failed to read decompress source bytes");
            }

            return(true);
        }
        private byte *MermaidMode1(byte *destination, int destinationSize, byte *destinationEndPtr, byte *destinationStart, byte *sourceEnd, MermaidLzTable *lz, int *savedDist, int startOff)
        {
            byte *  destinationEnd = destination + destinationSize;
            byte *  cmdStream      = lz->CmdStream;
            byte *  cmdStreamEnd   = lz->CmdStreamEnd;
            byte *  lengthStream   = lz->LengthStream;
            byte *  litStream      = lz->LitStream;
            byte *  litStreamEnd   = lz->LitStreamEnd;
            ushort *off16Stream    = lz->Offset16Stream;
            ushort *off16StreamEnd = lz->Offset16StreamEnd;
            uint *  off32Stream    = lz->Offset32Stream;
            uint *  off32StreamEnd = lz->Offset32StreamEnd;
            int     recentOffs     = *savedDist;
            byte *  match;
            int     length;
            byte *  destinationBegin = destination;

            destination += startOff;

            long test = cmdStreamEnd - cmdStream;

            while (cmdStream < cmdStreamEnd)
            {
                uint flag = *cmdStream++;

                if (flag >= 24)
                {
                    uint newDist     = *off16Stream;
                    uint useDistance = (flag >> 7) - 1;
                    uint litLen      = (uint)(flag & 7);

                    Util.Copy64(destination, litStream);

                    destination += litLen;
                    litStream   += litLen;

                    recentOffs ^= (int)(useDistance & (uint)(recentOffs ^ -newDist));

                    off16Stream = (ushort *)((byte *)off16Stream + (useDistance & 2));
                    match       = destination + recentOffs;

                    Util.Copy64(destination, match);
                    Util.Copy64(destination + 8, match + 8);

                    destination += (flag >> 3) & 0xF;
                }
                else if (flag > 2)
                {
                    length = (int)(flag + 5);

                    if (off32Stream == off32StreamEnd)
                    {
                        throw new DecoderException($"MermaidMode1: off32Stream == off32StreamEnd");
                    }

                    match      = destinationBegin - *off32Stream++;
                    recentOffs = (int)(match - destination);

                    if (destinationEnd - destination < length)
                    {
                        throw new DecoderException($"MermaidMode1: destinationEnd - destination < length");
                    }

                    Util.Copy64(destination, match);
                    Util.Copy64(destination + 8, match + 8);
                    Util.Copy64(destination + 16, match + 16);
                    Util.Copy64(destination + 24, match + 24);

                    destination += length;
                }
                else if (flag == 0)
                {
                    if (sourceEnd - lengthStream == 0)
                    {
                        throw new DecoderException($"MermaidMode1: Not enough source bytes remaining. Have {sourceEnd - lengthStream}");
                    }

                    length = *lengthStream;

                    if (length > 251)
                    {
                        if (sourceEnd - lengthStream < 3)
                        {
                            throw new DecoderException($"MermaidMode1: Not enough source bytes remaining. Have {sourceEnd - lengthStream}. Need 3");
                        }

                        length       += *(ushort *)(lengthStream + 1) * 4;
                        lengthStream += 2;
                    }

                    lengthStream += 1;

                    length += 64;

                    if (destinationEnd - destination < length || litStreamEnd - litStream < length)
                    {
                        throw new DecoderException($"MermaidMode1: destinationEnd - destination < length || litStreamEnd - litStream < length");
                    }

                    do
                    {
                        Util.Copy64(destination, litStream);
                        Util.Copy64(destination + 8, litStream + 8);

                        destination += 16;
                        litStream   += 16;
                        length      -= 16;
                    } while (length > 0);

                    destination += length;
                    litStream   += length;
                }
                else if (flag == 1)
                {
                    if (sourceEnd - lengthStream == 0)
                    {
                        throw new DecoderException($"MermaidMode1: Not enough source bytes remaining. Have {sourceEnd - lengthStream}");
                    }

                    length = *lengthStream;

                    if (length > 251)
                    {
                        if (sourceEnd - lengthStream < 3)
                        {
                            throw new DecoderException($"MermaidMode1: Not enough source bytes remaining. Have {sourceEnd - lengthStream}. Need 3");
                        }

                        length       += *(ushort *)(lengthStream + 1) * 4;
                        lengthStream += 2;
                    }

                    lengthStream += 1;
                    length       += 91;

                    if (off16Stream == off16StreamEnd)
                    {
                        throw new DecoderException($"MermaidMode1: off16Stream == off16StreamEnd");
                    }

                    match      = destination - *off16Stream++;
                    recentOffs = (int)(match - destination);

                    do
                    {
                        Util.Copy64(destination, match);
                        Util.Copy64(destination + 8, match + 8);

                        destination += 16;
                        match       += 16;
                        length      -= 16;
                    } while (length > 0);

                    destination += length;
                }
                else
                {
                    if (sourceEnd - lengthStream == 0)
                    {
                        throw new DecoderException($"MermaidMode1: sourceEnd - lengthStream == 0");
                    }

                    length = *lengthStream;

                    if (length > 251)
                    {
                        if (sourceEnd - lengthStream < 3)
                        {
                            throw new DecoderException($"MermaidMode1: Not enough source bytes remaining. Have {sourceEnd - lengthStream}. Need 3");
                        }

                        length       += *(ushort *)(lengthStream + 1) * 4;
                        lengthStream += 2;
                    }

                    lengthStream += 1;
                    length       += 29;

                    if (off32Stream == off32StreamEnd)
                    {
                        throw new DecoderException($"MermaidMode1: off32Stream == off32StreamEnd");
                    }

                    match      = destinationBegin - *off32Stream++;
                    recentOffs = (int)(match - destination);

                    do
                    {
                        Util.Copy64(destination, match);
                        Util.Copy64(destination + 8, match + 8);

                        destination += 16;
                        match       += 16;
                        length      -= 16;
                    } while (length > 0);

                    destination += length;
                }
            }

            length = (int)(destinationEnd - destination);

            if (length >= 8)
            {
                do
                {
                    Util.Copy64(destination, litStream);

                    destination += 8;
                    litStream   += 8;
                    length      -= 8;
                } while (length >= 8);
            }

            if (length > 0)
            {
                do
                {
                    *destination++ = *litStream++;
                } while (--length > 0);
            }

            *savedDist = recentOffs;
            lz->LengthStream   = lengthStream;
            lz->Offset16Stream = off16Stream;
            lz->LitStream      = litStream;

            return(lengthStream);
        }
        private bool MermaidReadLzTable(int mode, byte *source, byte *sourceEnd, byte *destination, int destinationSize, long offset, byte *scratch, byte *scratchEnd, MermaidLzTable *lz)
        {
            byte *scratchOut;
            int   decodeCount;
            int   numBytes;
            uint  temp;
            uint  offset32Size2;
            uint  offset32Size1;

            if (mode > 1)
            {
                return(false);
            }

            if (sourceEnd - source < 10)
            {
                return(false);
            }

            if (offset == 0)
            {
                Util.Copy64(destination, source);

                destination += 8;
                source      += 8;
            }

            //Decode lit stream
            scratchOut = scratch;

            numBytes = DecodeBytes(&scratchOut, source, sourceEnd, &decodeCount, (uint)Math.Min(scratchEnd - scratch, destinationSize), false, scratch, scratchEnd);

            source          += numBytes;
            lz->LitStream    = scratchOut;
            lz->LitStreamEnd = scratchOut + decodeCount;
            scratch         += decodeCount;

            //Decode flag stream
            scratchOut = scratch;
            numBytes   = DecodeBytes(&scratchOut, source, sourceEnd, &decodeCount, (uint)Math.Min(scratchEnd - scratch, destinationSize), false, scratch, scratchEnd);

            source          += numBytes;
            lz->CmdStream    = scratchOut;
            lz->CmdStreamEnd = scratchOut + decodeCount;
            scratch         += decodeCount;

            lz->CmdStream2OffsetsEnd = (uint)decodeCount;

            if (destinationSize <= 0x10000)
            {
                lz->CmdStream2Offsets = (uint)decodeCount;
            }
            else
            {
                if (sourceEnd - source < 2)
                {
                    throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
                }

                lz->CmdStream2Offsets = *(ushort *)source;

                source += 2;

                if (lz->CmdStream2Offsets > lz->CmdStream2OffsetsEnd)
                {
                    throw new DecoderException($"MermaidReadLzTable: lz->CmdStream2Offsets ({lz->CmdStream2Offsets}) > lz->CmdStream2OffsetsEnd ({lz->CmdStream2OffsetsEnd})");
                }
            }

            if (sourceEnd - source < 2)
            {
                throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
            }

            int off16Count = *(ushort *)source;

            if (off16Count == 0xFFFF)
            {
                byte *offset16Low;
                byte *offset16High;
                int   offset16LowCount;
                int   offset16HighCount;

                source      += 2;
                offset16High = scratch;
                numBytes     = DecodeBytes(&offset16High, source, sourceEnd, &offset16HighCount, (uint)Math.Min(scratchEnd - scratch, destinationSize >> 1), false, scratch, scratchEnd);

                source  += numBytes;
                scratch += offset16HighCount;

                offset16Low = scratch;
                numBytes    = DecodeBytes(&offset16Low, source, sourceEnd, &offset16LowCount, (uint)Math.Min(scratchEnd - scratch, destinationSize >> 1), false, scratch, scratchEnd);

                source  += numBytes;
                scratch += offset16LowCount;

                if (offset16LowCount != offset16HighCount)
                {
                    throw new DecoderException($"MermaidReadLzTable: offset16LowCount ({offset16LowCount}) != offset16HighCount ({offset16HighCount})");
                }

                scratch            = Util.AlignPointer(scratch, 2);
                lz->Offset16Stream = (ushort *)scratch;

                if (scratch + offset16LowCount * 2 > scratchEnd)
                {
                    throw new DecoderException($"MermaidReadLzTable: scratch + offset16LowCount * 2 > scratchEnd");
                }

                scratch += offset16LowCount * 2;
                lz->Offset16StreamEnd = (ushort *)scratch;

                MermaidCombineOffset16(lz->Offset16Stream, offset16LowCount, offset16Low, offset16High);
            }
            else
            {
                lz->Offset16Stream    = (ushort *)(source + 2);
                source               += 2 + off16Count * 2;
                lz->Offset16StreamEnd = (ushort *)source;
            }

            if (sourceEnd - source < 3)
            {
                throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
            }

            temp = (uint)(source[0] | source[1] << 8 | source[2] << 16);

            source += 3;

            if (temp != 0)
            {
                offset32Size1 = temp >> 12;
                offset32Size2 = temp & 0xFFF;

                if (offset32Size1 == 4095)
                {
                    if (sourceEnd - source < 2)
                    {
                        throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
                    }

                    offset32Size1 = *(ushort *)source;
                    source       += 2;
                }

                if (offset32Size2 == 4095)
                {
                    if (sourceEnd - source < 2)
                    {
                        throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
                    }

                    offset32Size2 = *(ushort *)source;
                    source       += 2;
                }

                lz->Offset32Stream1Size = offset32Size1;
                lz->Offset32Stream2Size = offset32Size2;

                if (scratch + 4 * (offset32Size1 + offset32Size2) + 64 > scratchEnd)
                {
                    throw new DecoderException($"MermaidReadLzTable: Not enough remaining scratch space");
                }

                scratch = Util.AlignPointer(scratch, 4);

                lz->Offset32Stream1 = (uint *)scratch;
                scratch            += offset32Size1 * 4;

                // store dummy bytes after for prefetcher.
                ((ulong *)scratch)[0] = 0;
                ((ulong *)scratch)[1] = 0;
                ((ulong *)scratch)[2] = 0;
                ((ulong *)scratch)[3] = 0;
                scratch += 32;

                lz->Offset32Stream2 = (uint *)scratch;
                scratch            += offset32Size2 * 4;

                // store dummy bytes after for prefetcher.
                ((ulong *)scratch)[0] = 0;
                ((ulong *)scratch)[1] = 0;
                ((ulong *)scratch)[2] = 0;
                ((ulong *)scratch)[3] = 0;
                scratch += 32;

                numBytes = MermaidDecodeFarOffsets(source, sourceEnd, lz->Offset32Stream1, lz->Offset32Stream1Size, offset);

                source += numBytes;

                numBytes = MermaidDecodeFarOffsets(source, sourceEnd, lz->Offset32Stream2, lz->Offset32Stream2Size, offset + 0x10000);

                source += numBytes;
            }
            else
            {
                if (scratchEnd - scratch < 32)
                {
                    throw new DecoderException($"MermaidReadLzTable: Too few bytes ({sourceEnd - source}) remaining");
                }


                lz->Offset32Stream1Size = 0;
                lz->Offset32Stream2Size = 0;
                lz->Offset32Stream1     = (uint *)scratch;
                lz->Offset32Stream2     = (uint *)scratch;

                // store dummy bytes after for prefetcher.
                ((ulong *)scratch)[0] = 0;
                ((ulong *)scratch)[1] = 0;
                ((ulong *)scratch)[2] = 0;
                ((ulong *)scratch)[3] = 0;
            }

            lz->LengthStream = source;

            return(true);
        }