Mercurial > repos > blastem
comparison cdimage.c @ 2516:69c28808b49a
Fix some TOC/CUE parser bugs and simplify how "fake" pregap is handled
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 05 Oct 2024 01:03:57 -0700 |
parents | 76dfad6a53b5 |
children | 1fdf7acc5165 |
comparison
equal
deleted
inserted
replaced
2515:0775f5e0c468 | 2516:69c28808b49a |
---|---|
96 static uint8_t bin_seek(system_media *media, uint32_t sector) | 96 static uint8_t bin_seek(system_media *media, uint32_t sector) |
97 { | 97 { |
98 media->cur_sector = sector; | 98 media->cur_sector = sector; |
99 uint32_t lba = sector; | 99 uint32_t lba = sector; |
100 uint32_t track; | 100 uint32_t track; |
101 uint32_t rel; | |
101 for (track = 0; track < media->num_tracks; track++) | 102 for (track = 0; track < media->num_tracks; track++) |
102 { | 103 { |
103 if (lba < media->tracks[track].fake_pregap) { | 104 rel = lba - media->tracks[track].pregap_lba; |
105 if (rel < media->tracks[track].fake_pregap) { | |
104 media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; | 106 media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; |
105 break; | 107 break; |
106 } | 108 } |
107 lba -= media->tracks[track].fake_pregap; | |
108 if (lba < media->tracks[track].start_lba) { | |
109 if (media->tracks[track].fake_pregap) { | |
110 media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; | |
111 } else { | |
112 media->in_fake_pregap = 0; | |
113 } | |
114 break; | |
115 } | |
116 if (lba < media->tracks[track].end_lba) { | 109 if (lba < media->tracks[track].end_lba) { |
117 media->in_fake_pregap = 0; | 110 media->in_fake_pregap = 0; |
111 rel -= media->tracks[track].fake_pregap; | |
118 break; | 112 break; |
119 } | 113 } |
120 } | 114 } |
121 if (track < media->num_tracks) { | 115 if (track < media->num_tracks) { |
122 media->cur_track = track; | 116 media->cur_track = track; |
123 if (!media->in_fake_pregap) { | 117 if (!media->in_fake_pregap) { |
124 if (track) { | |
125 lba -= media->tracks[track - 1].end_lba; | |
126 } | |
127 if (media->tracks[track].flac) { | 118 if (media->tracks[track].flac) { |
128 flac_seek(media->tracks[track].flac, (media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes) / 4); | 119 flac_seek(media->tracks[track].flac, (media->tracks[track].file_offset + rel * media->tracks[track].sector_bytes) / 4); |
129 } else { | 120 } else { |
130 if (media->tracks[track].has_subcodes) { | 121 if (media->tracks[track].has_subcodes) { |
131 if (!media->tmp_buffer) { | 122 if (!media->tmp_buffer) { |
132 media->tmp_buffer = calloc(1, 96); | 123 media->tmp_buffer = calloc(1, 96); |
133 } | 124 } |
134 fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); | 125 fseek(media->tracks[track].f, media->tracks[track].file_offset + (rel + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); |
135 int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); | 126 int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); |
136 if (bytes != 96) { | 127 if (bytes != 96) { |
137 fprintf(stderr, "Only read %d subcode bytes\n", bytes); | 128 fprintf(stderr, "Only read %d subcode bytes\n", bytes); |
138 } | 129 } |
139 } | 130 } |
140 fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); | 131 fseek(media->tracks[track].f, media->tracks[track].file_offset + rel * media->tracks[track].sector_bytes, SEEK_SET); |
141 } | 132 } |
142 } | 133 } |
143 if (media->tracks[track].type == TRACK_DATA) { | 134 if (media->tracks[track].type == TRACK_DATA) { |
144 media->cdrom_scramble_lsfr = 1; | 135 media->cdrom_scramble_lsfr = 1; |
145 } | 136 } |
210 //TODO: Fake PQ subcodes | 201 //TODO: Fake PQ subcodes |
211 return 0; | 202 return 0; |
212 } | 203 } |
213 //TODO: Translate "cooked" subcodes back to raw format | 204 //TODO: Translate "cooked" subcodes back to raw format |
214 return media->tmp_buffer[offset]; | 205 return media->tmp_buffer[offset]; |
206 } | |
207 | |
208 static void print_toc(system_media *media) | |
209 { | |
210 track_info * tracks = media->tracks; | |
211 for (uint32_t i = 0; i < media->num_tracks; i++) | |
212 { | |
213 uint32_t m,s,f; | |
214 f = tracks[i].pregap_lba % 75; | |
215 s = tracks[i].pregap_lba / 75; | |
216 m = s / 60; | |
217 s = s % 60; | |
218 printf("Track %02u - Index 0 %02u:%02u:%02u, Index 1 ", i + 1, m, s, f); | |
219 f = tracks[i].start_lba % 75; | |
220 s = tracks[i].start_lba / 75; | |
221 m = s / 60; | |
222 s = s % 60; | |
223 printf("%02u:%02u:%02u, Fake Pregap: %u\n", m, s, f, tracks[i].fake_pregap); | |
224 } | |
215 } | 225 } |
216 | 226 |
217 uint8_t parse_cue(system_media *media) | 227 uint8_t parse_cue(system_media *media) |
218 { | 228 { |
219 char *line = media->buffer; | 229 char *line = media->buffer; |
253 warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); | 263 warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); |
254 } | 264 } |
255 tracks[track].f = f; | 265 tracks[track].f = f; |
256 tracks[track].flac = flac; | 266 tracks[track].flac = flac; |
257 | 267 |
258 | |
259 cmd = cmd_start(end); | 268 cmd = cmd_start(end); |
260 if (*cmd) { | 269 if (*cmd) { |
261 if (startswith(cmd, "AUDIO")) { | 270 if (startswith(cmd, "AUDIO")) { |
262 tracks[track].type = TRACK_AUDIO; | 271 tracks[track].type = TRACK_AUDIO; |
263 tracks[track].need_swap = audio_byte_swap; | 272 tracks[track].need_swap = audio_byte_swap; |
295 fname = malloc(dirlen + 1 + (end-cmd) + 1); | 304 fname = malloc(dirlen + 1 + (end-cmd) + 1); |
296 memcpy(fname, media->dir, dirlen); | 305 memcpy(fname, media->dir, dirlen); |
297 fname[dirlen] = PATH_SEP[0]; | 306 fname[dirlen] = PATH_SEP[0]; |
298 memcpy(fname + dirlen + 1, cmd, end-cmd); | 307 memcpy(fname + dirlen + 1, cmd, end-cmd); |
299 fname[dirlen + 1 + (end-cmd)] = 0; | 308 fname[dirlen + 1 + (end-cmd)] = 0; |
309 } | |
310 | |
311 if (track_of_file >= 0) { | |
312 long track_size = 0; | |
313 if (flac) { | |
314 track_size = flac->total_samples * 4; | |
315 } else if (f) { | |
316 track_size = file_size(f); | |
317 } | |
318 track_size -= tracks[track].file_offset; | |
319 tracks[track].end_lba = tracks[track].pregap_lba + tracks[track].fake_pregap + track_size / tracks[track].sector_bytes; | |
300 } | 320 } |
301 flac = NULL; | 321 flac = NULL; |
302 f = fopen(fname, "rb"); | 322 f = fopen(fname, "rb"); |
303 if (!f) { | 323 if (!f) { |
304 fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); | 324 fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); |
346 tracks[track].fake_pregap = timecode_to_lba(cmd + 7); | 366 tracks[track].fake_pregap = timecode_to_lba(cmd + 7); |
347 } else if (startswith(cmd, "INDEX ")) { | 367 } else if (startswith(cmd, "INDEX ")) { |
348 char *after; | 368 char *after; |
349 int index = strtol(cmd + 6, &after, 10); | 369 int index = strtol(cmd + 6, &after, 10); |
350 uint8_t has_start_lba = 0; | 370 uint8_t has_start_lba = 0; |
351 uint32_t start_lba; | 371 uint32_t start_lba = timecode_to_lba(after); |
352 if (!index) { | 372 if (!index) { |
353 tracks[track].pregap_lba = start_lba = timecode_to_lba(after); | 373 tracks[track].file_offset = start_lba * tracks[track].sector_bytes + extra_offset; |
374 if (track > 0) { | |
375 if (track_of_file > 0) { | |
376 //Previous track end is implicit based on this index position | |
377 uint32_t last_track_size = tracks[track].file_offset - tracks[track-1].file_offset; | |
378 last_track_size /= tracks[track-1].sector_bytes; | |
379 tracks[track-1].end_lba = tracks[track-1].pregap_lba + tracks[track-1].fake_pregap + last_track_size; | |
380 } | |
381 tracks[track].pregap_lba = tracks[track-1].end_lba; | |
382 } else { | |
383 tracks[track].pregap_lba = 0; | |
384 } | |
354 has_index_0 = 1; | 385 has_index_0 = 1; |
355 has_start_lba = 1; | 386 has_start_lba = 1; |
356 } else if (index == 1) { | 387 } else if (index == 1) { |
357 tracks[track].start_lba = timecode_to_lba(after); | 388 if (has_index_0) { |
358 if (!has_index_0) { | 389 uint32_t real_pregap_size = start_lba * tracks[track].sector_bytes + extra_offset - tracks[track].file_offset; |
359 start_lba = tracks[track].start_lba; | 390 real_pregap_size /= tracks[track].sector_bytes; |
360 if (!tracks[track].fake_pregap) { | 391 tracks[track].start_lba = tracks[track].pregap_lba + tracks[track].fake_pregap + real_pregap_size; |
361 tracks[track].pregap_lba = start_lba; | 392 } else { |
393 tracks[track].file_offset = start_lba * tracks[track].sector_bytes + extra_offset; | |
394 if (track > 0) { | |
395 if (track_of_file > 0) { | |
396 //Previous track end is implicit based on this index position | |
397 uint32_t last_track_size = tracks[track].file_offset - tracks[track-1].file_offset; | |
398 last_track_size /= tracks[track-1].sector_bytes; | |
399 tracks[track-1].end_lba = tracks[track-1].pregap_lba + tracks[track-1].fake_pregap + last_track_size; | |
400 } | |
401 tracks[track].pregap_lba = tracks[track-1].end_lba; | |
402 tracks[track].start_lba = tracks[track].pregap_lba + tracks[track].fake_pregap; | |
403 } else { | |
404 tracks[track].pregap_lba = 0; | |
405 if (!tracks[track].fake_pregap) { | |
406 if (tracks[track].type == TRACK_DATA && tracks[track].sector_bytes == 2352) { | |
407 //Infer pregap from position in sector header | |
408 fseek(f, start_lba + 12, SEEK_SET); | |
409 uint8_t timecode[3]; | |
410 if (sizeof(timecode) == fread(timecode, 1, sizeof(timecode), f)) { | |
411 tracks[track].fake_pregap = (timecode[0] >> 4) * 600; | |
412 tracks[track].fake_pregap += (timecode[0] & 0xF) * 60; | |
413 tracks[track].fake_pregap += (timecode[1] >> 4) * 10; | |
414 tracks[track].fake_pregap += timecode[1] & 0xF; | |
415 tracks[track].fake_pregap *= 75; | |
416 tracks[track].fake_pregap += (timecode[2] >> 4) * 10; | |
417 tracks[track].fake_pregap += timecode[2] & 0xF; | |
418 } else { | |
419 fatal_error("Failed to read from CD image"); | |
420 } | |
421 } else { | |
422 //Just assume a 2-second pre-gap for first track | |
423 tracks[track].fake_pregap = 2 * 75; | |
424 } | |
425 } | |
426 tracks[track].start_lba = tracks[track].fake_pregap; | |
362 } | 427 } |
363 has_start_lba = 1; | |
364 } | |
365 } | |
366 if (has_start_lba) { | |
367 if (track > 0) { | |
368 tracks[track-1].end_lba = start_lba; | |
369 } | |
370 if (track_of_file > 0) { | |
371 tracks[track].file_offset = tracks[track-1].file_offset + tracks[track-1].end_lba * tracks[track-1].sector_bytes; | |
372 if (track_of_file > 1) { | |
373 tracks[track].file_offset -= tracks[track-2].end_lba * tracks[track-1].sector_bytes; | |
374 } | |
375 } else { | |
376 tracks[track].file_offset = extra_offset; | |
377 } | 428 } |
378 } | 429 } |
379 } | 430 } |
380 } | 431 } |
381 if (cmd && *cmd) { | 432 if (cmd && *cmd) { |
386 } else { | 437 } else { |
387 line = NULL; | 438 line = NULL; |
388 } | 439 } |
389 } while (line); | 440 } while (line); |
390 if (media->num_tracks > 0 && media->tracks[0].f) { | 441 if (media->num_tracks > 0 && media->tracks[0].f) { |
391 //end of last track in a file is implictly based on the size | 442 //end of last track in a file is implictly based on file size |
392 f = tracks[0].f; | 443 long track_size = 0; |
393 uint32_t offset = 0; | 444 if (flac) { |
394 for (int track = 0; track < media->num_tracks; track++) { | 445 track_size = flac->total_samples * 4; |
395 if (track == media->num_tracks - 1 && tracks[track].f) { | 446 } else if (f) { |
396 uint32_t start_lba =tracks[track].fake_pregap ? tracks[track].start_lba : tracks[track].pregap_lba; | 447 track_size = file_size(f); |
397 uint32_t fsize; | 448 } |
398 if (tracks[track].flac) { | 449 track_size -= tracks[track].file_offset; |
399 fsize = tracks[track].flac->total_samples * 4; | 450 tracks[track].end_lba = tracks[track].pregap_lba + tracks[track].fake_pregap + track_size / tracks[track].sector_bytes; |
400 } else { | 451 |
401 fsize = file_size(tracks[track].f); | 452 if (tracks[0].type == TRACK_DATA) { |
402 } | 453 //replace cue sheet with first sector |
403 tracks[track].end_lba = start_lba + (fsize - tracks[track].file_offset)/ tracks[track].sector_bytes; | 454 free(media->buffer); |
404 } else if (tracks[track].f != f) { | 455 media->buffer = calloc(2048, 1); |
405 uint32_t start_lba =tracks[track-1].fake_pregap ? tracks[track-1].start_lba : tracks[track-1].pregap_lba; | 456 fseek(tracks[0].f, tracks[0].sector_bytes >= 2352 ? 16 : 0, SEEK_SET); |
406 uint32_t fsize; | 457 media->size = fread(media->buffer, 1, 2048, tracks[0].f); |
407 if (tracks[track-1].flac) { | 458 } |
408 fsize = tracks[track-1].flac->total_samples * 4; | |
409 } else { | |
410 fsize = file_size(tracks[track-1].f); | |
411 } | |
412 tracks[track-1].end_lba = start_lba + (fsize - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; | |
413 offset = tracks[track-1].end_lba; | |
414 } | |
415 if (!tracks[track].fake_pregap) { | |
416 tracks[track].pregap_lba += offset; | |
417 } | |
418 tracks[track].start_lba += offset; | |
419 tracks[track].end_lba += offset; | |
420 } | |
421 //replace cue sheet with first sector | |
422 free(media->buffer); | |
423 media->buffer = calloc(2048, 1); | |
424 if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352 && !tracks[0].flac) { | |
425 // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector | |
426 uint8_t msf[3]; | |
427 fseek(tracks[0].f, 12, SEEK_SET); | |
428 if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { | |
429 tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; | |
430 } | |
431 } else if (!tracks[0].start_lba && !tracks[0].fake_pregap) { | |
432 tracks[0].fake_pregap = 2 * 75; | |
433 } | |
434 | |
435 fseek(tracks[0].f, tracks[0].sector_bytes >= 2352 ? 16 : 0, SEEK_SET); | |
436 media->size = fread(media->buffer, 1, 2048, tracks[0].f); | |
437 media->seek = bin_seek; | 459 media->seek = bin_seek; |
438 media->read = bin_read; | 460 media->read = bin_read; |
439 media->read_subcodes = bin_subcode_read; | 461 media->read_subcodes = bin_subcode_read; |
440 } | 462 } |
463 print_toc(media); | |
441 uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; | 464 uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; |
442 media->type = valid ? MEDIA_CDROM : MEDIA_CART; | 465 media->type = valid ? MEDIA_CDROM : MEDIA_CART; |
443 return valid; | 466 return valid; |
444 } | 467 } |
445 | 468 |
551 } | 574 } |
552 if (!is_datafile) { | 575 if (!is_datafile) { |
553 if (isdigit(*cmd)) { | 576 if (isdigit(*cmd)) { |
554 uint32_t start = timecode_to_lba(cmd); | 577 uint32_t start = timecode_to_lba(cmd); |
555 tracks[track].file_offset += start * tracks[track].sector_bytes; | 578 tracks[track].file_offset += start * tracks[track].sector_bytes; |
556 cmd = cmd_start_sameline(cmd); | 579 cmd = cmd_start_sameline(word_end(cmd)); |
557 } | 580 } |
558 } | 581 } |
559 if (isdigit(*cmd)) { | 582 if (isdigit(*cmd)) { |
560 uint32_t length = timecode_to_lba(cmd); | 583 uint32_t length = timecode_to_lba(cmd); |
561 tracks[track].end_lba += length; | 584 tracks[track].end_lba += length; |
562 } else { | 585 } else { |
563 long fsize = file_size(f); | 586 long fsize = file_size(f); |
564 tracks[track].end_lba += fsize - tracks[track].file_offset; | 587 tracks[track].end_lba += (fsize - tracks[track].file_offset) / tracks[track].sector_bytes; |
565 } | 588 } |
566 } | 589 } |
567 } | 590 } |
568 } | 591 } |
569 } else if (startswith(cmd, "SILENCE")) { | 592 } else if (startswith(cmd, "SILENCE")) { |
570 cmd = cmd_start_sameline(cmd + 7); | 593 cmd = cmd_start_sameline(cmd + 7); |
571 tracks[track].fake_pregap += timecode_to_lba(cmd); | 594 uint32_t length = timecode_to_lba(cmd); |
595 tracks[track].fake_pregap += length; | |
596 tracks[track].end_lba += length; | |
572 } else if (startswith(cmd, "START")) { | 597 } else if (startswith(cmd, "START")) { |
573 cmd = cmd_start_sameline(cmd + 5); | 598 cmd = cmd_start_sameline(cmd + 5); |
574 tracks[track].start_lba = tracks[track].pregap_lba + timecode_to_lba(cmd); | 599 tracks[track].start_lba = tracks[track].pregap_lba + timecode_to_lba(cmd); |
575 } | 600 } |
576 } | 601 } |
585 } while (line); | 610 } while (line); |
586 if (media->num_tracks > 0 && media->tracks[0].f) { | 611 if (media->num_tracks > 0 && media->tracks[0].f) { |
587 //replace cue sheet with first sector | 612 //replace cue sheet with first sector |
588 free(media->buffer); | 613 free(media->buffer); |
589 media->buffer = calloc(2048, 1); | 614 media->buffer = calloc(2048, 1); |
615 uint32_t old_fake_pregap = tracks[0].fake_pregap; | |
590 if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { | 616 if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { |
591 // if the first track is a data track, don't trust the TOC file and look at the MM:SS:FF from first sector | 617 // if the first track is a data track, don't trust the TOC file and look at the MM:SS:FF from first sector |
592 uint8_t msf[3]; | 618 uint8_t msf[3]; |
593 fseek(tracks[0].f, 12, SEEK_SET); | 619 fseek(tracks[0].f, 12, SEEK_SET); |
594 if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { | 620 if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { |
595 tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; | 621 tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; |
596 } | 622 } |
597 } else if (!tracks[0].start_lba && !tracks[0].fake_pregap) { | 623 } else if (!tracks[0].fake_pregap) { |
598 tracks[0].fake_pregap = 2 * 75; | 624 tracks[0].fake_pregap = 2 * 75; |
625 } | |
626 if (tracks[0].fake_pregap != old_fake_pregap) { | |
627 if (!tracks[0].start_lba) { | |
628 tracks[0].start_lba = tracks[0].fake_pregap; | |
629 } | |
630 uint32_t diff = tracks[0].fake_pregap - old_fake_pregap; | |
631 tracks[0].end_lba += diff; | |
632 for (uint32_t i = 1; i < media->num_tracks; i++) | |
633 { | |
634 tracks[i].pregap_lba += diff; | |
635 tracks[i].start_lba += diff; | |
636 tracks[i].end_lba += diff; | |
637 } | |
599 } | 638 } |
600 | 639 |
601 fseek(tracks[0].f, tracks[0].sector_bytes == 2352 ? 16 : 0, SEEK_SET); | 640 fseek(tracks[0].f, tracks[0].sector_bytes == 2352 ? 16 : 0, SEEK_SET); |
602 media->size = fread(media->buffer, 1, 2048, tracks[0].f); | 641 media->size = fread(media->buffer, 1, 2048, tracks[0].f); |
603 media->seek = bin_seek; | 642 media->seek = bin_seek; |
604 media->read = bin_read; | 643 media->read = bin_read; |
605 media->read_subcodes = bin_subcode_read; | 644 media->read_subcodes = bin_subcode_read; |
606 } | 645 } |
646 print_toc(media); | |
607 uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; | 647 uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; |
608 media->type = valid ? MEDIA_CDROM : MEDIA_CART; | 648 media->type = valid ? MEDIA_CDROM : MEDIA_CART; |
609 return valid; | 649 return valid; |
610 } | 650 } |
611 | 651 |