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