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