aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2020-05-07 04:42:34 -0400
committerB. Watson <yalhcru@gmail.com>2020-05-07 04:42:34 -0400
commitf8a072dfc63065a134268fbc0a75a1eff02294fe (patch)
tree3f8b613fc3f44620a4f9352c7d6c909aedca3e34
parent1aba77a39dea8fe7372ca6406eb870e61e27941a (diff)
downloadmiragextract-f8a072dfc63065a134268fbc0a75a1eff02294fe.tar.gz
add a bunch of options, make sndfile work
-rw-r--r--Makefile19
-rw-r--r--README70
-rw-r--r--miragextract.c187
3 files changed, 230 insertions, 46 deletions
diff --git a/Makefile b/Makefile
index 6e9e402..96a0566 100644
--- a/Makefile
+++ b/Makefile
@@ -15,5 +15,22 @@ LDFLAGS=$(LDEXTRA) $(MIRAGE_LIBS) $(SNDFILE_LIBS)
all: $(PROJ)
+man: $(PROJ).1
+
+html: $(PROJ).html
+
test: all
- ./$(PROJ)
+ ./$(PROJ) test.cue
+
+push: all man html
+ git add $(PROJ).1 $(PROJ).html
+ git commit -m'auto-regenerate man/html pages'
+ git push
+
+$(PROJ).1: $(PROJ).rst
+ rst2man.py $(PROJ).rst > $(PROJ).1
+
+$(PROJ).html: $(PROJ).rst
+ rst2html4.py $(PROJ).rst > $(PROJ).html
+
+.PHONY: all man html test push
diff --git a/README b/README
index 97fa814..eb3bf81 100644
--- a/README
+++ b/README
@@ -1,20 +1,68 @@
-miragextract
+README for miragextract v0.0.1
+==============================
+Description
+-----------
Extracts data and audio tracks from any CD image supported by libmirage.
+Data tracks are written as-is, and audio tracks can be written as-is
+or converted to wav, flag, or ogg/vorbis (via libsndfile).
-One day, it will work like this:
+Installing
+----------
+Requires GNU make, libmirage >= 3.0.0 and libsndfile (tested with 1.0.26,
+other recent-ish versions should work).
-Usage: miragextract [-t track] [-b basename] [-f cdda|wav|ogg|flac] image-file
+To build, run "make" or "gmake". There is no "make install", just copy the
+miragextract binary to /usr/local/bin and the man page (miragextract.1)
+to /usr/local/share/man/man1, or whatever paths make sense on your OS.
+There's also a miragextract.html, if you prefer your documentation
+that way.
--t takes a track number, and extracts only that one track. Default
-behaviour is to extract all tracks.
+Supported formats
+-----------------
+According to the README from libmirage-3.2.4:
--b sets the basename for the output files. Default is 'track'.
+> Currently supported image formats:
+> - BlindWrite 5/6 (B5T, B6T) file format (readonly)
+> - Roxio / WinOnCD (C2D) file format (readonly)
+> - CloneCD (CCD, SUB, IMG) image format (readonly)
+> - DiscJuggler (CDI) file format (readonly)
+> - Easy CD Creator (CIF) file format (readonly)
+> - CDRwin (CUE, BIN) image format (readonly)
+> - Raw track loader (ISO, UDF etc.) image format (read-write)
+> - Alcohol 120% (MDS) image format (readonly)
+> - Daemon Tools (MDX) image format (readonly)
+> - Nero Burning ROM (NRG) image format (readonly)
+> - ReadCD (TOC, BIN) image format (readonly)
+> - Cdrdao's (TOC, BIN) image format (read-write)
+> - XCDRoast (TOC, BIN) image format (readonly)
--f sets the format and filename extension for the output files. Default is
-'wav'. "cdda" files are raw CD audio samples.
+> Currently supported filter streams:
+> - Apple Disk Image (DMG) container format (readonly)
+> - Apple Disk Image (IMG, SMI) via MacBinary container format (readonly)
+> - GZip (GZ) container format (readonly)
+> - XZ (XZ) container format (readonly)
+> - Compressed ISO (CSO) container format (readonly)
+> - Compressed ISO (ISZ) container format (readonly)
+> - Error Code Modeller (ECM) container format (readonly)
+> - PowerISO (DAA) image format (readonly)
+> - SNDFILE audio files (read-write)
-Output files will be named e.g. track01.wav, track02.wav.
+Not all these formats have been tested with miragextract. I have tested
+bin/cue (both single-bin and multi-bin), mds, ecm, nrg, and ccd as of
+this writing, and all work fine.
-Requires libmirage >= 3.0.0 and libsndfile (tested with 1.0.26, other
-versions should work).
+Author and Copyright
+--------------------
+miragextract was written by B. Watson <yalchru@gmail.com>, and is released
+under the WTFPL: Do WTF you want with this.
+
+Support
+-------
+None guaranteed. If you run into problems, first try the latest code
+from git, which you can find at:
+
+http://urchlay.naptime.net/repos/miragextract/
+
+If this doesn't help, contact me by email (see above) and I'll see what
+I can do.
diff --git a/miragextract.c b/miragextract.c
index 2c16132..9d63476 100644
--- a/miragextract.c
+++ b/miragextract.c
@@ -1,27 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <string.h>
#include <sndfile.h>
#include <mirage/mirage.h>
+#define VERSION "0.0.1"
+
const char *self;
static MirageContext *mirage = NULL;
static MirageDisc *disc = NULL;
static MirageSession *session = NULL;
static int output_track_number = 0;
-// static int tracks;
+int total_bytes = 0;
+
+char *imagefile = NULL;
+char *outfilebase = "track";
+char *outfmt = "wav";
+int want_track = 0; /* -1 = no tracks, 0 = all, 1-99 = just that one */
+double quality = 0.7l;
+int swap_bytes = 0;
void die(char *msg) {
fprintf(stderr, "%s: %s\n", self, msg);
exit(1);
}
-void extract_audio(MirageTrack *track) {
+const guint8 *apply_s_opt(const guint8 *buf, gint len) {
+ int i;
+ static guint8 newbuf[4096];
+
+ if(!swap_bytes) return buf;
+
+ for(i = 0; i < len; i += 2) {
+ newbuf[i] = buf[i + 1];
+ newbuf[i + 1] = buf[i];
+ }
+
+ return (const guint8 *)newbuf;
}
-void extract_track(int t) {
+void extract_track(int t, int extract) {
MirageTrack *track;
MirageSector *sector;
int sector_type, sec = 0;
@@ -29,12 +50,12 @@ void extract_track(int t) {
const guint8 *buf;
char *ext;
char outfile[4096];
- FILE *out;
- SNDFILE *sfout;
+ FILE *out = NULL;
+ SNDFILE *sfout = NULL;
struct SF_INFO sfi;
- double quality = 0.7l;
+ int bytes = 0;
- output_track_number++;
+ printf(" Track %d (%d): ", t + 1, output_track_number);
if(!(track = mirage_session_get_track_by_index(session, t, NULL)))
die("can't read track");
@@ -45,14 +66,17 @@ void extract_track(int t) {
case MIRAGE_SECTOR_MODE1:
case MIRAGE_SECTOR_MODE2_FORM1:
// printf("Track %d is data, extracting\n", output_track_number);
+ printf("data, ");
ext = "iso";
break;
case MIRAGE_SECTOR_AUDIO:
// printf("Track %d is audio, extracting\n", output_track_number);
- ext = "ogg";
+ printf("audio, ");
+ ext = outfmt;
break;
default:
// printf("Track %d is unsupported type, dumping raw\n", output_track_number);
+ printf("<unknown>, ");
ext = "raw";
break;
}
@@ -64,56 +88,142 @@ void extract_track(int t) {
printf("got %d bytes per sector\n", len);
*/
- sprintf(outfile, "track%02d.%s", output_track_number, ext);
-
- if(sector_type == MIRAGE_SECTOR_AUDIO) {
- sfi.samplerate = 44100;
- sfi.channels = 2;
- sfi.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
- if(!(sfout = sf_open(outfile, SFM_WRITE, &sfi)))
- die("can't open output audio file");
- sf_command(sfout, SFC_SET_VBR_ENCODING_QUALITY, &quality, sizeof(double));
- } else {
- if(!(out = fopen(outfile, "wb"))) {
- die("can't open output file");
+ sprintf(outfile, "%s%02d.%s", outfilebase, output_track_number, ext);
+
+ if(extract) {
+ if((sector_type == MIRAGE_SECTOR_AUDIO) && (outfmt[0] != 'c')) {
+ sfi.samplerate = 44100;
+ sfi.channels = 2;
+
+ switch(outfmt[0]) {
+ case 'o':
+ sfi.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
+ break;
+ case 'f':
+ sfi.format = SF_FORMAT_FLAC | SF_FORMAT_PCM_16;
+ break;
+ case 'w':
+ sfi.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ break;
+ }
+
+ if(!(sfout = sf_open(outfile, SFM_WRITE, &sfi)))
+ die("can't open output audio file");
+ sf_command(sfout, SFC_SET_VBR_ENCODING_QUALITY, &quality, sizeof(double));
+
+ } else {
+ if(!(out = fopen(outfile, "wb"))) {
+ die("can't open output file");
+ }
}
}
sec = mirage_track_get_track_start(track);
while((sector = mirage_track_get_sector(track, sec++, 0, NULL))) {
mirage_sector_get_data(sector, &buf, &len, NULL);
- if(sector_type == MIRAGE_SECTOR_AUDIO) {
- /* TODO: find out what happens on big-endian platform! */
- sf_write_short(sfout, (const short *)buf, len / 2);
- } else {
- fwrite(buf, len, 1, out);
+ bytes += len;
+ if(extract) {
+ if(sfout) {
+ sf_write_short(sfout, (const short *)apply_s_opt(buf, len), len / 2);
+ } else {
+ fwrite(buf, len, 1, out);
+ }
}
g_object_unref(sector);
}
- if(sector_type == MIRAGE_SECTOR_AUDIO) {
- sf_close(sfout);
- } else {
- fclose(out);
- }
+ if(sfout) sf_close(sfout);
+ if(out) fclose(out);
- printf("read %d sectors\n", sec);
+ printf("%d bytes (%d sectors)\n", bytes, sec);
+ if(extract) printf(" Extracted to %s\n", outfile);
+ total_bytes += bytes;
return;
}
+void usage(int exit_code) {
+ fprintf(exit_code ? stderr : stdout,
+ "miragextract v" VERSION " by B. Watson, WTFPL\n"
+ "Usage: %s [-l] [-s] [-t track] [-b base] [-f fmt ] [-q quality ] image-file\n"
+ "See man page for details\n", self);
+ exit(exit_code);
+}
+
+void parse_args(int argc, char **argv) {
+ if(argc == 1) usage(0);
+ if(argc > 1 && strcmp(argv[1], "--help") == 0) usage(0);
+
+ while(++argv, --argc) {
+ if(argv[0][0] == '-') {
+ char *nextarg = argv[1];
+ switch(argv[0][1]) {
+ case 'l':
+ want_track = -1;
+ break;
+ case 's':
+ swap_bytes = 1;
+ break;
+ case 't':
+ if(nextarg) {
+ argv++, argc--;
+ want_track = atoi(nextarg);
+ if(want_track < 1 || want_track > 99)
+ die("invalid track number for -t (must be 1-99)");
+ } else
+ die("-t option requires track argument");
+ break;
+ case 'b':
+ if(nextarg) {
+ argv++, argc--;
+ outfilebase = nextarg;
+ } else
+ die("-b option requires base filename argument");
+ break;
+ case 'f':
+ if(nextarg) {
+ argv++, argc--;
+ outfmt = nextarg;
+ } else
+ die("-f option requires format argument");
+ break;
+ case 'q':
+ if(nextarg) {
+ argv++, argc--;
+ quality = (double)atoi(nextarg) / 10.0l;
+ if(quality < 0.0l || quality > 1.0l)
+ die("invalid quality for -q (must be 1-10)");
+ } else
+ die("-t option requires track argument");
+ break;
+ default:
+ die("unrecognized option");
+ break;
+ }
+ } else if(imagefile) {
+ die("too many arguments on command line");
+ } else {
+ imagefile = *argv;
+ }
+ }
+
+ if(!imagefile) die("missing image file argument");
+}
+
int main(int argc, char **argv) {
self = argv[0];
gchar *fn[2];
int s, t;
+ parse_args(argc, argv);
+
if(!((mirage = g_object_new(MIRAGE_TYPE_CONTEXT, NULL))))
die("couldn't get mirage context");
if(!mirage_initialize(NULL))
die("couldn't initialize libmirage");
- fn[0] = "test.cue";
+ fn[0] = imagefile;
fn[1] = NULL;
if(!(disc = mirage_context_load_image(mirage, fn, NULL)))
@@ -123,12 +233,21 @@ int main(int argc, char **argv) {
if(!(session = mirage_disc_get_session_by_index(disc, s, NULL)))
die("couldn't read session");
+ printf("Session %d\n", s + 1);
+
for(t = 0; t < mirage_session_get_number_of_tracks(session); t++) {
- printf("session %d, track %d\n", s, t);
- extract_track(t);
+ int extract = 0;
+ output_track_number++;
+
+ // printf("session %d, track %d\n", s + 1, t + 1);
+ if(want_track == 0 || want_track == output_track_number)
+ extract = 1;
+
+ extract_track(t, extract);
}
}
mirage_shutdown(NULL);
+ printf("Total size: %d bytes\n", total_bytes);
return 0;
}