From 5b7a3cc0e341b5e8e32f20f70a6d07dac06c3b6c Mon Sep 17 00:00:00 2001 From: jakemkz Date: Sun, 1 Feb 2026 15:26:15 -0600 Subject: [PATCH 1/6] Initial version for handling split .bin files and pregap audio tracks. --- ChangeLog | 11 +++ Makefile | 21 +++--- README | 13 ++-- bchunk.1 | 2 +- bchunk.c | 222 +++++++++++++++++++++++++++++++++++++++++------------- 5 files changed, 198 insertions(+), 71 deletions(-) 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..b1f5458 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ -all: bchunk - # For systems with GCC (Linux, and others with GCC installed): CC = gcc LD = gcc -CFLAGS = -Wall -Wstrict-prototypes -O2 +CFLAGS = -Wall -Wstrict-prototypes -O2 -Wextra -pedantic -Werror +CFLAGS_PED = -Wall -Wstrict-prototypes -O2 -g -Wextra -pedantic -Werror -Wformat -Wconversion -Wstrict-aliasing -Wundef -Wshadow -Wsign-conversion -fstrict-overflow + +all: bchunk # For systems with a legacy CC: #CC = cc @@ -12,9 +13,6 @@ CFLAGS = -Wall -Wstrict-prototypes -O2 # For BSD install: Which install to use and where to put the files INSTALL = install -INSTALL_DIR = $(INSTALL) -d -m 0755 -INSTALL_DATA = $(INSTALL) -m 0644 -INSTALL_EXEC = $(INSTALL) -m 0755 PREFIX = /usr/local BIN_DIR = $(PREFIX)/bin MAN_DIR = $(PREFIX)/man @@ -22,6 +20,9 @@ MAN_DIR = $(PREFIX)/man .c.o: $(CC) $(CFLAGS) -c $< +debug: CFLAGS = $(CFLAGS_PED) +debug: bchunk + clean: rm -f *.o *~ *.bak core distclean: clean @@ -29,16 +30,14 @@ distclean: clean install: installbin installman installbin: - $(INSTALL_DIR) $(DESTDIR)$(BIN_DIR) - $(INSTALL_EXEC) -s bchunk $(DESTDIR)$(BIN_DIR) + $(INSTALL) -m 755 -s -o root -g root bchunk $(BIN_DIR) installman: - $(INSTALL_DIR) $(DESTDIR)$(MAN_DIR)/man1 - $(INSTALL_DATA) bchunk.1 $(DESTDIR)$(MAN_DIR)/man1 + $(INSTALL) -m 644 -o bin -g bin bchunk.1 $(MAN_DIR)/man1 BITS = bchunk.o bchunk: $(BITS) - $(LD) -o bchunk $(BITS) $(LDFLAGS) + $(LD) $(LDFLAGS) -o bchunk $(BITS) bchunk.o: bchunk.c diff --git a/README b/README index 9282a6d..8079cd8 100644 --- a/README +++ b/README @@ -52,7 +52,7 @@ Colas Nahaboo and Godmar Back added support for MODE2/2352 ISO data tracks in bchunk 1.1.0. Matthew Green implemented the -r option for raw MODE2/2352 - extraction for bchunk 1.2.0. + exctraction for bchunk 1.2.0. --- @@ -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 make 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..7f4c6fa 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,8 @@ " -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" \ " -s swabaudio: swap byte order in audio tracks\n" #define VERSTR "binchunker for Unix, version " VERSION " by Heikki Hannikainen \n" \ @@ -59,11 +62,7 @@ */ #include -#ifdef _WIN32 -#include -#else #include -#endif #define bswap_16(x) \ ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) @@ -84,21 +83,24 @@ 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; /* * Parse arguments @@ -108,11 +110,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; @@ -144,6 +149,8 @@ void parse_args(int argc, char *argv[]) break; case 2: cuefile = strdup(argv[optind]); + cuefile_copy = strdup(cuefile); + bindir = dirname(cuefile_copy); break; case 1: basefile = strdup(argv[optind]); @@ -160,7 +167,7 @@ void parse_args(int argc, char *argv[]) * Convert a mins:secs:frames format to plain frames */ -long time2frames(char *s) +int32_t time2frames(char *s) { int mins = 0, secs = 0, frames = 0; char *p, *t; @@ -249,7 +256,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] = '*'; } @@ -270,7 +277,7 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) char *fname; FILE *f; char buf[SECTLEN+10]; - long sz, sect, realsz, reallen; + int32_t sz, sect, realsz, reallen; char c, *p, *p2, *ep; int32_t l; int16_t i; @@ -283,22 +290,23 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) printf("%2d: %s ", track->num, fname); - if (!(f = fopen(fname, "wb"))) { + if (!(f = fopen(fname, "w"))) { fprintf(stderr, " Could not fopen track file: %s\n", strerror(errno)); exit(4); } + if (fseek(bf, track->start, SEEK_SET)) { fprintf(stderr, " Could not fseek to track location: %s\n", strerror(errno)); exit(4); } - reallen = (track->stopsect - track->startsect + 1) * track->bsize; + reallen = (int32_t)(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 " (%" PRId32 ")", track->startsect, track->stopsect, track->stopsect - track->startsect + 1); + printf("\n mmc bytes %" PRId32 "->%" PRId32 " (%" PRId32 ")", 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 %" PRId32 " bytes", (track->stopsect - track->startsect + 1) * track->bsize); printf("\n"); } @@ -307,28 +315,28 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) if ((track->audio) && (towav)) { // RIFF header fputs("RIFF", f); - l = htolel(reallen + WAV_DATA_HLEN + WAV_FORMAT_HLEN + 4); + l = (int32_t)htolel((uint32_t)reallen + WAV_DATA_HLEN + WAV_FORMAT_HLEN + 4); fwrite(&l, 4, 1, f); // length of file, starting from WAVE fputs("WAVE", f); // FORMAT header fputs("fmt ", f); - l = htolel(0x10); // length of FORMAT header + l = (int32_t)htolel(0x10); // length of FORMAT header fwrite(&l, 4, 1, f); - i = htoles(0x01); // constant + i = (int16_t)htoles(0x01); // constant fwrite(&i, 2, 1, f); - i = htoles(0x02); // channels + i = (int16_t)htoles(0x02); // channels fwrite(&i, 2, 1, f); - l = htolel(44100); // sample rate + l = (int32_t)htolel(44100); // sample rate fwrite(&l, 4, 1, f); - l = htolel(44100 * 4); // bytes per second + l = (int32_t)htolel(44100 * 4); // bytes per second fwrite(&l, 4, 1, f); - i = htoles(4); // bytes per sample + i = (int16_t)htoles(4); // bytes per sample fwrite(&i, 2, 1, f); - i = htoles(2*8); // bits per channel + i = (int16_t)htoles(2*8); // bits per channel fwrite(&i, 2, 1, f); // DATA header fputs("data", f); - l = htolel(reallen); + l = (int32_t)htolel((uint32_t)reallen); fwrite(&l, 4, 1, f); } @@ -351,7 +359,7 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) } } } - if (fwrite(&buf[track->bstart], track->bsize, 1, f) < 1) { + if (fwrite(&buf[track->bstart], sizeof(track->bsize), 1, f) < 1) { fprintf(stderr, " Could not write to track: %s\n", strerror(errno)); exit(4); } @@ -360,13 +368,13 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) realsz += track->bsize; if (((sz / SECTLEN) % 500) == 0) { fl = (float)realsz / (float)reallen; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4ld/%-4ld MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(fl, 20), fl * 100); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4" PRId32 "/%-4" PRId32 " MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(fl, 20), fl * 100); fflush(stdout); } } fl = (float)realsz / (float)reallen; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4ld/%-4ld MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(1, 20), fl * 100); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4" PRId32 "/%-4" PRId32 " MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(1, 20), fl * 100); fflush(stdout); if (ferror(bf)) { @@ -395,31 +403,46 @@ int main(int argc, char **argv) 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; 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 { + if (!((mergef = fopen(binfile, "w+bx")))) { + fprintf(stderr, "Could not open merged BIN %s: %s\n",binfile,strerror(errno)); + return 2; + } } if (!((cuef = fopen(cuefile, "r")))) { fprintf(stderr, "Could not open CUE %s: %s\n", cuefile, strerror(errno)); return 2; } + + if (verbose) + printf("Path to BIN/CUE files:%s\n",bindir); - printf("Reading the CUE file:\n"); - + /* This unnecessarily ate the first line of the cue file + * which is usually used for file definitions. removed this + * to handle file descriptions correctly in -m (merge) mode + */ /* We don't really care about the first line. */ - if (!fgets(s, CUELLEN, cuef)) { + /*if (!fgets(s, CUELLEN, cuef)) { fprintf(stderr, "Could not read first line from %s: %s\n", cuefile, strerror(errno)); return 3; } - + */ + while (fgets(s, CUELLEN, cuef)) { while ((p = strchr(s, '\r')) || (p = strchr(s, '\n'))) *p = '\0'; @@ -472,31 +495,122 @@ 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 (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)) { + 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; + + // add commands to create an additional track here + // same configuration as this track + + } + } 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"); printf("Writing tracks:\n\n"); - for (track = tracks; (track); track = track->next) - writetrack(binf, track, basefile); - - fclose(binf); + for (track = tracks; (track); track = track->next) { + if (merge) { + writetrack(mergef, track, basefile); + } else { + writetrack(binf, track, basefile); + } + } + + if (merge) { + fclose(mergef); + } else { + fclose(binf); + } fclose(cuef); return 0; From b0cb4b01ce03197f1554c7839cc88e79f096cf00 Mon Sep 17 00:00:00 2001 From: jakemkz Date: Sun, 1 Feb 2026 15:37:17 -0600 Subject: [PATCH 2/6] Update README 1.3.0 --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 8079cd8..0504bea 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: From cd212c6c1ab60672e03d4adfe4b5677e2b723cee Mon Sep 17 00:00:00 2001 From: jakemkz Date: Sun, 1 Feb 2026 20:48:35 -0600 Subject: [PATCH 3/6] fixed issue with writing tracks --- Makefile | 2 +- bchunk.c | 37 ++++++++++++++++++------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index b1f5458..9b9083b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # For systems with GCC (Linux, and others with GCC installed): CC = gcc LD = gcc -CFLAGS = -Wall -Wstrict-prototypes -O2 -Wextra -pedantic -Werror +CFLAGS = -Wall -Wstrict-prototypes -O2 CFLAGS_PED = -Wall -Wstrict-prototypes -O2 -g -Wextra -pedantic -Werror -Wformat -Wconversion -Wstrict-aliasing -Wundef -Wshadow -Wsign-conversion -fstrict-overflow all: bchunk diff --git a/bchunk.c b/bchunk.c index 7f4c6fa..d96d406 100644 --- a/bchunk.c +++ b/bchunk.c @@ -277,7 +277,7 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) char *fname; FILE *f; char buf[SECTLEN+10]; - int32_t sz, sect, realsz, reallen; + long sz, sect, realsz, reallen; char c, *p, *p2, *ep; int32_t l; int16_t i; @@ -290,23 +290,22 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) printf("%2d: %s ", track->num, fname); - if (!(f = fopen(fname, "w"))) { + if (!(f = fopen(fname, "wb"))) { fprintf(stderr, " Could not fopen track file: %s\n", strerror(errno)); exit(4); } - if (fseek(bf, track->start, SEEK_SET)) { fprintf(stderr, " Could not fseek to track location: %s\n", strerror(errno)); exit(4); } - reallen = (int32_t)(track->stopsect - track->startsect + 1) * track->bsize; + reallen = (track->stopsect - track->startsect + 1) * track->bsize; if (verbose) { - printf("\n mmc sectors %" PRId32 "->%" PRId32 " (%" PRId32 ")", track->startsect, track->stopsect, track->stopsect - track->startsect + 1); - printf("\n mmc bytes %" PRId32 "->%" PRId32 " (%" PRId32 ")", 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 %" PRId32 " bytes", (track->stopsect - track->startsect + 1) * track->bsize); + printf("\n real data %d bytes", (track->stopsect - track->startsect + 1) * track->bsize); printf("\n"); } @@ -315,28 +314,28 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) if ((track->audio) && (towav)) { // RIFF header fputs("RIFF", f); - l = (int32_t)htolel((uint32_t)reallen + WAV_DATA_HLEN + WAV_FORMAT_HLEN + 4); + l = htolel(reallen + WAV_DATA_HLEN + WAV_FORMAT_HLEN + 4); fwrite(&l, 4, 1, f); // length of file, starting from WAVE fputs("WAVE", f); // FORMAT header fputs("fmt ", f); - l = (int32_t)htolel(0x10); // length of FORMAT header + l = htolel(0x10); // length of FORMAT header fwrite(&l, 4, 1, f); - i = (int16_t)htoles(0x01); // constant + i = htoles(0x01); // constant fwrite(&i, 2, 1, f); - i = (int16_t)htoles(0x02); // channels + i = htoles(0x02); // channels fwrite(&i, 2, 1, f); - l = (int32_t)htolel(44100); // sample rate + l = htolel(44100); // sample rate fwrite(&l, 4, 1, f); - l = (int32_t)htolel(44100 * 4); // bytes per second + l = htolel(44100 * 4); // bytes per second fwrite(&l, 4, 1, f); - i = (int16_t)htoles(4); // bytes per sample + i = htoles(4); // bytes per sample fwrite(&i, 2, 1, f); - i = (int16_t)htoles(2*8); // bits per channel + i = htoles(2*8); // bits per channel fwrite(&i, 2, 1, f); // DATA header fputs("data", f); - l = (int32_t)htolel((uint32_t)reallen); + l = htolel(reallen); fwrite(&l, 4, 1, f); } @@ -359,7 +358,7 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) } } } - if (fwrite(&buf[track->bstart], sizeof(track->bsize), 1, f) < 1) { + if (fwrite(&buf[track->bstart], track->bsize, 1, f) < 1) { fprintf(stderr, " Could not write to track: %s\n", strerror(errno)); exit(4); } @@ -368,13 +367,13 @@ int writetrack(FILE *bf, struct track_t *track, char *bname) realsz += track->bsize; if (((sz / SECTLEN) % 500) == 0) { fl = (float)realsz / (float)reallen; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4" PRId32 "/%-4" PRId32 " MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(fl, 20), fl * 100); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4ld/%-4ld MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(fl, 20), fl * 100); fflush(stdout); } } fl = (float)realsz / (float)reallen; - printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4" PRId32 "/%-4" PRId32 " MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(1, 20), fl * 100); + printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%4ld/%-4ld MB [%s] %3.0f %%", realsz/1024/1024, reallen/1024/1024, progressbar(1, 20), fl * 100); fflush(stdout); if (ferror(bf)) { From 5e1e9785be8b538ec8282edb65f894468cc052ec Mon Sep 17 00:00:00 2001 From: jakemkz Date: Mon, 2 Feb 2026 17:05:50 -0600 Subject: [PATCH 4/6] adding feature to generate merged .cue files and bypass additional file generation --- Makefile | 19 +++++--- README | 2 +- bchunk.c | 133 +++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index 9b9083b..bf95c73 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ +all: bchunk + # For systems with GCC (Linux, and others with GCC installed): CC = gcc LD = gcc CFLAGS = -Wall -Wstrict-prototypes -O2 -CFLAGS_PED = -Wall -Wstrict-prototypes -O2 -g -Wextra -pedantic -Werror -Wformat -Wconversion -Wstrict-aliasing -Wundef -Wshadow -Wsign-conversion -fstrict-overflow - -all: bchunk +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 @@ -13,6 +13,9 @@ all: bchunk # For BSD install: Which install to use and where to put the files INSTALL = install +INSTALL_DIR = $(INSTALL) -d -m 0755 +INSTALL_DATA = $(INSTALL) -m 0644 +INSTALL_EXEC = $(INSTALL) -m 0755 PREFIX = /usr/local BIN_DIR = $(PREFIX)/bin MAN_DIR = $(PREFIX)/man @@ -20,8 +23,8 @@ MAN_DIR = $(PREFIX)/man .c.o: $(CC) $(CFLAGS) -c $< -debug: CFLAGS = $(CFLAGS_PED) -debug: bchunk +debug: CFLAGS = $(CFLAGS_PED) +debug: bchunk clean: rm -f *.o *~ *.bak core @@ -30,9 +33,11 @@ distclean: clean install: installbin installman installbin: - $(INSTALL) -m 755 -s -o root -g root bchunk $(BIN_DIR) + $(INSTALL_DIR) $(DESTDIR)$(BIN_DIR) + $(INSTALL_EXEC) -s bchunk $(DESTDIR)$(BIN_DIR) installman: - $(INSTALL) -m 644 -o bin -g bin bchunk.1 $(MAN_DIR)/man1 + $(INSTALL_DIR) $(DESTDIR)$(MAN_DIR)/man1 + $(INSTALL_DATA) bchunk.1 $(DESTDIR)$(MAN_DIR)/man1 BITS = bchunk.o diff --git a/README b/README index 0504bea..2e0b2f5 100644 --- a/README +++ b/README @@ -52,7 +52,7 @@ Colas Nahaboo and Godmar Back added support for MODE2/2352 ISO data tracks in bchunk 1.1.0. Matthew Green implemented the -r option for raw MODE2/2352 - exctraction for bchunk 1.2.0. + extraction for bchunk 1.2.0. --- diff --git a/bchunk.c b/bchunk.c index d96d406..0dcf35e 100644 --- a/bchunk.c +++ b/bchunk.c @@ -37,6 +37,9 @@ " -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" \ @@ -62,7 +65,11 @@ */ #include +#ifdef _WIN32 +#include +#else #include +#endif #define bswap_16(x) \ ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)) @@ -101,6 +108,7 @@ int raw = 0; int swabaudio = 0; int towav = 0; int merge = 0; +int nowrite = 0; /* * Parse arguments @@ -137,40 +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]); - cuefile_copy = strdup(cuefile); - bindir = dirname(cuefile_copy); - 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 */ -int32_t 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; @@ -189,6 +216,16 @@ int32_t 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 */ @@ -398,6 +435,8 @@ 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; @@ -405,11 +444,12 @@ int main(int argc, char **argv) int32_t nextsectoroffset = 0; int32_t prevsectoroffset = 0; - FILE *binf = NULL, *cuef = NULL, *mergef = NULL; - + FILE *binf = NULL, *cuef = NULL, *mergef = NULL, *mergecf = NULL; + printf("%s", VERSTR); parse_args(argc, argv); + if (!merge) { if (!((binf = fopen(binfile, "r")))) { @@ -417,10 +457,25 @@ int main(int argc, char **argv) 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")))) { @@ -446,6 +501,9 @@ int main(int argc, char **argv) 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 track line as-is + if ((p = strstr(s, "TRACK"))) { printf("\nTrack "); if (!(p = strchr(p, ' '))) { @@ -494,6 +552,11 @@ int main(int argc, char **argv) *t = '\0'; t++; printf(" %s %s", p, t); + + 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; @@ -595,23 +658,25 @@ int main(int argc, char **argv) printf("\n\n"); - - printf("Writing tracks:\n\n"); - for (track = tracks; (track); track = track->next) { - if (merge) { - writetrack(mergef, track, basefile); - } else { - writetrack(binf, track, basefile); + 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); + } } - } + } + if (merge) { fclose(mergef); } else { fclose(binf); } fclose(cuef); - + return 0; } From 32fcd0d3a965a70034482d5058e97ab97fe5b759 Mon Sep 17 00:00:00 2001 From: jakemkz Date: Mon, 2 Feb 2026 18:30:21 -0600 Subject: [PATCH 5/6] added missing fclose for merged cue file --- bchunk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bchunk.c b/bchunk.c index 0dcf35e..8680fee 100644 --- a/bchunk.c +++ b/bchunk.c @@ -672,6 +672,7 @@ int main(int argc, char **argv) if (merge) { fclose(mergef); + fclose(mergecf); } else { fclose(binf); } From b58a6f7565fa87ccb18c448b197ec455bdc79945 Mon Sep 17 00:00:00 2001 From: jakemkz Date: Sat, 14 Feb 2026 22:04:38 -0600 Subject: [PATCH 6/6] minor updates to documentation --- Makefile | 2 +- README | 2 +- bchunk.c | 22 +++++++--------------- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index bf95c73..0df0d65 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ installman: BITS = bchunk.o bchunk: $(BITS) - $(LD) $(LDFLAGS) -o bchunk $(BITS) + $(LD) -o bchunk $(BITS) $(LDFLAGS) bchunk.o: bchunk.c diff --git a/README b/README index 2e0b2f5..f5a36a5 100644 --- a/README +++ b/README @@ -144,7 +144,7 @@ the audio tracks. If the audio sounds like loud static noise, try this. - The -m flag make binchunker use file names provided in the .cue + 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.c b/bchunk.c index 8680fee..8987de5 100644 --- a/bchunk.c +++ b/bchunk.c @@ -483,26 +483,18 @@ int main(int argc, char **argv) return 2; } - if (verbose) + if (verbose) { + printf("Reading the CUE file\n"); printf("Path to BIN/CUE files:%s\n",bindir); - - /* This unnecessarily ate the first line of the cue file - * which is usually used for file definitions. removed this - * to handle file descriptions correctly in -m (merge) mode - */ - /* 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; } - */ + // 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 track line as-is + fprintf(mergecf, "%s\n",s); // copy all lines as-is except FILE/INDEX if ((p = strstr(s, "TRACK"))) { printf("\nTrack "); @@ -567,6 +559,9 @@ int main(int argc, char **argv) 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); @@ -592,9 +587,6 @@ int main(int argc, char **argv) track->bstart = prevtrack->bstart; track->startsect = track->stopsect = -1; - // add commands to create an additional track here - // same configuration as this track - } } else if ((p = strstr(s, "FILE"))) { // detected a bin file descriptor