diff --git a/oodle-cli.cpp b/oodle-cli.cpp index 5dc4b09..d74b1b0 100644 --- a/oodle-cli.cpp +++ b/oodle-cli.cpp @@ -22,125 +22,39 @@ const LPCWSTR LIB_NAME = L"oo2core_8_win64"; // It's not like this is going to change often. 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" + "Usage: oodle-cli \n" "Decompress a to using the Oodle algorithm.\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." + "The size of the uncompressed data must be known.\n" "\n" "The following environmental variables are recognized:\n" + " VERBOSITY: An integer between 0 and 3, with 0 being no logging and 3 being maximum logging.\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"; } -void printf_callback(int verboseLevel, const char* file, int line, const char* fmt, ...) { - std::cout << "[OODLE] "; - - // For some reason, this doesn't actually map to the config value. - switch (verboseLevel) { - case 0: - std::cout << "EXTRAORDINARILY IMPORTANT: "; - break; - case 1: - std::cout << "EXTERMELY IMPORTANT: "; - break; - case 2: - std::cout << "VERY IMPORTANT: "; - break; - } - - va_list args; - va_start(args, fmt); - 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++) { - char* arg = argv[i]; - - if (strcmp(arg, "--") == 0 || arg[0] != '-') { - break; - } - - if (strcmp(arg, "--help") == 0) { - usage(); - return 0; - } - else if (strcmp(arg, "-c") == 0) { - check_lib = TRUE; - } - 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"; - } + if (argc < 4) { + std::cerr << "Arguments missing!\n\n"; + usage(); + return 1; } char* var; size_t len; + int verbosity = 0; + if (_dupenv_s(&var, &len, "VERBOSITY") == 0) { + if (var) { + verbosity = std::stoi(std::string(var, len)); + std::cout << "Setting Oodle verbosity to " << verbosity << ".\n"; + free(var); + } + } + else { + std::cerr << "Failed to read environment variable 'VERBOSITY'. Falling back to default.\n"; + } + DWORD load_flags = 0; if (_dupenv_s(&var, &len, "DLL_SEARCH_PATH") == 0) { if (var) { @@ -155,10 +69,10 @@ int main(int argc, char* argv[]) } if (!path.is_absolute() || !AddDllDirectory(path.c_str())) { - std::cerr << "WARN: Failed to add DLL search path: '" << token << "'!\n"; + std::cerr << "Failed to add DLL search path: '" << token << "'!\n"; } else { - std::cout << "INFO: Added DLL search path: '" << path << "'.\n"; + std::cout << "Added DLL search path: '" << path << "'.\n"; } } @@ -167,7 +81,7 @@ int main(int argc, char* argv[]) } } else { - std::cerr << "ERROR: Failed to read environment variable 'DLL_SEARCH_PATH'. Skipping.\n"; + std::cerr << "Failed to read environment variable 'DLL_SEARCH_PATH'. Skipping.\n"; } HINSTANCE hDLL = LoadLibraryEx(LIB_NAME, NULL, load_flags); @@ -176,41 +90,27 @@ int main(int argc, char* argv[]) return 1; } - auto decompress = reinterpret_cast(GetProcAddress((HMODULE)hDLL, "OodleLZ_Decompress")); + auto decompress = (decltype(OodleLZ_Decompress)*)GetProcAddress((HMODULE)hDLL, "OodleLZ_Decompress"); if (decompress == NULL) { std::cerr << "ERROR: The library is incompatible!\n"; return 1; } - if (check_lib) { - std::cout << "INFO: '" << LIB_FILE << "' found and loaded.\n"; - return 0; - } + std::string in_name = argv[1]; + std::string out_name = argv[2]; + size_t raw_buffer_size = std::stoi(argv[3]); - if (argc - i < 3) { - std::cerr << "ERROR: Arguments missing!\n\n"; - usage(); - return 1; - } - - std::string in_name = argv[i]; - std::string out_name = argv[i + 1]; - size_t raw_buffer_size = std::stoi(argv[i + 2]); - - std::cout << "INFO: Attempting to decompress from '" << in_name << "' to '" << out_name << "' (" << raw_buffer_size << " bytes).\n"; - - if (verbosity > 0) { - auto fn = reinterpret_cast(GetProcAddress((HMODULE)hDLL, "OodleCore_Plugins_SetPrintf")); - if (fn == NULL) { - std::cerr << "ERROR: The library is incompatible!\n"; - return 1; - } - reinterpret_cast(fn(printf_callback)); - } + std::cout << "Attempting to decompress from '" << in_name << "' to '" << out_name << "' (" << raw_buffer_size << " bytes).\n"; std::ifstream in_file(in_name, std::ios::binary | std::ios::ate); if (!in_file) { - std::cerr << "ERROR: Failed to open compressed file!\n"; + std::cerr << "Failed to open compressed file!\n"; + return 1; + } + + std::ofstream out_file(out_name, std::ios::binary); + if (!out_file) { + std::cerr << "Failed to open output file!\n"; return 1; } @@ -221,45 +121,40 @@ int main(int argc, char* argv[]) in_file.read(compressed_buffer.data(), compressed_buffer_size); if (in_file.fail()) { - std::cerr << "ERROR: Failed to read compressed file!\n"; + std::cerr << "Failed to read compressed file!\n"; return 1; } - if (raw_buffer_size > 0) { - int res = do_decompress(decompress, compressed_buffer, out_name, raw_buffer_size, (OodleLZ_Verbosity)verbosity); + std::vector raw_buffer(raw_buffer_size); - 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; + 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 + ); - 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; - } + out_file.write(raw_buffer.data(), res); - guessed_size++; - - if (guessed_size > max) { - std::cerr << "ERROR: Exceeded maximum value for guessing uncompressed size!\n"; - return 1; - } - } + if (out_file.fail()) { + std::cerr << "Failed to write output file!\n"; + return 1; } - std::cout << "INFO: Done!\n"; + if (res != raw_buffer_size) { + std::cerr << "Failed to decompress. Expected " << raw_buffer_size << " bytes, got " << res << "!\n"; + return 1; + } + + std::cout << "Done!\n"; } diff --git a/oodle2.h b/oodle2.h index 3133263..1e77d92 100644 --- a/oodle2.h +++ b/oodle2.h @@ -7,10 +7,12 @@ // the DLL is incompatible when MAJOR is bumped // MINOR is for internal revs and bug fixes that don't affect API compatibility // TODO: Check if the DLL gives a minor version -#define OODLE2_VERSION_MAJOR 8 -#define OODLE2_VERSION_MINOR 14 +#define OODLE2_VERSION_MAJOR 8 +#define OODLE2_VERSION_MINOR 14 // OodleVersion string is 1 . MAJOR . MINOR +// don't make it from macros cuz the doc tool has to parse the string literal + #define OodleVersion "2.8.14" @@ -47,6 +49,7 @@ typedef enum OodleLZ_Compressor { } OodleLZ_Compressor; + typedef enum OodleLZ_CheckCRC { OodleLZ_CheckCRC_No = 0, OodleLZ_CheckCRC_Yes = 1, @@ -160,16 +163,16 @@ typedef enum OodleLZ_FuzzSafe { seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_. */ -extern "C" intptr_t __stdcall OodleLZ_Compress( +int OodleLZ_Compress( OodleLZ_Compressor compressor, - const void* raw_buffer, + const void* raw_buffer, size_t raw_len, - void* compressed_buffer, + void* compressed_buffer, OodleLZ_CompressionLevel level, - void* options, // Default: null + void* options, // Default: null size_t dictionary_base, // Default: null - const void* lrm, // Default: null - void* scratch_memory, // Default: null + const void* lrm, // Default: null + void* scratch_memory, // Default: null size_t scratch_size // Default: null ); @@ -247,41 +250,19 @@ extern "C" intptr_t __stdcall OodleLZ_Compress( Use of OodleLZ_FuzzSafe_No is deprecated. */ -extern "C" intptr_t __stdcall OodleLZ_Decompress( +int OodleLZ_Decompress( const void* compressed_buffer, size_t compressed_length, - void* raw_buffer, + void* raw_buffer, size_t raw_length, OodleLZ_FuzzSafe fuzz_safe, // Default: OodleLZ_FuzzSafe_Yes OodleLZ_CheckCRC check_crc, // Default: OodleLZ_CheckCRC_No OodleLZ_Verbosity verbosity, // Default: OodleLZ_Verbosity_None - void* decBufBase, // Default: null + void* decBufBase, // Default: null size_t decBufSize, // Default: 0 - void* callback, // Default: null - void* callback_user_data, // Default: null - void* decoder_memory, // Default: null + void* callback, // Default: null + void* callback_user_data, // Default: null + void* decoder_memory, // Default: null size_t decoder_memory_size, // Default: 0 OodleLZ_Decode_ThreadPhase thread_phase // Default: OodleLZ_Decode_Unthreaded ); - -/* Function pointer to Oodle Core printf - $:verboseLevel verbosity of the message; 0-2 ; lower = more important - $:file C file that sent the message - $:line C line that sent the message - $:fmt vararg printf format string - The logging function installed here must parse varargs like printf. - _verboseLevel_ may be used to omit verbose messages. -*/ -extern "C" typedef void(__stdcall t_fp_OodleCore_Plugin_Printf)(int verboseLevel, const char* file, int line, const char* fmt, ...); - -/* Install the callback used by Oodle Core for logging - $:fp_rrRawPrintf function pointer to your log function; may be NULL to disable all logging - $:return returns the previous function pointer - Use this function to install your own printf for Oodle Core. - The default implementation in debug builds, if you install nothing, uses the C stdio printf for logging. - On Microsoft platforms, it uses OutputDebugString and not stdio. - To disable all logging, call OodleCore_Plugins_SetPrintf(NULL) - WARNING : this function is NOT thread safe! It should be done only once and done in a place where the caller can guarantee thread safety. - In the debug build of Oodle, you can install OodleCore_Plugin_Printf_Verbose to get more verbose logging -*/ -t_fp_OodleCore_Plugin_Printf* __stdcall OodleCore_Plugins_SetPrintf(t_fp_OodleCore_Plugin_Printf* fp_rrRawPrintf); \ No newline at end of file