diff --git a/src/flac/decode.c b/src/flac/decode.c index 1847c59b..67aff018 100644 --- a/src/flac/decode.c +++ b/src/flac/decode.c @@ -70,6 +70,7 @@ typedef struct { utils__CueSpecification *cue_specification; const char *inbasefilename; + const char *infilename; const char *outfilename; FLAC__uint64 samples_processed; @@ -98,6 +99,7 @@ typedef struct { FILE *fout; foreign_metadata_t *foreign_metadata; /* NULL unless --keep-foreign-metadata requested */ + off_t fm_offset1, fm_offset2, fm_offset3; } DecoderSession; @@ -115,6 +117,8 @@ static int DecoderSession_finish_ok(DecoderSession *d); static int DecoderSession_finish_error(DecoderSession *d); static FLAC__bool canonicalize_until_specification(utils__SkipUntilSpecification *spec, const char *inbasefilename, unsigned sample_rate, FLAC__uint64 skip, FLAC__uint64 total_samples_in_input); static FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uint64 samples); +static FLAC__bool write_riff_wave_fmt_chunk(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask); +static FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, unsigned bps, unsigned channels, unsigned sample_rate); static FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val); static FLAC__bool write_little_endian_uint32(FILE *f, FLAC__uint32 val); static FLAC__bool write_big_endian_uint16(FILE *f, FLAC__uint16 val); @@ -291,6 +295,7 @@ FLAC__bool DecoderSession_construct(DecoderSession *d, FLAC__bool is_ogg, FLAC__ d->cue_specification = cue_specification; d->inbasefilename = grabbag__file_get_basename(infilename); + d->infilename = infilename; d->outfilename = outfilename; d->samples_processed = 0; @@ -354,11 +359,13 @@ FLAC__bool DecoderSession_init_decoder(DecoderSession *decoder_session, const ch is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; - if(decoder_session->foreign_metadata) { - const char *error; - if(!flac__foreign_metadata_read_from_flac(decoder_session->foreign_metadata, infilename, &error)) { - flac__utils_printf(stderr, 1, "%s: ERROR reading foreign metadata: %s\n", decoder_session->inbasefilename, error); - return false; + if(!decoder_session->analysis_mode && !decoder_session->test_only && (decoder_session->is_wave_out || decoder_session->is_aiff_out)) { + if(decoder_session->foreign_metadata) { + const char *error; + if(!flac__foreign_metadata_read_from_flac(decoder_session->foreign_metadata, infilename, &error)) { + flac__utils_printf(stderr, 1, "%s: ERROR reading foreign metadata: %s\n", decoder_session->inbasefilename, error); + return false; + } } } @@ -515,9 +522,19 @@ int DecoderSession_finish_ok(DecoderSession *d) flac__utils_printf(stderr, 2, "\r%s: %s \n", d->inbasefilename, d->test_only? "ok ":d->analysis_mode?"done ":"done"); } DecoderSession_destroy(d, /*error_occurred=*/!ok); - if(!d->test_only && (d->is_wave_out || d->is_aiff_out) && (d->iff_headers_need_fixup || (!d->got_stream_info && strcmp(d->outfilename, "-")))) - if(!fixup_iff_headers(d)) - return 1; + if(!d->analysis_mode && !d->test_only && (d->is_wave_out || d->is_aiff_out)) { + if(d->iff_headers_need_fixup || (!d->got_stream_info && strcmp(d->outfilename, "-"))) { + if(!fixup_iff_headers(d)) + return 1; + } + if(d->foreign_metadata) { + const char *error; + if(!flac__foreign_metadata_write_to_iff(d->foreign_metadata, d->infilename, d->outfilename, d->fm_offset1, d->fm_offset2, d->fm_offset3, &error)) { + flac__utils_printf(stderr, 1, "ERROR updating foreign metadata from %s to %s: %s\n", d->infilename, d->outfilename, error); + return 1; + } + } + } return ok? 0 : 1; } @@ -584,6 +601,11 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin const FLAC__bool is_waveformatextensible = decoder_session->is_wave_out && (decoder_session->channel_mask == 2 || decoder_session->channel_mask > 3 || decoder_session->bps%8 || decoder_session->channels > 2); FLAC__uint64 data_size = samples * decoder_session->channels * ((decoder_session->bps+7)/8); const FLAC__uint32 aligned_data_size = (FLAC__uint32)((data_size+1) & (~1U)); /* we'll check for overflow later */ + + unsigned foreign_metadata_size = 0; /* size of all non-audio non-fmt/COMM foreign metadata chunks */ + foreign_metadata_t *fm = decoder_session->foreign_metadata; + size_t i; + if(samples == 0) { if(f == stdout) { flac__utils_printf(stderr, 1, "%s: WARNING, don't have accurate sample count available for %s header.\n", decoder_session->inbasefilename, fmt_desc); @@ -596,54 +618,58 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin decoder_session->iff_headers_need_fixup = true; } } - if(data_size >= 0xFFFFFFDC) { - flac__utils_printf(stderr, 1, "%s: ERROR: stream is too big to fit in a single %s file chunk\n", decoder_session->inbasefilename, fmt_desc); + + if(fm) { + FLAC__ASSERT(fm->format_block); + FLAC__ASSERT(fm->audio_block); + FLAC__ASSERT(fm->format_block < fm->audio_block); + /* calc foreign metadata size; for RIFF/AIFF we always skip the first chunk, format chunk, and sound chunk since we write our own */ + for(i = 1; i < fm->num_blocks; i++) { + if(i != fm->format_block && i != fm->audio_block) + foreign_metadata_size += fm->blocks[i].size; + } + } + + if(data_size + foreign_metadata_size + 60/*worst-case*/ >= 0xFFFFFFF4) { + flac__utils_printf(stderr, 1, "%s: ERROR: stream is too big to fit in a single %s file\n", decoder_session->inbasefilename, fmt_desc); return false; } + if(decoder_session->is_wave_out) { if(flac__utils_fwrite("RIFF", 1, 4, f) != 4) return false; - if(!write_little_endian_uint32(f, aligned_data_size+(is_waveformatextensible?60:36))) /* filesize-8 */ + if(!write_little_endian_uint32(f, foreign_metadata_size + aligned_data_size + (is_waveformatextensible?60:36))) /* filesize-8 */ return false; - if(flac__utils_fwrite("WAVEfmt ", 1, 8, f) != 8) + if(flac__utils_fwrite("WAVE", 1, 4, f) != 4) return false; - if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */ + decoder_session->fm_offset1 = ftello(f); + + if(fm) { + /* seek forward to {allocate} or {skip over already-written chunks} before "fmt " */ + for(i = 1; i < fm->format_block; i++) { + if(fseeko(f, fm->blocks[i].size, SEEK_CUR) < 0) { + flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping foreign metadata before \"fmt \"\n", decoder_session->inbasefilename); + return false; + } + } + } + + if(!write_riff_wave_fmt_chunk(f, is_waveformatextensible, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate, decoder_session->channel_mask)) return false; - if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */ - return false; + decoder_session->fm_offset2 = ftello(f); - if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels))) - return false; - - if(!write_little_endian_uint32(f, decoder_session->sample_rate)) - return false; - - if(!write_little_endian_uint32(f, decoder_session->sample_rate * decoder_session->channels * ((decoder_session->bps+7) / 8))) - return false; - - if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels * ((decoder_session->bps+7) / 8)))) /* block align */ - return false; - - if(!write_little_endian_uint16(f, (FLAC__uint16)(((decoder_session->bps+7)/8)*8))) /* bits per sample */ - return false; - - if(is_waveformatextensible) { - if(!write_little_endian_uint16(f, (FLAC__uint16)22)) /* cbSize */ - return false; - - if(!write_little_endian_uint16(f, (FLAC__uint16)decoder_session->bps)) /* validBitsPerSample */ - return false; - - if(!write_little_endian_uint32(f, decoder_session->channel_mask)) - return false; - - /* GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} */ - if(flac__utils_fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16) - return false; + if(fm) { + /* seek forward to {allocate} or {skip over already-written chunks} after "fmt " but before "data" */ + for(i = fm->format_block+1; i < fm->audio_block; i++) { + if(fseeko(f, fm->blocks[i].size, SEEK_CUR) < 0) { + flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping foreign metadata after \"fmt \"\n", decoder_session->inbasefilename); + return false; + } + } } if(flac__utils_fwrite("data", 1, 4, f) != 4) @@ -651,48 +677,143 @@ FLAC__bool write_iff_headers(FILE *f, DecoderSession *decoder_session, FLAC__uin if(!write_little_endian_uint32(f, (FLAC__uint32)data_size)) /* data size */ return false; + + decoder_session->fm_offset3 = ftello(f) + aligned_data_size; } else { + FLAC__uint32 ssnd_offset_size = (fm? fm->ssnd_offset_size : 0); + if(flac__utils_fwrite("FORM", 1, 4, f) != 4) return false; - if(!write_big_endian_uint32(f, aligned_data_size+46)) /* filesize-8 */ + if(!write_big_endian_uint32(f, foreign_metadata_size + aligned_data_size + 46 + ssnd_offset_size)) /* filesize-8 */ return false; - if(flac__utils_fwrite("AIFFCOMM", 1, 8, f) != 8) + if(flac__utils_fwrite("AIFF", 1, 4, f) != 4) return false; - if(flac__utils_fwrite("\000\000\000\022", 1, 4, f) != 4) /* chunk size = 18 */ + decoder_session->fm_offset1 = ftello(f); + + if(fm) { + /* seek forward to {allocate} or {skip over already-written chunks} before "COMM" */ + for(i = 1; i < fm->format_block; i++) { + if(fseeko(f, fm->blocks[i].size, SEEK_CUR) < 0) { + flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping foreign metadata before \"COMM\"\n", decoder_session->inbasefilename); + return false; + } + } + } + + if(!write_aiff_form_comm_chunk(f, samples, decoder_session->bps, decoder_session->channels, decoder_session->sample_rate)) return false; - if(!write_big_endian_uint16(f, (FLAC__uint16)(decoder_session->channels))) - return false; + decoder_session->fm_offset2 = ftello(f); - if(!write_big_endian_uint32(f, (FLAC__uint32)samples)) - return false; - - if(!write_big_endian_uint16(f, (FLAC__uint16)(decoder_session->bps))) - return false; - - if(!write_sane_extended(f, decoder_session->sample_rate)) - return false; + if(fm) { + /* seek forward to {allocate} or {skip over already-written chunks} after "COMM" but before "SSND" */ + for(i = fm->format_block+1; i < fm->audio_block; i++) { + if(fseeko(f, fm->blocks[i].size, SEEK_CUR) < 0) { + flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping foreign metadata after \"COMM\"\n", decoder_session->inbasefilename); + return false; + } + } + } if(flac__utils_fwrite("SSND", 1, 4, f) != 4) return false; - if(!write_big_endian_uint32(f, (FLAC__uint32)data_size+8)) /* data size */ + if(!write_big_endian_uint32(f, (FLAC__uint32)data_size + 8 + ssnd_offset_size)) /* data size */ return false; - if(!write_big_endian_uint32(f, 0/*offset*/)) + if(!write_big_endian_uint32(f, ssnd_offset_size)) return false; if(!write_big_endian_uint32(f, 0/*block_size*/)) return false; + + if(ssnd_offset_size) { + /* seek forward to {allocate} or {skip over already-written} SSND offset */ + if(fseeko(f, ssnd_offset_size, SEEK_CUR) < 0) { + flac__utils_printf(stderr, 1, "%s: ERROR: allocating/skipping \"SSND\" offset\n", decoder_session->inbasefilename); + return false; + } + } + + decoder_session->fm_offset3 = ftello(f) + aligned_data_size; } return true; } +FLAC__bool write_riff_wave_fmt_chunk(FILE *f, FLAC__bool is_waveformatextensible, unsigned bps, unsigned channels, unsigned sample_rate, FLAC__uint32 channel_mask) +{ + if(flac__utils_fwrite("fmt ", 1, 4, f) != 4) + return false; + + if(!write_little_endian_uint32(f, is_waveformatextensible? 40 : 16)) /* chunk size */ + return false; + + if(!write_little_endian_uint16(f, (FLAC__uint16)(is_waveformatextensible? 65534 : 1))) /* compression code */ + return false; + + if(!write_little_endian_uint16(f, (FLAC__uint16)channels)) + return false; + + if(!write_little_endian_uint32(f, sample_rate)) + return false; + + if(!write_little_endian_uint32(f, sample_rate * channels * ((bps+7) / 8))) + return false; + + if(!write_little_endian_uint16(f, (FLAC__uint16)(channels * ((bps+7) / 8)))) /* block align */ + return false; + + if(!write_little_endian_uint16(f, (FLAC__uint16)(((bps+7)/8)*8))) /* bits per sample */ + return false; + + if(is_waveformatextensible) { + if(!write_little_endian_uint16(f, (FLAC__uint16)22)) /* cbSize */ + return false; + + if(!write_little_endian_uint16(f, (FLAC__uint16)bps)) /* validBitsPerSample */ + return false; + + if(!write_little_endian_uint32(f, channel_mask)) + return false; + + /* GUID = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} */ + if(flac__utils_fwrite("\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71", 1, 16, f) != 16) + return false; + } + + return true; +} + +FLAC__bool write_aiff_form_comm_chunk(FILE *f, FLAC__uint64 samples, unsigned bps, unsigned channels, unsigned sample_rate) +{ + FLAC__ASSERT(samples <= 0xffffffff); + + if(flac__utils_fwrite("COMM", 1, 4, f) != 4) + return false; + + if(!write_big_endian_uint32(f, 18)) /* chunk size = 18 */ + return false; + + if(!write_big_endian_uint16(f, (FLAC__uint16)channels)) + return false; + + if(!write_big_endian_uint32(f, (FLAC__uint32)samples)) + return false; + + if(!write_big_endian_uint16(f, (FLAC__uint16)bps)) + return false; + + if(!write_sane_extended(f, sample_rate)) + return false; + + return true; +} + FLAC__bool write_little_endian_uint16(FILE *f, FLAC__uint16 val) { FLAC__byte *b = (FLAC__byte*)(&val); diff --git a/src/flac/encode.c b/src/flac/encode.c index 973af63c..b3a629cb 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -2098,7 +2098,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio } static_metadata_append(&static_metadata, p, /*needs_delete=*/true); static_metadata.metadata[static_metadata.num_metadata-1]->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8 + foreign_metadata->blocks[i].size; -fprintf(stderr,"@@@@@@ add PADDING=%u\n",static_metadata.metadata[static_metadata.num_metadata-1]->length); +/*fprintf(stderr,"@@@@@@ add PADDING=%u\n",static_metadata.metadata[static_metadata.num_metadata-1]->length);*/ } } if(options.padding != 0) { diff --git a/src/flac/flac.dsp b/src/flac/flac.dsp index b1fc5fae..fbf10002 100644 --- a/src/flac/flac.dsp +++ b/src/flac/flac.dsp @@ -101,6 +101,10 @@ SOURCE=.\encode.c # End Source File # Begin Source File +SOURCE=.\foreign_metadata.c +# End Source File +# Begin Source File + SOURCE=.\main.c # End Source File # Begin Source File @@ -133,6 +137,10 @@ SOURCE=.\encode.h # End Source File # Begin Source File +SOURCE=.\foreign_metadata.h +# End Source File +# Begin Source File + SOURCE=.\local_string_utils.h # End Source File # Begin Source File diff --git a/src/flac/flac.vcproj b/src/flac/flac.vcproj index 9c075e63..050a1613 100644 --- a/src/flac/flac.vcproj +++ b/src/flac/flac.vcproj @@ -199,6 +199,10 @@ RelativePath=".\encode.h" > + + @@ -229,6 +233,10 @@ RelativePath=".\encode.c" > + + diff --git a/src/flac/foreign_metadata.c b/src/flac/foreign_metadata.c index 8883f966..9487e9b3 100644 --- a/src/flac/foreign_metadata.c +++ b/src/flac/foreign_metadata.c @@ -40,7 +40,7 @@ #define min(x,y) ((x)<(y)?(x):(y)) -static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID = "FFMB";/*@@@@@@ settle on an ID */ +static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[2] = { "AIFF" , "RIFF" }; static FLAC__uint32 unpack32be_(const FLAC__byte *b) { @@ -52,6 +52,25 @@ static FLAC__uint32 unpack32le_(const FLAC__byte *b) return (FLAC__uint32)b[0] + ((FLAC__uint32)b[1]<<8) + ((FLAC__uint32)b[2]<<16) + ((FLAC__uint32)b[3]<<24); } +static FLAC__bool copy_data_(FILE *fin, FILE *fout, size_t size, const char **error, const char * const read_error, const char * const write_error) +{ + static FLAC__byte buffer[4096]; + size_t left; + for(left = size; left > 0; ) { + size_t need = min(sizeof(buffer), left); + if(fread(buffer, 1, need, fin) < need) { + if(error) *error = read_error; + return false; + } + if(fwrite(buffer, 1, need, fout) < need) { + if(error) *error = write_error; + return false; + } + left -= need; + } + return true; +} + static FLAC__bool append_block_(foreign_metadata_t *fm, off_t offset, FLAC__uint32 size, const char **error) { foreign_block_t *fb = realloc(fm->blocks, sizeof(foreign_block_t) * (fm->num_blocks+1)); @@ -60,6 +79,7 @@ static FLAC__bool append_block_(foreign_metadata_t *fm, off_t offset, FLAC__uint fb[fm->num_blocks].size = size; fm->num_blocks++; fm->blocks = fb; +/*fprintf(stderr,"@@@@@@ appended: block#%u offset=%d size=%u\n",fm->num_blocks-1,(int)fm->blocks[fm->num_blocks-1].offset,(unsigned)fm->blocks[fm->num_blocks-1].size);*/ return true; } if(error) *error = "out of memory"; @@ -81,8 +101,9 @@ static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char ** if(!append_block_(fm, offset, 12, error)) return false; eof_offset = 8 + unpack32be_(buffer+4); +/*fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset);*/ while(!feof(f)) { - FLAC__uint32 size, ssnd_offset_size = 0; + FLAC__uint32 size; if((offset = ftello(f)) < 0) { if(error) *error = "ftello() error (003)"; return false; @@ -102,43 +123,51 @@ static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char ** if(error) *error = "invalid AIFF file: multiple \"COMM\" chunks (005)"; return false; } + if(fm->audio_block) { + if(error) *error = "invalid AIFF file: \"SSND\" chunk before \"COMM\" chunk (006)"; + return false; + } fm->format_block = fm->num_blocks; } else if(!memcmp(buffer, "SSND", 4)) { if(fm->audio_block) { - if(error) *error = "invalid AIFF file: multiple \"SSND\" chunks (006)"; + if(error) *error = "invalid AIFF file: multiple \"SSND\" chunks (007)"; + return false; + } + if(!fm->format_block) { + if(error) *error = "invalid AIFF file: \"SSND\" chunk before \"COMM\" chunk (008)"; return false; } fm->audio_block = fm->num_blocks; /* read #offset bytes */ if(fread(buffer+8, 1, 4, f) < 4) { - if(error) *error = "invalid AIFF file (007)"; + if(error) *error = "invalid AIFF file (009)"; return false; } - ssnd_offset_size = unpack32be_(buffer+8); + fm->ssnd_offset_size = unpack32be_(buffer+8); if(fseeko(f, -4, SEEK_CUR) < 0) { - if(error) *error = "invalid AIFF file: seek error (008)"; + if(error) *error = "invalid AIFF file: seek error (010)"; return false; } } - if(!append_block_(fm, offset, 8 + (memcmp(buffer, "SSND", 4)? size : 8 + ssnd_offset_size), error)) + if(!append_block_(fm, offset, 8 + (memcmp(buffer, "SSND", 4)? size : 8 + fm->ssnd_offset_size), error)) return false; -fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size); +/*fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size);*/ if(fseeko(f, size, SEEK_CUR) < 0) { - if(error) *error = "invalid AIFF file: seek error (009)"; + if(error) *error = "invalid AIFF file: seek error (011)"; return false; } } if(eof_offset != ftello(f)) { - if(error) *error = "invalid AIFF file: unexpected EOF (010)"; + if(error) *error = "invalid AIFF file: unexpected EOF (012)"; return false; } if(!fm->format_block) { - if(error) *error = "invalid AIFF file: missing \"COMM\" chunk (011)"; + if(error) *error = "invalid AIFF file: missing \"COMM\" chunk (013)"; return false; } if(!fm->audio_block) { - if(error) *error = "invalid AIFF file: missing \"SSND\" chunk (012)"; + if(error) *error = "invalid AIFF file: missing \"SSND\" chunk (014)"; return false; } return true; @@ -159,7 +188,7 @@ static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char ** if(!append_block_(fm, offset, 12, error)) return false; eof_offset = 8 + unpack32le_(buffer+4); -fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset); +/*fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset);*/ while(!feof(f)) { FLAC__uint32 size; if((offset = ftello(f)) < 0) { @@ -181,33 +210,41 @@ fprintf(stderr,"@@@@@@ off=%d eof=%d\n",(int)offset,(int)eof_offset); if(error) *error = "invalid WAVE file: multiple \"fmt \" chunks (005)"; return false; } + if(fm->audio_block) { + if(error) *error = "invalid WAVE file: \"data\" chunk before \"fmt \" chunk (006)"; + return false; + } fm->format_block = fm->num_blocks; } else if(!memcmp(buffer, "data", 4)) { if(fm->audio_block) { - if(error) *error = "invalid WAVE file: multiple \"data\" chunks (006)"; + if(error) *error = "invalid WAVE file: multiple \"data\" chunks (007)"; + return false; + } + if(!fm->format_block) { + if(error) *error = "invalid WAVE file: \"data\" chunk before \"fmt \" chunk (008)"; return false; } fm->audio_block = fm->num_blocks; } if(!append_block_(fm, offset, 8 + (memcmp(buffer, "data", 4)? size : 0), error)) return false; -fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size); +/*fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],buffer[2],buffer[3],(int)offset,8+(int)size);*/ if(fseeko(f, size, SEEK_CUR) < 0) { - if(error) *error = "invalid WAVE file: seek error (007)"; + if(error) *error = "invalid WAVE file: seek error (009)"; return false; } } if(eof_offset != ftello(f)) { - if(error) *error = "invalid WAVE file: unexpected EOF (008)"; + if(error) *error = "invalid WAVE file: unexpected EOF (010)"; return false; } if(!fm->format_block) { - if(error) *error = "invalid WAVE file: missing \"fmt \" chunk (009)"; + if(error) *error = "invalid WAVE file: missing \"fmt \" chunk (011)"; return false; } if(!fm->audio_block) { - if(error) *error = "invalid WAVE file: missing \"data\" chunk (010)"; + if(error) *error = "invalid WAVE file: missing \"data\" chunk (012)"; return false; } return true; @@ -215,9 +252,9 @@ fprintf(stderr,"@@@@@@ chunk=%c%c%c%c offset=%d size=%d\n",buffer[0],buffer[1],b static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout, FLAC__Metadata_SimpleIterator *it, const char **error) { - static FLAC__byte buffer[4096]; + FLAC__byte buffer[4]; const unsigned ID_LEN = FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; - size_t left, block_num = 0; + size_t block_num = 0; FLAC__ASSERT(sizeof(buffer) >= ID_LEN); while(block_num < fm->num_blocks) { /* find next matching padding block */ @@ -232,14 +269,14 @@ static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout, if(error) *error = "PADDING block with wrong size found (005)"; return false; } -fprintf(stderr,"@@@@@@ flac offset = %d\n",(int)FLAC__metadata_simple_iterator_get_block_offset(it)); +/*fprintf(stderr,"@@@@@@ flac offset = %d\n",(int)FLAC__metadata_simple_iterator_get_block_offset(it));*/ /* transfer chunk into APPLICATION block */ /* first set up the file pointers */ - if(fseek(fin, fm->blocks[block_num].offset, SEEK_SET) < 0) { + if(fseeko(fin, fm->blocks[block_num].offset, SEEK_SET) < 0) { if(error) *error = "seek failed in WAVE/AIFF file (006)"; return false; } - if(fseek(fout, FLAC__metadata_simple_iterator_get_block_offset(it), SEEK_SET) < 0) { + if(fseeko(fout, FLAC__metadata_simple_iterator_get_block_offset(it), SEEK_SET) < 0) { if(error) *error = "seek failed in FLAC file (007)"; return false; } @@ -248,41 +285,200 @@ fprintf(stderr,"@@@@@@ flac offset = %d\n",(int)FLAC__metadata_simple_iterator_g if(FLAC__metadata_simple_iterator_is_last(it)) buffer[0] |= 0x80; /*MAGIC number*/ if(fwrite(buffer, 1, 1, fout) < 1) { - if(error) *error = "write failed in FLAC/AIFF file (008)"; + if(error) *error = "write failed in FLAC file (008)"; return false; } /* length stays the same so skip over it */ - if(fseek(fout, FLAC__STREAM_METADATA_LENGTH_LEN/8, SEEK_CUR) < 0) { + if(fseeko(fout, FLAC__STREAM_METADATA_LENGTH_LEN/8, SEEK_CUR) < 0) { if(error) *error = "seek failed in FLAC file (009)"; return false; } /* write the APPLICATION ID */ - memcpy(buffer, FLAC__FOREIGN_METADATA_APPLICATION_ID, ID_LEN); + memcpy(buffer, FLAC__FOREIGN_METADATA_APPLICATION_ID[fm->type], ID_LEN); if(fwrite(buffer, 1, ID_LEN, fout) < ID_LEN) { - if(error) *error = "write failed in FLAC/AIFF file (010)"; + if(error) *error = "write failed in FLAC file (010)"; return false; } /* transfer the foreign metadata */ - for(left = fm->blocks[block_num].size; left > 0; ) { - size_t need = min(sizeof(buffer), left); - if(fread(buffer, 1, need, fin) < need) { - if(error) *error = "read failed in WAVE/AIFF file (011)"; - return false; - } - if(fwrite(buffer, 1, need, fout) < need) { - if(error) *error = "write failed in FLAC/AIFF file (012)"; - return false; - } - left -= need; - } + if(!copy_data_(fin, fout, fm->blocks[block_num].size, error, "read failed in WAVE/AIFF file (011)", "write failed in FLAC file (012)")) + return false; block_num++; } return true; } -foreign_metadata_t *flac__foreign_metadata_new(void) +static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadata_SimpleIterator *it, const char **error) { - return (foreign_metadata_t*)calloc(sizeof(foreign_metadata_t), 1); + FLAC__byte id[4], buffer[12]; + off_t offset; + FLAC__bool type_found = false; + + FLAC__ASSERT(FLAC__STREAM_METADATA_APPLICATION_ID_LEN == sizeof(id)*8); + + while(FLAC__metadata_simple_iterator_next(it)) { + if(FLAC__metadata_simple_iterator_get_block_type(it) != FLAC__METADATA_TYPE_APPLICATION) + continue; + if(!FLAC__metadata_simple_iterator_get_application_id(it, id)) { + if(error) *error = "FLAC__metadata_simple_iterator_get_application_id() error (003)"; + return false; + } + if(memcmp(id, FLAC__FOREIGN_METADATA_APPLICATION_ID[fm->type], sizeof(id))) + continue; + offset = FLAC__metadata_simple_iterator_get_block_offset(it); + /* skip over header and app ID */ + offset += (FLAC__STREAM_METADATA_IS_LAST_LEN + FLAC__STREAM_METADATA_TYPE_LEN + FLAC__STREAM_METADATA_LENGTH_LEN) / 8; + offset += sizeof(id); + /* look for format or audio blocks */ + if(fseek(f, offset, SEEK_SET) < 0) { + if(error) *error = "seek error (004)"; + return false; + } + if(fread(buffer, 1, 4, f) != 4) { + if(error) *error = "read error (005)"; + return false; + } + if(fm->num_blocks == 0) { + if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && 0 == memcmp(buffer, "RIFF", 4)) + type_found = true; + else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF && 0 == memcmp(buffer, "FORM", 4)) + type_found = true; + else { + if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (005)"; + return false; + } + } + else if(!type_found) { + FLAC__ASSERT(0); + /* double protection: */ + if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (006)"; + return false; + } + else if(fm->type == FOREIGN_BLOCK_TYPE__RIFF) { + if(!memcmp(buffer, "fmt ", 4)) { + if(fm->format_block) { + if(error) *error = "invalid WAVE metadata: multiple \"fmt \" chunks (007)"; + return false; + } + if(fm->audio_block) { + if(error) *error = "invalid WAVE metadata: \"data\" chunk before \"fmt \" chunk (008)"; + return false; + } + fm->format_block = fm->num_blocks; + } + else if(!memcmp(buffer, "data", 4)) { + if(fm->audio_block) { + if(error) *error = "invalid WAVE metadata: multiple \"data\" chunks (009)"; + return false; + } + if(!fm->format_block) { + if(error) *error = "invalid WAVE metadata: \"data\" chunk before \"fmt \" chunk (010)"; + return false; + } + fm->audio_block = fm->num_blocks; + } + } + else if(fm->type == FOREIGN_BLOCK_TYPE__AIFF) { + if(!memcmp(buffer, "COMM", 4)) { + if(fm->format_block) { + if(error) *error = "invalid AIFF metadata: multiple \"COMM\" chunks (011)"; + return false; + } + if(fm->audio_block) { + if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (012)"; + return false; + } + fm->format_block = fm->num_blocks; + } + else if(!memcmp(buffer, "SSND", 4)) { + if(fm->audio_block) { + if(error) *error = "invalid AIFF metadata: multiple \"SSND\" chunks (013)"; + return false; + } + if(!fm->format_block) { + if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (014)"; + return false; + } + fm->audio_block = fm->num_blocks; + /* read SSND offset size */ + if(fread(buffer+4, 1, 8, f) != 8) { + if(error) *error = "read error (015)"; + return false; + } + fm->ssnd_offset_size = unpack32be_(buffer+8); + } + } + else { + FLAC__ASSERT(0); + /* double protection: */ + if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (016)"; + return false; + } + if(!append_block_(fm, offset, FLAC__metadata_simple_iterator_get_block_length(it)-sizeof(id), error)) + return false; + } + if(!type_found) { + if(error) *error = "no foreign metadata found (017)"; + return false; + } + if(!fm->format_block) { + if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"fmt \" chunk (018)" : "invalid AIFF file: missing \"COMM\" chunk (018)"; + return false; + } + if(!fm->audio_block) { + if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"data\" chunk (019)" : "invalid AIFF file: missing \"SSND\" chunk (019)"; + return false; + } + return true; +} + +static FLAC__bool write_to_iff_(foreign_metadata_t *fm, FILE *fin, FILE *fout, off_t offset1, off_t offset2, off_t offset3, const char **error) +{ + size_t i; + if(fseeko(fout, offset1, SEEK_SET) < 0) { + if(error) *error = "seek failed in WAVE/AIFF file (002)"; + return false; + } + for(i = 1; i < fm->format_block; i++) { + if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) { + if(error) *error = "seek failed in FLAC file (003)"; + return false; + } + if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (004)", "write failed in FLAC file (005)")) + return false; + } + if(fseeko(fout, offset2, SEEK_SET) < 0) { + if(error) *error = "seek failed in WAVE/AIFF file (006)"; + return false; + } + for(i = fm->format_block+1; i < fm->audio_block; i++) { + if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) { + if(error) *error = "seek failed in FLAC file (007)"; + return false; + } + if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (008)", "write failed in FLAC file (009)")) + return false; + } + if(fseeko(fout, offset3, SEEK_SET) < 0) { + if(error) *error = "seek failed in WAVE/AIFF file (010)"; + return false; + } + for(i = fm->audio_block+1; i < fm->num_blocks; i++) { + if(fseeko(fin, fm->blocks[i].offset, SEEK_SET) < 0) { + if(error) *error = "seek failed in FLAC file (011)"; + return false; + } + if(!copy_data_(fin, fout, fm->blocks[i].size, error, "read failed in WAVE/AIFF file (012)", "write failed in FLAC file (013)")) + return false; + } + return true; +} + +foreign_metadata_t *flac__foreign_metadata_new(foreign_block_type_t type) +{ + foreign_metadata_t *x = (foreign_metadata_t*)calloc(sizeof(foreign_metadata_t), 1); + if(x) + x->type = type; + return x; } void flac__foreign_metadata_delete(foreign_metadata_t *fm) @@ -354,12 +550,44 @@ FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const ch FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const char *filename, const char **error) { + FLAC__bool ok; + FILE *f; + FLAC__Metadata_SimpleIterator *it = FLAC__metadata_simple_iterator_new(); + if(!it) { + if(error) *error = "out of memory (000)"; + return false; + } + if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/false)) { + if(error) *error = "can't initialize iterator (001)"; + FLAC__metadata_simple_iterator_delete(it); + return false; + } + if(0 == (f = fopen(filename, "rb"))) { + if(error) *error = "can't open FLAC file for reading (002)"; + FLAC__metadata_simple_iterator_delete(it); + return false; + } + ok = read_from_flac_(fm, f, it, error); + FLAC__metadata_simple_iterator_delete(it); + fclose(f); + return ok; } -FLAC__bool flac__foreign_metadata_write_to_aiff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error) -{ -} - -FLAC__bool flac__foreign_metadata_write_to_wave(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error) +FLAC__bool flac__foreign_metadata_write_to_iff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, off_t offset1, off_t offset2, off_t offset3, const char **error) { + FLAC__bool ok; + FILE *fin, *fout; + if(0 == (fin = fopen(infilename, "rb"))) { + if(error) *error = "can't open FLAC file for reading (000)"; + return false; + } + if(0 == (fout = fopen(outfilename, "r+b"))) { + if(error) *error = "can't open WAVE/AIFF file for updating (001)"; + fclose(fin); + return false; + } + ok = write_to_iff_(fm, fin, fout, offset1, offset2, offset3, error); + fclose(fin); + fclose(fout); + return ok; } diff --git a/src/flac/foreign_metadata.h b/src/flac/foreign_metadata.h index 7e7c4aa0..baeda20e 100644 --- a/src/flac/foreign_metadata.h +++ b/src/flac/foreign_metadata.h @@ -26,6 +26,9 @@ #include "FLAC/metadata.h" #include "utils.h" +/* WATCHOUT: these enums are used to index internal arrays */ +typedef enum { FOREIGN_BLOCK_TYPE__AIFF = 0, FOREIGN_BLOCK_TYPE__RIFF = 1 } foreign_block_type_t; + typedef struct { /* for encoding, this will be the offset in the WAVE/AIFF file of the chunk */ /* for decoding, this will be the offset in the FLAC file of the chunk data inside the APPLICATION block */ @@ -34,13 +37,15 @@ typedef struct { } foreign_block_t; typedef struct { + foreign_block_type_t type; /* currently we don't support multiple foreign types in a stream (an maybe never will) */ foreign_block_t *blocks; size_t num_blocks; size_t format_block; /* block number of 'fmt ' or 'COMM' chunk */ size_t audio_block; /* block number of 'data' or 'SSND' chunk */ + FLAC__uint32 ssnd_offset_size; /* 0 if type!=AIFF */ } foreign_metadata_t; -foreign_metadata_t *flac__foreign_metadata_new(void); +foreign_metadata_t *flac__foreign_metadata_new(foreign_block_type_t type); void flac__foreign_metadata_delete(foreign_metadata_t *fm); @@ -49,7 +54,6 @@ FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const c FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error); FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const char *filename, const char **error); -FLAC__bool flac__foreign_metadata_write_to_aiff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error); -FLAC__bool flac__foreign_metadata_write_to_wave(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error); +FLAC__bool flac__foreign_metadata_write_to_iff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, off_t offset1, off_t offset2, off_t offset3, const char **error); #endif diff --git a/src/flac/main.c b/src/flac/main.c index e0075749..91002719 100644 --- a/src/flac/main.c +++ b/src/flac/main.c @@ -28,7 +28,6 @@ #include #include #include -#include #if !defined _MSC_VER && !defined __MINGW32__ /* unlink is in stdio.h in VC++ */ @@ -460,6 +459,10 @@ int do_it(void) /* we're not going to try and support the re-creation of broken WAVE files */ if(option_values.ignore_chunk_sizes) return usage_error("ERROR: using --keep-foreign-metadata cannot be used with --ignore-chunk-sizes\n"); + if(option_values.test_only) + return usage_error("ERROR: --keep-foreign-metadata is not allowed in test mode\n"); + if(option_values.analyze) + return usage_error("ERROR: --keep-foreign-metadata is not allowed in analyis mode\n"); /*@@@@@@*/ if(option_values.delete_input) return usage_error("ERROR: using --delete-input-file with --keep-foreign-metadata has been disabled until more testing has been done.\n"); @@ -1875,7 +1878,7 @@ int encode_file(const char *infilename, FLAC__bool is_first_file, FLAC__bool is_ /* read foreign metadata if requested */ if(option_values.keep_foreign_metadata) { - if(0 == (options.foreign_metadata = flac__foreign_metadata_new())) { + if(0 == (options.foreign_metadata = flac__foreign_metadata_new(input_format==AIF? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) { flac__utils_printf(stderr, 1, "ERROR: creating foreign metadata object\n"); conditional_fclose(encode_infile); return 1; @@ -2044,7 +2047,7 @@ int decode_file(const char *infilename) /* read foreign metadata if requested */ if(option_values.keep_foreign_metadata) { - if(0 == (options.foreign_metadata = flac__foreign_metadata_new())) { + if(0 == (options.foreign_metadata = flac__foreign_metadata_new(output_format==AIF? FOREIGN_BLOCK_TYPE__AIFF : FOREIGN_BLOCK_TYPE__RIFF))) { flac__utils_printf(stderr, 1, "ERROR: creating foreign metadata object\n"); return 1; }