xref: /aosp_15_r20/external/flac/src/metaflac/operations.c (revision 600f14f40d737144c998e2ec7a483122d3776fbc)
1 /* metaflac - Command-line FLAC metadata editor
2  * Copyright (C) 2001-2009  Josh Coalson
3  * Copyright (C) 2011-2023  Xiph.Org Foundation
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23 
24 #include "operations.h"
25 #include "usage.h"
26 #include "utils.h"
27 #include "FLAC/assert.h"
28 #include "FLAC/metadata.h"
29 #include "share/alloc.h"
30 #include "share/grabbag.h"
31 #include "share/compat.h"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "operations_shorthand.h"
36 
37 static void show_version(void);
38 static FLAC__bool do_major_operation(const CommandLineOptions *options);
39 static FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options);
40 static FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
41 static FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
42 static FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
43 static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
44 static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
45 static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options);
46 static 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);
47 static FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime, FLAC__bool scan);
48 static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
49 
50 static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number);
51 static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application);
52 static void write_metadata_binary(FLAC__StreamMetadata *block, FLAC__byte *block_raw, FLAC__bool headerless);
53 
54 /* from operations_shorthand_seektable.c */
55 extern FLAC__bool do_shorthand_operation__add_seekpoints(const char *filename, FLAC__Metadata_Chain *chain, const char *specification, FLAC__bool *needs_write);
56 
57 /* from operations_shorthand_streaminfo.c */
58 extern 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);
59 
60 /* from operations_shorthand_vorbiscomment.c */
61 extern 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);
62 
63 /* from operations_shorthand_cuesheet.c */
64 extern FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
65 
66 /* from operations_shorthand_picture.c */
67 extern FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
68 
69 
do_operations(const CommandLineOptions * options)70 FLAC__bool do_operations(const CommandLineOptions *options)
71 {
72 	FLAC__bool ok = true;
73 
74 	if(options->show_long_help) {
75 		long_usage(0);
76 	}
77 	if(options->show_version) {
78 		show_version();
79 	}
80 	else if(options->args.checks.num_major_ops > 0) {
81 		FLAC__ASSERT(options->args.checks.num_shorthand_ops == 0);
82 		FLAC__ASSERT(options->args.checks.num_major_ops == 1);
83 		FLAC__ASSERT(options->args.checks.num_major_ops == options->ops.num_operations);
84 		ok = do_major_operation(options);
85 	}
86 	else if(options->args.checks.num_shorthand_ops > 0) {
87 		FLAC__ASSERT(options->args.checks.num_shorthand_ops == options->ops.num_operations);
88 		ok = do_shorthand_operations(options);
89 	}
90 
91 	return ok;
92 }
93 
94 /*
95  * local routines
96  */
97 
show_version(void)98 void show_version(void)
99 {
100 	printf("metaflac %s\n", FLAC__VERSION_STRING);
101 }
102 
do_major_operation(const CommandLineOptions * options)103 FLAC__bool do_major_operation(const CommandLineOptions *options)
104 {
105 	unsigned i;
106 	FLAC__bool ok = true;
107 
108 	/* to die after first error,     v---  add '&& ok' here */
109 	for(i = 0; i < options->num_files; i++)
110 		ok &= do_major_operation_on_file(options->filenames[i], options);
111 
112 	return ok;
113 }
114 
do_major_operation_on_file(const char * filename,const CommandLineOptions * options)115 FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
116 {
117 	FLAC__bool ok = true, needs_write = false, is_ogg = false;
118 	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
119 
120 	if(0 == chain)
121 		die("out of memory allocating chain");
122 
123 	/*@@@@ lame way of guessing the file type */
124 	if(strlen(filename) >= 4 && (0 == strcmp(filename+strlen(filename)-4, ".oga") || 0 == strcmp(filename+strlen(filename)-4, ".ogg")))
125 		is_ogg = true;
126 
127 	if(! (is_ogg? FLAC__metadata_chain_read_ogg(chain, filename) : FLAC__metadata_chain_read(chain, filename)) ) {
128 		print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
129 		FLAC__metadata_chain_delete(chain);
130 		return false;
131 	}
132 
133 	switch(options->ops.operations[0].type) {
134 		case OP__LIST:
135 			ok = do_major_operation__list(options->prefix_with_filename? filename : 0, chain, options);
136 			break;
137 		case OP__APPEND:
138 			ok = do_major_operation__append(chain, options);
139 			needs_write = true;
140 			break;
141 		case OP__REMOVE:
142 			ok = do_major_operation__remove(chain, options);
143 			needs_write = true;
144 			break;
145 		case OP__REMOVE_ALL:
146 			ok = do_major_operation__remove_all(chain, options);
147 			needs_write = true;
148 			break;
149 		case OP__MERGE_PADDING:
150 			FLAC__metadata_chain_merge_padding(chain);
151 			needs_write = true;
152 			break;
153 		case OP__SORT_PADDING:
154 			FLAC__metadata_chain_sort_padding(chain);
155 			needs_write = true;
156 			break;
157 		default:
158 			FLAC__ASSERT(0);
159 			return false;
160 	}
161 
162 	if(ok && needs_write) {
163 		if(options->use_padding)
164 			FLAC__metadata_chain_sort_padding(chain);
165 		ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
166 		if(!ok)
167 			print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
168 	}
169 
170 	FLAC__metadata_chain_delete(chain);
171 
172 	return ok;
173 }
174 
do_major_operation__list(const char * filename,FLAC__Metadata_Chain * chain,const CommandLineOptions * options)175 FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
176 {
177 	FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
178 	FLAC__StreamMetadata *block;
179 	FLAC__bool ok = true;
180 	unsigned block_number;
181 
182 	if(0 == iterator)
183 		die("out of memory allocating iterator");
184 
185 	FLAC__metadata_iterator_init(iterator, chain);
186 
187 	block_number = 0;
188 	do {
189 		block = FLAC__metadata_iterator_get_block(iterator);
190 		ok &= (0 != block);
191 		if(!ok)
192 			flac_fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename);
193 		else if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
194 			if(!options->data_format_is_binary && !options->data_format_is_binary_headerless)
195 				write_metadata(filename, block, block_number, !options->utf8_convert, options->application_data_format_is_hexdump);
196 			else {
197 				FLAC__byte * block_raw = FLAC__metadata_object_get_raw(block);
198 				if(block_raw == 0) {
199 					flac_fprintf(stderr, "%s: ERROR: couldn't get block in raw form\n", filename);
200 					FLAC__metadata_iterator_delete(iterator);
201 					return false;
202 				}
203 				write_metadata_binary(block, block_raw, options->data_format_is_binary_headerless);
204 				free(block_raw);
205 			}
206 		}
207 		block_number++;
208 	} while(ok && FLAC__metadata_iterator_next(iterator));
209 
210 	FLAC__metadata_iterator_delete(iterator);
211 
212 	return ok;
213 }
214 
do_major_operation__append(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)215 FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
216 {
217 	FLAC__byte header[FLAC__STREAM_METADATA_HEADER_LENGTH];
218 	FLAC__byte *buffer;
219 	FLAC__uint32 buffer_size, num_objects = 0, i, append_after = UINT32_MAX;
220 	FLAC__StreamMetadata *object;
221 	FLAC__Metadata_Iterator *iterator = 0;
222 	FLAC__bool has_vorbiscomment = false;
223 
224 	/* First, find out after which block appending should take place */
225 	for(i = 0; i < options->args.num_arguments; i++) {
226 		if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
227 			if(append_after != UINT32_MAX || options->args.arguments[i].value.block_number.num_entries > 1)	{
228 				flac_fprintf(stderr, "ERROR: more than one block number specified with --append\n");
229 				return false;
230 			}
231 			append_after = options->args.arguments[i].value.block_number.entries[0];
232 		}
233 	}
234 
235 	iterator = FLAC__metadata_iterator_new();
236 
237 	if(0 == iterator)
238 		die("out of memory allocating iterator");
239 
240 	FLAC__metadata_iterator_init(iterator, chain);
241 
242 	/* Find out whether there is already a vorbis comment block present */
243 	do {
244 		FLAC__MetadataType type = FLAC__metadata_iterator_get_block_type(iterator);
245 		if(type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
246 			has_vorbiscomment = true;
247 	}
248 	while(FLAC__metadata_iterator_next(iterator));
249 
250 	/* Reset iterator */
251 	FLAC__metadata_iterator_init(iterator, chain);
252 
253 	/* Go to requested block */
254 	for(i = 0; i < append_after; i++) {
255 		if(!FLAC__metadata_iterator_next(iterator))
256 			break;
257 	}
258 
259 #ifdef _WIN32
260 	_setmode(fileno(stdin),_O_BINARY);
261 #endif
262 
263 	/* Read header from stdin */
264 	while(fread(header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, stdin) == FLAC__STREAM_METADATA_HEADER_LENGTH) {
265 
266 		buffer_size = ((FLAC__uint32)(header[1]) << 16) + ((FLAC__uint32)(header[2]) << 8) + header[3];
267 		buffer = safe_malloc_(buffer_size + FLAC__STREAM_METADATA_HEADER_LENGTH);
268 		if(0 == buffer)
269 			die("out of memory allocating read buffer");
270 		memcpy(buffer, header, FLAC__STREAM_METADATA_HEADER_LENGTH);
271 
272 		num_objects++;
273 
274 		if(fread(buffer+FLAC__STREAM_METADATA_HEADER_LENGTH, 1, buffer_size, stdin) < buffer_size) {
275 			flac_fprintf(stderr, "ERROR: couldn't read metadata block #%u from stdin\n",(num_objects));
276 			free(buffer);
277 			FLAC__metadata_iterator_delete(iterator);
278 			return false;
279 		}
280 
281 		if((object = FLAC__metadata_object_set_raw(buffer, buffer_size + FLAC__STREAM_METADATA_HEADER_LENGTH)) == NULL) {
282 			flac_fprintf(stderr, "ERROR: couldn't parse supplied metadata block #%u\n",(num_objects));
283 			free(buffer);
284 			FLAC__metadata_iterator_delete(iterator);
285 			return false;
286 		}
287 		free(buffer);
288 
289 		if(has_vorbiscomment && object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
290 			flac_fprintf(stderr, "ERROR: can't add another vorbis comment block to file, it already has one\n");
291 			FLAC__metadata_object_delete(object);
292 			FLAC__metadata_iterator_delete(iterator);
293 			return false;
294 		}
295 
296 
297 		if(object->type == FLAC__METADATA_TYPE_STREAMINFO) {
298 			flac_fprintf(stderr, "ERROR: can't add streaminfo to file\n");
299 			FLAC__metadata_object_delete(object);
300 			FLAC__metadata_iterator_delete(iterator);
301 			return false;
302 		}
303 
304 		if(object->type == FLAC__METADATA_TYPE_SEEKTABLE) {
305 			flac_fprintf(stderr, "ERROR: can't add seektable to file, please use --add-seekpoint instead\n");
306 			FLAC__metadata_object_delete(object);
307 			FLAC__metadata_iterator_delete(iterator);
308 			return false;
309 		}
310 
311 		if(!FLAC__metadata_iterator_insert_block_after(iterator, object)) {
312 			flac_fprintf(stderr, "ERROR: couldn't add supplied metadata block #%u to file\n",(num_objects));
313 			FLAC__metadata_object_delete(object);
314 			FLAC__metadata_iterator_delete(iterator);
315 			return false;
316 		}
317 		/* Now check whether what type of block was added */
318 		{
319 			FLAC__MetadataType type = FLAC__metadata_iterator_get_block_type(iterator);
320 			if(type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
321 				has_vorbiscomment = true;
322 		}
323 	}
324 
325 #ifdef _WIN32
326 	_setmode(fileno(stdin),_O_TEXT);
327 #endif
328 
329 	if(num_objects == 0)
330 		flac_fprintf(stderr, "ERROR: unable to find a metadata block in the supplied input\n");
331 
332 	FLAC__metadata_iterator_delete(iterator);
333 
334 	return true;
335 }
336 
do_major_operation__remove(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)337 FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
338 {
339 	FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
340 	FLAC__bool ok = true;
341 	unsigned block_number;
342 
343 	if(0 == iterator)
344 		die("out of memory allocating iterator");
345 
346 	FLAC__metadata_iterator_init(iterator, chain);
347 
348 	block_number = 0;
349 	while(ok && FLAC__metadata_iterator_next(iterator)) {
350 		block_number++;
351 		if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
352 			ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
353 			if(options->use_padding)
354 				ok &= FLAC__metadata_iterator_next(iterator);
355 		}
356 	}
357 
358 	FLAC__metadata_iterator_delete(iterator);
359 
360 	return ok;
361 }
362 
do_major_operation__remove_all(FLAC__Metadata_Chain * chain,const CommandLineOptions * options)363 FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
364 {
365 	FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
366 	FLAC__bool ok = true;
367 
368 	if(0 == iterator)
369 		die("out of memory allocating iterator");
370 
371 	FLAC__metadata_iterator_init(iterator, chain);
372 
373 	while(ok && FLAC__metadata_iterator_next(iterator)) {
374 		ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
375 		if(options->use_padding)
376 			ok &= FLAC__metadata_iterator_next(iterator);
377 	}
378 
379 	FLAC__metadata_iterator_delete(iterator);
380 
381 	return ok;
382 }
383 
do_shorthand_operations(const CommandLineOptions * options)384 FLAC__bool do_shorthand_operations(const CommandLineOptions *options)
385 {
386 	unsigned i;
387 	FLAC__bool ok = true;
388 
389 	/* to die after first error,     v---  add '&& ok' here */
390 	for(i = 0; i < options->num_files; i++)
391 		ok &= do_shorthand_operations_on_file(options->filenames[i], options);
392 
393 	/* check if OP__ADD_REPLAY_GAIN requested */
394 	if(ok && options->num_files > 0) {
395 		for(i = 0; i < options->ops.num_operations; i++) {
396 			if(options->ops.operations[i].type == OP__ADD_REPLAY_GAIN)
397 				ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files, options->preserve_modtime, false);
398 			else if(options->ops.operations[i].type == OP__SCAN_REPLAY_GAIN)
399 				ok = do_shorthand_operation__add_replay_gain(options->filenames, options->num_files, options->preserve_modtime, true);
400 		}
401 	}
402 
403 	return ok;
404 }
405 
do_shorthand_operations_on_file(const char * filename,const CommandLineOptions * options)406 FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options)
407 {
408 	unsigned i;
409 	FLAC__bool ok = true, needs_write = false, use_padding = options->use_padding;
410 	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
411 
412 	if(0 == chain)
413 		die("out of memory allocating chain");
414 
415 	if(!FLAC__metadata_chain_read(chain, filename)) {
416 		print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
417 		ok = false;
418 		goto cleanup;
419 	}
420 
421 	for(i = 0; i < options->ops.num_operations && ok; i++) {
422 		/*
423 		 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
424 		 * --add-seekpoint and --import-cuesheet-from are used.
425 		 */
426 		if(options->ops.operations[i].type != OP__ADD_SEEKPOINT)
427 			ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
428 
429 		/* The following seems counterintuitive but the meaning
430 		 * of 'use_padding' is 'try to keep the overall metadata
431 		 * to its original size, adding or truncating extra
432 		 * padding if necessary' which is why we need to turn it
433 		 * off in this case.  If we don't, the extra padding block
434 		 * will just be truncated.
435 		 */
436 		if(options->ops.operations[i].type == OP__ADD_PADDING)
437 			use_padding = false;
438 	}
439 
440 	/*
441 	 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
442 	 * --add-seekpoint and --import-cuesheet-from are used.
443 	 */
444 	for(i = 0; i < options->ops.num_operations && ok; i++) {
445 		if(options->ops.operations[i].type == OP__ADD_SEEKPOINT)
446 			ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
447 	}
448 
449 	if(ok && needs_write) {
450 		if(use_padding)
451 			FLAC__metadata_chain_sort_padding(chain);
452 		ok = FLAC__metadata_chain_write(chain, use_padding, options->preserve_modtime);
453 		if(!ok)
454 			print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
455 	}
456 
457   cleanup :
458 	FLAC__metadata_chain_delete(chain);
459 
460 	return ok;
461 }
462 
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)463 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)
464 {
465 	FLAC__bool ok = true;
466 
467 	switch(operation->type) {
468 		case OP__SHOW_MD5SUM:
469 		case OP__SHOW_MIN_BLOCKSIZE:
470 		case OP__SHOW_MAX_BLOCKSIZE:
471 		case OP__SHOW_MIN_FRAMESIZE:
472 		case OP__SHOW_MAX_FRAMESIZE:
473 		case OP__SHOW_SAMPLE_RATE:
474 		case OP__SHOW_CHANNELS:
475 		case OP__SHOW_BPS:
476 		case OP__SHOW_TOTAL_SAMPLES:
477 		case OP__SET_MD5SUM:
478 		case OP__SET_MIN_BLOCKSIZE:
479 		case OP__SET_MAX_BLOCKSIZE:
480 		case OP__SET_MIN_FRAMESIZE:
481 		case OP__SET_MAX_FRAMESIZE:
482 		case OP__SET_SAMPLE_RATE:
483 		case OP__SET_CHANNELS:
484 		case OP__SET_BPS:
485 		case OP__SET_TOTAL_SAMPLES:
486 			ok = do_shorthand_operation__streaminfo(filename, prefix_with_filename, chain, operation, needs_write);
487 			break;
488 		case OP__SHOW_VC_VENDOR:
489 		case OP__SHOW_VC_FIELD:
490 		case OP__REMOVE_VC_ALL:
491 		case OP__REMOVE_VC_ALL_EXCEPT:
492 		case OP__REMOVE_VC_FIELD:
493 		case OP__REMOVE_VC_FIRSTFIELD:
494 		case OP__SET_VC_FIELD:
495 		case OP__IMPORT_VC_FROM:
496 		case OP__EXPORT_VC_TO:
497 			ok = do_shorthand_operation__vorbis_comment(filename, prefix_with_filename, chain, operation, needs_write, !utf8_convert);
498 			break;
499 		case OP__IMPORT_CUESHEET_FROM:
500 		case OP__EXPORT_CUESHEET_TO:
501 			ok = do_shorthand_operation__cuesheet(filename, chain, operation, needs_write);
502 			break;
503 		case OP__IMPORT_PICTURE_FROM:
504 		case OP__EXPORT_PICTURE_TO:
505 			ok = do_shorthand_operation__picture(filename, chain, operation, needs_write);
506 			break;
507 		case OP__ADD_SEEKPOINT:
508 			ok = do_shorthand_operation__add_seekpoints(filename, chain, operation->argument.add_seekpoint.specification, needs_write);
509 			break;
510 		case OP__ADD_REPLAY_GAIN:
511 		case OP__SCAN_REPLAY_GAIN:
512 			/* these commands are always executed last */
513 			ok = true;
514 			break;
515 		case OP__ADD_PADDING:
516 			ok = do_shorthand_operation__add_padding(filename, chain, operation->argument.add_padding.length, needs_write);
517 			break;
518 		default:
519 			ok = false;
520 			FLAC__ASSERT(0);
521 			break;
522 	};
523 
524 	return ok;
525 }
526 
do_shorthand_operation__add_replay_gain(char ** filenames,unsigned num_files,FLAC__bool preserve_modtime,FLAC__bool scan)527 FLAC__bool do_shorthand_operation__add_replay_gain(char **filenames, unsigned num_files, FLAC__bool preserve_modtime, FLAC__bool scan)
528 {
529 	FLAC__StreamMetadata streaminfo;
530 	float *title_gains = 0, *title_peaks = 0;
531 	float album_gain, album_peak;
532 	unsigned sample_rate = 0;
533 	unsigned bits_per_sample = 0;
534 	unsigned channels = 0;
535 	unsigned i;
536 	const char *error;
537 	FLAC__bool first = true;
538 
539 	FLAC__ASSERT(num_files > 0);
540 
541 	for(i = 0; i < num_files; i++) {
542 		FLAC__ASSERT(0 != filenames[i]);
543 		if(!FLAC__metadata_get_streaminfo(filenames[i], &streaminfo)) {
544 			flac_fprintf(stderr, "%s: ERROR: can't open file or get STREAMINFO block\n", filenames[i]);
545 			return false;
546 		}
547 		if(first) {
548 			first = false;
549 			sample_rate = streaminfo.data.stream_info.sample_rate;
550 			bits_per_sample = streaminfo.data.stream_info.bits_per_sample;
551 			channels = streaminfo.data.stream_info.channels;
552 		}
553 		else {
554 			if(sample_rate != streaminfo.data.stream_info.sample_rate) {
555 				flac_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);
556 				return false;
557 			}
558 			if(bits_per_sample != streaminfo.data.stream_info.bits_per_sample) {
559 				flac_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);
560 				return false;
561 			}
562 			if(channels != streaminfo.data.stream_info.channels) {
563 				flac_fprintf(stderr, "%s: ERROR: # channels (%u) does not match previous files' (%u)\n", filenames[i], streaminfo.data.stream_info.channels, channels);
564 				return false;
565 			}
566 		}
567 		if(!grabbag__replaygain_is_valid_sample_frequency(sample_rate)) {
568 			flac_fprintf(stderr, "%s: ERROR: sample rate of %u Hz is not supported\n", filenames[i], sample_rate);
569 			return false;
570 		}
571 		if(channels != 1 && channels != 2) {
572 			flac_fprintf(stderr, "%s: ERROR: # of channels (%u) is not supported, must be 1 or 2\n", filenames[i], channels);
573 			return false;
574 		}
575 		if(bits_per_sample < FLAC__MIN_BITS_PER_SAMPLE || bits_per_sample > FLAC__MAX_BITS_PER_SAMPLE) {
576 			flac_fprintf(stderr, "%s: ERROR: resolution (%u) is not supported, must be between %u and %u\n", filenames[i], bits_per_sample, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE);
577 			return false;
578 		}
579 	}
580 
581 	if(!grabbag__replaygain_init(sample_rate)) {
582 		FLAC__ASSERT(0);
583 		/* double protection */
584 		flac_fprintf(stderr, "internal error\n");
585 		return false;
586 	}
587 
588 	if(
589 		0 == (title_gains = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files)) ||
590 		0 == (title_peaks = safe_malloc_mul_2op_(sizeof(float), /*times*/num_files))
591 	)
592 		die("out of memory allocating space for title gains/peaks");
593 
594 	for(i = 0; i < num_files; i++) {
595 		if(0 != (error = grabbag__replaygain_analyze_file(filenames[i], title_gains+i, title_peaks+i))) {
596 			flac_fprintf(stderr, "%s: ERROR: during analysis (%s)\n", filenames[i], error);
597 			free(title_gains);
598 			free(title_peaks);
599 			return false;
600 		}
601 	}
602 	grabbag__replaygain_get_album(&album_gain, &album_peak);
603 
604 	for(i = 0; i < num_files; i++) {
605 		if(!scan) {
606 			if(0 != (error = grabbag__replaygain_store_to_file(filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i], preserve_modtime))) {
607 				flac_fprintf(stderr, "%s: ERROR: writing tags (%s)\n", filenames[i], error);
608 				free(title_gains);
609 				free(title_peaks);
610 				return false;
611 			}
612 		} else {
613 			flac_fprintf(stdout, "%s: %f %f %f %f\n", filenames[i], album_gain, album_peak, title_gains[i], title_peaks[i]);
614 		}
615 	}
616 
617 	free(title_gains);
618 	free(title_peaks);
619 	return true;
620 }
621 
do_shorthand_operation__add_padding(const char * filename,FLAC__Metadata_Chain * chain,unsigned length,FLAC__bool * needs_write)622 FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
623 {
624 	FLAC__StreamMetadata *padding = 0;
625 	FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
626 
627 	if(0 == iterator)
628 		die("out of memory allocating iterator");
629 
630 	FLAC__metadata_iterator_init(iterator, chain);
631 
632 	while(FLAC__metadata_iterator_next(iterator))
633 		;
634 
635 	padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
636 	if(0 == padding)
637 		die("out of memory allocating PADDING block");
638 
639 	padding->length = length;
640 
641 	if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) {
642 		print_error_with_chain_status(chain, "%s: ERROR: adding new PADDING block to metadata", filename);
643 		FLAC__metadata_object_delete(padding);
644 		FLAC__metadata_iterator_delete(iterator);
645 		return false;
646 	}
647 
648 	FLAC__metadata_iterator_delete(iterator);
649 	*needs_write = true;
650 	return true;
651 }
652 
passes_filter(const CommandLineOptions * options,const FLAC__StreamMetadata * block,unsigned block_number)653 FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number)
654 {
655 	unsigned i, j;
656 	FLAC__bool matches_number = false, matches_type = false;
657 	FLAC__bool has_block_number_arg = false;
658 
659 	for(i = 0; i < options->args.num_arguments; i++) {
660 		if(options->args.arguments[i].type == ARG__BLOCK_TYPE || options->args.arguments[i].type == ARG__EXCEPT_BLOCK_TYPE) {
661 			for(j = 0; j < options->args.arguments[i].value.block_type.num_entries; j++) {
662 				if(options->args.arguments[i].value.block_type.entries[j].type == block->type) {
663 					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))
664 						matches_type = true;
665 				}
666 			}
667 		}
668 		else if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
669 			has_block_number_arg = true;
670 			for(j = 0; j < options->args.arguments[i].value.block_number.num_entries; j++) {
671 				if(options->args.arguments[i].value.block_number.entries[j] == block_number)
672 					matches_number = true;
673 			}
674 		}
675 	}
676 
677 	if(!has_block_number_arg)
678 		matches_number = true;
679 
680 	if(options->args.checks.has_block_type) {
681 		FLAC__ASSERT(!options->args.checks.has_except_block_type);
682 	}
683 	else if(options->args.checks.has_except_block_type)
684 		matches_type = !matches_type;
685 	else
686 		matches_type = true;
687 
688 	return matches_number && matches_type;
689 }
690 
write_metadata(const char * filename,FLAC__StreamMetadata * block,unsigned block_number,FLAC__bool raw,FLAC__bool hexdump_application)691 void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application)
692 {
693 	unsigned i, j;
694 
695 /*@@@ yuck, should do this with a varargs function or something: */
696 #define PPR if(filename) { if(raw) printf("%s:",filename); else flac_printf("%s:",filename); }
697 	PPR; printf("METADATA block #%u\n", block_number);
698 	PPR; printf("  type: %u (%s)\n", (unsigned)block->type, block->type < FLAC__METADATA_TYPE_UNDEFINED? FLAC__MetadataTypeString[block->type] : "UNKNOWN");
699 	PPR; printf("  is last: %s\n", block->is_last? "true":"false");
700 	PPR; printf("  length: %u\n", block->length);
701 
702 	switch(block->type) {
703 		case FLAC__METADATA_TYPE_STREAMINFO:
704 			PPR; printf("  minimum blocksize: %u samples\n", block->data.stream_info.min_blocksize);
705 			PPR; printf("  maximum blocksize: %u samples\n", block->data.stream_info.max_blocksize);
706 			PPR; printf("  minimum framesize: %u bytes\n", block->data.stream_info.min_framesize);
707 			PPR; printf("  maximum framesize: %u bytes\n", block->data.stream_info.max_framesize);
708 			PPR; printf("  sample_rate: %u Hz\n", block->data.stream_info.sample_rate);
709 			PPR; printf("  channels: %u\n", block->data.stream_info.channels);
710 			PPR; printf("  bits-per-sample: %u\n", block->data.stream_info.bits_per_sample);
711 			PPR; printf("  total samples: %" PRIu64 "\n", block->data.stream_info.total_samples);
712 			PPR; printf("  MD5 signature: ");
713 			for(i = 0; i < 16; i++) {
714 				printf("%02x", (unsigned)block->data.stream_info.md5sum[i]);
715 			}
716 			printf("\n");
717 			break;
718 		case FLAC__METADATA_TYPE_PADDING:
719 			/* nothing to print */
720 			break;
721 		case FLAC__METADATA_TYPE_APPLICATION:
722 			PPR; printf("  application ID: ");
723 			for(i = 0; i < 4; i++)
724 				printf("%02x", block->data.application.id[i]);
725 			printf("\n");
726 			PPR; printf("  data contents:\n");
727 			if(0 != block->data.application.data) {
728 				if(hexdump_application)
729 					hexdump(filename, block->data.application.data, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, "    ");
730 				else
731 					(void) local_fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
732 			}
733 			break;
734 		case FLAC__METADATA_TYPE_SEEKTABLE:
735 			PPR; printf("  seek points: %u\n", block->data.seek_table.num_points);
736 			for(i = 0; i < block->data.seek_table.num_points; i++) {
737 				if(block->data.seek_table.points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
738 					PPR; printf("    point %u: sample_number=%" PRIu64 ", stream_offset=%" PRIu64 ", 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);
739 				}
740 				else {
741 					PPR; printf("    point %u: PLACEHOLDER\n", i);
742 				}
743 			}
744 			break;
745 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
746 			PPR; printf("  vendor string: ");
747 			write_vc_field(0, &block->data.vorbis_comment.vendor_string, raw, stdout);
748 			PPR; printf("  comments: %u\n", block->data.vorbis_comment.num_comments);
749 			for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
750 				PPR; printf("    comment[%u]: ", i);
751 				write_vc_field(0, &block->data.vorbis_comment.comments[i], raw, stdout);
752 			}
753 			break;
754 		case FLAC__METADATA_TYPE_CUESHEET:
755 			PPR; printf("  media catalog number: %s\n", block->data.cue_sheet.media_catalog_number);
756 			PPR; printf("  lead-in: %" PRIu64 "\n", block->data.cue_sheet.lead_in);
757 			PPR; printf("  is CD: %s\n", block->data.cue_sheet.is_cd? "true":"false");
758 			PPR; printf("  number of tracks: %u\n", block->data.cue_sheet.num_tracks);
759 			for(i = 0; i < block->data.cue_sheet.num_tracks; i++) {
760 				const FLAC__StreamMetadata_CueSheet_Track *track = block->data.cue_sheet.tracks+i;
761 				const FLAC__bool is_last = (i == block->data.cue_sheet.num_tracks-1);
762 				const FLAC__bool is_leadout = is_last && track->num_indices == 0;
763 				PPR; printf("    track[%u]\n", i);
764 				PPR; printf("      offset: %" PRIu64 "\n", track->offset);
765 				if(is_last) {
766 					PPR; printf("      number: %u (%s)\n", (unsigned)track->number, is_leadout? "LEAD-OUT" : "INVALID");
767 				}
768 				else {
769 					PPR; printf("      number: %u\n", (unsigned)track->number);
770 				}
771 				if(!is_leadout) {
772 					PPR; printf("      ISRC: %s\n", track->isrc);
773 					PPR; printf("      type: %s\n", track->type == 1? "DATA" : "AUDIO");
774 					PPR; printf("      pre-emphasis: %s\n", track->pre_emphasis? "true":"false");
775 					PPR; printf("      number of index points: %u\n", track->num_indices);
776 					for(j = 0; j < track->num_indices; j++) {
777 						const FLAC__StreamMetadata_CueSheet_Index *indx = track->indices+j;
778 						PPR; printf("        index[%u]\n", j);
779 						PPR; printf("          offset: %" PRIu64 "\n", indx->offset);
780 						PPR; printf("          number: %u\n", (unsigned)indx->number);
781 					}
782 				}
783 			}
784 			break;
785 		case FLAC__METADATA_TYPE_PICTURE:
786 			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");
787 			PPR; printf("  MIME type: %s\n", block->data.picture.mime_type);
788 			PPR; printf("  description: %s\n", block->data.picture.description);
789 			PPR; printf("  width: %u\n", (unsigned)block->data.picture.width);
790 			PPR; printf("  height: %u\n", (unsigned)block->data.picture.height);
791 			PPR; printf("  depth: %u\n", (unsigned)block->data.picture.depth);
792 			PPR; printf("  colors: %u%s\n", (unsigned)block->data.picture.colors, block->data.picture.colors? "" : " (unindexed)");
793 			PPR; printf("  data length: %u\n", (unsigned)block->data.picture.data_length);
794 			PPR; printf("  data:\n");
795 			if(0 != block->data.picture.data)
796 				hexdump(filename, block->data.picture.data, block->data.picture.data_length, "    ");
797 			break;
798 		default:
799 			PPR; printf("  data contents:\n");
800 			if(0 != block->data.unknown.data)
801 				hexdump(filename, block->data.unknown.data, block->length, "    ");
802 			break;
803 	}
804 #undef PPR
805 }
806 
write_metadata_binary(FLAC__StreamMetadata * block,FLAC__byte * block_raw,FLAC__bool headerless)807 void write_metadata_binary(FLAC__StreamMetadata *block, FLAC__byte *block_raw, FLAC__bool headerless)
808 {
809 #ifdef _WIN32
810 	fflush(stdout);
811 	_setmode(fileno(stdout),_O_BINARY);
812 #endif
813 	if(!headerless)
814 		local_fwrite(block_raw, 1, block->length+FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
815 	else if(block->type == FLAC__METADATA_TYPE_APPLICATION && block->length > 3)
816 		local_fwrite(block_raw+FLAC__STREAM_METADATA_HEADER_LENGTH+4, 1, block->length-4, stdout);
817 	else
818 		local_fwrite(block_raw+FLAC__STREAM_METADATA_HEADER_LENGTH, 1, block->length, stdout);
819 #ifdef _WIN32
820 	fflush(stdout);
821 	_setmode(fileno(stdout),_O_TEXT);
822 #endif
823 }
824