add support for building on os/2 with emx
[platform/upstream/flac.git] / src / libFLAC / metadata_object.c
1 /* libFLAC - Free Lossless Audio Codec library
2  * Copyright (C) 2001,2002,2003,2004,2005  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 ensure_null_terminated_(FLAC__byte **entry, unsigned length)
64 {
65         FLAC__byte *x = (FLAC__byte*)realloc(*entry, length+1);
66         if(0 != x) {
67                 x[length] = '\0';
68                 *entry = x;
69                 return true;
70         }
71         else
72                 return false;
73 }
74
75 static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
76 {
77         to->length = from->length;
78         if(0 == from->entry) {
79                 FLAC__ASSERT(from->length == 0);
80                 to->entry = 0;
81         }
82         else {
83                 FLAC__byte *x;
84                 FLAC__ASSERT(from->length > 0);
85                 if(0 == (x = (FLAC__byte*)malloc(from->length+1)))
86                         return false;
87                 memcpy(x, from->entry, from->length);
88                 x[from->length] = '\0';
89                 to->entry = x;
90         }
91         return true;
92 }
93
94 static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
95 {
96         memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
97         if(0 == from->indices) {
98                 FLAC__ASSERT(from->num_indices == 0);
99         }
100         else {
101                 FLAC__StreamMetadata_CueSheet_Index *x;
102                 FLAC__ASSERT(from->num_indices > 0);
103                 if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index))))
104                         return false;
105                 memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
106                 to->indices = x;
107         }
108         return true;
109 }
110
111 static void seektable_calculate_length_(FLAC__StreamMetadata *object)
112 {
113         FLAC__ASSERT(0 != object);
114         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
115
116         object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
117 }
118
119 static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points)
120 {
121         FLAC__StreamMetadata_SeekPoint *object_array;
122
123         FLAC__ASSERT(num_points > 0);
124
125         object_array = (FLAC__StreamMetadata_SeekPoint*)malloc(num_points * sizeof(FLAC__StreamMetadata_SeekPoint));
126
127         if(0 != object_array) {
128                 unsigned i;
129                 for(i = 0; i < num_points; i++) {
130                         object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
131                         object_array[i].stream_offset = 0;
132                         object_array[i].frame_samples = 0;
133                 }
134         }
135
136         return object_array;
137 }
138
139 static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
140 {
141         unsigned i;
142
143         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
144
145         object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
146         object->length += object->data.vorbis_comment.vendor_string.length;
147         object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
148         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
149                 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
150                 object->length += object->data.vorbis_comment.comments[i].length;
151         }
152 }
153
154 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
155 {
156         FLAC__ASSERT(num_comments > 0);
157
158         return (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
159 }
160
161 static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
162 {
163         unsigned i;
164
165         FLAC__ASSERT(0 != object_array && num_comments > 0);
166
167         for(i = 0; i < num_comments; i++)
168                 if(0 != object_array[i].entry)
169                         free(object_array[i].entry);
170
171         if(0 != object_array)
172                 free(object_array);
173 }
174
175 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
176 {
177         FLAC__StreamMetadata_VorbisComment_Entry *return_array;
178
179         FLAC__ASSERT(0 != object_array);
180         FLAC__ASSERT(num_comments > 0);
181
182         return_array = vorbiscomment_entry_array_new_(num_comments);
183
184         if(0 != return_array) {
185                 unsigned i;
186
187                 for(i = 0; i < num_comments; i++) {
188                         if(!copy_vcentry_(return_array+i, object_array+i)) {
189                                 vorbiscomment_entry_array_delete_(return_array, num_comments);
190                                 return 0;
191                         }
192                 }
193         }
194
195         return return_array;
196 }
197
198 static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
199 {
200         FLAC__byte *save;
201
202         FLAC__ASSERT(0 != object);
203         FLAC__ASSERT(0 != dest);
204         FLAC__ASSERT(0 != src);
205         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
206         FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0));
207
208         save = dest->entry;
209
210         if(0 != src->entry && src->length > 0) {
211                 if(copy) {
212                         /* do the copy first so that if we fail we leave the dest object untouched */
213                         if(!copy_vcentry_(dest, src))
214                                 return false;
215                 }
216                 else {
217                         /* we have to make sure that the string we're taking over is null-terminated */
218
219                         /*
220                          * Stripping the const from src->entry is OK since we're taking
221                          * ownership of the pointer.  This is a hack around a deficiency
222                          * in the API where the same function is used for 'copy' and
223                          * 'own', but the source entry is a const pointer.  If we were
224                          * precise, the 'own' flavor would be a separate function with a
225                          * non-const source pointer.  But it's not, so we hack away.
226                          */
227                         if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length))
228                                 return false;
229                         *dest = *src;
230                 }
231         }
232         else {
233                 /* the src is null */
234                 *dest = *src;
235         }
236
237         if(0 != save)
238                 free(save);
239
240         vorbiscomment_calculate_length_(object);
241         return true;
242 }
243
244 static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length)
245 {
246         unsigned i;
247
248         FLAC__ASSERT(0 != object);
249         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
250         FLAC__ASSERT(0 != field_name);
251
252         for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
253                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length))
254                         return (int)i;
255         }
256
257         return -1;
258 }
259
260 static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
261 {
262         unsigned i;
263
264         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
265
266         object->length = (
267                 FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
268                 FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
269                 FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN +
270                 FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN +
271                 FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
272         ) / 8;
273
274         object->length += object->data.cue_sheet.num_tracks * (
275                 FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
276                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
277                 FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
278                 FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
279                 FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
280                 FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
281                 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
282         ) / 8;
283
284         for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
285                 object->length += object->data.cue_sheet.tracks[i].num_indices * (
286                         FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
287                         FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
288                         FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
289                 ) / 8;
290         }
291 }
292
293 static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
294 {
295         FLAC__ASSERT(num_indices > 0);
296
297         return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
298 }
299
300 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
301 {
302         FLAC__ASSERT(num_tracks > 0);
303
304         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
305 }
306
307 static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
308 {
309         unsigned i;
310
311         FLAC__ASSERT(0 != object_array && num_tracks > 0);
312
313         for(i = 0; i < num_tracks; i++) {
314                 if(0 != object_array[i].indices) {
315                         FLAC__ASSERT(object_array[i].num_indices > 0);
316                         free(object_array[i].indices);
317                 }
318         }
319
320         if(0 != object_array)
321                 free(object_array);
322 }
323
324 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
325 {
326         FLAC__StreamMetadata_CueSheet_Track *return_array;
327
328         FLAC__ASSERT(0 != object_array);
329         FLAC__ASSERT(num_tracks > 0);
330
331         return_array = cuesheet_track_array_new_(num_tracks);
332
333         if(0 != return_array) {
334                 unsigned i;
335
336                 for(i = 0; i < num_tracks; i++) {
337                         if(!copy_track_(return_array+i, object_array+i)) {
338                                 cuesheet_track_array_delete_(return_array, num_tracks);
339                                 return 0;
340                         }
341                 }
342         }
343
344         return return_array;
345 }
346
347 static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
348 {
349         FLAC__StreamMetadata_CueSheet_Index *save;
350
351         FLAC__ASSERT(0 != object);
352         FLAC__ASSERT(0 != dest);
353         FLAC__ASSERT(0 != src);
354         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
355         FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
356
357         save = dest->indices;
358
359         /* do the copy first so that if we fail we leave the object untouched */
360         if(copy) {
361                 if(!copy_track_(dest, src))
362                         return false;
363         }
364         else {
365                 *dest = *src;
366         }
367
368         if(0 != save)
369                 free(save);
370
371         cuesheet_calculate_length_(object);
372         return true;
373 }
374
375
376 /****************************************************************************
377  *
378  * Metadata object routines
379  *
380  ***************************************************************************/
381
382 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
383 {
384         FLAC__StreamMetadata *object;
385
386         if(type > FLAC__MAX_METADATA_TYPE_CODE)
387                 return 0;
388
389         object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
390         if(0 != object) {
391                 object->is_last = false;
392                 object->type = type;
393                 switch(type) {
394                         case FLAC__METADATA_TYPE_STREAMINFO:
395                                 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
396                                 break;
397                         case FLAC__METADATA_TYPE_PADDING:
398                                 /* calloc() took care of this for us:
399                                 object->length = 0;
400                                 */
401                                 break;
402                         case FLAC__METADATA_TYPE_APPLICATION:
403                                 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
404                                 /* calloc() took care of this for us:
405                                 object->data.application.data = 0;
406                                 */
407                                 break;
408                         case FLAC__METADATA_TYPE_SEEKTABLE:
409                                 /* calloc() took care of this for us:
410                                 object->length = 0;
411                                 object->data.seek_table.num_points = 0;
412                                 object->data.seek_table.points = 0;
413                                 */
414                                 break;
415                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
416                                 {
417                                         object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
418                                         if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) {
419                                                 free(object);
420                                                 return 0;
421                                         }
422                                         vorbiscomment_calculate_length_(object);
423                                 }
424                                 break;
425                         case FLAC__METADATA_TYPE_CUESHEET:
426                                 cuesheet_calculate_length_(object);
427                                 break;
428                         default:
429                                 /* calloc() took care of this for us:
430                                 object->length = 0;
431                                 object->data.unknown.data = 0;
432                                 */
433                                 break;
434                 }
435         }
436
437         return object;
438 }
439
440 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
441 {
442         FLAC__StreamMetadata *to;
443
444         FLAC__ASSERT(0 != object);
445
446         if(0 != (to = FLAC__metadata_object_new(object->type))) {
447                 to->is_last = object->is_last;
448                 to->type = object->type;
449                 to->length = object->length;
450                 switch(to->type) {
451                         case FLAC__METADATA_TYPE_STREAMINFO:
452                                 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
453                                 break;
454                         case FLAC__METADATA_TYPE_PADDING:
455                                 break;
456                         case FLAC__METADATA_TYPE_APPLICATION:
457                                 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
458                                 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
459                                         FLAC__metadata_object_delete(to);
460                                         return 0;
461                                 }
462                                 break;
463                         case FLAC__METADATA_TYPE_SEEKTABLE:
464                                 to->data.seek_table.num_points = object->data.seek_table.num_points;
465                                 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))) {
466                                         FLAC__metadata_object_delete(to);
467                                         return 0;
468                                 }
469                                 break;
470                         case FLAC__METADATA_TYPE_VORBIS_COMMENT:
471                                 if(0 != to->data.vorbis_comment.vendor_string.entry) {
472                                         free(to->data.vorbis_comment.vendor_string.entry);
473                                         to->data.vorbis_comment.vendor_string.entry = 0;
474                                 }
475                                 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
476                                         FLAC__metadata_object_delete(to);
477                                         return 0;
478                                 }
479                                 if(object->data.vorbis_comment.num_comments == 0) {
480                                         FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
481                                         to->data.vorbis_comment.comments = 0;
482                                 }
483                                 else {
484                                         FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
485                                         to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
486                                         if(0 == to->data.vorbis_comment.comments) {
487                                                 FLAC__metadata_object_delete(to);
488                                                 return 0;
489                                         }
490                                 }
491                                 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
492                                 break;
493                         case FLAC__METADATA_TYPE_CUESHEET:
494                                 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
495                                 if(object->data.cue_sheet.num_tracks == 0) {
496                                         FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
497                                 }
498                                 else {
499                                         FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
500                                         to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
501                                         if(0 == to->data.cue_sheet.tracks) {
502                                                 FLAC__metadata_object_delete(to);
503                                                 return 0;
504                                         }
505                                 }
506                                 break;
507                         default:
508                                 if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
509                                         FLAC__metadata_object_delete(to);
510                                         return 0;
511                                 }
512                                 break;
513                 }
514         }
515
516         return to;
517 }
518
519 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
520 {
521         FLAC__ASSERT(0 != object);
522
523         switch(object->type) {
524                 case FLAC__METADATA_TYPE_STREAMINFO:
525                 case FLAC__METADATA_TYPE_PADDING:
526                         break;
527                 case FLAC__METADATA_TYPE_APPLICATION:
528                         if(0 != object->data.application.data) {
529                                 free(object->data.application.data);
530                                 object->data.application.data = 0;
531                         }
532                         break;
533                 case FLAC__METADATA_TYPE_SEEKTABLE:
534                         if(0 != object->data.seek_table.points) {
535                                 free(object->data.seek_table.points);
536                                 object->data.seek_table.points = 0;
537                         }
538                         break;
539                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
540                         if(0 != object->data.vorbis_comment.vendor_string.entry) {
541                                 free(object->data.vorbis_comment.vendor_string.entry);
542                                 object->data.vorbis_comment.vendor_string.entry = 0;
543                         }
544                         if(0 != object->data.vorbis_comment.comments) {
545                                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
546                                 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
547                         }
548                         break;
549                 case FLAC__METADATA_TYPE_CUESHEET:
550                         if(0 != object->data.cue_sheet.tracks) {
551                                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
552                                 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
553                         }
554                         break;
555                 default:
556                         if(0 != object->data.unknown.data) {
557                                 free(object->data.unknown.data);
558                                 object->data.unknown.data = 0;
559                         }
560                         break;
561         }
562 }
563
564 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
565 {
566         FLAC__metadata_object_delete_data(object);
567         free(object);
568 }
569
570 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
571 {
572         if(block1->min_blocksize != block2->min_blocksize)
573                 return false;
574         if(block1->max_blocksize != block2->max_blocksize)
575                 return false;
576         if(block1->min_framesize != block2->min_framesize)
577                 return false;
578         if(block1->max_framesize != block2->max_framesize)
579                 return false;
580         if(block1->sample_rate != block2->sample_rate)
581                 return false;
582         if(block1->channels != block2->channels)
583                 return false;
584         if(block1->bits_per_sample != block2->bits_per_sample)
585                 return false;
586         if(block1->total_samples != block2->total_samples)
587                 return false;
588         if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
589                 return false;
590         return true;
591 }
592
593 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
594 {
595         FLAC__ASSERT(0 != block1);
596         FLAC__ASSERT(0 != block2);
597         FLAC__ASSERT(block_length >= sizeof(block1->id));
598
599         if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
600                 return false;
601         if(0 != block1->data && 0 != block2->data)
602                 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
603         else
604                 return block1->data == block2->data;
605 }
606
607 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
608 {
609         unsigned i;
610
611         FLAC__ASSERT(0 != block1);
612         FLAC__ASSERT(0 != block2);
613
614         if(block1->num_points != block2->num_points)
615                 return false;
616
617         if(0 != block1->points && 0 != block2->points) {
618                 for(i = 0; i < block1->num_points; i++) {
619                         if(block1->points[i].sample_number != block2->points[i].sample_number)
620                                 return false;
621                         if(block1->points[i].stream_offset != block2->points[i].stream_offset)
622                                 return false;
623                         if(block1->points[i].frame_samples != block2->points[i].frame_samples)
624                                 return false;
625                 }
626                 return true;
627         }
628         else
629                 return block1->points == block2->points;
630 }
631
632 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
633 {
634         unsigned i;
635
636         if(block1->vendor_string.length != block2->vendor_string.length)
637                 return false;
638
639         if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
640                 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
641                         return false;
642         }
643         else if(block1->vendor_string.entry != block2->vendor_string.entry)
644                 return false;
645
646         if(block1->num_comments != block2->num_comments)
647                 return false;
648
649         for(i = 0; i < block1->num_comments; i++) {
650                 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
651                         if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
652                                 return false;
653                 }
654                 else if(block1->comments[i].entry != block2->comments[i].entry)
655                         return false;
656         }
657         return true;
658 }
659
660 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
661 {
662         unsigned i, j;
663
664         if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
665                 return false;
666
667         if(block1->lead_in != block2->lead_in)
668                 return false;
669
670         if(block1->is_cd != block2->is_cd)
671                 return false;
672
673         if(block1->num_tracks != block2->num_tracks)
674                 return false;
675
676         if(0 != block1->tracks && 0 != block2->tracks) {
677                 FLAC__ASSERT(block1->num_tracks > 0);
678                 for(i = 0; i < block1->num_tracks; i++) {
679                         if(block1->tracks[i].offset != block2->tracks[i].offset)
680                                 return false;
681                         if(block1->tracks[i].number != block2->tracks[i].number)
682                                 return false;
683                         if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
684                                 return false;
685                         if(block1->tracks[i].type != block2->tracks[i].type)
686                                 return false;
687                         if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
688                                 return false;
689                         if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
690                                 return false;
691                         if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
692                                 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
693                                 for(j = 0; j < block1->tracks[i].num_indices; j++) {
694                                         if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
695                                                 return false;
696                                         if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
697                                                 return false;
698                                 }
699                         }
700                         else if(block1->tracks[i].indices != block2->tracks[i].indices)
701                                 return false;
702                 }
703         }
704         else if(block1->tracks != block2->tracks)
705                 return false;
706         return true;
707 }
708
709 static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length)
710 {
711         FLAC__ASSERT(0 != block1);
712         FLAC__ASSERT(0 != block2);
713
714         if(0 != block1->data && 0 != block2->data)
715                 return 0 == memcmp(block1->data, block2->data, block_length);
716         else
717                 return block1->data == block2->data;
718 }
719
720 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
721 {
722         FLAC__ASSERT(0 != block1);
723         FLAC__ASSERT(0 != block2);
724
725         if(block1->type != block2->type) {
726                 return false;
727         }
728         if(block1->is_last != block2->is_last) {
729                 return false;
730         }
731         if(block1->length != block2->length) {
732                 return false;
733         }
734         switch(block1->type) {
735                 case FLAC__METADATA_TYPE_STREAMINFO:
736                         return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
737                 case FLAC__METADATA_TYPE_PADDING:
738                         return true; /* we don't compare the padding guts */
739                 case FLAC__METADATA_TYPE_APPLICATION:
740                         return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
741                 case FLAC__METADATA_TYPE_SEEKTABLE:
742                         return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
743                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
744                         return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
745                 case FLAC__METADATA_TYPE_CUESHEET:
746                         return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
747                 default:
748                         return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
749         }
750 }
751
752 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
753 {
754         FLAC__byte *save;
755
756         FLAC__ASSERT(0 != object);
757         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
758         FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
759
760         save = object->data.application.data;
761
762         /* do the copy first so that if we fail we leave the object untouched */
763         if(copy) {
764                 if(!copy_bytes_(&object->data.application.data, data, length))
765                         return false;
766         }
767         else {
768                 object->data.application.data = data;
769         }
770
771         if(0 != save)
772                 free(save);
773
774         object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
775         return true;
776 }
777
778 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
779 {
780         FLAC__ASSERT(0 != object);
781         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
782
783         if(0 == object->data.seek_table.points) {
784                 FLAC__ASSERT(object->data.seek_table.num_points == 0);
785                 if(0 == new_num_points)
786                         return true;
787                 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
788                         return false;
789         }
790         else {
791                 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
792                 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
793
794                 FLAC__ASSERT(object->data.seek_table.num_points > 0);
795
796                 if(new_size == 0) {
797                         free(object->data.seek_table.points);
798                         object->data.seek_table.points = 0;
799                 }
800                 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
801                         return false;
802
803                 /* if growing, set new elements to placeholders */
804                 if(new_size > old_size) {
805                         unsigned i;
806                         for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
807                                 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
808                                 object->data.seek_table.points[i].stream_offset = 0;
809                                 object->data.seek_table.points[i].frame_samples = 0;
810                         }
811                 }
812         }
813
814         object->data.seek_table.num_points = new_num_points;
815
816         seektable_calculate_length_(object);
817         return true;
818 }
819
820 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
821 {
822         FLAC__ASSERT(0 != object);
823         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
824         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
825
826         object->data.seek_table.points[point_num] = point;
827 }
828
829 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
830 {
831         int i;
832
833         FLAC__ASSERT(0 != object);
834         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
835         FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
836
837         if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
838                 return false;
839
840         /* move all points >= point_num forward one space */
841         for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
842                 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
843
844         FLAC__metadata_object_seektable_set_point(object, point_num, point);
845         seektable_calculate_length_(object);
846         return true;
847 }
848
849 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
850 {
851         unsigned i;
852
853         FLAC__ASSERT(0 != object);
854         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
855         FLAC__ASSERT(point_num < object->data.seek_table.num_points);
856
857         /* move all points > point_num backward one space */
858         for(i = point_num; i < object->data.seek_table.num_points-1; i++)
859                 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
860
861         return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
862 }
863
864 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
865 {
866         FLAC__ASSERT(0 != object);
867         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
868
869         return FLAC__format_seektable_is_legal(&object->data.seek_table);
870 }
871
872 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
873 {
874         FLAC__ASSERT(0 != object);
875         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
876
877         if(num > 0)
878                 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
879                 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
880         else
881                 return true;
882 }
883
884 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
885 {
886         FLAC__StreamMetadata_SeekTable *seek_table;
887
888         FLAC__ASSERT(0 != object);
889         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
890
891         seek_table = &object->data.seek_table;
892
893         if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
894                 return false;
895
896         seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
897         seek_table->points[seek_table->num_points - 1].stream_offset = 0;
898         seek_table->points[seek_table->num_points - 1].frame_samples = 0;
899
900         return true;
901 }
902
903 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
904 {
905         FLAC__ASSERT(0 != object);
906         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
907         FLAC__ASSERT(0 != sample_numbers || num == 0);
908
909         if(num > 0) {
910                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
911                 unsigned i, j;
912
913                 i = seek_table->num_points;
914
915                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
916                         return false;
917
918                 for(j = 0; j < num; i++, j++) {
919                         seek_table->points[i].sample_number = sample_numbers[j];
920                         seek_table->points[i].stream_offset = 0;
921                         seek_table->points[i].frame_samples = 0;
922                 }
923         }
924
925         return true;
926 }
927
928 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
929 {
930         FLAC__ASSERT(0 != object);
931         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
932         FLAC__ASSERT(total_samples > 0);
933
934         if(num > 0) {
935                 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
936                 unsigned i, j;
937
938                 i = seek_table->num_points;
939
940                 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
941                         return false;
942
943                 for(j = 0; j < num; i++, j++) {
944                         seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
945                         seek_table->points[i].stream_offset = 0;
946                         seek_table->points[i].frame_samples = 0;
947                 }
948         }
949
950         return true;
951 }
952
953 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
954 {
955         unsigned unique;
956
957         FLAC__ASSERT(0 != object);
958         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
959
960         unique = FLAC__format_seektable_sort(&object->data.seek_table);
961
962         return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
963 }
964
965 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
966 {
967         if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length))
968                 return false;
969         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
970 }
971
972 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
973 {
974         FLAC__ASSERT(0 != object);
975         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
976
977         if(0 == object->data.vorbis_comment.comments) {
978                 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
979                 if(0 == new_num_comments)
980                         return true;
981                 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
982                         return false;
983         }
984         else {
985                 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
986                 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
987
988                 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
989
990                 /* if shrinking, free the truncated entries */
991                 if(new_num_comments < object->data.vorbis_comment.num_comments) {
992                         unsigned i;
993                         for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
994                                 if(0 != object->data.vorbis_comment.comments[i].entry)
995                                         free(object->data.vorbis_comment.comments[i].entry);
996                 }
997
998                 if(new_size == 0) {
999                         free(object->data.vorbis_comment.comments);
1000                         object->data.vorbis_comment.comments = 0;
1001                 }
1002                 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
1003                         return false;
1004
1005                 /* if growing, zero all the length/pointers of new elements */
1006                 if(new_size > old_size)
1007                         memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
1008         }
1009
1010         object->data.vorbis_comment.num_comments = new_num_comments;
1011
1012         vorbiscomment_calculate_length_(object);
1013         return true;
1014 }
1015
1016 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1017 {
1018         FLAC__ASSERT(0 != object);
1019         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1020
1021         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1022                 return false;
1023         return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
1024 }
1025
1026 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1027 {
1028         FLAC__StreamMetadata_VorbisComment *vc;
1029
1030         FLAC__ASSERT(0 != object);
1031         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1032         FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
1033
1034         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1035                 return false;
1036
1037         vc = &object->data.vorbis_comment;
1038
1039         if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
1040                 return false;
1041
1042         /* move all comments >= comment_num forward one space */
1043         memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
1044         vc->comments[comment_num].length = 0;
1045         vc->comments[comment_num].entry = 0;
1046
1047         return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
1048 }
1049
1050 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
1051 {
1052         FLAC__ASSERT(0 != object);
1053         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1054         return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy);
1055 }
1056
1057 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy)
1058 {
1059         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1060
1061         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1062                 return false;
1063
1064         {
1065                 int i;
1066                 unsigned field_name_length;
1067                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1068
1069                 FLAC__ASSERT(0 != eq);
1070
1071                 if(0 == eq)
1072                         return false; /* double protection */
1073
1074                 field_name_length = eq-entry.entry;
1075
1076                 if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) {
1077                         unsigned index = (unsigned)i;
1078                         if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy))
1079                                 return false;
1080                         if(all && (index+1 < object->data.vorbis_comment.num_comments)) {
1081                                 for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) {
1082                                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i))
1083                                                 return false;
1084                                         if((unsigned)i < object->data.vorbis_comment.num_comments)
1085                                                 i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length);
1086                                         else
1087                                                 i = -1;
1088                                 }
1089                         }
1090                         return true;
1091                 }
1092                 else
1093                         return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
1094         }
1095 }
1096
1097 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
1098 {
1099         FLAC__StreamMetadata_VorbisComment *vc;
1100
1101         FLAC__ASSERT(0 != object);
1102         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1103         FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
1104
1105         vc = &object->data.vorbis_comment;
1106
1107         /* free the comment at comment_num */
1108         if(0 != vc->comments[comment_num].entry)
1109                 free(vc->comments[comment_num].entry);
1110
1111         /* move all comments > comment_num backward one space */
1112         memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
1113         vc->comments[vc->num_comments-1].length = 0;
1114         vc->comments[vc->num_comments-1].entry = 0;
1115
1116         return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
1117 }
1118
1119 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value)
1120 {
1121         FLAC__ASSERT(0 != entry);
1122         FLAC__ASSERT(0 != field_name);
1123         FLAC__ASSERT(0 != field_value);
1124
1125         if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
1126                 return false;
1127         if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1)))
1128                 return false;
1129
1130         {
1131                 const size_t nn = strlen(field_name);
1132                 const size_t nv = strlen(field_value);
1133                 entry->length = nn + 1 /*=*/ + nv;
1134                 if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1)))
1135                         return false;
1136                 memcpy(entry->entry, field_name, nn);
1137                 entry->entry[nn] = '=';
1138                 memcpy(entry->entry+nn+1, field_value, nv);
1139                 entry->entry[entry->length] = '\0';
1140         }
1141         
1142         return true;
1143 }
1144
1145 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value)
1146 {
1147         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1148         FLAC__ASSERT(0 != field_name);
1149         FLAC__ASSERT(0 != field_value);
1150
1151         if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
1152                 return false;
1153
1154         {
1155                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1156                 const size_t nn = eq-entry.entry;
1157                 const size_t nv = entry.length-nn-1; /* -1 for the '=' */
1158                 FLAC__ASSERT(0 != eq);
1159                 if(0 == eq)
1160                         return false; /* double protection */
1161                 if(0 == (*field_name = (char*)malloc(nn+1)))
1162                         return false;
1163                 if(0 == (*field_value = (char*)malloc(nv+1))) {
1164                         free(*field_name);
1165                         return false;
1166                 }
1167                 memcpy(*field_name, entry.entry, nn);
1168                 memcpy(*field_value, entry.entry+nn+1, nv);
1169                 (*field_name)[nn] = '\0';
1170                 (*field_value)[nv] = '\0';
1171         }
1172
1173         return true;
1174 }
1175
1176 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length)
1177 {
1178         FLAC__ASSERT(0 != entry.entry && entry.length > 0);
1179         {
1180                 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
1181 #if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
1182 #define FLAC__STRNCASECMP strnicmp
1183 #else
1184 #define FLAC__STRNCASECMP strncasecmp
1185 #endif
1186                 return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length));
1187 #undef FLAC__STRNCASECMP
1188         }
1189 }
1190
1191 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
1192 {
1193         FLAC__ASSERT(0 != field_name);
1194
1195         return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name));
1196 }
1197
1198 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
1199 {
1200         const unsigned field_name_length = strlen(field_name);
1201         unsigned i;
1202
1203         FLAC__ASSERT(0 != object);
1204         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1205
1206         for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1207                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1208                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
1209                                 return -1;
1210                         else
1211                                 return 1;
1212                 }
1213         }
1214
1215         return 0;
1216 }
1217
1218 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1219 {
1220         FLAC__bool ok = true;
1221         unsigned matching = 0;
1222         const unsigned field_name_length = strlen(field_name);
1223         int i;
1224
1225         FLAC__ASSERT(0 != object);
1226         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1227
1228         /* must delete from end to start otherwise it will interfere with our iteration */
1229         for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1230                 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
1231                         matching++;
1232                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1233                 }
1234         }
1235
1236         return ok? (int)matching : -1;
1237 }
1238
1239 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new()
1240 {
1241         return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
1242 }
1243
1244 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
1245 {
1246         FLAC__StreamMetadata_CueSheet_Track *to;
1247
1248         FLAC__ASSERT(0 != object);
1249
1250         if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
1251                 if(!copy_track_(to, object)) {
1252                         FLAC__metadata_object_cuesheet_track_delete(to);
1253                         return 0;
1254                 }
1255         }
1256
1257         return to;
1258 }
1259
1260 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
1261 {
1262         FLAC__ASSERT(0 != object);
1263
1264         if(0 != object->indices) {
1265                 FLAC__ASSERT(object->num_indices > 0);
1266                 free(object->indices);
1267         }
1268 }
1269
1270 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
1271 {
1272         FLAC__metadata_object_cuesheet_track_delete_data(object);
1273         free(object);
1274 }
1275
1276 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1277 {
1278         FLAC__StreamMetadata_CueSheet_Track *track;
1279         FLAC__ASSERT(0 != object);
1280         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1281         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1282
1283         track = &object->data.cue_sheet.tracks[track_num];
1284
1285         if(0 == track->indices) {
1286                 FLAC__ASSERT(track->num_indices == 0);
1287                 if(0 == new_num_indices)
1288                         return true;
1289                 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1290                         return false;
1291         }
1292         else {
1293                 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1294                 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1295
1296                 FLAC__ASSERT(track->num_indices > 0);
1297
1298                 if(new_size == 0) {
1299                         free(track->indices);
1300                         track->indices = 0;
1301                 }
1302                 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1303                         return false;
1304
1305                 /* if growing, zero all the lengths/pointers of new elements */
1306                 if(new_size > old_size)
1307                         memset(track->indices + track->num_indices, 0, new_size - old_size);
1308         }
1309
1310         track->num_indices = new_num_indices;
1311
1312         cuesheet_calculate_length_(object);
1313         return true;
1314 }
1315
1316 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)
1317 {
1318         FLAC__StreamMetadata_CueSheet_Track *track;
1319
1320         FLAC__ASSERT(0 != object);
1321         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1322         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1323         FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1324
1325         track = &object->data.cue_sheet.tracks[track_num];
1326
1327         if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1328                 return false;
1329
1330         /* move all indices >= index_num forward one space */
1331         memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1332
1333         track->indices[index_num] = index;
1334         cuesheet_calculate_length_(object);
1335         return true;
1336 }
1337
1338 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1339 {
1340         FLAC__StreamMetadata_CueSheet_Index index;
1341         memset(&index, 0, sizeof(index));
1342         return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
1343 }
1344
1345 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1346 {
1347         FLAC__StreamMetadata_CueSheet_Track *track;
1348
1349         FLAC__ASSERT(0 != object);
1350         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1351         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1352         FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1353
1354         track = &object->data.cue_sheet.tracks[track_num];
1355
1356         /* move all indices > index_num backward one space */
1357         memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
1358
1359         FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1360         cuesheet_calculate_length_(object);
1361         return true;
1362 }
1363
1364 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1365 {
1366         FLAC__ASSERT(0 != object);
1367         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1368
1369         if(0 == object->data.cue_sheet.tracks) {
1370                 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1371                 if(0 == new_num_tracks)
1372                         return true;
1373                 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1374                         return false;
1375         }
1376         else {
1377                 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1378                 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1379
1380                 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1381
1382                 /* if shrinking, free the truncated entries */
1383                 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1384                         unsigned i;
1385                         for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1386                                 if(0 != object->data.cue_sheet.tracks[i].indices)
1387                                         free(object->data.cue_sheet.tracks[i].indices);
1388                 }
1389
1390                 if(new_size == 0) {
1391                         free(object->data.cue_sheet.tracks);
1392                         object->data.cue_sheet.tracks = 0;
1393                 }
1394                 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1395                         return false;
1396
1397                 /* if growing, zero all the lengths/pointers of new elements */
1398                 if(new_size > old_size)
1399                         memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1400         }
1401
1402         object->data.cue_sheet.num_tracks = new_num_tracks;
1403
1404         cuesheet_calculate_length_(object);
1405         return true;
1406 }
1407
1408 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1409 {
1410         FLAC__ASSERT(0 != object);
1411         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1412
1413         return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
1414 }
1415
1416 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
1417 {
1418         FLAC__StreamMetadata_CueSheet *cs;
1419
1420         FLAC__ASSERT(0 != object);
1421         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1422         FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1423
1424         cs = &object->data.cue_sheet;
1425
1426         if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1427                 return false;
1428
1429         /* move all tracks >= track_num forward one space */
1430         memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1431         cs->tracks[track_num].num_indices = 0;
1432         cs->tracks[track_num].indices = 0;
1433
1434         return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1435 }
1436
1437 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
1438 {
1439         FLAC__StreamMetadata_CueSheet_Track track;
1440         memset(&track, 0, sizeof(track));
1441         return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
1442 }
1443
1444 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1445 {
1446         FLAC__StreamMetadata_CueSheet *cs;
1447
1448         FLAC__ASSERT(0 != object);
1449         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1450         FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1451
1452         cs = &object->data.cue_sheet;
1453
1454         /* free the track at track_num */
1455         if(0 != cs->tracks[track_num].indices)
1456                 free(cs->tracks[track_num].indices);
1457
1458         /* move all tracks > track_num backward one space */
1459         memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1460         cs->tracks[cs->num_tracks-1].num_indices = 0;
1461         cs->tracks[cs->num_tracks-1].indices = 0;
1462
1463         return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1464 }
1465
1466 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1467 {
1468         FLAC__ASSERT(0 != object);
1469         FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1470
1471         return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1472 }