wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
wdbc.cpp
Go to the documentation of this file.
1
6
7#include <wowlib/db/wdbc.hpp>
8
9#include <cstring>
10#include <format>
11#include <string>
12#include <unordered_map>
13
15
16namespace wowlib::db {
17 Result<void> readWdbc(const TableInfo& info, std::span<const std::byte> data, RecordSink& sink, TableState& state) {
18 if (data.size() < sizeof(WdbcHeader))
20 std::format("{}: {} bytes is too small for a WDBC header", info.name, data.size()));
21 WdbcHeader header;
22 std::memcpy(&header, data.data(), sizeof header);
23
24 const std::size_t stride = detail::recordStride(info.schema);
25 if (header.recordSize != stride)
27 std::format("{}: file record_size {} disagrees with the generated schema stride {}", info.name,
28 header.recordSize, stride));
29
30 const std::size_t expected = sizeof header + std::size_t{header.recordCount} * header.recordSize + header.
31 stringBlockSize;
32 if (data.size() != expected)
34 std::format(
35 "{}: {} bytes on disk, but the header describes {} ({} records of {} bytes "
36 "+ {} string bytes)", info.name, data.size(), expected, header.recordCount,
37 header.recordSize, header.stringBlockSize));
38
39 sink.clear();
40 state.reset();
41 if (auto r = state.strings.read(data.subspan(sizeof header + std::size_t{header.recordCount} * header.recordSize,
42 header.stringBlockSize)); !r) return r;
43
44 sink.reserve(header.recordCount);
45 state.stringOffsets.reserve(std::size_t{header.recordCount} * detail::stringSlotCount(info.schema));
46 for (std::uint32_t i = 0; i < header.recordCount; ++i) {
47 const std::size_t rec = sink.add();
48 const auto image = data.subspan(sizeof header + std::size_t{i} * stride, stride);
49 detail::decodeInlineRecord(info.schema, rec, image, sink, state);
50 }
51
52 state.sourceMagic = header.magic;
53 state.fieldCount = header.fieldCount;
54 state.recordSize = header.recordSize;
55 return {};
56 }
57
58 Result<FileBuffer> writeWdbc(const TableInfo& info, const RecordSource& source, const TableState& state) {
59 const std::size_t stride = detail::recordStride(info.schema);
60 WdbcHeader header;
61 header.recordCount = static_cast<std::uint32_t>(source.size());
62 header.fieldCount = state.sourceMagic != 0 ? state.fieldCount : detail::fieldSlotCount(info.schema);
63 header.recordSize = state.sourceMagic != 0 ? state.recordSize : static_cast<std::uint32_t>(stride);
64
65 detail::StringPool pool{state.strings};
66 FileBuffer out;
67 out.reserve(sizeof header + source.size() * stride + pool.block.size());
68 out.resize(sizeof header);
69 std::size_t cursor = 0;
70 for (std::size_t r = 0; r < source.size(); ++r)
71 detail::encodeInlineRecord(info.schema, source, r, out, pool, state, cursor);
72
73 header.stringBlockSize = static_cast<std::uint32_t>(pool.block.size());
74 if (auto w = pool.block.write(out); !w) return std::unexpected{w.error()};
75 std::memcpy(out.data(), &header, sizeof header);
76 return out;
77 }
78}
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
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...
Definition wdbc.cpp:58
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.
Definition wdbc.cpp:17
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
@ 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
The per-table identity a codec needs beyond the record data: the client version (format selection,...
Definition codec.hpp:58
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
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::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::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 20-byte WDBC header (wowdev.wiki/DBC).
Definition wdbc.hpp:28
std::uint32_t fieldCount
Expanded on-disk field slots per record.
Definition wdbc.hpp:31
std::uint32_t recordSize
Record stride in bytes.
Definition wdbc.hpp:32
std::uint32_t stringBlockSize
String block bytes after the records.
Definition wdbc.hpp:33
std::uint32_t magic
"WDBC".
Definition wdbc.hpp:29
std::uint32_t recordCount
Records in the record block.
Definition wdbc.hpp:30
A writable string block resolving values to byte-perfect offsets: the journaled original offset while...
WDBC — the client-database format of every pre-Cataclysm .dbc (and the .dbc leftovers of Cataclysm....