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