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