From 2bc7751d3b0e2e6c6eb917fa12244400bcbb3bed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 20 Oct 2022 17:47:42 +0200 Subject: [PATCH] feat: Implement brute-forcing the raw size Oodle must know the size of the uncompressed data when decompressing data. If you don't know that size, the only option is to guess. And since it needs to be the exact number, you simply have to try every number. --- oodle-cli.cpp | 110 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 27 deletions(-) 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";