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