public WDC3Reader(Stream stream) { using (var reader = new BinaryReader(stream, Encoding.UTF8)) { if (reader.BaseStream.Length < HeaderSize) { throw new InvalidDataException("WDC3 file is corrupted!"); } uint magic = reader.ReadUInt32(); if (magic != WDC3FmtSig) { throw new InvalidDataException("WDC3 file is corrupted!"); } RecordsCount = reader.ReadInt32(); FieldsCount = reader.ReadInt32(); RecordSize = reader.ReadInt32(); StringTableSize = reader.ReadInt32(); TableHash = reader.ReadUInt32(); LayoutHash = reader.ReadUInt32(); MinIndex = reader.ReadInt32(); MaxIndex = reader.ReadInt32(); int locale = reader.ReadInt32(); Flags = (DB2Flags)reader.ReadUInt16(); IdFieldIndex = reader.ReadUInt16(); int totalFieldsCount = reader.ReadInt32(); PackedDataOffset = reader.ReadInt32(); // Offset within the field where packed data starts int lookupColumnCount = reader.ReadInt32(); // count of lookup columns int columnMetaDataSize = reader.ReadInt32(); // 24 * NumFields bytes, describes column bit packing, {ushort recordOffset, ushort size, uint additionalDataSize, uint compressionType, uint packedDataOffset or commonvalue, uint cellSize, uint cardinality}[NumFields], sizeof(DBC2CommonValue) == 8 int commonDataSize = reader.ReadInt32(); int palletDataSize = reader.ReadInt32(); // in bytes, sizeof(DBC2PalletValue) == 4 int sectionsCount = reader.ReadInt32(); if (sectionsCount == 0 || RecordsCount == 0) { return; } var sections = reader.ReadArray <SectionHeaderWDC3>(sectionsCount).ToList(); m_sections = sections.OfType <IEncryptableDatabaseSection>().ToList(); // field meta data Meta = reader.ReadArray <FieldMetaData>(FieldsCount); // column meta data ColumnMeta = reader.ReadArray <ColumnMetaData>(FieldsCount); // pallet data PalletData = new Value32[ColumnMeta.Length][]; for (int i = 0; i < ColumnMeta.Length; i++) { if (ColumnMeta[i].CompressionType == CompressionType.Pallet || ColumnMeta[i].CompressionType == CompressionType.PalletArray) { PalletData[i] = reader.ReadArray <Value32>((int)ColumnMeta[i].AdditionalDataSize / 4); } } // common data CommonData = new Dictionary <int, Value32> [ColumnMeta.Length]; for (int i = 0; i < ColumnMeta.Length; i++) { if (ColumnMeta[i].CompressionType == CompressionType.Common) { var commonValues = new Dictionary <int, Value32>((int)ColumnMeta[i].AdditionalDataSize / 8); CommonData[i] = commonValues; for (int j = 0; j < ColumnMeta[i].AdditionalDataSize / 8; j++) { commonValues[reader.ReadInt32()] = reader.Read <Value32>(); } } } int previousStringTableSize = 0, previousRecordCount = 0; foreach (var section in sections) { reader.BaseStream.Position = section.FileOffset; if (!Flags.HasFlagExt(DB2Flags.Sparse)) { // records data RecordsData = reader.ReadBytes(section.NumRecords * RecordSize); Array.Resize(ref RecordsData, RecordsData.Length + 8); // pad with extra zeros so we don't crash when reading // string data if (StringTable == null) { StringTable = new Dictionary <long, string>(section.StringTableSize / 0x20); } for (int i = 0; i < section.StringTableSize;) { long oldPos = reader.BaseStream.Position; StringTable[i + previousStringTableSize] = reader.ReadCString(); i += (int)(reader.BaseStream.Position - oldPos); } previousStringTableSize += section.StringTableSize; } else { // sparse data with inlined strings RecordsData = reader.ReadBytes(section.OffsetRecordsEndOffset - section.FileOffset); if (reader.BaseStream.Position != section.OffsetRecordsEndOffset) { throw new Exception("reader.BaseStream.Position != section.OffsetRecordsEndOffset"); } } // skip encrypted sections => has tact key + record data is zero filled if (section.TactKeyLookup != 0 && Array.TrueForAll(RecordsData, x => x == 0)) { previousRecordCount += section.NumRecords; continue; } // index data IndexData = reader.ReadArray <int>(section.IndexDataSize / 4); // fix zero-filled index data if (IndexData.Length > 0 && IndexData.All(x => x == 0)) { IndexData = Enumerable.Range(MinIndex + previousRecordCount, section.NumRecords).ToArray(); } // duplicate rows data if (section.CopyTableCount > 0) { if (CopyData == null) { CopyData = new Dictionary <int, int>(); } for (int i = 0; i < section.CopyTableCount; i++) { CopyData[reader.ReadInt32()] = reader.ReadInt32(); } } if (section.OffsetMapIDCount > 0) { // HACK unittestsparse is malformed and has sparseIndexData first if (TableHash == 145293629) { reader.BaseStream.Position += 4 * section.OffsetMapIDCount; } SparseEntries = reader.ReadArray <SparseEntry>(section.OffsetMapIDCount).ToList(); } // reference data ReferenceData refData = new ReferenceData(); if (section.ParentLookupDataSize > 0) { refData.NumRecords = reader.ReadInt32(); refData.MinId = reader.ReadInt32(); refData.MaxId = reader.ReadInt32(); var entries = reader.ReadArray <ReferenceEntry>(refData.NumRecords); for (int i = 0; i < entries.Length; i++) { refData.Entries[entries[i].Index] = entries[i].Id; } } if (section.OffsetMapIDCount > 0) { int[] sparseIndexData = reader.ReadArray <int>(section.OffsetMapIDCount); if (section.IndexDataSize > 0 && IndexData.Length != sparseIndexData.Length) { throw new Exception("m_indexData.Length != sparseIndexData.Length"); } IndexData = sparseIndexData; } int position = 0; for (int i = 0; i < section.NumRecords; i++) { BitReader bitReader = new BitReader(RecordsData) { Position = 0 }; if (Flags.HasFlagExt(DB2Flags.Sparse)) { bitReader.Position = position; position += SparseEntries[i].Size * 8; } else { bitReader.Offset = i * RecordSize; } refData.Entries.TryGetValue(i, out int refId); IDBRow rec = new WDC3Row(this, bitReader, section.IndexDataSize != 0 ? IndexData[i] : -1, refId, i + previousRecordCount); _Records.Add(_Records.Count, rec); } previousRecordCount += section.NumRecords; } } }
public WDC2Reader(Stream stream) { using (var reader = new BinaryReader(stream, Encoding.UTF8)) { if (reader.BaseStream.Length < HeaderSize) { throw new InvalidDataException("WDC2 file is corrupted!"); } uint magic = reader.ReadUInt32(); if (magic != WDC2FmtSig && magic != CLS1FmtSig) { throw new InvalidDataException("WDC2 file is corrupted!"); } Signature = magic; RecordsCount = reader.ReadInt32(); FieldsCount = reader.ReadInt32(); RecordSize = reader.ReadInt32(); StringTableSize = reader.ReadInt32(); TableHash = reader.ReadUInt32(); LayoutHash = reader.ReadUInt32(); MinIndex = reader.ReadInt32(); MaxIndex = reader.ReadInt32(); int locale = reader.ReadInt32(); Flags = (DB2Flags)reader.ReadUInt16(); IdFieldIndex = reader.ReadUInt16(); int totalFieldsCount = reader.ReadInt32(); PackedDataOffset = reader.ReadInt32(); // Offset within the field where packed data starts int lookupColumnCount = reader.ReadInt32(); // count of lookup columns int columnMetaDataSize = reader.ReadInt32(); // 24 * NumFields bytes, describes column bit packing, {ushort recordOffset, ushort size, uint additionalDataSize, uint compressionType, uint packedDataOffset or commonvalue, uint cellSize, uint cardinality}[NumFields], sizeof(DBC2CommonValue) == 8 int commonDataSize = reader.ReadInt32(); int palletDataSize = reader.ReadInt32(); // in bytes, sizeof(DBC2PalletValue) == 4 int sectionsCount = reader.ReadInt32(); if (sectionsCount > 1) { throw new Exception("WDC2 only supports 1 section"); } if (sectionsCount == 0 || RecordsCount == 0) { return; } var sections = reader.ReadArray <SectionHeader>(sectionsCount).ToList(); m_sections = sections.OfType <IEncryptableDatabaseSection>().ToList(); // field meta data Meta = reader.ReadArray <FieldMetaData>(FieldsCount); // column meta data ColumnMeta = reader.ReadArray <ColumnMetaData>(FieldsCount); // pallet data PalletData = new Value32[ColumnMeta.Length][]; for (int i = 0; i < ColumnMeta.Length; i++) { if (ColumnMeta[i].CompressionType == CompressionType.Pallet || ColumnMeta[i].CompressionType == CompressionType.PalletArray) { PalletData[i] = reader.ReadArray <Value32>((int)ColumnMeta[i].AdditionalDataSize / 4); } } // common data CommonData = new Dictionary <int, Value32> [ColumnMeta.Length]; for (int i = 0; i < ColumnMeta.Length; i++) { if (ColumnMeta[i].CompressionType == CompressionType.Common) { var commonValues = new Dictionary <int, Value32>((int)ColumnMeta[i].AdditionalDataSize / 8); CommonData[i] = commonValues; for (int j = 0; j < ColumnMeta[i].AdditionalDataSize / 8; j++) { commonValues[reader.ReadInt32()] = reader.Read <Value32>(); } } } for (int sectionIndex = 0; sectionIndex < sectionsCount; sectionIndex++) { reader.BaseStream.Position = sections[sectionIndex].FileOffset; if (!Flags.HasFlagExt(DB2Flags.Sparse)) { // records data RecordsData = reader.ReadBytes(sections[sectionIndex].NumRecords * RecordSize); Array.Resize(ref RecordsData, RecordsData.Length + 8); // pad with extra zeros so we don't crash when reading // string data StringTable = new Dictionary <long, string>(sections[sectionIndex].StringTableSize / 0x20); for (int i = 0; i < sections[sectionIndex].StringTableSize;) { long oldPos = reader.BaseStream.Position; StringTable[oldPos] = reader.ReadCString(); i += (int)(reader.BaseStream.Position - oldPos); } } else { // sparse data with inlined strings RecordsData = reader.ReadBytes(sections[sectionIndex].SparseTableOffset - sections[sectionIndex].FileOffset); if (reader.BaseStream.Position != sections[sectionIndex].SparseTableOffset) { throw new Exception("reader.BaseStream.Position != sections[sectionIndex].SparseTableOffset"); } int sparseCount = MaxIndex - MinIndex + 1; SparseEntries = new List <SparseEntry>(sparseCount); CopyData = new Dictionary <int, int>(sparseCount); var sparseIdLookup = new Dictionary <uint, int>(sparseCount); for (int i = 0; i < sparseCount; i++) { SparseEntry sparse = reader.Read <SparseEntry>(); if (sparse.Offset == 0 || sparse.Size == 0) { continue; } if (sparseIdLookup.TryGetValue(sparse.Offset, out int copyId)) { CopyData[MinIndex + i] = copyId; } else { SparseEntries.Add(sparse); sparseIdLookup.Add(sparse.Offset, MinIndex + i); } } } // index data IndexData = reader.ReadArray <int>(sections[sectionIndex].IndexDataSize / 4); // fix zero-filled index data if (IndexData.Length > 0 && IndexData.All(x => x == 0)) { IndexData = Enumerable.Range(MinIndex, MaxIndex - MinIndex + 1).ToArray(); } // duplicate rows data if (CopyData == null) { CopyData = new Dictionary <int, int>(sections[sectionIndex].CopyTableSize / 8); } for (int i = 0; i < sections[sectionIndex].CopyTableSize / 8; i++) { CopyData[reader.ReadInt32()] = reader.ReadInt32(); } // reference data ReferenceData refData = new ReferenceData(); if (sections[sectionIndex].ParentLookupDataSize > 0) { refData.NumRecords = reader.ReadInt32(); refData.MinId = reader.ReadInt32(); refData.MaxId = reader.ReadInt32(); var entries = reader.ReadArray <ReferenceEntry>(refData.NumRecords); for (int i = 0; i < entries.Length; i++) { refData.Entries[entries[i].Index] = entries[i].Id; } } int position = 0; for (int i = 0; i < RecordsCount; i++) { BitReader bitReader = new BitReader(RecordsData) { Position = 0 }; if (Flags.HasFlagExt(DB2Flags.Sparse)) { bitReader.Position = position; position += SparseEntries[i].Size * 8; } else { bitReader.Offset = i * RecordSize; } refData.Entries.TryGetValue(i, out int refId); IDBRow rec = new WDC2Row(this, bitReader, sections[sectionIndex].FileOffset, sections[sectionIndex].IndexDataSize != 0 ? IndexData[i] : -1, refId, i); _Records.Add(i, rec); } } } }