SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
dmxpp.cpp
Go to the documentation of this file.
1#include <dmxpp/dmxpp.h>
2
3#include <cstdio>
4#include <functional>
5
6#include <BufferStream.h>
7
8using namespace dmxpp;
9
10DMX::DMX(const std::byte* dmxData, std::size_t dmxSize) {
11 if (!dmxData || !dmxSize) {
12 return;
13 }
14
15 BufferStreamReadOnly stream{dmxData, dmxSize};
16
17 auto header = stream.read_string();
18 if (header.length() < 37) {
19 // Minimum possible header length - early "binary_v2" version, "keyvalues2_v1" unsupported
20 return;
21 }
22 char encodingTypeData[64];
23 int32_t encodingVersionData;
24 char formatTypeData[64];
25 int32_t formatVersionData;
26#ifdef _WIN32
27 sscanf_s(header.c_str(), "<!-- dmx encoding %63s %i format %63s %i -->\n", encodingTypeData, 64, &encodingVersionData, formatTypeData, 64, &formatVersionData);
28#else
29 std::sscanf(header.c_str(), "<!-- dmx encoding %s %i format %s %i -->\n", encodingTypeData, &encodingVersionData, formatTypeData, &formatVersionData);
30#endif
31
32 this->encodingType = encodingTypeData;
33 this->encodingVersion = encodingVersionData;
34 this->formatType = formatTypeData;
35 this->formatVersion = formatVersionData;
36
37 // DMX v9+
38 if (this->encodingVersion >= 9) {
39 // This format adds some stuff which really messes with v5 and below
40 // Difficult to support both, and very easy to be lazy
41 return;
42 }
43
44 if (this->encodingType == Format::BINARY) {
45 this->opened = this->openBinary(stream);
46 } else if (this->encodingType == Format::TEXT) {
47 this->opened = this->openText(dmxData, dmxSize);
48 }
49}
50
51DMX::DMX(const unsigned char* dmxData, std::size_t dmxSize)
52 : DMX(reinterpret_cast<const std::byte*>(dmxData), dmxSize) {}
53
54DMX::DMX(const std::vector<std::byte>& dmxData)
55 : DMX(dmxData.data(), dmxData.size()) {}
56
57DMX::DMX(const std::vector<unsigned char>& dmxData)
58 : DMX(dmxData.data(), dmxData.size()) {}
59
60DMX::operator bool() const {
61 return this->opened;
62}
63
64std::string_view DMX::getFormatType() const {
65 return this->formatType;
66}
67
69 return this->formatVersion;
70}
71
72std::string_view DMX::getEncodingType() const {
73 return this->encodingType;
74}
75
77 return this->encodingVersion;
78}
79
80const std::vector<DMXElement>& DMX::getElements() const {
81 return this->elements;
82}
83
84bool DMX::openText(const std::byte* dmxData, std::size_t dmxSize) {
85 return false; // todo: text decoding
86}
87
88bool DMX::openBinary(BufferStream& stream) {
89 // Version-specific conditionals
90 const bool stringListLengthIsShort = this->encodingVersion < 4;
91 const bool stringListIndicesAreShort = this->encodingVersion < 5;
92 const bool elementNamesAreStoredInStringList = this->encodingVersion >= 4;
93 const bool stringValuesAreStoredInStringList = this->encodingVersion >= 4;
94
95 // String list
96 std::vector<std::string> stringList;
97 uint32_t stringCount;
98 if (stringListLengthIsShort) {
99 stringCount = stream.read<uint16_t>();
100 } else {
101 stringCount = stream.read<uint32_t>();
102 }
103 stringList.reserve(stringCount);
104 for (int i = 0; i < stringCount; i++) {
105 stringList.push_back(stream.read_string());
106 }
107
108 // Read a string index and get the string from the list
109 const auto readStringFromIndex = [stringListIndicesAreShort, &stringList](BufferStream& stream_) {
110 if (stringListIndicesAreShort) {
111 return stringList.at(stream_.read<uint16_t>());
112 }
113 return stringList.at(stream_.read<uint32_t>());
114 };
115
116 // Read elements
117 const int elementCount = stream.read<int32_t>();
118 for (int i = 0; i < elementCount; i++) {
119 auto& [type, name, guid, attributes] = this->elements.emplace_back();
120 type = readStringFromIndex(stream);
121 if (elementNamesAreStoredInStringList) {
122 name = readStringFromIndex(stream);
123 } else {
124 name = stream.read_string();
125 }
126 guid = stream.read_bytes<16>();
127 }
128
129 // Helper to read a value for an attribute
130 std::function<Value::Generic(BufferStream&, Value::ID, bool)> readValue;
131 readValue = [&readValue, &readStringFromIndex](BufferStream& stream_, Value::ID type, bool useStringList) -> Value::Generic {
132 const auto readArrayValue = [&readValue]<typename T>(BufferStream& reader, Value::ID type_) {
133 std::vector<T> out;
134 auto size = reader.read<uint32_t>();
135 out.reserve(size);
136 for (int i = 0; i < size; i++) {
137 out.push_back(std::get<T>(readValue(reader, Value::arrayIDToInnerID(type_), true)));
138 }
139 return out;
140 };
141 switch (type) {
142 using enum Value::ID;
143 case INVALID:
144 return Value::Invalid{};
145 case ELEMENT: {
146 Value::Element value;
147 value.index = stream_.read<uint32_t>();
148 if (value.index == -2) {
149 // Parse the ASCII GUID if it's a stub
150 value.stubGUID = stream_.read_string();
151 }
152 return value;
153 }
154 case INT:
155 return stream_.read<int32_t>();
156 case FLOAT:
157 return stream_.read<float>();
158 case BOOL:
159 return stream_.read<bool>();
160 case STRING:
161 return useStringList ? readStringFromIndex(stream_) : stream_.read_string();
162 case BYTEARRAY:
163 return stream_.read_bytes(stream_.read<uint32_t>());
164 case TIME:
165 return Value::Time{static_cast<float>(static_cast<double>(stream_.read<int32_t>()) / 10000.0)};
166 case COLOR:
167 return stream_.read<Value::Color>();
168 case VECTOR2:
169 return stream_.read<Value::Vector2>();
170 case VECTOR3:
171 case EULER_ANGLE:
172 return stream_.read<Value::Vector3>();
173 case VECTOR4:
174 case QUATERNION:
175 return stream_.read<Value::Vector4>();
176 case MATRIX_4X4:
177 return stream_.read<Value::Matrix4x4>();
178 case ARRAY_ELEMENT:
179 return readArrayValue.operator()<Value::Element>(stream_, type);
180 case ARRAY_INT:
181 return readArrayValue.operator()<int32_t>(stream_, type);
182 case ARRAY_FLOAT:
183 return readArrayValue.operator()<float>(stream_, type);
184 case ARRAY_BOOL:
185 return readArrayValue.operator()<bool>(stream_, type);
186 case ARRAY_STRING:
187 return readArrayValue.operator()<std::string>(stream_, type);
188 case ARRAY_BYTEARRAY:
189 return readArrayValue.operator()<std::vector<std::byte>>(stream_, type);
190 case ARRAY_TIME:
191 return readArrayValue.operator()<Value::Time>(stream_, type);
192 case ARRAY_COLOR:
193 return readArrayValue.operator()<Value::Color>(stream_, type);
194 case ARRAY_VECTOR2:
195 return readArrayValue.operator()<Value::Vector2>(stream_, type);
196 case ARRAY_VECTOR3:
197 case ARRAY_EULER_ANGLE:
198 return readArrayValue.operator()<Value::Vector3>(stream_, type);
199 case ARRAY_VECTOR4:
200 case ARRAY_QUATERNION:
201 return readArrayValue.operator()<Value::Vector4>(stream_, type);
202 case ARRAY_MATRIX_4X4:
203 return readArrayValue.operator()<Value::Matrix4x4>(stream_, type);
204 }
205 return 0; // Dummy to get it to compile
206 };
207
208 // Read element attributes
209 for (auto& [type, name, guid, attributes] : this->elements) {
210 const int attributeCount = stream.read<int32_t>();
211 for (int i = 0; i < attributeCount; i++) {
212 auto& [attrName, attrType, attrValue] = attributes.emplace_back();
213 attrName = readStringFromIndex(stream);
214 attrType = Value::byteToID(stream.read<std::byte>());
215 attrValue = readValue(stream, attrType, stringValuesAreStoredInStringList);
216 }
217 }
218
219 return true;
220}
int getFormatVersion() const
Definition: dmxpp.cpp:68
DMX(const std::byte *dmxData, std::size_t dmxSize)
Definition: dmxpp.cpp:10
bool openBinary(BufferStream &stream)
Definition: dmxpp.cpp:88
const std::vector< DMXElement > & getElements() const
Definition: dmxpp.cpp:80
std::string_view getEncodingType() const
Definition: dmxpp.cpp:72
bool openText(const std::byte *dmxData, std::size_t dmxSize)
Definition: dmxpp.cpp:84
std::string_view getFormatType() const
Definition: dmxpp.cpp:64
int getEncodingVersion() const
Definition: dmxpp.cpp:76
constexpr std::string_view TEXT
Definition: dmxpp.h:11
constexpr std::string_view BINARY
Definition: dmxpp.h:12
constexpr ID arrayIDToInnerID(ID id)
Definition: Value.h:128
sourcepp::math::Mat4x4f Matrix4x4
Definition: Value.h:47
std::variant< Invalid, Element, int32_t, float, bool, std::string, std::vector< std::byte >, Time, Color, Vector2, Vector3, Vector4, Matrix4x4, std::vector< Element >, std::vector< int32_t >, std::vector< float >, std::vector< bool >, std::vector< std::string >, std::vector< std::vector< std::byte > >, std::vector< Time >, std::vector< Color >, std::vector< Vector2 >, std::vector< Vector3 >, std::vector< Vector4 >, std::vector< Matrix4x4 > > Generic
Definition: Value.h:80
sourcepp::math::Vec4f Vector4
Definition: Value.h:41
sourcepp::math::Vec2f Vector2
Definition: Value.h:37
sourcepp::math::Vec3f Vector3
Definition: Value.h:39
constexpr ID byteToID(std::byte id)
Definition: Value.h:124
Definition: dmxpp.h:7
std::string stubGUID
Definition: Value.h:21
uint32_t index
Definition: Value.h:20