add support for new 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_DATA_LENGTH_LEN +
485                                         0 /* no data */
486                                 ) / 8;
487                                 object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER;
488                                 object->data.picture.mime_type = 0;
489                                 object->data.picture.description = 0;
490                                 /* calloc() took care of this for us:
491                                 object->data.picture.width = 0;
492                                 object->data.picture.height = 0;
493                                 object->data.picture.depth = 0;
494                                 object->data.picture.data_length = 0;
495                                 object->data.picture.data = 0;
496                                 */
497                                 /* now initialize mime_type and description with empty strings to make things easier on the client */
498                                 if(!copy_cstring_(&object->data.picture.mime_type, "")) {
499                                         free(object);
500                                         return 0;
501                                 }
502                                 if(!copy_cstring_((char**)(&object->data.picture.description), "")) {
503                                         if(object->data.picture.mime_type)
504                                                 free(object->data.picture.mime_type);
505                                         free(object);
506                                         return 0;
507                                 }
508                                 break;
509                         default:
510                                 /* calloc() took care of this for us:
511                                 object->length = 0;
512                                 object->data.unknown.data = 0;
513                                 */
514                                 break;
515                 }
516         }
517
518         return object;
519 }
520
521 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
522 {
523         FLAC__StreamMetadata *to;
524
525         FLAC__ASSERT(0 != object);
526
527         if(0 != (to = FLAC__metadata_object_new(object->type))) {
528                 to->is_last = object->is_last;
529                 to->type = object->type;
530                 to->length = object->length;
531                 switch(to->type) {
532                         case FLAC__METADATA_TYPE_STREAMINFO:
533                                 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
534                                 break;
535                         case FLAC__METADATA_TYPE_PADDING:
536                                 break;
537                         case FLAC__METADATA_TYPE_APPLICATION:
538                                 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
539                                 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
540                                         FLAC__metadata_object_delete(to);
541                                         return 0;
542                                 }
543                                 break;
544                         case FLAC__METADATA_TYPE_SEEKTABLE:
545                                 to->data.seek_table.num_points = object->data.seek_table.num_points;
546                                 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))) {
547                                         FLAC__metadata_object_delete(to);
548                                         return 0;
549                                 }
550                                 break;
551                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
552                                 if(0 != to->data.vorbis_comment.vendor_string.entry) {
553                                         free(to->data.vorbis_comment.vendor_string.entry);
554                                         to->data.vorbis_comment.vendor_string.entry = 0;
555                                 }
556                                 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
557                                         FLAC__metadata_object_delete(to);
558                                         return 0;
559                                 }
560                                 if(object->data.vorbis_comment.num_comments == 0) {
561                                         FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
562                                         to->data.vorbis_comment.comments = 0;
563                                 }
564                                 else {
565                                         FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
566                                         to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
567                                         if(0 == to->data.vorbis_comment.comments) {
568                                                 FLAC__metadata_object_delete(to);
569                                                 return 0;
570                                         }
571                                 }
572                                 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
573                                 break;
574                         case FLAC__METADATA_TYPE_CUESHEET:
575                                 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
576                                 if(object->data.cue_sheet.num_tracks == 0) {
577                                         FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
578                                 }
579                                 else {
580                                         FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
581                                         to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
582                                         if(0 == to->data.cue_sheet.tracks) {
583                                                 FLAC__metadata_object_delete(to);
584                                                 return 0;
585                                         }
586                                 }
587                                 break;
588                         case FLAC__METADATA_TYPE_PICTURE:
589                                 to->data.picture.type = object->data.picture.type;
590                                 if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) {
591                                         FLAC__metadata_object_delete(to);
592                                         return 0;
593                                 }
594                                 if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) {
595                                         FLAC__metadata_object_delete(to);
596                                         return 0;
597                                 }
598                                 to->data.picture.width = object->data.picture.width;
599                                 to->data.picture.height = object->data.picture.height;
600                                 to->data.picture.depth = object->data.picture.depth;
601                                 to->data.picture.data_length = object->data.picture.data_length;
602                                 if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) {
603                                         FLAC__metadata_object_delete(to);
604                                         return 0;
605                                 }
606                                 break;
607                         default:
608                                 if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
609                                         FLAC__metadata_object_delete(to);
610                                         return 0;
611                                 }
612                                 break;
613                 }
614         }
615
616         return to;
617 }
618
619 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
620 {
621         FLAC__ASSERT(0 != object);
622
623         switch(object->type) {
624                 case FLAC__METADATA_TYPE_STREAMINFO:
625                 case FLAC__METADATA_TYPE_PADDING:
626                         break;
627                 case FLAC__METADATA_TYPE_APPLICATION:
628                         if(0 != object->data.application.data) {
629                                 free(object->data.application.data);
630                                 object->data.application.data = 0;
631                         }
632                         break;
633                 case FLAC__METADATA_TYPE_SEEKTABLE:
634                         if(0 != object->data.seek_table.points) {
635                                 free(object->data.seek_table.points);
636                                 object->data.seek_table.points = 0;
637                         }
638                         break;
639                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
640                         if(0 != object->data.vorbis_comment.vendor_string.entry) {
641                                 free(object->data.vorbis_comment.vendor_string.entry);
642                                 object->data.vorbis_comment.vendor_string.entry = 0;
643                         }
644                         if(0 != object->data.vorbis_comment.comments) {
645                                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
646                                 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
647                         }
648                         break;
649                 case FLAC__METADATA_TYPE_CUESHEET:
650                         if(0 != object->data.cue_sheet.tracks) {
651                                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
652                                 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
653                         }
654                         break;
655                 case FLAC__METADATA_TYPE_PICTURE:
656                         if(0 != object->data.picture.mime_type) {
657                                 free(object->data.picture.mime_type);
658                                 object->data.picture.mime_type = 0;
659                         }
660                         if(0 != object->data.picture.description) {
661                                 free(object->data.picture.description);
662                                 object->data.picture.description = 0;
663                         }
664                         if(0 != object->data.picture.data) {
665                                 free(object->data.picture.data);
666                                 object->data.picture.data = 0;
667                         }
668                         break;
669                 default:
670                         if(0 != object->data.unknown.data) {
671                                 free(object->data.unknown.data);
672                                 object->data.unknown.data = 0;
673                         }
674                         break;
675         }
676 }
677
678 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
679 {
680         FLAC__metadata_object_delete_data(object);
681         free(object);
682 }
683
684 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
685 {
686         if(block1->min_blocksize != block2->min_blocksize)
687                 return false;
688         if(block1->max_blocksize != block2->max_blocksize)
689                 return false;
690         if(block1->min_framesize != block2->min_framesize)
691                 return false;
692         if(block1->max_framesize != block2->max_framesize)
693                 return false;
694         if(block1->sample_rate != block2->sample_rate)
695                 return false;
696         if(block1->channels != block2->channels)
697                 return false;
698         if(block1->bits_per_sample != block2->bits_per_sample)
699                 return false;
700         if(block1->total_samples != block2->total_samples)
701                 return false;
702         if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
703                 return false;
704         return true;
705 }
706
707 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
708 {
709         FLAC__ASSERT(0 != block1);
710         FLAC__ASSERT(0 != block2);
711         FLAC__ASSERT(block_length >= sizeof(block1->id));
712
713         if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
714                 return false;
715         if(0 != block1->data && 0 != block2->data)
716                 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
717         else
718                 return block1->data == block2->data;
719 }
720
721 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
722 {
723         unsigned i;
724
725         FLAC__ASSERT(0 != block1);
726         FLAC__ASSERT(0 != block2);
727
728         if(block1->num_points != block2->num_points)
729                 return false;
730
731         if(0 != block1->points && 0 != block2->points) {
732                 for(i = 0; i < block1->num_points; i++) {
733                         if(block1->points[i].sample_number != block2->points[i].sample_number)
734                                 return false;
735                         if(block1->points[i].stream_offset != block2->points[i].stream_offset)
736                                 return false;
737                         if(block1->points[i].frame_samples != block2->points[i].frame_samples)
738                                 return false;
739                 }
740                 return true;
741         }
742         else
743                 return block1->points == block2->points;
744 }
745
746 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
747 {
748         unsigned i;
749
750         if(block1->vendor_string.length != block2->vendor_string.length)
751                 return false;
752
753         if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
754                 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
755                         return false;
756         }
757         else if(block1->vendor_string.entry != block2->vendor_string.entry)
758                 return false;
759
760         if(block1->num_comments != block2->num_comments)
761                 return false;
762
763         for(i = 0; i < block1->num_comments; i++) {
764                 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
765                         if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
766                                 return false;
767                 }
768                 else if(block1->comments[i].entry != block2->comments[i].entry)
769                         return false;
770         }
771         return true;
772 }
773
774 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
775 {
776         unsigned i, j;
777
778         if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
779                 return false;
780
781         if(block1->lead_in != block2->lead_in)
782                 return false;
783
784         if(block1->is_cd != block2->is_cd)
785                 return false;
786
787         if(block1->num_tracks != block2->num_tracks)
788                 return false;
789
790         if(0 != block1->tracks && 0 != block2->tracks) {
791                 FLAC__ASSERT(block1->num_tracks > 0);
792                 for(i = 0; i < block1->num_tracks; i++) {
793                         if(block1->tracks[i].offset != block2->tracks[i].offset)
794                                 return false;
795                         if(block1->tracks[i].number != block2->tracks[i].number)
796                                 return false;
797                         if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
798                                 return false;
799                         if(block1->tracks[i].type != block2->tracks[i].type)
800                                 return false;
801                         if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
802                                 return false;
803                         if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
804                                 return false;
805                         if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
806                                 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
807                                 for(j = 0; j < block1->tracks[i].num_indices; j++) {
808                                         if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
809                                                 return false;
810                                         if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
811                                                 return false;
812                                 }
813                         }
814                         else if(block1->tracks[i].indices != block2->tracks[i].indices)
815                                 return false;
816                 }
817         }
818         else if(block1->tracks != block2->tracks)
819                 return false;
820         return true;
821 }
822
823 static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2)
824 {
825         if(block1->type != block2->type)
826                 return false;
827         if(block1->mime_type != block2->mime_type && (0 == block1->mime_type || 0 == block2->mime_type || strcmp(block1->mime_type, block2->mime_type)))
828                 return false;
829         if(block1->description != block2->description && (0 == block1->description || 0 == block2->description || strcmp((const char *)block1->description, (const char *)block2->description)))
830                 return false;
831         if(block1->width != block2->width)
832                 return false;
833         if(block1->height != block2->height)
834                 return false;
835         if(block1->depth != block2->depth)
836                 return false;
837         if(block1->data_length != block2->data_length)
838                 return false;
839         if(block1->data != block2->data && (0 == block1->data || 0 == block2->data || memcmp(block1->data, block2->data, block1->data_length)))
840                 return false;
841         return true;
842 }
843
844 static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length)
845 {
846         FLAC__ASSERT(0 != block1);
847         FLAC__ASSERT(0 != block2);
848
849         if(0 != block1->data && 0 != block2->data)
850                 return 0 == memcmp(block1->data, block2->data, block_length);
851         else
852                 return block1->data == block2->data;
853 }
854
855 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
856 {
857         FLAC__ASSERT(0 != block1);
858         FLAC__ASSERT(0 != block2);
859
860         if(block1->type != block2->type) {
861                 return false;
862         }
863         if(block1->is_last != block2->is_last) {
864                 return false;
865         }
866         if(block1->length != block2->length) {
867                 return false;
868         }
869         switch(block1->type) {
870                 case FLAC__METADATA_TYPE_STREAMINFO:
871                         return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
872                 case FLAC__METADATA_TYPE_PADDING:
873                         return true; /* we don't compare the padding guts */
874                 case FLAC__METADATA_TYPE_APPLICATION:
875                         return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
876                 case FLAC__METADATA_TYPE_SEEKTABLE:
877                         return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
878                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
879                         return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
880                 case FLAC__METADATA_TYPE_CUESHEET:
881                         return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
882                 case FLAC__METADATA_TYPE_PICTURE:
883                         return compare_block_data_picture_(&block1->data.picture, &block2->data.picture);
884                 default:
885                         return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
886         }
887 }
888
889 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
890 {
891         FLAC__byte *save;
892
893         FLAC__ASSERT(0 != object);
894         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
895         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
896
897         save = object->data.application.data;
898
899         /* do the copy first so that if we fail we leave the object untouched */
900         if(copy) {
901                 if(!copy_bytes_(&object->data.application.data, data, length))
902                         return false;
903         }
904         else {
905                 object->data.application.data = data;
906         }
907
908         if(0 != save)
909                 free(save);
910
911         object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
912         return true;
913 }
914
915 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
916 {
917         FLAC__ASSERT(0 != object);
918         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
919
920         if(0 == object->data.seek_table.points) {
921                 FLAC__ASSERT(object->data.seek_table.num_points == 0);
922                 if(0 == new_num_points)
923                         return true;
924                 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
925                         return false;
926         }
927         else {
928                 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
929                 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
930
931                 FLAC__ASSERT(object->data.seek_table.num_points > 0);
932
933                 if(new_size == 0) {
934                         free(object->data.seek_table.points);
935                         object->data.seek_table.points = 0;
936                 }
937                 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
938                         return false;
939
940                 /* if growing, set new elements to placeholders */
941                 if(new_size > old_size) {
942                         unsigned i;
943                         for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
944                                 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
945                                 object->data.seek_table.points[i].stream_offset = 0;
946                                 object->data.seek_table.points[i].frame_samples = 0;
947                         }
948                 }
949         }
950
951         object->data.seek_table.num_points = new_num_points;
952
953         seektable_calculate_length_(object);
954         return true;
955 }
956
957 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
958 {
959         FLAC__ASSERT(0 != object);
960         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
961         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
962
963         object->data.seek_table.points[point_num] = point;
964 }
965
966 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
967 {
968         int i;
969
970         FLAC__ASSERT(0 != object);
971         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
972         FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
973
974         if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
975                 return false;
976
977         /* move all points >= point_num forward one space */
978         for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
979                 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
980
981         FLAC__metadata_object_seektable_set_point(object, point_num, point);
982         seektable_calculate_length_(object);
983         return true;
984 }
985
986 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
987 {
988         unsigned i;
989
990         FLAC__ASSERT(0 != object);
991         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
992         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
993
994         /* move all points > point_num backward one space */
995         for(i = point_num; i < object->data.seek_table.num_points-1; i++)
996                 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
997
998         return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
999 }
1000
1001 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
1002 {
1003         FLAC__ASSERT(0 != object);
1004         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1005
1006         return FLAC__format_seektable_is_legal(&object->data.seek_table);
1007 }
1008
1009 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
1010 {
1011         FLAC__ASSERT(0 != object);
1012         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1013
1014         if(num > 0)
1015                 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
1016                 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
1017         else
1018                 return true;
1019 }
1020
1021 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
1022 {
1023         FLAC__StreamMetadata_SeekTable *seek_table;
1024
1025         FLAC__ASSERT(0 != object);
1026         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1027
1028         seek_table = &object->data.seek_table;
1029
1030         if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
1031                 return false;
1032
1033         seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
1034         seek_table->points[seek_table->num_points - 1].stream_offset = 0;
1035         seek_table->points[seek_table->num_points - 1].frame_samples = 0;
1036
1037         return true;
1038 }
1039
1040 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
1041 {
1042         FLAC__ASSERT(0 != object);
1043         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1044         FLAC__ASSERT(0 != sample_numbers || num == 0);
1045
1046         if(num > 0) {
1047                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1048                 unsigned i, j;
1049
1050                 i = seek_table->num_points;
1051
1052                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
1053                         return false;
1054
1055                 for(j = 0; j < num; i++, j++) {
1056                         seek_table->points[i].sample_number = sample_numbers[j];
1057                         seek_table->points[i].stream_offset = 0;
1058                         seek_table->points[i].frame_samples = 0;
1059                 }
1060         }
1061
1062         return true;
1063 }
1064
1065 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
1066 {
1067         FLAC__ASSERT(0 != object);
1068         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1069         FLAC__ASSERT(total_samples > 0);
1070
1071         if(num > 0 && total_samples > 0) {
1072                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1073                 unsigned i, j;
1074
1075                 i = seek_table->num_points;
1076
1077                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
1078                         return false;
1079
1080                 for(j = 0; j < num; i++, j++) {
1081                         seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
1082                         seek_table->points[i].stream_offset = 0;
1083                         seek_table->points[i].frame_samples = 0;
1084                 }
1085         }
1086
1087         return true;
1088 }
1089
1090 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples)
1091 {
1092         FLAC__ASSERT(0 != object);
1093         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1094         FLAC__ASSERT(samples > 0);
1095         FLAC__ASSERT(total_samples > 0);
1096
1097         if(samples > 0 && total_samples > 0) {
1098                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
1099                 unsigned i, j;
1100                 FLAC__uint64 num, sample;
1101
1102                 num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */
1103                 /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */
1104                 if(total_samples % samples == 0)
1105                         num--;
1106
1107                 i = seek_table->num_points;
1108
1109                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
1110                         return false;
1111
1112                 sample = 0;
1113                 for(j = 0; j < num; i++, j++, sample += samples) {
1114                         seek_table->points[i].sample_number = sample;
1115                         seek_table->points[i].stream_offset = 0;
1116                         seek_table->points[i].frame_samples = 0;
1117                 }
1118         }
1119
1120         return true;
1121 }
1122
1123 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
1124 {
1125         unsigned unique;
1126
1127         FLAC__ASSERT(0 != object);
1128         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1129
1130         unique = FLAC__format_seektable_sort(&object->data.seek_table);
1131
1132         return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
1133 }
1134
1135 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1136 {
1137         if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length))
1138                 return false;
1139         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
1140 }
1141
1142 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
1143 {
1144         FLAC__ASSERT(0 != object);
1145         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1146
1147         if(0 == object->data.vorbis_comment.comments) {
1148                 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
1149                 if(0 == new_num_comments)
1150                         return true;
1151                 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
1152                         return false;
1153         }
1154         else {
1155                 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
1156                 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
1157
1158                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
1159
1160                 /* if shrinking, free the truncated entries */
1161                 if(new_num_comments < object->data.vorbis_comment.num_comments) {
1162                         unsigned i;
1163                         for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
1164                                 if(0 != object->data.vorbis_comment.comments[i].entry)
1165                                         free(object->data.vorbis_comment.comments[i].entry);
1166                 }
1167
1168                 if(new_size == 0) {
1169                         free(object->data.vorbis_comment.comments);
1170                         object->data.vorbis_comment.comments = 0;
1171                 }
1172                 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
1173                         return false;
1174
1175                 /* if growing, zero all the length/pointers of new elements */
1176                 if(new_size > old_size)
1177                         memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
1178         }
1179
1180         object->data.vorbis_comment.num_comments = new_num_comments;
1181
1182         vorbiscomment_calculate_length_(object);
1183         return true;
1184 }
1185
1186 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1187 {
1188         FLAC__ASSERT(0 != object);
1189         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1190
1191         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1192                 return false;
1193         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
1194 }
1195
1196 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1197 {
1198         FLAC__StreamMetadata_VorbisComment *vc;
1199
1200         FLAC__ASSERT(0 != object);
1201         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1202         FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
1203
1204         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1205                 return false;
1206
1207         vc = &object->data.vorbis_comment;
1208
1209         if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
1210                 return false;
1211
1212         /* move all comments >= comment_num forward one space */
1213         memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
1214         vc->comments[comment_num].length = 0;
1215         vc->comments[comment_num].entry = 0;
1216
1217         return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
1218 }
1219
1220 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1221 {
1222         FLAC__ASSERT(0 != object);
1223         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1224         return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy);
1225 }
1226
1227 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy)
1228 {
1229         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1230
1231         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1232                 return false;
1233
1234         {
1235                 int i;
1236                 unsigned field_name_length;
1237                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1238
1239                 FLAC__ASSERT(0 != eq);
1240
1241                 if(0 == eq)
1242                         return false; /* double protection */
1243
1244                 field_name_length = eq-entry.entry;
1245
1246                 if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) {
1247                         unsigned index = (unsigned)i;
1248                         if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy))
1249                                 return false;
1250                         if(all && (index+1 < object->data.vorbis_comment.num_comments)) {
1251                                 for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) {
1252                                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i))
1253                                                 return false;
1254                                         if((unsigned)i < object->data.vorbis_comment.num_comments)
1255                                                 i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length);
1256                                         else
1257                                                 i = -1;
1258                                 }
1259                         }
1260                         return true;
1261                 }
1262                 else
1263                         return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
1264         }
1265 }
1266
1267 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
1268 {
1269         FLAC__StreamMetadata_VorbisComment *vc;
1270
1271         FLAC__ASSERT(0 != object);
1272         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1273         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1274
1275         vc = &object->data.vorbis_comment;
1276
1277         /* free the comment at comment_num */
1278         if(0 != vc->comments[comment_num].entry)
1279                 free(vc->comments[comment_num].entry);
1280
1281         /* move all comments > comment_num backward one space */
1282         memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
1283         vc->comments[vc->num_comments-1].length = 0;
1284         vc->comments[vc->num_comments-1].entry = 0;
1285
1286         return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
1287 }
1288
1289 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)
1290 {
1291         FLAC__ASSERT(0 != entry);
1292         FLAC__ASSERT(0 != field_name);
1293         FLAC__ASSERT(0 != field_value);
1294
1295         if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
1296                 return false;
1297         if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1)))
1298                 return false;
1299
1300         {
1301                 const size_t nn = strlen(field_name);
1302                 const size_t nv = strlen(field_value);
1303                 entry->length = nn + 1 /*=*/ + nv;
1304                 if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1)))
1305                         return false;
1306                 memcpy(entry->entry, field_name, nn);
1307                 entry->entry[nn] = '=';
1308                 memcpy(entry->entry+nn+1, field_value, nv);
1309                 entry->entry[entry->length] = '\0';
1310         }
1311         
1312         return true;
1313 }
1314
1315 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)
1316 {
1317         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1318         FLAC__ASSERT(0 != field_name);
1319         FLAC__ASSERT(0 != field_value);
1320
1321         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1322                 return false;
1323
1324         {
1325                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1326                 const size_t nn = eq-entry.entry;
1327                 const size_t nv = entry.length-nn-1; /* -1 for the '=' */
1328                 FLAC__ASSERT(0 != eq);
1329                 if(0 == eq)
1330                         return false; /* double protection */
1331                 if(0 == (*field_name = (char*)malloc(nn+1)))
1332                         return false;
1333                 if(0 == (*field_value = (char*)malloc(nv+1))) {
1334                         free(*field_name);
1335                         return false;
1336                 }
1337                 memcpy(*field_name, entry.entry, nn);
1338                 memcpy(*field_value, entry.entry+nn+1, nv);
1339                 (*field_name)[nn] = '\0';
1340                 (*field_value)[nv] = '\0';
1341         }
1342
1343         return true;
1344 }
1345
1346 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length)
1347 {
1348         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1349         {
1350                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1351 #if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
1352 #define FLAC__STRNCASECMP strnicmp
1353 #else
1354 #define FLAC__STRNCASECMP strncasecmp
1355 #endif
1356                 return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length));
1357 #undef FLAC__STRNCASECMP
1358         }
1359 }
1360
1361 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
1362 {
1363         FLAC__ASSERT(0 != field_name);
1364
1365         return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name));
1366 }
1367
1368 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
1369 {
1370         const unsigned field_name_length = strlen(field_name);
1371         unsigned i;
1372
1373         FLAC__ASSERT(0 != object);
1374         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1375
1376         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1377                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1378                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
1379                                 return -1;
1380                         else
1381                                 return 1;
1382                 }
1383         }
1384
1385         return 0;
1386 }
1387
1388 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1389 {
1390         FLAC__bool ok = true;
1391         unsigned matching = 0;
1392         const unsigned field_name_length = strlen(field_name);
1393         int i;
1394
1395         FLAC__ASSERT(0 != object);
1396         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1397
1398         /* must delete from end to start otherwise it will interfere with our iteration */
1399         for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1400                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1401                         matching++;
1402                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1403                 }
1404         }
1405
1406         return ok? (int)matching : -1;
1407 }
1408
1409 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new()
1410 {
1411         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
1412 }
1413
1414 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
1415 {
1416         FLAC__StreamMetadata_CueSheet_Track *to;
1417
1418         FLAC__ASSERT(0 != object);
1419
1420         if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
1421                 if(!copy_track_(to, object)) {
1422                         FLAC__metadata_object_cuesheet_track_delete(to);
1423                         return 0;
1424                 }
1425         }
1426
1427         return to;
1428 }
1429
1430 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
1431 {
1432         FLAC__ASSERT(0 != object);
1433
1434         if(0 != object->indices) {
1435                 FLAC__ASSERT(object->num_indices > 0);
1436                 free(object->indices);
1437         }
1438 }
1439
1440 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
1441 {
1442         FLAC__metadata_object_cuesheet_track_delete_data(object);
1443         free(object);
1444 }
1445
1446 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1447 {
1448         FLAC__StreamMetadata_CueSheet_Track *track;
1449         FLAC__ASSERT(0 != object);
1450         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1451         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1452
1453         track = &object->data.cue_sheet.tracks[track_num];
1454
1455         if(0 == track->indices) {
1456                 FLAC__ASSERT(track->num_indices == 0);
1457                 if(0 == new_num_indices)
1458                         return true;
1459                 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1460                         return false;
1461         }
1462         else {
1463                 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1464                 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1465
1466                 FLAC__ASSERT(track->num_indices > 0);
1467
1468                 if(new_size == 0) {
1469                         free(track->indices);
1470                         track->indices = 0;
1471                 }
1472                 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1473                         return false;
1474
1475                 /* if growing, zero all the lengths/pointers of new elements */
1476                 if(new_size > old_size)
1477                         memset(track->indices + track->num_indices, 0, new_size - old_size);
1478         }
1479
1480         track->num_indices = new_num_indices;
1481
1482         cuesheet_calculate_length_(object);
1483         return true;
1484 }
1485
1486 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)
1487 {
1488         FLAC__StreamMetadata_CueSheet_Track *track;
1489
1490         FLAC__ASSERT(0 != object);
1491         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1492         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1493         FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1494
1495         track = &object->data.cue_sheet.tracks[track_num];
1496
1497         if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1498                 return false;
1499
1500         /* move all indices >= index_num forward one space */
1501         memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1502
1503         track->indices[index_num] = index;
1504         cuesheet_calculate_length_(object);
1505         return true;
1506 }
1507
1508 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1509 {
1510         FLAC__StreamMetadata_CueSheet_Index index;
1511         memset(&index, 0, sizeof(index));
1512         return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
1513 }
1514
1515 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1516 {
1517         FLAC__StreamMetadata_CueSheet_Track *track;
1518
1519         FLAC__ASSERT(0 != object);
1520         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1521         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1522         FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1523
1524         track = &object->data.cue_sheet.tracks[track_num];
1525
1526         /* move all indices > index_num backward one space */
1527         memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
1528
1529         FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1530         cuesheet_calculate_length_(object);
1531         return true;
1532 }
1533
1534 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1535 {
1536         FLAC__ASSERT(0 != object);
1537         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1538
1539         if(0 == object->data.cue_sheet.tracks) {
1540                 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1541                 if(0 == new_num_tracks)
1542                         return true;
1543                 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1544                         return false;
1545         }
1546         else {
1547                 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1548                 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1549
1550                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1551
1552                 /* if shrinking, free the truncated entries */
1553                 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1554                         unsigned i;
1555                         for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1556                                 if(0 != object->data.cue_sheet.tracks[i].indices)
1557                                         free(object->data.cue_sheet.tracks[i].indices);
1558                 }
1559
1560                 if(new_size == 0) {
1561                         free(object->data.cue_sheet.tracks);
1562                         object->data.cue_sheet.tracks = 0;
1563                 }
1564                 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1565                         return false;
1566
1567                 /* if growing, zero all the lengths/pointers of new elements */
1568                 if(new_size > old_size)
1569                         memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1570         }
1571
1572         object->data.cue_sheet.num_tracks = new_num_tracks;
1573
1574         cuesheet_calculate_length_(object);
1575         return true;
1576 }
1577
1578 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1579 {
1580         FLAC__ASSERT(0 != object);
1581         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1582
1583         return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
1584 }
1585
1586 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1587 {
1588         FLAC__StreamMetadata_CueSheet *cs;
1589
1590         FLAC__ASSERT(0 != object);
1591         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1592         FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1593
1594         cs = &object->data.cue_sheet;
1595
1596         if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1597                 return false;
1598
1599         /* move all tracks >= track_num forward one space */
1600         memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1601         cs->tracks[track_num].num_indices = 0;
1602         cs->tracks[track_num].indices = 0;
1603
1604         return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1605 }
1606
1607 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
1608 {
1609         FLAC__StreamMetadata_CueSheet_Track track;
1610         memset(&track, 0, sizeof(track));
1611         return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
1612 }
1613
1614 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1615 {
1616         FLAC__StreamMetadata_CueSheet *cs;
1617
1618         FLAC__ASSERT(0 != object);
1619         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1620         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1621
1622         cs = &object->data.cue_sheet;
1623
1624         /* free the track at track_num */
1625         if(0 != cs->tracks[track_num].indices)
1626                 free(cs->tracks[track_num].indices);
1627
1628         /* move all tracks > track_num backward one space */
1629         memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1630         cs->tracks[cs->num_tracks-1].num_indices = 0;
1631         cs->tracks[cs->num_tracks-1].indices = 0;
1632
1633         return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1634 }
1635
1636 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1637 {
1638         FLAC__ASSERT(0 != object);
1639         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1640
1641         return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1642 }
1643
1644 static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track)
1645 {
1646         if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1)
1647                 return 0;
1648         else if (cs->tracks[track].indices[0].number == 1)
1649                 return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in;
1650         else if (cs->tracks[track].num_indices < 2)
1651                 return 0;
1652         else if (cs->tracks[track].indices[1].number == 1)
1653                 return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in;
1654         else
1655                 return 0;
1656 }
1657
1658 static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x)
1659 {
1660         FLAC__uint32 n = 0;
1661         while (x) {
1662                 n += (x%10);
1663                 x /= 10;
1664         }
1665         return n;
1666 }
1667
1668 FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object)
1669 {
1670         const FLAC__StreamMetadata_CueSheet *cs;
1671
1672         FLAC__ASSERT(0 != object);
1673         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1674
1675         cs = &object->data.cue_sheet;
1676
1677         if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */
1678                 return 0;
1679
1680         {
1681                 FLAC__uint32 i, length, sum = 0;
1682                 for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */
1683                         sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100));
1684                 length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100);
1685
1686                 return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1);
1687         }
1688 }
1689
1690 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy)
1691 {
1692         char *old;
1693         size_t old_length, new_length;
1694
1695         FLAC__ASSERT(0 != object);
1696         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1697         FLAC__ASSERT(0 != mime_type);
1698
1699         old = object->data.picture.mime_type;
1700         old_length = old? strlen(old) : 0;
1701         new_length = strlen(mime_type);
1702
1703         /* do the copy first so that if we fail we leave the object untouched */
1704         if(copy) {
1705                 if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1))
1706                         return false;
1707         }
1708         else {
1709                 object->data.picture.mime_type = mime_type;
1710         }
1711
1712         if(0 != old)
1713                 free(old);
1714
1715         object->length -= old_length;
1716         object->length += new_length;
1717         return true;
1718 }
1719
1720 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy)
1721 {
1722         FLAC__byte *old;
1723         size_t old_length, new_length;
1724
1725         FLAC__ASSERT(0 != object);
1726         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1727         FLAC__ASSERT(0 != description);
1728
1729         old = object->data.picture.description;
1730         old_length = old? strlen((const char *)old) : 0;
1731         new_length = strlen((const char *)description);
1732
1733         /* do the copy first so that if we fail we leave the object untouched */
1734         if(copy) {
1735                 if(!copy_bytes_(&object->data.picture.description, description, new_length+1))
1736                         return false;
1737         }
1738         else {
1739                 object->data.picture.description = description;
1740         }
1741
1742         if(0 != old)
1743                 free(old);
1744
1745         object->length -= old_length;
1746         object->length += new_length;
1747         return true;
1748 }
1749
1750 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy)
1751 {
1752         FLAC__byte *old;
1753
1754         FLAC__ASSERT(0 != object);
1755         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1756         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
1757
1758         old = object->data.picture.data;
1759
1760         /* do the copy first so that if we fail we leave the object untouched */
1761         if(copy) {
1762                 if(!copy_bytes_(&object->data.picture.data, data, length))
1763                         return false;
1764         }
1765         else {
1766                 object->data.picture.data = data;
1767         }
1768
1769         if(0 != old)
1770                 free(old);
1771
1772         object->length -= object->data.picture.data_length;
1773         object->data.picture.data_length = length;
1774         object->length += length;
1775         return true;
1776 }
1777
1778 FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation)
1779 {
1780         FLAC__ASSERT(0 != object);
1781         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
1782
1783         return FLAC__format_picture_is_legal(&object->data.picture, violation);
1784 }