From 8af30789667f3f43d25855d05f2e8dff21f01f67 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Tue, 25 Jun 2019 02:51:51 -0400 Subject: [PATCH] track1 playback is working, redbook might have issues currently --- Makefile.common | 3 +- libretro-common/cdrom/cdrom.c | 143 ++++++-- libretro-common/include/cdrom/cdrom.h | 2 +- .../include/vfs/vfs_implementation.h | 30 ++ .../include/vfs/vfs_implementation_cdrom.h | 46 +++ libretro-common/vfs/vfs_implementation.c | 279 +--------------- .../vfs/vfs_implementation_cdrom.c | 311 ++++++++++++++++++ 7 files changed, 514 insertions(+), 300 deletions(-) create mode 100644 libretro-common/include/vfs/vfs_implementation_cdrom.h create mode 100644 libretro-common/vfs/vfs_implementation_cdrom.c diff --git a/Makefile.common b/Makefile.common index d2b8b88d65..48fec58b18 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1623,7 +1623,8 @@ ifeq ($(HAVE_CDROM), 1) endif DEFINES += -DHAVE_CDROM - OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o + OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o \ + $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o endif ifeq ($(HAVE_RTGA), 1) diff --git a/libretro-common/cdrom/cdrom.c b/libretro-common/cdrom/cdrom.c index 6af9a485f7..af338b7fed 100644 --- a/libretro-common/cdrom/cdrom.c +++ b/libretro-common/cdrom/cdrom.c @@ -40,7 +40,7 @@ #include #endif -#define CDROM_CUE_TRACK_BYTES 78 +#define CDROM_CUE_TRACK_BYTES 86 typedef enum { @@ -76,16 +76,23 @@ void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame) *frame = (*frame < 74) ? (*frame + 1) : 0; } -static int cdrom_send_command(int fd, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len) +static int cdrom_send_command(int fd, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, size_t skip) { #ifdef __linux__ sg_io_hdr_t sgio = {0}; unsigned char sense[SG_MAX_SENSE] = {0}; + unsigned char *xfer_buf; int rv; + unsigned char retries_left = 5; if (!cmd || cmd_len == 0) return 1; + xfer_buf = (unsigned char*)calloc(1, len + skip); + + if (!xfer_buf) + return 1; + sgio.interface_id = 'S'; switch (dir) @@ -105,23 +112,42 @@ static int cdrom_send_command(int fd, CDROM_CMD_Direction dir, void *buf, size_t sgio.cmd_len = cmd_len; sgio.cmdp = cmd; - if (buf) - sgio.dxferp = buf; + if (xfer_buf) + sgio.dxferp = xfer_buf; if (len) - sgio.dxfer_len = len; + sgio.dxfer_len = len + skip; sgio.sbp = sense; sgio.mx_sb_len = sizeof(sense); sgio.timeout = 30000; - +retry: rv = ioctl(fd, SG_IO, &sgio); -#ifdef CDROM_DEBUG if (sgio.info & SG_INFO_CHECK) { unsigned i; + const char *sense_key_text = NULL; + if ((sense[2] & 0xF) == 3) + { + if (retries_left) + { +#ifdef CDROM_DEBUG + printf("CDROM Read Retry...\n"); +#endif + retries_left--; + goto retry; + } + else + { +#ifdef CDROM_DEBUG + printf("CDROM Read Retries failed, giving up.\n"); +#endif + } + } + +#ifdef CDROM_DEBUG printf("CHECK CONDITION\n"); for (i = 0; i < SG_MAX_SENSE; i++) @@ -136,11 +162,65 @@ static int cdrom_send_command(int fd, CDROM_CMD_Direction dir, void *buf, size_t if (sense[0] == 0x71) printf("DEFERRED ERROR:\n"); - printf("Sense Key: %02X\n", sense[2] & 0xF); + switch (sense[2] & 0xF) + { + case 0: + sense_key_text = "NO SENSE"; + break; + case 1: + sense_key_text = "RECOVERED ERROR"; + break; + case 2: + sense_key_text = "NOT READY"; + break; + case 3: + sense_key_text = "MEDIUM ERROR"; + break; + case 4: + sense_key_text = "HARDWARE ERROR"; + break; + case 5: + sense_key_text = "ILLEGAL REQUEST"; + break; + case 6: + sense_key_text = "UNIT ATTENTION"; + break; + case 7: + sense_key_text = "DATA PROTECT"; + break; + case 8: + sense_key_text = "BLANK CHECK"; + break; + case 9: + sense_key_text = "VENDOR SPECIFIC"; + break; + case 10: + sense_key_text = "COPY ABORTED"; + break; + case 11: + sense_key_text = "ABORTED COMMAND"; + break; + case 13: + sense_key_text = "VOLUME OVERFLOW"; + break; + case 14: + sense_key_text = "MISCOMPARE"; + break; + } + + printf("Sense Key: %02X (%s)\n", sense[2] & 0xF, sense_key_text); printf("ASC: %02X\n", sense[12]); printf("ASCQ: %02X\n", sense[13]); - } #endif + } + + if (rv == 0 && buf) + { + memcpy(buf, xfer_buf + skip, len); + } + + if (xfer_buf) + free(xfer_buf); if (rv == -1) return 1; @@ -173,7 +253,7 @@ int cdrom_read_subq(int fd, unsigned char *buf, size_t len) if (!buf) return 1; - rv = cdrom_send_command(fd, DIRECTION_IN, buf, len, cdb, sizeof(cdb)); + rv = cdrom_send_command(fd, DIRECTION_IN, buf, len, cdb, sizeof(cdb), 0); if (rv) return 1; @@ -236,7 +316,7 @@ static int cdrom_read_track_info(int fd, unsigned char track, cdrom_toc_t *toc) unsigned char buf[384] = {0}; unsigned lba = 0; unsigned track_size = 0; - int rv = cdrom_send_command(fd, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb)); + int rv = cdrom_send_command(fd, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0); if (rv) return 1; @@ -247,6 +327,7 @@ static int cdrom_read_track_info(int fd, unsigned char track, cdrom_toc_t *toc) lba = swap_if_little32(lba); track_size = swap_if_little32(track_size); + /* lba_start may be earlier than the MSF start times seen in read_subq */ toc->track[track - 1].lba_start = lba; toc->track[track - 1].track_size = track_size; @@ -341,7 +422,7 @@ int cdrom_write_cue(int fd, char **out_buf, size_t *out_len, char cdrom_drive, u bool audio = false; const char *track_type = "MODE1/2352"; - rv = cdrom_send_command(fd, DIRECTION_IN, q_buf, sizeof(q_buf), q_cdb, sizeof(q_cdb)); + rv = cdrom_send_command(fd, DIRECTION_IN, q_buf, sizeof(q_buf), q_cdb, sizeof(q_cdb), 0); if (rv) continue; @@ -367,11 +448,11 @@ int cdrom_write_cue(int fd, char **out_buf, size_t *out_len, char cdrom_drive, u else if (mode == 2) track_type = "MODE2/2352"; - pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c.bin\" BINARY\n", cdrom_drive); + cdrom_read_track_info(fd, point, toc); + + pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c-track%02d.bin\" BINARY\n", cdrom_drive, point); pos += snprintf(*out_buf + pos, len - pos, " TRACK %02d %s\n", point, track_type); pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 %02d:%02d:%02d\n", pmin, psec, pframe); - - cdrom_read_track_info(fd, point, toc); } } @@ -384,7 +465,7 @@ int cdrom_get_inquiry(int fd, char *model, int len) /* MMC Command: INQUIRY */ unsigned char cdb[] = {0x12, 0, 0, 0, 0xff, 0}; unsigned char buf[256] = {0}; - int rv = cdrom_send_command(fd, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb)); + int rv = cdrom_send_command(fd, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0); if (rv) return 1; @@ -410,13 +491,13 @@ int cdrom_get_inquiry(int fd, char *model, int len) return 0; } -int cdrom_read(int fd, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len) +int cdrom_read(int fd, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len, size_t skip) { /* MMC Command: READ CD MSF */ unsigned char cdb[] = {0xB9, 0, 0, min, sec, frame, 0, 0, 0, 0xF8, 0, 0}; int rv; - if (len <= 2352) + if (len + skip <= 2352) { unsigned char next_min = (frame == 74) ? (sec < 59 ? min : min + 1) : min; unsigned char next_sec = (frame == 74) ? (sec < 59 ? (sec + 1) : 0) : sec; @@ -425,29 +506,23 @@ int cdrom_read(int fd, unsigned char min, unsigned char sec, unsigned char frame cdb[6] = next_min; cdb[7] = next_sec; cdb[8] = next_frame; + +#ifdef CDROM_DEBUG + printf("single-frame read: from %d %d %d to %d %d %d skip %ld\n", cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], skip); +#endif } else { - unsigned frames = round(len / 2352.0); + unsigned frames = msf_to_lba(min, sec, frame) + round((len + skip) / 2352.0); - cdb[6] = frames / 75 / 60; - cdb[7] = frames / 75; - cdb[8] = frames - ((cdb[6] * 75 * 60) + (cdb[7] * 75)); + lba_to_msf(frames, &cdb[6], &cdb[7], &cdb[8]); - if (cdb[8] > 74) - { - cdb[8] = 0; - cdb[7]++; - - if (cdb[7] > 59) - { - cdb[7] = 0; - cdb[6]++; - } - } +#ifdef CDROM_DEBUG + printf("multi-frame read: from %d %d %d to %d %d %d skip %ld\n", cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], skip); +#endif } - rv = cdrom_send_command(fd, DIRECTION_IN, s, len, cdb, sizeof(cdb)); + rv = cdrom_send_command(fd, DIRECTION_IN, s, len, cdb, sizeof(cdb), skip); #ifdef CDROM_DEBUG printf("read status code %d\n", rv); diff --git a/libretro-common/include/cdrom/cdrom.h b/libretro-common/include/cdrom/cdrom.h index 414451ec7d..740bb1ac52 100644 --- a/libretro-common/include/cdrom/cdrom.h +++ b/libretro-common/include/cdrom/cdrom.h @@ -70,7 +70,7 @@ int cdrom_write_cue(int fd, char **out_buf, size_t *out_len, char cdrom_drive, u /* needs 32 bytes for full vendor, product and version */ int cdrom_get_inquiry(int fd, char *model, int len); -int cdrom_read(int fd, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len); +int cdrom_read(int fd, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len, size_t skip); RETRO_END_DECLS diff --git a/libretro-common/include/vfs/vfs_implementation.h b/libretro-common/include/vfs/vfs_implementation.h index 9f49f2640d..fee6770c5b 100644 --- a/libretro-common/include/vfs/vfs_implementation.h +++ b/libretro-common/include/vfs/vfs_implementation.h @@ -26,6 +26,36 @@ #include #include +enum vfs_scheme +{ + VFS_SCHEME_NONE = 0, + VFS_SCHEME_CDROM +}; + +#ifdef VFS_FRONTEND +struct retro_vfs_file_handle +#else +struct libretro_vfs_implementation_file +#endif +{ + int fd; + unsigned hints; + int64_t size; + char *buf; + FILE *fp; + char* orig_path; + uint64_t mappos; + uint64_t mapsize; + uint8_t *mapped; + enum vfs_scheme scheme; +#ifdef HAVE_CDROM + char *cdrom_cue_buf; + size_t cdrom_cue_len; + size_t cdrom_byte_pos; + char cdrom_drive; +#endif +}; + /* Replace the following symbol with something appropriate * to signify the file is being compiled for a front end instead of a core. * This allows the same code to act as reference implementation diff --git a/libretro-common/include/vfs/vfs_implementation_cdrom.h b/libretro-common/include/vfs/vfs_implementation_cdrom.h new file mode 100644 index 0000000000..6d4c17807b --- /dev/null +++ b/libretro-common/include/vfs/vfs_implementation_cdrom.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2010-2019 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (vfs_implementation_cdrom.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_CDROM_H +#define __LIBRETRO_SDK_VFS_IMPLEMENTATION_CDROM_H + +#include +#include + +RETRO_BEGIN_DECLS + +int64_t retro_vfs_file_seek_cdrom(libretro_vfs_implementation_file *stream, int64_t offset, int whence); + +FILE* retro_vfs_file_open_cdrom( + libretro_vfs_implementation_file *stream, + const char *path, unsigned mode, unsigned hints); + +int retro_vfs_file_close_cdrom(libretro_vfs_implementation_file *stream); + +int64_t retro_vfs_file_tell_cdrom(libretro_vfs_implementation_file *stream); + +int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream, + void *s, uint64_t len); + +RETRO_END_DECLS + +#endif diff --git a/libretro-common/vfs/vfs_implementation.c b/libretro-common/vfs/vfs_implementation.c index f5ca4f812d..b22926109f 100644 --- a/libretro-common/vfs/vfs_implementation.c +++ b/libretro-common/vfs/vfs_implementation.c @@ -158,12 +158,6 @@ #define FIO_S_ISDIR SCE_S_ISDIR #endif -#if defined(__linux__) -#include -#include -#include -#endif - #if (defined(__CELLOS_LV2__) && !defined(__PSL1GHT__)) || defined(__QNX__) || defined(PSP) #include /* stat() is defined here */ #endif @@ -197,46 +191,11 @@ #include #ifdef HAVE_CDROM -#include +#include #endif #define RFILE_HINT_UNBUFFERED (1 << 8) -enum vfs_scheme -{ - VFS_SCHEME_NONE = 0, - VFS_SCHEME_CDROM -}; - -#ifdef VFS_FRONTEND -struct retro_vfs_file_handle -#else -struct libretro_vfs_implementation_file -#endif -{ - int fd; - unsigned hints; - int64_t size; - char *buf; - FILE *fp; - char* orig_path; -#if defined(HAVE_MMAP) - uint64_t mappos; - uint64_t mapsize; - uint8_t *mapped; -#endif - enum vfs_scheme scheme; -#ifdef HAVE_CDROM - char *cdrom_cue_buf; - size_t cdrom_cue_len; - size_t cdrom_cue_pos; - size_t cdrom_byte_pos; - char cdrom_drive; -#endif -}; - -static cdrom_toc_t vfs_cdrom_toc = {0}; - int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence) { if (!stream) @@ -263,79 +222,7 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i #else #ifdef HAVE_CDROM if (stream->scheme == VFS_SCHEME_CDROM) - { - const char *ext = path_get_extension(stream->orig_path); - - if (string_is_equal_noncase(ext, "cue")) - { - switch (whence) - { - case SEEK_SET: - stream->cdrom_cue_pos = offset; - break; - case SEEK_CUR: - stream->cdrom_cue_pos += offset; - break; - case SEEK_END: - stream->cdrom_cue_pos = (stream->cdrom_cue_len - 1) + offset; - break; - } - -#ifdef CDROM_DEBUG - printf("CDROM Seek: Path %s Offset %lu is now at %lu\n", stream->orig_path, offset, stream->cdrom_cue_pos); -#endif - } - else if (string_is_equal_noncase(ext, "bin")) - { - unsigned char min = offset / 75 / 60; - unsigned char sec = offset / 75; - unsigned char frame = offset - ((min * 75 * 60) + (sec * 75)); - - switch (whence) - { - case SEEK_CUR: - { - min += vfs_cdrom_toc.cur_min; - sec += vfs_cdrom_toc.cur_sec; - frame += vfs_cdrom_toc.cur_frame; - - stream->cdrom_byte_pos += offset; - - break; - } - case SEEK_END: - { - unsigned char end_min = 0; - unsigned char end_sec = 0; - unsigned char end_frame = 0; - size_t end_lba = vfs_cdrom_toc.track[vfs_cdrom_toc.num_tracks - 1].lba_start + vfs_cdrom_toc.track[vfs_cdrom_toc.num_tracks - 1].track_size; - - lba_to_msf(end_lba, &min, &sec, &frame); - - min += end_min; - sec += end_sec; - frame += end_frame; - - stream->cdrom_byte_pos = end_lba * 2352; - - break; - } - case SEEK_SET: - default: - stream->cdrom_byte_pos = offset; - break; - } - - vfs_cdrom_toc.cur_min = min; - vfs_cdrom_toc.cur_sec = sec; - vfs_cdrom_toc.cur_frame = frame; - -#ifdef CDROM_DEBUG - printf("CDROM Seek: Path %s Offset %lu is now at %lu\n", stream->orig_path, offset, stream->cdrom_byte_pos); -#endif - } - - } + return retro_vfs_file_seek_cdrom(stream, offset, whence); else #endif return fseeko(stream->fp, (off_t)offset, whence); @@ -421,7 +308,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( size_t cdrom_prefix_siz = strlen(cdrom_prefix); int cdrom_prefix_len = (int)cdrom_prefix_siz; - if (path_len >= cdrom_prefix_len) + if (path_len > cdrom_prefix_len) { if (!memcmp(path, cdrom_prefix, cdrom_prefix_len)) { @@ -522,70 +409,10 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( #ifdef HAVE_CDROM if (stream->scheme == VFS_SCHEME_CDROM) { -#ifdef __linux__ - char model[32] = {0}; - char cdrom_path[] = "/dev/sg1"; - size_t path_len = strlen(path); - const char *ext = path_get_extension(path); + fp = retro_vfs_file_open_cdrom(stream, path, mode, hints); - if (path_len >= strlen("drive1.cue")) - { - if (!memcmp(path, "drive", strlen("drive"))) - { - if (path[5] >= '0' && path[5] <= '9') - { - cdrom_path[7] = path[5]; - stream->cdrom_drive = path[5]; - } - } - } - -#ifdef CDROM_DEBUG - printf("CDROM Open: Path %s URI %s\n", cdrom_path, path); -#endif - fp = (FILE*)fopen_utf8(cdrom_path, "r+b"); - - if (fp) - { - int cdrom_fd = fileno(fp); - - if (!cdrom_get_inquiry(cdrom_fd, model, sizeof(model))) - { - size_t len = 0; - -#ifdef CDROM_DEBUG - printf("CDROM Model: %s\n", model); -#endif - } - } - else + if (!fp) goto error; - - if (string_is_equal_noncase(ext, "cue")) - { - if (stream->cdrom_cue_buf) - { - free(stream->cdrom_cue_buf); - stream->cdrom_cue_buf = NULL; - } - - cdrom_write_cue(fileno(fp), &stream->cdrom_cue_buf, &stream->cdrom_cue_len, stream->cdrom_drive, &vfs_cdrom_toc.num_tracks, &vfs_cdrom_toc); - - if (vfs_cdrom_toc.num_tracks > 1) - { - vfs_cdrom_toc.cur_min = vfs_cdrom_toc.track[0].min; - vfs_cdrom_toc.cur_sec = vfs_cdrom_toc.track[0].sec; - vfs_cdrom_toc.cur_frame = vfs_cdrom_toc.track[0].frame; - } - - if (string_is_empty(stream->cdrom_cue_buf)) - printf("Error writing cue sheet.\n"); -#ifdef CDROM_DEBUG - else - printf("CDROM CUE Sheet:\n%s\n", stream->cdrom_cue_buf); -#endif - } -#endif } else #endif @@ -678,7 +505,14 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { if (stream->fp) - fclose(stream->fp); + { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + retro_vfs_file_close_cdrom(stream); + else +#endif + fclose(stream->fp); + } } else { @@ -700,13 +534,6 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) if (stream->buf) free(stream->buf); -#ifdef HAVE_CDROM -#ifdef CDROM_DEBUG - if (stream->scheme == VFS_SCHEME_CDROM) - printf("CDROM Close: Path %s\n", stream->orig_path); -#endif -#endif - if (stream->orig_path) free(stream->orig_path); @@ -771,30 +598,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) #else #ifdef HAVE_CDROM if (stream->scheme == VFS_SCHEME_CDROM) - { - const char *ext = path_get_extension(stream->orig_path); - - if (string_is_equal_noncase(ext, "cue")) - { -#ifdef HAVE_CDROM -#ifdef CDROM_DEBUG - printf("CDROM (cue) Tell: Path %s Position %lu\n", stream->orig_path, stream->cdrom_cue_pos); -#endif -#endif - return stream->cdrom_cue_pos; - } - else if (string_is_equal_noncase(ext, "bin")) - { - unsigned lba = msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame); - -#ifdef HAVE_CDROM -#ifdef CDROM_DEBUG - printf("CDROM (bin) Tell: Path %s Position %u\n", stream->orig_path, lba * 2352); -#endif -#endif - return lba * 2352; - } - } + return retro_vfs_file_tell_cdrom(stream); else #endif return ftell(stream->fp); @@ -848,60 +652,7 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, #else #ifdef HAVE_CDROM if (stream->scheme == VFS_SCHEME_CDROM) - { - int rv; - const char *ext = path_get_extension(stream->orig_path); - - if (string_is_equal_noncase(ext, "cue")) - { - if (len < stream->cdrom_cue_len - stream->cdrom_cue_pos) - { -#ifdef CDROM_DEBUG - printf("CDROM Read: Reading %lu bytes from cuesheet starting at %lu...\n", len, stream->cdrom_cue_pos); -#endif - memcpy(s, stream->cdrom_cue_buf + stream->cdrom_cue_pos, len); - stream->cdrom_cue_pos += len; - } - else - { -#ifdef CDROM_DEBUG - printf("CDROM Read: Reading %lu bytes from cuesheet starting at %lu failed.\n", len, stream->cdrom_cue_pos); -#endif - } - } - else if (string_is_equal_noncase(ext, "bin")) - { - unsigned frames = len / 2352; - unsigned i; - -#ifdef CDROM_DEBUG - printf("CDROM Read: Reading %lu bytes from CD starting at byte offset %lu (MSF %02d:%02d:%02d) (LBA %u)...\n", len, stream->cdrom_byte_pos, vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame, msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame)); -#endif - - rv = cdrom_read(fileno(stream->fp), vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame, s, (size_t)len); - - if (rv) - { -#ifdef CDROM_DEBUG - printf("Failed to read %lu bytes from CD.\n", len); -#endif - return 0; - } - - stream->cdrom_byte_pos += len; - - for (i = 0; i < frames; i++) - { - increment_msf(&vfs_cdrom_toc.cur_min, &vfs_cdrom_toc.cur_sec, &vfs_cdrom_toc.cur_frame); - } - -#ifdef CDROM_DEBUG - printf("CDROM read %lu bytes, position is now: %lu (MSF %02d:%02d:%02d) (LBA %u)\n", len, stream->cdrom_byte_pos, vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame, msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame)); -#endif - } - - return len; - } + return retro_vfs_file_read_cdrom(stream, s, len); else #endif return fread(s, 1, (size_t)len, stream->fp); diff --git a/libretro-common/vfs/vfs_implementation_cdrom.c b/libretro-common/vfs/vfs_implementation_cdrom.c new file mode 100644 index 0000000000..46607acf0b --- /dev/null +++ b/libretro-common/vfs/vfs_implementation_cdrom.c @@ -0,0 +1,311 @@ +/* Copyright (C) 2010-2019 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (vfs_implementation_cdrom.c). +* --------------------------------------------------------------------------------------- +* +* Permission is hereby granted, free of charge, +* to any person obtaining a copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation the rights to +* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +static cdrom_toc_t vfs_cdrom_toc = {0}; + +int64_t retro_vfs_file_seek_cdrom(libretro_vfs_implementation_file *stream, int64_t offset, int whence) +{ + const char *ext = path_get_extension(stream->orig_path); + + if (string_is_equal_noncase(ext, "cue")) + { + switch (whence) + { + case SEEK_SET: + stream->cdrom_byte_pos = offset; + break; + case SEEK_CUR: + stream->cdrom_byte_pos += offset; + break; + case SEEK_END: + stream->cdrom_byte_pos = (stream->cdrom_cue_len - 1) + offset; + break; + } + +#ifdef CDROM_DEBUG + printf("CDROM Seek: Path %s Offset %lu is now at %lu\n", stream->orig_path, offset, stream->cdrom_byte_pos); +#endif + } + else if (string_is_equal_noncase(ext, "bin")) + { + unsigned frames = (offset / 2352); + unsigned char min = 0; + unsigned char sec = 0; + unsigned char frame = 0; + const char *seek_type = "SEEK_SET"; + + lba_to_msf(frames, &min, &sec, &frame); + + switch (whence) + { + case SEEK_CUR: + { + min += vfs_cdrom_toc.cur_min; + sec += vfs_cdrom_toc.cur_sec; + frame += vfs_cdrom_toc.cur_frame; + + stream->cdrom_byte_pos += offset; + seek_type = "SEEK_CUR"; + + break; + } + case SEEK_END: + { + unsigned char end_min = 0; + unsigned char end_sec = 0; + unsigned char end_frame = 0; + size_t end_lba = vfs_cdrom_toc.track[vfs_cdrom_toc.num_tracks - 1].lba_start + vfs_cdrom_toc.track[vfs_cdrom_toc.num_tracks - 1].track_size; + + lba_to_msf(end_lba, &min, &sec, &frame); + + min += end_min; + sec += end_sec; + frame += end_frame; + + stream->cdrom_byte_pos = end_lba * 2352; + seek_type = "SEEK_END"; + + break; + } + case SEEK_SET: + default: + { + unsigned lba = msf_to_lba(min, sec, frame); + + seek_type = "SEEK_SET"; + stream->cdrom_byte_pos = offset; + + break; + } + } + + vfs_cdrom_toc.cur_min = min; + vfs_cdrom_toc.cur_sec = sec; + vfs_cdrom_toc.cur_frame = frame; + +#ifdef CDROM_DEBUG + printf("CDROM Seek %s: Path %s Offset %lu is now at %lu (MSF %02d:%02d:%02d) (LBA %u)...\n", seek_type, stream->orig_path, offset, stream->cdrom_byte_pos, vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame, msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame)); +#endif + } + else + return -1; + + return 0; +} + +FILE* retro_vfs_file_open_cdrom( + libretro_vfs_implementation_file *stream, + const char *path, unsigned mode, unsigned hints) +{ + FILE *fp = NULL; +#ifdef __linux__ + char model[32] = {0}; + char cdrom_path[] = "/dev/sg1"; + size_t path_len = strlen(path); + const char *ext = path_get_extension(path); + + if (!string_is_equal_noncase(ext, "cue") && !string_is_equal_noncase(ext, "bin")) + return NULL; + + if (path_len >= strlen("drive1.cue")) + { + if (!memcmp(path, "drive", strlen("drive"))) + { + if (path[5] >= '0' && path[5] <= '9') + { + cdrom_path[7] = path[5]; + stream->cdrom_drive = path[5]; + } + } + } + +#ifdef CDROM_DEBUG + printf("CDROM Open: Path %s URI %s\n", cdrom_path, path); +#endif + fp = (FILE*)fopen_utf8(cdrom_path, "r+b"); + + if (fp) + { + int cdrom_fd = fileno(fp); + + if (!cdrom_get_inquiry(cdrom_fd, model, sizeof(model))) + { + size_t len = 0; + +#ifdef CDROM_DEBUG + printf("CDROM Model: %s\n", model); +#endif + } + } + else + return NULL; + + if (string_is_equal_noncase(ext, "cue")) + { + if (stream->cdrom_cue_buf) + { + free(stream->cdrom_cue_buf); + stream->cdrom_cue_buf = NULL; + } + + cdrom_write_cue(fileno(fp), &stream->cdrom_cue_buf, &stream->cdrom_cue_len, stream->cdrom_drive, &vfs_cdrom_toc.num_tracks, &vfs_cdrom_toc); + + if (vfs_cdrom_toc.num_tracks > 1) + { + vfs_cdrom_toc.cur_min = vfs_cdrom_toc.track[0].min; + vfs_cdrom_toc.cur_sec = vfs_cdrom_toc.track[0].sec; + vfs_cdrom_toc.cur_frame = vfs_cdrom_toc.track[0].frame; + } + +#ifdef CDROM_DEBUG + if (string_is_empty(stream->cdrom_cue_buf)) + printf("Error writing cue sheet.\n"); + else + { + printf("CDROM CUE Sheet:\n%s\n", stream->cdrom_cue_buf); + fflush(stdout); + } +#endif + } +#endif + + return fp; +} + +int retro_vfs_file_close_cdrom(libretro_vfs_implementation_file *stream) +{ +#ifdef CDROM_DEBUG + if (stream->scheme == VFS_SCHEME_CDROM) + printf("CDROM Close: Path %s\n", stream->orig_path); +#endif + + if (fclose(stream->fp)) + return -1; + + return 0; +} + +int64_t retro_vfs_file_tell_cdrom(libretro_vfs_implementation_file *stream) +{ + if (!stream) + return -1; + + const char *ext = path_get_extension(stream->orig_path); + + if (string_is_equal_noncase(ext, "cue")) + { +#ifdef CDROM_DEBUG + printf("CDROM (cue) Tell: Path %s Position %lu\n", stream->orig_path, stream->cdrom_byte_pos); +#endif + return stream->cdrom_byte_pos; + } + else if (string_is_equal_noncase(ext, "bin")) + { + unsigned lba = msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame); + +#ifdef CDROM_DEBUG + printf("CDROM (bin) Tell: Path %s Position %u\n", stream->orig_path, lba * 2352); +#endif + return lba * 2352; + } + + return -1; +} + +int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream, + void *s, uint64_t len) +{ + int rv; + const char *ext = path_get_extension(stream->orig_path); + + if (string_is_equal_noncase(ext, "cue")) + { + if (len < stream->cdrom_cue_len - stream->cdrom_byte_pos) + { +#ifdef CDROM_DEBUG + printf("CDROM Read: Reading %lu bytes from cuesheet starting at %lu...\n", len, stream->cdrom_byte_pos); +#endif + memcpy(s, stream->cdrom_cue_buf + stream->cdrom_byte_pos, len); + stream->cdrom_byte_pos += len; + + return len; + } + else + { +#ifdef CDROM_DEBUG + printf("CDROM Read: Reading %lu bytes from cuesheet starting at %lu failed.\n", len, stream->cdrom_byte_pos); +#endif + return 0; + } + } + else if (string_is_equal_noncase(ext, "bin")) + { + unsigned frames = len / 2352; + unsigned i; + size_t skip = stream->cdrom_byte_pos % 2352; + unsigned char min = 0; + unsigned char sec = 0; + unsigned char frame = 0; + unsigned lba_cur = 0; + unsigned lba_start = 0; + + lba_cur = msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame); + + lba_start = msf_to_lba(vfs_cdrom_toc.track[0].min, vfs_cdrom_toc.track[0].sec, vfs_cdrom_toc.track[0].frame); + + lba_to_msf(lba_start + lba_cur, &min, &sec, &frame); + +#ifdef CDROM_DEBUG + printf("CDROM Read: Reading %lu bytes from %s starting at byte offset %lu (MSF %02d:%02d:%02d) (LBA %u) skip %lu...\n", len, stream->orig_path, stream->cdrom_byte_pos, min, sec, frame, msf_to_lba(min, sec, frame), skip); +#endif + + rv = cdrom_read(fileno(stream->fp), min, sec, frame, s, (size_t)len, skip); + + if (rv) + { +#ifdef CDROM_DEBUG + printf("Failed to read %lu bytes from CD.\n", len); +#endif + return 0; + } + + stream->cdrom_byte_pos += len; + + for (i = 0; i < frames; i++) + { + increment_msf(&vfs_cdrom_toc.cur_min, &vfs_cdrom_toc.cur_sec, &vfs_cdrom_toc.cur_frame); + } + +#ifdef CDROM_DEBUG + printf("CDROM read %lu bytes, position is now: %lu (MSF %02d:%02d:%02d) (LBA %u)\n", len, stream->cdrom_byte_pos, vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame, msf_to_lba(vfs_cdrom_toc.cur_min, vfs_cdrom_toc.cur_sec, vfs_cdrom_toc.cur_frame)); +#endif + + return len; + } + + return 0; +}