# HG changeset patch # User Michael Pavone # Date 1645075332 28800 # Node ID 2449c88cea368d499091175635e07b20138926c3 # Parent 0013362c320c61992c464c14c038a2815a27a268 Enhance support for CUE files and add initial support for cdrdao TOC files diff -r 0013362c320c -r 2449c88cea36 Makefile --- a/Makefile Sun Feb 13 22:52:52 2022 -0800 +++ b/Makefile Wed Feb 16 21:22:12 2022 -0800 @@ -215,12 +215,12 @@ MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o \ - segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o cdd_fader.o + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) \ - segacd.o lc8951.o cue.o cdd_mcu.o cd_graphics.o cdd_fader.o + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o ifdef NONUKLEAR CFLAGS+= -DDISABLE_NUKLEAR diff -r 0013362c320c -r 2449c88cea36 blastem.c --- a/blastem.c Sun Feb 13 22:52:52 2022 -0800 +++ b/blastem.c Wed Feb 16 21:22:12 2022 -0800 @@ -30,7 +30,7 @@ #include "bindings.h" #include "menu.h" #include "zip.h" -#include "cue.h" +#include "cdimage.h" #include "event_log.h" #ifndef DISABLE_NUKLEAR #include "nuklear_ui/blastem_nuklear.h" @@ -250,6 +250,12 @@ *stype = SYSTEM_SEGACD; } } + } else if (!strcasecmp(dst->extension, "toc")) { + if (parse_toc(dst)) { + if (stype) { + *stype = SYSTEM_SEGACD; + } + } } return ret; diff -r 0013362c320c -r 2449c88cea36 cdimage.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdimage.c Wed Feb 16 21:22:12 2022 -0800 @@ -0,0 +1,518 @@ +#include +#include + +#include "system.h" +#include "util.h" + +static char* cmd_start(char *cur) +{ + while (*cur && isblank(*cur)) + { + cur++; + } + return cur; +} + +static char* cmd_start_sameline(char *cur) +{ + while (*cur && isblank(*cur) && *cur != '\n') + { + cur++; + } + return cur; +} + +static char* word_end(char *cur) +{ + while (*cur && !isblank(*cur)) + { + cur++; + } + return cur; +} + +static char* next_line(char *cur) +{ + while (*cur && *cur != '\n') + { + cur++; + } + if (*cur) { + return cur + 1; + } + return NULL; +} + +static char* next_blank(char *cur) +{ + while (*cur && !isblank(*cur)) + { + cur++; + } + return cur; +} + +static uint32_t timecode_to_lba(char *timecode) +{ + char *end; + int seconds = 0, frames = 0; + int minutes = strtol(timecode, &end, 10); + if (end) { + timecode = end + 1; + seconds = strtol(timecode, &end, 10); + if (end) { + timecode = end + 1; + frames = strtol(timecode, NULL, 10); + } + } + seconds += minutes * 60; + return seconds * 75 + frames; + +} + +enum { + FAKE_DATA = 1, + FAKE_AUDIO, +}; + +static uint8_t bin_seek(system_media *media, uint32_t sector) +{ + media->cur_sector = sector; + uint32_t lba = sector; + uint32_t track; + for (track = 0; track < media->num_tracks; track++) + { + if (lba < media->tracks[track].fake_pregap) { + media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; + break; + } + lba -= media->tracks[track].fake_pregap; + if (lba < media->tracks[track].start_lba) { + if (media->tracks[track].fake_pregap) { + media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; + } else { + media->in_fake_pregap = 0; + } + break; + } + if (lba < media->tracks[track].end_lba) { + media->in_fake_pregap = 0; + break; + } + } + if (track < media->num_tracks) { + fprintf(stderr, "bin_seek to sector %u, adjusted lba %u, track %u\n", sector, lba, track); + media->cur_track = track; + if (!media->in_fake_pregap) { + if (track) { + lba -= media->tracks[track - 1].end_lba; + } + fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); + } + } + return track; +} + +static uint8_t fake_read(uint32_t sector, uint32_t offset) +{ + if (!offset || (offset >= 16)) { + return 0; + //TODO: error detection and correction bytes + } else if (offset < 12) { + return 0xFF; + } else if (offset == 12) { + uint32_t minute = (sector / 75) / 60; + return (minute % 10) | ((minute / 10 ) << 4); + } else if (offset == 13) { + uint32_t seconds = (sector / 75) % 60; + return (seconds % 10) | ((seconds / 10 ) << 4); + } else if (offset == 14) { + uint32_t frames = sector % 75; + return (frames % 10) | ((frames / 10 ) << 4); + } else { + return 1; + } +} + +static uint8_t bin_read(system_media *media, uint32_t offset) +{ + if (media->in_fake_pregap == FAKE_DATA) { + return fake_read(media->cur_sector, offset); + } else if (media->in_fake_pregap == FAKE_AUDIO) { + return 0; + } else if ((media->tracks[media->cur_track].sector_bytes < 2352 && offset < 16) || offset > (media->tracks[media->cur_track].sector_bytes + 16)) { + return fake_read(media->cur_sector, offset); + } else { + if (media->tracks[media->cur_track].need_swap) { + if (offset & 1) { + return media->byte_storage; + } + media->byte_storage = fgetc(media->tracks[media->cur_track].f); + } + return fgetc(media->tracks[media->cur_track].f); + } +} + +uint8_t parse_cue(system_media *media) +{ + char *line = media->buffer; + media->num_tracks = 0; + do { + char *cmd = cmd_start(line); + if (cmd) { + if (startswith(cmd, "TRACK ")) { + media->num_tracks++; + } + line = next_line(cmd); + } else { + line = NULL; + } + } while (line); + track_info *tracks = calloc(sizeof(track_info), media->num_tracks); + media->tracks = tracks; + line = media->buffer; + int track = -1; + uint8_t audio_byte_swap = 0; + FILE *f = NULL; + int track_of_file = -1; + uint8_t has_index_0 = 0; + do { + char *cmd = cmd_start(line); + if (*cmd) { + if (startswith(cmd, "TRACK ")) { + track++; + track_of_file++; + has_index_0 = 0; + cmd += 6; + char *end; + int file_track = strtol(cmd, &end, 10); + if (file_track != (track + 1)) { + warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); + } + tracks[track].f = f; + + + cmd = cmd_start(end); + if (*cmd) { + if (startswith(cmd, "AUDIO")) { + tracks[track].type = TRACK_AUDIO; + tracks[track].need_swap = audio_byte_swap; + tracks[track].sector_bytes = 2352; + } else { + tracks[track].type = TRACK_DATA; + tracks[track].need_swap = 0; + tracks[track].sector_bytes = 0; + char *slash = strchr(cmd, '/'); + if (slash) { + tracks[track].sector_bytes = atoi(slash+1); + } + if (!tracks[track].sector_bytes) { + warning("Missing sector size for data track %d in cue", track + 1); + tracks[track].sector_bytes = 2352; + } + } + + } + } else if (startswith(cmd, "FILE ")) { + cmd += 5; + cmd = strchr(cmd, '"'); + if (cmd) { + cmd++; + char *end = strchr(cmd, '"'); + if (end) { + char *fname; + //TODO: zipped BIN/CUE support + if (is_absolute_path(cmd)) { + fname = malloc(end-cmd + 1); + memcpy(fname, cmd, end-cmd); + fname[end-cmd] = 0; + } else { + size_t dirlen = strlen(media->dir); + fname = malloc(dirlen + 1 + (end-cmd) + 1); + memcpy(fname, media->dir, dirlen); + fname[dirlen] = PATH_SEP[0]; + memcpy(fname + dirlen + 1, cmd, end-cmd); + fname[dirlen + 1 + (end-cmd)] = 0; + } + f = fopen(fname, "rb"); + if (!f) { + fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); + } + free(fname); + track_of_file = -1; + for (end++; *end && *end != '\n' && *end != '\r'; end++) + { + if (!isspace(*end)) { + if (startswith(end, "BINARY")) { + audio_byte_swap = 0; + } else if (startswith(end, "MOTOROLA")) { + audio_byte_swap = 1; + } else { + warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); + } + break; + } + } + } + } + } else if (track >= 0) { + if (startswith(cmd, "PREGAP ")) { + tracks[track].fake_pregap = timecode_to_lba(cmd + 7); + } else if (startswith(cmd, "INDEX ")) { + char *after; + int index = strtol(cmd + 6, &after, 10); + uint8_t has_start_lba = 0; + uint32_t start_lba; + if (!index) { + tracks[track].pregap_lba = start_lba = timecode_to_lba(after); + has_index_0 = 1; + has_start_lba = 1; + } else if (index == 1) { + tracks[track].start_lba = timecode_to_lba(after); + if (!has_index_0) { + start_lba = tracks[track].start_lba; + has_start_lba = 1; + } + } + if (has_start_lba) { + if (track > 0) { + tracks[track-1].end_lba = start_lba; + } + if (track_of_file > 0) { + tracks[track].file_offset = tracks[track-1].file_offset + tracks[track-1].end_lba * tracks[track-1].sector_bytes; + if (track_of_file > 1) { + tracks[track].file_offset -= tracks[track-2].end_lba * tracks[track-1].sector_bytes; + } + } else { + tracks[track].file_offset = 0; + } + } + } + } + if (cmd && *cmd) { + line = next_line(cmd); + } else { + line = NULL; + } + } else { + line = NULL; + } + } while (line); + if (media->num_tracks > 0 && media->tracks[0].f) { + //end of last track is implicitly defined by file size + if (tracks[media->num_tracks-1].f) { + uint32_t start_lba =tracks[media->num_tracks-1].pregap_lba ? tracks[media->num_tracks-1].pregap_lba : tracks[media->num_tracks-1].start_lba; + tracks[media->num_tracks-1].end_lba = start_lba + (file_size(tracks[media->num_tracks-1].f) - tracks[media->num_tracks-1].file_offset)/ tracks[media->num_tracks-1].sector_bytes; + } + //replace cue sheet with first sector + free(media->buffer); + media->buffer = calloc(2048, 1); + if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { + // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector + uint8_t msf[3]; + fseek(tracks[0].f, 12, SEEK_SET); + if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { + tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; + } + } else if (!tracks[0].start_lba && !tracks[0].fake_pregap) { + tracks[0].fake_pregap = 2 * 75; + } + + fseek(tracks[0].f, tracks[0].sector_bytes == 2352 ? 16 : 0, SEEK_SET); + media->size = fread(media->buffer, 1, 2048, tracks[0].f); + media->seek = bin_seek; + media->read = bin_read; + } + uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; + media->type = valid ? MEDIA_CDROM : MEDIA_CART; + return valid; +} + +uint8_t parse_toc(system_media *media) +{ + char *line = media->buffer; + media->num_tracks = 0; + do { + char *cmd = cmd_start(line); + if (cmd) { + if (startswith(cmd, "TRACK ")) { + media->num_tracks++; + } + line = next_line(cmd); + } else { + line = NULL; + } + } while (line); + track_info *tracks = calloc(sizeof(track_info), media->num_tracks); + media->tracks = tracks; + line = media->buffer; + char *last_file_name = NULL; + FILE *f = NULL; + int track = -1; + do { + char *cmd = cmd_start(line); + if (*cmd) { + if (startswith(cmd, "TRACK ")) { + track++; + cmd = cmd_start(cmd + 6); + if (startswith(cmd, "AUDIO")) { + tracks[track].type = TRACK_AUDIO; + tracks[track].sector_bytes = 2352; + tracks[track].need_swap = 1; + } else { + tracks[track].type = TRACK_DATA; + tracks[track].need_swap = 0; + if (startswith(cmd, "MODE1_RAW") || startswith(cmd, "MODE2_RAW")) { + tracks[track].sector_bytes = 2352; + } else if (startswith(cmd, "MODE2_FORM2")) { + tracks[track].sector_bytes = 2324; + } else if (startswith(cmd, "MODE1") || startswith(cmd, "MODE2_FORM1")) { + tracks[track].sector_bytes = 2048; + } else if (startswith(cmd, "MODE2")) { + tracks[track].sector_bytes = 2336; + } + } + cmd = word_end(cmd); + if (*cmd && *cmd != '\n') { + cmd = cmd_start_sameline(cmd); + if (*cmd && *cmd != '\n') { + //TODO: record whether subcode is in raw format or not + if (startswith(cmd, "RW_RAW")) { + tracks[track].sector_bytes += 96; + } else if (startswith(cmd, "RW")) { + tracks[track].sector_bytes += 96; + } + } + } + if (track) { + tracks[track].start_lba = tracks[track].pregap_lba = tracks[track].end_lba = tracks[track-1].end_lba; + } + } else if (track >= 0) { + uint8_t is_datafile = startswith(cmd, "DATAFILE"); + if (is_datafile || startswith(cmd, "FILE")) { + + if (tracks[track].f) { + warning("TOC file has more than one file for track %d, only one is supported\n", track + 1); + } else { + cmd += 8; + char *fname_start = strchr(cmd, '"'); + if (fname_start) { + ++fname_start; + char *fname_end = strchr(fname_start, '"'); + if (fname_end) { + if (!last_file_name || strncmp(last_file_name, fname_start, fname_end-fname_start)) { + free(last_file_name); + last_file_name = calloc(1, 1 + fname_end-fname_start); + memcpy(last_file_name, fname_start, fname_end-fname_start); + char *fname; + //TODO: zipped BIN/TOC support + if (is_absolute_path(last_file_name)) { + fname = last_file_name; + } else { + size_t dirlen = strlen(media->dir); + fname = malloc(dirlen + 1 + (fname_end-fname_start) + 1); + memcpy(fname, media->dir, dirlen); + fname[dirlen] = PATH_SEP[0]; + memcpy(fname + dirlen + 1, fname_start, fname_end-fname_start); + fname[dirlen + 1 + (fname_end-fname_start)] = 0; + } + f = fopen(fname, "rb"); + if (!f) { + fatal_error("Failed to open %s specified by DATAFILE command in TOC file %s.%s\n", fname, media->name, media->extension); + } + if (fname != last_file_name) { + free(fname); + } + } + tracks[track].f = f; + cmd = fname_end + 1; + cmd = cmd_start_sameline(cmd); + if (*cmd == '#') { + char *end; + tracks[track].file_offset = strtol(cmd + 1, &end, 10); + cmd = cmd_start_sameline(end); + } + if (!is_datafile) { + if (isdigit(*cmd)) { + uint32_t start = timecode_to_lba(cmd); + tracks[track].file_offset += start * tracks[track].sector_bytes; + cmd = next_blank(cmd); + } + } + if (isdigit(*cmd)) { + uint32_t length = timecode_to_lba(cmd); + tracks[track].end_lba += length; + } else { + long fsize = file_size(f); + tracks[track].end_lba += fsize - tracks[track].file_offset; + } + } + } + } + } else if (startswith(cmd, "SILENCE")) { + cmd = cmd_start_sameline(cmd + 7); + tracks[track].fake_pregap += timecode_to_lba(cmd); + } else if (startswith(cmd, "START")) { + cmd = cmd_start_sameline(cmd + 5); + tracks[track].start_lba = tracks[track].pregap_lba + timecode_to_lba(cmd); + } + } + if (cmd && *cmd) { + line = next_line(cmd); + } else { + line = NULL; + } + } else { + line = NULL; + } + } while (line); + if (media->num_tracks > 0 && media->tracks[0].f) { + //replace cue sheet with first sector + free(media->buffer); + media->buffer = calloc(2048, 1); + if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { + // if the first track is a data track, don't trust the TOC file and look at the MM:SS:FF from first sector + uint8_t msf[3]; + fseek(tracks[0].f, 12, SEEK_SET); + if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { + tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; + } + } else if (!tracks[0].start_lba && !tracks[0].fake_pregap) { + tracks[0].fake_pregap = 2 * 75; + } + + fseek(tracks[0].f, tracks[0].sector_bytes == 2352 ? 16 : 0, SEEK_SET); + media->size = fread(media->buffer, 1, 2048, tracks[0].f); + media->seek = bin_seek; + media->read = bin_read; + } + uint8_t valid = media->num_tracks > 0 && media->tracks[0].f != NULL; + media->type = valid ? MEDIA_CDROM : MEDIA_CART; + return valid; +} + +uint32_t make_iso_media(system_media *media, const char *filename) +{ + FILE *f = fopen(filename, "rb"); + if (!f) { + return 0; + } + media->buffer = calloc(2048, 1); + media->size = fread(media->buffer, 1, 2048, f); + media->num_tracks = 1; + media->tracks = calloc(sizeof(track_info), 1); + media->tracks[0] = (track_info){ + .f = f, + .file_offset = 0, + .fake_pregap = 2 * 75, + .start_lba = 0, + .end_lba = file_size(f), + .sector_bytes = 2048, + .need_swap = 0, + .type = TRACK_DATA + }; + media->type = MEDIA_CDROM; + media->seek = bin_seek; + media->read = bin_read; + return media->size; +} diff -r 0013362c320c -r 2449c88cea36 cdimage.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cdimage.h Wed Feb 16 21:22:12 2022 -0800 @@ -0,0 +1,8 @@ +#ifndef CUE_H_ +#define CUE_H_ + +uint8_t parse_cue(system_media *media); +uint8_t parse_toc(system_media *media); +uint32_t make_iso_media(system_media *media, const char *filename); + +#endif //CUE_H_ diff -r 0013362c320c -r 2449c88cea36 cue.c --- a/cue.c Sun Feb 13 22:52:52 2022 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -#include -#include - -#include "system.h" -#include "util.h" - -static char* cmd_start(char *cur) -{ - while (*cur && isblank(*cur)) - { - cur++; - } - return cur; -} - -static char* next_line(char *cur) -{ - while (*cur && *cur != '\n') - { - cur++; - } - if (*cur) { - return cur + 1; - } - return NULL; -} - -static uint32_t timecode_to_lba(char *timecode) -{ - char *end; - int seconds = 0, frames = 0; - int minutes = strtol(timecode, &end, 10); - if (end) { - timecode = end + 1; - seconds = strtol(timecode, &end, 10); - if (end) { - timecode = end + 1; - frames = strtol(timecode, NULL, 10); - } - } - seconds += minutes * 60; - return seconds * 75 + frames; - -} - -enum { - FAKE_DATA = 1, - FAKE_AUDIO, -}; - -static uint8_t bin_seek(system_media *media, uint32_t sector) -{ - media->cur_sector = sector; - uint32_t lba = sector; - uint32_t track; - for (track = 0; track < media->num_tracks; track++) - { - if (lba < media->tracks[track].fake_pregap) { - media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; - break; - } - lba -= media->tracks[track].fake_pregap; - if (lba < media->tracks[track].start_lba) { - media->in_fake_pregap = media->tracks[track].type == TRACK_DATA ? FAKE_DATA : FAKE_AUDIO; - break; - } - if (lba < media->tracks[track].end_lba) { - media->in_fake_pregap = 0; - break; - } - } - if (track < media->num_tracks) { - media->cur_track = track; - } - if (!media->in_fake_pregap) { - fseek(media->f, lba * 2352, SEEK_SET); - } - return track; -} - -static uint8_t fake_read(uint32_t sector, uint32_t offset) -{ - if (!offset || (offset >= 16)) { - return 0; - //TODO: error detection and correction bytes - } else if (offset < 12) { - return 0xFF; - } else if (offset == 12) { - uint32_t minute = (sector / 75) / 60; - return (minute % 10) | ((minute / 10 ) << 4); - } else if (offset == 13) { - uint32_t seconds = (sector / 75) % 60; - return (seconds % 10) | ((seconds / 10 ) << 4); - } else if (offset == 14) { - uint32_t frames = sector % 75; - return (frames % 10) | ((frames / 10 ) << 4); - } else { - return 1; - } -} - -static uint8_t bin_read(system_media *media, uint32_t offset) -{ - if (media->in_fake_pregap == FAKE_DATA) { - return fake_read(media->cur_sector, offset); - } else if (media->in_fake_pregap == FAKE_AUDIO) { - return 0; - } else { - if (media->tracks[media->cur_track].need_swap) { - if (offset & 1) { - return media->byte_storage; - } - media->byte_storage = fgetc(media->f); - } - return fgetc(media->f); - } -} - -static uint8_t iso_seek(system_media *media, uint32_t sector) -{ - media->cur_sector = sector; - if (sector < (2 * 75)) { - media->in_fake_pregap = FAKE_DATA; - } else { - media->in_fake_pregap = 0; - fseek(media->f, (sector - 2 * 75) * 2048, SEEK_SET); - } - return 0; -} - -static uint8_t iso_read(system_media *media, uint32_t offset) -{ - if (media->in_fake_pregap || offset < 16 || offset > (2048 + 16)) { - return fake_read(media->cur_sector, offset); - } else { - return fgetc(media->f); - } -} - -uint8_t parse_cue(system_media *media) -{ - char *line = media->buffer; - media->num_tracks = 0; - do { - char *cmd = cmd_start(line); - if (cmd) { - if (startswith(cmd, "TRACK ")) { - media->num_tracks++; - } - line = next_line(cmd); - } else { - line = NULL; - } - } while (line); - track_info *tracks = calloc(sizeof(track_info), media->num_tracks); - media->tracks = tracks; - line = media->buffer; - int track = -1; - uint8_t audio_byte_swap = 0; - do { - char *cmd = cmd_start(line); - if (cmd) { - if (startswith(cmd, "TRACK ")) { - track++; - cmd += 6; - char *end; - int file_track = strtol(cmd, &end, 10); - if (file_track != (track + 1)) { - warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); - } - cmd = cmd_start(end); - if (cmd) { - tracks[track].type = startswith(cmd, "AUDIO") ? TRACK_AUDIO : TRACK_DATA; - tracks[track].need_swap = tracks[track].type == TRACK_AUDIO && audio_byte_swap; - } - } else if (startswith(cmd, "FILE ")) { - if (media->f) { - warning("CUE sheets with multiple FILE commands are not supported\n"); - } else { - cmd += 5; - cmd = strchr(cmd, '"'); - if (cmd) { - cmd++; - char *end = strchr(cmd, '"'); - if (end) { - char *fname; - //TODO: zipped BIN/CUE support - if (is_absolute_path(cmd)) { - fname = malloc(end-cmd + 1); - memcpy(fname, cmd, end-cmd); - fname[end-cmd] = 0; - } else { - size_t dirlen = strlen(media->dir); - fname = malloc(dirlen + 1 + (end-cmd) + 1); - memcpy(fname, media->dir, dirlen); - fname[dirlen] = PATH_SEP[0]; - memcpy(fname + dirlen + 1, cmd, end-cmd); - fname[dirlen + 1 + (end-cmd)] = 0; - } - media->f = fopen(fname, "rb"); - if (!media->f) { - fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); - } - free(fname); - for (end++; *end && *end != '\n' && *end != '\r'; end++) - { - if (!isspace(*end)) { - if (startswith(end, "BINARY")) { - audio_byte_swap = 0; - } else if (startswith(end, "MOTOROLA")) { - audio_byte_swap = 1; - } else { - warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); - } - break; - } - } - } - } - } - } else if (track >= 0) { - if (startswith(cmd, "PREGAP ")) { - tracks[track].fake_pregap = timecode_to_lba(cmd + 7); - } else if (startswith(cmd, "INDEX ")) { - char *after; - int index = strtol(cmd + 6, &after, 10); - if (!index) { - tracks[track].pregap_lba = timecode_to_lba(after); - } else if (index == 1) { - tracks[track].start_lba = timecode_to_lba(after); - } - } - } - if (cmd) { - line = next_line(cmd); - } else { - line = NULL; - } - } else { - line = NULL; - } - } while (line); - for (uint32_t i = 0; i < (media->num_tracks - 1); i++) - { - uint32_t next = i + 1; - tracks[i].end_lba = tracks[next].pregap_lba ? tracks[next].pregap_lba : tracks[next].start_lba; - } - if (media->f) { - //end of last track is implicitly defined by file size - tracks[media->num_tracks-1].end_lba = file_size(media->f) / 2352; - //replace cue sheet with first sector - free(media->buffer); - media->buffer = calloc(2048, 1); - if (tracks[0].type == TRACK_DATA) { - // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector - uint8_t msf[3]; - fseek(media->f, 12, SEEK_SET); - if (sizeof(msf) == fread(msf, 1, sizeof(msf), media->f)) { - tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; - } - } else if (!tracks[0].start_lba && !tracks[0].fake_pregap) { - tracks[0].fake_pregap = 2 * 75; - } - - fseek(media->f, 16, SEEK_SET); - media->size = fread(media->buffer, 1, 2048, media->f); - media->seek = bin_seek; - media->read = bin_read; - } - uint8_t valid = tracks > 0 && media->f != NULL; - media->type = valid ? MEDIA_CDROM : MEDIA_CART; - return valid; -} - -uint32_t make_iso_media(system_media *media, const char *filename) -{ - media->f = fopen(filename, "rb"); - if (!media->f) { - return 0; - } - media->buffer = calloc(2048, 1); - media->size = fread(media->buffer, 1, 2048, media->f); - media->num_tracks = 1; - media->tracks = calloc(sizeof(track_info), 1); - media->tracks[0] = (track_info){ - .fake_pregap = 2 * 75, - .start_lba = 0, - .end_lba = file_size(media->f), - .type = TRACK_DATA - }; - media->type = MEDIA_CDROM; - media->seek = iso_seek; - media->read = iso_read; - return media->size; -} diff -r 0013362c320c -r 2449c88cea36 cue.h --- a/cue.h Sun Feb 13 22:52:52 2022 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -#ifndef CUE_H_ -#define CUE_H_ - -uint8_t parse_cue(system_media *media); -uint32_t make_iso_media(system_media *media, const char *filename); - -#endif //CUE_H_ diff -r 0013362c320c -r 2449c88cea36 system.h --- a/system.h Sun Feb 13 22:52:52 2022 -0800 +++ b/system.h Wed Feb 16 21:22:12 2022 -0800 @@ -91,10 +91,13 @@ } track_type; typedef struct { + FILE *f; + uint32_t file_offset; uint32_t fake_pregap; uint32_t pregap_lba; uint32_t start_lba; uint32_t end_lba; + uint16_t sector_bytes; uint8_t need_swap; track_type type; } track_info; @@ -109,7 +112,6 @@ char *extension; system_media *chain; track_info *tracks; - FILE *f; seek_fun seek; read_fun read; uint32_t num_tracks;