Skip to content

Commit fe0addc

Browse files
fix(cli): return error code on failure (closes #226)
1 parent 93e6c1f commit fe0addc

File tree

1 file changed

+58
-48
lines changed

1 file changed

+58
-48
lines changed

src/cli/Main.cpp

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,16 @@ namespace {
7777
return NO_COMPRESS;
7878
}
7979

80+
#define VPKEDIT_ERROR_TYPE(name) class vpkedit_##name##_error : public std::runtime_error { public: using runtime_error::runtime_error; }
81+
VPKEDIT_ERROR_TYPE(load);
82+
VPKEDIT_ERROR_TYPE(invalid_argument);
83+
VPKEDIT_ERROR_TYPE(runtime);
84+
8085
/// Extract file(s) from an existing pack file
8186
void extract(const argparse::ArgumentParser& cli, const std::string& inputPath) {
8287
auto packFile = PackFile::open(inputPath);
8388
if (!packFile) {
84-
std::cerr << "Could not open the pack file at \"" << inputPath << "\": it failed to load!" << std::endl;
89+
throw vpkedit_load_error{"Could not open the pack file at \"" + inputPath + "\": it failed to load!"};
8590
}
8691

8792
auto extractPath = cli.get(ARG_S(EXTRACT));
@@ -92,14 +97,13 @@ void extract(const argparse::ArgumentParser& cli, const std::string& inputPath)
9297
outputPath = cli.get(ARG_S(OUTPUT));
9398
}
9499
if (!std::filesystem::exists(outputPath) || !std::filesystem::is_directory(outputPath)) {
95-
std::cerr << "Output location must be an existing directory!" << std::endl;
96-
return;
100+
throw vpkedit_invalid_argument_error{"Output location must be an existing directory!"};
97101
}
98102
if (!packFile->extractAll(outputPath)) {
99-
std::cerr
100-
<< "Could not extract pack file contents to \"" << outputPath << "\"!\n"
101-
<< "Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
102-
<< std::endl;
103+
throw vpkedit_runtime_error{
104+
"Could not extract pack file contents to \"" + outputPath + "\"!\n"
105+
"Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
106+
};
103107
}
104108
std::cout << "Extracted pack file contents under \"" << outputPath << "\"." << std::endl;
105109
} else if (extractPath.ends_with('/')) {
@@ -109,14 +113,13 @@ void extract(const argparse::ArgumentParser& cli, const std::string& inputPath)
109113
outputPath = cli.get(ARG_S(OUTPUT));
110114
}
111115
if (!std::filesystem::exists(outputPath) || !std::filesystem::is_directory(outputPath)) {
112-
std::cerr << "Output location must be an existing directory!" << std::endl;
113-
return;
116+
throw vpkedit_invalid_argument_error{"Output location must be an existing directory!"};
114117
}
115118
if (!packFile->extractDirectory(extractPath, outputPath)) {
116-
std::cerr
117-
<< "Some or all files were unable to be extracted to \"" << outputPath << "\"!\n"
118-
<< "Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
119-
<< std::endl;
119+
throw vpkedit_runtime_error{
120+
"Some or all files were unable to be extracted to \"" + outputPath + "\"!\n"
121+
"Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
122+
};
120123
}
121124
std::cout << "Extracted directory under \"" << outputPath << "\"." << std::endl;
122125
} else {
@@ -127,14 +130,13 @@ void extract(const argparse::ArgumentParser& cli, const std::string& inputPath)
127130
}
128131
auto entry = packFile->findEntry(extractPath);
129132
if (!entry) {
130-
std::cerr << "Could not find file at \"" << extractPath << "\" in the pack file!" << std::endl;
131-
return;
133+
throw vpkedit_runtime_error{"Could not find file at \"" + extractPath + "\" in the pack file!"};
132134
}
133135
if (!packFile->extractEntry(extractPath, outputPath)) {
134-
std::cerr
135-
<< "Could not extract file at \"" << extractPath << "\" to \"" << outputPath << "\"!\n"
136-
<< "Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
137-
<< std::endl;
136+
throw vpkedit_runtime_error{
137+
"Could not extract file at \"" + extractPath + "\" to \"" + outputPath + "\"!\n"
138+
"Please ensure that a game or another application is not using the file, and that you have sufficient permissions to write to the output location."
139+
};
138140
}
139141
std::cout << "Extracted file at \"" << extractPath << "\" to \"" << outputPath << "\"." << std::endl;
140142
}
@@ -144,16 +146,15 @@ void extract(const argparse::ArgumentParser& cli, const std::string& inputPath)
144146
void fileTree(const std::string& inputPath) {
145147
auto packFile = PackFile::open(inputPath);
146148
if (!packFile) {
147-
std::cerr << "Could not open the pack file at \"" << inputPath << "\": it failed to load!" << std::endl;
149+
throw vpkedit_load_error{"Could not open the pack file at \"" + inputPath + "\": it failed to load!"};
148150
}
149151
::prettyPrintPackFile(packFile);
150152
}
151153

152154
/// Generate private/public key files
153155
void generateKeyPair(const std::string& inputPath) {
154156
if (!VPK::generateKeyPairFiles(inputPath)) {
155-
std::cerr << "Failed to generate public/private key files at \"" << inputPath << ".[private/public]key.vdf\"!" << std::endl;
156-
return;
157+
throw vpkedit_runtime_error{"Failed to generate public/private key files at \"" + inputPath + ".[private/public]key.vdf\"!"};
157158
}
158159
std::cout << "Generated private/public key files at \"" << inputPath << ".[private/public]key.vdf\"." << std::endl;
159160
std::cout << "Remember to NEVER share a private key! The public key is fine to share." << std::endl;
@@ -165,15 +166,13 @@ void edit(const argparse::ArgumentParser& cli, const std::string& inputPath) {
165166
if (cli.is_used(ARG_S(OUTPUT))) {
166167
outputPath = cli.get(ARG_S(OUTPUT));
167168
if (!std::filesystem::exists(outputPath) || !std::filesystem::is_directory(outputPath)) {
168-
std::cerr << "Output location must be an existing directory!" << std::endl;
169-
return;
169+
throw vpkedit_invalid_argument_error{"Output location must be an existing directory!"};
170170
}
171171
}
172172

173173
auto packFile = PackFile::open(inputPath);
174174
if (!packFile) {
175-
std::cerr << "Could not open the pack file at \"" << inputPath << "\": it failed to load!" << std::endl;
176-
return;
175+
throw vpkedit_load_error{"Could not open the pack file at \"" + inputPath + "\": it failed to load!"};
177176
}
178177

179178
auto compressionMethod = ::compressionMethodStringToCompressionType(cli.get<std::string>(ARG_S(COMPRESSION_METHOD)));
@@ -183,9 +182,10 @@ void edit(const argparse::ArgumentParser& cli, const std::string& inputPath) {
183182
if (cli.is_used(ARG_L(REMOVE_FILE))) {
184183
auto path = cli.get(ARG_L(REMOVE_FILE));
185184
if (!packFile->removeEntry(path)) {
186-
std::cerr
187-
<< "Unable to remove file at \"" << path << "\" from the pack file!\n"
188-
<< "Check the file exists in the pack file and the path is spelled correctly." << std::endl;
185+
throw vpkedit_runtime_error{
186+
"Unable to remove file at \"" + path + "\" from the pack file!\n"
187+
"Check the file exists in the pack file and the path is spelled correctly."
188+
};
189189
} else {
190190
std::cout << "Removed file at \"" << path << "\" from the pack file." << std::endl;
191191
}
@@ -194,9 +194,10 @@ void edit(const argparse::ArgumentParser& cli, const std::string& inputPath) {
194194
if (cli.is_used(ARG_L(REMOVE_DIR))) {
195195
auto path = cli.get(ARG_L(REMOVE_DIR));
196196
if (!packFile->removeDirectory(path)) {
197-
std::cerr
198-
<< "Unable to remove directory at \"" << path << "\" from the pack file!\n"
199-
<< "Check the directory exists in the pack file and the path is spelled correctly." << std::endl;
197+
throw vpkedit_runtime_error{
198+
"Unable to remove directory at \"" + path + "\" from the pack file!\n"
199+
"Check the directory exists in the pack file and the path is spelled correctly."
200+
};
200201
} else {
201202
std::cout << "Removed directory at \"" << path << "\" from the pack file." << std::endl;
202203
}
@@ -205,9 +206,9 @@ void edit(const argparse::ArgumentParser& cli, const std::string& inputPath) {
205206
if (cli.is_used(ARG_L(ADD_FILE))) {
206207
auto args = cli.get<std::vector<std::string>>(ARG_L(ADD_FILE));
207208
if (!std::filesystem::exists(args[0])) {
208-
std::cerr << "File at \"" << args[0] << "\" does not exist! Cannot add to pack file." << std::endl;
209+
throw vpkedit_invalid_argument_error{"File at \"" + args[0] + "\" does not exist! Cannot add to pack file."};
209210
} else if (!std::filesystem::is_regular_file(args[0])) {
210-
std::cerr << "Path \"" << args[0] << "\" does not point to a file! Cannot add to pack file." << std::endl;
211+
throw vpkedit_invalid_argument_error{"Path \"" + args[0] + "\" does not point to a file! Cannot add to pack file."};
211212
} else {
212213
packFile->addEntry(args[1], args[0], {});
213214
std::cout << "Added file at \"" << args[0] << "\" to the pack file at path \"" << args[1] << "\"." << std::endl;
@@ -217,9 +218,9 @@ void edit(const argparse::ArgumentParser& cli, const std::string& inputPath) {
217218
if (cli.is_used(ARG_L(ADD_DIR))) {
218219
auto args = cli.get<std::vector<std::string>>(ARG_L(ADD_DIR));
219220
if (!std::filesystem::exists(args[0])) {
220-
std::cerr << "Directory at \"" << args[0] << "\" does not exist! Cannot add to pack file." << std::endl;
221+
throw vpkedit_invalid_argument_error{"Directory at \"" + args[0] + "\" does not exist! Cannot add to pack file."};
221222
} else if (!std::filesystem::is_directory(args[0])) {
222-
std::cerr << "Path \"" << args[0] << "\" does not point to a directory! Cannot add to pack file." << std::endl;
223+
throw vpkedit_invalid_argument_error{"Path \"" + args[0] + "\" does not point to a directory! Cannot add to pack file."};
223224
} else {
224225
packFile->addDirectory(args[1], args[0]);
225226
std::cout << "Added directory at \"" << args[0] << "\" to the pack file at path \"" << args[1] << "\"." << std::endl;
@@ -240,13 +241,15 @@ void sign(const argparse::ArgumentParser& cli, const std::string& inputPath) {
240241

241242
if (saveToDir) {
242243
std::cerr << "Warning: Signed VPKs that contain files will not be treated as signed by the Source engine!" << std::endl;
243-
std::cerr << "Remove the " << ARG_S(SINGLE_FILE) << " / " << ARG_L(SINGLE_FILE) << " parameter for best results." << std::endl;
244+
std::cerr << "Rebuild the VPK and remove the " << ARG_S(SINGLE_FILE) << " / " << ARG_L(SINGLE_FILE) << " parameter for best results." << std::endl;
244245
}
245246

246247
auto vpk = VPK::open(inputPath);
247248
if (!vpk || !dynamic_cast<VPK*>(vpk.get())->sign(signPath)) {
248-
std::cerr << "Failed to sign VPK using private key file at \"" << signPath << "\"!" << std::endl;
249-
std::cerr << "Check that the file exists and it contains both the private key and public key." << std::endl;
249+
throw vpkedit_runtime_error{
250+
"Failed to sign VPK using private key file at \"" + signPath + "\"!\n"
251+
"Check that the file exists and it contains both the private key and public key."
252+
};
250253
} else {
251254
std::cout << "Signed VPK using private key at \"" << signPath << "\"." << std::endl;
252255
}
@@ -256,8 +259,7 @@ void sign(const argparse::ArgumentParser& cli, const std::string& inputPath) {
256259
void verify(const argparse::ArgumentParser& cli, const std::string& inputPath) {
257260
auto packFile = PackFile::open(inputPath);
258261
if (!packFile) {
259-
std::cerr << "Could not open the pack file at \"" << inputPath << "\": it failed to load!" << std::endl;
260-
return;
262+
throw vpkedit_load_error{"Could not open the pack file at \"" + inputPath + "\": it failed to load!"};
261263
}
262264

263265
if (cli.is_used(ARG_L(VERIFY_CHECKSUMS))) {
@@ -369,11 +371,10 @@ void pack(const argparse::ArgumentParser& cli, const std::string& inputPath) {
369371
packFile = WAD3::create(outputPath);
370372
}
371373
if (!packFile) {
372-
std::cerr << "Failed to create pack file!" << std::endl;
373374
if (!noProgressBar) {
374375
bar->mark_as_completed();
375376
}
376-
return;
377+
throw vpkedit_runtime_error{"Failed to create pack file!"};
377378
}
378379

379380
packFile->addDirectory("", inputPath, [compressionMethod, compressionLevel, &preloadExtensions, saveToDir](const std::string& path) -> EntryOptions {
@@ -610,23 +611,32 @@ int main(int argc, const char* const* argv) {
610611
::verify(cli, inputPath);
611612
}
612613
if (!foundAction) {
613-
throw std::runtime_error{"No action taken! Add some arguments to clarify your intent."};
614+
throw vpkedit_invalid_argument_error{"No action taken! Add some arguments to clarify your intent."};
614615
}
615616
}
616617
} else if (cli.get<bool>(ARG_L(GEN_KEYPAIR))) {
617618
::generateKeyPair(inputPath);
618619
} else {
619-
throw std::runtime_error{"Given path does not exist!"};
620+
throw vpkedit_invalid_argument_error{"Given path does not exist!"};
620621
}
622+
} catch (const vpkedit_runtime_error& e) {
623+
std::cerr << e.what() << std::endl;
624+
return EXIT_FAILURE;
625+
} catch (const vpkedit_load_error& e) {
626+
std::cerr << e.what() << std::endl;
627+
return EXIT_FAILURE;
628+
} catch (const vpkedit_invalid_argument_error& e) {
629+
std::cerr << e.what() << '\n' << std::endl;
630+
std::cerr << "Run with --help to see more information about how to use this program." << std::endl;
631+
return EXIT_FAILURE;
621632
} catch (const std::exception& e) {
622633
if (argc > 1) {
623634
std::cerr << e.what() << '\n' << std::endl;
624-
std::cerr << cli << std::endl;
635+
std::cerr << "Run with --help to see more information about how to use this program." << std::endl;
636+
return EXIT_FAILURE;
625637
} else {
626638
std::cout << cli << std::endl;
627639
}
628-
return EXIT_FAILURE;
629640
}
630-
631641
return EXIT_SUCCESS;
632642
}

0 commit comments

Comments
 (0)