private DBHeader ExtractHeader(BinaryReader dbReader) { DBHeader header = null; string signature = dbReader.ReadString(4); if (string.IsNullOrWhiteSpace(signature)) { return(null); } switch (signature) { case "WDB5": header = new WDB5(); break; case "WDB6": header = new WDB6(); break; case "WDC1": header = new WDC1(); break; default: return(null); } header?.ReadHeader(ref dbReader, signature); return(header); }
private DBHeader ExtractHeader(BinaryReader dbReader) { DBHeader header = null; string signature = dbReader.ReadString(4); if (string.IsNullOrWhiteSpace(signature)) { return(null); } if (signature[0] != 'W') { signature = signature.Reverse(); } switch (signature) { case "WDBC": header = new WDBC(); break; case "WDB2": case "WCH2": header = new WDB2(); break; case "WDB5": header = new WDB5(); break; case "WCH5": header = new WCH5(FileName); break; case "WCH7": header = new WCH7(FileName); break; case "WCH8": header = new WCH8(FileName); break; case "WMOB": case "WGOB": case "WQST": case "WIDB": case "WNDB": case "WITX": case "WNPC": case "WPTX": case "WRDN": header = new WDB(); break; } header?.ReadHeader(ref dbReader, signature); return(header); }
private int GetArraySize(ref WDB5 wdb5, int index) { int bytecount = wdb5.FieldStructure[index].ByteCount; int count = wdb5.FieldStructure[index].Count; int result = 1; if (index == wdb5.FieldStructure.Length - 1) { result = ((int)wdb5.RecordSize - count) / bytecount; //Get difference to end of the record and divide by bits } else { result = (wdb5.FieldStructure[index + 1].Count - count) / bytecount; //Get difference and divide by bits } return(result < 1 ? 1 : result); }
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."); } } }
private void Parse(MemoryStream stream, string file) { stream.Position = 0; using (var dbReader = new BinaryReader(stream, Encoding.UTF8)) { string signature = dbReader.ReadString(4); if (signature != "WDB5") { return; } WDB5 wdb5 = new WDB5(); wdb5.ReadHeader(dbReader, signature); long pos = dbReader.BaseStream.Position; long copyTablePos = dbReader.BaseStream.Length - wdb5.CopyTableSize; long indexTablePos = copyTablePos - (wdb5.HasIndexTable ? wdb5.RecordCount * 4 : 0); long stringTableStart = indexTablePos - wdb5.StringBlockSize; Dictionary <int, string> StringTable = new Dictionary <int, string>(); if (!wdb5.HasOffsetTable) { dbReader.Scrub(stringTableStart); StringTable = new StringTable().Read(dbReader, stringTableStart, stringTableStart + wdb5.StringBlockSize); dbReader.Scrub(pos); } Dictionary <int, FieldType> FieldTypes = new Dictionary <int, FieldType>() { { 4, FieldType.UNKNOWN }, { 3, FieldType.INT }, { 2, FieldType.USHORT }, { 1, FieldType.BYTE }, }; //Calculate known field types List <FieldInfo> fields = new List <FieldInfo>(); for (int i = 0; i < wdb5.FieldStructure.Length; i++) { int bytecount = wdb5.FieldStructure[i].ByteCount; FieldInfo fi = new FieldInfo(); fi.ArraySize = GetArraySize(ref wdb5, i); if (i == wdb5.IdIndex) { fi.Type = FieldType.INT; } else { fi.Type = FieldTypes[bytecount]; } fields.Add(fi); } var copytabledata = wdb5.ReadOffsetData(dbReader, pos).Values.ToList(); bool stringtableused = StringTable.Values.Any(x => !string.IsNullOrWhiteSpace(x)) && !wdb5.HasOffsetTable; //Attempt to figure out unknown types for (int i = 0; i < fields.Count; i++) { if (fields[i].Type != FieldType.UNKNOWN) { continue; } List <FieldType> options = new List <FieldType>() { FieldType.INT, FieldType.UINT, FieldType.FLOAT, FieldType.STRING }; if (!stringtableused) { options.Remove(FieldType.STRING); //Stringtable not used } List <int> intvals = new List <int>(); List <string> stringvals = new List <string>(); List <float> floatvals = new List <float>(); for (int d = 0; d < copytabledata.Count; d++) { byte[] cdata = copytabledata[d]; int start = wdb5.FieldStructure[i].Count; if (wdb5.HasOffsetTable) { start = 0; for (int x = 0; x < i; x++) { if (fields[x].Type != FieldType.STRING) { int bytecount = wdb5.FieldStructure[x].ByteCount; start += bytecount * fields[x].ArraySize; } else { start += cdata.Skip(start).TakeWhile(b => b != 0).Count() + 1; } } } byte[] data = cdata.Skip(start).Take(4).ToArray(); if (!wdb5.HasOffsetTable && data.All(x => x == 0)) { continue; //Ignore 0 byte columns as they could be anything } //Get int value int intval = BitConverter.ToInt32(data, 0); intvals.Add(intval); //String check if (options.Contains(FieldType.STRING)) { if (wdb5.HasOffsetTable) { //Check for control and nonunicode chars string stringval = Encoding.UTF8.GetString(cdata.Skip(start).TakeWhile(x => x != 0).ToArray()); if (stringval.Length >= 1 && stringval.Any(x => char.IsControl(x) || x == 0xFFFD)) { options.Remove(FieldType.STRING); } else { stringvals.Add(stringval); } } else { //Check it is in the stringtable and more than -1 if (intval < 0 || !StringTable.ContainsKey(intval)) { options.Remove(FieldType.STRING); } } } //Float check if (options.Contains(FieldType.FLOAT)) { //Basic float checks float single = BitConverter.ToSingle(data, 0); if (!float.IsInfinity(single) && !float.IsNaN(single) && (single >= 9.99999997475243E-07 && single <= 100000.0)) { floatvals.Add(single); } else if (single != 0) //Ignore 0s { options.Remove(FieldType.FLOAT); } } //UInt check if (options.Contains(FieldType.UINT)) { if (intval < 0) //If less than 0 must be signed { options.Remove(FieldType.UINT); } } } var uniquestr = new HashSet <string>(stringvals); var uniqueint = new HashSet <int>(intvals); var uniquefloat = new HashSet <float>(floatvals); if (uniqueint.Count == 1 && uniqueint.First() == 0) //All 0s { fields[i].Type = FieldType.INT; } else if (!wdb5.HasOffsetTable && options.Contains(FieldType.STRING)) //Int if only 1 Int else String { fields[i].Type = (uniqueint.Count == 1 ? FieldType.INT : FieldType.STRING); } else if (wdb5.HasOffsetTable && options.Contains(FieldType.STRING) && uniquestr.Count > 1) //More than 1 string { fields[i].Type = FieldType.STRING; } else if (wdb5.HasOffsetTable && options.Contains(FieldType.STRING) && uniquefloat.Count <= 1) //1 or less float and string { fields[i].Type = FieldType.STRING; } else if (options.Contains(FieldType.FLOAT) && floatvals.Count > 0) //Floats count more than 1 { fields[i].Type = FieldType.FLOAT; } else if (options.Contains(FieldType.UINT)) //Uint over Int { fields[i].Type = FieldType.UINT; } else { fields[i].Type = FieldType.INT; } } Table table = new Table(); table.Name = Path.GetFileNameWithoutExtension(file); table.Fields = new List <Field>(); string format = $"X{wdb5.FieldStructure.Max(x => x.Count).ToString().Length}"; //X2, X3 etc for (int i = 0; i < fields.Count; i++) { Field field = new Field(); field.Name = (i == wdb5.IdIndex ? "m_ID" : $"field{wdb5.FieldStructure[i].Count.ToString(format)}"); field.IsIndex = (i == wdb5.IdIndex); field.ArraySize = fields[i].ArraySize; field.Type = fields[i].Type.ToString().ToLower(); table.Fields.Add(field); } tables.Add(table); Database.ForceGC(); } }
private static DBHeader ReadHeader(BinaryReader dbReader, DBHeader counterpart = null) { DBHeader header = null; string signature = dbReader.ReadString(4); if (string.IsNullOrWhiteSpace(signature)) { return(null); } if (signature[0] != 'W') { signature = signature.Reverse(); } switch (signature) { case "WDBC": header = new WDBC(); break; case "WDB2": case "WCH2": header = new WDB2(); break; case "WDB5": header = new WDB5(); break; case "WDB6": header = new WDB6(); break; case "WCH5": header = new WCH5(counterpart); break; case "WCH7": header = new WCH7(counterpart); break; case "WCH8": header = new WCH8(counterpart); break; case "WMOB": case "WGOB": case "WQST": case "WIDB": case "WNDB": case "WITX": case "WNPC": case "WPTX": case "WRDN": header = new WDB(); break; case "HTFX": header = new HTFX(); break; } header?.ReadHeader(ref dbReader, signature); return(header); }