Brick icon

MM2 CPVS 2

Brick | PRO | 01/17/18 12:38:34 PM UTC | 0 ⭐ | 260 👁️ | Never ⏰ | []
C# |

7.09 KB

|

None

|

0 👍

/

0 👎

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApp2
{
    class Program
    {
        struct CPVSCompressor
        {
            public static byte[] Decompress(byte[] data, int start, int end)
            {
                byte[] result = new byte[512];
 
                for (int input = start, output = 0; input < end;)
                {
                    byte size = data[input++];
 
                    if (size >= 128) // Copy
                    {
                        size -= 127;
 
                        for (int j = 0; j < size; ++j)
                            result[output++] = data[input++];
                    }
                    else // Fill
                    {
                        byte value = data[input++];
 
                        for (int j = 0; j < size; ++j)
                            result[output++] = value;
                    }
                }
 
                return result;
            }
 
            public static int GetFillLength(byte[] data, int index)
            {
                var result = Array.FindIndex(data, index + 1, x => x != data[index]);
 
                if (result < 0)
                {
                    result = data.Length;
                }
 
                result -= index;
 
                return result;
            }
 
            public static (int, int) GetNextFill(byte[] data, int index)
            {
                int length = 0;
 
                while (index < data.Length)
                {
                    length = GetFillLength(data, index);
 
                    if (length >= 3)
                    {
                        break;
                    }
 
                    index += length;
                }
 
                return (index, length);
            }
 
            public static void WriteCopy(BinaryWriter writer, byte[] data, int index, int length)
            {
                const int MAX_COPY = 128;
 
                for (int i = 0; i < length; i += MAX_COPY)
                {
                    var block_length = Math.Min(length - i, MAX_COPY);
 
                    writer.Write((byte) (block_length + 127));
                    writer.Write(data, index + i, block_length);
                }
            }
 
            public static void WriteFill(BinaryWriter writer, byte value, int length)
            {
                const int MAX_FILL = 127;
 
                for (int i = 0; i < length; i += MAX_FILL)
                {
                    var block_length = Math.Min(length - i, MAX_FILL);
 
                    writer.Write((byte) block_length);
                    writer.Write(value);
                }
            }
 
            public static byte[] Compress(byte[] data)
            {
                var result = new MemoryStream();
                var writer = new BinaryWriter(result);
 
                for (int i = 0; i < data.Length;)
                {
                    var (fill_start, fill_length) = GetNextFill(data, i);
 
                    var copy_length = fill_start - i;
                    if (copy_length > 0)
                    {
                        WriteCopy(writer, data, i, copy_length);
                    }
 
                    if (fill_length > 0)
                    {
                        var value = data[fill_start];
 
                        // And remaining data is zeroed
                        if ((value != 0) || ((fill_start + fill_length) < data.Length))
                        {
                            WriteFill(writer, data[fill_start], fill_length);
                        }
                    }
 
                    i = fill_start + fill_length;
                }
 
                return result.ToArray();
            }
 
            public static bool[] ExpandCPVSData(byte[] data)
            {
                var results = new bool[data.Length * 4];
 
                for (int i = 0; i < results.Length; ++i)
                {
                    results[i] = ((data[i / 4] >> ((i % 4) * 2)) & 0x3) != 0;
                }
 
                return results;
            }
        }
 
        struct CPVS
        {
            static readonly uint PVS0_MAGIC = 0x30535650; /*PVS0*/
 
            public readonly byte[][] Values;
 
            public CPVS(BinaryReader reader)
            {
                if (reader.ReadUInt32() != PVS0_MAGIC)
                {
                    throw new Exception("Invalid Magic");
                }
 
                var indices = new int[reader.ReadUInt32()];
                indices[0] = 0;
 
                for (int i = 1; i < indices.Length; ++i)
                {
                    indices[i] = reader.ReadInt32();
                }
 
                var data = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
 
                Values = Enumerable.Range(0, indices.Length - 1).Select(x => CPVSCompressor.Decompress(data, indices[x], indices[x + 1])).ToArray();
            }
 
            public void Save(BinaryWriter writer)
            {
                writer.Write(PVS0_MAGIC);
 
                var compresed = Values.Select(x => CPVSCompressor.Compress(x)).ToArray();
 
                writer.Write(compresed.Length + 1);
 
                for (int i = 0, offset = 0; i < compresed.Length; ++i)
                {
                    offset += compresed[i].Length;
 
                    writer.Write(offset);
                }
 
                foreach (var value in compresed)
                {
                    writer.Write(value);
                }
            }
        }
 
        static CPVS ReadCPVS(string filename)
        {
            using (var fs = File.OpenRead(filename))
            {
                var reader = new BinaryReader(fs);
 
                return new CPVS(reader);
            }
        }
 
        static void WriteCPVS(CPVS cpvs, string filename)
        {
            using (var fs = File.OpenWrite(filename))
            {
                var writer = new BinaryWriter(fs);
 
                cpvs.Save(writer);
            }
        }
 
        static string ByteArrayToHex(byte[] data)
        {
            return String.Join(String.Empty, Array.ConvertAll(data, x => x.ToString("X2")));
        }
 
        static void Main(string[] args)
        {
            string original_name = "london.cpvs";
            string copy_name = "london_copy.cpvs";
 
            var original = ReadCPVS(original_name);
 
            WriteCPVS(original, copy_name);
 
            var copy = ReadCPVS(copy_name);
 
            if (original.Values.Length != copy.Values.Length)
            {
                Console.WriteLine("Failed Length");
            }
 
            for (int i = 0; i < original.Values.Length; ++i)
            {
                if (!Enumerable.SequenceEqual(original.Values[i], copy.Values[i]))
                {
                    Console.WriteLine("{0} Different", i);
                }
            }
        }
    }
}

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎