2
Fork 0

Compare commits

..

No commits in common. "2bc7751d3b0e2e6c6eb917fa12244400bcbb3bed" and "4cdd7f1b394ecb547ba0456925d02c7ced335058" have entirely different histories.

2 changed files with 79 additions and 203 deletions

View file

@ -22,125 +22,39 @@ const LPCWSTR LIB_NAME = L"oo2core_8_win64";
// It's not like this is going to change often. // It's not like this is going to change often.
const char* LIB_FILE = "oo2core_8_win64.dll"; 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() { void usage() {
std::cerr << std::cerr <<
"Usage: oodle-cli [OPTIONS] [--] <in_file> <out_file> <uncompressed_size>\n" "Usage: oodle-cli <in_file> <out_file> <uncompressed_size>\n"
"Decompress a <in_file> to <out_file> using the Oodle algorithm.\n" "Decompress a <in_file> to <out_file> using the Oodle algorithm.\n"
"The size of the uncompressed data must be known. If 0 is given, the tool will\n" "The size of the uncompressed data must be known.\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 <number> 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" "\n"
"The following environmental variables are recognized:\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"; " 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<char> compressed_buffer, std::string out_name, size_t raw_buffer_size, OodleLZ_Verbosity verbosity) {
std::vector<char> 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 main(int argc, char* argv[])
{ {
int verbosity = 0; if (argc < 4) {
bool check_lib = FALSE; std::cerr << "Arguments missing!\n\n";
int guess_factor = 10; usage();
int i = 1; return 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";
}
} }
char* var; char* var;
size_t len; 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; DWORD load_flags = 0;
if (_dupenv_s(&var, &len, "DLL_SEARCH_PATH") == 0) { if (_dupenv_s(&var, &len, "DLL_SEARCH_PATH") == 0) {
if (var) { if (var) {
@ -155,10 +69,10 @@ int main(int argc, char* argv[])
} }
if (!path.is_absolute() || !AddDllDirectory(path.c_str())) { 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 { 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 { 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); HINSTANCE hDLL = LoadLibraryEx(LIB_NAME, NULL, load_flags);
@ -176,41 +90,27 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
auto decompress = reinterpret_cast<decompress_func>(GetProcAddress((HMODULE)hDLL, "OodleLZ_Decompress")); auto decompress = (decltype(OodleLZ_Decompress)*)GetProcAddress((HMODULE)hDLL, "OodleLZ_Decompress");
if (decompress == NULL) { if (decompress == NULL) {
std::cerr << "ERROR: The library is incompatible!\n"; std::cerr << "ERROR: The library is incompatible!\n";
return 1; return 1;
} }
if (check_lib) { std::string in_name = argv[1];
std::cout << "INFO: '" << LIB_FILE << "' found and loaded.\n"; std::string out_name = argv[2];
return 0; size_t raw_buffer_size = std::stoi(argv[3]);
}
if (argc - i < 3) { std::cout << "Attempting to decompress from '" << in_name << "' to '" << out_name << "' (" << raw_buffer_size << " bytes).\n";
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<setprintf_func>(GetProcAddress((HMODULE)hDLL, "OodleCore_Plugins_SetPrintf"));
if (fn == NULL) {
std::cerr << "ERROR: The library is incompatible!\n";
return 1;
}
reinterpret_cast<void*>(fn(printf_callback));
}
std::ifstream in_file(in_name, std::ios::binary | std::ios::ate); std::ifstream in_file(in_name, std::ios::binary | std::ios::ate);
if (!in_file) { 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; return 1;
} }
@ -221,45 +121,40 @@ int main(int argc, char* argv[])
in_file.read(compressed_buffer.data(), compressed_buffer_size); in_file.read(compressed_buffer.data(), compressed_buffer_size);
if (in_file.fail()) { if (in_file.fail()) {
std::cerr << "ERROR: Failed to read compressed file!\n"; std::cerr << "Failed to read compressed file!\n";
return 1; return 1;
} }
if (raw_buffer_size > 0) { std::vector<char> raw_buffer(raw_buffer_size);
int res = do_decompress(decompress, compressed_buffer, out_name, raw_buffer_size, (OodleLZ_Verbosity)verbosity);
if (res == DECOMPRESS_ERROR) { int res = decompress(
std::cerr << "ERROR: Failed to decompress!\n"; compressed_buffer.data(),
return 1; compressed_buffer_size,
} raw_buffer.data(),
else if (res == GENERIC_ERROR) { raw_buffer_size,
return 1; OodleLZ_FuzzSafe_Yes,
} OodleLZ_CheckCRC_No,
} (OodleLZ_Verbosity)verbosity,
else { nullptr,
size_t guessed_size = compressed_buffer.size(); 0,
// I need some maximum value to avoid infinite loops. The factor is configurable. nullptr,
size_t max = guessed_size * guess_factor; nullptr,
nullptr,
0,
OodleLZ_Decode_Unthreaded
);
while (true) { out_file.write(raw_buffer.data(), res);
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 (out_file.fail()) {
std::cerr << "Failed to write output file!\n";
if (guessed_size > max) { return 1;
std::cerr << "ERROR: Exceeded maximum value for guessing uncompressed size!\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";
} }

View file

@ -7,10 +7,12 @@
// the DLL is incompatible when MAJOR is bumped // the DLL is incompatible when MAJOR is bumped
// MINOR is for internal revs and bug fixes that don't affect API compatibility // MINOR is for internal revs and bug fixes that don't affect API compatibility
// TODO: Check if the DLL gives a minor version // TODO: Check if the DLL gives a minor version
#define OODLE2_VERSION_MAJOR 8 #define OODLE2_VERSION_MAJOR 8
#define OODLE2_VERSION_MINOR 14 #define OODLE2_VERSION_MINOR 14
// OodleVersion string is 1 . MAJOR . MINOR // 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" #define OodleVersion "2.8.14"
@ -47,6 +49,7 @@ typedef enum OodleLZ_Compressor {
} OodleLZ_Compressor; } OodleLZ_Compressor;
typedef enum OodleLZ_CheckCRC { typedef enum OodleLZ_CheckCRC {
OodleLZ_CheckCRC_No = 0, OodleLZ_CheckCRC_No = 0,
OodleLZ_CheckCRC_Yes = 1, OodleLZ_CheckCRC_Yes = 1,
@ -160,16 +163,16 @@ typedef enum OodleLZ_FuzzSafe {
seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_. seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_.
*/ */
extern "C" intptr_t __stdcall OodleLZ_Compress( int OodleLZ_Compress(
OodleLZ_Compressor compressor, OodleLZ_Compressor compressor,
const void* raw_buffer, const void* raw_buffer,
size_t raw_len, size_t raw_len,
void* compressed_buffer, void* compressed_buffer,
OodleLZ_CompressionLevel level, OodleLZ_CompressionLevel level,
void* options, // Default: null void* options, // Default: null
size_t dictionary_base, // Default: null size_t dictionary_base, // Default: null
const void* lrm, // Default: null const void* lrm, // Default: null
void* scratch_memory, // Default: null void* scratch_memory, // Default: null
size_t scratch_size // 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. Use of OodleLZ_FuzzSafe_No is deprecated.
*/ */
extern "C" intptr_t __stdcall OodleLZ_Decompress( int OodleLZ_Decompress(
const void* compressed_buffer, const void* compressed_buffer,
size_t compressed_length, size_t compressed_length,
void* raw_buffer, void* raw_buffer,
size_t raw_length, size_t raw_length,
OodleLZ_FuzzSafe fuzz_safe, // Default: OodleLZ_FuzzSafe_Yes OodleLZ_FuzzSafe fuzz_safe, // Default: OodleLZ_FuzzSafe_Yes
OodleLZ_CheckCRC check_crc, // Default: OodleLZ_CheckCRC_No OodleLZ_CheckCRC check_crc, // Default: OodleLZ_CheckCRC_No
OodleLZ_Verbosity verbosity, // Default: OodleLZ_Verbosity_None OodleLZ_Verbosity verbosity, // Default: OodleLZ_Verbosity_None
void* decBufBase, // Default: null void* decBufBase, // Default: null
size_t decBufSize, // Default: 0 size_t decBufSize, // Default: 0
void* callback, // Default: null void* callback, // Default: null
void* callback_user_data, // Default: null void* callback_user_data, // Default: null
void* decoder_memory, // Default: null void* decoder_memory, // Default: null
size_t decoder_memory_size, // Default: 0 size_t decoder_memory_size, // Default: 0
OodleLZ_Decode_ThreadPhase thread_phase // Default: OodleLZ_Decode_Unthreaded 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);