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