WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
DB2Table.cpp
Go to the documentation of this file.
1#include "DB2Table.h"
2#include "DB2Reader.h"
3#include "Logger.h"
4
5#include <algorithm>
6#include <cctype>
7#include <cstring>
8
9// ── DB2Row ───────────────────────────────────────────────────────────────────
10
11uint32_t DB2Row::recordID() const
12{
13 if (!m_table) return 0;
15}
16
17uint32_t DB2Row::getUInt(const std::string& field, unsigned int arrayIndex) const
18{
19 if (!m_table) return 0;
20 unsigned int idx = arrayIndex;
21 const DB2FieldInfo* fi = m_table->resolveField(field, idx);
22 if (!fi) return 0;
23 return m_table->readUInt(m_recordIndex, *fi, idx);
24}
25
26int32_t DB2Row::getInt(const std::string& field, unsigned int arrayIndex) const
27{
28 if (!m_table) return 0;
29 unsigned int idx = arrayIndex;
30 const DB2FieldInfo* fi = m_table->resolveField(field, idx);
31 if (!fi) return 0;
32
33 uint32_t raw = m_table->readUInt(m_recordIndex, *fi, idx);
34
35 // Sign-extend based on the underlying type
36 if (fi->type == "int8")
37 {
38 int8_t v;
39 std::memcpy(&v, &raw, sizeof(int8_t));
40 return static_cast<int32_t>(v);
41 }
42 if (fi->type == "int16")
43 {
44 int16_t v;
45 std::memcpy(&v, &raw, sizeof(int16_t));
46 return static_cast<int32_t>(v);
47 }
48
49 int32_t v;
50 std::memcpy(&v, &raw, sizeof(int32_t));
51 return v;
52}
53
54float DB2Row::getFloat(const std::string& field, unsigned int arrayIndex) const
55{
56 if (!m_table) return 0.0f;
57 unsigned int idx = arrayIndex;
58 const DB2FieldInfo* fi = m_table->resolveField(field, idx);
59 if (!fi) return 0.0f;
60
61 uint32_t raw = m_table->readUInt(m_recordIndex, *fi, idx);
62 float result;
63 std::memcpy(&result, &raw, sizeof(float));
64 return result;
65}
66
67std::string DB2Row::getString(const std::string& field, unsigned int arrayIndex) const
68{
69 if (!m_table) return "";
70 unsigned int idx = arrayIndex;
71 const DB2FieldInfo* fi = m_table->resolveField(field, idx);
72 if (!fi) return "";
73 return m_table->readString(m_recordIndex, *fi, idx);
74}
75
76// ── helpers ──────────────────────────────────────────────────────────────────
77
78static std::string toLower(const std::string& s)
79{
80 std::string result = s;
81 std::transform(result.begin(), result.end(), result.begin(),
82 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
83 return result;
84}
85
86// ── DB2Table ─────────────────────────────────────────────────────────────────
87
88DB2Table::DB2Table(std::unique_ptr<DB2Reader> reader, std::vector<DB2FieldInfo> fields)
89 : m_reader(std::move(reader)), m_fields(std::move(fields))
90{
91 for (size_t i = 0; i < m_fields.size(); i++)
93}
94
95DB2Table::~DB2Table() = default;
96
97DB2Row DB2Table::getRow(uint32_t id) const
98{
99 auto it = m_reader->m_idToRecordIndex.find(id);
100 if (it != m_reader->m_idToRecordIndex.end())
101 return DB2Row(this, it->second);
102 return DB2Row();
103}
104
106{
107 if (index < m_reader->m_recordLocations.size())
108 return DB2Row(this, index);
109 return DB2Row();
110}
111
113{
114 return m_reader->m_recordLocations.size();
115}
116
117const DB2FieldInfo* DB2Table::findField(const std::string& name) const
118{
119 auto it = m_fieldNameToIndex.find(toLower(name));
120 if (it != m_fieldNameToIndex.end())
121 return &m_fields[it->second];
122 return nullptr;
123}
124
125const DB2FieldInfo* DB2Table::resolveField(const std::string& name, unsigned int& arrayIndex) const
126{
127 const std::string lower = toLower(name);
128
129 // Try exact match first (base name or single field)
130 auto it = m_fieldNameToIndex.find(lower);
131 if (it != m_fieldNameToIndex.end())
132 return &m_fields[it->second];
133
134 // Try expanded SQL-style array name: "Field1" -> "Field" + arrayIndex=0
135 size_t numStart = lower.size();
136 while (numStart > 0 && std::isdigit(static_cast<unsigned char>(lower[numStart - 1])))
137 --numStart;
138
139 if (numStart > 0 && numStart < lower.size())
140 {
141 std::string baseName = lower.substr(0, numStart);
142 it = m_fieldNameToIndex.find(baseName);
143 if (it != m_fieldNameToIndex.end() && m_fields[it->second].arraySize > 1)
144 {
145 int idx = std::stoi(lower.substr(numStart)) - 1; // SQL names are 1-based
146 if (idx >= 0 && static_cast<unsigned int>(idx) < m_fields[it->second].arraySize)
147 {
148 arrayIndex = static_cast<unsigned int>(idx);
149 return &m_fields[it->second];
150 }
151 }
152 }
153
154 LOG_ERROR << "DB2Table: field not found: " << name;
155 return nullptr;
156}
157
158uint32_t DB2Table::getRecordID(size_t recordIndex) const
159{
160 return m_reader->m_recordLocations[recordIndex].recordID;
161}
162
163uint32_t DB2Table::readUInt(size_t recordIndex, const DB2FieldInfo& field, unsigned int arrayIndex) const
164{
165 const auto& loc = m_reader->m_recordLocations[recordIndex];
166 const auto& sec = m_reader->m_sections[loc.sectionIndex];
167
168 if (field.isKey)
169 return loc.recordID;
170
171 if (field.isRelationshipData)
172 {
173 auto relIt = sec.relationshipMap.find(loc.localIndex);
174 return (relIt != sec.relationshipMap.end()) ? relIt->second : 0;
175 }
176
177 unsigned int result = 0;
178 m_reader->readFieldValue(loc.sectionIndex, loc.localIndex,
179 field.pos, arrayIndex, field.arraySize,
180 loc.recordID, result);
181 return result;
182}
183
184std::string DB2Table::readString(size_t recordIndex, const DB2FieldInfo& field, unsigned int arrayIndex) const
185{
186 const auto& loc = m_reader->m_recordLocations[recordIndex];
187 const auto& sec = m_reader->m_sections[loc.sectionIndex];
188 const unsigned char* recordOffset = m_reader->getRecordOffset(loc.sectionIndex, loc.localIndex);
189
190 // Read the raw field value (string table offset for normal records)
191 unsigned int val = 0;
192 if (field.pos >= 0)
193 m_reader->readFieldValue(loc.sectionIndex, loc.localIndex,
194 field.pos, arrayIndex, field.arraySize,
195 loc.recordID, val);
196
197 const char* stringPtr = nullptr;
198
199 if (!sec.isNormal)
200 {
201 // Sparse table: strings are inline; walk through fields to find offset
202 const unsigned char* ptr = recordOffset;
203 for (int f = 0; f <= field.pos && static_cast<size_t>(f) < m_fields.size(); f++)
204 {
205 if (m_fields[f].isKey || m_fields[f].isRelationshipData || m_fields[f].pos < 0)
206 continue;
207 if (m_fields[f].type == "uint64")
208 {
209 ptr += 8;
210 }
211 else
212 {
213 std::string v(reinterpret_cast<const char*>(ptr));
214 ptr += v.size() + 1;
215 }
216 }
217 stringPtr = reinterpret_cast<const char*>(ptr);
218 }
219 else if (m_reader->m_wdcVersion > 2)
220 {
221 // WDC3+: string table reference
222 const uint32_t dataPos = m_reader->m_fieldStorageInfo[field.pos].field_offset_bits / 8;
223
224 uint32_t outsideDataSize = 0;
225 for (uint32_t s = 0; s < loc.sectionIndex; s++)
226 outsideDataSize += m_reader->m_sections[s].recordDataSize;
227
228 const int32_t absoluteRecordOfs =
229 static_cast<int32_t>(loc.localIndex * m_reader->m_header.record_size) -
230 static_cast<int32_t>(m_reader->m_header.record_count * m_reader->m_header.record_size);
231
232 const int32_t stringTableIndex =
233 static_cast<int32_t>(outsideDataSize) + absoluteRecordOfs +
234 static_cast<int32_t>(dataPos) + static_cast<int32_t>(val);
235
236 const unsigned char* strBase = nullptr;
237 for (uint32_t s = 0; s < m_reader->m_sections.size(); s++)
238 {
239 const auto& strSec = m_reader->m_sections[s];
240 const int32_t localOfs = stringTableIndex - static_cast<int32_t>(strSec.previousStringTableSize);
241 if (localOfs >= 0 && static_cast<uint32_t>(localOfs) < strSec.stringTableSize)
242 {
243 strBase = m_reader->m_fileData + strSec.stringTableOffset + localOfs;
244 break;
245 }
246 }
247
248 if (strBase)
249 stringPtr = reinterpret_cast<const char*>(strBase);
250 else
251 stringPtr = "";
252 }
253 else
254 {
255 // WDC2: strings inline in record data
256 stringPtr = reinterpret_cast<const char*>(
257 recordOffset + m_reader->m_fieldStorageInfo[field.pos].field_offset_bits / 8 + val -
258 ((m_reader->m_header.record_count - sec.recordCount) * m_reader->m_header.record_size));
259 }
260
261 std::string result(stringPtr ? stringPtr : "");
262 std::replace(result.begin(), result.end(), '"', '\'');
263 return result;
264}
static std::string toLower(const std::string &s)
Definition DB2Table.cpp:78
#define LOG_ERROR
Definition Logger.h:11
Lightweight handle to a single row in a DB2Table.
Definition DB2Table.h:27
std::string getString(const std::string &field, unsigned int arrayIndex=0) const
Definition DB2Table.cpp:67
float getFloat(const std::string &field, unsigned int arrayIndex=0) const
Definition DB2Table.cpp:54
size_t m_recordIndex
Definition DB2Table.h:45
int32_t getInt(const std::string &field, unsigned int arrayIndex=0) const
Definition DB2Table.cpp:26
uint32_t recordID() const
Definition DB2Table.cpp:11
const DB2Table * m_table
Definition DB2Table.h:44
uint32_t getUInt(const std::string &field, unsigned int arrayIndex=0) const
Definition DB2Table.cpp:17
DB2Row getRow(uint32_t id) const
Definition DB2Table.cpp:97
const DB2FieldInfo * findField(const std::string &name) const
Definition DB2Table.cpp:117
uint32_t readUInt(size_t recordIndex, const DB2FieldInfo &field, unsigned int arrayIndex) const
Definition DB2Table.cpp:163
std::vector< DB2FieldInfo > m_fields
Definition DB2Table.h:101
DB2Table(std::unique_ptr< DB2Reader > reader, std::vector< DB2FieldInfo > fields)
Definition DB2Table.cpp:88
size_t getRowCount() const
Definition DB2Table.cpp:112
DB2Row getRowByIndex(size_t index) const
Definition DB2Table.cpp:105
std::unordered_map< std::string, size_t > m_fieldNameToIndex
Definition DB2Table.h:102
friend class DB2Row
Definition DB2Table.h:85
std::string readString(size_t recordIndex, const DB2FieldInfo &field, unsigned int arrayIndex) const
Definition DB2Table.cpp:184
std::unique_ptr< DB2Reader > m_reader
Definition DB2Table.h:100
const DB2FieldInfo * resolveField(const std::string &name, unsigned int &arrayIndex) const
Definition DB2Table.cpp:125
uint32_t getRecordID(size_t recordIndex) const
Definition DB2Table.cpp:158
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 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