]> 4ch.mooo.com Git - 16.git/blob - src/lib/dl/ext/flac/metadata_object.c
meh did some cleanings and i will work on mapread to mm thingy sometime soon! oops...
[16.git] / src / lib / dl / ext / flac / metadata_object.c
1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001,2002,2003,2004,2005,2006,2007  Josh Coalson
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of the Xiph.org Foundation nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #if HAVE_CONFIG_H
33 #  include "config.h"
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "private/metadata.h"
40
41 #include "flac/assert.h"
42 #include "share/alloc.h"
43
44
45 /****************************************************************************
46  *
47  * Local routines
48  *
49  ***************************************************************************/
50
51 /* copy bytes:
52  *  from = NULL  && bytes = 0
53  *       to <- NULL
54  *  from != NULL && bytes > 0
55  *       to <- copy of from
56  *  else ASSERT
57  * malloc error leaves 'to' unchanged
58  */
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 > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */
554                                         FLAC__metadata_object_delete(to);
555                                         return 0;
556                                 }
557                                 if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) {
558                                         FLAC__metadata_object_delete(to);
559                                         return 0;
560                                 }
561                                 break;
562                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
563                                 if(0 != to->data.vorbis_comment.vendor_string.entry) {
564                                         free(to->data.vorbis_comment.vendor_string.entry);
565                                         to->data.vorbis_comment.vendor_string.entry = 0;
566                                 }
567                                 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
568                                         FLAC__metadata_object_delete(to);
569                                         return 0;
570                                 }
571                                 if(object->data.vorbis_comment.num_comments == 0) {
572                                         FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
573                                         to->data.vorbis_comment.comments = 0;
574                                 }
575                                 else {
576                                         FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
577                                         to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
578                                         if(0 == to->data.vorbis_comment.comments) {
579                                                 FLAC__metadata_object_delete(to);
580                                                 return 0;
581                                         }
582                                 }
583                                 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
584                                 break;
585                         case FLAC__METADATA_TYPE_CUESHEET:
586                                 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
587                                 if(object->data.cue_sheet.num_tracks == 0) {
588                                         FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
589                                 }
590                                 else {
591                                         FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
592                                         to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
593                                         if(0 == to->data.cue_sheet.tracks) {
594                                                 FLAC__metadata_object_delete(to);
595                                                 return 0;
596                                         }
597                                 }
598                                 break;
599                         case FLAC__METADATA_TYPE_PICTURE:
600                                 to->data.picture.type = object->data.picture.type;
601                                 if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) {
602                                         FLAC__metadata_object_delete(to);
603                                         return 0;
604                                 }
605                                 if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) {
606                                         FLAC__metadata_object_delete(to);
607                                         return 0;
608                                 }
609                                 to->data.picture.width = object->data.picture.width;
610                                 to->data.picture.height = object->data.picture.height;
611                                 to->data.picture.depth = object->data.picture.depth;
612                                 to->data.picture.colors = object->data.picture.colors;
613                                 to->data.picture.data_length = object->data.picture.data_length;
614                                 if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) {
615                                         FLAC__metadata_object_delete(to);
616                                         return 0;
617                                 }
618                                 break;
619                         default:
620                                 if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
621                                         FLAC__metadata_object_delete(to);
622                                         return 0;
623                                 }
624                                 break;
625                 }
626         }
627
628         return to;
629 }
630
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((size_t)new_num_points > SIZE_MAX / sizeof(FLAC__StreamMetadata_SeekPoint))
947                         return false;
948
949                 FLAC__ASSERT(object->data.seek_table.num_points > 0);
950
951                 if(new_size == 0) {
952                         free(object->data.seek_table.points);
953                         object->data.seek_table.points = 0;
954                 }
955                 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
956                         return false;
957
958                 /* if growing, set new elements to placeholders */
959                 if(new_size > old_size) {
960                         unsigned i;
961                         for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
962                                 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
963                                 object->data.seek_table.points[i].stream_offset = 0;
964                                 object->data.seek_table.points[i].frame_samples = 0;
965                         }
966                 }
967         }
968
969         object->data.seek_table.num_points = new_num_points;
970
971         seektable_calculate_length_(object);
972         return true;
973 }
974
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((size_t)new_num_comments > SIZE_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry))
1178                         return false;
1179
1180                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
1181
1182                 /* if shrinking, free the truncated entries */
1183                 if(new_num_comments < object->data.vorbis_comment.num_comments) {
1184                         unsigned i;
1185                         for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
1186                                 if(0 != object->data.vorbis_comment.comments[i].entry)
1187                                         free(object->data.vorbis_comment.comments[i].entry);
1188                 }
1189
1190                 if(new_size == 0) {
1191                         free(object->data.vorbis_comment.comments);
1192                         object->data.vorbis_comment.comments = 0;
1193                 }
1194                 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
1195                         return false;
1196
1197                 /* if growing, zero all the length/pointers of new elements */
1198                 if(new_size > old_size)
1199                         memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
1200         }
1201
1202         object->data.vorbis_comment.num_comments = new_num_comments;
1203
1204         vorbiscomment_calculate_length_(object);
1205         return true;
1206 }
1207
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                 if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) {
1269                         unsigned index = (unsigned)i;
1270                         if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy))
1271                                 return false;
1272                         if(all && (index+1 < object->data.vorbis_comment.num_comments)) {
1273                                 for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) {
1274                                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i))
1275                                                 return false;
1276                                         if((unsigned)i < object->data.vorbis_comment.num_comments)
1277                                                 i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length);
1278                                         else
1279                                                 i = -1;
1280                                 }
1281                         }
1282                         return true;
1283                 }
1284                 else
1285                         return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
1286         }
1287 }
1288
1289 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
1290 {
1291         FLAC__StreamMetadata_VorbisComment *vc;
1292
1293         FLAC__ASSERT(0 != object);
1294         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1295         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1296
1297         vc = &object->data.vorbis_comment;
1298
1299         /* free the comment at comment_num */
1300         if(0 != vc->comments[comment_num].entry)
1301                 free(vc->comments[comment_num].entry);
1302
1303         /* move all comments > comment_num backward one space */
1304         memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
1305         vc->comments[vc->num_comments-1].length = 0;
1306         vc->comments[vc->num_comments-1].entry = 0;
1307
1308         return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
1309 }
1310
1311 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value)
1312 {
1313         FLAC__ASSERT(0 != entry);
1314         FLAC__ASSERT(0 != field_name);
1315         FLAC__ASSERT(0 != field_value);
1316
1317         if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
1318                 return false;
1319         if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1)))
1320                 return false;
1321
1322         {
1323                 const size_t nn = strlen(field_name);
1324                 const size_t nv = strlen(field_value);
1325                 entry->length = nn + 1 /*=*/ + nv;
1326                 if(0 == (entry->entry = (FLAC__byte*)safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1)))
1327                         return false;
1328                 memcpy(entry->entry, field_name, nn);
1329                 entry->entry[nn] = '=';
1330                 memcpy(entry->entry+nn+1, field_value, nv);
1331                 entry->entry[entry->length] = '\0';
1332         }
1333         
1334         return true;
1335 }
1336
1337 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value)
1338 {
1339         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1340         FLAC__ASSERT(0 != field_name);
1341         FLAC__ASSERT(0 != field_value);
1342
1343         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1344                 return false;
1345
1346         {
1347                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1348                 const size_t nn = eq-entry.entry;
1349                 const size_t nv = entry.length-nn-1; /* -1 for the '=' */
1350                 FLAC__ASSERT(0 != eq);
1351                 if(0 == eq)
1352                         return false; /* double protection */
1353                 if(0 == (*field_name = (char*)safe_malloc_add_2op_(nn, /*+*/1)))
1354                         return false;
1355                 if(0 == (*field_value = (char*)safe_malloc_add_2op_(nv, /*+*/1))) {
1356                         free(*field_name);
1357                         return false;
1358                 }
1359                 memcpy(*field_name, entry.entry, nn);
1360                 memcpy(*field_value, entry.entry+nn+1, nv);
1361                 (*field_name)[nn] = '\0';
1362                 (*field_value)[nv] = '\0';
1363         }
1364
1365         return true;
1366 }
1367
1368 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length)
1369 {
1370         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1371         {
1372                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1373 #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__
1374 #define FLAC__STRNCASECMP strnicmp
1375 #else
1376 #define FLAC__STRNCASECMP strncasecmp
1377 #endif
1378                 return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length));
1379 #undef FLAC__STRNCASECMP
1380         }
1381 }
1382
1383 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
1384 {
1385         FLAC__ASSERT(0 != field_name);
1386
1387         return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name));
1388 }
1389
1390 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
1391 {
1392         const unsigned field_name_length = strlen(field_name);
1393         unsigned i;
1394
1395         FLAC__ASSERT(0 != object);
1396         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1397
1398         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1399                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1400                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
1401                                 return -1;
1402                         else
1403                                 return 1;
1404                 }
1405         }
1406
1407         return 0;
1408 }
1409
1410 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1411 {
1412         FLAC__bool ok = true;
1413         unsigned matching = 0;
1414         const unsigned field_name_length = strlen(field_name);
1415         int i;
1416
1417         FLAC__ASSERT(0 != object);
1418         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1419
1420         /* must delete from end to start otherwise it will interfere with our iteration */
1421         for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1422                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1423                         matching++;
1424                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1425                 }
1426         }
1427
1428         return ok? (int)matching : -1;
1429 }
1430
1431 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void)
1432 {
1433         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
1434 }
1435
1436 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
1437 {
1438         FLAC__StreamMetadata_CueSheet_Track *to;
1439
1440         FLAC__ASSERT(0 != object);
1441
1442         if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
1443                 if(!copy_track_(to, object)) {
1444                         FLAC__metadata_object_cuesheet_track_delete(to);
1445                         return 0;
1446                 }
1447         }
1448
1449         return to;
1450 }
1451
1452 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
1453 {
1454         FLAC__ASSERT(0 != object);
1455
1456         if(0 != object->indices) {
1457                 FLAC__ASSERT(object->num_indices > 0);
1458                 free(object->indices);
1459         }
1460 }
1461
1462 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
1463 {
1464         FLAC__metadata_object_cuesheet_track_delete_data(object);
1465         free(object);
1466 }
1467
1468 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1469 {
1470         FLAC__StreamMetadata_CueSheet_Track *track;
1471         FLAC__ASSERT(0 != object);
1472         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1473         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1474
1475         track = &object->data.cue_sheet.tracks[track_num];
1476
1477         if(0 == track->indices) {
1478                 FLAC__ASSERT(track->num_indices == 0);
1479                 if(0 == new_num_indices)
1480                         return true;
1481                 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1482                         return false;
1483         }
1484         else {
1485                 const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1486                 const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1487
1488                 /* overflow check */
1489                 if((size_t)new_num_indices > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index))
1490                         return false;
1491
1492                 FLAC__ASSERT(track->num_indices > 0);
1493
1494                 if(new_size == 0) {
1495                         free(track->indices);
1496                         track->indices = 0;
1497                 }
1498                 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1499                         return false;
1500
1501                 /* if growing, zero all the lengths/pointers of new elements */
1502                 if(new_size > old_size)
1503                         memset(track->indices + track->num_indices, 0, new_size - old_size);
1504         }
1505
1506         track->num_indices = new_num_indices;
1507
1508         cuesheet_calculate_length_(object);
1509         return true;
1510 }
1511
1512 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index)
1513 {
1514         FLAC__StreamMetadata_CueSheet_Track *track;
1515
1516         FLAC__ASSERT(0 != object);
1517         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1518         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1519         FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1520
1521         track = &object->data.cue_sheet.tracks[track_num];
1522
1523         if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1524                 return false;
1525
1526         /* move all indices >= index_num forward one space */
1527         memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1528
1529         track->indices[index_num] = index;
1530         cuesheet_calculate_length_(object);
1531         return true;
1532 }
1533
1534 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1535 {
1536         FLAC__StreamMetadata_CueSheet_Index index;
1537         memset(&index, 0, sizeof(index));
1538         return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
1539 }
1540
1541 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1542 {
1543         FLAC__StreamMetadata_CueSheet_Track *track;
1544
1545         FLAC__ASSERT(0 != object);
1546         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1547         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1548         FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1549
1550         track = &object->data.cue_sheet.tracks[track_num];
1551
1552         /* move all indices > index_num backward one space */
1553         memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
1554
1555         FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1556         cuesheet_calculate_length_(object);
1557         return true;
1558 }
1559
1560 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1561 {
1562         FLAC__ASSERT(0 != object);
1563         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1564
1565         if(0 == object->data.cue_sheet.tracks) {
1566                 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1567                 if(0 == new_num_tracks)
1568                         return true;
1569                 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1570                         return false;
1571         }
1572         else {
1573                 const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1574                 const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1575
1576                 /* overflow check */
1577                 if((size_t)new_num_tracks > SIZE_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track))
1578                         return false;
1579
1580                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1581
1582                 /* if shrinking, free the truncated entries */
1583                 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1584                         unsigned i;
1585                         for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1586                                 if(0 != object->data.cue_sheet.tracks[i].indices)
1587                                         free(object->data.cue_sheet.tracks[i].indices);
1588                 }
1589
1590                 if(new_size == 0) {
1591                         free(object->data.cue_sheet.tracks);
1592                         object->data.cue_sheet.tracks = 0;
1593                 }
1594                 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1595                         return false;
1596
1597                 /* if growing, zero all the lengths/pointers of new elements */
1598                 if(new_size > old_size)
1599                         memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1600         }
1601
1602         object->data.cue_sheet.num_tracks = new_num_tracks;
1603
1604         cuesheet_calculate_length_(object);
1605         return true;
1606 }
1607
1608 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1609 {
1610         FLAC__ASSERT(0 != object);
1611         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1612
1613         return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
1614 }
1615
1616 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1617 {
1618         FLAC__StreamMetadata_CueSheet *cs;
1619
1620         FLAC__ASSERT(0 != object);
1621         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1622         FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1623
1624         cs = &object->data.cue_sheet;
1625
1626         if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1627                 return false;
1628
1629         /* move all tracks >= track_num forward one space */
1630         memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1631         cs->tracks[track_num].num_indices = 0;
1632         cs->tracks[track_num].indices = 0;
1633
1634         return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1635 }
1636
1637 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
1638 {
1639         FLAC__StreamMetadata_CueSheet_Track track;
1640         memset(&track, 0, sizeof(track));
1641         return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
1642 }
1643
1644 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1645 {
1646         FLAC__StreamMetadata_CueSheet *cs;
1647
1648         FLAC__ASSERT(0 != object);
1649         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1650         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1651
1652         cs = &object->data.cue_sheet;
1653
1654         /* free the track at track_num */
1655         if(0 != cs->tracks[track_num].indices)
1656                 free(cs->tracks[track_num].indices);
1657
1658         /* move all tracks > track_num backward one space */
1659         memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1660         cs->tracks[cs->num_tracks-1].num_indices = 0;
1661         cs->tracks[cs->num_tracks-1].indices = 0;
1662
1663         return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1664 }
1665
1666 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1667 {
1668         FLAC__ASSERT(0 != object);
1669         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1670
1671         return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1672 }
1673
1674 static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track)
1675 {
1676         if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1)
1677                 return 0;
1678         else if (cs->tracks[track].indices[0].number == 1)
1679                 return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in;
1680         else if (cs->tracks[track].num_indices < 2)
1681                 return 0;
1682         else if (cs->tracks[track].indices[1].number == 1)
1683                 return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in;
1684         else
1685                 return 0;
1686 }
1687
1688 static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x)
1689 {
1690         FLAC__uint32 n = 0;
1691         while (x) {
1692                 n += (x%10);
1693                 x /= 10;
1694         }
1695         return n;
1696 }
1697
1698 /*@@@@add to tests*/
1699 FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object)
1700 {
1701         const FLAC__StreamMetadata_CueSheet *cs;
1702
1703         FLAC__ASSERT(0 != object);
1704         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1705
1706         cs = &object->data.cue_sheet;
1707
1708         if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */
1709                 return 0;
1710
1711         {
1712                 FLAC__uint32 i, length, sum = 0;
1713                 for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */
1714                         sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100));
1715                 length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100);
1716
1717                 return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1);
1718         }
1719 }
1720
1721 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy)
1722 {
1723         char *old;
1724         size_t old_length, new_length;
1725
1726         FLAC__ASSERT(0 != object);
1727         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1728         FLAC__ASSERT(0 != mime_type);
1729
1730         old = object->data.picture.mime_type;
1731         old_length = old? strlen(old) : 0;
1732         new_length = strlen(mime_type);
1733
1734         /* do the copy first so that if we fail we leave the object untouched */
1735         if(copy) {
1736                 if(new_length >= SIZE_MAX) /* overflow check */
1737                         return false;
1738                 if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1))
1739                         return false;
1740         }
1741         else {
1742                 object->data.picture.mime_type = mime_type;
1743         }
1744
1745         if(0 != old)
1746                 free(old);
1747
1748         object->length -= old_length;
1749         object->length += new_length;
1750         return true;
1751 }
1752
1753 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy)
1754 {
1755         FLAC__byte *old;
1756         size_t old_length, new_length;
1757
1758         FLAC__ASSERT(0 != object);
1759         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1760         FLAC__ASSERT(0 != description);
1761
1762         old = object->data.picture.description;
1763         old_length = old? strlen((const char *)old) : 0;
1764         new_length = strlen((const char *)description);
1765
1766         /* do the copy first so that if we fail we leave the object untouched */
1767         if(copy) {
1768                 if(new_length >= SIZE_MAX) /* overflow check */
1769                         return false;
1770                 if(!copy_bytes_(&object->data.picture.description, description, new_length+1))
1771                         return false;
1772         }
1773         else {
1774                 object->data.picture.description = description;
1775         }
1776
1777         if(0 != old)
1778                 free(old);
1779
1780         object->length -= old_length;
1781         object->length += new_length;
1782         return true;
1783 }
1784
1785 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy)
1786 {
1787         FLAC__byte *old;
1788
1789         FLAC__ASSERT(0 != object);
1790         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1791         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
1792
1793         old = object->data.picture.data;
1794
1795         /* do the copy first so that if we fail we leave the object untouched */
1796         if(copy) {
1797                 if(!copy_bytes_(&object->data.picture.data, data, length))
1798                         return false;
1799         }
1800         else {
1801                 object->data.picture.data = data;
1802         }
1803
1804         if(0 != old)
1805                 free(old);
1806
1807         object->length -= object->data.picture.data_length;
1808         object->data.picture.data_length = length;
1809         object->length += length;
1810         return true;
1811 }
1812
1813 FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation)
1814 {
1815         FLAC__ASSERT(0 != object);
1816         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1817
1818         return FLAC__format_picture_is_legal(&object->data.picture, violation);
1819 }