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