Files.FAR3.Decompresser.Compress C# (CSharp) Method

Compress() public method

Compresses data and returns it as an array of bytes. Assumes that the array of bytes passed contains uncompressed data.
public Compress ( byte Data ) : byte[]
Data byte The data to be compressed.
return byte[]
        public byte[] Compress(byte[] Data)
        {
            // if data is big enough for compress
            if (Data.Length > 6)
            {
                // some Compression Data
                const int MAX_OFFSET = 0x20000;
                const int MAX_COPY_COUNT = 0x404;
                // used to finetune the lookup (small values increase the
                // compression for Big Files)
                const int QFS_MAXITER = 0x80;

                // contains the latest offset for a combination of two
                // characters
                Dictionary<int, ArrayList> cmpmap2 = new Dictionary<int, ArrayList>();

                // will contain the compressed data (maximal size =
                // uncompressedSize+MAX_COPY_COUNT)
                byte[] cData = new byte[Data.Length + MAX_COPY_COUNT];

                // init some vars
                int writeIndex = 9; // leave 9 bytes for the header
                int lastReadIndex = 0;
                ArrayList indexList = null;
                int copyOffset = 0;
                int copyCount = 0;
                int index = -1;
                bool end = false;

                // begin main compression loop
                while (index < Data.Length - 3)
                {
                    // get all Compression Candidates (list of offsets for all
                    // occurances of the current 3 bytes)
                    do
                    {
                        index++;
                        if (index >= Data.Length - 2)
                        {
                            end = true;
                            break;
                        }
                        int mapindex = Data[index] + (Data[index + 1] << 8)
                                + (Data[index + 2] << 16);

                        indexList = cmpmap2[mapindex];
                        if (indexList == null)
                        {
                            indexList = new ArrayList();
                            cmpmap2.Add(mapindex, indexList);
                        }
                        indexList.Add(index);
                    } while (index < lastReadIndex);
                    if (end)
                        break;

                    // find the longest repeating byte sequence in the index
                    // List (for offset copy)
                    int offsetCopyCount = 0;
                    int loopcount = 1;
                    while ((loopcount < indexList.Count) && (loopcount < QFS_MAXITER))
                    {
                        int foundindex = (int) indexList[(indexList.Count - 1) - loopcount];
                        if ((index - foundindex) >= MAX_OFFSET)
                        {
                            break;
                        }

                        loopcount++;
                        copyCount = 3;

                        while ((Data.Length > index + copyCount)&& (Data[index + copyCount] == Data[foundindex + copyCount]) && (copyCount < MAX_COPY_COUNT))
                        {
                            copyCount++;
                        }

                        if (copyCount > offsetCopyCount)
                        {
                            offsetCopyCount = copyCount;
                            copyOffset = index - foundindex;
                        }
                    }

                    // check if we can compress this
                    // In FSH Tool stand additionally this:
                    if (offsetCopyCount > Data.Length - index)
                    {
                        offsetCopyCount = index - Data.Length;
                    }
                    if (offsetCopyCount <= 2)
                    {
                        offsetCopyCount = 0;
                    }
                    else if ((offsetCopyCount == 3) && (copyOffset > 0x400))
                    { // 1024
                        offsetCopyCount = 0;
                    }
                    else if ((offsetCopyCount == 4) && (copyOffset > 0x4000))
                    { // 16384
                        offsetCopyCount = 0;
                    }

                    // this is offset-compressable? so do the compression
                    if (offsetCopyCount > 0)
                    {
                        // plaincopy

                        // In FSH Tool stand this (A):
                        while (index - lastReadIndex >= 4)
                        {
                            copyCount = (index - lastReadIndex) / 4 - 1;
                            if (copyCount > 0x1B)
                            {
                                copyCount = 0x1B;
                            }
                            cData[writeIndex++] = (byte)(0xE0 + copyCount);
                            copyCount = 4 * copyCount + 4;

                            ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
                            lastReadIndex += copyCount;
                            writeIndex += copyCount;
                        }

                        // offsetcopy
                        copyCount = index - lastReadIndex;
                        copyOffset--;
                        if ((offsetCopyCount <= 0x0A) && (copyOffset < 0x400))
                        {
                            cData[writeIndex++] = (byte) (((copyOffset >> 8) << 5)
                                    + ((offsetCopyCount - 3) << 2) + copyCount);
                            cData[writeIndex++] = (byte)(copyOffset & 0xff);
                        }
                        else if ((offsetCopyCount <= 0x43) && (copyOffset < 0x4000))
                        {
                            cData[writeIndex++] = (byte)(0x80 + (offsetCopyCount - 4));
                            cData[writeIndex++] = (byte) ((copyCount << 6) + (copyOffset >> 8));
                            cData[writeIndex++] = (byte) (copyOffset & 0xff);
                        }
                        else if ((offsetCopyCount <= MAX_COPY_COUNT) && (copyOffset < MAX_OFFSET))
                        {
                            cData[writeIndex++] = (byte)(0xc0
                                    + ((copyOffset >> 16) << 4)
                                    + (((offsetCopyCount - 5) >> 8) << 2) + copyCount);
                            cData[writeIndex++] = (byte)((copyOffset >> 8) & 0xff);
                            cData[writeIndex++] = (byte)(copyOffset & 0xff);
                            cData[writeIndex++] = (byte)((offsetCopyCount - 5) & 0xff);
                        }

                        // do the offset copy
                        ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
                        writeIndex += copyCount;
                        lastReadIndex += copyCount;
                        lastReadIndex += offsetCopyCount;
                    }
                }

                // add the End Record
                index = Data.Length;
                // in FSH Tool stand the same as above (A)
                while (index - lastReadIndex >= 4)
                {
                    copyCount = (index - lastReadIndex) / 4 - 1;

                    if (copyCount > 0x1B)
                        copyCount = 0x1B;

                    cData[writeIndex++] = (byte)(0xE0 + copyCount);
                    copyCount = 4 * copyCount + 4;

                    ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
                    lastReadIndex += copyCount;
                    writeIndex += copyCount;
                }

                copyCount = index - lastReadIndex;
                cData[writeIndex++] = (byte) (0xfc + copyCount);
                ArrayCopy2(Data, lastReadIndex, ref cData, writeIndex, copyCount);
                writeIndex += copyCount;
                lastReadIndex += copyCount;

                MemoryStream DataStream = new MemoryStream();
                BinaryWriter Writer = new BinaryWriter(DataStream);

                // write the header for the compressed data
                // set the compressed size
                Writer.Write((uint)writeIndex);
                m_CompressedSize = writeIndex;
                // set the MAGICNUMBER
                Writer.Write((ushort)0xFB10);
                // set the decompressed size
                byte[] revData = BitConverter.GetBytes(Data.Length);
                Writer.Write((revData[2] << 16) | (revData[1] << 8) | revData[0]);
                Writer.Write(cData);

                //Avoid nasty swearing here!
                Writer.Flush();

                m_DecompressedSize = Data.Length;

                return DataStream.ToArray();
            }

            return Data;
        }