diff options
-rw-r--r-- | Makefile | 19 | ||||
-rw-r--r-- | README | 70 | ||||
-rw-r--r-- | miragextract.c | 187 |
3 files changed, 230 insertions, 46 deletions
@@ -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 @@ -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; } |