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