715
|
1 /* libFLAC - Free Lossless Audio Codec library
|
|
2 * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 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 #if HAVE_CONFIG_H
|
|
33 # include <config.h>
|
|
34 #endif
|
|
35
|
|
36 #include <stdlib.h>
|
|
37 #include <string.h>
|
|
38
|
|
39 #include "private/metadata.h"
|
|
40
|
|
41 #include "FLAC/assert.h"
|
|
42
|
|
43
|
|
44 /****************************************************************************
|
|
45 *
|
|
46 * Local routines
|
|
47 *
|
|
48 ***************************************************************************/
|
|
49
|
|
50 /* copy bytes:
|
|
51 * from = NULL && bytes = 0
|
|
52 * to <- NULL
|
|
53 * from != NULL && bytes > 0
|
|
54 * to <- copy of from
|
|
55 * else ASSERT
|
|
56 * malloc error leaved 'to' unchanged
|
|
57 */
|
|
58 static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
|
|
59 {
|
|
60 FLAC__ASSERT(0 != to);
|
|
61 if(bytes > 0 && 0 != from) {
|
|
62 FLAC__byte *x;
|
|
63 if(0 == (x = (FLAC__byte*)malloc(bytes)))
|
|
64 return false;
|
|
65 memcpy(x, from, bytes);
|
|
66 *to = x;
|
|
67 }
|
|
68 else {
|
|
69 FLAC__ASSERT(0 == from);
|
|
70 FLAC__ASSERT(bytes == 0);
|
|
71 *to = 0;
|
|
72 }
|
|
73 return true;
|
|
74 }
|
|
75
|
|
76 #if 0 /* UNUSED */
|
|
77 /* like copy_bytes_(), but free()s the original '*to' if the copy succeeds and the original '*to' is non-NULL */
|
|
78 static FLAC__bool free_copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
|
|
79 {
|
|
80 FLAC__byte *copy;
|
|
81 FLAC__ASSERT(0 != to);
|
|
82 if(copy_bytes_(©, from, bytes)) {
|
|
83 if(*to)
|
|
84 free(*to);
|
|
85 *to = copy;
|
|
86 return true;
|
|
87 }
|
|
88 else
|
|
89 return false;
|
|
90 }
|
|
91 #endif
|
|
92
|
|
93 /* reallocate entry to 1 byte larger and add a terminating NUL */
|
|
94 /* realloc() failure leaves entry unchanged */
|
|
95 static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, unsigned length)
|
|
96 {
|
|
97 FLAC__byte *x = (FLAC__byte*)realloc(*entry, length+1);
|
|
98 if(0 != x) {
|
|
99 x[length] = '\0';
|
|
100 *entry = x;
|
|
101 return true;
|
|
102 }
|
|
103 else
|
|
104 return false;
|
|
105 }
|
|
106
|
|
107 /* copies the NUL-terminated C-string 'from' to '*to', leaving '*to'
|
|
108 * unchanged if malloc fails, free()ing the original '*to' if it
|
|
109 * succeeds and the original '*to' was not NULL
|
|
110 */
|
|
111 static FLAC__bool copy_cstring_(char **to, const char *from)
|
|
112 {
|
|
113 char *copy = strdup(from);
|
|
114 FLAC__ASSERT(to);
|
|
115 if(copy) {
|
|
116 if(*to)
|
|
117 free(*to);
|
|
118 *to = copy;
|
|
119 return true;
|
|
120 }
|
|
121 else
|
|
122 return false;
|
|
123 }
|
|
124
|
|
125 static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from)
|
|
126 {
|
|
127 to->length = from->length;
|
|
128 if(0 == from->entry) {
|
|
129 FLAC__ASSERT(from->length == 0);
|
|
130 to->entry = 0;
|
|
131 }
|
|
132 else {
|
|
133 FLAC__byte *x;
|
|
134 FLAC__ASSERT(from->length > 0);
|
|
135 if(0 == (x = (FLAC__byte*)malloc(from->length+1)))
|
|
136 return false;
|
|
137 memcpy(x, from->entry, from->length);
|
|
138 x[from->length] = '\0';
|
|
139 to->entry = x;
|
|
140 }
|
|
141 return true;
|
|
142 }
|
|
143
|
|
144 static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from)
|
|
145 {
|
|
146 memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track));
|
|
147 if(0 == from->indices) {
|
|
148 FLAC__ASSERT(from->num_indices == 0);
|
|
149 }
|
|
150 else {
|
|
151 FLAC__StreamMetadata_CueSheet_Index *x;
|
|
152 FLAC__ASSERT(from->num_indices > 0);
|
|
153 if(0 == (x = (FLAC__StreamMetadata_CueSheet_Index*)malloc(from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index))))
|
|
154 return false;
|
|
155 memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index));
|
|
156 to->indices = x;
|
|
157 }
|
|
158 return true;
|
|
159 }
|
|
160
|
|
161 static void seektable_calculate_length_(FLAC__StreamMetadata *object)
|
|
162 {
|
|
163 FLAC__ASSERT(0 != object);
|
|
164 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
165
|
|
166 object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
|
|
167 }
|
|
168
|
|
169 static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(unsigned num_points)
|
|
170 {
|
|
171 FLAC__StreamMetadata_SeekPoint *object_array;
|
|
172
|
|
173 FLAC__ASSERT(num_points > 0);
|
|
174
|
|
175 object_array = (FLAC__StreamMetadata_SeekPoint*)malloc(num_points * sizeof(FLAC__StreamMetadata_SeekPoint));
|
|
176
|
|
177 if(0 != object_array) {
|
|
178 unsigned i;
|
|
179 for(i = 0; i < num_points; i++) {
|
|
180 object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
|
|
181 object_array[i].stream_offset = 0;
|
|
182 object_array[i].frame_samples = 0;
|
|
183 }
|
|
184 }
|
|
185
|
|
186 return object_array;
|
|
187 }
|
|
188
|
|
189 static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object)
|
|
190 {
|
|
191 unsigned i;
|
|
192
|
|
193 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
194
|
|
195 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
|
|
196 object->length += object->data.vorbis_comment.vendor_string.length;
|
|
197 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
|
|
198 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
|
|
199 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
|
|
200 object->length += object->data.vorbis_comment.comments[i].length;
|
|
201 }
|
|
202 }
|
|
203
|
|
204 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
|
|
205 {
|
|
206 FLAC__ASSERT(num_comments > 0);
|
|
207
|
|
208 return (FLAC__StreamMetadata_VorbisComment_Entry*)calloc(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry));
|
|
209 }
|
|
210
|
|
211 static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
|
|
212 {
|
|
213 unsigned i;
|
|
214
|
|
215 FLAC__ASSERT(0 != object_array && num_comments > 0);
|
|
216
|
|
217 for(i = 0; i < num_comments; i++)
|
|
218 if(0 != object_array[i].entry)
|
|
219 free(object_array[i].entry);
|
|
220
|
|
221 if(0 != object_array)
|
|
222 free(object_array);
|
|
223 }
|
|
224
|
|
225 static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, unsigned num_comments)
|
|
226 {
|
|
227 FLAC__StreamMetadata_VorbisComment_Entry *return_array;
|
|
228
|
|
229 FLAC__ASSERT(0 != object_array);
|
|
230 FLAC__ASSERT(num_comments > 0);
|
|
231
|
|
232 return_array = vorbiscomment_entry_array_new_(num_comments);
|
|
233
|
|
234 if(0 != return_array) {
|
|
235 unsigned i;
|
|
236
|
|
237 for(i = 0; i < num_comments; i++) {
|
|
238 if(!copy_vcentry_(return_array+i, object_array+i)) {
|
|
239 vorbiscomment_entry_array_delete_(return_array, num_comments);
|
|
240 return 0;
|
|
241 }
|
|
242 }
|
|
243 }
|
|
244
|
|
245 return return_array;
|
|
246 }
|
|
247
|
|
248 static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy)
|
|
249 {
|
|
250 FLAC__byte *save;
|
|
251
|
|
252 FLAC__ASSERT(0 != object);
|
|
253 FLAC__ASSERT(0 != dest);
|
|
254 FLAC__ASSERT(0 != src);
|
|
255 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
256 FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0));
|
|
257
|
|
258 save = dest->entry;
|
|
259
|
|
260 if(0 != src->entry && src->length > 0) {
|
|
261 if(copy) {
|
|
262 /* do the copy first so that if we fail we leave the dest object untouched */
|
|
263 if(!copy_vcentry_(dest, src))
|
|
264 return false;
|
|
265 }
|
|
266 else {
|
|
267 /* we have to make sure that the string we're taking over is null-terminated */
|
|
268
|
|
269 /*
|
|
270 * Stripping the const from src->entry is OK since we're taking
|
|
271 * ownership of the pointer. This is a hack around a deficiency
|
|
272 * in the API where the same function is used for 'copy' and
|
|
273 * 'own', but the source entry is a const pointer. If we were
|
|
274 * precise, the 'own' flavor would be a separate function with a
|
|
275 * non-const source pointer. But it's not, so we hack away.
|
|
276 */
|
|
277 if(!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length))
|
|
278 return false;
|
|
279 *dest = *src;
|
|
280 }
|
|
281 }
|
|
282 else {
|
|
283 /* the src is null */
|
|
284 *dest = *src;
|
|
285 }
|
|
286
|
|
287 if(0 != save)
|
|
288 free(save);
|
|
289
|
|
290 vorbiscomment_calculate_length_(object);
|
|
291 return true;
|
|
292 }
|
|
293
|
|
294 static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name, unsigned field_name_length)
|
|
295 {
|
|
296 unsigned i;
|
|
297
|
|
298 FLAC__ASSERT(0 != object);
|
|
299 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
300 FLAC__ASSERT(0 != field_name);
|
|
301
|
|
302 for(i = offset; i < object->data.vorbis_comment.num_comments; i++) {
|
|
303 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length))
|
|
304 return (int)i;
|
|
305 }
|
|
306
|
|
307 return -1;
|
|
308 }
|
|
309
|
|
310 static void cuesheet_calculate_length_(FLAC__StreamMetadata *object)
|
|
311 {
|
|
312 unsigned i;
|
|
313
|
|
314 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
315
|
|
316 object->length = (
|
|
317 FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN +
|
|
318 FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN +
|
|
319 FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN +
|
|
320 FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN +
|
|
321 FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN
|
|
322 ) / 8;
|
|
323
|
|
324 object->length += object->data.cue_sheet.num_tracks * (
|
|
325 FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN +
|
|
326 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN +
|
|
327 FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN +
|
|
328 FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN +
|
|
329 FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN +
|
|
330 FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN +
|
|
331 FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN
|
|
332 ) / 8;
|
|
333
|
|
334 for(i = 0; i < object->data.cue_sheet.num_tracks; i++) {
|
|
335 object->length += object->data.cue_sheet.tracks[i].num_indices * (
|
|
336 FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN +
|
|
337 FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN +
|
|
338 FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN
|
|
339 ) / 8;
|
|
340 }
|
|
341 }
|
|
342
|
|
343 static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(unsigned num_indices)
|
|
344 {
|
|
345 FLAC__ASSERT(num_indices > 0);
|
|
346
|
|
347 return (FLAC__StreamMetadata_CueSheet_Index*)calloc(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index));
|
|
348 }
|
|
349
|
|
350 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(unsigned num_tracks)
|
|
351 {
|
|
352 FLAC__ASSERT(num_tracks > 0);
|
|
353
|
|
354 return (FLAC__StreamMetadata_CueSheet_Track*)calloc(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track));
|
|
355 }
|
|
356
|
|
357 static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
|
|
358 {
|
|
359 unsigned i;
|
|
360
|
|
361 FLAC__ASSERT(0 != object_array && num_tracks > 0);
|
|
362
|
|
363 for(i = 0; i < num_tracks; i++) {
|
|
364 if(0 != object_array[i].indices) {
|
|
365 FLAC__ASSERT(object_array[i].num_indices > 0);
|
|
366 free(object_array[i].indices);
|
|
367 }
|
|
368 }
|
|
369
|
|
370 if(0 != object_array)
|
|
371 free(object_array);
|
|
372 }
|
|
373
|
|
374 static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, unsigned num_tracks)
|
|
375 {
|
|
376 FLAC__StreamMetadata_CueSheet_Track *return_array;
|
|
377
|
|
378 FLAC__ASSERT(0 != object_array);
|
|
379 FLAC__ASSERT(num_tracks > 0);
|
|
380
|
|
381 return_array = cuesheet_track_array_new_(num_tracks);
|
|
382
|
|
383 if(0 != return_array) {
|
|
384 unsigned i;
|
|
385
|
|
386 for(i = 0; i < num_tracks; i++) {
|
|
387 if(!copy_track_(return_array+i, object_array+i)) {
|
|
388 cuesheet_track_array_delete_(return_array, num_tracks);
|
|
389 return 0;
|
|
390 }
|
|
391 }
|
|
392 }
|
|
393
|
|
394 return return_array;
|
|
395 }
|
|
396
|
|
397 static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy)
|
|
398 {
|
|
399 FLAC__StreamMetadata_CueSheet_Index *save;
|
|
400
|
|
401 FLAC__ASSERT(0 != object);
|
|
402 FLAC__ASSERT(0 != dest);
|
|
403 FLAC__ASSERT(0 != src);
|
|
404 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
405 FLAC__ASSERT((0 != src->indices && src->num_indices > 0) || (0 == src->indices && src->num_indices == 0));
|
|
406
|
|
407 save = dest->indices;
|
|
408
|
|
409 /* do the copy first so that if we fail we leave the object untouched */
|
|
410 if(copy) {
|
|
411 if(!copy_track_(dest, src))
|
|
412 return false;
|
|
413 }
|
|
414 else {
|
|
415 *dest = *src;
|
|
416 }
|
|
417
|
|
418 if(0 != save)
|
|
419 free(save);
|
|
420
|
|
421 cuesheet_calculate_length_(object);
|
|
422 return true;
|
|
423 }
|
|
424
|
|
425
|
|
426 /****************************************************************************
|
|
427 *
|
|
428 * Metadata object routines
|
|
429 *
|
|
430 ***************************************************************************/
|
|
431
|
|
432 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type)
|
|
433 {
|
|
434 FLAC__StreamMetadata *object;
|
|
435
|
|
436 if(type > FLAC__MAX_METADATA_TYPE_CODE)
|
|
437 return 0;
|
|
438
|
|
439 object = (FLAC__StreamMetadata*)calloc(1, sizeof(FLAC__StreamMetadata));
|
|
440 if(0 != object) {
|
|
441 object->is_last = false;
|
|
442 object->type = type;
|
|
443 switch(type) {
|
|
444 case FLAC__METADATA_TYPE_STREAMINFO:
|
|
445 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
|
|
446 break;
|
|
447 case FLAC__METADATA_TYPE_PADDING:
|
|
448 /* calloc() took care of this for us:
|
|
449 object->length = 0;
|
|
450 */
|
|
451 break;
|
|
452 case FLAC__METADATA_TYPE_APPLICATION:
|
|
453 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
|
|
454 /* calloc() took care of this for us:
|
|
455 object->data.application.data = 0;
|
|
456 */
|
|
457 break;
|
|
458 case FLAC__METADATA_TYPE_SEEKTABLE:
|
|
459 /* calloc() took care of this for us:
|
|
460 object->length = 0;
|
|
461 object->data.seek_table.num_points = 0;
|
|
462 object->data.seek_table.points = 0;
|
|
463 */
|
|
464 break;
|
|
465 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
|
466 object->data.vorbis_comment.vendor_string.length = (unsigned)strlen(FLAC__VENDOR_STRING);
|
|
467 if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) {
|
|
468 free(object);
|
|
469 return 0;
|
|
470 }
|
|
471 vorbiscomment_calculate_length_(object);
|
|
472 break;
|
|
473 case FLAC__METADATA_TYPE_CUESHEET:
|
|
474 cuesheet_calculate_length_(object);
|
|
475 break;
|
|
476 case FLAC__METADATA_TYPE_PICTURE:
|
|
477 object->length = (
|
|
478 FLAC__STREAM_METADATA_PICTURE_TYPE_LEN +
|
|
479 FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */
|
|
480 FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */
|
|
481 FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN +
|
|
482 FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN +
|
|
483 FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN +
|
|
484 FLAC__STREAM_METADATA_PICTURE_COLORS_LEN +
|
|
485 FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN +
|
|
486 0 /* no data */
|
|
487 ) / 8;
|
|
488 object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER;
|
|
489 object->data.picture.mime_type = 0;
|
|
490 object->data.picture.description = 0;
|
|
491 /* calloc() took care of this for us:
|
|
492 object->data.picture.width = 0;
|
|
493 object->data.picture.height = 0;
|
|
494 object->data.picture.depth = 0;
|
|
495 object->data.picture.colors = 0;
|
|
496 object->data.picture.data_length = 0;
|
|
497 object->data.picture.data = 0;
|
|
498 */
|
|
499 /* now initialize mime_type and description with empty strings to make things easier on the client */
|
|
500 if(!copy_cstring_(&object->data.picture.mime_type, "")) {
|
|
501 free(object);
|
|
502 return 0;
|
|
503 }
|
|
504 if(!copy_cstring_((char**)(&object->data.picture.description), "")) {
|
|
505 if(object->data.picture.mime_type)
|
|
506 free(object->data.picture.mime_type);
|
|
507 free(object);
|
|
508 return 0;
|
|
509 }
|
|
510 break;
|
|
511 default:
|
|
512 /* calloc() took care of this for us:
|
|
513 object->length = 0;
|
|
514 object->data.unknown.data = 0;
|
|
515 */
|
|
516 break;
|
|
517 }
|
|
518 }
|
|
519
|
|
520 return object;
|
|
521 }
|
|
522
|
|
523 FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object)
|
|
524 {
|
|
525 FLAC__StreamMetadata *to;
|
|
526
|
|
527 FLAC__ASSERT(0 != object);
|
|
528
|
|
529 if(0 != (to = FLAC__metadata_object_new(object->type))) {
|
|
530 to->is_last = object->is_last;
|
|
531 to->type = object->type;
|
|
532 to->length = object->length;
|
|
533 switch(to->type) {
|
|
534 case FLAC__METADATA_TYPE_STREAMINFO:
|
|
535 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo));
|
|
536 break;
|
|
537 case FLAC__METADATA_TYPE_PADDING:
|
|
538 break;
|
|
539 case FLAC__METADATA_TYPE_APPLICATION:
|
|
540 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
|
|
541 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
|
|
542 FLAC__metadata_object_delete(to);
|
|
543 return 0;
|
|
544 }
|
|
545 break;
|
|
546 case FLAC__METADATA_TYPE_SEEKTABLE:
|
|
547 to->data.seek_table.num_points = object->data.seek_table.num_points;
|
|
548 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))) {
|
|
549 FLAC__metadata_object_delete(to);
|
|
550 return 0;
|
|
551 }
|
|
552 break;
|
|
553 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
|
554 if(0 != to->data.vorbis_comment.vendor_string.entry) {
|
|
555 free(to->data.vorbis_comment.vendor_string.entry);
|
|
556 to->data.vorbis_comment.vendor_string.entry = 0;
|
|
557 }
|
|
558 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
|
|
559 FLAC__metadata_object_delete(to);
|
|
560 return 0;
|
|
561 }
|
|
562 if(object->data.vorbis_comment.num_comments == 0) {
|
|
563 FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
|
|
564 to->data.vorbis_comment.comments = 0;
|
|
565 }
|
|
566 else {
|
|
567 FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
|
|
568 to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
|
|
569 if(0 == to->data.vorbis_comment.comments) {
|
|
570 FLAC__metadata_object_delete(to);
|
|
571 return 0;
|
|
572 }
|
|
573 }
|
|
574 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
|
|
575 break;
|
|
576 case FLAC__METADATA_TYPE_CUESHEET:
|
|
577 memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet));
|
|
578 if(object->data.cue_sheet.num_tracks == 0) {
|
|
579 FLAC__ASSERT(0 == object->data.cue_sheet.tracks);
|
|
580 }
|
|
581 else {
|
|
582 FLAC__ASSERT(0 != object->data.cue_sheet.tracks);
|
|
583 to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
|
|
584 if(0 == to->data.cue_sheet.tracks) {
|
|
585 FLAC__metadata_object_delete(to);
|
|
586 return 0;
|
|
587 }
|
|
588 }
|
|
589 break;
|
|
590 case FLAC__METADATA_TYPE_PICTURE:
|
|
591 to->data.picture.type = object->data.picture.type;
|
|
592 if(!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) {
|
|
593 FLAC__metadata_object_delete(to);
|
|
594 return 0;
|
|
595 }
|
|
596 if(!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) {
|
|
597 FLAC__metadata_object_delete(to);
|
|
598 return 0;
|
|
599 }
|
|
600 to->data.picture.width = object->data.picture.width;
|
|
601 to->data.picture.height = object->data.picture.height;
|
|
602 to->data.picture.depth = object->data.picture.depth;
|
|
603 to->data.picture.colors = object->data.picture.colors;
|
|
604 to->data.picture.data_length = object->data.picture.data_length;
|
|
605 if(!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) {
|
|
606 FLAC__metadata_object_delete(to);
|
|
607 return 0;
|
|
608 }
|
|
609 break;
|
|
610 default:
|
|
611 if(!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) {
|
|
612 FLAC__metadata_object_delete(to);
|
|
613 return 0;
|
|
614 }
|
|
615 break;
|
|
616 }
|
|
617 }
|
|
618
|
|
619 return to;
|
|
620 }
|
|
621
|
|
622 void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object)
|
|
623 {
|
|
624 FLAC__ASSERT(0 != object);
|
|
625
|
|
626 switch(object->type) {
|
|
627 case FLAC__METADATA_TYPE_STREAMINFO:
|
|
628 case FLAC__METADATA_TYPE_PADDING:
|
|
629 break;
|
|
630 case FLAC__METADATA_TYPE_APPLICATION:
|
|
631 if(0 != object->data.application.data) {
|
|
632 free(object->data.application.data);
|
|
633 object->data.application.data = 0;
|
|
634 }
|
|
635 break;
|
|
636 case FLAC__METADATA_TYPE_SEEKTABLE:
|
|
637 if(0 != object->data.seek_table.points) {
|
|
638 free(object->data.seek_table.points);
|
|
639 object->data.seek_table.points = 0;
|
|
640 }
|
|
641 break;
|
|
642 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
|
643 if(0 != object->data.vorbis_comment.vendor_string.entry) {
|
|
644 free(object->data.vorbis_comment.vendor_string.entry);
|
|
645 object->data.vorbis_comment.vendor_string.entry = 0;
|
|
646 }
|
|
647 if(0 != object->data.vorbis_comment.comments) {
|
|
648 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
|
|
649 vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
|
|
650 }
|
|
651 break;
|
|
652 case FLAC__METADATA_TYPE_CUESHEET:
|
|
653 if(0 != object->data.cue_sheet.tracks) {
|
|
654 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
|
|
655 cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks);
|
|
656 }
|
|
657 break;
|
|
658 case FLAC__METADATA_TYPE_PICTURE:
|
|
659 if(0 != object->data.picture.mime_type) {
|
|
660 free(object->data.picture.mime_type);
|
|
661 object->data.picture.mime_type = 0;
|
|
662 }
|
|
663 if(0 != object->data.picture.description) {
|
|
664 free(object->data.picture.description);
|
|
665 object->data.picture.description = 0;
|
|
666 }
|
|
667 if(0 != object->data.picture.data) {
|
|
668 free(object->data.picture.data);
|
|
669 object->data.picture.data = 0;
|
|
670 }
|
|
671 break;
|
|
672 default:
|
|
673 if(0 != object->data.unknown.data) {
|
|
674 free(object->data.unknown.data);
|
|
675 object->data.unknown.data = 0;
|
|
676 }
|
|
677 break;
|
|
678 }
|
|
679 }
|
|
680
|
|
681 FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object)
|
|
682 {
|
|
683 FLAC__metadata_object_delete_data(object);
|
|
684 free(object);
|
|
685 }
|
|
686
|
|
687 static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2)
|
|
688 {
|
|
689 if(block1->min_blocksize != block2->min_blocksize)
|
|
690 return false;
|
|
691 if(block1->max_blocksize != block2->max_blocksize)
|
|
692 return false;
|
|
693 if(block1->min_framesize != block2->min_framesize)
|
|
694 return false;
|
|
695 if(block1->max_framesize != block2->max_framesize)
|
|
696 return false;
|
|
697 if(block1->sample_rate != block2->sample_rate)
|
|
698 return false;
|
|
699 if(block1->channels != block2->channels)
|
|
700 return false;
|
|
701 if(block1->bits_per_sample != block2->bits_per_sample)
|
|
702 return false;
|
|
703 if(block1->total_samples != block2->total_samples)
|
|
704 return false;
|
|
705 if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
|
|
706 return false;
|
|
707 return true;
|
|
708 }
|
|
709
|
|
710 static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, unsigned block_length)
|
|
711 {
|
|
712 FLAC__ASSERT(0 != block1);
|
|
713 FLAC__ASSERT(0 != block2);
|
|
714 FLAC__ASSERT(block_length >= sizeof(block1->id));
|
|
715
|
|
716 if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
|
|
717 return false;
|
|
718 if(0 != block1->data && 0 != block2->data)
|
|
719 return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
|
|
720 else
|
|
721 return block1->data == block2->data;
|
|
722 }
|
|
723
|
|
724 static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2)
|
|
725 {
|
|
726 unsigned i;
|
|
727
|
|
728 FLAC__ASSERT(0 != block1);
|
|
729 FLAC__ASSERT(0 != block2);
|
|
730
|
|
731 if(block1->num_points != block2->num_points)
|
|
732 return false;
|
|
733
|
|
734 if(0 != block1->points && 0 != block2->points) {
|
|
735 for(i = 0; i < block1->num_points; i++) {
|
|
736 if(block1->points[i].sample_number != block2->points[i].sample_number)
|
|
737 return false;
|
|
738 if(block1->points[i].stream_offset != block2->points[i].stream_offset)
|
|
739 return false;
|
|
740 if(block1->points[i].frame_samples != block2->points[i].frame_samples)
|
|
741 return false;
|
|
742 }
|
|
743 return true;
|
|
744 }
|
|
745 else
|
|
746 return block1->points == block2->points;
|
|
747 }
|
|
748
|
|
749 static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2)
|
|
750 {
|
|
751 unsigned i;
|
|
752
|
|
753 if(block1->vendor_string.length != block2->vendor_string.length)
|
|
754 return false;
|
|
755
|
|
756 if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
|
|
757 if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
|
|
758 return false;
|
|
759 }
|
|
760 else if(block1->vendor_string.entry != block2->vendor_string.entry)
|
|
761 return false;
|
|
762
|
|
763 if(block1->num_comments != block2->num_comments)
|
|
764 return false;
|
|
765
|
|
766 for(i = 0; i < block1->num_comments; i++) {
|
|
767 if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
|
|
768 if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
|
|
769 return false;
|
|
770 }
|
|
771 else if(block1->comments[i].entry != block2->comments[i].entry)
|
|
772 return false;
|
|
773 }
|
|
774 return true;
|
|
775 }
|
|
776
|
|
777 static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2)
|
|
778 {
|
|
779 unsigned i, j;
|
|
780
|
|
781 if(0 != strcmp(block1->media_catalog_number, block2->media_catalog_number))
|
|
782 return false;
|
|
783
|
|
784 if(block1->lead_in != block2->lead_in)
|
|
785 return false;
|
|
786
|
|
787 if(block1->is_cd != block2->is_cd)
|
|
788 return false;
|
|
789
|
|
790 if(block1->num_tracks != block2->num_tracks)
|
|
791 return false;
|
|
792
|
|
793 if(0 != block1->tracks && 0 != block2->tracks) {
|
|
794 FLAC__ASSERT(block1->num_tracks > 0);
|
|
795 for(i = 0; i < block1->num_tracks; i++) {
|
|
796 if(block1->tracks[i].offset != block2->tracks[i].offset)
|
|
797 return false;
|
|
798 if(block1->tracks[i].number != block2->tracks[i].number)
|
|
799 return false;
|
|
800 if(0 != memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)))
|
|
801 return false;
|
|
802 if(block1->tracks[i].type != block2->tracks[i].type)
|
|
803 return false;
|
|
804 if(block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis)
|
|
805 return false;
|
|
806 if(block1->tracks[i].num_indices != block2->tracks[i].num_indices)
|
|
807 return false;
|
|
808 if(0 != block1->tracks[i].indices && 0 != block2->tracks[i].indices) {
|
|
809 FLAC__ASSERT(block1->tracks[i].num_indices > 0);
|
|
810 for(j = 0; j < block1->tracks[i].num_indices; j++) {
|
|
811 if(block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset)
|
|
812 return false;
|
|
813 if(block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number)
|
|
814 return false;
|
|
815 }
|
|
816 }
|
|
817 else if(block1->tracks[i].indices != block2->tracks[i].indices)
|
|
818 return false;
|
|
819 }
|
|
820 }
|
|
821 else if(block1->tracks != block2->tracks)
|
|
822 return false;
|
|
823 return true;
|
|
824 }
|
|
825
|
|
826 static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2)
|
|
827 {
|
|
828 if(block1->type != block2->type)
|
|
829 return false;
|
|
830 if(block1->mime_type != block2->mime_type && (0 == block1->mime_type || 0 == block2->mime_type || strcmp(block1->mime_type, block2->mime_type)))
|
|
831 return false;
|
|
832 if(block1->description != block2->description && (0 == block1->description || 0 == block2->description || strcmp((const char *)block1->description, (const char *)block2->description)))
|
|
833 return false;
|
|
834 if(block1->width != block2->width)
|
|
835 return false;
|
|
836 if(block1->height != block2->height)
|
|
837 return false;
|
|
838 if(block1->depth != block2->depth)
|
|
839 return false;
|
|
840 if(block1->colors != block2->colors)
|
|
841 return false;
|
|
842 if(block1->data_length != block2->data_length)
|
|
843 return false;
|
|
844 if(block1->data != block2->data && (0 == block1->data || 0 == block2->data || memcmp(block1->data, block2->data, block1->data_length)))
|
|
845 return false;
|
|
846 return true;
|
|
847 }
|
|
848
|
|
849 static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, unsigned block_length)
|
|
850 {
|
|
851 FLAC__ASSERT(0 != block1);
|
|
852 FLAC__ASSERT(0 != block2);
|
|
853
|
|
854 if(0 != block1->data && 0 != block2->data)
|
|
855 return 0 == memcmp(block1->data, block2->data, block_length);
|
|
856 else
|
|
857 return block1->data == block2->data;
|
|
858 }
|
|
859
|
|
860 FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2)
|
|
861 {
|
|
862 FLAC__ASSERT(0 != block1);
|
|
863 FLAC__ASSERT(0 != block2);
|
|
864
|
|
865 if(block1->type != block2->type) {
|
|
866 return false;
|
|
867 }
|
|
868 if(block1->is_last != block2->is_last) {
|
|
869 return false;
|
|
870 }
|
|
871 if(block1->length != block2->length) {
|
|
872 return false;
|
|
873 }
|
|
874 switch(block1->type) {
|
|
875 case FLAC__METADATA_TYPE_STREAMINFO:
|
|
876 return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
|
|
877 case FLAC__METADATA_TYPE_PADDING:
|
|
878 return true; /* we don't compare the padding guts */
|
|
879 case FLAC__METADATA_TYPE_APPLICATION:
|
|
880 return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
|
|
881 case FLAC__METADATA_TYPE_SEEKTABLE:
|
|
882 return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
|
|
883 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
|
884 return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
|
|
885 case FLAC__METADATA_TYPE_CUESHEET:
|
|
886 return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet);
|
|
887 case FLAC__METADATA_TYPE_PICTURE:
|
|
888 return compare_block_data_picture_(&block1->data.picture, &block2->data.picture);
|
|
889 default:
|
|
890 return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length);
|
|
891 }
|
|
892 }
|
|
893
|
|
894 FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
|
|
895 {
|
|
896 FLAC__byte *save;
|
|
897
|
|
898 FLAC__ASSERT(0 != object);
|
|
899 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
|
|
900 FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
|
|
901
|
|
902 save = object->data.application.data;
|
|
903
|
|
904 /* do the copy first so that if we fail we leave the object untouched */
|
|
905 if(copy) {
|
|
906 if(!copy_bytes_(&object->data.application.data, data, length))
|
|
907 return false;
|
|
908 }
|
|
909 else {
|
|
910 object->data.application.data = data;
|
|
911 }
|
|
912
|
|
913 if(0 != save)
|
|
914 free(save);
|
|
915
|
|
916 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
|
|
917 return true;
|
|
918 }
|
|
919
|
|
920 FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, unsigned new_num_points)
|
|
921 {
|
|
922 FLAC__ASSERT(0 != object);
|
|
923 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
924
|
|
925 if(0 == object->data.seek_table.points) {
|
|
926 FLAC__ASSERT(object->data.seek_table.num_points == 0);
|
|
927 if(0 == new_num_points)
|
|
928 return true;
|
|
929 else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
|
|
930 return false;
|
|
931 }
|
|
932 else {
|
|
933 const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
|
|
934 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint);
|
|
935
|
|
936 FLAC__ASSERT(object->data.seek_table.num_points > 0);
|
|
937
|
|
938 if(new_size == 0) {
|
|
939 free(object->data.seek_table.points);
|
|
940 object->data.seek_table.points = 0;
|
|
941 }
|
|
942 else if(0 == (object->data.seek_table.points = (FLAC__StreamMetadata_SeekPoint*)realloc(object->data.seek_table.points, new_size)))
|
|
943 return false;
|
|
944
|
|
945 /* if growing, set new elements to placeholders */
|
|
946 if(new_size > old_size) {
|
|
947 unsigned i;
|
|
948 for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
|
|
949 object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
|
|
950 object->data.seek_table.points[i].stream_offset = 0;
|
|
951 object->data.seek_table.points[i].frame_samples = 0;
|
|
952 }
|
|
953 }
|
|
954 }
|
|
955
|
|
956 object->data.seek_table.num_points = new_num_points;
|
|
957
|
|
958 seektable_calculate_length_(object);
|
|
959 return true;
|
|
960 }
|
|
961
|
|
962 FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
|
|
963 {
|
|
964 FLAC__ASSERT(0 != object);
|
|
965 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
966 FLAC__ASSERT(point_num < object->data.seek_table.num_points);
|
|
967
|
|
968 object->data.seek_table.points[point_num] = point;
|
|
969 }
|
|
970
|
|
971 FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, unsigned point_num, FLAC__StreamMetadata_SeekPoint point)
|
|
972 {
|
|
973 int i;
|
|
974
|
|
975 FLAC__ASSERT(0 != object);
|
|
976 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
977 FLAC__ASSERT(point_num <= object->data.seek_table.num_points);
|
|
978
|
|
979 if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
|
|
980 return false;
|
|
981
|
|
982 /* move all points >= point_num forward one space */
|
|
983 for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
|
|
984 object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
|
|
985
|
|
986 FLAC__metadata_object_seektable_set_point(object, point_num, point);
|
|
987 seektable_calculate_length_(object);
|
|
988 return true;
|
|
989 }
|
|
990
|
|
991 FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, unsigned point_num)
|
|
992 {
|
|
993 unsigned i;
|
|
994
|
|
995 FLAC__ASSERT(0 != object);
|
|
996 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
997 FLAC__ASSERT(point_num < object->data.seek_table.num_points);
|
|
998
|
|
999 /* move all points > point_num backward one space */
|
|
1000 for(i = point_num; i < object->data.seek_table.num_points-1; i++)
|
|
1001 object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
|
|
1002
|
|
1003 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
|
|
1004 }
|
|
1005
|
|
1006 FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object)
|
|
1007 {
|
|
1008 FLAC__ASSERT(0 != object);
|
|
1009 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1010
|
|
1011 return FLAC__format_seektable_is_legal(&object->data.seek_table);
|
|
1012 }
|
|
1013
|
|
1014 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, unsigned num)
|
|
1015 {
|
|
1016 FLAC__ASSERT(0 != object);
|
|
1017 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1018
|
|
1019 if(num > 0)
|
|
1020 /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */
|
|
1021 return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num);
|
|
1022 else
|
|
1023 return true;
|
|
1024 }
|
|
1025
|
|
1026 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number)
|
|
1027 {
|
|
1028 FLAC__StreamMetadata_SeekTable *seek_table;
|
|
1029
|
|
1030 FLAC__ASSERT(0 != object);
|
|
1031 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1032
|
|
1033 seek_table = &object->data.seek_table;
|
|
1034
|
|
1035 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1))
|
|
1036 return false;
|
|
1037
|
|
1038 seek_table->points[seek_table->num_points - 1].sample_number = sample_number;
|
|
1039 seek_table->points[seek_table->num_points - 1].stream_offset = 0;
|
|
1040 seek_table->points[seek_table->num_points - 1].frame_samples = 0;
|
|
1041
|
|
1042 return true;
|
|
1043 }
|
|
1044
|
|
1045 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], unsigned num)
|
|
1046 {
|
|
1047 FLAC__ASSERT(0 != object);
|
|
1048 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1049 FLAC__ASSERT(0 != sample_numbers || num == 0);
|
|
1050
|
|
1051 if(num > 0) {
|
|
1052 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
|
|
1053 unsigned i, j;
|
|
1054
|
|
1055 i = seek_table->num_points;
|
|
1056
|
|
1057 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
|
|
1058 return false;
|
|
1059
|
|
1060 for(j = 0; j < num; i++, j++) {
|
|
1061 seek_table->points[i].sample_number = sample_numbers[j];
|
|
1062 seek_table->points[i].stream_offset = 0;
|
|
1063 seek_table->points[i].frame_samples = 0;
|
|
1064 }
|
|
1065 }
|
|
1066
|
|
1067 return true;
|
|
1068 }
|
|
1069
|
|
1070 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, unsigned num, FLAC__uint64 total_samples)
|
|
1071 {
|
|
1072 FLAC__ASSERT(0 != object);
|
|
1073 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1074 FLAC__ASSERT(total_samples > 0);
|
|
1075
|
|
1076 if(num > 0 && total_samples > 0) {
|
|
1077 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
|
|
1078 unsigned i, j;
|
|
1079
|
|
1080 i = seek_table->num_points;
|
|
1081
|
|
1082 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num))
|
|
1083 return false;
|
|
1084
|
|
1085 for(j = 0; j < num; i++, j++) {
|
|
1086 seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num;
|
|
1087 seek_table->points[i].stream_offset = 0;
|
|
1088 seek_table->points[i].frame_samples = 0;
|
|
1089 }
|
|
1090 }
|
|
1091
|
|
1092 return true;
|
|
1093 }
|
|
1094
|
|
1095 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, unsigned samples, FLAC__uint64 total_samples)
|
|
1096 {
|
|
1097 FLAC__ASSERT(0 != object);
|
|
1098 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1099 FLAC__ASSERT(samples > 0);
|
|
1100 FLAC__ASSERT(total_samples > 0);
|
|
1101
|
|
1102 if(samples > 0 && total_samples > 0) {
|
|
1103 FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table;
|
|
1104 unsigned i, j;
|
|
1105 FLAC__uint64 num, sample;
|
|
1106
|
|
1107 num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */
|
|
1108 /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */
|
|
1109 if(total_samples % samples == 0)
|
|
1110 num--;
|
|
1111
|
|
1112 i = seek_table->num_points;
|
|
1113
|
|
1114 if(!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (unsigned)num))
|
|
1115 return false;
|
|
1116
|
|
1117 sample = 0;
|
|
1118 for(j = 0; j < num; i++, j++, sample += samples) {
|
|
1119 seek_table->points[i].sample_number = sample;
|
|
1120 seek_table->points[i].stream_offset = 0;
|
|
1121 seek_table->points[i].frame_samples = 0;
|
|
1122 }
|
|
1123 }
|
|
1124
|
|
1125 return true;
|
|
1126 }
|
|
1127
|
|
1128 FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact)
|
|
1129 {
|
|
1130 unsigned unique;
|
|
1131
|
|
1132 FLAC__ASSERT(0 != object);
|
|
1133 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
|
|
1134
|
|
1135 unique = FLAC__format_seektable_sort(&object->data.seek_table);
|
|
1136
|
|
1137 return !compact || FLAC__metadata_object_seektable_resize_points(object, unique);
|
|
1138 }
|
|
1139
|
|
1140 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
|
|
1141 {
|
|
1142 if(!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length))
|
|
1143 return false;
|
|
1144 return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy);
|
|
1145 }
|
|
1146
|
|
1147 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, unsigned new_num_comments)
|
|
1148 {
|
|
1149 FLAC__ASSERT(0 != object);
|
|
1150 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1151
|
|
1152 if(0 == object->data.vorbis_comment.comments) {
|
|
1153 FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
|
|
1154 if(0 == new_num_comments)
|
|
1155 return true;
|
|
1156 else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
|
|
1157 return false;
|
|
1158 }
|
|
1159 else {
|
|
1160 const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
|
|
1161 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry);
|
|
1162
|
|
1163 FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
|
|
1164
|
|
1165 /* if shrinking, free the truncated entries */
|
|
1166 if(new_num_comments < object->data.vorbis_comment.num_comments) {
|
|
1167 unsigned i;
|
|
1168 for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
|
|
1169 if(0 != object->data.vorbis_comment.comments[i].entry)
|
|
1170 free(object->data.vorbis_comment.comments[i].entry);
|
|
1171 }
|
|
1172
|
|
1173 if(new_size == 0) {
|
|
1174 free(object->data.vorbis_comment.comments);
|
|
1175 object->data.vorbis_comment.comments = 0;
|
|
1176 }
|
|
1177 else if(0 == (object->data.vorbis_comment.comments = (FLAC__StreamMetadata_VorbisComment_Entry*)realloc(object->data.vorbis_comment.comments, new_size)))
|
|
1178 return false;
|
|
1179
|
|
1180 /* if growing, zero all the length/pointers of new elements */
|
|
1181 if(new_size > old_size)
|
|
1182 memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
|
|
1183 }
|
|
1184
|
|
1185 object->data.vorbis_comment.num_comments = new_num_comments;
|
|
1186
|
|
1187 vorbiscomment_calculate_length_(object);
|
|
1188 return true;
|
|
1189 }
|
|
1190
|
|
1191 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
|
|
1192 {
|
|
1193 FLAC__ASSERT(0 != object);
|
|
1194 FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
|
|
1195
|
|
1196 if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
|
|
1197 return false;
|
|
1198 return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy);
|
|
1199 }
|
|
1200
|
|
1201 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, unsigned comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
|
|
1202 {
|
|
1203 FLAC__StreamMetadata_VorbisComment *vc;
|
|
1204
|
|
1205 FLAC__ASSERT(0 != object);
|
|
1206 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1207 FLAC__ASSERT(comment_num <= object->data.vorbis_comment.num_comments);
|
|
1208
|
|
1209 if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
|
|
1210 return false;
|
|
1211
|
|
1212 vc = &object->data.vorbis_comment;
|
|
1213
|
|
1214 if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1))
|
|
1215 return false;
|
|
1216
|
|
1217 /* move all comments >= comment_num forward one space */
|
|
1218 memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num));
|
|
1219 vc->comments[comment_num].length = 0;
|
|
1220 vc->comments[comment_num].entry = 0;
|
|
1221
|
|
1222 return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
|
|
1223 }
|
|
1224
|
|
1225 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy)
|
|
1226 {
|
|
1227 FLAC__ASSERT(0 != object);
|
|
1228 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1229 return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy);
|
|
1230 }
|
|
1231
|
|
1232 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy)
|
|
1233 {
|
|
1234 FLAC__ASSERT(0 != entry.entry && entry.length > 0);
|
|
1235
|
|
1236 if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
|
|
1237 return false;
|
|
1238
|
|
1239 {
|
|
1240 int i;
|
|
1241 unsigned field_name_length;
|
|
1242 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
|
|
1243
|
|
1244 FLAC__ASSERT(0 != eq);
|
|
1245
|
|
1246 if(0 == eq)
|
|
1247 return false; /* double protection */
|
|
1248
|
|
1249 field_name_length = eq-entry.entry;
|
|
1250
|
|
1251 if((i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, field_name_length)) >= 0) {
|
|
1252 unsigned index = (unsigned)i;
|
|
1253 if(!FLAC__metadata_object_vorbiscomment_set_comment(object, index, entry, copy))
|
|
1254 return false;
|
|
1255 if(all && (index+1 < object->data.vorbis_comment.num_comments)) {
|
|
1256 for(i = vorbiscomment_find_entry_from_(object, index+1, (const char *)entry.entry, field_name_length); i >= 0; ) {
|
|
1257 if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i))
|
|
1258 return false;
|
|
1259 if((unsigned)i < object->data.vorbis_comment.num_comments)
|
|
1260 i = vorbiscomment_find_entry_from_(object, (unsigned)i, (const char *)entry.entry, field_name_length);
|
|
1261 else
|
|
1262 i = -1;
|
|
1263 }
|
|
1264 }
|
|
1265 return true;
|
|
1266 }
|
|
1267 else
|
|
1268 return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy);
|
|
1269 }
|
|
1270 }
|
|
1271
|
|
1272 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, unsigned comment_num)
|
|
1273 {
|
|
1274 FLAC__StreamMetadata_VorbisComment *vc;
|
|
1275
|
|
1276 FLAC__ASSERT(0 != object);
|
|
1277 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1278 FLAC__ASSERT(comment_num < object->data.vorbis_comment.num_comments);
|
|
1279
|
|
1280 vc = &object->data.vorbis_comment;
|
|
1281
|
|
1282 /* free the comment at comment_num */
|
|
1283 if(0 != vc->comments[comment_num].entry)
|
|
1284 free(vc->comments[comment_num].entry);
|
|
1285
|
|
1286 /* move all comments > comment_num backward one space */
|
|
1287 memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1));
|
|
1288 vc->comments[vc->num_comments-1].length = 0;
|
|
1289 vc->comments[vc->num_comments-1].entry = 0;
|
|
1290
|
|
1291 return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1);
|
|
1292 }
|
|
1293
|
|
1294 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)
|
|
1295 {
|
|
1296 FLAC__ASSERT(0 != entry);
|
|
1297 FLAC__ASSERT(0 != field_name);
|
|
1298 FLAC__ASSERT(0 != field_value);
|
|
1299
|
|
1300 if(!FLAC__format_vorbiscomment_entry_name_is_legal(field_name))
|
|
1301 return false;
|
|
1302 if(!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (unsigned)(-1)))
|
|
1303 return false;
|
|
1304
|
|
1305 {
|
|
1306 const size_t nn = strlen(field_name);
|
|
1307 const size_t nv = strlen(field_value);
|
|
1308 entry->length = nn + 1 /*=*/ + nv;
|
|
1309 if(0 == (entry->entry = (FLAC__byte*)malloc(entry->length+1)))
|
|
1310 return false;
|
|
1311 memcpy(entry->entry, field_name, nn);
|
|
1312 entry->entry[nn] = '=';
|
|
1313 memcpy(entry->entry+nn+1, field_value, nv);
|
|
1314 entry->entry[entry->length] = '\0';
|
|
1315 }
|
|
1316
|
|
1317 return true;
|
|
1318 }
|
|
1319
|
|
1320 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)
|
|
1321 {
|
|
1322 FLAC__ASSERT(0 != entry.entry && entry.length > 0);
|
|
1323 FLAC__ASSERT(0 != field_name);
|
|
1324 FLAC__ASSERT(0 != field_value);
|
|
1325
|
|
1326 if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length))
|
|
1327 return false;
|
|
1328
|
|
1329 {
|
|
1330 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
|
|
1331 const size_t nn = eq-entry.entry;
|
|
1332 const size_t nv = entry.length-nn-1; /* -1 for the '=' */
|
|
1333 FLAC__ASSERT(0 != eq);
|
|
1334 if(0 == eq)
|
|
1335 return false; /* double protection */
|
|
1336 if(0 == (*field_name = (char*)malloc(nn+1)))
|
|
1337 return false;
|
|
1338 if(0 == (*field_value = (char*)malloc(nv+1))) {
|
|
1339 free(*field_name);
|
|
1340 return false;
|
|
1341 }
|
|
1342 memcpy(*field_name, entry.entry, nn);
|
|
1343 memcpy(*field_value, entry.entry+nn+1, nv);
|
|
1344 (*field_name)[nn] = '\0';
|
|
1345 (*field_value)[nv] = '\0';
|
|
1346 }
|
|
1347
|
|
1348 return true;
|
|
1349 }
|
|
1350
|
|
1351 FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, unsigned field_name_length)
|
|
1352 {
|
|
1353 FLAC__ASSERT(0 != entry.entry && entry.length > 0);
|
|
1354 {
|
|
1355 const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length);
|
|
1356 #if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__
|
|
1357 #define FLAC__STRNCASECMP strnicmp
|
|
1358 #else
|
|
1359 #define FLAC__STRNCASECMP strncasecmp
|
|
1360 #endif
|
|
1361 return (0 != eq && (unsigned)(eq-entry.entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length));
|
|
1362 #undef FLAC__STRNCASECMP
|
|
1363 }
|
|
1364 }
|
|
1365
|
|
1366 FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, unsigned offset, const char *field_name)
|
|
1367 {
|
|
1368 FLAC__ASSERT(0 != field_name);
|
|
1369
|
|
1370 return vorbiscomment_find_entry_from_(object, offset, field_name, strlen(field_name));
|
|
1371 }
|
|
1372
|
|
1373 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name)
|
|
1374 {
|
|
1375 const unsigned field_name_length = strlen(field_name);
|
|
1376 unsigned i;
|
|
1377
|
|
1378 FLAC__ASSERT(0 != object);
|
|
1379 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1380
|
|
1381 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
|
|
1382 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
|
|
1383 if(!FLAC__metadata_object_vorbiscomment_delete_comment(object, i))
|
|
1384 return -1;
|
|
1385 else
|
|
1386 return 1;
|
|
1387 }
|
|
1388 }
|
|
1389
|
|
1390 return 0;
|
|
1391 }
|
|
1392
|
|
1393 FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name)
|
|
1394 {
|
|
1395 FLAC__bool ok = true;
|
|
1396 unsigned matching = 0;
|
|
1397 const unsigned field_name_length = strlen(field_name);
|
|
1398 int i;
|
|
1399
|
|
1400 FLAC__ASSERT(0 != object);
|
|
1401 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
|
1402
|
|
1403 /* must delete from end to start otherwise it will interfere with our iteration */
|
|
1404 for(i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
|
|
1405 if(FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) {
|
|
1406 matching++;
|
|
1407 ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (unsigned)i);
|
|
1408 }
|
|
1409 }
|
|
1410
|
|
1411 return ok? (int)matching : -1;
|
|
1412 }
|
|
1413
|
|
1414 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void)
|
|
1415 {
|
|
1416 return (FLAC__StreamMetadata_CueSheet_Track*)calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track));
|
|
1417 }
|
|
1418
|
|
1419 FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object)
|
|
1420 {
|
|
1421 FLAC__StreamMetadata_CueSheet_Track *to;
|
|
1422
|
|
1423 FLAC__ASSERT(0 != object);
|
|
1424
|
|
1425 if(0 != (to = FLAC__metadata_object_cuesheet_track_new())) {
|
|
1426 if(!copy_track_(to, object)) {
|
|
1427 FLAC__metadata_object_cuesheet_track_delete(to);
|
|
1428 return 0;
|
|
1429 }
|
|
1430 }
|
|
1431
|
|
1432 return to;
|
|
1433 }
|
|
1434
|
|
1435 void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object)
|
|
1436 {
|
|
1437 FLAC__ASSERT(0 != object);
|
|
1438
|
|
1439 if(0 != object->indices) {
|
|
1440 FLAC__ASSERT(object->num_indices > 0);
|
|
1441 free(object->indices);
|
|
1442 }
|
|
1443 }
|
|
1444
|
|
1445 FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object)
|
|
1446 {
|
|
1447 FLAC__metadata_object_cuesheet_track_delete_data(object);
|
|
1448 free(object);
|
|
1449 }
|
|
1450
|
|
1451 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, unsigned track_num, unsigned new_num_indices)
|
|
1452 {
|
|
1453 FLAC__StreamMetadata_CueSheet_Track *track;
|
|
1454 FLAC__ASSERT(0 != object);
|
|
1455 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1456 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
|
|
1457
|
|
1458 track = &object->data.cue_sheet.tracks[track_num];
|
|
1459
|
|
1460 if(0 == track->indices) {
|
|
1461 FLAC__ASSERT(track->num_indices == 0);
|
|
1462 if(0 == new_num_indices)
|
|
1463 return true;
|
|
1464 else if(0 == (track->indices = cuesheet_track_index_array_new_(new_num_indices)))
|
|
1465 return false;
|
|
1466 }
|
|
1467 else {
|
|
1468 const unsigned old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
|
|
1469 const unsigned new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index);
|
|
1470
|
|
1471 FLAC__ASSERT(track->num_indices > 0);
|
|
1472
|
|
1473 if(new_size == 0) {
|
|
1474 free(track->indices);
|
|
1475 track->indices = 0;
|
|
1476 }
|
|
1477 else if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)realloc(track->indices, new_size)))
|
|
1478 return false;
|
|
1479
|
|
1480 /* if growing, zero all the lengths/pointers of new elements */
|
|
1481 if(new_size > old_size)
|
|
1482 memset(track->indices + track->num_indices, 0, new_size - old_size);
|
|
1483 }
|
|
1484
|
|
1485 track->num_indices = new_num_indices;
|
|
1486
|
|
1487 cuesheet_calculate_length_(object);
|
|
1488 return true;
|
|
1489 }
|
|
1490
|
|
1491 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)
|
|
1492 {
|
|
1493 FLAC__StreamMetadata_CueSheet_Track *track;
|
|
1494
|
|
1495 FLAC__ASSERT(0 != object);
|
|
1496 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1497 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
|
|
1498 FLAC__ASSERT(index_num <= object->data.cue_sheet.tracks[track_num].num_indices);
|
|
1499
|
|
1500 track = &object->data.cue_sheet.tracks[track_num];
|
|
1501
|
|
1502 if(!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1))
|
|
1503 return false;
|
|
1504
|
|
1505 /* move all indices >= index_num forward one space */
|
|
1506 memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num));
|
|
1507
|
|
1508 track->indices[index_num] = index;
|
|
1509 cuesheet_calculate_length_(object);
|
|
1510 return true;
|
|
1511 }
|
|
1512
|
|
1513 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
|
|
1514 {
|
|
1515 FLAC__StreamMetadata_CueSheet_Index index;
|
|
1516 memset(&index, 0, sizeof(index));
|
|
1517 return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, index);
|
|
1518 }
|
|
1519
|
|
1520 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, unsigned track_num, unsigned index_num)
|
|
1521 {
|
|
1522 FLAC__StreamMetadata_CueSheet_Track *track;
|
|
1523
|
|
1524 FLAC__ASSERT(0 != object);
|
|
1525 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1526 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
|
|
1527 FLAC__ASSERT(index_num < object->data.cue_sheet.tracks[track_num].num_indices);
|
|
1528
|
|
1529 track = &object->data.cue_sheet.tracks[track_num];
|
|
1530
|
|
1531 /* move all indices > index_num backward one space */
|
|
1532 memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1));
|
|
1533
|
|
1534 FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1);
|
|
1535 cuesheet_calculate_length_(object);
|
|
1536 return true;
|
|
1537 }
|
|
1538
|
|
1539 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, unsigned new_num_tracks)
|
|
1540 {
|
|
1541 FLAC__ASSERT(0 != object);
|
|
1542 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1543
|
|
1544 if(0 == object->data.cue_sheet.tracks) {
|
|
1545 FLAC__ASSERT(object->data.cue_sheet.num_tracks == 0);
|
|
1546 if(0 == new_num_tracks)
|
|
1547 return true;
|
|
1548 else if(0 == (object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)))
|
|
1549 return false;
|
|
1550 }
|
|
1551 else {
|
|
1552 const unsigned old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
|
|
1553 const unsigned new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track);
|
|
1554
|
|
1555 FLAC__ASSERT(object->data.cue_sheet.num_tracks > 0);
|
|
1556
|
|
1557 /* if shrinking, free the truncated entries */
|
|
1558 if(new_num_tracks < object->data.cue_sheet.num_tracks) {
|
|
1559 unsigned i;
|
|
1560 for(i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++)
|
|
1561 if(0 != object->data.cue_sheet.tracks[i].indices)
|
|
1562 free(object->data.cue_sheet.tracks[i].indices);
|
|
1563 }
|
|
1564
|
|
1565 if(new_size == 0) {
|
|
1566 free(object->data.cue_sheet.tracks);
|
|
1567 object->data.cue_sheet.tracks = 0;
|
|
1568 }
|
|
1569 else if(0 == (object->data.cue_sheet.tracks = (FLAC__StreamMetadata_CueSheet_Track*)realloc(object->data.cue_sheet.tracks, new_size)))
|
|
1570 return false;
|
|
1571
|
|
1572 /* if growing, zero all the lengths/pointers of new elements */
|
|
1573 if(new_size > old_size)
|
|
1574 memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size);
|
|
1575 }
|
|
1576
|
|
1577 object->data.cue_sheet.num_tracks = new_num_tracks;
|
|
1578
|
|
1579 cuesheet_calculate_length_(object);
|
|
1580 return true;
|
|
1581 }
|
|
1582
|
|
1583 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
|
|
1584 {
|
|
1585 FLAC__ASSERT(0 != object);
|
|
1586 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
|
|
1587
|
|
1588 return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy);
|
|
1589 }
|
|
1590
|
|
1591 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, unsigned track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy)
|
|
1592 {
|
|
1593 FLAC__StreamMetadata_CueSheet *cs;
|
|
1594
|
|
1595 FLAC__ASSERT(0 != object);
|
|
1596 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1597 FLAC__ASSERT(track_num <= object->data.cue_sheet.num_tracks);
|
|
1598
|
|
1599 cs = &object->data.cue_sheet;
|
|
1600
|
|
1601 if(!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1))
|
|
1602 return false;
|
|
1603
|
|
1604 /* move all tracks >= track_num forward one space */
|
|
1605 memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num));
|
|
1606 cs->tracks[track_num].num_indices = 0;
|
|
1607 cs->tracks[track_num].indices = 0;
|
|
1608
|
|
1609 return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy);
|
|
1610 }
|
|
1611
|
|
1612 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, unsigned track_num)
|
|
1613 {
|
|
1614 FLAC__StreamMetadata_CueSheet_Track track;
|
|
1615 memset(&track, 0, sizeof(track));
|
|
1616 return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false);
|
|
1617 }
|
|
1618
|
|
1619 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, unsigned track_num)
|
|
1620 {
|
|
1621 FLAC__StreamMetadata_CueSheet *cs;
|
|
1622
|
|
1623 FLAC__ASSERT(0 != object);
|
|
1624 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1625 FLAC__ASSERT(track_num < object->data.cue_sheet.num_tracks);
|
|
1626
|
|
1627 cs = &object->data.cue_sheet;
|
|
1628
|
|
1629 /* free the track at track_num */
|
|
1630 if(0 != cs->tracks[track_num].indices)
|
|
1631 free(cs->tracks[track_num].indices);
|
|
1632
|
|
1633 /* move all tracks > track_num backward one space */
|
|
1634 memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1));
|
|
1635 cs->tracks[cs->num_tracks-1].num_indices = 0;
|
|
1636 cs->tracks[cs->num_tracks-1].indices = 0;
|
|
1637
|
|
1638 return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1);
|
|
1639 }
|
|
1640
|
|
1641 FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation)
|
|
1642 {
|
|
1643 FLAC__ASSERT(0 != object);
|
|
1644 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1645
|
|
1646 return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation);
|
|
1647 }
|
|
1648
|
|
1649 static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, unsigned track)
|
|
1650 {
|
|
1651 if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1)
|
|
1652 return 0;
|
|
1653 else if (cs->tracks[track].indices[0].number == 1)
|
|
1654 return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in;
|
|
1655 else if (cs->tracks[track].num_indices < 2)
|
|
1656 return 0;
|
|
1657 else if (cs->tracks[track].indices[1].number == 1)
|
|
1658 return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in;
|
|
1659 else
|
|
1660 return 0;
|
|
1661 }
|
|
1662
|
|
1663 static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x)
|
|
1664 {
|
|
1665 FLAC__uint32 n = 0;
|
|
1666 while (x) {
|
|
1667 n += (x%10);
|
|
1668 x /= 10;
|
|
1669 }
|
|
1670 return n;
|
|
1671 }
|
|
1672
|
|
1673 FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object)
|
|
1674 {
|
|
1675 const FLAC__StreamMetadata_CueSheet *cs;
|
|
1676
|
|
1677 FLAC__ASSERT(0 != object);
|
|
1678 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_CUESHEET);
|
|
1679
|
|
1680 cs = &object->data.cue_sheet;
|
|
1681
|
|
1682 if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */
|
|
1683 return 0;
|
|
1684
|
|
1685 {
|
|
1686 FLAC__uint32 i, length, sum = 0;
|
|
1687 for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */
|
|
1688 sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100));
|
|
1689 length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100);
|
|
1690
|
|
1691 return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1);
|
|
1692 }
|
|
1693 }
|
|
1694
|
|
1695 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy)
|
|
1696 {
|
|
1697 char *old;
|
|
1698 size_t old_length, new_length;
|
|
1699
|
|
1700 FLAC__ASSERT(0 != object);
|
|
1701 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
|
|
1702 FLAC__ASSERT(0 != mime_type);
|
|
1703
|
|
1704 old = object->data.picture.mime_type;
|
|
1705 old_length = old? strlen(old) : 0;
|
|
1706 new_length = strlen(mime_type);
|
|
1707
|
|
1708 /* do the copy first so that if we fail we leave the object untouched */
|
|
1709 if(copy) {
|
|
1710 if(!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, new_length+1))
|
|
1711 return false;
|
|
1712 }
|
|
1713 else {
|
|
1714 object->data.picture.mime_type = mime_type;
|
|
1715 }
|
|
1716
|
|
1717 if(0 != old)
|
|
1718 free(old);
|
|
1719
|
|
1720 object->length -= old_length;
|
|
1721 object->length += new_length;
|
|
1722 return true;
|
|
1723 }
|
|
1724
|
|
1725 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy)
|
|
1726 {
|
|
1727 FLAC__byte *old;
|
|
1728 size_t old_length, new_length;
|
|
1729
|
|
1730 FLAC__ASSERT(0 != object);
|
|
1731 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
|
|
1732 FLAC__ASSERT(0 != description);
|
|
1733
|
|
1734 old = object->data.picture.description;
|
|
1735 old_length = old? strlen((const char *)old) : 0;
|
|
1736 new_length = strlen((const char *)description);
|
|
1737
|
|
1738 /* do the copy first so that if we fail we leave the object untouched */
|
|
1739 if(copy) {
|
|
1740 if(!copy_bytes_(&object->data.picture.description, description, new_length+1))
|
|
1741 return false;
|
|
1742 }
|
|
1743 else {
|
|
1744 object->data.picture.description = description;
|
|
1745 }
|
|
1746
|
|
1747 if(0 != old)
|
|
1748 free(old);
|
|
1749
|
|
1750 object->length -= old_length;
|
|
1751 object->length += new_length;
|
|
1752 return true;
|
|
1753 }
|
|
1754
|
|
1755 FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy)
|
|
1756 {
|
|
1757 FLAC__byte *old;
|
|
1758
|
|
1759 FLAC__ASSERT(0 != object);
|
|
1760 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
|
|
1761 FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
|
|
1762
|
|
1763 old = object->data.picture.data;
|
|
1764
|
|
1765 /* do the copy first so that if we fail we leave the object untouched */
|
|
1766 if(copy) {
|
|
1767 if(!copy_bytes_(&object->data.picture.data, data, length))
|
|
1768 return false;
|
|
1769 }
|
|
1770 else {
|
|
1771 object->data.picture.data = data;
|
|
1772 }
|
|
1773
|
|
1774 if(0 != old)
|
|
1775 free(old);
|
|
1776
|
|
1777 object->length -= object->data.picture.data_length;
|
|
1778 object->data.picture.data_length = length;
|
|
1779 object->length += length;
|
|
1780 return true;
|
|
1781 }
|
|
1782
|
|
1783 FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation)
|
|
1784 {
|
|
1785 FLAC__ASSERT(0 != object);
|
|
1786 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_PICTURE);
|
|
1787
|
|
1788 return FLAC__format_picture_is_legal(&object->data.picture, violation);
|
|
1789 }
|