implement new CUESHEET metadata block
[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_NUM_TRACKS_LEN
211         ) / 8;
212
213         object->length += object->data.cue_sheet.num_tracks * (
214                 FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
215                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
216                 FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
217                 FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
218                 FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
219                 FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
220                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
221         ) / 8;
222
223         for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
224                 object->length += object->data.cue_sheet.tracks[i].num_indices * (
225                         FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
226                         FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
227                         FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
228                 ) / 8;
229         }
230 }
231
232 static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
233 {
234         FLAC__ASSERT(num_indices > 0);
235
236         return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
237 }
238
239 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
240 {
241         FLAC__ASSERT(num_tracks > 0);
242
243         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
244 }
245
246 static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
247 {
248         unsigned i;
249
250         FLAC__ASSERT(0 != object_array && num_tracks > 0);
251
252         for(i = 0; i < num_tracks; i++) {
253                 if(0 != object_array[i].indices) {
254                         FLAC__ASSERT(object_array[i].num_indices > 0);
255                         free(object_array[i].indices);
256                 }
257         }
258
259         if(0 != object_array)
260                 free(object_array);
261 }
262
263 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
264 {
265         FLAC__StreamMetadata_CueSheet_Track *return_array;
266
267         FLAC__ASSERT(0 != object_array);
268         FLAC__ASSERT(num_tracks > 0);
269
270         return_array = cuesheet_track_array_new_(num_tracks);
271
272         if(0 != return_array) {
273                 unsigned i;
274
275                 for(i = 0; i < num_tracks; i++) {
276                         if(!copy_track_(return_array+i, object_array+i)) {
277                                 cuesheet_track_array_delete_(return_array, num_tracks);
278                                 return 0;
279                         }
280                 }
281         }
282
283         return return_array;
284 }
285
286 static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
287 {
288         FLAC__StreamMetadata_CueSheet_Index *save;
289
290         FLAC__ASSERT(0 != object);
291         FLAC__ASSERT(0 != dest);
292         FLAC__ASSERT(0 != src);
293         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
294         FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
295         /*@@@@ for docs, note that there is no "&& copy == false" at the end here ^^^ which will filter up */
296
297         save = dest->indices;
298
299         /* do the copy first so that if we fail we leave the object untouched */
300         if(copy) {
301                 if(!copy_track_(dest, src))
302                         return false;
303         }
304         else {
305                 *dest = *src;
306         }
307
308         if(0 != save)
309                 free(save);
310
311         cuesheet_calculate_length_(object);
312         return true;
313 }
314
315
316 /****************************************************************************
317  *
318  * Metadata object routines
319  *
320  ***************************************************************************/
321
322 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
323 {
324         FLAC__StreamMetadata *object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
325         if(0 != object) {
326                 object->is_last = false;
327                 object->type = type;
328                 switch(type) {
329                         case FLAC__METADATA_TYPE_STREAMINFO:
330                                 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
331                                 break;
332                         case FLAC__METADATA_TYPE_PADDING:
333                                 break;
334                         case FLAC__METADATA_TYPE_APPLICATION:
335                                 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
336                                 break;
337                         case FLAC__METADATA_TYPE_SEEKTABLE:
338                                 break;
339                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
340                                 {
341                                         object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
342                                         if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length)) {
343                                                 free(object);
344                                                 return 0;
345                                         }
346                                         vorbiscomment_calculate_length_(object);
347                                 }
348                                 break;
349                         case FLAC__METADATA_TYPE_CUESHEET:
350                                 cuesheet_calculate_length_(object);
351                                 break;
352                         default:
353                                 /* double protection: */
354                                 FLAC__ASSERT(0);
355                                 free(object);
356                                 return 0;
357                 }
358         }
359
360         return object;
361 }
362
363 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
364 {
365         FLAC__StreamMetadata *to;
366
367         FLAC__ASSERT(0 != object);
368
369         if(0 != (to = FLAC__metadata_object_new(object->type))) {
370                 to->is_last = object->is_last;
371                 to->type = object->type;
372                 to->length = object->length;
373                 switch(to->type) {
374                         case FLAC__METADATA_TYPE_STREAMINFO:
375                                 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
376                                 break;
377                         case FLAC__METADATA_TYPE_PADDING:
378                                 break;
379                         case FLAC__METADATA_TYPE_APPLICATION:
380                                 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
381                                 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
382                                         FLAC__metadata_object_delete(to);
383                                         return 0;
384                                 }
385                                 break;
386                         case FLAC__METADATA_TYPE_SEEKTABLE:
387                                 to->data.seek_table.num_points = object->data.seek_table.num_points;
388                                 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))) {
389                                         FLAC__metadata_object_delete(to);
390                                         return 0;
391                                 }
392                                 break;
393                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
394                                 if(0 != to->data.vorbis_comment.vendor_string.entry)
395                                         free(to->data.vorbis_comment.vendor_string.entry);
396                                 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
397                                         FLAC__metadata_object_delete(to);
398                                         return 0;
399                                 }
400                                 if(object->data.vorbis_comment.num_comments == 0) {
401                                         FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
402                                         to->data.vorbis_comment.comments = 0;
403                                 }
404                                 else {
405                                         FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
406                                         to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
407                                         if(0 == to->data.vorbis_comment.comments) {
408                                                 FLAC__metadata_object_delete(to);
409                                                 return 0;
410                                         }
411                                 }
412                                 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
413                                 break;
414                         case FLAC__METADATA_TYPE_CUESHEET:
415                                 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
416                                 if(object->data.cue_sheet.num_tracks == 0) {
417                                         FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
418                                 }
419                                 else {
420                                         FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
421                                         to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
422                                         if(0 == to->data.cue_sheet.tracks) {
423                                                 FLAC__metadata_object_delete(to);
424                                                 return 0;
425                                         }
426                                 }
427                                 break;
428                         default:
429                                 /* double protection: */
430                                 FLAC__ASSERT(0);
431                                 free(to);
432                                 return 0;
433                 }
434         }
435
436         return to;
437 }
438
439 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
440 {
441         FLAC__ASSERT(0 != object);
442
443         switch(object->type) {
444                 case FLAC__METADATA_TYPE_STREAMINFO:
445                 case FLAC__METADATA_TYPE_PADDING:
446                         break;
447                 case FLAC__METADATA_TYPE_APPLICATION:
448                         if(0 != object->data.application.data)
449                                 free(object->data.application.data);
450                         break;
451                 case FLAC__METADATA_TYPE_SEEKTABLE:
452                         if(0 != object->data.seek_table.points)
453                                 free(object->data.seek_table.points);
454                         break;
455                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
456                         if(0 != object->data.vorbis_comment.vendor_string.entry)
457                                 free(object->data.vorbis_comment.vendor_string.entry);
458                         if(0 != object->data.vorbis_comment.comments) {
459                                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
460                                 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
461                         }
462                         break;
463                 case FLAC__METADATA_TYPE_CUESHEET:
464                         if(0 != object->data.cue_sheet.tracks) {
465                                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
466                                 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
467                         }
468                         break;
469                 default:
470                         FLAC__ASSERT(0);
471         }
472 }
473
474 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
475 {
476         FLAC__metadata_object_delete_data(object);
477         free(object);
478 }
479
480 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
481 {
482         if(block1->min_blocksize != block2->min_blocksize)
483                 return false;
484         if(block1->max_blocksize != block2->max_blocksize)
485                 return false;
486         if(block1->min_framesize != block2->min_framesize)
487                 return false;
488         if(block1->max_framesize != block2->max_framesize)
489                 return false;
490         if(block1->sample_rate != block2->sample_rate)
491                 return false;
492         if(block1->channels != block2->channels)
493                 return false;
494         if(block1->bits_per_sample != block2->bits_per_sample)
495                 return false;
496         if(block1->total_samples != block2->total_samples)
497                 return false;
498         if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
499                 return false;
500         return true;
501 }
502
503 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
504 {
505         FLAC__ASSERT(0 != block1);
506         FLAC__ASSERT(0 != block2);
507         FLAC__ASSERT(block_length >= sizeof(block1->id));
508
509         if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
510                 return false;
511         if(0 != block1->data && 0 != block2->data)
512                 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
513         else
514                 return block1->data == block2->data;
515 }
516
517 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
518 {
519         unsigned i;
520
521         FLAC__ASSERT(0 != block1);
522         FLAC__ASSERT(0 != block2);
523
524         if(block1->num_points != block2->num_points)
525                 return false;
526
527         if(0 != block1->points && 0 != block2->points) {
528                 for(i = 0; i < block1->num_points; i++) {
529                         if(block1->points[i].sample_number != block2->points[i].sample_number)
530                                 return false;
531                         if(block1->points[i].stream_offset != block2->points[i].stream_offset)
532                                 return false;
533                         if(block1->points[i].frame_samples != block2->points[i].frame_samples)
534                                 return false;
535                 }
536                 return true;
537         }
538         else
539                 return block1->points == block2->points;
540 }
541
542 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
543 {
544         unsigned i;
545
546         if(block1->vendor_string.length != block2->vendor_string.length)
547                 return false;
548
549         if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
550                 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
551                         return false;
552         }
553         else if(block1->vendor_string.entry != block2->vendor_string.entry)
554                 return false;
555
556         if(block1->num_comments != block2->num_comments)
557                 return false;
558
559         for(i = 0; i < block1->num_comments; i++) {
560                 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
561                         if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
562                                 return false;
563                 }
564                 else if(block1->comments[i].entry != block2->comments[i].entry)
565                         return false;
566         }
567         return true;
568 }
569
570 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
571 {
572         unsigned i, j;
573
574         if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
575                 return false;
576
577         if(block1->lead_in != block2->lead_in)
578                 return false;
579
580         if(block1->num_tracks != block2->num_tracks)
581                 return false;
582
583         if(0 != block1->tracks && 0 != block2->tracks) {
584                 FLAC__ASSERT(block1->num_tracks > 0);
585                 for(i = 0; i < block1->num_tracks; i++) {
586                         if(block1->tracks[i].offset != block2->tracks[i].offset)
587                                 return false;
588                         if(block1->tracks[i].number != block2->tracks[i].number)
589                                 return false;
590                         if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
591                                 return false;
592                         if(block1->tracks[i].type != block2->tracks[i].type)
593                                 return false;
594                         if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
595                                 return false;
596                         if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
597                                 return false;
598                         if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
599                                 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
600                                 for(j = 0; j < block1->tracks[i].num_indices; j++) {
601                                         if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
602                                                 return false;
603                                         if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
604                                                 return false;
605                                 }
606                         }
607                         else if(block1->tracks[i].indices != block2->tracks[i].indices)
608                                 return false;
609                 }
610         }
611         else if(block1->tracks != block2->tracks)
612                 return false;
613         return true;
614 }
615
616 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
617 {
618         FLAC__ASSERT(0 != block1);
619         FLAC__ASSERT(0 != block2);
620
621         if(block1->type != block2->type) {
622                 return false;
623         }
624         if(block1->is_last != block2->is_last) {
625                 return false;
626         }
627         if(block1->length != block2->length) {
628                 return false;
629         }
630         switch(block1->type) {
631                 case FLAC__METADATA_TYPE_STREAMINFO:
632                         return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
633                 case FLAC__METADATA_TYPE_PADDING:
634                         return true; /* we don't compare the padding guts */
635                 case FLAC__METADATA_TYPE_APPLICATION:
636                         return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
637                 case FLAC__METADATA_TYPE_SEEKTABLE:
638                         return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
639                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
640                         return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
641                 case FLAC__METADATA_TYPE_CUESHEET:
642                         return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
643                 default:
644                         FLAC__ASSERT(0);
645                         return false;
646         }
647 }
648
649 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
650 {
651         FLAC__byte *save;
652
653         FLAC__ASSERT(0 != object);
654         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
655         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
656
657         save = object->data.application.data;
658
659         /* do the copy first so that if we fail we leave the object untouched */
660         if(copy) {
661                 if(!copy_bytes_(&object->data.application.data, data, length))
662                         return false;
663         }
664         else {
665                 object->data.application.data = data;
666         }
667
668         if(0 != save)
669                 free(save);
670
671         object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
672         return true;
673 }
674
675 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
676 {
677         FLAC__ASSERT(0 != object);
678         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
679
680         if(0 == object->data.seek_table.points) {
681                 FLAC__ASSERT(object->data.seek_table.num_points == 0);
682                 if(0 == new_num_points)
683                         return true;
684                 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
685                         return false;
686         }
687         else {
688                 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
689                 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
690
691                 FLAC__ASSERT(object->data.seek_table.num_points > 0);
692
693                 if(new_size == 0) {
694                         free(object->data.seek_table.points);
695                         object->data.seek_table.points = 0;
696                 }
697                 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
698                         return false;
699
700                 /* if growing, set new elements to placeholders */
701                 if(new_size > old_size) {
702                         unsigned i;
703                         for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
704                                 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
705                                 object->data.seek_table.points[i].stream_offset = 0;
706                                 object->data.seek_table.points[i].frame_samples = 0;
707                         }
708                 }
709         }
710
711         object->data.seek_table.num_points = new_num_points;
712
713         seektable_calculate_length_(object);
714         return true;
715 }
716
717 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
718 {
719         FLAC__ASSERT(0 != object);
720         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
721         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
722
723         object->data.seek_table.points[point_num] = point;
724 }
725
726 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
727 {
728         int i;
729
730         FLAC__ASSERT(0 != object);
731         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
732         FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
733
734         if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
735                 return false;
736
737         /* move all points >= point_num forward one space */
738         for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
739                 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
740
741         FLAC__metadata_object_seektable_set_point(object, point_num, point);
742         seektable_calculate_length_(object);
743         return true;
744 }
745
746 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
747 {
748         unsigned i;
749
750         FLAC__ASSERT(0 != object);
751         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
752         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
753
754         /* move all points > point_num backward one space */
755         for(i = point_num; i < object->data.seek_table.num_points-1; i++)
756                 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
757
758         return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
759 }
760
761 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
762 {
763         FLAC__ASSERT(0 != object);
764         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
765
766         return FLAC__format_seektable_is_legal(&object->data.seek_table);
767 }
768
769 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
770 {
771         FLAC__ASSERT(0 != object);
772         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
773
774         if(num > 0)
775                 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
776                 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
777         else
778                 return true;
779 }
780
781 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
782 {
783         FLAC__StreamMetadata_SeekTable *seek_table;
784
785         FLAC__ASSERT(0 != object);
786         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
787
788         seek_table = &object->data.seek_table;
789
790         if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
791                 return false;
792
793         seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
794         seek_table->points[seek_table->num_points - 1].stream_offset = 0;
795         seek_table->points[seek_table->num_points - 1].frame_samples = 0;
796
797         return true;
798 }
799
800 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
801 {
802         FLAC__ASSERT(0 != object);
803         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
804         FLAC__ASSERT(0 != sample_numbers || num == 0);
805
806         if(num > 0) {
807                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
808                 unsigned i, j;
809
810                 i = seek_table->num_points;
811
812                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
813                         return false;
814
815                 for(j = 0; j < num; i++, j++) {
816                         seek_table->points[i].sample_number = sample_numbers[j];
817                         seek_table->points[i].stream_offset = 0;
818                         seek_table->points[i].frame_samples = 0;
819                 }
820         }
821
822         return true;
823 }
824
825 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
826 {
827         FLAC__ASSERT(0 != object);
828         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
829         FLAC__ASSERT(total_samples > 0);
830
831         if(num > 0) {
832                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
833                 unsigned i, j;
834
835                 i = seek_table->num_points;
836
837                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
838                         return false;
839
840                 for(j = 0; j < num; i++, j++) {
841                         seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
842                         seek_table->points[i].stream_offset = 0;
843                         seek_table->points[i].frame_samples = 0;
844                 }
845         }
846
847         return true;
848 }
849
850 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
851 {
852         unsigned unique;
853
854         FLAC__ASSERT(0 != object);
855         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
856
857         unique = FLAC__format_seektable_sort(&object->data.seek_table);
858
859         return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
860 }
861
862 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
863 {
864         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
865 }
866
867 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
868 {
869         FLAC__ASSERT(0 != object);
870         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
871
872         if(0 == object->data.vorbis_comment.comments) {
873                 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
874                 if(0 == new_num_comments)
875                         return true;
876                 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
877                         return false;
878         }
879         else {
880                 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
881                 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
882
883                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
884
885                 /* if shrinking, free the truncated entries */
886                 if(new_num_comments < object->data.vorbis_comment.num_comments) {
887                         unsigned i;
888                         for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
889                                 if(0 != object->data.vorbis_comment.comments[i].entry)
890                                         free(object->data.vorbis_comment.comments[i].entry);
891                 }
892
893                 if(new_size == 0) {
894                         free(object->data.vorbis_comment.comments);
895                         object->data.vorbis_comment.comments = 0;
896                 }
897                 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
898                         return false;
899
900                 /* if growing, zero all the length/pointers of new elements */
901                 if(new_size > old_size)
902                         memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
903         }
904
905         object->data.vorbis_comment.num_comments = new_num_comments;
906
907         vorbiscomment_calculate_length_(object);
908         return true;
909 }
910
911 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
912 {
913         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
914 }
915
916 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
917 {
918         FLAC__StreamMetadata_VorbisComment *vc;
919
920         FLAC__ASSERT(0 != object);
921         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
922         FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
923
924         vc = &object->data.vorbis_comment;
925
926         if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
927                 return false;
928
929         /* move all comments >= comment_num forward one space */
930         memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
931         vc->comments[comment_num].length = 0;
932         vc->comments[comment_num].entry = 0;
933
934         return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
935 }
936
937 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
938 {
939         FLAC__StreamMetadata_VorbisComment *vc;
940
941         FLAC__ASSERT(0 != object);
942         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
943         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
944
945         vc = &object->data.vorbis_comment;
946
947         /* free the comment at comment_num */
948         if(0 != vc->comments[comment_num].entry)
949                 free(vc->comments[comment_num].entry);
950
951         /* move all comments > comment_num backward one space */
952         memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
953         vc->comments[vc->num_comments-1].length = 0;
954         vc->comments[vc->num_comments-1].entry = 0;
955
956         return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
957 }
958
959 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, unsigned field_name_length)
960 {
961         const FLAC__byte *eq = (FLAC__byte*)memchr(entry->entry, '=', entry->length);
962 #if defined _MSC_VER || defined __MINGW32__
963 #define FLAC__STRNCASECMP strnicmp
964 #else
965 #define FLAC__STRNCASECMP strncasecmp
966 #endif
967         return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry->entry, field_name_length));
968 #undef FLAC__STRNCASECMP
969 }
970
971 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
972 {
973         const unsigned field_name_length = strlen(field_name);
974         unsigned i;
975
976         FLAC__ASSERT(0 != object);
977         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
978
979         for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
980                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length))
981                         return (int)i;
982         }
983
984         return -1;
985 }
986
987 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
988 {
989         const unsigned field_name_length = strlen(field_name);
990         unsigned i;
991
992         FLAC__ASSERT(0 != object);
993         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
994
995         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
996                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
997                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
998                                 return -1;
999                         else
1000                                 return 1;
1001                 }
1002         }
1003
1004         return 0;
1005 }
1006
1007 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1008 {
1009         FLAC__bool ok = true;
1010         unsigned matching = 0;
1011         const unsigned field_name_length = strlen(field_name);
1012         int i;
1013
1014         FLAC__ASSERT(0 != object);
1015         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1016
1017         /* must delete from end to start otherwise it will interfere with our iteration */
1018         for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1019                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
1020                         matching++;
1021                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1022                 }
1023         }
1024
1025         return ok? (int)matching : -1;
1026 }
1027
1028 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1029 {
1030         FLAC__StreamMetadata_CueSheet_Track *track;
1031         FLAC__ASSERT(0 != object);
1032         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1033         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1034
1035         track = &object->data.cue_sheet.tracks[track_num];
1036
1037         if(0 == track->indices) {
1038                 FLAC__ASSERT(track->num_indices == 0);
1039                 if(0 == new_num_indices)
1040                         return true;
1041                 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1042                         return false;
1043         }
1044         else {
1045                 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1046                 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1047
1048                 FLAC__ASSERT(track->num_indices > 0);
1049
1050                 if(new_size == 0) {
1051                         free(track->indices);
1052                         track->indices = 0;
1053                 }
1054                 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1055                         return false;
1056
1057                 /* if growing, zero all the lengths/pointers of new elements */
1058                 if(new_size > old_size)
1059                         memset(track->indices + track->num_indices, 0, new_size - old_size);
1060         }
1061
1062         track->num_indices = new_num_indices;
1063
1064         cuesheet_calculate_length_(object);
1065         return true;
1066 }
1067
1068 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)
1069 {
1070         FLAC__StreamMetadata_CueSheet_Track *track;
1071
1072         FLAC__ASSERT(0 != object);
1073         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1074         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1075         FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1076
1077         track = &object->data.cue_sheet.tracks[track_num];
1078
1079         if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1080                 return false;
1081
1082         /* move all indices >= index_num forward one space */
1083         memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1084
1085         track->indices[index_num] = index;
1086         cuesheet_calculate_length_(object);
1087         return true;
1088 }
1089
1090 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1091 {
1092         FLAC__StreamMetadata_CueSheet_Track *track;
1093
1094         FLAC__ASSERT(0 != object);
1095         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1096         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1097         FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1098
1099         track = &object->data.cue_sheet.tracks[track_num];
1100
1101         /* move all indices > index_num backward one space */
1102         memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(track->num_indices-index_num-1));
1103
1104         FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1105         cuesheet_calculate_length_(object);
1106         return true;
1107 }
1108
1109 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1110 {
1111         FLAC__ASSERT(0 != object);
1112         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1113
1114         if(0 == object->data.cue_sheet.tracks) {
1115                 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1116                 if(0 == new_num_tracks)
1117                         return true;
1118                 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1119                         return false;
1120         }
1121         else {
1122                 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1123                 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1124
1125                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1126
1127                 /* if shrinking, free the truncated entries */
1128                 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1129                         unsigned i;
1130                         for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1131                                 if(0 != object->data.cue_sheet.tracks[i].indices)
1132                                         free(object->data.cue_sheet.tracks[i].indices);
1133                 }
1134
1135                 if(new_size == 0) {
1136                         free(object->data.cue_sheet.tracks);
1137                         object->data.cue_sheet.tracks = 0;
1138                 }
1139                 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1140                         return false;
1141
1142                 /* if growing, zero all the lengths/pointers of new elements */
1143                 if(new_size > old_size)
1144                         memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1145         }
1146
1147         object->data.cue_sheet.num_tracks = new_num_tracks;
1148
1149         cuesheet_calculate_length_(object);
1150         return true;
1151 }
1152
1153 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track track, FLAC__bool copy)
1154 {
1155         return cuesheet_set_track_(object, &object->data.cue_sheet.tracks[track_num], &track, copy);
1156 }
1157
1158 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track track, FLAC__bool copy)
1159 {
1160         FLAC__StreamMetadata_CueSheet *cs;
1161
1162         FLAC__ASSERT(0 != object);
1163         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1164         FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1165
1166         cs = &object->data.cue_sheet;
1167
1168         if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1169                 return false;
1170
1171         /* move all tracks >= track_num forward one space */
1172         memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1173         cs->tracks[track_num].num_indices = 0;
1174         cs->tracks[track_num].indices = 0;
1175
1176         return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1177 }
1178
1179 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1180 {
1181         FLAC__StreamMetadata_CueSheet *cs;
1182
1183         FLAC__ASSERT(0 != object);
1184         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1185         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1186
1187         cs = &object->data.cue_sheet;
1188
1189         /* free the track at track_num */
1190         if(0 != cs->tracks[track_num].indices)
1191                 free(cs->tracks[track_num].indices);
1192
1193         /* move all tracks > track_num backward one space */
1194         memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1195         cs->tracks[cs->num_tracks-1].num_indices = 0;
1196         cs->tracks[cs->num_tracks-1].indices = 0;
1197
1198         return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1199 }
1200
1201 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1202 {
1203         FLAC__ASSERT(0 != object);
1204         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1205
1206         return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1207 }