# HG changeset patch # User Michael Pavone # Date 1642831186 28800 # Node ID 6399a776e98196cdcb936554dbdffb574a6eecb5 # Parent 70260f6051ddcdb8d2fee9b82a8bf1d4e8d3241a Add basic support for BIN/CUE images diff -r 70260f6051dd -r 6399a776e981 Makefile --- a/Makefile Fri Jan 21 20:24:48 2022 -0800 +++ b/Makefile Fri Jan 21 21:59:46 2022 -0800 @@ -214,7 +214,7 @@ 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 + $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o segacd.o lc8951.o cue.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 \ diff -r 70260f6051dd -r 6399a776e981 blastem.c --- a/blastem.c Fri Jan 21 20:24:48 2022 -0800 +++ b/blastem.c Fri Jan 21 21:59:46 2022 -0800 @@ -30,6 +30,7 @@ #include "bindings.h" #include "menu.h" #include "zip.h" +#include "cue.h" #include "event_log.h" #ifndef DISABLE_NUKLEAR #include "nuklear_ui/blastem_nuklear.h" @@ -235,8 +236,13 @@ dst->name = basename_no_extension(filename); dst->extension = path_extension(filename); dst->size = ret; + romclose(f); + if (!strcasecmp(dst->extension, "cue")) { + if (parse_cue(dst)) { + *stype = SYSTEM_SEGACD; + } + } - romclose(f); return ret; } diff -r 70260f6051dd -r 6399a776e981 cue.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cue.c Fri Jan 21 21:59:46 2022 -0800 @@ -0,0 +1,148 @@ +#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; + +} + +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; + 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; + } + } 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); + } + } + } + } 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); + fseek(media->f, 16, SEEK_SET); + media->size = fread(media->buffer, 1, 2048, media->f); + } + return tracks > 0 && media->f != NULL; +} diff -r 70260f6051dd -r 6399a776e981 cue.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cue.h Fri Jan 21 21:59:46 2022 -0800 @@ -0,0 +1,6 @@ +#ifndef CUE_H_ +#define CUE_H_ + +uint8_t parse_cue(system_media *media); + +#endif //CUE_H_ diff -r 70260f6051dd -r 6399a776e981 system.h --- a/system.h Fri Jan 21 20:24:48 2022 -0800 +++ b/system.h Fri Jan 21 21:59:46 2022 -0800 @@ -2,6 +2,7 @@ #define SYSTEM_H_ #include #include +#include typedef struct system_header system_header; typedef struct system_media system_media; @@ -84,12 +85,28 @@ MEDIA_CDROM } media_type; +typedef enum { + TRACK_AUDIO, + TRACK_DATA +} track_type; + +typedef struct { + uint32_t fake_pregap; + uint32_t pregap_lba; + uint32_t start_lba; + uint32_t end_lba; + track_type type; +} track_info; + struct system_media { void *buffer; char *dir; char *name; char *extension; system_media *chain; + track_info *tracks; + FILE *f; + uint32_t num_tracks; uint32_t size; media_type type; };