• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001,2002,2003,2004,2005,2006,2007  Josh Coalson
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of the Xiph.org Foundation nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #if HAVE_CONFIG_H
33 #  include <config.h>
34 #endif
35 
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include "private/metadata.h"
40 
41 #include "FLAC/assert.h"
42 #include "share/alloc.h"
43 
44 
45 /****************************************************************************
46  *
47  * Local routines
48  *
49  ***************************************************************************/
50 
51 /* copy bytes:
52  *  from = NULL  && bytes = 0
53  *       to <- NULL
54  *  from != NULL && bytes > 0
55  *       to <- copy of from
56  *  else ASSERT
57  * malloc error leaves 'to' unchanged
58  */
copy_bytes_(FLAC__byte ** to,const FLAC__byte * from,unsigned bytes)59 static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
60 {
61 	FLAC__ASSERT(0 != to);
62 	if(bytes > 0 && 0 != from) {
63 		FLAC__byte *x;
64 		if(0 == (x = (FLAC__byte*)safe_malloc_(bytes)))
65 			return false;
66 		memcpy(x, from, bytes);
67 		*to = x;
68 	}
69 	else {
70 		FLAC__ASSERT(0 == from);
71 		FLAC__ASSERT(bytes == 0);
72 		*to = 0;
73 	}
74 	return true;
75 }
76 
77 #if 0 /* UNUSED */
78 /* like copy_bytes_(), but free()s the original '*to' if the copy succeeds and the original '*to' is non-NULL */
79 static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
80 {
81 	FLAC__byte *copy;
82 	FLAC__ASSERT(0 != to);
83 	if(copy_bytes_(&copy, from, bytes)) {
84 		if(*to)
85 			free(*to);
86 		*to = copy;
87 		return true;
88 	}
89 	else
90 		return false;
91 }
92 #endif
93 
94 /* reallocate entry to 1 byte larger and add a terminating NUL */
95 /* realloc() failure leaves entry unchanged */
ensure_null_terminated_(FLAC__byte ** entry,unsigned length)96 static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length)
97 {
98 	FLAC__byte *x = (FLAC__byte*)safe_realloc_add_2op_(*entry, length, /*+*/1);
99 	if(0 != x) {
100 		x[length] = '\0';
101 		*entry = x;
102 		return true;
103 	}
104 	else
105 		return false;
106 }
107 
108 /* copies the NUL-terminated C-string 'from' to '*to', leaving '*to'
109  * unchanged if malloc fails, free()ing the original '*to' if it
110  * succeeds and the original '*to' was not NULL
111  */
copy_cstring_(char ** to,const char * from)112 static FLAC__bool copy_cstring_(char **to, const char *from)
113 {
114 	char *copy = strdup(from);
115 	FLAC__ASSERT(to);
116 	if(copy) {
117 		if(*to)
118 			free(*to);
119 		*to = copy;
120 		return true;
121 	}
122 	else
123 		return false;
124 }
125 
copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry * to,const FLAC__StreamMetadata_VorbisComment_Entry * from)126 static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
127 {
128 	to->length = from->length;
129 	if(0 == from->entry) {
130 		FLAC__ASSERT(from->length == 0);
131 		to->entry = 0;
132 	}
133 	else {
134 		FLAC__byte *x;
135 		FLAC__ASSERT(from->length > 0);
136 		if(0 == (x = (FLAC__byte*)safe_malloc_add_2op_(from->length, /*+*/1)))
137 			return false;
138 		memcpy(x, from->entry, from->length);
139 		x[from->length] = '\0';
140 		to->entry = x;
141 	}
142 	return true;
143 }
144 
copy_track_(FLAC__StreamMetadata_CueSheet_Track * to,const FLAC__StreamMetadata_CueSheet_Track * from)145 static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
146 {
147 	memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
148 	if(0 == from->indices) {
149 		FLAC__ASSERT(from->num_indices == 0);
150 	}
151 	else {
152 		FLAC__StreamMetadata_CueSheet_Index *x;
153 		FLAC__ASSERT(from->num_indices > 0);
154 		if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)safe_malloc_mul_2op_(from->num_indices, /*times*/sizeof(FLAC__StreamMetadata_CueSheet_Index))))
155 			return false;
156 		memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
157 		to->indices = x;
158 	}
159 	return true;
160 }
161 
seektable_calculate_length_(FLAC__StreamMetadata * object)162 static void seektable_calculate_length_(FLAC__StreamMetadata *object)
163 {
164 	FLAC__ASSERT(0 != object);
165 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
166 
167 	object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
168 }
169 
seekpoint_array_new_(unsigned num_points)170 static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points)
171 {
172 	FLAC__StreamMetadata_SeekPoint *object_array;
173 
174 	FLAC__ASSERT(num_points > 0);
175 
176 	object_array = (FLAC__StreamMetadata_SeekPoint*)safe_malloc_mul_2op_(num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint));
177 
178 	if(0 != object_array) {
179 		unsigned i;
180 		for(i = 0; i < num_points; i++) {
181 			object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
182 			object_array[i].stream_offset = 0;
183 			object_array[i].frame_samples = 0;
184 		}
185 	}
186 
187 	return object_array;
188 }
189 
vorbiscomment_calculate_length_(FLAC__StreamMetadata * object)190 static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
191 {
192 	unsigned i;
193 
194 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
195 
196 	object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
197 	object->length += object->data.vorbis_comment.vendor_string.length;
198 	object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
199 	for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
200 		object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
201 		object->length += object->data.vorbis_comment.comments[i].length;
202 	}
203 }
204 
vorbiscomment_entry_array_new_(unsigned num_comments)205 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
206 {
207 	FLAC__ASSERT(num_comments > 0);
208 
209 	return (FLAC__StreamMetadata_VorbisComment_Entry*)safe_calloc_(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
210 }
211 
vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry * object_array,unsigned num_comments)212 static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
213 {
214 	unsigned i;
215 
216 	FLAC__ASSERT(0 != object_array && num_comments > 0);
217 
218 	for(i = 0; i < num_comments; i++)
219 		if(0 != object_array[i].entry)
220 			free(object_array[i].entry);
221 
222 	if(0 != object_array)
223 		free(object_array);
224 }
225 
vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry * object_array,unsigned num_comments)226 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
227 {
228 	FLAC__StreamMetadata_VorbisComment_Entry *return_array;
229 
230 	FLAC__ASSERT(0 != object_array);
231 	FLAC__ASSERT(num_comments > 0);
232 
233 	return_array = vorbiscomment_entry_array_new_(num_comments);
234 
235 	if(0 != return_array) {
236 		unsigned i;
237 
238 		for(i = 0; i < num_comments; i++) {
239 			if(!copy_vcentry_(return_array+i, object_array+i)) {
240 				vorbiscomment_entry_array_delete_(return_array, num_comments);
241 				return 0;
242 			}
243 		}
244 	}
245 
246 	return return_array;
247 }
248 
vorbiscomment_set_entry_(FLAC__StreamMetadata * object,FLAC__StreamMetadata_VorbisComment_Entry * dest,const FLAC__StreamMetadata_VorbisComment_Entry * src,FLAC__bool copy)249 static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
250 {
251 	FLAC__byte *save;
252 
253 	FLAC__ASSERT(0 != object);
254 	FLAC__ASSERT(0 != dest);
255 	FLAC__ASSERT(0 != src);
256 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
257 	FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0));
258 
259 	save = dest->entry;
260 
261 	if(0 != src->entry && src->length > 0) {
262 		if(copy) {
263 			/* do the copy first so that if we fail we leave the dest object untouched */
264 			if(!copy_vcentry_(dest, src))
265 				return false;
266 		}
267 		else {
268 			/* we have to make sure that the string we're taking over is null-terminated */
269 
270 			/*
271 			 * Stripping the const from src->entry is OK since we're taking
272 			 * ownership of the pointer.  This is a hack around a deficiency
273 			 * in the API where the same function is used for 'copy' and
274 			 * 'own', but the source entry is a const pointer.  If we were
275 			 * precise, the 'own' flavor would be a separate function with a
276 			 * non-const source pointer.  But it's not, so we hack away.
277 			 */
278 			if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length))
279 				return false;
280 			*dest = *src;
281 		}
282 	}
283 	else {
284 		/* the src is null */
285 		*dest = *src;
286 	}
287 
288 	if(0 != save)
289 		free(save);
290 
291 	vorbiscomment_calculate_length_(object);
292 	return true;
293 }
294 
vorbiscomment_find_entry_from_(const FLAC__StreamMetadata * object,unsigned offset,const char * field_name,unsigned field_name_length)295 static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length)
296 {
297 	unsigned i;
298 
299 	FLAC__ASSERT(0 != object);
300 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
301 	FLAC__ASSERT(0 != field_name);
302 
303 	for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
304 		if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length))
305 			return (int)i;
306 	}
307 
308 	return -1;
309 }
310 
cuesheet_calculate_length_(FLAC__StreamMetadata * object)311 static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
312 {
313 	unsigned i;
314 
315 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
316 
317 	object->length = (
318 		FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
319 		FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
320 		FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN +
321 		FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN +
322 		FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
323 	) / 8;
324 
325 	object->length += object->data.cue_sheet.num_tracks * (
326 		FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
327 		FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
328 		FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
329 		FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
330 		FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
331 		FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
332 		FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
333 	) / 8;
334 
335 	for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
336 		object->length += object->data.cue_sheet.tracks[i].num_indices * (
337 			FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
338 			FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
339 			FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
340 		) / 8;
341 	}
342 }
343 
cuesheet_track_index_array_new_(unsigned num_indices)344 static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
345 {
346 	FLAC__ASSERT(num_indices > 0);
347 
348 	return (FLAC__StreamMetadata_CueSheet_Index*)safe_calloc_(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
349 }
350 
cuesheet_track_array_new_(unsigned num_tracks)351 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
352 {
353 	FLAC__ASSERT(num_tracks > 0);
354 
355 	return (FLAC__StreamMetadata_CueSheet_Track*)safe_calloc_(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
356 }
357 
cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track * object_array,unsigned num_tracks)358 static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
359 {
360 	unsigned i;
361 
362 	FLAC__ASSERT(0 != object_array && num_tracks > 0);
363 
364 	for(i = 0; i < num_tracks; i++) {
365 		if(0 != object_array[i].indices) {
366 			FLAC__ASSERT(object_array[i].num_indices > 0);
367 			free(object_array[i].indices);
368 		}
369 	}
370 
371 	if(0 != object_array)
372 		free(object_array);
373 }
374 
cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track * object_array,unsigned num_tracks)375 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
376 {
377 	FLAC__StreamMetadata_CueSheet_Track *return_array;
378 
379 	FLAC__ASSERT(0 != object_array);
380 	FLAC__ASSERT(num_tracks > 0);
381 
382 	return_array = cuesheet_track_array_new_(num_tracks);
383 
384 	if(0 != return_array) {
385 		unsigned i;
386 
387 		for(i = 0; i < num_tracks; i++) {
388 			if(!copy_track_(return_array+i, object_array+i)) {
389 				cuesheet_track_array_delete_(return_array, num_tracks);
390 				return 0;
391 			}
392 		}
393 	}
394 
395 	return return_array;
396 }
397 
cuesheet_set_track_(FLAC__StreamMetadata * object,FLAC__StreamMetadata_CueSheet_Track * dest,const FLAC__StreamMetadata_CueSheet_Track * src,FLAC__bool copy)398 static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
399 {
400 	FLAC__StreamMetadata_CueSheet_Index *save;
401 
402 	FLAC__ASSERT(0 != object);
403 	FLAC__ASSERT(0 != dest);
404 	FLAC__ASSERT(0 != src);
405 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
406 	FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
407 
408 	save = dest->indices;
409 
410 	/* do the copy first so that if we fail we leave the object untouched */
411 	if(copy) {
412 		if(!copy_track_(dest, src))
413 			return false;
414 	}
415 	else {
416 		*dest = *src;
417 	}
418 
419 	if(0 != save)
420 		free(save);
421 
422 	cuesheet_calculate_length_(object);
423 	return true;
424 }
425 
426 
427 /****************************************************************************
428  *
429  * Metadata object routines
430  *
431  ***************************************************************************/
432 
FLAC__metadata_object_new(FLAC__MetadataType type)433 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
434 {
435 	FLAC__StreamMetadata *object;
436 
437 	if(type > FLAC__MAX_METADATA_TYPE_CODE)
438 		return 0;
439 
440 	object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
441 	if(0 != object) {
442 		object->is_last = false;
443 		object->type = type;
444 		switch(type) {
445 			case FLAC__METADATA_TYPE_STREAMINFO:
446 				object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
447 				break;
448 			case FLAC__METADATA_TYPE_PADDING:
449 				/* calloc() took care of this for us:
450 				object->length = 0;
451 				*/
452 				break;
453 			case FLAC__METADATA_TYPE_APPLICATION:
454 				object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
455 				/* calloc() took care of this for us:
456 				object->data.application.data = 0;
457 				*/
458 				break;
459 			case FLAC__METADATA_TYPE_SEEKTABLE:
460 				/* calloc() took care of this for us:
461 				object->length = 0;
462 				object->data.seek_table.num_points = 0;
463 				object->data.seek_table.points = 0;
464 				*/
465 				break;
466 			case FLAC__METADATA_TYPE_VORBIS_COMMENT:
467 				object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
468 				if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) {
469 					free(object);
470 					return 0;
471 				}
472 				vorbiscomment_calculate_length_(object);
473 				break;
474 			case FLAC__METADATA_TYPE_CUESHEET:
475 				cuesheet_calculate_length_(object);
476 				break;
477 			case FLAC__METADATA_TYPE_PICTURE:
478 				object->length = (
479 					FLAC__STREAM_METADATA_PICTURE_TYPE_LEN +
480 					FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */
481 					FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */
482 					FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN +
483 					FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN +
484 					FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN +
485 					FLAC__STREAM_METADATA_PICTURE_COLORS_LEN +
486 					FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN +
487 					0 /* no data */
488 				) / 8;
489 				object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER;
490 				object->data.picture.mime_type = 0;
491 				object->data.picture.description = 0;
492 				/* calloc() took care of this for us:
493 				object->data.picture.width = 0;
494 				object->data.picture.height = 0;
495 				object->data.picture.depth = 0;
496 				object->data.picture.colors = 0;
497 				object->data.picture.data_length = 0;
498 				object->data.picture.data = 0;
499 				*/
500 				/* now initialize mime_type and description with empty strings to make things easier on the client */
501 				if(!copy_cstring_(&object->data.picture.mime_type, "")) {
502 					free(object);
503 					return 0;
504 				}
505 				if(!copy_cstring_((char**)(&object->data.picture.description), "")) {
506 					if(object->data.picture.mime_type)
507 						free(object->data.picture.mime_type);
508 					free(object);
509 					return 0;
510 				}
511 				break;
512 			default:
513 				/* calloc() took care of this for us:
514 				object->length = 0;
515 				object->data.unknown.data = 0;
516 				*/
517 				break;
518 		}
519 	}
520 
521 	return object;
522 }
523 
FLAC__metadata_object_clone(const FLAC__StreamMetadata * object)524 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
525 {
526 	FLAC__StreamMetadata *to;
527 
528 	FLAC__ASSERT(0 != object);
529 
530 	if(0 != (to = FLAC__metadata_object_new(object->type))) {
531 		to->is_last = object->is_last;
532 		to->type = object->type;
533 		to->length = object->length;
534 		switch(to->type) {
535 			case FLAC__METADATA_TYPE_STREAMINFO:
536 				memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
537 				break;
538 			case FLAC__METADATA_TYPE_PADDING:
539 				break;
540 			case FLAC__METADATA_TYPE_APPLICATION:
541 				if(to->length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) { /* underflow check */
542 					FLAC__metadata_object_delete(to);
543 					return 0;
544 				}
545 				memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
546 				if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
547 					FLAC__metadata_object_delete(to);
548 					return 0;
549 				}
550 				break;
551 			case FLAC__METADATA_TYPE_SEEKTABLE:
552 				to->data.seek_table.num_points = object->data.seek_table.num_points;
553 				if(to->data.seek_table.num_points > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */
554 					FLAC__metadata_object_delete(to);
555 					return 0;
556 				}
557 				if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) {
558 					FLAC__metadata_object_delete(to);
559 					return 0;
560 				}
561 				break;
562 			case FLAC__METADATA_TYPE_VORBIS_COMMENT:
563 				if(0 != to->data.vorbis_comment.vendor_string.entry) {
564 					free(to->data.vorbis_comment.vendor_string.entry);
565 					to->data.vorbis_comment.vendor_string.entry = 0;
566 				}
567 				if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
568 					FLAC__metadata_object_delete(to);
569 					return 0;
570 				}
571 				if(object->data.vorbis_comment.num_comments == 0) {
572 					FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
573 					to->data.vorbis_comment.comments = 0;
574 				}
575 				else {
576 					FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
577 					to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
578 					if(0 == to->data.vorbis_comment.comments) {
579 						FLAC__metadata_object_delete(to);
580 						return 0;
581 					}
582 				}
583 				to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
584 				break;
585 			case FLAC__METADATA_TYPE_CUESHEET:
586 				memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
587 				if(object->data.cue_sheet.num_tracks == 0) {
588 					FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
589 				}
590 				else {
591 					FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
592 					to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
593 					if(0 == to->data.cue_sheet.tracks) {
594 						FLAC__metadata_object_delete(to);
595 						return 0;
596 					}
597 				}
598 				break;
599 			case FLAC__METADATA_TYPE_PICTURE:
600 				to->data.picture.type = object->data.picture.type;
601 				if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) {
602 					FLAC__metadata_object_delete(to);
603 					return 0;
604 				}
605 				if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) {
606 					FLAC__metadata_object_delete(to);
607 					return 0;
608 				}
609 				to->data.picture.width = object->data.picture.width;
610 				to->data.picture.height = object->data.picture.height;
611 				to->data.picture.depth = object->data.picture.depth;
612 				to->data.picture.colors = object->data.picture.colors;
613 				to->data.picture.data_length = object->data.picture.data_length;
614 				if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) {
615 					FLAC__metadata_object_delete(to);
616 					return 0;
617 				}
618 				break;
619 			default:
620 				if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
621 					FLAC__metadata_object_delete(to);
622 					return 0;
623 				}
624 				break;
625 		}
626 	}
627 
628 	return to;
629 }
630 
FLAC__metadata_object_delete_data(FLAC__StreamMetadata * object)631 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
632 {
633 	FLAC__ASSERT(0 != object);
634 
635 	switch(object->type) {
636 		case FLAC__METADATA_TYPE_STREAMINFO:
637 		case FLAC__METADATA_TYPE_PADDING:
638 			break;
639 		case FLAC__METADATA_TYPE_APPLICATION:
640 			if(0 != object->data.application.data) {
641 				free(object->data.application.data);
642 				object->data.application.data = 0;
643 			}
644 			break;
645 		case FLAC__METADATA_TYPE_SEEKTABLE:
646 			if(0 != object->data.seek_table.points) {
647 				free(object->data.seek_table.points);
648 				object->data.seek_table.points = 0;
649 			}
650 			break;
651 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
652 			if(0 != object->data.vorbis_comment.vendor_string.entry) {
653 				free(object->data.vorbis_comment.vendor_string.entry);
654 				object->data.vorbis_comment.vendor_string.entry = 0;
655 			}
656 			if(0 != object->data.vorbis_comment.comments) {
657 				FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
658 				vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
659 			}
660 			break;
661 		case FLAC__METADATA_TYPE_CUESHEET:
662 			if(0 != object->data.cue_sheet.tracks) {
663 				FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
664 				cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
665 			}
666 			break;
667 		case FLAC__METADATA_TYPE_PICTURE:
668 			if(0 != object->data.picture.mime_type) {
669 				free(object->data.picture.mime_type);
670 				object->data.picture.mime_type = 0;
671 			}
672 			if(0 != object->data.picture.description) {
673 				free(object->data.picture.description);
674 				object->data.picture.description = 0;
675 			}
676 			if(0 != object->data.picture.data) {
677 				free(object->data.picture.data);
678 				object->data.picture.data = 0;
679 			}
680 			break;
681 		default:
682 			if(0 != object->data.unknown.data) {
683 				free(object->data.unknown.data);
684 				object->data.unknown.data = 0;
685 			}
686 			break;
687 	}
688 }
689 
FLAC__metadata_object_delete(FLAC__StreamMetadata * object)690 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
691 {
692 	FLAC__metadata_object_delete_data(object);
693 	free(object);
694 }
695 
compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo * block1,const FLAC__StreamMetadata_StreamInfo * block2)696 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
697 {
698 	if(block1->min_blocksize != block2->min_blocksize)
699 		return false;
700 	if(block1->max_blocksize != block2->max_blocksize)
701 		return false;
702 	if(block1->min_framesize != block2->min_framesize)
703 		return false;
704 	if(block1->max_framesize != block2->max_framesize)
705 		return false;
706 	if(block1->sample_rate != block2->sample_rate)
707 		return false;
708 	if(block1->channels != block2->channels)
709 		return false;
710 	if(block1->bits_per_sample != block2->bits_per_sample)
711 		return false;
712 	if(block1->total_samples != block2->total_samples)
713 		return false;
714 	if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
715 		return false;
716 	return true;
717 }
718 
compare_block_data_application_(const FLAC__StreamMetadata_Application * block1,const FLAC__StreamMetadata_Application * block2,unsigned block_length)719 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
720 {
721 	FLAC__ASSERT(0 != block1);
722 	FLAC__ASSERT(0 != block2);
723 	FLAC__ASSERT(block_length >= sizeof(block1->id));
724 
725 	if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
726 		return false;
727 	if(0 != block1->data && 0 != block2->data)
728 		return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
729 	else
730 		return block1->data == block2->data;
731 }
732 
compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable * block1,const FLAC__StreamMetadata_SeekTable * block2)733 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
734 {
735 	unsigned i;
736 
737 	FLAC__ASSERT(0 != block1);
738 	FLAC__ASSERT(0 != block2);
739 
740 	if(block1->num_points != block2->num_points)
741 		return false;
742 
743 	if(0 != block1->points && 0 != block2->points) {
744 		for(i = 0; i < block1->num_points; i++) {
745 			if(block1->points[i].sample_number != block2->points[i].sample_number)
746 				return false;
747 			if(block1->points[i].stream_offset != block2->points[i].stream_offset)
748 				return false;
749 			if(block1->points[i].frame_samples != block2->points[i].frame_samples)
750 				return false;
751 		}
752 		return true;
753 	}
754 	else
755 		return block1->points == block2->points;
756 }
757 
compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment * block1,const FLAC__StreamMetadata_VorbisComment * block2)758 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
759 {
760 	unsigned i;
761 
762 	if(block1->vendor_string.length != block2->vendor_string.length)
763 		return false;
764 
765 	if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
766 		if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
767 			return false;
768 	}
769 	else if(block1->vendor_string.entry != block2->vendor_string.entry)
770 		return false;
771 
772 	if(block1->num_comments != block2->num_comments)
773 		return false;
774 
775 	for(i = 0; i < block1->num_comments; i++) {
776 		if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
777 			if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
778 				return false;
779 		}
780 		else if(block1->comments[i].entry != block2->comments[i].entry)
781 			return false;
782 	}
783 	return true;
784 }
785 
compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet * block1,const FLAC__StreamMetadata_CueSheet * block2)786 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
787 {
788 	unsigned i, j;
789 
790 	if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
791 		return false;
792 
793 	if(block1->lead_in != block2->lead_in)
794 		return false;
795 
796 	if(block1->is_cd != block2->is_cd)
797 		return false;
798 
799 	if(block1->num_tracks != block2->num_tracks)
800 		return false;
801 
802 	if(0 != block1->tracks && 0 != block2->tracks) {
803 		FLAC__ASSERT(block1->num_tracks > 0);
804 		for(i = 0; i < block1->num_tracks; i++) {
805 			if(block1->tracks[i].offset != block2->tracks[i].offset)
806 				return false;
807 			if(block1->tracks[i].number != block2->tracks[i].number)
808 				return false;
809 			if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
810 				return false;
811 			if(block1->tracks[i].type != block2->tracks[i].type)
812 				return false;
813 			if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
814 				return false;
815 			if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
816 				return false;
817 			if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
818 				FLAC__ASSERT(block1->tracks[i].num_indices > 0);
819 				for(j = 0; j < block1->tracks[i].num_indices; j++) {
820 					if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
821 						return false;
822 					if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
823 						return false;
824 				}
825 			}
826 			else if(block1->tracks[i].indices != block2->tracks[i].indices)
827 				return false;
828 		}
829 	}
830 	else if(block1->tracks != block2->tracks)
831 		return false;
832 	return true;
833 }
834 
compare_block_data_picture_(const FLAC__StreamMetadata_Picture * block1,const FLAC__StreamMetadata_Picture * block2)835 static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2)
836 {
837 	if(block1->type != block2->type)
838 		return false;
839 	if(block1->mime_type != block2->mime_type && (0 == block1->mime_type || 0 == block2->mime_type || strcmp(block1->mime_type, block2->mime_type)))
840 		return false;
841 	if(block1->description != block2->description && (0 == block1->description || 0 == block2->description || strcmp((const char *)block1->description, (const char *)block2->description)))
842 		return false;
843 	if(block1->width != block2->width)
844 		return false;
845 	if(block1->height != block2->height)
846 		return false;
847 	if(block1->depth != block2->depth)
848 		return false;
849 	if(block1->colors != block2->colors)
850 		return false;
851 	if(block1->data_length != block2->data_length)
852 		return false;
853 	if(block1->data != block2->data && (0 == block1->data || 0 == block2->data || memcmp(block1->data, block2->data, block1->data_length)))
854 		return false;
855 	return true;
856 }
857 
compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown * block1,const FLAC__StreamMetadata_Unknown * block2,unsigned block_length)858 static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length)
859 {
860 	FLAC__ASSERT(0 != block1);
861 	FLAC__ASSERT(0 != block2);
862 
863 	if(0 != block1->data && 0 != block2->data)
864 		return 0 == memcmp(block1->data, block2->data, block_length);
865 	else
866 		return block1->data == block2->data;
867 }
868 
FLAC__metadata_object_is_equal(const FLAC__StreamMetadata * block1,const FLAC__StreamMetadata * block2)869 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
870 {
871 	FLAC__ASSERT(0 != block1);
872 	FLAC__ASSERT(0 != block2);
873 
874 	if(block1->type != block2->type) {
875 		return false;
876 	}
877 	if(block1->is_last != block2->is_last) {
878 		return false;
879 	}
880 	if(block1->length != block2->length) {
881 		return false;
882 	}
883 	switch(block1->type) {
884 		case FLAC__METADATA_TYPE_STREAMINFO:
885 			return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
886 		case FLAC__METADATA_TYPE_PADDING:
887 			return true; /* we don't compare the padding guts */
888 		case FLAC__METADATA_TYPE_APPLICATION:
889 			return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
890 		case FLAC__METADATA_TYPE_SEEKTABLE:
891 			return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
892 		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
893 			return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
894 		case FLAC__METADATA_TYPE_CUESHEET:
895 			return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
896 		case FLAC__METADATA_TYPE_PICTURE:
897 			return compare_block_data_picture_(&block1->data.picture, &block2->data.picture);
898 		default:
899 			return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
900 	}
901 }
902 
FLAC__metadata_object_application_set_data(FLAC__StreamMetadata * object,FLAC__byte * data,unsigned length,FLAC__bool copy)903 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
904 {
905 	FLAC__byte *save;
906 
907 	FLAC__ASSERT(0 != object);
908 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
909 	FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
910 
911 	save = object->data.application.data;
912 
913 	/* do the copy first so that if we fail we leave the object untouched */
914 	if(copy) {
915 		if(!copy_bytes_(&object->data.application.data, data, length))
916 			return false;
917 	}
918 	else {
919 		object->data.application.data = data;
920 	}
921 
922 	if(0 != save)
923 		free(save);
924 
925 	object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
926 	return true;
927 }
928 
FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata * object,unsigned new_num_points)929 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
930 {
931 	FLAC__ASSERT(0 != object);
932 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
933 
934 	if(0 == object->data.seek_table.points) {
935 		FLAC__ASSERT(object->data.seek_table.num_points == 0);
936 		if(0 == new_num_points)
937 			return true;
938 		else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
939 			return false;
940 	}
941 	else {
942 		const size_t old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
943 		const size_t new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
944 
945 		/* overflow check */
946 		if((size_t)new_num_points > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint))
947 			return false;
948 
949 		FLAC__ASSERT(object->data.seek_table.num_points > 0);
950 
951 		if(new_size == 0) {
952 			free(object->data.seek_table.points);
953 			object->data.seek_table.points = 0;
954 		}
955 		else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
956 			return false;
957 
958 		/* if growing, set new elements to placeholders */
959 		if(new_size > old_size) {
960 			unsigned i;
961 			for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
962 				object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
963 				object->data.seek_table.points[i].stream_offset = 0;
964 				object->data.seek_table.points[i].frame_samples = 0;
965 			}
966 		}
967 	}
968 
969 	object->data.seek_table.num_points = new_num_points;
970 
971 	seektable_calculate_length_(object);
972 	return true;
973 }
974 
FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata * object,unsigned point_num,FLAC__StreamMetadata_SeekPoint point)975 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
976 {
977 	FLAC__ASSERT(0 != object);
978 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
979 	FLAC__ASSERT(point_num < object->data.seek_table.num_points);
980 
981 	object->data.seek_table.points[point_num] = point;
982 }
983 
FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata * object,unsigned point_num,FLAC__StreamMetadata_SeekPoint point)984 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
985 {
986 	int i;
987 
988 	FLAC__ASSERT(0 != object);
989 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
990 	FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
991 
992 	if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
993 		return false;
994 
995 	/* move all points >= point_num forward one space */
996 	for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
997 		object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
998 
999 	FLAC__metadata_object_seektable_set_point(object, point_num, point);
1000 	seektable_calculate_length_(object);
1001 	return true;
1002 }
1003 
FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata * object,unsigned point_num)1004 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
1005 {
1006 	unsigned i;
1007 
1008 	FLAC__ASSERT(0 != object);
1009 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1010 	FLAC__ASSERT(point_num < object->data.seek_table.num_points);
1011 
1012 	/* move all points > point_num backward one space */
1013 	for(i = point_num; i < object->data.seek_table.num_points-1; i++)
1014 		object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
1015 
1016 	return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
1017 }
1018 
FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata * object)1019 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
1020 {
1021 	FLAC__ASSERT(0 != object);
1022 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1023 
1024 	return FLAC__format_seektable_is_legal(&object->data.seek_table);
1025 }
1026 
FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata * object,unsigned num)1027 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
1028 {
1029 	FLAC__ASSERT(0 != object);
1030 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1031 
1032 	if(num > 0)
1033 		/* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
1034 		return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
1035 	else
1036 		return true;
1037 }
1038 
FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata * object,FLAC__uint64 sample_number)1039 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
1040 {
1041 	FLAC__StreamMetadata_SeekTable *seek_table;
1042 
1043 	FLAC__ASSERT(0 != object);
1044 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1045 
1046 	seek_table = &object->data.seek_table;
1047 
1048 	if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
1049 		return false;
1050 
1051 	seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
1052 	seek_table->points[seek_table->num_points - 1].stream_offset = 0;
1053 	seek_table->points[seek_table->num_points - 1].frame_samples = 0;
1054 
1055 	return true;
1056 }
1057 
FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata * object,FLAC__uint64 sample_numbers[],unsigned num)1058 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
1059 {
1060 	FLAC__ASSERT(0 != object);
1061 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1062 	FLAC__ASSERT(0 != sample_numbers || num == 0);
1063 
1064 	if(num > 0) {
1065 		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1066 		unsigned i, j;
1067 
1068 		i = seek_table->num_points;
1069 
1070 		if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
1071 			return false;
1072 
1073 		for(j = 0; j < num; i++, j++) {
1074 			seek_table->points[i].sample_number = sample_numbers[j];
1075 			seek_table->points[i].stream_offset = 0;
1076 			seek_table->points[i].frame_samples = 0;
1077 		}
1078 	}
1079 
1080 	return true;
1081 }
1082 
FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata * object,unsigned num,FLAC__uint64 total_samples)1083 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
1084 {
1085 	FLAC__ASSERT(0 != object);
1086 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1087 	FLAC__ASSERT(total_samples > 0);
1088 
1089 	if(num > 0 && total_samples > 0) {
1090 		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1091 		unsigned i, j;
1092 
1093 		i = seek_table->num_points;
1094 
1095 		if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
1096 			return false;
1097 
1098 		for(j = 0; j < num; i++, j++) {
1099 			seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
1100 			seek_table->points[i].stream_offset = 0;
1101 			seek_table->points[i].frame_samples = 0;
1102 		}
1103 	}
1104 
1105 	return true;
1106 }
1107 
FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata * object,unsigned samples,FLAC__uint64 total_samples)1108 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples)
1109 {
1110 	FLAC__ASSERT(0 != object);
1111 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1112 	FLAC__ASSERT(samples > 0);
1113 	FLAC__ASSERT(total_samples > 0);
1114 
1115 	if(samples > 0 && total_samples > 0) {
1116 		FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1117 		unsigned i, j;
1118 		FLAC__uint64 num, sample;
1119 
1120 		num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */
1121 		/* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */
1122 		if(total_samples % samples == 0)
1123 			num--;
1124 
1125 		i = seek_table->num_points;
1126 
1127 		if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (unsigned)num))
1128 			return false;
1129 
1130 		sample = 0;
1131 		for(j = 0; j < num; i++, j++, sample += samples) {
1132 			seek_table->points[i].sample_number = sample;
1133 			seek_table->points[i].stream_offset = 0;
1134 			seek_table->points[i].frame_samples = 0;
1135 		}
1136 	}
1137 
1138 	return true;
1139 }
1140 
FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata * object,FLAC__bool compact)1141 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
1142 {
1143 	unsigned unique;
1144 
1145 	FLAC__ASSERT(0 != object);
1146 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1147 
1148 	unique = FLAC__format_seektable_sort(&object->data.seek_table);
1149 
1150 	return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
1151 }
1152 
FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata * object,FLAC__StreamMetadata_VorbisComment_Entry entry,FLAC__bool copy)1153 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1154 {
1155 	if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length))
1156 		return false;
1157 	return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
1158 }
1159 
FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata * object,unsigned new_num_comments)1160 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
1161 {
1162 	FLAC__ASSERT(0 != object);
1163 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1164 
1165 	if(0 == object->data.vorbis_comment.comments) {
1166 		FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
1167 		if(0 == new_num_comments)
1168 			return true;
1169 		else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
1170 			return false;
1171 	}
1172 	else {
1173 		const size_t old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
1174 		const size_t new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
1175 
1176 		/* overflow check */
1177 		if((size_t)new_num_comments > SIZE_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry))
1178 			return false;
1179 
1180 		FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
1181 
1182 		/* if shrinking, free the truncated entries */
1183 		if(new_num_comments < object->data.vorbis_comment.num_comments) {
1184 			unsigned i;
1185 			for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
1186 				if(0 != object->data.vorbis_comment.comments[i].entry)
1187 					free(object->data.vorbis_comment.comments[i].entry);
1188 		}
1189 
1190 		if(new_size == 0) {
1191 			free(object->data.vorbis_comment.comments);
1192 			object->data.vorbis_comment.comments = 0;
1193 		}
1194 		else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
1195 			return false;
1196 
1197 		/* if growing, zero all the length/pointers of new elements */
1198 		if(new_size > old_size)
1199 			memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
1200 	}
1201 
1202 	object->data.vorbis_comment.num_comments = new_num_comments;
1203 
1204 	vorbiscomment_calculate_length_(object);
1205 	return true;
1206 }
1207 
FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata * object,unsigned comment_num,FLAC__StreamMetadata_VorbisComment_Entry entry,FLAC__bool copy)1208 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1209 {
1210 	FLAC__ASSERT(0 != object);
1211 	FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1212 
1213 	if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1214 		return false;
1215 	return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
1216 }
1217 
FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata * object,unsigned comment_num,FLAC__StreamMetadata_VorbisComment_Entry entry,FLAC__bool copy)1218 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1219 {
1220 	FLAC__StreamMetadata_VorbisComment *vc;
1221 
1222 	FLAC__ASSERT(0 != object);
1223 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1224 	FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
1225 
1226 	if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1227 		return false;
1228 
1229 	vc = &object->data.vorbis_comment;
1230 
1231 	if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
1232 		return false;
1233 
1234 	/* move all comments >= comment_num forward one space */
1235 	memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
1236 	vc->comments[comment_num].length = 0;
1237 	vc->comments[comment_num].entry = 0;
1238 
1239 	return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
1240 }
1241 
FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata * object,FLAC__StreamMetadata_VorbisComment_Entry entry,FLAC__bool copy)1242 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1243 {
1244 	FLAC__ASSERT(0 != object);
1245 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1246 	return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy);
1247 }
1248 
FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata * object,FLAC__StreamMetadata_VorbisComment_Entry entry,FLAC__bool all,FLAC__bool copy)1249 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy)
1250 {
1251 	FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1252 
1253 	if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1254 		return false;
1255 
1256 	{
1257 		int i;
1258 		size_t field_name_length;
1259 		const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1260 
1261 		FLAC__ASSERT(0 != eq);
1262 
1263 		if(0 == eq)
1264 			return false; /* double protection */
1265 
1266 		field_name_length = eq-entry.entry;
1267 
1268 		if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) {
1269 			unsigned index = (unsigned)i;
1270 			if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy))
1271 				return false;
1272 			if(all && (index+1 < object->data.vorbis_comment.num_comments)) {
1273 				for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) {
1274 					if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i))
1275 						return false;
1276 					if((unsigned)i < object->data.vorbis_comment.num_comments)
1277 						i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length);
1278 					else
1279 						i = -1;
1280 				}
1281 			}
1282 			return true;
1283 		}
1284 		else
1285 			return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
1286 	}
1287 }
1288 
FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata * object,unsigned comment_num)1289 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
1290 {
1291 	FLAC__StreamMetadata_VorbisComment *vc;
1292 
1293 	FLAC__ASSERT(0 != object);
1294 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1295 	FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1296 
1297 	vc = &object->data.vorbis_comment;
1298 
1299 	/* free the comment at comment_num */
1300 	if(0 != vc->comments[comment_num].entry)
1301 		free(vc->comments[comment_num].entry);
1302 
1303 	/* move all comments > comment_num backward one space */
1304 	memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
1305 	vc->comments[vc->num_comments-1].length = 0;
1306 	vc->comments[vc->num_comments-1].entry = 0;
1307 
1308 	return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
1309 }
1310 
FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry * entry,const char * field_name,const char * field_value)1311 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value)
1312 {
1313 	FLAC__ASSERT(0 != entry);
1314 	FLAC__ASSERT(0 != field_name);
1315 	FLAC__ASSERT(0 != field_value);
1316 
1317 	if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
1318 		return false;
1319 	if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1)))
1320 		return false;
1321 
1322 	{
1323 		const size_t nn = strlen(field_name);
1324 		const size_t nv = strlen(field_value);
1325 		entry->length = nn + 1 /*=*/ + nv;
1326 		if(0 == (entry->entry = (FLAC__byte*)safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1)))
1327 			return false;
1328 		memcpy(entry->entry, field_name, nn);
1329 		entry->entry[nn] = '=';
1330 		memcpy(entry->entry+nn+1, field_value, nv);
1331 		entry->entry[entry->length] = '\0';
1332 	}
1333 
1334 	return true;
1335 }
1336 
FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry,char ** field_name,char ** field_value)1337 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value)
1338 {
1339 	FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1340 	FLAC__ASSERT(0 != field_name);
1341 	FLAC__ASSERT(0 != field_value);
1342 
1343 	if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1344 		return false;
1345 
1346 	{
1347 		const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1348 		const size_t nn = eq-entry.entry;
1349 		const size_t nv = entry.length-nn-1; /* -1 for the '=' */
1350 		FLAC__ASSERT(0 != eq);
1351 		if(0 == eq)
1352 			return false; /* double protection */
1353 		if(0 == (*field_name = (char*)safe_malloc_add_2op_(nn, /*+*/1)))
1354 			return false;
1355 		if(0 == (*field_value = (char*)safe_malloc_add_2op_(nv, /*+*/1))) {
1356 			free(*field_name);
1357 			return false;
1358 		}
1359 		memcpy(*field_name, entry.entry, nn);
1360 		memcpy(*field_value, entry.entry+nn+1, nv);
1361 		(*field_name)[nn] = '\0';
1362 		(*field_value)[nv] = '\0';
1363 	}
1364 
1365 	return true;
1366 }
1367 
FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry,const char * field_name,unsigned field_name_length)1368 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length)
1369 {
1370 	FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1371 	{
1372 		const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1373 #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__
1374 #define FLAC__STRNCASECMP strnicmp
1375 #else
1376 #define FLAC__STRNCASECMP strncasecmp
1377 #endif
1378 		return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length));
1379 #undef FLAC__STRNCASECMP
1380 	}
1381 }
1382 
FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata * object,unsigned offset,const char * field_name)1383 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
1384 {
1385 	FLAC__ASSERT(0 != field_name);
1386 
1387 	return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name));
1388 }
1389 
FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata * object,const char * field_name)1390 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
1391 {
1392 	const unsigned field_name_length = strlen(field_name);
1393 	unsigned i;
1394 
1395 	FLAC__ASSERT(0 != object);
1396 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1397 
1398 	for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1399 		if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1400 			if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
1401 				return -1;
1402 			else
1403 				return 1;
1404 		}
1405 	}
1406 
1407 	return 0;
1408 }
1409 
FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata * object,const char * field_name)1410 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1411 {
1412 	FLAC__bool ok = true;
1413 	unsigned matching = 0;
1414 	const unsigned field_name_length = strlen(field_name);
1415 	int i;
1416 
1417 	FLAC__ASSERT(0 != object);
1418 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1419 
1420 	/* must delete from end to start otherwise it will interfere with our iteration */
1421 	for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1422 		if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1423 			matching++;
1424 			ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1425 		}
1426 	}
1427 
1428 	return ok? (int)matching : -1;
1429 }
1430 
FLAC__metadata_object_cuesheet_track_new(void)1431 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void)
1432 {
1433 	return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
1434 }
1435 
FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track * object)1436 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
1437 {
1438 	FLAC__StreamMetadata_CueSheet_Track *to;
1439 
1440 	FLAC__ASSERT(0 != object);
1441 
1442 	if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
1443 		if(!copy_track_(to, object)) {
1444 			FLAC__metadata_object_cuesheet_track_delete(to);
1445 			return 0;
1446 		}
1447 	}
1448 
1449 	return to;
1450 }
1451 
FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track * object)1452 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
1453 {
1454 	FLAC__ASSERT(0 != object);
1455 
1456 	if(0 != object->indices) {
1457 		FLAC__ASSERT(object->num_indices > 0);
1458 		free(object->indices);
1459 	}
1460 }
1461 
FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track * object)1462 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
1463 {
1464 	FLAC__metadata_object_cuesheet_track_delete_data(object);
1465 	free(object);
1466 }
1467 
FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata * object,unsigned track_num,unsigned new_num_indices)1468 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1469 {
1470 	FLAC__StreamMetadata_CueSheet_Track *track;
1471 	FLAC__ASSERT(0 != object);
1472 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1473 	FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1474 
1475 	track = &object->data.cue_sheet.tracks[track_num];
1476 
1477 	if(0 == track->indices) {
1478 		FLAC__ASSERT(track->num_indices == 0);
1479 		if(0 == new_num_indices)
1480 			return true;
1481 		else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1482 			return false;
1483 	}
1484 	else {
1485 		const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1486 		const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1487 
1488 		/* overflow check */
1489 		if((size_t)new_num_indices > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index))
1490 			return false;
1491 
1492 		FLAC__ASSERT(track->num_indices > 0);
1493 
1494 		if(new_size == 0) {
1495 			free(track->indices);
1496 			track->indices = 0;
1497 		}
1498 		else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1499 			return false;
1500 
1501 		/* if growing, zero all the lengths/pointers of new elements */
1502 		if(new_size > old_size)
1503 			memset(track->indices + track->num_indices, 0, new_size - old_size);
1504 	}
1505 
1506 	track->num_indices = new_num_indices;
1507 
1508 	cuesheet_calculate_length_(object);
1509 	return true;
1510 }
1511 
FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata * object,unsigned track_num,unsigned index_num,FLAC__StreamMetadata_CueSheet_Index index)1512 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index)
1513 {
1514 	FLAC__StreamMetadata_CueSheet_Track *track;
1515 
1516 	FLAC__ASSERT(0 != object);
1517 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1518 	FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1519 	FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1520 
1521 	track = &object->data.cue_sheet.tracks[track_num];
1522 
1523 	if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1524 		return false;
1525 
1526 	/* move all indices >= index_num forward one space */
1527 	memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1528 
1529 	track->indices[index_num] = index;
1530 	cuesheet_calculate_length_(object);
1531 	return true;
1532 }
1533 
FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata * object,unsigned track_num,unsigned index_num)1534 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1535 {
1536 	FLAC__StreamMetadata_CueSheet_Index index;
1537 	memset(&index, 0, sizeof(index));
1538 	return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
1539 }
1540 
FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata * object,unsigned track_num,unsigned index_num)1541 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1542 {
1543 	FLAC__StreamMetadata_CueSheet_Track *track;
1544 
1545 	FLAC__ASSERT(0 != object);
1546 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1547 	FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1548 	FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1549 
1550 	track = &object->data.cue_sheet.tracks[track_num];
1551 
1552 	/* move all indices > index_num backward one space */
1553 	memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
1554 
1555 	FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1556 	cuesheet_calculate_length_(object);
1557 	return true;
1558 }
1559 
FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata * object,unsigned new_num_tracks)1560 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1561 {
1562 	FLAC__ASSERT(0 != object);
1563 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1564 
1565 	if(0 == object->data.cue_sheet.tracks) {
1566 		FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1567 		if(0 == new_num_tracks)
1568 			return true;
1569 		else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1570 			return false;
1571 	}
1572 	else {
1573 		const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1574 		const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1575 
1576 		/* overflow check */
1577 		if((size_t)new_num_tracks > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track))
1578 			return false;
1579 
1580 		FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1581 
1582 		/* if shrinking, free the truncated entries */
1583 		if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1584 			unsigned i;
1585 			for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1586 				if(0 != object->data.cue_sheet.tracks[i].indices)
1587 					free(object->data.cue_sheet.tracks[i].indices);
1588 		}
1589 
1590 		if(new_size == 0) {
1591 			free(object->data.cue_sheet.tracks);
1592 			object->data.cue_sheet.tracks = 0;
1593 		}
1594 		else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1595 			return false;
1596 
1597 		/* if growing, zero all the lengths/pointers of new elements */
1598 		if(new_size > old_size)
1599 			memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1600 	}
1601 
1602 	object->data.cue_sheet.num_tracks = new_num_tracks;
1603 
1604 	cuesheet_calculate_length_(object);
1605 	return true;
1606 }
1607 
FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata * object,unsigned track_num,FLAC__StreamMetadata_CueSheet_Track * track,FLAC__bool copy)1608 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1609 {
1610 	FLAC__ASSERT(0 != object);
1611 	FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1612 
1613 	return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
1614 }
1615 
FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata * object,unsigned track_num,FLAC__StreamMetadata_CueSheet_Track * track,FLAC__bool copy)1616 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1617 {
1618 	FLAC__StreamMetadata_CueSheet *cs;
1619 
1620 	FLAC__ASSERT(0 != object);
1621 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1622 	FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1623 
1624 	cs = &object->data.cue_sheet;
1625 
1626 	if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1627 		return false;
1628 
1629 	/* move all tracks >= track_num forward one space */
1630 	memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1631 	cs->tracks[track_num].num_indices = 0;
1632 	cs->tracks[track_num].indices = 0;
1633 
1634 	return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1635 }
1636 
FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata * object,unsigned track_num)1637 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
1638 {
1639 	FLAC__StreamMetadata_CueSheet_Track track;
1640 	memset(&track, 0, sizeof(track));
1641 	return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
1642 }
1643 
FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata * object,unsigned track_num)1644 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1645 {
1646 	FLAC__StreamMetadata_CueSheet *cs;
1647 
1648 	FLAC__ASSERT(0 != object);
1649 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1650 	FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1651 
1652 	cs = &object->data.cue_sheet;
1653 
1654 	/* free the track at track_num */
1655 	if(0 != cs->tracks[track_num].indices)
1656 		free(cs->tracks[track_num].indices);
1657 
1658 	/* move all tracks > track_num backward one space */
1659 	memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1660 	cs->tracks[cs->num_tracks-1].num_indices = 0;
1661 	cs->tracks[cs->num_tracks-1].indices = 0;
1662 
1663 	return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1664 }
1665 
FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata * object,FLAC__bool check_cd_da_subset,const char ** violation)1666 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1667 {
1668 	FLAC__ASSERT(0 != object);
1669 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1670 
1671 	return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1672 }
1673 
get_index_01_offset_(const FLAC__StreamMetadata_CueSheet * cs,unsigned track)1674 static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track)
1675 {
1676 	if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1)
1677 		return 0;
1678 	else if (cs->tracks[track].indices[0].number == 1)
1679 		return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in;
1680 	else if (cs->tracks[track].num_indices < 2)
1681 		return 0;
1682 	else if (cs->tracks[track].indices[1].number == 1)
1683 		return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in;
1684 	else
1685 		return 0;
1686 }
1687 
cddb_add_digits_(FLAC__uint32 x)1688 static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x)
1689 {
1690 	FLAC__uint32 n = 0;
1691 	while (x) {
1692 		n += (x%10);
1693 		x /= 10;
1694 	}
1695 	return n;
1696 }
1697 
1698 /*@@@@add to tests*/
FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata * object)1699 FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object)
1700 {
1701 	const FLAC__StreamMetadata_CueSheet *cs;
1702 
1703 	FLAC__ASSERT(0 != object);
1704 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1705 
1706 	cs = &object->data.cue_sheet;
1707 
1708 	if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */
1709 		return 0;
1710 
1711 	{
1712 		FLAC__uint32 i, length, sum = 0;
1713 		for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */
1714 			sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100));
1715 		length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100);
1716 
1717 		return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1);
1718 	}
1719 }
1720 
FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata * object,char * mime_type,FLAC__bool copy)1721 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy)
1722 {
1723 	char *old;
1724 	size_t old_length, new_length;
1725 
1726 	FLAC__ASSERT(0 != object);
1727 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1728 	FLAC__ASSERT(0 != mime_type);
1729 
1730 	old = object->data.picture.mime_type;
1731 	old_length = old? strlen(old) : 0;
1732 	new_length = strlen(mime_type);
1733 
1734 	/* do the copy first so that if we fail we leave the object untouched */
1735 	if(copy) {
1736 		if(new_length >= SIZE_MAX) /* overflow check */
1737 			return false;
1738 		if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1))
1739 			return false;
1740 	}
1741 	else {
1742 		object->data.picture.mime_type = mime_type;
1743 	}
1744 
1745 	if(0 != old)
1746 		free(old);
1747 
1748 	object->length -= old_length;
1749 	object->length += new_length;
1750 	return true;
1751 }
1752 
FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata * object,FLAC__byte * description,FLAC__bool copy)1753 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy)
1754 {
1755 	FLAC__byte *old;
1756 	size_t old_length, new_length;
1757 
1758 	FLAC__ASSERT(0 != object);
1759 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1760 	FLAC__ASSERT(0 != description);
1761 
1762 	old = object->data.picture.description;
1763 	old_length = old? strlen((const char *)old) : 0;
1764 	new_length = strlen((const char *)description);
1765 
1766 	/* do the copy first so that if we fail we leave the object untouched */
1767 	if(copy) {
1768 		if(new_length >= SIZE_MAX) /* overflow check */
1769 			return false;
1770 		if(!copy_bytes_(&object->data.picture.description, description, new_length+1))
1771 			return false;
1772 	}
1773 	else {
1774 		object->data.picture.description = description;
1775 	}
1776 
1777 	if(0 != old)
1778 		free(old);
1779 
1780 	object->length -= old_length;
1781 	object->length += new_length;
1782 	return true;
1783 }
1784 
FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata * object,FLAC__byte * data,FLAC__uint32 length,FLAC__bool copy)1785 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy)
1786 {
1787 	FLAC__byte *old;
1788 
1789 	FLAC__ASSERT(0 != object);
1790 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1791 	FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
1792 
1793 	old = object->data.picture.data;
1794 
1795 	/* do the copy first so that if we fail we leave the object untouched */
1796 	if(copy) {
1797 		if(!copy_bytes_(&object->data.picture.data, data, length))
1798 			return false;
1799 	}
1800 	else {
1801 		object->data.picture.data = data;
1802 	}
1803 
1804 	if(0 != old)
1805 		free(old);
1806 
1807 	object->length -= object->data.picture.data_length;
1808 	object->data.picture.data_length = length;
1809 	object->length += length;
1810 	return true;
1811 }
1812 
FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata * object,const char ** violation)1813 FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation)
1814 {
1815 	FLAC__ASSERT(0 != object);
1816 	FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1817 
1818 	return FLAC__format_picture_is_legal(&object->data.picture, violation);
1819 }
1820