12#include <unordered_map>
23 if ((_vec && _ops) || (_extSink && _extSource))
return {};
25 "this table base carries no records: construct a concrete "
26 "table class, not the TableBase supertype");
30 if (
auto w = _requireWired(); !w)
return w;
31 if (data.size() <
sizeof(std::uint32_t))
33 std::format(
"{}: {} bytes is too small for a client database", _info.name, data.size()));
34 std::uint32_t magic = 0;
35 std::memcpy(&magic, data.data(),
sizeof magic);
43 "{}: magic '{}' is not a client-database format wowlib supports for " "client {}.{}.{}.{}",
45 _info.version.major, _info.version.minor, _info.version.patch, _info.version.build));
49 if (
auto w = _requireWired(); !w)
return w;
50 const auto data =
fs.readFile(key);
51 if (!data)
return std::unexpected{data.error()};
56 if (
auto w = _requireWired(); !w)
return std::unexpected{w.error()};
57 if (_state.sourceMagic != 0)
return _writeAs(_state.sourceMagic, policy);
58 const auto magic = _freshMagic(std::nullopt);
59 if (!magic)
return std::unexpected{magic.error()};
60 return _writeAs(*magic, policy);
64 if (
auto w = _requireWired(); !w)
return w;
68 std::format(
"saving table {} needs a path for the file key", _info.name));
70 if (_state.sourceMagic != 0)
return _writeAs(_state.sourceMagic, policy);
71 const auto magic = _freshMagic(*resolved.
path);
72 if (!magic)
return std::unexpected{magic.error()};
73 return _writeAs(*magic, policy);
75 if (!data)
return std::unexpected{data.error()};
76 if (
auto r =
fs.addFile(*resolved.
path, *data); !r)
return std::unexpected{r.error()};
82 if (!_requireWired())
return report;
83 const std::span<const Column> schema = _info.schema;
89 const bool hasId = std::ranges::any_of(schema, [](
const Column& c) {
return c.
isId; });
90 std::unordered_map<std::uint32_t, std::size_t> firstSeen;
91 const std::size_t n = source.
size();
92 for (std::size_t r = 0; r < n && !report.
full(); ++r) {
93 for (std::size_t c = 0; c < schema.size(); ++c) {
94 const Column& column = schema[c];
96 for (std::size_t e = 0; e < slots; ++e)
97 if (source.
getString(r, c, e).find(
'\0') != std::string_view::npos)
100 ? std::format(
"records[{}].{}[{}]", r, column.
nameView(), e)
101 : std::format(
"records[{}].{}", r, column.
nameView()),
102 "the string holds an embedded NUL; the string block "
103 "terminates entries with it, so it would read back truncated");
107 const std::uint32_t recordId = source.
idOf(r);
108 if (
const auto [at, fresh] = firstSeen.try_emplace(recordId, r); !fresh)
109 report.
addError(std::format(
"records[{}]", r),
111 "duplicate id {} (already used by records[{}]); the " "client indexes records by id",
112 recordId, at->second));
118 std::uint32_t TableCore::_db2MagicForVersion()
const {
129 if (path->ends_with(
".dbc"))
return WdbcMagic;
130 if (path->ends_with(
".db2"))
133 std::format(
"{}: .db2 does not exist before Cataclysm", _info.name))
134 :
Result<std::uint32_t>{_db2MagicForVersion()};
137 return _db2MagicForVersion();
141 ErasedRecordSource erased{_vec, _ops};
142 const RecordSource& source = _extSource ? *_extSource :
static_cast<const RecordSource&
>(erased);
RecordSink over a live std::vector<Record>& — the decode target.
RecordSource over a const std::vector<Record>& — the encode source.
The decode target: the codecs build the record vector through this, one field at a time.
The encode source: the codecs read the record vector through this.
virtual std::size_t size() const =0
virtual std::uint32_t idOf(std::size_t record) const =0
virtual std::string_view getString(std::size_t record, std::size_t column, std::size_t element) const =0
Result< FileBuffer > write(EncryptedPolicy policy) const
Result< void > read(std::span< const std::byte > data)
formats::ValidationReport validate() const
Named ClientVersion constants for the exact client builds format features appeared (or vanished) at —...
The runtime facade over the static compositions — the primary welder binding surface of the fs layer.
FourCC chunk identifiers: compile-time conversion of the four-letter codes to the host integers chunk...
constexpr ClientVersion Legion
legion (any 7.x client).
constexpr ClientVersion Cata
Cataclysm (any 4.x client).
constexpr ClientVersion BfA
Battle for Azeroth (any 8.x client).
constexpr bool isWdcMagic(std::uint32_t magic)
Whether magic names a format of the WDC family this codec speaks.
constexpr std::uint32_t Wdc3Magic
The WDC3 magic as memcpy'd off the file front (the bytes "WDC3").
constexpr std::uint32_t Wdc4Magic
The WDC4 magic as memcpy'd off the file front (the bytes "WDC4").
Result< void > readWdc(const TableInfo &info, std::span< const std::byte > data, RecordSink &sink, TableState &state)
Decode a WDC-family image onto the schema: every unencrypted section's records (inline fields via the...
Result< FileBuffer > writeWdc(std::uint32_t magic, const TableInfo &info, const RecordSource &source, const TableState &state, EncryptedPolicy policy)
Encode a canonical image of flavor magic: one unencrypted section, integers bitpacked to their minimu...
constexpr std::uint32_t Wdc1Magic
The WDC1 magic as memcpy'd off the file front (the bytes "WDC1").
constexpr std::uint32_t Wdc5Magic
The WDC5 magic as memcpy'd off the file front (the bytes "WDC5").
constexpr std::uint32_t WdbcMagic
The WDBC magic as memcpy'd off the file front (the bytes "WDBC").
Result< FileBuffer > writeWdbc(const TableInfo &info, const RecordSource &source, const TableState &state)
Encode a WDBC image; the string block preserves decoded offsets and appends new/changed strings (byte...
constexpr std::uint32_t Wdb2Magic
The WDB2 magic as memcpy'd off the file front (the bytes "WDB2").
EncryptedPolicy
How write() treats a table that still holds keyless (undecryptable) encrypted sections.
Result< void > readWdbc(const TableInfo &info, std::span< const std::byte > data, RecordSink &sink, TableState &state)
Decode a WDBC image: 20-byte header, fixed-stride records, string block.
Result< FileBuffer > writeWdb2(const TableInfo &info, const RecordSource &source, const TableState &state)
Encode a WDB2 image; header identity and the verbatim index/copy blocks are re-emitted.
Result< void > readWdb2(const TableInfo &info, std::span< const std::byte > data, RecordSink &sink, TableState &state)
Decode a WDB2 image: header, optional id-index block, fixed-stride records, string block,...
std::expected< T, Error > Result
Every fallible wowlib operation returns Result<T>; bindings translate the error branch into a target-...
std::unexpected< Error > makeError(ErrorCode code, std::string message, std::uint32_t nativeError=0)
Shorthand for constructing the error branch of a Result.
@ InvalidEntityState
An entity's members disagree (e.g.
@ PathNotResolvable
No FileDataID is known for the given path (listfile miss).
@ TableMagicUnknown
A client-database magic wowlib does not support for the requested version.
@ NotImplemented
Placeholder during phased implementation.
@ NotSupported
Operation not supported by this backend/provider.
@ TableTruncated
A client-database header, record block or satellite block overruns the file.
std::optional< std::string > path
The canonical client-internal path, if known.
One column of a record schema, derived from a record member by reflection (typed records) or from the...
constexpr std::string_view nameView() const
The member spelling as a view.
bool isId
Whether this is the table's primary key ($id$).
constexpr std::size_t stringSlots() const
The string-block references the column stores per record (the width of the engine's per-record offset...
ClientVersion version
The record's client version.
TableCore + TableBase — the non-templated heart of every client-database table, and the ONE welded su...
WDB2 — the .db2 format of the Cataclysm..WoD MPQ/early-CASC era (4.0.1.12911 .
WDBC — the client-database format of every pre-Cataclysm .dbc (and the .dbc leftovers of Cataclysm....
The WDC-family codec: the non-templated entry points Table<Record> dispatches to for every column-com...