SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
KV1Binary.cpp
Go to the documentation of this file.
1// ReSharper disable CppRedundantQualifier
2
3#include <kvpp/KV1Binary.h>
4
5#include <cstdlib>
6#include <format>
7#include <functional>
8#include <utility>
9
10#include <BufferStream.h>
11#include <kvpp/KV1Writer.h>
13#include <sourcepp/FS.h>
14
15using namespace kvpp;
16using namespace sourcepp;
17
18std::string_view KV1BinaryElement::getKey() const {
19 return this->key;
20}
21
22void KV1BinaryElement::setKey(std::string_view key_) {
23 this->key = key_;
24}
25
27 return this->value;
28}
29
31 this->value = std::move(value_);
32}
33
35 this->setValue(std::move(value_));
36 return *this;
37}
38
39bool KV1BinaryElement::hasChild(std::string_view childKey) const {
40 return !this->operator[](childKey).isInvalid();
41}
42
45 elem.setKey(key_);
46 elem.setValue(std::move(value_));
47 this->children.push_back(std::move(elem));
48 return this->children.back();
49}
50
52 return this->children.size();
53}
54
55uint64_t KV1BinaryElement::getChildCount(std::string_view childKey) const {
56 uint64_t count = 0;
57 for (const KV1BinaryElement& element : this->children) {
58 if (string::iequals(element.key, childKey)) {
59 ++count;
60 }
61 }
62 return count;
63}
64
65const std::vector<KV1BinaryElement>& KV1BinaryElement::getChildren() const {
66 return this->children;
67}
68
69std::vector<KV1BinaryElement> & KV1BinaryElement::getChildren() {
70 return this->children;
71}
72
73const KV1BinaryElement& KV1BinaryElement::operator[](unsigned int n) const {
74 return this->children.at(n);
75}
76
78 return this->children.at(n);
79}
80
81const KV1BinaryElement& KV1BinaryElement::operator[](std::string_view childKey) const {
82 return this->operator()(childKey);
83}
84
86 return this->operator()(childKey);
87}
88
89const KV1BinaryElement& KV1BinaryElement::operator()(std::string_view childKey) const {
90 for (const auto& element : this->children) {
91 if (string::iequals(element.getKey(), childKey)) {
92 return element;
93 }
94 }
95 return getInvalid();
96}
97
99 for (auto& element : this->children) {
100 if (string::iequals(element.getKey(), childKey)) {
101 return element;
102 }
103 }
104 return this->addChild(childKey);
105}
106
107const KV1BinaryElement& KV1BinaryElement::operator()(std::string_view childKey, unsigned int n) const {
108 unsigned int count = 0;
109 for (const auto& element : this->children) {
110 if (string::iequals(element.getKey(), childKey)) {
111 if (count == n) {
112 return element;
113 }
114 if (++count > n) {
115 break;
116 }
117 }
118 }
119 return getInvalid();
120}
121
122KV1BinaryElement& KV1BinaryElement::operator()(std::string_view childKey, unsigned int n) {
123 unsigned int count = 0;
124 for (auto& element: this->children) {
125 if (string::iequals(element.getKey(), childKey)) {
126 if (count == n) {
127 return element;
128 }
129 if (++count > n) {
130 break;
131 }
132 }
133 }
134 return this->addChild(childKey);
135}
136
137void KV1BinaryElement::removeChild(unsigned int n) {
138 if (this->children.size() > n) {
139 this->children.erase(this->children.begin() + n);
140 }
141}
142
143void KV1BinaryElement::removeChild(std::string_view childKey, int n) {
144 unsigned int count = 0;
145 for (auto element = this->children.begin(); element != this->children.end(); ++element) {
146 if (string::iequals(element->getKey(), childKey)) {
147 if (n < 0 || count == n) {
148 element = this->children.erase(element);
149 if (count == n) {
150 break;
151 }
152 }
153 ++count;
154 }
155 }
156}
157
159 return this == &getInvalid();
160}
161
163 static KV1BinaryElement element;
164 return element;
165}
166
167KV1BinaryElement::operator bool() const {
168 return !this->isInvalid();
169}
170
171KV1Binary::KV1Binary(std::span<const std::byte> kv1Data, bool use64BitPointers)
172 : KV1BinaryElement() {
173 if (kv1Data.empty()) {
174 return;
175 }
176 BufferStreamReadOnly stream{kv1Data};
177
178 std::function<void(std::vector<KV1BinaryElement>&)> recursiveReader;
179 recursiveReader = [use64BitPointers, &stream, &recursiveReader](std::vector<KV1BinaryElement>& elements) {
180 for (;;) {
181 const auto type = stream.read<KV1BinaryValueType>();
182 if (type == KV1BinaryValueType::COUNT) {
183 break;
184 }
185 KV1BinaryElement& element = elements.emplace_back();
186 element.setKey(stream.read_string());
187 switch (type) {
188 using enum KV1BinaryValueType;
189 case CHILDREN:
190 recursiveReader(element.getChildren());
191 break;
192 case STRING:
193 element.setValue(stream.read_string());
194 break;
195 case INT32:
196 element.setValue(stream.read<int32_t>());
197 break;
198 case FLOAT:
199 element.setValue(stream.read<float>());
200 break;
201 case POINTER: {
202 const auto data32 = stream.read<uint32_t>();
203 if (use64BitPointers && data32 == KV1BinaryPointer::MAGIC_64_BIT) {
204 const auto data64 = stream.read<uint64_t>();
205 element.setValue(KV1BinaryPointer{
206 .v64 = data64,
207 .is64Bit = true,
208 });
209 } else {
210 element.setValue(KV1BinaryPointer{
211 .v32 = data32,
212 .is64Bit = false,
213 });
214 }
215 break;
216 }
217 case WSTRING: {
218 const auto len = stream.read<uint16_t>();
219 std::wstring value;
220 for (int i = 0; i < len; i++) {
221 value += stream.read<char16_t>();
222 }
223 element.setValue(std::move(value));
224 break;
225 }
226 case COLOR_RGBA: {
227 math::Vec4ui8 value;
228 value[0] = stream.read<uint8_t>();
229 value[1] = stream.read<uint8_t>();
230 value[2] = stream.read<uint8_t>();
231 value[3] = stream.read<uint8_t>();
232 element.setValue(value);
233 break;
234 }
235 case UINT64:
236 element.setValue(stream.read<uint64_t>());
237 break;
238 default:
239 break;
240 }
241 }
242 };
243 recursiveReader(this->children);
244}
245
246KV1Binary::KV1Binary(const std::filesystem::path& kv1Path, bool use64BitPointers)
247 : KV1Binary(fs::readFileBuffer(kv1Path), use64BitPointers) {}
248
249std::vector<std::byte> KV1Binary::bake() const {
250 std::vector<std::byte> buffer;
251 BufferStream stream{buffer};
252
253 std::function<void(const std::vector<KV1BinaryElement>&)> recursiveWriter;
254 recursiveWriter = [&stream, &recursiveWriter](const std::vector<KV1BinaryElement>& elements){
255 for (const auto& element : elements) {
256 const auto type = static_cast<KV1BinaryValueType>(element.getValue().index());
257 stream
258 .write(type)
259 .write(element.getKey());
260 switch (type) {
261 using enum KV1BinaryValueType;
262 case CHILDREN:
263 recursiveWriter(element.getChildren());
264 break;
265 case STRING:
266 stream.write(*element.getValue<std::string>());
267 break;
268 case INT32:
269 stream.write(*element.getValue<int32_t>());
270 break;
271 case FLOAT:
272 stream.write(*element.getValue<float>());
273 break;
274 case POINTER: {
275 const auto pointer = *element.getValue<KV1BinaryPointer>();
276 if (pointer.is64Bit) {
277 stream
278 .write<uint32_t>(KV1BinaryPointer::MAGIC_64_BIT)
279 .write(pointer.v64);
280 } else {
281 stream.write(pointer.v32);
282 }
283 break;
284 }
285 case WSTRING: {
286 const auto val = *element.getValue<std::wstring>();
287 stream
288 .write<uint16_t>(val.size() + 1)
289 .write(reinterpret_cast<const char16_t*>(val.data()), val.size() * sizeof(char16_t) / sizeof(wchar_t))
290 .write<uint16_t>(0);
291 break;
292 }
293 case COLOR_RGBA: {
294 const auto val = *element.getValue<math::Vec4ui8>();
295 stream << val[0] << val[1] << val[2] << val[3];
296 break;
297 }
298 case UINT64:
299 stream.write(*element.getValue<uint64_t>());
300 break;
301 case COUNT:
302 break;
303 }
304 }
305 stream.write(KV1BinaryValueType::COUNT);
306 };
307 recursiveWriter(this->children);
308
309 buffer.resize(stream.size());
310 return buffer;
311}
312
313void KV1Binary::bake(const std::filesystem::path& kv1Path) const {
314 fs::writeFileBuffer(kv1Path, this->bake());
315}
316
317std::string KV1Binary::bakeText() const {
318 KV1Writer writer;
319 std::function<void(const KV1BinaryElement&, KV1ElementWritable<>&)> recurseBinaryKeyValues;
320 recurseBinaryKeyValues = [&recurseBinaryKeyValues](const KV1BinaryElement& element, KV1ElementWritable<>& kv) {
321 auto& child = kv.addChild(element.getKey());
322 switch (static_cast<KV1BinaryValueType>(element.getValue().index())) {
323 using enum KV1BinaryValueType;
324 case CHILDREN:
325 for (const auto& elementChild : element.getChildren()) {
326 recurseBinaryKeyValues(elementChild, child);
327 }
328 break;
329 case STRING:
330 child = *element.getValue<std::string>();
331 break;
332 case INT32:
333 child = *element.getValue<int32_t>();
334 break;
335 case FLOAT:
336 child = *element.getValue<float>();
337 break;
338 case POINTER: {
339 child = static_cast<int64_t>(static_cast<uint64_t>(*element.getValue<KV1BinaryPointer>()));
340 break;
341 }
342 case WSTRING: {
343 std::string value;
344 value.resize(std::wcstombs(nullptr, element.getValue<std::wstring>()->c_str(), 0));
345 std::wcstombs(value.data(), element.getValue<std::wstring>()->c_str(), value.size());
346 child = std::move(value);
347 break;
348 }
349 case COLOR_RGBA: {
350 const auto val = *element.getValue<math::Vec4ui8>();
351 child = std::format("{} {} {} {}", val[0], val[1], val[2], val[3]);
352 break;
353 }
354 case UINT64:
355 child = std::format("{}", *element.getValue<uint64_t>());
356 break;
357 case COUNT:
358 break;
359 }
360 };
361 recurseBinaryKeyValues(*this, writer);
362 return writer.bake();
363}
364
365void KV1Binary::bakeText(const std::filesystem::path& kv1Path) const {
366 fs::writeFileText(kv1Path, this->bakeText());
367}
const std::vector< KV1BinaryElement > & getChildren() const
Get the child elements of the element.
Definition KV1Binary.cpp:65
std::vector< KV1BinaryElement > children
Definition KV1Binary.h:200
const KV1BinaryElement & operator()(std::string_view childKey) const
Get the first child element of the element with the given key.
Definition KV1Binary.cpp:89
KV1BinaryElement & addChild(std::string_view key_, KV1BinaryValue value_={})
Add a child element to the element.
Definition KV1Binary.cpp:43
static const KV1BinaryElement & getInvalid()
void removeChild(unsigned int n)
Remove a child element from the element.
bool hasChild(std::string_view childKey) const
Check if the element has one or more children with the given name.
Definition KV1Binary.cpp:39
KV1BinaryValue value
Definition KV1Binary.h:199
void setKey(std::string_view key_)
Set the key associated with the element.
Definition KV1Binary.cpp:22
const KV1BinaryValue & getValue() const
Get the value associated with the element.
Definition KV1Binary.cpp:26
uint64_t getChildCount() const
Get the number of child elements.
Definition KV1Binary.cpp:51
KV1BinaryElement & operator=(KV1BinaryValue value_)
Set the value associated with the element.
Definition KV1Binary.cpp:34
void setValue(KV1BinaryValue value_)
Set the value associated with the element.
Definition KV1Binary.cpp:30
const KV1BinaryElement & operator[](unsigned int n) const
Get the child element of the element at the given index.
Definition KV1Binary.cpp:73
bool isInvalid() const
Check if the given element is invalid.
std::string_view getKey() const
Get the key associated with the element.
Definition KV1Binary.cpp:18
std::vector< std::byte > bake() const
KV1Binary(std::span< const std::byte > kv1Data={}, bool use64BitPointers=true)
std::string bakeText() const
std::string bake() const
Definition KV1Writer.h:365
Definition DMX.h:13
std::variant< std::monostate, std::string, int32_t, float, KV1BinaryPointer, std::wstring, sourcepp::math::Vec4ui8, uint64_t > KV1BinaryValue
Definition KV1Binary.h:43
KV1BinaryValueType
Definition KV1Binary.h:14
bool writeFileText(const std::filesystem::path &filepath, std::string_view text)
Definition FS.cpp:46
bool writeFileBuffer(const std::filesystem::path &filepath, std::span< const std::byte > buffer)
Definition FS.cpp:37
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:96
static constexpr auto MAGIC_64_BIT
Definition KV1Binary.h:27