blob: 204d2a4cbac3367584e8f5dbbef6b32b2d0fece9 [file] [log] [blame]
Josh Coalsonee44de42002-11-08 06:07:20 +00001/* metaflac - Command-line FLAC metadata editor
Josh Coalsone74bd952007-02-02 06:58:19 +00002 * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson
Josh Coalsonee44de42002-11-08 06:07:20 +00003 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
Josh Coalsonb1ec7962006-05-24 04:41:36 +000019#if HAVE_CONFIG_H
20# include <config.h>
21#endif
22
Josh Coalsonee44de42002-11-08 06:07:20 +000023#include "operations.h"
24#include "usage.h"
25#include "utils.h"
26#include "FLAC/assert.h"
27#include "FLAC/metadata.h"
28#include "share/grabbag.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
Josh Coalsonf9cf27f2007-07-25 03:11:00 +000032#include "operations_shorthand.h"
Josh Coalsonee44de42002-11-08 06:07:20 +000033
Josh Coalsone3ec2ad2007-01-31 03:53:22 +000034static void show_version(void);
Josh Coalsonee44de42002-11-08 06:07:20 +000035static FLAC__bool do_major_operation(const CommandLineOptions *options);
36static FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options);
37static FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
38static FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
39static FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
40static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
41static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
42static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options);
Josh Coalson3c18ade2002-11-08 22:15:08 +000043static FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert);
Josh Coalsonee44de42002-11-08 06:07:20 +000044static FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime);
45static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
46
47static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number);
48static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application);
49
50/* from operations_shorthand_seektable.c */
51extern FLAC__bool do_shorthand_operation__add_seekpoints(const char *filename, FLAC__Metadata_Chain *chain, const char *specification, FLAC__bool *needs_write);
52
53/* from operations_shorthand_streaminfo.c */
Josh Coalson3c18ade2002-11-08 22:15:08 +000054extern FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
Josh Coalsonee44de42002-11-08 06:07:20 +000055
56/* from operations_shorthand_vorbiscomment.c */
Josh Coalson3c18ade2002-11-08 22:15:08 +000057extern FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw);
Josh Coalsonee44de42002-11-08 06:07:20 +000058
Josh Coalson303123f2002-11-26 06:21:06 +000059/* from operations_shorthand_cuesheet.c */
Josh Coalsoncfae6cd2002-11-27 04:44:54 +000060extern FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
Josh Coalson303123f2002-11-26 06:21:06 +000061
Josh Coalsonb02574e2006-09-26 00:43:48 +000062/* from operations_shorthand_picture.c */
63extern FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
64
Josh Coalson303123f2002-11-26 06:21:06 +000065
Josh Coalsonee44de42002-11-08 06:07:20 +000066FLAC__bool do_operations(const CommandLineOptions *options)
67{
68 FLAC__bool ok = true;
69
70 if(options->show_long_help) {
71 long_usage(0);
72 }
73 if(options->show_version) {
74 show_version();
75 }
76 else if(options->args.checks.num_major_ops > 0) {
77 FLAC__ASSERT(options->args.checks.num_shorthand_ops == 0);
78 FLAC__ASSERT(options->args.checks.num_major_ops == 1);
79 FLAC__ASSERT(options->args.checks.num_major_ops == options->ops.num_operations);
80 ok = do_major_operation(options);
81 }
82 else if(options->args.checks.num_shorthand_ops > 0) {
83 FLAC__ASSERT(options->args.checks.num_shorthand_ops == options->ops.num_operations);
84 ok = do_shorthand_operations(options);
85 }
86
87 return ok;
88}
89
90/*
91 * local routines
92 */
93
Josh Coalsone3ec2ad2007-01-31 03:53:22 +000094void show_version(void)
Josh Coalsonee44de42002-11-08 06:07:20 +000095{
96 printf("metaflac %s\n", FLAC__VERSION_STRING);
97}
98
99FLAC__bool do_major_operation(const CommandLineOptions *options)
100{
101 unsigned i;
102 FLAC__bool ok = true;
103
Josh Coalsonbeb12842006-10-03 01:04:41 +0000104 /* to die after first error, v--- add '&& ok' here */
Josh Coalsonee44de42002-11-08 06:07:20 +0000105 for(i = 0; i < options->num_files; i++)
106 ok &= do_major_operation_on_file(options->filenames[i], options);
107
108 return ok;
109}
110
111FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
112{
Josh Coalson6fba8472006-11-15 06:13:16 +0000113 FLAC__bool ok = true, needs_write = false, is_ogg = false;
Josh Coalsonee44de42002-11-08 06:07:20 +0000114 FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
115
116 if(0 == chain)
117 die("out of memory allocating chain");
118
Josh Coalson6fba8472006-11-15 06:13:16 +0000119 /*@@@@ lame way of guessing the file type */
120 if(strlen(filename) >= 4 && 0 == strcmp(filename+strlen(filename)-4, ".ogg"))
121 is_ogg = true;
122
123 if(! (is_ogg? FLAC__metadata_chain_read_ogg(chain, filename) : FLAC__metadata_chain_read(chain, filename)) ) {
Josh Coalson9454b412004-07-23 05:11:52 +0000124 print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
Josh Coalson07be2912004-10-19 04:23:57 +0000125 FLAC__metadata_chain_delete(chain);
Josh Coalsonee44de42002-11-08 06:07:20 +0000126 return false;
127 }
128
129 switch(options->ops.operations[0].type) {
130 case OP__LIST:
131 ok = do_major_operation__list(options->prefix_with_filename? filename : 0, chain, options);
132 break;
133 case OP__APPEND:
134 ok = do_major_operation__append(chain, options);
135 needs_write = true;
136 break;
137 case OP__REMOVE:
138 ok = do_major_operation__remove(chain, options);
139 needs_write = true;
140 break;
141 case OP__REMOVE_ALL:
142 ok = do_major_operation__remove_all(chain, options);
143 needs_write = true;
144 break;
145 case OP__MERGE_PADDING:
146 FLAC__metadata_chain_merge_padding(chain);
147 needs_write = true;
148 break;
149 case OP__SORT_PADDING:
150 FLAC__metadata_chain_sort_padding(chain);
151 needs_write = true;
152 break;
153 default:
154 FLAC__ASSERT(0);
155 return false;
156 }
157
158 if(ok && needs_write) {
159 if(options->use_padding)
160 FLAC__metadata_chain_sort_padding(chain);
161 ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
162 if(!ok)
Josh Coalson9454b412004-07-23 05:11:52 +0000163 print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
Josh Coalsonee44de42002-11-08 06:07:20 +0000164 }
165
166 FLAC__metadata_chain_delete(chain);
167
168 return ok;
169}
170
171FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
172{
173 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
174 FLAC__StreamMetadata *block;
175 FLAC__bool ok = true;
176 unsigned block_number;
177
178 if(0 == iterator)
179 die("out of memory allocating iterator");
180
181 FLAC__metadata_iterator_init(iterator, chain);
182
183 block_number = 0;
184 do {
185 block = FLAC__metadata_iterator_get_block(iterator);
186 ok &= (0 != block);
187 if(!ok)
188 fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename);
189 else if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number))
190 write_metadata(filename, block, block_number, !options->utf8_convert, options->application_data_format_is_hexdump);
191 block_number++;
192 } while(ok && FLAC__metadata_iterator_next(iterator));
193
194 FLAC__metadata_iterator_delete(iterator);
195
196 return ok;
197}
198
199FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
200{
201 (void) chain, (void) options;
Josh Coalsonbeb12842006-10-03 01:04:41 +0000202 fprintf(stderr, "ERROR: --append not implemented yet\n");
Josh Coalsonee44de42002-11-08 06:07:20 +0000203 return false;
204}
205
206FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
207{
208 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
209 FLAC__bool ok = true;
210 unsigned block_number;
211
212 if(0 == iterator)
213 die("out of memory allocating iterator");
214
215 FLAC__metadata_iterator_init(iterator, chain);
216
217 block_number = 0;
218 while(ok && FLAC__metadata_iterator_next(iterator)) {
219 block_number++;
220 if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
221 ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
222 if(options->use_padding)
223 ok &= FLAC__metadata_iterator_next(iterator);
224 }
225 }
226
227 FLAC__metadata_iterator_delete(iterator);
228
229 return ok;
230}
231
232FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
233{
234 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
235 FLAC__bool ok = true;
236
237 if(0 == iterator)
238 die("out of memory allocating iterator");
239
240 FLAC__metadata_iterator_init(iterator, chain);
241
242 while(ok && FLAC__metadata_iterator_next(iterator)) {
243 ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
244 if(options->use_padding)
245 ok &= FLAC__metadata_iterator_next(iterator);
246 }
247
248 FLAC__metadata_iterator_delete(iterator);
249
250 return ok;
251}
252
253FLAC__bool do_shorthand_operations(const CommandLineOptions *options)
254{
255 unsigned i;
256 FLAC__bool ok = true;
257
258 /* to die after first error, v--- add '&& ok' here */
259 for(i = 0; i < options->num_files; i++)
260 ok &= do_shorthand_operations_on_file(options->filenames[i], options);
261
262 /* check if OP__ADD_REPLAY_GAIN requested */
263 if(ok && options->num_files > 0) {
264 for(i = 0; i < options->ops.num_operations; i++) {
265 if(options->ops.operations[i].type == OP__ADD_REPLAY_GAIN)
266 ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files, options->preserve_modtime);
267 }
268 }
269
270 return ok;
271}
272
273FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options)
274{
275 unsigned i;
276 FLAC__bool ok = true, needs_write = false, use_padding = options->use_padding;
277 FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
278
279 if(0 == chain)
280 die("out of memory allocating chain");
281
282 if(!FLAC__metadata_chain_read(chain, filename)) {
Josh Coalson9454b412004-07-23 05:11:52 +0000283 print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
Josh Coalsonee44de42002-11-08 06:07:20 +0000284 return false;
285 }
286
287 for(i = 0; i < options->ops.num_operations && ok; i++) {
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000288 /*
289 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
290 * --add-seekpoint and --import-cuesheet-from are used.
291 */
292 if(options->ops.operations[i].type != OP__ADD_SEEKPOINT)
293 ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
Josh Coalsonee44de42002-11-08 06:07:20 +0000294
295 /* The following seems counterintuitive but the meaning
296 * of 'use_padding' is 'try to keep the overall metadata
297 * to its original size, adding or truncating extra
298 * padding if necessary' which is why we need to turn it
299 * off in this case. If we don't, the extra padding block
300 * will just be truncated.
301 */
302 if(options->ops.operations[i].type == OP__ADD_PADDING)
303 use_padding = false;
304 }
305
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000306 /*
307 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
308 * --add-seekpoint and --import-cuesheet-from are used.
309 */
310 for(i = 0; i < options->ops.num_operations && ok; i++) {
311 if(options->ops.operations[i].type == OP__ADD_SEEKPOINT)
312 ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
313 }
314
Josh Coalsonee44de42002-11-08 06:07:20 +0000315 if(ok && needs_write) {
316 if(use_padding)
317 FLAC__metadata_chain_sort_padding(chain);
318 ok = FLAC__metadata_chain_write(chain, use_padding, options->preserve_modtime);
319 if(!ok)
Josh Coalson9454b412004-07-23 05:11:52 +0000320 print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
Josh Coalsonee44de42002-11-08 06:07:20 +0000321 }
322
323 FLAC__metadata_chain_delete(chain);
324
325 return ok;
326}
327
Josh Coalson3c18ade2002-11-08 22:15:08 +0000328FLAC__bool do_shorthand_operation(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert)
Josh Coalsonee44de42002-11-08 06:07:20 +0000329{
330 FLAC__bool ok = true;
331
332 switch(operation->type) {
333 case OP__SHOW_MD5SUM:
334 case OP__SHOW_MIN_BLOCKSIZE:
335 case OP__SHOW_MAX_BLOCKSIZE:
336 case OP__SHOW_MIN_FRAMESIZE:
337 case OP__SHOW_MAX_FRAMESIZE:
338 case OP__SHOW_SAMPLE_RATE:
339 case OP__SHOW_CHANNELS:
340 case OP__SHOW_BPS:
341 case OP__SHOW_TOTAL_SAMPLES:
342 case OP__SET_MD5SUM:
343 case OP__SET_MIN_BLOCKSIZE:
344 case OP__SET_MAX_BLOCKSIZE:
345 case OP__SET_MIN_FRAMESIZE:
346 case OP__SET_MAX_FRAMESIZE:
347 case OP__SET_SAMPLE_RATE:
348 case OP__SET_CHANNELS:
349 case OP__SET_BPS:
350 case OP__SET_TOTAL_SAMPLES:
Josh Coalson3c18ade2002-11-08 22:15:08 +0000351 ok = do_shorthand_operation__streaminfo(filename, prefix_with_filename, chain, operation, needs_write);
Josh Coalsonee44de42002-11-08 06:07:20 +0000352 break;
353 case OP__SHOW_VC_VENDOR:
354 case OP__SHOW_VC_FIELD:
355 case OP__REMOVE_VC_ALL:
356 case OP__REMOVE_VC_FIELD:
357 case OP__REMOVE_VC_FIRSTFIELD:
358 case OP__SET_VC_FIELD:
359 case OP__IMPORT_VC_FROM:
360 case OP__EXPORT_VC_TO:
Josh Coalson3c18ade2002-11-08 22:15:08 +0000361 ok = do_shorthand_operation__vorbis_comment(filename, prefix_with_filename, chain, operation, needs_write, !utf8_convert);
Josh Coalsonee44de42002-11-08 06:07:20 +0000362 break;
Josh Coalson303123f2002-11-26 06:21:06 +0000363 case OP__IMPORT_CUESHEET_FROM:
364 case OP__EXPORT_CUESHEET_TO:
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000365 ok = do_shorthand_operation__cuesheet(filename, chain, operation, needs_write);
Josh Coalson303123f2002-11-26 06:21:06 +0000366 break;
Josh Coalsonaec8bb32006-11-02 04:06:56 +0000367 case OP__IMPORT_PICTURE_FROM:
Josh Coalson989f2672006-11-02 03:40:44 +0000368 case OP__EXPORT_PICTURE_TO:
Josh Coalsonb02574e2006-09-26 00:43:48 +0000369 ok = do_shorthand_operation__picture(filename, chain, operation, needs_write);
370 break;
Josh Coalsonee44de42002-11-08 06:07:20 +0000371 case OP__ADD_SEEKPOINT:
372 ok = do_shorthand_operation__add_seekpoints(filename, chain, operation->argument.add_seekpoint.specification, needs_write);
373 break;
374 case OP__ADD_REPLAY_GAIN:
375 /* this command is always executed last */
376 ok = true;
377 break;
378 case OP__ADD_PADDING:
379 ok = do_shorthand_operation__add_padding(filename, chain, operation->argument.add_padding.length, needs_write);
380 break;
381 default:
382 ok = false;
383 FLAC__ASSERT(0);
384 break;
385 };
386
387 return ok;
388}
389
390FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime)
391{
392 FLAC__StreamMetadata streaminfo;
393 float *title_gains = 0, *title_peaks = 0;
394 float album_gain, album_peak;
395 unsigned sample_rate = 0;
396 unsigned bits_per_sample = 0;
397 unsigned channels = 0;
398 unsigned i;
399 const char *error;
400 FLAC__bool first = true;
401
402 FLAC__ASSERT(num_files > 0);
403
404 for(i = 0; i < num_files; i++) {
405 FLAC__ASSERT(0 != filenames[i]);
406 if(!FLAC__metadata_get_streaminfo(filenames[i], &streaminfo)) {
407 fprintf(stderr, "%s: ERROR: can't open file or get STREAMINFO block\n", filenames[i]);
408 return false;
409 }
410 if(first) {
411 first = false;
412 sample_rate = streaminfo.data.stream_info.sample_rate;
413 bits_per_sample = streaminfo.data.stream_info.bits_per_sample;
414 channels = streaminfo.data.stream_info.channels;
415 }
416 else {
417 if(sample_rate != streaminfo.data.stream_info.sample_rate) {
418 fprintf(stderr, "%s: ERROR: sample rate of %u Hz does not match previous files' %u Hz\n", filenames[i], streaminfo.data.stream_info.sample_rate, sample_rate);
419 return false;
420 }
421 if(bits_per_sample != streaminfo.data.stream_info.bits_per_sample) {
422 fprintf(stderr, "%s: ERROR: resolution of %u bps does not match previous files' %u bps\n", filenames[i], streaminfo.data.stream_info.bits_per_sample, bits_per_sample);
423 return false;
424 }
425 if(channels != streaminfo.data.stream_info.channels) {
426 fprintf(stderr, "%s: ERROR: # channels (%u) does not match previous files' (%u)\n", filenames[i], streaminfo.data.stream_info.channels, channels);
427 return false;
428 }
429 }
430 if(!grabbag__replaygain_is_valid_sample_frequency(sample_rate)) {
431 fprintf(stderr, "%s: ERROR: sample rate of %u Hz is not supported\n", filenames[i], sample_rate);
432 return false;
433 }
434 if(channels != 1 && channels != 2) {
435 fprintf(stderr, "%s: ERROR: # of channels (%u) is not supported, must be 1 or 2\n", filenames[i], channels);
436 return false;
437 }
438 }
439 FLAC__ASSERT(bits_per_sample >= FLAC__MIN_BITS_PER_SAMPLE && bits_per_sample <= FLAC__MAX_BITS_PER_SAMPLE);
440
Josh Coalsonc191ab42002-12-23 19:52:20 +0000441 if(!grabbag__replaygain_init(sample_rate)) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000442 FLAC__ASSERT(0);
Josh Coalsonc191ab42002-12-23 19:52:20 +0000443 /* double protection */
444 fprintf(stderr, "internal error\n");
445 return false;
446 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000447
448 if(
449 0 == (title_gains = (float*)malloc(sizeof(float) * num_files)) ||
450 0 == (title_peaks = (float*)malloc(sizeof(float) * num_files))
451 )
452 die("out of memory allocating space for title gains/peaks");
453
454 for(i = 0; i < num_files; i++) {
455 if(0 != (error = grabbag__replaygain_analyze_file(filenames[i], title_gains+i, title_peaks+i))) {
456 fprintf(stderr, "%s: ERROR: during analysis (%s)\n", filenames[i], error);
457 free(title_gains);
458 free(title_peaks);
459 return false;
460 }
461 }
462 grabbag__replaygain_get_album(&album_gain, &album_peak);
463
464 for(i = 0; i < num_files; i++) {
465 if(0 != (error = grabbag__replaygain_store_to_file(filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i], preserve_modtime))) {
466 fprintf(stderr, "%s: ERROR: writing tags (%s)\n", filenames[i], error);
467 free(title_gains);
468 free(title_peaks);
469 return false;
470 }
471 }
472
473 free(title_gains);
474 free(title_peaks);
475 return true;
476}
477
478FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
479{
480 FLAC__StreamMetadata *padding = 0;
481 FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
482
483 if(0 == iterator)
484 die("out of memory allocating iterator");
485
486 FLAC__metadata_iterator_init(iterator, chain);
487
488 while(FLAC__metadata_iterator_next(iterator))
489 ;
490
491 padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
492 if(0 == padding)
493 die("out of memory allocating PADDING block");
494
495 padding->length = length;
496
497 if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) {
Josh Coalson9454b412004-07-23 05:11:52 +0000498 print_error_with_chain_status(chain, "%s: ERROR: adding new PADDING block to metadata", filename);
Josh Coalsonee44de42002-11-08 06:07:20 +0000499 FLAC__metadata_object_delete(padding);
500 FLAC__metadata_iterator_delete(iterator);
501 return false;
502 }
503
504 FLAC__metadata_iterator_delete(iterator);
505 *needs_write = true;
506 return true;
507}
508
509FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number)
510{
511 unsigned i, j;
512 FLAC__bool matches_number = false, matches_type = false;
513 FLAC__bool has_block_number_arg = false;
514
515 for(i = 0; i < options->args.num_arguments; i++) {
516 if(options->args.arguments[i].type == ARG__BLOCK_TYPE || options->args.arguments[i].type == ARG__EXCEPT_BLOCK_TYPE) {
517 for(j = 0; j < options->args.arguments[i].value.block_type.num_entries; j++) {
518 if(options->args.arguments[i].value.block_type.entries[j].type == block->type) {
519 if(block->type != FLAC__METADATA_TYPE_APPLICATION || !options->args.arguments[i].value.block_type.entries[j].filter_application_by_id || 0 == memcmp(options->args.arguments[i].value.block_type.entries[j].application_id, block->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))
520 matches_type = true;
521 }
522 }
523 }
524 else if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
525 has_block_number_arg = true;
526 for(j = 0; j < options->args.arguments[i].value.block_number.num_entries; j++) {
527 if(options->args.arguments[i].value.block_number.entries[j] == block_number)
528 matches_number = true;
529 }
530 }
531 }
532
533 if(!has_block_number_arg)
534 matches_number = true;
535
536 if(options->args.checks.has_block_type) {
537 FLAC__ASSERT(!options->args.checks.has_except_block_type);
538 }
539 else if(options->args.checks.has_except_block_type)
540 matches_type = !matches_type;
541 else
542 matches_type = true;
543
544 return matches_number && matches_type;
545}
546
547void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application)
548{
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000549 unsigned i, j;
Josh Coalsonee44de42002-11-08 06:07:20 +0000550
551/*@@@ yuck, should do this with a varargs function or something: */
552#define PPR if(filename)printf("%s:",filename);
553 PPR; printf("METADATA block #%u\n", block_number);
Josh Coalsond91f8fa2002-11-15 05:44:26 +0000554 PPR; printf(" type: %u (%s)\n", (unsigned)block->type, block->type < FLAC__METADATA_TYPE_UNDEFINED? FLAC__MetadataTypeString[block->type] : "UNKNOWN");
Josh Coalsonee44de42002-11-08 06:07:20 +0000555 PPR; printf(" is last: %s\n", block->is_last? "true":"false");
556 PPR; printf(" length: %u\n", block->length);
557
558 switch(block->type) {
559 case FLAC__METADATA_TYPE_STREAMINFO:
Josh Coalson14502c62006-08-28 03:33:01 +0000560 PPR; printf(" minimum blocksize: %u samples\n", block->data.stream_info.min_blocksize);
Josh Coalsonee44de42002-11-08 06:07:20 +0000561 PPR; printf(" maximum blocksize: %u samples\n", block->data.stream_info.max_blocksize);
562 PPR; printf(" minimum framesize: %u bytes\n", block->data.stream_info.min_framesize);
563 PPR; printf(" maximum framesize: %u bytes\n", block->data.stream_info.max_framesize);
564 PPR; printf(" sample_rate: %u Hz\n", block->data.stream_info.sample_rate);
565 PPR; printf(" channels: %u\n", block->data.stream_info.channels);
566 PPR; printf(" bits-per-sample: %u\n", block->data.stream_info.bits_per_sample);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000567#ifdef _MSC_VER
568 PPR; printf(" total samples: %I64u\n", block->data.stream_info.total_samples);
569#else
Josh Coalsonacd4a432006-11-11 22:43:25 +0000570 PPR; printf(" total samples: %llu\n", (unsigned long long)block->data.stream_info.total_samples);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000571#endif
Josh Coalsonee44de42002-11-08 06:07:20 +0000572 PPR; printf(" MD5 signature: ");
573 for(i = 0; i < 16; i++) {
574 printf("%02x", (unsigned)block->data.stream_info.md5sum[i]);
575 }
576 printf("\n");
577 break;
578 case FLAC__METADATA_TYPE_PADDING:
579 /* nothing to print */
580 break;
581 case FLAC__METADATA_TYPE_APPLICATION:
582 PPR; printf(" application ID: ");
Josh Coalson292d20f2003-01-11 06:25:20 +0000583 for(i = 0; i < 4; i++)
584 printf("%02x", block->data.application.id[i]);
585 printf("\n");
Josh Coalsonee44de42002-11-08 06:07:20 +0000586 PPR; printf(" data contents:\n");
587 if(0 != block->data.application.data) {
588 if(hexdump_application)
589 hexdump(filename, block->data.application.data, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, " ");
590 else
Josh Coalsonf91251d2002-12-28 07:04:49 +0000591 (void) local_fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
Josh Coalsonee44de42002-11-08 06:07:20 +0000592 }
593 break;
594 case FLAC__METADATA_TYPE_SEEKTABLE:
595 PPR; printf(" seek points: %u\n", block->data.seek_table.num_points);
596 for(i = 0; i < block->data.seek_table.num_points; i++) {
597 if(block->data.seek_table.points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
Josh Coalsonfec4a772004-03-22 05:47:25 +0000598#ifdef _MSC_VER
599 PPR; printf(" point %u: sample_number=%I64u, stream_offset=%I64u, frame_samples=%u\n", i, block->data.seek_table.points[i].sample_number, block->data.seek_table.points[i].stream_offset, block->data.seek_table.points[i].frame_samples);
600#else
Josh Coalsonacd4a432006-11-11 22:43:25 +0000601 PPR; printf(" point %u: sample_number=%llu, stream_offset=%llu, frame_samples=%u\n", i, (unsigned long long)block->data.seek_table.points[i].sample_number, (unsigned long long)block->data.seek_table.points[i].stream_offset, block->data.seek_table.points[i].frame_samples);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000602#endif
Josh Coalsonee44de42002-11-08 06:07:20 +0000603 }
604 else {
Josh Coalsonfec4a772004-03-22 05:47:25 +0000605 PPR; printf(" point %u: PLACEHOLDER\n", i);
Josh Coalsonee44de42002-11-08 06:07:20 +0000606 }
607 }
608 break;
609 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
610 PPR; printf(" vendor string: ");
611 write_vc_field(0, &block->data.vorbis_comment.vendor_string, raw, stdout);
612 PPR; printf(" comments: %u\n", block->data.vorbis_comment.num_comments);
613 for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
614 PPR; printf(" comment[%u]: ", i);
615 write_vc_field(0, &block->data.vorbis_comment.comments[i], raw, stdout);
616 }
617 break;
Josh Coalson303123f2002-11-26 06:21:06 +0000618 case FLAC__METADATA_TYPE_CUESHEET:
619 PPR; printf(" media catalog number: %s\n", block->data.cue_sheet.media_catalog_number);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000620#ifdef _MSC_VER
621 PPR; printf(" lead-in: %I64u\n", block->data.cue_sheet.lead_in);
622#else
Josh Coalsonacd4a432006-11-11 22:43:25 +0000623 PPR; printf(" lead-in: %llu\n", (unsigned long long)block->data.cue_sheet.lead_in);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000624#endif
Josh Coalson8f0c71b2002-12-05 06:37:46 +0000625 PPR; printf(" is CD: %s\n", block->data.cue_sheet.is_cd? "true":"false");
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000626 PPR; printf(" number of tracks: %u\n", block->data.cue_sheet.num_tracks);
627 for(i = 0; i < block->data.cue_sheet.num_tracks; i++) {
628 const FLAC__StreamMetadata_CueSheet_Track *track = block->data.cue_sheet.tracks+i;
629 const FLAC__bool is_last = (i == block->data.cue_sheet.num_tracks-1);
630 const FLAC__bool is_leadout = is_last && track->num_indices == 0;
631 PPR; printf(" track[%u]\n", i);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000632#ifdef _MSC_VER
633 PPR; printf(" offset: %I64u\n", track->offset);
634#else
Josh Coalsonacd4a432006-11-11 22:43:25 +0000635 PPR; printf(" offset: %llu\n", (unsigned long long)track->offset);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000636#endif
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000637 if(is_last) {
638 PPR; printf(" number: %u (%s)\n", (unsigned)track->number, is_leadout? "LEAD-OUT" : "INVALID");
639 }
640 else {
641 PPR; printf(" number: %u\n", (unsigned)track->number);
642 }
643 if(!is_leadout) {
644 PPR; printf(" ISRC: %s\n", track->isrc);
645 PPR; printf(" type: %s\n", track->type == 1? "DATA" : "AUDIO");
Josh Coalson8f0c71b2002-12-05 06:37:46 +0000646 PPR; printf(" pre-emphasis: %s\n", track->pre_emphasis? "true":"false");
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000647 PPR; printf(" number of index points: %u\n", track->num_indices);
648 for(j = 0; j < track->num_indices; j++) {
649 const FLAC__StreamMetadata_CueSheet_Index *index = track->indices+j;
650 PPR; printf(" index[%u]\n", j);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000651#ifdef _MSC_VER
652 PPR; printf(" offset: %I64u\n", index->offset);
653#else
Josh Coalsonacd4a432006-11-11 22:43:25 +0000654 PPR; printf(" offset: %llu\n", (unsigned long long)index->offset);
Josh Coalsonfec4a772004-03-22 05:47:25 +0000655#endif
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000656 PPR; printf(" number: %u\n", (unsigned)index->number);
657 }
658 }
659 }
Josh Coalson303123f2002-11-26 06:21:06 +0000660 break;
Josh Coalsone343ab22006-09-23 19:21:19 +0000661 case FLAC__METADATA_TYPE_PICTURE:
662 PPR; printf(" type: %u (%s)\n", block->data.picture.type, block->data.picture.type < FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED? FLAC__StreamMetadata_Picture_TypeString[block->data.picture.type] : "UNDEFINED");
663 PPR; printf(" MIME type: %s\n", block->data.picture.mime_type);
664 PPR; printf(" description: %s\n", block->data.picture.description);
665 PPR; printf(" width: %u\n", (unsigned)block->data.picture.width);
666 PPR; printf(" height: %u\n", (unsigned)block->data.picture.height);
667 PPR; printf(" depth: %u\n", (unsigned)block->data.picture.depth);
Josh Coalson74ed2942006-09-23 23:15:05 +0000668 PPR; printf(" colors: %u%s\n", (unsigned)block->data.picture.colors, block->data.picture.colors? "" : " (unindexed)");
Josh Coalsone343ab22006-09-23 19:21:19 +0000669 PPR; printf(" data length: %u\n", (unsigned)block->data.picture.data_length);
670 PPR; printf(" data:\n");
671 if(0 != block->data.picture.data)
672 hexdump(filename, block->data.picture.data, block->data.picture.data_length, " ");
673 break;
Josh Coalsonee44de42002-11-08 06:07:20 +0000674 default:
Josh Coalsond0609472003-01-10 05:37:13 +0000675 PPR; printf(" data contents:\n");
676 if(0 != block->data.unknown.data)
677 hexdump(filename, block->data.unknown.data, block->length, " ");
Josh Coalsonee44de42002-11-08 06:07:20 +0000678 break;
679 }
680#undef PPR
681}