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