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;
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 const auto specialCmd = kvCommand["specialcmd"].getValue();
175 if (parser::text::isNumber(specialCmd)) {
176 string::toInt(specialCmd, reinterpret_cast<std::underlying_type_t<Command::Special>&>(special));
179 }
180 } else {
181 special = ::specialCmdFromString(specialCmd);
182 }
183 executable = kvCommand["run"].getValue();
184 arguments = kvCommand["parms"].getValue();
185 }
186 }
187}
188
189void CmdSeq::parseKeyValuesStrata(const std::filesystem::path& path) {
190 this->version = 0.2f;
191
192 const KV1 cmdSeq{fs::readFileText(path)};
193 for (const auto& kvSequence : cmdSeq["Command Sequences"].getChildren()) {
194 auto& [seqName, seqCommands] = this->sequences.emplace_back();
195 seqName = kvSequence.getKey();
196
197 for (const auto& kvCommand : kvSequence.getChildren()) {
198 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
199 string::toBool(kvCommand["enabled"].getValue(), enabled);
200 const auto specialCmd = kvCommand["special_cmd"].getValue();
201 if (parser::text::isNumber(specialCmd)) {
202 string::toInt(specialCmd, reinterpret_cast<std::underlying_type_t<Command::Special>&>(special));
205 }
206 } else {
207 special = ::specialCmdFromString(specialCmd);
208 }
209 executable = kvCommand["run"].getValue();
210 arguments = kvCommand["params"].getValue();
211 string::toBool(kvCommand["ensure_check"].getValue(), ensureFileExists);
212 pathToTheoreticallyExistingFile = kvCommand["ensure_fn"].getValue();
213 string::toBool(kvCommand["use_process_wnd"].getValue(), useProcessWindow);
214 string::toBool(kvCommand["no_wait"].getValue(), waitForKeypress);
215 }
216 }
217}
218
219std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() {
220 return this->sequences;
221}
222
223const std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() const {
224 return this->sequences;
225}
226
227std::vector<std::byte> CmdSeq::bakeBinary() const {
228 std::vector<std::byte> out;
229 BufferStream writer{out};
230
231 writer
232 .write("Worldcraft Command Sequences\r\n\x1a", 31)
233 .write<float>(this->getBinaryVersion())
234 .write<uint32_t>(this->getSequences().size());
235
236 for (const auto& [seqName, seqCommands] : this->getSequences()) {
237 writer
238 .write(seqName, true, 128)
239 .write<uint32_t>(seqCommands.size());
240
241 for (const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] : seqCommands) {
242 writer
243 .write<uint32_t>(enabled)
244 .write(special)
245 .write(executable, true, 260)
246 .write(arguments, true, 260)
247 .write<uint32_t>(true)
248 .write<uint32_t>(ensureFileExists)
249 .write(pathToTheoreticallyExistingFile, true, 260)
250 .write<uint32_t>(useProcessWindow);
251
252 if (this->getBinaryVersion() > 0.15f) {
253 writer.write<uint32_t>(waitForKeypress);
254 }
255 }
256 }
257
258 out.resize(writer.size());
259 return out;
260}
261
262std::vector<std::byte> CmdSeq::bakeKeyValuesHPP() const {
263 KV1Writer kv;
264 auto& kvFile = kv.addChild("Command Sequences");
265 for (const auto& [seqName, seqCommands] : this->getSequences()) {
266 auto& kvSequence = kvFile.addChild(seqName);
267 for (int i = 1; i <= seqCommands.size(); i++) {
268 const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands[i - 1];
269 auto& kvCommand = kvSequence.addChild(std::format("{}", i));
270 kvCommand["enable"] = enabled;
271 kvCommand["specialcmd"] = static_cast<int>(special);
272 kvCommand["run"] = executable;
273 kvCommand["parms"] = arguments;
274 }
275 }
276
277 const auto kvStr = kv.bake();
278 std::vector<std::byte> out;
279 out.resize(kvStr.length());
280 std::memcpy(out.data(), kvStr.data(), kvStr.length());
281 return out;
282}
283
284std::vector<std::byte> CmdSeq::bakeKeyValuesStrata() const {
285 KV1Writer kv;
286 auto& kvFile = kv.addChild("Command Sequences");
287 for (const auto& [seqName, seqCommands] : this->getSequences()) {
288 auto& kvSequence = kvFile.addChild(seqName);
289 for (int i = 1; i <= seqCommands.size(); i++) {
290 const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands[i - 1];
291 auto& kvCommand = kvSequence.addChild(std::format("{}", i));
292 kvCommand["enabled"] = enabled;
293 kvCommand["special_cmd"] = static_cast<int>(special);
294 kvCommand["run"] = executable;
295 kvCommand["params"] = arguments;
296 kvCommand["ensure_check"] = ensureFileExists;
297 kvCommand["ensure_fn"] = pathToTheoreticallyExistingFile;
298 kvCommand["use_process_wnd"] = useProcessWindow;
299 kvCommand["no_wait"] = waitForKeypress;
300 }
301 }
302
303 const auto kvStr = kv.bake();
304 std::vector<std::byte> out;
305 out.resize(kvStr.length());
306 std::memcpy(out.data(), kvStr.data(), kvStr.length());
307 return out;
308}
309
310std::vector<std::byte> CmdSeq::bake() const {
311 switch (this->type) {
312 using enum Type;
313 case INVALID:
314 return {};
315 case BINARY:
316 return this->bakeBinary();
317 case KEYVALUES_HPP:
318 return this->bakeKeyValuesHPP();
319 case KEYVALUES_STRATA:
320 return this->bakeKeyValuesStrata();
321 }
322 return {};
323}
324
325bool CmdSeq::bake(const std::filesystem::path& path) const {
326 FileStream writer{path};
327 if (!writer) {
328 return false;
329 }
330 writer.seek_out(0).write(this->bake());
331 return true;
332}
KV1ElementWritable & addChild(std::string_view key_, V value_={}, std::string_view conditional_="")
Definition KV1Writer.h:89
std::string bake() const
Definition KV1Writer.h:361
void parseKeyValuesHPP(const std::filesystem::path &path)
Definition CmdSeq.cpp:163
std::vector< std::byte > bake() const
Definition CmdSeq.cpp:310
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:189
CmdSeq(Type type_)
Definition CmdSeq.cpp:65
void setType(Type type_)
Definition CmdSeq.cpp:113
std::vector< std::byte > bakeKeyValuesStrata() const
Definition CmdSeq.cpp:284
std::vector< Sequence > & getSequences()
Definition CmdSeq.cpp:219
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:262
float version
Definition CmdSeq.h:91
std::vector< std::byte > bakeBinary() const
Definition CmdSeq.cpp:227
Definition DMX.h:13
std::string readFileText(const std::filesystem::path &filepath, std::size_t startOffset=0)
Definition FS.cpp:22
bool isNumber(char c)
If a char is a numerical character (0-9).
Definition Text.cpp:48
std::from_chars_result toBool(std::string_view number, bool &out, int base=10)
Definition String.cpp:246
std::from_chars_result toInt(std::string_view number, std::integral auto &out, int base=10)
Definition String.h:81
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:62
void toLower(std::string &input)
Definition String.cpp:166
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