예제 #1
0
파일: DBReader.cs 프로젝트: DeKaDeNcE/Tools
        bool Load(Stream stream)
        {
            using (var reader = new BinaryReader(stream, Encoding.UTF8))
            {
                Header           = new WDCHeader();
                Header.Signature = reader.ReadUInt32();
                if (Header.Signature != WDC3FmtSig)
                {
                    return(false);
                }

                Header.RecordCount         = reader.ReadUInt32();
                Header.FieldCount          = reader.ReadUInt32();
                Header.RecordSize          = reader.ReadUInt32();
                Header.StringTableSize     = reader.ReadUInt32();
                Header.TableHash           = reader.ReadUInt32();
                Header.LayoutHash          = reader.ReadUInt32();
                Header.MinId               = reader.ReadInt32();
                Header.MaxId               = reader.ReadInt32();
                Header.Locale              = reader.ReadInt32();
                Header.Flags               = (HeaderFlags)reader.ReadUInt16();
                Header.IdIndex             = reader.ReadUInt16();
                Header.TotalFieldCount     = reader.ReadUInt32();
                Header.BitpackedDataOffset = reader.ReadUInt32();
                Header.LookupColumnCount   = reader.ReadUInt32();
                Header.ColumnMetaSize      = reader.ReadUInt32();
                Header.CommonDataSize      = reader.ReadUInt32();
                Header.PalletDataSize      = reader.ReadUInt32();
                Header.SectionsCount       = reader.ReadUInt32();

                var sections = reader.ReadArray <SectionHeader>(Header.SectionsCount);

                // field meta data
                FieldMeta = reader.ReadArray <FieldMetaData>(Header.FieldCount);

                // column meta data
                ColumnMeta = reader.ReadArray <ColumnMetaData>(Header.FieldCount);

                // pallet data
                PalletData = new Value32[ColumnMeta.Length][];
                for (int i = 0; i < ColumnMeta.Length; i++)
                {
                    if (ColumnMeta[i].CompressionType == DB2ColumnCompression.Pallet || ColumnMeta[i].CompressionType == DB2ColumnCompression.PalletArray)
                    {
                        PalletData[i] = reader.ReadArray <Value32>(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 == DB2ColumnCompression.Common)
                    {
                        Dictionary <int, Value32> commonValues = new();
                        CommonData[i] = commonValues;

                        for (int j = 0; j < ColumnMeta[i].AdditionalDataSize / 8; j++)
                        {
                            commonValues[reader.ReadInt32()] = reader.Read <Value32>();
                        }
                    }
                }

                long previousStringTableSize = 0;
                long previousRecordCount     = 0;
                for (int sectionIndex = 0; sectionIndex < Header.SectionsCount; sectionIndex++)
                {
                    if (sections[sectionIndex].TactKeyLookup != 0)// && !hasTactKeyFunc(sections[sectionIndex].TactKeyLookup))
                    {
                        previousStringTableSize += sections[sectionIndex].StringTableSize;
                        previousRecordCount     += sections[sectionIndex].NumRecords;
                        //Console.WriteLine("Detected db2 with encrypted section! HasKey {0}", CASC.HasKey(Sections[sectionIndex].TactKeyLookup));
                        continue;
                    }

                    reader.BaseStream.Position = sections[sectionIndex].FileOffset;

                    byte[] recordsData;
                    Dictionary <long, string> stringsTable  = null;
                    SparseEntry[]             sparseEntries = null;

                    if (!Header.HasOffsetTable())
                    {
                        // records data
                        recordsData = reader.ReadBytes((int)(sections[sectionIndex].NumRecords * Header.RecordSize));

                        // string data
                        stringsTable = new Dictionary <long, string>();

                        for (int i = 0; i < sections[sectionIndex].StringTableSize;)
                        {
                            long oldPos = reader.BaseStream.Position;

                            stringsTable[i + previousStringTableSize] = 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");
                        }
                    }

                    Array.Resize(ref recordsData, recordsData.Length + 8); // pad with extra zeros so we don't crash when reading

                    // index data
                    int[] indexData    = reader.ReadArray <int>((uint)(sections[sectionIndex].IndexDataSize / 4));
                    bool  isIndexEmpty = Header.HasIndexTable() && indexData.Count(i => i == 0) == sections[sectionIndex].NumRecords;

                    // duplicate rows data
                    Dictionary <int, int> copyData = new();

                    for (int i = 0; i < sections[sectionIndex].NumCopyRecords; i++)
                    {
                        copyData[reader.ReadInt32()] = reader.ReadInt32();
                    }

                    if (sections[sectionIndex].NumSparseRecords > 0)
                    {
                        sparseEntries = reader.ReadArray <SparseEntry>((uint)sections[sectionIndex].NumSparseRecords);
                    }

                    // reference data
                    ReferenceData refData = null;

                    if (sections[sectionIndex].ParentLookupDataSize > 0)
                    {
                        refData = new ReferenceData
                        {
                            NumRecords = reader.ReadInt32(),
                            MinId      = reader.ReadInt32(),
                            MaxId      = reader.ReadInt32()
                        };

                        refData.Entries = new Dictionary <int, int>();
                        ReferenceEntry[] entries = reader.ReadArray <ReferenceEntry>((uint)refData.NumRecords);
                        foreach (var entry in entries)
                        {
                            if (!refData.Entries.ContainsKey(entry.Index))
                            {
                                refData.Entries[entry.Index] = entry.Id;
                            }
                        }
                    }
                    else
                    {
                        refData = new ReferenceData
                        {
                            Entries = new Dictionary <int, int>()
                        };
                    }

                    if (sections[sectionIndex].NumSparseRecords > 0)
                    {
                        // TODO: use this shit
                        int[] sparseIndexData = reader.ReadArray <int>((uint)sections[sectionIndex].NumSparseRecords);

                        if (Header.HasIndexTable() && indexData.Length != sparseIndexData.Length)
                        {
                            throw new Exception("indexData.Length != sparseIndexData.Length");
                        }

                        //indexData = sparseIndexData;
                    }

                    BitReader bitReader = new(recordsData);

                    for (int i = 0; i < sections[sectionIndex].NumRecords; ++i)
                    {
                        bitReader.Position = 0;
                        if (Header.HasOffsetTable())
                        {
                            bitReader.Offset = sparseEntries[i].Offset - sections[sectionIndex].FileOffset;
                        }
                        else
                        {
                            bitReader.Offset = i * (int)Header.RecordSize;
                        }

                        bool hasRef = refData.Entries.TryGetValue(i, out int refId);

                        long recordIndex  = i + previousRecordCount;
                        long recordOffset = (recordIndex * Header.RecordSize) - (Header.RecordCount * Header.RecordSize);

                        var rec = new WDC3Row(this, bitReader, (int)recordOffset, Header.HasIndexTable() ? (isIndexEmpty ? i : indexData[i]) : -1, hasRef ? refId : -1, stringsTable);
                        _records.Add(rec.Id, rec);
                    }

                    foreach (var copyRow in copyData)
                    {
                        var rec = _records[copyRow.Value].Clone();
                        rec.Id = copyRow.Key;
                        _records.Add(copyRow.Key, rec);
                    }

                    previousStringTableSize += sections[sectionIndex].StringTableSize;
                    previousRecordCount     += sections[sectionIndex].NumRecords;
                }
            }

            return(true);
        }