Found this on EON but i guess it cant hurt to post it here aswell, delicious copy pasta
original post on eon, DBCFile Reader Credits to testout2
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
namespace Word.GameData.DBC
{
internal class DBC<T> : IEnumerable where T : new()
{
private T[] Entrys;
/*
1 Signature String (4-bytes) string, always 'WDBC'
2 Records Integer (4-bytes) number of records in the file
3 Fields Integer (4-bytes) number of fields per record
4 Record Size Integer (4-bytes) Fields*FieldSize (FieldSize is usually 4, but not always)
5 String Block Size Integer Size of the string block
*/
public uint Signature;
public uint Records;
public uint Fields;
public uint RecordWidth;
public uint StringSize;
protected uint StringOffset;
protected BinaryReader stream;
protected DBC()
: this(null)
{
}
string[] stri;
int[] offsets;
protected DBC(string filename)
{
if (filename == null)
filename = this.GetType().Name;
//filename = "C:\\temp\\DBFilesClient\\DBFilesClient\\BankBagSlotPrices.dbc";
FileStream fs = File.OpenRead(string.Format("DBfilesClient\\{0}.dbc", filename));
//BinaryReader stream = new BinaryReader(fs);
stream = new BinaryReader(fs);//MPQManager.GetFile("DBfilesClient\\" + filename + ".dbc"));
Signature = stream.ReadUInt32();
Records = stream.ReadUInt32();
Fields = stream.ReadUInt32();
RecordWidth = stream.ReadUInt32();
StringSize = stream.ReadUInt32();
StringOffset = Records * RecordWidth + 20;
Entrys = new T[Records];
//ConsoleBar cb = new ConsoleBar("Loading " + filename + ".dbc (" + Records + " records)", (int)Records);
Console.WriteLine("Loading {0}.dbc (Records {1})", filename, Records);
byte[] strings = new byte[StringSize];
stream.BaseStream.Position = StringOffset;
stream.BaseStream.Read(strings, 0, (int)StringSize);
stream.BaseStream.Position = 20;
var enc = new System.Text.UTF8Encoding();
stri = enc.GetString(strings).Split('\0');
offsets = new int[stri.Length];
int oo = 0;
for (int i = 0; i < stri.Length; i++)
{
offsets[i] = oo;
oo += enc.GetByteCount(stri[i] + "\0");
}
for (int i = 0; i < Records; i++)
{
MoveToRecord(i);
Entrys[i] = Read();
//cb.Step();
}
stri = null;
offsets = null;
stream.Close();
//cb.End();
}
protected void MoveToRecord(int i)
{
if (stream.BaseStream.Position != 20 + i * RecordWidth)
{
Console.WriteLine("dbc was " + (stream.BaseStream.Position - (20 + i * RecordWidth)) + " bytes over");
stream.BaseStream.Position = 20 + i * RecordWidth;
}
}
protected string ReadString(int offset)
{
if (offset >= 0 && offset < StringSize)
{
int j = Array.BinarySearch<int>(offsets, offset);
if (j > -1)
return stri[j];
else
return "";
}
else
{
return "";
}
}
protected T Read()
{
T o = new T();
foreach (var v in typeof(T).GetFields())
{
if (v.FieldType == typeof(string))
{
string str = ReadString(stream.ReadInt32());
v.SetValue(o, str);
}
else if (v.FieldType == typeof(LocalizedString))
{
LocalizedString ls;
ls.Strings = new string[16];
for (int i = 0; i < 16; i++)
ls.Strings[i] = ReadString(stream.ReadInt32());
ls.mask = stream.ReadUInt32();
v.SetValue(o, ls);
}
else if (v.FieldType == typeof(float))
{
Single s = stream.ReadSingle();
v.SetValue(o, s);
}
else if (v.FieldType == typeof(int))
{
Int32 i = stream.ReadInt32();
v.SetValue(o, i);
}
else if (v.FieldType == typeof(uint))
{
UInt32 i = stream.ReadUInt32();
v.SetValue(o, i);
}
else if (v.FieldType == typeof(bool))
{
UInt32 i = stream.ReadUInt32();
v.SetValue(o, (i > 0));
}
else if (v.FieldType.BaseType == typeof(Enum))
{
if (Enum.GetUnderlyingType(v.FieldType) == typeof(UInt32))
{
UInt32 i = stream.ReadUInt32();
v.SetValue(o, i);
}
else if (Enum.GetUnderlyingType(v.FieldType) == typeof(int))
{
int i = stream.ReadInt32();
v.SetValue(o, i);
}
else
{
stream.ReadBytes(4);
}
}
else if (v.FieldType.BaseType == typeof(int))
{
int i = stream.ReadInt32();
v.SetValue(o, i);
}
else
{
stream.ReadBytes(4);
}
}
return o;
}
#region IEnumerable Members
public IEnumerator<T> GetEnumerator()
{
foreach (T t in Entrys)
{
yield return t;
}
}
public T this[int index]
{
get { return Entrys[index]; }
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (T t in Entrys)
{
yield return t;
}
}
#endregion
}
struct LocalizedString
{
public string this[int index]
{
get
{
return Strings[index];
}
}
public string[] Strings;
public UInt32 mask;
public override string ToString()
{
string s = "Ls(";
foreach (string t in Strings)
s += t + ",";
s += mask.ToString("x") + ")";
return s;
}
}
}
Some of the dbc structures
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Word.GameData.DBC
{
class ItemBagFamily : DBC<ItemBagFamily.ItemBagFamilyEntry>
{
[StructLayout(LayoutKind.Sequential)]
public class ItemBagFamilyEntry
{
/*
Column Field Type
1 ID Integer
2-10 sRefName String + Loc
*/
public int ID;
public LocalizedString sRefName;
}
}
class ItemClass : DBC<ItemClass.ItemClassEntry>
{
[StructLayout(LayoutKind.Sequential)]
public class ItemClassEntry
{
/*
Column Field Type Notes
1 ID Integer
2 Subclass? Integer pointing back to an other ID ;)
3 isWeapon? Boolean Only set on id=2 (name="Weapon", subclass=2)
4-12 Name String + Loc
*/
public int ID;
public int Subclass;
public bool isWeapon;
public LocalizedString Name;
}
}
class ItemDisenchantLoot : DBC<ItemDisenchantLoot.ItemDisenchantLootEntry>
{
[StructLayout(LayoutKind.Sequential)]
public class ItemDisenchantLootEntry
{
/*
Column Field Type Notes
1 ID Integer
2 Unknown Integer values are either 2 or 4 (Theory iRefID_ItemClass, 2 = Weapons, 4 = Armor)
3 Unknown BitMask
4 Unknown Integer values are 2, 3, or 4 (Theory Quality: 2=Uncommon, 3=Rare, 4=Epic)
5 Unknown Integer Theory Minimum Item Level
6 Unknown Integer Theory Maximum Item Level
7 Unknown Integer sRef to somewhere?
8 Unknown Integer
*/
public int ID;
public int unknown1;
public int unknown2;
public int unknown3;
public int unknown4;
public int unknown5;
public int unknown6;
public int unknown7;
}
}
class BankBagSlotPrices : DBC<BankBagSlotPrices.BankBagSlotPricesEntry>
{
[StructLayout(LayoutKind.Sequential)]
public class BankBagSlotPricesEntry
{
/*
1 ID Integer
2 Price Integer As said, in copper.
*/
public int ID;
public int Prices;
}
}
class Map : DBC<Map.MapEntry>
{
[StructLayout(LayoutKind.Sequential)]
public class MapEntry
{
/*
Column Field Type Notes
1 ID Integer
2 Internalname String reference to World\Map\ [...] \
3 AreaType Integer 0=norm; 1=dungeon; 2=raid; 3=pvp_zone; 4=arena
4 IsBattleground Boolean Simple flag for battleground maps
5-13 sRefName String + Loc Name (displayed on World Map for example)
14 MinLevel Integer Minimum level to enter, only set for Battlegrounds / Arenas
15 MaxLevel Integer Maximum level, only set for Battlegrounds / Arenas
16 MaxPlayers Integer Maximum amount of players, only set for Battlegrounds / Arenas
17 Unknown Signed Integer -1 for all except BlackRockSpire & BlackrockDepths (0) & PVPZone02 (azshara)
18 Unknown Float only set on PVPZone01 & 02 (Alterac and Azshara)
19 Unknown Float only set on PVPZone01 & 02 (Alterac and Azshara)
20 iRefID_AreaTable Integer Ref to ID in AreaTable.dbc
21-29 Description_1 String + Loc Shown for Horde
30-38 Description_2 String + Loc Shown for Alliance
39 iRefID_LoadingScreens Integer
40 LevelSteps Integer Increment in level per battlefield instance
41 Unknown Boolean All 1 except for Blackfathom Deeps
42 Unknown Float All 1.0 except of PVPZone04 (Arathi)
43-51 Unknown String + Loc No mapping to string block (6320).
52-60 HeroicRequirement String + Loc Used to describe requirement for heroic dungeons.
61-69 Unknown String + Loc No mapping to string block (6320).
70 parentMap Signed Integer Parent Map-ID, not always right. -1 for unfinished/unaccesable zones (and continents in Azeroth).
71 Unknown Float
72 Unknown Float
73 Unknown BitMask* Raid. 0x93A80 = 40/25/10-man raid, 0x3F480 = 20-man raid, 0x15180 = ? (only for ED)
74 wingedDungeon Integer 86400 (0x15180) if winged. (doesn't apply to raid dungeons)
75 Flybox Boolean 1 for maps with ADT files containing MFBO chunk (Flybox), 0 for the others.
*/
public int ID;
public string Internalname1;
public MapTypes AreaType;
public bool IsBattleground;
public LocalizedString sRefName;
public int iRefID_AreaTable;
public LocalizedString Description_1;
public LocalizedString Description_2;
public int iRefID_LoadingScreens;
public float Unknown4;
public LocalizedString NormalRequirement;
public LocalizedString HeroicRequirement;
public LocalizedString Unknown6;
public int Unknown3;
public float StartX;
public float StartY;
public int resetRaid;
public int resetHeroic;
public int parentMap;
public int Unknown7;
public int Unknown8;
public int Addon;
}
public enum MapTypes
{
MAP_COMMON = 0,
MAP_INSTANCE = 1,
MAP_RAID = 2,
MAP_BATTLEGROUND = 3,
MAP_ARENA = 4
};
}
Here's how you use it
Code:
Map _Map = new Map();
foreach (Map.MapEntry Entry in _Map)
Console.WriteLine("ID:{0} Name:{1}", Entry.ID, Entry.Internalname1);
Output
Code:
ID:0 Name:Azeroth
ID:1 Name:Kalimdor
ID:13 Name:test
ID:25 Name:ScottTest
ID:29 Name:Test
ID:30 Name:PVPZone01
ID:33 Name:Shadowfang
ID:34 Name:StormwindJail
ID:35 Name:StormwindPrison
ID:36 Name:DeadminesInstance
ID:37 Name:PVPZone02
ID:42 Name:Collin
ID:43 Name:WailingCaverns
ID:44 Name:Monastery
ID:47 Name:RazorfenKraulInstance
ID:48 Name:Blackfathom
ID:70 Name:Uldaman
ID:90 Name:GnomeragonInstance
ID:109 Name:SunkenTemple
ID:129 Name:RazorfenDowns
ID:169 Name:EmeraldDream
ID:189 Name:MonasteryInstances
ID:209 Name:TanarisInstance
ID:229 Name:BlackRockSpire
ID:230 Name:BlackrockDepths
ID:249 Name:OnyxiaLairInstance
ID:269 Name:CavernsOfTime
ID:289 Name:SchoolofNecromancy
ID:309 Name:Zul'gurub
ID:329 Name:Stratholme
ID:349 Name:Mauradon
ID:369 Name:DeeprunTram
ID:389 Name:OrgrimmarInstance
ID:409 Name:MoltenCore
ID:429 Name:DireMaul
ID:449 Name:AlliancePVPBarracks
ID:450 Name:HordePVPBarracks
ID:451 Name:development
ID:469 Name:BlackwingLair
ID:489 Name:PVPZone03
ID:509 Name:AhnQiraj
ID:529 Name:PVPZone04
ID:530 Name:Expansion01
ID:531 Name:AhnQirajTemple
ID:532 Name:Karazahn
ID:533 Name:Stratholme Raid
ID:534 Name:HyjalPast
ID:540 Name:HellfireMilitary
ID:542 Name:HellfireDemon
ID:543 Name:HellfireRampart
ID:544 Name:HellfireRaid
ID:545 Name:CoilfangPumping
ID:546 Name:CoilfangMarsh
ID:547 Name:CoilfangDraenei
ID:548 Name:CoilfangRaid
ID:550 Name:TempestKeepRaid
ID:552 Name:TempestKeepArcane
ID:553 Name:TempestKeepAtrium
ID:554 Name:TempestKeepFactory
ID:555 Name:AuchindounShadow
ID:556 Name:AuchindounDemon
ID:557 Name:AuchindounEthereal
ID:558 Name:AuchindounDraenei
ID:559 Name:PVPZone05
ID:560 Name:HillsbradPast
ID:562 Name:bladesedgearena
ID:564 Name:BlackTemple
ID:565 Name:GruulsLair
ID:566 Name:NetherstormBG
ID:568 Name:ZulAman
ID:571 Name:Northrend
ID:572 Name:PVPLordaeron
ID:573 Name:ExteriorTest
ID:574 Name:Valgarde70
ID:575 Name:UtgardePinnacle
ID:576 Name:Nexus70
ID:578 Name:Nexus80
ID:580 Name:SunwellPlateau
ID:582 Name:Transport176244
ID:584 Name:Transport176231
ID:585 Name:Sunwell5ManFix
ID:586 Name:Transport181645
ID:587 Name:Transport177233
ID:588 Name:Transport176310
ID:589 Name:Transport175080
ID:590 Name:Transport176495
ID:591 Name:Transport164871
ID:592 Name:Transport186238
ID:593 Name:Transport20808
ID:594 Name:Transport187038
ID:595 Name:StratholmeCOT
ID:596 Name:Transport187263
ID:597 Name:CraigTest
ID:598 Name:Sunwell5Man
ID:599 Name:Ulduar70
ID:600 Name:DrakTheronKeep
ID:601 Name:Azjol_Uppercity
ID:602 Name:Ulduar80
ID:603 Name:UlduarRaid
ID:604 Name:GunDrak
ID:605 Name:development_nonweighted
ID:606 Name:QA_DVD
ID:607 Name:NorthrendBG
ID:608 Name:DalaranPrison
ID:609 Name:DeathKnightStart
ID:610 Name:Transport_Tirisfal _Vengeance_Landing
ID:612 Name:Transport_Menethil_Valgarde
ID:613 Name:Transport_Orgrimmar_Warsong_Hold
ID:614 Name:Transport_Stormwind_Valiance_Keep
ID:615 Name:ChamberOfAspectsBlack
ID:616 Name:NexusRaid
ID:617 Name:DalaranArena
ID:618 Name:OrgrimmarArena
ID:619 Name:Azjol_LowerCity
ID:620 Name:Transport_Moa'ki_Unu'pe
ID:621 Name:Transport_Moa'ki_Kamagua
ID:622 Name:Transport192241
ID:623 Name:Transport192242