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\"") {
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;
96 this->parseKeyValuesStrata(cmdSeqPath);
97 break;
98 }
99}
100
101CmdSeq::operator bool() const {
102 return this->type != Type::INVALID;
103}
104
106 return this->type;
107}
108
110 this->type = type_;
111}
112
113float CmdSeq::getVersion() const {
114 return this->version;
115}
116
117void CmdSeq::setVersion(bool isV02) {
118 if (isV02) {
119 this->version = 0.2f;
120 } else {
121 this->version = 0.1f;
122 }
123}
124
125void CmdSeq::parseBinary(const std::filesystem::path& path) {
126 FileStream reader{path};
127 if (!reader) {
128 return;
129 }
130
131 reader.seek_in(31).read(this->version);
132
133 const auto sequenceCount = reader.read<uint32_t>();
134 for (uint32_t s = 0; s < sequenceCount; s++) {
135 auto& [seqName, seqCommands] = this->sequences.emplace_back();
136 seqName = reader.read_string(128);
137
138 const auto commandCount = reader.read<uint32_t>();
139 for (uint32_t c = 0; c < commandCount; c++) {
140 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
141 enabled = reader.read<int32_t>() & 0xFF;
142 special = reader.read<Command::Special>();
145 }
146 executable = reader.read_string(260);
147 arguments = reader.read_string(260);
148 reader.skip_in<int32_t>();
149 ensureFileExists = reader.read<int32_t>();
150 pathToTheoreticallyExistingFile = reader.read_string(260);
151 useProcessWindow = reader.read<int32_t>();
152 if (version > 0.15f) {
153 waitForKeypress = reader.read<int32_t>();
154 }
155 }
156 }
157}
158
159void CmdSeq::parseKeyValuesStrata(const std::filesystem::path& path) {
160 this->version = 0.2f;
161
162 const KV1 cmdSeq{fs::readFileText(path)};
163 for (const auto& kvSequence : cmdSeq["Command Sequences"].getChildren()) {
164 auto& [seqName, seqCommands] = this->sequences.emplace_back();
165 seqName = kvSequence.getKey();
166
167 for (const auto& kvCommand : kvSequence.getChildren()) {
168 auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands.emplace_back();
169 string::toBool(kvCommand["enabled"].getValue(), enabled);
170 const auto specialCmd = kvCommand["special_cmd"].getValue();
171 if (parser::text::isNumber(specialCmd)) {
172 string::toInt(specialCmd, reinterpret_cast<std::underlying_type_t<Command::Special>&>(special));
175 }
176 } else {
177 special = ::specialCmdFromString(specialCmd);
178 }
179 executable = kvCommand["run"].getValue();
180 arguments = kvCommand["params"].getValue();
181 string::toBool(kvCommand["ensure_check"].getValue(), ensureFileExists);
182 pathToTheoreticallyExistingFile = kvCommand["ensure_fn"].getValue();
183 string::toBool(kvCommand["use_process_wnd"].getValue(), useProcessWindow);
184 string::toBool(kvCommand["no_wait"].getValue(), waitForKeypress);
185 }
186 }
187}
188
189std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() {
190 return this->sequences;
191}
192
193const std::vector<CmdSeq::Sequence>& CmdSeq::getSequences() const {
194 return this->sequences;
195}
196
197std::vector<std::byte> CmdSeq::bakeBinary() const {
198 std::vector<std::byte> out;
199 BufferStream writer{out};
200
201 writer
202 .write("Worldcraft Command Sequences\r\n\x1a", 31)
203 .write<float>(this->getVersion())
204 .write<uint32_t>(this->getSequences().size());
205
206 for (const auto& [seqName, seqCommands] : this->getSequences()) {
207 writer
208 .write(seqName, true, 128)
209 .write<uint32_t>(seqCommands.size());
210
211 for (const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] : seqCommands) {
212 writer
213 .write<uint32_t>(enabled)
214 .write(special)
215 .write(executable, true, 260)
216 .write(arguments, true, 260)
217 .write<uint32_t>(true)
218 .write<uint32_t>(ensureFileExists)
219 .write(pathToTheoreticallyExistingFile, true, 260)
220 .write<uint32_t>(useProcessWindow);
221
222 if (this->getVersion() > 0.15f) {
223 writer.write<uint32_t>(waitForKeypress);
224 }
225 }
226 }
227
228 out.resize(writer.size());
229 return out;
230}
231
232std::vector<std::byte> CmdSeq::bakeKeyValuesStrata() const {
233 KV1Writer kv;
234 auto& kvFile = kv.addChild("Command Sequences");
235 for (const auto& [seqName, seqCommands] : this->getSequences()) {
236 auto& kvSequence = kvFile.addChild(seqName);
237 for (int i = 1; i <= seqCommands.size(); i++) {
238 const auto& [enabled, special, executable, arguments, ensureFileExists, pathToTheoreticallyExistingFile, useProcessWindow, waitForKeypress] = seqCommands[i - 1];
239 auto& kvCommand = kvSequence.addChild(std::to_string(i));
240 kvCommand["enabled"] = enabled;
241 kvCommand["special_cmd"] = static_cast<int>(special);
242 kvCommand["run"] = executable;
243 kvCommand["params"] = arguments;
244 kvCommand["ensure_check"] = ensureFileExists;
245 kvCommand["ensure_fn"] = pathToTheoreticallyExistingFile;
246 kvCommand["use_process_wnd"] = useProcessWindow;
247 kvCommand["no_wait"] = waitForKeypress;
248 }
249 }
250
251 const auto kvStr = kv.bake();
252 std::vector<std::byte> out;
253 out.resize(kvStr.length());
254 std::memcpy(out.data(), kvStr.data(), kvStr.length());
255 return out;
256}
257
258std::vector<std::byte> CmdSeq::bake() const {
259 switch (this->type) {
260 using enum Type;
261 case INVALID:
262 return {};
263 case BINARY:
264 return this->bakeBinary();
265 case KEYVALUES_STRATA:
266 return this->bakeKeyValuesStrata();
267 }
268 return {};
269}
270
271bool CmdSeq::bake(const std::filesystem::path& path) const {
272 FileStream writer{path};
273 if (!writer) {
274 return false;
275 }
276 writer.seek_out(0).write(this->bake());
277 return true;
278}
KV1ElementWritable & addChild(std::string_view key_, V value_={}, std::string_view conditional_="")
Definition KV1Writer.h:85
std::string bake() const
Definition KV1Writer.h:357
std::vector< std::byte > bake() const
Definition CmdSeq.cpp:258
Type getType() const
Definition CmdSeq.cpp:105
void parseKeyValuesStrata(const std::filesystem::path &path)
Definition CmdSeq.cpp:159
CmdSeq(Type type_)
Definition CmdSeq.cpp:64
float getVersion() const
Definition CmdSeq.cpp:113
void setType(Type type_)
Definition CmdSeq.cpp:109
std::vector< std::byte > bakeKeyValuesStrata() const
Definition CmdSeq.cpp:232
void setVersion(bool isV02)
Definition CmdSeq.cpp:117
std::vector< Sequence > & getSequences()
Definition CmdSeq.cpp:189
void parseBinary(const std::filesystem::path &path)
Definition CmdSeq.cpp:125
std::vector< Sequence > sequences
Definition CmdSeq.h:87
float version
Definition CmdSeq.h:86
std::vector< std::byte > bakeBinary() const
Definition CmdSeq.cpp:197
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