wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
schema_catalog.cpp
Go to the documentation of this file.
2
3#include <fstream>
4#include <utility>
5
7
8namespace wowlib::db {
9 SchemaCatalog SchemaCatalog::_materialize(std::span<const unsigned char> bytes, std::vector<unsigned char> owned) {
10 const blob::View view{bytes};
11 SchemaCatalog out{};
12 out._owned = std::move(owned);
13
14 out._eras.reserve(view.eraCount());
15 for (std::size_t i = 0; i < view.eraCount(); ++i) out._eras.push_back(view.era(i));
16
17 out._columns.reserve(view.columnCount());
18 for (std::size_t i = 0; i < view.columnCount(); ++i) {
19 const blob::ColumnEntry entry = view.column(i);
20 Column col{};
21 col.name = view.stringAt(entry.nameOff).data();
22 col.dbdName = view.stringAt(entry.dbdNameOff).data();
23 col.type = entry.type;
24 col.bits = entry.bits;
25 col.isSigned = entry.isSigned;
26 col.arrayLen = entry.arrayLen;
27 col.localeCount = entry.localeCount;
28 col.isId = entry.isId;
29 col.isRelation = entry.isRelation;
30 col.noninline = entry.noninline;
31 out._columns.push_back(col);
32 }
33
34 out._ranges.reserve(view.rangeCount());
35 for (std::size_t i = 0; i < view.rangeCount(); ++i) {
36 const blob::RangeEntry entry = view.range(i);
37 out._ranges.push_back(RangeIndex{entry.firstColumn, entry.columnCount, entry.eraMask});
38 }
39
40 out._tables.reserve(view.tableCount());
41 for (std::size_t i = 0; i < view.tableCount(); ++i) {
42 const blob::TableEntry entry = view.table(i);
43 out._tables.push_back(TableIndex{
44 view.stringAt(entry.nameOff),
45 view.stringAt(entry.diskNameOff),
46 entry.firstRange,
47 entry.rangeCount
48 });
49 }
50 return out;
51 }
52
53#if WOWLIB_DB_SCHEMA_RUNTIME
54 Result<SchemaCatalog> SchemaCatalog::fromBlob(std::vector<unsigned char> bytes) {
55 const blob::View view{bytes};
56 if (!view.valid())
58 "not a WDBS v2 schema blob (bad magic, version or " "section bounds)");
59 // The span must reference the FINAL resting place of the bytes — move the
60 // vector first, then view it inside _materialize's caller frame.
61 std::vector<unsigned char> owned{std::move(bytes)};
62 const std::span<const unsigned char> stable{owned};
63 return _materialize(stable, std::move(owned));
64 } Result<SchemaCatalog> SchemaCatalog::fromBlobFile(const std::filesystem::path& path) {
65 std::ifstream in{path, std::ios::binary};
66 if (!in)
67 return makeError(ErrorCode::IoError, "cannot open schema blob: " + path.string());
68 std::vector<unsigned char> bytes{std::istreambuf_iterator<char>{in}, std::istreambuf_iterator<char>{}};
69 if (!in.good() && !in.eof())
70 return makeError(ErrorCode::IoError, "cannot read schema blob: " + path.string());
71 return fromBlob(std::move(bytes));
72 }
73#endif
74
75 Result<std::size_t> SchemaCatalog::_eraIndexOf(ClientVersion version) const {
76 for (std::size_t i = 0; i < _eras.size(); ++i)
77 if (_eras[i].major == version.major) return i;
79 "no targeted era shares major version " + std::to_string(version.major));
80 }
81
82 Result<TableSchema> SchemaCatalog::lookup(std::string_view table, ClientVersion version) const {
83 // Binary search the name-sorted directory (the blob writer sorts).
84 std::size_t lo = 0;
85 std::size_t hi = _tables.size();
86 const TableIndex* found = nullptr;
87 while (lo < hi) {
88 const std::size_t mid = lo + (hi - lo) / 2;
89 const TableIndex& entry = _tables[mid];
90 if (entry.name == table) {
91 found = &entry;
92 break;
93 }
94 if (entry.name < table) lo = mid + 1;
95 else hi = mid;
96 }
97 if (found == nullptr)
98 return makeError(ErrorCode::TableUnknown, "schema catalog knows no table '" + std::string{table} + '\'');
99 return _eraIndexOf(version).and_then([&](std::size_t era) -> Result<TableSchema> {
100 for (std::size_t r = 0; r < found->rangeCount; ++r) {
101 const RangeIndex& range = _ranges[found->firstRange + r];
102 if ((range.eraMask & (std::uint16_t{1} << era)) == 0) continue;
103 return TableSchema{
104 found->name,
105 found->diskName,
106 std::span<const Column>{_columns}.subspan(range.firstColumn, range.columnCount)
107 };
108 }
110 "table '" + std::string{found->name} + "' has no schema for the era of build " + std::to_string(
111 version.build));
112 });
113 }
114
115#if WOWLIB_DB_SCHEMA_EMBEDDED
116 namespace detail {
120 constexpr unsigned char EmbeddedSchemaBlob[] = {
121#embed <wowlib_schema.wdbs>
122 };
123 static_assert(blob::View{std::span<const unsigned char>{EmbeddedSchemaBlob}}.valid(),
124 "the embedded schema blob failed structural validation — "
125 "dbdgen output and schema_blob.hpp disagree on the format");
126 } const SchemaCatalog& SchemaCatalog::embedded() {
127 static const SchemaCatalog Catalog = _materialize(std::span<const unsigned char>{detail::EmbeddedSchemaBlob}, {});
128 return Catalog;
129 }
130#endif
131}
The parsed, materialized form of one WDBS schema blob.
SchemaCatalog(SchemaCatalog &&) noexcept=default
Result< TableSchema > lookup(std::string_view table, ClientVersion version) const
Resolve the schema of table for client version.
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
Definition error.hpp:100
std::unexpected< Error > makeError(ErrorCode code, std::string message, std::uint32_t nativeError=0)
Shorthand for constructing the error branch of a Result.
Definition error.hpp:107
@ TableUnknown
The schema catalog knows no table of the requested name.
Definition error.hpp:49
@ UnsupportedClientVersion
No format instantiation exists for the requested client version.
Definition error.hpp:42
@ SchemaBlobInvalid
A WDBS schema blob (or WoWDBDefs source) is malformed or truncated.
Definition error.hpp:48
@ IoError
Generic filesystem I/O failure (project directory).
Definition error.hpp:30
The WDBS schema-blob format: a constexpr-parseable view over the compact binary dbdgen emits (--schem...
The runtime schema source of the generic ClientDB engine: table name + client version in,...
std::uint32_t build
Exact client build number, e.g.
One resolved (table, client version) schema: what SchemaCatalog::lookup hands the engine.