static FLAC__uint32 unpack32_(const FLAC__byte *b, foreign_block_type_t type) { if(type == FOREIGN_BLOCK_TYPE__AIFF) return unpack32be_(b); else return unpack32le_(b); }
static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char **error) { FLAC__byte buffer[12]; FLAC__off_t offset, eof_offset; if((offset = ftello(f)) < 0) { if(error) *error = "ftello() error (001)"; return false; } if(fread(buffer, 1, 12, f) < 12 || memcmp(buffer, "FORM", 4) || (memcmp(buffer+8, "AIFF", 4) && memcmp(buffer+8, "AIFC", 4))) { if(error) *error = "unsupported FORM layout (002)"; return false; } if(!append_block_(fm, offset, 12, error)) return false; eof_offset = (FLAC__off_t)8 + (FLAC__off_t)unpack32be_(buffer+4); while(!feof(f)) { FLAC__uint32 size; if((offset = ftello(f)) < 0) { if(error) *error = "ftello() error (003)"; return false; } if((size = fread(buffer, 1, 8, f)) < 8) { if(size == 0 && feof(f)) break; if(error) *error = "invalid AIFF file (004)"; return false; } size = unpack32be_(buffer+4); /* check if pad byte needed */ if(size & 1) size++; if(!memcmp(buffer, "COMM", 4)) { if(fm->format_block) { 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 (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 (009)"; return false; } fm->ssnd_offset_size = unpack32be_(buffer+8); if(fseeko(f, -4, SEEK_CUR) < 0) { if(error) *error = "invalid AIFF file: seek error (010)"; return false; } /* WATCHOUT: For SSND we ignore the blockSize and are not saving any * unaligned part at the end of the chunk. In retrospect it is pretty * pointless to save the unaligned data before the PCM but now it is * done and cast in stone. */ } if(!append_block_(fm, offset, 8 + (memcmp(buffer, "SSND", 4)? size : 8 + fm->ssnd_offset_size), error)) return false; /* skip to next chunk */ if(fseeko(f, size, SEEK_CUR) < 0) { if(error) *error = "invalid AIFF file: seek error (011)"; return false; } } if(eof_offset != ftello(f)) { if(error) *error = "invalid AIFF file: unexpected EOF (012)"; return false; } if(!fm->format_block) { 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 (014)"; return false; } 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]; FLAC__off_t offset; FLAC__bool type_found = false, ds64_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 (002)"; 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(fseeko(f, offset, SEEK_SET) < 0) { if(error) *error = "seek error (003)"; return false; } if(fread(buffer, 1, 4, f) != 4) { if(error) *error = "read error (004)"; return false; } if(fm->num_blocks == 0) { /* first block? */ fm->is_rf64 = 0 == memcmp(buffer, "RF64", 4); if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && (0 == memcmp(buffer, "RIFF", 4) || fm->is_rf64)) type_found = true; else if(fm->type == FOREIGN_BLOCK_TYPE__WAVE64 && 0 == memcmp(buffer, "riff", 4)) /* use first 4 bytes instead of whole GUID */ 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->is_rf64 && fm->num_blocks == 1) { if(memcmp(buffer, "ds64", 4)) { if(error) *error = "invalid RF64 metadata: second chunk is not \"ds64\" (011)"; return false; } ds64_found = true; } } else if(fm->type == FOREIGN_BLOCK_TYPE__WAVE64) { if(!memcmp(buffer, "fmt ", 4)) { /* use first 4 bytes instead of whole GUID */ if(fm->format_block) { if(error) *error = "invalid Wave64 metadata: multiple \"fmt \" chunks (012)"; return false; } if(fm->audio_block) { if(error) *error = "invalid Wave64 metadata: \"data\" chunk before \"fmt \" chunk (013)"; return false; } fm->format_block = fm->num_blocks; } else if(!memcmp(buffer, "data", 4)) { /* use first 4 bytes instead of whole GUID */ if(fm->audio_block) { if(error) *error = "invalid Wave64 metadata: multiple \"data\" chunks (014)"; return false; } if(!fm->format_block) { if(error) *error = "invalid Wave64 metadata: \"data\" chunk before \"fmt \" chunk (015)"; 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 (016)"; return false; } if(fm->audio_block) { if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (017)"; 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 (018)"; return false; } if(!fm->format_block) { if(error) *error = "invalid AIFF metadata: \"SSND\" chunk before \"COMM\" chunk (019)"; 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 (020)"; 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 (021)"; 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 (022)"; return false; } if(fm->is_rf64 && !ds64_found) { if(error) *error = "invalid RF64 file: second chunk is not \"ds64\" (023)"; return false; } if(!fm->format_block) { if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"fmt \" chunk (024)" : fm->type==FOREIGN_BLOCK_TYPE__WAVE64? "invalid Wave64 file: missing \"fmt \" chunk (025)" : "invalid AIFF file: missing \"COMM\" chunk (026)"; return false; } if(!fm->audio_block) { if(error) *error = fm->type==FOREIGN_BLOCK_TYPE__RIFF? "invalid WAVE file: missing \"data\" chunk (027)" : fm->type==FOREIGN_BLOCK_TYPE__WAVE64? "invalid Wave64 file: missing \"data\" chunk (028)" : "invalid AIFF file: missing \"SSND\" chunk (029)"; return false; } return true; }