finish foreign metadata implemetation
authorJosh Coalson <jcoalson@users.sourceforce.net>
Tue, 21 Aug 2007 08:05:29 +0000 (08:05 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Tue, 21 Aug 2007 08:05:29 +0000 (08:05 +0000)
src/flac/decode.c
src/flac/encode.c
src/flac/flac.dsp
src/flac/flac.vcproj
src/flac/foreign_metadata.c
src/flac/foreign_metadata.h
src/flac/main.c

index 1847c59..67aff01 100644 (file)
@@ -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 */
-                       return false;
-
-               if(flac__utils_fwrite("WAVEfmt ", 1, 8, f) != 8)
-                       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 */
+               if(!write_little_endian_uint32(f, foreign_metadata_size + aligned_data_size + (is_waveformatextensible?60:36))) /* filesize-8 */
                        return false;
 
-               if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels)))
+               if(flac__utils_fwrite("WAVE", 1, 4, f) != 4)
                        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;
+               decoder_session->fm_offset1 = ftello(f);
 
-               if(!write_little_endian_uint16(f, (FLAC__uint16)(decoder_session->channels * ((decoder_session->bps+7) / 8)))) /* block align */
-                       return false;
+               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_little_endian_uint16(f, (FLAC__uint16)(((decoder_session->bps+7)/8)*8))) /* bits per sample */
+               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(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;
+               decoder_session->fm_offset2 = ftello(f);
 
-                       /* 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)))
+               decoder_session->fm_offset2 = ftello(f);
+
+               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)samples))
+               if(!write_big_endian_uint32(f, (FLAC__uint32)data_size + 8 + ssnd_offset_size)) /* data size */
                        return false;
 
-               if(!write_big_endian_uint16(f, (FLAC__uint16)(decoder_session->bps)))
+               if(!write_big_endian_uint32(f, ssnd_offset_size))
                        return false;
 
-               if(!write_sane_extended(f, decoder_session->sample_rate))
+               if(!write_big_endian_uint32(f, 0/*block_size*/))
                        return false;
 
-               if(flac__utils_fwrite("SSND", 1, 4, f) != 4)
+               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_big_endian_uint32(f, (FLAC__uint32)data_size+8)) /* data size */
+               if(!write_little_endian_uint16(f, (FLAC__uint16)bps)) /* validBitsPerSample */
                        return false;
 
-               if(!write_big_endian_uint32(f, 0/*offset*/))
+               if(!write_little_endian_uint32(f, channel_mask))
                        return false;
 
-               if(!write_big_endian_uint32(f, 0/*block_size*/))
+               /* 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);
index 973af63..b3a629c 100644 (file)
@@ -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) {
index b1fc5fa..fbf1000 100644 (file)
@@ -101,6 +101,10 @@ SOURCE=.\encode.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\foreign_metadata.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\main.c\r
 # End Source File\r
 # Begin Source File\r
@@ -133,6 +137,10 @@ SOURCE=.\encode.h
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=.\foreign_metadata.h\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=.\local_string_utils.h\r
 # End Source File\r
 # Begin Source File\r
index 9c075e6..050a161 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\foreign_metadata.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\local_string_utils.h"\r
                                >\r
                        </File>\r
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\foreign_metadata.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\local_string_utils.c"\r
                                >\r
                        </File>\r
index 8883f96..9487e9b 100644 (file)
@@ -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)";
+               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;
+}
+
+static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadata_SimpleIterator *it, const char **error)
+{
+       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;
                        }
-                       if(fwrite(buffer, 1, need, fout) < need) {
-                               if(error) *error = "write failed in FLAC/AIFF file (012)";
-                               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;
                        }
-                       left -= need;
                }
-               block_num++;
+               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(void)
+foreign_metadata_t *flac__foreign_metadata_new(foreign_block_type_t type)
 {
-       return (foreign_metadata_t*)calloc(sizeof(foreign_metadata_t), 1);
+       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;
 }
index 7e7c4aa..baeda20 100644 (file)
@@ -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
index e007574..9100271 100644 (file)
@@ -28,7 +28,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#include <unistd.h>
 
 #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;
                        }