Example #1
0
        public DBEntry Read(MemoryStream stream, string dbFile)
        {
            FileName        = dbFile;
            stream.Position = 0;

            using (var dbReader = new BinaryReader(stream, Encoding.UTF8))
            {
                DBHeader header = ExtractHeader(dbReader);
                long     pos    = dbReader.BaseStream.Position;

                //No header - must be invalid
                if (!(header?.IsValidFile ?? false))
                {
                    throw new Exception("Unknown file type.");
                }

                if (header.RecordCount == 0 || header.RecordSize == 0)
                {
                    throw new Exception("File contains no records.");
                }

                DBEntry entry = new DBEntry(header, dbFile);
                if (entry.TableStructure == null)
                {
                    throw new Exception("Definition missing.");
                }

                if (header.IsTypeOf <WDBC>() || header.IsTypeOf <WDB2>())
                {
                    long stringTableStart = dbReader.BaseStream.Position += header.RecordCount * header.RecordSize;
                    Dictionary <int, string> StringTable = new StringTable().Read(dbReader, stringTableStart); //Get stringtable
                    dbReader.Scrub(pos);

                    ReadIntoTable(ref entry, dbReader, StringTable); //Read data

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <WDB5>() || header.IsTypeOf <WCH5>())
                {
                    WDB5 wdb5 = (header as WDB5);
                    WCH5 wch5 = (header as WCH5);
                    WCH7 wch7 = (header as WCH7);

                    int CopyTableSize = wdb5?.CopyTableSize ?? 0; //Only WDB5 has a copy table

                    //StringTable - only if applicable
                    long copyTablePos     = dbReader.BaseStream.Length - CopyTableSize;
                    long indexTablePos    = copyTablePos - (header.HasIndexTable ? header.RecordCount * 4 : 0);
                    long wch7TablePos     = indexTablePos - (wch7?.UnknownWCH7 * 4 ?? 0);
                    long stringTableStart = wch7TablePos - header.StringBlockSize;
                    Dictionary <int, string> StringTable = new Dictionary <int, string>();
                    if (!header.HasOffsetTable) //Stringtable is only present if there isn't an offset map
                    {
                        dbReader.Scrub(stringTableStart);
                        StringTable = new StringTable().Read(dbReader, stringTableStart, stringTableStart + header.StringBlockSize);
                        dbReader.Scrub(pos);
                    }

                    //Read the data
                    using (MemoryStream ms = new MemoryStream(header.ReadData(dbReader, pos)))
                        using (BinaryReader dataReader = new BinaryReader(ms, Encoding.UTF8))
                        {
                            ReadIntoTable(ref entry, dataReader, StringTable);
                        }

                    //Cleanup
                    if (header.IsTypeOf <WDB5>())
                    {
                        wdb5.OffsetLengths = null;
                    }
                    else if (header.IsTypeOf <WCH5>())
                    {
                        wch5.OffsetLengths = null;
                    }
                    else if (header.IsTypeOf <WCH7>())
                    {
                        wch7.OffsetLengths = null;
                    }

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <WDB>())
                {
                    WDB wdb = (WDB)header;
                    wdb.ReadExtendedHeader(dbReader, entry.Build);

                    using (MemoryStream ms = new MemoryStream(wdb.ReadData(dbReader)))
                        using (BinaryReader dataReader = new BinaryReader(ms, Encoding.UTF8))
                        {
                            ReadIntoTable(ref entry, dataReader, new Dictionary <int, string>());
                        }

                    stream.Dispose();
                    return(entry);
                }
                else
                {
                    stream.Dispose();
                    throw new Exception($"Invalid filetype.");
                }
            }
        }
Example #2
0
        public void Write(DBEntry entry, string savepath)
        {
            using (var fs = new FileStream(savepath, FileMode.Create))
                using (var ms = new MemoryStream())
                    using (var bw = new BinaryWriter(ms))
                    {
                        StringTable st = new StringTable(entry.Header.ExtendedStringTable); //Preloads null byte(s)
                        entry.Header.WriteHeader(bw, entry);

                        if (entry.Header.IsTypeOf <WDB5>() && !entry.Header.HasOffsetTable)
                        {
                            ReadIntoFile(entry, bw, entry.GetUniqueRows().ToArray(), ref st); //Insert unique rows
                        }
                        else
                        {
                            ReadIntoFile(entry, bw, entry.Data.AsEnumerable(), ref st); //Insert all rows
                        }
                        //Copy StringTable and StringTable size if it doesn't have inline strings
                        if (st.Size > 0 && !entry.Header.HasOffsetTable)
                        {
                            long pos = bw.BaseStream.Position;
                            bw.Scrub(entry.Header.StringTableOffset);
                            bw.Write(st.Size);
                            bw.Scrub(pos);
                            st.CopyTo(bw.BaseStream);
                        }
                        st.Dispose();

                        //Legion+ only
                        if (entry.Header.IsLegionFile)
                        {
                            //WCH7 Map
                            if (entry.Header.IsTypeOf <WCH7>())
                            {
                                bw.WriteArray(((WCH7)entry.Header).WCH7Table);
                            }

                            //OffsetMap
                            if (entry.Header.HasOffsetTable)
                            {
                                //Update StringTableOffset with current position
                                long pos = bw.BaseStream.Position;
                                bw.Scrub(entry.Header.StringTableOffset);
                                bw.Write((int)pos);
                                bw.Scrub(pos);

                                entry.Header.WriteOffsetMap(bw, entry, OffsetMap);

                                OffsetMap.Clear(); //Cleanup
                            }

                            //Index Table
                            if (entry.Header.HasIndexTable)
                            {
                                entry.Header.WriteIndexTable(bw, entry);
                            }

                            //CopyTable - WDB5 only
                            if (entry.Header.IsTypeOf <WDB5>())
                            {
                                ((WDB5)entry.Header).WriteCopyTable(bw, entry);
                            }
                        }

                        //Copy data to file
                        ms.Position = 0;
                        ms.CopyTo(fs);
                    }
        }
Example #3
0
        private void ReadIntoFile(DBEntry entry, BinaryWriter bw, IEnumerable <DataRow> rows, ref StringTable st)
        {
            TypeCode[] columnTypes = entry.Data.Columns.Cast <DataColumn>().Select(x => Type.GetTypeCode(x.DataType)).ToArray();
            int[]      padding     = entry.TableStructure.Padding.ToArray();
            var        bits        = entry.GetBits();

            bool duplicates = false;

            if (entry.Header.IsTypeOf <WDB2>() && ((WDB2)entry.Header).MaxId != 0) //WDB2 with MaxId > 0 allows duplicates
            {
                duplicates = true;
            }
            else if (entry.Header.IsTypeOf <WCH7>() && ((WCH7)entry.Header).UnknownWCH7 != 0) //WCH7 with Unknown > 0 allows duplicates
            {
                duplicates = true;
            }

            var lastrow = entry.Data.Rows[entry.Data.Rows.Count - 1];

            foreach (DataRow row in rows)
            {
                long offset = bw.BaseStream.Position;

                for (int j = 0; j < entry.Data.Columns.Count; j++)
                {
                    if (entry.Data.Columns[j].ExtendedProperties.ContainsKey(AUTO_GENERATED)) //Autogenerated so skip
                    {
                        continue;
                    }

                    if (entry.Header.HasIndexTable && j == 0) //Inline Id so skip
                    {
                        continue;
                    }

                    if (entry.Header.IsTypeOf <WCH5>() && entry.Header.HasOffsetTable && j == 0) //Inline Id so skip
                    {
                        continue;
                    }

                    switch (columnTypes[j])
                    {
                    case TypeCode.SByte:
                        bw.Write(row.Field <sbyte>(j));
                        bw.BaseStream.Position += sizeof(sbyte) * padding[j];
                        break;

                    case TypeCode.Byte:
                        bw.Write(row.Field <byte>(j));
                        bw.BaseStream.Position += sizeof(byte) * padding[j];
                        break;

                    case TypeCode.Int16:
                        bw.Write(row.Field <short>(j));
                        bw.BaseStream.Position += sizeof(short) * padding[j];
                        break;

                    case TypeCode.UInt16:
                        bw.Write(row.Field <ushort>(j));
                        bw.BaseStream.Position += sizeof(ushort) * padding[j];
                        break;

                    case TypeCode.Int32:
                        bw.WriteInt32(row.Field <int>(j), bits?[j]);
                        bw.BaseStream.Position += sizeof(int) * padding[j];
                        break;

                    case TypeCode.UInt32:
                        bw.WriteUInt32(row.Field <uint>(j), bits?[j]);
                        bw.BaseStream.Position += sizeof(uint) * padding[j];
                        break;

                    case TypeCode.Int64:
                        bw.WriteInt64(row.Field <long>(j), bits?[j]);
                        bw.BaseStream.Position += sizeof(long) * padding[j];
                        break;

                    case TypeCode.UInt64:
                        bw.WriteUInt64(row.Field <ulong>(j), bits?[j]);
                        bw.BaseStream.Position += sizeof(ulong) * padding[j];
                        break;

                    case TypeCode.Single:
                        bw.Write(row.Field <float>(j));
                        bw.BaseStream.Position += sizeof(float) * padding[j];
                        break;

                    case TypeCode.String:
                        if (entry.Header.HasOffsetTable)
                        {
                            bw.Write(Encoding.UTF8.GetBytes(row.Field <string>(j)));
                            bw.Write((byte)0);
                        }
                        else
                        {
                            bw.Write(st.Write(row.Field <string>(j), duplicates));
                        }
                        break;

                    default:
                        throw new Exception($"Unknown TypeCode {columnTypes[j].ToString()}");
                    }
                }

                //Calculate and write the row's padding
                entry.Header.WriteRecordPadding(bw, entry, offset);

                //Store the offset map
                if (entry.Header.HasOffsetTable)
                {
                    OffsetMap.Add(new Tuple <int, short>((int)offset, (short)(bw.BaseStream.Position - offset)));
                }

                //WDB5 + OffsetMap without SecondIndex for the last row pads to next mod 4
                if (entry.Header.IsTypeOf <WDB5>() && entry.Header.HasOffsetTable && !entry.Header.HasSecondIndex && row == lastrow)
                {
                    long rem = bw.BaseStream.Position % 4;
                    bw.BaseStream.Position += (rem == 0 ? 0 : (4 - rem));
                }
            }
        }
Example #4
0
        public DBEntry Read(MemoryStream stream, string dbFile)
        {
            FileName        = dbFile;
            stream.Position = 0;

            using (var dbReader = new BinaryReader(stream, Encoding.UTF8))
            {
                DBHeader header = ExtractHeader(dbReader);
                long     pos    = dbReader.BaseStream.Position;

                //No header - must be invalid
                if (header == null)
                {
                    throw new Exception("Unknown file type.");
                }

                if (header.CheckRecordSize && header.RecordSize == 0)
                {
                    throw new Exception("File contains no records.");
                }
                if (header.CheckRecordCount && header.RecordCount == 0)
                {
                    throw new Exception("File contains no records.");
                }

                DBEntry entry = new DBEntry(header, dbFile);
                if (header.CheckTableStructure && entry.TableStructure == null)
                {
                    throw new Exception("Definition missing.");
                }

                if (header is WDC1 wdc1)
                {
                    Dictionary <int, string> StringTable = wdc1.ReadStringTable(dbReader);
                    wdc1.LoadDefinitionSizes(entry);

                    //Read the data
                    using (MemoryStream ms = new MemoryStream(header.ReadData(dbReader, pos)))
                        using (BinaryReader dataReader = new BinaryReader(ms, Encoding.UTF8))
                        {
                            wdc1.AddRelationshipColumn(entry);
                            //wdc1.SetColumnMinMaxValues(entry);
                            ReadIntoTable(ref entry, dataReader, StringTable);
                        }

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <WDBC>() || header.IsTypeOf <WDB2>())
                {
                    long stringTableStart = dbReader.BaseStream.Position += header.RecordCount * header.RecordSize;
                    Dictionary <int, string> StringTable = new StringTable().Read(dbReader, stringTableStart);                    //Get stringtable
                    dbReader.Scrub(pos);

                    ReadIntoTable(ref entry, dbReader, StringTable);                     //Read data

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <WDB5>() || header.IsTypeOf <WCH5>() || header.IsTypeOf <WDB6>())
                {
                    int  CopyTableSize       = header.CopyTableSize;              //Only WDB5 has a copy table
                    uint CommonDataTableSize = header.CommonDataTableSize;        //Only WDB6 has a CommonDataTable

                    //StringTable - only if applicable
                    long copyTablePos     = dbReader.BaseStream.Length - CommonDataTableSize - CopyTableSize;
                    long indexTablePos    = copyTablePos - (header.HasIndexTable ? header.RecordCount * 4 : 0);
                    long wch7TablePos     = indexTablePos - (header.UnknownWCH7 * 4);
                    long stringTableStart = wch7TablePos - header.StringBlockSize;
                    Dictionary <int, string> StringTable = new Dictionary <int, string>();
                    if (!header.HasOffsetTable)                     //Stringtable is only present if there isn't an offset map
                    {
                        dbReader.Scrub(stringTableStart);
                        StringTable = new StringTable().Read(dbReader, stringTableStart, stringTableStart + header.StringBlockSize);
                        dbReader.Scrub(pos);
                    }

                    //Read the data
                    using (MemoryStream ms = new MemoryStream(header.ReadData(dbReader, pos)))
                        using (BinaryReader dataReader = new BinaryReader(ms, Encoding.UTF8))
                        {
                            entry.UpdateColumnTypes();
                            ReadIntoTable(ref entry, dataReader, StringTable);
                        }

                    //Cleanup
                    header.OffsetLengths = null;

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <WDB>())
                {
                    WDB wdb = (WDB)header;
                    using (MemoryStream ms = new MemoryStream(wdb.ReadData(dbReader)))
                        using (BinaryReader dataReader = new BinaryReader(ms, Encoding.UTF8))
                        {
                            ReadIntoTable(ref entry, dataReader, new Dictionary <int, string>());
                        }

                    stream.Dispose();
                    return(entry);
                }
                else if (header.IsTypeOf <HTFX>())
                {
                    //Load data when needed later
                    stream.Dispose();
                    return(entry);
                }
                else
                {
                    stream.Dispose();
                    throw new Exception($"Invalid filetype.");
                }
            }
        }