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