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)
171 : KV1BinaryElement() {
172 if (kv1Data.empty()) {
173 return;
174 }
175 BufferStreamReadOnly stream{kv1Data};
176
177 std::function<void(std::vector<KV1BinaryElement>&)> recursiveReader;
178 recursiveReader = [&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 element.setValue(stream.read<KV1BinaryPointer>());
202 break;
203 case WSTRING: {
204 const auto len = stream.read<uint16_t>();
205 std::wstring value;
206 for (int i = 0; i < len; i++) {
207 value += stream.read<char16_t>();
208 }
209 element.setValue(std::move(value));
210 break;
211 }
212 case COLOR_RGBA: {
213 math::Vec4ui8 value;
214 value[0] = stream.read<uint8_t>();
215 value[1] = stream.read<uint8_t>();
216 value[2] = stream.read<uint8_t>();
217 value[3] = stream.read<uint8_t>();
218 element.setValue(value);
219 break;
220 }
221 case UINT64:
222 element.setValue(stream.read<uint64_t>());
223 break;
224 default:
225 break;
226 }
227 }
228 };
229 recursiveReader(this->children);
230}
231
232KV1Binary::KV1Binary(const std::filesystem::path& kv1Path)
233 : KV1Binary(fs::readFileBuffer(kv1Path)) {}
234
235std::vector<std::byte> KV1Binary::bake() const {
236 std::vector<std::byte> buffer;
237 BufferStream stream{buffer};
238
239 std::function<void(const std::vector<KV1BinaryElement>&)> recursiveWriter;
240 recursiveWriter = [&stream, &recursiveWriter](const std::vector<KV1BinaryElement>& elements){
241 for (const auto& element : elements) {
242 const auto type = static_cast<KV1BinaryValueType>(element.getValue().index());
243 stream
244 .write(type)
245 .write(element.getKey());
246 switch (type) {
247 using enum KV1BinaryValueType;
248 case CHILDREN:
249 recursiveWriter(element.getChildren());
250 break;
251 case STRING:
252 stream.write(*element.getValue<std::string>());
253 break;
254 case INT32:
255 stream.write(*element.getValue<int32_t>());
256 break;
257 case FLOAT:
258 stream.write(*element.getValue<float>());
259 break;
260 case POINTER:
261 stream.write(*element.getValue<KV1BinaryPointer>());
262 break;
263 case WSTRING: {
264 const auto val = *element.getValue<std::wstring>();
265 stream
266 .write<uint16_t>(val.size() + 1)
267 .write(reinterpret_cast<const char16_t*>(val.data()), val.size() * sizeof(char16_t) / sizeof(wchar_t))
268 .write<uint16_t>(0);
269 break;
270 }
271 case COLOR_RGBA: {
272 const auto val = *element.getValue<math::Vec4ui8>();
273 stream << val[0] << val[1] << val[2] << val[3];
274 break;
275 }
276 case UINT64:
277 stream.write(*element.getValue<uint64_t>());
278 break;
279 case COUNT:
280 break;
281 }
282 }
283 stream.write(KV1BinaryValueType::COUNT);
284 };
285 recursiveWriter(this->children);
286
287 buffer.resize(stream.size());
288 return buffer;
289}
290
291void KV1Binary::bake(const std::filesystem::path& kv1Path) const {
292 fs::writeFileBuffer(kv1Path, this->bake());
293}
294
295std::string KV1Binary::bakeText() const {
296 KV1Writer writer;
297 std::function<void(const KV1BinaryElement&, KV1ElementWritable<>&)> recurseBinaryKeyValues;
298 recurseBinaryKeyValues = [&recurseBinaryKeyValues](const KV1BinaryElement& element, KV1ElementWritable<>& kv) {
299 auto& child = kv.addChild(element.getKey());
300 switch (static_cast<KV1BinaryValueType>(element.getValue().index())) {
301 using enum KV1BinaryValueType;
302 case CHILDREN:
303 for (const auto& elementChild : element.getChildren()) {
304 recurseBinaryKeyValues(elementChild, child);
305 }
306 break;
307 case STRING:
308 child = *element.getValue<std::string>();
309 break;
310 case INT32:
311 child = *element.getValue<int32_t>();
312 break;
313 case FLOAT:
314 child = *element.getValue<float>();
315 break;
316 case POINTER:
317 child = static_cast<int64_t>(*element.getValue<KV1BinaryPointer>());
318 break;
319 case WSTRING:
320 break;
321 case COLOR_RGBA: {
322 const auto val = *element.getValue<math::Vec4ui8>();
323 child = std::format("{} {} {} {}", val[0], val[1], val[2], val[3]);
324 break;
325 }
326 case UINT64:
327 child = std::format("{}", *element.getValue<uint64_t>());
328 break;
329 case COUNT:
330 break;
331 }
332 };
333 recurseBinaryKeyValues(*this, writer);
334 return writer.bake();
335}
336
337void KV1Binary::bakeText(const std::filesystem::path& kv1Path) const {
338 fs::writeFileText(kv1Path, this->bakeText());
339}
const std::vector< KV1BinaryElement > & getChildren() const
Get the child elements of the element.
Definition KV1Binary.cpp:64
std::vector< KV1BinaryElement > children
Definition KV1Binary.h:185
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:184
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={})
std::string bakeText() const
std::string bake() const
Definition KV1Writer.h:357
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:28
KV1BinaryValueType
Definition KV1Binary.h:14
uint32_t KV1BinaryPointer
Definition KV1Binary.h:26
bool writeFileText(const std::filesystem::path &filepath, std::string_view text)
Definition FS.cpp:34
bool writeFileBuffer(const std::filesystem::path &filepath, std::span< const std::byte > buffer)
Definition FS.cpp:25
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:61