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