Huge Windows utf8 I/O patch.
[platform/upstream/flac.git] / src / flac / foreign_metadata.c
index 9487e9b..8372926 100644 (file)
@@ -1,5 +1,5 @@
 /* flac - Command-line FLAC encoder/decoder
- * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson
+ * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008,2009  Josh Coalson
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #if HAVE_CONFIG_H
 #  include <config.h>
 #endif
 
-#if defined _MSC_VER || defined __MINGW32__
-#include <sys/types.h> /* for off_t */
-#if _MSC_VER <= 1600 /* @@@ [2G limit] */
-#define fseeko fseek
-#define ftello ftell
-#endif
-#endif
 #include <stdio.h> /* for FILE etc. */
 #include <stdlib.h> /* for calloc() etc. */
 #include <string.h> /* for memcmp() etc. */
 #include "FLAC/assert.h"
 #include "FLAC/metadata.h"
+#include "share/alloc.h"
+#include "share/compat.h"
 #include "foreign_metadata.h"
 
 #ifdef min
@@ -40,7 +35,7 @@
 #define min(x,y) ((x)<(y)?(x):(y))
 
 
-static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[2] = { "AIFF" , "RIFF" };
+static const char *FLAC__FOREIGN_METADATA_APPLICATION_ID[3] = { "aiff" , "riff", "w64 " };
 
 static FLAC__uint32 unpack32be_(const FLAC__byte *b)
 {
@@ -52,9 +47,15 @@ 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__uint64 unpack64le_(const FLAC__byte *b)
+{
+       return (FLAC__uint64)b[0] + ((FLAC__uint64)b[1]<<8) + ((FLAC__uint64)b[2]<<16) + ((FLAC__uint64)b[3]<<24) + ((FLAC__uint64)b[4]<<32) + ((FLAC__uint64)b[5]<<40) + ((FLAC__uint64)b[6]<<48) + ((FLAC__uint64)b[7]<<56);
+}
+
+/* copies 'size' bytes from file 'fin' to 'fout', filling in *error with 'read_error' or 'write_error' as necessary */
 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];
+       FLAC__byte buffer[4096];
        size_t left;
        for(left = size; left > 0; ) {
                size_t need = min(sizeof(buffer), left);
@@ -71,15 +72,14 @@ static FLAC__bool copy_data_(FILE *fin, FILE *fout, size_t size, const char **er
        return true;
 }
 
-static FLAC__bool append_block_(foreign_metadata_t *fm, off_t offset, FLAC__uint32 size, const char **error)
+static FLAC__bool append_block_(foreign_metadata_t *fm, FLAC__off_t offset, FLAC__uint32 size, const char **error)
 {
-       foreign_block_t *fb = realloc(fm->blocks, sizeof(foreign_block_t) * (fm->num_blocks+1));
+       foreign_block_t *fb = safe_realloc_muladd2_(fm->blocks, sizeof(foreign_block_t), /*times (*/fm->num_blocks, /*+*/1/*)*/);
        if(fb) {
                fb[fm->num_blocks].offset = offset;
                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";
@@ -89,7 +89,7 @@ static FLAC__bool append_block_(foreign_metadata_t *fm, off_t offset, FLAC__uint
 static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char **error)
 {
        FLAC__byte buffer[12];
-       off_t offset, eof_offset;
+       FLAC__off_t offset, eof_offset;
        if((offset = ftello(f)) < 0) {
                if(error) *error = "ftello() error (001)";
                return false;
@@ -100,8 +100,7 @@ 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);*/
+       eof_offset = (FLAC__off_t)8 + (FLAC__off_t)unpack32be_(buffer+4);
        while(!feof(f)) {
                FLAC__uint32 size;
                if((offset = ftello(f)) < 0) {
@@ -149,10 +148,15 @@ static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char **
                                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;
-/*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);*/
+               /* skip to next chunk */
                if(fseeko(f, size, SEEK_CUR) < 0) {
                        if(error) *error = "invalid AIFF file: seek error (011)";
                        return false;
@@ -176,19 +180,25 @@ static FLAC__bool read_from_aiff_(foreign_metadata_t *fm, FILE *f, const char **
 static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char **error)
 {
        FLAC__byte buffer[12];
-       off_t offset, eof_offset;
+       FLAC__off_t offset, eof_offset = -1, ds64_data_size = -1;
        if((offset = ftello(f)) < 0) {
                if(error) *error = "ftello() error (001)";
                return false;
        }
-       if(fread(buffer, 1, 12, f) < 12 || memcmp(buffer, "RIFF", 4) || memcmp(buffer+8, "WAVE", 4)) {
+       if(fread(buffer, 1, 12, f) < 12 || (memcmp(buffer, "RIFF", 4) && memcmp(buffer, "RF64", 4)) || memcmp(buffer+8, "WAVE", 4)) {
                if(error) *error = "unsupported RIFF layout (002)";
                return false;
        }
+       if(!memcmp(buffer, "RF64", 4))
+               fm->is_rf64 = true;
+       if(fm->is_rf64 && sizeof(FLAC__off_t) < 8) {
+               if(error) *error = "RF64 is not supported on this compile (r00)";
+               return false;
+       }
        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);*/
+       if(!fm->is_rf64 || unpack32le_(buffer+4) != 0xffffffffu)
+               eof_offset = (FLAC__off_t)8 + (FLAC__off_t)unpack32le_(buffer+4);
        while(!feof(f)) {
                FLAC__uint32 size;
                if((offset = ftello(f)) < 0) {
@@ -226,15 +236,77 @@ static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char **
                                return false;
                        }
                        fm->audio_block = fm->num_blocks;
+                       if(fm->is_rf64 && fm->num_blocks < 2) {
+                               if(error) *error = "invalid RF64 file: \"data\" chunk before \"ds64\" chunk (r01)";
+                               return false;
+                       }
                }
                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);*/
-               if(fseeko(f, size, SEEK_CUR) < 0) {
-                       if(error) *error = "invalid WAVE file: seek error (009)";
-                       return false;
+               /* parse ds64 chunk if necessary */
+               if(fm->is_rf64 && fm->num_blocks == 2) {
+                       FLAC__byte buffer2[7*4];
+                       if(memcmp(buffer, "ds64", 4)) {
+                               if(error) *error = "invalid RF64 file: \"ds64\" chunk does not immediately follow \"WAVE\" marker (r02)";
+                               return false;
+                       }
+                       /* unpack the size again since we don't want the padding byte effect */
+                       size = unpack32le_(buffer+4);
+                       if(size < sizeof(buffer2)) {
+                               if(error) *error = "invalid RF64 file: \"ds64\" chunk size is < 28 (r03)";
+                               return false;
+                       }
+                       if(size > sizeof(buffer2)) {
+                               if(error) *error = "RF64 file has \"ds64\" chunk with extra size table, which is not currently supported (r04)";
+                               return false;
+                       }
+                       if(fread(buffer2, 1, sizeof(buffer2), f) < sizeof(buffer2)) {
+                               if(error) *error = "unexpected EOF reading \"ds64\" chunk data in RF64 file (r05)";
+                               return false;
+                       }
+                       ds64_data_size = (FLAC__off_t)unpack64le_(buffer2+8);
+                       if(ds64_data_size == (FLAC__off_t)(-1)) {
+                               if(error) *error = "RF64 file has \"ds64\" chunk with data size == -1 (r08)";
+                               return false;
+                       }
+                       /* check if pad byte needed */
+                       if(ds64_data_size & 1)
+                               ds64_data_size++;
+                       /* @@@ [2^63 limit] */
+                       if(ds64_data_size < 0) {
+                               if(error) *error = "RF64 file too large (r09)";
+                               return false;
+                       }
+                       if(unpack32le_(buffer2+24)) {
+                               if(error) *error = "RF64 file has \"ds64\" chunk with extra size table, which is not currently supported (r06)";
+                               return false;
+                       }
+                       eof_offset = (FLAC__off_t)8 + (FLAC__off_t)unpack64le_(buffer2);
+                       /* @@@ [2^63 limit] */
+                       if((FLAC__off_t)unpack64le_(buffer2) < 0 || eof_offset < 0) {
+                               if(error) *error = "RF64 file too large (r07)";
+                               return false;
+                       }
+               }
+               else { /* skip to next chunk */
+                       if(fm->is_rf64 && !memcmp(buffer, "data", 4) && unpack32le_(buffer+4) == 0xffffffffu) {
+                               if(fseeko(f, ds64_data_size, SEEK_CUR) < 0) {
+                                       if(error) *error = "invalid RF64 file: seek error (r10)";
+                                       return false;
+                               }
+                       }
+                       else {
+                               if(fseeko(f, size, SEEK_CUR) < 0) {
+                                       if(error) *error = "invalid WAVE file: seek error (009)";
+                                       return false;
+                               }
+                       }
                }
        }
+       if(fm->is_rf64 && eof_offset == (FLAC__off_t)(-1)) {
+               if(error) *error = "invalid RF64 file: all RIFF sizes are -1 (r11)";
+               return false;
+       }
        if(eof_offset != ftello(f)) {
                if(error) *error = "invalid WAVE file: unexpected EOF (010)";
                return false;
@@ -250,6 +322,94 @@ static FLAC__bool read_from_wave_(foreign_metadata_t *fm, FILE *f, const char **
        return true;
 }
 
+static FLAC__bool read_from_wave64_(foreign_metadata_t *fm, FILE *f, const char **error)
+{
+       FLAC__byte buffer[40];
+       FLAC__off_t offset, eof_offset = -1;
+       if((offset = ftello(f)) < 0) {
+               if(error) *error = "ftello() error (001)";
+               return false;
+       }
+       if(
+               fread(buffer, 1, 40, f) < 40 ||
+               /* RIFF GUID 66666972-912E-11CF-A5D6-28DB04C10000 */
+               memcmp(buffer, "\x72\x69\x66\x66\x2E\x91\xCF\x11\xA5\xD6\x28\xDB\x04\xC1\x00\x00", 16) ||
+               /* WAVE GUID 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
+               memcmp(buffer+24, "\x77\x61\x76\x65\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A", 16)
+       ) {
+               if(error) *error = "unsupported Wave64 layout (002)";
+               return false;
+       }
+       if(sizeof(FLAC__off_t) < 8) {
+               if(error) *error = "Wave64 is not supported on this compile (r00)";
+               return false;
+       }
+       if(!append_block_(fm, offset, 40, error))
+               return false;
+       eof_offset = (FLAC__off_t)unpack64le_(buffer+16); /*@@@ [2^63 limit] */
+       while(!feof(f)) {
+               FLAC__uint64 size;
+               if((offset = ftello(f)) < 0) {
+                       if(error) *error = "ftello() error (003)";
+                       return false;
+               }
+               if((size = fread(buffer, 1, 24, f)) < 24) {
+                       if(size == 0 && feof(f))
+                               break;
+                       if(error) *error = "invalid Wave64 file (004)";
+                       return false;
+               }
+               size = unpack64le_(buffer+16);
+               /* check if pad bytes needed */
+               if(size & 7)
+                       size = (size+7) & (~((FLAC__uint64)7));
+               /* fmt GUID 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
+               if(!memcmp(buffer, "\x66\x6D\x74\x20\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A", 16)) {
+                       if(fm->format_block) {
+                               if(error) *error = "invalid Wave64 file: multiple \"fmt \" chunks (005)";
+                               return false;
+                       }
+                       if(fm->audio_block) {
+                               if(error) *error = "invalid Wave64 file: \"data\" chunk before \"fmt \" chunk (006)";
+                               return false;
+                       }
+                       fm->format_block = fm->num_blocks;
+               }
+               /* data GUID 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
+               else if(!memcmp(buffer, "\x64\x61\x74\x61\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A", 16)) {
+                       if(fm->audio_block) {
+                               if(error) *error = "invalid Wave64 file: multiple \"data\" chunks (007)";
+                               return false;
+                       }
+                       if(!fm->format_block) {
+                               if(error) *error = "invalid Wave64 file: \"data\" chunk before \"fmt \" chunk (008)";
+                               return false;
+                       }
+                       fm->audio_block = fm->num_blocks;
+               }
+               if(!append_block_(fm, offset, memcmp(buffer, "\x64\x61\x74\x61\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A", 16)? (FLAC__uint32)size : 16+8, error))
+                       return false;
+               /* skip to next chunk */
+               if(fseeko(f, size-24, SEEK_CUR) < 0) {
+                       if(error) *error = "invalid Wave64 file: seek error (009)";
+                       return false;
+               }
+       }
+       if(eof_offset != ftello(f)) {
+               if(error) *error = "invalid Wave64 file: unexpected EOF (010)";
+               return false;
+       }
+       if(!fm->format_block) {
+               if(error) *error = "invalid Wave64 file: missing \"fmt \" chunk (011)";
+               return false;
+       }
+       if(!fm->audio_block) {
+               if(error) *error = "invalid Wave64 file: missing \"data\" chunk (012)";
+               return false;
+       }
+       return true;
+}
+
 static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout, FLAC__Metadata_SimpleIterator *it, const char **error)
 {
        FLAC__byte buffer[4];
@@ -269,7 +429,6 @@ 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));*/
                /* transfer chunk into APPLICATION block */
                /* first set up the file pointers */
                if(fseeko(fin, fm->blocks[block_num].offset, SEEK_SET) < 0) {
@@ -310,8 +469,8 @@ static FLAC__bool write_to_flac_(foreign_metadata_t *fm, FILE *fin, FILE *fout,
 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__off_t offset;
+       FLAC__bool type_found = false, ds64_found = false;
 
        FLAC__ASSERT(FLAC__STREAM_METADATA_APPLICATION_ID_LEN == sizeof(id)*8);
 
@@ -319,7 +478,7 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
                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)";
+                       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)))
@@ -329,16 +488,19 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
                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)";
+               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 (005)";
+                       if(error) *error = "read error (004)";
                        return false;
                }
-               if(fm->num_blocks == 0) {
-                       if(fm->type == FOREIGN_BLOCK_TYPE__RIFF && 0 == memcmp(buffer, "RIFF", 4))
+               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;
@@ -376,32 +538,63 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
                                }
                                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 (011)";
+                                       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 (012)";
+                                       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 (013)";
+                                       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 (014)";
+                                       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 (015)";
+                                       if(error) *error = "read error (020)";
                                        return false;
                                }
                                fm->ssnd_offset_size = unpack32be_(buffer+8);
@@ -410,35 +603,48 @@ static FLAC__bool read_from_flac_(foreign_metadata_t *fm, FILE *f, FLAC__Metadat
                else {
                        FLAC__ASSERT(0);
                        /* double protection: */
-                       if(error) *error = "unsupported foreign metadata found, may need newer FLAC decoder (016)";
+                       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 (017)";
+               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 (018)" : "invalid AIFF file: missing \"COMM\" chunk (018)";
+               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 (019)" : "invalid AIFF file: missing \"SSND\" chunk (019)";
+               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;
 }
 
-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)
+static FLAC__bool write_to_iff_(foreign_metadata_t *fm, FILE *fin, FILE *fout, FLAC__off_t offset1, FLAC__off_t offset2, FLAC__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++) {
+       /* don't write first (RIFF/RF64/FORM) chunk, or ds64 chunk in the case of RF64 */
+       for(i = fm->is_rf64?2: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;
@@ -475,9 +681,12 @@ static FLAC__bool write_to_iff_(foreign_metadata_t *fm, FILE *fin, FILE *fout, o
 
 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)
+       /* calloc() to zero all the member variables */
+       foreign_metadata_t *x = calloc(sizeof(foreign_metadata_t), 1);
+       if(x) {
                x->type = type;
+               x->is_rf64 = false;
+       }
        return x;
 }
 
@@ -493,7 +702,7 @@ void flac__foreign_metadata_delete(foreign_metadata_t *fm)
 FLAC__bool flac__foreign_metadata_read_from_aiff(foreign_metadata_t *fm, const char *filename, const char **error)
 {
        FLAC__bool ok;
-       FILE *f = fopen(filename, "rb");
+       FILE *f = flac_fopen(filename, "rb");
        if(!f) {
                if(error) *error = "can't open AIFF file for reading (000)";
                return false;
@@ -506,7 +715,7 @@ FLAC__bool flac__foreign_metadata_read_from_aiff(foreign_metadata_t *fm, const c
 FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const char *filename, const char **error)
 {
        FLAC__bool ok;
-       FILE *f = fopen(filename, "rb");
+       FILE *f = flac_fopen(filename, "rb");
        if(!f) {
                if(error) *error = "can't open WAVE file for reading (000)";
                return false;
@@ -516,6 +725,19 @@ FLAC__bool flac__foreign_metadata_read_from_wave(foreign_metadata_t *fm, const c
        return ok;
 }
 
+FLAC__bool flac__foreign_metadata_read_from_wave64(foreign_metadata_t *fm, const char *filename, const char **error)
+{
+       FLAC__bool ok;
+       FILE *f = flac_fopen(filename, "rb");
+       if(!f) {
+               if(error) *error = "can't open Wave64 file for reading (000)";
+               return false;
+       }
+       ok = read_from_wave64_(fm, f, error);
+       fclose(f);
+       return ok;
+}
+
 FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const char *infilename, const char *outfilename, const char **error)
 {
        FLAC__bool ok;
@@ -530,12 +752,12 @@ FLAC__bool flac__foreign_metadata_write_to_flac(foreign_metadata_t *fm, const ch
                FLAC__metadata_simple_iterator_delete(it);
                return false;
        }
-       if(0 == (fin = fopen(infilename, "rb"))) {
+       if(0 == (fin = flac_fopen(infilename, "rb"))) {
                if(error) *error = "can't open WAVE/AIFF file for reading (002)";
                FLAC__metadata_simple_iterator_delete(it);
                return false;
        }
-       if(0 == (fout = fopen(outfilename, "r+b"))) {
+       if(0 == (fout = flac_fopen(outfilename, "r+b"))) {
                if(error) *error = "can't open FLAC file for updating (003)";
                FLAC__metadata_simple_iterator_delete(it);
                fclose(fin);
@@ -562,7 +784,7 @@ FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const c
                FLAC__metadata_simple_iterator_delete(it);
                return false;
        }
-       if(0 == (f = fopen(filename, "rb"))) {
+       if(0 == (f = flac_fopen(filename, "rb"))) {
                if(error) *error = "can't open FLAC file for reading (002)";
                FLAC__metadata_simple_iterator_delete(it);
                return false;
@@ -573,15 +795,15 @@ FLAC__bool flac__foreign_metadata_read_from_flac(foreign_metadata_t *fm, const c
        return ok;
 }
 
-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 flac__foreign_metadata_write_to_iff(foreign_metadata_t *fm, const char *infilename, const char *outfilename, FLAC__off_t offset1, FLAC__off_t offset2, FLAC__off_t offset3, const char **error)
 {
        FLAC__bool ok;
        FILE *fin, *fout;
-       if(0 == (fin = fopen(infilename, "rb"))) {
+       if(0 == (fin = flac_fopen(infilename, "rb"))) {
                if(error) *error = "can't open FLAC file for reading (000)";
                return false;
        }
-       if(0 == (fout = fopen(outfilename, "r+b"))) {
+       if(0 == (fout = flac_fopen(outfilename, "r+b"))) {
                if(error) *error = "can't open WAVE/AIFF file for updating (001)";
                fclose(fin);
                return false;