wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
string_block.hpp
Go to the documentation of this file.
1#pragma once
2
7
8#include <algorithm>
9#include <cstddef>
10#include <cstdint>
11#include <cstring>
12#include <span>
13#include <string>
14#include <string_view>
15#include <vector>
16
17#include <welder/vocabulary.hpp>
18
20#include <wowlib/core/error.hpp>
21
22namespace wowlib::formats {
23 class [[
24 =welder::weld,
25 =welder::doc(R"(
26 A chunk of zero-terminated strings (texture and model filenames, group
27 names), decoded into (offset, value) entries. Other chunks reference
28 entries by their byte offset in the on-disk blob; adding appends, and
29 existing offsets never move.)")
30 ]] StringBlock {
31 public:
32 struct [[=welder::doc(
33 "One decoded string and the blob byte offset other chunks "
34 "reference it by.")]] Entry {
35 [[=welder::doc("Byte offset of the string in the on-disk blob.")]]
36 std::uint32_t offset = 0;
37
38 [[=welder::doc("The string, without its terminator.")]]
39 std::string value;
40 };
42 StringBlock() = default;
43
49 [[=welder::mark::exclude]]
50 Result<void> read(std::span<const std::byte> payload) {
51 _entries.clear();
52 _blobSize = static_cast<std::uint32_t>(payload.size());
53 const auto* bytes = reinterpret_cast<const char*>(payload.data());
54 for (std::size_t pos = 0; pos < payload.size();) {
55 if (bytes[pos] == '\0') {
56 ++pos;
57 continue;
58 }
59 std::size_t end = pos;
60 while (end < payload.size() && bytes[end] != '\0') ++end;
61 _entries.push_back({static_cast<std::uint32_t>(pos), std::string{bytes + pos, end - pos}});
62 pos = end + 1;
63 }
64 return {};
65 }
66
70
72 [[=welder::mark::exclude]]
73 Result<void> write(FileBuffer& out) const {
74 const std::size_t base = out.size();
75 out.resize(base + _blobSize); // value-initialized: zero fill
76 for (const Entry& entry : _entries)
77 std::memcpy(out.data() + base + entry.offset, entry.value.data(), entry.value.size());
78 return {};
79 }
80
81 [[nodiscard]]
82 [[=welder::doc("The string at a byte offset another chunk stored; empty if "
83 "the offset lands on padding or out of range."),
84 =welder::returns("the referenced string (a mid-entry offset yields the "
85 "suffix)")]]
86 std::string_view at(std::uint32_t offset [[=welder::doc("byte offset into the on-disk blob")]]) const {
87 // entries are ordered by offset: find the last one starting at or before
88 const auto after = std::ranges::upper_bound(_entries, offset, {}, &Entry::offset);
89 if (after == _entries.begin()) return {};
90 const Entry& entry = *std::prev(after);
91 const std::uint32_t delta = offset - entry.offset;
92 if (delta >= entry.value.size()) return {};
93 return std::string_view{entry.value}.substr(delta);
94 }
95
96 [[=welder::doc("Append a string; existing offsets never move."),
97 =welder::returns("the offset the new string starts at - the value to "
98 "store in referencing chunks")]]
99 std::uint32_t add(std::string_view string
100 [[=welder::doc("the string to append (no embedded zero "
101 "bytes)")]]) {
102 const std::uint32_t offset = _blobSize;
103 _entries.push_back({offset, std::string{string}});
104 _blobSize += static_cast<std::uint32_t>(string.size()) + 1;
105 return offset;
106 }
107
108 [[nodiscard]]
109 [[=welder::doc("The decoded entries, in blob order."),
110 =welder::returns("the (offset, value) entries")]]
111 const std::vector<Entry>& entries() const {
112 return _entries;
113 }
115 [[nodiscard]]
116 [[=welder::getter, =welder::doc("Whether the on-disk blob holds any bytes.")
117 ]]
118 bool empty() const {
119 return _blobSize == 0;
120 }
121
122 [[=welder::getter,
123 =welder::doc("The on-disk blob size in bytes, trailing padding included.")
124 ]]
125 std::size_t size() const {
126 return _blobSize;
127 }
128
129 private:
130 std::vector<Entry> _entries;
131 std::uint32_t _blobSize = 0;
132 };
133}
The owning byte buffer file contents are read into.
std::size_t size() const
The on-disk blob size in bytes, trailing padding included.
bool empty() const
Whether the on-disk blob holds any bytes.
std::uint32_t add(std::string_view string)
Append a string; existing offsets never move.
std::string_view at(std::uint32_t offset) const
The string at a byte offset another chunk stored; empty if the offset lands on padding or out of rang...
const std::vector< Entry > & entries() const
The decoded entries, in blob order.
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...
The error-handling vocabulary: ErrorCode, Error and the Result<T> alias every fallible wowlib operati...
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::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
Definition buffer.hpp:16
One decoded string and the blob byte offset other chunks reference it by.
std::uint32_t offset
Byte offset of the string in the on-disk blob.
std::string value
The string, without its terminator.