blob: 9adb6f539250035688b589064696a0a33804e8b4 [file] [log] [blame]
Josh Coalson90ced912002-05-30 05:23:38 +00001/* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001,2002 Josh Coalson
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20#include <stdlib.h>
21#include <string.h>
22
23#include "private/metadata.h"
24
25#include "FLAC/assert.h"
26
27
28/****************************************************************************
29 *
30 * Local routines
31 *
32 ***************************************************************************/
33
34static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
35{
36 if(bytes > 0 && 0 != from) {
37 FLAC__byte *x;
Josh Coalson5e3fcb22002-11-06 07:11:26 +000038 if(0 == (x = (FLAC__byte*)malloc(bytes)))
Josh Coalson90ced912002-05-30 05:23:38 +000039 return false;
40 memcpy(x, from, bytes);
41 *to = x;
42 }
43 else {
44 FLAC__ASSERT(0 == from);
45 FLAC__ASSERT(bytes == 0);
46 *to = 0;
47 }
48 return true;
49}
50
Josh Coalsoncc682512002-06-08 04:53:42 +000051static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
Josh Coalson90ced912002-05-30 05:23:38 +000052{
53 to->length = from->length;
54 if(0 == from->entry) {
55 FLAC__ASSERT(from->length == 0);
56 to->entry = 0;
57 }
58 else {
59 FLAC__byte *x;
60 FLAC__ASSERT(from->length > 0);
Josh Coalson5e3fcb22002-11-06 07:11:26 +000061 if(0 == (x = (FLAC__byte*)malloc(from->length)))
Josh Coalson90ced912002-05-30 05:23:38 +000062 return false;
63 memcpy(x, from->entry, from->length);
64 to->entry = x;
65 }
66 return true;
67}
68
Josh Coalson8e9c4512002-11-14 05:00:24 +000069static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
70{
71 memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
72 if(0 == from->indices) {
73 FLAC__ASSERT(from->num_indices == 0);
74 }
75 else {
76 FLAC__StreamMetadata_CueSheet_Index *x;
77 FLAC__ASSERT(from->num_indices > 0);
78 if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index))))
79 return false;
80 memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
81 to->indices = x;
82 }
83 return true;
84}
85
Josh Coalsoncc682512002-06-08 04:53:42 +000086static void seektable_calculate_length_(FLAC__StreamMetadata *object)
Josh Coalson90ced912002-05-30 05:23:38 +000087{
88 FLAC__ASSERT(0 != object);
89 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
90
91 object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
92}
93
Josh Coalsoncc682512002-06-08 04:53:42 +000094static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points)
Josh Coalson90ced912002-05-30 05:23:38 +000095{
Josh Coalsoncc682512002-06-08 04:53:42 +000096 FLAC__StreamMetadata_SeekPoint *object_array;
Josh Coalson90ced912002-05-30 05:23:38 +000097
98 FLAC__ASSERT(num_points > 0);
99
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000100 object_array = (FLAC__StreamMetadata_SeekPoint*)malloc(num_points * sizeof(FLAC__StreamMetadata_SeekPoint));
Josh Coalson90ced912002-05-30 05:23:38 +0000101
102 if(0 != object_array) {
103 unsigned i;
104 for(i = 0; i < num_points; i++) {
105 object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
106 object_array[i].stream_offset = 0;
107 object_array[i].frame_samples = 0;
108 }
109 }
110
111 return object_array;
112}
113
Josh Coalsoncc682512002-06-08 04:53:42 +0000114static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
Josh Coalson90ced912002-05-30 05:23:38 +0000115{
116 unsigned i;
117
118 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
119
120 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
121 object->length += object->data.vorbis_comment.vendor_string.length;
122 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
123 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
124 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
125 object->length += object->data.vorbis_comment.comments[i].length;
126 }
127}
128
Josh Coalsoncc682512002-06-08 04:53:42 +0000129static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
Josh Coalson90ced912002-05-30 05:23:38 +0000130{
Josh Coalson90ced912002-05-30 05:23:38 +0000131 FLAC__ASSERT(num_comments > 0);
132
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000133 return (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
Josh Coalson90ced912002-05-30 05:23:38 +0000134}
135
Josh Coalsoncc682512002-06-08 04:53:42 +0000136static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
Josh Coalson90ced912002-05-30 05:23:38 +0000137{
138 unsigned i;
139
140 FLAC__ASSERT(0 != object_array && num_comments > 0);
141
142 for(i = 0; i < num_comments; i++)
143 if(0 != object_array[i].entry)
144 free(object_array[i].entry);
145
146 if(0 != object_array)
147 free(object_array);
148}
149
Josh Coalsoncc682512002-06-08 04:53:42 +0000150static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
Josh Coalson90ced912002-05-30 05:23:38 +0000151{
Josh Coalsoncc682512002-06-08 04:53:42 +0000152 FLAC__StreamMetadata_VorbisComment_Entry *return_array;
Josh Coalson90ced912002-05-30 05:23:38 +0000153
154 FLAC__ASSERT(0 != object_array);
155 FLAC__ASSERT(num_comments > 0);
156
157 return_array = vorbiscomment_entry_array_new_(num_comments);
158
159 if(0 != return_array) {
160 unsigned i;
161
Josh Coalson90ced912002-05-30 05:23:38 +0000162 for(i = 0; i < num_comments; i++) {
163 if(!copy_vcentry_(return_array+i, object_array+i)) {
164 vorbiscomment_entry_array_delete_(return_array, num_comments);
165 return 0;
166 }
167 }
168 }
169
170 return return_array;
171}
172
Josh Coalsoncc682512002-06-08 04:53:42 +0000173static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
Josh Coalson90ced912002-05-30 05:23:38 +0000174{
175 FLAC__byte *save;
176
177 FLAC__ASSERT(0 != object);
178 FLAC__ASSERT(0 != dest);
179 FLAC__ASSERT(0 != src);
180 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
181 FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0 && copy == false));
182
Josh Coalson6b8e5302002-05-31 06:23:09 +0000183 save = dest->entry;
Josh Coalson90ced912002-05-30 05:23:38 +0000184
185 /* do the copy first so that if we fail we leave the object untouched */
186 if(copy) {
187 if(!copy_vcentry_(dest, src))
188 return false;
189 }
190 else {
191 *dest = *src;
192 }
193
194 if(0 != save)
195 free(save);
196
197 vorbiscomment_calculate_length_(object);
198 return true;
199}
200
Josh Coalson8e9c4512002-11-14 05:00:24 +0000201static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
202{
203 unsigned i;
204
205 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
206
207 object->length = (
208 FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
209 FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
210 FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
211 ) / 8;
212
213 object->length += object->data.cue_sheet.num_tracks * (
214 FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
215 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
216 FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
217 FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
218 FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
219 FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
220 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
221 ) / 8;
222
223 for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
224 object->length += object->data.cue_sheet.tracks[i].num_indices * (
225 FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
226 FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
227 FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
228 ) / 8;
229 }
230}
231
232static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
233{
234 FLAC__ASSERT(num_indices > 0);
235
236 return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
237}
238
239static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
240{
241 FLAC__ASSERT(num_tracks > 0);
242
243 return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
244}
245
246static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
247{
248 unsigned i;
249
250 FLAC__ASSERT(0 != object_array && num_tracks > 0);
251
252 for(i = 0; i < num_tracks; i++) {
253 if(0 != object_array[i].indices) {
254 FLAC__ASSERT(object_array[i].num_indices > 0);
255 free(object_array[i].indices);
256 }
257 }
258
259 if(0 != object_array)
260 free(object_array);
261}
262
263static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
264{
265 FLAC__StreamMetadata_CueSheet_Track *return_array;
266
267 FLAC__ASSERT(0 != object_array);
268 FLAC__ASSERT(num_tracks > 0);
269
270 return_array = cuesheet_track_array_new_(num_tracks);
271
272 if(0 != return_array) {
273 unsigned i;
274
275 for(i = 0; i < num_tracks; i++) {
276 if(!copy_track_(return_array+i, object_array+i)) {
277 cuesheet_track_array_delete_(return_array, num_tracks);
278 return 0;
279 }
280 }
281 }
282
283 return return_array;
284}
285
286static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
287{
288 FLAC__StreamMetadata_CueSheet_Index *save;
289
290 FLAC__ASSERT(0 != object);
291 FLAC__ASSERT(0 != dest);
292 FLAC__ASSERT(0 != src);
293 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
294 FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
295 /*@@@@ for docs, note that there is no "&& copy == false" at the end here ^^^ which will filter up */
296
297 save = dest->indices;
298
299 /* do the copy first so that if we fail we leave the object untouched */
300 if(copy) {
301 if(!copy_track_(dest, src))
302 return false;
303 }
304 else {
305 *dest = *src;
306 }
307
308 if(0 != save)
309 free(save);
310
311 cuesheet_calculate_length_(object);
312 return true;
313}
314
Josh Coalson90ced912002-05-30 05:23:38 +0000315
316/****************************************************************************
317 *
318 * Metadata object routines
319 *
320 ***************************************************************************/
321
Josh Coalson6afed9f2002-10-16 22:29:47 +0000322FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
Josh Coalson90ced912002-05-30 05:23:38 +0000323{
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000324 FLAC__StreamMetadata *object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
Josh Coalson90ced912002-05-30 05:23:38 +0000325 if(0 != object) {
Josh Coalson90ced912002-05-30 05:23:38 +0000326 object->is_last = false;
327 object->type = type;
328 switch(type) {
329 case FLAC__METADATA_TYPE_STREAMINFO:
330 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
331 break;
332 case FLAC__METADATA_TYPE_PADDING:
333 break;
334 case FLAC__METADATA_TYPE_APPLICATION:
335 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
336 break;
337 case FLAC__METADATA_TYPE_SEEKTABLE:
338 break;
339 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
Josh Coalson45bb9882002-10-26 04:34:16 +0000340 {
341 object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
342 if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length)) {
343 free(object);
344 return 0;
345 }
Josh Coalson8e9c4512002-11-14 05:00:24 +0000346 vorbiscomment_calculate_length_(object);
Josh Coalson45bb9882002-10-26 04:34:16 +0000347 }
Josh Coalson90ced912002-05-30 05:23:38 +0000348 break;
Josh Coalson8e9c4512002-11-14 05:00:24 +0000349 case FLAC__METADATA_TYPE_CUESHEET:
350 cuesheet_calculate_length_(object);
351 break;
Josh Coalson90ced912002-05-30 05:23:38 +0000352 default:
353 /* double protection: */
354 FLAC__ASSERT(0);
355 free(object);
356 return 0;
357 }
358 }
359
360 return object;
361}
362
Josh Coalson6afed9f2002-10-16 22:29:47 +0000363FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
Josh Coalson90ced912002-05-30 05:23:38 +0000364{
Josh Coalsoncc682512002-06-08 04:53:42 +0000365 FLAC__StreamMetadata *to;
Josh Coalson90ced912002-05-30 05:23:38 +0000366
367 FLAC__ASSERT(0 != object);
368
369 if(0 != (to = FLAC__metadata_object_new(object->type))) {
370 to->is_last = object->is_last;
371 to->type = object->type;
372 to->length = object->length;
373 switch(to->type) {
374 case FLAC__METADATA_TYPE_STREAMINFO:
Josh Coalsoncc682512002-06-08 04:53:42 +0000375 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
Josh Coalson90ced912002-05-30 05:23:38 +0000376 break;
377 case FLAC__METADATA_TYPE_PADDING:
378 break;
379 case FLAC__METADATA_TYPE_APPLICATION:
380 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
381 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
382 FLAC__metadata_object_delete(to);
383 return 0;
384 }
385 break;
386 case FLAC__METADATA_TYPE_SEEKTABLE:
387 to->data.seek_table.num_points = object->data.seek_table.num_points;
Josh Coalsoncc682512002-06-08 04:53:42 +0000388 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))) {
Josh Coalson90ced912002-05-30 05:23:38 +0000389 FLAC__metadata_object_delete(to);
390 return 0;
391 }
392 break;
393 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
Josh Coalson45bb9882002-10-26 04:34:16 +0000394 if(0 != to->data.vorbis_comment.vendor_string.entry)
395 free(to->data.vorbis_comment.vendor_string.entry);
Josh Coalson90ced912002-05-30 05:23:38 +0000396 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
397 FLAC__metadata_object_delete(to);
398 return 0;
399 }
400 if(object->data.vorbis_comment.num_comments == 0) {
401 FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
402 to->data.vorbis_comment.comments = 0;
403 }
404 else {
405 FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
406 to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
407 if(0 == to->data.vorbis_comment.comments) {
408 FLAC__metadata_object_delete(to);
409 return 0;
410 }
411 }
412 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
413 break;
Josh Coalson8e9c4512002-11-14 05:00:24 +0000414 case FLAC__METADATA_TYPE_CUESHEET:
415 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
416 if(object->data.cue_sheet.num_tracks == 0) {
417 FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
418 }
419 else {
420 FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
421 to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
422 if(0 == to->data.cue_sheet.tracks) {
423 FLAC__metadata_object_delete(to);
424 return 0;
425 }
426 }
427 break;
Josh Coalson90ced912002-05-30 05:23:38 +0000428 default:
429 /* double protection: */
430 FLAC__ASSERT(0);
431 free(to);
432 return 0;
433 }
434 }
435
436 return to;
437}
438
Josh Coalsoncc682512002-06-08 04:53:42 +0000439void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
Josh Coalson90ced912002-05-30 05:23:38 +0000440{
441 FLAC__ASSERT(0 != object);
442
443 switch(object->type) {
444 case FLAC__METADATA_TYPE_STREAMINFO:
445 case FLAC__METADATA_TYPE_PADDING:
446 break;
447 case FLAC__METADATA_TYPE_APPLICATION:
448 if(0 != object->data.application.data)
449 free(object->data.application.data);
450 break;
451 case FLAC__METADATA_TYPE_SEEKTABLE:
452 if(0 != object->data.seek_table.points)
453 free(object->data.seek_table.points);
454 break;
455 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
456 if(0 != object->data.vorbis_comment.vendor_string.entry)
457 free(object->data.vorbis_comment.vendor_string.entry);
458 if(0 != object->data.vorbis_comment.comments) {
459 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
460 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
461 }
462 break;
Josh Coalson8e9c4512002-11-14 05:00:24 +0000463 case FLAC__METADATA_TYPE_CUESHEET:
464 if(0 != object->data.cue_sheet.tracks) {
465 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
466 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
467 }
468 break;
Josh Coalson90ced912002-05-30 05:23:38 +0000469 default:
470 FLAC__ASSERT(0);
471 }
472}
473
Josh Coalson6afed9f2002-10-16 22:29:47 +0000474FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
Josh Coalson90ced912002-05-30 05:23:38 +0000475{
476 FLAC__metadata_object_delete_data(object);
477 free(object);
478}
479
Josh Coalsoncc682512002-06-08 04:53:42 +0000480static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
Josh Coalson57ba6f42002-06-07 05:27:37 +0000481{
482 if(block1->min_blocksize != block2->min_blocksize)
483 return false;
484 if(block1->max_blocksize != block2->max_blocksize)
485 return false;
486 if(block1->min_framesize != block2->min_framesize)
487 return false;
488 if(block1->max_framesize != block2->max_framesize)
489 return false;
490 if(block1->sample_rate != block2->sample_rate)
491 return false;
492 if(block1->channels != block2->channels)
493 return false;
494 if(block1->bits_per_sample != block2->bits_per_sample)
495 return false;
496 if(block1->total_samples != block2->total_samples)
497 return false;
498 if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
499 return false;
500 return true;
501}
502
Josh Coalsoncc682512002-06-08 04:53:42 +0000503static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
Josh Coalson57ba6f42002-06-07 05:27:37 +0000504{
505 FLAC__ASSERT(0 != block1);
506 FLAC__ASSERT(0 != block2);
507 FLAC__ASSERT(block_length >= sizeof(block1->id));
508
509 if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
510 return false;
511 if(0 != block1->data && 0 != block2->data)
512 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
513 else
514 return block1->data == block2->data;
515}
516
Josh Coalsoncc682512002-06-08 04:53:42 +0000517static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
Josh Coalson57ba6f42002-06-07 05:27:37 +0000518{
519 unsigned i;
520
521 FLAC__ASSERT(0 != block1);
522 FLAC__ASSERT(0 != block2);
523
524 if(block1->num_points != block2->num_points)
525 return false;
526
527 if(0 != block1->points && 0 != block2->points) {
528 for(i = 0; i < block1->num_points; i++) {
529 if(block1->points[i].sample_number != block2->points[i].sample_number)
530 return false;
531 if(block1->points[i].stream_offset != block2->points[i].stream_offset)
532 return false;
533 if(block1->points[i].frame_samples != block2->points[i].frame_samples)
534 return false;
535 }
536 return true;
537 }
538 else
539 return block1->points == block2->points;
540}
541
Josh Coalsoncc682512002-06-08 04:53:42 +0000542static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
Josh Coalson57ba6f42002-06-07 05:27:37 +0000543{
544 unsigned i;
545
546 if(block1->vendor_string.length != block2->vendor_string.length)
547 return false;
548
549 if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
550 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
551 return false;
552 }
553 else if(block1->vendor_string.entry != block2->vendor_string.entry)
554 return false;
555
556 if(block1->num_comments != block2->num_comments)
557 return false;
558
559 for(i = 0; i < block1->num_comments; i++) {
560 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
561 if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
562 return false;
563 }
564 else if(block1->comments[i].entry != block2->comments[i].entry)
565 return false;
566 }
567 return true;
568}
569
Josh Coalson8e9c4512002-11-14 05:00:24 +0000570static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
571{
572 unsigned i, j;
573
574 if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
575 return false;
576
577 if(block1->lead_in != block2->lead_in)
578 return false;
579
580 if(block1->num_tracks != block2->num_tracks)
581 return false;
582
583 if(0 != block1->tracks && 0 != block2->tracks) {
584 FLAC__ASSERT(block1->num_tracks > 0);
585 for(i = 0; i < block1->num_tracks; i++) {
586 if(block1->tracks[i].offset != block2->tracks[i].offset)
587 return false;
588 if(block1->tracks[i].number != block2->tracks[i].number)
589 return false;
590 if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
591 return false;
592 if(block1->tracks[i].type != block2->tracks[i].type)
593 return false;
594 if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
595 return false;
596 if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
597 return false;
598 if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
599 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
600 for(j = 0; j < block1->tracks[i].num_indices; j++) {
601 if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
602 return false;
603 if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
604 return false;
605 }
606 }
607 else if(block1->tracks[i].indices != block2->tracks[i].indices)
608 return false;
609 }
610 }
611 else if(block1->tracks != block2->tracks)
612 return false;
613 return true;
614}
615
Josh Coalson6afed9f2002-10-16 22:29:47 +0000616FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
Josh Coalson57ba6f42002-06-07 05:27:37 +0000617{
Josh Coalsond8ab3462002-07-11 05:54:05 +0000618 FLAC__ASSERT(0 != block1);
619 FLAC__ASSERT(0 != block2);
620
Josh Coalson57ba6f42002-06-07 05:27:37 +0000621 if(block1->type != block2->type) {
622 return false;
Josh Coalson5a5de732002-08-01 06:37:11 +0000623 }
Josh Coalson57ba6f42002-06-07 05:27:37 +0000624 if(block1->is_last != block2->is_last) {
625 return false;
Josh Coalson5a5de732002-08-01 06:37:11 +0000626 }
Josh Coalson57ba6f42002-06-07 05:27:37 +0000627 if(block1->length != block2->length) {
628 return false;
Josh Coalson5a5de732002-08-01 06:37:11 +0000629 }
Josh Coalson57ba6f42002-06-07 05:27:37 +0000630 switch(block1->type) {
631 case FLAC__METADATA_TYPE_STREAMINFO:
632 return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
633 case FLAC__METADATA_TYPE_PADDING:
634 return true; /* we don't compare the padding guts */
635 case FLAC__METADATA_TYPE_APPLICATION:
636 return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
637 case FLAC__METADATA_TYPE_SEEKTABLE:
638 return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
639 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
640 return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000641 case FLAC__METADATA_TYPE_CUESHEET:
642 return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
Josh Coalson57ba6f42002-06-07 05:27:37 +0000643 default:
644 FLAC__ASSERT(0);
645 return false;
646 }
647}
648
Josh Coalson6afed9f2002-10-16 22:29:47 +0000649FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
Josh Coalson90ced912002-05-30 05:23:38 +0000650{
651 FLAC__byte *save;
652
Josh Coalsond8ab3462002-07-11 05:54:05 +0000653 FLAC__ASSERT(0 != object);
Josh Coalson90ced912002-05-30 05:23:38 +0000654 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
655 FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
656
657 save = object->data.application.data;
658
659 /* do the copy first so that if we fail we leave the object untouched */
660 if(copy) {
661 if(!copy_bytes_(&object->data.application.data, data, length))
662 return false;
663 }
664 else {
665 object->data.application.data = data;
666 }
667
668 if(0 != save)
669 free(save);
670
671 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
672 return true;
673}
674
Josh Coalson6afed9f2002-10-16 22:29:47 +0000675FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
Josh Coalson90ced912002-05-30 05:23:38 +0000676{
677 FLAC__ASSERT(0 != object);
678 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
679
680 if(0 == object->data.seek_table.points) {
681 FLAC__ASSERT(object->data.seek_table.num_points == 0);
682 if(0 == new_num_points)
683 return true;
684 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
685 return false;
686 }
687 else {
Josh Coalsoncc682512002-06-08 04:53:42 +0000688 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
689 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
Josh Coalson90ced912002-05-30 05:23:38 +0000690
691 FLAC__ASSERT(object->data.seek_table.num_points > 0);
692
693 if(new_size == 0) {
694 free(object->data.seek_table.points);
695 object->data.seek_table.points = 0;
696 }
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000697 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
Josh Coalson90ced912002-05-30 05:23:38 +0000698 return false;
699
700 /* if growing, set new elements to placeholders */
701 if(new_size > old_size) {
702 unsigned i;
703 for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
704 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
705 object->data.seek_table.points[i].stream_offset = 0;
706 object->data.seek_table.points[i].frame_samples = 0;
707 }
708 }
709 }
710
711 object->data.seek_table.num_points = new_num_points;
712
713 seektable_calculate_length_(object);
714 return true;
715}
716
Josh Coalson6afed9f2002-10-16 22:29:47 +0000717FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
Josh Coalson90ced912002-05-30 05:23:38 +0000718{
719 FLAC__ASSERT(0 != object);
720 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000721 FLAC__ASSERT(point_num < object->data.seek_table.num_points);
Josh Coalson90ced912002-05-30 05:23:38 +0000722
723 object->data.seek_table.points[point_num] = point;
724}
725
Josh Coalson6afed9f2002-10-16 22:29:47 +0000726FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
Josh Coalson90ced912002-05-30 05:23:38 +0000727{
728 int i;
729
730 FLAC__ASSERT(0 != object);
Josh Coalson6b8e5302002-05-31 06:23:09 +0000731 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000732 FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
Josh Coalson90ced912002-05-30 05:23:38 +0000733
734 if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
735 return false;
736
737 /* move all points >= point_num forward one space */
738 for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
739 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
740
741 FLAC__metadata_object_seektable_set_point(object, point_num, point);
742 seektable_calculate_length_(object);
743 return true;
744}
745
Josh Coalson6afed9f2002-10-16 22:29:47 +0000746FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
Josh Coalson90ced912002-05-30 05:23:38 +0000747{
748 unsigned i;
749
750 FLAC__ASSERT(0 != object);
751 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000752 FLAC__ASSERT(point_num < object->data.seek_table.num_points);
Josh Coalson90ced912002-05-30 05:23:38 +0000753
754 /* move all points > point_num backward one space */
755 for(i = point_num; i < object->data.seek_table.num_points-1; i++)
756 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
757
758 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
759}
760
Josh Coalson6afed9f2002-10-16 22:29:47 +0000761FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
Josh Coalson28e08d82002-06-05 05:56:41 +0000762{
Josh Coalson28e08d82002-06-05 05:56:41 +0000763 FLAC__ASSERT(0 != object);
764 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
765
Josh Coalson0833f342002-07-15 05:31:55 +0000766 return FLAC__format_seektable_is_legal(&object->data.seek_table);
Josh Coalson28e08d82002-06-05 05:56:41 +0000767}
768
Josh Coalson6afed9f2002-10-16 22:29:47 +0000769FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
Josh Coalson5a5de732002-08-01 06:37:11 +0000770{
771 FLAC__ASSERT(0 != object);
772 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
773
774 if(num > 0)
775 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
776 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
777 else
778 return true;
779}
780
Josh Coalson6afed9f2002-10-16 22:29:47 +0000781FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
Josh Coalson5a5de732002-08-01 06:37:11 +0000782{
783 FLAC__StreamMetadata_SeekTable *seek_table;
784
785 FLAC__ASSERT(0 != object);
786 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
787
788 seek_table = &object->data.seek_table;
789
790 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
791 return false;
792
793 seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
794 seek_table->points[seek_table->num_points - 1].stream_offset = 0;
795 seek_table->points[seek_table->num_points - 1].frame_samples = 0;
796
797 return true;
798}
799
Josh Coalson6afed9f2002-10-16 22:29:47 +0000800FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
Josh Coalson5a5de732002-08-01 06:37:11 +0000801{
802 FLAC__ASSERT(0 != object);
803 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
804 FLAC__ASSERT(0 != sample_numbers || num == 0);
805
806 if(num > 0) {
807 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
808 unsigned i, j;
809
810 i = seek_table->num_points;
811
812 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
813 return false;
814
815 for(j = 0; j < num; i++, j++) {
816 seek_table->points[i].sample_number = sample_numbers[j];
817 seek_table->points[i].stream_offset = 0;
818 seek_table->points[i].frame_samples = 0;
819 }
820 }
821
822 return true;
823}
824
Josh Coalson6afed9f2002-10-16 22:29:47 +0000825FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
Josh Coalson5a5de732002-08-01 06:37:11 +0000826{
827 FLAC__ASSERT(0 != object);
828 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
829 FLAC__ASSERT(total_samples > 0);
830
831 if(num > 0) {
832 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
833 unsigned i, j;
834
835 i = seek_table->num_points;
836
837 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
838 return false;
839
840 for(j = 0; j < num; i++, j++) {
841 seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
842 seek_table->points[i].stream_offset = 0;
843 seek_table->points[i].frame_samples = 0;
844 }
845 }
846
847 return true;
848}
849
Josh Coalson6afed9f2002-10-16 22:29:47 +0000850FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
Josh Coalson5a5de732002-08-01 06:37:11 +0000851{
852 unsigned unique;
853
854 FLAC__ASSERT(0 != object);
855 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
856
857 unique = FLAC__format_seektable_sort(&object->data.seek_table);
858
859 return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
860}
861
Josh Coalson6afed9f2002-10-16 22:29:47 +0000862FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
Josh Coalson90ced912002-05-30 05:23:38 +0000863{
Josh Coalson6b8e5302002-05-31 06:23:09 +0000864 return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
Josh Coalson90ced912002-05-30 05:23:38 +0000865}
866
Josh Coalson6afed9f2002-10-16 22:29:47 +0000867FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
Josh Coalson90ced912002-05-30 05:23:38 +0000868{
869 FLAC__ASSERT(0 != object);
870 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
871
872 if(0 == object->data.vorbis_comment.comments) {
873 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
874 if(0 == new_num_comments)
875 return true;
876 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
877 return false;
878 }
879 else {
Josh Coalsoncc682512002-06-08 04:53:42 +0000880 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
881 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
Josh Coalson90ced912002-05-30 05:23:38 +0000882
883 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
884
885 /* if shrinking, free the truncated entries */
886 if(new_num_comments < object->data.vorbis_comment.num_comments) {
887 unsigned i;
888 for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
889 if(0 != object->data.vorbis_comment.comments[i].entry)
890 free(object->data.vorbis_comment.comments[i].entry);
891 }
892
893 if(new_size == 0) {
894 free(object->data.vorbis_comment.comments);
895 object->data.vorbis_comment.comments = 0;
896 }
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000897 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
Josh Coalson90ced912002-05-30 05:23:38 +0000898 return false;
899
900 /* if growing, zero all the length/pointers of new elements */
901 if(new_size > old_size)
902 memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
903 }
904
905 object->data.vorbis_comment.num_comments = new_num_comments;
906
907 vorbiscomment_calculate_length_(object);
908 return true;
909}
910
Josh Coalson6afed9f2002-10-16 22:29:47 +0000911FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
Josh Coalson90ced912002-05-30 05:23:38 +0000912{
Josh Coalson6b8e5302002-05-31 06:23:09 +0000913 return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
Josh Coalson90ced912002-05-30 05:23:38 +0000914}
915
Josh Coalson6afed9f2002-10-16 22:29:47 +0000916FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
Josh Coalson90ced912002-05-30 05:23:38 +0000917{
Josh Coalsoncc682512002-06-08 04:53:42 +0000918 FLAC__StreamMetadata_VorbisComment *vc;
Josh Coalson90ced912002-05-30 05:23:38 +0000919
920 FLAC__ASSERT(0 != object);
Josh Coalson90ced912002-05-30 05:23:38 +0000921 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000922 FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
Josh Coalson90ced912002-05-30 05:23:38 +0000923
Josh Coalson6b8e5302002-05-31 06:23:09 +0000924 vc = &object->data.vorbis_comment;
925
926 if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
Josh Coalson90ced912002-05-30 05:23:38 +0000927 return false;
928
929 /* move all comments >= comment_num forward one space */
Josh Coalsoncc682512002-06-08 04:53:42 +0000930 memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
Josh Coalson6b8e5302002-05-31 06:23:09 +0000931 vc->comments[comment_num].length = 0;
932 vc->comments[comment_num].entry = 0;
Josh Coalson90ced912002-05-30 05:23:38 +0000933
934 return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
935}
936
Josh Coalson6afed9f2002-10-16 22:29:47 +0000937FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
Josh Coalson90ced912002-05-30 05:23:38 +0000938{
Josh Coalsoncc682512002-06-08 04:53:42 +0000939 FLAC__StreamMetadata_VorbisComment *vc;
Josh Coalson90ced912002-05-30 05:23:38 +0000940
941 FLAC__ASSERT(0 != object);
942 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
Josh Coalson8e9c4512002-11-14 05:00:24 +0000943 FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
Josh Coalson90ced912002-05-30 05:23:38 +0000944
Josh Coalson6b8e5302002-05-31 06:23:09 +0000945 vc = &object->data.vorbis_comment;
946
Josh Coalson90ced912002-05-30 05:23:38 +0000947 /* free the comment at comment_num */
Josh Coalson6b8e5302002-05-31 06:23:09 +0000948 if(0 != vc->comments[comment_num].entry)
949 free(vc->comments[comment_num].entry);
Josh Coalson90ced912002-05-30 05:23:38 +0000950
951 /* move all comments > comment_num backward one space */
Josh Coalsoncc682512002-06-08 04:53:42 +0000952 memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
Josh Coalson6b8e5302002-05-31 06:23:09 +0000953 vc->comments[vc->num_comments-1].length = 0;
954 vc->comments[vc->num_comments-1].entry = 0;
Josh Coalson90ced912002-05-30 05:23:38 +0000955
Josh Coalson6b8e5302002-05-31 06:23:09 +0000956 return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
Josh Coalson90ced912002-05-30 05:23:38 +0000957}
Josh Coalson45bb9882002-10-26 04:34:16 +0000958
959FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, unsigned field_name_length)
960{
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000961 const FLAC__byte *eq = (FLAC__byte*)memchr(entry->entry, '=', entry->length);
Josh Coalson45bb9882002-10-26 04:34:16 +0000962#if defined _MSC_VER || defined __MINGW32__
963#define FLAC__STRNCASECMP strnicmp
964#else
965#define FLAC__STRNCASECMP strncasecmp
966#endif
Josh Coalson5e3fcb22002-11-06 07:11:26 +0000967 return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry->entry, field_name_length));
Josh Coalson45bb9882002-10-26 04:34:16 +0000968#undef FLAC__STRNCASECMP
969}
970
Josh Coalsonb667e702002-11-05 07:24:33 +0000971FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
Josh Coalson45bb9882002-10-26 04:34:16 +0000972{
973 const unsigned field_name_length = strlen(field_name);
974 unsigned i;
975
976 FLAC__ASSERT(0 != object);
977 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
978
979 for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
980 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length))
981 return (int)i;
982 }
983
984 return -1;
985}
986
987FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
988{
989 const unsigned field_name_length = strlen(field_name);
990 unsigned i;
991
992 FLAC__ASSERT(0 != object);
993 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
994
995 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
996 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
997 if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
998 return -1;
999 else
1000 return 1;
1001 }
1002 }
1003
1004 return 0;
1005}
1006
1007FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
1008{
1009 FLAC__bool ok = true;
1010 unsigned matching = 0;
1011 const unsigned field_name_length = strlen(field_name);
1012 int i;
1013
1014 FLAC__ASSERT(0 != object);
1015 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1016
1017 /* must delete from end to start otherwise it will interfere with our iteration */
1018 for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
1019 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments + i, field_name, field_name_length)) {
1020 matching++;
1021 ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
1022 }
1023 }
1024
1025 return ok? (int)matching : -1;
1026}
Josh Coalson8e9c4512002-11-14 05:00:24 +00001027
1028FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
1029{
1030 FLAC__StreamMetadata_CueSheet_Track *track;
1031 FLAC__ASSERT(0 != object);
1032 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1033 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1034
1035 track = &object->data.cue_sheet.tracks[track_num];
1036
1037 if(0 == track->indices) {
1038 FLAC__ASSERT(track->num_indices == 0);
1039 if(0 == new_num_indices)
1040 return true;
1041 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
1042 return false;
1043 }
1044 else {
1045 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1046 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
1047
1048 FLAC__ASSERT(track->num_indices > 0);
1049
1050 if(new_size == 0) {
1051 free(track->indices);
1052 track->indices = 0;
1053 }
1054 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
1055 return false;
1056
1057 /* if growing, zero all the lengths/pointers of new elements */
1058 if(new_size > old_size)
1059 memset(track->indices + track->num_indices, 0, new_size - old_size);
1060 }
1061
1062 track->num_indices = new_num_indices;
1063
1064 cuesheet_calculate_length_(object);
1065 return true;
1066}
1067
1068FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num, FLAC__StreamMetadata_CueSheet_Index index)
1069{
1070 FLAC__StreamMetadata_CueSheet_Track *track;
1071
1072 FLAC__ASSERT(0 != object);
1073 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1074 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1075 FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
1076
1077 track = &object->data.cue_sheet.tracks[track_num];
1078
1079 if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
1080 return false;
1081
1082 /* move all indices >= index_num forward one space */
1083 memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
1084
1085 track->indices[index_num] = index;
1086 cuesheet_calculate_length_(object);
1087 return true;
1088}
1089
1090FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
1091{
1092 FLAC__StreamMetadata_CueSheet_Track *track;
1093
1094 FLAC__ASSERT(0 != object);
1095 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1096 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1097 FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
1098
1099 track = &object->data.cue_sheet.tracks[track_num];
1100
1101 /* move all indices > index_num backward one space */
1102 memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(track->num_indices-index_num-1));
1103
1104 FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
1105 cuesheet_calculate_length_(object);
1106 return true;
1107}
1108
1109FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
1110{
1111 FLAC__ASSERT(0 != object);
1112 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1113
1114 if(0 == object->data.cue_sheet.tracks) {
1115 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
1116 if(0 == new_num_tracks)
1117 return true;
1118 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
1119 return false;
1120 }
1121 else {
1122 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1123 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
1124
1125 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
1126
1127 /* if shrinking, free the truncated entries */
1128 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
1129 unsigned i;
1130 for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
1131 if(0 != object->data.cue_sheet.tracks[i].indices)
1132 free(object->data.cue_sheet.tracks[i].indices);
1133 }
1134
1135 if(new_size == 0) {
1136 free(object->data.cue_sheet.tracks);
1137 object->data.cue_sheet.tracks = 0;
1138 }
1139 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
1140 return false;
1141
1142 /* if growing, zero all the lengths/pointers of new elements */
1143 if(new_size > old_size)
1144 memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
1145 }
1146
1147 object->data.cue_sheet.num_tracks = new_num_tracks;
1148
1149 cuesheet_calculate_length_(object);
1150 return true;
1151}
1152
1153FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track track, FLAC__bool copy)
1154{
1155 return cuesheet_set_track_(object, &object->data.cue_sheet.tracks[track_num], &track, copy);
1156}
1157
1158FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track track, FLAC__bool copy)
1159{
1160 FLAC__StreamMetadata_CueSheet *cs;
1161
1162 FLAC__ASSERT(0 != object);
1163 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1164 FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
1165
1166 cs = &object->data.cue_sheet;
1167
1168 if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
1169 return false;
1170
1171 /* move all tracks >= track_num forward one space */
1172 memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
1173 cs->tracks[track_num].num_indices = 0;
1174 cs->tracks[track_num].indices = 0;
1175
1176 return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
1177}
1178
1179FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
1180{
1181 FLAC__StreamMetadata_CueSheet *cs;
1182
1183 FLAC__ASSERT(0 != object);
1184 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1185 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
1186
1187 cs = &object->data.cue_sheet;
1188
1189 /* free the track at track_num */
1190 if(0 != cs->tracks[track_num].indices)
1191 free(cs->tracks[track_num].indices);
1192
1193 /* move all tracks > track_num backward one space */
1194 memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
1195 cs->tracks[cs->num_tracks-1].num_indices = 0;
1196 cs->tracks[cs->num_tracks-1].indices = 0;
1197
1198 return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
1199}
1200
1201FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
1202{
1203 FLAC__ASSERT(0 != object);
1204 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
1205
1206 return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
1207}