From 0b92224c36f2a2f800f912e42766cff769670e2d Mon Sep 17 00:00:00 2001 From: Jamiras Date: Thu, 10 Oct 2019 20:21:46 -0600 Subject: [PATCH] update PCEngine-CD hash algorithm; pregap cdfs support for CUE and CHD files --- cheevos-new/cheevos.c | 179 +++++++++++++----- libretro-common/formats/cdfs/cdfs.c | 161 ++++++++++++---- libretro-common/formats/libchdr/libchdr_chd.c | 2 + libretro-common/include/formats/cdfs.h | 25 ++- libretro-common/include/streams/chd_stream.h | 2 + .../include/streams/interface_stream.h | 2 + libretro-common/streams/chd_stream.c | 19 +- libretro-common/streams/interface_stream.c | 10 + 8 files changed, 304 insertions(+), 96 deletions(-) diff --git a/cheevos-new/cheevos.c b/cheevos-new/cheevos.c index 7525165fdb..a8c5311d9e 100644 --- a/cheevos-new/cheevos.c +++ b/cheevos-new/cheevos.c @@ -1127,6 +1127,7 @@ typedef struct struct http_connection_t *conn; struct http_t *http; const rcheevos_cheevo_t *cheevo_end; + cdfs_track_t *track; cdfs_file_t cdfp; /* co-routine required fields */ @@ -1151,7 +1152,8 @@ enum RCHEEVOS_HTTP_GET = -13, RCHEEVOS_DEACTIVATE = -14, RCHEEVOS_PLAYING = -15, - RCHEEVOS_DELAY = -16 + RCHEEVOS_DELAY = -16, + RCHEEVOS_PCE_CD_MD5 = -17 }; static int rcheevos_iterate(rcheevos_coro_t* coro) @@ -1212,6 +1214,13 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) 0 }; + static const uint32_t pce_cd_exts[] = + { + 0x0b886782U, /* cue */ + 0x0b8865d4U, /* chd */ + 0 + }; + static const uint32_t arcade_exts[] = { 0x0b88c7d8U, /* zip */ @@ -1224,6 +1233,7 @@ static int rcheevos_iterate(rcheevos_coro_t* coro) {RCHEEVOS_LYNX_MD5, "Atari Lynx (discards header)", lynx_exts}, {RCHEEVOS_NES_MD5, "NES (discards header)", nes_exts}, {RCHEEVOS_PSX_MD5, "Playstation (main executable)", psx_exts}, + {RCHEEVOS_PCE_CD_MD5, "PC Engine CD (boot sector)", pce_cd_exts}, {RCHEEVOS_SEGACD_MD5, "Sega CD/Saturn (first sector)", segacd_exts}, {RCHEEVOS_ARCADE_MD5, "Arcade (filename)", arcade_exts}, {RCHEEVOS_GENERIC_MD5, "Generic (plain content)", NULL} @@ -1584,11 +1594,11 @@ found: 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) + coro->track = cdfs_open_data_track(coro->path); + if (coro->track) { /* open the raw CD */ - if (cdfs_open_file(&coro->cdfp, coro->stream, NULL)) + if (cdfs_open_file(&coro->cdfp, coro->track, NULL)) { coro->count = 512; free(coro->data); @@ -1601,14 +1611,87 @@ found: cdfs_close_file(&coro->cdfp); - intfstream_close(coro->stream); - CHEEVOS_FREE(coro->stream); + cdfs_close_track(coro->track); + coro->track = NULL; CORO_GOTO(RCHEEVOS_GET_GAMEID); } - intfstream_close(coro->stream); - CHEEVOS_FREE(coro->stream); + cdfs_close_track(coro->track); + coro->track = NULL; + } + + CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n", coro->gameid); + coro->gameid = 0; + CORO_RET(); + } + + + /************************************************************************** + * Info Tries to identify a PC Engine 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_PCE_CD_MD5) + { + MD5_Init(&coro->md5); + + /* find the data track - it should be the second one */ + coro->track = cdfs_open_data_track(coro->path); + if (coro->track) + { + /* open the raw CD */ + if (cdfs_open_file(&coro->cdfp, coro->track, NULL)) + { + /* the PC-Engine uses the second sector to specify boot information and program name. + * the string "PC Engine CD-ROM SYSTEM" should exist at 32 bytes into the sector + * http://shu.sheldows.com/shu/download/pcedocs/pce_cdrom.html + */ + cdfs_seek_sector(&coro->cdfp, 1); + cdfs_read_file(&coro->cdfp, buffer, 128); + + if (strncmp("PC Engine CD-ROM SYSTEM", (const char*)& buffer[32], 23) != 0) + { + CHEEVOS_LOG(RCHEEVOS_TAG "not a PC Engine CD\n", coro->gameid); + + cdfs_close_track(coro->track); + coro->track = NULL; + + coro->gameid = 0; + CORO_RET(); + } + + { + /* the first three bytes specify the sector of the program data, and the fourth byte + * is the number of sectors. + */ + const unsigned int first_sector = buffer[0] * 65536 + buffer[1] * 256 + buffer[2]; + cdfs_seek_sector(&coro->cdfp, first_sector); + + to_read = buffer[3] * 2048; + } + + coro->count = to_read + 22; + free(coro->data); + coro->data = (uint8_t*)malloc(coro->count); + memcpy(coro->data, &buffer[106], 22); + + cdfs_read_file(&coro->cdfp, ((uint8_t*)coro->data) + 22, to_read); + coro->len = coro->count; + + 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); + } + + cdfs_close_track(coro->track); + coro->track = NULL; } CHEEVOS_LOG(RCHEEVOS_TAG "could not open CD\n", coro->gameid); @@ -1626,41 +1709,12 @@ found: { MD5_Init(&coro->md5); - /* if we're looking at an m3u file, get the first disc from the playlist */ - end = path_get_extension(coro->path); - if (string_is_equal_noncase(end, "m3u")) - { - intfstream_t* m3u_stream = intfstream_open_file(coro->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); - if (m3u_stream) - { - char disc_path[PATH_MAX_LENGTH]; - char* tmp; - - num_read = intfstream_read(m3u_stream, buffer, sizeof(buffer)); - intfstream_close(m3u_stream); - buffer[num_read] = '\0'; - - tmp = buffer; - while (*tmp && *tmp != '\n') - ++tmp; - if (tmp > buffer && tmp[-1] == '\r') - --tmp; - *tmp = '\0'; - - fill_pathname_basedir(disc_path, coro->path, sizeof(disc_path)); - strlcat(disc_path, buffer, sizeof(disc_path)); - - free((void*)coro->path); - coro->path = strdup(disc_path); - } - } - /* find the data track - it should be the first one */ - coro->stream = cdfs_open_data_track(coro->path); - if (coro->stream) + coro->track = cdfs_open_data_track(coro->path); + if (coro->track) { /* open the SYSTEM.CNF file and find the BOOT= record */ - if (cdfs_open_file(&coro->cdfp, coro->stream, "SYSTEM.CNF")) + if (cdfs_open_file(&coro->cdfp, coro->track, "SYSTEM.CNF")) { cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); @@ -1703,7 +1757,7 @@ found: 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->stream, exe_name_buffer)) + if (exe_name_buffer[0] && cdfs_open_file(&coro->cdfp, coro->track, exe_name_buffer)) { cdfs_read_file(&coro->cdfp, buffer, sizeof(buffer)); @@ -1750,8 +1804,8 @@ found: cdfs_close_file(&coro->cdfp); - intfstream_close(coro->stream); - CHEEVOS_FREE(coro->stream); + cdfs_close_track(coro->track); + coro->track = NULL; CORO_GOTO(RCHEEVOS_GET_GAMEID); } @@ -1761,8 +1815,8 @@ found: CHEEVOS_LOG(RCHEEVOS_TAG "could not locate primary executable\n", coro->gameid); - intfstream_close(coro->stream); - CHEEVOS_FREE(coro->stream); + cdfs_close_track(coro->track); + coro->track = NULL; } else { @@ -2331,6 +2385,7 @@ bool rcheevos_load(const void *data) CORO_SETUP(); info = (const struct retro_game_info*)data; + strncpy(buffer, path_get_extension(info->path), sizeof(buffer)); if (info->data) { @@ -2356,9 +2411,41 @@ bool rcheevos_load(const void *data) { coro->data = NULL; coro->path = strdup(info->path); + + /* if we're looking at an m3u file, get the first disc from the playlist */ + const char* end = path_get_extension(coro->path); + if (string_is_equal_noncase(end, "m3u")) + { + intfstream_t* m3u_stream = intfstream_open_file(coro->path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (m3u_stream) + { + char m3u_contents[1024]; + char disc_path[PATH_MAX_LENGTH]; + char* tmp; + int64_t num_read; + + num_read = intfstream_read(m3u_stream, m3u_contents, sizeof(m3u_contents) - 1); + intfstream_close(m3u_stream); + m3u_contents[num_read] = '\0'; + + tmp = m3u_contents; + while (*tmp && *tmp != '\n') + ++tmp; + if (tmp > buffer && tmp[-1] == '\r') + --tmp; + *tmp = '\0'; + + fill_pathname_basedir(disc_path, coro->path, sizeof(disc_path)); + strlcat(disc_path, m3u_contents, sizeof(disc_path)); + + free((void*)coro->path); + coro->path = strdup(disc_path); + + strncpy(buffer, path_get_extension(disc_path), sizeof(buffer)); + } + } } - 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)); diff --git a/libretro-common/formats/cdfs/cdfs.c b/libretro-common/formats/cdfs/cdfs.c index 7d7faa32cd..045729c87e 100644 --- a/libretro-common/formats/cdfs/cdfs.c +++ b/libretro-common/formats/cdfs/cdfs.c @@ -5,10 +5,15 @@ #include #include -static void cdfs_determine_sector_size(cdfs_file_t* file) +#ifdef HAVE_CHD +#include +#endif + +static void cdfs_determine_sector_size(cdfs_track_t* track) { uint8_t buffer[32]; int64_t stream_size; + const int toc_sector = track->pregap_sectors + 16; /* MODE information is normally found in the CUE sheet, but we can try to determine it from the raw data. * @@ -23,23 +28,23 @@ static void cdfs_determine_sector_size(cdfs_file_t* file) */ /* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */ - intfstream_seek(file->stream, 16 * 2352, SEEK_SET); - if (intfstream_read(file->stream, buffer, sizeof(buffer)) < sizeof(buffer)) + intfstream_seek(track->stream, toc_sector * 2352, SEEK_SET); + if (intfstream_read(track->stream, buffer, sizeof(buffer)) < sizeof(buffer)) return; /* if this is a CDROM-XA data source, the "CD001" tag will be 25 bytes into the sector */ if (buffer[25] == 0x43 && buffer[26] == 0x44 && buffer[27] == 0x30 && buffer[28] == 0x30 && buffer[29] == 0x31) { - file->stream_sector_size = 2352; - file->stream_sector_header_size = 24; + track->stream_sector_size = 2352; + track->stream_sector_header_size = 24; } /* otherwise it should be 17 bytes into the sector */ else if (buffer[17] == 0x43 && buffer[18] == 0x44 && buffer[19] == 0x30 && buffer[20] == 0x30 && buffer[21] == 0x31) { - file->stream_sector_size = 2352; - file->stream_sector_header_size = 16; + track->stream_sector_size = 2352; + track->stream_sector_header_size = 16; } else { @@ -48,40 +53,56 @@ static void cdfs_determine_sector_size(cdfs_file_t* file) buffer[4] == 0xFF && buffer[5] == 0xFF && buffer[6] == 0xFF && buffer[7] == 0xFF && buffer[8] == 0xFF && buffer[9] == 0xFF && buffer[10] == 0xFF && buffer[11] == 0) { - /* don't actually expect to get here - a properly headered sector should have had the CD001 tag */ + /* if we didn't find a CD001 tag, this format may predate ISO-9660 */ /* after the 12 byte sync pattern is three bytes identifying the sector and then one byte for the mode (total 16 bytes) */ - file->stream_sector_size = 2352; - file->stream_sector_header_size = 16; + track->stream_sector_size = 2352; + track->stream_sector_header_size = 16; } else { /* no recognizable header - attempt to determine sector size from stream size */ - stream_size = intfstream_get_size(file->stream); + stream_size = intfstream_get_size(track->stream); if ((stream_size % 2352) == 0) { /* audio tracks use all 2352 bytes without a header */ - file->stream_sector_size = 2352; + track->stream_sector_size = 2352; } else if ((stream_size % 2048) == 0) { /* cooked tracks eliminate all header/footer data */ - file->stream_sector_size = 2048; + track->stream_sector_size = 2048; } else if ((stream_size % 2336) == 0) { /* MODE 2 format without 16-byte sync data */ - file->stream_sector_size = 2336; - file->stream_sector_header_size = 8; + track->stream_sector_size = 2336; + track->stream_sector_header_size = 8; } } } } -static void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector) +static void cdfs_seek_track_sector(cdfs_track_t* track, unsigned int sector) { - intfstream_seek(file->stream, sector * file->stream_sector_size + file->stream_sector_header_size, SEEK_SET); + intfstream_seek(track->stream, (sector + track->pregap_sectors) * track->stream_sector_size + track->stream_sector_header_size, SEEK_SET); +} + +void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector) +{ + /* only allowed if open_file was called with a NULL path */ + if (file->first_sector == 0) + { + if (sector != file->current_sector) + { + file->current_sector = sector; + file->sector_buffer_valid = 0; + } + + file->pos = file->current_sector * 2048; + file->current_sector_offset = 0; + } } static int cdfs_find_file(cdfs_file_t* file, const char* path) @@ -108,8 +129,8 @@ static int cdfs_find_file(cdfs_file_t* file, const char* path) int offset; /* find the cd information (always 16 frames in) */ - cdfs_seek_sector(file, 16); - intfstream_read(file->stream, buffer, sizeof(buffer)); + cdfs_seek_track_sector(file->track, 16); + intfstream_read(file->track->stream, buffer, sizeof(buffer)); /* the directory_record starts at 156 bytes into the sector. * the sector containing the root directory contents is a 3 byte value that is 2 bytes into the directory_record. */ @@ -118,8 +139,8 @@ static int cdfs_find_file(cdfs_file_t* file, const char* path) } /* process the contents of the directory */ - cdfs_seek_sector(file, sector); - intfstream_read(file->stream, buffer, sizeof(buffer)); + cdfs_seek_track_sector(file->track, sector); + intfstream_read(file->track->stream, buffer, sizeof(buffer)); path_length = strlen(path); tmp = buffer; @@ -149,25 +170,24 @@ static int cdfs_find_file(cdfs_file_t* file, const char* path) return -1; } -int cdfs_open_file(cdfs_file_t* file, intfstream_t* stream, const char* path) +int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* track, const char* path) { - if (!file || !stream) + if (!file || !track) return 0; memset(file, 0, sizeof(*file)); - file->stream = stream; - cdfs_determine_sector_size(file); + file->track = track; file->current_sector = -1; if (path != NULL) { file->first_sector = cdfs_find_file(file, path); } - else if (file->stream_sector_size) + else if (file->track->stream_sector_size) { file->first_sector = 0; - file->size = (intfstream_get_size(file->stream) / file->stream_sector_size) * 2048; + file->size = (intfstream_get_size(file->track->stream) / file->track->stream_sector_size) * 2048; } else { @@ -222,8 +242,8 @@ int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len) while (len >= 2048) { - cdfs_seek_sector(file, file->current_sector); - intfstream_read(file->stream, buffer, 2048); + cdfs_seek_track_sector(file->track, file->current_sector); + intfstream_read(file->track->stream, buffer, 2048); buffer = (char*)buffer + 2048; bytes_read += 2048; @@ -234,8 +254,8 @@ int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len) if (len > 0) { - cdfs_seek_sector(file, file->current_sector); - intfstream_read(file->stream, file->sector_buffer, 2048); + cdfs_seek_track_sector(file->track, file->current_sector); + intfstream_read(file->track->stream, file->sector_buffer, 2048); memcpy(buffer, file->sector_buffer, len); file->current_sector_offset = len; file->sector_buffer_valid = 1; @@ -322,7 +342,21 @@ static void cdfs_skip_spaces(const char** ptr) ++(*ptr); } -static intfstream_t* cdfs_open_cue_track(const char* path, unsigned int track_index) +static cdfs_track_t* cdfs_wrap_stream(intfstream_t* stream, unsigned pregap_sectors) +{ + cdfs_track_t* track; + + if (stream == NULL) + return NULL; + + track = (cdfs_track_t*)calloc(1, sizeof(*track)); + track->stream = stream; + track->pregap_sectors = pregap_sectors; + cdfs_determine_sector_size(track); + return track; +} + +static cdfs_track_t* cdfs_open_cue_track(const char* path, unsigned int track_index) { char* cue_contents = NULL; char* cue = NULL; @@ -331,7 +365,9 @@ static intfstream_t* cdfs_open_cue_track(const char* path, unsigned int track_in char current_track_path[PATH_MAX_LENGTH] = {0}; char track_path[PATH_MAX_LENGTH] = {0}; intfstream_t* cue_stream = NULL; + cdfs_track_t* track = NULL; int64_t stream_size = 0; + unsigned int pregap_sectors = 0; cue_stream = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); @@ -420,6 +456,14 @@ static intfstream_t* cdfs_open_cue_track(const char* path, unsigned int track_in if (index_number == 1) { + unsigned min = 0, sec = 0, frame = 0; + const char* ptr = index; + while (*ptr && *ptr != ' ' && *ptr != '\n') + ++ptr; + cdfs_skip_spaces(&ptr); + sscanf(ptr, "%u:%u:%u", &min, &sec, &frame); + pregap_sectors = ((min * 60) + sec) * 75 + frame; + if (strstr(current_track_path, "/") || strstr(current_track_path, "\\")) { strncpy(track_path, current_track_path, sizeof(track_path)); @@ -441,47 +485,80 @@ static intfstream_t* cdfs_open_cue_track(const char* path, unsigned int track_in if (string_is_empty(track_path)) return NULL; - return intfstream_open_file(track_path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + return cdfs_wrap_stream(intfstream_open_file(track_path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE), pregap_sectors); } -intfstream_t* cdfs_open_track(const char* path, unsigned int track_index) +#ifdef HAVE_CHD +static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index) { + intfstream_t* intf_stream; + cdfs_track_t* track; + unsigned int pregap_sectors; + + intf_stream = intfstream_open_chd_track(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, track_index); + if (!intf_stream) + return NULL; + + pregap_sectors = intfstream_get_chd_pregap(intf_stream); + + track = cdfs_wrap_stream(intf_stream, pregap_sectors); + + /* CHD removes the markers from the header, so we can't detect the header size, just assume its 16 bytes */ + if (track->stream_sector_header_size == 0) + track->stream_sector_header_size = 16; + return track; +} +#endif + +struct cdfs_track_t* cdfs_open_track(const char* path, unsigned int track_index) +{ + intfstream_t* stream = NULL; const char* ext = path_get_extension(path); if (string_is_equal_noncase(ext, "cue")) return cdfs_open_cue_track(path, track_index); +#ifdef HAVE_CHD if (string_is_equal_noncase(ext, "chd")) - return intfstream_open_chd_track(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, track_index); + return cdfs_open_chd_track(path, track_index); +#endif /* unsupported file type */ return NULL; } -intfstream_t* cdfs_open_data_track(const char* path) +struct cdfs_track_t* cdfs_open_data_track(const char* path) { const char* ext = path_get_extension(path); if (string_is_equal_noncase(ext, "cue")) return cdfs_open_cue_track(path, 0); +#ifdef HAVE_CHD if (string_is_equal_noncase(ext, "chd")) - { - /* TODO: determine data track */ - return intfstream_open_chd_track(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, 1); - } + return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY); +#endif /* unsupported file type - try opening as a raw track */ return cdfs_open_raw_track(path); } -intfstream_t* cdfs_open_raw_track(const char* path) +cdfs_track_t* cdfs_open_raw_track(const char* path) { const char* ext = path_get_extension(path); if (string_is_equal_noncase(ext, "bin") || string_is_equal_noncase(ext, "iso")) - return intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + return cdfs_wrap_stream(intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE), 0); /* unsupported file type */ return NULL; } + +void cdfs_close_track(cdfs_track_t* track) +{ + if (track) + { + intfstream_close(track->stream); + free(track); + } +} diff --git a/libretro-common/formats/libchdr/libchdr_chd.c b/libretro-common/formats/libchdr/libchdr_chd.c index 1f36c060ee..d076ee1291 100644 --- a/libretro-common/formats/libchdr/libchdr_chd.c +++ b/libretro-common/formats/libchdr/libchdr_chd.c @@ -1682,6 +1682,8 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des bytes = read_compressed(chd, blockoffs, blocklen); if (bytes == NULL) return CHDERR_READ_ERROR; + if (!chd->codecintf[rawmap[0]]) + return CHDERR_UNSUPPORTED_FORMAT; switch (chd->codecintf[rawmap[0]]->compression) { case CHD_CODEC_CD_LZMA: diff --git a/libretro-common/include/formats/cdfs.h b/libretro-common/include/formats/cdfs.h index efe01cff66..b5d7a90f04 100644 --- a/libretro-common/include/formats/cdfs.h +++ b/libretro-common/include/formats/cdfs.h @@ -31,25 +31,31 @@ RETRO_BEGIN_DECLS * of a CD (following the ISO-9660 directory structure definition) */ +typedef struct cdfs_track_t +{ + intfstream_t* stream; + unsigned int stream_sector_size; + unsigned int stream_sector_header_size; + unsigned int pregap_sectors; +} cdfs_track_t; + typedef struct cdfs_file_t { int first_sector; int current_sector; unsigned int current_sector_offset; int sector_buffer_valid; - unsigned int stream_sector_size; - unsigned int stream_sector_header_size; unsigned int size; unsigned int pos; - intfstream_t* stream; uint8_t sector_buffer[2048]; + struct cdfs_track_t* track; } 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); +int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* stream, const char* path); void cdfs_close_file(cdfs_file_t* file); @@ -61,6 +67,8 @@ int64_t cdfs_tell(cdfs_file_t* file); int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence); +void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector); + /* opens the specified track in a CD or virtual CD file - the resulting stream should be passed to * cdfs_open_file to get access to a file within the CD. * @@ -75,11 +83,11 @@ int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence); * MODE1/2048 - untested * MODE2/2336 - untested */ -intfstream_t* cdfs_open_track(const char* path, unsigned int track_index); +cdfs_track_t* cdfs_open_track(const char* path, unsigned int track_index); /* opens the first data track in a CD or virtual CD file. see cdfs_open_track for supported file formats */ -intfstream_t* cdfs_open_data_track(const char* path); +cdfs_track_t* cdfs_open_data_track(const char* path); /* opens a raw track file for a CD or virtual CD. * @@ -89,7 +97,10 @@ intfstream_t* cdfs_open_data_track(const char* path); * bin - path will point to the bin file * iso - path will point to the iso file */ -intfstream_t* cdfs_open_raw_track(const char* path); +cdfs_track_t* cdfs_open_raw_track(const char* path); + +/* closes the CD or virtual CD track and frees the associated memory */ +void cdfs_close_track(cdfs_track_t* track); RETRO_END_DECLS diff --git a/libretro-common/include/streams/chd_stream.h b/libretro-common/include/streams/chd_stream.h index 246a2dcfc9..3b0ac4e131 100644 --- a/libretro-common/include/streams/chd_stream.h +++ b/libretro-common/include/streams/chd_stream.h @@ -57,6 +57,8 @@ int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence); ssize_t chdstream_get_size(chdstream_t *stream); +uint32_t chdstream_get_pregap(chdstream_t* stream); + RETRO_END_DECLS #endif diff --git a/libretro-common/include/streams/interface_stream.h b/libretro-common/include/streams/interface_stream.h index 5d7da563d1..58371e2ca0 100644 --- a/libretro-common/include/streams/interface_stream.h +++ b/libretro-common/include/streams/interface_stream.h @@ -96,6 +96,8 @@ int64_t intfstream_get_size(intfstream_internal_t *intf); int intfstream_flush(intfstream_internal_t *intf); +uint32_t intfstream_get_chd_pregap(intfstream_internal_t *intf); + intfstream_t* intfstream_open_file(const char *path, unsigned mode, unsigned hints); diff --git a/libretro-common/streams/chd_stream.c b/libretro-common/streams/chd_stream.c index 97d3eb4699..8a7f742453 100644 --- a/libretro-common/streams/chd_stream.c +++ b/libretro-common/streams/chd_stream.c @@ -426,5 +426,22 @@ int64_t chdstream_seek(chdstream_t *stream, int64_t offset, int whence) ssize_t chdstream_get_size(chdstream_t *stream) { - return stream->track_end; + return stream->track_end; +} + +uint32_t chdstream_get_pregap(chdstream_t *stream) +{ + metadata_t meta; + uint32_t frame_offset = 0; + uint32_t i; + + for (i = 0; chdstream_get_meta(stream->chd, i, &meta); ++i) + { + if (stream->track_frame == frame_offset) + return meta.pregap; + + frame_offset += meta.frames + meta.extra; + } + + return 0; } diff --git a/libretro-common/streams/interface_stream.c b/libretro-common/streams/interface_stream.c index 1ce6383936..752e23b5ee 100644 --- a/libretro-common/streams/interface_stream.c +++ b/libretro-common/streams/interface_stream.c @@ -421,6 +421,16 @@ void intfstream_putc(intfstream_internal_t *intf, int c) } } +uint32_t intfstream_get_chd_pregap(intfstream_internal_t *intf) +{ +#ifdef HAVE_CHD + if (intf->type == INTFSTREAM_CHD) + return chdstream_get_pregap(intf->chd.fp); +#endif + + return 0; +} + intfstream_t* intfstream_open_file(const char *path, unsigned mode, unsigned hints) {