private void WriteStringTableTo(UtfHeader header, Stream bufferStream) { bufferStream.Seek(header.StringTableOffset, SeekOrigin.Begin); bufferStream.WriteBytes(TableNameBytesCache); var row0 = Rows[0]; foreach (var fieldImage in row0) { bufferStream.WriteBytes(fieldImage.NameBytesCache); } foreach (var fieldImage in row0) { if ((fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2) && fieldImage.Type == ColumnType.String) { bufferStream.WriteBytes(fieldImage.StringValueBytesCache); } } foreach (var row in Rows) { foreach (var fieldImage in row) { if (fieldImage.Type == ColumnType.String && fieldImage.Storage == ColumnStorage.PerRow) { bufferStream.WriteBytes(fieldImage.StringValueBytesCache); } } } }
private void WriteExtraDataTo(UtfHeader header, Stream bufferStream) { var baseOffset = header.ExtraDataOffset; for (var i = 0; i < Rows.Count; ++i) { var row = Rows[i]; foreach (var fieldImage in row) { if (fieldImage.Type == ColumnType.Data) { var shouldWrite = false; switch (fieldImage.Storage) { case ColumnStorage.Constant: case ColumnStorage.Constant2: shouldWrite = i == 0; break; case ColumnStorage.PerRow: shouldWrite = true; break; } if (shouldWrite && fieldImage.DataValue.Length > 0) { bufferStream.SeekAndWriteBytes(fieldImage.DataValue, baseOffset + fieldImage.DataOffset); } } } } }
private uint GetExtraDataSize(UtfHeader header, out UtfFieldImage[] orderedDataFieldImages) { var baseOffset = header.ExtraDataOffset; uint totalSize = 0; var fieldImagesInNewOrder = new List <UtfFieldImage>(); var row0 = Rows[0]; var firstTableDataFieldIndex = -1; foreach (var fieldImage in row0) { if (fieldImage.Type == ColumnType.Data && (fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2)) { AddToNewOrdererdDataFieldList(fieldImagesInNewOrder, fieldImage, ref firstTableDataFieldIndex); } } foreach (var row in Rows) { foreach (var fieldImage in row) { if (fieldImage.Type == ColumnType.Data && fieldImage.Storage == ColumnStorage.PerRow) { AddToNewOrdererdDataFieldList(fieldImagesInNewOrder, fieldImage, ref firstTableDataFieldIndex); } } } // If the first field is a table, rebase the whole data field. if (firstTableDataFieldIndex == 0) { baseOffset = SerializationHelper.RoundUpAsTable(baseOffset, Alignment); header.ExtraDataOffset = baseOffset; } orderedDataFieldImages = fieldImagesInNewOrder.ToArray(); foreach (var fieldImage in orderedDataFieldImages) { var rawOffset = baseOffset; // Tables' starting locations are rounded by Alignment (usually 0x20). if (fieldImage.IsTable && fieldImage.DataValue.Length > 0) { baseOffset = SerializationHelper.RoundUpAsTable(baseOffset, Alignment); } baseOffset += (uint)fieldImage.DataValue.Length; // Tables' ending locations are rounded by 4. if (fieldImage.IsTable && fieldImage.DataValue.Length > 0) { baseOffset = AcbHelper.RoundUpToAlignment(baseOffset, 4); } totalSize += (baseOffset - rawOffset); } return(totalSize); }
private static void WriteHeaderTo(UtfHeader header, Stream bufferStream) { bufferStream.SeekAndWriteBytes(UtfTable.UtfSignature, 0); bufferStream.SeekAndWriteUInt32BE(header.TableSize - 8, 4); bufferStream.SeekAndWriteUInt16BE(header.Unknown1, 8); bufferStream.SeekAndWriteUInt16BE((ushort)(header.PerRowDataOffset - 8), 10); bufferStream.SeekAndWriteUInt32BE(header.StringTableOffset - 8, 12); bufferStream.SeekAndWriteUInt32BE(header.ExtraDataOffset - 8, 16); bufferStream.SeekAndWriteUInt32BE(header.TableNameOffset, 20); bufferStream.SeekAndWriteUInt16BE(header.FieldCount, 24); bufferStream.SeekAndWriteUInt16BE(header.RowSize, 26); bufferStream.SeekAndWriteUInt32BE(header.RowCount, 28); }
private void WritePerRowDataTo(UtfHeader header, Stream bufferStream) { bufferStream.Seek(header.PerRowDataOffset, SeekOrigin.Begin); foreach (var row in Rows) { foreach (var fieldImage in row) { if (fieldImage.Storage == ColumnStorage.PerRow) { fieldImage.WriteValueTo(bufferStream); } } } }
private void WriteFieldDescriptorsTo(UtfHeader header, Stream bufferStream) { bufferStream.Seek(SchemaOffset, SeekOrigin.Begin); var row = Rows[0]; foreach (var fieldImage in row) { bufferStream.WriteByte((byte)((byte)fieldImage.Storage | (byte)fieldImage.Type)); bufferStream.WriteUInt32BE(fieldImage.NameOffset); if (fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2) { fieldImage.WriteValueTo(bufferStream); } } }
/// <summary> /// Some fields are not filled. /// </summary> /// <returns></returns> public UtfHeader GetHeader(out UtfFieldImage[] orderedDataFieldImages) { // Basic information if (Rows.Count < 1) { throw new InvalidOperationException("Rows should not be empty."); } var fieldCount = Rows[0].Count; for (var i = 1; i < Rows.Count; ++i) { if (Rows[i].Count != fieldCount) { throw new InvalidOperationException("Number of fields in each row do not match."); } } var singleRowDataSize = GetSingleRowDataSize(); var rowDescriptorSize = GetRowDescriptorSize(); var header = new UtfHeader { TableName = TableName, RowCount = (uint)Rows.Count, FieldCount = (ushort)fieldCount, Unknown1 = 1, RowSize = singleRowDataSize, PerRowDataOffset = rowDescriptorSize + SchemaOffset // "per row" data offset, actually }; var perRowDataSize = header.RowCount * header.RowSize; header.StringTableOffset = header.PerRowDataOffset + perRowDataSize; header.TableNameOffset = 0; var stringTableSize = GetStringTableSize(); header.ExtraDataOffset = header.StringTableOffset + stringTableSize; var extraDataSize = GetExtraDataSize(header, out orderedDataFieldImages); header.TableSize = header.ExtraDataOffset + extraDataSize; // ??? header.TableSize = AcbHelper.RoundUpToAlignment(header.TableSize, 4); return(header); }
private void SetExtraDataFieldRelocations(UtfHeader header, UtfFieldImage[] orderedFieldImages) { // This value is already calibrated in GetExtraDataSize(). It will move to a mod16 position if the first item is a table. var baseOffset = header.ExtraDataOffset; var currentOffset = baseOffset; foreach (var fieldImage in orderedFieldImages) { if (fieldImage.DataValue.Length > 0) { // For priority, see AddToNewOrderedDataFieldList(). if (fieldImage.IsTable) { currentOffset = SerializationHelper.RoundUpAsTable(currentOffset, Alignment); } fieldImage.DataOffset = currentOffset - baseOffset; currentOffset += (uint)fieldImage.DataValue.Length; // Extra 4-byte alignment at the end of tables. if (fieldImage.IsTable) { currentOffset = AcbHelper.RoundUpToAlignment(currentOffset, Alignment); } } else { fieldImage.DataOffset = 0; } } var row0 = Rows[0]; for (var i = 1; i < Rows.Count; ++i) { for (var j = 0; j < Rows[i].Count; ++j) { var fieldImage = Rows[i][j]; if (fieldImage.Type == ColumnType.Data && (fieldImage.Storage == ColumnStorage.Constant || fieldImage.Storage == ColumnStorage.Constant2)) { fieldImage.DataOffset = row0[j].DataOffset; } } } }
private void SetStringFieldRelocations(UtfHeader header) { // String table var currentStringTableOffset = (uint)TableNameBytesCache.Length; var row0 = Rows[0]; // Field names and static strings foreach (var fieldImage in row0) { fieldImage.NameOffset = currentStringTableOffset; currentStringTableOffset += (uint)fieldImage.NameBytesCache.Length; } foreach (var fieldImage in row0) { switch (fieldImage.Storage) { case ColumnStorage.Constant: case ColumnStorage.Constant2: if (fieldImage.Type == ColumnType.String) { fieldImage.StringOffset = currentStringTableOffset; currentStringTableOffset += (uint)fieldImage.StringValueBytesCache.Length; } break; } } for (var i = 1; i < Rows.Count; ++i) { for (var j = 0; j < Rows[i].Count; ++j) { var fieldImage = Rows[i][j]; fieldImage.NameOffset = row0[j].NameOffset; switch (fieldImage.Storage) { case ColumnStorage.Constant: case ColumnStorage.Constant2: if (fieldImage.Type == ColumnType.String) { fieldImage.StringOffset = row0[j].StringOffset; } break; } } } // Per-row strings foreach (var r in Rows) { foreach (var fieldImage in r) { if (fieldImage.Storage == ColumnStorage.PerRow && fieldImage.Type == ColumnType.String) { fieldImage.StringOffset = currentStringTableOffset; currentStringTableOffset += (uint)fieldImage.StringValueBytesCache.Length; } } } }