public void Deserialize(Stream input)
        {
            var magic = input.ReadValueU32(Endian.Little);

            if (magic != 0x666D726D && // fmrm
                magic.Swap() != 0x666D726D)
            {
                throw new FormatException();
            }
            var endian = magic == 0x666D726D ? Endian.Little : Endian.Big;

            var version = input.ReadValueU32(endian);

            if (version != 1)
            {
                throw new FormatException();
            }
            this.Version = version;

            /*var maxKeyLength =*/ input.ReadValueS32(endian);
            var maxValueLength = input.ReadValueS32(endian);

            var stringTableSize = input.ReadValueU32(endian);
            var huffmanSize     = input.ReadValueU32(endian);
            var indexSize       = input.ReadValueU32(endian);
            var dataSize        = input.ReadValueU32(endian);

            var strings = new List <KeyValuePair <uint, string> >();

            using (var data = input.ReadToMemoryStream(stringTableSize))
            {
                var localStringTableSize = data.ReadValueU32(endian);
                if (localStringTableSize != stringTableSize)
                {
                    throw new FormatException();
                }

                var count = data.ReadValueU32(endian);

                var offsets = new List <KeyValuePair <uint, uint> >();
                for (uint i = 0; i < count; i++)
                {
                    var hash   = data.ReadValueU32(endian);
                    var offset = data.ReadValueU32(endian);
                    offsets.Add(new KeyValuePair <uint, uint>(hash, offset));
                }

                foreach (var kv in offsets)
                {
                    var hash   = kv.Key;
                    var offset = kv.Value;

                    data.Seek(8 + offset, SeekOrigin.Begin);
                    var length = data.ReadValueU16(endian);
                    var text   = data.ReadString(length, Encoding.UTF8);

                    if (text.HashCrc32() != hash)
                    {
                        throw new InvalidOperationException();
                    }

                    strings.Add(new KeyValuePair <uint, string>(hash, text));
                }
            }

            Huffman.Pair[] huffmanTree;
            using (var data = input.ReadToMemoryStream(huffmanSize))
            {
                var count = data.ReadValueU16(endian);
                huffmanTree = new Huffman.Pair[count];
                for (ushort i = 0; i < count; i++)
                {
                    var left  = data.ReadValueS32(endian);
                    var right = data.ReadValueS32(endian);
                    huffmanTree[i] = new Huffman.Pair(left, right);
                }
            }

            using (var index = input.ReadToMemoryStream(indexSize))
            {
                var totalBits = input.ReadValueS32(endian);
                var data      = input.ReadBytes(dataSize);
                var bitArray  = new BitArray(data)
                {
                    Length = totalBits
                };

                var files     = new List <KeyValuePair <string, uint> >();
                var fileCount = index.ReadValueU16(endian);
                for (ushort i = 0; i < fileCount; i++)
                {
                    var nameIndex = index.ReadValueU16(endian);
                    var name      = strings[nameIndex].Value;
                    var offset    = index.ReadValueU32(endian);
                    files.Add(new KeyValuePair <string, uint>(name, offset));
                }

                foreach (var fileInfo in files.OrderBy(f => f.Key))
                {
                    var file = new Coalesced.File()
                    {
                        Name = fileInfo.Key
                    };

                    index.Seek(fileInfo.Value, SeekOrigin.Begin);
                    var sectionCount = index.ReadValueU16(endian);
                    var sections     = new List <KeyValuePair <string, uint> >();
                    for (ushort i = 0; i < sectionCount; i++)
                    {
                        var nameIndex = index.ReadValueU16(endian);
                        var name      = strings[nameIndex].Value;
                        var offset    = index.ReadValueU32(endian);
                        sections.Add(new KeyValuePair <string, uint>(name, offset));
                    }

                    foreach (var sectionInfo in sections.OrderBy(s => s.Key))
                    {
                        var section = new Dictionary <string, List <string> >();

                        index.Seek(fileInfo.Value + sectionInfo.Value, SeekOrigin.Begin);
                        var valueCount = index.ReadValueU16(endian);
                        var values     = new List <KeyValuePair <string, uint> >();
                        for (ushort i = 0; i < valueCount; i++)
                        {
                            var nameIndex = index.ReadValueU16(endian);
                            var name      = strings[nameIndex].Value;
                            var offset    = index.ReadValueU32(endian);
                            values.Add(new KeyValuePair <string, uint>(name, offset));
                        }

                        foreach (var valueInfo in values.OrderBy(v => v.Key))
                        {
                            var value = new List <string>();

                            index.Seek(fileInfo.Value + sectionInfo.Value + valueInfo.Value, SeekOrigin.Begin);
                            var itemCount = index.ReadValueU16(endian);

                            for (ushort i = 0; i < itemCount; i++)
                            {
                                var offset = index.ReadValueS32(endian);

                                var type = (offset & 0xE0000000) >> 29;
                                if (type == 1)
                                {
                                    value.Add(null);
                                }
                                else if (type == 2)
                                {
                                    offset &= 0x1FFFFFFF;
                                    var text = Huffman.Decoder.Decode(
                                        huffmanTree, bitArray, offset, maxValueLength);
                                    value.Add(text);
                                }

                                /*else if (type == 0 || type == 3)
                                 * {
                                 *  offset &= 0x1FFFFFFF;
                                 *  var text = Huffman.Decoder.Decode(
                                 *      huffmanTree, bitArray, offset, maxValueLength);
                                 *  value.Add("<<wtf" + type.ToString() + ">>" + text);
                                 * }*/
                                else
                                {
                                    throw new NotImplementedException();
                                }
                            }

                            section.Add(valueInfo.Key, value);
                        }

                        file.Sections.Add(sectionInfo.Key, section);
                    }

                    this.Files.Add(file);
                }
            }

            this.Endian = endian;
        }
        public void Deserialize(Stream input)
        {
            var magic = input.ReadValueU32(Endian.Little);
            if (magic != 0x666D726D && // fmrm
                magic.Swap() != 0x666D726D)
            {
                throw new FormatException();
            }
            var endian = magic == 0x666D726D ? Endian.Little : Endian.Big;

            var version = input.ReadValueU32(endian);
            if (version != 1)
            {
                throw new FormatException();
            }
            this.Version = version;

            /*var maxKeyLength =*/ input.ReadValueS32(endian);
            var maxValueLength = input.ReadValueS32(endian);

            var stringTableSize = input.ReadValueU32(endian);
            var huffmanSize = input.ReadValueU32(endian);
            var indexSize = input.ReadValueU32(endian);
            var dataSize = input.ReadValueU32(endian);

            var strings = new List<KeyValuePair<uint, string>>();
            using (var data = input.ReadToMemoryStream(stringTableSize))
            {
                var localStringTableSize = data.ReadValueU32(endian);
                if (localStringTableSize != stringTableSize)
                {
                    throw new FormatException();
                }

                var count = data.ReadValueU32(endian);

                var offsets = new List<KeyValuePair<uint, uint>>();
                for (uint i = 0; i < count; i++)
                {
                    var hash = data.ReadValueU32(endian);
                    var offset = data.ReadValueU32(endian);
                    offsets.Add(new KeyValuePair<uint, uint>(hash, offset));
                }

                foreach (var kv in offsets)
                {
                    var hash = kv.Key;
                    var offset = kv.Value;

                    data.Seek(8 + offset, SeekOrigin.Begin);
                    var length = data.ReadValueU16(endian);
                    var text = data.ReadString(length, Encoding.UTF8);

                    if (text.HashCrc32() != hash)
                    {
                        throw new InvalidOperationException();
                    }

                    strings.Add(new KeyValuePair<uint, string>(hash, text));
                }
            }

            Huffman.Pair[] huffmanTree;
            using (var data = input.ReadToMemoryStream(huffmanSize))
            {
                var count = data.ReadValueU16(endian);
                huffmanTree = new Huffman.Pair[count];
                for (ushort i = 0; i < count; i++)
                {
                    var left = data.ReadValueS32(endian);
                    var right = data.ReadValueS32(endian);
                    huffmanTree[i] = new Huffman.Pair(left, right);
                }
            }

            using (var index = input.ReadToMemoryStream(indexSize))
            {
                var totalBits = input.ReadValueS32(endian);
                var data = input.ReadBytes(dataSize);
                var bitArray = new BitArray(data) { Length = totalBits };

                var files = new List<KeyValuePair<string, uint>>();
                var fileCount = index.ReadValueU16(endian);
                for (ushort i = 0; i < fileCount; i++)
                {
                    var nameIndex = index.ReadValueU16(endian);
                    var name = strings[nameIndex].Value;
                    var offset = index.ReadValueU32(endian);
                    files.Add(new KeyValuePair<string, uint>(name, offset));
                }

                foreach (var fileInfo in files.OrderBy(f => f.Key))
                {
                    var file = new Coalesced.File() { Name = fileInfo.Key };

                    index.Seek(fileInfo.Value, SeekOrigin.Begin);
                    var sectionCount = index.ReadValueU16(endian);
                    var sections = new List<KeyValuePair<string, uint>>();
                    for (ushort i = 0; i < sectionCount; i++)
                    {
                        var nameIndex = index.ReadValueU16(endian);
                        var name = strings[nameIndex].Value;
                        var offset = index.ReadValueU32(endian);
                        sections.Add(new KeyValuePair<string, uint>(name, offset));
                    }

                    foreach (var sectionInfo in sections.OrderBy(s => s.Key))
                    {
                        var section = new Dictionary<string, List<Coalesced.Entry>>();

                        index.Seek(fileInfo.Value + sectionInfo.Value, SeekOrigin.Begin);
                        var valueCount = index.ReadValueU16(endian);
                        var values = new List<KeyValuePair<string, uint>>();
                        for (ushort i = 0; i < valueCount; i++)
                        {
                            var nameIndex = index.ReadValueU16(endian);
                            var name = strings[nameIndex].Value;
                            var offset = index.ReadValueU32(endian);
                            values.Add(new KeyValuePair<string, uint>(name, offset));
                        }

                        foreach (var valueInfo in values.OrderBy(v => v.Key))
                        {
                            var value = new List<Coalesced.Entry>();

                            index.Seek(fileInfo.Value + sectionInfo.Value + valueInfo.Value, SeekOrigin.Begin);
                            var itemCount = index.ReadValueU16(endian);

                            for (ushort i = 0; i < itemCount; i++)
                            {
                                var offset = index.ReadValueS32(endian);

                                var type = (offset & 0xE0000000) >> 29;
                                if (type == 1)
                                {
                                    value.Add(new Coalesced.Entry(1, null));
                                }
                                else if (type == 0 || type == 2 || type == 3 || type == 4)
                                {
                                    offset &= 0x1FFFFFFF;
                                    var text = Huffman.Decoder.Decode(
                                        huffmanTree, bitArray, offset, maxValueLength);
                                    value.Add(new Coalesced.Entry(2, text));
                                }
                                else
                                {
                                    throw new NotImplementedException();
                                }
                            }

                            section.Add(valueInfo.Key, value);
                        }

                        file.Sections.Add(sectionInfo.Key, section);
                    }

                    this.Files.Add(file);
                }
            }

            this.Endian = endian;
        }