WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
WoWDatabase.cpp
Go to the documentation of this file.
1#include "WoWDatabase.h"
2#include "DB2Table.h"
3#include "Game.h"
4#include "Logger.h"
5#include "DB2Reader.h"
6#include "DBDFile.h"
7
8#include <sstream>
9#include <cstdio>
10
11const std::vector<std::string> POSSIBLE_DB_EXT = {".db2", ".dbc"};
12
16
18
19const DB2Table* wow::WoWDatabase::getTable(const std::string& name)
20{
21 auto it = m_tables.find(name);
22 if (it != m_tables.end())
23 return it->second.get();
24
25 auto table = buildDB2Table(name);
26 const DB2Table* result = table.get();
27 m_tables[name] = std::move(table);
28 return result;
29}
30
31std::unique_ptr<DB2Table> wow::WoWDatabase::buildDB2Table(const std::string& tableName)
32{
33 LOG_INFO << "buildDB2Table: start for " << tableName;
34
35 LOG_INFO << "buildDB2Table: buildTableStructure for " << tableName;
36 std::unique_ptr<core::TableStructure> tblStruct(buildTableStructure(tableName));
37 if (!tblStruct)
38 return nullptr;
39
40 // Build DB2FieldInfo vector from the TableStructure
41 std::vector<DB2FieldInfo> fields;
42 for (auto* f : tblStruct->fields)
43 {
44 auto* wf = dynamic_cast<wow::FieldStructure*>(f);
45 DB2FieldInfo info;
46 info.name = f->name;
47 info.type = f->type;
48 info.pos = wf ? wf->pos : -1;
49 info.arraySize = f->arraySize;
50 info.isKey = f->isKey;
51 info.isRelationshipData = wf ? wf->isRelationshipData : false;
52 fields.push_back(std::move(info));
53 }
54
55 // Create the DB2Reader directly from CASC
56 LOG_INFO << "buildDB2Table: creating DB2Reader for " << tableName;
57
58 // Resolve the DB2 file in CASC by name or file data ID
59 std::string db2Path;
60 for (const auto& ext : POSSIBLE_DB_EXT)
61 {
62 GameFile* f = GAMEDIRECTORY.getFile("DBFilesClient\\" + tblStruct->file + ext);
63 if (f)
64 {
65 db2Path = f->fullname();
66 break;
67 }
68 }
69 if (db2Path.empty())
70 {
71 const int fileDataId = GAMEDATABASE.getFileDataIdForTable(tblStruct->file);
72 if (fileDataId > 0)
73 {
74 GameFile* f = GAMEDIRECTORY.getFile(fileDataId);
75 if (f)
76 db2Path = f->fullname();
77 }
78 }
79 if (db2Path.empty())
80 {
81 LOG_ERROR << "WoWDatabase: DB2 file not found for table " << tableName;
82 return nullptr;
83 }
84
85 auto reader = std::make_unique<DB2Reader>(db2Path);
86
87 LOG_INFO << "buildDB2Table: reader->open() for " << tableName;
88 if (!reader->open())
89 {
90 LOG_ERROR << "WoWDatabase: failed to open DB2 for table " << tableName;
91 return nullptr;
92 }
93
94 LOG_INFO << "buildDB2Table: done for " << tableName;
95 return std::make_unique<DB2Table>(std::move(reader), std::move(fields));
96}
97
102
107
109 core::TableStructure* tblStruct)
110{
111 wow::TableStructure* tbl = dynamic_cast<wow::TableStructure*>(tblStruct);
112 if (!tbl)
113 return;
114
115 // Extract the layout hash from the version definition
116 if (!verDef.layoutHashes.empty())
117 {
118 // Layout hash is a hex string - convert to uint32
119 unsigned long val = std::stoul(verDef.layoutHashes[0], nullptr, 16);
120 tbl->hash = static_cast<unsigned int>(val);
121 }
122}
123
125 const core::DBDColumnDef& colDef,
126 core::FieldStructure* fieldStruct)
127{
128 wow::FieldStructure* field = dynamic_cast<wow::FieldStructure*>(fieldStruct);
129 if (!field)
130 return;
131
132 field->isRelationshipData = vField.isRelation && vField.isNonInline;
133
134 // Only noninline fields have no DB2 position
135 if (vField.isNonInline)
136 field->pos = -1;
137}
138
140{
141 wow::FieldStructure* field = dynamic_cast<wow::FieldStructure*>(fieldStruct);
142 if (field)
143 field->pos = pos;
144}
145
146std::string wow::WoWDatabase::getLayoutHashForTable(const std::string& tableName)
147{
148 GameFile* fileToOpen = nullptr;
149 for (const auto& ext : POSSIBLE_DB_EXT)
150 {
151 fileToOpen = GAMEDIRECTORY.getFile("DBFilesClient\\" + tableName + ext);
152 if (fileToOpen)
153 break;
154 }
155
156 // Fallback: try file data ID from manifest
157 if (!fileToOpen)
158 {
159 const int fileDataId = GAMEDATABASE.getFileDataIdForTable(tableName);
160 if (fileDataId > 0)
161 fileToOpen = GAMEDIRECTORY.getFile(fileDataId);
162 }
163
164 if (!fileToOpen)
165 return "";
166
167 if (!fileToOpen->open(false))
168 return "";
169
170 char magic[4] = {};
171 fileToOpen->read(magic, 4);
172
173 // Determine offset to layout_hash based on format.
174 // In the WDC3 header struct, layout_hash is at byte offset 24 from file start:
175 // magic(4) + record_count(4) + field_count(4) + record_size(4) + string_table_size(4) + table_hash(4)
176 // WDC5 adds versionNum(4) + schemaString(128) = 132 extra bytes after magic.
177 size_t layoutHashOffset = 0;
178
179 if (strncmp(magic, "WDC5", 4) == 0)
180 layoutHashOffset = 4 + 132 + 20; // 156
181 else if (strncmp(magic, "WDC2", 4) == 0 || strncmp(magic, "WDC3", 4) == 0 || strncmp(magic, "WDC4", 4) == 0)
182 layoutHashOffset = 24;
183 else
184 {
185 fileToOpen->close();
186 return "";
187 }
188
189 fileToOpen->seek(layoutHashOffset);
190 unsigned int layoutHash = 0;
191 fileToOpen->read(&layoutHash, sizeof(layoutHash));
192 fileToOpen->close();
193
194 // Convert to uppercase hex string to match DBD format (e.g. "0E84A21C")
195 char hexBuf[9];
196 std::snprintf(hexBuf, sizeof(hexBuf), "%08X", layoutHash);
197 return std::string(hexBuf);
198}
199
200// createDBFile() removed — DB2Reader is created directly in buildDB2Table().
#define GAMEDATABASE
Definition Game.h:10
#define GAMEDIRECTORY
Definition Game.h:9
#define LOG_ERROR
Definition Logger.h:11
#define LOG_INFO
Definition Logger.h:10
const std::vector< std::string > POSSIBLE_DB_EXT
Provides typed, field-name-based access to records in a WDC DB2 file.
Definition DB2Table.h:50
Abstract base class representing a file within the game data archive.
Definition GameFile.h:12
bool open(bool useMemoryBuffer=true)
Open the file, optionally loading into a memory buffer.
Definition GameFile.cpp:41
virtual void seek(size_t offset)
Seek to an absolute byte offset.
Definition GameFile.cpp:29
bool close()
Close the file and release the internal buffer.
Definition GameFile.cpp:74
virtual size_t read(void *dest, size_t bytes)
Read bytes from the file into dest.
Definition GameFile.cpp:5
const std::string & fullname() const
Definition GameFile.h:56
Describes a single field (column) in a database table.
Describes the schema of a database table (name, file path, fields).
unsigned int hash
Definition WoWDatabase.h:21
std::string getLayoutHashForTable(const std::string &tableName) override
void setFieldPos(core::FieldStructure *, int pos) override
core::FieldStructure * createFieldStructure()
void readSpecificFieldAttributesFromDBD(const core::DBDVersionField &, const core::DBDColumnDef &, core::FieldStructure *) override
const DB2Table * getTable(const std::string &name)
void readSpecificTableAttributesFromDBD(const core::DBDVersionDef &, core::TableStructure *) override
~WoWDatabase() override
std::unique_ptr< DB2Table > buildDB2Table(const std::string &tableName)
core::TableStructure * createTableStructure()
Describes a single field (column) in a DB2 table.
Definition DB2Table.h:14
bool isRelationshipData
Whether this field is relationship data.
Definition DB2Table.h:20
int pos
DB2 field position index (-1 for non-inline/key).
Definition DB2Table.h:17
std::string name
Column name.
Definition DB2Table.h:15
std::string type
Type string ("text", "float", "int8", "uint8", etc.).
Definition DB2Table.h:16
unsigned int arraySize
Array size (> 1 for array fields).
Definition DB2Table.h:18
bool isKey
Whether this field is the primary key.
Definition DB2Table.h:19
std::vector< std::string > layoutHashes
Definition DBDFile.h:51