wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
table_core.hpp
Go to the documentation of this file.
1#pragma once
2
28
29#include <cstddef>
30#include <cstdint>
31#include <optional>
32#include <span>
33#include <string_view>
34#include <vector>
35
36#include <welder/vocabulary.hpp>
37
39#include <wowlib/core/error.hpp>
41#include <wowlib/db/codec.hpp>
45
46namespace wowlib::fs {
47 class FileSystem;
48}
49
50namespace wowlib::db {
54 class TableCore {
55 public:
58 _vec = recordsVec;
59 _ops = ops;
60 _info = info;
61 }
62
70 void wire(RecordSink* sink, const RecordSource* source, TableInfo info) {
71 _extSink = sink;
72 _extSource = source;
73 _info = info;
74 }
75
78 void rewire(void* recordsVec) { _vec = recordsVec; }
79
83 void rewire(RecordSink* sink, const RecordSource* source) {
84 _extSink = sink;
85 _extSource = source;
86 }
87
88 Result<void> read(std::span<const std::byte> data);
91 Result<void> write(fs::FileSystem& fs, const FileKey& key, EncryptedPolicy policy) const;
92
93 const formats::StringBlock& strings() const { return _state.strings; }
94
95 const std::vector<EncryptedSection>& encryptedSections() const {
96 return _state.encrypted;
97 }
98
99 bool fullyDecoded() const { return _state.encrypted.empty(); }
101 Result<void> ensureValid() const { return validate().toResult(); }
102
104 const TableInfo& info() const { return _info; }
105
108 void* recordsVec() const { return _vec; }
109 const detail::RecordOps* ops() const { return _ops; }
110
111 private:
112 Result<void> _requireWired() const;
113 std::uint32_t _db2MagicForVersion() const;
114 Result<std::uint32_t> _freshMagic(std::optional<std::string_view> path) const;
115 Result<FileBuffer> _writeAs(std::uint32_t magic, EncryptedPolicy policy) const;
116
117 void* _vec = nullptr;
118 const detail::RecordOps* _ops = nullptr;
119 RecordSink* _extSink = nullptr;
120 const RecordSource* _extSource = nullptr;
121 TableInfo _info{};
122 TableState _state;
123 };
124
131 class [[
132 =welder::weld,
133 =welder::doc("The common surface of every client-database table: decode "
134 "(read), encode (write), validation, and the preserved decode "
135 "state. Concrete tables add their typed records.")]] TableBase {
136 public:
137 [[=welder::doc("Decode a table file image."),
138 =welder::returns(
139 "nothing; raises on malformed input or a schema mismatch")]]
140 Result<void> read(std::span<const std::byte> data [[=welder::doc("the whole file content")]]) {
141 return _core.read(data);
142 }
143
144 [[=welder::doc("Load the table from a client filesystem."),
145 =welder::returns("nothing; raises when loading fails")]]
146 Result<void> read(fs::FileSystem& fs [[=welder::doc("the filesystem gateway")]],
147 const FileKey& key [[=welder::doc("the file to read")]]) {
148 return _core.read(fs, key);
149 }
150
151 [[=welder::doc(
152 "Serialize the table to a file image; a loaded table re-emits "
153 "the format it was read from. `policy` decides how keyless "
154 "encrypted sections are handled."),
155 =welder::returns("the file bytes")]]
157 [[=welder::doc("keyless-section handling (WDC only)")]]
159 return _core.write(policy);
160 }
161
162 [[=welder::doc("Serialize the table into a client filesystem (project "
163 "overlay); the target path's extension picks .dbc/.db2 in "
164 "the mixed eras."),
165 =welder::returns("nothing; raises when saving fails")]]
166 Result<void> write(fs::FileSystem& fs [[=welder::doc("the filesystem gateway")]],
167 const FileKey& key [[=welder::doc("the file to write")]],
168 EncryptedPolicy policy
169 [[=welder::doc("keyless-section handling (WDC only)")]]
171 return _core.write(fs, key, policy);
172 }
173
174 [[=welder::getter,
175 =welder::doc(
176 "The preserved string block the record string fields were decoded "
177 "from; offsets never move, write() appends new strings past its "
178 "end.")]]
179 const formats::StringBlock& strings() const { return _core.strings(); }
180
181 [[=welder::getter,
182 =welder::doc(
183 "The encrypted sections skipped on read: their records are not "
184 "in records, but the file re-writes them verbatim.")]]
185 const std::vector<EncryptedSection>& encryptedSections() const {
186 return _core.encryptedSections();
187 }
188
189 [[=welder::getter,
190 =welder::doc(
191 "Whether the whole table decoded — false when encrypted sections "
192 "were skipped.")]]
193 bool fullyDecoded() const { return _core.fullyDecoded(); }
194
195 [[nodiscard]]
196 [[=welder::doc(R"(
197 Check the logical integrity contracts the records must satisfy to
198 survive a write and load in the client: the primary key stays unique,
199 and no string holds an embedded NUL the string block would truncate.
200 write() never runs this.)"),
201 =welder::returns("every violated contract, in record order")]]
202 formats::ValidationReport validate() const { return _core.validate(); }
203
204 [[nodiscard]]
205 [[=welder::doc("Validate and raise on the first error instead of returning "
206 "a report — the assert-style face of validate()."),
207 =welder::returns("nothing; raises when validate() finds any error")]]
208 Result<void> ensureValid() const { return _core.ensureValid(); }
209
213 [[=welder::mark::exclude]]
214 const TableCore& core() const { return _core; }
215
216 protected:
217 TableCore _core;
218 };
219}
The owning byte buffer file contents are read into.
The decode target: the codecs build the record vector through this, one field at a time.
Definition codec.hpp:110
The encode source: the codecs read the record vector through this.
Definition codec.hpp:135
The welded supertype of every generated table class: the whole table surface — decode,...
bool fullyDecoded() const
Whether the whole table decoded — false when encrypted sections were skipped.
const TableCore & core() const
The erased engine (bindings build live record views off it).
Result< void > read(fs::FileSystem &fs, const FileKey &key)
Load the table from a client filesystem.
formats::ValidationReport validate() const
const formats::StringBlock & strings() const
The preserved string block the record string fields were decoded from; offsets never move,...
Result< void > ensureValid() const
Validate and raise on the first error instead of returning a report — the assert-style face of valida...
Result< void > write(fs::FileSystem &fs, const FileKey &key, EncryptedPolicy policy=EncryptedPolicy::Preserve) const
Serialize the table into a client filesystem (project overlay); the target path's extension picks ....
const std::vector< EncryptedSection > & encryptedSections() const
The encrypted sections skipped on read: their records are not in records, but the file re-writes them...
Result< void > read(std::span< const std::byte > data)
Decode a table file image.
Result< FileBuffer > write(EncryptedPolicy policy=EncryptedPolicy::Preserve) const
Serialize the table to a file image; a loaded table re-emits the format it was read from.
The erased engine of one table: identity + records access + preserved decode state,...
Result< FileBuffer > write(EncryptedPolicy policy) const
void wire(void *recordsVec, const detail::RecordOps *ops, TableInfo info)
Point the core at its owner's records vector and identity.
const TableInfo & info() const
The identity the codecs work from (empty-schema when unwired).
void * recordsVec() const
The owner's records vector + access facts (bindings use these to build live record views without re-t...
Result< void > read(std::span< const std::byte > data)
Result< void > ensureValid() const
const detail::RecordOps * ops() const
formats::ValidationReport validate() const
bool fullyDecoded() const
void rewire(RecordSink *sink, const RecordSource *source)
The external-wiring twin of rewire.
const std::vector< EncryptedSection > & encryptedSections() const
void rewire(void *recordsVec)
Re-point at the owner's vector after the owner was copied/moved (state and identity travel with the c...
void wire(RecordSink *sink, const RecordSource *source, TableInfo info)
Point the core at an EXTERNAL sink/source pair instead of a typed records vector — the generic column...
const formats::StringBlock & strings() const
The type-erased boundary between the templated Table<Record> facade (table.hpp) and the per-format co...
The error-handling vocabulary: ErrorCode, Error and the Result<T> alias every fallible wowlib operati...
File identity types: the strong FileDataID and the FileKey a read request travels as.
EncryptedPolicy
How write() treats a table that still holds keyless (undecryptable) encrypted sections.
Definition codec.hpp:33
@ Preserve
Re-emit the original image verbatim; edits are not applied.
Definition codec.hpp:34
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
Definition error.hpp:100
The record bridge: ErasedRecordSink / ErasedRecordSource implement the non-templated RecordSink / Rec...
StringBlock — the decoded representation of a chunk of zero-terminated strings (MOTX,...
The per-table identity a codec needs beyond the record data: the client version (format selection,...
Definition codec.hpp:58
The one thing that stays per-record: a table of thunks over the record VECTOR (only they know sizeof(...
The validation vocabulary: the severity scale, the single finding and the report validate() fills.