internal static Dictionary <int, string> GetStringTable(BinaryReader reader, DBCInfo info, long headerSize) { if (reader is null) { throw new ArgumentNullException(nameof(reader), "Reader cannot be null."); } // DBC records can contain strings. These strings are not stored in the record but in an additional string block, at the end of the dbc file. // A record contains an offset into that string block. // These stings are zero terminated (read: c strings) and might be zero length. reader.BaseStream.Position = info.DBCRecords * info.RecordSize + headerSize; var stringData = reader.ReadBytes((int)info.StringSize); string fullString = Encoding.UTF8.GetString(stringData); var strings = fullString.Split(new[] { '\0' }); var stringTable = new Dictionary <int, string>(); var currentPosition = 0; foreach (string str in strings) { stringTable.Add(currentPosition, str); currentPosition += Encoding.UTF8.GetByteCount(str) + 1; } return(stringTable); }
internal static DBCInfo GetDBCInfo(BinaryReader reader) { if (reader is null) { throw new ArgumentNullException(nameof(reader), "Reader cannot be null."); } var info = new DBCInfo( dbcRecords: reader.ReadUInt32(), dbcFields: reader.ReadUInt32(), recordSize: reader.ReadUInt32(), stringSize: reader.ReadUInt32() ); return(info); }
public void LoadDBC() { if (isLoaded) { return; } if (!File.Exists(filePath)) { throw new FileNotFoundException(filePath); } using (var reader = new BinaryReader(File.OpenRead(filePath))) { var byteSignature = reader.ReadBytes(signature.Length); string stringSignature = Encoding.UTF8.GetString(byteSignature); if (stringSignature != signature) { throw new InvalidSignatureException(stringSignature); } var info = new DBCInfo( dbcRecords: reader.ReadUInt32(), dbcFields: reader.ReadUInt32(), recordSize: reader.ReadUInt32(), stringSize: reader.ReadUInt32() ); // Read the DBC File var dbcReader = new DBCReader <T>(); dbcReader.ReadDBC(this, reader, info); } // Set IsLoaded to true to avoid loading the same DBC file multiple times isLoaded = true; }
internal void ReadDBC(DBCFile <T> dbcFile, BinaryReader reader, DBCInfo info) { if (reader is null) { return; } // Validate the DBC fields var fields = dbcFile.GetDBCType().GetFields(); int fieldCounts = dbcFile.FieldCount(fields, dbcFile.GetDBCType()); if (info.DBCFields != fieldCounts) { throw new InvalidDBCFields(dbcFile.GetDBCType().ToString()); } // We don't need to read the first bytes again (signature, dbcRecords, dbcFields, recordSize & stringSize) long headerSize = reader.BaseStream.Position; // Set position of reader reader.BaseStream.Position = info.DBCRecords * info.RecordSize + headerSize; var stringData = reader.ReadBytes((int)info.StringSize); string fullString = Encoding.UTF8.GetString(stringData); var strings = fullString.Split(new[] { '\0' }, StringSplitOptions.None); var stringTable = new Dictionary <int, string>(); var currentPosition = 0; foreach (string str in strings) { stringTable.Add(currentPosition, str); currentPosition += Encoding.UTF8.GetByteCount(str) + 1; } // Reset position to base position reader.BaseStream.Position = headerSize; // Loop through all of the records in the DBC file for (uint i = 0; i < info.DBCRecords; ++i) { var instance = Activator.CreateInstance(dbcFile.GetDBCType()); foreach (var field in fields) { switch (Type.GetTypeCode(field.FieldType)) { case TypeCode.Object: { if (field.FieldType == typeof(LocalizedString)) { var value = ""; for (uint j = 0; j < LocalizedString.Size - 1; ++j) { int offsetKey = reader.ReadInt32(); if (value == "" && offsetKey != 0 && stringTable.TryGetValue(offsetKey, out string stringFromTable)) { value = stringFromTable; dbcFile.LocalPosition = j; } } dbcFile.LocalFlag = reader.ReadUInt32(); field.SetValue(instance, (LocalizedString)value); } else if (field.FieldType.IsArray) { Array array; int arrayLength; switch (Type.GetTypeCode(field.FieldType.GetElementType())) { case TypeCode.Int32: // Get length of array arrayLength = ((int[])field.GetValue(instance)).Length; // Set Array array = new int[arrayLength]; // Set Value of DBC object by looping through the array for (var j = 0; j < arrayLength; ++j) { array.SetValue(reader.ReadInt32(), j); } field.SetValue(instance, array); break; case TypeCode.UInt32: // Get length of array arrayLength = ((uint[])field.GetValue(instance)).Length; // Set Array array = new uint[arrayLength]; // Set Value of DBC object by looping through the array for (var j = 0; j < arrayLength; ++j) { array.SetValue(reader.ReadUInt32(), j); } field.SetValue(instance, array); break; case TypeCode.Single: // Get length of array arrayLength = ((float[])field.GetValue(instance)).Length; // Set Array array = new float[arrayLength]; // Set Value of DBC object by looping through the array for (var j = 0; j < arrayLength; ++j) { array.SetValue(reader.ReadSingle(), j); } field.SetValue(instance, array); break; default: throw new NotImplementedException(Type.GetTypeCode(field.FieldType.GetElementType()).ToString()); } } break; } case TypeCode.Byte: { byte value = reader.ReadByte(); field.SetValue(instance, value); break; } case TypeCode.Int32: { int value = reader.ReadInt32(); field.SetValue(instance, value); break; } case TypeCode.UInt32: { uint value = reader.ReadUInt32(); field.SetValue(instance, value); break; } case TypeCode.Single: { float value = reader.ReadSingle(); field.SetValue(instance, value); break; } case TypeCode.String: { // Get offset for string table int offsetKey = reader.ReadInt32(); // Check if offset exists in the string table if (!stringTable.TryGetValue(offsetKey, out string stringFromTable)) { throw new KeyNotFoundException(offsetKey.ToString()); } string value = stringFromTable; field.SetValue(instance, value); break; } default: throw new NotImplementedException(Type.GetTypeCode(field.FieldType).ToString()); } } // Get the first value of the DBC file and use that as key for the DBC record var firstValue = fields[0].GetValue(instance); var key = (uint)Convert.ChangeType(firstValue, typeof(uint)); dbcFile.AddEntry(key, (T)instance); } }