diff --git a/Makefile.common b/Makefile.common index f7f962214f..48fec58b18 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1617,6 +1617,16 @@ ifeq ($(HAVE_ZLIB_COMMON), 1) endif endif +ifeq ($(HAVE_CDROM), 1) + ifeq ($(CDROM_DEBUG), 1) + DEFINES += -DCDROM_DEBUG + endif + + DEFINES += -DHAVE_CDROM + OBJ += $(LIBRETRO_COMM_DIR)/cdrom/cdrom.o \ + $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_cdrom.o +endif + ifeq ($(HAVE_RTGA), 1) DEFINES += -DHAVE_RTGA OBJ += $(LIBRETRO_COMM_DIR)/formats/tga/rtga.o diff --git a/core_type.h b/core_type.h index 40b2d8b16e..5c477f2396 100644 --- a/core_type.h +++ b/core_type.h @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/dynamic.c b/dynamic.c index ce8f081f4f..8da15379a3 100644 --- a/dynamic.c +++ b/dynamic.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/dynamic.h b/dynamic.h index a879e38d71..04e3f81589 100644 --- a/dynamic.h +++ b/dynamic.h @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/file_path_special.c b/file_path_special.c index f1c3bcbbec..b3f6bffcd1 100644 --- a/file_path_special.c +++ b/file_path_special.c @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/file_path_special.h b/file_path_special.h index f2db2208ee..a2eed197a2 100644 --- a/file_path_special.h +++ b/file_path_special.h @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2016 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/griffin/griffin.c b/griffin/griffin.c index 4767fe42f0..7af09ff1f6 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis +* Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/griffin/griffin_cpp.cpp b/griffin/griffin_cpp.cpp index 79bbfeff06..686c70b20c 100644 --- a/griffin/griffin_cpp.cpp +++ b/griffin/griffin_cpp.cpp @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis +* Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/libretro-common/cdrom/cdrom.c b/libretro-common/cdrom/cdrom.c new file mode 100644 index 0000000000..0f1918e172 --- /dev/null +++ b/libretro-common/cdrom/cdrom.c @@ -0,0 +1,647 @@ +/* Copyright (C) 2010-2019 The RetroArch team +* +* --------------------------------------------------------------------------------------- +* The following license statement only applies to this file (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. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __linux__ +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#define CDROM_CUE_TRACK_BYTES 86 +#define CDROM_MAX_SENSE_BYTES 16 +#define CDROM_MAX_RETRIES 10 + +typedef enum +{ + DIRECTION_NONE, + DIRECTION_IN, + DIRECTION_OUT +} CDROM_CMD_Direction; + +void lba_to_msf(unsigned lba, unsigned char *min, unsigned char *sec, unsigned char *frame) +{ + if (!min || !sec || !frame) + return; + + *frame = lba % 75; + lba /= 75; + *sec = lba % 60; + lba /= 60; + *min = lba; +} + +unsigned msf_to_lba(unsigned char min, unsigned char sec, unsigned char frame) +{ + return (min * 60 + sec) * 75 + frame; +} + +void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame) +{ + if (!min || !sec || !frame) + return; + + *min = (*frame == 74) ? (*sec < 59 ? *min : *min + 1) : *min; + *sec = (*frame == 74) ? (*sec < 59 ? (*sec + 1) : 0) : *sec; + *frame = (*frame < 74) ? (*frame + 1) : 0; +} + +#ifdef _WIN32 +static int cdrom_send_command_win32(HANDLE fh, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, unsigned char *sense, size_t sense_len) +{ + DWORD ioctl_bytes; + BOOL ioctl_rv; + struct sptd_with_sense + { + SCSI_PASS_THROUGH_DIRECT s; + UCHAR sense[128]; + } sptd; + + memset(&sptd, 0, sizeof(sptd)); + + sptd.s.Length = sizeof(sptd.s); + sptd.s.CdbLength = cmd_len; + + switch (dir) + { + case DIRECTION_IN: + sptd.s.DataIn = SCSI_IOCTL_DATA_IN; + break; + case DIRECTION_OUT: + sptd.s.DataIn = SCSI_IOCTL_DATA_OUT; + break; + case DIRECTION_NONE: + default: + sptd.s.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; + break; + } + + sptd.s.TimeOutValue = 30; + sptd.s.DataBuffer = buf; + sptd.s.DataTransferLength = len; + sptd.s.SenseInfoLength = sizeof(sptd.sense); + sptd.s.SenseInfoOffset = offsetof(struct sptd_with_sense, sense); + + memcpy(sptd.s.Cdb, cmd, cmd_len); + + ioctl_rv = DeviceIoControl(fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, + sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL); + + if (!ioctl_rv) + return 1; + + return 0; +} +#endif + +#ifdef __linux__ +static int cdrom_send_command_linux(int fd, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, unsigned char *sense, size_t sense_len) +{ + sg_io_hdr_t sgio = {0}; + int rv; + + switch (dir) + { + case DIRECTION_IN: + sgio.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case DIRECTION_OUT: + sgio.dxfer_direction = SG_DXFER_TO_DEV; + break; + case DIRECTION_NONE: + default: + sgio.dxfer_direction = SG_DXFER_NONE; + break; + } + + sgio.interface_id = 'S'; + sgio.cmd_len = cmd_len; + sgio.cmdp = cmd; + sgio.dxferp = buf; + sgio.dxfer_len = len; + sgio.sbp = sense; + sgio.mx_sb_len = sense_len; + sgio.timeout = 30000; + + rv = ioctl(fd, SG_IO, &sgio); + + if (rv == -1 || sgio.info & SG_INFO_CHECK) + return 1; + + return 0; +} +#endif + +static int cdrom_send_command(libretro_vfs_implementation_file *stream, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, size_t skip) +{ + unsigned char *xfer_buf; + unsigned char sense[CDROM_MAX_SENSE_BYTES] = {0}; + unsigned char retries_left = CDROM_MAX_RETRIES; + int rv = 0; + + if (!cmd || cmd_len == 0) + return 1; + + xfer_buf = (unsigned char*)calloc(1, len + skip); + + if (!xfer_buf) + return 1; + +#ifdef CDROM_DEBUG + { + unsigned i; + + printf("CDROM Send Command: "); + + for (i = 0; i < cmd_len / sizeof(*cmd); i++) + { + printf("%02X ", cmd[i]); + } + + printf("\n"); + fflush(stdout); + } +#endif + +retry: +#ifdef __linux__ + if (!cdrom_send_command_linux(fileno(stream->fp), dir, xfer_buf, len + skip, cmd, cmd_len, sense, sizeof(sense))) +#else +#ifdef _WIN32 + if (!cdrom_send_command_win32(stream->fh, dir, xfer_buf, len + skip, cmd, cmd_len, sense, sizeof(sense))) +#endif +#endif + { + rv = 0; + + if (buf) + memcpy(buf, xfer_buf + skip, len); + } + else + { + unsigned i; + const char *sense_key_text = NULL; + + (void)sense_key_text; + (void)i; + + switch (sense[2] & 0xF) + { + case 0: + case 2: + case 3: + case 4: + case 6: + if (retries_left) + { +#ifdef CDROM_DEBUG + printf("CDROM Read Retry...\n"); + fflush(stdout); +#endif + retries_left--; + usleep(1000 * 1000); + goto retry; + } + else + { + rv = 1; +#ifdef CDROM_DEBUG + printf("CDROM Read Retries failed, giving up.\n"); + fflush(stdout); +#endif + } + + break; + default: + break; + } + +#ifdef CDROM_DEBUG + printf("CHECK CONDITION\n"); + + for (i = 0; i < CDROM_MAX_SENSE_BYTES; i++) + { + printf("%02X ", sense[i]); + } + + printf("\n"); + + if (sense[0] == 0x70) + printf("CURRENT ERROR:\n"); + if (sense[0] == 0x71) + printf("DEFERRED ERROR:\n"); + + 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]); + + fflush(stdout); + + rv = 1; +#endif + } + + if (xfer_buf) + free(xfer_buf); + + return rv; +} + +int cdrom_read_subq(libretro_vfs_implementation_file *stream, unsigned char *buf, size_t len) +{ + /* MMC Command: READ TOC/PMA/ATIP */ + unsigned char cdb[] = {0x43, 0x2, 0x2, 0, 0, 0, 0x1, 0x9, 0x30, 0}; +#ifdef CDROM_DEBUG + unsigned short data_len = 0; + unsigned char first_session = 0; + unsigned char last_session = 0; + int i; +#endif + int rv; + + if (!buf) + return 1; + + rv = cdrom_send_command(stream, DIRECTION_IN, buf, len, cdb, sizeof(cdb), 0); + + if (rv) + return 1; + +#ifdef CDROM_DEBUG + data_len = buf[0] << 8 | buf[1]; + first_session = buf[2]; + last_session = buf[3]; + + printf("Data Length: %d\n", data_len); + printf("First Session: %d\n", first_session); + printf("Last Session: %d\n", last_session); + + for (i = 0; i < (data_len - 2) / 11; i++) + { + unsigned char session_num = buf[4 + (i * 11) + 0]; + unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF; + /*unsigned char control = buf[4 + (i * 11) + 1] & 0xF;*/ + unsigned char tno = buf[4 + (i * 11) + 2]; + unsigned char point = buf[4 + (i * 11) + 3]; + unsigned char pmin = buf[4 + (i * 11) + 8]; + unsigned char psec = buf[4 + (i * 11) + 9]; + unsigned char pframe = buf[4 + (i * 11) + 10]; + + /*printf("i %d control %d adr %d tno %d point %d: ", i, control, adr, tno, point);*/ + /* why is control always 0? */ + + if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99) + { + printf("- Session#: %d TNO %d POINT %d ", session_num, tno, point); + printf("Track start time: (MSF %02u:%02u:%02u) ", pmin, psec, pframe); + } + else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA0) + { + printf("- Session#: %d TNO %d POINT %d ", session_num, tno, point); + printf("First Track Number: %d ", pmin); + printf("Disc Type: %d ", psec); + } + else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1) + { + printf("- Session#: %d TNO %d POINT %d ", session_num, tno, point); + printf("Last Track Number: %d ", pmin); + } + else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA2) + { + printf("- Session#: %d TNO %d POINT %d ", session_num, tno, point); + printf("Lead-out runtime: (MSF %02u:%02u:%02u) ", pmin, psec, pframe); + } + + printf("\n"); + } + + fflush(stdout); +#endif + return 0; +} + +static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsigned char track, cdrom_toc_t *toc) +{ + /* MMC Command: READ TRACK INFORMATION */ + unsigned char cdb[] = {0x52, 0x1, 0, 0, 0, track, 0, 0x1, 0x80, 0}; + unsigned char buf[384] = {0}; + unsigned lba = 0; + unsigned track_size = 0; + int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0); + + if (rv) + return 1; + + memcpy(&lba, buf + 8, 4); + memcpy(&track_size, buf + 24, 4); + + 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; + +#ifdef CDROM_DEBUG + printf("Track %d Info: ", track); + printf("Copy: %d ", (buf[5] & 0x10) > 0); + printf("Data Mode: %d ", buf[6] & 0xF); + printf("LBA Start: %d ", lba); + printf("Track Size: %d\n", track_size); + fflush(stdout); +#endif + + return 0; +} + +int cdrom_set_read_speed(libretro_vfs_implementation_file *stream, unsigned speed) +{ + unsigned new_speed = swap_if_big32(speed); + /* MMC Command: SET CD SPEED */ + unsigned char cmd[] = {0xBB, 0, (new_speed >> 24) & 0xFF, (new_speed >> 16) & 0xFF, (new_speed >> 8) & 0xFF, new_speed & 0xFF, 0, 0, 0, 0, 0, 0 }; + + return cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cmd, sizeof(cmd), 0); +} + +int cdrom_write_cue(libretro_vfs_implementation_file *stream, char **out_buf, size_t *out_len, char cdrom_drive, unsigned char *num_tracks, cdrom_toc_t *toc) +{ + unsigned char buf[2352] = {0}; + unsigned short data_len = 0; + size_t len = 0; + size_t pos = 0; + int rv = 0; + int i; + + if (!out_buf || !out_len || !num_tracks || !toc) + { +#ifdef CDROM_DEBUG + printf("Invalid buffer/length pointer for CDROM cue sheet\n"); + fflush(stdout); +#endif + return 1; + } + + cdrom_set_read_speed(stream, 0xFFFFFFFF); + + rv = cdrom_read_subq(stream, buf, sizeof(buf)); + + if (rv) + return rv; + + data_len = buf[0] << 8 | buf[1]; + + for (i = 0; i < (data_len - 2) / 11; i++) + { + unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF; + unsigned char tno = buf[4 + (i * 11) + 2]; + unsigned char point = buf[4 + (i * 11) + 3]; + unsigned char pmin = buf[4 + (i * 11) + 8]; + + if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1) + { + *num_tracks = pmin; +#ifdef CDROM_DEBUG + printf("Number of CDROM tracks: %d\n", *num_tracks); + fflush(stdout); +#endif + break; + } + } + + if (!*num_tracks || *num_tracks > 99) + { +#ifdef CDROM_DEBUG + printf("Invalid number of CDROM tracks: %d\n", *num_tracks); + fflush(stdout); +#endif + return 1; + } + + len = CDROM_CUE_TRACK_BYTES * (*num_tracks); + toc->num_tracks = *num_tracks; + *out_buf = (char*)calloc(1, len); + *out_len = len; + + for (i = 0; i < (data_len - 2) / 11; i++) + { + /*unsigned char session_num = buf[4 + (i * 11) + 0];*/ + unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF; + unsigned char control = buf[4 + (i * 11) + 1] & 0xF; + unsigned char tno = buf[4 + (i * 11) + 2]; + unsigned char point = buf[4 + (i * 11) + 3]; + unsigned char pmin = buf[4 + (i * 11) + 8]; + unsigned char psec = buf[4 + (i * 11) + 9]; + unsigned char pframe = buf[4 + (i * 11) + 10]; + unsigned lba = msf_to_lba(pmin, psec, pframe); + + /*printf("i %d control %d adr %d tno %d point %d: ", i, control, adr, tno, point);*/ + /* why is control always 0? */ + + if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99) + { + unsigned char mode = 1; + bool audio = false; + const char *track_type = "MODE1/2352"; + + mode = adr; + audio = (!(control & 0x4) && !(control & 0x5)); + +#ifdef CDROM_DEBUG + printf("Track %02d CONTROL %01X ADR %01X MODE %d AUDIO? %d\n", point, control, adr, mode, audio); + fflush(stdout); +#endif + + toc->track[point - 1].track_num = point; + toc->track[point - 1].min = pmin; + toc->track[point - 1].sec = psec; + toc->track[point - 1].frame = pframe; + toc->track[point - 1].lba = lba; + toc->track[point - 1].mode = mode; + toc->track[point - 1].audio = audio; + + if (audio) + track_type = "AUDIO"; + else if (mode == 1) + track_type = "MODE1/2352"; + else if (mode == 2) + track_type = "MODE2/2352"; + + cdrom_read_track_info(stream, point, toc); + +#ifdef _WIN32 + pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://%c://drive-track%02d.bin\" BINARY\n", cdrom_drive, point); +#else + pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c-track%02d.bin\" BINARY\n", cdrom_drive, point); +#endif + pos += snprintf(*out_buf + pos, len - pos, " TRACK %02d %s\n", point, track_type); + pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 00:00:00\n"); + } + } + + return 0; +} + +/* needs 32 bytes for full vendor, product and version */ +int cdrom_get_inquiry(libretro_vfs_implementation_file *stream, 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(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0); + + if (rv) + return 1; + + if (model && len >= 32) + { + memset(model, 0, len); + + /* vendor */ + memcpy(model, buf + 8, 8); + + model[8] = ' '; + + /* product */ + memcpy(model + 9, buf + 16, 16); + + model[25] = ' '; + + /* version */ + memcpy(model + 26, buf + 32, 4); + } + + return 0; +} + +int cdrom_read(libretro_vfs_implementation_file *stream, 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 + 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; + unsigned char next_frame = (frame < 74) ? (frame + 1) : 0; + + 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 %" PRId64 "\n", cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], skip); + fflush(stdout); +#endif + } + else + { + unsigned frames = msf_to_lba(min, sec, frame) + round((len + skip) / 2352.0); + + lba_to_msf(frames, &cdb[6], &cdb[7], &cdb[8]); + +#ifdef CDROM_DEBUG + printf("multi-frame read: from %d %d %d to %d %d %d skip %" PRId64 "\n", cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], skip); + fflush(stdout); +#endif + } + + rv = cdrom_send_command(stream, DIRECTION_IN, s, len, cdb, sizeof(cdb), skip); + +#ifdef CDROM_DEBUG + printf("read status code %d\n", rv); + fflush(stdout); +#endif + + if (rv) + return 1; + + return 0; +} + diff --git a/libretro-common/include/cdrom/cdrom.h b/libretro-common/include/cdrom/cdrom.h new file mode 100644 index 0000000000..c751af5658 --- /dev/null +++ b/libretro-common/include/cdrom/cdrom.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2010-2019 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (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_CDROM_H +#define __LIBRETRO_SDK_CDROM_H + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef VFS_FRONTEND +typedef struct retro_vfs_file_handle libretro_vfs_implementation_file; +#else +typedef struct libretro_vfs_implementation_file libretro_vfs_implementation_file; +#endif + +RETRO_BEGIN_DECLS + +typedef struct +{ + unsigned lba_start; /* start of pregap */ + unsigned lba; /* start of data */ + unsigned track_size; + unsigned char track_num; + unsigned char min; /* start of data */ + unsigned char sec; + unsigned char frame; + unsigned char mode; + bool audio; +} cdrom_track_t; + +typedef struct +{ + char drive; + unsigned char num_tracks; + cdrom_track_t track[99]; +} cdrom_toc_t; + +void lba_to_msf(unsigned lba, unsigned char *min, unsigned char *sec, unsigned char *frame); + +unsigned msf_to_lba(unsigned char min, unsigned char sec, unsigned char frame); + +void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame); + +int cdrom_read_subq(libretro_vfs_implementation_file *stream, unsigned char *buf, size_t len); + +int cdrom_write_cue(libretro_vfs_implementation_file *stream, char **out_buf, size_t *out_len, char cdrom_drive, unsigned char *num_tracks, cdrom_toc_t *toc); + +/* needs 32 bytes for full vendor, product and version */ +int cdrom_get_inquiry(libretro_vfs_implementation_file *stream, char *model, int len); + +int cdrom_read(libretro_vfs_implementation_file *stream, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len, size_t skip); + +int cdrom_set_read_speed(libretro_vfs_implementation_file *stream, unsigned speed); + +RETRO_END_DECLS + +#endif diff --git a/libretro-common/include/streams/file_stream.h b/libretro-common/include/streams/file_stream.h index 8e83f6d91b..c25dfe78b5 100644 --- a/libretro-common/include/streams/file_stream.h +++ b/libretro-common/include/streams/file_stream.h @@ -36,6 +36,7 @@ #include #include +#include #define FILESTREAM_REQUIRED_VFS_VERSION 2 @@ -106,6 +107,8 @@ bool filestream_exists(const char *path); char *filestream_getline(RFILE *stream); +const libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream); + RETRO_END_DECLS #endif diff --git a/libretro-common/include/vfs/vfs_implementation.h b/libretro-common/include/vfs/vfs_implementation.h index 9f49f2640d..9b69b7eb81 100644 --- a/libretro-common/include/vfs/vfs_implementation.h +++ b/libretro-common/include/vfs/vfs_implementation.h @@ -23,9 +23,48 @@ #ifndef __LIBRETRO_SDK_VFS_IMPLEMENTATION_H #define __LIBRETRO_SDK_VFS_IMPLEMENTATION_H +#include #include #include +#ifdef HAVE_CDROM +#include +#endif + +#ifdef _WIN32 +typedef void* HANDLE; +#endif + +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; +#ifdef _WIN32 + HANDLE fh; +#endif + char* orig_path; + uint64_t mappos; + uint64_t mapsize; + uint8_t *mapped; + enum vfs_scheme scheme; +#ifdef HAVE_CDROM + vfs_cdrom_t cdrom; +#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..9a91f26d33 --- /dev/null +++ b/libretro-common/include/vfs/vfs_implementation_cdrom.h @@ -0,0 +1,72 @@ +/* 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 + +RETRO_BEGIN_DECLS + +typedef struct RFILE RFILE; + +typedef struct +{ + char *cue_buf; + size_t cue_len; + int64_t byte_pos; + char drive; + unsigned char cur_min; + unsigned char cur_sec; + unsigned char cur_frame; + unsigned char cur_track; + unsigned cur_lba; +} vfs_cdrom_t; + +#ifdef VFS_FRONTEND +struct retro_vfs_file_handle; +#else +struct libretro_vfs_implementation_file; +#endif + +int64_t retro_vfs_file_seek_cdrom(libretro_vfs_implementation_file *stream, int64_t offset, int whence); + +void 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); + +int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream); + +const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void); + +const vfs_cdrom_t* retro_vfs_file_get_cdrom_position(const libretro_vfs_implementation_file *stream); + +RETRO_END_DECLS + +#endif diff --git a/libretro-common/streams/file_stream.c b/libretro-common/streams/file_stream.c index 3d03fe3e53..aa22e0cdab 100644 --- a/libretro-common/streams/file_stream.c +++ b/libretro-common/streams/file_stream.c @@ -609,3 +609,8 @@ char *filestream_getline(RFILE *stream) newline[idx] = '\0'; return newline; } + +const libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream) +{ + return (const libretro_vfs_implementation_file*)stream->hfile; +} diff --git a/libretro-common/vfs/vfs_implementation.c b/libretro-common/vfs/vfs_implementation.c index 245b3c78d3..18dc470676 100644 --- a/libretro-common/vfs/vfs_implementation.c +++ b/libretro-common/vfs/vfs_implementation.c @@ -190,26 +190,11 @@ #include #include -#define RFILE_HINT_UNBUFFERED (1 << 8) +#ifdef HAVE_CDROM +#include +#endif -#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 -}; +#define RFILE_HINT_UNBUFFERED (1 << 8) int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, int64_t offset, int whence) { @@ -235,7 +220,12 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i return -1; return 0; #else - return fseeko(stream->fp, (off_t)offset, whence); +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_seek_cdrom(stream, offset, whence); + else +#endif + return fseeko(stream->fp, (off_t)offset, whence); #endif } #ifdef HAVE_MMAP @@ -296,6 +286,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( { int flags = 0; const char *mode_str = NULL; + int path_len = (int)strlen(path); libretro_vfs_implementation_file *stream = (libretro_vfs_implementation_file*) calloc(1, sizeof(*stream)); @@ -303,7 +294,6 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( const char *dumb_prefix = "vfsonly://"; size_t dumb_prefix_siz = strlen(dumb_prefix); int dumb_prefix_len = (int)dumb_prefix_siz; - int path_len = (int)strlen(path); if (path_len >= dumb_prefix_len) { @@ -312,6 +302,23 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( } #endif +#ifdef HAVE_CDROM + { + const char *cdrom_prefix = "cdrom://"; + size_t cdrom_prefix_siz = strlen(cdrom_prefix); + int cdrom_prefix_len = (int)cdrom_prefix_siz; + + if (path_len > cdrom_prefix_len) + { + if (!memcmp(path, cdrom_prefix, cdrom_prefix_len)) + { + path += cdrom_prefix_siz; + stream->scheme = VFS_SCHEME_CDROM; + } + } + } +#endif + if (!stream) return NULL; @@ -398,11 +405,29 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( } stream->fd = fd; #else - FILE *fp = (FILE*)fopen_utf8(path, mode_str); + FILE *fp; +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_open_cdrom(stream, path, mode, hints); +#ifdef _WIN32 + if (!stream->fh) + goto error; +#else + if (!stream->fp) + goto error; +#endif + } + else +#endif + { + fp = (FILE*)fopen_utf8(path, mode_str); - if (!fp) - goto error; + if (!fp) + goto error; + stream->fp = fp; + } /* Regarding setvbuf: * * https://www.freebsd.org/cgi/man.cgi?query=setvbuf&apropos=0&sektion=0&manpath=FreeBSD+11.1-RELEASE&arch=default&format=html @@ -413,10 +438,10 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( * Since C89 does not support specifying a null buffer with a non-zero size, we create and track our own buffer for it. */ /* TODO: this is only useful for a few platforms, find which and add ifdef */ - stream->fp = fp; #if !defined(PS2) && !defined(PSP) stream->buf = (char*)calloc(1, 0x4000); - setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); + if (stream->fp) + setvbuf(stream->fp, stream->buf, _IOFBF, 0x4000); #endif #endif } @@ -465,12 +490,26 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl( stream->size = orbisLseek(stream->fd, 0, SEEK_END); orbisLseek(stream->fd, 0, SEEK_SET); #else - retro_vfs_file_seek_internal(stream, 0, SEEK_SET); - retro_vfs_file_seek_internal(stream, 0, SEEK_END); +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); + retro_vfs_file_seek_cdrom(stream, 0, SEEK_END); - stream->size = retro_vfs_file_tell_impl(stream); + stream->size = retro_vfs_file_tell_impl(stream); - retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + retro_vfs_file_seek_cdrom(stream, 0, SEEK_SET); + } + else +#endif + { + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + retro_vfs_file_seek_internal(stream, 0, SEEK_END); + + stream->size = retro_vfs_file_tell_impl(stream); + + retro_vfs_file_seek_internal(stream, 0, SEEK_SET); + } #endif return stream; @@ -484,10 +523,20 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) if (!stream) return -1; +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + { + retro_vfs_file_close_cdrom(stream); + goto end; + } +#endif + if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { if (stream->fp) + { fclose(stream->fp); + } } else { @@ -506,10 +555,17 @@ int retro_vfs_file_close_impl(libretro_vfs_implementation_file *stream) close(stream->fd); #endif } +#ifdef HAVE_CDROM +end: + if (stream->cdrom.cue_buf) + free(stream->cdrom.cue_buf); +#endif if (stream->buf) free(stream->buf); + if (stream->orig_path) free(stream->orig_path); + free(stream); return 0; @@ -520,9 +576,12 @@ int retro_vfs_file_error_impl(libretro_vfs_implementation_file *stream) #ifdef ORBIS /* TODO/FIXME - implement this? */ return 0; -#else - return ferror(stream->fp); #endif +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_error_cdrom(stream); +#endif + return ferror(stream->fp); } int64_t retro_vfs_file_size_impl(libretro_vfs_implementation_file *stream) @@ -555,6 +614,10 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) if ((stream->hints & RFILE_HINT_UNBUFFERED) == 0) { +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_tell_cdrom(stream); +#endif #ifdef ORBIS int64_t ret = orbisLseek(stream->fd, 0, SEEK_CUR); if (ret < 0) @@ -565,7 +628,7 @@ int64_t retro_vfs_file_tell_impl(libretro_vfs_implementation_file *stream) #ifdef ATLEAST_VC2005 return _ftelli64(stream->fp); #else - return ftell(stream->fp); + return ftell(stream->fp); #endif #endif } @@ -614,7 +677,12 @@ int64_t retro_vfs_file_read_impl(libretro_vfs_implementation_file *stream, return -1; return 0; #else - return fread(s, 1, (size_t)len, stream->fp); +#ifdef HAVE_CDROM + if (stream->scheme == VFS_SCHEME_CDROM) + return retro_vfs_file_read_cdrom(stream, s, len); + else +#endif + return fread(s, 1, (size_t)len, stream->fp); #endif } #ifdef HAVE_MMAP diff --git a/libretro-common/vfs/vfs_implementation_cdrom.c b/libretro-common/vfs/vfs_implementation_cdrom.c new file mode 100644 index 0000000000..531716b519 --- /dev/null +++ b/libretro-common/vfs/vfs_implementation_cdrom.c @@ -0,0 +1,429 @@ +/* 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 +#include + +#ifdef _WIN32 +#include +#endif + +static cdrom_toc_t vfs_cdrom_toc = {0}; + +const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void) +{ + return &vfs_cdrom_toc; +} + +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 %" PRIu64 " is now at %" PRIu64 "\n", stream->orig_path, offset, stream->cdrom.byte_pos); + fflush(stdout); +#endif + } + else if (string_is_equal_noncase(ext, "bin")) + { + int lba = (offset / 2352); + unsigned char min = 0; + unsigned char sec = 0; + unsigned char frame = 0; + const char *seek_type = "SEEK_SET"; + + (void)seek_type; + + switch (whence) + { + case SEEK_CUR: + { + unsigned new_lba; + + stream->cdrom.byte_pos += offset; + new_lba = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352); + seek_type = "SEEK_CUR"; + + lba_to_msf(new_lba, &min, &sec, &frame); + + break; + } + case SEEK_END: + { + ssize_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) - 1; + + lba_to_msf(end_lba + lba, &min, &sec, &frame); + + stream->cdrom.byte_pos = end_lba * 2352; + seek_type = "SEEK_END"; + + break; + } + case SEEK_SET: + default: + { + seek_type = "SEEK_SET"; + stream->cdrom.byte_pos = offset; + lba_to_msf(vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352), &min, &sec, &frame); + break; + } + } + + stream->cdrom.cur_min = min; + stream->cdrom.cur_sec = sec; + stream->cdrom.cur_frame = frame; + stream->cdrom.cur_lba = msf_to_lba(min, sec, frame); + +#ifdef CDROM_DEBUG + printf("CDROM Seek %s: Path %s Offset %" PRIu64 " is now at %" PRIu64 " (MSF %02d:%02d:%02d) (LBA %u)...\n", seek_type, stream->orig_path, offset, stream->cdrom.byte_pos, stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame, stream->cdrom.cur_lba); + fflush(stdout); +#endif + } + else + return -1; + + return 0; +} + +void retro_vfs_file_open_cdrom( + libretro_vfs_implementation_file *stream, + const char *path, unsigned mode, unsigned hints) +{ +#ifdef __linux__ + char model[32] = {0}; + char cdrom_path[] = "/dev/sg1"; + size_t path_len = strlen(path); + const char *ext = path_get_extension(path); + + stream->cdrom.cur_track = 1; + + if (!string_is_equal_noncase(ext, "cue") && !string_is_equal_noncase(ext, "bin")) + return; + + if (path_len >= strlen("drive1-track01.bin")) + { + if (!memcmp(path, "drive", strlen("drive"))) + { + if (!memcmp(path + 6, "-track", strlen("-track"))) + { + if (sscanf(path + 12, "%02u", (unsigned*)&stream->cdrom.cur_track)) + { +#ifdef CDROM_DEBUG + printf("CDROM: Opening track %d\n", stream->cdrom.cur_track); + fflush(stdout); +#endif + } + } + } + } + + 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]; + vfs_cdrom_toc.drive = stream->cdrom.drive; + } + } + } + +#ifdef CDROM_DEBUG + printf("CDROM Open: Path %s URI %s\n", cdrom_path, path); + fflush(stdout); +#endif + stream->fp = (FILE*)fopen_utf8(cdrom_path, "r+b"); + + if (stream->fp) + { + if (!cdrom_get_inquiry(stream, model, sizeof(model))) + { +#ifdef CDROM_DEBUG + printf("CDROM Model: %s\n", model); + fflush(stdout); +#endif + } + } + else + return; + + 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(stream, &stream->cdrom.cue_buf, &stream->cdrom.cue_len, stream->cdrom.drive, &vfs_cdrom_toc.num_tracks, &vfs_cdrom_toc); + +#ifdef CDROM_DEBUG + if (string_is_empty(stream->cdrom.cue_buf)) + { + printf("Error writing cue sheet.\n"); + fflush(stdout); + } + else + { + printf("CDROM CUE Sheet:\n%s\n", stream->cdrom.cue_buf); + fflush(stdout); + } +#endif + } +#endif +#ifdef _WIN32 + char model[32] = {0}; + char cdrom_path[] = "\\\\.\\D:"; + 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; + + if (path_len >= strlen("d:/drive-track01.bin")) + { + if (!memcmp(path + 1, ":/drive-track", strlen(":/drive-track"))) + { + if (sscanf(path + 14, "%02u", (unsigned*)&stream->cdrom.cur_track)) + { +#ifdef CDROM_DEBUG + printf("CDROM: Opening track %d\n", stream->cdrom.cur_track); + fflush(stdout); +#endif + } + } + } + + if (path_len >= strlen("d:/drive.cue")) + { + if (!memcmp(path + 1, ":/drive", strlen(":/drive"))) + { + if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) + { + cdrom_path[4] = path[0]; + stream->cdrom.drive = path[0]; + vfs_cdrom_toc.drive = stream->cdrom.drive; + } + } + } + +#ifdef CDROM_DEBUG + printf("CDROM Open: Path %s URI %s\n", cdrom_path, path); + fflush(stdout); +#endif + stream->fh = CreateFile(cdrom_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (stream->fh == INVALID_HANDLE_VALUE) + return; + else + { + if (!cdrom_get_inquiry(stream, model, sizeof(model))) + { +#ifdef CDROM_DEBUG + printf("CDROM Model: %s\n", model); + fflush(stdout); +#endif + } + } + + 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(stream, &stream->cdrom.cue_buf, &stream->cdrom.cue_len, stream->cdrom.drive, &vfs_cdrom_toc.num_tracks, &vfs_cdrom_toc); + +#ifdef CDROM_DEBUG + if (string_is_empty(stream->cdrom.cue_buf)) + { + printf("Error writing cue sheet.\n"); + fflush(stdout); + } + else + { + printf("CDROM CUE Sheet:\n%s\n", stream->cdrom.cue_buf); + fflush(stdout); + } +#endif + } +#endif + if (vfs_cdrom_toc.num_tracks > 1 && stream->cdrom.cur_track) + { + stream->cdrom.cur_min = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].min; + stream->cdrom.cur_sec = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].sec; + stream->cdrom.cur_frame = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].frame; + stream->cdrom.cur_lba = msf_to_lba(stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame); + } + else + { + stream->cdrom.cur_min = vfs_cdrom_toc.track[0].min; + stream->cdrom.cur_sec = vfs_cdrom_toc.track[0].sec; + stream->cdrom.cur_frame = vfs_cdrom_toc.track[0].frame; + stream->cdrom.cur_lba = msf_to_lba(stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame); + } +} + +int retro_vfs_file_close_cdrom(libretro_vfs_implementation_file *stream) +{ +#ifdef CDROM_DEBUG + printf("CDROM Close: Path %s\n", stream->orig_path); + fflush(stdout); +#endif + +#ifdef _WIN32 + if (!stream->fh || !CloseHandle(stream->fh)) + return -1; +#else + if (!stream->fp || fclose(stream->fp)) + return -1; +#endif + + 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 %" PRIu64 "\n", stream->orig_path, stream->cdrom.byte_pos); + fflush(stdout); +#endif + return stream->cdrom.byte_pos; + } + else if (string_is_equal_noncase(ext, "bin")) + { +#ifdef CDROM_DEBUG + printf("CDROM (bin) Tell: Path %s Position %" PRId64 "\n", stream->orig_path, stream->cdrom.byte_pos); + fflush(stdout); +#endif + return stream->cdrom.byte_pos; + } + + 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 %" PRIu64 " bytes from cuesheet starting at %" PRIu64 "...\n", len, stream->cdrom.byte_pos); + fflush(stdout); +#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 %" PRIu64 " bytes from cuesheet starting at %" PRIu64 " failed.\n", len, stream->cdrom.byte_pos); + fflush(stdout); +#endif + return 0; + } + } + else if (string_is_equal_noncase(ext, "bin")) + { + size_t skip = stream->cdrom.byte_pos % 2352; + unsigned char min = 0; + unsigned char sec = 0; + unsigned char frame = 0; + + lba_to_msf(stream->cdrom.cur_lba, &min, &sec, &frame); + +#ifdef CDROM_DEBUG + printf("CDROM Read: Reading %" PRIu64 " bytes from %s starting at byte offset %" PRIu64 " (MSF %02d:%02d:%02d) (LBA %u) skip %" PRIu64 "...\n", len, stream->orig_path, stream->cdrom.byte_pos, min, sec, frame, stream->cdrom.cur_lba, skip); + fflush(stdout); +#endif + + rv = cdrom_read(stream, min, sec, frame, s, (size_t)len, skip); + + if (rv) + { +#ifdef CDROM_DEBUG + printf("Failed to read %" PRIu64 " bytes from CD.\n", len); + fflush(stdout); +#endif + return 0; + } + + stream->cdrom.byte_pos += len; + stream->cdrom.cur_lba = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352); + lba_to_msf(stream->cdrom.cur_lba, &stream->cdrom.cur_min, &stream->cdrom.cur_sec, &stream->cdrom.cur_frame); + +#ifdef CDROM_DEBUG + printf("CDROM read %" PRIu64 " bytes, position is now: %" PRIu64 " (MSF %02d:%02d:%02d) (LBA %u)\n", len, stream->cdrom.byte_pos, stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame, msf_to_lba(stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame)); + fflush(stdout); +#endif + + return len; + } + + return 0; +} + +int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream) +{ + return 0; +} + +const vfs_cdrom_t* retro_vfs_file_get_cdrom_position(const libretro_vfs_implementation_file *stream) +{ + return &stream->cdrom; +} diff --git a/paths.c b/paths.c index e5e2b6ab06..b63eb07a2f 100644 --- a/paths.c +++ b/paths.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2019 - Daniel De Matteis * Copyright (C) 2017-2019 - Andrés Suárez + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/paths.h b/paths.h index fbb043630e..b734e7fe12 100644 --- a/paths.h +++ b/paths.h @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2019 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/performance_counters.c b/performance_counters.c index 6ac89ac286..f453e67afe 100644 --- a/performance_counters.c +++ b/performance_counters.c @@ -1,6 +1,7 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- diff --git a/verbosity.c b/verbosity.c index c39dfd678c..435042eafd 100644 --- a/verbosity.c +++ b/verbosity.c @@ -1,5 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2016-2019 - Brad Parker * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found-