/// <summary> /// Generates a Bit map for all columns as the Blizzard one combines array columns /// </summary> /// <returns></returns> public FieldStructureEntry[] GetBits() { FieldStructureEntry[] bits = new FieldStructureEntry[TableStructure.Length]; if (!Header.IsTypeOf <WDB5>()) { return(bits); } int c = 0, f = 0; for (int i = 0; i < TableStructure.Length; i++) { var dbField = TableStructure[i].GetCustomAttribute <DBFieldAttribute>(); if (dbField != null && dbField.Bits > -1) { bits[c] = new FieldStructureEntry(dbField.Bits, 0, (byte)Convert.ChangeType((dbField.DefaultValue ?? 0xFF), TypeCode.Byte)); } else { FieldStructureEntry field = Header.FieldStructure[f]; bits[c] = new FieldStructureEntry(field?.Bits ?? 0, 0, field?.CommonDataType ?? 0xFF); f++; } c++; } return(bits); }
private void Parse(MemoryStream stream, string file) { ParseWDC1(stream, file); return; #pragma warning disable CS0162 // Unreachable code detected stream.Position = 0; using (var dbReader = new BinaryReader(stream, Encoding.UTF8)) { DBHeader header = ExtractHeader(dbReader); if (header == null) { return; } long pos = dbReader.BaseStream.Position; int CopyTableSize = header.CopyTableSize; //Only WDB5 has a copy table uint CommonDataTableSize = header.CommonDataTableSize; //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) { dbReader.Scrub(stringTableStart); StringTable = new StringTable().Read(dbReader, stringTableStart, stringTableStart + header.StringBlockSize); dbReader.Scrub(pos); } Dictionary <int, FieldType> FieldTypes = new Dictionary <int, FieldType>() { { 4, FieldType.UNKNOWN }, { 3, FieldType.INT }, { 2, FieldType.USHORT }, { 1, FieldType.BYTE }, }; //Read data List <byte[]> copytabledata = new List <byte[]>(); if (header.IsTypeOf <WDB6>()) { copytabledata = (header as WDB6).ReadOffsetData(dbReader, pos).Values.ToList(); } else { copytabledata = (header as WDB5).ReadOffsetData(dbReader, pos).Values.ToList(); } //String table bool stringtableused = StringTable.Values.Any(x => !string.IsNullOrWhiteSpace(x)) && !header.HasOffsetTable; //Calculate known field types List <FieldInfo> fields = new List <FieldInfo>(); for (int i = 0; i < header.FieldStructure.Count; i++) { int bytecount = header.FieldStructure[i].ByteCount; FieldInfo fi = new FieldInfo(); fi.ArraySize = GetArraySize(ref header, i); if (i == header.IdIndex) { fi.Type = FieldType.INT; } else { fi.Type = FieldTypes[bytecount]; } //WDB6 Common Data check if (header.FieldStructure[i].CommonDataColumn) { switch (header.FieldStructure[i].CommonDataType) { case 0: fi.Type = FieldType.STRING; break; case 1: fi.Type = FieldType.USHORT; break; case 2: fi.Type = FieldType.BYTE; break; case 3: fi.Type = FieldType.FLOAT; break; case 4: fi.Type = FieldType.INT; break; } } fields.Add(fi); } //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 = header.FieldStructure[i].Offset; if (header.HasOffsetTable) { start = 0; for (int x = 0; x < i; x++) { if (fields[x].Type != FieldType.STRING) { int bytecount = header.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 (!header.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 (header.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) && 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 (!header.HasOffsetTable && options.Contains(FieldType.STRING)) //Int if only 1 Int else String { fields[i].Type = (uniqueint.Count == 1 ? FieldType.INT : FieldType.STRING); } else if (header.HasOffsetTable && options.Contains(FieldType.STRING) && uniquestr.Count > 1) //More than 1 string { fields[i].Type = FieldType.STRING; } else if (header.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{header.FieldStructure.Max(x => x.Offset).ToString().Length}"; //X2, X3 etc for (int i = 0; i < fields.Count; i++) { Field field = new Field(); field.Name = (i == header.IdIndex ? "ID" : $"field{header.FieldStructure[i].Offset.ToString(format)}"); field.IsIndex = (i == header.IdIndex); field.ArraySize = (field.IsIndex ? 1 : fields[i].ArraySize); field.Type = fields[i].Type.ToString().ToLower(); table.Fields.Add(field); Console.WriteLine($"Name: {field.Name} | Array: {field.ArraySize} | Type: {field.Type}"); } tables.Add(table); Database.ForceGC(); } #pragma warning restore CS0162 // Unreachable code detected }