public void Serialize(Stream output) { const uint headerSize = 32; output.WriteUInt32(0x42424947); output.WriteUInt32(Version); var keys = new List <string> { "" }; var maxValueLength = 0; var blob = new StringBuilder(); foreach (var file in Files) { keys.Add(file.Name); foreach (var section in file.Sections) { keys.Add(section.Key); foreach (var value in section.Value) { keys.Add(value.Key); foreach (var item in value.Value) { if (item.Value != null) { blob.Append(item.Value + '\0'); maxValueLength = Math.Max(maxValueLength, item.Value.Length); } } } } } var huffmanEncoder = new Encoder(); huffmanEncoder.Build(blob.ToString()); keys = keys.Distinct().OrderBy(k => k.HashCrc32()).ToList(); var maxKeyLength = keys.Max(k => k.Length); uint stringTableSize; using (var data = new MemoryStream()) { data.Position = 4; data.WriteInt32(keys.Count); data.Position = 4 + 4 + (8 * keys.Count); var offsets = new List <KeyValuePair <uint, uint> >(); foreach (var key in keys) { var offset = (uint)data.Position; data.WriteUInt16((ushort)key.Length); //data.WriteString(key, Encoding.UTF8); data.WriteStringUTF8(key); offsets.Add(new KeyValuePair <uint, uint>(key.HashCrc32(), offset)); } data.Position = 8; foreach (var kv in offsets) { data.WriteUInt32(kv.Key); data.WriteUInt32(kv.Value - 8); } data.Position = 0; data.WriteUInt32((uint)data.Length); data.Position = 0; stringTableSize = (uint)data.Length; output.Seek(headerSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } uint huffmanSize; using (var data = new MemoryStream()) { var pairs = huffmanEncoder.GetPairs(); data.WriteUInt16((ushort)pairs.Length); foreach (var pair in pairs) { data.WriteInt32(pair.Left); data.WriteInt32(pair.Right); } data.Position = 0; huffmanSize = (uint)data.Length; output.Seek(headerSize + stringTableSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } var bits = new BitArray(huffmanEncoder.TotalBits); var bitOffset = 0; uint indexSize; using (var index = new MemoryStream()) { var fileDataOffset = 2 + (Files.Count * 6); var files = new List <KeyValuePair <ushort, int> >(); foreach (var file in Files.OrderBy(f => keys.IndexOf(f.Name))) { files.Add(new KeyValuePair <ushort, int>((ushort)keys.IndexOf(file.Name), fileDataOffset)); var sectionDataOffset = 2 + (file.Sections.Count * 6); var sections = new List <KeyValuePair <ushort, int> >(); foreach (var section in file.Sections.OrderBy(s => keys.IndexOf(s.Key))) { sections.Add(new KeyValuePair <ushort, int>((ushort)keys.IndexOf(section.Key), sectionDataOffset)); var valueDataOffset = 2 + (section.Value.Count * 6); var values = new List <KeyValuePair <ushort, int> >(); foreach (var value in section.Value.OrderBy(v => keys.IndexOf(v.Key))) { index.Position = fileDataOffset + sectionDataOffset + valueDataOffset; values.Add(new KeyValuePair <ushort, int>((ushort)keys.IndexOf(value.Key), valueDataOffset)); index.WriteUInt16((ushort)value.Value.Count); valueDataOffset += 2; foreach (var item in value.Value) { switch (item.Type) { case -1: { continue; } case 1: { index.WriteInt32((1 << 29) | bitOffset); break; } case 0: case 2: case 3: case 4: { var type = item.Type; if (OverrideCompileValueTypes >= 0) { type = OverrideCompileValueTypes; } index.WriteInt32((type << 29) | bitOffset); bitOffset += huffmanEncoder.Encode((item.Value ?? "") + '\0', bits, bitOffset); break; } } valueDataOffset += 4; } } index.Position = fileDataOffset + sectionDataOffset; index.WriteUInt16((ushort)values.Count); sectionDataOffset += 2; foreach (var value in values) { index.WriteUInt16(value.Key); index.WriteInt32(value.Value); sectionDataOffset += 6; } sectionDataOffset += valueDataOffset; } index.Position = fileDataOffset; index.WriteUInt16((ushort)sections.Count); fileDataOffset += 2; foreach (var section in sections) { index.WriteUInt16(section.Key); index.WriteInt32(section.Value); fileDataOffset += 6; } fileDataOffset += sectionDataOffset; } index.Position = 0; index.WriteUInt16((ushort)files.Count); foreach (var file in files) { index.WriteUInt16(file.Key); index.WriteInt32(file.Value); } index.Position = 0; indexSize = (uint)index.Length; output.Seek(headerSize + stringTableSize + huffmanSize, SeekOrigin.Begin); output.WriteFromStream(index, index.Length); } output.Seek(headerSize + stringTableSize + huffmanSize + indexSize, SeekOrigin.Begin); output.WriteInt32(bits.Length); var bytes = new byte[(bits.Length - 1) / 8 + 1]; bits.CopyTo(bytes, 0); output.Write(bytes); output.Seek(8, SeekOrigin.Begin); output.WriteInt32(maxKeyLength); output.WriteInt32(maxValueLength); output.WriteUInt32(stringTableSize); output.WriteUInt32(huffmanSize); output.WriteUInt32(indexSize); output.WriteInt32(bytes.Length); output.Seek(0, SeekOrigin.Begin); output.WriteUInt32(0x666D726D); }
public void Serialize(Stream output) { var endian = ByteOrder; const uint headerSize = 32; output.WriteUInt32(0x42424947, endian); output.WriteUInt32(Version, endian); var keys = new List<string> { "" }; var maxValueLength = 0; var blob = new StringBuilder(); foreach (var file in Files) { keys.Add(file.Name); foreach (var section in file.Sections) { keys.Add(section.Key); foreach (var value in section.Value) { keys.Add(value.Key); foreach (var item in value.Value) { if (item.Value != null) { blob.Append(item.Value + '\0'); maxValueLength = Math.Max(maxValueLength, item.Value.Length); } } } } } var huffmanEncoder = new Encoder(); huffmanEncoder.Build(blob.ToString()); keys = keys.Distinct().OrderBy(k => k.HashCrc32()).ToList(); var maxKeyLength = keys.Max(k => k.Length); uint stringTableSize; using (var data = new MemoryStream()) { data.Position = 4; data.WriteInt32(keys.Count, endian); data.Position = 4 + 4 + (8 * keys.Count); var offsets = new List<KeyValuePair<uint, uint>>(); foreach (var key in keys) { var offset = (uint) data.Position; data.WriteUInt16((ushort) key.Length, endian); data.WriteString(key, Encoding.UTF8); offsets.Add(new KeyValuePair<uint, uint>(key.HashCrc32(), offset)); } data.Position = 8; foreach (var kv in offsets) { data.WriteUInt32(kv.Key, endian); data.WriteUInt32(kv.Value - 8, endian); } data.Position = 0; data.WriteUInt32((uint) data.Length, endian); data.Position = 0; stringTableSize = (uint) data.Length; output.Seek(headerSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } uint huffmanSize; using (var data = new MemoryStream()) { var pairs = huffmanEncoder.GetPairs(); data.WriteUInt16((ushort) pairs.Length, endian); foreach (var pair in pairs) { data.WriteInt32(pair.Left, endian); data.WriteInt32(pair.Right, endian); } data.Position = 0; huffmanSize = (uint) data.Length; output.Seek(headerSize + stringTableSize, SeekOrigin.Begin); output.WriteFromStream(data, data.Length); } var bits = new BitArray(huffmanEncoder.TotalBits); var bitOffset = 0; uint indexSize; using (var index = new MemoryStream()) { var fileDataOffset = 2 + (Files.Count * 6); var files = new List<KeyValuePair<ushort, int>>(); foreach (var file in Files.OrderBy(f => keys.IndexOf(f.Name))) { files.Add(new KeyValuePair<ushort, int>((ushort) keys.IndexOf(file.Name), fileDataOffset)); var sectionDataOffset = 2 + (file.Sections.Count * 6); var sections = new List<KeyValuePair<ushort, int>>(); foreach (var section in file.Sections.OrderBy(s => keys.IndexOf(s.Key))) { sections.Add(new KeyValuePair<ushort, int>((ushort) keys.IndexOf(section.Key), sectionDataOffset)); var valueDataOffset = 2 + (section.Value.Count * 6); var values = new List<KeyValuePair<ushort, int>>(); foreach (var value in section.Value.OrderBy(v => keys.IndexOf(v.Key))) { index.Position = fileDataOffset + sectionDataOffset + valueDataOffset; values.Add(new KeyValuePair<ushort, int>((ushort) keys.IndexOf(value.Key), valueDataOffset)); index.WriteUInt16((ushort) value.Value.Count, endian); valueDataOffset += 2; foreach (var item in value.Value) { switch (item.Type) { case -1: { continue; } case 1: { index.WriteInt32((1 << 29) | bitOffset, endian); break; } case 0: case 2: case 3: case 4: { var type = item.Type; if (OverrideCompileValueTypes >= 0) { type = OverrideCompileValueTypes; } index.WriteInt32((type << 29) | bitOffset, endian); bitOffset += huffmanEncoder.Encode((item.Value ?? "") + '\0', bits, bitOffset); break; } } valueDataOffset += 4; } } index.Position = fileDataOffset + sectionDataOffset; index.WriteUInt16((ushort) values.Count, endian); sectionDataOffset += 2; foreach (var value in values) { index.WriteUInt16(value.Key, endian); index.WriteInt32(value.Value, endian); sectionDataOffset += 6; } sectionDataOffset += valueDataOffset; } index.Position = fileDataOffset; index.WriteUInt16((ushort) sections.Count, endian); fileDataOffset += 2; foreach (var section in sections) { index.WriteUInt16(section.Key, endian); index.WriteInt32(section.Value, endian); fileDataOffset += 6; } fileDataOffset += sectionDataOffset; } index.Position = 0; index.WriteUInt16((ushort) files.Count, endian); foreach (var file in files) { index.WriteUInt16(file.Key, endian); index.WriteInt32(file.Value, endian); } index.Position = 0; indexSize = (uint) index.Length; output.Seek(headerSize + stringTableSize + huffmanSize, SeekOrigin.Begin); output.WriteFromStream(index, index.Length); } output.Seek(headerSize + stringTableSize + huffmanSize + indexSize, SeekOrigin.Begin); output.WriteInt32(bits.Length, endian); var bytes = new byte[(bits.Length - 1) / 8 + 1]; bits.CopyTo(bytes, 0); output.WriteBytes(bytes); output.Seek(8, SeekOrigin.Begin); output.WriteInt32(maxKeyLength, endian); output.WriteInt32(maxValueLength, endian); output.WriteUInt32(stringTableSize, endian); output.WriteUInt32(huffmanSize, endian); output.WriteUInt32(indexSize, endian); output.WriteInt32(bytes.Length, endian); output.Seek(0, SeekOrigin.Begin); output.WriteUInt32(0x666D726D, endian); }