static void Main(string[] args) { if (args.Length < 2) { throw new ArgumentException("Need DBCache.bin location and DBef dir location!"); } var actuallyV8 = false; var dumpKeys = false; if (args.Length == 3 && args[2] == "true") { dumpKeys = true; } var hotfixFile = args[0]; if (!File.Exists(hotfixFile)) { throw new FileNotFoundException("File " + hotfixFile + " not found!"); } var definitionDir = args[1]; if (!Directory.Exists(definitionDir)) { throw new FileNotFoundException("DBD definition directory " + definitionDir + " not found!"); } var tableHashes = new Dictionary <uint, string>(); foreach (var file in Directory.GetFiles(definitionDir)) { var dbName = Path.GetFileNameWithoutExtension(file); tableHashes.Add(Utils.Hash(dbName.ToUpper()), dbName); } var xfthMagic = 'X' << 0 | 'F' << 8 | 'T' << 16 | 'H' << 24; uint build; var hotfixes = new List <DBCacheEntry>(); using (var ms = new MemoryStream(File.ReadAllBytes(hotfixFile))) using (var bin = new BinaryReader(ms)) { if (bin.ReadUInt32() != xfthMagic) { throw new Exception("Invalid hotfix file!"); } var version = bin.ReadUInt32(); if (version != 7 && version != 8) { throw new Exception("Unsupported version: " + version); } build = bin.ReadUInt32(); var hash = bin.ReadBytes(32); // --- Temporary code to detect if DBCache is actually V8 or not by checking if next hotfix magic is in the right spot or not if (version == 8) { var prePos = bin.BaseStream.Position; if (bin.ReadUInt32() != xfthMagic) { throw new Exception("Invalid hotfix entry magic!"); } bin.ReadUInt32(); // PushID bin.ReadUInt32(); // UniqueID but also maybe not! bin.ReadUInt32(); // TableHash bin.ReadUInt32(); // RecordID var dataSize = bin.ReadInt32(); // DataSize bin.ReadBytes(dataSize + 4); if (bin.ReadUInt32() != xfthMagic) { actuallyV8 = false; } else { actuallyV8 = true; } bin.BaseStream.Position = prePos; } // -- End of temp code while (bin.BaseStream.Length > bin.BaseStream.Position) { var hotfix = new DBCacheEntry(); hotfix.header = new DBCacheEntryHeader(); hotfix.header.magic = bin.ReadUInt32(); hotfix.header.pushID = bin.ReadInt32(); if (actuallyV8) { hotfix.header.uniqueID = bin.ReadUInt32(); } hotfix.header.tableHash = bin.ReadUInt32(); hotfix.header.recordID = bin.ReadUInt32(); hotfix.header.dataSize = bin.ReadInt32(); hotfix.header.isValid = bin.ReadByte(); hotfix.header.pad0 = bin.ReadByte(); hotfix.header.pad1 = bin.ReadByte(); hotfix.header.pad2 = bin.ReadByte(); //hotfix.header = bin.Read<DBCacheEntryHeader>(); if (hotfix.header.magic != xfthMagic) { throw new Exception("Invalid hotfix entry magic!"); } if (tableHashes.ContainsKey(hotfix.header.tableHash)) { hotfix.tableName = tableHashes[hotfix.header.tableHash]; } else { hotfix.tableName = "UNKNOWN " + hotfix.header.tableHash.ToString("X8"); } hotfix.data = bin.ReadBytes(hotfix.header.dataSize); hotfixes.Add(hotfix); } } var dbdCache = new Dictionary <string, DBDefinition>(); var filteredList = new List <HotfixEntry>(); foreach (var hotfix in hotfixes) { var hotfixDataMD5 = ""; if (hotfix.data.Length > 0) { using (var md5 = System.Security.Cryptography.MD5.Create()) { md5.TransformFinalBlock(hotfix.data, 0, hotfix.data.Length); hotfixDataMD5 = BitConverter.ToString(md5.Hash).Replace("-", string.Empty).ToLower(); } } filteredList.Add(new HotfixEntry { pushID = hotfix.header.pushID, recordID = hotfix.header.recordID, isValid = hotfix.header.isValid, tableName = hotfix.tableName, dataMD5 = hotfixDataMD5 }); if (dumpKeys) { if (!dbdCache.ContainsKey(hotfix.tableName) && File.Exists(Path.Combine(definitionDir, hotfix.tableName + ".dbd"))) { var reader = new DBDReader(); var dbd = reader.Read(Path.Combine(definitionDir, hotfix.tableName + ".dbd")); dbdCache.Add(hotfix.tableName, dbd); } if (dbdCache.ContainsKey(hotfix.tableName) && hotfix.header.isValid == 1) { var dbd = dbdCache[hotfix.tableName]; VersionDefinitions?versionToUse = null; var buildFound = false; foreach (var definition in dbd.versionDefinitions) { foreach (var versionBuild in definition.builds) { if (versionBuild.build == build) { versionToUse = definition; buildFound = true; } } } if (buildFound) { long dataLength = 0; var versionDef = (VersionDefinitions)versionToUse; //Console.WriteLine(hotfix.header.pad0 + " " + hotfix.header.pad1 + " " + hotfix.header.pad2 + " " + hotfix.tableName + " " + hotfix.header.recordID); try { using (var dataBin = new BinaryReader(new MemoryStream(hotfix.data))) { foreach (var field in versionDef.definitions) { if (dataBin.BaseStream.Position == dataBin.BaseStream.Length) { continue; } if (field.isNonInline && field.isID) { continue; } if (field.arrLength > 0) { for (var i = 0; i < field.arrLength; i++) { if (dataBin.BaseStream.Position == dataBin.BaseStream.Length) { continue; } if (field.size == 0) { if (dbd.columnDefinitions[field.name].type == "float") { dataBin.ReadSingle(); dataLength += 4; } else { var prevPos = dataBin.BaseStream.Position; dataBin.ReadCString(); dataLength += dataBin.BaseStream.Position - prevPos; } } else { dataLength += field.size / 8; dataBin.ReadBytes(field.size / 8); } } } else { if (field.size == 0) { if (dbd.columnDefinitions[field.name].type == "float") { dataBin.ReadSingle(); dataLength += 4; } else { var prevPos = dataBin.BaseStream.Position; dataBin.ReadCString(); dataLength += dataBin.BaseStream.Position - prevPos; } } else { dataLength += field.size / 8; dataBin.ReadBytes(field.size / 8); } } } if (dataBin.BaseStream.Length != dataBin.BaseStream.Position) { var tableHash = dataBin.ReadUInt32(); if (tableHash == 0) { continue; } if (!tableHashes.ContainsKey(tableHash)) { Console.WriteLine("Encountered an extra " + tableHash.ToString("X8") + " (unk table) record of " + (dataBin.BaseStream.Length - dataBin.BaseStream.Position) + " bytes in " + hotfix.tableName + " ID " + hotfix.header.recordID); } else { Console.WriteLine("Encountered an extra " + tableHashes[tableHash] + " record of " + (dataBin.BaseStream.Length - dataBin.BaseStream.Position) + " bytes in " + hotfix.tableName + " ID " + hotfix.header.recordID); if (tableHashes[tableHash] == "TactKey") { var lookup = dataBin.ReadUInt64(); var keyBytes = dataBin.ReadBytes(16); Console.WriteLine(lookup.ToString("X8").PadLeft(16, '0') + " " + BitConverter.ToString(keyBytes).Replace("-", "")); } } } } } catch (Exception e) { Console.WriteLine("Encountered exception while reading record data:" + e.Message); } } } } } var cache = new DBCache(); cache.build = build; cache.entries = filteredList.ToArray(); if (!dumpKeys) { Console.WriteLine(JsonConvert.SerializeObject(cache, Formatting.None)); } }
private static List <DBCacheEntry> ReadHotfixFile(string hotfixFile, string definitionDir) { var tableHashes = new Dictionary <uint, string>(); foreach (var file in Directory.GetFiles(definitionDir)) { var dbName = Path.GetFileNameWithoutExtension(file); tableHashes.Add(Utils.Hash(dbName.ToUpper()), dbName); } var xfthMagic = 'X' << 0 | 'F' << 8 | 'T' << 16 | 'H' << 24; uint build; var hotfixes = new List <DBCacheEntry>(); using (var ms = new MemoryStream(File.ReadAllBytes(hotfixFile))) using (var bin = new BinaryReader(ms)) { if (bin.ReadUInt32() != xfthMagic) { throw new Exception("Invalid hotfix file!"); } var version = bin.ReadUInt32(); if (version != 8) { throw new Exception("Unsupported version: " + version); } build = bin.ReadUInt32(); var hash = bin.ReadBytes(32); while (bin.BaseStream.Length > bin.BaseStream.Position) { if (bin.ReadUInt32() != xfthMagic) { throw new Exception("Invalid hotfix entry magic!"); } var hotfix = new DBCacheEntry(); hotfix.header = new DBCacheEntryHeader(); hotfix.header.build = build; hotfix.header.pushID = bin.ReadInt32(); hotfix.header.uniqueID = bin.ReadUInt32(); hotfix.header.tableHash = bin.ReadUInt32(); hotfix.header.recordID = bin.ReadUInt32(); hotfix.header.dataSize = bin.ReadInt32(); hotfix.header.isValid = bin.ReadByte(); hotfix.header.pad0 = bin.ReadByte(); hotfix.header.pad1 = bin.ReadByte(); hotfix.header.pad2 = bin.ReadByte(); if (tableHashes.ContainsKey(hotfix.header.tableHash)) { hotfix.tableName = tableHashes[hotfix.header.tableHash]; } else { hotfix.tableName = "UNKNOWN " + hotfix.header.tableHash.ToString("X8"); } hotfix.data = bin.ReadBytes(hotfix.header.dataSize); hotfixes.Add(hotfix); } } return(hotfixes.OrderBy(x => x.header.pushID).ToList()); }