refactor quest .bin validation hack-fixes. remove .dat table eof warning

This commit is contained in:
Gered 2021-03-25 16:23:19 -04:00
parent 2a7e4afd8e
commit 11ef22e8da
5 changed files with 58 additions and 34 deletions

View file

@ -82,16 +82,17 @@ int main(int argc, char *argv[]) {
/** prs decompress the .bin file, parse out it's header and validate it **/
printf("Decompressing and validating .bin file ...\n");
uint32_t decompressed_bin_size;
size_t decompressed_bin_size;
result = fuzziqer_prs_decompress_buf(compressed_bin, &decompressed_bin, compressed_bin_size);
if (result < 0) {
printf("Error code %d decompressing .dat data.\n", result);
goto error;
}
decompressed_bin_size = (uint32_t)result;
decompressed_bin_size = result;
QUEST_BIN_HEADER *bin_header = (QUEST_BIN_HEADER*)decompressed_bin;
validation_result = validate_quest_bin(bin_header, decompressed_bin_size, true);
validation_result = handle_quest_bin_validation_issues(validation_result, bin_header, &decompressed_bin, &decompressed_bin_size);
if (validation_result) {
printf("Aborting due to invalid quest .bin data.\n");
goto error;
@ -110,7 +111,7 @@ int main(int argc, char *argv[]) {
decompressed_dat_size = result;
validation_result = validate_quest_dat(decompressed_dat, decompressed_dat_size, true);
if (validation_result != QUESTDAT_ERROR_EOF_EMPTY_TABLE) {
if (validation_result) {
printf("Aborting due to invalid quest .dat data.\n");
goto error;
}

View file

@ -127,7 +127,7 @@ int main(int argc, char *argv[]) {
uint8_t *decompressed_bin_data = NULL;
uint8_t *decompressed_dat_data = NULL;
uint32_t bin_data_size, dat_data_size;
uint32_t decompressed_bin_size, decompressed_dat_size;
size_t decompressed_bin_size, decompressed_dat_size;
char out_filename[FILENAME_MAX];
if (argc != 3 && argc != 5) {
@ -172,17 +172,8 @@ int main(int argc, char *argv[]) {
QUEST_BIN_HEADER *bin_header = (QUEST_BIN_HEADER*)decompressed_bin_data;
validation_result = validate_quest_bin(bin_header, decompressed_bin_size, true);
if (validation_result == QUESTBIN_ERROR_SMALLER_BIN_SIZE) {
printf("WARNING: Decompressed .bin data is larger than expected. Proceeding using the smaller .bin header bin_size value ...\n");
decompressed_bin_size = bin_header->bin_size;
} else if (validation_result == QUESTBIN_ERROR_LARGER_BIN_SIZE) {
if ((decompressed_bin_size + 1) == bin_header->bin_size) {
printf("WARNING: Decompressed .bin data is 1 byte smaller than the .bin header bin_size specifies. Correcting by adding a null byte ...\n");
++decompressed_bin_size;
decompressed_bin_data = realloc(decompressed_bin_data, decompressed_bin_size);
decompressed_bin_data[decompressed_bin_size - 1] = 0;
}
} else if (validation_result) {
validation_result = handle_quest_bin_validation_issues(validation_result, bin_header, &decompressed_bin_data, &decompressed_bin_size);
if (validation_result) {
printf("Aborting due to invalid quest .bin data.\n");
goto error;
}
@ -199,7 +190,7 @@ int main(int argc, char *argv[]) {
decompressed_dat_size = result;
validation_result = validate_quest_dat(decompressed_dat_data, decompressed_dat_size, true);
if (validation_result != QUESTDAT_ERROR_EOF_EMPTY_TABLE) {
if (validation_result) {
printf("Aborting due to invalid quest .dat data.\n");
goto error;
}

View file

@ -102,17 +102,9 @@ void display_info(uint8_t *bin_data, size_t bin_length, uint8_t *dat_data, size_
printf("Validating .bin data ...\n");
QUEST_BIN_HEADER *bin_header = (QUEST_BIN_HEADER*)decompressed_bin_data;
validation_result = validate_quest_bin(bin_header, decompressed_bin_length, true);
if (validation_result == QUESTBIN_ERROR_SMALLER_BIN_SIZE) {
printf("WARNING: Decompressed .bin data is larger than expected. Proceeding using the smaller .bin header bin_size value ...\n");
decompressed_bin_length = bin_header->bin_size;
} else if (validation_result == QUESTBIN_ERROR_LARGER_BIN_SIZE) {
if ((decompressed_bin_length + 1) == bin_header->bin_size) {
printf("WARNING: Decompressed .bin data is 1 byte smaller than the .bin header bin_size specifies. Correcting by adding a null byte ...\n");
++decompressed_bin_length;
decompressed_bin_data = realloc(decompressed_bin_data, decompressed_bin_length);
decompressed_bin_data[decompressed_bin_length - 1] = 0;
}
} else if (validation_result) {
validation_result = handle_quest_bin_validation_issues(validation_result, bin_header, &decompressed_bin_data,
&decompressed_bin_length);
if (validation_result) {
printf("Aborting due to invalid quest .bin data.\n");
goto error;
}
@ -120,7 +112,7 @@ void display_info(uint8_t *bin_data, size_t bin_length, uint8_t *dat_data, size_
printf("Validating .dat data ...\n");
validation_result = validate_quest_dat(decompressed_dat_data, decompressed_dat_length, true);
if (validation_result != QUESTDAT_ERROR_EOF_EMPTY_TABLE) {
if (validation_result) {
printf("Aborting due to invalid quest .dat data.\n");
goto error;
}

View file

@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <malloc.h>
#include "retvals.h"
#include "quests.h"
@ -67,6 +68,11 @@ int validate_quest_bin(const QUEST_BIN_HEADER *header, uint32_t length, bool pri
printf("Quest bin file issue: quest_number is zero\n");
result |= QUESTBIN_ERROR_NAME;
}
if (header->episode > 1) {
if (print_errors)
printf("Quest bin file issue: unexpected episode value %d, quest was probably created using a 16-bit quest_number\n", header->episode);
result |= QUESTBIN_ERROR_EPISODE;
}
return result;
}
@ -89,11 +95,8 @@ int validate_quest_dat(const uint8_t *data, uint32_t length, bool print_errors)
table_header->table_size == 0 &&
table_header->area == 0 &&
table_header->table_body_size == 0) {
// all zeros seems to be used to indicate end of file ???
if ((offset + sizeof(QUEST_DAT_TABLE_HEADER)) == length) {
if (print_errors)
printf("Quest dat file warning: empty table encountered at end of file (probably normal?)\n");
result |= QUESTDAT_ERROR_EOF_EMPTY_TABLE;
// ignore this case ... this empty table is used to mark EOF apparently
} else {
if (print_errors)
printf("Quest dat file warning: empty table encountered at table index %d\n", table_index);
@ -116,3 +119,39 @@ int validate_quest_dat(const uint8_t *data, uint32_t length, bool print_errors)
return result;
}
// HACK: this function applies some arguably shitty hack-fixes under certain circumstances.
int handle_quest_bin_validation_issues(int bin_validation_result, QUEST_BIN_HEADER *bin_header, uint8_t **decompressed_bin_data, size_t *decompressed_bin_length) {
// this hacky fix _probably_ isn't so bad. in these cases, the extra data sitting in the decompressed memory seems
// to just be repeated subsets of the previous "good" data. almost as if the PRS decompression was stuck in a loop
// that it eventually worked itself out of. just a wild guess though ...
if (bin_validation_result & QUESTBIN_ERROR_SMALLER_BIN_SIZE) {
bin_validation_result &= ~QUESTBIN_ERROR_SMALLER_BIN_SIZE;
printf("WARNING: Decompressed .bin data is larger than expected. Proceeding using the smaller .bin header bin_size value ...\n");
*decompressed_bin_length = bin_header->bin_size;
}
// this hacky fix is _probably_ not too bad either, but might have more potential for breaking things than the
// above hack fix. maybe. i also think this is a result of some PRS decompression bug (or maybe a PRS compression
// bug? since i believe the decompression implementation is based on game code disassembly, but most (all?) of the
// PRS-compression implementations are based on the fuzziqer implementation which he coded himself instead of it
// being based on game code disassembly?) ... who knows!
if (bin_validation_result & QUESTBIN_ERROR_LARGER_BIN_SIZE) {
bin_validation_result &= ~QUESTBIN_ERROR_LARGER_BIN_SIZE;
if ((*decompressed_bin_length + 1) == bin_header->bin_size) {
printf("WARNING: Decompressed .bin data is 1 byte smaller than the .bin header bin_size specifies. Correcting by adding a null byte ...\n");
size_t length = *decompressed_bin_length + 1;
uint8_t *new_bin_data;
new_bin_data = realloc(*decompressed_bin_data, length);
new_bin_data[length - 1] = 0;
*decompressed_bin_data = new_bin_data;
*decompressed_bin_length = length;
}
}
if (bin_validation_result & QUESTBIN_ERROR_EPISODE) {
bin_validation_result &= ~QUESTBIN_ERROR_EPISODE;
printf("WARNING: .bin header episode value should be ignored due to apparent 16-bit quest_number value\n");
}
return bin_validation_result;
}

View file

@ -12,11 +12,11 @@
#define QUESTBIN_ERROR_SMALLER_BIN_SIZE 4
#define QUESTBIN_ERROR_NAME 8
#define QUESTBIN_ERROR_NUMBER 16
#define QUESTBIN_ERROR_EPISODE 32
#define QUESTDAT_ERROR_TYPE 1
#define QUESTDAT_ERROR_TABLE_BODY_SIZE 2
#define QUESTDAT_ERROR_EOF_EMPTY_TABLE 4 // more of a warning i guess? maybe this is totally normal?
#define QUESTDAT_ERROR_EMPTY_TABLE 8
#define QUESTDAT_ERROR_EMPTY_TABLE 4
#define PACKET_ID_QUEST_INFO_ONLINE 0x44
#define PACKET_ID_QUEST_INFO_DOWNLOAD 0xa6
@ -116,5 +116,6 @@ int generate_qst_header(const char *src_file, size_t src_file_size, const QUEST_
int generate_qst_data_chunk(const char *base_filename, uint8_t counter, const uint8_t *src, uint32_t size, QST_DATA_CHUNK *out_chunk);
int validate_quest_bin(const QUEST_BIN_HEADER *header, uint32_t length, bool print_errors);
int validate_quest_dat(const uint8_t *data, uint32_t length, bool print_errors);
int handle_quest_bin_validation_issues(int bin_validation_result, QUEST_BIN_HEADER *bin_header, uint8_t **decompressed_bin_data, size_t *decompressed_bin_length);
#endif