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