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(_WIN32)
482
483 #if defined(__linux__) && !defined(ANDROID)
484 if (cached_read || !cdrom_send_command_linux(stream, dir, xfer_buf_pos, request_len, cmd, cmd_len, sense, sizeof(sense)))
485 #else
486 #if defined(_WIN32) && !defined(_XBOX)
487 if (cached_read || !cdrom_send_command_win32(stream, dir, xfer_buf_pos, request_len, cmd, cmd_len, sense, sizeof(sense)))
488 #endif
489 #endif
490 {
491 rv = 0;
492
493 if (buf)
494 {
495 #if 0
496 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);
497 fflush(stdout);
498 #endif
499 memcpy((char*)buf + copied_bytes, xfer_buf_pos + skip, copy_len);
500 copied_bytes += copy_len;
501
502 if (read_cd && !cached_read && request_len >= 2352)
503 {
504 unsigned frame_end = cdrom_msf_to_lba(cmd[6], cmd[7], cmd[8]);
505
506 /* cache the last received frame */
507 memcpy(stream->cdrom.last_frame, xfer_buf_pos, sizeof(stream->cdrom.last_frame));
508 stream->cdrom.last_frame_valid = true;
509 /* the ending frame is never actually read, so what we really just read is the one right before that */
510 stream->cdrom.last_frame_lba = frame_end - 1;
511 }
512 else
513 stream->cdrom.last_frame_valid = false;
514
515 #if 0
516 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);
517 fflush(stdout);
518 #endif
519 xfer_buf_pos += request_len;
520 }
521 }
522 else
523 #endif
524 {
525 cdrom_print_sense_data(sense, sizeof(sense));
526
527 /* INQUIRY/TEST/SENSE should never fail, don't retry. */
528 /* READ ATIP seems to fail outright on some drives with pressed discs, skip retries. */
529 if (cmd[0] != 0x0 && cmd[0] != 0x12 && cmd[0] != 0x5A && !(cmd[0] == 0x43 && cmd[2] == 0x4))
530 {
531 unsigned char key = sense[2] & 0xF;
532
533 switch (key)
534 {
535 case 0:
536 case 2:
537 case 3:
538 case 4:
539 case 6:
540 if (retries_left)
541 {
542 #ifdef CDROM_DEBUG
543 printf("[CDROM] Read Retry...\n");
544 fflush(stdout);
545 #endif
546 retries_left--;
547 retro_sleep(1000);
548 goto retry;
549 }
550 else
551 {
552 rv = 1;
553 #ifdef CDROM_DEBUG
554 printf("[CDROM] Read retries failed, giving up.\n");
555 fflush(stdout);
556 #endif
557 }
558
559 break;
560 default:
561 break;
562 }
563 }
564
565 rv = 1;
566 }
567 }
568
569 if (xfer_buf)
570 memalign_free(xfer_buf);
571
572 return rv;
573 }
574
get_profile(unsigned short profile)575 static const char* get_profile(unsigned short profile)
576 {
577 switch (profile)
578 {
579 case 2:
580 return "Removable disk";
581 break;
582 case 8:
583 return "CD-ROM";
584 break;
585 case 9:
586 return "CD-R";
587 break;
588 case 0xA:
589 return "CD-RW";
590 break;
591 case 0x10:
592 return "DVD-ROM";
593 break;
594 case 0x11:
595 return "DVD-R Sequential Recording";
596 break;
597 case 0x12:
598 return "DVD-RAM";
599 break;
600 case 0x13:
601 return "DVD-RW Restricted Overwrite";
602 break;
603 case 0x14:
604 return "DVD-RW Sequential recording";
605 break;
606 case 0x15:
607 return "DVD-R Dual Layer Sequential Recording";
608 break;
609 case 0x16:
610 return "DVD-R Dual Layer Jump Recording";
611 break;
612 case 0x17:
613 return "DVD-RW Dual Layer";
614 break;
615 case 0x1A:
616 return "DVD+RW";
617 break;
618 case 0x1B:
619 return "DVD+R";
620 break;
621 case 0x2A:
622 return "DVD+RW Dual Layer";
623 break;
624 case 0x2B:
625 return "DVD+R Dual Layer";
626 break;
627 case 0x40:
628 return "BD-ROM";
629 break;
630 case 0x41:
631 return "BD-R SRM";
632 break;
633 case 0x42:
634 return "BD-R RRM";
635 break;
636 case 0x43:
637 return "BD-RE";
638 break;
639 case 0x50:
640 return "HD DVD-ROM";
641 break;
642 case 0x51:
643 return "HD DVD-R";
644 break;
645 case 0x52:
646 return "HD DVD-RAM";
647 break;
648 case 0x53:
649 return "HD DVD-RW";
650 break;
651 case 0x58:
652 return "HD DVD-R Dual Layer";
653 break;
654 case 0x5A:
655 return "HD DVD-RW Dual Layer";
656 break;
657 default:
658 break;
659 }
660
661 return "Unknown";
662 }
663
cdrom_get_sense(libretro_vfs_implementation_file * stream,unsigned char * sense,size_t len)664 int cdrom_get_sense(libretro_vfs_implementation_file *stream, unsigned char *sense, size_t len)
665 {
666 unsigned char cdb[] = {0x3, 0, 0, 0, 0xFC, 0};
667 unsigned char buf[0xFC] = {0};
668 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
669
670 #ifdef CDROM_DEBUG
671 printf("[CDROM] get sense data status code %d\n", rv);
672 fflush(stdout);
673 #endif
674
675 if (rv)
676 return 1;
677
678 cdrom_print_sense_data(buf, sizeof(buf));
679
680 return 0;
681 }
682
cdrom_get_current_config_random_readable(libretro_vfs_implementation_file * stream)683 void cdrom_get_current_config_random_readable(libretro_vfs_implementation_file *stream)
684 {
685 unsigned char cdb[] = {0x46, 0x2, 0, 0x10, 0, 0, 0, 0, 0x14, 0};
686 unsigned char buf[0x14] = {0};
687 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
688 int i;
689
690 printf("[CDROM] get current config random readable status code %d\n", rv);
691
692 if (rv)
693 return;
694
695 printf("[CDROM] Feature Header: ");
696
697 for (i = 0; i < 8; i++)
698 {
699 printf("%02X ", buf[i]);
700 }
701
702 printf("\n");
703
704 printf("[CDROM] Random Readable Feature Descriptor: ");
705
706 for (i = 0; i < 12; i++)
707 {
708 printf("%02X ", buf[8 + i]);
709 }
710
711 printf("\n");
712
713 printf("[CDROM] Supported commands: READ CAPACITY, READ (10)\n");
714 }
715
cdrom_get_current_config_multiread(libretro_vfs_implementation_file * stream)716 void cdrom_get_current_config_multiread(libretro_vfs_implementation_file *stream)
717 {
718 unsigned char cdb[] = {0x46, 0x2, 0, 0x1D, 0, 0, 0, 0, 0xC, 0};
719 unsigned char buf[0xC] = {0};
720 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
721 int i;
722
723 printf("[CDROM] get current config multi-read status code %d\n", rv);
724
725 if (rv)
726 return;
727
728 printf("[CDROM] Feature Header: ");
729
730 for (i = 0; i < 8; i++)
731 {
732 printf("%02X ", buf[i]);
733 }
734
735 printf("\n");
736
737 printf("[CDROM] Multi-Read Feature Descriptor: ");
738
739 for (i = 0; i < 4; i++)
740 {
741 printf("%02X ", buf[8 + i]);
742 }
743
744 printf("\n");
745
746 printf("[CDROM] Supported commands: READ (10), READ CD, READ DISC INFORMATION, READ TRACK INFORMATION\n");
747 }
748
cdrom_get_current_config_cdread(libretro_vfs_implementation_file * stream)749 void cdrom_get_current_config_cdread(libretro_vfs_implementation_file *stream)
750 {
751 unsigned char cdb[] = {0x46, 0x2, 0, 0x1E, 0, 0, 0, 0, 0x10, 0};
752 unsigned char buf[0x10] = {0};
753 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
754 int i;
755
756 printf("[CDROM] get current config cd read status code %d\n", rv);
757
758 if (rv)
759 return;
760
761 printf("[CDROM] Feature Header: ");
762
763 for (i = 0; i < 8; i++)
764 {
765 printf("%02X ", buf[i]);
766 }
767
768 printf("\n");
769
770 printf("[CDROM] CD Read Feature Descriptor: ");
771
772 for (i = 0; i < 8; i++)
773 {
774 printf("%02X ", buf[8 + i]);
775 }
776
777 if (buf[8 + 2] & 1)
778 printf("(current)\n");
779
780 printf("[CDROM] Supported commands: READ CD, READ CD MSF, READ TOC/PMA/ATIP\n");
781 }
782
cdrom_get_current_config_profiles(libretro_vfs_implementation_file * stream)783 void cdrom_get_current_config_profiles(libretro_vfs_implementation_file *stream)
784 {
785 unsigned char cdb[] = {0x46, 0x2, 0, 0x0, 0, 0, 0, 0xFF, 0xFA, 0};
786 unsigned char buf[0xFFFA] = {0};
787 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
788 int i;
789
790 printf("[CDROM] get current config profiles status code %d\n", rv);
791
792 if (rv)
793 return;
794
795 printf("[CDROM] Feature Header: ");
796
797 for (i = 0; i < 8; i++)
798 {
799 printf("%02X ", buf[i]);
800 }
801
802 printf("\n");
803
804 printf("[CDROM] Profile List Descriptor: ");
805
806 for (i = 0; i < 4; i++)
807 {
808 printf("%02X ", buf[8 + i]);
809 }
810
811 printf("\n");
812
813 printf("[CDROM] Number of profiles: %u\n", buf[8 + 3] / 4);
814
815 for (i = 0; i < buf[8 + 3] / 4; i++)
816 {
817 unsigned short profile = (buf[8 + (4 * (i + 1))] << 8) | buf[8 + (4 * (i + 1)) + 1];
818
819 printf("[CDROM] Profile Number: %04X (%s) ", profile, get_profile(profile));
820
821 if (buf[8 + (4 * (i + 1)) + 2] & 1)
822 printf("(current)\n");
823 else
824 printf("\n");
825 }
826 }
827
cdrom_get_current_config_core(libretro_vfs_implementation_file * stream)828 void cdrom_get_current_config_core(libretro_vfs_implementation_file *stream)
829 {
830 unsigned char cdb[] = {0x46, 0x2, 0, 0x1, 0, 0, 0, 0, 0x14, 0};
831 unsigned char buf[20] = {0};
832 unsigned intf_std = 0;
833 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
834 int i;
835 const char *intf_std_name = "Unknown";
836
837 printf("[CDROM] get current config core status code %d\n", rv);
838
839 if (rv)
840 return;
841
842 printf("[CDROM] Feature Header: ");
843
844 for (i = 0; i < 8; i++)
845 {
846 printf("%02X ", buf[i]);
847 }
848
849 printf("\n");
850
851 if (buf[6] == 0 && buf[7] == 8)
852 printf("[CDROM] Current Profile: CD-ROM\n");
853 else
854 printf("[CDROM] Current Profile: %02X%02X\n", buf[6], buf[7]);
855
856 printf("[CDROM] Core Feature Descriptor: ");
857
858 for (i = 0; i < 12; i++)
859 {
860 printf("%02X ", buf[8 + i]);
861 }
862
863 printf("\n");
864
865 intf_std = buf[8 + 4] << 24 | buf[8 + 5] << 16 | buf[8 + 6] << 8 | buf[8 + 7];
866
867 switch (intf_std)
868 {
869 case 0:
870 intf_std_name = "Unspecified";
871 break;
872 case 1:
873 intf_std_name = "SCSI Family";
874 break;
875 case 2:
876 intf_std_name = "ATAPI";
877 break;
878 case 7:
879 intf_std_name = "Serial ATAPI";
880 break;
881 case 8:
882 intf_std_name = "USB";
883 break;
884 default:
885 break;
886 }
887
888 printf("[CDROM] Physical Interface Standard: %u (%s)\n", intf_std, intf_std_name);
889 }
890
cdrom_read_subq(libretro_vfs_implementation_file * stream,unsigned char * buf,size_t len)891 int cdrom_read_subq(libretro_vfs_implementation_file *stream, unsigned char *buf, size_t len)
892 {
893 /* MMC Command: READ TOC/PMA/ATIP */
894 unsigned char cdb[] = {0x43, 0x2, 0x2, 0, 0, 0, 0x1, 0x9, 0x30, 0};
895 #ifdef CDROM_DEBUG
896 unsigned short data_len = 0;
897 unsigned char first_session = 0;
898 unsigned char last_session = 0;
899 int i;
900 #endif
901 int rv;
902
903 if (!buf)
904 return 1;
905
906 rv = cdrom_send_command(stream, DIRECTION_IN, buf, len, cdb, sizeof(cdb), 0);
907
908 if (rv)
909 return 1;
910
911 #ifdef CDROM_DEBUG
912 data_len = buf[0] << 8 | buf[1];
913 first_session = buf[2];
914 last_session = buf[3];
915
916 printf("[CDROM] Data Length: %d\n", data_len);
917 printf("[CDROM] First Session: %d\n", first_session);
918 printf("[CDROM] Last Session: %d\n", last_session);
919
920 for (i = 0; i < (data_len - 2) / 11; i++)
921 {
922 unsigned char session_num = buf[4 + (i * 11) + 0];
923 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
924 /*unsigned char control = buf[4 + (i * 11) + 1] & 0xF;*/
925 unsigned char tno = buf[4 + (i * 11) + 2];
926 unsigned char point = buf[4 + (i * 11) + 3];
927 unsigned char pmin = buf[4 + (i * 11) + 8];
928 unsigned char psec = buf[4 + (i * 11) + 9];
929 unsigned char pframe = buf[4 + (i * 11) + 10];
930
931 /*printf("i %d control %d adr %d tno %d point %d: ", i, control, adr, tno, point);*/
932 /* why is control always 0? */
933
934 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
935 {
936 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
937 printf("[CDROM] Track start time: (MSF %02u:%02u:%02u) ", (unsigned)pmin, (unsigned)psec, (unsigned)pframe);
938 }
939 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA0)
940 {
941 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
942 printf("[CDROM] First Track Number: %d ", pmin);
943 printf("[CDROM] Disc Type: %d ", psec);
944 }
945 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
946 {
947 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
948 printf("[CDROM] Last Track Number: %d ", pmin);
949 }
950 else if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA2)
951 {
952 printf("[CDROM] - Session#: %d TNO %d POINT %d ", session_num, tno, point);
953 printf("[CDROM] Lead-out runtime: (MSF %02u:%02u:%02u) ", (unsigned)pmin, (unsigned)psec, (unsigned)pframe);
954 }
955
956 printf("\n");
957 }
958
959 fflush(stdout);
960 #endif
961 return 0;
962 }
963
cdrom_read_track_info(libretro_vfs_implementation_file * stream,unsigned char track,cdrom_toc_t * toc)964 static int cdrom_read_track_info(libretro_vfs_implementation_file *stream, unsigned char track, cdrom_toc_t *toc)
965 {
966 /* MMC Command: READ TRACK INFORMATION */
967 unsigned char cdb[] = {0x52, 0x1, 0, 0, 0, 0, 0, 0x1, 0x80, 0};
968 unsigned char buf[384] = {0};
969 unsigned lba = 0;
970 unsigned track_size = 0;
971 int rv;
972 ssize_t pregap_lba_len;
973
974 cdb[5] = track;
975
976 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
977
978 if (rv)
979 return 1;
980
981 memcpy(&lba, buf + 8, 4);
982 memcpy(&track_size, buf + 24, 4);
983
984 lba = swap_if_little32(lba);
985 track_size = swap_if_little32(track_size);
986
987 /* lba_start may be earlier than the MSF start times seen in read_subq */
988 toc->track[track - 1].lba_start = lba;
989 toc->track[track - 1].track_size = track_size;
990
991 pregap_lba_len = (toc->track[track - 1].audio ? 0 : (toc->track[track - 1].lba - toc->track[track - 1].lba_start));
992
993 toc->track[track - 1].track_bytes = (track_size - pregap_lba_len) * 2352;
994 toc->track[track - 1].mode = buf[6] & 0xF;
995
996 #ifdef CDROM_DEBUG
997 printf("[CDROM] Track %d Info: ", track);
998 printf("[CDROM] Copy: %d ", (buf[5] & 0x10) > 0);
999 printf("[CDROM] Data Mode: %d ", toc->track[track - 1].mode);
1000 printf("[CDROM] LBA Start: %d (%d) ", lba, toc->track[track - 1].lba);
1001 printf("[CDROM] Track Size: %d\n", track_size);
1002 fflush(stdout);
1003 #endif
1004
1005 return 0;
1006 }
1007
cdrom_set_read_speed(libretro_vfs_implementation_file * stream,unsigned speed)1008 int cdrom_set_read_speed(libretro_vfs_implementation_file *stream, unsigned speed)
1009 {
1010 /* MMC Command: SET CD SPEED */
1011 unsigned char cmd[] = {0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1012
1013 cmd[2] = (speed >> 24) & 0xFF;
1014 cmd[3] = (speed >> 16) & 0xFF;
1015 cmd[4] = (speed >> 8) & 0xFF;
1016 cmd[5] = speed & 0xFF;
1017
1018 return cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cmd, sizeof(cmd), 0);
1019 }
1020
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)1021 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)
1022 {
1023 unsigned char buf[2352] = {0};
1024 unsigned short data_len = 0;
1025 size_t len = 0;
1026 size_t pos = 0;
1027 int rv = 0;
1028 int i;
1029
1030 if (!out_buf || !out_len || !num_tracks || !toc)
1031 {
1032 #ifdef CDROM_DEBUG
1033 printf("[CDROM] Invalid buffer/length pointer for CDROM cue sheet\n");
1034 fflush(stdout);
1035 #endif
1036 return 1;
1037 }
1038
1039 cdrom_set_read_speed(stream, 0xFFFFFFFF);
1040
1041 rv = cdrom_read_subq(stream, buf, sizeof(buf));
1042
1043 if (rv)
1044 return rv;
1045
1046 data_len = buf[0] << 8 | buf[1];
1047
1048 for (i = 0; i < (data_len - 2) / 11; i++)
1049 {
1050 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
1051 unsigned char tno = buf[4 + (i * 11) + 2];
1052 unsigned char point = buf[4 + (i * 11) + 3];
1053 unsigned char pmin = buf[4 + (i * 11) + 8];
1054
1055 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point == 0xA1)
1056 {
1057 *num_tracks = pmin;
1058 #ifdef CDROM_DEBUG
1059 printf("[CDROM] Number of CDROM tracks: %d\n", *num_tracks);
1060 fflush(stdout);
1061 #endif
1062 break;
1063 }
1064 }
1065
1066 if (!*num_tracks || *num_tracks > 99)
1067 {
1068 #ifdef CDROM_DEBUG
1069 printf("[CDROM] Invalid number of CDROM tracks: %d\n", *num_tracks);
1070 fflush(stdout);
1071 #endif
1072 return 1;
1073 }
1074
1075 len = CDROM_CUE_TRACK_BYTES * (*num_tracks);
1076 toc->num_tracks = *num_tracks;
1077 *out_buf = (char*)calloc(1, len);
1078 *out_len = len;
1079
1080 for (i = 0; i < (data_len - 2) / 11; i++)
1081 {
1082 /*unsigned char session_num = buf[4 + (i * 11) + 0];*/
1083 unsigned char adr = (buf[4 + (i * 11) + 1] >> 4) & 0xF;
1084 unsigned char control = buf[4 + (i * 11) + 1] & 0xF;
1085 unsigned char tno = buf[4 + (i * 11) + 2];
1086 unsigned char point = buf[4 + (i * 11) + 3];
1087 /*unsigned char amin = buf[4 + (i * 11) + 4];
1088 unsigned char asec = buf[4 + (i * 11) + 5];
1089 unsigned char aframe = buf[4 + (i * 11) + 6];*/
1090 unsigned char pmin = buf[4 + (i * 11) + 8];
1091 unsigned char psec = buf[4 + (i * 11) + 9];
1092 unsigned char pframe = buf[4 + (i * 11) + 10];
1093 unsigned lba = cdrom_msf_to_lba(pmin, psec, pframe);
1094
1095 /*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);*/
1096 /* why is control always 0? */
1097
1098 if (/*(control == 4 || control == 6) && */adr == 1 && tno == 0 && point >= 1 && point <= 99)
1099 {
1100 bool audio = false;
1101 const char *track_type = "MODE1/2352";
1102
1103 audio = (!(control & 0x4) && !(control & 0x5));
1104
1105 #ifdef CDROM_DEBUG
1106 printf("[CDROM] Track %02d CONTROL %01X ADR %01X AUDIO? %d\n", point, control, adr, audio);
1107 fflush(stdout);
1108 #endif
1109
1110 toc->track[point - 1].track_num = point;
1111 toc->track[point - 1].min = pmin;
1112 toc->track[point - 1].sec = psec;
1113 toc->track[point - 1].frame = pframe;
1114 toc->track[point - 1].lba = lba;
1115 toc->track[point - 1].audio = audio;
1116
1117 cdrom_read_track_info(stream, point, toc);
1118
1119 if (audio)
1120 track_type = "AUDIO";
1121 else if (toc->track[point - 1].mode == 1)
1122 track_type = "MODE1/2352";
1123 else if (toc->track[point - 1].mode == 2)
1124 track_type = "MODE2/2352";
1125
1126 #if defined(_WIN32) && !defined(_XBOX)
1127 pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://%c:/drive-track%02d.bin\" BINARY\n", cdrom_drive, point);
1128 #else
1129 pos += snprintf(*out_buf + pos, len - pos, "FILE \"cdrom://drive%c-track%02d.bin\" BINARY\n", cdrom_drive, point);
1130 #endif
1131 pos += snprintf(*out_buf + pos, len - pos, " TRACK %02d %s\n", point, track_type);
1132
1133 {
1134 unsigned pregap_lba_len = toc->track[point - 1].lba - toc->track[point - 1].lba_start;
1135
1136 if (toc->track[point - 1].audio && pregap_lba_len > 0)
1137 {
1138 unsigned char min = 0;
1139 unsigned char sec = 0;
1140 unsigned char frame = 0;
1141
1142 cdrom_lba_to_msf(pregap_lba_len, &min, &sec, &frame);
1143
1144 pos += snprintf(*out_buf + pos, len - pos, " INDEX 00 00:00:00\n");
1145 pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 %02u:%02u:%02u\n", (unsigned)min, (unsigned)sec, (unsigned)frame);
1146 }
1147 else
1148 pos += snprintf(*out_buf + pos, len - pos, " INDEX 01 00:00:00\n");
1149 }
1150 }
1151 }
1152
1153 return 0;
1154 }
1155
1156 /* needs 32 bytes for full vendor, product and version */
cdrom_get_inquiry(libretro_vfs_implementation_file * stream,char * model,int len,bool * is_cdrom)1157 int cdrom_get_inquiry(libretro_vfs_implementation_file *stream, char *model, int len, bool *is_cdrom)
1158 {
1159 /* MMC Command: INQUIRY */
1160 unsigned char cdb[] = {0x12, 0, 0, 0, 0xff, 0};
1161 unsigned char buf[256] = {0};
1162 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1163 bool cdrom = false;
1164
1165 if (rv)
1166 return 1;
1167
1168 if (model && len >= 32)
1169 {
1170 memset(model, 0, len);
1171
1172 /* vendor */
1173 memcpy(model, buf + 8, 8);
1174
1175 model[8] = ' ';
1176
1177 /* product */
1178 memcpy(model + 9, buf + 16, 16);
1179
1180 model[25] = ' ';
1181
1182 /* version */
1183 memcpy(model + 26, buf + 32, 4);
1184 }
1185
1186 cdrom = (buf[0] == 5);
1187
1188 if (is_cdrom && cdrom)
1189 *is_cdrom = true;
1190
1191 #ifdef CDROM_DEBUG
1192 printf("[CDROM] Device Model: %s (is CD-ROM? %s)\n", model, (cdrom ? "yes" : "no"));
1193 #endif
1194 return 0;
1195 }
1196
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)1197 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)
1198 {
1199 /* MMC Command: READ CD MSF */
1200 unsigned char cdb[] = {0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0xF8, 0, 0};
1201 int rv;
1202 double frames = ceil((len + skip) / 2352.0);
1203 unsigned frame_end = cdrom_msf_to_lba(min, sec, frame) + frames;
1204
1205 cdb[3] = min;
1206 cdb[4] = sec;
1207 cdb[5] = frame;
1208
1209 if (frames <= 1)
1210 {
1211 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1212 #ifdef CDROM_DEBUG
1213 printf("[CDROM] single-frame read: %d %d %d skip %" PRId64 "\n", cdb[3], cdb[4], cdb[5], skip);
1214 fflush(stdout);
1215 #endif
1216 }
1217 else
1218 {
1219 cdrom_lba_to_msf(frame_end, &cdb[6], &cdb[7], &cdb[8]);
1220
1221 #ifdef CDROM_DEBUG
1222 printf("[CDROM] multi-frame read: %d sectors starting from %02d:%02d:%02d skip %" PRId64 "\n", (int)frames, cdb[3], cdb[4], cdb[5], skip);
1223 fflush(stdout);
1224 #endif
1225 }
1226
1227 /* regardless of the length specified here, a new buffer will be allocated and padded to a sector multiple inside cdrom_send_command */
1228 rv = cdrom_send_command(stream, DIRECTION_IN, s, len, cdb, sizeof(cdb), skip);
1229
1230 #ifdef CDROM_DEBUG
1231 printf("[CDROM] read msf status code %d\n", rv);
1232 fflush(stdout);
1233 #endif
1234
1235 if (rv)
1236 {
1237 stream->cdrom.last_frame_valid = false;
1238 return 1;
1239 }
1240
1241 return 0;
1242 }
1243
cdrom_stop(libretro_vfs_implementation_file * stream)1244 int cdrom_stop(libretro_vfs_implementation_file *stream)
1245 {
1246 /* MMC Command: START STOP UNIT */
1247 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x0, 0};
1248 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1249
1250 #ifdef CDROM_DEBUG
1251 printf("[CDROM] stop status code %d\n", rv);
1252 fflush(stdout);
1253 #endif
1254
1255 if (rv)
1256 return 1;
1257
1258 return 0;
1259 }
1260
cdrom_unlock(libretro_vfs_implementation_file * stream)1261 int cdrom_unlock(libretro_vfs_implementation_file *stream)
1262 {
1263 /* MMC Command: PREVENT ALLOW MEDIUM REMOVAL */
1264 unsigned char cdb[] = {0x1E, 0, 0, 0, 0x2, 0};
1265 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1266
1267 #ifdef CDROM_DEBUG
1268 printf("[CDROM] persistent prevent clear status code %d\n", rv);
1269 fflush(stdout);
1270 #endif
1271
1272 if (rv)
1273 return 1;
1274
1275 cdb[4] = 0x0;
1276
1277 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1278
1279 #ifdef CDROM_DEBUG
1280 printf("[CDROM] prevent clear status code %d\n", rv);
1281 fflush(stdout);
1282 #endif
1283
1284 if (rv)
1285 return 1;
1286
1287 return 0;
1288 }
1289
cdrom_open_tray(libretro_vfs_implementation_file * stream)1290 int cdrom_open_tray(libretro_vfs_implementation_file *stream)
1291 {
1292 /* MMC Command: START STOP UNIT */
1293 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x2, 0};
1294 int rv;
1295
1296 cdrom_unlock(stream);
1297 cdrom_stop(stream);
1298
1299 rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1300
1301 #ifdef CDROM_DEBUG
1302 printf("[CDROM] open tray status code %d\n", rv);
1303 fflush(stdout);
1304 #endif
1305
1306 if (rv)
1307 return 1;
1308
1309 return 0;
1310 }
1311
cdrom_close_tray(libretro_vfs_implementation_file * stream)1312 int cdrom_close_tray(libretro_vfs_implementation_file *stream)
1313 {
1314 /* MMC Command: START STOP UNIT */
1315 unsigned char cdb[] = {0x1B, 0, 0, 0, 0x3, 0};
1316 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1317
1318 #ifdef CDROM_DEBUG
1319 printf("[CDROM] close tray status code %d\n", rv);
1320 fflush(stdout);
1321 #endif
1322
1323 if (rv)
1324 return 1;
1325
1326 return 0;
1327 }
1328
cdrom_get_available_drives(void)1329 struct string_list* cdrom_get_available_drives(void)
1330 {
1331 struct string_list *list = string_list_new();
1332 #if defined(__linux__) && !defined(ANDROID)
1333 struct string_list *dir_list = dir_list_new("/dev", NULL, false, false, false, false);
1334 int i;
1335
1336 if (!dir_list)
1337 return list;
1338
1339 for (i = 0; i < (int)dir_list->size; i++)
1340 {
1341 if (strstr(dir_list->elems[i].data, "/dev/sg"))
1342 {
1343 char drive_model[32] = {0};
1344 char drive_string[33] = {0};
1345 union string_list_elem_attr attr = {0};
1346 int dev_index = 0;
1347 RFILE *file = filestream_open(dir_list->elems[i].data, RETRO_VFS_FILE_ACCESS_READ, 0);
1348 libretro_vfs_implementation_file *stream;
1349 bool is_cdrom = false;
1350
1351 if (!file)
1352 continue;
1353
1354 stream = filestream_get_vfs_handle(file);
1355 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1356 filestream_close(file);
1357
1358 if (!is_cdrom)
1359 continue;
1360
1361 sscanf(dir_list->elems[i].data + strlen("/dev/sg"), "%d", &dev_index);
1362
1363 dev_index = '0' + dev_index;
1364 attr.i = dev_index;
1365
1366 if (!string_is_empty(drive_model))
1367 strlcat(drive_string, drive_model, sizeof(drive_string));
1368 else
1369 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1370
1371 string_list_append(list, drive_string, attr);
1372 }
1373 }
1374
1375 string_list_free(dir_list);
1376 #endif
1377 #if defined(_WIN32) && !defined(_XBOX)
1378 DWORD drive_mask = GetLogicalDrives();
1379 int i;
1380
1381 for (i = 0; i < sizeof(DWORD) * 8; i++)
1382 {
1383 char path[] = {"a:\\"};
1384 char cdrom_path[] = {"cdrom://a:/drive-track01.bin"};
1385
1386 path[0] += i;
1387 cdrom_path[8] += i;
1388
1389 /* this drive letter doesn't exist */
1390 if (!(drive_mask & (1 << i)))
1391 continue;
1392
1393 if (GetDriveType(path) != DRIVE_CDROM)
1394 continue;
1395 else
1396 {
1397 char drive_model[32] = {0};
1398 char drive_string[33] = {0};
1399 union string_list_elem_attr attr = {0};
1400 RFILE *file = filestream_open(cdrom_path, RETRO_VFS_FILE_ACCESS_READ, 0);
1401 libretro_vfs_implementation_file *stream;
1402 bool is_cdrom = false;
1403
1404 if (!file)
1405 continue;
1406
1407 stream = filestream_get_vfs_handle(file);
1408 cdrom_get_inquiry(stream, drive_model, sizeof(drive_model), &is_cdrom);
1409 filestream_close(file);
1410
1411 if (!is_cdrom)
1412 continue;
1413
1414 attr.i = path[0];
1415
1416 if (!string_is_empty(drive_model))
1417 strlcat(drive_string, drive_model, sizeof(drive_string));
1418 else
1419 strlcat(drive_string, "Unknown Drive", sizeof(drive_string));
1420
1421 string_list_append(list, drive_string, attr);
1422 }
1423 }
1424 #endif
1425 return list;
1426 }
1427
cdrom_is_media_inserted(libretro_vfs_implementation_file * stream)1428 bool cdrom_is_media_inserted(libretro_vfs_implementation_file *stream)
1429 {
1430 /* MMC Command: TEST UNIT READY */
1431 unsigned char cdb[] = {0x00, 0, 0, 0, 0, 0};
1432 int rv = cdrom_send_command(stream, DIRECTION_NONE, NULL, 0, cdb, sizeof(cdb), 0);
1433
1434 #ifdef CDROM_DEBUG
1435 printf("[CDROM] media inserted status code %d\n", rv);
1436 fflush(stdout);
1437 #endif
1438
1439 /* Will also return false if the drive is simply not ready yet (tray open, disc spinning back up after tray closed etc).
1440 * Command will not block or wait for media to become ready. */
1441 if (rv)
1442 return false;
1443
1444 return true;
1445 }
1446
cdrom_drive_has_media(const char drive)1447 bool cdrom_drive_has_media(const char drive)
1448 {
1449 RFILE *file;
1450 char cdrom_path_bin[256];
1451
1452 cdrom_path_bin[0] = '\0';
1453
1454 cdrom_device_fillpath(cdrom_path_bin, sizeof(cdrom_path_bin), drive, 1, false);
1455
1456 file = filestream_open(cdrom_path_bin, RETRO_VFS_FILE_ACCESS_READ, 0);
1457
1458 if (file)
1459 {
1460 libretro_vfs_implementation_file *stream = filestream_get_vfs_handle(file);
1461 bool has_media = false;
1462
1463 has_media = cdrom_is_media_inserted(stream);
1464
1465 filestream_close(file);
1466
1467 return has_media;
1468 }
1469
1470 return false;
1471 }
1472
cdrom_set_read_cache(libretro_vfs_implementation_file * stream,bool enabled)1473 bool cdrom_set_read_cache(libretro_vfs_implementation_file *stream, bool enabled)
1474 {
1475 /* MMC Command: MODE SENSE (10) and MODE SELECT (10) */
1476 unsigned char cdb_sense_changeable[] = {0x5A, 0, 0x48, 0, 0, 0, 0, 0, 0x14, 0};
1477 unsigned char cdb_sense[] = {0x5A, 0, 0x8, 0, 0, 0, 0, 0, 0x14, 0};
1478 unsigned char cdb_select[] = {0x55, 0x10, 0, 0, 0, 0, 0, 0, 0x14, 0};
1479 unsigned char buf[20] = {0};
1480 int rv, i;
1481
1482 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense_changeable, sizeof(cdb_sense_changeable), 0);
1483
1484 #ifdef CDROM_DEBUG
1485 printf("[CDROM] mode sense changeable status code %d\n", rv);
1486 fflush(stdout);
1487 #endif
1488
1489 if (rv)
1490 return false;
1491
1492 if (!(buf[10] & 0x1))
1493 {
1494 /* RCD (read cache disable) bit is not changeable */
1495 #ifdef CDROM_DEBUG
1496 printf("[CDROM] RCD (read cache disable) bit is not changeable.\n");
1497 fflush(stdout);
1498 #endif
1499 return false;
1500 }
1501
1502 memset(buf, 0, sizeof(buf));
1503
1504 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb_sense, sizeof(cdb_sense), 0);
1505
1506 #ifdef CDROM_DEBUG
1507 printf("mode sense status code %d\n", rv);
1508 fflush(stdout);
1509 #endif
1510
1511 if (rv)
1512 return false;
1513
1514 #ifdef CDROM_DEBUG
1515 printf("Mode sense data for caching mode page: ");
1516
1517 for (i = 0; i < (int)sizeof(buf); i++)
1518 {
1519 printf("%02X ", buf[i]);
1520 }
1521
1522 printf("\n");
1523 fflush(stdout);
1524 #endif
1525
1526 /* "When transferred during execution of the MODE SELECT (10) command, Mode Data Length is reserved." */
1527 for (i = 0; i < 8; i++)
1528 buf[i] = 0;
1529
1530 if (enabled)
1531 buf[10] &= ~1;
1532 else
1533 buf[10] |= 1;
1534
1535 rv = cdrom_send_command(stream, DIRECTION_OUT, buf, sizeof(buf), cdb_select, sizeof(cdb_select), 0);
1536
1537 #ifdef CDROM_DEBUG
1538 printf("mode select status code %d\n", rv);
1539 fflush(stdout);
1540 #endif
1541
1542 if (rv)
1543 return false;
1544
1545 return true;
1546 }
1547
cdrom_get_timeouts(libretro_vfs_implementation_file * stream,cdrom_group_timeouts_t * timeouts)1548 bool cdrom_get_timeouts(libretro_vfs_implementation_file *stream, cdrom_group_timeouts_t *timeouts)
1549 {
1550 /* MMC Command: MODE SENSE (10) */
1551 int rv;
1552 unsigned char cdb[] = {0x5A, 0, 0x1D, 0, 0, 0, 0, 0, 0x14, 0};
1553 unsigned char buf[20] = {0};
1554 unsigned short g1 = 0;
1555 unsigned short g2 = 0;
1556 unsigned short g3 = 0;
1557
1558 if (!timeouts)
1559 return false;
1560
1561 rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1562
1563 #ifdef CDROM_DEBUG
1564 printf("get timeouts status code %d\n", rv);
1565 fflush(stdout);
1566 #endif
1567
1568 if (rv)
1569 return false;
1570
1571 g1 = buf[14] << 8 | buf[15];
1572 g2 = buf[16] << 8 | buf[17];
1573 g3 = buf[18] << 8 | buf[19];
1574
1575 #ifdef CDROM_DEBUG
1576 {
1577 int i;
1578
1579 printf("Mode sense data for timeout groups: ");
1580
1581 for (i = 0; i < (int)sizeof(buf); i++)
1582 {
1583 printf("%02X ", buf[i]);
1584 }
1585
1586 printf("\n");
1587
1588 printf("Group 1 Timeout: %d\n", g1);
1589 printf("Group 2 Timeout: %d\n", g2);
1590 printf("Group 3 Timeout: %d\n", g3);
1591
1592 fflush(stdout);
1593 }
1594 #endif
1595
1596 timeouts->g1_timeout = g1;
1597 timeouts->g2_timeout = g2;
1598 timeouts->g3_timeout = g3;
1599
1600 return true;
1601 }
1602
cdrom_has_atip(libretro_vfs_implementation_file * stream)1603 bool cdrom_has_atip(libretro_vfs_implementation_file *stream)
1604 {
1605 /* MMC Command: READ TOC/PMA/ATIP */
1606 unsigned char cdb[] = {0x43, 0x2, 0x4, 0, 0, 0, 0, 0x9, 0x30, 0};
1607 unsigned char buf[32] = {0};
1608 unsigned short atip_len = 0;
1609 int rv = cdrom_send_command(stream, DIRECTION_IN, buf, sizeof(buf), cdb, sizeof(cdb), 0);
1610
1611 if (rv)
1612 return false;
1613
1614 atip_len = buf[0] << 8 | buf[1];
1615
1616 #ifdef CDROM_DEBUG
1617 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);
1618 #endif
1619
1620 if (atip_len < 5)
1621 return false;
1622
1623 return true;
1624 }
1625
cdrom_device_fillpath(char * path,size_t len,char drive,unsigned char track,bool is_cue)1626 void cdrom_device_fillpath(char *path, size_t len, char drive, unsigned char track, bool is_cue)
1627 {
1628 size_t pos = 0;
1629
1630 if (!path || len == 0)
1631 return;
1632
1633 if (is_cue)
1634 {
1635 #ifdef _WIN32
1636 pos = strlcpy(path, "cdrom://", len);
1637
1638 if (len > pos)
1639 path[pos++] = drive;
1640
1641 pos = strlcat(path, ":/drive.cue", len);
1642 #else
1643 #ifdef __linux__
1644 pos = strlcpy(path, "cdrom://drive", len);
1645
1646 if (len > pos)
1647 path[pos++] = drive;
1648
1649 pos = strlcat(path, ".cue", len);
1650 #endif
1651 #endif
1652 }
1653 else
1654 {
1655 #ifdef _WIN32
1656 pos = strlcpy(path, "cdrom://", len);
1657
1658 if (len > pos)
1659 path[pos++] = drive;
1660
1661 pos += snprintf(path + pos, len - pos, ":/drive-track%02d.bin", track);
1662 #else
1663 #ifdef __linux__
1664 pos = strlcpy(path, "cdrom://drive", len);
1665
1666 if (len > pos)
1667 path[pos++] = drive;
1668
1669 pos += snprintf(path + pos, len - pos, "-track%02d.bin", track);
1670 #endif
1671 #endif
1672 }
1673 }
1674