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