WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
GameDatabase.cpp
Go to the documentation of this file.
1#include "GameDatabase.h"
2#include "Logger.h"
3#include "Game.h"
4#include "DBDFile.h"
5#include "string_utils.h"
6#include "HttpClient.h"
7
8#include <filesystem>
9#include <fstream>
10#include <sstream>
11#include <string>
12#include <vector>
13
14#include <nlohmann/json.hpp>
15
16static std::string dbdTypeToSqlType(const std::string& baseType, const std::string& sizeStr);
17
21
23{
24}
25
27{
28 if (m_manifestUrl.empty())
29 {
30 LOG_ERROR << "Manifest URL not set.";
31 return false;
32 }
33
34 LOG_INFO << "Downloading DBD manifest from " << m_manifestUrl;
35 const auto resp = HttpClient::Get(m_manifestUrl);
36 if (!resp.success || resp.body.empty())
37 {
38 LOG_ERROR << "Failed to download manifest:" << resp.error;
39 return false;
40 }
41
42 try
43 {
44 const auto manifest = nlohmann::json::parse(resp.body);
45 for (const auto& entry : manifest)
46 {
47 if (entry.contains("tableName") && entry.contains("db2FileDataID"))
48 {
49 const std::string tableName = entry["tableName"].get<std::string>();
50 const int fileDataId = entry["db2FileDataID"].get<int>();
51 if (!tableName.empty() && fileDataId > 0)
52 m_tableFileDataIds[tableName] = fileDataId;
53 }
54 }
55
56 LOG_INFO << "Loaded DBD manifest with " << m_tableFileDataIds.size() << " entries ";
57 return true;
58 }
59 catch (const std::exception& e)
60 {
61 LOG_ERROR << "Failed to parse manifest JSON:" << e.what();
62 return false;
63 }
64}
65
66int core::GameDatabase::getFileDataIdForTable(const std::string& tableName) const
67{
68 const auto it = m_tableFileDataIds.find(tableName);
69 if (it != m_tableFileDataIds.end())
70 return it->second;
71 return 0;
72}
73
75{
76 namespace fs = std::filesystem;
77
78 std::string dbdPath = m_dbdDir + "/" + tableName + ".dbd";
79
80 if (!fs::exists(dbdPath) && !m_dbdBaseUrl.empty())
81 {
82 std::string url = m_dbdBaseUrl;
83 auto pos = url.find("%s");
84 if (pos != std::string::npos)
85 url.replace(pos, 2, tableName);
86 else
87 url += tableName + ".dbd";
88
89 LOG_INFO << "Downloading DBD for" << tableName << "from" << url;
90 const auto resp = HttpClient::Get(url);
91 if (resp.success && !resp.body.empty())
92 {
93 fs::create_directories(m_dbdDir);
94 std::ofstream out(dbdPath, std::ios::binary);
95 if (out.is_open())
96 {
97 out.write(resp.body.data(), resp.body.size());
98 out.close();
99 }
100 }
101 else
102 {
103 LOG_ERROR << "Failed to download DBD for" << tableName << ":" << resp.error;
104 }
105 }
106
107 if (!fs::exists(dbdPath))
108 {
109 LOG_ERROR << "DBD file not found:" << dbdPath;
110 return nullptr;
111 }
112
113 core::DBDFile dbd;
114 if (!dbd.parse(dbdPath))
115 {
116 LOG_ERROR << "Failed to parse DBD file:" << dbdPath;
117 return nullptr;
118 }
119
120 const std::string layoutHash = getLayoutHashForTable(tableName);
121 const core::DBDVersionDef* verDef = dbd.findVersion(m_build, layoutHash);
122 if (!verDef)
123 {
124 LOG_ERROR << "No matching version definition found in" << dbdPath
125 << "for build" << m_build.major << "." << m_build.minor
126 << "." << m_build.patch << "." << m_build.build;
127 return nullptr;
128 }
129
130 core::TableStructure* tblStruct = createTableStructure();
131 tblStruct->name = tableName;
132 tblStruct->file = tableName;
133
134 readSpecificTableAttributesFromDBD(*verDef, tblStruct);
135
136 int fieldId = 0;
137 int fieldPos = 0;
138
139 for (const auto& vField : verDef->fields)
140 {
141 const core::DBDColumnDef* colDef = dbd.findColumn(vField.name);
142 if (!colDef)
143 {
144 LOG_ERROR << "Column definition not found for field" << vField.name
145 << "in table" << tableName;
146 if (!vField.isNonInline)
147 fieldPos++;
148 continue;
149 }
150
151 core::FieldStructure* fieldStruct = createFieldStructure();
152 fieldStruct->id = fieldId++;
153 fieldStruct->name = vField.name;
154 fieldStruct->type = dbdTypeToSqlType(colDef->type, vField.sizeStr);
155 fieldStruct->arraySize = vField.arraySize;
156 fieldStruct->isKey = vField.isID;
157 fieldStruct->needIndex = false;
158
159 readSpecificFieldAttributesFromDBD(vField, *colDef, fieldStruct);
160
161 if (!vField.isNonInline)
162 {
163 setFieldPos(fieldStruct, fieldPos);
164 fieldPos++;
165 }
166
167 tblStruct->fields.push_back(fieldStruct);
168 }
169
170 return tblStruct;
171}
172
174{
175 for (const auto it : fields)
176 delete it;
177}
178
179// --- DBD support ---
180
181bool core::GameDatabase::initFromDBD(const std::string& dbdDir, const std::string& buildVersion)
182{
183 return initFromDBD(dbdDir, buildVersion, {});
184}
185
186bool core::GameDatabase::initFromDBD(const std::string& dbdDir, const std::string& buildVersion,
187 const std::vector<std::string>& tableNames)
188{
189 m_dbdDir = dbdDir;
190 m_build = core::DBDBuild::fromString(buildVersion);
191
192 LOG_INFO << "Database initialized for on-demand loading (build "
193 << m_build.major << "." << m_build.minor << "."
194 << m_build.patch << "." << m_build.build << ")";
195 return true;
196}
197
198static std::string dbdTypeToSqlType(const std::string& baseType, const std::string& sizeStr)
199{
200 if (baseType == "string" || baseType == "locstring")
201 return "text";
202
203 if (baseType == "float")
204 return "float";
205
206 // int type - determine signed/unsigned and bit size from sizeStr
207 if (baseType == "int")
208 {
209 if (sizeStr.empty())
210 return "int32"; // default
211
212 bool isUnsigned = false;
213 std::string numStr = sizeStr;
214
215 if (!numStr.empty() && (numStr[0] == 'u' || numStr[0] == 'U'))
216 {
217 isUnsigned = true;
218 numStr = numStr.substr(1);
219 }
220
221 std::string prefix = isUnsigned ? "uint" : "int";
222 return prefix + numStr;
223 }
224
225 return "int32";
226}
static std::string dbdTypeToSqlType(const std::string &baseType, const std::string &sizeStr)
#define LOG_ERROR
Definition Logger.h:11
#define LOG_INFO
Definition Logger.h:10
Parsed content of a single .dbd file (database definition).
Definition DBDFile.h:63
bool parse(const std::string &filepath)
Definition DBDFile.cpp:78
const DBDVersionDef * findVersion(const DBDBuild &build) const
Definition DBDFile.cpp:375
const DBDColumnDef * findColumn(const std::string &name) const
Definition DBDFile.cpp:401
Describes a single field (column) in a database table.
unsigned int arraySize
bool initFromDBD(const std::string &dbdDir, const std::string &buildVersion)
int getFileDataIdForTable(const std::string &tableName) const
core::TableStructure * buildTableStructure(const std::string &tableName)
bool downloadAndParseManifest()
Describes the schema of a database table (name, file path, fields).
std::vector< FieldStructure * > fields
Response Get(const std::string &url, const ProgressCallback &progress=nullptr)
Perform a synchronous HTTP(S) GET request.
static DBDBuild fromString(const std::string &s)
Definition DBDFile.cpp:12
std::string type
Definition DBDFile.h:29
std::string name
Definition DBDFile.h:30
std::vector< DBDVersionField > fields
Definition DBDFile.h:55