libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / cdrom / cdrom.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2*
3* ---------------------------------------------------------------------------------------
4* The following license statement only applies to this file (cdrom.c).
5* ---------------------------------------------------------------------------------------
6*
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:
12*
13* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14*
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.
21*/
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <cdrom/cdrom.h>
28#include <libretro.h>
29#include <stdio.h>
30#include <string.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>
42#include <memalign.h>
43
44#include <math.h>
45#ifdef _WIN32
46#include <direct.h>
47#else
48#include <unistd.h>
49#endif
50
51#if defined(__linux__) && !defined(ANDROID)
52#include <sys/ioctl.h>
53#include <scsi/sg.h>
54#endif
55
56#if defined(_WIN32) && !defined(_XBOX)
57#include <windows.h>
58#include <winioctl.h>
59#include <ntddscsi.h>
60#endif
61
62#define CDROM_CUE_TRACK_BYTES 107
63#define CDROM_MAX_SENSE_BYTES 16
64#define CDROM_MAX_RETRIES 10
65
66typedef enum
67{
68 DIRECTION_NONE,
69 DIRECTION_IN,
70 DIRECTION_OUT
71} CDROM_CMD_Direction;
72
73void cdrom_lba_to_msf(unsigned lba, unsigned char *min, unsigned char *sec, unsigned char *frame)
74{
75 if (!min || !sec || !frame)
76 return;
77
78 *frame = lba % 75;
79 lba /= 75;
80 *sec = lba % 60;
81 lba /= 60;
82 *min = lba;
83}
84
85unsigned cdrom_msf_to_lba(unsigned char min, unsigned char sec, unsigned char frame)
86{
87 return (min * 60 + sec) * 75 + frame;
88}
89
90void increment_msf(unsigned char *min, unsigned char *sec, unsigned char *frame)
91{
92 if (!min || !sec || !frame)
93 return;
94
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;
98}
99
100#ifdef CDROM_DEBUG
101static void cdrom_print_sense_data(const unsigned char *sense, size_t len)
102{
103 unsigned i;
104 const char *sense_key_text = NULL;
105 unsigned char key;
106 unsigned char asc;
107 unsigned char ascq;
108
109 if (len < 16)
110 {
111 printf("[CDROM] Sense data buffer length too small.\n");
112 fflush(stdout);
113 return;
114 }
115
116 key = sense[2] & 0xF;
117 asc = sense[12];
118 ascq = sense[13];
119
120 printf("[CDROM] Sense Data: ");
121
122 for (i = 0; i < MIN(len, 16); i++)
123 {
124 printf("%02X ", sense[i]);
125 }
126
127 printf("\n");
128
129 if (sense[0] == 0x70)
130 printf("[CDROM] CURRENT ERROR:\n");
131 if (sense[0] == 0x71)
132 printf("[CDROM] DEFERRED ERROR:\n");
133
134 switch (key)
135 {
136 case 0:
137 sense_key_text = "NO SENSE";
138 break;
139 case 1:
140 sense_key_text = "RECOVERED ERROR";
141 break;
142 case 2:
143 sense_key_text = "NOT READY";
144 break;
145 case 3:
146 sense_key_text = "MEDIUM ERROR";
147 break;
148 case 4:
149 sense_key_text = "HARDWARE ERROR";
150 break;
151 case 5:
152 sense_key_text = "ILLEGAL REQUEST";
153 break;
154 case 6:
155 sense_key_text = "UNIT ATTENTION";
156 break;
157 case 7:
158 sense_key_text = "DATA PROTECT";
159 break;
160 case 8:
161 sense_key_text = "BLANK CHECK";
162 break;
163 case 9:
164 sense_key_text = "VENDOR SPECIFIC";
165 break;
166 case 10:
167 sense_key_text = "COPY ABORTED";
168 break;
169 case 11:
170 sense_key_text = "ABORTED COMMAND";
171 break;
172 case 13:
173 sense_key_text = "VOLUME OVERFLOW";
174 break;
175 case 14:
176 sense_key_text = "MISCOMPARE";
177 break;
178 }
179
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);
183
184 switch (key)
185 {
186 case 2:
187 {
188 switch (asc)
189 {
190 case 4:
191 {
192 switch (ascq)
193 {
194 case 1:
195 printf("[CDROM] Description: LOGICAL UNIT IS IN PROCESS OF BECOMING READY\n");
196 break;
197 default:
198 break;
199 }
200
201 break;
202 }
203 case 0x3a:
204 {
205 switch (ascq)
206 {
207 case 0:
208 printf("[CDROM] Description: MEDIUM NOT PRESENT\n");
209 break;
210 case 3:
211 printf("[CDROM] Description: MEDIUM NOT PRESENT - LOADABLE\n");
212 break;
213 case 1:
214 printf("[CDROM] Description: MEDIUM NOT PRESENT - TRAY CLOSED\n");
215 break;
216 case 2:
217 printf("[CDROM] Description: MEDIUM NOT PRESENT - TRAY OPEN\n");
218 break;
219 default:
220 break;
221 }
222
223 break;
224 }
225 default:
226 break;
227 }
228 }
229 case 3:
230 {
231 if (asc == 0x11 && ascq == 0x5)
232 printf("[CDROM] Description: L-EC UNCORRECTABLE ERROR\n");
233 break;
234 }
235 case 5:
236 {
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");
243 break;
244 }
245 case 6:
246 {
247 if (asc == 0x28 && ascq == 0)
248 printf("[CDROM] Description: NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED\n");
249 break;
250 }
251 default:
252 break;
253 }
254
255 fflush(stdout);
256}
257#endif
258
259#if defined(_WIN32) && !defined(_XBOX)
260static 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)
261{
262 DWORD ioctl_bytes;
263 BOOL ioctl_rv;
264#ifdef CDROM_DEBUG
265 clock_t t = clock();
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;
270
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]);
273#endif
274 struct sptd_with_sense
275 {
276 SCSI_PASS_THROUGH_DIRECT s;
277 UCHAR sense[128];
278 } sptd;
279
280 memset(&sptd, 0, sizeof(sptd));
281
282 sptd.s.Length = sizeof(sptd.s);
283 sptd.s.CdbLength = cmd_len;
284
285 switch (dir)
286 {
287 case DIRECTION_IN:
288 sptd.s.DataIn = SCSI_IOCTL_DATA_IN;
289 break;
290 case DIRECTION_OUT:
291 sptd.s.DataIn = SCSI_IOCTL_DATA_OUT;
292 break;
293 case DIRECTION_NONE:
294 default:
295 sptd.s.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
296 break;
297 }
298
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);
304
305 memcpy(sptd.s.Cdb, cmd, cmd_len);
306
307 ioctl_rv = DeviceIoControl(stream->fh, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd,
308 sizeof(sptd), &sptd, sizeof(sptd), &ioctl_bytes, NULL);
309
310#ifdef CDROM_DEBUG
311 if (lba_req < lba_cur)
312 extra = " BACKWARDS SECTOR READ";
313 else if (lba_req > lba_cur)
314 extra = " SKIPPED SECTOR READ";
315
316 if (cmd[0] == 0xB9)
317 {
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);
320 fflush(stdout);
321 }
322
323 last_min = cmd[3];
324 last_sec = cmd[4];
325 last_frame = cmd[5];
326 increment_msf(&last_min, &last_sec, &last_frame);
327#endif
328
329 if (!ioctl_rv || sptd.s.ScsiStatus != 0)
330 return 1;
331
332 return 0;
333}
334#endif
335
336#if defined(__linux__) && !defined(ANDROID)
337static 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)
338{
339 sg_io_hdr_t sgio = {0};
340 int rv;
341
342 switch (dir)
343 {
344 case DIRECTION_IN:
345 sgio.dxfer_direction = SG_DXFER_FROM_DEV;
346 break;
347 case DIRECTION_OUT:
348 sgio.dxfer_direction = SG_DXFER_TO_DEV;
349 break;
350 case DIRECTION_NONE:
351 default:
352 sgio.dxfer_direction = SG_DXFER_NONE;
353 break;
354 }
355
356 sgio.interface_id = 'S';
357 sgio.cmd_len = cmd_len;
358 sgio.cmdp = cmd;
359 sgio.dxferp = buf;
360 sgio.dxfer_len = len;
361 sgio.sbp = sense;
362 sgio.mx_sb_len = sense_len;
363 sgio.timeout = 5000;
364
365 rv = ioctl(fileno(stream->fp), SG_IO, &sgio);
366
367 if (rv == -1 || sgio.info & SG_INFO_CHECK)
368 return 1;
369
370 return 0;
371}
372#endif
373
374static 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)
375{
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;
380 int i, rv = 0;
381 int frames = 1;
382 size_t padded_req_bytes;
383 size_t copied_bytes = 0;
384 bool read_cd = false;
385
386 if (!cmd || cmd_len == 0)
387 return 1;
388
389 if (cmd[0] == 0xBE || cmd[0] == 0xB9)
390 {
391 frames = ceil((len + skip) / 2352.0);
392 padded_req_bytes = 2352 * frames;
393 read_cd = true;
394 /* these will be incremented below */
395 cmd[6] = cmd[3];
396 cmd[7] = cmd[4];
397 cmd[8] = cmd[5];
398 }
399 else
400 {
401 padded_req_bytes = len + skip;
402 }
403
404 xfer_buf = (unsigned char*)memalign_alloc(4096, padded_req_bytes);
405 xfer_buf_pos = xfer_buf;
406
407 if (!xfer_buf)
408 return 1;
409
410 memset(xfer_buf, 0, padded_req_bytes);
411#ifdef CDROM_DEBUG
412 printf("Number of frames to read: %d\n", frames);
413 fflush(stdout);
414#endif
415 for (i = 0; i < frames; i++)
416 {
417 size_t request_len = padded_req_bytes;
418 size_t copy_len = request_len;
419 bool cached_read = false;
420
421 if (read_cd)
422 {
423 unsigned lba_req = 0;
424
425 request_len = 2352;
426 copy_len = request_len;
427
428 increment_msf(&cmd[6], &cmd[7], &cmd[8]);
429
430 if (i > 0)
431 {
432 skip = 0;
433 increment_msf(&cmd[3], &cmd[4], &cmd[5]);
434 }
435 else
436 {
437 if (skip)
438 copy_len -= skip;
439 }
440
441 if (i == frames - 1)
442 {
443 copy_len = len - copied_bytes;
444 }
445
446 lba_req = cdrom_msf_to_lba(cmd[3], cmd[4], cmd[5]);
447
448 if (stream->cdrom.last_frame_valid && lba_req == stream->cdrom.last_frame_lba)
449 {
450 /* use cached frame */
451 cached_read = true;
452#ifdef CDROM_DEBUG
453 printf("[CDROM] Using cached frame\n");
454 fflush(stdout);
455#endif
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));
458 }
459
460 }
461
462#ifdef CDROM_DEBUG
463 if (!cached_read)
464 {
465 unsigned j;
466
467 printf("[CDROM] Send Command: ");
468
469 for (j = 0; j < cmd_len / sizeof(*cmd); j++)
470 {
471 printf("%02X ", cmd[j]);
472 }
473
474 if (len)
475 printf("(buffer of size %" PRId64 " with skip bytes %" PRId64 " padded to %" PRId64 "), frame %d\n", len, skip, padded_req_bytes, i);
476 else
477 printf("\n");
478
479 fflush(stdout);
480 }
481#endif
482
483retry:
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)))
486#else
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)))
489#endif
490#endif
491 {
492 rv = 0;
493
494 if (buf)
495 {
496#if 0
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);
498 fflush(stdout);
499#endif
500 memcpy((char*)buf + copied_bytes, xfer_buf_pos + skip, copy_len);
501 copied_bytes += copy_len;
502
503 if (read_cd && !cached_read && request_len >= 2352)
504 {
505 unsigned frame_end = cdrom_msf_to_lba(cmd[6], cmd[7], cmd[8]);
506
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;
512 }
513 else
514 stream->cdrom.last_frame_valid = false;
515
516#if 0
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);
518 fflush(stdout);
519#endif
520 xfer_buf_pos += request_len;
521 }
522 }
523 else
524 {
525#ifdef CDROM_DEBUG
526 cdrom_print_sense_data(sense, sizeof(sense));
527#endif
528
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))
532 {
533 unsigned char key = sense[2] & 0xF;
534
535 switch (key)
536 {
537 case 0:
538 case 2:
539 case 3:
540 case 4:
541 case 6:
542 if (retries_left)
543 {
544 #ifdef CDROM_DEBUG
545 printf("[CDROM] Read Retry...\n");
546 fflush(stdout);
547 #endif
548 retries_left--;
549 retro_sleep(1000);
550 goto retry;
551 }
552 else
553 {
554 rv = 1;
555 #ifdef CDROM_DEBUG
556 printf("[CDROM] Read retries failed, giving up.\n");
557 fflush(stdout);
558 #endif
559 }
560
561 break;
562 default:
563 break;
564 }
565 }
566
567 rv = 1;
568 }
569 }
570
571 if (xfer_buf)
572 memalign_free(xfer_buf);
573
574 return rv;
575}
576
577static const char* get_profile(unsigned short profile)
578{
579 switch (profile)
580 {
581 case 2:
582 return "Removable disk";
583 break;
584 case 8:
585 return "CD-ROM";
586 break;
587 case 9:
588 return "CD-R";
589 break;
590 case 0xA:
591 return "CD-RW";
592 break;
593 case 0x10:
594 return "DVD-ROM";
595 break;
596 case 0x11:
597 return "DVD-R Sequential Recording";
598 break;
599 case 0x12:
600 return "DVD-RAM";
601 break;
602 case 0x13:
603 return "DVD-RW Restricted Overwrite";
604 break;
605 case 0x14:
606 return "DVD-RW Sequential recording";
607 break;
608 case 0x15:
609 return "DVD-R Dual Layer Sequential Recording";
610 break;
611 case 0x16:
612 return "DVD-R Dual Layer Jump Recording";
613 break;
614 case 0x17:
615 return "DVD-RW Dual Layer";
616 break;
617 case 0x1A:
618 return "DVD+RW";
619 break;
620 case 0x1B:
621 return "DVD+R";
622 break;
623 case 0x2A:
624 return "DVD+RW Dual Layer";
625 break;
626 case 0x2B:
627 return "DVD+R Dual Layer";
628 break;
629 case 0x40:
630 return "BD-ROM";
631 break;
632 case 0x41:
633 return "BD-R SRM";
634 break;
635 case 0x42:
636 return "BD-R RRM";
637 break;
638 case 0x43:
639 return "BD-RE";
640 break;
641 case 0x50:
642 return "HD DVD-ROM";
643 break;
644 case 0x51:
645 return "HD DVD-R";
646 break;
647 case 0x52:
648 return "HD DVD-RAM";
649 break;
650 case 0x53:
651 return "HD DVD-RW";
652 break;
653 case 0x58:
654 return "HD DVD-R Dual Layer";
655 break;
656 case 0x5A:
657 return "HD DVD-RW Dual Layer";
658 break;
659 default:
660 break;
661 }
662
663 return "Unknown";
664}
665
666int cdrom_get_sense(libretro_vfs_implementation_file *stream, unsigned char *sense, size_t len)
667{
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);
671
672#ifdef CDROM_DEBUG
673 printf("[CDROM] get sense data status code %d\n", rv);
674 fflush(stdout);
675#endif
676
677 if (rv)
678 return 1;
679
680#ifdef CDROM_DEBUG
681 cdrom_print_sense_data(buf, sizeof(buf));
682#endif
683
684 return 0;
685}
686
687void cdrom_get_current_config_random_readable(libretro_vfs_implementation_file *stream)
688{
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);
692 int i;
693
694 printf("[CDROM] get current config random readable status code %d\n", rv);
695
696 if (rv)
697 return;
698
699 printf("[CDROM] Feature Header: ");
700
701 for (i = 0; i < 8; i++)
702 {
703 printf("%02X ", buf[i]);
704 }
705
706 printf("\n");
707
708 printf("[CDROM] Random Readable Feature Descriptor: ");
709
710 for (i = 0; i < 12; i++)
711 {
712 printf("%02X ", buf[8 + i]);
713 }
714
715 printf("\n");
716
717 printf("[CDROM] Supported commands: READ CAPACITY, READ (10)\n");
718}
719
720void cdrom_get_current_config_multiread(libretro_vfs_implementation_file *stream)
721{
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);
725 int i;
726
727 printf("[CDROM] get current config multi-read status code %d\n", rv);
728
729 if (rv)
730 return;
731
732 printf("[CDROM] Feature Header: ");
733
734 for (i = 0; i < 8; i++)
735 {
736 printf("%02X ", buf[i]);
737 }
738
739 printf("\n");
740
741 printf("[CDROM] Multi-Read Feature Descriptor: ");
742
743 for (i = 0; i < 4; i++)
744 {
745 printf("%02X ", buf[8 + i]);
746 }
747
748 printf("\n");
749
750 printf("[CDROM] Supported commands: READ (10), READ CD, READ DISC INFORMATION, READ TRACK INFORMATION\n");
751}
752
753void cdrom_get_current_config_cdread(libretro_vfs_implementation_file *stream)
754{
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);
758 int i;
759
760 printf("[CDROM] get current config cd read status code %d\n", rv);
761
762 if (rv)
763 return;
764
765 printf("[CDROM] Feature Header: ");
766
767 for (i = 0; i < 8; i++)
768 {
769 printf("%02X ", buf[i]);
770 }
771
772 printf("\n");
773
774 printf("[CDROM] CD Read Feature Descriptor: ");
775
776 for (i = 0; i < 8; i++)
777 {
778 printf("%02X ", buf[8 + i]);
779 }
780
781 if (buf[8 + 2] & 1)
782 printf("(current)\n");
783
784 printf("[CDROM] Supported commands: READ CD, READ CD MSF, READ TOC/PMA/ATIP\n");
785}
786
787void cdrom_get_current_config_profiles(libretro_vfs_implementation_file *stream)
788{
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);
792 int i;
793
794 printf("[CDROM] get current config profiles status code %d\n", rv);
795
796 if (rv)
797 return;
798
799 printf("[CDROM] Feature Header: ");
800
801 for (i = 0; i < 8; i++)
802 {
803 printf("%02X ", buf[i]);
804 }
805
806 printf("\n");
807
808 printf("[CDROM] Profile List Descriptor: ");
809
810 for (i = 0; i < 4; i++)
811 {
812 printf("%02X ", buf[8 + i]);
813 }
814
815 printf("\n");
816
817 printf("[CDROM] Number of profiles: %u\n", buf[8 + 3] / 4);
818
819 for (i = 0; i < buf[8 + 3] / 4; i++)
820 {
821 unsigned short profile = (buf[8 + (4 * (i + 1))] << 8) | buf[8 + (4 * (i + 1)) + 1];
822
823 printf("[CDROM] Profile Number: %04X (%s) ", profile, get_profile(profile));
824
825 if (buf[8 + (4 * (i + 1)) + 2] & 1)
826 printf("(current)\n");
827 else
828 printf("\n");
829 }
830}
831
832void cdrom_get_current_config_core(libretro_vfs_implementation_file *stream)
833{
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);
838 int i;
839 const char *intf_std_name = "Unknown";
840
841 printf("[CDROM] get current config core status code %d\n", rv);
842
843 if (rv)
844 return;
845
846 printf("[CDROM] Feature Header: ");
847
848 for (i = 0; i < 8; i++)
849 {
850 printf("%02X ", buf[i]);
851 }
852
853 printf("\n");
854
855 if (buf[6] == 0 && buf[7] == 8)
856 printf("[CDROM] Current Profile: CD-ROM\n");
857 else
858 printf("[CDROM] Current Profile: %02X%02X\n", buf[6], buf[7]);
859
860 printf("[CDROM] Core Feature Descriptor: ");
861
862 for (i = 0; i < 12; i++)
863 {
864 printf("%02X ", buf[8 + i]);
865 }
866
867 printf("\n");
868
869 intf_std = buf[8 + 4] << 24 | buf[8 + 5] << 16 | buf[8 + 6] << 8 | buf[8 + 7];
870
871 switch (intf_std)
872 {
873 case 0:
874 intf_std_name = "Unspecified";
875 break;
876 case 1:
877 intf_std_name = "SCSI Family";
878 break;
879 case 2:
880 intf_std_name = "ATAPI";
881 break;
882 case 7:
883 intf_std_name = "Serial ATAPI";
884 break;
885 case 8:
886 intf_std_name = "USB";
887 break;
888 default:
889 break;
890 }
891
892 printf("[CDROM] Physical Interface Standard: %u (%s)\n", intf_std, intf_std_name);
893}
894
895int cdrom_read_subq(libretro_vfs_implementation_file *stream, unsigned char *buf, size_t len)
896{
897 /* MMC Command: READ TOC/PMA/ATIP */
898 unsigned char cdb[] = {0x43, 0x2, 0x2, 0, 0, 0, 0x1, 0x9, 0x30, 0};
899#ifdef CDROM_DEBUG
900 unsigned short data_len = 0;
901 unsigned char first_session = 0;
902 unsigned char last_session = 0;
903 int i;
904#endif
905 int rv;
906
907 if (!buf)
908 return 1;
909
910 rv = cdrom_send_command(stream, DIRECTION_IN, buf, len, cdb, sizeof(cdb), 0);
911
912 if (rv)
913 return 1;
914
915#ifdef CDROM_DEBUG
916 data_len = buf[0] << 8 | buf[1];
917 first_session = buf[2];
918 last_session = buf[3];
919
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);
923
924 for (i = 0; i < (data_len - 2) / 11; i++)
925 {
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];
934
935 /*printf("i %d control %d adr %d tno %d point %d: ", i, control, adr, tno, point);*/
936 /* why is control always 0? */
937
938 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
939 {
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);
942 }
943 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA0)
944 {
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);
948 }
949 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
950 {
951 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
952 printf("Last Track Number: %d ", pmin);
953 }
954 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA2)
955 {
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);
958 }
959
960 printf("\n");
961 }
962
963 fflush(stdout);
964#endif
965 return 0;
966}
967
968static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsigned char track, cdrom_toc_t *toc)
969{
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};
973 unsigned lba = 0;
974 unsigned track_size = 0;
975 int rv;
976 ssize_t pregap_lba_len;
977
978 cdb[5] = track;
979
980 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
981
982 if (rv)
983 return 1;
984
985 memcpy(&lba, buf + 8, 4);
986 memcpy(&track_size, buf + 24, 4);
987
988 lba = swap_if_little32(lba);
989 track_size = swap_if_little32(track_size);
990
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;
994
995 pregap_lba_len = (toc->track[track - 1].audio ? 0 : (toc->track[track - 1].lba - toc->track[track - 1].lba_start));
996
997 toc->track[track - 1].track_bytes = (track_size - pregap_lba_len) * 2352;
998 toc->track[track - 1].mode = buf[6] & 0xF;
999
1000#ifdef CDROM_DEBUG
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);
1006 fflush(stdout);
1007#endif
1008
1009 return 0;
1010}
1011
1012int cdrom_set_read_speed(libretro_vfs_implementation_file *stream, unsigned speed)
1013{
1014 /* MMC Command: SET CD SPEED */
1015 unsigned char cmd[] = {0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1016
1017 cmd[2] = (speed >> 24) & 0xFF;
1018 cmd[3] = (speed >> 16) & 0xFF;
1019 cmd[4] = (speed >> 8) & 0xFF;
1020 cmd[5] = speed & 0xFF;
1021
1022 return cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cmd, sizeof(cmd), 0);
1023}
1024
1025int 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)
1026{
1027 unsigned char buf[2352] = {0};
1028 unsigned short data_len = 0;
1029 size_t len = 0;
1030 size_t pos = 0;
1031 int rv = 0;
1032 int i;
1033
1034 if (!out_buf || !out_len || !num_tracks || !toc)
1035 {
1036#ifdef CDROM_DEBUG
1037 printf("[CDROM] Invalid buffer/length pointer for CDROM cue sheet\n");
1038 fflush(stdout);
1039#endif
1040 return 1;
1041 }
1042
1043 cdrom_set_read_speed(stream, 0xFFFFFFFF);
1044
1045 rv = cdrom_read_subq(stream, buf, sizeof(buf));
1046
1047 if (rv)
1048 return rv;
1049
1050 data_len = buf[0] << 8 | buf[1];
1051
1052 for (i = 0; i < (data_len - 2) / 11; i++)
1053 {
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];
1058
1059 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
1060 {
1061 *num_tracks = pmin;
1062#ifdef CDROM_DEBUG
1063 printf("[CDROM] Number of CDROM tracks: %d\n", *num_tracks);
1064 fflush(stdout);
1065#endif
1066 break;
1067 }
1068 }
1069
1070 if (!*num_tracks || *num_tracks > 99)
1071 {
1072#ifdef CDROM_DEBUG
1073 printf("[CDROM] Invalid number of CDROM tracks: %d\n", *num_tracks);
1074 fflush(stdout);
1075#endif
1076 return 1;
1077 }
1078
1079 len = CDROM_CUE_TRACK_BYTES * (*num_tracks);
1080 toc->num_tracks = *num_tracks;
1081 *out_buf = (char*)calloc(1, len);
1082 *out_len = len;
1083
1084 for (i = 0; i < (data_len - 2) / 11; i++)
1085 {
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);
1098
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? */
1101
1102 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
1103 {
1104 bool audio = false;
1105 const char *track_type = "MODE1/2352";
1106
1107 audio = (!(control & 0x4) && !(control & 0x5));
1108
1109#ifdef CDROM_DEBUG
1110 printf("[CDROM] Track %02d CONTROL %01X ADR %01X AUDIO? %d\n", point, control, adr, audio);
1111 fflush(stdout);
1112#endif
1113
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;
1120
1121 cdrom_read_track_info(stream, point, toc);
1122
1123 if (audio)
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";
1129
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);
1132#else
1133 pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c-track%02d.bin\" BINARY\n", cdrom_drive, point);
1134#endif
1135 pos += snprintf(*out_buf + pos, len - pos, " TRACK %02d %s\n", point, track_type);
1136
1137 {
1138 unsigned pregap_lba_len = toc->track[point - 1].lba - toc->track[point - 1].lba_start;
1139
1140 if (toc->track[point - 1].audio && pregap_lba_len > 0)
1141 {
1142 unsigned char min = 0;
1143 unsigned char sec = 0;
1144 unsigned char frame = 0;
1145
1146 cdrom_lba_to_msf(pregap_lba_len, &min, &sec, &frame);
1147
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);
1150 }
1151 else
1152 pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 00:00:00\n");
1153 }
1154 }
1155 }
1156
1157 return 0;
1158}
1159
1160/* needs 32 bytes for full vendor, product and version */
1161int cdrom_get_inquiry(libretro_vfs_implementation_file *stream, char *model, int len, bool *is_cdrom)
1162{
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);
1167 bool cdrom = false;
1168
1169 if (rv)
1170 return 1;
1171
1172 if (model && len >= 32)
1173 {
1174 memset(model, 0, len);
1175
1176 /* vendor */
1177 memcpy(model, buf + 8, 8);
1178
1179 model[8] = ' ';
1180
1181 /* product */
1182 memcpy(model + 9, buf + 16, 16);
1183
1184 model[25] = ' ';
1185
1186 /* version */
1187 memcpy(model + 26, buf + 32, 4);
1188 }
1189
1190 cdrom = (buf[0] == 5);
1191
1192 if (is_cdrom && cdrom)
1193 *is_cdrom = true;
1194
1195#ifdef CDROM_DEBUG
1196 printf("[CDROM] Device Model: %s (is CD-ROM? %s)\n", model, (cdrom ? "yes" : "no"));
1197#endif
1198 return 0;
1199}
1200
1201int 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)
1202{
1203 /* MMC Command: READ CD MSF */
1204 unsigned char cdb[] = {0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0xF8, 0, 0};
1205 int rv;
1206 double frames = ceil((len + skip) / 2352.0);
1207 unsigned frame_end = cdrom_msf_to_lba(min, sec, frame) + frames;
1208
1209 cdb[3] = min;
1210 cdb[4] = sec;
1211 cdb[5] = frame;
1212
1213 if (frames <= 1)
1214 {
1215 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1216#ifdef CDROM_DEBUG
1217 printf("[CDROM] single-frame read: %d %d %d skip %" PRId64 "\n", cdb[3], cdb[4], cdb[5], skip);
1218 fflush(stdout);
1219#endif
1220 }
1221 else
1222 {
1223 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1224
1225#ifdef CDROM_DEBUG
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);
1227 fflush(stdout);
1228#endif
1229 }
1230
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);
1233
1234#ifdef CDROM_DEBUG
1235 printf("[CDROM] read msf status code %d\n", rv);
1236 fflush(stdout);
1237#endif
1238
1239 if (rv)
1240 {
1241 stream->cdrom.last_frame_valid = false;
1242 return 1;
1243 }
1244
1245 return 0;
1246}
1247
1248int cdrom_stop(libretro_vfs_implementation_file *stream)
1249{
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);
1253
1254#ifdef CDROM_DEBUG
1255 printf("[CDROM] stop status code %d\n", rv);
1256 fflush(stdout);
1257#endif
1258
1259 if (rv)
1260 return 1;
1261
1262 return 0;
1263}
1264
1265int cdrom_unlock(libretro_vfs_implementation_file *stream)
1266{
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);
1270
1271#ifdef CDROM_DEBUG
1272 printf("[CDROM] persistent prevent clear status code %d\n", rv);
1273 fflush(stdout);
1274#endif
1275
1276 if (rv)
1277 return 1;
1278
1279 cdb[4] = 0x0;
1280
1281 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1282
1283#ifdef CDROM_DEBUG
1284 printf("[CDROM] prevent clear status code %d\n", rv);
1285 fflush(stdout);
1286#endif
1287
1288 if (rv)
1289 return 1;
1290
1291 return 0;
1292}
1293
1294int cdrom_open_tray(libretro_vfs_implementation_file *stream)
1295{
1296 /* MMC Command: START STOP UNIT */
1297 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x2, 0};
1298 int rv;
1299
1300 cdrom_unlock(stream);
1301 cdrom_stop(stream);
1302
1303 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1304
1305#ifdef CDROM_DEBUG
1306 printf("[CDROM] open tray status code %d\n", rv);
1307 fflush(stdout);
1308#endif
1309
1310 if (rv)
1311 return 1;
1312
1313 return 0;
1314}
1315
1316int cdrom_close_tray(libretro_vfs_implementation_file *stream)
1317{
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);
1321
1322#ifdef CDROM_DEBUG
1323 printf("[CDROM] close tray status code %d\n", rv);
1324 fflush(stdout);
1325#endif
1326
1327 if (rv)
1328 return 1;
1329
1330 return 0;
1331}
1332
1333struct string_list* cdrom_get_available_drives(void)
1334{
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);
1338 int i;
1339 bool found = false;
1340
1341 if (!dir_list)
1342 return list;
1343
1344 for (i = 0; i < (int)dir_list->size; i++)
1345 {
1346 if (string_starts_with_size(dir_list->elems[i].data, "/dev/sg",
1347 STRLEN_CONST("/dev/sg")))
1348 {
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};
1353 int dev_index = 0;
1354 RFILE *file = filestream_open(
1355 dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
1356 bool is_cdrom = false;
1357
1358 found = true;
1359
1360 if (!file)
1361 {
1362#ifdef CDROM_DEBUG
1363 printf("[CDROM] Could not open %s, please check permissions.\n", dir_list->elems[i].data);
1364 fflush(stdout);
1365#endif
1366 continue;
1367 }
1368
1369 stream = filestream_get_vfs_handle(file);
1370 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1371 filestream_close(file);
1372
1373 if (!is_cdrom)
1374 continue;
1375
1376 sscanf(dir_list->elems[i].data + STRLEN_CONST("/dev/sg"),
1377 "%d", &dev_index);
1378
1379 dev_index = '0' + dev_index;
1380 attr.i = dev_index;
1381
1382 if (!string_is_empty(drive_model))
1383 strlcat(drive_string, drive_model, sizeof(drive_string));
1384 else
1385 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1386
1387 string_list_append(list, drive_string, attr);
1388 }
1389 }
1390
1391 if (!found)
1392 {
1393 char *buf = NULL;
1394 int64_t len = 0;
1395
1396 if (filestream_read_file("/proc/modules", (void**)&buf, &len))
1397 {
1398#ifdef CDROM_DEBUG
1399 bool found = false;
1400#endif
1401 struct string_list mods = {0};
1402
1403 string_list_initialize(&mods);
1404
1405 if (string_split_noalloc(&mods, buf, "\n"))
1406 {
1407 for (i = 0; i < (int)mods.size; i++)
1408 {
1409 if (strcasestr(mods.elems[i].data, "sg "))
1410 {
1411#ifdef CDROM_DEBUG
1412 found = true;
1413#endif
1414 break;
1415 }
1416 }
1417 }
1418 string_list_deinitialize(&mods);
1419 free(buf);
1420
1421#ifdef CDROM_DEBUG
1422 if (found)
1423 {
1424 printf("[CDROM] No sg devices found but kernel module is loaded.\n");
1425 fflush(stdout);
1426 }
1427 else
1428 {
1429 printf("[CDROM] No sg devices found and sg kernel module is not loaded.\n");
1430 fflush(stdout);
1431 }
1432#endif
1433 }
1434#ifdef CDROM_DEBUG
1435 else
1436 {
1437 printf("[CDROM] No sg devices found, could not check if sg kernel module is loaded.\n");
1438 fflush(stdout);
1439 }
1440#endif
1441 }
1442
1443 string_list_free(dir_list);
1444#endif
1445#if defined(_WIN32) && !defined(_XBOX)
1446 DWORD drive_mask = GetLogicalDrives();
1447 int i;
1448
1449 for (i = 0; i < (int)(sizeof(DWORD) * 8); i++)
1450 {
1451 char path[] = {"a:\\"};
1452 char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
1453
1454 path[0] += i;
1455 cdrom_path[8] += i;
1456
1457 /* this drive letter doesn't exist */
1458 if (!(drive_mask & (1 << i)))
1459 continue;
1460
1461 if (GetDriveType(path) != DRIVE_CDROM)
1462 continue;
1463
1464 {
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);
1471 if (!file)
1472 continue;
1473
1474 stream = filestream_get_vfs_handle(file);
1475 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1476 filestream_close(file);
1477
1478 if (!is_cdrom)
1479 continue;
1480
1481 attr.i = path[0];
1482
1483 if (!string_is_empty(drive_model))
1484 strlcat(drive_string, drive_model, sizeof(drive_string));
1485 else
1486 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1487
1488 string_list_append(list, drive_string, attr);
1489 }
1490 }
1491#endif
1492 return list;
1493}
1494
1495bool cdrom_is_media_inserted(libretro_vfs_implementation_file *stream)
1496{
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);
1500
1501#ifdef CDROM_DEBUG
1502 printf("[CDROM] media inserted status code %d\n", rv);
1503 fflush(stdout);
1504#endif
1505
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. */
1508 if (rv)
1509 return false;
1510
1511 return true;
1512}
1513
1514bool cdrom_drive_has_media(const char drive)
1515{
1516 RFILE *file;
1517 char cdrom_path_bin[256] = {0};
1518
1519 cdrom_device_fillpath(cdrom_path_bin, sizeof(cdrom_path_bin), drive, 1, false);
1520
1521 file = filestream_open(cdrom_path_bin, RETRO_VFS_FILE_ACCESS_READ, 0);
1522
1523 if (file)
1524 {
1525 libretro_vfs_implementation_file *stream = filestream_get_vfs_handle(file);
1526 bool has_media = cdrom_is_media_inserted(stream);
1527
1528 filestream_close(file);
1529
1530 return has_media;
1531 }
1532
1533 return false;
1534}
1535
1536bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled)
1537{
1538 int i;
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);
1546
1547#ifdef CDROM_DEBUG
1548 printf("[CDROM] mode sense changeable status code %d\n", rv);
1549 fflush(stdout);
1550#endif
1551
1552 if (rv)
1553 return false;
1554
1555 if (!(buf[10] & 0x1))
1556 {
1557 /* RCD (read cache disable) bit is not changeable */
1558#ifdef CDROM_DEBUG
1559 printf("[CDROM] RCD (read cache disable) bit is not changeable.\n");
1560 fflush(stdout);
1561#endif
1562 return false;
1563 }
1564
1565 memset(buf, 0, sizeof(buf));
1566
1567 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense, sizeof(cdb_sense), 0);
1568
1569#ifdef CDROM_DEBUG
1570 printf("mode sense status code %d\n", rv);
1571 fflush(stdout);
1572#endif
1573
1574 if (rv)
1575 return false;
1576
1577#ifdef CDROM_DEBUG
1578 printf("Mode sense data for caching mode page: ");
1579
1580 for (i = 0; i < (int)sizeof(buf); i++)
1581 printf("%02X ", buf[i]);
1582
1583 printf("\n");
1584 fflush(stdout);
1585#endif
1586
1587 /* "When transferred during execution of the MODE SELECT (10) command, Mode Data Length is reserved." */
1588 for (i = 0; i < 8; i++)
1589 buf[i] = 0;
1590
1591 if (enabled)
1592 buf[10] &= ~1;
1593 else
1594 buf[10] |= 1;
1595
1596 rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0);
1597
1598#ifdef CDROM_DEBUG
1599 printf("mode select status code %d\n", rv);
1600 fflush(stdout);
1601#endif
1602
1603 if (rv)
1604 return false;
1605
1606 return true;
1607}
1608
1609bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t *timeouts)
1610{
1611 /* MMC Command: MODE SENSE (10) */
1612 int rv;
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;
1618
1619 if (!timeouts)
1620 return false;
1621
1622 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1623
1624#ifdef CDROM_DEBUG
1625 printf("get timeouts status code %d\n", rv);
1626 fflush(stdout);
1627#endif
1628
1629 if (rv)
1630 return false;
1631
1632 g1 = buf[14] << 8 | buf[15];
1633 g2 = buf[16] << 8 | buf[17];
1634 g3 = buf[18] << 8 | buf[19];
1635
1636#ifdef CDROM_DEBUG
1637 {
1638 int i;
1639
1640 printf("Mode sense data for timeout groups: ");
1641
1642 for (i = 0; i < (int)sizeof(buf); i++)
1643 printf("%02X ", buf[i]);
1644
1645 printf("\n");
1646
1647 printf("Group 1 Timeout: %d\n", g1);
1648 printf("Group 2 Timeout: %d\n", g2);
1649 printf("Group 3 Timeout: %d\n", g3);
1650
1651 fflush(stdout);
1652 }
1653#endif
1654
1655 timeouts->g1_timeout = g1;
1656 timeouts->g2_timeout = g2;
1657 timeouts->g3_timeout = g3;
1658
1659 return true;
1660}
1661
1662bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
1663{
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);
1669
1670 if (rv)
1671 return false;
1672
1673 atip_len = buf[0] << 8 | buf[1];
1674
1675#ifdef CDROM_DEBUG
1676 printf("ATIP Length %d, Disc Type %d, Disc Sub-Type %d\n",
1677 atip_len,
1678 (buf[6] >> 6) & 0x1,
1679 ((buf[6] >> 5) & 0x1) << 2 | ((buf[6] >> 4) & 0x1) << 1 | ((buf[6] >> 3) & 0x1) << 0);
1680#endif
1681
1682 if (atip_len < 5)
1683 return false;
1684
1685 return true;
1686}
1687
1688void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
1689{
1690 if (!path || len == 0)
1691 return;
1692 if (is_cue)
1693 {
1694#ifdef _WIN32
1695 size_t pos = strlcpy(path, "cdrom://", len);
1696 if (len > pos)
1697 path[pos++] = drive;
1698 pos = strlcat(path, ":/drive.cue", len);
1699#else
1700#ifdef __linux__
1701 size_t pos = strlcpy(path, "cdrom://drive", len);
1702 if (len > pos + 1)
1703 {
1704 path[pos++] = drive;
1705 path[pos] = '\0';
1706 }
1707 pos = strlcat(path, ".cue", len);
1708#endif
1709#endif
1710 }
1711 else
1712 {
1713#ifdef _WIN32
1714 size_t pos = strlcpy(path, "cdrom://", len);
1715 if (len > pos + 1)
1716 {
1717 path[pos++] = drive;
1718 path[pos] = '\0';
1719 }
1720 pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
1721#else
1722#ifdef __linux__
1723 size_t pos = strlcpy(path, "cdrom://drive", len);
1724 if (len > pos)
1725 path[pos++] = drive;
1726 pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);
1727#endif
1728#endif
1729 }
1730}