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