Compare commits
4 commits
4cdd7f1b39
...
2bc7751d3b
Author | SHA1 | Date | |
---|---|---|---|
2bc7751d3b | |||
36edf93538 | |||
a615ae0354 | |||
7c74a3e786 |
2 changed files with 203 additions and 79 deletions
225
oodle-cli.cpp
225
oodle-cli.cpp
|
@ -22,39 +22,125 @@ 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 <in_file> <out_file> <uncompressed_size>\n"
|
"Usage: oodle-cli [OPTIONS] [--] <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.\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 <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[])
|
||||||
{
|
{
|
||||||
if (argc < 4) {
|
int verbosity = 0;
|
||||||
std::cerr << "Arguments missing!\n\n";
|
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();
|
usage();
|
||||||
return 1;
|
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) {
|
||||||
|
@ -69,10 +155,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 << "Failed to add DLL search path: '" << token << "'!\n";
|
std::cerr << "WARN: Failed to add DLL search path: '" << token << "'!\n";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << "Added DLL search path: '" << path << "'.\n";
|
std::cout << "INFO: Added DLL search path: '" << path << "'.\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +167,7 @@ int main(int argc, char* argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cerr << "Failed to read environment variable 'DLL_SEARCH_PATH'. Skipping.\n";
|
std::cerr << "ERROR: 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);
|
||||||
|
@ -90,27 +176,41 @@ int main(int argc, char* argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto decompress = (decltype(OodleLZ_Decompress)*)GetProcAddress((HMODULE)hDLL, "OodleLZ_Decompress");
|
auto decompress = reinterpret_cast<decompress_func>(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string in_name = argv[1];
|
if (check_lib) {
|
||||||
std::string out_name = argv[2];
|
std::cout << "INFO: '" << LIB_FILE << "' found and loaded.\n";
|
||||||
size_t raw_buffer_size = std::stoi(argv[3]);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "Attempting to decompress from '" << in_name << "' to '" << out_name << "' (" << raw_buffer_size << " bytes).\n";
|
if (argc - i < 3) {
|
||||||
|
std::cerr << "ERROR: Arguments missing!\n\n";
|
||||||
std::ifstream in_file(in_name, std::ios::binary | std::ios::ate);
|
usage();
|
||||||
if (!in_file) {
|
|
||||||
std::cerr << "Failed to open compressed file!\n";
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream out_file(out_name, std::ios::binary);
|
std::string in_name = argv[i];
|
||||||
if (!out_file) {
|
std::string out_name = argv[i + 1];
|
||||||
std::cerr << "Failed to open output file!\n";
|
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);
|
||||||
|
if (!in_file) {
|
||||||
|
std::cerr << "ERROR: Failed to open compressed file!\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,40 +221,45 @@ 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 << "Failed to read compressed file!\n";
|
std::cerr << "ERROR: Failed to read compressed file!\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<char> 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(
|
if (res == DECOMPRESS_ERROR) {
|
||||||
compressed_buffer.data(),
|
std::cerr << "ERROR: Failed to decompress!\n";
|
||||||
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
else if (res == GENERIC_ERROR) {
|
||||||
if (res != raw_buffer_size) {
|
|
||||||
std::cerr << "Failed to decompress. Expected " << raw_buffer_size << " bytes, got " << res << "!\n";
|
|
||||||
return 1;
|
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;
|
||||||
|
|
||||||
std::cout << "Done!\n";
|
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";
|
||||||
}
|
}
|
||||||
|
|
29
oodle2.h
29
oodle2.h
|
@ -11,8 +11,6 @@
|
||||||
#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"
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,7 +47,6 @@ 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,
|
||||||
|
@ -163,7 +160,7 @@ typedef enum OodleLZ_FuzzSafe {
|
||||||
seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_.
|
seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
int OodleLZ_Compress(
|
extern "C" intptr_t __stdcall OodleLZ_Compress(
|
||||||
OodleLZ_Compressor compressor,
|
OodleLZ_Compressor compressor,
|
||||||
const void* raw_buffer,
|
const void* raw_buffer,
|
||||||
size_t raw_len,
|
size_t raw_len,
|
||||||
|
@ -250,7 +247,7 @@ int OodleLZ_Compress(
|
||||||
Use of OodleLZ_FuzzSafe_No is deprecated.
|
Use of OodleLZ_FuzzSafe_No is deprecated.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
int OodleLZ_Decompress(
|
extern "C" intptr_t __stdcall OodleLZ_Decompress(
|
||||||
const void* compressed_buffer,
|
const void* compressed_buffer,
|
||||||
size_t compressed_length,
|
size_t compressed_length,
|
||||||
void* raw_buffer,
|
void* raw_buffer,
|
||||||
|
@ -266,3 +263,25 @@ int OodleLZ_Decompress(
|
||||||
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);
|
Reference in a new issue