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."); } } }
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); } }
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)); } } }
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."); } } }