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