private void WriteEmptyValues(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.EmptyValues.Length == 0) { return; } int[] offsets = new int[table.FieldCount]; for (int i = 0; i < table.FieldCount; i++) { bool[] data = table.EmptyValues[i]; offsets[i] = data == null ? -1 : WriteBits(writer, data, 4, ref offset); } header.EmptyValuesPointer = offset; long startPos = writer.Stream.Position; foreach (int o in offsets) { writer.Write(o); } writer.WritePadding(0x00, 0x08); long endPos = writer.Stream.Position; offset += (int)(endPos - startPos); }
private void WriteRecordMemberInfo(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.RawRecordMemberInfo.Length == 0) { return; } header.RawRecordMemberInfoPointer = WriteTypes(writer, table.RawRecordMemberInfo, 0, ref offset); }
private void WriteFieldTypes(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.FieldTypes.Length == 0) { return; } header.FieldTypePointer = WriteTypes(writer, table.FieldTypes, 8, ref offset); }
private void WriteFieldIds(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.FieldIds.Length == 0) { return; } header.FieldIdPointer = WriteStrings(writer, table.FieldIds, 0, ref offset); }
private void WriteGameVarFieldType(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.GameVarFieldType.Length == 0) { return; } object[] temp = Array.ConvertAll(table.GameVarFieldType, x => (object)x); header.GameVarFieldTypePointer = WriteNumbers(writer, temp, typeof(int), 8, ref offset); }
private void WriteFieldInfo(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.FieldInfo.Length == 0) { return; } object[] temp = Array.ConvertAll(table.FieldInfo, x => (object)x); header.FieldInfoPointer = WriteNumbers(writer, temp, typeof(byte), 8, ref offset); }
private void WriteRecordExistence(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.RecordExistence == null) { return; } if (table.RecordExistence.Length == 0) { return; } header.RecordExistencePointer = WriteBits(writer, table.RecordExistence, 4, ref offset); }
private void WriteValueStrings(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.ValueStrings.Length == 0) { long startPos = writer.Stream.Position; writer.Write(0); writer.WritePadding(0x00, 16); long endPos = writer.Stream.Position; offset += (int)(endPos - startPos); header.ValueStringPointer = offset; return; } header.ValueStringPointer = WriteStrings(writer, table.ValueStrings, 8, ref offset); }
private void WriteRecordOrder(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, ref int offset) { if (table.RecordOrder.Length == 0) { return; } object[] temp = Array.ConvertAll(table.RecordOrder, x => (object)x); long startPos = writer.Stream.Position; header.RecordOrderPointer = WriteNumbers(writer, temp, typeof(int), 0, ref offset); long mod = (writer.Stream.Position - startPos) % 8; if (mod > 0) { writer.WriteTimes(0x00, 8 - mod); offset += (int)(8 - mod); } }
private void WriteValues(DataWriter writer, ArmpTable table, Types.ArmpTableHeader header, long[][] subTablesOffsets, ref int offset) { int[] offsets = new int[table.FieldCount]; for (int i = 0; i < table.FieldCount; i++) { FieldType fieldType = table.RawRecordMemberInfo[i]; if (fieldType == FieldType.Unused) { offsets[i] = 0; continue; } object[] data = table.Values[i]; if (data == null) { offsets[i] = -1; continue; } switch (fieldType) { case FieldType.UInt8: offsets[i] = WriteNumbers(writer, data, typeof(byte), 4, ref offset); break; case FieldType.UInt16: offsets[i] = WriteNumbers(writer, data, typeof(ushort), 4, ref offset); break; case FieldType.UInt32: offsets[i] = WriteNumbers(writer, data, typeof(uint), 8, ref offset); break; case FieldType.UInt64: offsets[i] = WriteNumbers(writer, data, typeof(ulong), 8, ref offset); break; case FieldType.Int8: offsets[i] = WriteNumbers(writer, data, typeof(sbyte), 4, ref offset); break; case FieldType.Int16: offsets[i] = WriteNumbers(writer, data, typeof(short), 4, ref offset); break; case FieldType.Int32: offsets[i] = WriteNumbers(writer, data, typeof(int), 8, ref offset); break; case FieldType.Int64: offsets[i] = WriteNumbers(writer, data, typeof(long), 8, ref offset); break; case FieldType.Float16: throw new FormatException("Float16 not supported."); case FieldType.Float32: offsets[i] = WriteNumbers(writer, data, typeof(float), 8, ref offset); break; case FieldType.Float64: offsets[i] = WriteNumbers(writer, data, typeof(double), 8, ref offset); break; case FieldType.Boolean: // Booleans are read as a bitmask try { bool[] temp = Array.ConvertAll(data, x => (bool)x); offsets[i] = WriteBits(writer, temp, 4, ref offset); } catch (NullReferenceException) { offsets[i] = -1; continue; } break; case FieldType.String: // Strings are stored as Value String index offsets[i] = WriteNumbers(writer, data, typeof(int), 8, ref offset); break; case FieldType.Table: offsets[i] = WriteSubTables(writer, subTablesOffsets[i], 8, ref offset); break; default: throw new ArgumentOutOfRangeException($"Unknown field type: {fieldType}"); } } header.ValuesPointer = offset; long startPos = writer.Stream.Position; foreach (int o in offsets) { writer.Write(o); } long mod = (writer.Stream.Position - startPos) % 8; if (mod > 0) { writer.WriteTimes(0x00, 8 - mod); } long endPos = writer.Stream.Position; offset += (int)(endPos - startPos); }
private Tuple <byte[], int> SerializeTable(ArmpTable table, int baseOffset) { using var currentTable = new System.IO.MemoryStream(); using DataStream ds = DataStreamFactory.FromStream(currentTable); var writer = new DataWriter(ds) { DefaultEncoding = Encoding.UTF8, Endianness = EndiannessMode.LittleEndian, }; var header = new Types.ArmpTableHeader { RecordCount = table.RecordCount, FieldCount = table.FieldCount, ValueStringCount = table.ValueStringCount, RecordInvalid = table.RecordInvalid, RecordIdPointer = table.RecordIds == null ? -1 : 0, RecordExistencePointer = table.RecordExistence == null ? -1 : 0, FieldTypePointer = table.FieldTypes == null ? -1 : 0, ValuesPointer = 0, Id = table.Id, Flags = table.Flags, ValueStringPointer = 0, FieldIdPointer = table.FieldIds == null ? -1 : 0, FieldInvalid = table.FieldInvalid, RecordOrderPointer = table.RecordOrder == null ? -1 : 0, FieldOrderPointer = table.FieldOrder == null ? -1 : 0, FieldExistencePointer = table.FieldExistence == null ? -1 : 0, IndexerPointer = 0, GameVarFieldTypePointer = 0, EmptyValuesPointer = table.EmptyValues == null ? -1 : 0, RawRecordMemberInfoPointer = table.RawRecordMemberInfo == null ? -1 : 0, FieldInfoPointer = table.FieldInfo == null ? -1 : 0, }; int currentOffset = baseOffset; long[][] subTablesOffsets = new long[table.FieldCount][]; // 1. Child tables for (int field = 0; field < table.FieldCount; field++) { subTablesOffsets[field] = new long[table.RecordCount]; if (table.RawRecordMemberInfo[field] != Enums.FieldType.Table) { continue; } object[] data = table.Values[field]; if (data == null) { continue; } for (int record = 0; record < table.RecordCount; record++) { var subTable = (ArmpTable)data[record]; if (subTable == null) { subTablesOffsets[field][record] = 0x00000000; continue; } (byte[] subTableData, int subTableOffset) = SerializeTable(subTable, currentOffset); long startPos = writer.Stream.Position; writer.Write(subTableData); subTablesOffsets[field][record] = subTableOffset; writer.WritePadding(0x00, 0x10); long endPos = writer.Stream.Position; currentOffset += (int)(endPos - startPos); } } // 2. Indexer if (table.Indexer != null) { (byte[] indexerData, int indexerOffset) = SerializeTable(table.Indexer, currentOffset); long startPos = writer.Stream.Position; writer.Write(indexerData); header.IndexerPointer = indexerOffset; writer.WritePadding(0x00, 0x10); long endPos = writer.Stream.Position; currentOffset += (int)(endPos - startPos); } int currentTableOffset = currentOffset; long headerOffset = writer.Stream.Position; writer.WriteTimes(0x00, 0x50); currentOffset += 0x50; // Header size WriteRecordExistence(writer, table, header, ref currentOffset); WriteFieldExistence(writer, table, header, ref currentOffset); WriteRecordIds(writer, table, header, ref currentOffset); WriteFieldIds(writer, table, header, ref currentOffset); WriteValueStrings(writer, table, header, ref currentOffset); WriteFieldTypes(writer, table, header, ref currentOffset); WriteRecordMemberInfo(writer, table, header, ref currentOffset); WriteValues(writer, table, header, subTablesOffsets, ref currentOffset); WriteEmptyValues(writer, table, header, ref currentOffset); WriteRecordOrder(writer, table, header, ref currentOffset); WriteFieldOrder(writer, table, header, ref currentOffset); WriteFieldInfo(writer, table, header, ref currentOffset); WriteGameVarFieldType(writer, table, header, ref currentOffset); _ = writer.Stream.Seek(headerOffset, System.IO.SeekOrigin.Begin); writer.WriteOfType(header); return(new Tuple <byte[], int>(currentTable.ToArray(), currentTableOffset)); }