diff --git a/cheevos-new/cheevos.c b/cheevos-new/cheevos.c index 3e5e347c1c..1b5b54f2d7 100644 --- a/cheevos-new/cheevos.c +++ b/cheevos-new/cheevos.c @@ -1215,18 +1215,139 @@ enum RCHEEVOS_PCE_CD_MD5 = -17 }; +static int rcheevos_hash_psx(rcheevos_coro_t* coro) +{ + char exe_name_buffer[64]; + size_t exe_name_size; + char* exe_name = NULL; + uint8_t* ptr = NULL; + const char* end = NULL; + char* scan = NULL; + char buffer[2048]; + int success = 0; + size_t to_read = 0; + + /* find the data track - it should be the first one */ + coro->track = cdfs_open_data_track(coro->path); + if (!coro->track) + { + CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n"); + return false; + } + + /* open the SYSTEM.CNF file and find the BOOT= record */ + if (cdfs_open_file(&coro->cdfp, coro->track, "SYSTEM.CNF")) + { + cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); + + for (scan = buffer; scan < &buffer[sizeof(buffer)] && *scan; ++scan) + { + if (strncmp(scan, "BOOT", 4) == 0) + { + exe_name = scan + 4; + while (isspace(*exe_name)) + ++exe_name; + + if (*exe_name == '=') + { + ++exe_name; + while (isspace(*exe_name)) + ++exe_name; + + if (strncmp(exe_name, "cdrom:", 6) == 0) + exe_name += 6; + if (*exe_name == '\\') + ++exe_name; + break; + } + } + + while (*scan && *scan != '\n') + ++scan; + } + + cdfs_close_file(&coro->cdfp); + + if (exe_name) + { + scan = exe_name; + while (!isspace(*scan) && *scan != ';') + ++scan; + *scan = '\0'; + } + } + else + { + /* no SYSTEM.CNF, check for a PSX.EXE */ + exe_name = "PSX.EXE"; + } + + if (!exe_name || !cdfs_open_file(&coro->cdfp, coro->track, exe_name)) + { + CHEEVOS_LOG(RCHEEVOS_TAG "could not locate primary executable\n"); + } + else + { + /* store the exe name, we're about to overwrite buffer */ + strncpy(exe_name_buffer, exe_name, sizeof(exe_name_buffer)); + exe_name_buffer[sizeof(exe_name_buffer) - 1] = '\0'; + exe_name_size = strlen(exe_name_buffer); + + /* read the first sector of the executable */ + cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); + + /* the PSX-E header specifies the executable size as a 4-byte value 28 bytes into the header, which doesn't + * include the header itself. We want to include the header in the hash, so append another 2048 to that value. + * ASSERT: this results in the same value as coro->cdfp->size */ + coro->count = 2048 + (((uint8_t)buffer[28 + 3] << 24) | ((uint8_t)buffer[28 + 2] << 16) | + ((uint8_t)buffer[28 + 1] << 8) | (uint8_t)buffer[28]); + + if (coro->count <= CHEEVOS_MB(16)) /* sanity check */ + { + /* there's a few games that use a singular engine and only differ via their data files. + * luckily, they have unique serial numbers, and use the serial number as the boot file in the + * standard way. include the boot executable name in the hash */ + coro->count += exe_name_size; + + free(coro->data); + coro->data = (uint8_t*)malloc(coro->count); + memcpy(coro->data, exe_name_buffer, exe_name_size); + coro->len = exe_name_size; + + memcpy((uint8_t*)coro->data + coro->len, buffer, sizeof(buffer)); + coro->len += sizeof(buffer); + + while (coro->len < coro->count) + { + to_read = coro->count - coro->len; + if (to_read > 2048) + to_read = 2048; + + cdfs_read_file(&coro->cdfp, (uint8_t*)coro->data + coro->len, to_read); + + coro->len += to_read; + }; + + success = 1; + } + + cdfs_close_file(&coro->cdfp); + } + + cdfs_close_track(coro->track); + coro->track = NULL; + + return success; +} + static int rcheevos_iterate(rcheevos_coro_t* coro) { const int snes_header_len = 0x200; const int lynx_header_len = 0x40; ssize_t num_read = 0; size_t to_read = 4096; - uint8_t *ptr = NULL; - const char *end = NULL; - size_t exe_name_size = 0; - char exe_name_buffer[64]; - char* exe_name = NULL; - char* scan = NULL; + uint8_t* ptr = NULL; + const char* end = NULL; char buffer[2048]; static const uint32_t snes_exts[] = @@ -1766,120 +1887,13 @@ found: *************************************************************************/ CORO_SUB(RCHEEVOS_PSX_MD5) { - MD5_Init(&coro->md5); - - /* find the data track - it should be the first one */ - coro->track = cdfs_open_data_track(coro->path); - if (coro->track) + if (rcheevos_hash_psx(coro)) { - /* open the SYSTEM.CNF file and find the BOOT= record */ - if (cdfs_open_file(&coro->cdfp, coro->track, "SYSTEM.CNF")) - { - cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); + MD5_Init(&coro->md5); + CORO_GOSUB(RCHEEVOS_EVAL_MD5); + MD5_Final(coro->hash, &coro->md5); - for (scan = buffer; scan < &buffer[sizeof(buffer)] && *scan; ++scan) - { - if (strncmp(scan, "BOOT", 4) == 0) - { - exe_name = scan + 4; - while (isspace(*exe_name)) - ++exe_name; - if (*exe_name == '=') - { - ++exe_name; - while (isspace(*exe_name)) - ++exe_name; - - if (strncmp(exe_name, "cdrom:", 6) == 0) - exe_name += 6; - if (*exe_name == '\\') - ++exe_name; - break; - } - } - - while (*scan && *scan != '\n') - ++scan; - } - - cdfs_close_file(&coro->cdfp); - - if (exe_name) - { - scan = exe_name; - while (*scan != '\n' && *scan != '\r' && *scan != ';' && *scan != ' ') - ++scan; - *scan = '\0'; - - exe_name_size = scan - exe_name; - if (exe_name_size < sizeof(exe_name_buffer)) - strcpy(exe_name_buffer, exe_name); - - /* open the file pointed to by the BOOT= record */ - if (exe_name_buffer[0] && cdfs_open_file(&coro->cdfp, coro->track, exe_name_buffer)) - { - cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); - - /* the PSX-E header specifies the executable size as a 4-byte value 28 bytes into the header, which doesn't - * include the header itself. We want to include the header in the hash, so append another 2048 to that value. - * ASSERT: this results in the same value as coro->cdfp->size */ - coro->count = 2048 + (((uint8_t)buffer[28 + 3] << 24) | ((uint8_t)buffer[28 + 2] << 16) | - ((uint8_t)buffer[28 + 1] << 8) | (uint8_t)buffer[28]); - - if (coro->count > CHEEVOS_MB(16)) /* sanity check */ - { - cdfs_close_file(&coro->cdfp); - } - else - { - /* there's a few games that are use a singular engine and only differ via their data files. - * luckily, they have unique serial numbers, and use the serial number as the boot file in the - * standard way. include the boot executable name in the hash */ - coro->count += exe_name_size; - - free(coro->data); - coro->data = (uint8_t*)malloc(coro->count); - memcpy(coro->data, exe_name_buffer, exe_name_size); - coro->len = exe_name_size; - - memcpy((uint8_t*)coro->data + coro->len, buffer, sizeof(buffer)); - coro->len += sizeof(buffer); - - while (coro->len < coro->count) - { - CORO_YIELD(); - - to_read = coro->count - coro->len; - if (to_read > 2048) - to_read = 2048; - - cdfs_read_file(&coro->cdfp, (uint8_t*)coro->data + coro->len, to_read); - - coro->len += to_read; - }; - - CORO_GOSUB(RCHEEVOS_EVAL_MD5); - MD5_Final(coro->hash, &coro->md5); - - cdfs_close_file(&coro->cdfp); - - cdfs_close_track(coro->track); - coro->track = NULL; - - CORO_GOTO(RCHEEVOS_GET_GAMEID); - } - } - } - } - - CHEEVOS_LOG(RCHEEVOS_TAG "could not locate primary executable\n", coro->gameid); - - cdfs_close_track(coro->track); - coro->track = NULL; - } - else - { - CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n", coro->gameid); + CORO_GOTO(RCHEEVOS_GET_GAMEID); } coro->gameid = 0;