diff --git a/ChangeLog b/ChangeLog index 326f61a..7555368 100644 --- a/ChangeLog +++ b/ChangeLog @@ -63,3 +63,14 @@ Clarify manual page for input/output file types Improvement from Reuben Thomas, debian bug: #503151 +1.3.0 - Feb 01 2026 - Jakemkz + + Added support for split bin files for example those produced by + redumper using the new -m flag + + Added support for converting pre-gap audio tracks to wav. + + Added additional logic to ensure audio tracks are started at INDEX 01 + + Added debug mode to makefile with strict compiler flags and made + adjustments to compile without warnings diff --git a/Makefile b/Makefile index 49928d2..0df0d65 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ all: bchunk CC = gcc LD = gcc CFLAGS = -Wall -Wstrict-prototypes -O2 +CFLAGS_PED = -Wall -Wstrict-prototypes -O2 -g -Wextra -pedantic -Wformat -Wconversion -Wstrict-aliasing -Wundef -Wshadow -Wsign-conversion -fstrict-overflow # For systems with a legacy CC: #CC = cc @@ -22,6 +23,9 @@ MAN_DIR = $(PREFIX)/man .c.o: $(CC) $(CFLAGS) -c $< +debug: CFLAGS = $(CFLAGS_PED) +debug: bchunk + clean: rm -f *.o *~ *.bak core distclean: clean @@ -38,7 +42,7 @@ installman: BITS = bchunk.o bchunk: $(BITS) - $(LD) -o bchunk $(BITS) $(LDFLAGS) + $(LD) -o bchunk $(BITS) $(LDFLAGS) bchunk.o: bchunk.c diff --git a/README b/README index 9282a6d..f5a36a5 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ - binchunker for Unix, version 1.2.2 + binchunker for Unix, version 1.3.0 Copyright (C) 1998-2004 Heikki Hannikainen Enhancements provided by: @@ -85,8 +85,8 @@ How to install this stuff: - $ gzip -d -c bchunk-1.2.2.tar.gz | tar xvf - - $ cd bchunk-1.2.2 + $ gzip -d -c bchunk-1.3.0.tar.gz | tar xvf - + $ cd bchunk-1.3.0 $ make # make install @@ -144,5 +144,8 @@ the audio tracks. If the audio sounds like loud static noise, try this. - - + The -m flag makes binchunker use file names provided in the .cue + file and merge them into a new combined binary . + This is intended to correctly handle split .bin files like those + produced by redumper. + diff --git a/bchunk.1 b/bchunk.1 index 65c09f9..ab56c1d 100644 --- a/bchunk.1 +++ b/bchunk.1 @@ -1,4 +1,4 @@ -.TH BCHUNK 1 "v1.2.2 14 Nov 2017" "Heikki Hannikainen" +.TH BCHUNK 1 "v1.3.0 01 Feb 2026" "Heikki Hannikainen" .SH NAME bchunk \- CD image format conversion from bin/cue to iso/cdr .SH SYNOPSIS diff --git a/bchunk.c b/bchunk.c index 52f1aa9..8987de5 100644 --- a/bchunk.c +++ b/bchunk.c @@ -24,8 +24,9 @@ #include #include #include +#include -#define VERSION "1.2.2" +#define VERSION "1.3.0" #define USAGE "Usage: bchunk [-v] [-r] [-p (PSX)] [-w (wav)] [-s (swabaudio)]\n" \ " \n" \ "Example: bchunk foo.bin foo.cue foo\n" \ @@ -34,6 +35,11 @@ " -p PSX mode for MODE2/2352: write 2336 bytes from offset 24\n" \ " (default MODE2/2352 mode writes 2048 bytes from offset 24)\n"\ " -w Output audio files in WAV format\n" \ + " -m Merge and convert bin files based on files in .cue (experimental)\n" \ + " in this mode is the path to a new file that will be created\n" \ + " a matching file will be produced to accompany the merged bin\n" \ + " dropping the argument in merge mode performs the bin/cue\n" \ + " merge without creating additional output files (eg. WAV, ISO, etc.)\n" \ " -s swabaudio: swap byte order in audio tracks\n" #define VERSTR "binchunker for Unix, version " VERSION " by Heikki Hannikainen \n" \ @@ -84,21 +90,25 @@ struct track_t { char *extension; int bstart; int bsize; - long startsect; - long stopsect; - long start; - long stop; + int32_t startsect; + int32_t stopsect; + int32_t start; + int32_t stop; struct track_t *next; }; char *basefile = NULL; char *binfile = NULL; char *cuefile = NULL; +char *cuefile_copy = NULL; +char *bindir = NULL; int verbose = 0; int psxtruncate = 0; int raw = 0; int swabaudio = 0; int towav = 0; +int merge = 0; +int nowrite = 0; /* * Parse arguments @@ -108,11 +118,14 @@ void parse_args(int argc, char *argv[]) { int s; - while ((s = getopt(argc, argv, "swvp?hr")) != -1) { + while ((s = getopt(argc, argv, "swvp?hrm")) != -1) { switch (s) { case 'r': raw = 1; break; + case 'm': + merge = 1; + break; case 'v': verbose = 1; break; @@ -132,38 +145,59 @@ void parse_args(int argc, char *argv[]) } } - if (argc - optind != 3) { + if (argc - optind == 3) { + while (optind < argc) { + switch (argc - optind) { + case 3: + binfile = strdup(argv[optind]); + break; + case 2: + cuefile = strdup(argv[optind]); + cuefile_copy = strdup(cuefile); + bindir = dirname(cuefile_copy); + break; + case 1: + basefile = strdup(argv[optind]); + break; + default: + fprintf(stderr, "%s", USAGE); + exit(1); + } + optind++; + } + } else if ((argc - optind == 2) && merge) { + nowrite = 1; + while (optind < argc) { + switch (argc - optind) { + case 2: + binfile = strdup(argv[optind]); + break; + case 1: + cuefile = strdup(argv[optind]); + cuefile_copy = strdup(cuefile); + bindir = dirname(cuefile_copy); + break; + default: + fprintf(stderr, "%s", USAGE); + exit(1); + } + optind++; + } + } else { fprintf(stderr, "%s", USAGE); exit(1); } - - while (optind < argc) { - switch (argc - optind) { - case 3: - binfile = strdup(argv[optind]); - break; - case 2: - cuefile = strdup(argv[optind]); - break; - case 1: - basefile = strdup(argv[optind]); - break; - default: - fprintf(stderr, "%s", USAGE); - exit(1); - } - optind++; - } } /* * Convert a mins:secs:frames format to plain frames */ -long time2frames(char *s) +int32_t time2frames(char *msf) { int mins = 0, secs = 0, frames = 0; - char *p, *t; + char *p, *t, *s; + s = strdup(msf); if (!(p = strchr(s, ':'))) return -1; @@ -182,6 +216,16 @@ long time2frames(char *s) return 75 * (mins * 60 + secs) + frames; } +void frames2time(int32_t totalframes, char* msfstring) +{ + int mins = totalframes / (75*60); + int secs = (totalframes - mins * (75*60)) / 75; + int frames = totalframes - mins * (75*60) - secs * (75); + + snprintf(msfstring,50,"%02d" ":" "%02d" ":" "%02d",mins,secs,frames); + +} + /* * Parse the mode string */ @@ -249,7 +293,7 @@ char *progressbar(float f, int l) static char s[80]; int i, n; - n = l * f; + n = l * (int)f; for (i = 0; i < n; i++) { s[i] = '*'; } @@ -295,10 +339,10 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) reallen = (track->stopsect - track->startsect + 1) * track->bsize; if (verbose) { - printf("\n mmc sectors %ld->%ld (%ld)", track->startsect, track->stopsect, track->stopsect - track->startsect + 1); - printf("\n mmc bytes %ld->%ld (%ld)", track->start, track->stop, track->stop - track->start + 1); + printf("\n mmc sectors %" PRId32"->%" PRId32" (%d)", track->startsect, track->stopsect, track->stopsect - track->startsect + 1); + printf("\n mmc bytes %" PRId32 "->%" PRId32" (%d)", track->start, track->stop, track->stop - track->start + 1); printf("\n sector data at %d, %d bytes per sector", track->bstart, track->bsize); - printf("\n real data %ld bytes", (track->stopsect - track->startsect + 1) * track->bsize); + printf("\n real data %d bytes", (track->stopsect - track->startsect + 1) * track->bsize); printf("\n"); } @@ -391,39 +435,67 @@ int main(int argc, char **argv) { char s[CUELLEN+1]; char *p, *t; + char shiftedmsf[50]; + char *mergedcue, *ext_pos; struct track_t *tracks = NULL; struct track_t *track = NULL; struct track_t *prevtrack = NULL; struct track_t **prevp = &tracks; + int32_t nextsectoroffset = 0; + int32_t prevsectoroffset = 0; - FILE *binf, *cuef; - + FILE *binf = NULL, *cuef = NULL, *mergef = NULL, *mergecf = NULL; + printf("%s", VERSTR); parse_args(argc, argv); + - if (!((binf = fopen(binfile, "rb")))) { - fprintf(stderr, "Could not open BIN %s: %s\n", binfile, strerror(errno)); - return 2; + if (!merge) { + if (!((binf = fopen(binfile, "r")))) { + fprintf(stderr, "Could not open BIN %s: %s\n", binfile, strerror(errno)); + return 2; + } + } else { + // create path to matching cue file for merged bin + mergedcue = strdup(binfile); + ext_pos = strstr(mergedcue, ".bin"); + if (!(ext_pos == NULL) && mergedcue[0] != '\0') { + strcpy(ext_pos,".cue"); + }else { + fprintf(stderr, "Bin file name must end in .bin for merge mode\n"); + return 2; + } + // open merged bin and cue files + if (!((mergef = fopen(binfile, "w+bx")))) { + fprintf(stderr, "Could not open merged BIN %s: %s\n",binfile,strerror(errno)); + return 2; + } + if (!((mergecf = fopen(mergedcue, "w+x")))) { + fprintf(stderr, "Could not open merged CUE %s: %s\n",mergedcue,strerror(errno)); + return 2; + } + fprintf(mergecf, "FILE \"%s\" BINARY\n", binfile); } if (!((cuef = fopen(cuefile, "r")))) { fprintf(stderr, "Could not open CUE %s: %s\n", cuefile, strerror(errno)); return 2; } - - printf("Reading the CUE file:\n"); - - /* We don't really care about the first line. */ - if (!fgets(s, CUELLEN, cuef)) { - fprintf(stderr, "Could not read first line from %s: %s\n", cuefile, strerror(errno)); - return 3; + + if (verbose) { + printf("Reading the CUE file\n"); + printf("Path to BIN/CUE files:%s\n",bindir); } - + + // loop over lines in CUE file while (fgets(s, CUELLEN, cuef)) { while ((p = strchr(s, '\r')) || (p = strchr(s, '\n'))) *p = '\0'; + if (!strstr(s, "FILE") && !strstr(s, "INDEX") && merge) + fprintf(mergecf, "%s\n",s); // copy all lines as-is except FILE/INDEX + if ((p = strstr(s, "TRACK"))) { printf("\nTrack "); if (!(p = strchr(p, ' '))) { @@ -472,33 +544,132 @@ int main(int argc, char **argv) *t = '\0'; t++; printf(" %s %s", p, t); - track->startsect = time2frames(t); - track->start = track->startsect * SECTLEN; - if (verbose) - printf(" (startsect %ld ofs %ld)", track->startsect, track->start); - if ((prevtrack) && (prevtrack->stopsect < 0)) { - prevtrack->stopsect = track->startsect - 1; - prevtrack->stop = track->start - 1; + + if(merge) { + frames2time(prevsectoroffset+time2frames(t), shiftedmsf); + fprintf(mergecf, " INDEX %s %s\n", p, shiftedmsf); + } + if (strcmp(p,"01") == 0) { + track->startsect = prevsectoroffset + time2frames(t); + track->start = track->startsect * SECTLEN; + if (verbose) + printf(" (startsect %" PRId32 " ofs %" PRId32 ")", track->startsect, track->start); + if ((prevtrack) && (prevtrack->stopsect < 0)) { + prevtrack->stopsect = track->startsect - 1; + prevtrack->stop = track->start - 1; + } + } else if ((strcmp(p,"00") == 0) && (track->num == 1) && (track->audio == 1)) { + // special handling for audio at INDEX 00 on TRACK 01 + // split audio out into track->num 0 so that pre-gap + // audio is preserved, create a new track for INDEX 01 + printf("detected pregap track at track one\n"); + track->num = 0; + track->startsect = prevsectoroffset + time2frames(t); + track->start = track->startsect * SECTLEN; + + + prevtrack = track; + if (!(track = malloc(sizeof(struct track_t)))) { + fprintf(stderr, "main(): malloc() failed, out of memory\n"); + exit(4); + } + + *prevp = track; + prevp = &track->next; + track->next = NULL; + track->num = 1; + + track->modes = strdup(prevtrack->modes); + track->extension = prevtrack->extension; + track->mode = prevtrack->mode; + track->audio = prevtrack->audio; + track->bsize = prevtrack->bsize; + track->bstart = prevtrack->bstart; + track->startsect = track->stopsect = -1; + + } + } else if ((p = strstr(s, "FILE"))) { + // detected a bin file descriptor + if (!(p = strchr(p, '"'))) { + printf("... did not detect open quote for bin file.\n"); + exit(3); + } + p++; + + if (!(t = strchr(p, '"'))) { + printf("... did not detect closing quote for bin file.\n"); + exit(3); + } + *t = '\0'; + t++; + + if (merge) { + char fullpath[200]; + snprintf(fullpath, sizeof(fullpath), "%s/%s", bindir, p); + printf("\nLoading new BIN file: %s", fullpath); + if (!((binf = fopen(fullpath, "r")))) { + fprintf(stderr, "Could not open BIN %s: %s\n", p, strerror(errno)); + return 2; + } + + prevsectoroffset = nextsectoroffset; + + unsigned char buffer[8192]; + size_t bytesRead; + + // copy bytes from new file into merged file + while ((bytesRead = fread(buffer, 1, sizeof(buffer), binf)) > 0) { + if (fwrite(buffer, 1, bytesRead, mergef) != bytesRead) { + fprintf(stderr,"Error writing to merged BIN file\n"); + return 2; + break; + } + } + + fseek(binf, 0, SEEK_END); + nextsectoroffset = prevsectoroffset + (1+((int32_t)ftell(binf) - 1) / SECTLEN); + + fclose(binf); } } } - if (track) { - fseek(binf, 0, SEEK_END); - track->stop = ftell(binf) - 1; - track->stopsect = track->stop / SECTLEN; + if (merge) { + if (track) { + fseek(mergef, 0, SEEK_END); + track->stop = (int32_t)ftell(mergef) - 1; + track->stopsect = track->stop / SECTLEN; + } + } else { + if (track) { + fseek(binf, 0, SEEK_END); + track->stop = (int32_t)ftell(binf) - 1; + track->stopsect = track->stop / SECTLEN; + } } - + printf("\n\n"); + if (!nowrite) { + printf("Writing tracks:\n\n"); + for (track = tracks; (track); track = track->next) { + if (merge) { + writetrack(mergef, track, basefile); + } else { + writetrack(binf, track, basefile); + } + } - printf("Writing tracks:\n\n"); - for (track = tracks; (track); track = track->next) - writetrack(binf, track, basefile); - - fclose(binf); + } + + if (merge) { + fclose(mergef); + fclose(mergecf); + } else { + fclose(binf); + } fclose(cuef); - + return 0; }