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