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