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
66 typedef enum
67 {
68 DIRECTION_NONE,
69 DIRECTION_IN,
70 DIRECTION_OUT
71 } CDROM_CMD_Direction;
72
cdrom_lba_to_msf(unsigned lba,unsigned char * min,unsigned char * sec,unsigned char * frame)73 void 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
cdrom_msf_to_lba(unsigned char min,unsigned char sec,unsigned char frame)85 unsigned cdrom_msf_to_lba(unsigned char min, unsigned char sec, unsigned char frame)
86 {
87 return (min * 60 + sec) * 75 + frame;
88 }
89
increment_msf(unsigned char * min,unsigned char * sec,unsigned char * frame)90 void 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
cdrom_print_sense_data(const unsigned char * sense,size_t len)101 static 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)
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)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)
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)
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)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)
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
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)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)
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
483 retry:
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
get_profile(unsigned short profile)577 static 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
cdrom_get_sense(libretro_vfs_implementation_file * stream,unsigned char * sense,size_t len)666 int 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
cdrom_get_current_config_random_readable(libretro_vfs_implementation_file * stream)687 void 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
cdrom_get_current_config_multiread(libretro_vfs_implementation_file * stream)720 void 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
cdrom_get_current_config_cdread(libretro_vfs_implementation_file * stream)753 void 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
cdrom_get_current_config_profiles(libretro_vfs_implementation_file * stream)787 void 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
cdrom_get_current_config_core(libretro_vfs_implementation_file * stream)832 void 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
cdrom_read_subq(libretro_vfs_implementation_file * stream,unsigned char * buf,size_t len)895 int 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
cdrom_read_track_info(libretro_vfs_implementation_file * stream,unsigned char track,cdrom_toc_t * toc)968 static 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
cdrom_set_read_speed(libretro_vfs_implementation_file * stream,unsigned speed)1012 int 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
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)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)
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 */
cdrom_get_inquiry(libretro_vfs_implementation_file * stream,char * model,int len,bool * is_cdrom)1161 int 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
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)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)
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
cdrom_stop(libretro_vfs_implementation_file * stream)1248 int 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
cdrom_unlock(libretro_vfs_implementation_file * stream)1265 int 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
cdrom_open_tray(libretro_vfs_implementation_file * stream)1294 int 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
cdrom_close_tray(libretro_vfs_implementation_file * stream)1316 int 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
cdrom_get_available_drives(void)1333 struct 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 < 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
1420 #ifdef CDROM_DEBUG
1421 if (found)
1422 {
1423 printf("[CDROM] No sg devices found but kernel module is loaded.\n");
1424 fflush(stdout);
1425 }
1426 else
1427 {
1428 printf("[CDROM] No sg devices found and sg kernel module is not loaded.\n");
1429 fflush(stdout);
1430 }
1431 #endif
1432 }
1433 #ifdef CDROM_DEBUG
1434 else
1435 {
1436 printf("[CDROM] No sg devices found, could not check if sg kernel module is loaded.\n");
1437 fflush(stdout);
1438 }
1439 #endif
1440 }
1441
1442 string_list_free(dir_list);
1443 #endif
1444 #if defined(_WIN32) && !defined(_XBOX)
1445 DWORD drive_mask = GetLogicalDrives();
1446 int i;
1447
1448 for (i = 0; i < sizeof(DWORD) * 8; i++)
1449 {
1450 char path[] = {"a:\\"};
1451 char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
1452
1453 path[0] += i;
1454 cdrom_path[8] += i;
1455
1456 /* this drive letter doesn't exist */
1457 if (!(drive_mask & (1 << i)))
1458 continue;
1459
1460 if (GetDriveType(path) != DRIVE_CDROM)
1461 continue;
1462 else
1463 {
1464 char drive_model[32] = {0};
1465 char drive_string[33] = {0};
1466 union string_list_elem_attr attr = {0};
1467 RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
1468 libretro_vfs_implementation_file *stream;
1469 bool is_cdrom = false;
1470
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
cdrom_is_media_inserted(libretro_vfs_implementation_file * stream)1495 bool 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
cdrom_drive_has_media(const char drive)1514 bool 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 = false;
1527
1528 has_media = cdrom_is_media_inserted(stream);
1529
1530 filestream_close(file);
1531
1532 return has_media;
1533 }
1534
1535 return false;
1536 }
1537
cdrom_set_read_cache(libretro_vfs_implementation_file * stream,bool enabled)1538 bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled)
1539 {
1540 /* MMC Command: MODE SENSE (10) and MODE SELECT (10) */
1541 unsigned char cdb_sense_changeable[] = {0x5A, 0, 0x48, 0, 0, 0, 0, 0, 0x14, 0};
1542 unsigned char cdb_sense[] = {0x5A, 0, 0x8, 0, 0, 0, 0, 0, 0x14, 0};
1543 unsigned char cdb_select[] = {0x55, 0x10, 0, 0, 0, 0, 0, 0, 0x14, 0};
1544 unsigned char buf[20] = {0};
1545 int rv, i;
1546
1547 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense_changeable, sizeof(cdb_sense_changeable), 0);
1548
1549 #ifdef CDROM_DEBUG
1550 printf("[CDROM] mode sense changeable status code %d\n", rv);
1551 fflush(stdout);
1552 #endif
1553
1554 if (rv)
1555 return false;
1556
1557 if (!(buf[10] & 0x1))
1558 {
1559 /* RCD (read cache disable) bit is not changeable */
1560 #ifdef CDROM_DEBUG
1561 printf("[CDROM] RCD (read cache disable) bit is not changeable.\n");
1562 fflush(stdout);
1563 #endif
1564 return false;
1565 }
1566
1567 memset(buf, 0, sizeof(buf));
1568
1569 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense, sizeof(cdb_sense), 0);
1570
1571 #ifdef CDROM_DEBUG
1572 printf("mode sense status code %d\n", rv);
1573 fflush(stdout);
1574 #endif
1575
1576 if (rv)
1577 return false;
1578
1579 #ifdef CDROM_DEBUG
1580 printf("Mode sense data for caching mode page: ");
1581
1582 for (i = 0; i < (int)sizeof(buf); i++)
1583 {
1584 printf("%02X ", buf[i]);
1585 }
1586
1587 printf("\n");
1588 fflush(stdout);
1589 #endif
1590
1591 /* "When transferred during execution of the MODE SELECT (10) command, Mode Data Length is reserved." */
1592 for (i = 0; i < 8; i++)
1593 buf[i] = 0;
1594
1595 if (enabled)
1596 buf[10] &= ~1;
1597 else
1598 buf[10] |= 1;
1599
1600 rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0);
1601
1602 #ifdef CDROM_DEBUG
1603 printf("mode select status code %d\n", rv);
1604 fflush(stdout);
1605 #endif
1606
1607 if (rv)
1608 return false;
1609
1610 return true;
1611 }
1612
cdrom_get_timeouts(libretro_vfs_implementation_file * stream,cdrom_group_timeouts_t * timeouts)1613 bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t *timeouts)
1614 {
1615 /* MMC Command: MODE SENSE (10) */
1616 int rv;
1617 unsigned char cdb[] = {0x5A, 0, 0x1D, 0, 0, 0, 0, 0, 0x14, 0};
1618 unsigned char buf[20] = {0};
1619 unsigned short g1 = 0;
1620 unsigned short g2 = 0;
1621 unsigned short g3 = 0;
1622
1623 if (!timeouts)
1624 return false;
1625
1626 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1627
1628 #ifdef CDROM_DEBUG
1629 printf("get timeouts status code %d\n", rv);
1630 fflush(stdout);
1631 #endif
1632
1633 if (rv)
1634 return false;
1635
1636 g1 = buf[14] << 8 | buf[15];
1637 g2 = buf[16] << 8 | buf[17];
1638 g3 = buf[18] << 8 | buf[19];
1639
1640 #ifdef CDROM_DEBUG
1641 {
1642 int i;
1643
1644 printf("Mode sense data for timeout groups: ");
1645
1646 for (i = 0; i < (int)sizeof(buf); i++)
1647 {
1648 printf("%02X ", buf[i]);
1649 }
1650
1651 printf("\n");
1652
1653 printf("Group 1 Timeout: %d\n", g1);
1654 printf("Group 2 Timeout: %d\n", g2);
1655 printf("Group 3 Timeout: %d\n", g3);
1656
1657 fflush(stdout);
1658 }
1659 #endif
1660
1661 timeouts->g1_timeout = g1;
1662 timeouts->g2_timeout = g2;
1663 timeouts->g3_timeout = g3;
1664
1665 return true;
1666 }
1667
cdrom_has_atip(libretro_vfs_implementation_file * stream)1668 bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
1669 {
1670 /* MMC Command: READ TOC/PMA/ATIP */
1671 unsigned char cdb[] = {0x43, 0x2, 0x4, 0, 0, 0, 0, 0x9, 0x30, 0};
1672 unsigned char buf[32] = {0};
1673 unsigned short atip_len = 0;
1674 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1675
1676 if (rv)
1677 return false;
1678
1679 atip_len = buf[0] << 8 | buf[1];
1680
1681 #ifdef CDROM_DEBUG
1682 printf("ATIP Length %d, Disc Type %d, Disc Sub-Type %d\n", atip_len, (buf[6] >> 6) & 0x1, ((buf[6] >> 5) & 0x1) << 2 | ((buf[6] >> 4) & 0x1) << 1 | ((buf[6] >> 3) & 0x1) << 0);
1683 #endif
1684
1685 if (atip_len < 5)
1686 return false;
1687
1688 return true;
1689 }
1690
cdrom_device_fillpath(char * path,size_t len,char drive,unsigned char track,bool is_cue)1691 void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
1692 {
1693 size_t pos = 0;
1694
1695 if (!path || len == 0)
1696 return;
1697
1698 if (is_cue)
1699 {
1700 #ifdef _WIN32
1701 pos = strlcpy(path, "cdrom://", len);
1702
1703 if (len > pos)
1704 path[pos++] = drive;
1705
1706 pos = strlcat(path, ":/drive.cue", len);
1707 #else
1708 #ifdef __linux__
1709 pos = strlcpy(path, "cdrom://drive", len);
1710
1711 if (len > pos + 1)
1712 {
1713 path[pos++] = drive;
1714 path[pos] = '\0';
1715 }
1716
1717 pos = strlcat(path, ".cue", len);
1718 #endif
1719 #endif
1720 }
1721 else
1722 {
1723 #ifdef _WIN32
1724 pos = strlcpy(path, "cdrom://", len);
1725
1726 if (len > pos + 1)
1727 {
1728 path[pos++] = drive;
1729 path[pos] = '\0';
1730 }
1731
1732 pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
1733 #else
1734 #ifdef __linux__
1735 pos = strlcpy(path, "cdrom://drive", len);
1736
1737 if (len > pos)
1738 path[pos++] = drive;
1739
1740 pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);
1741 #endif
1742 #endif
1743 }
1744 }
1745