1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (cdrom.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #include <cdrom/cdrom.h>
31 #include <compat/strl.h>
32 #include <compat/strcasestr.h>
33 #include <retro_math.h>
34 #include <retro_timers.h>
35 #include <streams/file_stream.h>
36 #include <retro_endianness.h>
37 #include <retro_miscellaneous.h>
38 #include <vfs/vfs_implementation.h>
39 #include <lists/string_list.h>
40 #include <lists/dir_list.h>
41 #include <string/stdstring.h>
51 #if defined(__linux__) && !defined(ANDROID)
52 #include <sys/ioctl.h>
56 #if defined(_WIN32) && !defined(_XBOX)
62 #define CDROM_CUE_TRACK_BYTES 107
63 #define CDROM_MAX_SENSE_BYTES 16
64 #define CDROM_MAX_RETRIES 10
71 } CDROM_CMD_Direction;
73 void cdrom_lba_to_msf(unsigned lba, unsigned char *min, unsigned char *sec, unsigned char *frame)
75 if (!min || !sec || !frame)
85 unsigned cdrom_msf_to_lba(unsigned char min, unsigned char sec, unsigned char frame)
87 return (min * 60 + sec) * 75 + frame;
90 void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame)
92 if (!min || !sec || !frame)
95 *min = (*frame == 74) ? (*sec < 59 ? *min : *min + 1) : *min;
96 *sec = (*frame == 74) ? (*sec < 59 ? (*sec + 1) : 0) : *sec;
97 *frame = (*frame < 74) ? (*frame + 1) : 0;
101 static void cdrom_print_sense_data(const unsigned char *sense, size_t len)
104 const char *sense_key_text = NULL;
111 printf("[CDROM] Sense data buffer length too small.\n");
116 key = sense[2] & 0xF;
120 printf("[CDROM] Sense Data: ");
122 for (i = 0; i < MIN(len, 16); i++)
124 printf("%02X ", sense[i]);
129 if (sense[0] == 0x70)
130 printf("[CDROM] CURRENT ERROR:\n");
131 if (sense[0] == 0x71)
132 printf("[CDROM] DEFERRED ERROR:\n");
137 sense_key_text = "NO SENSE";
140 sense_key_text = "RECOVERED ERROR";
143 sense_key_text = "NOT READY";
146 sense_key_text = "MEDIUM ERROR";
149 sense_key_text = "HARDWARE ERROR";
152 sense_key_text = "ILLEGAL REQUEST";
155 sense_key_text = "UNIT ATTENTION";
158 sense_key_text = "DATA PROTECT";
161 sense_key_text = "BLANK CHECK";
164 sense_key_text = "VENDOR SPECIFIC";
167 sense_key_text = "COPY ABORTED";
170 sense_key_text = "ABORTED COMMAND";
173 sense_key_text = "VOLUME OVERFLOW";
176 sense_key_text = "MISCOMPARE";
180 printf("[CDROM] Sense Key: %02X (%s)\n", key, sense_key_text ? sense_key_text : "null");
181 printf("[CDROM] ASC: %02X\n", asc);
182 printf("[CDROM] ASCQ: %02X\n", ascq);
195 printf("[CDROM] Description: LOGICAL UNIT IS IN PROCESS OF BECOMING READY\n");
208 printf("[CDROM] Description: MEDIUM NOT PRESENT\n");
211 printf("[CDROM] Description: MEDIUM NOT PRESENT - LOADABLE\n");
214 printf("[CDROM] Description: MEDIUM NOT PRESENT - TRAY CLOSED\n");
217 printf("[CDROM] Description: MEDIUM NOT PRESENT - TRAY OPEN\n");
231 if (asc == 0x11 && ascq == 0x5)
232 printf("[CDROM] Description: L-EC UNCORRECTABLE ERROR\n");
237 if (asc == 0x20 && ascq == 0)
238 printf("[CDROM] Description: INVALID COMMAND OPERATION CODE\n");
239 else if (asc == 0x24 && ascq == 0)
240 printf("[CDROM] Description: INVALID FIELD IN CDB\n");
241 else if (asc == 0x26 && ascq == 0)
242 printf("[CDROM] Description: INVALID FIELD IN PARAMETER LIST\n");
247 if (asc == 0x28 && ascq == 0)
248 printf("[CDROM] Description: NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED\n");
259 #if defined(_WIN32) && !defined(_XBOX)
260 static int cdrom_send_command_win32(const libretro_vfs_implementation_file *stream, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, unsigned char *sense, size_t sense_len)
266 const char *extra = " ";
267 static unsigned char last_min = 0;
268 static unsigned char last_sec = 0;
269 static unsigned char last_frame = 0;
271 unsigned lba_cur = cdrom_msf_to_lba(last_min, last_sec, last_frame);
272 unsigned lba_req = cdrom_msf_to_lba(cmd[3], cmd[4], cmd[5]);
274 struct sptd_with_sense
276 SCSI_PASS_THROUGH_DIRECT s;
280 memset(&sptd, 0, sizeof(sptd));
282 sptd.s.Length = sizeof(sptd.s);
283 sptd.s.CdbLength = cmd_len;
288 sptd.s.DataIn = SCSI_IOCTL_DATA_IN;
291 sptd.s.DataIn = SCSI_IOCTL_DATA_OUT;
295 sptd.s.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
299 sptd.s.TimeOutValue = 5;
300 sptd.s.DataBuffer = buf;
301 sptd.s.DataTransferLength = len;
302 sptd.s.SenseInfoLength = sizeof(sptd.sense);
303 sptd.s.SenseInfoOffset = offsetof(struct sptd_with_sense, sense);
305 memcpy(sptd.s.Cdb, cmd, cmd_len);
307 ioctl_rv = DeviceIoControl(stream->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
308 sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL);
311 if (lba_req < lba_cur)
312 extra = " BACKWARDS SECTOR READ";
313 else if (lba_req > lba_cur)
314 extra = " SKIPPED SECTOR READ";
318 double time_taken = (double)(((clock() - t) * 1000) / CLOCKS_PER_SEC);
319 printf("time taken %f ms for DT received length %ld of %" PRId64 " for %02d:%02d:%02d to %02d:%02d:%02d%s req %d cur %d cur_lba %d\n", time_taken, sptd.s.DataTransferLength, len, cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], cmd[8], extra, lba_req, lba_cur, stream->cdrom.cur_lba);
326 increment_msf(&last_min, &last_sec, &last_frame);
329 if (!ioctl_rv || sptd.s.ScsiStatus != 0)
336 #if defined(__linux__) && !defined(ANDROID)
337 static int cdrom_send_command_linux(const libretro_vfs_implementation_file *stream, CDROM_CMD_Direction dir, void *buf, size_t len, unsigned char *cmd, size_t cmd_len, unsigned char *sense, size_t sense_len)
339 sg_io_hdr_t sgio = {0};
345 sgio.dxfer_direction = SG_DXFER_FROM_DEV;
348 sgio.dxfer_direction = SG_DXFER_TO_DEV;
352 sgio.dxfer_direction = SG_DXFER_NONE;
356 sgio.interface_id = 'S';
357 sgio.cmd_len = cmd_len;
360 sgio.dxfer_len = len;
362 sgio.mx_sb_len = sense_len;
365 rv = ioctl(fileno(stream->fp), SG_IO, &sgio);
367 if (rv == -1 || sgio.info & SG_INFO_CHECK)
374 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)
376 unsigned char *xfer_buf = NULL;
377 unsigned char *xfer_buf_pos = xfer_buf;
378 unsigned char sense[CDROM_MAX_SENSE_BYTES] = {0};
379 unsigned char retries_left = CDROM_MAX_RETRIES;
382 size_t padded_req_bytes;
383 size_t copied_bytes = 0;
384 bool read_cd = false;
386 if (!cmd || cmd_len == 0)
389 if (cmd[0] == 0xBE || cmd[0] == 0xB9)
391 frames = ceil((len + skip) / 2352.0);
392 padded_req_bytes = 2352 * frames;
394 /* these will be incremented below */
401 padded_req_bytes = len + skip;
404 xfer_buf = (unsigned char*)memalign_alloc(4096, padded_req_bytes);
405 xfer_buf_pos = xfer_buf;
410 memset(xfer_buf, 0, padded_req_bytes);
412 printf("Number of frames to read: %d\n", frames);
415 for (i = 0; i < frames; i++)
417 size_t request_len = padded_req_bytes;
418 size_t copy_len = request_len;
419 bool cached_read = false;
423 unsigned lba_req = 0;
426 copy_len = request_len;
428 increment_msf(&cmd[6], &cmd[7], &cmd[8]);
433 increment_msf(&cmd[3], &cmd[4], &cmd[5]);
443 copy_len = len - copied_bytes;
446 lba_req = cdrom_msf_to_lba(cmd[3], cmd[4], cmd[5]);
448 if (stream->cdrom.last_frame_valid && lba_req == stream->cdrom.last_frame_lba)
450 /* use cached frame */
453 printf("[CDROM] Using cached frame\n");
456 /* assumes request_len is always equal to the size of last_frame */
457 memcpy(xfer_buf_pos, stream->cdrom.last_frame, sizeof(stream->cdrom.last_frame));
467 printf("[CDROM] Send Command: ");
469 for (j = 0; j < cmd_len / sizeof(*cmd); j++)
471 printf("%02X ", cmd[j]);
475 printf("(buffer of size %" PRId64 " with skip bytes %" PRId64 " padded to %" PRId64 "), frame %d\n", len, skip, padded_req_bytes, i);
484 #if defined(__linux__) && !defined(ANDROID)
485 if (cached_read || !cdrom_send_command_linux(stream, dir, xfer_buf_pos, request_len, cmd, cmd_len, sense, sizeof(sense)))
487 #if defined(_WIN32) && !defined(_XBOX)
488 if (cached_read || !cdrom_send_command_win32(stream, dir, xfer_buf_pos, request_len, cmd, cmd_len, sense, sizeof(sense)))
497 printf("offsetting %" PRId64 " from buf, copying at xfer_buf offset %" PRId64 ", copying %" PRId64 " bytes\n", copied_bytes, (xfer_buf_pos + skip) - xfer_buf, copy_len);
500 memcpy((char*)buf + copied_bytes, xfer_buf_pos + skip, copy_len);
501 copied_bytes += copy_len;
503 if (read_cd && !cached_read && request_len >= 2352)
505 unsigned frame_end = cdrom_msf_to_lba(cmd[6], cmd[7], cmd[8]);
507 /* cache the last received frame */
508 memcpy(stream->cdrom.last_frame, xfer_buf_pos, sizeof(stream->cdrom.last_frame));
509 stream->cdrom.last_frame_valid = true;
510 /* the ending frame is never actually read, so what we really just read is the one right before that */
511 stream->cdrom.last_frame_lba = frame_end - 1;
514 stream->cdrom.last_frame_valid = false;
517 printf("Frame %d, adding %" PRId64 " to buf_pos, is now %" PRId64 ". skip is %" PRId64 "\n", i, request_len, (xfer_buf_pos + request_len) - xfer_buf, skip);
520 xfer_buf_pos += request_len;
526 cdrom_print_sense_data(sense, sizeof(sense));
529 /* INQUIRY/TEST/SENSE should never fail, don't retry. */
530 /* READ ATIP seems to fail outright on some drives with pressed discs, skip retries. */
531 if (cmd[0] != 0x0 && cmd[0] != 0x12 && cmd[0] != 0x5A && !(cmd[0] == 0x43 && cmd[2] == 0x4))
533 unsigned char key = sense[2] & 0xF;
545 printf("[CDROM] Read Retry...\n");
556 printf("[CDROM] Read retries failed, giving up.\n");
572 memalign_free(xfer_buf);
577 static const char* get_profile(unsigned short profile)
582 return "Removable disk";
597 return "DVD-R Sequential Recording";
603 return "DVD-RW Restricted Overwrite";
606 return "DVD-RW Sequential recording";
609 return "DVD-R Dual Layer Sequential Recording";
612 return "DVD-R Dual Layer Jump Recording";
615 return "DVD-RW Dual Layer";
624 return "DVD+RW Dual Layer";
627 return "DVD+R Dual Layer";
654 return "HD DVD-R Dual Layer";
657 return "HD DVD-RW Dual Layer";
666 int cdrom_get_sense(libretro_vfs_implementation_file *stream, unsigned char *sense, size_t len)
668 unsigned char cdb[] = {0x3, 0, 0, 0, 0xFC, 0};
669 unsigned char buf[0xFC] = {0};
670 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
673 printf("[CDROM] get sense data status code %d\n", rv);
681 cdrom_print_sense_data(buf, sizeof(buf));
687 void cdrom_get_current_config_random_readable(libretro_vfs_implementation_file *stream)
689 unsigned char cdb[] = {0x46, 0x2, 0, 0x10, 0, 0, 0, 0, 0x14, 0};
690 unsigned char buf[0x14] = {0};
691 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
694 printf("[CDROM] get current config random readable status code %d\n", rv);
699 printf("[CDROM] Feature Header: ");
701 for (i = 0; i < 8; i++)
703 printf("%02X ", buf[i]);
708 printf("[CDROM] Random Readable Feature Descriptor: ");
710 for (i = 0; i < 12; i++)
712 printf("%02X ", buf[8 + i]);
717 printf("[CDROM] Supported commands: READ CAPACITY, READ (10)\n");
720 void cdrom_get_current_config_multiread(libretro_vfs_implementation_file *stream)
722 unsigned char cdb[] = {0x46, 0x2, 0, 0x1D, 0, 0, 0, 0, 0xC, 0};
723 unsigned char buf[0xC] = {0};
724 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
727 printf("[CDROM] get current config multi-read status code %d\n", rv);
732 printf("[CDROM] Feature Header: ");
734 for (i = 0; i < 8; i++)
736 printf("%02X ", buf[i]);
741 printf("[CDROM] Multi-Read Feature Descriptor: ");
743 for (i = 0; i < 4; i++)
745 printf("%02X ", buf[8 + i]);
750 printf("[CDROM] Supported commands: READ (10), READ CD, READ DISC INFORMATION, READ TRACK INFORMATION\n");
753 void cdrom_get_current_config_cdread(libretro_vfs_implementation_file *stream)
755 unsigned char cdb[] = {0x46, 0x2, 0, 0x1E, 0, 0, 0, 0, 0x10, 0};
756 unsigned char buf[0x10] = {0};
757 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
760 printf("[CDROM] get current config cd read status code %d\n", rv);
765 printf("[CDROM] Feature Header: ");
767 for (i = 0; i < 8; i++)
769 printf("%02X ", buf[i]);
774 printf("[CDROM] CD Read Feature Descriptor: ");
776 for (i = 0; i < 8; i++)
778 printf("%02X ", buf[8 + i]);
782 printf("(current)\n");
784 printf("[CDROM] Supported commands: READ CD, READ CD MSF, READ TOC/PMA/ATIP\n");
787 void cdrom_get_current_config_profiles(libretro_vfs_implementation_file *stream)
789 unsigned char cdb[] = {0x46, 0x2, 0, 0x0, 0, 0, 0, 0xFF, 0xFA, 0};
790 unsigned char buf[0xFFFA] = {0};
791 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
794 printf("[CDROM] get current config profiles status code %d\n", rv);
799 printf("[CDROM] Feature Header: ");
801 for (i = 0; i < 8; i++)
803 printf("%02X ", buf[i]);
808 printf("[CDROM] Profile List Descriptor: ");
810 for (i = 0; i < 4; i++)
812 printf("%02X ", buf[8 + i]);
817 printf("[CDROM] Number of profiles: %u\n", buf[8 + 3] / 4);
819 for (i = 0; i < buf[8 + 3] / 4; i++)
821 unsigned short profile = (buf[8 + (4 * (i + 1))] << 8) | buf[8 + (4 * (i + 1)) + 1];
823 printf("[CDROM] Profile Number: %04X (%s) ", profile, get_profile(profile));
825 if (buf[8 + (4 * (i + 1)) + 2] & 1)
826 printf("(current)\n");
832 void cdrom_get_current_config_core(libretro_vfs_implementation_file *stream)
834 unsigned char cdb[] = {0x46, 0x2, 0, 0x1, 0, 0, 0, 0, 0x14, 0};
835 unsigned char buf[20] = {0};
836 unsigned intf_std = 0;
837 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
839 const char *intf_std_name = "Unknown";
841 printf("[CDROM] get current config core status code %d\n", rv);
846 printf("[CDROM] Feature Header: ");
848 for (i = 0; i < 8; i++)
850 printf("%02X ", buf[i]);
855 if (buf[6] == 0 && buf[7] == 8)
856 printf("[CDROM] Current Profile: CD-ROM\n");
858 printf("[CDROM] Current Profile: %02X%02X\n", buf[6], buf[7]);
860 printf("[CDROM] Core Feature Descriptor: ");
862 for (i = 0; i < 12; i++)
864 printf("%02X ", buf[8 + i]);
869 intf_std = buf[8 + 4] << 24 | buf[8 + 5] << 16 | buf[8 + 6] << 8 | buf[8 + 7];
874 intf_std_name = "Unspecified";
877 intf_std_name = "SCSI Family";
880 intf_std_name = "ATAPI";
883 intf_std_name = "Serial ATAPI";
886 intf_std_name = "USB";
892 printf("[CDROM] Physical Interface Standard: %u (%s)\n", intf_std, intf_std_name);
895 int cdrom_read_subq(libretro_vfs_implementation_file *stream, unsigned char *buf, size_t len)
897 /* MMC Command: READ TOC/PMA/ATIP */
898 unsigned char cdb[] = {0x43, 0x2, 0x2, 0, 0, 0, 0x1, 0x9, 0x30, 0};
900 unsigned short data_len = 0;
901 unsigned char first_session = 0;
902 unsigned char last_session = 0;
910 rv = cdrom_send_command(stream, DIRECTION_IN, buf, len, cdb, sizeof(cdb), 0);
916 data_len = buf[0] << 8 | buf[1];
917 first_session = buf[2];
918 last_session = buf[3];
920 printf("[CDROM] Data Length: %d\n", data_len);
921 printf("[CDROM] First Session: %d\n", first_session);
922 printf("[CDROM] Last Session: %d\n", last_session);
924 for (i = 0; i < (data_len - 2) / 11; i++)
926 unsigned char session_num = buf[4 + (i * 11) + 0];
927 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
928 /*unsigned char control = buf[4 + (i * 11) + 1] & 0xF;*/
929 unsigned char tno = buf[4 + (i * 11) + 2];
930 unsigned char point = buf[4 + (i * 11) + 3];
931 unsigned char pmin = buf[4 + (i * 11) + 8];
932 unsigned char psec = buf[4 + (i * 11) + 9];
933 unsigned char pframe = buf[4 + (i * 11) + 10];
935 /*printf("i %d control %d adr %d tno %d point %d: ", i, control, adr, tno, point);*/
936 /* why is control always 0? */
938 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
940 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
941 printf("Track start time: (aMSF %02u:%02u:%02u) ", (unsigned)pmin, (unsigned)psec, (unsigned)pframe);
943 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA0)
945 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
946 printf("First Track Number: %d ", pmin);
947 printf("Disc Type: %d ", psec);
949 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
951 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
952 printf("Last Track Number: %d ", pmin);
954 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA2)
956 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
957 printf("Lead-out start time: (aMSF %02u:%02u:%02u) ", (unsigned)pmin, (unsigned)psec, (unsigned)pframe);
968 static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsigned char track, cdrom_toc_t *toc)
970 /* MMC Command: READ TRACK INFORMATION */
971 unsigned char cdb[] = {0x52, 0x1, 0, 0, 0, 0, 0, 0x1, 0x80, 0};
972 unsigned char buf[384] = {0};
974 unsigned track_size = 0;
976 ssize_t pregap_lba_len;
980 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
985 memcpy(&lba, buf + 8, 4);
986 memcpy(&track_size, buf + 24, 4);
988 lba = swap_if_little32(lba);
989 track_size = swap_if_little32(track_size);
991 /* lba_start may be earlier than the MSF start times seen in read_subq */
992 toc->track[track - 1].lba_start = lba;
993 toc->track[track - 1].track_size = track_size;
995 pregap_lba_len = (toc->track[track - 1].audio ? 0 : (toc->track[track - 1].lba - toc->track[track - 1].lba_start));
997 toc->track[track - 1].track_bytes = (track_size - pregap_lba_len) * 2352;
998 toc->track[track - 1].mode = buf[6] & 0xF;
1001 printf("[CDROM] Track %d Info: ", track);
1002 printf("Copy: %d ", (buf[5] & 0x10) > 0);
1003 printf("Data Mode: %d ", toc->track[track - 1].mode);
1004 printf("LBA Start: %d (%d) ", lba, toc->track[track - 1].lba);
1005 printf("Track Size: %d\n", track_size);
1012 int cdrom_set_read_speed(libretro_vfs_implementation_file *stream, unsigned speed)
1014 /* MMC Command: SET CD SPEED */
1015 unsigned char cmd[] = {0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1017 cmd[2] = (speed >> 24) & 0xFF;
1018 cmd[3] = (speed >> 16) & 0xFF;
1019 cmd[4] = (speed >> 8) & 0xFF;
1020 cmd[5] = speed & 0xFF;
1022 return cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cmd, sizeof(cmd), 0);
1025 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)
1027 unsigned char buf[2352] = {0};
1028 unsigned short data_len = 0;
1034 if (!out_buf || !out_len || !num_tracks || !toc)
1037 printf("[CDROM] Invalid buffer/length pointer for CDROM cue sheet\n");
1043 cdrom_set_read_speed(stream, 0xFFFFFFFF);
1045 rv = cdrom_read_subq(stream, buf, sizeof(buf));
1050 data_len = buf[0] << 8 | buf[1];
1052 for (i = 0; i < (data_len - 2) / 11; i++)
1054 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
1055 unsigned char tno = buf[4 + (i * 11) + 2];
1056 unsigned char point = buf[4 + (i * 11) + 3];
1057 unsigned char pmin = buf[4 + (i * 11) + 8];
1059 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
1063 printf("[CDROM] Number of CDROM tracks: %d\n", *num_tracks);
1070 if (!*num_tracks || *num_tracks > 99)
1073 printf("[CDROM] Invalid number of CDROM tracks: %d\n", *num_tracks);
1079 len = CDROM_CUE_TRACK_BYTES * (*num_tracks);
1080 toc->num_tracks = *num_tracks;
1081 *out_buf = (char*)calloc(1, len);
1084 for (i = 0; i < (data_len - 2) / 11; i++)
1086 /*unsigned char session_num = buf[4 + (i * 11) + 0];*/
1087 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
1088 unsigned char control = buf[4 + (i * 11) + 1] & 0xF;
1089 unsigned char tno = buf[4 + (i * 11) + 2];
1090 unsigned char point = buf[4 + (i * 11) + 3];
1091 /*unsigned char amin = buf[4 + (i * 11) + 4];
1092 unsigned char asec = buf[4 + (i * 11) + 5];
1093 unsigned char aframe = buf[4 + (i * 11) + 6];*/
1094 unsigned char pmin = buf[4 + (i * 11) + 8];
1095 unsigned char psec = buf[4 + (i * 11) + 9];
1096 unsigned char pframe = buf[4 + (i * 11) + 10];
1097 unsigned lba = cdrom_msf_to_lba(pmin, psec, pframe);
1099 /*printf("i %d control %d adr %d tno %d point %d: amin %d asec %d aframe %d pmin %d psec %d pframe %d\n", i, control, adr, tno, point, amin, asec, aframe, pmin, psec, pframe);*/
1100 /* why is control always 0? */
1102 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
1105 const char *track_type = "MODE1/2352";
1107 audio = (!(control & 0x4) && !(control & 0x5));
1110 printf("[CDROM] Track %02d CONTROL %01X ADR %01X AUDIO? %d\n", point, control, adr, audio);
1114 toc->track[point - 1].track_num = point;
1115 toc->track[point - 1].min = pmin;
1116 toc->track[point - 1].sec = psec;
1117 toc->track[point - 1].frame = pframe;
1118 toc->track[point - 1].lba = lba;
1119 toc->track[point - 1].audio = audio;
1121 cdrom_read_track_info(stream, point, toc);
1124 track_type = "AUDIO";
1125 else if (toc->track[point - 1].mode == 1)
1126 track_type = "MODE1/2352";
1127 else if (toc->track[point - 1].mode == 2)
1128 track_type = "MODE2/2352";
1130 #if defined(_WIN32) && !defined(_XBOX)
1131 pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://%c:/drive-track%02d.bin\" BINARY\n", cdrom_drive, point);
1133 pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c-track%02d.bin\" BINARY\n", cdrom_drive, point);
1135 pos += snprintf(*out_buf + pos, len - pos, " TRACK %02d %s\n", point, track_type);
1138 unsigned pregap_lba_len = toc->track[point - 1].lba - toc->track[point - 1].lba_start;
1140 if (toc->track[point - 1].audio && pregap_lba_len > 0)
1142 unsigned char min = 0;
1143 unsigned char sec = 0;
1144 unsigned char frame = 0;
1146 cdrom_lba_to_msf(pregap_lba_len, &min, &sec, &frame);
1148 pos += snprintf(*out_buf + pos, len - pos, " INDEX 00 00:00:00\n");
1149 pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 %02u:%02u:%02u\n", (unsigned)min, (unsigned)sec, (unsigned)frame);
1152 pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 00:00:00\n");
1160 /* needs 32 bytes for full vendor, product and version */
1161 int cdrom_get_inquiry(libretro_vfs_implementation_file *stream, char *model, int len, bool *is_cdrom)
1163 /* MMC Command: INQUIRY */
1164 unsigned char cdb[] = {0x12, 0, 0, 0, 0xff, 0};
1165 unsigned char buf[256] = {0};
1166 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1172 if (model && len >= 32)
1174 memset(model, 0, len);
1177 memcpy(model, buf + 8, 8);
1182 memcpy(model + 9, buf + 16, 16);
1187 memcpy(model + 26, buf + 32, 4);
1190 cdrom = (buf[0] == 5);
1192 if (is_cdrom && cdrom)
1196 printf("[CDROM] Device Model: %s (is CD-ROM? %s)\n", model, (cdrom ? "yes" : "no"));
1201 int cdrom_read(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t *timeouts, unsigned char min, unsigned char sec, unsigned char frame, void *s, size_t len, size_t skip)
1203 /* MMC Command: READ CD MSF */
1204 unsigned char cdb[] = {0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0xF8, 0, 0};
1206 double frames = ceil((len + skip) / 2352.0);
1207 unsigned frame_end = cdrom_msf_to_lba(min, sec, frame) + frames;
1215 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1217 printf("[CDROM] single-frame read: %d %d %d skip %" PRId64 "\n", cdb[3], cdb[4], cdb[5], skip);
1223 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1226 printf("[CDROM] multi-frame read: %d sectors starting from %02d:%02d:%02d skip %" PRId64 "\n", (int)frames, cdb[3], cdb[4], cdb[5], skip);
1231 /* regardless of the length specified here, a new buffer will be allocated and padded to a sector multiple inside cdrom_send_command */
1232 rv = cdrom_send_command(stream, DIRECTION_IN, s, len, cdb, sizeof(cdb), skip);
1235 printf("[CDROM] read msf status code %d\n", rv);
1241 stream->cdrom.last_frame_valid = false;
1248 int cdrom_stop(libretro_vfs_implementation_file *stream)
1250 /* MMC Command: START STOP UNIT */
1251 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x0, 0};
1252 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1255 printf("[CDROM] stop status code %d\n", rv);
1265 int cdrom_unlock(libretro_vfs_implementation_file *stream)
1267 /* MMC Command: PREVENT ALLOW MEDIUM REMOVAL */
1268 unsigned char cdb[] = {0x1E, 0, 0, 0, 0x2, 0};
1269 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1272 printf("[CDROM] persistent prevent clear status code %d\n", rv);
1281 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1284 printf("[CDROM] prevent clear status code %d\n", rv);
1294 int cdrom_open_tray(libretro_vfs_implementation_file *stream)
1296 /* MMC Command: START STOP UNIT */
1297 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x2, 0};
1300 cdrom_unlock(stream);
1303 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1306 printf("[CDROM] open tray status code %d\n", rv);
1316 int cdrom_close_tray(libretro_vfs_implementation_file *stream)
1318 /* MMC Command: START STOP UNIT */
1319 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x3, 0};
1320 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1323 printf("[CDROM] close tray status code %d\n", rv);
1333 struct string_list* cdrom_get_available_drives(void)
1335 struct string_list *list = string_list_new();
1336 #if defined(__linux__) && !defined(ANDROID)
1337 struct string_list *dir_list = dir_list_new("/dev", NULL, false, false, false, false);
1344 for (i = 0; i < (int)dir_list->size; i++)
1346 if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg",
1347 STRLEN_CONST("/dev/sg")))
1349 libretro_vfs_implementation_file *stream;
1350 char drive_model[32] = {0};
1351 char drive_string[33] = {0};
1352 union string_list_elem_attr attr = {0};
1354 RFILE *file = filestream_open(
1355 dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
1356 bool is_cdrom = false;
1363 printf("[CDROM] Could not open %s, please check permissions.\n", dir_list->elems[i].data);
1369 stream = filestream_get_vfs_handle(file);
1370 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1371 filestream_close(file);
1376 sscanf(dir_list->elems[i].data + STRLEN_CONST("/dev/sg"),
1379 dev_index = '0' + dev_index;
1382 if (!string_is_empty(drive_model))
1383 strlcat(drive_string, drive_model, sizeof(drive_string));
1385 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1387 string_list_append(list, drive_string, attr);
1396 if (filestream_read_file("/proc/modules", (void**)&buf, &len))
1401 struct string_list mods = {0};
1403 string_list_initialize(&mods);
1405 if (string_split_noalloc(&mods, buf, "\n"))
1407 for (i = 0; i < (int)mods.size; i++)
1409 if (strcasestr(mods.elems[i].data, "sg "))
1418 string_list_deinitialize(&mods);
1424 printf("[CDROM] No sg devices found but kernel module is loaded.\n");
1429 printf("[CDROM] No sg devices found and sg kernel module is not loaded.\n");
1437 printf("[CDROM] No sg devices found, could not check if sg kernel module is loaded.\n");
1443 string_list_free(dir_list);
1445 #if defined(_WIN32) && !defined(_XBOX)
1446 DWORD drive_mask = GetLogicalDrives();
1449 for (i = 0; i < (int)(sizeof(DWORD) * 8); i++)
1451 char path[] = {"a:\\"};
1452 char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
1457 /* this drive letter doesn't exist */
1458 if (!(drive_mask & (1 << i)))
1461 if (GetDriveType(path) != DRIVE_CDROM)
1465 libretro_vfs_implementation_file *stream;
1466 bool is_cdrom = false;
1467 char drive_model[32] = {0};
1468 char drive_string[33] = {0};
1469 union string_list_elem_attr attr = {0};
1470 RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
1474 stream = filestream_get_vfs_handle(file);
1475 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1476 filestream_close(file);
1483 if (!string_is_empty(drive_model))
1484 strlcat(drive_string, drive_model, sizeof(drive_string));
1486 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1488 string_list_append(list, drive_string, attr);
1495 bool cdrom_is_media_inserted(libretro_vfs_implementation_file *stream)
1497 /* MMC Command: TEST UNIT READY */
1498 unsigned char cdb[] = {0x00, 0, 0, 0, 0, 0};
1499 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1502 printf("[CDROM] media inserted status code %d\n", rv);
1506 /* Will also return false if the drive is simply not ready yet (tray open, disc spinning back up after tray closed etc).
1507 * Command will not block or wait for media to become ready. */
1514 bool cdrom_drive_has_media(const char drive)
1517 char cdrom_path_bin[256] = {0};
1519 cdrom_device_fillpath(cdrom_path_bin, sizeof(cdrom_path_bin), drive, 1, false);
1521 file = filestream_open(cdrom_path_bin, RETRO_VFS_FILE_ACCESS_READ, 0);
1525 libretro_vfs_implementation_file *stream = filestream_get_vfs_handle(file);
1526 bool has_media = cdrom_is_media_inserted(stream);
1528 filestream_close(file);
1536 bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled)
1539 /* MMC Command: MODE SENSE (10) and MODE SELECT (10) */
1540 unsigned char cdb_sense_changeable[] = {0x5A, 0, 0x48, 0, 0, 0, 0, 0, 0x14, 0};
1541 unsigned char cdb_sense[] = {0x5A, 0, 0x8, 0, 0, 0, 0, 0, 0x14, 0};
1542 unsigned char cdb_select[] = {0x55, 0x10, 0, 0, 0, 0, 0, 0, 0x14, 0};
1543 unsigned char buf[20] = {0};
1544 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf),
1545 cdb_sense_changeable, sizeof(cdb_sense_changeable), 0);
1548 printf("[CDROM] mode sense changeable status code %d\n", rv);
1555 if (!(buf[10] & 0x1))
1557 /* RCD (read cache disable) bit is not changeable */
1559 printf("[CDROM] RCD (read cache disable) bit is not changeable.\n");
1565 memset(buf, 0, sizeof(buf));
1567 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense, sizeof(cdb_sense), 0);
1570 printf("mode sense status code %d\n", rv);
1578 printf("Mode sense data for caching mode page: ");
1580 for (i = 0; i < (int)sizeof(buf); i++)
1581 printf("%02X ", buf[i]);
1587 /* "When transferred during execution of the MODE SELECT (10) command, Mode Data Length is reserved." */
1588 for (i = 0; i < 8; i++)
1596 rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0);
1599 printf("mode select status code %d\n", rv);
1609 bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t *timeouts)
1611 /* MMC Command: MODE SENSE (10) */
1613 unsigned char cdb[] = {0x5A, 0, 0x1D, 0, 0, 0, 0, 0, 0x14, 0};
1614 unsigned char buf[20] = {0};
1615 unsigned short g1 = 0;
1616 unsigned short g2 = 0;
1617 unsigned short g3 = 0;
1622 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1625 printf("get timeouts status code %d\n", rv);
1632 g1 = buf[14] << 8 | buf[15];
1633 g2 = buf[16] << 8 | buf[17];
1634 g3 = buf[18] << 8 | buf[19];
1640 printf("Mode sense data for timeout groups: ");
1642 for (i = 0; i < (int)sizeof(buf); i++)
1643 printf("%02X ", buf[i]);
1647 printf("Group 1 Timeout: %d\n", g1);
1648 printf("Group 2 Timeout: %d\n", g2);
1649 printf("Group 3 Timeout: %d\n", g3);
1655 timeouts->g1_timeout = g1;
1656 timeouts->g2_timeout = g2;
1657 timeouts->g3_timeout = g3;
1662 bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
1664 /* MMC Command: READ TOC/PMA/ATIP */
1665 unsigned char cdb[] = {0x43, 0x2, 0x4, 0, 0, 0, 0, 0x9, 0x30, 0};
1666 unsigned char buf[32] = {0};
1667 unsigned short atip_len = 0;
1668 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1673 atip_len = buf[0] << 8 | buf[1];
1676 printf("ATIP Length %d, Disc Type %d, Disc Sub-Type %d\n",
1678 (buf[6] >> 6) & 0x1,
1679 ((buf[6] >> 5) & 0x1) << 2 | ((buf[6] >> 4) & 0x1) << 1 | ((buf[6] >> 3) & 0x1) << 0);
1688 void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
1690 if (!path || len == 0)
1695 size_t pos = strlcpy(path, "cdrom://", len);
1697 path[pos++] = drive;
1698 pos = strlcat(path, ":/drive.cue", len);
1701 size_t pos = strlcpy(path, "cdrom://drive", len);
1704 path[pos++] = drive;
1707 pos = strlcat(path, ".cue", len);
1714 size_t pos = strlcpy(path, "cdrom://", len);
1717 path[pos++] = drive;
1720 pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
1723 size_t pos = strlcpy(path, "cdrom://drive", len);
1725 path[pos++] = drive;
1726 pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);