internal void WriteDBC(DBCFile <T> dbcFile, string path, string signature) { using var fileStream = File.OpenWrite(path); using var writer = new BinaryWriter(fileStream); // Sign the file with the signature var signatureBytes = Encoding.UTF8.GetBytes(signature); writer.Write(signatureBytes); writer.Write(dbcFile.Records.Count); // Get fields of the DBC type and write to the DBC file var dbcType = dbcFile.GetDBCType(); var fields = dbcType.GetFields(); int fieldCount = DBCUtility.FieldCount(fields, dbcType); writer.Write(fieldCount); writer.Write(fieldCount * 4); writer.Write(0); // Adding an empty string to obtain the correct size if (signature == "WDBC") { AddStringToDictionary(string.Empty); } foreach (var record in dbcFile.Records) { foreach (var field in fields) { switch (Type.GetTypeCode(field.FieldType)) { case TypeCode.Object: { if (field.FieldType == typeof(LocalizedString)) { int position = AddStringToDictionary((LocalizedString)field.GetValue(record)); // Local strings before the local position for (uint i = 0; i < dbcFile.LocalPosition; ++i) { writer.Write(0); } // Write to the Local Position writer.Write(position); // Local strings after the local position for (uint j = dbcFile.LocalPosition + 1; j < LocalizedString.Size - 1; ++j) { writer.Write(0); } // 17th location field writer.Write(dbcFile.LocalFlag); } else { if (field.GetValue(record) is Array array) { int arrayLength = array.Length; switch (Type.GetTypeCode(field.FieldType.GetElementType())) { case TypeCode.Int32: for (var i = 0; i < arrayLength; ++i) { writer.Write((int)array.GetValue(i)); } break; case TypeCode.UInt32: for (var i = 0; i < arrayLength; ++i) { writer.Write((uint)array.GetValue(i)); } break; case TypeCode.Single: for (var i = 0; i < arrayLength; ++i) { writer.Write((float)array.GetValue(i)); } break; default: throw new NotImplementedException(Type.GetTypeCode(field.FieldType.GetElementType()).ToString()); } } } break; } case TypeCode.Byte: { var value = (byte)field.GetValue(record); writer.Write(value); break; } case TypeCode.Int32: { var value = (int)field.GetValue(record); writer.Write(value); break; } case TypeCode.UInt32: { var value = (uint)field.GetValue(record); writer.Write(value); break; } case TypeCode.String: { var str = field.GetValue(record) as string; writer.Write(AddStringToDictionary(str)); break; } case TypeCode.Single: { var value = (float)field.GetValue(record); writer.Write(value); break; } default: throw new NotImplementedException(Type.GetTypeCode(field.FieldType).ToString()); } } } // Write all of the strings to the DBC file foreach (var stringTableBytes in stringTable.Values.Select(str => Encoding.UTF8.GetBytes(str))) { writer.Write(stringTableBytes); writer.Write((byte)0); } // TODO: Allow for dynamic header size writer.BaseStream.Position = 16; if (stringTable.Count > 0) { writer.Write(stringTable.Last().Key + Encoding.UTF8.GetByteCount(stringTable.Last().Value) + 1); } }
internal static void ReadDBC(DBCFile <T> dbcFile, BinaryReader reader) { if (reader is null) { return; } var info = DBCUtility.GetDBCInfo(reader); // Validate the DBC fields var fields = dbcFile.GetDBCType().GetFields(); int fieldCounts = DBCUtility.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) long headerSize = reader.BaseStream.Position; // Extract all strings and construct string table var stringTable = DBCUtility.GetStringTable(reader, info, headerSize); // 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 (string.IsNullOrEmpty(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 record and use that as the key for the DBC record var firstValue = fields[0].GetValue(instance); var key = (uint)Convert.ChangeType(firstValue, typeof(uint)); dbcFile.AddEntry(key, (T)instance); } }