diff --git a/oodle-cli.cpp b/oodle-cli.cpp index 09406d5..5dc4b09 100644 --- a/oodle-cli.cpp +++ b/oodle-cli.cpp @@ -25,15 +25,21 @@ const char* LIB_FILE = "oo2core_8_win64.dll"; typedef decltype(OodleLZ_Decompress)* decompress_func; typedef decltype(OodleCore_Plugins_SetPrintf)* setprintf_func; +#define OK 0 +#define GENERIC_ERROR -1 +#define DECOMPRESS_ERROR -2 + void usage() { std::cerr << "Usage: oodle-cli [OPTIONS] [--] \n" "Decompress a to using the Oodle algorithm.\n" - "The size of the uncompressed data must be known.\n" + "The size of the uncompressed data must be known. If 0 is given, the tool will\n" + "attempt to brute-force the correct number by incrementing from the compressed size.\n" "\n" "Options:\n" " -v Increase Oodle's verbosity. May be specified up to three times.\n" " -c Only check if the library can be found and used.\n" + " --guess When guessing the uncompressed size, a maximum number as multiple of the compressed size prevents an infinite loop. This changes the factor. Defaults to 10." "\n" "The following environmental variables are recognized:\n" " DLL_SEARCH_PATH: A color (':') separated list of directories to search for the Oodle library. May be relative to the current working directory.\n"; @@ -60,10 +66,51 @@ void printf_callback(int verboseLevel, const char* file, int line, const char* f vfprintf(stdout, fmt, args); } +int do_decompress(decompress_func decompress, std::vector compressed_buffer, std::string out_name, size_t raw_buffer_size, OodleLZ_Verbosity verbosity) { + std::vector raw_buffer(raw_buffer_size); + + intptr_t res = decompress( + compressed_buffer.data(), + compressed_buffer.size(), + raw_buffer.data(), + raw_buffer_size, + OodleLZ_FuzzSafe_Yes, + OodleLZ_CheckCRC_No, + verbosity, + nullptr, + 0, + nullptr, + nullptr, + nullptr, + 0, + OodleLZ_Decode_Unthreaded + ); + + if (res != raw_buffer_size) { + return DECOMPRESS_ERROR; + } + + std::ofstream out_file(out_name, std::ios::binary); + if (!out_file) { + std::cerr << "ERROR: Failed to open output file!\n"; + return GENERIC_ERROR; + } + + out_file.write(raw_buffer.data(), res); + + if (out_file.fail()) { + std::cerr << "ERROR: Failed to write output file!\n"; + return GENERIC_ERROR; + } + + return OK; +} + int main(int argc, char* argv[]) { int verbosity = 0; bool check_lib = FALSE; + int guess_factor = 10; int i = 1; for (; i < argc; i++) { @@ -83,6 +130,10 @@ int main(int argc, char* argv[]) else if (strcmp(arg, "-v") == 0) { verbosity++; } + else if (strcmp(arg, "--guess") == 0) { + i++; + guess_factor = std::stoi(argv[i]); + } else { std::cerr << "WARN: Unknown option '" << arg << "'!\n\n"; } @@ -174,35 +225,40 @@ int main(int argc, char* argv[]) return 1; } - std::vector raw_buffer(raw_buffer_size); + if (raw_buffer_size > 0) { + int res = do_decompress(decompress, compressed_buffer, out_name, raw_buffer_size, (OodleLZ_Verbosity)verbosity); - int res = decompress( - compressed_buffer.data(), - compressed_buffer_size, - raw_buffer.data(), - raw_buffer_size, - OodleLZ_FuzzSafe_Yes, - OodleLZ_CheckCRC_No, - (OodleLZ_Verbosity)verbosity, - nullptr, - 0, - nullptr, - nullptr, - nullptr, - 0, - OodleLZ_Decode_Unthreaded - ); - - out_file.write(raw_buffer.data(), res); - - if (out_file.fail()) { - std::cerr << "Failed to write output file!\n"; - return 1; + if (res == DECOMPRESS_ERROR) { + std::cerr << "ERROR: Failed to decompress!\n"; + return 1; + } + else if (res == GENERIC_ERROR) { + return 1; + } } + else { + size_t guessed_size = compressed_buffer.size(); + // I need some maximum value to avoid infinite loops. The factor is configurable. + size_t max = guessed_size * guess_factor; - if (res != raw_buffer_size) { - std::cerr << "Failed to decompress. Expected " << raw_buffer_size << " bytes, got " << res << "!\n"; - return 1; + while (true) { + int res = do_decompress(decompress, compressed_buffer, out_name, guessed_size, (OodleLZ_Verbosity)verbosity); + + if (res == GENERIC_ERROR) { + return 1; + } + else if (res == OK) { + std::cout << "INFO: Found decompressed size as " << guessed_size << "\n"; + break; + } + + guessed_size++; + + if (guessed_size > max) { + std::cerr << "ERROR: Exceeded maximum value for guessing uncompressed size!\n"; + return 1; + } + } } std::cout << "INFO: Done!\n";