be more precise in long long int literal specification for ANSI compilers (gcc3 requi...
[flac.git] / src / libFLAC / metadata_object.c
1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001,2002,2003,2004  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 #include <stdlib.h>
33 #include <string.h>
34
35 #include "private/metadata.h"
36
37 #include "FLAC/assert.h"
38
39
40 /****************************************************************************
41  *
42  * Local routines
43  *
44  ***************************************************************************/
45
46 static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
47 {
48         if(bytes > 0 && 0 != from) {
49                 FLAC__byte *x;
50                 if(0 == (x = (FLAC__byte*)malloc(bytes)))
51                         return false;
52                 memcpy(x, from, bytes);
53                 *to = x;
54         }
55         else {
56                 FLAC__ASSERT(0 == from);
57                 FLAC__ASSERT(bytes == 0);
58                 *to = 0;
59         }
60         return true;
61 }
62
63 static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
64 {
65         to->length = from->length;
66         if(0 == from->entry) {
67                 FLAC__ASSERT(from->length == 0);
68                 to->entry = 0;
69         }
70         else {
71                 FLAC__byte *x;
72                 FLAC__ASSERT(from->length > 0);
73                 if(0 == (x = (FLAC__byte*)malloc(from->length)))
74                         return false;
75                 memcpy(x, from->entry, from->length);
76                 to->entry = x;
77         }
78         return true;
79 }
80
81 static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
82 {
83         memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
84         if(0 == from->indices) {
85                 FLAC__ASSERT(from->num_indices == 0);
86         }
87         else {
88                 FLAC__StreamMetadata_CueSheet_Index *x;
89                 FLAC__ASSERT(from->num_indices > 0);
90                 if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index))))
91                         return false;
92                 memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
93                 to->indices = x;
94         }
95         return true;
96 }
97
98 static void seektable_calculate_length_(FLAC__StreamMetadata *object)
99 {
100         FLAC__ASSERT(0 != object);
101         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
102
103         object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
104 }
105
106 static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points)
107 {
108         FLAC__StreamMetadata_SeekPoint *object_array;
109
110         FLAC__ASSERT(num_points > 0);
111
112         object_array = (FLAC__StreamMetadata_SeekPoint*)malloc(num_points * sizeof(FLAC__StreamMetadata_SeekPoint));
113
114         if(0 != object_array) {
115                 unsigned i;
116                 for(i = 0; i < num_points; i++) {
117                         object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
118                         object_array[i].stream_offset = 0;
119                         object_array[i].frame_samples = 0;
120                 }
121         }
122
123         return object_array;
124 }
125
126 static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
127 {
128         unsigned i;
129
130         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
131
132         object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
133         object->length += object->data.vorbis_comment.vendor_string.length;
134         object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
135         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
136                 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
137                 object->length += object->data.vorbis_comment.comments[i].length;
138         }
139 }
140
141 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
142 {
143         FLAC__ASSERT(num_comments > 0);
144
145         return (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
146 }
147
148 static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
149 {
150         unsigned i;
151
152         FLAC__ASSERT(0 != object_array && num_comments > 0);
153
154         for(i = 0; i < num_comments; i++)
155                 if(0 != object_array[i].entry)
156                         free(object_array[i].entry);
157
158         if(0 != object_array)
159                 free(object_array);
160 }
161
162 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
163 {
164         FLAC__StreamMetadata_VorbisComment_Entry *return_array;
165
166         FLAC__ASSERT(0 != object_array);
167         FLAC__ASSERT(num_comments > 0);
168
169         return_array = vorbiscomment_entry_array_new_(num_comments);
170
171         if(0 != return_array) {
172                 unsigned i;
173
174                 for(i = 0; i < num_comments; i++) {
175                         if(!copy_vcentry_(return_array+i, object_array+i)) {
176                                 vorbiscomment_entry_array_delete_(return_array, num_comments);
177                                 return 0;
178                         }
179                 }
180         }
181
182         return return_array;
183 }
184
185 static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
186 {
187         FLAC__byte *save;
188
189         FLAC__ASSERT(0 != object);
190         FLAC__ASSERT(0 != dest);
191         FLAC__ASSERT(0 != src);
192         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
193         FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0 && copy == false));
194
195         save = dest->entry;
196
197         /* do the copy first so that if we fail we leave the object untouched */
198         if(copy) {
199                 if(!copy_vcentry_(dest, src))
200                         return false;
201         }
202         else {
203                 *dest = *src;
204         }
205
206         if(0 != save)
207                 free(save);
208
209         vorbiscomment_calculate_length_(object);
210         return true;
211 }
212
213 static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
214 {
215         unsigned i;
216
217         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
218
219         object->length = (
220                 FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
221                 FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
222                 FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN +
223                 FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN +
224                 FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
225         ) / 8;
226
227         object->length += object->data.cue_sheet.num_tracks * (
228                 FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
229                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
230                 FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
231                 FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
232                 FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
233                 FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
234                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
235         ) / 8;
236
237         for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
238                 object->length += object->data.cue_sheet.tracks[i].num_indices * (
239                         FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
240                         FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
241                         FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
242                 ) / 8;
243         }
244 }
245
246 static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
247 {
248         FLAC__ASSERT(num_indices > 0);
249
250         return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
251 }
252
253 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
254 {
255         FLAC__ASSERT(num_tracks > 0);
256
257         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
258 }
259
260 static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
261 {
262         unsigned i;
263
264         FLAC__ASSERT(0 != object_array && num_tracks > 0);
265
266         for(i = 0; i < num_tracks; i++) {
267                 if(0 != object_array[i].indices) {
268                         FLAC__ASSERT(object_array[i].num_indices > 0);
269                         free(object_array[i].indices);
270                 }
271         }
272
273         if(0 != object_array)
274                 free(object_array);
275 }
276
277 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
278 {
279         FLAC__StreamMetadata_CueSheet_Track *return_array;
280
281         FLAC__ASSERT(0 != object_array);
282         FLAC__ASSERT(num_tracks > 0);
283
284         return_array = cuesheet_track_array_new_(num_tracks);
285
286         if(0 != return_array) {
287                 unsigned i;
288
289                 for(i = 0; i < num_tracks; i++) {
290                         if(!copy_track_(return_array+i, object_array+i)) {
291                                 cuesheet_track_array_delete_(return_array, num_tracks);
292                                 return 0;
293                         }
294                 }
295         }
296
297         return return_array;
298 }
299
300 static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
301 {
302         FLAC__StreamMetadata_CueSheet_Index *save;
303
304         FLAC__ASSERT(0 != object);
305         FLAC__ASSERT(0 != dest);
306         FLAC__ASSERT(0 != src);
307         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
308         FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
309
310         save = dest->indices;
311
312         /* do the copy first so that if we fail we leave the object untouched */
313         if(copy) {
314                 if(!copy_track_(dest, src))
315                         return false;
316         }
317         else {
318                 *dest = *src;
319         }
320
321         if(0 != save)
322                 free(save);
323
324         cuesheet_calculate_length_(object);
325         return true;
326 }
327
328
329 /****************************************************************************
330  *
331  * Metadata object routines
332  *
333  ***************************************************************************/
334
335 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
336 {
337         FLAC__StreamMetadata *object;
338
339         if(type > FLAC__MAX_METADATA_TYPE_CODE)
340                 return 0;
341
342         object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
343         if(0 != object) {
344                 object->is_last = false;
345                 object->type = type;
346                 switch(type) {
347                         case FLAC__METADATA_TYPE_STREAMINFO:
348                                 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
349                                 break;
350                         case FLAC__METADATA_TYPE_PADDING:
351                                 /* calloc() took care of this for us:
352                                 object->length = 0;
353                                 */
354                                 break;
355                         case FLAC__METADATA_TYPE_APPLICATION:
356                                 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
357                                 /* calloc() took care of this for us:
358                                 object->data.application.data = 0;
359                                 */
360                                 break;
361                         case FLAC__METADATA_TYPE_SEEKTABLE:
362                                 /* calloc() took care of this for us:
363                                 object->length = 0;
364                                 object->data.seek_table.num_points = 0;
365                                 object->data.seek_table.points = 0;
366                                 */
367                                 break;
368                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
369                                 {
370                                         object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
371                                         if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length)) {
372                                                 free(object);
373                                                 return 0;
374                                         }
375                                         vorbiscomment_calculate_length_(object);
376                                 }
377                                 break;
378                         case FLAC__METADATA_TYPE_CUESHEET:
379                                 cuesheet_calculate_length_(object);
380                                 break;
381                         default:
382                                 /* calloc() took care of this for us:
383                                 object->length = 0;
384                                 object->data.unknown.data = 0;
385                                 */
386                                 break;
387                 }
388         }
389
390         return object;
391 }
392
393 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
394 {
395         FLAC__StreamMetadata *to;
396
397         FLAC__ASSERT(0 != object);
398
399         if(0 != (to = FLAC__metadata_object_new(object->type))) {
400                 to->is_last = object->is_last;
401                 to->type = object->type;
402                 to->length = object->length;
403                 switch(to->type) {
404                         case FLAC__METADATA_TYPE_STREAMINFO:
405                                 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
406                                 break;
407                         case FLAC__METADATA_TYPE_PADDING:
408                                 break;
409                         case FLAC__METADATA_TYPE_APPLICATION:
410                                 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
411                                 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
412                                         FLAC__metadata_object_delete(to);
413                                         return 0;
414                                 }
415                                 break;
416                         case FLAC__METADATA_TYPE_SEEKTABLE:
417                                 to->data.seek_table.num_points = object->data.seek_table.num_points;
418                                 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))) {
419                                         FLAC__metadata_object_delete(to);
420                                         return 0;
421                                 }
422                                 break;
423                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
424                                 if(0 != to->data.vorbis_comment.vendor_string.entry) {
425                                         free(to->data.vorbis_comment.vendor_string.entry);
426                                         to->data.vorbis_comment.vendor_string.entry = 0;
427                                 }
428                                 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
429                                         FLAC__metadata_object_delete(to);
430                                         return 0;
431                                 }
432                                 if(object->data.vorbis_comment.num_comments == 0) {
433                                         FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
434                                         to->data.vorbis_comment.comments = 0;
435                                 }
436                                 else {
437                                         FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
438                                         to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
439                                         if(0 == to->data.vorbis_comment.comments) {
440                                                 FLAC__metadata_object_delete(to);
441                                                 return 0;
442                                         }
443                                 }
444                                 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
445                                 break;
446                         case FLAC__METADATA_TYPE_CUESHEET:
447                                 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
448                                 if(object->data.cue_sheet.num_tracks == 0) {
449                                         FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
450                                 }
451                                 else {
452                                         FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
453                                         to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
454                                         if(0 == to->data.cue_sheet.tracks) {
455                                                 FLAC__metadata_object_delete(to);
456                                                 return 0;
457                                         }
458                                 }
459                                 break;
460                         default:
461                                 if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
462                                         FLAC__metadata_object_delete(to);
463                                         return 0;
464                                 }
465                                 break;
466                 }
467         }
468
469         return to;
470 }
471
472 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
473 {
474         FLAC__ASSERT(0 != object);
475
476         switch(object->type) {
477                 case FLAC__METADATA_TYPE_STREAMINFO:
478                 case FLAC__METADATA_TYPE_PADDING:
479                         break;
480                 case FLAC__METADATA_TYPE_APPLICATION:
481                         if(0 != object->data.application.data) {
482                                 free(object->data.application.data);
483                                 object->data.application.data = 0;
484                         }
485                         break;
486                 case FLAC__METADATA_TYPE_SEEKTABLE:
487                         if(0 != object->data.seek_table.points) {
488                                 free(object->data.seek_table.points);
489                                 object->data.seek_table.points = 0;
490                         }
491                         break;
492                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
493                         if(0 != object->data.vorbis_comment.vendor_string.entry) {
494                                 free(object->data.vorbis_comment.vendor_string.entry);
495                                 object->data.vorbis_comment.vendor_string.entry = 0;
496                         }
497                         if(0 != object->data.vorbis_comment.comments) {
498                                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
499                                 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
500                         }
501                         break;
502                 case FLAC__METADATA_TYPE_CUESHEET:
503                         if(0 != object->data.cue_sheet.tracks) {
504                                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
505                                 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
506                         }
507                         break;
508                 default:
509                         if(0 != object->data.unknown.data) {
510                                 free(object->data.unknown.data);
511                                 object->data.unknown.data = 0;
512                         }
513                         break;
514         }
515 }
516
517 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
518 {
519         FLAC__metadata_object_delete_data(object);
520         free(object);
521 }
522
523 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
524 {
525         if(block1->min_blocksize != block2->min_blocksize)
526                 return false;
527         if(block1->max_blocksize != block2->max_blocksize)
528                 return false;
529         if(block1->min_framesize != block2->min_framesize)
530                 return false;
531         if(block1->max_framesize != block2->max_framesize)
532                 return false;
533         if(block1->sample_rate != block2->sample_rate)
534                 return false;
535         if(block1->channels != block2->channels)
536                 return false;
537         if(block1->bits_per_sample != block2->bits_per_sample)
538                 return false;
539         if(block1->total_samples != block2->total_samples)
540                 return false;
541         if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
542                 return false;
543         return true;
544 }
545
546 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
547 {
548         FLAC__ASSERT(0 != block1);
549         FLAC__ASSERT(0 != block2);
550         FLAC__ASSERT(block_length >= sizeof(block1->id));
551
552         if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
553                 return false;
554         if(0 != block1->data && 0 != block2->data)
555                 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
556         else
557                 return block1->data == block2->data;
558 }
559
560 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
561 {
562         unsigned i;
563
564         FLAC__ASSERT(0 != block1);
565         FLAC__ASSERT(0 != block2);
566
567         if(block1->num_points != block2->num_points)
568                 return false;
569
570         if(0 != block1->points && 0 != block2->points) {
571                 for(i = 0; i < block1->num_points; i++) {
572                         if(block1->points[i].sample_number != block2->points[i].sample_number)
573                                 return false;
574                         if(block1->points[i].stream_offset != block2->points[i].stream_offset)
575                                 return false;
576                         if(block1->points[i].frame_samples != block2->points[i].frame_samples)
577                                 return false;
578                 }
579                 return true;
580         }
581         else
582                 return block1->points == block2->points;
583 }
584
585 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
586 {
587         unsigned i;
588
589         if(block1->vendor_string.length != block2->vendor_string.length)
590                 return false;
591
592         if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
593                 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
594                         return false;
595         }
596         else if(block1->vendor_string.entry != block2->vendor_string.entry)
597                 return false;
598
599         if(block1->num_comments != block2->num_comments)
600                 return false;
601
602         for(i = 0; i < block1->num_comments; i++) {
603                 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
604                         if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
605                                 return false;
606                 }
607                 else if(block1->comments[i].entry != block2->comments[i].entry)
608                         return false;
609         }
610         return true;
611 }
612
613 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
614 {
615         unsigned i, j;
616
617         if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
618                 return false;
619
620         if(block1->lead_in != block2->lead_in)
621                 return false;
622
623         if(block1->is_cd != block2->is_cd)
624                 return false;
625
626         if(block1->num_tracks != block2->num_tracks)
627                 return false;
628
629         if(0 != block1->tracks && 0 != block2->tracks) {
630                 FLAC__ASSERT(block1->num_tracks > 0);
631                 for(i = 0; i < block1->num_tracks; i++) {
632                         if(block1->tracks[i].offset != block2->tracks[i].offset)
633                                 return false;
634                         if(block1->tracks[i].number != block2->tracks[i].number)
635                                 return false;
636                         if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
637                                 return false;
638                         if(block1->tracks[i].type != block2->tracks[i].type)
639                                 return false;
640                         if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
641                                 return false;
642                         if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
643                                 return false;
644                         if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
645                                 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
646                                 for(j = 0; j < block1->tracks[i].num_indices; j++) {
647                                         if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
648                                                 return false;
649                                         if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
650                                                 return false;
651                                 }
652                         }
653                         else if(block1->tracks[i].indices != block2->tracks[i].indices)
654                                 return false;
655                 }
656         }
657         else if(block1->tracks != block2->tracks)
658                 return false;
659         return true;
660 }
661
662 static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length)
663 {
664         FLAC__ASSERT(0 != block1);
665         FLAC__ASSERT(0 != block2);
666
667         if(0 != block1->data && 0 != block2->data)
668                 return 0 == memcmp(block1->data, block2->data, block_length);
669         else
670                 return block1->data == block2->data;
671 }
672
673 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
674 {
675         FLAC__ASSERT(0 != block1);
676         FLAC__ASSERT(0 != block2);
677
678         if(block1->type != block2->type) {
679                 return false;
680         }
681         if(block1->is_last != block2->is_last) {
682                 return false;
683         }
684         if(block1->length != block2->length) {
685                 return false;
686         }
687         switch(block1->type) {
688                 case FLAC__METADATA_TYPE_STREAMINFO:
689                         return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
690                 case FLAC__METADATA_TYPE_PADDING:
691                         return true; /* we don't compare the padding guts */
692                 case FLAC__METADATA_TYPE_APPLICATION:
693                         return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
694                 case FLAC__METADATA_TYPE_SEEKTABLE:
695                         return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
696                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
697                         return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
698                 case FLAC__METADATA_TYPE_CUESHEET:
699                         return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
700                 default:
701                         return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
702         }
703 }
704
705 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
706 {
707         FLAC__byte *save;
708
709         FLAC__ASSERT(0 != object);
710         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
711         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
712
713         save = object->data.application.data;
714
715         /* do the copy first so that if we fail we leave the object untouched */
716         if(copy) {
717                 if(!copy_bytes_(&object->data.application.data, data, length))
718                         return false;
719         }
720         else {
721                 object->data.application.data = data;
722         }
723
724         if(0 != save)
725                 free(save);
726
727         object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
728         return true;
729 }
730
731 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
732 {
733         FLAC__ASSERT(0 != object);
734         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
735
736         if(0 == object->data.seek_table.points) {
737                 FLAC__ASSERT(object->data.seek_table.num_points == 0);
738                 if(0 == new_num_points)
739                         return true;
740                 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
741                         return false;
742         }
743         else {
744                 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
745                 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
746
747                 FLAC__ASSERT(object->data.seek_table.num_points > 0);
748
749                 if(new_size == 0) {
750                         free(object->data.seek_table.points);
751                         object->data.seek_table.points = 0;
752                 }
753                 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
754                         return false;
755
756                 /* if growing, set new elements to placeholders */
757                 if(new_size > old_size) {
758                         unsigned i;
759                         for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
760                                 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
761                                 object->data.seek_table.points[i].stream_offset = 0;
762                                 object->data.seek_table.points[i].frame_samples = 0;
763                         }
764                 }
765         }
766
767         object->data.seek_table.num_points = new_num_points;
768
769         seektable_calculate_length_(object);
770         return true;
771 }
772
773 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
774 {
775         FLAC__ASSERT(0 != object);
776         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
777         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
778
779         object->data.seek_table.points[point_num] = point;
780 }
781
782 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
783 {
784         int i;
785
786         FLAC__ASSERT(0 != object);
787         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
788         FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
789
790         if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
791                 return false;
792
793         /* move all points >= point_num forward one space */
794         for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
795                 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
796
797         FLAC__metadata_object_seektable_set_point(object, point_num, point);
798         seektable_calculate_length_(object);
799         return true;
800 }
801
802 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
803 {
804         unsigned i;
805
806         FLAC__ASSERT(0 != object);
807         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
808         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
809
810         /* move all points > point_num backward one space */
811         for(i = point_num; i < object->data.seek_table.num_points-1; i++)
812                 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
813
814         return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
815 }
816
817 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
818 {
819         FLAC__ASSERT(0 != object);
820         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
821
822         return FLAC__format_seektable_is_legal(&object->data.seek_table);
823 }
824
825 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
826 {
827         FLAC__ASSERT(0 != object);
828         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
829
830         if(num > 0)
831                 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
832                 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
833         else
834                 return true;
835 }
836
837 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
838 {
839         FLAC__StreamMetadata_SeekTable *seek_table;
840
841         FLAC__ASSERT(0 != object);
842         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
843
844         seek_table = &object->data.seek_table;
845
846         if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
847                 return false;
848
849         seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
850         seek_table->points[seek_table->num_points - 1].stream_offset = 0;
851         seek_table->points[seek_table->num_points - 1].frame_samples = 0;
852
853         return true;
854 }
855
856 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
857 {
858         FLAC__ASSERT(0 != object);
859         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
860         FLAC__ASSERT(0 != sample_numbers || num == 0);
861
862         if(num > 0) {
863                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
864                 unsigned i, j;
865
866                 i = seek_table->num_points;
867
868                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
869                         return false;
870
871                 for(j = 0; j < num; i++, j++) {
872                         seek_table->points[i].sample_number = sample_numbers[j];
873                         seek_table->points[i].stream_offset = 0;
874                         seek_table->points[i].frame_samples = 0;
875                 }
876         }
877
878         return true;
879 }
880
881 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
882 {
883         FLAC__ASSERT(0 != object);
884         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
885         FLAC__ASSERT(total_samples > 0);
886
887         if(num > 0) {
888                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
889                 unsigned i, j;
890
891                 i = seek_table->num_points;
892
893                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
894                         return false;
895
896                 for(j = 0; j < num; i++, j++) {
897                         seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
898                         seek_table->points[i].stream_offset = 0;
899                         seek_table->points[i].frame_samples = 0;
900                 }
901         }
902
903         return true;
904 }
905
906 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
907 {
908         unsigned unique;
909
910         FLAC__ASSERT(0 != object);
911         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
912
913         unique = FLAC__format_seektable_sort(&object->data.seek_table);
914
915         return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
916 }
917
918 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
919 {
920         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
921 }
922
923 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
924 {
925         FLAC__ASSERT(0 != object);
926         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
927
928         if(0 == object->data.vorbis_comment.comments) {
929                 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
930                 if(0 == new_num_comments)
931                         return true;
932                 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
933                         return false;
934         }
935         else {
936                 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
937                 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
938
939                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
940
941                 /* if shrinking, free the truncated entries */
942                 if(new_num_comments < object->data.vorbis_comment.num_comments) {
943                         unsigned i;
944                         for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
945                                 if(0 != object->data.vorbis_comment.comments[i].entry)
946                                         free(object->data.vorbis_comment.comments[i].entry);
947                 }
948
949                 if(new_size == 0) {
950                         free(object->data.vorbis_comment.comments);
951                         object->data.vorbis_comment.comments = 0;
952                 }
953                 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
954                         return false;
955
956                 /* if growing, zero all the length/pointers of new elements */
957                 if(new_size > old_size)
958                         memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
959         }
960
961         object->data.vorbis_comment.num_comments = new_num_comments;
962
963         vorbiscomment_calculate_length_(object);
964         return true;
965 }
966
967 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
968 {
969         FLAC__ASSERT(0 != object);
970         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
971
972         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
973 }
974
975 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
976 {
977         FLAC__StreamMetadata_VorbisComment *vc;
978
979         FLAC__ASSERT(0 != object);
980         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
981         FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
982
983         vc = &object->data.vorbis_comment;
984
985         if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
986                 return false;
987
988         /* move all comments >= comment_num forward one space */
989         memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
990         vc->comments[comment_num].length = 0;
991         vc->comments[comment_num].entry = 0;
992
993         return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
994 }
995
996 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
997 {
998         FLAC__StreamMetadata_VorbisComment *vc;
999
1000         FLAC__ASSERT(0 != object);
1001         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1002         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1003
1004         vc = &object->data.vorbis_comment;
1005
1006         /* free the comment at comment_num */
1007         if(0 != vc->comments[comment_num].entry)
1008                 free(vc->comments[comment_num].entry);
1009
1010         /* move all comments > comment_num backward one space */
1011         memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
1012         vc->comments[vc->num_comments-1].length = 0;
1013         vc->comments[vc->num_comments-1].entry = 0;
1014
1015         return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
1016 }
1017
1018 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, unsigned field_name_length)
1019 {
1020         const FLAC__byte *eq = (FLAC__byte*)memchr(entry->entry, '=', entry->length);
1021 #if defined _MSC_VER || defined __MINGW32__
1022 #define FLAC__STRNCASECMP strnicmp
1023 #else
1024 #define FLAC__STRNCASECMP strncasecmp
1025 #endif
1026         return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry->entry, field_name_length));
1027 #undef FLAC__STRNCASECMP
1028 }
1029
1030 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
1031 {
1032         const unsigned field_name_length = strlen(field_name);
1033         unsigned i;
1034
1035         FLAC__ASSERT(0 != object);
1036         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1037
1038         for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
1039                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length))
1040                         return (int)i;
1041         }
1042
1043         return -1;
1044 }
1045
1046 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
1047 {
1048         const unsigned field_name_length = strlen(field_name);
1049         unsigned i;
1050
1051         FLAC__ASSERT(0 != object);
1052         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1053
1054         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1055                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
1056                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
1057                                 return -1;
1058                         else
1059                                 return 1;
1060                 }
1061         }
1062
1063         return 0;
1064 }
1065
1066 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1067 {
1068         FLAC__bool ok = true;
1069         unsigned matching = 0;
1070         const unsigned field_name_length = strlen(field_name);
1071         int i;
1072
1073         FLAC__ASSERT(0 != object);
1074         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1075
1076         /* must delete from end to start otherwise it will interfere with our iteration */
1077         for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1078                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
1079                         matching++;
1080                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1081                 }
1082         }
1083
1084         return ok? (int)matching : -1;
1085 }
1086
1087 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new()
1088 {
1089         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
1090 }
1091
1092 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
1093 {
1094         FLAC__StreamMetadata_CueSheet_Track *to;
1095
1096         FLAC__ASSERT(0 != object);
1097
1098         if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
1099                 if(!copy_track_(to, object)) {
1100                         FLAC__metadata_object_cuesheet_track_delete(to);
1101                         return 0;
1102                 }
1103         }
1104
1105         return to;
1106 }
1107
1108 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
1109 {
1110         FLAC__ASSERT(0 != object);
1111
1112         if(0 != object->indices) {
1113                 FLAC__ASSERT(object->num_indices > 0);
1114                 free(object->indices);
1115         }
1116 }
1117
1118 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
1119 {
1120         FLAC__metadata_object_cuesheet_track_delete_data(object);
1121         free(object);
1122 }
1123
1124 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1125 {
1126         FLAC__StreamMetadata_CueSheet_Track *track;
1127         FLAC__ASSERT(0 != object);
1128         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1129         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1130
1131         track = &object->data.cue_sheet.tracks[track_num];
1132
1133         if(0 == track->indices) {
1134                 FLAC__ASSERT(track->num_indices == 0);
1135                 if(0 == new_num_indices)
1136                         return true;
1137                 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1138                         return false;
1139         }
1140         else {
1141                 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1142                 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1143
1144                 FLAC__ASSERT(track->num_indices > 0);
1145
1146                 if(new_size == 0) {
1147                         free(track->indices);
1148                         track->indices = 0;
1149                 }
1150                 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1151                         return false;
1152
1153                 /* if growing, zero all the lengths/pointers of new elements */
1154                 if(new_size > old_size)
1155                         memset(track->indices + track->num_indices, 0, new_size - old_size);
1156         }
1157
1158         track->num_indices = new_num_indices;
1159
1160         cuesheet_calculate_length_(object);
1161         return true;
1162 }
1163
1164 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index)
1165 {
1166         FLAC__StreamMetadata_CueSheet_Track *track;
1167
1168         FLAC__ASSERT(0 != object);
1169         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1170         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1171         FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1172
1173         track = &object->data.cue_sheet.tracks[track_num];
1174
1175         if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1176                 return false;
1177
1178         /* move all indices >= index_num forward one space */
1179         memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1180
1181         track->indices[index_num] = index;
1182         cuesheet_calculate_length_(object);
1183         return true;
1184 }
1185
1186 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1187 {
1188         FLAC__StreamMetadata_CueSheet_Index index;
1189         memset(&index, 0, sizeof(index));
1190         return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
1191 }
1192
1193 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1194 {
1195         FLAC__StreamMetadata_CueSheet_Track *track;
1196
1197         FLAC__ASSERT(0 != object);
1198         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1199         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1200         FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1201
1202         track = &object->data.cue_sheet.tracks[track_num];
1203
1204         /* move all indices > index_num backward one space */
1205         memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
1206
1207         FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1208         cuesheet_calculate_length_(object);
1209         return true;
1210 }
1211
1212 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1213 {
1214         FLAC__ASSERT(0 != object);
1215         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1216
1217         if(0 == object->data.cue_sheet.tracks) {
1218                 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1219                 if(0 == new_num_tracks)
1220                         return true;
1221                 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1222                         return false;
1223         }
1224         else {
1225                 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1226                 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1227
1228                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1229
1230                 /* if shrinking, free the truncated entries */
1231                 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1232                         unsigned i;
1233                         for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1234                                 if(0 != object->data.cue_sheet.tracks[i].indices)
1235                                         free(object->data.cue_sheet.tracks[i].indices);
1236                 }
1237
1238                 if(new_size == 0) {
1239                         free(object->data.cue_sheet.tracks);
1240                         object->data.cue_sheet.tracks = 0;
1241                 }
1242                 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1243                         return false;
1244
1245                 /* if growing, zero all the lengths/pointers of new elements */
1246                 if(new_size > old_size)
1247                         memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1248         }
1249
1250         object->data.cue_sheet.num_tracks = new_num_tracks;
1251
1252         cuesheet_calculate_length_(object);
1253         return true;
1254 }
1255
1256 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1257 {
1258         FLAC__ASSERT(0 != object);
1259         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1260
1261         return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
1262 }
1263
1264 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1265 {
1266         FLAC__StreamMetadata_CueSheet *cs;
1267
1268         FLAC__ASSERT(0 != object);
1269         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1270         FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1271
1272         cs = &object->data.cue_sheet;
1273
1274         if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1275                 return false;
1276
1277         /* move all tracks >= track_num forward one space */
1278         memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1279         cs->tracks[track_num].num_indices = 0;
1280         cs->tracks[track_num].indices = 0;
1281
1282         return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1283 }
1284
1285 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
1286 {
1287         FLAC__StreamMetadata_CueSheet_Track track;
1288         memset(&track, 0, sizeof(track));
1289         return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
1290 }
1291
1292 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1293 {
1294         FLAC__StreamMetadata_CueSheet *cs;
1295
1296         FLAC__ASSERT(0 != object);
1297         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1298         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1299
1300         cs = &object->data.cue_sheet;
1301
1302         /* free the track at track_num */
1303         if(0 != cs->tracks[track_num].indices)
1304                 free(cs->tracks[track_num].indices);
1305
1306         /* move all tracks > track_num backward one space */
1307         memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1308         cs->tracks[cs->num_tracks-1].num_indices = 0;
1309         cs->tracks[cs->num_tracks-1].indices = 0;
1310
1311         return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1312 }
1313
1314 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1315 {
1316         FLAC__ASSERT(0 != object);
1317         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1318
1319         return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1320 }