SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
CmdSeq.cpp
Go to the documentation of this file.
1// ReSharper disable CppRedundantQualifier
2
3#include <toolpp/CmdSeq.h>
4
5#include <cstring>
6#include <format>
7
8#include <FileStream.h>
9#include <kvpp/kvpp.h>
10#include <sourcepp/String.h>
11
12using namespace kvpp;
13using namespace sourcepp;
14using namespace toolpp;
15
16namespace {
17
18CmdSeq::Command::Special specialCmdFromString(std::string_view specialCmd) {
19 using enum CmdSeq::Command::Special;
20 if (string::iequals(specialCmd, "change_dir")) {
21 return CHANGE_DIRECTORY;
22 }
23 if (string::iequals(specialCmd, "copy_file")) {
24 return COPY_FILE;
25 }
26 if (string::iequals(specialCmd, "delete_file")) {
27 return DELETE_FILE;
28 }
29 if (string::iequals(specialCmd, "rename_file")) {
30 return RENAME_FILE;
31 }
32 if (string::iequals(specialCmd, "copy_file_if_exists")) {
33 return COPY_FILE_IF_EXISTS;
34 }
35 return NONE;
36}
37
38} // namespace
39
41 switch (special) {
42 case Special::NONE:
43 break;
45 return "Change Directory";
47 return "Copy File";
49 return "Delete File";
51 return "Rename File";
53 return "Copy File If It Exists";
54 }
55 return "None";
56}
57
59 if (this->special != Command::Special::NONE) {
61 }
62 return this->executable;
63}
64
66 : type(type_)
67 , version(0.2f) {}
68
69CmdSeq::CmdSeq(const std::filesystem::path& cmdSeqPath)
70 : type(Type::INVALID)
71 , version(0.2f) {
72 {
73 FileStream reader{cmdSeqPath};
74 if (!reader) {
75 return;
76 }
77 if (const auto binStr = reader.seek_in(0).read_string(10); binStr == "Worldcraft") {
78 this->type = Type::BINARY;
79 } else {
80 auto kvStr = reader.seek_in(0).read_string(19);
81 string::toLower(kvStr);
82 if (kvStr == "\"command sequences\"") {
83 this->type = cmdSeqPath.extension() == ".cfg" ? Type::KEYVALUES_HPP : Type::KEYVALUES_STRATA;
84 } else {
85 return;
86 }
87 }
88 }
89 switch (this->type) {
90 using enum Type;
91 case INVALID:
92 break;
93 case BINARY:
94 this->parseBinary(cmdSeqPath);
95 break;
96 case KEYVALUES_HPP:
97 this->parseKeyValuesHPP(cmdSeqPath);
98 break;
99 case KEYVALUES_STRATA:
100 this->parseKeyValuesStrata(cmdSeqPath);
101 break;
102 }
103}
104
105CmdSeq::operator bool() const {
106 return this->type != Type::INVALID;
107}
108
110 return this->type;
111}
112
114 this->type = type_;
115}
116
118 return this->version;
119}
120
121void CmdSeq::setBinaryVersion(bool isV02) {
122 if (isV02) {
123 this->version = 0.2f;
124 } else {
125 this->version = 0.1f;
126 }
127}
128
129void CmdSeq::parseBinary(const std::filesystem::path& path) {
130 FileStream reader{path};
131 if (!reader) {
132 return;
133 }
134
135 reader.seek_in(31).read(this->version);
136
137 const auto sequenceCount = reader.read<uint32_t>();
138 for (uint32_t s = 0; s < sequenceCount; s++) {
139 auto& [seqName, seqCommands] = this->sequences.emplace_back();
140 seqName = reader.read_string(128);
141
142 const auto commandCount = reader.read<uint32_t>();
143 for (uint32_t c = 0; c < commandCount; c++) {
144 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
145 enabled = reader.read<int32_t>() & 0xFF;
146 special = reader.read<Command::Special>();
149 }
150 executable = reader.read_string(260);
151 arguments = reader.read_string(260);
152 reader.skip_in<int32_t>();
153 ensureFileExists = reader.read<int32_t>();
154 pathToTheoreticallyExistingFile = reader.read_string(260);
155 useProcessWindow = reader.read<int32_t>();
156 if (version > 0.15f) {
157 waitForKeypress = reader.read<int32_t>();
158 }
159 }
160 }
161}
162
163void CmdSeq::parseKeyValuesHPP(const std::filesystem::path& path) {
164 this->version = 0.2f;
165
166 const KV1 cmdSeq{fs::readFileText(path)};
167 for (const auto& kvSequence : cmdSeq["Command Sequences"].getChildren()) {
168 auto& [seqName, seqCommands] = this->sequences.emplace_back();
169 seqName = kvSequence.getKey();
170
171 for (const auto& kvCommand : kvSequence.getChildren()) {
172 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
173 string::toBool(kvCommand["enable"].getValue(), enabled);
174 if (
175 const auto specialCmd = kvCommand["specialcmd"].getValue();
176 string::toInt(specialCmd, reinterpret_cast<std::underlying_type_t<Command::Special>&>(special))
177 ) {
180 }
181 } else {
182 special = ::specialCmdFromString(specialCmd);
183 }
184 executable = kvCommand["run"].getValue();
185 arguments = kvCommand["parms"].getValue();
186 }
187 }
188}
189
190void CmdSeq::parseKeyValuesStrata(const std::filesystem::path& path) {
191 this->version = 0.2f;
192
193 const KV1 cmdSeq{fs::readFileText(path)};
194 for (const auto& kvSequence : cmdSeq["Command Sequences"].getChildren()) {
195 auto& [seqName, seqCommands] = this->sequences.emplace_back();
196 seqName = kvSequence.getKey();
197
198 for (const auto& kvCommand : kvSequence.getChildren()) {
199 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
200 string::toBool(kvCommand["enabled"].getValue(), enabled);
201 if (
202 const auto specialCmd = kvCommand["special_cmd"].getValue();
203 string::toInt(specialCmd, reinterpret_cast<std::underlying_type_t<Command::Special>&>(special))
204 ) {
207 }
208 } else {
209 special = ::specialCmdFromString(specialCmd);
210 }
211 executable = kvCommand["run"].getValue();
212 arguments = kvCommand["params"].getValue();
213 string::toBool(kvCommand["ensure_check"].getValue(), ensureFileExists);
214 pathToTheoreticallyExistingFile = kvCommand["ensure_fn"].getValue();
215 string::toBool(kvCommand["use_process_wnd"].getValue(), useProcessWindow);
216 string::toBool(kvCommand["no_wait"].getValue(), waitForKeypress);
217 }
218 }
219}
220
221std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() {
222 return this->sequences;
223}
224
225const std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() const {
226 return this->sequences;
227}
228
229std::vector<std::byte> CmdSeq::bakeBinary() const {
230 std::vector<std::byte> out;
231 BufferStream writer{out};
232
233 writer
234 .write("Worldcraft Command Sequences\r\n\x1a", 31)
235 .write<float>(this->getBinaryVersion())
236 .write<uint32_t>(this->getSequences().size());
237
238 for (const auto& [seqName, seqCommands] : this->getSequences()) {
239 writer
240 .write(seqName, true, 128)
241 .write<uint32_t>(seqCommands.size());
242
243 for (const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] : seqCommands) {
244 writer
245 .write<uint32_t>(enabled)
246 .write(special)
247 .write(executable, true, 260)
248 .write(arguments, true, 260)
249 .write<uint32_t>(true)
250 .write<uint32_t>(ensureFileExists)
251 .write(pathToTheoreticallyExistingFile, true, 260)
252 .write<uint32_t>(useProcessWindow);
253
254 if (this->getBinaryVersion() > 0.15f) {
255 writer.write<uint32_t>(waitForKeypress);
256 }
257 }
258 }
259
260 out.resize(writer.size());
261 return out;
262}
263
264std::vector<std::byte> CmdSeq::bakeKeyValuesHPP() const {
265 KV1Writer kv;
266 auto& kvFile = kv.addChild("Command Sequences");
267 for (const auto& [seqName, seqCommands] : this->getSequences()) {
268 auto& kvSequence = kvFile.addChild(seqName);
269 for (int i = 1; i <= seqCommands.size(); i++) {
270 const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands[i - 1];
271 auto& kvCommand = kvSequence.addChild(std::format("{}", i));
272 kvCommand["enable"] = enabled;
273 kvCommand["specialcmd"] = static_cast<int>(special);
274 kvCommand["run"] = executable;
275 kvCommand["parms"] = arguments;
276 }
277 }
278
279 const auto kvStr = kv.bake();
280 std::vector<std::byte> out;
281 out.resize(kvStr.length());
282 std::memcpy(out.data(), kvStr.data(), kvStr.length());
283 return out;
284}
285
286std::vector<std::byte> CmdSeq::bakeKeyValuesStrata() const {
287 KV1Writer kv;
288 auto& kvFile = kv.addChild("Command Sequences");
289 for (const auto& [seqName, seqCommands] : this->getSequences()) {
290 auto& kvSequence = kvFile.addChild(seqName);
291 for (int i = 1; i <= seqCommands.size(); i++) {
292 const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands[i - 1];
293 auto& kvCommand = kvSequence.addChild(std::format("{}", i));
294 kvCommand["enabled"] = enabled;
295 kvCommand["special_cmd"] = static_cast<int>(special);
296 kvCommand["run"] = executable;
297 kvCommand["params"] = arguments;
298 kvCommand["ensure_check"] = ensureFileExists;
299 kvCommand["ensure_fn"] = pathToTheoreticallyExistingFile;
300 kvCommand["use_process_wnd"] = useProcessWindow;
301 kvCommand["no_wait"] = waitForKeypress;
302 }
303 }
304
305 const auto kvStr = kv.bake();
306 std::vector<std::byte> out;
307 out.resize(kvStr.length());
308 std::memcpy(out.data(), kvStr.data(), kvStr.length());
309 return out;
310}
311
312std::vector<std::byte> CmdSeq::bake() const {
313 switch (this->type) {
314 using enum Type;
315 case INVALID:
316 return {};
317 case BINARY:
318 return this->bakeBinary();
319 case KEYVALUES_HPP:
320 return this->bakeKeyValuesHPP();
321 case KEYVALUES_STRATA:
322 return this->bakeKeyValuesStrata();
323 }
324 return {};
325}
326
327bool CmdSeq::bake(const std::filesystem::path& path) const {
328 FileStream writer{path};
329 if (!writer) {
330 return false;
331 }
332 writer.seek_out(0).write(this->bake());
333 return true;
334}
KV1ElementWritable & addChild(std::string_view key_, V value_={}, std::string_view conditional_="")
Definition KV1Writer.h:89
std::string bake() const
Definition KV1Writer.h:365
void parseKeyValuesHPP(const std::filesystem::path &path)
Definition CmdSeq.cpp:163
std::vector< std::byte > bake() const
Definition CmdSeq.cpp:312
float getBinaryVersion() const
Definition CmdSeq.cpp:117
void setBinaryVersion(bool isV02)
Definition CmdSeq.cpp:121
Type getType() const
Definition CmdSeq.cpp:109
void parseKeyValuesStrata(const std::filesystem::path &path)
Definition CmdSeq.cpp:190
CmdSeq(Type type_)
Definition CmdSeq.cpp:65
void setType(Type type_)
Definition CmdSeq.cpp:113
std::vector< std::byte > bakeKeyValuesStrata() const
Definition CmdSeq.cpp:286
std::vector< Sequence > & getSequences()
Definition CmdSeq.cpp:221
void parseBinary(const std::filesystem::path &path)
Definition CmdSeq.cpp:129
std::vector< Sequence > sequences
Definition CmdSeq.h:92
std::vector< std::byte > bakeKeyValuesHPP() const
Definition CmdSeq.cpp:264
float version
Definition CmdSeq.h:91
std::vector< std::byte > bakeBinary() const
Definition CmdSeq.cpp:229
Definition DMX.h:13
std::string readFileText(const std::filesystem::path &filepath, std::size_t startOffset=0)
Definition FS.cpp:22
bool toBool(std::string_view number, bool &out, int base=10)
Definition String.cpp:280
bool toInt(std::string_view number, std::integral auto &out, int base=10)
Definition String.h:141
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:96
void toLower(std::string &input)
Definition String.cpp:200
Definition CmdSeq.h:9
std::string executable
Definition CmdSeq.h:28
std::string getExecutableDisplayName() const
Definition CmdSeq.cpp:58
enum toolpp::CmdSeq::Command::Special special
static constexpr auto SPECIAL_COPY_FILE_IF_EXISTS_ALIAS
Definition CmdSeq.h:26
static std::string getSpecialDisplayNameFor(Special special)
Definition CmdSeq.cpp:40