diff --git a/bindat_to_gcdl.c b/bindat_to_gcdl.c index 99c86d9..5f80049 100644 --- a/bindat_to_gcdl.c +++ b/bindat_to_gcdl.c @@ -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; } diff --git a/gci_extract.c b/gci_extract.c index a565095..d58c696 100644 --- a/gci_extract.c +++ b/gci_extract.c @@ -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; } diff --git a/quest_info.c b/quest_info.c index 39b1306..91cbd0c 100644 --- a/quest_info.c +++ b/quest_info.c @@ -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; } diff --git a/quests.c b/quests.c index 1a88fb9..ff74472 100644 --- a/quests.c +++ b/quests.c @@ -1,6 +1,7 @@ #include #include #include +#include #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; +} diff --git a/quests.h b/quests.h index 624d37b..dea4d3c 100644 --- a/quests.h +++ b/quests.h @@ -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