From 1ab81e571163960cca9e0332e523a722399888ef Mon Sep 17 00:00:00 2001 From: Jamiras Date: Fri, 13 Sep 2019 21:38:35 -0600 Subject: [PATCH] support for Sega CD/Saturn; reduce hash calls to server --- cheevos-new/cheevos.c | 401 ++++++++++++++----------- libretro-common/formats/cdfs/cdfs.c | 32 +- libretro-common/include/formats/cdfs.h | 4 + 3 files changed, 259 insertions(+), 178 deletions(-) diff --git a/cheevos-new/cheevos.c b/cheevos-new/cheevos.c index 1d930b921d..7525165fdb 100644 --- a/cheevos-new/cheevos.c +++ b/cheevos-new/cheevos.c @@ -1068,7 +1068,9 @@ static void rcheevos_unlock_cb(unsigned id, void* userdata) #ifndef CHEEVOS_DONT_DEACTIVATE cheevo->active &= ~*(unsigned*)userdata; #endif - CHEEVOS_LOG(RCHEEVOS_TAG "cheevo %u deactivated: %s\n", id, cheevo->info->title); + CHEEVOS_LOG(RCHEEVOS_TAG "cheevo %u deactivated (%s): %s\n", id, + (*(unsigned*)userdata) == RCHEEVOS_ACTIVE_HARDCORE ? "hardcore" : "softcore", + cheevo->info->title); return; } } @@ -1099,8 +1101,9 @@ typedef struct char url[256]; char badge_basepath[PATH_MAX_LENGTH]; char badge_fullpath[PATH_MAX_LENGTH]; + unsigned char last_hash[16]; unsigned char hash[16]; - bool round; + unsigned ext_hash; unsigned gameid; unsigned i; unsigned j; @@ -1133,14 +1136,14 @@ typedef struct enum { /* Negative values because CORO_SUB generates positive values */ - RCHEEVOS_SNES_MD5 = -1, - RCHEEVOS_GENESIS_MD5 = -2, + RCHEEVOS_GENERIC_MD5 = -1, + RCHEEVOS_SNES_MD5 = -2, RCHEEVOS_LYNX_MD5 = -3, RCHEEVOS_NES_MD5 = -4, - RCHEEVOS_GENERIC_MD5 = -5, - RCHEEVOS_FILENAME_MD5 = -6, + RCHEEVOS_PSX_MD5 = -5, + RCHEEVOS_ARCADE_MD5 = -6, RCHEEVOS_EVAL_MD5 = -7, - RCHEEVOS_FILL_MD5 = -8, + RCHEEVOS_SEGACD_MD5 = -8, RCHEEVOS_GET_GAMEID = -9, RCHEEVOS_GET_CHEEVOS = -10, RCHEEVOS_GET_BADGES = -11, @@ -1148,8 +1151,7 @@ enum RCHEEVOS_HTTP_GET = -13, RCHEEVOS_DEACTIVATE = -14, RCHEEVOS_PLAYING = -15, - RCHEEVOS_DELAY = -16, - RCHEEVOS_PSX_MD5 = -17 + RCHEEVOS_DELAY = -16 }; static int rcheevos_iterate(rcheevos_coro_t* coro) @@ -1166,21 +1168,6 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) char* scan = NULL; char buffer[2048]; - static const uint32_t genesis_exts[] = - { - 0x0b888feeU, /* mdx */ - 0x005978b6U, /* md */ - 0x0b88aa89U, /* smd */ - 0x0b88767fU, /* gen */ - 0x0b8861beU, /* bin */ - 0x0b886782U, /* cue */ - 0x0b8880d0U, /* iso */ - 0x0b88aa98U, /* sms */ - 0x005977f3U, /* gg */ - 0x0059797fU, /* sg */ - 0 - }; - static const uint32_t snes_exts[] = { 0x0b88aa88U, /* smc */ @@ -1194,6 +1181,12 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) 0 }; + static const uint32_t nes_exts[] = + { + 0x0b88944bU, /* nes */ + 0 + }; + static const uint32_t lynx_exts[] = { 0x0b888cf7U, /* lnx */ @@ -1211,15 +1204,29 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) 0 }; + static const uint32_t segacd_exts[] = + { + 0x0b886782U, /* cue */ + 0x0b8880d0U, /* iso */ + 0x0b8865d4U, /* chd */ + 0 + }; + + static const uint32_t arcade_exts[] = + { + 0x0b88c7d8U, /* zip */ + 0 + }; + static rcheevos_finder_t finders[] = { {RCHEEVOS_SNES_MD5, "SNES (discards header)", snes_exts}, - {RCHEEVOS_GENESIS_MD5, "Genesis (6Mb padding)", genesis_exts}, {RCHEEVOS_LYNX_MD5, "Atari Lynx (discards header)", lynx_exts}, - {RCHEEVOS_NES_MD5, "NES (discards header)", NULL}, + {RCHEEVOS_NES_MD5, "NES (discards header)", nes_exts}, {RCHEEVOS_PSX_MD5, "Playstation (main executable)", psx_exts}, - {RCHEEVOS_GENERIC_MD5, "Generic (plain content)", NULL}, - {RCHEEVOS_FILENAME_MD5, "Generic (filename)", NULL} + {RCHEEVOS_SEGACD_MD5, "Sega CD/Saturn (first sector)", segacd_exts}, + {RCHEEVOS_ARCADE_MD5, "Arcade (filename)", arcade_exts}, + {RCHEEVOS_GENERIC_MD5, "Generic (plain content)", NULL} }; CORO_ENTER(); @@ -1286,14 +1293,44 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) CHEEVOS_FREE(coro->stream); } - /* Use the supported extensions as a hint - * to what method we should use. */ - core_get_system_info(&coro->sysinfo); - + /* Use the selected file's extension to determine which method to use */ for (coro->i = 0; coro->i < ARRAY_SIZE(finders); coro->i++) { if (finders[coro->i].ext_hashes) { + for (coro->j = 0; finders[coro->i].ext_hashes[coro->j]; coro->j++) + { + if (finders[coro->i].ext_hashes[coro->j] == coro->ext_hash) + { + CHEEVOS_LOG(RCHEEVOS_TAG "testing %s\n", finders[coro->i].name); + CORO_GOSUB(finders[coro->i].label); + + if (coro->gameid != 0) + goto found; + + break; + } + } + } + } + + /* Use the extensions supported by the core as a hint to what method we should use. */ + core_get_system_info(&coro->sysinfo); + CHEEVOS_LOG(RCHEEVOS_TAG "no method for file extension, trying core supported extensions: %s\n", coro->sysinfo.valid_extensions); + for (coro->i = 0; coro->i < ARRAY_SIZE(finders); coro->i++) + { + if (finders[coro->i].ext_hashes) + { + for (coro->j = 0; finders[coro->i].ext_hashes[coro->j]; coro->j++) + { + if (finders[coro->i].ext_hashes[coro->j] == coro->ext_hash) + break; + } + + /* did we already check this one? */ + if (finders[coro->i].ext_hashes[coro->j] == coro->ext_hash) + continue; + coro->ext = coro->sysinfo.valid_extensions; while (coro->ext) @@ -1316,13 +1353,7 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) { if (finders[coro->i].ext_hashes[coro->j] == hash) { - CHEEVOS_LOG(RCHEEVOS_TAG "testing %s\n", - finders[coro->i].name); - - /* - * Inputs: CHEEVOS_VAR_INFO - * Outputs: CHEEVOS_VAR_GAMEID, the game was found if it's different from 0 - */ + CHEEVOS_LOG(RCHEEVOS_TAG "testing %s\n", finders[coro->i].name); CORO_GOSUB(finders[coro->i].label); if (coro->gameid != 0) @@ -1336,18 +1367,13 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) } } + /* Try hashing methods not specifically tied to a file extension */ for (coro->i = 0; coro->i < ARRAY_SIZE(finders); coro->i++) { if (finders[coro->i].ext_hashes) continue; - CHEEVOS_LOG(RCHEEVOS_TAG "testing %s\n", - finders[coro->i].name); - - /* - * Inputs: CHEEVOS_VAR_INFO - * Outputs: CHEEVOS_VAR_GAMEID - */ + CHEEVOS_LOG(RCHEEVOS_TAG "testing %s\n", finders[coro->i].name); CORO_GOSUB(finders[coro->i].label); if (coro->gameid != 0) @@ -1450,11 +1476,12 @@ found: CORO_GOSUB(RCHEEVOS_GET_BADGES); CORO_STOP(); - /************************************************************************** - * Info Tries to identify a SNES game - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ + + /************************************************************************** + * Info Tries to identify a SNES game + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ CORO_SUB(RCHEEVOS_SNES_MD5) MD5_Init(&coro->md5); @@ -1462,6 +1489,7 @@ found: Unheadered files fall back to RCHEEVOS_GENERIC_MD5. */ if (coro->len < 0x2000 || coro->len % 0x2000 != snes_header_len) { + CHEEVOS_LOG(RCHEEVOS_TAG "could not locate SNES header\n", coro->gameid); coro->gameid = 0; CORO_RET(); } @@ -1474,41 +1502,12 @@ found: CORO_GOTO(RCHEEVOS_GET_GAMEID); - /************************************************************************** - * Info Tries to identify a Genesis game - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ - CORO_SUB(RCHEEVOS_GENESIS_MD5) - MD5_Init(&coro->md5); - - coro->offset = 0; - coro->count = 0; - CORO_GOSUB(RCHEEVOS_EVAL_MD5); - - if (coro->count == 0) - { - MD5_Final(coro->hash, &coro->md5); - coro->gameid = 0; - CORO_RET(); - } - - if (coro->count < CHEEVOS_MB(6)) - { - coro->offset = 0; - coro->count = CHEEVOS_MB(6) - coro->count; - CORO_GOSUB(RCHEEVOS_FILL_MD5); - } - - MD5_Final(coro->hash, &coro->md5); - CORO_GOTO(RCHEEVOS_GET_GAMEID); - - /************************************************************************** - * Info Tries to identify an Atari Lynx game - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ + /************************************************************************** + * Info Tries to identify an Atari Lynx game + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ CORO_SUB(RCHEEVOS_LYNX_MD5) /* Checks for the existence of a headered Lynx file. @@ -1516,6 +1515,7 @@ found: if (coro->len <= (unsigned)lynx_header_len || memcmp("LYNX", (void *)coro->data, 5) != 0) { + CHEEVOS_LOG(RCHEEVOS_TAG "could not locate LYNX header\n", coro->gameid); coro->gameid = 0; CORO_RET(); } @@ -1528,11 +1528,12 @@ found: MD5_Final(coro->hash, &coro->md5); CORO_GOTO(RCHEEVOS_GET_GAMEID); - /************************************************************************** - * Info Tries to identify a NES game - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ + + /************************************************************************** + * Info Tries to identify a NES game + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ CORO_SUB(RCHEEVOS_NES_MD5) /* Checks for the existence of a headered NES file. @@ -1552,6 +1553,7 @@ found: || coro->header.id[3] != 0x1a) { coro->gameid = 0; + CHEEVOS_LOG(RCHEEVOS_TAG "could not locate NES header\n", coro->gameid); CORO_RET(); } @@ -1564,6 +1566,57 @@ found: CORO_GOTO(RCHEEVOS_GET_GAMEID); + /************************************************************************** + * Info Tries to identify a Sega CD game + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ + CORO_SUB(RCHEEVOS_SEGACD_MD5) + { + /* ignore bin files less than 16MB - they're probably a ROM, not a CD */ + if (coro->ext_hash == 0x0b8861beU && coro->len < CHEEVOS_MB(16)) + { + CHEEVOS_LOG(RCHEEVOS_TAG "ignoring small BIN file - assuming not CD\n", coro->gameid); + coro->gameid = 0; + CORO_RET(); + } + + MD5_Init(&coro->md5); + + /* find the data track - it should be the first one */ + coro->stream = cdfs_open_data_track(coro->path); + if (coro->stream) + { + /* open the raw CD */ + if (cdfs_open_file(&coro->cdfp, coro->stream, NULL)) + { + coro->count = 512; + free(coro->data); + coro->data = (uint8_t*)malloc(coro->count); + cdfs_read_file(&coro->cdfp, coro->data, coro->count); + coro->len = coro->count; + + CORO_GOSUB(RCHEEVOS_EVAL_MD5); + MD5_Final(coro->hash, &coro->md5); + + cdfs_close_file(&coro->cdfp); + + intfstream_close(coro->stream); + CHEEVOS_FREE(coro->stream); + + CORO_GOTO(RCHEEVOS_GET_GAMEID); + } + + intfstream_close(coro->stream); + CHEEVOS_FREE(coro->stream); + } + + CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n", coro->gameid); + coro->gameid = 0; + CORO_RET(); + } + + /************************************************************************** * Info Tries to identify a Playstation game * Input CHEEVOS_VAR_INFO the content info @@ -1706,20 +1759,26 @@ found: } } + CHEEVOS_LOG(RCHEEVOS_TAG "could not locate primary executable\n", coro->gameid); + intfstream_close(coro->stream); CHEEVOS_FREE(coro->stream); } + else + { + CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n", coro->gameid); + } coro->gameid = 0; CORO_RET(); } - /************************************************************************** - * Info Tries to identify a "generic" game - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ + /************************************************************************** + * Info Tries to identify a game by examining the entire file (no special processing) + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ CORO_SUB(RCHEEVOS_GENERIC_MD5) MD5_Init(&coro->md5); @@ -1735,12 +1794,14 @@ found: CORO_GOTO(RCHEEVOS_GET_GAMEID); - /************************************************************************** - * Info Tries to identify a game based on its filename (with no extension) - * Input CHEEVOS_VAR_INFO the content info - * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found - *************************************************************************/ - CORO_SUB(RCHEEVOS_FILENAME_MD5) + + /************************************************************************** + * Info Tries to identify an arcade game based on its filename (with no extension). + * An arcade game "rom" is a zip file containing many ROMs. + * Input CHEEVOS_VAR_INFO the content info + * Output CHEEVOS_VAR_GAMEID the Retro Achievements game ID, or 0 if not found + *************************************************************************/ + CORO_SUB(RCHEEVOS_ARCADE_MD5) if (!string_is_empty(coro->path)) { char base_noext[PATH_MAX_LENGTH]; @@ -1754,11 +1815,12 @@ found: } CORO_RET(); - /************************************************************************** - * Info Evaluates the CHEEVOS_VAR_MD5 hash - * Inputs CHEEVOS_VAR_INFO, CHEEVOS_VAR_OFFSET, CHEEVOS_VAR_COUNT - * Outputs CHEEVOS_VAR_MD5, CHEEVOS_VAR_COUNT - *************************************************************************/ + + /************************************************************************** + * Info Evaluates the CHEEVOS_VAR_MD5 hash + * Inputs CHEEVOS_VAR_INFO, CHEEVOS_VAR_OFFSET, CHEEVOS_VAR_COUNT + * Outputs CHEEVOS_VAR_MD5, CHEEVOS_VAR_COUNT + *************************************************************************/ CORO_SUB(RCHEEVOS_EVAL_MD5) if (coro->count == 0) @@ -1776,40 +1838,24 @@ found: coro->count); CORO_RET(); - /************************************************************************** - * Info Updates the CHEEVOS_VAR_MD5 hash with a repeated value - * Inputs CHEEVOS_VAR_OFFSET, CHEEVOS_VAR_COUNT - * Outputs CHEEVOS_VAR_MD5 - *************************************************************************/ - CORO_SUB(RCHEEVOS_FILL_MD5) - { - char buffer[4096]; - - while (coro->count > 0) - { - size_t len = sizeof(buffer); - - if (len > coro->count) - len = coro->count; - - memset((void*)buffer, coro->offset, len); - MD5_Update(&coro->md5, (void*)buffer, len); - coro->count -= len; - } - } - - CORO_RET(); - - /************************************************************************** - * Info Gets the achievements from Retro Achievements - * Inputs coro->hash - * Outputs CHEEVOS_VAR_GAMEID - *************************************************************************/ + /************************************************************************** + * Info Gets the achievements from Retro Achievements + * Inputs coro->hash + * Outputs CHEEVOS_VAR_GAMEID + *************************************************************************/ CORO_SUB(RCHEEVOS_GET_GAMEID) { int size; + + if (memcmp(coro->last_hash, coro->hash, sizeof(coro->hash)) == 0) + { + CHEEVOS_LOG(RCHEEVOS_TAG "hash did not change, returning %u\n", coro->gameid); + CORO_RET(); + } + memcpy(coro->last_hash, coro->hash, sizeof(coro->hash)); + size = rc_url_get_gameid(coro->url, sizeof(coro->url), coro->hash); if (size < 0) @@ -1818,6 +1864,11 @@ found: CORO_RET(); } + CHEEVOS_LOG(RCHEEVOS_TAG "checking %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + coro->hash[0], coro->hash[1], coro->hash[2], coro->hash[3], + coro->hash[4], coro->hash[5], coro->hash[6], coro->hash[7], + coro->hash[8], coro->hash[9], coro->hash[10], coro->hash[11], + coro->hash[12], coro->hash[13], coro->hash[14], coro->hash[15]); rcheevos_log_url(RCHEEVOS_TAG "rc_url_get_gameid: %s\n", coro->url); CORO_GOSUB(RCHEEVOS_HTTP_GET); @@ -1831,11 +1882,12 @@ found: CORO_RET(); } - /************************************************************************** - * Info Gets the achievements from Retro Achievements - * Inputs CHEEVOS_VAR_GAMEID - * Outputs CHEEVOS_VAR_JSON - *************************************************************************/ + + /************************************************************************** + * Info Gets the achievements from Retro Achievements + * Inputs CHEEVOS_VAR_GAMEID + * Outputs CHEEVOS_VAR_JSON + *************************************************************************/ CORO_SUB(RCHEEVOS_GET_CHEEVOS) { int ret; @@ -1863,11 +1915,12 @@ found: CORO_RET(); } - /************************************************************************** - * Info Gets the achievements from Retro Achievements - * Inputs CHEEVOS_VAR_GAMEID - * Outputs CHEEVOS_VAR_JSON - *************************************************************************/ + + /************************************************************************** + * Info Gets the achievements from Retro Achievements + * Inputs CHEEVOS_VAR_GAMEID + * Outputs CHEEVOS_VAR_JSON + *************************************************************************/ CORO_SUB(RCHEEVOS_GET_BADGES) badges_ctx = new_badges_ctx; @@ -1958,9 +2011,10 @@ found: CORO_RET(); - /************************************************************************** - * Info Logs in the user at Retro Achievements - *************************************************************************/ + + /************************************************************************** + * Info Logs in the user at Retro Achievements + *************************************************************************/ CORO_SUB(RCHEEVOS_LOGIN) { const char* username; @@ -2050,9 +2104,10 @@ found: CORO_RET(); } - /************************************************************************** - * Info Pauses execution for five seconds - *************************************************************************/ + + /************************************************************************** + * Info Pauses execution for five seconds + *************************************************************************/ CORO_SUB(RCHEEVOS_DELAY) { @@ -2068,11 +2123,12 @@ found: CORO_RET(); - /************************************************************************** - * Info Makes a HTTP GET request - * Inputs CHEEVOS_VAR_URL - * Outputs CHEEVOS_VAR_JSON - *************************************************************************/ + + /************************************************************************** + * Info Makes a HTTP GET request + * Inputs CHEEVOS_VAR_URL + * Outputs CHEEVOS_VAR_JSON + *************************************************************************/ CORO_SUB(RCHEEVOS_HTTP_GET) for (coro->k = 0; coro->k < 5; coro->k++) @@ -2143,11 +2199,12 @@ found: CHEEVOS_LOG(RCHEEVOS_TAG "Couldn't connect to server after 5 tries\n"); CORO_RET(); - /************************************************************************** - * Info Deactivates the achievements already awarded - * Inputs CHEEVOS_VAR_GAMEID - * Outputs - *************************************************************************/ + + /************************************************************************** + * Info Deactivates the achievements already awarded + * Inputs CHEEVOS_VAR_GAMEID + * Outputs + *************************************************************************/ CORO_SUB(RCHEEVOS_DEACTIVATE) CORO_GOSUB(RCHEEVOS_LOGIN); @@ -2155,6 +2212,7 @@ found: int ret; unsigned mode; + /* Two calls - one for softcore and one for hardcore */ for (coro->i = 0; coro->i < 2; coro->i++) { ret = rc_url_get_unlock_list(coro->url, sizeof(coro->url), coro->settings->arrays.cheevos_username, rcheevos_locals.token, coro->gameid, coro->i); @@ -2181,11 +2239,12 @@ found: CORO_RET(); - /************************************************************************** - * Info Posts the "playing" activity to Retro Achievements - * Inputs CHEEVOS_VAR_GAMEID - * Outputs - *************************************************************************/ + + /************************************************************************** + * Info Posts the "playing" activity to Retro Achievements + * Inputs CHEEVOS_VAR_GAMEID + * Outputs + *************************************************************************/ CORO_SUB(RCHEEVOS_PLAYING) snprintf( @@ -2208,7 +2267,6 @@ found: else CHEEVOS_ERR(RCHEEVOS_TAG "error posting playing activity\n"); - CHEEVOS_LOG(RCHEEVOS_TAG "posted playing activity\n"); CORO_RET(); CORO_LEAVE(); @@ -2248,7 +2306,8 @@ bool rcheevos_load(const void *data) { retro_task_t *task; const struct retro_game_info *info = NULL; - rcheevos_coro_t *coro = NULL; + rcheevos_coro_t *coro = NULL; + char buffer[32]; rcheevos_loaded = false; rcheevos_hardcore_paused = false; @@ -2299,6 +2358,12 @@ bool rcheevos_load(const void *data) coro->path = strdup(info->path); } + strncpy(buffer, path_get_extension(info->path), sizeof(buffer)); + buffer[sizeof(buffer) - 1] = '\0'; + string_to_lower(buffer); + coro->ext_hash = rcheevos_djb2(buffer, strlen(buffer)); + CHEEVOS_LOG(RCHEEVOS_TAG "ext_hash %08x ('%s')\n", coro->ext_hash, buffer); + task->handler = rcheevos_task_handler; task->state = (void*)coro; task->mute = true; diff --git a/libretro-common/formats/cdfs/cdfs.c b/libretro-common/formats/cdfs/cdfs.c index 36f4ae251e..7d7faa32cd 100644 --- a/libretro-common/formats/cdfs/cdfs.c +++ b/libretro-common/formats/cdfs/cdfs.c @@ -151,7 +151,7 @@ static int cdfs_find_file(cdfs_file_t* file, const char* path) int cdfs_open_file(cdfs_file_t* file, intfstream_t* stream, const char* path) { - if (!file || !stream || !path) + if (!file || !stream) return 0; memset(file, 0, sizeof(*file)); @@ -160,16 +160,28 @@ int cdfs_open_file(cdfs_file_t* file, intfstream_t* stream, const char* path) cdfs_determine_sector_size(file); file->current_sector = -1; - file->first_sector = cdfs_find_file(file, path); + if (path != NULL) + { + file->first_sector = cdfs_find_file(file, path); + } + else if (file->stream_sector_size) + { + file->first_sector = 0; + file->size = (intfstream_get_size(file->stream) / file->stream_sector_size) * 2048; + } + else + { + file->first_sector = -1; + } - return (file->first_sector > 0); + return (file->first_sector >= 0); } int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len) { int bytes_read = 0; - if (!file || !file->first_sector || !buffer) + if (!file || file->first_sector < 0 || !buffer) return 0; if (len > file->size - file->pos) @@ -240,13 +252,13 @@ void cdfs_close_file(cdfs_file_t* file) if (file) { /* not really anything to do here, just clear out the first_sector so read() won't do anything */ - file->first_sector = 0; + file->first_sector = -1; } } int64_t cdfs_get_size(cdfs_file_t* file) { - if (!file || !file->first_sector) + if (!file || file->first_sector < 0) return 0; return file->size; @@ -254,7 +266,7 @@ int64_t cdfs_get_size(cdfs_file_t* file) int64_t cdfs_tell(cdfs_file_t* file) { - if (!file || !file->first_sector) + if (!file || file->first_sector < 0) return -1; return file->pos; @@ -265,7 +277,7 @@ int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence) int64_t new_pos; int new_sector; - if (!file || !file->first_sector) + if (!file || file->first_sector < 0) return -1; switch (whence) @@ -459,8 +471,8 @@ intfstream_t* cdfs_open_data_track(const char* path) return intfstream_open_chd_track(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, 1); } - /* unsupported file type */ - return NULL; + /* unsupported file type - try opening as a raw track */ + return cdfs_open_raw_track(path); } intfstream_t* cdfs_open_raw_track(const char* path) diff --git a/libretro-common/include/formats/cdfs.h b/libretro-common/include/formats/cdfs.h index ff9cd0da0f..efe01cff66 100644 --- a/libretro-common/include/formats/cdfs.h +++ b/libretro-common/include/formats/cdfs.h @@ -45,6 +45,10 @@ typedef struct cdfs_file_t uint8_t sector_buffer[2048]; } cdfs_file_t; +/* opens the specified file within the CD or virtual CD. + * if path is NULL, will open the raw CD (useful for reading CD without having to worry about sector sizes, + * headers, or checksum data) + */ int cdfs_open_file(cdfs_file_t* file, intfstream_t* stream, const char* path); void cdfs_close_file(cdfs_file_t* file);