wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
schema_blob.hpp
Go to the documentation of this file.
1#pragma once
2
37
38#include <cstddef>
39#include <cstdint>
40#include <optional>
41#include <span>
42#include <string>
43#include <string_view>
44
46#include <wowlib/db/schema.hpp>
47
50 inline constexpr std::uint32_t Magic = 0x53424457u;
52 inline constexpr std::uint32_t FormatVersion = 2;
53
55 inline constexpr std::size_t HeaderBytes = 28;
56 inline constexpr std::size_t EraBytes = 12;
57 inline constexpr std::size_t TableBytes = 16;
58 inline constexpr std::size_t RangeBytes = 8;
59 inline constexpr std::size_t ColumnBytes = 16;
60
63 struct TableEntry {
64 std::uint32_t nameOff = 0;
65 std::uint32_t diskNameOff = 0;
66 std::uint32_t firstRange = 0;
67 std::uint32_t rangeCount = 0;
68 };
69
71 struct RangeEntry {
72 std::uint32_t firstColumn = 0;
73 std::uint16_t columnCount = 0;
74 std::uint16_t eraMask = 0;
75 };
76
79 struct ColumnEntry {
80 std::uint32_t nameOff = 0;
81 std::uint32_t dbdNameOff = 0;
83 std::uint8_t bits = 0;
84 bool isSigned = false;
85 bool isId = false;
86 bool isRelation = false;
87 bool noninline = false;
88 std::uint8_t localeCount = 0;
89 std::uint16_t arrayLen = 1;
90 };
91
99 class View {
100 public:
103 explicit constexpr View(std::span<const unsigned char> bytes) : _bytes{bytes} {}
104
109 constexpr bool valid() const {
110 if (_bytes.size() < HeaderBytes || _u32(0) != Magic || _u32(4) != FormatVersion) return false;
111 const std::size_t need = HeaderBytes + eraCount() * EraBytes + tableCount() * TableBytes + rangeCount() *
113 if (_bytes.size() != need || strpoolSize() == 0) return false;
114 // The pool must end in a terminator or stringAt would run off the end.
115 return _bytes[_bytes.size() - 1] == 0u;
116 }
117
119 constexpr std::size_t eraCount() const { return _bytes[24]; }
121 constexpr std::size_t tableCount() const { return _u32(8); }
123 constexpr std::size_t rangeCount() const { return _u32(12); }
125 constexpr std::size_t columnCount() const { return _u32(16); }
127 constexpr std::size_t strpoolSize() const { return _u32(20); }
128
132 constexpr ClientVersion era(std::size_t i) const {
133 const std::size_t at = HeaderBytes + i * EraBytes;
134 return ClientVersion{_u16(at), _u16(at + 2), _u16(at + 4), _u32(at + 8)};
135 }
136
140 constexpr TableEntry table(std::size_t i) const {
141 const std::size_t at = _tablesOff() + i * TableBytes;
142 return TableEntry{_u32(at), _u32(at + 4), _u32(at + 8), _u32(at + 12)};
143 }
144
148 constexpr RangeEntry range(std::size_t i) const {
149 const std::size_t at = _rangesOff() + i * RangeBytes;
150 return RangeEntry{_u32(at), _u16(at + 4), _u16(at + 6)};
151 }
152
156 constexpr ColumnEntry column(std::size_t i) const {
157 const std::size_t at = _columnsOff() + i * ColumnBytes;
158 const std::uint8_t flags = _bytes[at + 6];
159 ColumnEntry out{};
160 out.nameOff = _u32(at);
161 out.type = static_cast<ColumnType>(_bytes[at + 4]);
162 out.bits = _bytes[at + 5];
163 out.isSigned = (flags & 1u) != 0;
164 out.isId = (flags & 2u) != 0;
165 out.isRelation = (flags & 4u) != 0;
166 out.noninline = (flags & 8u) != 0;
167 out.localeCount = _bytes[at + 7];
168 out.arrayLen = _u16(at + 8);
169 out.dbdNameOff = _u32(at + 12);
170 return out;
171 }
172
177 std::string_view stringAt(std::uint32_t off) const {
178 const char* base = reinterpret_cast<const char*>(_bytes.data()) + _strpoolOff() + off;
179 return std::string_view{base};
180 }
181
186 constexpr std::string copyStringAt(std::uint32_t off) const {
187 std::string out{};
188 for (std::size_t at = _strpoolOff() + off; _bytes[at] != 0u; ++at) out += static_cast<char>(_bytes[at]);
189 return out;
190 }
191
195 constexpr std::optional<std::size_t>
196 findTable(std::string_view name) const {
197 std::size_t lo = 0;
198 std::size_t hi = tableCount();
199 while (lo < hi) {
200 const std::size_t mid = lo + (hi - lo) / 2;
201 const int cmp = _comparePool(table(mid).nameOff, name);
202 if (cmp == 0) return mid;
203 if (cmp < 0) lo = mid + 1;
204 else hi = mid;
205 }
206 return std::nullopt;
207 }
208
210 constexpr std::span<const unsigned char> bytes() const { return _bytes; }
211
212 private:
214 constexpr std::uint16_t _u16(std::size_t at) const {
215 return static_cast<std::uint16_t>(_bytes[at] | (std::uint16_t{_bytes[at + 1]} << 8));
216 }
217
219 constexpr std::uint32_t _u32(std::size_t at) const {
220 return _bytes[at] | (std::uint32_t{_bytes[at + 1]} << 8) | (std::uint32_t{_bytes[at + 2]} << 16) | (std::uint32_t{
221 _bytes[at + 3]
222 } << 24);
223 }
224
226 constexpr std::size_t _tablesOff() const {
227 return HeaderBytes + eraCount() * EraBytes;
228 }
229
230 constexpr std::size_t _rangesOff() const {
231 return _tablesOff() + tableCount() * TableBytes;
232 }
233
234 constexpr std::size_t _columnsOff() const {
235 return _rangesOff() + rangeCount() * RangeBytes;
236 }
237
238 constexpr std::size_t _strpoolOff() const {
239 return _columnsOff() + columnCount() * ColumnBytes;
240 }
241
245 constexpr int _comparePool(std::uint32_t off, std::string_view name) const {
246 std::size_t at = _strpoolOff() + off;
247 for (const char c : name) {
248 const unsigned char p = _bytes[at];
249 if (p == 0u || p < static_cast<unsigned char>(c)) return -1;
250 if (p > static_cast<unsigned char>(c)) return 1;
251 ++at;
252 }
253 return _bytes[at] == 0u ? 0 : 1;
254 }
255
256 std::span<const unsigned char> _bytes;
257 };
258}
constexpr ColumnEntry column(std::size_t i) const
The column entry i (blob-global index).
constexpr std::size_t tableCount() const
constexpr TableEntry table(std::size_t i) const
The directory entry of table i (name-sorted order).
constexpr std::optional< std::size_t > findTable(std::string_view name) const
Binary-search the (name-sorted) table directory for name.
constexpr std::size_t eraCount() const
std::string_view stringAt(std::uint32_t off) const
The pool string at off as a view into the blob (runtime only — the pointer cast out of the byte pool ...
constexpr std::span< const unsigned char > bytes() const
constexpr ClientVersion era(std::size_t i) const
The client version of era slot i.
constexpr std::size_t columnCount() const
constexpr RangeEntry range(std::size_t i) const
The range entry i (blob-global index).
constexpr bool valid() const
Whether the bytes are a structurally valid WDBS v2 blob: magic, version, and every section (plus the ...
constexpr std::string copyStringAt(std::uint32_t off) const
The pool string at off, copied byte by byte — the consteval-safe twin of stringAt.
constexpr std::size_t strpoolSize() const
constexpr View(std::span< const unsigned char > bytes)
Bind the view to bytes (unvalidated — call valid).
constexpr std::size_t rangeCount() const
Client version identity, the flavor axis that separates a client's CONTENT version from the engine ge...
constexpr std::uint32_t FormatVersion
The one format version this reader understands.
constexpr std::size_t EraBytes
constexpr std::size_t RangeBytes
constexpr std::size_t TableBytes
constexpr std::size_t ColumnBytes
constexpr std::size_t HeaderBytes
Section entry sizes (bytes), fixed by the format.
constexpr std::uint32_t Magic
The WDBS magic ('WDBS' little-endian).
ColumnType
The logical value class of a column.
Definition schema.hpp:34
@ Int
An integer column; Column::bits / Column::isSigned give the shape.
Definition schema.hpp:35
Schema reflection over generated client-database record structs: the column list, record stride and f...
One column, decoded to the raw blob facts (the catalog turns this into a wowlib::db::Column with an i...
std::uint16_t arrayLen
element count; 1 for scalars.
std::uint32_t dbdNameOff
The verbatim WoWDBDefs spelling's pool offset (0 = none).
std::uint32_t nameOff
The column name's pool offset.
std::uint8_t localeCount
LocString language slots; 0 otherwise.
ColumnType type
The logical value class.
std::uint8_t bits
Integer element width in bits.
One schema range: a run of columns valid for the eras in the mask.
std::uint32_t firstColumn
Index of the range's first column.
std::uint16_t eraMask
Bit i set = valid for the blob's era i.
std::uint16_t columnCount
Number of consecutive columns.
One table's directory entry, decoded.
std::uint32_t rangeCount
Number of consecutive ranges.
std::uint32_t diskNameOff
The on-disk table name's pool offset.
std::uint32_t nameOff
The identifier name's pool offset.
std::uint32_t firstRange
Index of the table's first range.