duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cdreader.c (25115B)


      1 #include "rc_hash.h"
      2 
      3 #include "../rc_compat.h"
      4 
      5 #include <ctype.h>
      6 #include <string.h>
      7 #include <stdlib.h>
      8 
      9 /* internal helper functions in hash.c */
     10 extern void* rc_file_open(const char* path);
     11 extern void rc_file_seek(void* file_handle, int64_t offset, int origin);
     12 extern int64_t rc_file_tell(void* file_handle);
     13 extern size_t rc_file_read(void* file_handle, void* buffer, int requested_bytes);
     14 extern void rc_file_close(void* file_handle);
     15 extern int rc_hash_error(const char* message);
     16 extern const char* rc_path_get_filename(const char* path);
     17 extern int rc_path_compare_extension(const char* path, const char* ext);
     18 extern rc_hash_message_callback verbose_message_callback;
     19 
     20 struct cdrom_t
     21 {
     22   void* file_handle;        /* the file handle for reading the track data */
     23   int sector_size;          /* the size of each sector in the track data */
     24   int sector_header_size;   /* the offset to the raw data within a sector block */
     25   int raw_data_size;        /* the amount of raw data within a sector block */
     26   int64_t file_track_offset;/* the offset of the track data within the file */
     27   int track_first_sector;   /* the first absolute sector associated to the track (includes pregap) */
     28   int track_pregap_sectors; /* the number of pregap sectors */
     29 #ifndef NDEBUG
     30   uint32_t track_id;        /* the index of the track */
     31 #endif
     32 };
     33 
     34 static int cdreader_get_sector(uint8_t header[16])
     35 {
     36   int minutes = (header[12] >> 4) * 10 + (header[12] & 0x0F);
     37   int seconds = (header[13] >> 4) * 10 + (header[13] & 0x0F);
     38   int frames = (header[14] >> 4) * 10 + (header[14] & 0x0F);
     39 
     40   /* convert the MSF value to a sector index, and subtract 150 (2 seconds) per:
     41    *   For data and mixed mode media (those conforming to ISO/IEC 10149), logical block address
     42    *   zero shall be assigned to the block at MSF address 00/02/00 */
     43   return ((minutes * 60) + seconds) * 75 + frames - 150;
     44 }
     45 
     46 static void cdreader_determine_sector_size(struct cdrom_t* cdrom)
     47 {
     48   /* Attempt to determine the sector and header sizes. The CUE file may be lying.
     49    * Look for the sync pattern using each of the supported sector sizes.
     50    * Then check for the presence of "CD001", which is gauranteed to be in either the
     51    * boot record or primary volume descriptor, one of which is always at sector 16.
     52    */
     53   const uint8_t sync_pattern[] = {
     54     0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
     55   };
     56 
     57   uint8_t header[32];
     58   const int64_t toc_sector = 16 + cdrom->track_pregap_sectors;
     59 
     60   cdrom->sector_size = 0;
     61   cdrom->sector_header_size = 0;
     62   cdrom->raw_data_size = 2048;
     63 
     64   rc_file_seek(cdrom->file_handle, toc_sector * 2352 + cdrom->file_track_offset, SEEK_SET);
     65   if (rc_file_read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
     66     return;
     67 
     68   if (memcmp(header, sync_pattern, 12) == 0)
     69   {
     70     cdrom->sector_size = 2352;
     71 
     72     if (memcmp(&header[25], "CD001", 5) == 0)
     73       cdrom->sector_header_size = 24;
     74     else
     75       cdrom->sector_header_size = 16;
     76 
     77     cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
     78   }
     79   else
     80   {
     81     rc_file_seek(cdrom->file_handle, toc_sector * 2336 + cdrom->file_track_offset, SEEK_SET);
     82     rc_file_read(cdrom->file_handle, header, sizeof(header));
     83 
     84     if (memcmp(header, sync_pattern, 12) == 0)
     85     {
     86       cdrom->sector_size = 2336;
     87 
     88       if (memcmp(&header[25], "CD001", 5) == 0)
     89         cdrom->sector_header_size = 24;
     90       else
     91         cdrom->sector_header_size = 16;
     92 
     93       cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
     94     }
     95     else
     96     {
     97       rc_file_seek(cdrom->file_handle, toc_sector * 2048 + cdrom->file_track_offset, SEEK_SET);
     98       rc_file_read(cdrom->file_handle, header, sizeof(header));
     99 
    100       if (memcmp(&header[1], "CD001", 5) == 0)
    101       {
    102         cdrom->sector_size = 2048;
    103         cdrom->sector_header_size = 0;
    104       }
    105     }
    106   }
    107 }
    108 
    109 static void* cdreader_open_bin_track(const char* path, uint32_t track)
    110 {
    111   void* file_handle;
    112   struct cdrom_t* cdrom;
    113 
    114   if (track > 1)
    115   {
    116     if (verbose_message_callback)
    117       verbose_message_callback("Cannot locate secondary tracks without a cue sheet");
    118 
    119     return NULL;
    120   }
    121 
    122   file_handle = rc_file_open(path);
    123   if (!file_handle)
    124     return NULL;
    125 
    126   cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
    127   if (!cdrom)
    128     return NULL;
    129   cdrom->file_handle = file_handle;
    130 #ifndef NDEBUG
    131   cdrom->track_id = track;
    132 #endif
    133 
    134   cdreader_determine_sector_size(cdrom);
    135 
    136   if (cdrom->sector_size == 0)
    137   {
    138     int64_t size;
    139 
    140     rc_file_seek(cdrom->file_handle, 0, SEEK_END);
    141     size = rc_file_tell(cdrom->file_handle);
    142 
    143     if ((size % 2352) == 0)
    144     {
    145       /* raw tracks use all 2352 bytes and have a 24 byte header */
    146       cdrom->sector_size = 2352;
    147       cdrom->sector_header_size = 24;
    148     }
    149     else if ((size % 2048) == 0)
    150     {
    151       /* cooked tracks eliminate all header/footer data */
    152       cdrom->sector_size = 2048;
    153       cdrom->sector_header_size = 0;
    154     }
    155     else if ((size % 2336) == 0)
    156     {
    157       /* MODE 2 format without 16-byte sync data */
    158       cdrom->sector_size = 2336;
    159       cdrom->sector_header_size = 8;
    160     }
    161     else
    162     {
    163       free(cdrom);
    164 
    165       if (verbose_message_callback)
    166         verbose_message_callback("Could not determine sector size");
    167 
    168       return NULL;
    169     }
    170   }
    171 
    172   return cdrom;
    173 }
    174 
    175 static int cdreader_open_bin(struct cdrom_t* cdrom, const char* path, const char* mode)
    176 {
    177   cdrom->file_handle = rc_file_open(path);
    178   if (!cdrom->file_handle)
    179     return 0;
    180 
    181   /* determine sector size */
    182   cdreader_determine_sector_size(cdrom);
    183 
    184   /* could not determine, which means we'll probably have more issues later
    185    * but use the CUE provided information anyway
    186    */
    187   if (cdrom->sector_size == 0)
    188   {
    189     /* All of these modes have 2048 byte payloads. In MODE1/2352 and MODE2/2352
    190      * modes, the mode can actually be specified per sector to change the payload
    191      * size, but that reduces the ability to recover from errors when the disc
    192      * is damaged, so it's seldomly used, and when it is, it's mostly for audio
    193      * or video data where a blip or two probably won't be noticed by the user.
    194      * So, while we techincally support all of the following modes, we only do
    195      * so with 2048 byte payloads.
    196      * http://totalsonicmastering.com/cuesheetsyntax.htm
    197      * MODE1/2048 ? CDROM Mode1 Data (cooked) [no header, no footer]
    198      * MODE1/2352 ? CDROM Mode1 Data (raw)    [16 byte header, 288 byte footer]
    199      * MODE2/2336 ? CDROM-XA Mode2 Data       [8 byte header, 280 byte footer]
    200      * MODE2/2352 ? CDROM-XA Mode2 Data       [24 byte header, 280 byte footer]
    201      */
    202     if (memcmp(mode, "MODE2/2352", 10) == 0)
    203     {
    204       cdrom->sector_size = 2352;
    205       cdrom->sector_header_size = 24;
    206     }
    207     else if (memcmp(mode, "MODE1/2048", 10) == 0)
    208     {
    209       cdrom->sector_size = 2048;
    210       cdrom->sector_header_size = 0;
    211     }
    212     else if (memcmp(mode, "MODE2/2336", 10) == 0)
    213     {
    214       cdrom->sector_size = 2336;
    215       cdrom->sector_header_size = 8;
    216     }
    217     else if (memcmp(mode, "MODE1/2352", 10) == 0)
    218     {
    219       cdrom->sector_size = 2352;
    220       cdrom->sector_header_size = 16;
    221     }
    222     else if (memcmp(mode, "AUDIO", 5) == 0)
    223     {
    224       cdrom->sector_size = 2352;
    225       cdrom->sector_header_size = 0;
    226       cdrom->raw_data_size = 2352; /* no header or footer data on audio tracks */
    227     }
    228   }
    229 
    230   return (cdrom->sector_size != 0);
    231 }
    232 
    233 static char* cdreader_get_bin_path(const char* cue_path, const char* bin_name)
    234 {
    235   const char* filename = rc_path_get_filename(cue_path);
    236   const size_t bin_name_len = strlen(bin_name);
    237   const size_t cue_path_len = filename - cue_path;
    238   const size_t needed = cue_path_len + bin_name_len + 1;
    239 
    240   char* bin_filename = (char*)malloc(needed);
    241   if (!bin_filename)
    242   {
    243     char buffer[64];
    244     snprintf(buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)needed);
    245     rc_hash_error((const char*)buffer);
    246   }
    247   else
    248   {
    249     memcpy(bin_filename, cue_path, cue_path_len);
    250     memcpy(bin_filename + cue_path_len, bin_name, bin_name_len + 1);
    251   }
    252 
    253   return bin_filename;
    254 }
    255 
    256 static int64_t cdreader_get_bin_size(const char* cue_path, const char* bin_name)
    257 {
    258   int64_t size = 0;
    259   char* bin_filename = cdreader_get_bin_path(cue_path, bin_name);
    260   if (bin_filename)
    261   {
    262     /* disable verbose messaging while getting file size */
    263     rc_hash_message_callback old_verbose_message_callback = verbose_message_callback;
    264     void* file_handle;
    265     verbose_message_callback = NULL;
    266 
    267     file_handle = rc_file_open(bin_filename);
    268     if (file_handle)
    269     {
    270       rc_file_seek(file_handle, 0, SEEK_END);
    271       size = rc_file_tell(file_handle);
    272       rc_file_close(file_handle);
    273     }
    274 
    275     verbose_message_callback = old_verbose_message_callback;
    276     free(bin_filename);
    277   }
    278 
    279   return size;
    280 }
    281 
    282 static void* cdreader_open_cue_track(const char* path, uint32_t track)
    283 {
    284   void* cue_handle;
    285   int64_t cue_offset = 0;
    286   char buffer[1024];
    287   char* bin_filename = NULL;
    288   char *ptr, *ptr2, *end;
    289   int done = 0;
    290   int session = 1;
    291   size_t num_read = 0;
    292   struct cdrom_t* cdrom = NULL;
    293 
    294   struct track_t
    295   {
    296     uint32_t id;
    297     int sector_size;
    298     int sector_count;
    299     int first_sector;
    300     int pregap_sectors;
    301     int is_data;
    302     int file_track_offset;
    303     int file_first_sector;
    304     char mode[16];
    305     char filename[256];
    306   } current_track, previous_track, largest_track;
    307 
    308   cue_handle = rc_file_open(path);
    309   if (!cue_handle)
    310     return NULL;
    311 
    312   memset(&current_track, 0, sizeof(current_track));
    313   memset(&previous_track, 0, sizeof(previous_track));
    314   memset(&largest_track, 0, sizeof(largest_track));
    315 
    316   do
    317   {
    318     num_read = rc_file_read(cue_handle, buffer, sizeof(buffer) - 1);
    319     if (num_read == 0)
    320       break;
    321 
    322     buffer[num_read] = 0;
    323     if (num_read == sizeof(buffer) - 1)
    324       end = buffer + sizeof(buffer) * 3 / 4;
    325     else
    326       end = buffer + num_read;
    327 
    328     for (ptr = buffer; ptr < end; ++ptr)
    329     {
    330       while (*ptr == ' ')
    331         ++ptr;
    332 
    333       if (strncasecmp(ptr, "INDEX ", 6) == 0)
    334       {
    335         int m = 0, s = 0, f = 0;
    336         int index;
    337         int sector_offset;
    338 
    339         ptr += 6;
    340         index = atoi(ptr);
    341 
    342         while (*ptr != ' ' && *ptr != '\n')
    343           ++ptr;
    344         while (*ptr == ' ')
    345           ++ptr;
    346 
    347         /* convert mm:ss:ff to sector count */
    348         sscanf_s(ptr, "%d:%d:%d", &m, &s, &f);
    349         sector_offset = ((m * 60) + s) * 75 + f;
    350 
    351         if (current_track.first_sector == -1)
    352         {
    353           current_track.first_sector = sector_offset;
    354           if (strcmp(current_track.filename, previous_track.filename) == 0)
    355           {
    356             previous_track.sector_count = current_track.first_sector - previous_track.first_sector;
    357             current_track.file_track_offset += previous_track.sector_count * previous_track.sector_size;
    358           }
    359 
    360           /* if looking for the largest data track, determine previous track size */
    361           if (track == RC_HASH_CDTRACK_LARGEST && previous_track.sector_count > largest_track.sector_count &&
    362               previous_track.is_data)
    363           {
    364             memcpy(&largest_track, &previous_track, sizeof(largest_track));
    365           }
    366         }
    367 
    368         if (index == 1)
    369         {
    370           current_track.pregap_sectors = (sector_offset - current_track.first_sector);
    371 
    372           if (verbose_message_callback)
    373           {
    374             char message[128];
    375             char* scan = current_track.mode;
    376             while (*scan && !isspace((unsigned char)*scan))
    377               ++scan;
    378             *scan = '\0';
    379 
    380             /* it's undesirable to truncate offset to 32-bits, but %lld isn't defined in c89. */
    381             snprintf(message, sizeof(message), "Found %s track %d (first sector %d, sector size %d, %d pregap sectors)",
    382                      current_track.mode, current_track.id, current_track.first_sector, current_track.sector_size, current_track.pregap_sectors);
    383             verbose_message_callback(message);
    384           }
    385 
    386           if (current_track.id == track)
    387           {
    388             done = 1;
    389             break;
    390           }
    391 
    392           if (track == RC_HASH_CDTRACK_FIRST_DATA && current_track.is_data)
    393           {
    394             track = current_track.id;
    395             done = 1;
    396             break;
    397           }
    398 
    399           if (track == RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION && session == 2)
    400           {
    401             track = current_track.id;
    402             done = 1;
    403             break;
    404           }
    405         }
    406       }
    407       else if (strncasecmp(ptr, "TRACK ", 6) == 0)
    408       {
    409         if (current_track.sector_size)
    410           memcpy(&previous_track, &current_track, sizeof(current_track));
    411 
    412         ptr += 6;
    413         current_track.id = atoi(ptr);
    414 
    415         current_track.pregap_sectors = -1;
    416         current_track.first_sector = -1;
    417 
    418         while (*ptr != ' ')
    419           ++ptr;
    420         while (*ptr == ' ')
    421           ++ptr;
    422         memcpy(current_track.mode, ptr, sizeof(current_track.mode));
    423         current_track.is_data = (memcmp(current_track.mode, "MODE", 4) == 0);
    424 
    425         if (current_track.is_data)
    426         {
    427           current_track.sector_size = atoi(ptr + 6);
    428         }
    429         else
    430         {
    431           /* assume AUDIO */
    432           current_track.sector_size = 2352;
    433         }
    434       }
    435       else if (strncasecmp(ptr, "FILE ", 5) == 0)
    436       {
    437         if (current_track.sector_size)
    438         {
    439           memcpy(&previous_track, &current_track, sizeof(previous_track));
    440 
    441           if (previous_track.sector_count == 0)
    442           {
    443             const uint32_t file_sector_count = (uint32_t)cdreader_get_bin_size(path, previous_track.filename) / previous_track.sector_size;
    444             previous_track.sector_count = file_sector_count - previous_track.first_sector;
    445           }
    446 
    447           /* if looking for the largest data track, check to see if this one is larger */
    448           if (track == RC_HASH_CDTRACK_LARGEST && previous_track.is_data &&
    449               previous_track.sector_count > largest_track.sector_count)
    450           {
    451             memcpy(&largest_track, &previous_track, sizeof(largest_track));
    452           }
    453         }
    454 
    455         memset(&current_track, 0, sizeof(current_track));
    456 
    457         current_track.file_first_sector = previous_track.file_first_sector + 
    458             previous_track.first_sector + previous_track.sector_count;
    459 
    460         ptr += 5;
    461         ptr2 = ptr;
    462         if (*ptr == '"')
    463         {
    464           ++ptr;
    465           do
    466           {
    467             ++ptr2;
    468           } while (*ptr2 && *ptr2 != '\n' && *ptr2 != '"');
    469         }
    470         else
    471         {
    472           do
    473           {
    474             ++ptr2;
    475           } while (*ptr2 && *ptr2 != '\n' && *ptr2 != ' ');
    476         }
    477 
    478         if (ptr2 - ptr < (int)sizeof(current_track.filename))
    479           memcpy(current_track.filename, ptr, ptr2 - ptr);
    480       }
    481       else if (strncasecmp(ptr, "REM ", 4) == 0)
    482       {
    483         ptr += 4;
    484         while (*ptr == ' ')
    485           ++ptr;
    486 
    487         if (strncasecmp(ptr, "SESSION ", 8) == 0)
    488         {
    489           ptr += 8;
    490           while (*ptr == ' ')
    491             ++ptr;
    492           session = atoi(ptr);
    493         }
    494       }
    495 
    496       while (*ptr && *ptr != '\n')
    497         ++ptr;
    498     }
    499 
    500     if (done)
    501       break;
    502 
    503     cue_offset += (ptr - buffer);
    504     rc_file_seek(cue_handle, cue_offset, SEEK_SET);
    505 
    506   } while (1);
    507 
    508   rc_file_close(cue_handle);
    509 
    510   if (track == RC_HASH_CDTRACK_LARGEST)
    511   {
    512     if (current_track.sector_size && current_track.is_data)
    513     {
    514       const uint32_t file_sector_count = (uint32_t)cdreader_get_bin_size(path, current_track.filename) / current_track.sector_size;
    515       current_track.sector_count = file_sector_count - current_track.first_sector;
    516 
    517       if (largest_track.sector_count > current_track.sector_count)
    518         memcpy(&current_track, &largest_track, sizeof(current_track));
    519     }
    520     else
    521     {
    522       memcpy(&current_track, &largest_track, sizeof(current_track));
    523     }
    524 
    525     track = current_track.id;
    526   }
    527   else if (track == RC_HASH_CDTRACK_LAST && !done)
    528   {
    529     track = current_track.id;
    530   }
    531 
    532   if (current_track.id == track)
    533   {
    534     cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
    535     if (!cdrom)
    536     {
    537       snprintf((char*)buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
    538       rc_hash_error((const char*)buffer);
    539       return NULL;
    540     }
    541 
    542     cdrom->file_track_offset = current_track.file_track_offset;
    543     cdrom->track_pregap_sectors = current_track.pregap_sectors;
    544     cdrom->track_first_sector = current_track.file_first_sector + current_track.first_sector;
    545 #ifndef NDEBUG
    546     cdrom->track_id = current_track.id;
    547 #endif
    548 
    549     /* verify existance of bin file */
    550     bin_filename = cdreader_get_bin_path(path, current_track.filename);
    551     if (bin_filename)
    552     {
    553       if (cdreader_open_bin(cdrom, bin_filename, current_track.mode))
    554       {
    555         if (verbose_message_callback)
    556         {
    557           if (cdrom->track_pregap_sectors)
    558             snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d, %d pregap sectors)",
    559                      track, cdrom->sector_size, cdrom->track_pregap_sectors);
    560           else
    561             snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d)", track, cdrom->sector_size);
    562 
    563           verbose_message_callback((const char*)buffer);
    564         }
    565       }
    566       else
    567       {
    568         if (cdrom->file_handle)
    569         {
    570           rc_file_close(cdrom->file_handle);
    571           snprintf((char*)buffer, sizeof(buffer), "Could not determine sector size for %s track", current_track.mode);
    572         }
    573         else
    574         {
    575           snprintf((char*)buffer, sizeof(buffer), "Could not open %s", bin_filename);
    576         }
    577 
    578         rc_hash_error((const char*)buffer);
    579 
    580         free(cdrom);
    581         cdrom = NULL;
    582       }
    583 
    584       free(bin_filename);
    585     }
    586   }
    587 
    588   return cdrom;
    589 }
    590 
    591 static void* cdreader_open_gdi_track(const char* path, uint32_t track)
    592 {
    593   void* file_handle;
    594   char buffer[1024];
    595   char mode[16] = "MODE1/";
    596   char sector_size[16];
    597   char file[256];
    598   int64_t track_size;
    599   int track_type;
    600   char* bin_path = NULL;
    601   uint32_t current_track = 0;
    602   char* ptr, *ptr2, *end;
    603   int lba = 0;
    604 
    605   uint32_t largest_track = 0;
    606   int64_t largest_track_size = 0;
    607   char largest_track_file[256];
    608   char largest_track_sector_size[16];
    609   int largest_track_lba = 0;
    610 
    611   int found = 0;
    612   size_t num_read = 0;
    613   int64_t file_offset = 0;
    614   struct cdrom_t* cdrom = NULL;
    615 
    616   file_handle = rc_file_open(path);
    617   if (!file_handle)
    618     return NULL;
    619 
    620   file[0] = '\0';
    621   do
    622   {
    623     num_read = rc_file_read(file_handle, buffer, sizeof(buffer) - 1);
    624     if (num_read == 0)
    625       break;
    626 
    627     buffer[num_read] = 0;
    628     if (num_read == sizeof(buffer) - 1)
    629       end = buffer + sizeof(buffer) * 3 / 4;
    630     else
    631       end = buffer + num_read;
    632 
    633     ptr = buffer;
    634 
    635     /* the first line contains the number of tracks, so we can get the last track index from it */
    636     if (track == RC_HASH_CDTRACK_LAST)
    637       track = atoi(ptr);
    638 
    639     /* first line contains the number of tracks and will be skipped */
    640     while (ptr < end)
    641     {
    642       /* skip until next newline */
    643       while (*ptr != '\n' && ptr < end)
    644         ++ptr;
    645 
    646       /* skip newlines */
    647       while ((*ptr == '\n' || *ptr == '\r') && ptr < end)
    648         ++ptr;
    649 
    650       /* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
    651       while (isspace((unsigned char)*ptr))
    652         ++ptr;
    653 
    654       current_track = (uint32_t)atoi(ptr);
    655       if (track && current_track != track && track != RC_HASH_CDTRACK_FIRST_DATA)
    656         continue;
    657 
    658       while (isdigit((unsigned char)*ptr))
    659         ++ptr;
    660       ++ptr;
    661 
    662       while (isspace((unsigned char)*ptr))
    663         ++ptr;
    664 
    665       lba = atoi(ptr);
    666       while (isdigit((unsigned char)*ptr))
    667         ++ptr;
    668       ++ptr;
    669 
    670       while (isspace((unsigned char)*ptr))
    671         ++ptr;
    672 
    673       track_type = atoi(ptr);
    674       while (isdigit((unsigned char)*ptr))
    675         ++ptr;
    676       ++ptr;
    677 
    678       while (isspace((unsigned char)*ptr))
    679         ++ptr;
    680 
    681       ptr2 = sector_size;
    682       while (isdigit((unsigned char)*ptr))
    683         *ptr2++ = *ptr++;
    684       *ptr2 = '\0';
    685       ++ptr;
    686 
    687       while (isspace((unsigned char)*ptr))
    688         ++ptr;
    689 
    690       ptr2 = file;
    691       if (*ptr == '\"')
    692       {
    693         ++ptr;
    694         while (*ptr != '\"')
    695           *ptr2++ = *ptr++;
    696         ++ptr;
    697       }
    698       else
    699       {
    700         while (*ptr != ' ')
    701           *ptr2++ = *ptr++;
    702       }
    703       *ptr2 = '\0';
    704 
    705       if (track == current_track)
    706       {
    707         found = 1;
    708         break;
    709       }
    710       else if (track == RC_HASH_CDTRACK_FIRST_DATA && track_type == 4)
    711       {
    712         track = current_track;
    713         found = 1;
    714         break;
    715       }
    716       else if (track == RC_HASH_CDTRACK_LARGEST && track_type == 4)
    717       {
    718         track_size = cdreader_get_bin_size(path, file);
    719         if (track_size > largest_track_size)
    720         {
    721           largest_track_size = track_size;
    722           largest_track = current_track;
    723           largest_track_lba = lba;
    724           strcpy_s(largest_track_file, sizeof(largest_track_file), file);
    725           strcpy_s(largest_track_sector_size, sizeof(largest_track_sector_size), sector_size);
    726         }
    727       }
    728     }
    729 
    730     if (found)
    731       break;
    732 
    733     file_offset += (ptr - buffer);
    734     rc_file_seek(file_handle, file_offset, SEEK_SET);
    735 
    736   } while (1);
    737 
    738   rc_file_close(file_handle);
    739 
    740   cdrom = (struct cdrom_t*)calloc(1, sizeof(*cdrom));
    741   if (!cdrom)
    742   {
    743     snprintf((char*)buffer, sizeof(buffer), "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
    744     rc_hash_error((const char*)buffer);
    745     return NULL;
    746   }
    747 
    748   /* if we were tracking the largest track, make it the current track.
    749    * otherwise, current_track will be the requested track, or last track. */
    750   if (largest_track != 0 && largest_track != current_track)
    751   {
    752     current_track = largest_track;
    753     strcpy_s(file, sizeof(file), largest_track_file);
    754     strcpy_s(sector_size, sizeof(sector_size), largest_track_sector_size);
    755     lba = largest_track_lba;
    756   }
    757 
    758   /* open the bin file for the track - construct mode parameter from sector_size */
    759   ptr = &mode[6];
    760   ptr2 = sector_size;
    761   while (*ptr2 && *ptr2 != '\"')
    762     *ptr++ = *ptr2++;
    763   *ptr = '\0';
    764 
    765   bin_path = cdreader_get_bin_path(path, file);
    766   if (cdreader_open_bin(cdrom, bin_path, mode))
    767   {
    768     cdrom->track_pregap_sectors = 0;
    769     cdrom->track_first_sector = lba;
    770 #ifndef NDEBUG
    771     cdrom->track_id = current_track;
    772 #endif
    773 
    774     if (verbose_message_callback)
    775     {
    776       snprintf((char*)buffer, sizeof(buffer), "Opened track %d (sector size %d)", current_track, cdrom->sector_size);
    777       verbose_message_callback((const char*)buffer);
    778     }
    779   }
    780   else
    781   {
    782     snprintf((char*)buffer, sizeof(buffer), "Could not open %s", bin_path);
    783     rc_hash_error((const char*)buffer);
    784 
    785     free(cdrom);
    786     cdrom = NULL;
    787   }
    788 
    789   free(bin_path);
    790 
    791   return cdrom;
    792 }
    793 
    794 static void* cdreader_open_track(const char* path, uint32_t track)
    795 {
    796   /* backwards compatibility - 0 used to mean largest */
    797   if (track == 0)
    798     track = RC_HASH_CDTRACK_LARGEST;
    799 
    800   if (rc_path_compare_extension(path, "cue"))
    801     return cdreader_open_cue_track(path, track);
    802   if (rc_path_compare_extension(path, "gdi"))
    803     return cdreader_open_gdi_track(path, track);
    804 
    805   return cdreader_open_bin_track(path, track);
    806 }
    807 
    808 static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes)
    809 {
    810   int64_t sector_start;
    811   size_t num_read, total_read = 0;
    812   uint8_t* buffer_ptr = (uint8_t*)buffer;
    813 
    814   struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
    815   if (!cdrom)
    816     return 0;
    817 
    818   if (sector < (uint32_t)cdrom->track_first_sector)
    819     return 0;
    820 
    821   sector_start = (int64_t)(sector - cdrom->track_first_sector) * cdrom->sector_size + 
    822       cdrom->sector_header_size + cdrom->file_track_offset;
    823 
    824   while (requested_bytes > (size_t)cdrom->raw_data_size)
    825   {
    826     rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
    827     num_read = rc_file_read(cdrom->file_handle, buffer_ptr, cdrom->raw_data_size);
    828     total_read += num_read;
    829 
    830     if (num_read < (size_t)cdrom->raw_data_size)
    831       return total_read;
    832 
    833     buffer_ptr += cdrom->raw_data_size;
    834     sector_start += cdrom->sector_size;
    835     requested_bytes -= cdrom->raw_data_size;
    836   }
    837 
    838   rc_file_seek(cdrom->file_handle, sector_start, SEEK_SET);
    839   num_read = rc_file_read(cdrom->file_handle, buffer_ptr, (int)requested_bytes);
    840   total_read += num_read;
    841 
    842   return total_read;
    843 }
    844 
    845 static void cdreader_close_track(void* track_handle)
    846 {
    847   struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
    848   if (cdrom)
    849   {
    850     if (cdrom->file_handle)
    851       rc_file_close(cdrom->file_handle);
    852 
    853     free(track_handle);
    854   }
    855 }
    856 
    857 static uint32_t cdreader_first_track_sector(void* track_handle)
    858 {
    859   struct cdrom_t* cdrom = (struct cdrom_t*)track_handle;
    860   if (cdrom)
    861     return cdrom->track_first_sector + cdrom->track_pregap_sectors;
    862 
    863   return 0;
    864 }
    865 
    866 void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader)
    867 {
    868   cdreader->open_track = cdreader_open_track;
    869   cdreader->read_sector = cdreader_read_sector;
    870   cdreader->close_track = cdreader_close_track;
    871   cdreader->first_track_sector = cdreader_first_track_sector;
    872 }
    873 
    874 void rc_hash_init_default_cdreader(void)
    875 {
    876   struct rc_hash_cdreader cdreader;
    877   rc_hash_get_default_cdreader(&cdreader);
    878   rc_hash_init_custom_cdreader(&cdreader);
    879 }