From aab5f27168a0474d2738631871e13728691a8760 Mon Sep 17 00:00:00 2001 From: Josh Coalson Date: Sat, 7 Sep 2002 05:28:26 +0000 Subject: [PATCH] add --export-vc-to option and undocumented --set-... options for STREAMINFO editing --- src/metaflac/main.c | 389 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 327 insertions(+), 62 deletions(-) diff --git a/src/metaflac/main.c b/src/metaflac/main.c index ba3c74b8..c1c3d057 100644 --- a/src/metaflac/main.c +++ b/src/metaflac/main.c @@ -63,6 +63,15 @@ static struct FLAC__share__option long_options_[] = { { "show-channels", 0, 0, 0 }, { "show-bps", 0, 0, 0 }, { "show-total-samples", 0, 0, 0 }, + { "set-md5sum", 1, 0, 0 }, /* undocumented */ + { "set-min-blocksize", 1, 0, 0 }, /* undocumented */ + { "set-max-blocksize", 1, 0, 0 }, /* undocumented */ + { "set-min-framesize", 1, 0, 0 }, /* undocumented */ + { "set-max-framesize", 1, 0, 0 }, /* undocumented */ + { "set-sample-rate", 1, 0, 0 }, /* undocumented */ + { "set-channels", 1, 0, 0 }, /* undocumented */ + { "set-bps", 1, 0, 0 }, /* undocumented */ + { "set-total-samples", 1, 0, 0 }, /* undocumented */ { "show-vc-vendor", 0, 0, 0 }, { "show-vc-field", 1, 0, 0 }, { "remove-vc-all", 0, 0, 0 }, @@ -70,6 +79,7 @@ static struct FLAC__share__option long_options_[] = { { "remove-vc-firstfield", 1, 0, 0 }, { "set-vc-field", 1, 0, 0 }, { "import-vc-from", 1, 0, 0 }, + { "export-vc-to", 1, 0, 0 }, { "add-padding", 1, 0, 0 }, /* major operations */ { "help", 0, 0, 0 }, @@ -100,6 +110,15 @@ typedef enum { OP__SHOW_CHANNELS, OP__SHOW_BPS, OP__SHOW_TOTAL_SAMPLES, + OP__SET_MD5SUM, + OP__SET_MIN_BLOCKSIZE, + OP__SET_MAX_BLOCKSIZE, + OP__SET_MIN_FRAMESIZE, + OP__SET_MAX_FRAMESIZE, + OP__SET_SAMPLE_RATE, + OP__SET_CHANNELS, + OP__SET_BPS, + OP__SET_TOTAL_SAMPLES, OP__SHOW_VC_VENDOR, OP__SHOW_VC_FIELD, OP__REMOVE_VC_ALL, @@ -107,6 +126,7 @@ typedef enum { OP__REMOVE_VC_FIRSTFIELD, OP__SET_VC_FIELD, OP__IMPORT_VC_FROM, + OP__EXPORT_VC_TO, OP__ADD_PADDING, OP__LIST, OP__APPEND, @@ -125,7 +145,19 @@ typedef enum { } ArgumentType; typedef struct { - char *field_name; + FLAC__byte value[16]; +} Argument_StreaminfoMD5; + +typedef struct { + FLAC__uint32 value; +} Argument_StreaminfoUInt32; + +typedef struct { + FLAC__uint64 value; +} Argument_StreaminfoUInt64; + +typedef struct { + char *value; } Argument_VcFieldName; typedef struct { @@ -137,8 +169,8 @@ typedef struct { } Argument_VcField; typedef struct { - char *file_name; -} Argument_VcImportFile; + char *value; +} Argument_VcFilename; typedef struct { unsigned num_entries; @@ -171,11 +203,12 @@ typedef struct { typedef struct { OperationType type; union { - Argument_VcFieldName show_vc_field; - Argument_VcFieldName remove_vc_field; - Argument_VcFieldName remove_vc_firstfield; - Argument_VcField set_vc_field; - Argument_VcImportFile import_vc_from; + Argument_StreaminfoMD5 streaminfo_md5; + Argument_StreaminfoUInt32 streaminfo_uint32; + Argument_StreaminfoUInt64 streaminfo_uint64; + Argument_VcFieldName vc_field_name; + Argument_VcField vc_field; + Argument_VcFilename vc_filename; Argument_AddPadding add_padding; } argument; } Operation; @@ -232,6 +265,9 @@ static void show_version(); static int short_usage(const char *message, ...); static int long_usage(const char *message, ...); static char *local_strdup(const char *source); +static FLAC__bool parse_md5(const char *src, FLAC__byte dest[16]); +static FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest); +static FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest); static FLAC__bool parse_filename(const char *src, char **dest); static FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation); static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation); @@ -251,19 +287,21 @@ static FLAC__bool do_shorthand_operations(const CommandLineOptions *options); static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options); static FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert); static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write); -static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, OperationType op); +static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write); static FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw); static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number); static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool hexdump_application); -static void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw); -static void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw); +static void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f); +static void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f); static FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write); static FLAC__bool remove_vc_field(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write); static FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write); static FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw); -static FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcImportFile *file, FLAC__bool *needs_write, FLAC__bool raw); +static FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw); +static FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool raw); static FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry); static void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent); +static void undocumented_warning(const char *opt); int main(int argc, char *argv[]) { @@ -429,6 +467,88 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi else if(0 == strcmp(opt, "show-total-samples")) { (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES); } + else if(0 == strcmp(opt, "set-md5sum")) { + op = append_shorthand_operation(options, OP__SET_MD5SUM); + FLAC__ASSERT(0 != option_argument); + if(!parse_md5(option_argument, op->argument.streaminfo_md5.value)) { + fprintf(stderr, "ERROR (--%s): bad MD5 sum\n", opt); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-min-blocksize")) { + op = append_shorthand_operation(options, OP__SET_MIN_BLOCKSIZE); + if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) { + fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-max-blocksize")) { + op = append_shorthand_operation(options, OP__SET_MAX_BLOCKSIZE); + if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) { + fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-min-framesize")) { + op = append_shorthand_operation(options, OP__SET_MIN_FRAMESIZE); + if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<argument.streaminfo_uint32.value)) || !FLAC__format_sample_rate_is_valid(op->argument.streaminfo_uint32.value)) { + fprintf(stderr, "ERROR (--%s): invalid sample rate\n", opt); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-channels")) { + op = append_shorthand_operation(options, OP__SET_CHANNELS); + if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value > FLAC__MAX_CHANNELS) { + fprintf(stderr, "ERROR (--%s): value must be > 0 and <= %u\n", opt, FLAC__MAX_CHANNELS); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-bps")) { + op = append_shorthand_operation(options, OP__SET_BPS); + if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BITS_PER_SAMPLE || op->argument.streaminfo_uint32.value > FLAC__MAX_BITS_PER_SAMPLE) { + fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE); + ok = false; + } + else + undocumented_warning(opt); + } + else if(0 == strcmp(opt, "set-total-samples")) { + op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES); + if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (1u<argument.show_vc_field.field_name), &violation)) { + if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); - fprintf(stderr, "ERROR: malformed vorbis comment field name \"%s\",\n %s\n", option_argument, violation); + fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } @@ -449,9 +569,9 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi const char *violation; op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD); FLAC__ASSERT(0 != option_argument); - if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.remove_vc_field.field_name), &violation)) { + if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); - fprintf(stderr, "ERROR: malformed vorbis comment field name \"%s\",\n %s\n", option_argument, violation); + fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } @@ -459,9 +579,9 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi const char *violation; op = append_shorthand_operation(options, OP__REMOVE_VC_FIRSTFIELD); FLAC__ASSERT(0 != option_argument); - if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.remove_vc_firstfield.field_name), &violation)) { + if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) { FLAC__ASSERT(0 != violation); - fprintf(stderr, "ERROR: malformed vorbis comment field name \"%s\",\n %s\n", option_argument, violation); + fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } @@ -469,17 +589,25 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi const char *violation; op = append_shorthand_operation(options, OP__SET_VC_FIELD); FLAC__ASSERT(0 != option_argument); - if(!parse_vorbis_comment_field(option_argument, &(op->argument.set_vc_field.field), &(op->argument.set_vc_field.field_name), &(op->argument.set_vc_field.field_value), &(op->argument.set_vc_field.field_value_length), &violation)) { + if(!parse_vorbis_comment_field(option_argument, &(op->argument.vc_field.field), &(op->argument.vc_field.field_name), &(op->argument.vc_field.field_value), &(op->argument.vc_field.field_value_length), &violation)) { FLAC__ASSERT(0 != violation); - fprintf(stderr, "ERROR: malformed vorbis comment field \"%s\",\n %s\n", option_argument, violation); + fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n %s\n", opt, option_argument, violation); ok = false; } } else if(0 == strcmp(opt, "import-vc-from")) { op = append_shorthand_operation(options, OP__IMPORT_VC_FROM); FLAC__ASSERT(0 != option_argument); - if(!parse_filename(option_argument, &(op->argument.import_vc_from.file_name))) { - fprintf(stderr, "ERROR: missing filename\n"); + if(!parse_filename(option_argument, &(op->argument.vc_filename.value))) { + fprintf(stderr, "ERROR (--%s): missing filename\n", opt); + ok = false; + } + } + else if(0 == strcmp(opt, "export-vc-to")) { + op = append_shorthand_operation(options, OP__EXPORT_VC_TO); + FLAC__ASSERT(0 != option_argument); + if(!parse_filename(option_argument, &(op->argument.vc_filename.value))) { + fprintf(stderr, "ERROR (--%s): missing filename\n", opt); ok = false; } } @@ -487,7 +615,7 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi op = append_shorthand_operation(options, OP__ADD_PADDING); FLAC__ASSERT(0 != option_argument); if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) { - fprintf(stderr, "ERROR: illegal length \"%s\", length must be >= 0 and < 2^%u\n", option_argument, FLAC__STREAM_METADATA_LENGTH_LEN); + fprintf(stderr, "ERROR (--%s): illegal length \"%s\", length must be >= 0 and < 2^%u\n", opt, option_argument, FLAC__STREAM_METADATA_LENGTH_LEN); ok = false; } } @@ -527,7 +655,7 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi arg = append_argument(options, ARG__BLOCK_TYPE); FLAC__ASSERT(0 != option_argument); if(!parse_block_type(option_argument, &(arg->value.block_type))) { - fprintf(stderr, "ERROR: malformed block type specification \"%s\"\n", option_argument); + fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument); ok = false; } options->args.checks.has_block_type = true; @@ -536,7 +664,7 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi arg = append_argument(options, ARG__EXCEPT_BLOCK_TYPE); FLAC__ASSERT(0 != option_argument); if(!parse_block_type(option_argument, &(arg->value.block_type))) { - fprintf(stderr, "ERROR: malformed block type specification \"%s\"\n", option_argument); + fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument); ok = false; } options->args.checks.has_except_block_type = true; @@ -545,14 +673,14 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi arg = append_argument(options, ARG__DATA_FORMAT); FLAC__ASSERT(0 != option_argument); if(!parse_data_format(option_argument, &(arg->value.data_format))) { - fprintf(stderr, "ERROR: illegal data format \"%s\"\n", option_argument); + fprintf(stderr, "ERROR (--%s): illegal data format \"%s\"\n", opt, option_argument); ok = false; } } else if(0 == strcmp(opt, "application-data-format")) { FLAC__ASSERT(0 != option_argument); if(!parse_application_data_format(option_argument, &(options->application_data_format_is_hexdump))) { - fprintf(stderr, "ERROR: illegal application data format \"%s\"\n", option_argument); + fprintf(stderr, "ERROR (--%s): illegal application data format \"%s\"\n", opt, option_argument); ok = false; } } @@ -582,20 +710,21 @@ void free_options(CommandLineOptions *options) case OP__SHOW_VC_FIELD: case OP__REMOVE_VC_FIELD: case OP__REMOVE_VC_FIRSTFIELD: - if(0 != op->argument.show_vc_field.field_name) - free(op->argument.show_vc_field.field_name); + if(0 != op->argument.vc_field_name.value) + free(op->argument.vc_field_name.value); break; case OP__SET_VC_FIELD: - if(0 != op->argument.set_vc_field.field) - free(op->argument.set_vc_field.field); - if(0 != op->argument.set_vc_field.field_name) - free(op->argument.set_vc_field.field_name); - if(0 != op->argument.set_vc_field.field_value) - free(op->argument.set_vc_field.field_value); + if(0 != op->argument.vc_field.field) + free(op->argument.vc_field.field); + if(0 != op->argument.vc_field.field_name) + free(op->argument.vc_field.field_name); + if(0 != op->argument.vc_field.field_value) + free(op->argument.vc_field.field_value); break; case OP__IMPORT_VC_FROM: - if(0 != op->argument.import_vc_from.file_name) - free(op->argument.import_vc_from.file_name); + case OP__EXPORT_VC_TO: + if(0 != op->argument.vc_filename.value) + free(op->argument.vc_filename.value); break; default: break; @@ -817,6 +946,9 @@ int long_usage(const char *message, ...) fprintf(out, " line comments are currently not supported. Specify\n"); fprintf(out, " --remove-vc-all and/or --no-utf8-convert before\n"); fprintf(out, " --import-vc-from if necessary.\n"); + fprintf(out, "--export-vc-to=file Export Vorbis comments to a file. Use '-' for stdin.\n"); + fprintf(out, " Each line will be of the form NAME=VALUE. Specify\n"); + fprintf(out, " --no-utf8-convert if necessary.\n"); fprintf(out, "--add-padding=length Add a padding block of the given length (in bytes).\n"); fprintf(out, " The overall length of the new block will be 4 + length;\n"); fprintf(out, " the extra 4 bytes is for the metadata block header.\n"); @@ -921,6 +1053,57 @@ char *local_strdup(const char *source) return ret; } +FLAC__bool parse_md5(const char *src, FLAC__byte dest[16]) +{ + unsigned i, d; + char c; + FLAC__ASSERT(0 != src); + if(strlen(src) != 32) + return false; + /* strtoul() accepts negative numbers which we do not want, so we do it the hard way */ + for(i = 0; i < 16; i++) { + c = *src++; + if(isdigit(c)) + d = (unsigned)c - (unsigned)'0'; + else if(c >= 'a' && c <= 'f') + d = (unsigned)c - (unsigned)'a' + 10u; + else if(c >= 'A' && c <= 'F') + d = (unsigned)c - (unsigned)'A' + 10u; + else + return false; + d <<= 4; + c = *src++; + if(isdigit(c)) + d |= (unsigned)c - (unsigned)'0'; + else if(c >= 'a' && c <= 'f') + d |= (unsigned)c - (unsigned)'a' + 10u; + else if(c >= 'A' && c <= 'F') + d |= (unsigned)c - (unsigned)'A' + 10u; + else + return false; + dest[i] = (FLAC__byte)d; + } + return true; +} + +FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest) +{ + FLAC__ASSERT(0 != src); + if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src)) + return false; + *dest = strtoul(src, 0, 10); + return true; +} + +FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest) +{ + FLAC__ASSERT(0 != src); + if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src)) + return false; + *dest = strtoull(src, 0, 10); + return true; +} + FLAC__bool parse_filename(const char *src, char **dest) { if(0 == src || strlen(src) == 0) @@ -1364,7 +1547,16 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *ch case OP__SHOW_CHANNELS: case OP__SHOW_BPS: case OP__SHOW_TOTAL_SAMPLES: - ok = do_shorthand_operation__streaminfo(filename, chain, operation->type); + case OP__SET_MD5SUM: + case OP__SET_MIN_BLOCKSIZE: + case OP__SET_MAX_BLOCKSIZE: + case OP__SET_MIN_FRAMESIZE: + case OP__SET_MAX_FRAMESIZE: + case OP__SET_SAMPLE_RATE: + case OP__SET_CHANNELS: + case OP__SET_BPS: + case OP__SET_TOTAL_SAMPLES: + ok = do_shorthand_operation__streaminfo(filename, chain, operation, needs_write); break; case OP__SHOW_VC_VENDOR: case OP__SHOW_VC_FIELD: @@ -1373,6 +1565,7 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *ch case OP__REMOVE_VC_FIRSTFIELD: case OP__SET_VC_FIELD: case OP__IMPORT_VC_FROM: + case OP__EXPORT_VC_TO: ok = do_shorthand_operation__vorbis_comment(filename, chain, operation, needs_write, !utf8_convert); break; case OP__ADD_PADDING: @@ -1416,7 +1609,7 @@ FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metad return true; } -FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, OperationType op) +FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write) { unsigned i; FLAC__bool ok = true; @@ -1436,7 +1629,7 @@ FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metada if(0 != filename) printf("%s:", filename); - switch(op) { + switch(operation->type) { case OP__SHOW_MD5SUM: for(i = 0; i < 16; i++) printf("%02x", block->data.stream_info.md5sum[i]); @@ -1466,6 +1659,42 @@ FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metada case OP__SHOW_TOTAL_SAMPLES: printf("%llu\n", block->data.stream_info.total_samples); break; + case OP__SET_MD5SUM: + memcpy(block->data.stream_info.md5sum, operation->argument.streaminfo_md5.value, 16); + *needs_write = true; + break; + case OP__SET_MIN_BLOCKSIZE: + block->data.stream_info.min_blocksize = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_MAX_BLOCKSIZE: + block->data.stream_info.max_blocksize = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_MIN_FRAMESIZE: + block->data.stream_info.min_framesize = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_MAX_FRAMESIZE: + block->data.stream_info.max_framesize = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_SAMPLE_RATE: + block->data.stream_info.sample_rate = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_CHANNELS: + block->data.stream_info.channels = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_BPS: + block->data.stream_info.bits_per_sample = operation->argument.streaminfo_uint32.value; + *needs_write = true; + break; + case OP__SET_TOTAL_SAMPLES: + block->data.stream_info.total_samples = operation->argument.streaminfo_uint64.value; + *needs_write = true; + break; default: ok = false; FLAC__ASSERT(0); @@ -1514,25 +1743,28 @@ FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Me switch(operation->type) { case OP__SHOW_VC_VENDOR: - write_vc_field(filename, &block->data.vorbis_comment.vendor_string, raw); + write_vc_field(filename, &block->data.vorbis_comment.vendor_string, raw, stdout); break; case OP__SHOW_VC_FIELD: - write_vc_fields(filename, operation->argument.show_vc_field.field_name, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw); + write_vc_fields(filename, operation->argument.vc_field_name.value, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, stdout); break; case OP__REMOVE_VC_ALL: ok = remove_vc_all(filename, block, needs_write); break; case OP__REMOVE_VC_FIELD: - ok = remove_vc_field(block, operation->argument.remove_vc_field.field_name, needs_write); + ok = remove_vc_field(block, operation->argument.vc_field_name.value, needs_write); break; case OP__REMOVE_VC_FIRSTFIELD: - ok = remove_vc_firstfield(filename, block, operation->argument.remove_vc_firstfield.field_name, needs_write); + ok = remove_vc_firstfield(filename, block, operation->argument.vc_field_name.value, needs_write); break; case OP__SET_VC_FIELD: - ok = set_vc_field(filename, block, &operation->argument.set_vc_field, needs_write, raw); + ok = set_vc_field(filename, block, &operation->argument.vc_field, needs_write, raw); break; case OP__IMPORT_VC_FROM: - ok = import_vc_from(filename, block, &operation->argument.import_vc_from, needs_write, raw); + ok = import_vc_from(filename, block, &operation->argument.vc_filename, needs_write, raw); + break; + case OP__EXPORT_VC_TO: + ok = export_vc_to(filename, block, &operation->argument.vc_filename, raw); break; default: ok = false; @@ -1655,34 +1887,34 @@ void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned #undef PPR } -void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw) +void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f) { if(0 != entry->entry) { char *converted; if(filename) - printf("%s:", filename); + fprintf(f, "%s:", filename); if(!raw && utf8_decode(entry->entry, &converted) >= 0) { - (void) fwrite(converted, 1, strlen(converted), stdout); + (void) fwrite(converted, 1, strlen(converted), f); free(converted); } else { - (void) fwrite(entry->entry, 1, entry->length, stdout); + (void) fwrite(entry->entry, 1, entry->length, f); } } - printf("\n"); + fprintf(f, "\n"); } -void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw) +void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f) { unsigned i; - const unsigned field_name_length = strlen(field_name); + const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0; for(i = 0; i < num_entries; i++) { - if(field_name_matches_entry(field_name, field_name_length, entry + i)) - write_vc_field(filename, entry + i, raw); + if(0 == field_name || field_name_matches_entry(field_name, field_name_length, entry + i)) + write_vc_field(filename, entry + i, raw, f); } } @@ -1792,23 +2024,23 @@ FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const } } -FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcImportFile *file, FLAC__bool *needs_write, FLAC__bool raw) +FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw) { FILE *f; char line[65536]; FLAC__bool ret; - if(0 == file->file_name || strlen(file->file_name) == 0) { + if(0 == vc_filename->value || strlen(vc_filename->value) == 0) { fprintf(stderr, "%s: ERROR: empty import file name\n", filename); return false; } - if(0 == strcmp(file->file_name, "-")) + if(0 == strcmp(vc_filename->value, "-")) f = stdin; else - f = fopen(file->file_name, "r"); + f = fopen(vc_filename->value, "r"); if(0 == f) { - fprintf(stderr, "%s: ERROR: can't open import file %s\n", filename, file->file_name); + fprintf(stderr, "%s: ERROR: can't open import file %s\n", filename, vc_filename->value); return false; } @@ -1818,7 +2050,7 @@ FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, con if(!feof(f)) { char *p = strchr(line, '\n'); if(0 == p) { - fprintf(stderr, "%s: ERROR: line too long, aborting\n", file->file_name); + fprintf(stderr, "%s: ERROR: line too long, aborting\n", vc_filename->value); ret = false; } else { @@ -1828,7 +2060,7 @@ FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, con memset(&field, 0, sizeof(Argument_VcField)); if(!parse_vorbis_comment_field(line, &field.field, &field.field_name, &field.field_value, &field.field_value_length, &violation)) { FLAC__ASSERT(0 != violation); - fprintf(stderr, "%s: ERROR: malformed vorbis comment field \"%s\",\n %s\n", file->file_name, line, violation); + fprintf(stderr, "%s: ERROR: malformed vorbis comment field \"%s\",\n %s\n", vc_filename->value, line, violation); ret = false; } else { @@ -1849,6 +2081,34 @@ FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, con return ret; } +FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool raw) +{ + FILE *f; + FLAC__bool ret; + + if(0 == vc_filename->value || strlen(vc_filename->value) == 0) { + fprintf(stderr, "%s: ERROR: empty export file name\n", filename); + return false; + } + if(0 == strcmp(vc_filename->value, "-")) + f = stdout; + else + f = fopen(vc_filename->value, "w"); + + if(0 == f) { + fprintf(stderr, "%s: ERROR: can't open export file %s\n", filename, vc_filename->value); + return false; + } + + ret = true; + + write_vc_fields(0, 0, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, f); + + if(f != stdout) + fclose(f); + return ret; +} + FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry) { const FLAC__byte *eq = memchr(entry->entry, '=', entry->length); @@ -1910,3 +2170,8 @@ void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const b += 16; } } + +void undocumented_warning(const char *opt) +{ + fprintf(stderr, "WARNING: undocmented option --%s should be used with caution,\n only for repairing a damaged STREAMINFO block\n", opt); +}