From 972aa1feef27027f59d41dc925d0a3c83a37f11d Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 6 Aug 2014 11:53:49 +0900 Subject: [PATCH 01/46] Replaced codecvt with Win32 API. --- ConfigureChecks.cmake | 10 ----- config.h.cmake | 3 -- taglib/CMakeLists.txt | 21 +++++---- taglib/toolkit/tstring.cpp | 87 +++++++++++++------------------------- taglib/toolkit/unicode.h | 5 --- 5 files changed, 42 insertions(+), 84 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 23830f93..d0069f57 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -220,16 +220,6 @@ if(NOT HAVE_SNPRINTF) " HAVE_SPRINTF_S) endif() -# Determine whether your compiler supports codecvt. - -check_cxx_source_compiles(" - #include - int main() { - std::codecvt_utf8_utf16 x; - return 0; - } -" HAVE_STD_CODECVT) - # Check for libz using the cmake supplied FindZLIB.cmake if(NOT ZLIB_SOURCE) diff --git a/config.h.cmake b/config.h.cmake index 07abffa6..8fd8391d 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -17,9 +17,6 @@ #cmakedefine HAVE_MAC_BYTESWAP 1 #cmakedefine HAVE_OPENBSD_BYTESWAP 1 -/* Defined if your compiler supports codecvt */ -#cmakedefine HAVE_STD_CODECVT 1 - /* Defined if your compiler supports some atomic operations */ #cmakedefine HAVE_STD_ATOMIC 1 #cmakedefine HAVE_BOOST_ATOMIC 1 diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index eebe61a5..2eadf391 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -303,9 +303,14 @@ set(toolkit_SRCS toolkit/tpropertymap.cpp toolkit/trefcounter.cpp toolkit/tdebuglistener.cpp - toolkit/unicode.cpp ) +if(NOT WIN32) + set(unicode_SRCS + toolkit/unicode.cpp + ) +endif() + if(HAVE_ZLIB_SOURCE) set(zlib_SRCS ${ZLIB_SOURCE}/adler32.c @@ -323,13 +328,14 @@ set(tag_LIB_SRCS ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} + ${unicode_SRCS} ${zlib_SRCS} tag.cpp tagunion.cpp fileref.cpp audioproperties.cpp ) -add_library(tag ${tag_LIB_SRCS} ${zlib_SRCS} ${tag_HDRS}) +add_library(tag ${tag_LIB_SRCS} ${tag_HDRS}) if(ZLIB_FOUND) target_link_libraries(tag ${ZLIB_LIBRARIES}) @@ -348,10 +354,9 @@ if(BUILD_FRAMEWORK) endif() install(TARGETS tag - FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib + FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + RUNTIME DESTINATION ${BIN_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib ) - diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 8287244b..aae26edf 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -39,101 +39,72 @@ #include #include -#ifdef HAVE_STD_CODECVT -# include +#ifdef _WIN32 +# include #else # include "unicode.h" #endif namespace { + using namespace TagLib; - void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) + inline void UTF16toUTF8( + const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) { -#ifdef HAVE_STD_CODECVT +#ifdef _WIN32 - typedef std::codecvt_utf8_utf16 utf8_utf16_t; - - using namespace TagLib; - - const wchar_t *srcBegin = src; - const wchar_t *srcEnd = srcBegin + srcLength; - - char *dstBegin = dst; - char *dstEnd = dstBegin + dstLength; - - std::mbstate_t st; - const wchar_t *source; - char *target; - memset(&st, 0, sizeof(st)); - std::codecvt_base::result result = utf8_utf16_t().out( - st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); - - if(result != utf8_utf16_t::ok) { + const int len = ::WideCharToMultiByte( + CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL); + if(len == 0) { debug("String::UTF16toUTF8() - Unicode conversion error."); } #else using namespace Unicode; - using namespace TagLib; - const Unicode::UTF16 *srcBegin = src; - const Unicode::UTF16 *srcEnd = srcBegin + srcLength; + const UTF16 *srcBegin = src; + const UTF16 *srcEnd = srcBegin + srcLength; - Unicode::UTF8 *dstBegin = reinterpret_cast(dst); - Unicode::UTF8 *dstEnd = dstBegin + dstLength; + UTF8 *dstBegin = reinterpret_cast(dst); + UTF8 *dstEnd = dstBegin + dstLength; - Unicode::ConversionResult result = Unicode::ConvertUTF16toUTF8( - &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + ConversionResult result = ConvertUTF16toUTF8( + &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != Unicode::conversionOK) { + if(result != conversionOK) { debug("String::UTF16toUTF8() - Unicode conversion error."); } #endif } - void UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) + inline void UTF8toUTF16( + const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) { -#ifdef HAVE_STD_CODECVT +#ifdef _WIN32 - typedef std::codecvt_utf8_utf16 utf8_utf16_t; - - using namespace TagLib; - - const char *srcBegin = src; - const char *srcEnd = srcBegin + srcLength; - - wchar_t *dstBegin = dst; - wchar_t *dstEnd = dstBegin + dstLength; - - std::mbstate_t st; - const char *source; - wchar_t *target; - memset(&st, 0, sizeof(st)); - std::codecvt_base::result result = utf8_utf16_t().in( - st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); - - if(result != utf8_utf16_t::ok) { + const int len = ::MultiByteToWideChar( + CP_UTF8, 0, src, srcLength, dst, dstLength); + if (len == 0) { debug("String::UTF8toUTF16() - Unicode conversion error."); } #else using namespace Unicode; - using namespace TagLib; - const Unicode::UTF8 *srcBegin = reinterpret_cast(src); - const Unicode::UTF8 *srcEnd = srcBegin + srcLength; + const UTF8 *srcBegin = reinterpret_cast(src); + const UTF8 *srcEnd = srcBegin + srcLength; - Unicode::UTF16 *dstBegin = dst; - Unicode::UTF16 *dstEnd = dstBegin + dstLength; + UTF16 *dstBegin = dst; + UTF16 *dstEnd = dstBegin + dstLength; - Unicode::ConversionResult result = Unicode::ConvertUTF8toUTF16( - &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + ConversionResult result = ConvertUTF8toUTF16( + &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != Unicode::conversionOK) { + if(result != conversionOK) { debug("String::UTF8toUTF16() - Unicode conversion error."); } diff --git a/taglib/toolkit/unicode.h b/taglib/toolkit/unicode.h index ebf1915d..d3a869f0 100644 --- a/taglib/toolkit/unicode.h +++ b/taglib/toolkit/unicode.h @@ -106,11 +106,6 @@ bit mask & shift operations. ------------------------------------------------------------------------ */ -// Workaround for when MSVC doesn't have wchar_t as a built-in type. -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -# include -#endif - /* Some fundamental constants */ #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD #define UNI_MAX_BMP (UTF32)0x0000FFFF From 929829b2b547607b999a8a98158b9dfc0eb423ab Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 10 Aug 2014 01:13:25 +0900 Subject: [PATCH 02/46] Removed useless strlen() and wcslen(). --- taglib/toolkit/tstring.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index aae26edf..9bed6180 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -49,7 +49,7 @@ namespace { using namespace TagLib; - inline void UTF16toUTF8( + inline size_t UTF16toUTF8( const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) { #ifdef _WIN32 @@ -59,6 +59,7 @@ namespace if(len == 0) { debug("String::UTF16toUTF8() - Unicode conversion error."); } + return len; #else @@ -73,23 +74,29 @@ namespace ConversionResult result = ConvertUTF16toUTF8( &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != conversionOK) { + if(result == conversionOK) { + return (dstBegin - reinterpret_cast(dst)); + } + else + { debug("String::UTF16toUTF8() - Unicode conversion error."); + return 0; } #endif } - inline void UTF8toUTF16( + inline size_t UTF8toUTF16( const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) { #ifdef _WIN32 const int len = ::MultiByteToWideChar( CP_UTF8, 0, src, srcLength, dst, dstLength); - if (len == 0) { + if(len == 0) { debug("String::UTF8toUTF16() - Unicode conversion error."); } + return len; #else @@ -104,8 +111,12 @@ namespace ConversionResult result = ConvertUTF8toUTF16( &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != conversionOK) { + if(result == conversionOK) { + return (dstBegin - dst); + } + else { debug("String::UTF8toUTF16() - Unicode conversion error."); + return 0; } #endif @@ -410,8 +421,9 @@ ByteVector String::data(Type t) const { ByteVector v(size() * 4 + 1, 0); - UTF16toUTF8(d->data.c_str(), d->data.size(), v.data(), v.size()); - v.resize(::strlen(v.data())); + const size_t len = UTF16toUTF8( + d->data.c_str(), d->data.size(), v.data(), v.size()); + v.resize(len); return v; } @@ -744,8 +756,8 @@ void String::copyFromUTF8(const char *s, size_t length) d->data.resize(length); if(length > 0) { - UTF8toUTF16(s, length, &d->data[0], d->data.size()); - d->data.resize(::wcslen(d->data.c_str())); + const size_t len = UTF8toUTF16(s, length, &d->data[0], d->data.size()); + d->data.resize(len); } } From 20c0aac30987c36db7e046d1b365d46dabeff3ce Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 11 Aug 2014 01:09:07 +0900 Subject: [PATCH 03/46] Unified redundant string format functions. (backport from taglib2) --- taglib/toolkit/tdebug.cpp | 40 +++++----------------------------- taglib/toolkit/tstring.cpp | 25 +--------------------- taglib/toolkit/tutils.h | 44 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/taglib/toolkit/tdebug.cpp b/taglib/toolkit/tdebug.cpp index 71350af7..557baed6 100644 --- a/taglib/toolkit/tdebug.cpp +++ b/taglib/toolkit/tdebug.cpp @@ -30,43 +30,12 @@ #include "tdebug.h" #include "tstring.h" #include "tdebuglistener.h" +#include "tutils.h" #include #include #include -using namespace TagLib; - -namespace -{ - String format(const char *fmt, ...) - { - va_list args; - va_start(args, fmt); - - char buf[256]; - -#if defined(HAVE_SNPRINTF) - - vsnprintf(buf, sizeof(buf), fmt, args); - -#elif defined(HAVE_SPRINTF_S) - - vsprintf_s(buf, fmt, args); - -#else - - // Be careful. May cause a buffer overflow. - vsprintf(buf, fmt, args); - -#endif - - va_end(args); - - return String(buf); - } -} - namespace TagLib { // The instance is defined in tdebuglistener.cpp. @@ -75,7 +44,7 @@ namespace TagLib void debug(const String &s) { #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - + debugListener->printMessage("TagLib: " + s + "\n"); #endif @@ -85,10 +54,11 @@ namespace TagLib { #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - for(size_t i = 0; i < v.size(); ++i) + for(size_t i = 0; i < v.size(); ++i) { std::string bits = std::bitset<8>(v[i]).to_string(); - String msg = format("*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n", + String msg = Utils::formatString( + "*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n", i, v[i], v[i], v[i], bits.c_str()); debugListener->printMessage(msg); diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 8287244b..47624e32 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -568,30 +568,7 @@ bool String::isAscii() const String String::number(int n) // static { - static const size_t BufferSize = 11; // Sufficient to store "-214748364". - static const char *Format = "%d"; - - char buffer[BufferSize]; - int length; - -#if defined(HAVE_SNPRINTF) - - length = snprintf(buffer, BufferSize, Format, n); - -#elif defined(HAVE_SPRINTF_S) - - length = sprintf_s(buffer, Format, n); - -#else - - length = sprintf(buffer, Format, n); - -#endif - - if(length > 0) - return String(buffer); - else - return String::null; + return Utils::formatString("%d", n); } TagLib::wchar &String::operator[](int i) diff --git a/taglib/toolkit/tutils.h b/taglib/toolkit/tutils.h index 0874684a..92486e23 100644 --- a/taglib/toolkit/tutils.h +++ b/taglib/toolkit/tutils.h @@ -44,6 +44,9 @@ # include #endif +#include "tstring.h" +#include +#include #include namespace TagLib @@ -147,6 +150,47 @@ namespace TagLib #endif } + inline String formatString(const char *format, ...) + { + // Sufficient buffer size for the current internal uses. + // Consider changing this value when you use this function. + + static const size_t BufferSize = 128; + + va_list args; + va_start(args, format); + + char buf[BufferSize]; + int length; + +#if defined(HAVE_SNPRINTF) + + length = vsnprintf(buf, BufferSize, format, args); + +#elif defined(HAVE_SPRINTF_S) + + length = vsprintf_s(buf, format, args); + +#else + + // The last resort. May cause a buffer overflow. + + length = vsprintf(buf, format, args); + if(length >= BufferSize) { + debug("Utils::formatString() - Buffer overflow! Returning an empty string."); + length = -1; + } + +#endif + + va_end(args); + + if(length != -1) + return String(buf); + else + return String::null; + } + enum ByteOrder { LittleEndian, From a055933e10b65ea35817ff2dddf63a9b96ee25fe Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 12 Aug 2014 13:33:25 +0900 Subject: [PATCH 04/46] Unified the same debug messages. --- taglib/toolkit/tstring.cpp | 47 ++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 9bed6180..b311cfbb 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -52,14 +52,11 @@ namespace inline size_t UTF16toUTF8( const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) { + size_t len = 0; + #ifdef _WIN32 - const int len = ::WideCharToMultiByte( - CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL); - if(len == 0) { - debug("String::UTF16toUTF8() - Unicode conversion error."); - } - return len; + len = ::WideCharToMultiByte(CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL); #else @@ -74,29 +71,25 @@ namespace ConversionResult result = ConvertUTF16toUTF8( &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result == conversionOK) { - return (dstBegin - reinterpret_cast(dst)); - } - else - { - debug("String::UTF16toUTF8() - Unicode conversion error."); - return 0; - } + if(result == conversionOK) + len = dstBegin - reinterpret_cast(dst); #endif + + if(len == 0) + debug("String::UTF16toUTF8() - Unicode conversion error."); + + return len; } inline size_t UTF8toUTF16( const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) { + size_t len = 0; + #ifdef _WIN32 - const int len = ::MultiByteToWideChar( - CP_UTF8, 0, src, srcLength, dst, dstLength); - if(len == 0) { - debug("String::UTF8toUTF16() - Unicode conversion error."); - } - return len; + len = ::MultiByteToWideChar(CP_UTF8, 0, src, srcLength, dst, dstLength); #else @@ -111,15 +104,15 @@ namespace ConversionResult result = ConvertUTF8toUTF16( &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result == conversionOK) { - return (dstBegin - dst); - } - else { - debug("String::UTF8toUTF16() - Unicode conversion error."); - return 0; - } + if(result == conversionOK) + len = dstBegin - dst; #endif + + if(len == 0) + debug("String::UTF8toUTF16() - Unicode conversion error."); + + return len; } } From f29c5f45f8af33532ec768bae6c826e700139d62 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 4 Aug 2014 23:45:59 +0900 Subject: [PATCH 05/46] Workaround for 64-bit MP4 atoms. --- ConfigureChecks.cmake | 51 +++++++++++++++++++++++------------------- config.h.cmake | 3 +++ taglib/mp4/mp4atom.cpp | 14 +++++++++--- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index bd7c5658..cae533e3 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -20,6 +20,11 @@ if(NOT ${SIZEOF_INT} EQUAL 4) MESSAGE(FATAL_ERROR "TagLib requires that int is 32-bit wide.") endif() +check_type_size("long" SIZEOF_LONG) +if(${SIZEOF_LONG} EQUAL 8) + set(LONG_IS_INT64 1) +endif() + check_type_size("long long" SIZEOF_LONGLONG) if(NOT ${SIZEOF_LONGLONG} EQUAL 8) MESSAGE(FATAL_ERROR "TagLib requires that long long is 64-bit wide.") @@ -66,65 +71,65 @@ endif() check_cxx_source_compiles(" #include - int main() { + int main() { std::atomic x; x.fetch_add(1); x.fetch_sub(1); - return 0; + return 0; } " HAVE_STD_ATOMIC) if(NOT HAVE_STD_ATOMIC) check_cxx_source_compiles(" #include - int main() { + int main() { boost::atomic x(1); x.fetch_add(1); x.fetch_sub(1); - return 0; + return 0; } " HAVE_BOOST_ATOMIC) if(NOT HAVE_BOOST_ATOMIC) check_cxx_source_compiles(" - int main() { + int main() { volatile int x; __sync_add_and_fetch(&x, 1); int y = __sync_sub_and_fetch(&x, 1); - return 0; + return 0; } " HAVE_GCC_ATOMIC) if(NOT HAVE_GCC_ATOMIC) check_cxx_source_compiles(" #include - int main() { + int main() { volatile int32_t x; OSAtomicIncrement32Barrier(&x); int32_t y = OSAtomicDecrement32Barrier(&x); - return 0; + return 0; } " HAVE_MAC_ATOMIC) if(NOT HAVE_MAC_ATOMIC) check_cxx_source_compiles(" #include - int main() { + int main() { volatile LONG x; InterlockedIncrement(&x); LONG y = InterlockedDecrement(&x); - return 0; + return 0; } " HAVE_WIN_ATOMIC) if(NOT HAVE_WIN_ATOMIC) check_cxx_source_compiles(" #include - int main() { + int main() { volatile int x; __sync_add_and_fetch(&x, 1); int y = __sync_sub_and_fetch(&x, 1); - return 0; + return 0; } " HAVE_IA64_ATOMIC) endif() @@ -135,26 +140,26 @@ endif() # Determine which kind of byte swap functions your compiler supports. -# GCC's __builtin_bswap* should be checked individually +# GCC's __builtin_bswap* should be checked individually # because some of them can be missing depends on the GCC version. check_cxx_source_compiles(" int main() { __builtin_bswap16(0); - return 0; + return 0; } " HAVE_GCC_BYTESWAP_16) check_cxx_source_compiles(" int main() { __builtin_bswap32(0); - return 0; + return 0; } " HAVE_GCC_BYTESWAP_32) check_cxx_source_compiles(" int main() { __builtin_bswap64(0); - return 0; + return 0; } " HAVE_GCC_BYTESWAP_64) @@ -165,7 +170,7 @@ if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP __bswap_16(0); __bswap_32(0); __bswap_64(0); - return 0; + return 0; } " HAVE_GLIBC_BYTESWAP) @@ -176,7 +181,7 @@ if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP _byteswap_ushort(0); _byteswap_ulong(0); _byteswap_uint64(0); - return 0; + return 0; } " HAVE_MSC_BYTESWAP) @@ -187,7 +192,7 @@ if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP OSSwapInt16(0); OSSwapInt32(0); OSSwapInt64(0); - return 0; + return 0; } " HAVE_MAC_BYTESWAP) @@ -198,7 +203,7 @@ if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP swap16(0); swap32(0); swap64(0); - return 0; + return 0; } " HAVE_OPENBSD_BYTESWAP) endif() @@ -224,9 +229,9 @@ endif() check_cxx_source_compiles(" #include - int main() { - std::codecvt_utf8_utf16 x; - return 0; + int main() { + std::codecvt_utf8_utf16 x; + return 0; } " HAVE_STD_CODECVT) diff --git a/config.h.cmake b/config.h.cmake index 07abffa6..abecfbfb 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -8,6 +8,9 @@ /* 1 if little-endian, 2 if big-endian. */ #cmakedefine FLOAT_BYTEORDER ${FLOAT_BYTEORDER} +/* Defined if long is 64-bit wide */ +#cmakedefine LONG_IS_INT64 ${LONG_IS_INT64} + /* Defined if your compiler supports some byte swap functions */ #cmakedefine HAVE_GCC_BYTESWAP_16 1 #cmakedefine HAVE_GCC_BYTESWAP_32 1 diff --git a/taglib/mp4/mp4atom.cpp b/taglib/mp4/mp4atom.cpp index 7b87a479..5a304e3f 100644 --- a/taglib/mp4/mp4atom.cpp +++ b/taglib/mp4/mp4atom.cpp @@ -23,6 +23,10 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include #include "mp4atom.h" @@ -50,9 +54,12 @@ MP4::Atom::Atom(File *file) length = header.toUInt(); - if (length == 1) { + if(length == 1) { const long long longLength = file->readBlock(8).toLongLong(); - if (longLength >= 8 && longLength <= 0xFFFFFFFF) { +#ifdef LONG_IS_INT64 + length = longLength; +#else + if(longLength <= 0xFFFFFFFF) { // The atom has a 64-bit length, but it's actually a 32-bit value length = (long)longLength; } @@ -62,8 +69,9 @@ MP4::Atom::Atom(File *file) file->seek(0, File::End); return; } +#endif } - if (length < 8) { + if(length < 8) { debug("MP4: Invalid atom size"); length = 0; file->seek(0, File::End); From dfbaee4103384954abe712029498054ce29d5d7c Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 5 Aug 2014 13:52:29 +0900 Subject: [PATCH 06/46] Removed an ambiguous cast from FileNameHandle to FileName. --- taglib/toolkit/tfilestream.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 93078ae9..0195302a 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -85,10 +85,14 @@ namespace #else // _WIN32 - struct FileNameHandle : public std::string + class FileNameHandle { - FileNameHandle(FileName name) : std::string(name) {} - operator FileName () const { return c_str(); } + private: + std::string name; + + public: + FileNameHandle(FileName n) : name(n) {} + FileName toFileName() const { return name.c_str(); } }; typedef FILE* FileHandle; @@ -122,12 +126,10 @@ namespace class FileStream::FileStreamPrivate { public: - FileStreamPrivate(const FileName &fileName) - : file(InvalidFileHandle) - , name(fileName) - , readOnly(true) - { - } + FileStreamPrivate(const FileName &fileName) : + file(InvalidFileHandle), + name(fileName), + readOnly(true) {} FileHandle file; FileNameHandle name; @@ -138,8 +140,8 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -FileStream::FileStream(FileName fileName, bool openReadOnly) - : d(new FileStreamPrivate(fileName)) +FileStream::FileStream(FileName fileName, bool openReadOnly) : + d(new FileStreamPrivate(fileName)) { // First try with read / write mode, if that fails, fall back to read only. @@ -156,7 +158,7 @@ FileStream::FileStream(FileName fileName, bool openReadOnly) # ifdef _WIN32 debug("Could not open file " + fileName.toString()); # else - debug("Could not open file " + String(static_cast(d->name))); + debug("Could not open file " + String(d->name.toFileName())); # endif } } @@ -171,7 +173,11 @@ FileStream::~FileStream() FileName FileStream::name() const { +# ifdef _WIN32 return d->name; +# else + return d->name.toFileName(); +# endif } ByteVector FileStream::readBlock(ulong length) From 0e6d8617ae38ded8bb9c5d030c4e90a5a5838e5f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 23 Aug 2014 00:01:11 +0900 Subject: [PATCH 07/46] Revert "Removed an ambiguous cast from FileNameHandle to FileName." This reverts commit 9af7601baee66ed8fa7cac01b5dc272dd2544ee5. --- taglib/toolkit/tfilestream.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 0195302a..93078ae9 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -85,14 +85,10 @@ namespace #else // _WIN32 - class FileNameHandle + struct FileNameHandle : public std::string { - private: - std::string name; - - public: - FileNameHandle(FileName n) : name(n) {} - FileName toFileName() const { return name.c_str(); } + FileNameHandle(FileName name) : std::string(name) {} + operator FileName () const { return c_str(); } }; typedef FILE* FileHandle; @@ -126,10 +122,12 @@ namespace class FileStream::FileStreamPrivate { public: - FileStreamPrivate(const FileName &fileName) : - file(InvalidFileHandle), - name(fileName), - readOnly(true) {} + FileStreamPrivate(const FileName &fileName) + : file(InvalidFileHandle) + , name(fileName) + , readOnly(true) + { + } FileHandle file; FileNameHandle name; @@ -140,8 +138,8 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -FileStream::FileStream(FileName fileName, bool openReadOnly) : - d(new FileStreamPrivate(fileName)) +FileStream::FileStream(FileName fileName, bool openReadOnly) + : d(new FileStreamPrivate(fileName)) { // First try with read / write mode, if that fails, fall back to read only. @@ -158,7 +156,7 @@ FileStream::FileStream(FileName fileName, bool openReadOnly) : # ifdef _WIN32 debug("Could not open file " + fileName.toString()); # else - debug("Could not open file " + String(d->name.toFileName())); + debug("Could not open file " + String(static_cast(d->name))); # endif } } @@ -173,11 +171,7 @@ FileStream::~FileStream() FileName FileStream::name() const { -# ifdef _WIN32 return d->name; -# else - return d->name.toFileName(); -# endif } ByteVector FileStream::readBlock(ulong length) From ee283789b788981be44092d83a1459d6af4cc93d Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Sun, 14 Sep 2014 20:04:31 +0100 Subject: [PATCH 08/46] Fix ByteVector sizes in some test cases --- tests/test_id3v2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index a77e46af..50e0fef4 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -199,7 +199,7 @@ public: "JPG" "\x01" "d\x00" - "\x00", 18); + "\x00", 14); ID3v2::AttachedPictureFrame *frame = static_cast(factory->createFrame(data, TagLib::uint(2))); @@ -218,7 +218,7 @@ public: "JPG" "\x01" "d\x00" - "\x00", 18); + "\x00", 14); ID3v2::AttachedPictureFrame *frame = static_cast(factory->createFrame(data, TagLib::uint(2))); From 0051351ebbb4e458a916a316c7487416b472d800 Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Thu, 25 Sep 2014 19:32:53 +0200 Subject: [PATCH 09/46] TableOfContents and ChapterFrame can be added to v2.3 or v2.4 tags --- taglib/mpeg/id3v2/frames/chapterframe.cpp | 18 +++-- taglib/mpeg/id3v2/frames/chapterframe.h | 8 +-- .../id3v2/frames/tableofcontentsframe.cpp | 17 +++-- .../mpeg/id3v2/frames/tableofcontentsframe.h | 70 +++++++++---------- taglib/mpeg/id3v2/id3v2framefactory.cpp | 4 +- tests/test_id3v2.cpp | 6 +- 6 files changed, 66 insertions(+), 57 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/chapterframe.cpp b/taglib/mpeg/id3v2/frames/chapterframe.cpp index c8650f9d..99bcb3a0 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.cpp +++ b/taglib/mpeg/id3v2/frames/chapterframe.cpp @@ -36,11 +36,13 @@ using namespace ID3v2; class ChapterFrame::ChapterFramePrivate { public: - ChapterFramePrivate() + ChapterFramePrivate() : + tagHeader(0) { embeddedFrameList.setAutoDelete(true); } + const ID3v2::Header *tagHeader; ByteVector elementID; uint startTime; uint endTime; @@ -54,10 +56,11 @@ public: // public methods //////////////////////////////////////////////////////////////////////////////// -ChapterFrame::ChapterFrame(const ByteVector &data) : +ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data) { d = new ChapterFramePrivate; + d->tagHeader = tagHeader; setData(data); } @@ -227,9 +230,9 @@ void ChapterFrame::parseFields(const ByteVector &data) d->endOffset = data.mid(pos, 4).toUInt(true); pos += 4; size -= pos; - while((uint)embPos < size - Frame::headerSize(4)) - { - Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos)); + + while((uint)embPos < size - header()->size()) { + Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); if(!frame) return; @@ -240,7 +243,7 @@ void ChapterFrame::parseFields(const ByteVector &data) return; } - embPos += frame->size() + Frame::headerSize(4); + embPos += frame->size() + header()->size(); addEmbeddedFrame(frame); } } @@ -261,9 +264,10 @@ ByteVector ChapterFrame::renderFields() const return data; } -ChapterFrame::ChapterFrame(const ByteVector &data, Header *h) : +ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : Frame(h) { d = new ChapterFramePrivate; + d->tagHeader = tagHeader; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/chapterframe.h b/taglib/mpeg/id3v2/frames/chapterframe.h index 96827e1f..05bdd980 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.h +++ b/taglib/mpeg/id3v2/frames/chapterframe.h @@ -47,9 +47,10 @@ namespace TagLib { public: /*! - * Creates a chapter frame based on \a data. + * Creates a chapter frame based on \a data. \a tagHeader is required as + * the internal frames are parsed based on the tag version. */ - ChapterFrame(const ByteVector &data); + ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data); /*! * Creates a chapter frame with the element ID \a eID, @@ -229,11 +230,10 @@ namespace TagLib { virtual ByteVector renderFields() const; private: + ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); ChapterFrame(const ChapterFrame &); ChapterFrame &operator=(const ChapterFrame &); - ChapterFrame(const ByteVector &data, Header *h); - class ChapterFramePrivate; ChapterFramePrivate *d; }; diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 4698b7ec..6c9dade5 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -35,11 +35,13 @@ using namespace ID3v2; class TableOfContentsFrame::TableOfContentsFramePrivate { public: - TableOfContentsFramePrivate() + TableOfContentsFramePrivate() : + tagHeader(0) { embeddedFrameList.setAutoDelete(true); } + const ID3v2::Header *tagHeader; ByteVector elementID; bool isTopLevel; bool isOrdered; @@ -52,7 +54,7 @@ public: // public methods //////////////////////////////////////////////////////////////////////////////// -TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data) : +TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ID3v2::Frame(data) { d = new TableOfContentsFramePrivate; @@ -248,9 +250,8 @@ void TableOfContentsFrame::parseFields(const ByteVector &data) } size -= pos; - while((uint)embPos < size - Frame::headerSize(4)) - { - Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos)); + while(embPos < size - header()->size()) { + Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader); if(!frame) return; @@ -261,7 +262,7 @@ void TableOfContentsFrame::parseFields(const ByteVector &data) return; } - embPos += frame->size() + Frame::headerSize(4); + embPos += frame->size() + header()->size(); addEmbeddedFrame(frame); } } @@ -290,9 +291,11 @@ ByteVector TableOfContentsFrame::renderFields() const return data; } -TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data, Header *h) : +TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, + const ByteVector &data, Header *h) : Frame(h) { d = new TableOfContentsFramePrivate; + d->tagHeader = tagHeader; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.h b/taglib/mpeg/id3v2/frames/tableofcontentsframe.h index bf578d42..532e1d0e 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.h +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.h @@ -46,9 +46,10 @@ namespace TagLib { public: /*! - * Creates a table of contents frame based on \a data. + * Creates a table of contents frame based on \a data. \a tagHeader is + * required as the internal frames are parsed based on the tag version. */ - TableOfContentsFrame(const ByteVector &data); + TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data); /*! * Creates a table of contents frame with the element ID \a eID, @@ -60,39 +61,39 @@ namespace TagLib { * Destroys the frame. */ ~TableOfContentsFrame(); - + /*! * Returns the elementID of the frame. Element ID * is a null terminated string, however it's not human-readable. - * + * * \see setElementID() */ ByteVector elementID() const; - + /*! * Returns true, if the frame is top-level (doen't have * any parent CTOC frame). - * + * * \see setIsTopLevel() */ bool isTopLevel() const; - + /*! * Returns true, if the child elements list entries * are ordered. - * + * * \see setIsOrdered() */ bool isOrdered() const; - + /*! * Returns count of child elements of the frame. It allways * corresponds to size of child elements list. - * + * * \see childElements() */ uint entryCount() const; - + /*! * Returns list of child elements of the frame. * @@ -103,55 +104,55 @@ namespace TagLib { /*! * Sets the elementID of the frame to \a eID. If \a eID isn't * null terminated, a null char is appended automatically. - * + * * \see elementID() */ void setElementID(const ByteVector &eID); - + /*! * Sets, if the frame is top-level (doen't have * any parent CTOC frame). - * + * * \see isTopLevel() */ void setIsTopLevel(const bool &t); - + /*! * Sets, if the child elements list entries * are ordered. - * + * * \see isOrdered() */ void setIsOrdered(const bool &o); - + /*! * Sets list of child elements of the frame to \a l. * * \see childElements() */ void setChildElements(const ByteVectorList &l); - + /*! * Adds \a cE to list of child elements of the frame. * * \see childElements() */ void addChildElement(const ByteVector &cE); - + /*! * Removes \a cE to list of child elements of the frame. * * \see childElements() */ void removeChildElement(const ByteVector &cE); - + /*! * Returns a reference to the frame list map. This is an FrameListMap of * all of the frames embedded in the CTOC frame. * - * This is the most convenient structure for accessing the CTOC frame's - * embedded frames. Many frame types allow multiple instances of the same - * frame type so this is a map of lists. In most cases however there will + * This is the most convenient structure for accessing the CTOC frame's + * embedded frames. Many frame types allow multiple instances of the same + * frame type so this is a map of lists. In most cases however there will * only be a single frame of a certain type. * * \warning You should not modify this data structure directly, instead @@ -160,13 +161,13 @@ namespace TagLib { * \see embeddedFrameList() */ const FrameListMap &embeddedFrameListMap() const; - + /*! - * Returns a reference to the embedded frame list. This is an FrameList + * Returns a reference to the embedded frame list. This is an FrameList * of all of the frames embedded in the CTOC frame in the order that they * were parsed. * - * This can be useful if for example you want iterate over the CTOC frame's + * This can be useful if for example you want iterate over the CTOC frame's * embedded frames in the order that they occur in the CTOC frame. * * \warning You should not modify this data structure directly, instead @@ -175,7 +176,7 @@ namespace TagLib { const FrameList &embeddedFrameList() const; /*! - * Returns the embedded frame list for frames with the id \a frameID + * Returns the embedded frame list for frames with the id \a frameID * or an empty list if there are no embedded frames of that type. This * is just a convenience and is equivalent to: * @@ -188,7 +189,7 @@ namespace TagLib { const FrameList &embeddedFrameList(const ByteVector &frameID) const; /*! - * Add an embedded frame to the CTOC frame. At this point the CTOC frame + * Add an embedded frame to the CTOC frame. At this point the CTOC frame * takes ownership of the embedded frame and will handle freeing its memory. * * \note Using this method will invalidate any pointers on the list @@ -197,7 +198,7 @@ namespace TagLib { void addEmbeddedFrame(Frame *frame); /*! - * Remove an embedded frame from the CTOC frame. If \a del is true the frame's + * Remove an embedded frame from the CTOC frame. If \a del is true the frame's * memory will be freed; if it is false, it must be deleted by the user. * * \note Using this method will invalidate any pointers on the list @@ -206,7 +207,7 @@ namespace TagLib { void removeEmbeddedFrame(Frame *frame, bool del = true); /*! - * Remove all embedded frames of type \a id from the CTOC frame and free their + * Remove all embedded frames of type \a id from the CTOC frame and free their * memory. * * \note Using this method will invalidate any pointers on the list @@ -220,16 +221,16 @@ namespace TagLib { /*! * CTOC frames each have a unique element ID. This searches for a CTOC - * frame with the element ID \a eID and returns a pointer to it. This + * frame with the element ID \a eID and returns a pointer to it. This * can be used to link together parent and child CTOC frames. * * \see elementID() */ static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); - + /*! * CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't - * any frame, which contains this frame in its child elements list). Only a single frame + * any frame, which contains this frame in its child elements list). Only a single frame * within tag can be top-level. This searches for a top-level CTOC frame. * * \see isTopLevel() @@ -241,11 +242,10 @@ namespace TagLib { virtual ByteVector renderFields() const; private: + TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); TableOfContentsFrame(const TableOfContentsFrame &); TableOfContentsFrame &operator=(const TableOfContentsFrame &); - TableOfContentsFrame(const ByteVector &data, Header *h); - class TableOfContentsFramePrivate; TableOfContentsFramePrivate *d; }; diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index e2b1d0de..fbf38adb 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -280,12 +280,12 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) // Chapter (ID3v2 chapters 1.0) if(frameID == "CHAP") - return new ChapterFrame(data, header); + return new ChapterFrame(tagHeader, data, header); // Table of contents (ID3v2 chapters 1.0) if(frameID == "CTOC") - return new TableOfContentsFrame(data, header); + return new TableOfContentsFrame(tagHeader, data, header); return new UnknownFrame(data, header); } diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 42bfaa38..fde7c4d2 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -889,6 +889,7 @@ public: void testParseChapterFrame() { ID3v2::ChapterFrame f( + new ID3v2::Header, ByteVector("CHAP" // Frame ID "\x00\x00\x00\x20" // Frame size "\x00\x00" // Frame flags @@ -915,7 +916,7 @@ public: void testRenderChapterFrame() { - ID3v2::ChapterFrame f("CHAP"); + ID3v2::ChapterFrame f(new ID3v2::Header, "CHAP"); f.setElementID(ByteVector("\x43\x00", 2)); f.setStartTime(3); f.setEndTime(5); @@ -944,6 +945,7 @@ public: void testParseTableOfContentsFrame() { ID3v2::TableOfContentsFrame f( + new ID3v2::Header, ByteVector("CTOC" // Frame ID "\x00\x00\x00\x16" // Frame size "\x00\x00" // Frame flags @@ -973,7 +975,7 @@ public: void testRenderTableOfContentsFrame() { - ID3v2::TableOfContentsFrame f("CTOC"); + ID3v2::TableOfContentsFrame f(new ID3v2::Header, "CTOC"); f.setElementID(ByteVector("\x54\x00", 2)); f.setIsTopLevel(false); f.setIsOrdered(true); From cff8d2281836d00d247fefdeae65641d1360bceb Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Thu, 25 Sep 2014 20:41:19 +0200 Subject: [PATCH 10/46] Missing assignment --- taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 6c9dade5..7b6bb9f1 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -58,6 +58,7 @@ TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ID3v2::Frame(data) { d = new TableOfContentsFramePrivate; + d->tagHeader = tagHeader; setData(data); } From 71c1ce375f33a74a1a818b50503fd73e52010bc0 Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Thu, 25 Sep 2014 20:45:45 +0200 Subject: [PATCH 11/46] Don't leak --- tests/test_id3v2.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index fde7c4d2..ac8de33b 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -888,8 +888,9 @@ public: void testParseChapterFrame() { + ID3v2::Header header; ID3v2::ChapterFrame f( - new ID3v2::Header, + &header, ByteVector("CHAP" // Frame ID "\x00\x00\x00\x20" // Frame size "\x00\x00" // Frame flags @@ -916,7 +917,8 @@ public: void testRenderChapterFrame() { - ID3v2::ChapterFrame f(new ID3v2::Header, "CHAP"); + ID3v2::Header header; + ID3v2::ChapterFrame f(&header, "CHAP"); f.setElementID(ByteVector("\x43\x00", 2)); f.setStartTime(3); f.setEndTime(5); @@ -944,8 +946,9 @@ public: void testParseTableOfContentsFrame() { + ID3v2::Header header; ID3v2::TableOfContentsFrame f( - new ID3v2::Header, + &header, ByteVector("CTOC" // Frame ID "\x00\x00\x00\x16" // Frame size "\x00\x00" // Frame flags @@ -975,7 +978,8 @@ public: void testRenderTableOfContentsFrame() { - ID3v2::TableOfContentsFrame f(new ID3v2::Header, "CTOC"); + ID3v2::Header header; + ID3v2::TableOfContentsFrame f(&header, "CTOC"); f.setElementID(ByteVector("\x54\x00", 2)); f.setIsTopLevel(false); f.setIsOrdered(true); From bd7419f0bdc9eb818776b6e42e8dad9784dc261f Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Thu, 25 Sep 2014 22:19:09 +0200 Subject: [PATCH 12/46] Rebuild TRDC from v2.3 fields This fixes an issue that was reported to me via email with the recording date being thrown away from v2.3 tags. --- taglib/mpeg/id3v2/id3v2framefactory.cpp | 22 ++++++++++++++++++++++ taglib/mpeg/id3v2/id3v2framefactory.h | 8 ++++++++ taglib/mpeg/id3v2/id3v2tag.cpp | 4 +++- tests/test_id3v2.cpp | 2 +- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index fbf38adb..507a24d3 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -290,6 +290,28 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) return new UnknownFrame(data, header); } +void FrameFactory::rebuildAggregateFrames(Tag *tag) const +{ + if(tag->header()->majorVersion() < 4 && + tag->frameList("TDRC").size() == 1 && + tag->frameList("TDAT").size() == 1) + { + TextIdentificationFrame *trdc = + static_cast(tag->frameList("TDRC").front()); + UnknownFrame *tdat = + static_cast(tag->frameList("TDAT").front()); + + if(trdc->fieldList().size() == 1 && + trdc->fieldList().front().size() == 4 && + tdat->data().size() >= 5) + { + String date(tdat->data().mid(1), String::Type(tdat->data()[0])); + if(date.length() == 4) + trdc->setText(trdc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2)); + } + } +} + String::Type FrameFactory::defaultTextEncoding() const { return d->defaultEncoding; diff --git a/taglib/mpeg/id3v2/id3v2framefactory.h b/taglib/mpeg/id3v2/id3v2framefactory.h index 762d7eb3..d991fb2b 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/taglib/mpeg/id3v2/id3v2framefactory.h @@ -93,6 +93,14 @@ namespace TagLib { // BIC: make virtual Frame *createFrame(const ByteVector &data, Header *tagHeader) const; + /*! + * After a tag has been read, this tries to rebuild some of them + * information, most notably the recording date, from frames that + * have been deprecated and can't be upgraded directly. + */ + // BIC: Make virtual + void rebuildAggregateFrames(Tag *tag) const; + /*! * Returns the default text encoding for text frames. If setTextEncoding() * has not been explicitly called this will only be used for new text diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 715cb1bc..c6aa4f93 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -693,7 +693,7 @@ void ID3v2::Tag::parse(const ByteVector &origData) } d->paddingSize = frameDataLength - frameDataPosition; - return; + break; } Frame *frame = d->factory->createFrame(data.mid(frameDataPosition), @@ -712,6 +712,8 @@ void ID3v2::Tag::parse(const ByteVector &origData) frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion()); addFrame(frame); } + + d->factory->rebuildAggregateFrames(this); } void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value) diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index ac8de33b..ebe86fc3 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -687,7 +687,7 @@ public: tf = static_cast(bar.ID3v2Tag()->frameList("TDRC").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), tf->fieldList().size()); - CPPUNIT_ASSERT_EQUAL(String("2012"), tf->fieldList().front()); + CPPUNIT_ASSERT_EQUAL(String("2012-04-17"), tf->fieldList().front()); tf = dynamic_cast(bar.ID3v2Tag()->frameList("TIPL").front()); CPPUNIT_ASSERT(tf); CPPUNIT_ASSERT_EQUAL(TagLib::uint(8), tf->fieldList().size()); From a8bfcd81be5fb4db58d59c633e04ccebbc45c751 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 16 Oct 2014 07:19:31 +0900 Subject: [PATCH 13/46] Fix a compilation error on MSVC. --- taglib/mpeg/id3v2/id3v2framefactory.cpp | 2 +- taglib/mpeg/id3v2/id3v2framefactory.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 507a24d3..f6a4aac9 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -290,7 +290,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) return new UnknownFrame(data, header); } -void FrameFactory::rebuildAggregateFrames(Tag *tag) const +void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const { if(tag->header()->majorVersion() < 4 && tag->frameList("TDRC").size() == 1 && diff --git a/taglib/mpeg/id3v2/id3v2framefactory.h b/taglib/mpeg/id3v2/id3v2framefactory.h index d991fb2b..d5dcec32 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/taglib/mpeg/id3v2/id3v2framefactory.h @@ -99,7 +99,7 @@ namespace TagLib { * have been deprecated and can't be upgraded directly. */ // BIC: Make virtual - void rebuildAggregateFrames(Tag *tag) const; + void rebuildAggregateFrames(ID3v2::Tag *tag) const; /*! * Returns the default text encoding for text frames. If setTextEncoding() From cfad95144270bad3d1e7861747fc89dc9f5c733f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 20 Oct 2014 11:27:30 +0900 Subject: [PATCH 14/46] Use EditorConfig to help us stick to our coding style. http://editorconfig.org/ --- .editorconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..32b8a7df --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Non-specified newlines with a newline ending every file +[*] +insert_final_newline = true + +# 2 space indentation +[*.{h,cpp,tcc,cmake}] +indent_style = space +indent_size = 2 + +# Trim traling whitespaces +[*.{h,cpp,tcc,cmake}] +trim_trailing_whitespace = true + +# UTF-8 without BOM +[*] +charset = utf-8 From e41dc68a6bc840dad5d9372dfadf25d4439f85aa Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 20 Oct 2014 21:21:32 +0900 Subject: [PATCH 15/46] Skip duplicate ID3v2 tags in MPEG files. --- taglib/mpeg/mpegfile.cpp | 27 +++++++++++++++++++-------- taglib/mpeg/mpegfile.h | 14 +++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index f765befb..e64e8303 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -433,9 +433,20 @@ long MPEG::File::firstFrameOffset() { long position = 0; - if(ID3v2Tag()) + if(ID3v2Tag()) { + // Skip duplicate ID3v2 tags. + position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); + long location; + while((location = findID3v2(position)) >= 0) { + ID3v2::Tag dupTag(this, location); + position = location + dupTag.header()->completeTagSize(); + + debug("MPEG::File::firstFrameOffset() - Duplicate ID3v2 tag found."); + } + } + return nextFrameOffset(position); } @@ -467,7 +478,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle { // Look for an ID3v2 tag - d->ID3v2Location = findID3v2(); + d->ID3v2Location = findID3v2(0); if(d->ID3v2Location >= 0) { @@ -510,7 +521,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle ID3v1Tag(true); } -long MPEG::File::findID3v2() +long MPEG::File::findID3v2(long offset) { // This method is based on the contents of TagLib::File::find(), but because // of some subtlteies -- specifically the need to look for the bit pattern of @@ -534,9 +545,9 @@ long MPEG::File::findID3v2() long originalPosition = tell(); - // Start the search at the beginning of the file. + // Start the search at the offset. - seek(0); + seek(offset); // This loop is the crux of the find method. There are three cases that we // want to account for: @@ -547,7 +558,7 @@ long MPEG::File::findID3v2() // (2) The search pattern is wholly contained within the current buffer. // // (3) The current buffer ends with a partial match of the pattern. We will - // note this for use in the next itteration, where we will check for the rest + // note this for use in the next iteration, where we will check for the rest // of the pattern. for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) { @@ -561,7 +572,7 @@ long MPEG::File::findID3v2() const int patternOffset = (bufferSize() - previousPartialMatch); if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) { seek(originalPosition); - return bufferOffset - bufferSize() + previousPartialMatch; + return offset + bufferOffset - bufferSize() + previousPartialMatch; } } @@ -570,7 +581,7 @@ long MPEG::File::findID3v2() long location = buffer.find(ID3v2::Header::fileIdentifier()); if(location >= 0) { seek(originalPosition); - return bufferOffset + location; + return offset + bufferOffset + location; } int firstSynchByte = buffer.find(char(uchar(255))); diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h index 3fc01e68..a03887a3 100644 --- a/taglib/mpeg/mpegfile.h +++ b/taglib/mpeg/mpegfile.h @@ -242,8 +242,8 @@ namespace TagLib { * if there is no valid ID3v2 tag. If \a create is true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -261,8 +261,8 @@ namespace TagLib { * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -280,8 +280,8 @@ namespace TagLib { * if there is no valid APE tag. If \a create is true it will create * an APE tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an APE tag. Use hasAPETag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -370,7 +370,7 @@ namespace TagLib { File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); - long findID3v2(); + long findID3v2(long offset); long findID3v1(); void findAPE(); From 71acf3b6f705dbcc9c760d2fec664be9d68c5a49 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 20 Oct 2014 23:13:15 +0900 Subject: [PATCH 16/46] Comment on a weird workaround for duplicate ID3v2 tags. --- taglib/mpeg/mpegfile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index e64e8303..5f953ccb 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -434,8 +434,12 @@ long MPEG::File::firstFrameOffset() long position = 0; if(ID3v2Tag()) { + // Skip duplicate ID3v2 tags. + // Workaround for some faulty files that have duplicate ID3v2 tags. + // Combination of EAC and LAME creates such files when configured incorrectly. + position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); long location; From 269e78f1a013a9673c62075a68d38a14d47fbf34 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 21 Oct 2014 00:16:43 +0900 Subject: [PATCH 17/46] Add a test for duplicate ID3v2 tags. --- tests/data/duplicate_id3v2.mp3 | Bin 0 -> 10138 bytes tests/test_mpeg.cpp | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/data/duplicate_id3v2.mp3 diff --git a/tests/data/duplicate_id3v2.mp3 b/tests/data/duplicate_id3v2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..34f4f158ce454d71677f450d1db8c40abbb85d30 GIT binary patch literal 10138 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 + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); From 73b9b9b58d5bfee2ea9baba62b4db5d92cb4b42f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 23 Oct 2014 08:14:10 +0900 Subject: [PATCH 18/46] Avoid reading an entire ID3v2 tag when skipping it. --- taglib/mpeg/mpegfile.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 5f953ccb..0ac87d10 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -434,18 +434,18 @@ long MPEG::File::firstFrameOffset() long position = 0; if(ID3v2Tag()) { + position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); // Skip duplicate ID3v2 tags. // Workaround for some faulty files that have duplicate ID3v2 tags. // Combination of EAC and LAME creates such files when configured incorrectly. - position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); - long location; while((location = findID3v2(position)) >= 0) { - ID3v2::Tag dupTag(this, location); - position = location + dupTag.header()->completeTagSize(); + seek(location); + const ID3v2::Header header(readBlock(ID3v2::Header::size())); + position = location + header.completeTagSize(); debug("MPEG::File::firstFrameOffset() - Duplicate ID3v2 tag found."); } From d80c4c96c38d8b66529e7cb845c8704d60b33f64 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 16 Oct 2014 07:16:57 +0900 Subject: [PATCH 19/46] ID3v2 padding won't increase beyond 1% of the file size. --- taglib/mpeg/id3v2/id3v2tag.cpp | 23 ++++++++++++++++------- tests/test_id3v2.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index c6aa4f93..e8fa23df 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -81,6 +81,11 @@ public: static const Latin1StringHandler defaultStringHandler; const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler; +namespace +{ + const uint DefaultPaddingSize = 1024; +} + //////////////////////////////////////////////////////////////////////////////// // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// @@ -596,15 +601,19 @@ ByteVector ID3v2::Tag::render(int version) const // Compute the amount of padding, and append that to tagData. - uint paddingSize = 0; - uint originalSize = d->header.tagSize(); + uint paddingSize = DefaultPaddingSize; - if(tagData.size() < originalSize) - paddingSize = originalSize - tagData.size(); - else - paddingSize = 1024; + if(d->file && tagData.size() < d->header.tagSize()) { + paddingSize = d->header.tagSize() - tagData.size(); - tagData.append(ByteVector(paddingSize, char(0))); + // Padding won't increase beyond 1% of the file size. + + const uint threshold = d->file->length() / 100; // should be ulonglong in taglib2. + if(paddingSize > d->paddingSize && paddingSize > threshold) + paddingSize = DefaultPaddingSize; + } + + tagData.append(ByteVector(paddingSize, '\0')); // Set the version and data size. d->header.setMajorVersion(version); diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index ebe86fc3..a0443cd8 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -91,6 +91,7 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testRenderChapterFrame); CPPUNIT_TEST(testParseTableOfContentsFrame); CPPUNIT_TEST(testRenderTableOfContentsFrame); + CPPUNIT_TEST(testShrinkPadding); CPPUNIT_TEST_SUITE_END(); public: @@ -1005,6 +1006,39 @@ public: f.render()); } + void testShrinkPadding() + { + ScopedFileCopy copy("xing", ".mp3"); + string newname = copy.fileName(); + + { + MPEG::File f(newname.c_str()); + ID3v2::Tag *tag = f.ID3v2Tag(true); + + ID3v2::TextIdentificationFrame *frame1 = new ID3v2::TextIdentificationFrame("TIT2"); + frame1->setText("Title"); + tag->addFrame(frame1); + + ID3v2::AttachedPictureFrame *frame2 = new ID3v2::AttachedPictureFrame(); + frame2->setPicture(ByteVector(100 * 1024, '\xff')); + tag->addFrame(frame2); + + f.save(); + CPPUNIT_ASSERT(f.length() > 100 * 1024); + } + + { + MPEG::File f(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); + + ID3v2::Tag *tag = f.ID3v2Tag(); + tag->removeFrames("APIC"); + + f.save(); + CPPUNIT_ASSERT(f.length() < 10 * 1024); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2); From 7b0340379121df09fbfc228afc231378820ab49b Mon Sep 17 00:00:00 2001 From: Achal Dave Date: Tue, 18 Nov 2014 15:06:06 -0800 Subject: [PATCH 20/46] Fix more uint/TagLib::uint ambiguities --- taglib/mpeg/id3v2/frames/chapterframe.cpp | 30 +++++++++---------- .../id3v2/frames/tableofcontentsframe.cpp | 8 ++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/chapterframe.cpp b/taglib/mpeg/id3v2/frames/chapterframe.cpp index 99bcb3a0..667abc53 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.cpp +++ b/taglib/mpeg/id3v2/frames/chapterframe.cpp @@ -44,10 +44,10 @@ public: const ID3v2::Header *tagHeader; ByteVector elementID; - uint startTime; - uint endTime; - uint startOffset; - uint endOffset; + TagLib::uint startTime; + TagLib::uint endTime; + TagLib::uint startOffset; + TagLib::uint endOffset; FrameListMap embeddedFrameListMap; FrameList embeddedFrameList; }; @@ -64,8 +64,8 @@ ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &dat setData(data); } -ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, - const uint &sO, const uint &eO, const FrameList &eF) : +ChapterFrame::ChapterFrame(const ByteVector &eID, const TagLib::uint &sT, const TagLib::uint &eT, + const TagLib::uint &sO, const TagLib::uint &eO, const FrameList &eF) : ID3v2::Frame("CHAP") { d = new ChapterFramePrivate; @@ -89,22 +89,22 @@ ByteVector ChapterFrame::elementID() const return d->elementID; } -uint ChapterFrame::startTime() const +TagLib::uint ChapterFrame::startTime() const { return d->startTime; } -uint ChapterFrame::endTime() const +TagLib::uint ChapterFrame::endTime() const { return d->endTime; } -uint ChapterFrame::startOffset() const +TagLib::uint ChapterFrame::startOffset() const { return d->startOffset; } -uint ChapterFrame::endOffset() const +TagLib::uint ChapterFrame::endOffset() const { return d->endOffset; } @@ -116,22 +116,22 @@ void ChapterFrame::setElementID(const ByteVector &eID) d->elementID.append(char(0)); } -void ChapterFrame::setStartTime(const uint &sT) +void ChapterFrame::setStartTime(const TagLib::uint &sT) { d->startTime = sT; } -void ChapterFrame::setEndTime(const uint &eT) +void ChapterFrame::setEndTime(const TagLib::uint &eT) { d->endTime = eT; } -void ChapterFrame::setStartOffset(const uint &sO) +void ChapterFrame::setStartOffset(const TagLib::uint &sO) { d->startOffset = sO; } -void ChapterFrame::setEndOffset(const uint &eO) +void ChapterFrame::setEndOffset(const TagLib::uint &eO) { d->endOffset = eO; } @@ -211,7 +211,7 @@ ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVec void ChapterFrame::parseFields(const ByteVector &data) { - uint size = data.size(); + TagLib::uint size = data.size(); if(size < 18) { debug("A CHAP frame must contain at least 18 bytes (1 byte element ID " "terminated by null and 4x4 bytes for start and end time and offset)."); diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 7b6bb9f1..b7d6dad9 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -94,7 +94,7 @@ bool TableOfContentsFrame::isOrdered() const return d->isOrdered; } -uint TableOfContentsFrame::entryCount() const +TagLib::uint TableOfContentsFrame::entryCount() const { return d->childElements.size(); } @@ -229,7 +229,7 @@ TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) void TableOfContentsFrame::parseFields(const ByteVector &data) { - uint size = data.size(); + TagLib::uint size = data.size(); if(size < 6) { debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by " "null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated " @@ -242,8 +242,8 @@ void TableOfContentsFrame::parseFields(const ByteVector &data) d->elementID.append(char(0)); d->isTopLevel = (data.at(pos) & 2) > 0; d->isOrdered = (data.at(pos++) & 1) > 0; - uint entryCount = data.at(pos++); - for(uint i = 0; i < entryCount; i++) + TagLib::uint entryCount = data.at(pos++); + for(TagLib::uint i = 0; i < entryCount; i++) { ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); childElementID.append(char(0)); From fb1c744dafc13495240e8eaa6975654c1894c18b Mon Sep 17 00:00:00 2001 From: Bart van der Velden Date: Mon, 8 Dec 2014 09:42:04 -0800 Subject: [PATCH 21/46] Compile without warnings with MSVC --- bindings/c/tag_c.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/c/tag_c.cpp b/bindings/c/tag_c.cpp index d2567fef..6e507b19 100644 --- a/bindings/c/tag_c.cpp +++ b/bindings/c/tag_c.cpp @@ -46,12 +46,12 @@ static bool stringManagementEnabled = true; void taglib_set_strings_unicode(BOOL unicode) { - unicodeStrings = bool(unicode); + unicodeStrings = (unicode != 0); } void taglib_set_string_management_enabled(BOOL management) { - stringManagementEnabled = bool(management); + stringManagementEnabled = (management != 0); } void taglib_free(void* pointer) From 0731bc7b2e2b532b8dec588313aca73d83ed4955 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 9 Dec 2014 08:55:46 +0900 Subject: [PATCH 22/46] Remove CMake check for sizeof(long). --- ConfigureChecks.cmake | 5 ----- config.h.cmake | 3 --- taglib/mp4/mp4atom.cpp | 15 ++++++++------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index cae533e3..99cc1de5 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -20,11 +20,6 @@ if(NOT ${SIZEOF_INT} EQUAL 4) MESSAGE(FATAL_ERROR "TagLib requires that int is 32-bit wide.") endif() -check_type_size("long" SIZEOF_LONG) -if(${SIZEOF_LONG} EQUAL 8) - set(LONG_IS_INT64 1) -endif() - check_type_size("long long" SIZEOF_LONGLONG) if(NOT ${SIZEOF_LONGLONG} EQUAL 8) MESSAGE(FATAL_ERROR "TagLib requires that long long is 64-bit wide.") diff --git a/config.h.cmake b/config.h.cmake index abecfbfb..07abffa6 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -8,9 +8,6 @@ /* 1 if little-endian, 2 if big-endian. */ #cmakedefine FLOAT_BYTEORDER ${FLOAT_BYTEORDER} -/* Defined if long is 64-bit wide */ -#cmakedefine LONG_IS_INT64 ${LONG_IS_INT64} - /* Defined if your compiler supports some byte swap functions */ #cmakedefine HAVE_GCC_BYTESWAP_16 1 #cmakedefine HAVE_GCC_BYTESWAP_32 1 diff --git a/taglib/mp4/mp4atom.cpp b/taglib/mp4/mp4atom.cpp index 5a304e3f..b12f9459 100644 --- a/taglib/mp4/mp4atom.cpp +++ b/taglib/mp4/mp4atom.cpp @@ -56,20 +56,21 @@ MP4::Atom::Atom(File *file) if(length == 1) { const long long longLength = file->readBlock(8).toLongLong(); -#ifdef LONG_IS_INT64 - length = longLength; -#else - if(longLength <= 0xFFFFFFFF) { - // The atom has a 64-bit length, but it's actually a 32-bit value - length = (long)longLength; + if(sizeof(long) == sizeof(long long)) { + length = longLength; } else { + if(longLength <= 0xFFFFFFFF) { + // The atom has a 64-bit length, but it's actually a 32-bit value + length = (long)longLength; + } + else { debug("MP4: 64-bit atoms are not supported"); length = 0; file->seek(0, File::End); return; + } } -#endif } if(length < 8) { debug("MP4: Invalid atom size"); From 1a917a38cdcb1c16c3c0759821b4010163d08005 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 9 Dec 2014 10:54:21 +0900 Subject: [PATCH 23/46] Fix ID3v2 padding size calculation. --- taglib/mpeg/id3v2/id3v2tag.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index e8fa23df..267a45d0 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -608,9 +608,11 @@ ByteVector ID3v2::Tag::render(int version) const // Padding won't increase beyond 1% of the file size. - const uint threshold = d->file->length() / 100; // should be ulonglong in taglib2. - if(paddingSize > d->paddingSize && paddingSize > threshold) - paddingSize = DefaultPaddingSize; + if(paddingSize > DefaultPaddingSize) { + const uint threshold = d->file->length() / 100; // should be ulonglong in taglib2. + if(paddingSize > threshold) + paddingSize = DefaultPaddingSize; + } } tagData.append(ByteVector(paddingSize, '\0')); From 4a9614bfc376a31f4995db9c294df8466f843eb6 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 23 Dec 2014 20:54:20 +0900 Subject: [PATCH 24/46] Fix a division by zero error when parsing an APE file. --- taglib/ape/apeproperties.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/taglib/ape/apeproperties.cpp b/taglib/ape/apeproperties.cpp index 4940edea..cf829fe5 100644 --- a/taglib/ape/apeproperties.cpp +++ b/taglib/ape/apeproperties.cpp @@ -221,12 +221,20 @@ void APE::Properties::analyzeOld() blocksPerFrame = 73728; else blocksPerFrame = 9216; + d->channels = header.toShort(4, false); d->sampleRate = header.toUInt(6, false); + const uint finalFrameBlocks = header.toUInt(22, false); - const uint totalBlocks - = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; - d->length = totalBlocks / d->sampleRate; - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; + + uint totalBlocks = 0; + if(totalFrames > 0) + totalBlocks = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; + + if(d->sampleRate > 0) + d->length = totalBlocks / d->sampleRate; + + if(d->length > 0) + d->bitrate = ((d->streamLength * 8L) / d->length) / 1000; } From 61543432c0d9f31ff65d8c6793bd8208cc64440f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 23 Dec 2014 20:58:36 +0900 Subject: [PATCH 25/46] Fix an excessive loop when parsing an APE file. --- taglib/ape/apetag.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index e1252193..22471d40 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -368,10 +368,13 @@ ByteVector APE::Tag::render() const void APE::Tag::parse(const ByteVector &data) { - uint pos = 0; - // 11 bytes is the minimum size for an APE item + if(data.size() < 11) + return; + + uint pos = 0; + for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) { APE::Item item; item.parse(data.mid(pos)); From 16ac2cd24015fcafa8529ede06f56a1dd5c4f13b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 23 Dec 2014 21:02:00 +0900 Subject: [PATCH 26/46] Added some tests for fuzzed APE files. --- tests/data/longloop.ape | Bin 0 -> 184 bytes tests/data/zerodiv.ape | Bin 0 -> 946 bytes tests/test_ape.cpp | 10 ++++++++++ 3 files changed, 10 insertions(+) create mode 100644 tests/data/longloop.ape create mode 100644 tests/data/zerodiv.ape diff --git a/tests/data/longloop.ape b/tests/data/longloop.ape new file mode 100644 index 0000000000000000000000000000000000000000..3800387acbae60e24fd724f466422521ee53fdf9 GIT binary patch literal 184 zcmeZubXJ(g&%j{9z`!5@#8ZHH4G`Z5Nd}S(0t<{7wrmE2Lz_3R0WuisAzTER^niuo z7$ZBw;+0$sle>5s?t2O_Y?Bve_&clAfSrL03>;tnxaN1m@@;UZhv@5~EW>mK-~7Dn z)JoT41;^5q%zS6Pw1QNiSZ+a4YH@L9ex751Ylx$}Ys3Y11_mXdLtcFO|NlP&g98Ht HkOndU{4PaD literal 0 HcmV?d00001 diff --git a/tests/data/zerodiv.ape b/tests/data/zerodiv.ape new file mode 100644 index 0000000000000000000000000000000000000000..683bc2ddb5ae4f932fd7554664421bd0134f871e GIT binary patch literal 946 zcmV;j15Nx*K|>&x00960001BW0001#000220002kRAT@D000000001iN0A!y@L(fn z1FU6N?rMC{2LJ#7AOQfNG5`Q206+i`00IC+tN;KE0RR9H4gvsT-~<51$p-+VsR{s- zij4rzNe}?G9}@um{uKbw(-;7Lp%VZJXC46PH6Z}&6eIwKtR?`4NGSlc5i9^d=q>>8 zg)snlwKD*2CN==G`Zxfn+B*O$yFLK?Zb1MVG(-T(7DoUL^GN_yy-Wy#iBABVR#E^k zO;rHm?NQMOH;-ZFC?I0000100IC+tN;KI zu|fa@01yCVVRT`D%Ax?3!cHIFm<<4a!=@`~=9M8_a>)?-3o?rrEy&ZL%zk)>+(y7{ zti6@zFo%OMvExuM@b9ZWskn=Nx)d zp{lxNl;FOWNjsXPDz006ie=`z|Sc`@D|u6b|?eVW<@?#of*UN^Y1 zOL~}h7^3Oos~_tf*;Xi9HdD$1_aPTqNN9T?_8E|DYC5#x>-?NN5Ai5voy)Q3NK4cG zJuC2S8B)}7DLfa8z^x4ieZdyl&G)oQCu}+rb?G;-8Vma+oiAWXea%wR_ z$qYG4i(SKF?(75aFPu{Z3be-lU%pm{{PTCgQ6?MbnuMe`Cm{S`1}090IRnYaX#b4J zgEP$xl{{Z*&K!#sAt@QUHWtKSbN?o=80P1Dx3}|w776XkW{5LLU{{;6uu0kbouJ5| zm;VK#|1S|zq>i2O-okJhkE^gBL^7Dc?{k7xXp+w3tutQlp`kcIK?MK?4tzlMFtAho zx5^vD}ZcdL`aRAUqP~eRFlu_v`AWZb?g{O&9mmZd=QMX+YL*6(|ofqh$x$UsampleRate()); } + void testFuzzedFiles() + { + APE::File f1(TEST_FILE_PATH_C("longloop.ape")); + CPPUNIT_ASSERT(f1.isValid()); + + APE::File f2(TEST_FILE_PATH_C("zerodiv.ape")); + CPPUNIT_ASSERT(f2.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAPE); From 3b8c7d4e3a5d82298680d3b0b92be5feeb73ea4f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 30 Dec 2014 23:53:40 +0900 Subject: [PATCH 27/46] Add support for AIFF-C files. --- taglib/fileref.cpp | 2 +- taglib/riff/aiff/aiffproperties.cpp | 33 ++++++++++++++++++++++++---- taglib/riff/aiff/aiffproperties.h | 24 ++++++++++++++++++++ tests/data/alaw.aifc | Bin 0 -> 1890 bytes tests/data/segfault.aif | Bin 0 -> 31 bytes tests/test_aiff.cpp | 20 +++++++++++++++++ 6 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/data/alaw.aifc create mode 100644 tests/data/segfault.aif diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index 4403a5fb..2b5d5f23 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -265,7 +265,7 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WMA" || ext == "ASF") return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle); - if(ext == "AIF" || ext == "AIFF") + if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WAV") return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); diff --git a/taglib/riff/aiff/aiffproperties.cpp b/taglib/riff/aiff/aiffproperties.cpp index 1afb4a99..63fed45f 100644 --- a/taglib/riff/aiff/aiffproperties.cpp +++ b/taglib/riff/aiff/aiffproperties.cpp @@ -38,16 +38,17 @@ public: sampleRate(0), channels(0), sampleWidth(0), - sampleFrames(0) - { - - } + sampleFrames(0) {} int length; int bitrate; int sampleRate; int channels; int sampleWidth; + + ByteVector compressionType; + String compressionName; + uint sampleFrames; }; @@ -96,12 +97,31 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const return d->sampleFrames; } +bool RIFF::AIFF::Properties::isAiffC() const +{ + return (!d->compressionType.isEmpty()); +} + +ByteVector RIFF::AIFF::Properties::compressionType() const +{ + return d->compressionType; +} + +String RIFF::AIFF::Properties::compressionName() const +{ + return d->compressionName; +} //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::AIFF::Properties::read(const ByteVector &data) { + if(data.size() < 18) { + debug("RIFF::AIFF::Properties::read() - \"COMM\" chunk is too short for AIFF."); + return; + } + d->channels = data.toShort(0U); d->sampleFrames = data.toUInt(2U); d->sampleWidth = data.toShort(6U); @@ -109,4 +129,9 @@ void RIFF::AIFF::Properties::read(const ByteVector &data) d->sampleRate = (int)sampleRate; d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; + + if(data.size() >= 23) { + d->compressionType = data.mid(18, 4); + d->compressionName = String(data.mid(23, static_cast(data[22]))); + } } diff --git a/taglib/riff/aiff/aiffproperties.h b/taglib/riff/aiff/aiffproperties.h index 68e90b79..d0778704 100644 --- a/taglib/riff/aiff/aiffproperties.h +++ b/taglib/riff/aiff/aiffproperties.h @@ -67,6 +67,30 @@ namespace TagLib { int sampleWidth() const; uint sampleFrames() const; + /*! + * Returns true if the file is in AIFF-C format, false if AIFF format. + */ + bool isAiffC() const; + + /*! + * Returns the compression type of the AIFF-C file. For example, "NONE" for + * not compressed, "ACE2" for ACE 2-to-1. + * + * If the file is in AIFF format, always returns an empty vector. + * + * \see isAiffC() + */ + ByteVector compressionType() const; + + /*! + * Returns the concrete compression name of the AIFF-C file. + * + * If the file is in AIFF format, always returns an empty string. + * + * \see isAiffC() + */ + String compressionName() const; + private: Properties(const Properties &); Properties &operator=(const Properties &); diff --git a/tests/data/alaw.aifc b/tests/data/alaw.aifc new file mode 100644 index 0000000000000000000000000000000000000000..33b4ea2a5734c10af92aa556de6c50aa102efac6 GIT binary patch literal 1890 zcmX|B2~d+~8vb*iBm@uwf(e)eaX^Vl{)7;~EUlJSP=Q*Zvn~oOpa_aq?d(j=eIJC| zaD+e(?1G$0AV6@{+3u+9+6ubuw2Kr$%28cfS9i3#`_plKXTF*5d*0`J-s_uh-@)8$ z0KgQAv-V}~JMwNW0077?$>F`32eTm@NCaR2fIb2Q?a zKcBvT7oW|hi<2skU(6QgegF~P2aub$004iYw2Xd^URhmz1_0o{BBfO&ZxO^>PR$z* z@-62Ll9iK}4`E><0Jsdhyy=CMH5b`&Sjp))nJ`pl;_ z6=mh;>Fksgb|U@%{v2`kyO1AuvAVkCL}hjHDIhN|2dW8u3_^dk**7*b^Kd3_U)FCI zlq!laDQPJ+ISExx0J&9OfuK9vuW`Fj?E_q7&M|5v|8N zJnHJ}bE=3Lse+U=He0}ArH6X_F1vetK;xV8PpAO_9m*w{NCx+S(ZuB%tOC#+?mkw^qLB{nfJR)qOMEjMZg$Gflf^f^6$hJ&1B zPDvZ_1hJB`bRC?0RoxA8l4!k_y;&JwV{y7vvcaO*gMe&I{5)xS=Oq1R_ zG-~hfx!ULUm4PWjA(IuCm_$z{{NnSwT%BG0*ZMmr8o=1Z-El0YkjWHsqD<~hy4^Y9 zH8n?Z81%R}db}`=Po&fhxcYl~dR>#ohTKqWI(>J-t~e&|c)iLnGUe{NcCE{)seu}l zA&iIautaE!XDBf4bh|n`9m7|m*eOf~BR)Q!kxqK$3AkM@$9Q03K+}K%1@W;gW{N18 zhrDhaa@srXj=+FUeFMeKV8qg6nIb+=S+ALNLSuD}jVKFHygeyQ7Cn}cMmpK98xHs# z(4fY23rN050M*A}q;i0l+DWIw;SWqr`XoOi`58i{P>`OH1_l*dy`yfs9SS~Vc!37f z8LU_qL%_iWHyQ@V98glDLmtIl8W|J`p!l$m7(9qbCL)kw&pM=1wW8J5-rjDRKO3VI&>_K`WYjw7k65Xj93h*4p(O7fUan zF9}8yNko8JIlZvUGHHS2tvZEbm779NQvbCdUQ_;AY2<_@LG zsNeLM>o3#LP`BA>86ptH|QPF7>sgN`^HH$ zmb;1bNSLYxg~mHD;Psegs+QG*F+?y;#Np#$FL7(Qqu6izmaV z71vkmuc)*>?}%2?v3NWt3JH+0SR6DyYRQ^art^7ydYPj3<@u@*43q&I-Ld-M6cr%hxjX_1i2jX5W!7p*(Nfo;JzG+I{15Ma_0Ly}|GD$s%vaZ+zxes~7y0{h z4v60oAIv(ixA4&1%^Tm`fAHmZU*Ebpee3?=V;_HfwBT4l!S8ZEdU)^Nmk(!VXa5J= CPx9XY literal 0 HcmV?d00001 diff --git a/tests/data/segfault.aif b/tests/data/segfault.aif new file mode 100644 index 0000000000000000000000000000000000000000..5dce192b05e0219b947cefde7573b7df1f3393b1 GIT binary patch literal 31 icmZ?s5AtP9KB4F6>E`C_@9WC|0>Qz4EaudioProperties()->isAiffC()); + CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f->audioProperties()->compressionType()); + CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f->audioProperties()->compressionName()); + delete f; + } + + void testFuzzedFiles() + { + RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif")); + CPPUNIT_ASSERT(!f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF); From 977fb2aeb081114c49d3c2def21bd226fbf0b410 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 31 Dec 2014 00:41:22 +0900 Subject: [PATCH 28/46] Fix compilation errors on Clang. --- tests/test_aiff.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/tests/test_aiff.cpp b/tests/test_aiff.cpp index e90f5280..a99b2350 100644 --- a/tests/test_aiff.cpp +++ b/tests/test_aiff.cpp @@ -21,24 +21,16 @@ public: void testReading() { - ScopedFileCopy copy("empty", ".aiff"); - string filename = copy.fileName(); - - RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str()); - CPPUNIT_ASSERT_EQUAL(705, f->audioProperties()->bitrate()); - delete f; + RIFF::AIFF::File f(TEST_FILE_PATH_C("empty.aiff")); + CPPUNIT_ASSERT_EQUAL(705, f.audioProperties()->bitrate()); } void testAiffCProperties() { - ScopedFileCopy copy("alaw", ".aifc"); - string filename = copy.fileName(); - - RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str()); - CPPUNIT_ASSERT(f->audioProperties()->isAiffC()); - CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f->audioProperties()->compressionType()); - CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f->audioProperties()->compressionName()); - delete f; + RIFF::AIFF::File f(TEST_FILE_PATH_C("alaw.aifc")); + CPPUNIT_ASSERT(f.audioProperties()->isAiffC()); + CPPUNIT_ASSERT(f.audioProperties()->compressionType() == "ALAW"); + CPPUNIT_ASSERT(f.audioProperties()->compressionName() == "SGI CCITT G.711 A-law"); } void testFuzzedFiles() From 3170d47ec331fc778431f920903a1c0a56a4a14f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 23 Dec 2014 15:44:17 +0900 Subject: [PATCH 29/46] Fix an infinite loop when parsing an INFO tag. --- taglib/riff/wav/infotag.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp index 7cd2a192..050ff37c 100644 --- a/taglib/riff/wav/infotag.cpp +++ b/taglib/riff/wav/infotag.cpp @@ -258,9 +258,15 @@ void RIFF::Info::Tag::parse(const ByteVector &data) uint p = 4; while(p < data.size()) { const uint size = data.toUInt(p + 4, false); - d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + if(size > data.size() - p - 8) + break; + + const ByteVector id = data.mid(p, 4); + if(isValidChunkID(id)) { + const String text = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + d->fieldListMap[id] = text; + } p += ((size + 1) & ~1) + 8; } } - From 695fb5ec16e32f43495813ef21a60c31d1e9b992 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 23 Dec 2014 20:26:32 +0900 Subject: [PATCH 30/46] Add a test for fuzzed WAV files. --- tests/data/infloop.wav | Bin 0 -> 14272 bytes tests/test_wav.cpp | 9 ++++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/data/infloop.wav diff --git a/tests/data/infloop.wav b/tests/data/infloop.wav new file mode 100644 index 0000000000000000000000000000000000000000..c220baa8f52a0e69f02d91a20e360ff4d1d9fb41 GIT binary patch literal 14272 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 literal 0 HcmV?d00001 diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 518f37cf..b0073ebc 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -15,6 +15,7 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST(testLength); CPPUNIT_TEST(testZeroSizeDataChunk); CPPUNIT_TEST(testStripTags); + CPPUNIT_TEST(testFuzzedFiles); CPPUNIT_TEST_SUITE_END(); public: @@ -67,7 +68,13 @@ public: CPPUNIT_ASSERT(!f->hasID3v2Tag()); CPPUNIT_ASSERT(f->hasInfoTag()); delete f; - } + } + + void testFuzzedFiles() + { + RIFF::WAV::File f(TEST_FILE_PATH_C("infloop.wav")); + CPPUNIT_ASSERT(!f.isValid()); + } }; From 0d2e01df61d34d2a084acb992dc1f4eb2a46cc26 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 31 Dec 2014 01:46:30 +0900 Subject: [PATCH 31/46] Fix a segfault when parsing WAV properties. --- taglib/riff/wav/wavproperties.cpp | 5 +++++ tests/data/segfault.wav | Bin 0 -> 30 bytes tests/test_wav.cpp | 7 +++++-- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/data/segfault.wav diff --git a/taglib/riff/wav/wavproperties.cpp b/taglib/riff/wav/wavproperties.cpp index 8062df5f..439a1954 100644 --- a/taglib/riff/wav/wavproperties.cpp +++ b/taglib/riff/wav/wavproperties.cpp @@ -115,6 +115,11 @@ TagLib::uint RIFF::WAV::Properties::sampleFrames() const void RIFF::WAV::Properties::read(const ByteVector &data) { + if(data.size() < 16) { + debug("RIFF::WAV::Properties::read() - \"fmt \" chunk is too short for WAV."); + return; + } + d->format = data.toShort(0, false); d->channels = data.toShort(2, false); d->sampleRate = data.toUInt(4, false); diff --git a/tests/data/segfault.wav b/tests/data/segfault.wav new file mode 100644 index 0000000000000000000000000000000000000000..0385e99be958fdf68181244ea8cb2ecd74eb1725 GIT binary patch literal 30 kcmWIYbaR_v$-ofq80MOmTcW_kz`*eTe@bFWB9OrV0D{&CB>(^b literal 0 HcmV?d00001 diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index b0073ebc..72efe4f6 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -72,8 +72,11 @@ public: void testFuzzedFiles() { - RIFF::WAV::File f(TEST_FILE_PATH_C("infloop.wav")); - CPPUNIT_ASSERT(!f.isValid()); + RIFF::WAV::File f1(TEST_FILE_PATH_C("infloop.wav")); + CPPUNIT_ASSERT(!f1.isValid()); + + RIFF::WAV::File f2(TEST_FILE_PATH_C("segfault.wav")); + CPPUNIT_ASSERT(f2.isValid()); } }; From 5ebb2ece80387e60abca5bec0dc65f02391ee5f9 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 1 Jan 2015 19:54:17 +0900 Subject: [PATCH 32/46] Fix a segfault when reading faulty Ogg/FLAC files. --- taglib/ogg/flac/oggflacfile.cpp | 9 +++++++-- tests/data/segfault.oga | Bin 0 -> 120 bytes tests/test_oggflac.cpp | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/data/segfault.oga diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index bdf82459..0c1d61b6 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -103,7 +103,7 @@ PropertyMap Ogg::FLAC::File::properties() const PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); -} +} Properties *Ogg::FLAC::File::audioProperties() const { @@ -233,7 +233,12 @@ void Ogg::FLAC::File::scan() } - header = metadataHeader.mid(0,4); + header = metadataHeader.mid(0, 4); + if(header.size() < 4) { + debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); + return; + } + // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE diff --git a/tests/data/segfault.oga b/tests/data/segfault.oga new file mode 100644 index 0000000000000000000000000000000000000000..e23c21706e2f87776b3b210be638f431e416c00e GIT binary patch literal 120 zcmeZIPY-5bVt|6`<8dhhK(-;${T;af=&?wZf>p|rsRR^Hyd>{Y-^*$$~ literal 0 HcmV?d00001 diff --git a/tests/test_oggflac.cpp b/tests/test_oggflac.cpp index 1cdb24b0..975af44e 100644 --- a/tests/test_oggflac.cpp +++ b/tests/test_oggflac.cpp @@ -15,6 +15,7 @@ class TestOggFLAC : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestOggFLAC); CPPUNIT_TEST(testFramingBit); + CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST_SUITE_END(); public: @@ -39,6 +40,12 @@ public: delete f; } + void testFuzzedFile() + { + Ogg::FLAC::File f(TEST_FILE_PATH_C("segfault.oga")); + CPPUNIT_ASSERT(!f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestOggFLAC); From 7adea3df229e81ebde0de3d0481fb21aa7237b41 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 1 Jan 2015 23:18:43 +0900 Subject: [PATCH 33/46] Separate tests for fuzzed WAV files. --- tests/test_wav.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 72efe4f6..7367357a 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -15,7 +15,8 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST(testLength); CPPUNIT_TEST(testZeroSizeDataChunk); CPPUNIT_TEST(testStripTags); - CPPUNIT_TEST(testFuzzedFiles); + CPPUNIT_TEST(testFuzzedFile1); + CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST_SUITE_END(); public: @@ -70,11 +71,14 @@ public: delete f; } - void testFuzzedFiles() + void testFuzzedFile1() { RIFF::WAV::File f1(TEST_FILE_PATH_C("infloop.wav")); CPPUNIT_ASSERT(!f1.isValid()); + } + void testFuzzedFile2() + { RIFF::WAV::File f2(TEST_FILE_PATH_C("segfault.wav")); CPPUNIT_ASSERT(f2.isValid()); } From bc9bbfe3fa3f396bffb951c8f5681edea2c03b44 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 2 Jan 2015 00:10:51 +0900 Subject: [PATCH 34/46] Add a check for faulty Ogg/FLAC files. --- taglib/ogg/flac/oggflacfile.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index 0c1d61b6..1258e705 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -211,30 +211,26 @@ void Ogg::FLAC::File::scan() long overhead = 0; ByteVector metadataHeader = packet(ipacket); - if(metadataHeader.isNull()) + if(metadataHeader.isEmpty()) return; - ByteVector header; - - if (!metadataHeader.startsWith("fLaC")) { + if(!metadataHeader.startsWith("fLaC")) { // FLAC 1.1.2+ - if (metadataHeader.mid(1,4) != "FLAC") return; + if(metadataHeader.mid(1, 4) != "FLAC") + return; - if (metadataHeader[5] != 1) return; // not version 1 + if(metadataHeader[5] != 1) + return; // not version 1 metadataHeader = metadataHeader.mid(13); } else { // FLAC 1.1.0 & 1.1.1 metadataHeader = packet(++ipacket); - - if(metadataHeader.isNull()) - return; - } - header = metadataHeader.mid(0, 4); - if(header.size() < 4) { + ByteVector header = metadataHeader.mid(0, 4); + if(header.size() != 4) { debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); return; } @@ -267,11 +263,12 @@ void Ogg::FLAC::File::scan() while(!lastBlock) { metadataHeader = packet(++ipacket); - - if(metadataHeader.isNull()) - return; - header = metadataHeader.mid(0, 4); + if(header.size() != 4) { + debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); + return; + } + blockType = header[0] & 0x7f; lastBlock = (header[0] & 0x80) != 0; length = header.toUInt(1, 3, true); From caa53e8de5591a75138f90cced544908d016222b Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Sun, 4 Jan 2015 19:58:18 +0100 Subject: [PATCH 35/46] Read the compressed data as a stream This avoids allocating the complete buffer at first based solely on the value read from the frame header. This then does a sanity check at the end of reading to make sure that the two values match. At present, it just prints a debugging message if the values do not match. Fixes #466 --- taglib/mpeg/id3v2/id3v2frame.cpp | 44 +++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 3cafcff9..98ec2ff7 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -254,12 +254,44 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const if(d->header->compression() && !d->header->encryption()) { - ByteVector data(frameDataLength); - uLongf uLongTmp = frameDataLength; - ::uncompress((Bytef *) data.data(), - (uLongf *) &uLongTmp, - (Bytef *) frameData.data() + frameDataOffset, - size()); + z_stream stream; + memset(&stream, 0, sizeof(z_stream)); + + if(inflateInit(&stream) != Z_OK) + return ByteVector(); + + stream.avail_in = (uLongf) frameDataLength; + stream.next_in = (Bytef *) frameData.data() + frameDataOffset; + + static const uint chunkSize = 1024; + + ByteVector data; + ByteVector chunk(chunkSize); + + do { + stream.avail_out = (uLongf) chunk.size(); + stream.next_out = (Bytef *) chunk.data(); + + int result = inflate(&stream, Z_NO_FLUSH); + + if(result == Z_STREAM_ERROR) + return ByteVector(); + else if(result == Z_NEED_DICT || + result == Z_DATA_ERROR || + result == Z_MEM_ERROR) + { + inflateEnd(&stream); + return ByteVector(); + } + + data.append(stream.avail_out == 0 ? chunk : chunk.mid(0, chunk.size() - stream.avail_out)); + } while(stream.avail_out == 0); + + inflateEnd(&stream); + + if(frameDataLength != data.size()) + debug("frameDataLength does not match the data length returned by zlib"); + return data; } else From 57729b834a25e0c910b07ca3bdc9ca6c1134261c Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Sun, 4 Jan 2015 20:13:24 +0100 Subject: [PATCH 36/46] Show a debugging message when we have an error reading --- taglib/mpeg/id3v2/id3v2frame.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 98ec2ff7..bb251d18 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -274,13 +274,14 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const int result = inflate(&stream, Z_NO_FLUSH); - if(result == Z_STREAM_ERROR) - return ByteVector(); - else if(result == Z_NEED_DICT || - result == Z_DATA_ERROR || - result == Z_MEM_ERROR) + if(result == Z_STREAM_ERROR || + result == Z_NEED_DICT || + result == Z_DATA_ERROR || + result == Z_MEM_ERROR) { - inflateEnd(&stream); + if(result != Z_STREAM_ERROR) + inflateEnd(&stream); + debug("Error reading compressed stream"); return ByteVector(); } From ed253d369166e973657fe4c3d586e863bcbc9643 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 5 Jan 2015 09:22:50 +0900 Subject: [PATCH 37/46] Add some tests for huge memory allocation due to bad ID3v2 frame header flags. The tests covers #466 and #486. Also fixes a compilation error on some compilers. --- taglib/mpeg/id3v2/id3v2frame.cpp | 3 ++- tests/data/excessive_alloc.aif | Bin 0 -> 2170 bytes tests/data/excessive_alloc.mp3 | Bin 0 -> 925 bytes tests/test_aiff.cpp | 13 ++++++++++--- tests/test_mpeg.cpp | 7 +++++++ 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 tests/data/excessive_alloc.aif create mode 100644 tests/data/excessive_alloc.mp3 diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index bb251d18..5dc84971 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -32,6 +32,7 @@ #endif #include +#include #include #include @@ -255,7 +256,7 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const !d->header->encryption()) { z_stream stream; - memset(&stream, 0, sizeof(z_stream)); + ::memset(&stream, 0, sizeof(z_stream)); if(inflateInit(&stream) != Z_OK) return ByteVector(); diff --git a/tests/data/excessive_alloc.aif b/tests/data/excessive_alloc.aif new file mode 100644 index 0000000000000000000000000000000000000000..9cb3a6e10defc7bff06bfa670aae859f687b0193 GIT binary patch literal 2170 zcmcIlT}V@57=EV``LmcKNCe}f=nrExr9=>Moi$x`oX!*NCMb8-oS8>9BPwfyBB8tP zg!C@D>ME$a0;3BtyeR4-qPmIjCdxq#wfuU&?|eIdZeYw7DPb#A}pNZ&U0Ggi=wtF;1DYC2pL>B_VWYar5*4`(#~N z_<=>ZpaSz%LRFxz?*#s;%ZzLB?zWc00MlJ(PbxiV+uWDdGTP|H%-;Aug8v(&+7#`mCyzlL8=zw%sby6T?4)GR&fpIJHN>kPkJSXd++h_!Rj(%2wt8LRHnt1cf4dlqg|ZT(92Nui%@`jT1_y7 zM3`MklA&1^MobLD01Lw!GQ$*GG-@TnVlKyr>l<1Q>z)=@v!ldn+RMb%6x{;fK%-lG znYVc1Z!J5#u1x0+w-T|80KQpMS^#qWXC#Fmo#;ClWUk%d=_*o6Fzle%MC;HDh5_8Q zpv~HFqRkph+S^D=I{qcl&L9=H*{Nubz}Z_#sSql%%2kB(glU@rg2_moC)*h#D-53K z`^VD&FTXx(+ECf8$aX3eKfO-8e>YfO<7I2&qb=N5iA9UNODSZhe;^ZuE| z%5suPsa-N1QIJ{|VvgPJ?yZNlZ3Ti83xrcEv= cyi`A-?dL@P#oEN=*U17yN%yzO_lEu9H9*zMH2W62`|JguzhA_xdCIqAXw*fMj$arOu z#h)_KRLZrm6g043F?E6|gFF}}wMRq5r?>(>n=x*WJP*MYE-q%UTCJK&<~bLNt!2@S z<$^kajoAiDv*!P zXPH=Q{drU_GwL|}tAl?Lv+QCYYn zNZp>XG3;sQ(ba1yrsMrO<0 #include #include #include #include #include +#include #include "utils.h" using namespace std; @@ -14,7 +14,8 @@ class TestAIFF : public CppUnit::TestFixture CPPUNIT_TEST_SUITE(TestAIFF); CPPUNIT_TEST(testReading); CPPUNIT_TEST(testAiffCProperties); - CPPUNIT_TEST(testReading); + CPPUNIT_TEST(testFuzzedFile1); + CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST_SUITE_END(); public: @@ -33,12 +34,18 @@ public: CPPUNIT_ASSERT(f.audioProperties()->compressionName() == "SGI CCITT G.711 A-law"); } - void testFuzzedFiles() + void testFuzzedFile1() { RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif")); CPPUNIT_ASSERT(!f.isValid()); } + void testFuzzedFile2() + { + RIFF::AIFF::File f(TEST_FILE_PATH_C("excessive_alloc.aif")); + CPPUNIT_ASSERT(!f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF); diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 024da6a0..07b970ee 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -17,6 +17,7 @@ class TestMPEG : public CppUnit::TestFixture CPPUNIT_TEST(testSaveID3v24WrongParam); CPPUNIT_TEST(testSaveID3v23); CPPUNIT_TEST(testDuplicateID3v2); + CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST_SUITE_END(); public: @@ -106,6 +107,12 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); } + void testFuzzedFile() + { + MPEG::File f(TEST_FILE_PATH_C("excessive_alloc.mp3")); + CPPUNIT_ASSERT(f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); From 2685ec184293c54ebb70de3b856ec57378e85395 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 5 Jan 2015 11:00:47 +0900 Subject: [PATCH 38/46] Remove an unused file from taglib/CMakeLists.txt. uncompr.c is no longer used since caa53e8 --- taglib/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 2eadf391..33a0030b 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -318,7 +318,6 @@ if(HAVE_ZLIB_SOURCE) ${ZLIB_SOURCE}/inffast.c ${ZLIB_SOURCE}/inflate.c ${ZLIB_SOURCE}/inftrees.c - ${ZLIB_SOURCE}/uncompr.c ${ZLIB_SOURCE}/zutil.c ) endif() From c6a63a3a2f82866f5015b7b6f68fea2cf26f4fe3 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 5 Jan 2015 18:20:31 +0900 Subject: [PATCH 39/46] Implement missing AIFF::File::hasID3v2Tag(). --- taglib/riff/aiff/aifffile.cpp | 12 ++++++++++-- tests/test_aiff.cpp | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/taglib/riff/aiff/aifffile.cpp b/taglib/riff/aiff/aifffile.cpp index d20c148b..2f79920c 100644 --- a/taglib/riff/aiff/aifffile.cpp +++ b/taglib/riff/aiff/aifffile.cpp @@ -39,7 +39,8 @@ public: FilePrivate() : properties(0), tag(0), - tagChunkID("ID3 ") + tagChunkID("ID3 "), + hasID3v2(false) { } @@ -53,6 +54,8 @@ public: Properties *properties; ID3v2::Tag *tag; ByteVector tagChunkID; + + bool hasID3v2; }; //////////////////////////////////////////////////////////////////////////////// @@ -100,7 +103,6 @@ PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties) return d->tag->setProperties(properties); } - RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const { return d->properties; @@ -119,10 +121,15 @@ bool RIFF::AIFF::File::save() } setChunkData(d->tagChunkID, d->tag->render()); + d->hasID3v2 = true; return true; } +bool RIFF::AIFF::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} //////////////////////////////////////////////////////////////////////////////// // private members @@ -134,6 +141,7 @@ void RIFF::AIFF::File::read(bool readProperties, Properties::ReadStyle propertie if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { d->tagChunkID = chunkName(i); d->tag = new ID3v2::Tag(this, chunkOffset(i)); + d->hasID3v2 = true; } else if(chunkName(i) == "COMM" && readProperties) d->properties = new Properties(chunkData(i), propertiesStyle); diff --git a/tests/test_aiff.cpp b/tests/test_aiff.cpp index 495049b2..4df8d893 100644 --- a/tests/test_aiff.cpp +++ b/tests/test_aiff.cpp @@ -13,6 +13,7 @@ class TestAIFF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestAIFF); CPPUNIT_TEST(testReading); + CPPUNIT_TEST(testSaveID3v2); CPPUNIT_TEST(testAiffCProperties); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); @@ -26,6 +27,25 @@ public: CPPUNIT_ASSERT_EQUAL(705, f.audioProperties()->bitrate()); } + void testSaveID3v2() + { + ScopedFileCopy copy("empty", ".aiff"); + string newname = copy.fileName(); + + { + RIFF::AIFF::File f(newname.c_str()); + CPPUNIT_ASSERT(!f.hasID3v2Tag()); + f.tag()->setTitle(L"TitleXXX"); + f.save(); + } + + { + RIFF::AIFF::File f(newname.c_str()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title()); + } + } + void testAiffCProperties() { RIFF::AIFF::File f(TEST_FILE_PATH_C("alaw.aifc")); From 9d91610fc0b978d6d34061bb1046317e087dc30a Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 6 Jan 2015 17:20:03 +0900 Subject: [PATCH 40/46] Fix a wrong parameter for zlib. z_stream.avail_in has to be the length of the input buffer. It will fail when frameDataLength is smaller than the actual compressed data size. --- taglib/mpeg/id3v2/id3v2frame.cpp | 11 +++++++---- tests/data/compressed_id3_frame.mp3 | Bin 5000 -> 5000 bytes 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 5dc84971..bee5375a 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -32,7 +32,6 @@ #endif #include -#include #include #include @@ -255,13 +254,17 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const if(d->header->compression() && !d->header->encryption()) { - z_stream stream; - ::memset(&stream, 0, sizeof(z_stream)); + if(frameData.size() <= frameDataOffset) { + debug("Compressed frame doesn't have enough data to decode"); + return ByteVector(); + } + + z_stream stream = {}; if(inflateInit(&stream) != Z_OK) return ByteVector(); - stream.avail_in = (uLongf) frameDataLength; + stream.avail_in = (uLongf) frameData.size() - frameDataOffset; stream.next_in = (Bytef *) frameData.data() + frameDataOffset; static const uint chunkSize = 1024; diff --git a/tests/data/compressed_id3_frame.mp3 b/tests/data/compressed_id3_frame.mp3 index 60bc895633b1ce32894e54800f6aa65d4e2cb546..824d036fa4459e57ad671a8c3bb910e384b94ff2 100644 GIT binary patch delta 13 UcmeBB?@*s0%EZ91QM^zX02%88QUCw| delta 13 UcmeBB?@*s0%ETDBQM^zX02_t_qyPW_ From 2b5ee8deb93fd08da1351cd3e746125cb83a157d Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 30 Jan 2015 14:43:05 +0900 Subject: [PATCH 41/46] Fix saving ID3v2/INFO tags of WAV files. The old tag won't be removed when the new tag is empty. --- taglib/riff/wav/wavfile.cpp | 37 +++++++++++------- tests/test_wav.cpp | 76 +++++++++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index c8d0578c..6c835e70 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -57,7 +57,7 @@ public: } Properties *properties; - + ByteVector tagChunkID; TagUnion tag; @@ -146,21 +146,30 @@ bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version) if(stripOthers) strip(static_cast(AllTags & ~tags)); - ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); - if((tags & ID3v2) && !id3v2tag->isEmpty()) { - setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); - d->hasID3v2 = true; + const ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); + if(tags & ID3v2) { + if(d->hasID3v2) { + removeChunk(d->tagChunkID); + d->hasID3v2 = false; + } + + if(!id3v2tag->isEmpty()) { + setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); + d->hasID3v2 = true; + } } - Info::Tag *infotag = d->tag.access(InfoIndex, false); - if((tags & Info) && !infotag->isEmpty()) { - int chunkId = findInfoTagChunk(); - if(chunkId != -1) - setChunkData(chunkId, infotag->render()); - else - setChunkData("LIST", infotag->render(), true); + const Info::Tag *infotag = d->tag.access(InfoIndex, false); + if(tags & Info) { + if(d->hasInfo) { + removeChunk(findInfoTagChunk()); + d->hasInfo = false; + } - d->hasInfo = true; + if(!infotag->isEmpty()) { + setChunkData("LIST", infotag->render(), true); + d->hasInfo = true; + } } return true; @@ -239,6 +248,6 @@ TagLib::uint RIFF::WAV::File::findInfoTagChunk() return i; } } - + return TagLib::uint(-1); } diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 7367357a..a962ca23 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -1,9 +1,11 @@ -#include #include #include #include +#include +#include #include #include +#include #include "utils.h" using namespace std; @@ -14,6 +16,8 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST_SUITE(TestWAV); CPPUNIT_TEST(testLength); CPPUNIT_TEST(testZeroSizeDataChunk); + CPPUNIT_TEST(testID3v2Tag); + CPPUNIT_TEST(testInfoTag); CPPUNIT_TEST(testStripTags); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); @@ -24,14 +28,80 @@ public: void testLength() { RIFF::WAV::File f(TEST_FILE_PATH_C("empty.wav")); - CPPUNIT_ASSERT_EQUAL(true, f.isValid()); + CPPUNIT_ASSERT(f.isValid()); CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length()); } void testZeroSizeDataChunk() { RIFF::WAV::File f(TEST_FILE_PATH_C("zero-size-chunk.wav")); - CPPUNIT_ASSERT_EQUAL(false, f.isValid()); + CPPUNIT_ASSERT(!f.isValid()); + } + + void testID3v2Tag() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + + f.ID3v2Tag()->setTitle(L"Title"); + f.ID3v2Tag()->setArtist(L"Artist"); + f.save(); + } + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.ID3v2Tag()->title()); + CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.ID3v2Tag()->artist()); + + f.ID3v2Tag()->setTitle(L""); + f.ID3v2Tag()->setArtist(L""); + f.save(); + } + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->title()); + CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->artist()); + } + } + + void testInfoTag() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + + f.InfoTag()->setTitle(L"Title"); + f.InfoTag()->setArtist(L"Artist"); + f.save(); + } + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.InfoTag()->artist()); + + f.InfoTag()->setTitle(L""); + f.InfoTag()->setArtist(L""); + f.save(); + } + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->artist()); + } } void testStripTags() From 2193d6dd84a16603017c2904f7fbf2160b88f2ce Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 8 Jan 2015 12:05:17 +0900 Subject: [PATCH 42/46] Fix an out-of-bounds access and consequent errors while parsing fuzzed MPC files. Consequent errors may vary: segfault, zerodiv and so forth. --- taglib/mpc/mpcproperties.cpp | 20 +++++++++++--------- tests/data/zerodiv.mpc | Bin 0 -> 405 bytes tests/test_mpc.cpp | 9 ++++++++- 3 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 tests/data/zerodiv.mpc diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index d406f8d7..f11f8ecf 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -183,7 +183,9 @@ unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength) return size; } -static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 }; +// This array looks weird, but the same as original MusePack code found at: +// https://www.musepack.net/index.php?pg=src +static const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 }; void MPC::Properties::readSV8(File *file) { @@ -207,17 +209,17 @@ void MPC::Properties::readSV8(File *file) d->sampleFrames = readSize(data.mid(pos), pos); ulong begSilence = readSize(data.mid(pos), pos); - std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true))); + const ushort flags = data.toUShort(pos, true); pos += 2; - d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]]; - d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1; + d->sampleRate = sftable[(flags >> 13) & 0x07]; + d->channels = ((flags >> 4) & 0x0F) + 1; - if((d->sampleFrames - begSilence) != 0) - d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence)); - d->bitrate = d->bitrate / 1000; - - d->length = (d->sampleFrames - begSilence) / d->sampleRate; + const uint frameCount = d->sampleFrames - begSilence; + if(frameCount != 0 && d->sampleRate != 0) { + d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / frameCount / 1000); + d->length = frameCount / d->sampleRate; + } } else if (packetType == "RG") { diff --git a/tests/data/zerodiv.mpc b/tests/data/zerodiv.mpc new file mode 100644 index 0000000000000000000000000000000000000000..d3ea57c75ca8a58f8bcc9bf0388935d59f8d70d5 GIT binary patch literal 405 zcmeYbaP|)N;Gg>Ipa@4}XS)kS0|OX1<|LKoGB_oAYnU*r0Omshf*Zff{mv8pNPtA(!>Pz6wf z0!T@+LUF5wf&$P~h4RdjjQ>DT4AJ|VAp})#WNKm&gS~-)fn$Jch@-n}`~`LfhLsE) z3~X^FD3+ip)Jy}q&kSN6Lx=zalI;x9V8`SZpgQCK|B}RXJkI!o #include #include #include #include #include #include +#include #include "utils.h" using namespace std; @@ -17,6 +17,7 @@ class TestMPC : public CppUnit::TestFixture CPPUNIT_TEST(testPropertiesSV7); CPPUNIT_TEST(testPropertiesSV5); CPPUNIT_TEST(testPropertiesSV4); + CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST_SUITE_END(); public: @@ -61,6 +62,12 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); } + void testFuzzedFile1() + { + MPC::File f(TEST_FILE_PATH_C("zerodiv.mpc")); + CPPUNIT_ASSERT(f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC); From 65664e685553579493c91b38b93bf0f981c27688 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 8 Jan 2015 12:28:20 +0900 Subject: [PATCH 43/46] Check for EOF to fix an infinite loop while parsing fuzzed MPC files. --- taglib/mpc/mpcproperties.cpp | 8 ++++++-- tests/data/infloop.mpc | Bin 0 -> 434 bytes tests/test_mpc.cpp | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/data/infloop.mpc diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index f11f8ecf..a162b8ee 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -197,10 +197,15 @@ void MPC::Properties::readSV8(File *file) unsigned long packetSize = readSize(file, packetSizeLength); unsigned long dataSize = packetSize - 2 - packetSizeLength; + const ByteVector data = file->readBlock(dataSize); + if(data.size() != dataSize) { + debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size."); + break; + } + if(packetType == "SH") { // Stream Header // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket - ByteVector data = file->readBlock(dataSize); readSH = true; TagLib::uint pos = 4; @@ -225,7 +230,6 @@ void MPC::Properties::readSV8(File *file) else if (packetType == "RG") { // Replay Gain // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket - ByteVector data = file->readBlock(dataSize); readRG = true; int replayGainVersion = data[0]; diff --git a/tests/data/infloop.mpc b/tests/data/infloop.mpc new file mode 100644 index 0000000000000000000000000000000000000000..46861ab378cc3060fb759333490bb4d2852ccd36 GIT binary patch literal 434 zcmeYbaP|&9fgfCgN*5#-zhGv70Qc0qqErT_#GIVO6fuxcNKs;OHUooWQFdl=395O- z=_{dukwB*~WR(_|D5T{VDU_rZm#|@Rz9v*GB(o$Zl_3|+wT3KE>B!W?A_gM^0|UnZ j*APc{*N6)Z Date: Thu, 8 Jan 2015 12:49:33 +0900 Subject: [PATCH 44/46] Check the packet size to fix a segfault error while parsing fuzzed MPC files. --- taglib/mpc/mpcproperties.cpp | 21 +++++++++++++++++++++ tests/data/segfault.mpc | Bin 0 -> 19 bytes tests/test_mpc.cpp | 7 +++++++ 3 files changed, 28 insertions(+) create mode 100644 tests/data/segfault.mpc diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index a162b8ee..ac9d1b45 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -206,13 +206,28 @@ void MPC::Properties::readSV8(File *file) if(packetType == "SH") { // Stream Header // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket + + if(dataSize <= 5) { + debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse."); + break; + } + readSH = true; TagLib::uint pos = 4; d->version = data[pos]; pos += 1; d->sampleFrames = readSize(data.mid(pos), pos); + if(pos > dataSize - 3) { + debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); + break; + } + ulong begSilence = readSize(data.mid(pos), pos); + if(pos > dataSize - 2) { + debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); + break; + } const ushort flags = data.toUShort(pos, true); pos += 2; @@ -230,6 +245,12 @@ void MPC::Properties::readSV8(File *file) else if (packetType == "RG") { // Replay Gain // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket + + if(dataSize <= 9) { + debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse."); + break; + } + readRG = true; int replayGainVersion = data[0]; diff --git a/tests/data/segfault.mpc b/tests/data/segfault.mpc new file mode 100644 index 0000000000000000000000000000000000000000..2c7e29fb78fc1b5eff50e48411925d50562fd666 GIT binary patch literal 19 ZcmeYbaP|)NV4nKxpa=&85d3Fg0026+1vLNw literal 0 HcmV?d00001 diff --git a/tests/test_mpc.cpp b/tests/test_mpc.cpp index c79d0a8c..1204b0c2 100644 --- a/tests/test_mpc.cpp +++ b/tests/test_mpc.cpp @@ -19,6 +19,7 @@ class TestMPC : public CppUnit::TestFixture CPPUNIT_TEST(testPropertiesSV4); CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); + CPPUNIT_TEST(testFuzzedFile3); CPPUNIT_TEST_SUITE_END(); public: @@ -75,6 +76,12 @@ public: CPPUNIT_ASSERT(f.isValid()); } + void testFuzzedFile3() + { + MPC::File f(TEST_FILE_PATH_C("segfault.mpc")); + CPPUNIT_ASSERT(f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC); From e463d14f2e483f21ab14b18a7c84574e60691fd0 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 8 Jan 2015 13:05:56 +0900 Subject: [PATCH 45/46] Check for EOF to fix a segfault while parsing fuzzed MPC files. --- taglib/mpc/mpcproperties.cpp | 37 ++++++++++++++++++++++++------------ tests/data/segfault2.mpc | 1 + tests/test_mpc.cpp | 7 +++++++ 3 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/data/segfault2.mpc diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index ac9d1b45..128eff42 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -155,30 +155,36 @@ int MPC::Properties::albumPeak() const // private members //////////////////////////////////////////////////////////////////////////////// -unsigned long readSize(File *file, TagLib::uint &sizelength) +unsigned long readSize(File *file, TagLib::uint &sizeLength, bool &eof) { + sizeLength = 0; + eof = false; + unsigned char tmp; unsigned long size = 0; do { - ByteVector b = file->readBlock(1); + const ByteVector b = file->readBlock(1); + if(b.isEmpty()) { + eof = true; + break; + } + tmp = b[0]; size = (size << 7) | (tmp & 0x7F); - sizelength++; + sizeLength++; } while((tmp & 0x80)); return size; } -unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength) +unsigned long readSize(const ByteVector &data, TagLib::uint &pos) { unsigned char tmp; unsigned long size = 0; - unsigned long pos = 0; do { tmp = data[pos++]; size = (size << 7) | (tmp & 0x7F); - sizelength++; } while((tmp & 0x80) && (pos < data.size())); return size; } @@ -192,10 +198,17 @@ void MPC::Properties::readSV8(File *file) bool readSH = false, readRG = false; while(!readSH && !readRG) { - ByteVector packetType = file->readBlock(2); - uint packetSizeLength = 0; - unsigned long packetSize = readSize(file, packetSizeLength); - unsigned long dataSize = packetSize - 2 - packetSizeLength; + const ByteVector packetType = file->readBlock(2); + + uint packetSizeLength; + bool eof; + const unsigned long packetSize = readSize(file, packetSizeLength, eof); + if(eof) { + debug("MPC::Properties::readSV8() - Reached to EOF."); + break; + } + + const unsigned long dataSize = packetSize - 2 - packetSizeLength; const ByteVector data = file->readBlock(dataSize); if(data.size() != dataSize) { @@ -217,13 +230,13 @@ void MPC::Properties::readSV8(File *file) TagLib::uint pos = 4; d->version = data[pos]; pos += 1; - d->sampleFrames = readSize(data.mid(pos), pos); + d->sampleFrames = readSize(data, pos); if(pos > dataSize - 3) { debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); break; } - ulong begSilence = readSize(data.mid(pos), pos); + ulong begSilence = readSize(data, pos); if(pos > dataSize - 2) { debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); break; diff --git a/tests/data/segfault2.mpc b/tests/data/segfault2.mpc new file mode 100644 index 00000000..fcfa982f --- /dev/null +++ b/tests/data/segfault2.mpc @@ -0,0 +1 @@ +MPCKSH \ No newline at end of file diff --git a/tests/test_mpc.cpp b/tests/test_mpc.cpp index 1204b0c2..0589740f 100644 --- a/tests/test_mpc.cpp +++ b/tests/test_mpc.cpp @@ -20,6 +20,7 @@ class TestMPC : public CppUnit::TestFixture CPPUNIT_TEST(testFuzzedFile1); CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST(testFuzzedFile3); + CPPUNIT_TEST(testFuzzedFile4); CPPUNIT_TEST_SUITE_END(); public: @@ -82,6 +83,12 @@ public: CPPUNIT_ASSERT(f.isValid()); } + void testFuzzedFile4() + { + MPC::File f(TEST_FILE_PATH_C("segfault2.mpc")); + CPPUNIT_ASSERT(f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC); From 0839a23902ad83407bd0bcafff1177014dbfb90b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 17 Feb 2015 12:36:57 +0900 Subject: [PATCH 46/46] Separate the tests for fuzzed APE files. --- tests/test_ape.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/test_ape.cpp b/tests/test_ape.cpp index e79e8f08..2f842f65 100644 --- a/tests/test_ape.cpp +++ b/tests/test_ape.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include "utils.h" using namespace std; @@ -16,7 +16,8 @@ class TestAPE : public CppUnit::TestFixture CPPUNIT_TEST(testProperties399); CPPUNIT_TEST(testProperties396); CPPUNIT_TEST(testProperties390); - CPPUNIT_TEST(testFuzzedFiles); + CPPUNIT_TEST(testFuzzedFile1); + CPPUNIT_TEST(testFuzzedFile2); CPPUNIT_TEST_SUITE_END(); public: @@ -48,13 +49,16 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); } - void testFuzzedFiles() + void testFuzzedFile1() { - APE::File f1(TEST_FILE_PATH_C("longloop.ape")); - CPPUNIT_ASSERT(f1.isValid()); + APE::File f(TEST_FILE_PATH_C("longloop.ape")); + CPPUNIT_ASSERT(f.isValid()); + } - APE::File f2(TEST_FILE_PATH_C("zerodiv.ape")); - CPPUNIT_ASSERT(f2.isValid()); + void testFuzzedFile2() + { + APE::File f(TEST_FILE_PATH_C("zerodiv.ape")); + CPPUNIT_ASSERT(f.isValid()); } };