quest validation improvements/hacks. provide quest id byte/word access

it goes without saying that the hacks here for ignoring bin size
mismatches are definitely _hacks_ ...
This commit is contained in:
Gered 2021-03-24 14:22:56 -04:00
parent beef271ac3
commit 00c52cc6be
3 changed files with 89 additions and 34 deletions

View file

@ -120,7 +120,7 @@ int get_quest_data(const char *filename, uint8_t **dest, uint32_t *dest_size, GC
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int returncode; int returncode, validation_result;
int32_t result; int32_t result;
uint8_t *bin_data = NULL; uint8_t *bin_data = NULL;
uint8_t *dat_data = NULL; uint8_t *dat_data = NULL;
@ -170,7 +170,18 @@ int main(int argc, char *argv[]) {
decompressed_bin_size = result; decompressed_bin_size = result;
QUEST_BIN_HEADER *bin_header = (QUEST_BIN_HEADER*)decompressed_bin_data; QUEST_BIN_HEADER *bin_header = (QUEST_BIN_HEADER*)decompressed_bin_data;
if (validate_quest_bin(bin_header, decompressed_bin_size)) { 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 {
printf("Aborting due to invalid quest .bin data.\n"); printf("Aborting due to invalid quest .bin data.\n");
goto error; goto error;
} }
@ -186,14 +197,16 @@ int main(int argc, char *argv[]) {
} }
decompressed_dat_size = result; decompressed_dat_size = result;
if (validate_quest_dat(decompressed_dat_data, decompressed_dat_size)) { validation_result = validate_quest_dat(decompressed_dat_data, decompressed_dat_size, true);
if (validation_result != QUESTDAT_ERROR_EOF_EMPTY_TABLE) {
printf("Aborting due to invalid quest .dat data.\n"); printf("Aborting due to invalid quest .dat data.\n");
goto error; goto error;
} }
printf("Quest: id=%d, episode=%d, download=%d, unknown=0x%02x, name=\"%s\", compressed_bin_size=%d, compressed_dat_size=%d\n", printf("Quest: id=%d (%d), episode=%d, download=%d, unknown=0x%02x, name=\"%s\", compressed_bin_size=%d, compressed_dat_size=%d\n",
bin_header->quest_number, bin_header->quest_number_byte,
bin_header->quest_number_word,
bin_header->episode+1, bin_header->episode+1,
bin_header->download, bin_header->download,
bin_header->unknown, bin_header->unknown,
@ -229,7 +242,7 @@ int main(int argc, char *argv[]) {
if (out_bin_filename) if (out_bin_filename)
strncpy(out_filename, out_bin_filename, FILENAME_MAX-1); strncpy(out_filename, out_bin_filename, FILENAME_MAX-1);
else else
snprintf(out_filename, FILENAME_MAX-1, "q%03de%01d.bin", bin_header->quest_number, bin_header->episode+1); snprintf(out_filename, FILENAME_MAX-1, "q%03de%01d.bin", bin_header->quest_number_byte, bin_header->episode+1);
printf("Writing compressed quest .bin data to %s ...\n", out_filename); printf("Writing compressed quest .bin data to %s ...\n", out_filename);
result = write_file(out_filename, bin_data, bin_data_size); result = write_file(out_filename, bin_data, bin_data_size);
@ -244,7 +257,7 @@ int main(int argc, char *argv[]) {
if (out_dat_filename) if (out_dat_filename)
strncpy(out_filename, out_dat_filename, FILENAME_MAX-1); strncpy(out_filename, out_dat_filename, FILENAME_MAX-1);
else else
snprintf(out_filename, FILENAME_MAX-1, "q%03de%01d.dat", bin_header->quest_number, bin_header->episode+1); snprintf(out_filename, FILENAME_MAX-1, "q%03de%01d.dat", bin_header->quest_number_byte, bin_header->episode+1);
printf("Writing compressed quest .dat data to %s ...\n", out_filename); printf("Writing compressed quest .dat data to %s ...\n", out_filename);
result = write_file(out_filename, dat_data, dat_data_size); result = write_file(out_filename, dat_data, dat_data_size);

View file

@ -39,57 +39,80 @@ int generate_qst_data_chunk(const char *base_filename, uint8_t counter, const ui
return SUCCESS; return SUCCESS;
} }
int validate_quest_bin(QUEST_BIN_HEADER *header, uint32_t length) { int validate_quest_bin(QUEST_BIN_HEADER *header, uint32_t length, bool print_errors) {
int result = 0;
// TODO: validations might need tweaking ... // TODO: validations might need tweaking ...
if (header->object_code_offset != 468) { if (header->object_code_offset != 468) {
printf("Quest bin file invalid (unexpected object_code_offset = %d).\n", header->object_code_offset); if (print_errors)
return 1; printf("Quest bin file issue: unexpected object_code_offset = %d\n", header->object_code_offset);
result |= QUESTBIN_ERROR_OBJECT_CODE_OFFSET;
} }
if (header->bin_size != length) { if (header->bin_size < length) {
printf("Quest bin file invalid (decompressed size does not match header bin_size value: %d).\n", header->bin_size); if (print_errors)
return 2; printf("Quest bin file issue: bin_size %d is smaller than the actual decompressed bin size %d\n", header->bin_size, length);
result |= QUESTBIN_ERROR_SMALLER_BIN_SIZE;
} else if (header->bin_size > length) {
if (print_errors)
printf("Quest bin file issue: bin_size %d is larger than the actual decompressed bin size %d\n", header->bin_size, length);
result |= QUESTBIN_ERROR_LARGER_BIN_SIZE;
} }
if (strlen(header->name) == 0) { if (strlen(header->name) == 0) {
printf("Quest bin file invalid or missing quest name.\n"); if (print_errors)
return 3; printf("Quest bin file issue: blank quest name\n");
result |= QUESTBIN_ERROR_NAME;
} }
if (header->quest_number == 0) { if (header->quest_number_word == 0) {
printf("Quest bin file invalid (quest_number is zero).\n"); if (print_errors)
return 4; printf("Quest bin file issue: quest_number is zero\n");
result |= QUESTBIN_ERROR_NAME;
} }
return 0; return result;
} }
int validate_quest_dat(uint8_t *data, uint32_t length) { int validate_quest_dat(uint8_t *data, uint32_t length, bool print_errors) {
// TODO: validations might need tweaking ... int result = 0;
if (!data || length == 0) { int table_index = 0;
// printf("Invalid")
}
// TODO: validations might need tweaking ...
uint32_t offset = 0; uint32_t offset = 0;
while (offset < length) { while (offset < length) {
QUEST_DAT_TABLE_HEADER *table_header = (QUEST_DAT_TABLE_HEADER*)(data + offset); QUEST_DAT_TABLE_HEADER *table_header = (QUEST_DAT_TABLE_HEADER*)(data + offset);
if (table_header->type > 5) { if (table_header->type > 5) {
printf("Invalid table type value found (type = %d)\n", table_header->type); if (print_errors)
return 1; printf("Quest dat file issue: invalid table type value %d found in table index %d\n", table_header->type, table_index);
result |= QUESTDAT_ERROR_TYPE;
} }
if (table_header->type == 0 && if (table_header->type == 0 &&
table_header->table_size == 0 && table_header->table_size == 0 &&
table_header->area == 0 && table_header->area == 0 &&
table_header->table_body_size == 0) { table_header->table_body_size == 0) {
// all zeros seems to be used to indicate end of file ??? // all zeros seems to be used to indicate end of file ???
// just ignore this and move on ... if ((offset + sizeof(QUEST_DAT_TABLE_HEADER)) == length) {
if (print_errors)
printf("Quest dat file issue: empty table encountered at end of file\n");
result |= QUESTDAT_ERROR_EOF_EMPTY_TABLE;
} else {
if (print_errors)
printf("Quest dat file issue: empty table encountered at table index %d\n", table_index);
result |= QUESTDAT_ERROR_EMPTY_TABLE;
}
} else if (table_header->table_size == (table_header->table_body_size - 16)) { } else if (table_header->table_size == (table_header->table_body_size - 16)) {
printf("Invalid table_body_size found (table_size = %d, table_body_size = %d)\n", table_header->table_size, table_header->table_body_size); if (print_errors)
return 2; printf("Quest dat file issue: mismatching table_size (%d) and table_body_size (%d) found in table index %d\n",
table_header->table_size,
table_header->table_body_size,
table_index);
result |= QUESTDAT_ERROR_TABLE_BODY_SIZE;
} }
offset += sizeof(QUEST_DAT_TABLE_HEADER); offset += sizeof(QUEST_DAT_TABLE_HEADER);
offset += table_header->table_body_size; offset += table_header->table_body_size;
++table_index;
} }
return 0; return result;
} }

View file

@ -3,9 +3,21 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "defs.h" #include "defs.h"
#define QUESTBIN_ERROR_OBJECT_CODE_OFFSET 1
#define QUESTBIN_ERROR_LARGER_BIN_SIZE 2
#define QUESTBIN_ERROR_SMALLER_BIN_SIZE 4
#define QUESTBIN_ERROR_NAME 8
#define QUESTBIN_ERROR_NUMBER 16
#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 PACKET_ID_QUEST_INFO_ONLINE 0x44 #define PACKET_ID_QUEST_INFO_ONLINE 0x44
#define PACKET_ID_QUEST_INFO_DOWNLOAD 0xa6 #define PACKET_ID_QUEST_INFO_DOWNLOAD 0xa6
#define PACKET_ID_QUEST_CHUNK_ONLINE 0x13 #define PACKET_ID_QUEST_CHUNK_ONLINE 0x13
@ -28,8 +40,15 @@ typedef struct _PACKED_ {
// is *probably* better when dealing with non-custom quests. however, some custom quests (which are mostly of // is *probably* better when dealing with non-custom quests. however, some custom quests (which are mostly of
// dubious quality anyway) clearly were created using a tool which had quest_number as a 16-bit value ... // dubious quality anyway) clearly were created using a tool which had quest_number as a 16-bit value ...
// ... so .... i dunno! i guess i'll just leave it like this ... // ... so .... i dunno! i guess i'll just leave it like this ...
uint8_t quest_number; union {
uint8_t episode; struct {
uint8_t quest_number_byte;
uint8_t episode;
};
struct {
uint16_t quest_number_word;
};
};
// some sources say these strings are all UTF-16LE, but i'm not sure that is really the case for gamecube data? // some sources say these strings are all UTF-16LE, but i'm not sure that is really the case for gamecube data?
// for gamecube-format quest .bin files, it instead looks like SHIFT-JIS probably ... ? // for gamecube-format quest .bin files, it instead looks like SHIFT-JIS probably ... ?
@ -88,7 +107,7 @@ typedef struct _PACKED_ {
int generate_qst_header(const char *src_file, size_t src_file_size, QUEST_BIN_HEADER *bin_header, QST_HEADER *out_header); int generate_qst_header(const char *src_file, size_t src_file_size, QUEST_BIN_HEADER *bin_header, QST_HEADER *out_header);
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 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(QUEST_BIN_HEADER *header, uint32_t length); int validate_quest_bin(QUEST_BIN_HEADER *header, uint32_t length, bool print_errors);
int validate_quest_dat(uint8_t *data, uint32_t length); int validate_quest_dat(uint8_t *data, uint32_t length, bool print_errors);
#endif #endif