add support for RF64
[platform/upstream/flac.git] / src / flac / main.c
index a8e4690..6f29d01 100644 (file)
@@ -1,5 +1,5 @@
 /* flac - Command-line FLAC encoder/decoder
- * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson
+ * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008  Josh Coalson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -57,8 +57,6 @@
 #  include "share/getopt.h"
 #endif
 
-typedef enum { RAW, WAV, AIF, FLAC, OGGFLAC } FileFormat;
-
 static int do_it(void);
 
 static FLAC__bool init_options(void);
@@ -112,6 +110,7 @@ static struct share__option long_options_[] = {
        { "warnings-as-errors"    , share__no_argument, 0, 'w' },
        { "force"                 , share__no_argument, 0, 'f' },
        { "delete-input-file"     , share__no_argument, 0, 0 },
+       { "preserve-modtime"      , share__no_argument, 0, 0 },
        { "keep-foreign-metadata" , share__no_argument, 0, 0 },
        { "output-prefix"         , share__required_argument, 0, 0 },
        { "output-name"           , share__required_argument, 0, 'o' },
@@ -148,11 +147,12 @@ static struct share__option long_options_[] = {
        { "fast"                      , share__no_argument, 0, '0' },
        { "verify"                    , share__no_argument, 0, 'V' },
        { "force-aiff-format"         , share__no_argument, 0, 0 },
+       { "force-rf64-format"         , share__no_argument, 0, 0 },
        { "force-raw-format"          , share__no_argument, 0, 0 },
        { "lax"                       , share__no_argument, 0, 0 },
        { "replay-gain"               , share__no_argument, 0, 0 },
        { "ignore-chunk-sizes"        , share__no_argument, 0, 0 },
-       { "sector-align"              , share__no_argument, 0, 0 },
+       { "sector-align"              , share__no_argument, 0, 0 }, /* DEPRECATED */
        { "seekpoint"                 , share__required_argument, 0, 'S' },
        { "padding"                   , share__required_argument, 0, 'P' },
 #if FLAC__HAS_OGG
@@ -184,6 +184,7 @@ static struct share__option long_options_[] = {
        /*
         * negatives
         */
+       { "no-preserve-modtime"       , share__no_argument, 0, 0 },
        { "no-decode-through-errors"  , share__no_argument, 0, 0 },
        { "no-silent"                 , share__no_argument, 0, 0 },
        { "no-force"                  , share__no_argument, 0, 0 },
@@ -192,7 +193,7 @@ static struct share__option long_options_[] = {
        { "no-keep-foreign-metadata"  , share__no_argument, 0, 0 },
        { "no-replay-gain"            , share__no_argument, 0, 0 },
        { "no-ignore-chunk-sizes"     , share__no_argument, 0, 0 },
-       { "no-sector-align"           , share__no_argument, 0, 0 },
+       { "no-sector-align"           , share__no_argument, 0, 0 }, /* DEPRECATED */
        { "no-utf8-convert"           , share__no_argument, 0, 0 },
        { "no-lax"                    , share__no_argument, 0, 0 },
 #if FLAC__HAS_OGG
@@ -241,8 +242,10 @@ static struct {
        long serial_number; /* this is the Ogg serial number and is unused for native FLAC */
        FLAC__bool force_to_stdout;
        FLAC__bool force_aiff_format;
+       FLAC__bool force_rf64_format;
        FLAC__bool force_raw_format;
        FLAC__bool delete_input;
+       FLAC__bool preserve_modtime;
        FLAC__bool keep_foreign_metadata;
        FLAC__bool replay_gain;
        FLAC__bool ignore_chunk_sizes;
@@ -289,7 +292,7 @@ static struct {
  * miscellaneous globals
  */
 
-static FLAC__int32 align_reservoir_0[588], align_reservoir_1[588]; /* for carrying over samples from --sector-align */
+static FLAC__int32 align_reservoir_0[588], align_reservoir_1[588]; /* for carrying over samples from --sector-align */ /* DEPRECATED */
 static FLAC__int32 *align_reservoir[2] = { align_reservoir_0, align_reservoir_1 };
 static unsigned align_reservoir_samples = 0; /* 0 .. 587 */
 
@@ -377,8 +380,8 @@ int do_it(void)
                        if(!FLAC__format_sample_rate_is_valid(option_values.format_sample_rate))
                                return usage_error("ERROR: invalid sample rate '%u', must be > 0 and <= %u\n", option_values.format_sample_rate, FLAC__MAX_SAMPLE_RATE);
                }
-               if(option_values.force_raw_format && option_values.force_aiff_format)
-                       return usage_error("ERROR: only one of --force-raw-format and --force-aiff-format allowed\n");
+               if((option_values.force_raw_format?1:0) + (option_values.force_aiff_format?1:0) + (option_values.force_rf64_format?1:0) > 1)
+                       return usage_error("ERROR: only one of --force-raw-format/--force-aiff-format/--force-rf64-format allowed\n");
                if(option_values.mode_decode) {
                        if(!option_values.force_raw_format) {
                                if(option_values.format_is_big_endian >= 0)
@@ -472,7 +475,7 @@ int do_it(void)
        }
 
        flac__utils_printf(stderr, 2, "\n");
-       flac__utils_printf(stderr, 2, "flac %s, Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson\n", FLAC__VERSION_STRING);
+       flac__utils_printf(stderr, 2, "flac %s, Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008  Josh Coalson\n", FLAC__VERSION_STRING);
        flac__utils_printf(stderr, 2, "flac comes with ABSOLUTELY NO WARRANTY.  This is free software, and you are\n");
        flac__utils_printf(stderr, 2, "welcome to redistribute it under certain conditions.  Type `flac' for details.\n\n");
 
@@ -528,7 +531,7 @@ int do_it(void)
                                                flac__utils_printf(stderr, 1, "internal error\n");
                                                return 2;
                                        }
-                                       if(0 != (error = grabbag__replaygain_store_to_file_album(outfilename, album_gain, album_peak, /*preserve_modtime=*/true))) {
+                                       if(0 != (error = grabbag__replaygain_store_to_file_album(outfilename, album_gain, album_peak, option_values.preserve_modtime))) {
                                                flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain album tags (%s)\n", outfilename, error);
                                                retval = 1;
                                        }
@@ -562,8 +565,10 @@ FLAC__bool init_options(void)
        option_values.serial_number = 0;
        option_values.force_to_stdout = false;
        option_values.force_aiff_format = false;
+       option_values.force_rf64_format = false;
        option_values.force_raw_format = false;
        option_values.delete_input = false;
+       option_values.preserve_modtime = true;
        option_values.keep_foreign_metadata = false;
        option_values.replay_gain = false;
        option_values.ignore_chunk_sizes = false;
@@ -662,6 +667,9 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                else if(0 == strcmp(long_option, "delete-input-file")) {
                        option_values.delete_input = true;
                }
+               else if(0 == strcmp(long_option, "preserve-modtime")) {
+                       option_values.preserve_modtime = true;
+               }
                else if(0 == strcmp(long_option, "keep-foreign-metadata")) {
                        option_values.keep_foreign_metadata = true;
                }
@@ -755,6 +763,9 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                else if(0 == strcmp(long_option, "force-aiff-format")) {
                        option_values.force_aiff_format = true;
                }
+               else if(0 == strcmp(long_option, "force-rf64-format")) {
+                       option_values.force_rf64_format = true;
+               }
                else if(0 == strcmp(long_option, "force-raw-format")) {
                        option_values.force_raw_format = true;
                }
@@ -768,6 +779,8 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                        option_values.ignore_chunk_sizes = true;
                }
                else if(0 == strcmp(long_option, "sector-align")) {
+                       flac__utils_printf(stderr, 1, "WARNING: --sector-align is DEPRECATED and may not exist in future versions of flac.\n");
+                       flac__utils_printf(stderr, 1, "         shntool provides similar functionality\n");
                        option_values.sector_align = true;
                }
 #if FLAC__HAS_OGG
@@ -818,6 +831,9 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                /*
                 * negatives
                 */
+               else if(0 == strcmp(long_option, "no-preserve-modtime")) {
+                       option_values.preserve_modtime = false;
+               }
                else if(0 == strcmp(long_option, "no-decode-through-errors")) {
                        option_values.continue_through_decode_errors = false;
                }
@@ -1134,7 +1150,7 @@ static void usage_header(void)
 {
        printf("===============================================================================\n");
        printf("flac - Command-line FLAC encoder/decoder version %s\n", FLAC__VERSION_STRING);
-       printf("Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson\n");
+       printf("Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008  Josh Coalson\n");
        printf("\n");
        printf("This program is free software; you can redistribute it and/or\n");
        printf("modify it under the terms of the GNU General Public License\n");
@@ -1161,6 +1177,9 @@ static void usage_summary(void)
        printf("  Testing: flac -t [<general-options>] [FLACFILE [...]]\n");
        printf("Analyzing: flac -a [<general-options>] [<analysis-options>] [FLACFILE [...]]\n");
        printf("\n");
+       printf("Be sure to read the list of known bugs at:\n");
+       printf("http://flac.sourceforge.net/documentation_bugs.html\n");
+       printf("\n");
 }
 
 void short_usage(void)
@@ -1170,6 +1189,9 @@ void short_usage(void)
        printf("This is the short help; for all options use 'flac --help'; for even more\n");
        printf("instructions use 'flac --explain'\n");
        printf("\n");
+       printf("Be sure to read the list of known bugs at:\n");
+       printf("http://flac.sourceforge.net/documentation_bugs.html\n");
+       printf("\n");
        printf("To encode:\n");
        printf("  flac [-#] [INPUTFILE [...]]\n");
        printf("\n");
@@ -1202,6 +1224,7 @@ void show_help(void)
        printf("  -o, --output-name=FILENAME   Force the output file name\n");
        printf("      --output-prefix=STRING   Prepend STRING to output names\n");
        printf("      --delete-input-file      Deletes after a successful encode/decode\n");
+       printf("      --preserve-modtime       Output files keep timestamp of input (default)\n");
        printf("      --keep-foreign-metadata  Save/restore WAVE or AIFF non-audio chunks\n");
        printf("      --skip={#|mm:ss.ss}      Skip the given initial samples for each input\n");
        printf("      --until={#|[+|-]mm:ss.ss}  Stop at the given sample for each input file\n");
@@ -1221,7 +1244,7 @@ void show_help(void)
 #if 0 /*@@@ currently undocumented */
        printf("      --ignore-chunk-sizes     Ignore data chunk sizes in WAVE/AIFF files\n");
 #endif
-       printf("      --sector-align           Align multiple files on sector boundaries\n");
+       printf("      --sector-align (DEPRECATED) Align multiple files on sector boundaries\n");
        printf("      --replay-gain            Calculate ReplayGain & store in FLAC tags\n");
        printf("      --cuesheet=FILENAME      Import cuesheet and store in CUESHEET block\n");
        printf("      --picture=SPECIFICATION  Import picture and store in PICTURE block\n");
@@ -1255,11 +1278,13 @@ void show_help(void)
        printf("      --sign={signed|unsigned} Sign of samples\n");
        printf("      --input-size=#           Size of the raw input in bytes\n");
        printf("      --force-aiff-format      Force decoding to AIFF format\n");
+       printf("      --force-rf64-format      Force decoding to RF64 format\n");
        printf("      --force-raw-format       Treat input or output as raw samples\n");
        printf("negative options:\n");
        printf("      --no-adaptive-mid-side\n");
        printf("      --no-decode-through-errors\n");
        printf("      --no-delete-input-file\n");
+       printf("      --no-preserve-modtime\n");
        printf("      --no-keep-foreign-metadata\n");
        printf("      --no-exhaustive-model-search\n");
        printf("      --no-lax\n");
@@ -1288,8 +1313,8 @@ void show_explain(void)
        usage_header();
        usage_summary();
        printf("For encoding:\n");
-       printf("  The input file(s) may be a PCM WAVE file, AIFF (or uncompressed AIFF-C)\n");
-       printf("  file, or raw samples.\n");
+       printf("  The input file(s) may be a PCM WAVE or RF64 file, AIFF (or uncompressed\n");
+       printf("  AIFF-C) file, or raw samples.\n");
        printf("  The output file(s) will be in native FLAC or Ogg FLAC format\n");
        printf("For decoding, the reverse is true.\n");
        printf("\n");
@@ -1338,13 +1363,18 @@ void show_explain(void)
        printf("                               successful encode or decode.  If there was an\n");
        printf("                               error (including a verify error) the input file\n");
        printf("                               is left intact.\n");
+       printf("      --preserve-modtime       Output files have their timestamps/permissions\n");
+       printf("                               set to match those of their inputs (this is\n");
+       printf("                               default).  Use --no-preserve-modtime to make\n");
+       printf("                               output files have the current time and default\n");
+       printf("                               permissions.\n");
        printf("      --keep-foreign-metadata  If encoding, save WAVE or AIFF non-audio chunks\n");
        printf("                               in FLAC metadata.  If decoding, restore any saved\n");
        printf("                               non-audio chunks from FLAC metadata when writing\n");
        printf("                               the decoded file.  Foreign metadata cannot be\n");
        printf("                               transcoded, e.g. WAVE chunks saved in a FLAC file\n");
        printf("                               cannot be restored when decoding to AIFF.  Input\n");
-       printf("                               and output must be regular files.\n");
+       printf("                               and output must be regular files, not stdin/out.\n");
        printf("      --skip={#|mm:ss.ss}      Skip the first # samples of each input file; can\n");
        printf("                               be used both for encoding and decoding.  The\n");
        printf("                               alternative form mm:ss.ss can be used to specify\n");
@@ -1414,7 +1444,9 @@ void show_explain(void)
        printf("                               generate bogus data chunk sizes.\n");
 #endif
        printf("      --sector-align           Align encoding of multiple CD format WAVE files\n");
-       printf("                               on sector boundaries.\n");
+       printf("                               on sector boundaries.  This option is DEPRECATED\n");
+       printf("                               and may not exist in future versions of flac.\n");
+       printf("                               shntool offers similar functionality.\n");
        printf("      --replay-gain            Calculate ReplayGain values and store them as\n");
        printf("                               FLAC tags.  Title gains/peaks will be computed\n");
        printf("                               for each file, and an album gain/peak will be\n");
@@ -1582,12 +1614,18 @@ void show_explain(void)
        printf("                               set by -o) ends with .aif or .aiff; this option\n");
        printf("                               has no effect when encoding since input AIFF is\n");
        printf("                               auto-detected.\n");
+       printf("      --force-rf64-format      Force the decoder to output RF64 format.  This\n");
+       printf("                               option is not needed if the output filename (as\n");
+       printf("                               set by -o) ends with .rf64; this option\n");
+       printf("                               has no effect when encoding since input RF64 is\n");
+       printf("                               auto-detected.\n");
        printf("      --force-raw-format       Force input (when encoding) or output (when\n");
        printf("                               decoding) to be treated as raw samples\n");
        printf("negative options:\n");
        printf("      --no-adaptive-mid-side\n");
        printf("      --no-decode-through-errors\n");
        printf("      --no-delete-input-file\n");
+       printf("      --no-preserve-modtime\n");
        printf("      --no-keep-foreign-metadata\n");
        printf("      --no-exhaustive-model-search\n");
        printf("      --no-lax\n");
@@ -1613,8 +1651,8 @@ void show_explain(void)
 void format_mistake(const char *infilename, FileFormat wrong, FileFormat right)
 {
        /* WATCHOUT: indexed by FileFormat */
-       static const char * const ff[] = { "raw", "WAVE", "AIFF", "FLAC", "Ogg FLAC" };
-       flac__utils_printf(stderr, 1, "WARNING: %s is not a %s file; treating as a %s file\n", infilename, ff[wrong], ff[right]);
+       static const char * const ff[] = { " raw", " WAVE", "n RF64", "n AIFF", "n AIFF-C", " FLAC", "n Ogg FLAC" };
+       flac__utils_printf(stderr, 1, "WARNING: %s is not a%s file; treating as a%s file\n", infilename, ff[wrong], ff[right]);
 }
 
 int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_last_file)
@@ -1622,11 +1660,10 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
        FILE *encode_infile;
        FLAC__byte lookahead[12];
        unsigned lookahead_length = 0;
-       FileFormat input_format = RAW;
-       FLAC__bool is_aifc = false;
+       FileFormat input_format = FORMAT_RAW;
        int retval;
        off_t infilesize;
-       encode_options_t common_options;
+       encode_options_t encode_options;
        const char *outfilename = get_encoded_outfilename(infilename); /* the final name of the encoded file */
        /* internal_outfilename is the file we will actually write to; it will be a temporary name if infilename==outfilename */
        char *internal_outfilename = 0; /* NULL implies 'use outfilename' */
@@ -1651,56 +1688,62 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
        if(!option_values.force_raw_format) {
                /* first set format based on name */
                if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".wav"))
-                       input_format = WAV;
+                       input_format = FORMAT_WAVE;
+               if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".rf64"))
+                       input_format = FORMAT_RF64;
                else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".aif"))
-                       input_format = AIF;
+                       input_format = FORMAT_AIFF;
                else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".aiff"))
-                       input_format = AIF;
+                       input_format = FORMAT_AIFF;
                else if(strlen(infilename) >= 5 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-5), ".flac"))
-                       input_format = FLAC;
+                       input_format = FORMAT_FLAC;
                else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".oga"))
-                       input_format = OGGFLAC;
+                       input_format = FORMAT_OGGFLAC;
                else if(strlen(infilename) >= 4 && 0 == FLAC__STRCASECMP(infilename+(strlen(infilename)-4), ".ogg"))
-                       input_format = OGGFLAC;
+                       input_format = FORMAT_OGGFLAC;
 
                /* attempt to guess the file type based on the first 12 bytes */
                if((lookahead_length = fread(lookahead, 1, 12, encode_infile)) < 12) {
-                       if(input_format != RAW) {
-                               format_mistake(infilename, input_format, RAW);
+                       /* all supported non-raw formats have at least 12 bytes of header to read */
+                       if(input_format != FORMAT_RAW) {
+                               format_mistake(infilename, input_format, FORMAT_RAW);
                                if(option_values.treat_warnings_as_errors) {
                                        conditional_fclose(encode_infile);
                                        return 1;
                                }
                        }
-                       input_format = RAW;
+                       /* force to raw */
+                       input_format = FORMAT_RAW;
                }
                else {
-                       if(!strncmp((const char *)lookahead, "ID3", 3)) {
+                       if(!memcmp(lookahead, "ID3", 3)) {
                                flac__utils_printf(stderr, 1, "ERROR: input file %s has an ID3v2 tag\n", infilename);
                                return 1;
                        }
-                       else if(!strncmp((const char *)lookahead, "RIFF", 4) && !strncmp((const char *)lookahead+8, "WAVE", 4))
-                               input_format = WAV;
-                       else if(!strncmp((const char *)lookahead, "FORM", 4) && !strncmp((const char *)lookahead+8, "AIFF", 4))
-                               input_format = AIF;
-                       else if(!strncmp((const char *)lookahead, "FORM", 4) && !strncmp((const char *)lookahead+8, "AIFC", 4)) {
-                               input_format = AIF;
-                               is_aifc = true;
-                       }
+                       else if(!memcmp(lookahead, "RIFF", 4) && !memcmp(lookahead+8, "WAVE", 4))
+                               input_format = FORMAT_WAVE;
+                       else if(!memcmp(lookahead, "RF64", 4) && !memcmp(lookahead+8, "WAVE", 4))
+                               input_format = FORMAT_RF64;
+                       else if(!memcmp(lookahead, "FORM", 4) && !memcmp(lookahead+8, "AIFF", 4))
+                               input_format = FORMAT_AIFF;
+                       else if(!memcmp(lookahead, "FORM", 4) && !memcmp(lookahead+8, "AIFC", 4))
+                               input_format = FORMAT_AIFF_C;
                        else if(!memcmp(lookahead, FLAC__STREAM_SYNC_STRING, sizeof(FLAC__STREAM_SYNC_STRING)))
-                               input_format = FLAC;
-                       /* this could be made more accurate by looking at the first packet */
+                               input_format = FORMAT_FLAC;
+                       /*@@@ this could be made more accurate by looking at the first packet to make sure it's Ogg FLAC and not, say, Ogg Vorbis.  we do catch such problems later though. */
                        else if(!memcmp(lookahead, "OggS", 4))
-                               input_format = OGGFLAC;
+                               input_format = FORMAT_OGGFLAC;
                        else {
-                               if(input_format != RAW) {
-                                       format_mistake(infilename, input_format, RAW);
+                               /* didn't find header of any supported format */
+                               if(input_format != FORMAT_RAW) {
+                                       format_mistake(infilename, input_format, FORMAT_RAW);
                                        if(option_values.treat_warnings_as_errors) {
                                                conditional_fclose(encode_infile);
                                                return 1;
                                        }
                                }
-                               input_format = RAW;
+                               /* force to raw */
+                               input_format = FORMAT_RAW;
                        }
                }
        }
@@ -1710,9 +1753,9 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                        conditional_fclose(encode_infile);
                        return usage_error("ERROR: --keep-foreign-metadata cannot be used when encoding from stdin or to stdout\n");
                }
-               if(input_format != WAV && input_format != AIF) {
+               if(input_format != FORMAT_WAVE && input_format != FORMAT_RF64 && input_format != FORMAT_AIFF && input_format != FORMAT_AIFF_C) {
                        conditional_fclose(encode_infile);
-                       return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE or AIFF input\n");
+                       return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, RF64, or AIFF input\n");
                }
        }
 
@@ -1721,7 +1764,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
         * Use grabbag__file_get_filesize() as a cheap way to check.
         */
        if(!option_values.test_only && !option_values.force_file_overwrite && strcmp(outfilename, "-") && grabbag__file_get_filesize(outfilename) != (off_t)(-1)) {
-               if(input_format == FLAC) {
+               if(input_format == FORMAT_FLAC) {
                        /* need more detailed error message when re-flac'ing to avoid confusing the user */
                        flac__utils_printf(stderr, 1,
                                "ERROR: output file %s already exists.\n\n"
@@ -1732,7 +1775,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                                outfilename
                        );
                }
-               else if(input_format == OGGFLAC) {
+               else if(input_format == FORMAT_OGGFLAC) {
                        /* need more detailed error message when re-flac'ing to avoid confusing the user */
                        flac__utils_printf(stderr, 1,
                                "ERROR: output file %s already exists.\n\n"
@@ -1750,7 +1793,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
        }
 
        if(option_values.format_input_size >= 0) {
-               if (input_format != RAW || infilesize >= 0) {
+               if (input_format != FORMAT_RAW || infilesize >= 0) {
                        flac__utils_printf(stderr, 1, "ERROR: can only use --input-size when encoding raw samples from stdin\n");
                        conditional_fclose(encode_infile);
                        return 1;
@@ -1760,18 +1803,18 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                }
        }
 
-       if(option_values.sector_align && (input_format == FLAC || input_format == OGGFLAC)) {
+       if(option_values.sector_align && (input_format == FORMAT_FLAC || input_format == FORMAT_OGGFLAC)) {
                flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input file is FLAC or Ogg FLAC\n");
                conditional_fclose(encode_infile);
                return 1;
        }
-       if(option_values.sector_align && input_format == RAW && infilesize < 0) {
+       if(option_values.sector_align && input_format == FORMAT_RAW && infilesize < 0) {
                flac__utils_printf(stderr, 1, "ERROR: can't use --sector-align when the input size is unknown\n");
                conditional_fclose(encode_infile);
                return 1;
        }
 
-       if(input_format == RAW) {
+       if(input_format == FORMAT_RAW) {
                if(option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0 || option_values.format_channels < 0 || option_values.format_bps < 0 || option_values.format_sample_rate < 0) {
                        conditional_fclose(encode_infile);
                        return usage_error("ERROR: for encoding a raw file you must specify a value for --endian, --sign, --channels, --bps, and --sample-rate\n");
@@ -1789,56 +1832,57 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                return usage_error("ERROR: --replay-gain cannot be used when encoding to Ogg FLAC yet\n");
        }
 
-       if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative) {
+       if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &encode_options.skip_specification) || encode_options.skip_specification.is_relative) {
                conditional_fclose(encode_infile);
                return usage_error("ERROR: invalid value for --skip\n");
        }
 
-       if(!flac__utils_parse_skip_until_specification(option_values.until_specification, &common_options.until_specification)) { /*@@@@ more checks: no + without --skip, no - unless known total_samples_to_{en,de}code */
+       if(!flac__utils_parse_skip_until_specification(option_values.until_specification, &encode_options.until_specification)) { /*@@@@ more checks: no + without --skip, no - unless known total_samples_to_{en,de}code */
                conditional_fclose(encode_infile);
                return usage_error("ERROR: invalid value for --until\n");
        }
        /* if there is no "--until" we want to default to "--until=-0" */
        if(0 == option_values.until_specification)
-               common_options.until_specification.is_relative = true;
+               encode_options.until_specification.is_relative = true;
 
-       common_options.verify = option_values.verify;
-       common_options.treat_warnings_as_errors = option_values.treat_warnings_as_errors;
+       encode_options.verify = option_values.verify;
+       encode_options.treat_warnings_as_errors = option_values.treat_warnings_as_errors;
 #if FLAC__HAS_OGG
-       common_options.use_ogg = option_values.use_ogg;
+       encode_options.use_ogg = option_values.use_ogg;
        /* set a random serial number if one has not yet been specified */
        if(!option_values.has_serial_number) {
                option_values.serial_number = rand();
                option_values.has_serial_number = true;
        }
-       common_options.serial_number = option_values.serial_number++;
+       encode_options.serial_number = option_values.serial_number++;
 #endif
-       common_options.lax = option_values.lax;
-       common_options.padding = option_values.padding;
-       common_options.num_compression_settings = option_values.num_compression_settings;
-       FLAC__ASSERT(sizeof(common_options.compression_settings) >= sizeof(option_values.compression_settings));
-       memcpy(common_options.compression_settings, option_values.compression_settings, sizeof(option_values.compression_settings));
-       common_options.requested_seek_points = option_values.requested_seek_points;
-       common_options.num_requested_seek_points = option_values.num_requested_seek_points;
-       common_options.cuesheet_filename = option_values.cuesheet_filename;
-       common_options.continue_through_decode_errors = option_values.continue_through_decode_errors;
-       common_options.cued_seekpoints = option_values.cued_seekpoints;
-       common_options.channel_map_none = option_values.channel_map_none;
-       common_options.is_first_file = is_first_file;
-       common_options.is_last_file = is_last_file;
-       common_options.align_reservoir = align_reservoir;
-       common_options.align_reservoir_samples = &align_reservoir_samples;
-       common_options.replay_gain = option_values.replay_gain;
-       common_options.ignore_chunk_sizes = option_values.ignore_chunk_sizes;
-       common_options.sector_align = option_values.sector_align;
-       common_options.vorbis_comment = option_values.vorbis_comment;
-       FLAC__ASSERT(sizeof(common_options.pictures) >= sizeof(option_values.pictures));
-       memcpy(common_options.pictures, option_values.pictures, sizeof(option_values.pictures));
-       common_options.num_pictures = option_values.num_pictures;
-       common_options.debug.disable_constant_subframes = option_values.debug.disable_constant_subframes;
-       common_options.debug.disable_fixed_subframes = option_values.debug.disable_fixed_subframes;
-       common_options.debug.disable_verbatim_subframes = option_values.debug.disable_verbatim_subframes;
-       common_options.debug.do_md5 = option_values.debug.do_md5;
+       encode_options.lax = option_values.lax;
+       encode_options.padding = option_values.padding;
+       encode_options.num_compression_settings = option_values.num_compression_settings;
+       FLAC__ASSERT(sizeof(encode_options.compression_settings) >= sizeof(option_values.compression_settings));
+       memcpy(encode_options.compression_settings, option_values.compression_settings, sizeof(option_values.compression_settings));
+       encode_options.requested_seek_points = option_values.requested_seek_points;
+       encode_options.num_requested_seek_points = option_values.num_requested_seek_points;
+       encode_options.cuesheet_filename = option_values.cuesheet_filename;
+       encode_options.continue_through_decode_errors = option_values.continue_through_decode_errors;
+       encode_options.cued_seekpoints = option_values.cued_seekpoints;
+       encode_options.channel_map_none = option_values.channel_map_none;
+       encode_options.is_first_file = is_first_file;
+       encode_options.is_last_file = is_last_file;
+       encode_options.align_reservoir = align_reservoir;
+       encode_options.align_reservoir_samples = &align_reservoir_samples;
+       encode_options.replay_gain = option_values.replay_gain;
+       encode_options.ignore_chunk_sizes = option_values.ignore_chunk_sizes;
+       encode_options.sector_align = option_values.sector_align;
+       encode_options.vorbis_comment = option_values.vorbis_comment;
+       FLAC__ASSERT(sizeof(encode_options.pictures) >= sizeof(option_values.pictures));
+       memcpy(encode_options.pictures, option_values.pictures, sizeof(option_values.pictures));
+       encode_options.num_pictures = option_values.num_pictures;
+       encode_options.format = input_format;
+       encode_options.debug.disable_constant_subframes = option_values.debug.disable_constant_subframes;
+       encode_options.debug.disable_fixed_subframes = option_values.debug.disable_fixed_subframes;
+       encode_options.debug.disable_verbatim_subframes = option_values.debug.disable_verbatim_subframes;
+       encode_options.debug.do_md5 = option_values.debug.do_md5;
 
        /* if infilename and outfilename point to the same file, we need to write to a temporary file */
        if(encode_infile != stdin && grabbag__file_are_same(infilename, outfilename)) {
@@ -1853,47 +1897,44 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                strcat(internal_outfilename, tmp_suffix);
        }
 
-       if(input_format == RAW) {
-               raw_encode_options_t options;
+       if(input_format == FORMAT_RAW) {
+               encode_options.format_options.raw.is_big_endian = option_values.format_is_big_endian;
+               encode_options.format_options.raw.is_unsigned_samples = option_values.format_is_unsigned_samples;
+               encode_options.format_options.raw.channels = option_values.format_channels;
+               encode_options.format_options.raw.bps = option_values.format_bps;
+               encode_options.format_options.raw.sample_rate = option_values.format_sample_rate;
 
-               options.common = common_options;
-               options.is_big_endian = option_values.format_is_big_endian;
-               options.is_unsigned_samples = option_values.format_is_unsigned_samples;
-               options.channels = option_values.format_channels;
-               options.bps = option_values.format_bps;
-               options.sample_rate = option_values.format_sample_rate;
-
-               retval = flac__encode_raw(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
+               retval = flac__encode_file(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, encode_options);
        }
-       else if(input_format == FLAC || input_format == OGGFLAC) {
-               flac_encode_options_t options;
-
-               options.common = common_options;
-
-               retval = flac__encode_flac(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options, input_format==OGGFLAC);
+       else if(input_format == FORMAT_FLAC || input_format == FORMAT_OGGFLAC) {
+               retval = flac__encode_file(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, encode_options);
        }
-       else {
-               wav_encode_options_t options;
-
-               options.common = common_options;
-               options.foreign_metadata = 0;
+       else if(input_format == FORMAT_WAVE || input_format == FORMAT_RF64 || input_format == FORMAT_AIFF || input_format == FORMAT_AIFF_C) {
+               encode_options.format_options.iff.foreign_metadata = 0;
 
-               /* read foreign metadata if requested */
+               /* initialize foreign metadata if requested */
                if(option_values.keep_foreign_metadata) {
-                       if(0 == (options.foreign_metadata = flac__foreign_metadata_new(input_format==AIF? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) {
+                       encode_options.format_options.iff.foreign_metadata =
+                               flac__foreign_metadata_new(
+                                       input_format==FORMAT_WAVE || input_format==FORMAT_RF64?
+                                               FOREIGN_BLOCK_TYPE__RIFF :
+                                               FOREIGN_BLOCK_TYPE__AIFF
+                               );
+                       if(0 == encode_options.format_options.iff.foreign_metadata) {
                                flac__utils_printf(stderr, 1, "ERROR: creating foreign metadata object\n");
                                conditional_fclose(encode_infile);
                                return 1;
                        }
                }
 
-               if(input_format == AIF)
-                       retval = flac__encode_aif(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options, is_aifc);
-               else
-                       retval = flac__encode_wav(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, options);
+               retval = flac__encode_file(encode_infile, infilesize, infilename, internal_outfilename? internal_outfilename : outfilename, lookahead, lookahead_length, encode_options);
 
-               if(options.foreign_metadata)
-                       flac__foreign_metadata_delete(options.foreign_metadata);
+               if(encode_options.format_options.iff.foreign_metadata)
+                       flac__foreign_metadata_delete(encode_options.format_options.iff.foreign_metadata);
+       }
+       else {
+               FLAC__ASSERT(0);
+               retval = 1; /* double protection */
        }
 
        if(retval == 0) {
@@ -1903,14 +1944,14 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_
                                const char *error;
                                grabbag__replaygain_get_title(&title_gain, &title_peak);
                                if(
-                                       0 != (error = grabbag__replaygain_store_to_file_reference(internal_outfilename? internal_outfilename : outfilename, /*preserve_modtime=*/true)) ||
-                                       0 != (error = grabbag__replaygain_store_to_file_title(internal_outfilename? internal_outfilename : outfilename, title_gain, title_peak, /*preserve_modtime=*/true))
+                                       0 != (error = grabbag__replaygain_store_to_file_reference(internal_outfilename? internal_outfilename : outfilename, option_values.preserve_modtime)) ||
+                                       0 != (error = grabbag__replaygain_store_to_file_title(internal_outfilename? internal_outfilename : outfilename, title_gain, title_peak, option_values.preserve_modtime))
                                ) {
                                        flac__utils_printf(stderr, 1, "%s: ERROR writing ReplayGain reference/title tags (%s)\n", outfilename, error);
                                        retval = 1;
                                }
                        }
-                       if(strcmp(infilename, "-"))
+                       if(option_values.preserve_modtime && strcmp(infilename, "-"))
                                grabbag__file_copy_metadata(infilename, internal_outfilename? internal_outfilename : outfilename);
                }
        }
@@ -1949,8 +1990,8 @@ int decode_file(const char *infilename)
 {
        int retval;
        FLAC__bool treat_as_ogg = false;
-       FileFormat output_format = WAV;
-       decode_options_t common_options;
+       FileFormat output_format = FORMAT_WAVE;
+       decode_options_t decode_options;
        const char *outfilename = get_decoded_outfilename(infilename);
 
        if(0 == outfilename) {
@@ -1968,26 +2009,31 @@ int decode_file(const char *infilename)
        }
 
        if(option_values.force_raw_format)
-               output_format = RAW;
+               output_format = FORMAT_RAW;
        else if(
                option_values.force_aiff_format ||
                (strlen(outfilename) >= 4 && 0 == FLAC__STRCASECMP(outfilename+(strlen(outfilename)-4), ".aif")) ||
                (strlen(outfilename) >= 5 && 0 == FLAC__STRCASECMP(outfilename+(strlen(outfilename)-5), ".aiff"))
        )
-               output_format = AIF;
+               output_format = FORMAT_AIFF;
+       else if(
+               option_values.force_rf64_format ||
+               (strlen(outfilename) >= 5 && 0 == FLAC__STRCASECMP(outfilename+(strlen(outfilename)-5), ".rf64"))
+       )
+               output_format = FORMAT_RF64;
        else
-               output_format = WAV;
+               output_format = FORMAT_WAVE;
 
        if(!option_values.test_only && !option_values.analyze) {
-               if(output_format == RAW && (option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0))
+               if(output_format == FORMAT_RAW && (option_values.format_is_big_endian < 0 || option_values.format_is_unsigned_samples < 0))
                        return usage_error("ERROR: for decoding to a raw file you must specify a value for --endian and --sign\n");
        }
 
        if(option_values.keep_foreign_metadata) {
                if(0 == strcmp(infilename, "-") || 0 == strcmp(outfilename, "-"))
                        return usage_error("ERROR: --keep-foreign-metadata cannot be used when decoding from stdin or to stdout\n");
-               if(output_format != WAV && output_format != AIF)
-                       return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE or AIFF output\n");
+               if(output_format != FORMAT_WAVE && output_format != FORMAT_RF64 && output_format != FORMAT_AIFF && output_format != FORMAT_AIFF_C)
+                       return usage_error("ERROR: --keep-foreign-metadata can only be used with WAVE, RF64, or AIFF output\n");
        }
 
        if(option_values.use_ogg)
@@ -2006,67 +2052,65 @@ int decode_file(const char *infilename)
        }
 #endif
 
-       if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &common_options.skip_specification) || common_options.skip_specification.is_relative)
+       if(!flac__utils_parse_skip_until_specification(option_values.skip_specification, &decode_options.skip_specification) || decode_options.skip_specification.is_relative)
                return usage_error("ERROR: invalid value for --skip\n");
 
-       if(!flac__utils_parse_skip_until_specification(option_values.until_specification, &common_options.until_specification)) /*@@@ more checks: no + without --skip, no - unless known total_samples_to_{en,de}code */
+       if(!flac__utils_parse_skip_until_specification(option_values.until_specification, &decode_options.until_specification)) /*@@@ more checks: no + without --skip, no - unless known total_samples_to_{en,de}code */
                return usage_error("ERROR: invalid value for --until\n");
        /* if there is no "--until" we want to default to "--until=-0" */
        if(0 == option_values.until_specification)
-               common_options.until_specification.is_relative = true;
+               decode_options.until_specification.is_relative = true;
 
        if(option_values.cue_specification) {
-               if(!flac__utils_parse_cue_specification(option_values.cue_specification, &common_options.cue_specification))
+               if(!flac__utils_parse_cue_specification(option_values.cue_specification, &decode_options.cue_specification))
                        return usage_error("ERROR: invalid value for --cue\n");
-               common_options.has_cue_specification = true;
+               decode_options.has_cue_specification = true;
        }
        else
-               common_options.has_cue_specification = false;
+               decode_options.has_cue_specification = false;
 
-       common_options.treat_warnings_as_errors = option_values.treat_warnings_as_errors;
-       common_options.continue_through_decode_errors = option_values.continue_through_decode_errors;
-       common_options.replaygain_synthesis_spec = option_values.replaygain_synthesis_spec;
+       decode_options.treat_warnings_as_errors = option_values.treat_warnings_as_errors;
+       decode_options.continue_through_decode_errors = option_values.continue_through_decode_errors;
+       decode_options.replaygain_synthesis_spec = option_values.replaygain_synthesis_spec;
 #if FLAC__HAS_OGG
-       common_options.is_ogg = treat_as_ogg;
-       common_options.use_first_serial_number = !option_values.has_serial_number;
-       common_options.serial_number = option_values.serial_number;
+       decode_options.is_ogg = treat_as_ogg;
+       decode_options.use_first_serial_number = !option_values.has_serial_number;
+       decode_options.serial_number = option_values.serial_number;
 #endif
-       common_options.channel_map_none = option_values.channel_map_none;
-
-       if(output_format == RAW) {
-               raw_decode_options_t options;
+       decode_options.channel_map_none = option_values.channel_map_none;
+       decode_options.format = output_format;
 
-               options.common = common_options;
-               options.is_big_endian = option_values.format_is_big_endian;
-               options.is_unsigned_samples = option_values.format_is_unsigned_samples;
+       if(output_format == FORMAT_RAW) {
+               decode_options.format_options.raw.is_big_endian = option_values.format_is_big_endian;
+               decode_options.format_options.raw.is_unsigned_samples = option_values.format_is_unsigned_samples;
 
-               retval = flac__decode_raw(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, options);
+               retval = flac__decode_file(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, decode_options);
        }
        else {
-               wav_decode_options_t options;
-
-               options.common = common_options;
-               options.foreign_metadata = 0;
+               decode_options.format_options.iff.foreign_metadata = 0;
 
-               /* read foreign metadata if requested */
+               /* initialize foreign metadata if requested */
                if(option_values.keep_foreign_metadata) {
-                       if(0 == (options.foreign_metadata = flac__foreign_metadata_new(output_format==AIF? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) {
+                       decode_options.format_options.iff.foreign_metadata =
+                               flac__foreign_metadata_new(
+                                       output_format==FORMAT_WAVE || output_format==FORMAT_RF64?
+                                               FOREIGN_BLOCK_TYPE__RIFF :
+                                               FOREIGN_BLOCK_TYPE__AIFF
+                               );
+                       if(0 == decode_options.format_options.iff.foreign_metadata) {
                                flac__utils_printf(stderr, 1, "ERROR: creating foreign metadata object\n");
                                return 1;
                        }
                }
 
-               if(output_format == AIF)
-                       retval = flac__decode_aiff(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, options);
-               else
-                       retval = flac__decode_wav(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, options);
+               retval = flac__decode_file(infilename, option_values.test_only? 0 : outfilename, option_values.analyze, option_values.aopts, decode_options);
 
-               if(options.foreign_metadata)
-                       flac__foreign_metadata_delete(options.foreign_metadata);
+               if(decode_options.format_options.iff.foreign_metadata)
+                       flac__foreign_metadata_delete(decode_options.format_options.iff.foreign_metadata);
        }
 
        if(retval == 0 && strcmp(infilename, "-")) {
-               if(strcmp(outfilename, "-"))
+               if(option_values.preserve_modtime && strcmp(outfilename, "-"))
                        grabbag__file_copy_metadata(infilename, outfilename);
                if(option_values.delete_input && !option_values.test_only && !option_values.analyze)
                        unlink(infilename);
@@ -2093,6 +2137,9 @@ const char *get_decoded_outfilename(const char *infilename)
        else if(option_values.force_aiff_format) {
                suffix = ".aiff";
        }
+       else if(option_values.force_rf64_format) {
+               suffix = ".rf64";
+       }
        else {
                suffix = ".wav";
        }