blob: 0baa30f967fc0c7cea7b04da0a92dc9d7c68bff0 [file] [log] [blame]
Josh Coalsoncae58732002-05-04 17:30:32 +00001/* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001,2002 Josh Coalson
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#if defined _MSC_VER || defined __MINGW32__
26#include <sys/utime.h> /* for utime() */
27#include <io.h> /* for chmod() */
28#else
29#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
30#include <utime.h> /* for utime() */
31#include <unistd.h> /* for chown(), unlink() */
32#endif
33#include <sys/stat.h> /* for stat(), maybe chmod() */
34
35#include "FLAC/assert.h"
36#include "FLAC/metadata.h"
37#include "FLAC/file_decoder.h"
38
39#ifdef max
40#undef max
41#endif
42#define max(a,b) ((a)>(b)?(a):(b))
43#ifdef min
44#undef min
45#endif
46#define min(a,b) ((a)<(b)?(a):(b))
47
48
49/****************************************************************************
50 *
51 * Local function declarations
52 *
53 ***************************************************************************/
54
55/* this one should probably not go in the public interface: */
56static void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object);
57
58static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
59static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
60static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes);
61static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes);
62static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes);
63static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes);
64
65static FLAC__bool read_metadata_block_header_(FLAC__MetaData_SimpleIterator *iterator);
66static FLAC__bool read_metadata_block_data_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block);
67static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_streaminfo_(FILE *file, FLAC__StreamMetaData_StreamInfo *block);
68static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_padding_(FILE *file, FLAC__StreamMetaData_Padding *block, unsigned block_length);
69static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_application_(FILE *file, FLAC__StreamMetaData_Application *block, unsigned block_length);
70static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_seektable_(FILE *file, FLAC__StreamMetaData_SeekTable *block, unsigned block_length);
71static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_(FILE *file, FLAC__StreamMetaData_VorbisComment_Entry *entry);
72static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_(FILE *file, FLAC__StreamMetaData_VorbisComment *block);
73
74static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block);
75static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block);
76static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_streaminfo_(FILE *file, const FLAC__StreamMetaData_StreamInfo *block);
77static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_padding_(FILE *file, const FLAC__StreamMetaData_Padding *block, unsigned block_length);
78static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_application_(FILE *file, const FLAC__StreamMetaData_Application *block, unsigned block_length);
79static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_seektable_(FILE *file, const FLAC__StreamMetaData_SeekTable *block);
80static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_vorbis_comment_(FILE *file, const FLAC__StreamMetaData_VorbisComment *block);
81static FLAC__bool write_metadata_block_stationary_(FLAC__MetaData_SimpleIterator *iterator, const FLAC__StreamMetaData *block);
82static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, unsigned padding_length, FLAC__bool padding_is_last);
83static FLAC__bool rewrite_whole_file_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool append);
84
85static FLAC__bool chain_rewrite_chain_(FLAC__MetaData_Chain *chain);
86static FLAC__bool chain_rewrite_file_(FLAC__MetaData_Chain *chain, const char *tempfile_path_prefix);
87
88static void simple_iterator_push_(FLAC__MetaData_SimpleIterator *iterator);
89static FLAC__bool simple_iterator_pop_(FLAC__MetaData_SimpleIterator *iterator);
90
91/* return 0 if OK, 1 if read error, 2 if not a FLAC file */
92static unsigned seek_to_first_metadata_block_(FILE *f);
93
94static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append);
Josh Coalson6b02a752002-05-10 06:42:02 +000095static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, long fixup_is_last_flag_offset, FLAC__bool backup);
Josh Coalsoncae58732002-05-04 17:30:32 +000096
97static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, unsigned bytes/*@@@ 4G limit*/, FLAC__MetaData_SimpleIteratorStatus *status);
98static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__MetaData_SimpleIteratorStatus *status);
99
100static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status);
101static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status);
102static void cleanup_tempfile_(FILE **tempfile, char **tempfilename);
103
104static FLAC__bool get_file_stats_(const char *filename, struct stat *stats);
105static void set_file_stats_(const char *filename, struct stat *stats);
106
107static FLAC__MetaData_ChainStatus get_equivalent_status_(FLAC__MetaData_SimpleIteratorStatus status);
108
109
110/****************************************************************************
111 *
112 * Level 0 implementation
113 *
114 ***************************************************************************/
115
116static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data);
117static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data);
118static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
119
120FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetaData_StreamInfo *streaminfo)
121{
122 FLAC__FileDecoder *decoder = FLAC__file_decoder_new();
123
124 if(0 == decoder)
125 return false;
126
127 FLAC__file_decoder_set_md5_checking(decoder, false);
128 FLAC__file_decoder_set_filename(decoder, filename);
129 FLAC__file_decoder_set_write_callback(decoder, write_callback_);
130 FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
131 FLAC__file_decoder_set_error_callback(decoder, error_callback_);
132 FLAC__file_decoder_set_client_data(decoder, &streaminfo);
133
134 if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK)
135 return false;
136
137 if(!FLAC__file_decoder_process_metadata(decoder))
138 return false;
139
Josh Coalson51ee4652002-05-09 05:46:23 +0000140 FLAC__file_decoder_finish(decoder);
Josh Coalsoncae58732002-05-04 17:30:32 +0000141 FLAC__file_decoder_delete(decoder);
142
143 return 0 != streaminfo; /* the metadata_callback_() will set streaminfo to 0 on an error */
144}
145
146FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
147{
148 (void)decoder, (void)frame, (void)buffer, (void)client_data;
149
150 return FLAC__STREAM_DECODER_WRITE_CONTINUE;
151}
152
153void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
154{
155 FLAC__StreamMetaData_StreamInfo **streaminfo = (FLAC__StreamMetaData_StreamInfo **)client_data;
156 (void)decoder;
157
158 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && 0 != *streaminfo)
159 **streaminfo = metadata->data.stream_info;
160}
161
162void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
163{
164 FLAC__StreamMetaData_StreamInfo **streaminfo = (FLAC__StreamMetaData_StreamInfo **)client_data;
165 (void)decoder;
166
167 if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
168 *streaminfo = 0;
169}
170
171
172/****************************************************************************
173 *
174 * Level 1 implementation
175 *
176 ***************************************************************************/
177
178#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4)
179/* 1 for initial offset, +4 for our own personal use */
180
181struct FLAC__MetaData_SimpleIterator {
182 FILE *file;
183 char *filename, *tempfile_path_prefix;
184 struct stat stats;
185 FLAC__bool has_stats;
186 FLAC__bool is_writable;
187 FLAC__MetaData_SimpleIteratorStatus status;
188 /*@@@ 2G limits here because of the offset type: */
189 long offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH];
190 long first_offset; /* this is the offset to the STREAMINFO block */
191 unsigned depth;
192 /* this is the metadata block header of the current block we are pointing to: */
193 FLAC__bool is_last;
194 FLAC__MetaDataType type;
195 unsigned length;
196};
197
198const char *FLAC__MetaData_SimpleIteratorStatusString[] = {
199 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK",
200 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT",
201 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE",
202 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE",
203 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE",
204 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR",
205 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR",
206 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR",
207 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR",
208 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR",
209 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR",
210 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR"
211};
212
213
214FLAC__MetaData_SimpleIterator *FLAC__metadata_simple_iterator_new()
215{
216 FLAC__MetaData_SimpleIterator *iterator = malloc(sizeof(FLAC__MetaData_SimpleIterator));
217
218 if(0 != iterator) {
219 iterator->file = 0;
220 iterator->filename = 0;
221 iterator->tempfile_path_prefix = 0;
222 iterator->has_stats = false;
223 iterator->is_writable = false;
224 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
225 iterator->first_offset = iterator->offset[0] = -1;
226 iterator->depth = 0;
227 }
228
229 return iterator;
230}
231
232static void simple_iterator_free_guts_(FLAC__MetaData_SimpleIterator *iterator)
233{
234 FLAC__ASSERT(0 != iterator);
235
236 if(0 != iterator->file) {
237 fclose(iterator->file);
238 iterator->file = 0;
239 if(iterator->has_stats)
240 set_file_stats_(iterator->filename, &iterator->stats);
241 }
242 if(0 != iterator->filename) {
243 free(iterator->filename);
244 iterator->filename = 0;
245 }
246 if(0 != iterator->tempfile_path_prefix) {
247 free(iterator->tempfile_path_prefix);
248 iterator->tempfile_path_prefix = 0;
249 }
250}
251
252void FLAC__metadata_simple_iterator_delete(FLAC__MetaData_SimpleIterator *iterator)
253{
254 FLAC__ASSERT(0 != iterator);
255
256 simple_iterator_free_guts_(iterator);
257 free(iterator);
258}
259
260FLAC__MetaData_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__MetaData_SimpleIterator *iterator)
261{
262 FLAC__MetaData_SimpleIteratorStatus status;
263
264 FLAC__ASSERT(0 != iterator);
265
266 status = iterator->status;
267 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
268 return status;
269}
270
271static FLAC__bool simple_iterator_prime_input_(FLAC__MetaData_SimpleIterator *iterator)
272{
273 unsigned ret;
274
275 FLAC__ASSERT(0 != iterator);
276
277 iterator->is_writable = false;
278
279 if(0 == (iterator->file = fopen(iterator->filename, "r+b"))) {
280 if(errno == EACCES) {
281 if(0 == (iterator->file = fopen(iterator->filename, "rb"))) {
282 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
283 return false;
284 }
285 }
286 else {
287 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
288 return false;
289 }
290 }
291 else {
292 iterator->is_writable = true;
293 }
294
295 ret = seek_to_first_metadata_block_(iterator->file);
296 switch(ret) {
297 case 0:
298 iterator->depth = 0;
299 iterator->first_offset = iterator->offset[iterator->depth] = ftell(iterator->file);
300 return read_metadata_block_header_(iterator);
301 case 1:
302 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
303 return false;
304 case 2:
305 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE;
306 return false;
307 default:
308 FLAC__ASSERT(0);
309 return false;
310 }
311}
312
313#if 0
314@@@ If we decide to finish implementing this, put this comment back in metadata.h
315/*
316 * The 'tempfile_path_prefix' allows you to specify a directory where
317 * tempfiles should go. Remember that if your metadata edits cause the
318 * FLAC file to grow, the entire file will have to be rewritten. If
319 * 'tempfile_path_prefix' is NULL, the temp file will be written in the
320 * same directory as the original FLAC file. This makes replacing the
321 * original with the tempfile fast but requires extra space in the same
322 * partition for the tempfile. If space is a problem, you can pass a
323 * directory name belonging to a different partition in
324 * 'tempfile_path_prefix'. Note that you should use the forward slash
325 * '/' as the directory separator. A trailing slash is not needed; it
326 * will be added automatically.
327 */
328FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__MetaData_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix);
329#endif
330
331FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__MetaData_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats)
332{
333 const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'rename(' for what it will take to finish implementing this */
334
335 FLAC__ASSERT(0 != iterator);
336 FLAC__ASSERT(0 != filename);
337
338 simple_iterator_free_guts_(iterator);
339
340 if(preserve_file_stats)
341 iterator->has_stats = get_file_stats_(filename, &iterator->stats);
342
343 if(0 == (iterator->filename = strdup(filename))) {
344 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
345 return false;
346 }
347 if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) {
348 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
349 return false;
350 }
351
352 return simple_iterator_prime_input_(iterator);
353}
354
355FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__MetaData_SimpleIterator *iterator)
356{
357 FLAC__ASSERT(0 != iterator);
358 FLAC__ASSERT(0 != iterator->file);
359
360 return iterator->is_writable;
361}
362
363FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__MetaData_SimpleIterator *iterator)
364{
365 FLAC__ASSERT(0 != iterator);
366 FLAC__ASSERT(0 != iterator->file);
367
368 if(iterator->is_last)
369 return false;
370
371 if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
372 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
373 return false;
374 }
375
376 iterator->offset[iterator->depth] = ftell(iterator->file);
377
378 return read_metadata_block_header_(iterator);
379}
380
381FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__MetaData_SimpleIterator *iterator)
382{
383 long this_offset;
384
385 FLAC__ASSERT(0 != iterator);
386 FLAC__ASSERT(0 != iterator->file);
387
388 if(iterator->offset[iterator->depth] == iterator->first_offset)
389 return false;
390
391 if(0 != fseek(iterator->file, iterator->first_offset, SEEK_SET)) {
392 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
393 return false;
394 }
395 this_offset = iterator->first_offset;
396 if(!read_metadata_block_header_(iterator))
397 return false;
398
399 /* we ignore any error from ftell() and catch it in fseek() */
400 while(ftell(iterator->file) + (long)iterator->length < iterator->offset[iterator->depth]) {
401 if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
402 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
403 return false;
404 }
405 this_offset = ftell(iterator->file);
406 if(!read_metadata_block_header_(iterator))
407 return false;
408 }
409
410 iterator->offset[iterator->depth] = this_offset;
411
412 return true;
413}
414
415FLAC__MetaDataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__MetaData_SimpleIterator *iterator)
416{
417 FLAC__ASSERT(0 != iterator);
418 FLAC__ASSERT(0 != iterator->file);
419
420 return iterator->type;
421}
422
423FLAC__StreamMetaData *FLAC__metadata_simple_iterator_get_block(FLAC__MetaData_SimpleIterator *iterator)
424{
425 FLAC__StreamMetaData *block = FLAC__metadata_object_new(iterator->type);
426
427 FLAC__ASSERT(0 != iterator);
428 FLAC__ASSERT(0 != iterator->file);
429
430 if(0 != block) {
431 block->is_last = iterator->is_last;
432 block->length = iterator->length;
433
434 if(!read_metadata_block_data_(iterator, block)) {
435 FLAC__metadata_object_delete(block);
436 return 0;
437 }
438
439 /* back up to the beginning of the block data to stay consistent */
440 if(0 != fseek(iterator->file, iterator->offset[iterator->depth] + 4, SEEK_SET)) { /*@@@ 4 = MAGIC NUMBER for metadata block header bytes */
441 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
442 FLAC__metadata_object_delete(block);
443 return 0;
444 }
445 }
446 else
447 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
448
449 return block;
450}
451
452FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding)
453{
Josh Coalson51ee4652002-05-09 05:46:23 +0000454 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth];)
455 FLAC__bool ret;
456
Josh Coalsoncae58732002-05-04 17:30:32 +0000457 FLAC__ASSERT(0 != iterator);
458 FLAC__ASSERT(0 != iterator->file);
Josh Coalson51ee4652002-05-09 05:46:23 +0000459/*@@@@fprintf(stderr,"A:460:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
Josh Coalsoncae58732002-05-04 17:30:32 +0000460
461 if(!iterator->is_writable) {
462 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE;
463 return false;
464 }
465
466 if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) {
467 if(iterator->type != block->type) {
468 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
469 return false;
470 }
471 }
472
473 block->is_last = iterator->is_last;
Josh Coalsoncae58732002-05-04 17:30:32 +0000474
475 if(iterator->length == block->length)
476 return write_metadata_block_stationary_(iterator, block);
477 else if(iterator->length > block->length) {
Josh Coalson51ee4652002-05-09 05:46:23 +0000478 if(use_padding && iterator->length >= 4 + block->length) { /*@@@ 4 = MAGIC NUMBER for metadata block header bytes */
479 ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - 4 - block->length, block->is_last);
480/*@@@@fprintf(stderr,"Z:483:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
481 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
482 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
483 return ret;
484 }
485 else {
486 ret = rewrite_whole_file_(iterator, block, /*append=*/false);
487/*@@@@fprintf(stderr,"Z:491:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
488 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
489 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
490 return ret;
491 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000492 }
493 else /* iterator->length < block->length */ {
494 unsigned padding_leftover = 0;
495 FLAC__bool padding_is_last = false;
496 if(use_padding) {
497 /* first see if we can even use padding */
498 if(iterator->is_last) {
499 use_padding = false;
500 }
501 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000502 const unsigned extra_padding_bytes_required = block->length - iterator->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000503 simple_iterator_push_(iterator);
504 if(!FLAC__metadata_simple_iterator_next(iterator)) {
505 (void)simple_iterator_pop_(iterator);
506 return false;
507 }
508 if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
509 use_padding = false;
510 }
511 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000512 if(4 + iterator->length == extra_padding_bytes_required) { /*@@@ MAGIC NUMBER 4 = metadata block header length */
Josh Coalsoncae58732002-05-04 17:30:32 +0000513 padding_leftover = 0;
514 block->is_last = iterator->is_last;
515 }
Josh Coalson51ee4652002-05-09 05:46:23 +0000516 else if(iterator->length < extra_padding_bytes_required)
Josh Coalsoncae58732002-05-04 17:30:32 +0000517 use_padding = false;
518 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000519 padding_leftover = 4 + iterator->length - extra_padding_bytes_required; /*@@@ MAGIC NUMBER 4 = metadata block header length */
Josh Coalsoncae58732002-05-04 17:30:32 +0000520 padding_is_last = iterator->is_last;
521 block->is_last = false;
522 }
523 }
524 if(!simple_iterator_pop_(iterator))
525 return false;
526 }
527 }
528 if(use_padding) {
Josh Coalson51ee4652002-05-09 05:46:23 +0000529 if(padding_leftover == 0) {
530 ret = write_metadata_block_stationary_(iterator, block);
531/*@@@@fprintf(stderr,"Z:536:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
532 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
533 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
534 return ret;
535 }
536 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000537 /*@@@ MAGIC NUMBER 4 = metadata block header length */
538 FLAC__ASSERT(padding_leftover >= 4);
539 ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - 4, padding_is_last);
Josh Coalson51ee4652002-05-09 05:46:23 +0000540/*@@@@fprintf(stderr,"Z:544:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
541 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
542 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
543 return ret;
544 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000545 }
546 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000547 ret = rewrite_whole_file_(iterator, block, /*append=*/false);
548/*@@@@fprintf(stderr,"Z:553:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
549 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
550 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
551 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000552 }
553 }
554}
555
556FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding)
557{
558 unsigned padding_leftover = 0;
559 FLAC__bool padding_is_last = false;
560
Josh Coalson51ee4652002-05-09 05:46:23 +0000561 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth] + 4 + iterator->length;)
562 FLAC__bool ret;
563
Josh Coalsoncae58732002-05-04 17:30:32 +0000564 FLAC__ASSERT(0 != iterator);
565 FLAC__ASSERT(0 != iterator->file);
Josh Coalson51ee4652002-05-09 05:46:23 +0000566/*@@@@fprintf(stderr,"A:568:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
Josh Coalsoncae58732002-05-04 17:30:32 +0000567
568 if(!iterator->is_writable)
569 return false;
570
571 if(block->type == FLAC__METADATA_TYPE_STREAMINFO) {
572 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
573 return false;
574 }
575
Josh Coalson6b02a752002-05-10 06:42:02 +0000576 block->is_last = iterator->is_last;
Josh Coalson51ee4652002-05-09 05:46:23 +0000577
Josh Coalsoncae58732002-05-04 17:30:32 +0000578 if(use_padding) {
579 /* first see if we can even use padding */
580 if(iterator->is_last) {
581 use_padding = false;
582 }
583 else {
584 simple_iterator_push_(iterator);
585 if(!FLAC__metadata_simple_iterator_next(iterator)) {
586 (void)simple_iterator_pop_(iterator);
587 return false;
588 }
589 if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
590 use_padding = false;
591 }
592 else {
Josh Coalsoncae58732002-05-04 17:30:32 +0000593 if(iterator->length == block->length) {
594 padding_leftover = 0;
595 block->is_last = iterator->is_last;
596 }
Josh Coalson6b02a752002-05-10 06:42:02 +0000597 else if(iterator->length < 4 + block->length) /*@@@ MAGIC NUMBER 4 = metadata block header length */
Josh Coalsoncae58732002-05-04 17:30:32 +0000598 use_padding = false;
599 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000600 padding_leftover = iterator->length - block->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000601 padding_is_last = iterator->is_last;
602 block->is_last = false;
603 }
604 }
605 if(!simple_iterator_pop_(iterator))
606 return false;
607 }
608 }
609 if(use_padding) {
610 /* move to the next block, which is suitable padding */
611 if(!FLAC__metadata_simple_iterator_next(iterator))
612 return false;
Josh Coalson51ee4652002-05-09 05:46:23 +0000613 if(padding_leftover == 0) {
614 ret = write_metadata_block_stationary_(iterator, block);
615/*@@@@fprintf(stderr,"Z:620:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
616 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
617 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
618 return ret;
619 }
620 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000621 /*@@@ MAGIC NUMBER 4 = metadata block header length */
622 FLAC__ASSERT(padding_leftover >= 4);
623 ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - 4, padding_is_last);
Josh Coalson51ee4652002-05-09 05:46:23 +0000624/*@@@@fprintf(stderr,"Z:628:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
625 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
626 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
627 return ret;
628 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000629 }
630 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000631 ret = rewrite_whole_file_(iterator, block, /*append=*/true);
632/*@@@@fprintf(stderr,"Z:637:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
633 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
634 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + 4);
635 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000636 }
637}
638
Josh Coalsoncae58732002-05-04 17:30:32 +0000639FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__bool use_padding)
640{
Josh Coalson51ee4652002-05-09 05:46:23 +0000641 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth];)
642 FLAC__bool ret;
643
644/*@@@@fprintf(stderr,"A:649:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
Josh Coalsoncae58732002-05-04 17:30:32 +0000645 if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) {
646 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
647 return false;
648 }
649
650 if(use_padding) {
651 FLAC__StreamMetaData *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
652 if(0 == padding) {
653 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
654 return false;
655 }
656 padding->length = iterator->length;
657 if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) {
658 FLAC__metadata_object_delete(padding);
659 return false;
660 }
661 FLAC__metadata_object_delete(padding);
Josh Coalson6b02a752002-05-10 06:42:02 +0000662 if(!FLAC__metadata_simple_iterator_prev(iterator))
663 return false;
Josh Coalson51ee4652002-05-09 05:46:23 +0000664/*@@@@fprintf(stderr,"Z:667:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
665 FLAC__ASSERT(iterator->offset[iterator->depth] + 4 + (long)iterator->length == debug_target_offset);
666 FLAC__ASSERT(ftell(iterator->file) + (long)iterator->length == debug_target_offset);
Josh Coalsoncae58732002-05-04 17:30:32 +0000667 return true;
668 }
669 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000670 ret = rewrite_whole_file_(iterator, 0, /*append=*/false);
671/*@@@@fprintf(stderr,"Z:675:offset/length/depth=%ld/%u/%u target=%ld ftell=%ld\n",iterator->offset[iterator->depth],iterator->length,iterator->depth,debug_target_offset,ftell(iterator->file));*/
672 FLAC__ASSERT(iterator->offset[iterator->depth] + 4 + (long)iterator->length == debug_target_offset);
673 FLAC__ASSERT(ftell(iterator->file) + (long)iterator->length == debug_target_offset);
674 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000675 }
676}
677
678
679
680/****************************************************************************
681 *
682 * Level 2 implementation
683 *
684 ***************************************************************************/
685
686
687typedef struct FLAC__MetaData_Node {
688 FLAC__StreamMetaData *data;
689 struct FLAC__MetaData_Node *prev, *next;
690} FLAC__MetaData_Node;
691
692struct FLAC__MetaData_Chain {
693 char *filename;
694 FLAC__MetaData_Node *head;
695 FLAC__MetaData_Node *tail;
696 unsigned nodes;
697 FLAC__MetaData_ChainStatus status;
698 long first_offset, last_offset; /*@@@ 2G limit */
699 /*
700 * This is the length of the chain initially read from the FLAC file.
701 * it is used to compare against the current_length to decide whether
702 * or not the whole file has to be rewritten.
703 */
704 unsigned initial_length; /*@@@ 4G limit */
705 unsigned current_length; /*@@@ 4G limit */
706};
707
708struct FLAC__MetaData_Iterator {
709 FLAC__MetaData_Chain *chain;
710 FLAC__MetaData_Node *current;
711};
712
713const char *FLAC__MetaData_ChainStatusString[] = {
714 "FLAC__METADATA_CHAIN_STATUS_OK",
715 "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT",
716 "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE",
717 "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE",
718 "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE",
719 "FLAC__METADATA_CHAIN_STATUS_READ_ERROR",
720 "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR",
721 "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR",
722 "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR",
723 "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR",
724 "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR",
725 "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR"
726};
727
728
729static FLAC__MetaData_Node *node_new_()
730{
731 FLAC__MetaData_Node *node = (FLAC__MetaData_Node*)malloc(sizeof(FLAC__MetaData_Node));
732 if(0 != node)
733 memset(node, 0, sizeof(FLAC__MetaData_Node));
734 return node;
735}
736
737static void node_delete_(FLAC__MetaData_Node *node)
738{
739 FLAC__ASSERT(0 != node);
740 if(0 != node->data)
741 FLAC__metadata_object_delete(node->data);
742 free(node);
743}
744
745static void chain_append_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
746{
747 FLAC__ASSERT(0 != chain);
748 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000749 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000750
Josh Coalson6b02a752002-05-10 06:42:02 +0000751 node->next = node->prev = 0;
Josh Coalsoncae58732002-05-04 17:30:32 +0000752 node->data->is_last = true;
753 if(0 != chain->tail)
754 chain->tail->data->is_last = false;
755
756 if(0 == chain->head)
757 chain->head = node;
758 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000759 FLAC__ASSERT(0 != chain->tail);
Josh Coalsoncae58732002-05-04 17:30:32 +0000760 chain->tail->next = node;
761 node->prev = chain->tail;
762 }
763 chain->tail = node;
764 chain->nodes++;
765 chain->current_length += (4 + node->data->length); /*@@@ MAGIC NUMBER 4 = metadata block header bytes */
766}
767
768static void chain_remove_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
769{
770 FLAC__ASSERT(0 != chain);
771 FLAC__ASSERT(0 != node);
772
773 if(node == chain->head)
774 chain->head = node->next;
775 else
776 node->prev->next = node->next;
777
778 if(node == chain->tail)
779 chain->tail = node->prev;
780 else
781 node->next->prev = node->prev;
782
Josh Coalson6b02a752002-05-10 06:42:02 +0000783 if(0 != chain->tail)
784 chain->tail->data->is_last = true;
785
Josh Coalsoncae58732002-05-04 17:30:32 +0000786 chain->nodes--;
787 chain->current_length -= (4 + node->data->length); /*@@@ MAGIC NUMBER 4 = metadata block header bytes */
788}
789
790static void chain_delete_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
791{
792 chain_remove_node_(chain, node);
793 node_delete_(node);
794}
795
796static void iterator_insert_node_(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Node *node)
797{
798 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000799 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000800 FLAC__ASSERT(0 != iterator);
801 FLAC__ASSERT(0 != iterator->current);
802 FLAC__ASSERT(0 != iterator->chain);
803 FLAC__ASSERT(0 != iterator->chain->head);
804 FLAC__ASSERT(0 != iterator->chain->tail);
805
Josh Coalson6b02a752002-05-10 06:42:02 +0000806 node->data->is_last = false;
Josh Coalson2745fe72002-05-11 05:31:56 +0000807
Josh Coalsoncae58732002-05-04 17:30:32 +0000808 node->prev = iterator->current->prev;
Josh Coalson2745fe72002-05-11 05:31:56 +0000809 node->next = iterator->current;
810
Josh Coalsoncae58732002-05-04 17:30:32 +0000811 if(0 == node->prev)
812 iterator->chain->head = node;
813 else
814 node->prev->next = node;
815
Josh Coalsoncae58732002-05-04 17:30:32 +0000816 iterator->current->prev = node;
817
818 iterator->chain->nodes++;
819 iterator->chain->current_length += (4 + node->data->length); /*@@@ MAGIC NUMBER 4 = metadata block header bytes */
820}
821
822static void iterator_insert_node_after_(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Node *node)
823{
824 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000825 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000826 FLAC__ASSERT(0 != iterator);
827 FLAC__ASSERT(0 != iterator->current);
828 FLAC__ASSERT(0 != iterator->chain);
829 FLAC__ASSERT(0 != iterator->chain->head);
830 FLAC__ASSERT(0 != iterator->chain->tail);
831
832 iterator->current->data->is_last = false;
833
834 node->prev = iterator->current;
Josh Coalsoncae58732002-05-04 17:30:32 +0000835 node->next = iterator->current->next;
Josh Coalson2745fe72002-05-11 05:31:56 +0000836
Josh Coalsoncae58732002-05-04 17:30:32 +0000837 if(0 == node->next)
838 iterator->chain->tail = node;
839 else
840 node->next->prev = node;
841
Josh Coalson2745fe72002-05-11 05:31:56 +0000842 node->prev->next = node;
843
Josh Coalsoncae58732002-05-04 17:30:32 +0000844 iterator->chain->tail->data->is_last = true;
845
846 iterator->chain->nodes++;
847 iterator->chain->current_length += (4 + node->data->length); /*@@@ MAGIC NUMBER 4 = metadata block header bytes */
848}
849
Josh Coalson2745fe72002-05-11 05:31:56 +0000850/* return true iff node and node->next are both padding */
851static FLAC__bool chain_merge_adjacent_padding_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
852{
853 if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) {
854 const unsigned growth = 4 + node->next->data->length; /*@@@ MAGIC NUMBER 4 = metadata data block header bytes */
855 node->data->length += growth;
856 chain->current_length += growth;
857
858 chain_delete_node_(chain, node->next);
859 return true;
860 }
861 else
862 return false;
863}
864
Josh Coalsoncae58732002-05-04 17:30:32 +0000865FLAC__MetaData_Chain *FLAC__metadata_chain_new()
866{
867 FLAC__MetaData_Chain *chain = malloc(sizeof(FLAC__MetaData_Chain));
868
869 if(0 != chain) {
870 chain->filename = 0;
871 chain->head = chain->tail = 0;
872 chain->nodes = 0;
873 chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
874 chain->initial_length = chain->current_length = 0;
875 }
876
877 return chain;
878}
879
880void FLAC__metadata_chain_delete(FLAC__MetaData_Chain *chain)
881{
882 FLAC__MetaData_Node *node, *next;
883
884 FLAC__ASSERT(0 != chain);
885
886 for(node = chain->head; node; ) {
887 next = node->next;
888 node_delete_(node);
889 node = next;
890 }
891
892 if(0 != chain->filename)
893 free(chain->filename);
894
895 free(chain);
896}
897
898FLAC__MetaData_ChainStatus FLAC__metadata_chain_status(FLAC__MetaData_Chain *chain)
899{
900 FLAC__MetaData_ChainStatus status;
901
902 FLAC__ASSERT(0 != chain);
903
904 status = chain->status;
905 chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
906 return status;
907}
908
909FLAC__bool FLAC__metadata_chain_read(FLAC__MetaData_Chain *chain, const char *filename)
910{
911 FLAC__MetaData_SimpleIterator *iterator;
912 FLAC__MetaData_Node *node;
913
914 FLAC__ASSERT(0 != chain);
915 FLAC__ASSERT(0 != filename);
916
917 if(0 == (chain->filename = strdup(filename))) {
918 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
919 return false;
920 }
921
922 if(0 == (iterator = FLAC__metadata_simple_iterator_new())) {
923 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
924 return false;
925 }
926
927 if(!FLAC__metadata_simple_iterator_init(iterator, filename, /*preserve_file_stats=*/false)) {
928 chain->status = get_equivalent_status_(iterator->status);
929 return false;
930 }
931
Josh Coalson2745fe72002-05-11 05:31:56 +0000932 chain->first_offset = iterator->offset[iterator->depth];
933
Josh Coalsoncae58732002-05-04 17:30:32 +0000934 do {
935 node = node_new_();
936 if(0 == node) {
937 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
938 return false;
939 }
940 node->data = FLAC__metadata_simple_iterator_get_block(iterator);
941 if(0 == node->data) {
942 node_delete_(node);
943 chain->status = get_equivalent_status_(iterator->status);
944 return false;
945 }
946 chain_append_node_(chain, node);
947 } while(FLAC__metadata_simple_iterator_next(iterator));
948
949 if(!iterator->is_last || iterator->status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) {
950 chain->status = get_equivalent_status_(iterator->status);
951 return false;
952 }
953
Josh Coalson2745fe72002-05-11 05:31:56 +0000954 chain->last_offset = ftell(iterator->file) + iterator->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000955 FLAC__metadata_simple_iterator_delete(iterator);
956
957 chain->initial_length = chain->current_length;
958 return true;
959}
960
Josh Coalsoncae58732002-05-04 17:30:32 +0000961FLAC__bool FLAC__metadata_chain_write(FLAC__MetaData_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats)
962{
963 struct stat stats;
964 const char *tempfile_path_prefix = 0;
965
Josh Coalson2745fe72002-05-11 05:31:56 +0000966 if(use_padding) {
967 /*@@@ MAGIC NUMBER 4 = metadata header bytes, appears several times here */
968 if(chain->current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
969 const unsigned delta = chain->initial_length - chain->current_length;
970 chain->tail->data->length += delta;
971 chain->current_length += delta;
972 FLAC__ASSERT(chain->current_length == chain->initial_length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000973 }
Josh Coalson2745fe72002-05-11 05:31:56 +0000974 else if(chain->current_length + 4 <= chain->initial_length) {
975 FLAC__StreamMetaData *padding;
976 FLAC__MetaData_Node *node;
977 if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) {
978 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
979 return false;
980 }
981 padding->length = chain->initial_length - (4 + chain->current_length);
982 if(0 == (node = node_new_())) {
983 FLAC__metadata_object_delete(padding);
984 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
985 return false;
986 }
987 node->data = padding;
988 chain_append_node_(chain, node);
989 FLAC__ASSERT(chain->current_length == chain->initial_length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000990 }
Josh Coalson2745fe72002-05-11 05:31:56 +0000991 else if(chain->current_length > chain->initial_length) {
992 const unsigned delta = chain->current_length - chain->initial_length;
993 if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
994 if(chain->tail->data->length + 4 == delta) {
995 chain_delete_node_(chain, chain->tail);
996 FLAC__ASSERT(chain->current_length == chain->initial_length);
997 }
998 else if(chain->tail->data->length <= delta) {
999 chain->tail->data->length -= delta;
1000 chain->current_length -= delta;
1001 FLAC__ASSERT(chain->current_length == chain->initial_length);
1002 }
1003 }
1004 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001005 }
1006
1007 if(preserve_file_stats)
1008 get_file_stats_(chain->filename, &stats);
1009
1010 if(chain->current_length == chain->initial_length) {
1011 if(!chain_rewrite_chain_(chain))
1012 return false;
1013 }
1014 else {
1015 if(!chain_rewrite_file_(chain, tempfile_path_prefix))
1016 return false;
1017 }
1018
1019 if(preserve_file_stats)
1020 set_file_stats_(chain->filename, &stats);
1021
1022 chain->initial_length = chain->current_length;
1023 return true;
1024}
1025
1026void FLAC__metadata_chain_merge_padding(FLAC__MetaData_Chain *chain)
1027{
1028 FLAC__MetaData_Node *node;
1029
1030 FLAC__ASSERT(0 != chain);
1031
1032 for(node = chain->head; node; ) {
Josh Coalson2745fe72002-05-11 05:31:56 +00001033 if(!chain_merge_adjacent_padding_(chain, node))
Josh Coalsoncae58732002-05-04 17:30:32 +00001034 node = node->next;
Josh Coalsoncae58732002-05-04 17:30:32 +00001035 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001036}
1037
1038void FLAC__metadata_chain_sort_padding(FLAC__MetaData_Chain *chain)
1039{
1040 FLAC__MetaData_Node *node, *save;
1041 unsigned i;
1042
1043 FLAC__ASSERT(0 != chain);
1044
1045 /*
1046 * Don't try and be too smart... this simple algo is good enough for
1047 * the small number of nodes that we deal with.
1048 */
1049 for(i = 0, node = chain->head; i < chain->nodes; i++) {
1050 node->data->is_last = false; /* we'll fix at the end */
1051 if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
1052 save = node->next;
1053 chain_remove_node_(chain, node);
1054 chain_append_node_(chain, node);
1055 node = save;
1056 }
1057 else {
1058 node = node->next;
1059 }
1060 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001061
1062 FLAC__metadata_chain_merge_padding(chain);
1063}
1064
1065
1066FLAC__MetaData_Iterator *FLAC__metadata_iterator_new()
1067{
1068 FLAC__MetaData_Iterator *iterator = malloc(sizeof(FLAC__MetaData_Iterator));
1069
1070 if(0 != iterator) {
1071 iterator->current = 0;
1072 iterator->chain = 0;
1073 }
1074
1075 return iterator;
1076}
1077
1078void FLAC__metadata_iterator_delete(FLAC__MetaData_Iterator *iterator)
1079{
1080 FLAC__ASSERT(0 != iterator);
1081
1082 free(iterator);
1083}
1084
1085/*
1086 * Initialize the iterator to point to the first metadata block in the
1087 * given chain.
1088 */
1089void FLAC__metadata_iterator_init(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Chain *chain)
1090{
1091 FLAC__ASSERT(0 != iterator);
1092 FLAC__ASSERT(0 != chain);
1093 FLAC__ASSERT(0 != chain->head);
1094
1095 iterator->chain = chain;
1096 iterator->current = chain->head;
1097}
1098
1099FLAC__bool FLAC__metadata_iterator_next(FLAC__MetaData_Iterator *iterator)
1100{
1101 FLAC__ASSERT(0 != iterator);
1102
1103 if(0 == iterator->current || 0 == iterator->current->next)
1104 return false;
1105
1106 iterator->current = iterator->current->next;
1107 return true;
1108}
1109
1110FLAC__bool FLAC__metadata_iterator_prev(FLAC__MetaData_Iterator *iterator)
1111{
1112 FLAC__ASSERT(0 != iterator);
1113
1114 if(0 == iterator->current || 0 == iterator->current->prev)
1115 return false;
1116
1117 iterator->current = iterator->current->prev;
1118 return true;
1119}
1120
1121FLAC__MetaDataType FLAC__metadata_iterator_get_block_type(const FLAC__MetaData_Iterator *iterator)
1122{
1123 FLAC__ASSERT(0 != iterator);
1124 FLAC__ASSERT(0 != iterator->current);
Josh Coalson2745fe72002-05-11 05:31:56 +00001125 FLAC__ASSERT(0 != iterator->current->data);
Josh Coalsoncae58732002-05-04 17:30:32 +00001126
1127 return iterator->current->data->type;
1128}
1129
1130FLAC__StreamMetaData *FLAC__metadata_iterator_get_block(FLAC__MetaData_Iterator *iterator)
1131{
1132 FLAC__ASSERT(0 != iterator);
1133 FLAC__ASSERT(0 != iterator->current);
1134
1135 return iterator->current->data;
1136}
1137
Josh Coalson2745fe72002-05-11 05:31:56 +00001138FLAC__bool FLAC__metadata_iterator_set_block(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1139{
1140 return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block);
1141}
1142
Josh Coalsoncae58732002-05-04 17:30:32 +00001143FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__MetaData_Iterator *iterator, FLAC__bool replace_with_padding)
1144{
1145 FLAC__MetaData_Node *save;
1146
1147 FLAC__ASSERT(0 != iterator);
1148 FLAC__ASSERT(0 != iterator->current);
1149
1150 if(0 == iterator->current->prev) {
1151 FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO);
1152 return false;
1153 }
1154
1155 save = iterator->current->prev;
1156
1157 if(replace_with_padding) {
1158 FLAC__metadata_object_delete_data_(iterator->current->data);
1159 iterator->current->data->type = FLAC__METADATA_TYPE_PADDING;
1160 }
1161 else {
1162 chain_delete_node_(iterator->chain, iterator->current);
1163 }
1164
1165 iterator->current = save;
1166 return true;
1167}
1168
1169FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1170{
1171 FLAC__MetaData_Node *node;
1172
1173 FLAC__ASSERT(0 != iterator);
1174 FLAC__ASSERT(0 != iterator->current);
1175
1176 if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
1177 return false;
1178
1179 if(0 == iterator->current->prev) {
1180 FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO);
1181 return false;
1182 }
1183
1184 if(0 == (node = node_new_()))
1185 return false;
1186
1187 node->data = block;
1188 iterator_insert_node_(iterator, node);
1189 iterator->current = node;
1190 return true;
1191}
1192
1193FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1194{
1195 FLAC__MetaData_Node *node;
1196
1197 FLAC__ASSERT(0 != iterator);
1198 FLAC__ASSERT(0 != iterator->current);
1199
1200 if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
1201 return false;
1202
1203 if(0 == (node = node_new_()))
1204 return false;
1205
1206 node->data = block;
1207 iterator_insert_node_after_(iterator, node);
1208 iterator->current = node;
1209 return true;
1210}
1211
1212
1213/****************************************************************************
1214 *
1215 * Metadata object routines
1216 *
1217 ***************************************************************************/
1218
1219FLAC__StreamMetaData *FLAC__metadata_object_new(FLAC__MetaDataType type)
1220{
1221 FLAC__StreamMetaData *object = malloc(sizeof(FLAC__StreamMetaData));
1222 if(0 != object) {
1223 memset(object, 0, sizeof(FLAC__StreamMetaData));
1224 object->is_last = false;
1225 object->type = type;
1226 switch(type) {
1227 case FLAC__METADATA_TYPE_STREAMINFO:
1228 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
1229 break;
1230 case FLAC__METADATA_TYPE_PADDING:
1231 break;
1232 case FLAC__METADATA_TYPE_APPLICATION:
1233 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
1234 break;
1235 case FLAC__METADATA_TYPE_SEEKTABLE:
1236 break;
1237 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1238 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
1239 break;
1240 default:
1241 FLAC__ASSERT(0);
1242 }
1243 }
1244
1245 return object;
1246}
1247
1248static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
1249{
1250 if(0 == (*to = malloc(bytes)))
1251 return false;
1252 memcpy(*to, from, bytes);
1253 return true;
1254}
1255
1256static FLAC__bool copy_vcentry_(FLAC__StreamMetaData_VorbisComment_Entry *to, const FLAC__StreamMetaData_VorbisComment_Entry *from)
1257{
1258 to->length = from->length;
1259 if(0 == from->entry) {
1260 FLAC__ASSERT(0 == from->length);
1261 to->entry = 0;
1262 }
1263 else {
1264 if(0 == (to->entry = malloc(from->length)))
1265 return false;
1266 memcpy(to->entry, from->entry, from->length);
1267 }
1268 return true;
1269}
1270
1271FLAC__StreamMetaData *FLAC__metadata_object_copy(const FLAC__StreamMetaData *object)
1272{
1273 FLAC__StreamMetaData *to;
1274 unsigned i;
1275
1276 FLAC__ASSERT(0 != object);
1277
1278 if(0 != (to = FLAC__metadata_object_new(object->type))) {
1279 to->is_last = object->is_last;
1280 to->type = object->type;
1281 to->length = object->length;
1282 switch(to->type) {
1283 case FLAC__METADATA_TYPE_STREAMINFO:
1284 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetaData_StreamInfo));
1285 break;
1286 case FLAC__METADATA_TYPE_PADDING:
1287 break;
1288 case FLAC__METADATA_TYPE_APPLICATION:
1289 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
1290 if(0 == object->data.application.data)
1291 to->data.application.data = 0;
1292 else {
1293 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
1294 FLAC__metadata_object_delete(to);
1295 return 0;
1296 }
1297 }
1298 break;
1299 case FLAC__METADATA_TYPE_SEEKTABLE:
1300 to->data.seek_table.num_points = object->data.seek_table.num_points;
1301 if(0 == object->data.seek_table.points)
1302 to->data.seek_table.points = 0;
1303 else {
1304 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))) {
1305 FLAC__metadata_object_delete(to);
1306 return 0;
1307 }
1308 }
1309 break;
1310 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1311 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
1312 FLAC__metadata_object_delete(to);
1313 return 0;
1314 }
1315 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
1316 if(0 == (to->data.vorbis_comment.comments = malloc(object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry*)))) {
1317 FLAC__metadata_object_delete(to);
1318 return 0;
1319 }
1320 /* Need to do this to set the pointers inside the comments to 0.
1321 * In case of an error in the following loop, the object will be
1322 * deleted and we don't want the destructor freeing uninitialized
1323 * pointers.
1324 */
1325 memset(to->data.vorbis_comment.comments, 0, object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry*));
1326 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1327 if(!copy_vcentry_(to->data.vorbis_comment.comments+i, object->data.vorbis_comment.comments+i)) {
1328 FLAC__metadata_object_delete(to);
1329 return 0;
1330 }
1331 }
1332 break;
1333 default:
1334 FLAC__ASSERT(0);
1335 }
1336 }
1337
1338 return to;
1339}
1340
1341void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object)
1342{
1343 unsigned i;
1344
1345 FLAC__ASSERT(0 != object);
1346
1347 switch(object->type) {
1348 case FLAC__METADATA_TYPE_STREAMINFO:
1349 case FLAC__METADATA_TYPE_PADDING:
1350 break;
1351 case FLAC__METADATA_TYPE_APPLICATION:
1352 if(0 != object->data.application.data)
1353 free(object->data.application.data);
1354 break;
1355 case FLAC__METADATA_TYPE_SEEKTABLE:
1356 if(0 != object->data.seek_table.points)
1357 free(object->data.seek_table.points);
1358 break;
1359 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1360 if(0 != object->data.vorbis_comment.vendor_string.entry)
1361 free(object->data.vorbis_comment.vendor_string.entry);
1362 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1363 if(0 != object->data.vorbis_comment.comments[i].entry)
1364 free(object->data.vorbis_comment.comments[i].entry);
1365 }
Josh Coalsonfad2e612002-05-07 05:33:10 +00001366 if(0 != object->data.vorbis_comment.comments)
1367 free(object->data.vorbis_comment.comments);
Josh Coalsoncae58732002-05-04 17:30:32 +00001368 break;
1369 default:
1370 FLAC__ASSERT(0);
1371 }
1372}
1373
1374void FLAC__metadata_object_delete(FLAC__StreamMetaData *object)
1375{
1376 FLAC__metadata_object_delete_data_(object);
1377 free(object);
1378}
1379
Josh Coalsoncae58732002-05-04 17:30:32 +00001380/*@@@@ Allow setting pointer to 0 to free, or let length be 0 also. fix everywhere */
1381FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetaData *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
1382{
1383 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
1384
1385 if(0 != object->data.application.data)
1386 free(object->data.application.data);
1387
1388 if(copy) {
1389 if(!copy_bytes_(&object->data.application.data, data, length))
1390 return false;
1391 }
1392 else {
1393 object->data.application.data = data;
1394 }
1395 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
1396 return true;
1397}
1398
1399FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_new(unsigned num_points)
1400{
1401 FLAC__StreamMetaData_SeekPoint *object_array = malloc(num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
1402
1403 if(0 != object_array)
1404 memset(object_array, 0, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
1405
1406 return object_array;
1407}
1408
1409FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_copy(const FLAC__StreamMetaData_SeekPoint *object_array, unsigned num_points)
1410{
1411 FLAC__StreamMetaData_SeekPoint *return_array;
1412
1413 FLAC__ASSERT(0 != object_array);
1414
1415 return_array = FLAC__metadata_object_seekpoint_array_new(num_points);
1416
1417 if(0 != return_array)
1418 memcpy(return_array, object_array, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
1419
1420 return return_array;
1421}
1422
1423void FLAC__metadata_object_seekpoint_array_delete(FLAC__StreamMetaData_SeekPoint *object_array)
1424{
1425 FLAC__ASSERT(0 != object_array);
1426
1427 free(object_array);
1428}
1429
1430FLAC__bool FLAC__metadata_object_seekpoint_array_resize(FLAC__StreamMetaData_SeekPoint **object_array, unsigned old_num_points, unsigned new_num_points)
1431{
1432 FLAC__ASSERT(0 != object_array);
1433
1434 if(0 == *object_array) {
1435 FLAC__ASSERT(old_num_points == 0);
1436 return 0 != (*object_array = FLAC__metadata_object_seekpoint_array_new(new_num_points));
1437 }
1438 else {
1439 const unsigned old_size = old_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
1440 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
1441
1442 if(0 == (*object_array = realloc(*object_array, new_size)))
1443 return false;
1444
1445 if(new_size > old_size)
1446 memset(*object_array + old_num_points, 0, new_size - old_size);
1447
1448 return true;
1449 }
1450}
1451
1452FLAC__bool FLAC__metadata_object_seektable_set_points(FLAC__StreamMetaData *object, FLAC__StreamMetaData_SeekPoint *points, unsigned num_points, FLAC__bool copy)
1453{
1454 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
1455
1456 object->data.seek_table.num_points = num_points;
1457
1458 if(0 != object->data.seek_table.points)
1459 FLAC__metadata_object_seekpoint_array_delete(object->data.seek_table.points);
1460
1461 if(copy) {
1462 if(0 == (object->data.seek_table.points = FLAC__metadata_object_seekpoint_array_copy(points, num_points)))
1463 return false;
1464 }
1465 else {
1466 object->data.seek_table.points = points;
1467 }
1468 object->length = num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1469 return true;
1470}
1471
1472FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_new(unsigned num_comments)
1473{
1474 FLAC__StreamMetaData_VorbisComment_Entry *object_array = malloc(num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
1475
1476 if(0 != object_array)
1477 memset(object_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
1478
1479 return object_array;
1480}
1481
1482FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_copy(const FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
1483{
1484 FLAC__StreamMetaData_VorbisComment_Entry *return_array;
1485
1486 FLAC__ASSERT(0 != object_array);
1487
1488 return_array = FLAC__metadata_object_vorbiscomment_entry_array_new(num_comments);
1489
1490 if(0 != return_array) {
1491 unsigned i;
1492 for(i = 0; i < num_comments; i++) {
1493 return_array[i].length = object_array[i].length;
1494 if(!copy_bytes_(&(return_array[i].entry), object_array[i].entry, object_array[i].length)) {
1495 FLAC__metadata_object_vorbiscomment_entry_array_delete(return_array, num_comments);
1496 return 0;
1497 }
1498 }
1499 }
1500
1501 return return_array;
1502}
1503
1504void FLAC__metadata_object_vorbiscomment_entry_array_delete(FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
1505{
1506 unsigned i;
1507
1508 FLAC__ASSERT(0 != object_array);
1509
1510 for(i = 0; i < num_comments; i++)
1511 if(0 != object_array[i].entry)
1512 free(object_array[i].entry);
1513
1514 free(object_array);
1515}
1516
1517FLAC__bool FLAC__metadata_object_vorbiscomment_entry_array_resize(FLAC__StreamMetaData_VorbisComment_Entry **object_array, unsigned old_num_comments, unsigned new_num_comments)
1518{
1519 FLAC__ASSERT(0 != object_array);
1520
1521 if(0 == *object_array) {
1522 FLAC__ASSERT(old_num_comments == 0);
1523 return 0 != (*object_array = FLAC__metadata_object_vorbiscomment_entry_array_new(new_num_comments));
1524 }
1525 else {
1526 const unsigned old_size = old_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
1527 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
1528
1529 /* if shrinking, free the truncated entries */
1530 if(new_num_comments < old_num_comments) {
1531 unsigned i;
1532 for(i = new_num_comments; i < old_num_comments; i++)
1533 free((*object_array)[i].entry);
1534 }
1535
1536 if(0 == (*object_array = realloc(*object_array, new_size)))
1537 return false;
1538
1539 /* if growing, zero all the length/pointers of new elements */
1540 if(new_size > old_size)
1541 memset(*object_array + old_num_comments, 0, new_size - old_size);
1542
1543 return true;
1544 }
1545}
1546
1547FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetaData *object, FLAC__byte *entry, unsigned length, FLAC__bool copy)
1548{
1549 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1550
1551 if(0 != object->data.vorbis_comment.vendor_string.entry)
1552 free(object->data.vorbis_comment.vendor_string.entry);
1553
1554 object->length -= object->data.vorbis_comment.vendor_string.length;
1555
1556 if(copy) {
1557 if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, entry, length)) {
1558 object->data.vorbis_comment.vendor_string.length = 0;
1559 return false;
1560 }
1561 }
1562 else {
1563 object->data.vorbis_comment.vendor_string.entry = entry;
1564 }
1565
1566 object->data.vorbis_comment.vendor_string.length = length;
1567 object->length += object->data.vorbis_comment.vendor_string.length;
1568
1569 return true;
1570}
1571
1572FLAC__bool FLAC__metadata_object_vorbiscomment_set_comments(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *comments, unsigned num_comments, FLAC__bool copy)
1573{
1574 unsigned i;
1575
1576 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1577
1578 object->data.vorbis_comment.num_comments = num_comments;
1579
1580 if(0 != object->data.vorbis_comment.comments)
1581 FLAC__metadata_object_vorbiscomment_entry_array_delete(object->data.vorbis_comment.comments, num_comments);
1582
1583 if(copy) {
1584 if(0 == (object->data.vorbis_comment.comments = FLAC__metadata_object_vorbiscomment_entry_array_copy(comments, num_comments)))
1585 return false;
1586 }
1587 else {
1588 object->data.vorbis_comment.comments = comments;
1589 }
1590 object->length = num_comments * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1591
1592 /* calculate the new length */
1593 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
1594 object->length += object->data.vorbis_comment.vendor_string.length;
1595 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
1596 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1597 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
1598 object->length += object->data.vorbis_comment.comments[i].length;
1599 }
1600
1601 return true;
1602}
1603
1604
1605/****************************************************************************
1606 *
1607 * Local function definitions
1608 *
1609 ***************************************************************************/
1610
1611void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes)
1612{
1613 unsigned i;
1614
1615 b += bytes;
1616
1617 for(i = 0; i < bytes; i++) {
1618 *(--b) = val & 0xff;
1619 val >>= 8;
1620 }
1621}
1622
1623void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes)
1624{
1625 unsigned i;
1626
1627 for(i = 0; i < bytes; i++) {
1628 *(b++) = val & 0xff;
1629 val >>= 8;
1630 }
1631}
1632
1633void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes)
1634{
1635 unsigned i;
1636
1637 b += bytes;
1638
1639 for(i = 0; i < bytes; i++) {
1640 *(--b) = val & 0xff;
1641 val >>= 8;
1642 }
1643}
1644
1645FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes)
1646{
1647 FLAC__uint32 ret = 0;
1648 unsigned i;
1649
1650 for(i = 0; i < bytes; i++)
1651 ret = (ret << 8) | (FLAC__uint32)(*b++);
1652
1653 return ret;
1654}
1655
1656FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes)
1657{
1658 FLAC__uint32 ret = 0;
1659 unsigned i;
1660
1661 b += bytes;
1662
1663 for(i = 0; i < bytes; i++)
1664 ret = (ret << 8) | (FLAC__uint32)(*--b);
1665
1666 return ret;
1667}
1668
1669FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes)
1670{
1671 FLAC__uint64 ret = 0;
1672 unsigned i;
1673
1674 for(i = 0; i < bytes; i++)
1675 ret = (ret << 8) | (FLAC__uint64)(*b++);
1676
1677 return ret;
1678}
1679
1680FLAC__bool read_metadata_block_header_(FLAC__MetaData_SimpleIterator *iterator)
1681{
1682 FLAC__byte raw_header[4]; /*@@@ 4 = MAGIC NUMBER for metadata block header bytes */
1683
1684 FLAC__ASSERT(0 != iterator);
1685 FLAC__ASSERT(0 != iterator->file);
1686
1687 if(fread(raw_header, 1, 4, iterator->file) != 4) { /*@@@ 4 = MAGIC NUMBER for metadata block header bytes */
1688 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1689 return false;
1690 }
1691
1692 iterator->is_last = raw_header[0] & 0x80? true : false;
1693 iterator->type = (FLAC__MetaDataType)(raw_header[0] & 0x7f);
1694 iterator->length = unpack_uint32_(raw_header + 1, 3);
1695
1696 return true;
1697}
1698
1699FLAC__bool read_metadata_block_data_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block)
1700{
1701 FLAC__ASSERT(0 != iterator);
1702 FLAC__ASSERT(0 != iterator->file);
1703
1704 switch(block->type) {
1705 case FLAC__METADATA_TYPE_STREAMINFO:
1706 iterator->status = read_metadata_block_data_streaminfo_(iterator->file, &block->data.stream_info);
1707 break;
1708 case FLAC__METADATA_TYPE_PADDING:
1709 iterator->status = read_metadata_block_data_padding_(iterator->file, &block->data.padding, block->length);
1710 break;
1711 case FLAC__METADATA_TYPE_APPLICATION:
1712 iterator->status = read_metadata_block_data_application_(iterator->file, &block->data.application, block->length);
1713 break;
1714 case FLAC__METADATA_TYPE_SEEKTABLE:
1715 iterator->status = read_metadata_block_data_seektable_(iterator->file, &block->data.seek_table, block->length);
1716 break;
1717 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1718 iterator->status = read_metadata_block_data_vorbis_comment_(iterator->file, &block->data.vorbis_comment);
1719 break;
1720 default:
1721 FLAC__ASSERT(0);
1722 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR;
1723 }
1724
1725 return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK);
1726}
1727
1728FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_streaminfo_(FILE *file, FLAC__StreamMetaData_StreamInfo *block)
1729{
1730 FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b;
1731
1732 FLAC__ASSERT(0 != file);
1733
1734 if(fread(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, file) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
1735 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1736
1737 b = buffer;
1738
1739 /* @@@ we are using hardcoded numbers for simplicity but we should
1740 * probably eventually write a bit-level unpacker and use the
1741 * _STREAMINFO_ constants.
1742 */
1743 block->min_blocksize = unpack_uint32_(b, 2); b += 2;
1744 block->max_blocksize = unpack_uint32_(b, 2); b += 2;
1745 block->min_framesize = unpack_uint32_(b, 3); b += 3;
1746 block->max_framesize = unpack_uint32_(b, 3); b += 3;
1747 block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4);
1748 block->channels = (unsigned)((b[2] & 0x0e) >> 1) + 1;
1749 block->bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 1) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1;
1750 block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4);
1751 memcpy(block->md5sum, b+8, 16);
1752
1753 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1754}
1755
1756
1757FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_padding_(FILE *file, FLAC__StreamMetaData_Padding *block, unsigned block_length)
1758{
1759 FLAC__ASSERT(0 != file);
1760
1761 (void)block; /* nothing to do; we don't care about reading the padding bytes */
1762
1763 if(0 != fseek(file, block_length, SEEK_CUR))
1764 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
1765
1766 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1767}
1768
1769FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_application_(FILE *file, FLAC__StreamMetaData_Application *block, unsigned block_length)
1770{
1771 const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
1772
1773 FLAC__ASSERT(0 != file);
1774
1775 if(fread(block->id, 1, id_bytes, file) != id_bytes)
1776 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1777
1778 block_length -= id_bytes;
1779
1780 if(0 == (block->data = malloc(block_length)))
1781 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1782
1783 if(fread(block->data, 1, block_length, file) != block_length)
1784 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1785
1786 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1787}
1788
1789FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_seektable_(FILE *file, FLAC__StreamMetaData_SeekTable *block, unsigned block_length)
1790{
1791 unsigned i;
1792 FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
1793
1794 FLAC__ASSERT(0 != file);
1795 FLAC__ASSERT(block_length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH == 0);
1796
1797 block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1798
1799 if(0 == (block->points = malloc(block->num_points * sizeof(FLAC__StreamMetaData_SeekPoint))))
1800 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1801
1802 for(i = 0; i < block->num_points; i++) {
1803 if(fread(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, file) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
1804 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1805 /*@@@ some MAGIC NUMBERs here */
1806 block->points[i].sample_number = unpack_uint64_(buffer, 8);
1807 block->points[i].stream_offset = unpack_uint64_(buffer+8, 8);
1808 block->points[i].frame_samples = unpack_uint32_(buffer+16, 2);
1809 }
1810
1811 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1812}
1813
1814FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_(FILE *file, FLAC__StreamMetaData_VorbisComment_Entry *entry)
1815{
1816 const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
1817 FLAC__byte buffer[FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8];
1818
1819 FLAC__ASSERT(0 != file);
1820
1821 if(fread(buffer, 1, entry_length_len, file) != entry_length_len)
1822 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1823 entry->length = unpack_uint32_little_endian_(buffer, 4);
1824
1825 if(0 == (entry->entry = malloc(entry->length)))
1826 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1827
1828 if(fread(entry->entry, 1, entry->length, file) != entry->length)
1829 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1830
1831 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1832}
1833
1834FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_(FILE *file, FLAC__StreamMetaData_VorbisComment *block)
1835{
1836 unsigned i;
1837 FLAC__MetaData_SimpleIteratorStatus status;
1838 const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
1839 FLAC__byte buffer[FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8];
1840
1841 FLAC__ASSERT(0 != file);
1842
1843 if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_(file, &(block->vendor_string))))
1844 return status;
1845
1846 if(fread(buffer, 1, num_comments_len, file) != num_comments_len)
1847 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1848 block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len);
1849
1850 if(0 == (block->comments = malloc(block->num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry))))
1851 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1852
1853 for(i = 0; i < block->num_comments; i++) {
1854 if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_(file, block->comments + i)))
1855 return status;
1856 }
1857
1858 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1859}
1860
1861FLAC__bool write_metadata_block_header_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block)
1862{
1863 FLAC__byte buffer[4];
1864
1865 FLAC__ASSERT(0 != file);
1866 FLAC__ASSERT(0 != status);
1867 FLAC__ASSERT(block->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN));
1868
1869 buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type;
1870 pack_uint32_(block->length, buffer + 1, 3);
1871
1872 if(fwrite(buffer, 1, 4, file) != 4) {
1873 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1874 return false;
1875 }
1876
1877 return true;
1878}
1879
1880FLAC__bool write_metadata_block_data_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block)
1881{
1882 FLAC__ASSERT(0 != file);
1883 FLAC__ASSERT(0 != status);
1884
1885 switch(block->type) {
1886 case FLAC__METADATA_TYPE_STREAMINFO:
1887 *status = write_metadata_block_data_streaminfo_(file, &block->data.stream_info);
1888 break;
1889 case FLAC__METADATA_TYPE_PADDING:
1890 *status = write_metadata_block_data_padding_(file, &block->data.padding, block->length);
1891 break;
1892 case FLAC__METADATA_TYPE_APPLICATION:
1893 *status = write_metadata_block_data_application_(file, &block->data.application, block->length);
1894 break;
1895 case FLAC__METADATA_TYPE_SEEKTABLE:
1896 *status = write_metadata_block_data_seektable_(file, &block->data.seek_table);
1897 break;
1898 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1899 *status = write_metadata_block_data_vorbis_comment_(file, &block->data.vorbis_comment);
1900 break;
1901 default:
1902 FLAC__ASSERT(0);
1903 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR;
1904 }
1905 return (*status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK);
1906}
1907
1908FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_streaminfo_(FILE *file, const FLAC__StreamMetaData_StreamInfo *block)
1909{
1910 FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
1911 const unsigned channels1 = block->channels - 1;
1912 const unsigned bps1 = block->bits_per_sample - 1;
1913
1914 FLAC__ASSERT(0 != file);
1915
1916 /* @@@ we are using hardcoded numbers for simplicity but we should
1917 * probably eventually write a bit-level packer and use the
1918 * _STREAMINFO_ constants.
1919 */
1920 pack_uint32_(block->min_blocksize, buffer, 2);
1921 pack_uint32_(block->max_blocksize, buffer+2, 2);
1922 pack_uint32_(block->min_framesize, buffer+4, 3);
1923 pack_uint32_(block->max_framesize, buffer+7, 3);
1924 buffer[10] = (block->sample_rate >> 12) & 0xff;
1925 buffer[11] = (block->sample_rate >> 4) & 0xff;
1926 buffer[12] = ((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4);
1927 buffer[13] = ((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f);
1928 pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4);
1929 memcpy(buffer+18, block->md5sum, 16);
1930
1931 if(fwrite(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, file) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
1932 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1933
1934 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1935}
1936
1937FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_padding_(FILE *file, const FLAC__StreamMetaData_Padding *block, unsigned block_length)
1938{
1939 unsigned i, n = block_length;
1940 FLAC__byte buffer[1024];
1941
1942 FLAC__ASSERT(0 != file);
1943
1944 (void)block;
1945
1946 memset(buffer, 0, 1024);
1947
1948 for(i = 0; i < n/1024; i++)
1949 if(fwrite(buffer, 1, 1024, file) != 1024)
1950 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1951
1952 n %= 1024;
1953
1954 if(fwrite(buffer, 1, n, file) != n)
1955 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1956
1957 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1958}
1959
1960FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_application_(FILE *file, const FLAC__StreamMetaData_Application *block, unsigned block_length)
1961{
1962 const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
1963
1964 FLAC__ASSERT(0 != file);
1965
1966 if(fwrite(block->id, 1, id_bytes, file) != id_bytes)
1967 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1968
1969 block_length -= id_bytes;
1970
1971 if(fwrite(block->data, 1, block_length, file) != block_length)
1972 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1973
1974 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1975}
1976
1977FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_seektable_(FILE *file, const FLAC__StreamMetaData_SeekTable *block)
1978{
1979 unsigned i;
1980 FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
1981
1982 FLAC__ASSERT(0 != file);
1983
1984 for(i = 0; i < block->num_points; i++) {
1985 /*@@@ some MAGIC NUMBERs here */
1986 pack_uint64_(block->points[i].sample_number, buffer, 8);
1987 pack_uint64_(block->points[i].stream_offset, buffer+8, 8);
1988 pack_uint32_(block->points[i].frame_samples, buffer+16, 2);
1989 if(fwrite(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, file) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
1990 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1991 }
1992
1993 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1994}
1995
1996FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_vorbis_comment_(FILE *file, const FLAC__StreamMetaData_VorbisComment *block)
1997{
1998 unsigned i;
1999 const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
2000 const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
2001 FLAC__byte buffer[max(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN, FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8];
2002
2003 FLAC__ASSERT(0 != file);
2004
2005 pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len);
2006 if(fwrite(buffer, 1, entry_length_len, file) != entry_length_len)
2007 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2008 if(fwrite(block->vendor_string.entry, 1, block->vendor_string.length, file) != block->vendor_string.length)
2009 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2010
2011 pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len);
2012 if(fwrite(buffer, 1, num_comments_len, file) != num_comments_len)
2013 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2014
2015 for(i = 0; i < block->num_comments; i++) {
2016 pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len);
2017 if(fwrite(buffer, 1, entry_length_len, file) != entry_length_len)
2018 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2019 if(fwrite(block->comments[i].entry, 1, block->comments[i].length, file) != block->comments[i].length)
2020 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2021 }
2022
2023 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
2024}
2025
2026FLAC__bool write_metadata_block_stationary_(FLAC__MetaData_SimpleIterator *iterator, const FLAC__StreamMetaData *block)
2027{
2028 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2029 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2030 return false;
2031 }
2032
2033 if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
2034 return false;
2035
2036 if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
2037 return false;
2038
2039 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2040 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2041 return false;
2042 }
2043
2044 return read_metadata_block_header_(iterator);
2045}
2046
2047FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, unsigned padding_length, FLAC__bool padding_is_last)
2048{
2049 FLAC__StreamMetaData *padding;
2050
2051 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2052 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2053 return false;
2054 }
2055
2056 block->is_last = false;
2057
2058 if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
2059 return false;
2060
2061 if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
2062 return false;
2063
2064 if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)))
2065 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2066
2067 padding->is_last = padding_is_last;
2068 padding->length = padding_length;
2069
2070 if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) {
2071 FLAC__metadata_object_delete(padding);
2072 return false;
2073 }
2074
2075 if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) {
2076 FLAC__metadata_object_delete(padding);
2077 return false;
2078 }
2079
2080 FLAC__metadata_object_delete(padding);
2081
2082 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2083 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2084 return false;
2085 }
2086
2087 return read_metadata_block_header_(iterator);
2088}
2089
2090FLAC__bool rewrite_whole_file_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool append)
2091{
2092 FILE *tempfile;
2093 char *tempfilename;
Josh Coalson6b02a752002-05-10 06:42:02 +00002094 int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */
2095 long fixup_is_last_flag_offset = -1;
Josh Coalsoncae58732002-05-04 17:30:32 +00002096
Josh Coalson51ee4652002-05-09 05:46:23 +00002097 FLAC__ASSERT(0 != block || append == false);
2098
Josh Coalson6b02a752002-05-10 06:42:02 +00002099 if(iterator->is_last) {
2100 if(append) {
2101 fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */
2102 fixup_is_last_flag_offset = iterator->offset[iterator->depth];
2103 }
2104 else if(0 == block) {
2105 simple_iterator_push_(iterator);
2106 if(!FLAC__metadata_simple_iterator_prev(iterator)) {
2107 (void)simple_iterator_pop_(iterator);
2108 return false;
2109 }
2110 fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */
2111 fixup_is_last_flag_offset = iterator->offset[iterator->depth];
2112 if(!simple_iterator_pop_(iterator))
2113 return false;
2114 }
2115 }
2116
Josh Coalsoncae58732002-05-04 17:30:32 +00002117 if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append))
2118 return false;
2119
2120 if(0 != block) {
2121 if(!write_metadata_block_header_(tempfile, &iterator->status, block)) {
2122 cleanup_tempfile_(&tempfile, &tempfilename);
2123 return false;
2124 }
2125
2126 if(!write_metadata_block_data_(tempfile, &iterator->status, block)) {
2127 cleanup_tempfile_(&tempfile, &tempfilename);
2128 return false;
2129 }
2130 }
2131
Josh Coalson6b02a752002-05-10 06:42:02 +00002132 if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0))
Josh Coalsoncae58732002-05-04 17:30:32 +00002133 return false;
2134
Josh Coalson51ee4652002-05-09 05:46:23 +00002135 if(append)
2136 return FLAC__metadata_simple_iterator_next(iterator);
2137
Josh Coalsoncae58732002-05-04 17:30:32 +00002138 return true;
2139}
2140
2141FLAC__bool chain_rewrite_chain_(FLAC__MetaData_Chain *chain)
2142{
2143 FILE *f;
2144 FLAC__MetaData_Node *node;
2145 FLAC__MetaData_SimpleIteratorStatus status;
2146
2147 FLAC__ASSERT(0 != chain);
2148 FLAC__ASSERT(0 != chain->filename);
2149 FLAC__ASSERT(0 != chain->head);
2150
2151 if(0 == (f = fopen(chain->filename, "r+b"))) {
2152 chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2153 return false;
2154 }
2155 if(0 != fseek(f, chain->first_offset, SEEK_SET)) {
2156 chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2157 return false;
2158 }
2159
2160 for(node = chain->head; node; node = node->next) {
2161 if(!write_metadata_block_header_(f, &status, node->data)) {
2162 chain->status = get_equivalent_status_(status);
2163 return false;
2164 }
2165 if(!write_metadata_block_data_(f, &status, node->data)) {
2166 chain->status = get_equivalent_status_(status);
2167 return false;
2168 }
2169 }
2170
2171 /*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
2172
2173 (void)fclose(f);
2174
2175 return true;
2176}
2177
2178FLAC__bool chain_rewrite_file_(FLAC__MetaData_Chain *chain, const char *tempfile_path_prefix)
2179{
2180 FILE *f, *tempfile;
2181 char *tempfilename;
2182 FLAC__MetaData_SimpleIteratorStatus status;
2183 const FLAC__MetaData_Node *node;
2184
2185 FLAC__ASSERT(0 != chain);
2186 FLAC__ASSERT(0 != chain->filename);
2187 FLAC__ASSERT(0 != chain->head);
2188
2189 /* copy the file prefix (data up to first metadata block */
2190 if(0 == (f = fopen(chain->filename, "rb"))) {
2191 chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2192 return false;
2193 }
2194 if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) {
2195 chain->status = get_equivalent_status_(status);
2196 cleanup_tempfile_(&tempfile, &tempfilename);
2197 return false;
2198 }
2199 if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) {
2200 chain->status = get_equivalent_status_(status);
2201 cleanup_tempfile_(&tempfile, &tempfilename);
2202 return false;
2203 }
2204
2205 /* write the metadata */
2206 for(node = chain->head; node; node = node->next) {
2207 if(!write_metadata_block_header_(tempfile, &status, node->data)) {
2208 chain->status = get_equivalent_status_(status);
2209 return false;
2210 }
2211 if(!write_metadata_block_data_(tempfile, &status, node->data)) {
2212 chain->status = get_equivalent_status_(status);
2213 return false;
2214 }
2215 }
2216 /*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
2217
2218 /* copy the file postfix (everything after the metadata) */
2219 if(0 != fseek(f, chain->last_offset, SEEK_SET)) {
2220 cleanup_tempfile_(&tempfile, &tempfilename);
2221 chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2222 return false;
2223 }
2224 if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) {
2225 cleanup_tempfile_(&tempfile, &tempfilename);
2226 chain->status = get_equivalent_status_(status);
2227 return false;
2228 }
2229
2230 /* move the tempfile on top of the original */
2231 (void)fclose(f);
2232 if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status))
2233 return false;
2234
2235 return true;
2236}
2237
2238void simple_iterator_push_(FLAC__MetaData_SimpleIterator *iterator)
2239{
2240 FLAC__ASSERT(iterator->depth+1 < SIMPLE_ITERATOR_MAX_PUSH_DEPTH);
2241 iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth];
2242 iterator->depth++;
2243}
2244
2245FLAC__bool simple_iterator_pop_(FLAC__MetaData_SimpleIterator *iterator)
2246{
2247 FLAC__ASSERT(iterator->depth > 0);
2248 iterator->depth--;
2249 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2250 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2251 return false;
2252 }
2253
2254 return read_metadata_block_header_(iterator);
2255}
2256
2257unsigned seek_to_first_metadata_block_(FILE *f)
2258{
2259 FLAC__byte signature[4]; /*@@@ 4 = MAGIC NUMBER for signature bytes */
2260
2261 FLAC__ASSERT(0 != f);
2262
2263 /*@@@@ skip id3v2, change comment about id3v2 in metadata.h*/
2264
2265 /* search for the fLaC signature */
2266 if(fread(signature, 1, 4, f) == 4) { /*@@@ 4 = MAGIC NUMBER for signature bytes */
2267 if(0 == memcmp(FLAC__STREAM_SYNC_STRING, signature, 4)) {
2268 return 0;
2269 }
2270 else {
2271 return 2;
2272 }
2273 }
2274 else {
2275 return 1;
2276 }
2277}
2278
2279FLAC__bool simple_iterator_copy_file_prefix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append)
2280{
2281 const long offset_end = append? iterator->offset[iterator->depth] + 4 + (long)iterator->length : iterator->offset[iterator->depth]; /*@@@ MAGIC NUMBER 4 = metadata block header bytes */
2282
2283 if(0 != fseek(iterator->file, 0, SEEK_SET)) {
2284 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2285 return false;
2286 }
2287 if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) {
2288 cleanup_tempfile_(tempfile, tempfilename);
2289 return false;
2290 }
2291 if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) {
2292 cleanup_tempfile_(tempfile, tempfilename);
2293 return false;
2294 }
2295
2296 return true;
2297}
2298
Josh Coalson6b02a752002-05-10 06:42:02 +00002299FLAC__bool simple_iterator_copy_file_postfix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, long fixup_is_last_flag_offset, FLAC__bool backup)
Josh Coalsoncae58732002-05-04 17:30:32 +00002300{
2301 long save_offset = iterator->offset[iterator->depth]; /*@@@ 2G limit */
2302 FLAC__ASSERT(0 != *tempfile);
2303
2304 /*@@@ MAGIC NUMBER 4 = metadata header bytes */
2305 if(0 != fseek(iterator->file, save_offset + 4 + iterator->length, SEEK_SET)) {
2306 cleanup_tempfile_(tempfile, tempfilename);
2307 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2308 return false;
2309 }
2310 if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) {
2311 cleanup_tempfile_(tempfile, tempfilename);
2312 return false;
2313 }
2314
Josh Coalson6b02a752002-05-10 06:42:02 +00002315 if(fixup_is_last_code != 0) {
2316 /*
2317 * if code == 1, it means a block was appended to the end so
2318 * we have to clear the is_last flag of the previous block
2319 * if code == -1, it means the last block was deleted so
2320 * we have to set the is_last flag of the previous block
2321 */
Josh Coalson51ee4652002-05-09 05:46:23 +00002322 /*@@@ MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */
Josh Coalsoncae58732002-05-04 17:30:32 +00002323 FLAC__byte x;
2324 if(0 != fseek(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
2325 cleanup_tempfile_(tempfile, tempfilename);
2326 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2327 return false;
2328 }
2329 if(fread(&x, 1, 1, *tempfile) != 1) {
2330 cleanup_tempfile_(tempfile, tempfilename);
2331 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2332 return false;
2333 }
Josh Coalson6b02a752002-05-10 06:42:02 +00002334 if(fixup_is_last_code > 0) {
2335 FLAC__ASSERT(x & 0x80);
2336 x &= 0x7f;
2337 }
2338 else {
2339 FLAC__ASSERT(!(x & 0x80));
2340 x |= 0x80;
2341 }
Josh Coalsoncae58732002-05-04 17:30:32 +00002342 if(0 != fseek(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
2343 cleanup_tempfile_(tempfile, tempfilename);
2344 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2345 return false;
2346 }
2347 if(fwrite(&x, 1, 1, *tempfile) != 1) {
2348 cleanup_tempfile_(tempfile, tempfilename);
2349 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2350 return false;
2351 }
2352 }
2353
2354 (void)fclose(iterator->file);
2355
2356 if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status))
2357 return false;
2358
2359 if(iterator->has_stats)
2360 set_file_stats_(iterator->filename, &iterator->stats);
2361
2362 if(!simple_iterator_prime_input_(iterator))
2363 return false;
2364 if(backup) {
2365 while(iterator->offset[iterator->depth] + 4 + (long)iterator->length < save_offset)
2366 if(!FLAC__metadata_simple_iterator_next(iterator))
2367 return false;
2368 return true;
2369 }
2370 else {
2371 /* move the iterator to it's original block faster by faking a push, then doing a pop_ */
2372 FLAC__ASSERT(iterator->depth == 0);
2373 iterator->offset[0] = save_offset;
2374 iterator->depth++;
2375 return simple_iterator_pop_(iterator);
2376 }
2377}
2378
2379FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, unsigned bytes/*@@@ 4G limit*/, FLAC__MetaData_SimpleIteratorStatus *status)
2380{
2381 FLAC__byte buffer[8192];
2382 unsigned n;
2383
2384 while(bytes > 0) {
2385 n = min(sizeof(buffer), bytes);
2386 if(fread(buffer, 1, n, file) != n) {
2387 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2388 return false;
2389 }
2390 if(fwrite(buffer, 1, n, tempfile) != n) {
2391 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2392 return false;
2393 }
2394 bytes -= n;
2395 }
2396
2397 return true;
2398}
2399
2400FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__MetaData_SimpleIteratorStatus *status)
2401{
2402 FLAC__byte buffer[8192];
2403 size_t n;
2404
2405 while(!feof(file)) {
2406 n = fread(buffer, 1, sizeof(buffer), file);
2407 if(n == 0 && !feof(file)) {
2408 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2409 return false;
2410 }
2411 if(n > 0 && fwrite(buffer, 1, n, tempfile) != n) {
2412 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2413 return false;
2414 }
2415 }
2416
2417 return true;
2418}
2419
2420FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status)
2421{
2422 static const char *tempfile_suffix = ".metadata_edit";
2423 if(0 == tempfile_path_prefix) {
2424 if(0 == (*tempfilename = malloc(strlen(filename) + strlen(tempfile_suffix) + 1))) {
2425 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2426 return false;
2427 }
2428 strcpy(*tempfilename, filename);
2429 strcat(*tempfilename, tempfile_suffix);
2430 }
2431 else {
2432 const char *p = strrchr(filename, '/');
2433 if(0 == p)
2434 p = filename;
2435 else
2436 p++;
2437
2438 if(0 == (*tempfilename = malloc(strlen(tempfile_path_prefix) + 1 + strlen(p) + strlen(tempfile_suffix) + 1))) {
2439 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2440 return false;
2441 }
2442 strcpy(*tempfilename, tempfile_path_prefix);
2443 strcat(*tempfilename, "/");
2444 strcat(*tempfilename, p);
2445 strcat(*tempfilename, tempfile_suffix);
2446 }
2447
Josh Coalson51ee4652002-05-09 05:46:23 +00002448 if(0 == (*tempfile = fopen(*tempfilename, "w+b"))) {
Josh Coalsoncae58732002-05-04 17:30:32 +00002449 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
2450 return false;
2451 }
2452
2453 return true;
2454}
2455
2456FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status)
2457{
2458 FLAC__ASSERT(0 != filename);
2459 FLAC__ASSERT(0 != tempfile);
2460 FLAC__ASSERT(0 != *tempfile);
2461 FLAC__ASSERT(0 != tempfilename);
2462 FLAC__ASSERT(0 != *tempfilename);
2463 FLAC__ASSERT(0 != status);
2464
2465 (void)fclose(*tempfile);
2466 *tempfile = 0;
2467 /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just rename(): */
2468 if(0 != rename(*tempfilename, filename)) {
2469 cleanup_tempfile_(tempfile, tempfilename);
2470 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR;
2471 return false;
2472 }
2473
2474 cleanup_tempfile_(tempfile, tempfilename);
2475
2476 return true;
2477}
2478
2479void cleanup_tempfile_(FILE **tempfile, char **tempfilename)
2480{
2481 if(0 != *tempfile) {
2482 (void)fclose(*tempfile);
2483 *tempfile = 0;
2484 }
2485
2486 if(0 != *tempfilename) {
2487 (void)unlink(*tempfilename);
2488 free(*tempfilename);
2489 *tempfilename = 0;
2490 }
2491}
2492
2493FLAC__bool get_file_stats_(const char *filename, struct stat *stats)
2494{
2495 FLAC__ASSERT(0 != filename);
2496 FLAC__ASSERT(0 != stats);
2497 return (0 == stat(filename, stats));
2498}
2499
2500void set_file_stats_(const char *filename, struct stat *stats)
2501{
2502 struct utimbuf srctime;
2503
2504 FLAC__ASSERT(0 != filename);
2505 FLAC__ASSERT(0 != stats);
2506
2507 srctime.actime = stats->st_atime;
2508 srctime.modtime = stats->st_mtime;
2509 (void)chmod(filename, stats->st_mode);
2510 (void)utime(filename, &srctime);
2511#if !defined _MSC_VER && !defined __MINGW32__
2512 (void)chown(filename, stats->st_uid, -1);
2513 (void)chown(filename, -1, stats->st_gid);
2514#endif
2515}
2516
2517FLAC__MetaData_ChainStatus get_equivalent_status_(FLAC__MetaData_SimpleIteratorStatus status)
2518{
2519 switch(status) {
2520 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK:
2521 return FLAC__METADATA_CHAIN_STATUS_OK;
2522 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
2523 return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT;
2524 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
2525 return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2526 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
2527 return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE;
2528 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE:
2529 return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE;
2530 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR:
2531 return FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
2532 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR:
2533 return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2534 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR:
2535 return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
2536 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR:
2537 return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR;
2538 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR:
2539 return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR;
2540 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR:
2541 return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
2542 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR:
2543 default:
2544 return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
2545 }
2546}