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