From 38a9d0ec489925eb25b8a0c837b5230da4eb8a68 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Mon, 11 May 2020 21:43:44 -0400 Subject: add cuerecover --- cuerecover.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 cuerecover.c (limited to 'cuerecover.c') diff --git a/cuerecover.c b/cuerecover.c new file mode 100644 index 0000000..34542d2 --- /dev/null +++ b/cuerecover.c @@ -0,0 +1,311 @@ +/* references: +https://www.gnu.org/software/ccd2cue/manual/html_node/CUE-sheet-format.html +https://wiki.osdev.org/User:Combuster/CDRom_BS +https://en.wikipedia.org/wiki/CD-ROM */ + +#include +#include +#include +#include +#include +#include + +const char *self; + +#define RAW_SECTOR_SIZE 2352 +#define DATA_SECTOR_SIZE 2048 + +unsigned char mode1_sync[] = { + 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00 }; + +double silence_sec = 2.0l; +int silence_frames; /* same as above in 1/75 sec frames */ +int silence_thresh = 0; +int multi_bin = 0; +int verbose = 0; + +void die(const char *msg) { + if(!msg) msg = strerror(errno); + fprintf(stderr, "%s: %s\n", self, msg); + exit(1); +} + +void usage() { + fprintf(stderr, "cuerecover v" VERSION " by B. Watson, WTFPL\n"); + fprintf(stderr, "Usage: %s [-s sec] [-t thresh] bin-file [bin-file ...]\n", self); + fprintf(stderr, "See man page for details\n"); + exit(0); +} + +int frame_is_silent(const char *data) { + int i, nonzero = 0; + for(i=0; i> 4) & 0x0f; + return hi * 10 + lo; +} + +int get_sector_addr(char *buf) { + int mm, ss, ff; + mm = unbcd(buf[12]); + ss = unbcd(buf[13]); + ff = unbcd(buf[14]); + return mm * 75 * 60 + ss * 75 + ff; +} + +const char *mode_string(int mode) { + switch(mode) { + case 1: return "MODE1/2352"; + case 2: return "MODE2/2352"; + default: return "AUDIO"; + } +} + +struct track { + int mode; + int index0; + int index1; +}; + +void process_single(char *filename) { + char buf[RAW_SECTOR_SIZE]; + FILE *f; + int i; + int track = 0; + int sector = 0; + int mode; + int silence_start, in_silence = 0; + struct track tracklist[100]; + struct track *t = tracklist; + + if(!(f = fopen(filename, "rb"))) + die(NULL); + + /* only the first track can be data */ + if(read_sector(f, buf) && (mode = get_sector_mode(buf)) != 0) { + if(verbose) fprintf(stderr, "track %d is data, mode %d\n", track, mode); + t->index0 = sector; + t->index1 = sector; + t->mode = mode; + + /* skip over the data sectors */ + while(read_sector(f, buf) && (get_sector_mode(buf) == mode)) + sector++; + + /* now buf holds the first audio sector, which might or might not + begin with silence */ + t++; track++; + t->mode = 0; + t->index0 = sector; + t->index1 = sector; + } + + if(silence_frames) while(read_sector(f, buf)) { + if(frame_is_silent(buf)) { + if(in_silence) { + /* nothing */ + } else { + t->index0 = sector; + t->index1 = -1; + silence_start = sector; + in_silence = 1; + } + } else { + if(in_silence) { + if((sector - silence_start) >= silence_frames) { + in_silence = 0; + t->index1 = sector; + t++; + track++; + t->mode = 0; + } else { + /* nothing */ + } + } + } + sector++; + } + + fclose(f); + + t = tracklist; + printf("FILE \"%s\" BINARY\r\n", filename); + for(i=0; i<=track; t++, i++) { + if(verbose) { + fprintf(stderr, "track %d, mode %d, index0 %d, index1 %d\n", + i, t->mode, t->index0, t->index1); + } + if(t->index1 > -1) { + printf(" TRACK %02d %s\r\n", i + 1, mode_string(t->mode)); + if(t->index0 < t->index1) { + if(silence_frames && t->index1 - t->index0 > silence_frames) { + t->index0 = t->index1 - silence_frames; + if(verbose) + fprintf(stderr, " (index0 adjusted to %d)\n", t->index0); + } + printf(" INDEX 00 %s\r\n", frame2time(t->index0)); + } + printf(" INDEX 01 %s\r\n", frame2time(t->index1)); + } + } +} + +void process_multi(char **filenames) { + int track = 1; + char buf[RAW_SECTOR_SIZE]; + FILE *f; + + while(*filenames) { + int index0 = 0, index1 = -1; + int mode = -1; + int sector = 0; + + if(verbose) fprintf(stderr, "reading %s\n", *filenames); + if(!(f = fopen(*filenames, "rb"))) + die(NULL); + + printf("FILE \"%s\" BINARY\r\n", *filenames); + + while(read_sector(f, buf)) { + mode = get_sector_mode(buf); + + /* if first sector is data, there's no index 01, we're done */ + if(mode > 0) { + index0 = 0; + index1 = -1; + break; + } + + /* for audio, keep reading silence until we hit sound */ + if(mode == 0 && index1 == -1 && !frame_is_silent(buf)) { + index1 = (sector / 75) * 75; /* round to integer number of seconds */ + break; + } + + sector++; + } + fclose(f); + + printf(" TRACK %02d %s\r\n", track, mode_string(mode)); + if(index1 < 1) { + printf(" INDEX 01 %s\r\n", frame2time(index0)); + } else { + printf(" INDEX 00 %s\r\n", frame2time(index0)); + printf(" INDEX 01 %s\r\n", frame2time(index1)); + } + + track++; + filenames++; + } +} + +char **parse_args(int argc, char **argv) { + if(argc == 1) usage(); + if(argc > 1 && strcmp(argv[1], "--help") == 0) usage(); + + while(++argv, --argc) { + if(argv[0][0] == '-') { + char *nextarg = argv[1]; + switch(argv[0][1]) { + case 'v': verbose++; break; + case 's': + if(nextarg && ((*nextarg >= '0' && *nextarg <= '9') || (*nextarg == '.'))) { + silence_sec = atof(nextarg); + argv++, argc--; + } else { + die("-s requires numeric seconds argument"); + } + break; + case 't': + if(nextarg && + (*nextarg >= '0' && *nextarg <= '9') && + (silence_thresh = atoi(nextarg) <= 100)) + { + silence_thresh = (RAW_SECTOR_SIZE / 100.0) * atoi(nextarg); + argv++, argc--; + } else { + die("-t requires numeric percentage argument, 0-100"); + } + break; + default: + fprintf(stderr, "unrecognized option '%s', try --help\n", *argv); + exit(1); + break; + } + } else { + break; + } + } + + silence_frames = (int)(75.0l * silence_sec); + multi_bin = (argc > 1); + return argv; +} + +void set_exe_name(const char *argv0) { + const char *p; + self = argv0; + for(p = self; *p; p++) + if(p[0] == '/' && p[1]) self = p + 1; +} + +int main(int argc, char **argv) { + set_exe_name(argv[0]); + + argv = parse_args(argc, argv); + + if(verbose) fprintf(stderr, "writing %s CUE file to stdout\n", + (multi_bin ? "multi-bin" : "single-bin")); + + if(multi_bin) + process_multi(argv); + else + process_single(*argv); + + return 0; +} -- cgit v1.2.3