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; }