gpu_neon: fix some missing ebuf updates
[pcsx_rearmed.git] / deps / libretro-common / formats / cdfs / cdfs.c
1 #include <formats/cdfs.h>
2
3 #include <retro_miscellaneous.h>
4 #include <compat/strl.h>
5 #include <file/file_path.h>
6 #include <string/stdstring.h>
7
8 #ifdef HAVE_CHD
9 #include <streams/chd_stream.h>
10 #endif
11
12 static void cdfs_determine_sector_size(cdfs_track_t* track)
13 {
14    uint8_t buffer[32];
15    const int toc_sector = 16;
16
17    /* MODE information is normally found in the CUE sheet, but we can try to determine it from the raw data.
18     *
19     *   MODE1/2048 - CDROM Mode1 Data (cooked) [no header, no footer]
20     *   MODE1/2352 - CDROM Mode1 Data (raw)    [16 byte header, 288 byte footer]
21     *   MODE2/2336 - CDROM-XA Mode2 Data       [8 byte header, 280 byte footer]
22     *   MODE2/2352 - CDROM-XA Mode2 Data       [24 byte header, 280 byte footer]
23     *
24     * Note that MODE is actually a property on each sector and can change between 1 and 2 depending on how much error
25     * correction the author desired. To support that, the data format must be "/2352" to include the full header and
26     * data without error correction information, at which point the CUE sheet information becomes just a hint.
27     */
28
29    /* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */
30    intfstream_seek(track->stream, toc_sector * 2352 + track->first_sector_offset, SEEK_SET);
31    if (intfstream_read(track->stream, &buffer, sizeof(buffer)) != (int64_t)sizeof(buffer))
32       return;
33
34    /* if this is a CDROM-XA data source, the "CD001" tag will be 25 bytes into the sector */
35    if (  buffer[25] == 0x43 
36       && buffer[26] == 0x44
37       && buffer[27] == 0x30 
38       && buffer[28] == 0x30 
39       && buffer[29] == 0x31)
40    {
41       track->stream_sector_size        = 2352;
42       track->stream_sector_header_size = 24;
43    }
44    /* otherwise it should be 17 bytes into the sector */
45    else if (buffer[17] == 0x43
46       &&    buffer[18] == 0x44
47       &&    buffer[19] == 0x30 
48       &&    buffer[20] == 0x30 
49       &&    buffer[21] == 0x31)
50    {
51       track->stream_sector_size = 2352;
52       track->stream_sector_header_size = 16;
53    }
54    else
55    {
56       /* ISO-9660 says the first twelve bytes of a sector should be the sync pattern 00 FF FF FF FF FF FF FF FF FF FF 00 */
57       if (
58             buffer[ 0] == 0
59          && buffer[ 1] == 0xFF
60          && buffer[ 2] == 0xFF
61          && buffer[ 3] == 0xFF
62          && buffer[ 4] == 0xFF
63          && buffer[ 5] == 0xFF
64          && buffer[ 6] == 0xFF
65          && buffer[ 7] == 0xFF
66          && buffer[ 8] == 0xFF
67          && buffer[ 9] == 0xFF
68          && buffer[10] == 0xFF 
69          && buffer[11] == 0)
70       {
71          /* if we didn't find a CD001 tag, this format may predate ISO-9660 */
72
73          /* after the 12 byte sync pattern is three bytes identifying the sector and then one byte for the mode (total 16 bytes) */
74          track->stream_sector_size        = 2352;
75          track->stream_sector_header_size = 16;
76       }
77    }
78 }
79
80 static void cdfs_determine_sector_size_from_file_size(cdfs_track_t* track)
81 {
82    /* attempt to determine stream_sector_size from file size */
83    size_t size = intfstream_get_size(track->stream);
84
85    if ((size % 2352) == 0)
86    {
87       /* raw tracks use all 2352 bytes and have a 24 byte header */
88       track->stream_sector_size        = 2352;
89       track->stream_sector_header_size = 24;
90    }
91    else if ((size % 2048) == 0)
92    {
93       /* cooked tracks eliminate all header/footer data */
94       track->stream_sector_size        = 2048;
95       track->stream_sector_header_size = 0;
96    }
97    else if ((size % 2336) == 0)
98    {
99       /* MODE 2 format without 16-byte sync data */
100       track->stream_sector_size        = 2336;
101       track->stream_sector_header_size = 8;
102    }
103 }
104
105 static void cdfs_seek_track_sector(cdfs_track_t* track, unsigned int sector)
106 {
107    intfstream_seek(track->stream,
108            sector * track->stream_sector_size
109          + track->stream_sector_header_size
110          + track->first_sector_offset, SEEK_SET);
111 }
112
113 void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector)
114 {
115    /* only allowed if open_file was called with a NULL path */
116    if (file->first_sector == 0)
117    {
118       if (file->current_sector != (int)sector)
119       {
120          file->current_sector      = (int)sector;
121          file->sector_buffer_valid = 0;
122       }
123
124       file->pos                    = sector * 2048;
125       file->current_sector_offset  = 0;
126    }
127 }
128
129 uint32_t cdfs_get_num_sectors(cdfs_file_t* file)
130 {
131    uint32_t frame_size = intfstream_get_frame_size(file->track->stream);
132    if (frame_size == 0)
133    {
134       frame_size = file->track->stream_sector_size;
135       if (frame_size == 0)
136          frame_size = 1; /* prevent divide by 0 error if sector size is unknown */
137    }
138    return (uint32_t)(intfstream_get_size(file->track->stream) / frame_size);
139 }
140
141 uint32_t cdfs_get_first_sector(cdfs_file_t* file)
142 {
143    return file->track->first_sector_index;
144 }
145
146 static int cdfs_find_file(cdfs_file_t* file, const char* path)
147 {
148    size_t path_length;
149    int sector;
150    uint8_t buffer[2048], *tmp;
151    const char* slash = strrchr(path, '\\');
152
153    if (slash)
154    {
155       /* navigate the path to the directory record for the file */
156       const int dir_length = (int)(slash - path);
157       memcpy(buffer, path, dir_length);
158       buffer[dir_length] = '\0';
159
160       sector = cdfs_find_file(file, (const char*)buffer);
161       if (sector < 0)
162          return sector;
163
164       path += dir_length + 1;
165    }
166    else
167    {
168       int offset;
169
170       /* find the CD information (always 16 frames in) */
171       cdfs_seek_track_sector(file->track, 16);
172       intfstream_read(file->track->stream, buffer, sizeof(buffer));
173
174       /* the directory_record starts at 156 bytes into the sector.
175        * the sector containing the root directory contents is a 
176        * 3 byte value that is 2 bytes into the directory_record. */
177       offset = 156 + 2;
178       sector = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16);
179    }
180
181    /* process the contents of the directory */
182    cdfs_seek_track_sector(file->track, sector);
183    intfstream_read(file->track->stream, buffer, sizeof(buffer));
184
185    path_length = strlen(path);
186    tmp         = buffer;
187
188    while (tmp < buffer + sizeof(buffer))
189    {
190       /* The first byte of the record is the length of 
191        * the record - if 0, we reached the end of the data */
192       if (!*tmp)
193          break;
194
195       /* filename is 33 bytes into the record and 
196        * the format is "FILENAME;version" or "DIRECTORY" */
197       if (        (tmp[33 + path_length] == ';' 
198                || (tmp[33 + path_length] == '\0'))
199                &&  strncasecmp((const char*)(tmp + 33), path, path_length) == 0)
200       {
201          /* the file size is in bytes 10-13 of the record */
202          file->size = 
203               (tmp[10])
204             | (tmp[11] << 8) 
205             | (tmp[12] << 16) 
206             | (tmp[13] << 24);
207
208          /* the file contents are in the sector identified 
209           * in bytes 2-4 of the record */
210          sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16);
211          return sector;
212       }
213
214       /* the first byte of the record is the length of the record */
215       tmp += tmp[0];
216    }
217
218    return -1;
219 }
220
221 int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* track, const char* path)
222 {
223    if (!file || !track)
224       return 0;
225
226    memset(file, 0, sizeof(*file));
227
228    file->track          = track;
229    file->current_sector = -1;
230    file->first_sector   = -1;
231
232    if (path)
233       file->first_sector = cdfs_find_file(file, path);
234    else if (file->track->stream_sector_size)
235    {
236       file->first_sector = 0;
237       file->size         = (unsigned int)((intfstream_get_size(
238                file->track->stream) / file->track->stream_sector_size) 
239          * 2048);
240       return 1;
241    }
242
243    return (file->first_sector >= 0);
244 }
245
246 int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len)
247 {
248    int bytes_read = 0;
249
250    if (!file || file->first_sector < 0 || !buffer)
251       return 0;
252
253    if (len > file->size - file->pos)
254       len = file->size - file->pos;
255
256    if (len == 0)
257       return 0;
258
259    if (file->sector_buffer_valid)
260    {
261       size_t remaining = 2048 - file->current_sector_offset;
262       if (remaining > 0)
263       {
264          if (remaining >= len)
265          {
266             memcpy(buffer,
267                   &file->sector_buffer[file->current_sector_offset],
268                   (size_t)len);
269             file->current_sector_offset += len;
270             return len;
271          }
272
273          memcpy(buffer,
274                &file->sector_buffer[file->current_sector_offset], remaining);
275          buffer      = (char*)buffer + remaining;
276          bytes_read += remaining;
277          len        -= remaining;
278
279          file->current_sector_offset += remaining;
280       }
281
282       ++file->current_sector;
283       file->current_sector_offset = 0;
284       file->sector_buffer_valid   = 0;
285    }
286    else if (file->current_sector < file->first_sector)
287    {
288       file->current_sector        = file->first_sector;
289       file->current_sector_offset = 0;
290    }
291
292    while (len >= 2048)
293    {
294       cdfs_seek_track_sector(file->track, file->current_sector);
295       intfstream_read(file->track->stream, buffer, 2048);
296
297       buffer      = (char*)buffer + 2048;
298       bytes_read += 2048;
299
300       ++file->current_sector;
301
302       len        -= 2048;
303    }
304
305    if (len > 0)
306    {
307       cdfs_seek_track_sector(file->track, file->current_sector);
308       intfstream_read(file->track->stream, file->sector_buffer, 2048);
309       memcpy(buffer, file->sector_buffer, (size_t)len);
310       file->current_sector_offset = (unsigned int)len;
311       file->sector_buffer_valid   = 1;
312
313       bytes_read += len;
314    }
315
316    file->pos += bytes_read;
317    return bytes_read;
318 }
319
320 void cdfs_close_file(cdfs_file_t* file)
321 {
322    /* Not really anything to do here, just 
323     * clear out the first_sector so 
324     * read() won't do anything */
325    if (file)
326       file->first_sector = -1;
327 }
328
329 int64_t cdfs_get_size(cdfs_file_t* file)
330 {
331    if (!file || file->first_sector < 0)
332       return 0;
333    return file->size;
334 }
335
336 int64_t cdfs_tell(cdfs_file_t* file)
337 {
338    if (!file || file->first_sector < 0)
339       return -1;
340    return file->pos;
341 }
342
343 int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence)
344 {
345    int64_t new_pos;
346    int new_sector;
347
348    if (!file || file->first_sector < 0)
349       return -1;
350
351    switch (whence)
352    {
353       case SEEK_SET:
354          new_pos = offset;
355          break;
356
357       case SEEK_CUR:
358          new_pos = file->pos + offset;
359          break;
360
361       case SEEK_END:
362          new_pos = file->size - offset;
363          break;
364
365       default:
366          return -1;
367    }
368
369    if (new_pos < 0)
370       return -1;
371    else if (new_pos > file->size)
372       return -1;
373
374    file->pos = (unsigned int)new_pos;
375    file->current_sector_offset = file->pos % 2048;
376
377    new_sector = file->pos / 2048;
378    if (new_sector != file->current_sector)
379    {
380       file->current_sector      = new_sector;
381       file->sector_buffer_valid = false;
382    }
383
384    return 0;
385 }
386
387 static void cdfs_skip_spaces(const char** ptr)
388 {
389    while (**ptr && (**ptr == ' ' || **ptr == '\t'))
390       ++(*ptr);
391 }
392
393 static cdfs_track_t* cdfs_wrap_stream(
394       intfstream_t* stream,
395       unsigned first_sector_offset,
396       unsigned first_sector_index)
397 {
398    cdfs_track_t* track = NULL;
399
400    if (!stream)
401       return NULL;
402
403    track                      = (cdfs_track_t*)
404       calloc(1, sizeof(*track));
405    track->stream              = stream;
406    track->first_sector_offset = first_sector_offset;
407    track->first_sector_index  = first_sector_index;
408
409    cdfs_determine_sector_size(track);
410
411    return track;
412 }
413
414 static cdfs_track_t* cdfs_open_cue_track(
415       const char* path, unsigned int track_index)
416 {
417    char* cue                                = NULL;
418    const char* line                         = NULL;
419    int found_track                          = 0;
420    char current_track_path[PATH_MAX_LENGTH] = {0};
421    char track_path[PATH_MAX_LENGTH]         = {0};
422    unsigned int sector_size                 = 0;
423    unsigned int previous_sector_size        = 0;
424    unsigned int previous_index_sector_offset= 0;
425    unsigned int track_offset                = 0;
426    intfstream_t *cue_stream                 = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
427    int64_t stream_size                      = intfstream_get_size(cue_stream);
428    char *cue_contents                       = (char*)malloc((size_t)(stream_size + 1));
429    cdfs_track_t* track                      = NULL;
430
431    if (!cue_contents)
432    {
433       intfstream_close(cue_stream);
434       return NULL;
435    }
436
437    intfstream_read(cue_stream, cue_contents, stream_size);
438    intfstream_close(cue_stream);
439
440    cue_contents[stream_size] = '\0';
441
442    cue = cue_contents;
443    while (*cue)
444    {
445       cdfs_skip_spaces((const char**)&cue);
446       line = cue;
447
448       while (*cue && *cue != '\n')
449          ++cue;
450       if (*cue)
451          *cue++ = '\0';
452
453       if (!strncasecmp(line, "FILE", 4))
454       {
455          const char *file = line + 4;
456          cdfs_skip_spaces(&file);
457
458          if (file[0])
459          {
460             const char *file_end = cue - 1;
461             while (file_end > file && *file_end != ' ' && *file_end != '\t')
462                --file_end;
463
464             if (     file[0]      == '"' 
465                   && file_end[-1] == '"')
466             {
467                ++file;
468                --file_end;
469             }
470
471             memcpy(current_track_path, file, file_end - file);
472             current_track_path[file_end - file] = '\0';
473          }
474
475          previous_sector_size = 0;
476          previous_index_sector_offset = 0;
477          track_offset = 0;
478       }
479       else if (!strncasecmp(line, "TRACK", 5))
480       {
481          char *ptr             = NULL;
482          unsigned track_number = 0;
483          const char *track     = line + 5;
484          cdfs_skip_spaces(&track);
485
486          track_number = (unsigned)strtol(track, &ptr, 10);
487          while (*track && *track != ' ' && *track != '\n')
488             ++track;
489
490          previous_sector_size = sector_size;
491
492          cdfs_skip_spaces(&track);
493
494          if (!strncasecmp(track, "MODE", 4))
495          {
496             /* track_index = 0 means find the first data track */
497             if (!track_index || track_index == track_number)
498                found_track = track_number;
499
500             sector_size = atoi(track + 6);
501          }
502          else /* assume AUDIO */
503             sector_size = 2352;
504       }
505       else if (!strncasecmp(line, "INDEX", 5))
506       {
507          unsigned sector_offset;
508          unsigned min = 0, sec = 0, frame = 0;
509          unsigned index_number = 0;
510          const char *index     = line + 5;
511
512          cdfs_skip_spaces(&index);
513          sscanf(index, "%u", &index_number);
514          while (*index && *index != ' ' && *index != '\n')
515             ++index;
516          cdfs_skip_spaces(&index);
517
518          sscanf(index, "%u:%u:%u", &min, &sec, &frame);
519          sector_offset                 = ((min * 60) + sec) * 75 + frame;
520          sector_offset                -= previous_index_sector_offset;
521          track_offset                 += sector_offset * previous_sector_size;
522          previous_sector_size          = sector_size;
523          previous_index_sector_offset += sector_offset;
524
525          if (found_track && index_number == 1)
526          {
527             if (     strstr(current_track_path, "/")
528                   || strstr(current_track_path, "\\"))
529                strncpy(track_path, current_track_path, sizeof(track_path));
530             else
531             {
532                fill_pathname_basedir(track_path, path, sizeof(track_path));
533                strlcat(track_path, current_track_path, sizeof(track_path));
534             }
535
536             break;
537          }
538       }
539    }
540
541    free(cue_contents);
542
543    if (string_is_empty(track_path))
544       return NULL;
545
546    /* NOTE: previous_index_sector_offset will only be valid if all tracks are in the same BIN file.
547     * Otherwise, we need to determine how many tracks are in each previous BIN file, which is not
548     * stored in the CUE file. This will affect cdfs_get_first_sector, which luckily isn't used much. */
549    track = cdfs_wrap_stream(intfstream_open_file(
550             track_path, RETRO_VFS_FILE_ACCESS_READ,
551             RETRO_VFS_FILE_ACCESS_HINT_NONE), track_offset, previous_index_sector_offset);
552
553    if (track && track->stream_sector_size == 0)
554    {
555       track->stream_sector_size = sector_size;
556
557       if (sector_size == 2352)
558          track->stream_sector_header_size = 16;
559       else if (sector_size == 2336)
560          track->stream_sector_header_size = 8;
561    }
562
563    return track;
564 }
565
566 #ifdef HAVE_CHD
567 static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index)
568 {
569    cdfs_track_t *track;
570    intfstream_t *intf_stream = intfstream_open_chd_track(path,
571          RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE,
572          track_index);
573    if (!intf_stream)
574       return NULL;
575
576    track = cdfs_wrap_stream(intf_stream,
577          intfstream_get_offset_to_start(intf_stream),
578          intfstream_get_first_sector(intf_stream));
579
580    if (track && track->stream_sector_header_size == 0)
581    {
582       track->stream_sector_size = intfstream_get_frame_size(intf_stream);
583
584       if (track->stream_sector_size == 2352)
585          track->stream_sector_header_size = 16;
586       else if (track->stream_sector_size == 2336)
587          track->stream_sector_header_size = 8;
588    }
589
590    return track;
591 }
592 #endif
593
594 struct cdfs_track_t* cdfs_open_track(const char* path,
595       unsigned int track_index)
596 {
597    const char* ext = path_get_extension(path);
598
599    if (string_is_equal_noncase(ext, "cue"))
600       return cdfs_open_cue_track(path, track_index);
601
602 #ifdef HAVE_CHD
603    if (string_is_equal_noncase(ext, "chd"))
604       return cdfs_open_chd_track(path, track_index);
605 #endif
606
607    /* if opening track 1, try opening as a raw track */
608    if (track_index == 1)
609       return cdfs_open_raw_track(path);
610
611    /* unsupported file type */
612    return NULL;
613 }
614
615 struct cdfs_track_t* cdfs_open_data_track(const char* path)
616 {
617    const char* ext = path_get_extension(path);
618
619    if (string_is_equal_noncase(ext, "cue"))
620       return cdfs_open_cue_track(path, 0);
621
622 #ifdef HAVE_CHD
623    if (string_is_equal_noncase(ext, "chd"))
624       return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY);
625 #endif
626
627    /* unsupported file type - try opening as a raw track */
628    return cdfs_open_raw_track(path);
629 }
630
631 cdfs_track_t* cdfs_open_raw_track(const char* path)
632 {
633    const char *ext     = path_get_extension(path);
634    cdfs_track_t *track = NULL;
635
636    if (     string_is_equal_noncase(ext, "bin")
637          || string_is_equal_noncase(ext, "iso"))
638    {
639       intfstream_t* file = intfstream_open_file(path,
640          RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
641
642       track = cdfs_wrap_stream(file, 0, 0);
643       if (track && track->stream_sector_size == 0)
644       {
645          cdfs_determine_sector_size_from_file_size(track);
646          if (track->stream_sector_size == 0)
647          {
648             cdfs_close_track(track);
649             track = NULL;
650          }
651       }
652    }
653
654    return track;
655 }
656
657 void cdfs_close_track(cdfs_track_t* track)
658 {
659    if (track)
660    {
661       if (track->stream)
662       {
663          intfstream_close(track->stream);
664          free(track->stream);
665       }
666
667       free(track);
668    }
669 }