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