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