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.
This commit is contained in:
parent
36edf93538
commit
2bc7751d3b
1 changed files with 83 additions and 27 deletions
108
oodle-cli.cpp
108
oodle-cli.cpp
|
@ -25,15 +25,21 @@ const char* LIB_FILE = "oo2core_8_win64.dll";
|
||||||
typedef decltype(OodleLZ_Decompress)* decompress_func;
|
typedef decltype(OodleLZ_Decompress)* decompress_func;
|
||||||
typedef decltype(OodleCore_Plugins_SetPrintf)* setprintf_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 [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"
|
"\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
" -v Increase Oodle's verbosity. May be specified up to three times.\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"
|
" -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"
|
||||||
" 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";
|
||||||
|
@ -60,10 +66,51 @@ void printf_callback(int verboseLevel, const char* file, int line, const char* f
|
||||||
vfprintf(stdout, fmt, args);
|
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;
|
int verbosity = 0;
|
||||||
bool check_lib = FALSE;
|
bool check_lib = FALSE;
|
||||||
|
int guess_factor = 10;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
for (; i < argc; i++) {
|
for (; i < argc; i++) {
|
||||||
|
@ -83,6 +130,10 @@ int main(int argc, char* argv[])
|
||||||
else if (strcmp(arg, "-v") == 0) {
|
else if (strcmp(arg, "-v") == 0) {
|
||||||
verbosity++;
|
verbosity++;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(arg, "--guess") == 0) {
|
||||||
|
i++;
|
||||||
|
guess_factor = std::stoi(argv[i]);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
std::cerr << "WARN: Unknown option '" << arg << "'!\n\n";
|
std::cerr << "WARN: Unknown option '" << arg << "'!\n\n";
|
||||||
}
|
}
|
||||||
|
@ -174,36 +225,41 @@ int main(int argc, char* argv[])
|
||||||
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;
|
||||||
|
|
||||||
|
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";
|
std::cout << "INFO: Done!\n";
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue