wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
wdb2.cpp
Go to the documentation of this file.
1
5
6#include <wowlib/db/wdb2.hpp>
7
8#include <cstring>
9#include <format>
10
12
13namespace wowlib::db {
14 Result<void> readWdb2(const TableInfo& info, std::span<const std::byte> data, RecordSink& sink, TableState& state) {
15 if (data.size() < sizeof(Wdb2Header))
17 std::format("{}: {} bytes is too small for a WDB2 header", info.name, data.size()));
18 Wdb2Header header;
19 std::memcpy(&header, data.data(), sizeof header);
20
21 const std::size_t stride = detail::recordStride(info.schema);
22 if (header.recordSize != stride)
24 std::format("{}: file record_size {} disagrees with the generated schema stride {}", info.name,
25 header.recordSize, stride));
26 if (header.maxId != 0 && header.maxId < header.minId)
28 std::format("{}: WDB2 max_id {} below min_id {}", info.name, header.maxId, header.minId));
29
30 const std::size_t indexBytes = header.maxId != 0
31 ? (std::size_t{header.maxId} - header.minId + 1) * Wdb2IndexEntryBytes
32 : 0;
33 const std::size_t recordsAt = sizeof header + indexBytes;
34 const std::size_t expected = recordsAt + std::size_t{header.recordCount} * header.recordSize + header.
35 stringBlockSize + header.copyTableSize;
36 if (data.size() != expected)
37 return makeError(ErrorCode::TableTruncated, std::format(
38 "{}: {} bytes on disk, but the header describes {} ({} records of {} bytes "
39 "+ {} index + {} string + {} copy-table bytes)", info.name, data.size(), expected,
40 header.recordCount, header.recordSize, indexBytes, header.stringBlockSize,
41 header.copyTableSize));
42
43 sink.clear();
44 state.reset();
45 if (auto r = state.strings.read(data.subspan(recordsAt + std::size_t{header.recordCount} * header.recordSize,
46 header.stringBlockSize)); !r) return r;
47
48 sink.reserve(header.recordCount);
49 state.stringOffsets.reserve(std::size_t{header.recordCount} * detail::stringSlotCount(info.schema));
50 for (std::uint32_t i = 0; i < header.recordCount; ++i) {
51 const std::size_t rec = sink.add();
52 const auto image = data.subspan(recordsAt + std::size_t{i} * stride, stride);
53 detail::decodeInlineRecord(info.schema, rec, image, sink, state);
54 }
55
56 const auto indexBlock = data.subspan(sizeof header, indexBytes);
57 state.wdb2Index.assign(indexBlock.begin(), indexBlock.end());
58 const auto copyBlock = data.subspan(expected - header.copyTableSize, header.copyTableSize);
59 state.wdb2Copy.assign(copyBlock.begin(), copyBlock.end());
60 const auto* hb = reinterpret_cast<const std::byte*>(&header);
61 state.wdb2Header.assign(hb, hb + sizeof header);
62 state.sourceMagic = header.magic;
63 state.fieldCount = header.fieldCount;
64 state.recordSize = header.recordSize;
65 return {};
66 }
67
68 Result<FileBuffer> writeWdb2(const TableInfo& info, const RecordSource& source, const TableState& state) {
69 const std::size_t stride = detail::recordStride(info.schema);
70 const bool loaded = state.sourceMagic == Wdb2Magic;
71
72 Wdb2Header header{};
73 if (loaded && state.wdb2Header.size() == sizeof header) std::memcpy(
74 &header, state.wdb2Header.data(), sizeof header);
75
76 if (loaded && !state.wdb2Index.empty() && source.size() != header.recordCount)
78 std::format(
79 "{}: the WDB2 id-index block cannot be rebuilt yet; adding or removing "
80 "records of an indexed table is unsupported (had {}, have {})", info.name,
81 header.recordCount, source.size()));
82
83 header.magic = Wdb2Magic;
84 header.recordCount = static_cast<std::uint32_t>(source.size());
85 header.fieldCount = loaded ? state.fieldCount : detail::fieldSlotCount(info.schema);
86 header.recordSize = loaded ? state.recordSize : static_cast<std::uint32_t>(stride);
87 if (!loaded) header.build = info.version.build;
88
89 detail::StringPool pool{state.strings};
90 FileBuffer out;
91 out.reserve(
92 sizeof header + state.wdb2Index.size() + source.size() * stride + pool.block.size() + state.wdb2Copy.size());
93 out.resize(sizeof header);
94 out.insert(out.end(), state.wdb2Index.begin(), state.wdb2Index.end());
95 std::size_t cursor = 0;
96 for (std::size_t r = 0; r < source.size(); ++r)
97 detail::encodeInlineRecord(info.schema, source, r, out, pool, state, cursor);
98
99 header.stringBlockSize = static_cast<std::uint32_t>(pool.block.size());
100 if (auto w = pool.block.write(out); !w) return std::unexpected{w.error()};
101 out.insert(out.end(), state.wdb2Copy.begin(), state.wdb2Copy.end());
102 header.copyTableSize = static_cast<std::uint32_t>(state.wdb2Copy.size());
103 std::memcpy(out.data(), &header, sizeof header);
104 return out;
105 }
106}
The decode target: the codecs build the record vector through this, one field at a time.
Definition codec.hpp:110
virtual std::size_t add()=0
virtual void reserve(std::size_t n)=0
virtual void clear()=0
The encode source: the codecs read the record vector through this.
Definition codec.hpp:135
virtual std::size_t size() const =0
std::size_t size() const
The on-disk blob size in bytes, trailing padding included.
Result< void > read(std::span< const std::byte > payload)
Decode a chunk payload (the serializer's read hook): every run of non-zero bytes becomes an entry at ...
Result< void > write(FileBuffer &out) const
Blobify (the serializer's write hook): append size() zero bytes to out, then lay each entry's charact...
Shared codec internals for the fixed-stride formats (WDBC, WDB2): the string pool that reproduces byt...
std::size_t stringSlotCount(std::span< const Column > schema)
The per-record string-reference count of schema (the offset journal width).
Definition codec.hpp:161
void decodeInlineRecord(std::span< const Column > schema, std::size_t record, std::span< const std::byte > image, RecordSink &sink, TableState &state)
Decode one fixed-stride record's inline fields into sink and journal its string offsets (record then ...
void encodeInlineRecord(std::span< const Column > schema, const RecordSource &source, std::size_t record, FileBuffer &out, StringPool &pool, const TableState &state, std::size_t &cursor)
Encode one fixed-stride record's inline fields from source, appending to out and resolving strings th...
std::size_t recordStride(std::span< const Column > schema)
The fixed record stride (WDBC/WDB2) of schema.
Definition codec.hpp:147
std::uint32_t fieldSlotCount(std::span< const Column > schema)
The expanded on-disk field-slot count of schema (WDBC/WDB2 fieldCount).
Definition codec.hpp:154
constexpr std::size_t Wdb2IndexEntryBytes
The per-id index block entry stride when maxId != 0: int32 indices[maxId - minId + 1] followed by int...
Definition wdb2.hpp:50
constexpr std::uint32_t Wdb2Magic
The WDB2 magic as memcpy'd off the file front (the bytes "WDB2").
Definition wdb2.hpp:27
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.
Definition wdb2.cpp:68
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,...
Definition wdb2.cpp:14
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
@ InvalidEntityState
An entity's members disagree (e.g.
Definition error.hpp:40
@ SchemaMismatch
A client-database record layout disagrees with the generated WoWDBDefs schema.
Definition error.hpp:46
@ TableTruncated
A client-database header, record block or satellite block overruns the file.
Definition error.hpp:44
std::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
Definition buffer.hpp:16
std::uint32_t build
Exact client build number, e.g.
The per-table identity a codec needs beyond the record data: the client version (format selection,...
Definition codec.hpp:58
ClientVersion version
The record's client version.
Definition codec.hpp:59
std::span< const Column > schema
The record schema (schemaOf<Record>()).
Definition codec.hpp:61
std::string_view name
The WoWDBDefs table name.
Definition codec.hpp:60
The preserved decode state a table carries between read() and write() so a WDBC/WDB2 write is byte-pe...
Definition codec.hpp:68
std::vector< std::byte > wdb2Copy
Preserved WDB2 copy table, verbatim.
Definition codec.hpp:80
formats::StringBlock strings
Preserved string block; offsets never move.
Definition codec.hpp:72
void reset()
Reset the per-read state before decoding a fresh image.
Definition codec.hpp:91
std::vector< std::byte > wdb2Header
Preserved WDB2 header identity bytes.
Definition codec.hpp:78
std::vector< std::uint32_t > stringOffsets
The original string-block offset of every string field, row-major in record then schema order — the b...
Definition codec.hpp:76
std::vector< std::byte > wdb2Index
Preserved WDB2 id-index block, verbatim.
Definition codec.hpp:79
std::uint32_t recordSize
Preserved header recordSize.
Definition codec.hpp:71
std::uint32_t fieldCount
Preserved header fieldCount (not always derivable).
Definition codec.hpp:70
std::uint32_t sourceMagic
The magic read() sniffed; 0 for a fresh table.
Definition codec.hpp:69
The 48-byte WDB2 header (wowdev.wiki/DB2).
Definition wdb2.hpp:30
std::uint32_t maxId
Highest record id; non-zero engages the index block.
Definition wdb2.hpp:40
std::uint32_t minId
Lowest record id; 0 when no index block.
Definition wdb2.hpp:39
std::uint32_t recordSize
Record stride in bytes.
Definition wdb2.hpp:34
std::uint32_t recordCount
Records in the record block.
Definition wdb2.hpp:32
std::uint32_t fieldCount
Expanded on-disk field slots per record.
Definition wdb2.hpp:33
std::uint32_t stringBlockSize
String block bytes after the records.
Definition wdb2.hpp:35
std::uint32_t copyTableSize
Trailing copy-table bytes (id pairs).
Definition wdb2.hpp:42
std::uint32_t build
Client build the file was generated for.
Definition wdb2.hpp:37
std::uint32_t magic
"WDB2".
Definition wdb2.hpp:31
A writable string block resolving values to byte-perfect offsets: the journaled original offset while...
WDB2 — the .db2 format of the Cataclysm..WoD MPQ/early-CASC era (4.0.1.12911 .