public UtfBuilder(DuplicatableStream stream, Endianness endian = Endianness.BigEndian) { long utfHeaderOffset = stream.Position; uint utfMagic = stream.ReadUInt32(Endianness.LittleEndian); if (utfMagic != 0x46545540) { throw new Exception("wrong UTF magic"); } uint size = stream.ReadUInt32(endian); // size of the whole UTF chunk minus the magic and this value itself // offsets are relative to here long offset = stream.Position; uint rowsLocation = stream.ReadUInt32(endian); uint stringTableLocation = stream.ReadUInt32(endian); uint dataTableLocation = stream.ReadUInt32(endian); uint tableNameLocationInStringTable = stream.ReadUInt32(endian); ushort colcount = stream.ReadUInt16(endian); ushort rowwidth = stream.ReadUInt16(endian); uint rowcount = stream.ReadUInt32(endian); string tableName = stream.ReadUTF8NulltermFromLocationAndReset(offset + stringTableLocation + tableNameLocationInStringTable); // utf_tab calls this the 'schema' List <ColumnData> columns = new List <ColumnData>(colcount); for (int i = 0; i < colcount; ++i) { ColumnData col = new ColumnData(); col.Type = stream.ReadUInt8(); uint stroffset = stream.ReadUInt32(endian); col.Name = stream.ReadUTF8NulltermFromLocationAndReset(offset + stringTableLocation + stroffset); if ((col.Type & utf_tab_sharp.UtfTab.COLUMN_STORAGE_MASK) == utf_tab_sharp.UtfTab.COLUMN_STORAGE_CONSTANT) { switch (col.Type & utf_tab_sharp.UtfTab.COLUMN_TYPE_MASK) { case utf_tab_sharp.UtfTab.COLUMN_TYPE_STRING: uint datastroffset = stream.ReadUInt32(endian); col.Data = stream.ReadUTF8NulltermFromLocationAndReset(offset + stringTableLocation + datastroffset); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_8BYTE: col.Data = stream.ReadUInt64(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_DATA: uint dataOffset = stream.ReadUInt32(endian); uint dataSize = stream.ReadUInt32(endian); col.Data = stream.ReadBytesFromLocationAndReset(offset + dataTableLocation + dataOffset, dataSize); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_FLOAT: col.Data = stream.ReadUInt32(endian).UIntToFloat(); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE: col.Data = stream.ReadUInt32(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE: col.Data = stream.ReadUInt16(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE: col.Data = stream.ReadUInt8(); break; default: throw new Exception("unknown type for constant"); } } columns.Add(col); } List <RowData> rows = new List <RowData>((int)rowcount); for (long i = 0; i < rowcount; ++i) { stream.Position = offset + rowsLocation + (i * rowwidth); RowData row = new RowData(); row.Cells = new List <CellData>(); for (int j = 0; j < colcount; ++j) { CellData cell = new CellData(); byte type = columns[j].Type; switch ((type & utf_tab_sharp.UtfTab.COLUMN_STORAGE_MASK)) { case utf_tab_sharp.UtfTab.COLUMN_STORAGE_PERROW: switch (type & utf_tab_sharp.UtfTab.COLUMN_TYPE_MASK) { case utf_tab_sharp.UtfTab.COLUMN_TYPE_STRING: uint datastroffset = stream.ReadUInt32(endian); cell.Data = stream.ReadUTF8NulltermFromLocationAndReset(offset + stringTableLocation + datastroffset); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_8BYTE: cell.Data = stream.ReadUInt64(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_DATA: uint dataOffset = stream.ReadUInt32(endian); uint dataSize = stream.ReadUInt32(endian); cell.Data = stream.ReadBytesFromLocationAndReset(offset + dataTableLocation + dataOffset, dataSize); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_FLOAT: cell.Data = stream.ReadUInt32(endian).UIntToFloat(); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE: cell.Data = stream.ReadUInt32(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE: cell.Data = stream.ReadUInt16(endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE: cell.Data = stream.ReadUInt8(); break; default: throw new Exception("unknown type for value"); } break; case utf_tab_sharp.UtfTab.COLUMN_STORAGE_CONSTANT: cell.Data = columns[j].Data; break; case utf_tab_sharp.UtfTab.COLUMN_STORAGE_ZERO: switch (type & utf_tab_sharp.UtfTab.COLUMN_TYPE_MASK) { case utf_tab_sharp.UtfTab.COLUMN_TYPE_STRING: cell.Data = ""; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_8BYTE: cell.Data = (ulong)0; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_DATA: cell.Data = new byte[0]; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_FLOAT: cell.Data = 0.0f; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE: cell.Data = (uint)0; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE: cell.Data = (ushort)0; break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE: cell.Data = (byte)0; break; default: throw new Exception("unknown type for value"); } break; default: throw new Exception("unknown storage for value"); } row.Cells.Add(cell); } rows.Add(row); } Name = tableName; Columns = columns; Rows = rows; stream.Position = utfHeaderOffset + ((long)8) + ((long)size); return; }
public void Build(Stream s, Endianness endian = Endianness.BigEndian) { DataChunk stringTable = new DataChunk(); stringTable.Add("<NULL>"); DataChunk dataTable = new DataChunk(); ushort colcount = (ushort)Columns.Count; ushort rowwidth = CalculateRowWidth(); uint rowcount = (uint)Rows.Count; long baseOffset = s.Position; s.WriteUInt32(0x46545540, Endianness.LittleEndian); s.WriteUInt32(0); // size of this chunk, fill in later long offset = s.Position; s.WriteUInt32(0); // rowsLocation s.WriteUInt32(0); // stringTableLocation s.WriteUInt32(0); // dataTableLocation s.WriteUInt32(stringTable.Add(Name), endian); s.WriteUInt16(colcount, endian); s.WriteUInt16(rowwidth, endian); s.WriteUInt32(rowcount, endian); for (int i = 0; i < colcount; ++i) { ColumnData col = Columns[i]; s.WriteUInt8(col.Type); s.WriteUInt32(stringTable.Add(col.Name), endian); if ((col.Type & utf_tab_sharp.UtfTab.COLUMN_STORAGE_MASK) == utf_tab_sharp.UtfTab.COLUMN_STORAGE_CONSTANT) { switch (col.Type & utf_tab_sharp.UtfTab.COLUMN_TYPE_MASK) { case utf_tab_sharp.UtfTab.COLUMN_TYPE_STRING: s.WriteUInt32(stringTable.Add((string)col.Data), endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_8BYTE: s.WriteUInt64((ulong)col.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_DATA: byte[] b = (byte[])col.Data; uint dataOffset = dataTable.Add(b); uint dataSize = (uint)b.LongLength; s.WriteUInt32(dataOffset, endian); s.WriteUInt32(dataSize, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_FLOAT: s.WriteUInt32(BitConverter.ToUInt32(BitConverter.GetBytes(((float)col.Data)), 0), endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE: s.WriteUInt32((uint)col.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE: s.WriteUInt16((ushort)col.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE: s.WriteByte((byte)col.Data); break; default: throw new Exception("unknown type for constant"); } } } uint rowsLocation = (uint)(s.Position - offset); for (long i = 0; i < rowcount; ++i) { var row = Rows[(int)i]; for (int j = 0; j < colcount; ++j) { byte type = Columns[j].Type; var cell = row.Cells[j]; switch ((type & utf_tab_sharp.UtfTab.COLUMN_STORAGE_MASK)) { case utf_tab_sharp.UtfTab.COLUMN_STORAGE_PERROW: switch (type & utf_tab_sharp.UtfTab.COLUMN_TYPE_MASK) { case utf_tab_sharp.UtfTab.COLUMN_TYPE_STRING: s.WriteUInt32(stringTable.Add((string)cell.Data), endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_8BYTE: s.WriteUInt64((ulong)cell.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_DATA: byte[] b = (byte[])cell.Data; uint dataOffset = dataTable.Add(b); uint dataSize = (uint)b.LongLength; s.WriteUInt32(dataOffset, endian); s.WriteUInt32(dataSize, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_FLOAT: s.WriteUInt32(BitConverter.ToUInt32(BitConverter.GetBytes(((float)cell.Data)), 0), endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_4BYTE: s.WriteUInt32((uint)cell.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_2BYTE: s.WriteUInt16((ushort)cell.Data, endian); break; case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE2: case utf_tab_sharp.UtfTab.COLUMN_TYPE_1BYTE: s.WriteByte((byte)cell.Data); break; default: throw new Exception("unknown type for value"); } break; case utf_tab_sharp.UtfTab.COLUMN_STORAGE_CONSTANT: case utf_tab_sharp.UtfTab.COLUMN_STORAGE_ZERO: break; default: throw new Exception("unknown storage for value"); } } } var stringTableStream = stringTable.GetData(); stringTableStream.Position = 0; uint stringTableLocation = (uint)(s.Position - offset); StreamUtils.CopyStream(stringTableStream, s); s.WriteAlign(8, 0, offset); var dataTableStream = dataTable.GetData(); dataTableStream.Position = 0; uint dataTableLocation = (uint)(s.Position - offset); StreamUtils.CopyStream(dataTableStream, s); s.WriteAlign(8, 0, offset); uint size = (uint)(s.Position - offset); long endpos = s.Position; s.Position = baseOffset + 4; s.WriteUInt32(size, endian); s.WriteUInt32(rowsLocation, endian); s.WriteUInt32(stringTableLocation, endian); s.WriteUInt32(dataTableLocation, endian); s.Position = endpos; return; }