/// <summary> /// Parse the root file into <see cref="RootFile"/> /// </summary> /// <param name="content"></param> /// <returns></returns> public static RootFile ParseRoot(MemoryStream contentStream) { var rootfile = new RootFile { Lookup = new MultiDictionary <ulong, RootEntry>(), FileDataIds = new MultiDictionary <uint, RootEntry>() }; var namedCount = 0; var unnamedCount = 0; var newRoot = false; using (var stream = new MemoryStream(BLTE.Parse(contentStream.ToArray()))) using (var reader = new BinaryReader(stream)) { var header = reader.ReadUInt32(); if (header == HeaderFmt) { var totalFiles = reader.ReadUInt32(); var namedFiles = reader.ReadUInt32(); newRoot = true; } else { reader.BaseStream.Position = 0; } while (reader.BaseStream.Position < reader.BaseStream.Length) { var count = reader.ReadUInt32(); var contentFlags = (ContentFlags)reader.ReadUInt32(); var localeFlags = (LocaleFlags)reader.ReadUInt32(); var rootEntries = new RootEntry[count]; var fileDataIds = new int[count]; var idx = 0; for (var i = 0; i < count; ++i) { rootEntries[i].LocaleFlags = localeFlags; rootEntries[i].ContentFlags = contentFlags; fileDataIds[i] = idx + reader.ReadInt32(); rootEntries[i].FileDataId = (uint)fileDataIds[i]; idx = fileDataIds[i] + 1; } if (!newRoot) { for (var i = 0; i < count; ++i) { rootEntries[i].MD5 = reader.Read <MD5Hash>(); rootEntries[i].Lookup = reader.ReadUInt64(); rootfile.Lookup.Add(rootEntries[i].Lookup, rootEntries[i]); rootfile.FileDataIds.Add(rootEntries[i].FileDataId, rootEntries[i]); } } else { for (var i = 0; i < count; ++i) { rootEntries[i].MD5 = reader.Read <MD5Hash>(); } for (var i = 0; i < count; ++i) { if (contentFlags.HasFlag(ContentFlags.NoNameHash)) { rootEntries[i].Lookup = 0; unnamedCount++; } else { rootEntries[i].Lookup = reader.ReadUInt64(); namedCount++; rootfile.Lookup.Add(rootEntries[i].Lookup, rootEntries[i]); } rootfile.FileDataIds.Add(rootEntries[i].FileDataId, rootEntries[i]); } } } } return(rootfile); }
/// <summary> /// Read the Encoding file and return a parsed <see cref="EncodingFile"/> /// </summary> /// <param name="contentStream"></param> /// <returns></returns> public static EncodingFile ParseEncoding(MemoryStream contentStream) { var encodingFile = new EncodingFile(); using (var stream = new MemoryStream(BLTE.Parse(contentStream.ToArray()))) using (var reader = new BinaryReader(stream)) { if (System.Text.Encoding.UTF8.GetString(reader.ReadBytes(2)) != "EN") { throw new Exception("Encoding file might be corrupted!"); } encodingFile.version = reader.ReadByte(); encodingFile.cKeyLength = reader.ReadByte(); encodingFile.eKeyLength = reader.ReadByte(); encodingFile.cKeyPageSize = reader.ReadUInt16(true); encodingFile.eKeyPageSize = reader.ReadUInt16(true); encodingFile.cKeyPageCount = reader.ReadUInt32(true); encodingFile.eKeyPageCount = reader.ReadUInt32(true); encodingFile.stringBlockSize = reader.ReadUInt40(true); reader.BaseStream.Position += (long)encodingFile.stringBlockSize; reader.BaseStream.Position += encodingFile.cKeyPageCount * 32; var tableAStart = reader.BaseStream.Position; var entries = new Dictionary <MD5Hash, EncodingFileEntry>(new MD5HashComparer()); for (var i = 0; i < encodingFile.cKeyPageCount; ++i) { byte keysCount; while ((keysCount = reader.ReadByte()) != 0) { var entry = new EncodingFileEntry { size = reader.ReadInt40BE() }; var cKey = reader.Read <MD5Hash>(); // @TODO add support for multiple encoding keys for (int key = 0; key < keysCount; key++) { if (key == 0) { entry.eKey = reader.Read <MD5Hash>(); } else { reader.ReadBytes(16); } } entries.Add(cKey, entry); if (!EncodingDictionary.ContainsKey(cKey)) { EncodingDictionary.Add(cKey, entry.eKey); } } var remaining = 4096 - ((reader.BaseStream.Position - tableAStart) % 4096); if (remaining > 0) { reader.BaseStream.Position += remaining; } } encodingFile.aEntries = entries; } return(encodingFile); }