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 returncode;
int returncode, validation_result;
int32_t result;
uint8_t *bin_data = NULL;
uint8_t *dat_data = NULL;
@ -170,7 +170,18 @@ int main(int argc, char *argv[]) {
decompressed_bin_size = result;
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");
goto error;
}
@ -186,14 +197,16 @@ int main(int argc, char *argv[]) {
}
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");
goto error;
}
printf("Quest: id=%d, episode=%d, download=%d, unknown=0x%02x, name=\"%s\", compressed_bin_size=%d, compressed_dat_size=%d\n",
bin_header->quest_number,
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_byte,
bin_header->quest_number_word,
bin_header->episode+1,
bin_header->download,
bin_header->unknown,
@ -229,7 +242,7 @@ int main(int argc, char *argv[]) {
if (out_bin_filename)
strncpy(out_filename, out_bin_filename, FILENAME_MAX-1);
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);
result = write_file(out_filename, bin_data, bin_data_size);
@ -244,7 +257,7 @@ int main(int argc, char *argv[]) {
if (out_dat_filename)
strncpy(out_filename, out_dat_filename, FILENAME_MAX-1);
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);
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;
}
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 ...
if (header->object_code_offset != 468) {
printf("Quest bin file invalid (unexpected object_code_offset = %d).\n", header->object_code_offset);
return 1;
if (print_errors)
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) {
printf("Quest bin file invalid (decompressed size does not match header bin_size value: %d).\n", header->bin_size);
return 2;
if (header->bin_size < length) {
if (print_errors)
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) {
printf("Quest bin file invalid or missing quest name.\n");
return 3;
if (print_errors)
printf("Quest bin file issue: blank quest name\n");
result |= QUESTBIN_ERROR_NAME;
}
if (header->quest_number == 0) {
printf("Quest bin file invalid (quest_number is zero).\n");
return 4;
if (header->quest_number_word == 0) {
if (print_errors)
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) {
// TODO: validations might need tweaking ...
if (!data || length == 0) {
// printf("Invalid")
}
int validate_quest_dat(uint8_t *data, uint32_t length, bool print_errors) {
int result = 0;
int table_index = 0;
// TODO: validations might need tweaking ...
uint32_t offset = 0;
while (offset < length) {
QUEST_DAT_TABLE_HEADER *table_header = (QUEST_DAT_TABLE_HEADER*)(data + offset);
if (table_header->type > 5) {
printf("Invalid table type value found (type = %d)\n", table_header->type);
return 1;
if (print_errors)
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 &&
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 ???
// 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)) {
printf("Invalid table_body_size found (table_size = %d, table_body_size = %d)\n", table_header->table_size, table_header->table_body_size);
return 2;
if (print_errors)
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 += table_header->table_body_size;
++table_index;
}
return 0;
return result;
}

View file

@ -3,9 +3,21 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.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_DOWNLOAD 0xa6
#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
// 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 ...
uint8_t quest_number;
uint8_t episode;
union {
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?
// 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_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_dat(uint8_t *data, 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, bool print_errors);
#endif