diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index 92cfccd4..91f3fdd7 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,8 +47,8 @@ if (MSVC AND ENABLE_STATIC_RUNTIME) endif() set(TAGLIB_LIB_MAJOR_VERSION "1") -set(TAGLIB_LIB_MINOR_VERSION "8") -set(TAGLIB_LIB_PATCH_VERSION "0") +set(TAGLIB_LIB_MINOR_VERSION "9") +set(TAGLIB_LIB_PATCH_VERSION "1") set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}") @@ -56,9 +56,9 @@ set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VE # 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0. # 3. If any interfaces have been added since the last public release, then increment age. # 4. If any interfaces have been removed since the last public release, then set age to 0. -set(TAGLIB_SOVERSION_CURRENT 13) +set(TAGLIB_SOVERSION_CURRENT 15) set(TAGLIB_SOVERSION_REVISION 0) -set(TAGLIB_SOVERSION_AGE 12) +set(TAGLIB_SOVERSION_AGE 14) math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}") math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}") diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake old mode 100755 new mode 100644 diff --git a/INSTALL b/INSTALL index 3be4d131..9e6e8075 100644 --- a/INSTALL +++ b/INSTALL @@ -46,15 +46,102 @@ the include folder to the project's User Header Search Paths. Windows ------- -For building a static library on Windows with Visual Studio 2010, cd to -the TagLib folder then: +It's Windows ... Systems vary! +This means you need to adjust things to suit your system, especially paths. - cmake -DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON -G "Visual Studio 10" ... +Tested with: + Microsoft Visual Studio 2010 + Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b) + MinGW32-4.8.0 + +Requirements: + 1. Tool chain, Build Environment, Whatever ya want to call it ... + Installed and working. + 2. CMake program. (Available at: www.cmake.org) + Installed and working. + +Optional: + 1. Zlib library. + Available in some Tool Chains, Not all. + Search the web, Take your choice. + +Useful configuration options used with CMake (GUI and/or Command line): + Any of the ZLIB_ variables may be used at the command line, ZLIB_ROOT is only + available on the Command line. + ZLIB_ROOT= Where to find ZLib's root directory. + Assumes parent of: \include and \lib. + ZLIB_INCLUDE_DIR= Where to find ZLib's Include directory. + ZLIB_LIBRARY= Where to find ZLib's Library. + CMAKE_INSTALL_PREFIX= Where to install Taglib. + CMAKE_BUILD_TYPE= Release, Debug, etc ... (Not available in MSVC) + +The easiest way is at the Command Prompt. + MSVS Command Prompt for MSVS Users. + (Batch file and/or Shortcuts are your friends) + + 1. Build the Makefiles: + Replace "GENERATOR" with your needs. + For MSVS : "Visual Studio X" where X is the single or two digit version. + For MinGW: "MinGW Makefiles" + + C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib + + Or use the CMake GUI: + 1. Open CMake GUI. + 2. Set Paths. + "Where is the source code" and "Where to build the binaries" + Example, Both would be: C:\GitRoot\taglib + 3. Tick: Advanced + 4. Select: Configure + 5. Select: Generator + 6. Tick: Use default native compilers + 7. Select: Finish + Wait until done. + 5. If using ZLib, Scroll down. + (to the bottom of the list of options ... should go over them all) + 1. Edit: ZLIB_INCLUDE_DIR + 2. Edit: ZLIB_LIBRARY + 6. Select: Generate + + 2. Build the project: + MSVS: + C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release + OR (Depending on MSVS version or personal choice) + C:\GitRoot\taglib> devenv all_build.vcxproj /build Release + MinGW: + C:\GitRoot\taglib> gmake + OR (Depending on MinGW install) + C:\GitRoot\taglib> mingw32-make + + Or in the MSVS GUI: + 1. Open MSVS. + 2. Open taglib solution. + 3. Set build type to: Release (look in the tool bars) + 2. Hit F7 to build the solution. (project) + + 3. Install the project: + (Change 'install' to 'uninstall' to uninstall the project) + MSVS: + C:\GitRoot\taglib> msbuild install.vcxproj + OR (Depending on MSVC version or personal choice) + C:\GitRoot\taglib> devenv install.vcxproj + MinGW: + C:\GitRoot\taglib> gmake install + OR (Depending on MinGW install) + C:\GitRoot\taglib> mingw32-make install + + Or in the MSVS GUI: + 1. Open project. + 2. Open Solution Explorer. + 3. Right Click: INSTALL + 4. Select: Project Only + 5. Select: Build Only INSTALL + +To build a static library enable the following two options with CMake. + -DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the static runtime library, rather than the DLL form of the runtime. -CMake will create a Visual Studio solution, taglib.sln that you can open and -build as normal. Unit Tests ---------- diff --git a/NEWS b/NEWS index 24d396cd..72d41fba 100644 --- a/NEWS +++ b/NEWS @@ -1,16 +1,33 @@ -TagLib 1.9 (In Development) +TagLib 1.9.1 (Oct 8, 2013) ========================== + * Fixed binary incompatible change in TagLib::Map and TagLib::List. + * Fixed constructing String from ByteVector. + * Fixed compilation on MSVC with the /Zc:wchar_t- option. + * Fixed detecting of RIFF files with invalid chunk sizes. + * Added TagLib::MP4::PropertyMap::codec(). + +TagLib 1.9 (Oct 6, 2013) +======================== + * Added support for the Ogg Opus file format. * Added support for INFO tags in WAV files. * Changed FileStream to use Windows file API. * Included taglib-config.cmd script for Windows. * New ID3v1::Tag methods for working directly with genre numbers. * New MPEG::File methods for checking which tags are saved in the file. + * Added support for the PropertyMap API to ASF and MP4 files. + * Added MusicBrainz identifiers to the PropertyMap API. + * Allowed reading of MP4 cover art without an explicitly specified format. * Better parsing of corrupted FLAC files. * Fixed saving of PropertyMap comments without description into ID3v2 tags. * Fixed crash when parsing certain XM files. * Fixed compilation of unit test with clang. + * Better handling of files that can't be open or have read-only permissions. + * Improved atomic reference counting. + * New hookable API for debug messages. + * More complete Windows install instructions. + * Many smaller bug fixes and performance improvements. TagLib 1.8 (Sep 6, 2012) ======================== diff --git a/taglib-config.cmd.cmake b/taglib-config.cmd.cmake old mode 100755 new mode 100644 diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/taglib/fileref.h b/taglib/fileref.h old mode 100755 new mode 100644 diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index 379ecc80..8f6ef0fa 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -25,45 +25,16 @@ #include "config.h" -#include -#include - #include "taglib.h" #include "tdebug.h" #include "tsmartptr.h" #include "mp4item.h" +#include "tutils.h" 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); - } - struct ItemData { bool valid; @@ -263,26 +234,26 @@ MP4::Item::toString() const case TypeBool: return d->data->m_bool ? "true" : "false"; case TypeInt: - return format("%d", d->data->m_int); + return Utils::formatString("%d", d->data->m_int); case TypeIntPair: - return format("%d/%d", d->data->m_intPair.first, d->data->m_intPair.second); + return Utils::formatString("%d/%d", d->data->m_intPair.first, d->data->m_intPair.second); case TypeByte: - return format("%d", d->data->m_byte); + return Utils::formatString("%d", d->data->m_byte); case TypeUInt: - return format("%u", d->data->m_uint); + return Utils::formatString("%u", d->data->m_uint); case TypeLongLong: - return format("%lld", d->data->m_longlong); + return Utils::formatString("%lld", d->data->m_longlong); case TypeStringList: return d->data->m_stringList.toString(" / "); case TypeByteVectorList: for(TagLib::uint i = 0; i < d->data->m_byteVectorList.size(); i++) { - desc.append(format( + desc.append(Utils::formatString( "[%d bytes of data]", static_cast(d->data->m_byteVectorList[i].size()))); } return desc.toString(", "); case TypeCoverArtList: for(TagLib::uint i = 0; i < d->data->m_coverArtList.size(); i++) { - desc.append(format( + desc.append(Utils::formatString( "[%d bytes of data]", static_cast(d->data->m_coverArtList[i].data().size()))); } return desc.toString(", "); diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index 5ab189d8..2eef4ec2 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -41,13 +41,7 @@ public: channels(0), bitsPerSample(0), encrypted(false), - format(Unknown) {} - - enum Format { - Unknown = 0, - AAC = 1, - ALAC = 2, - }; + codec(Unknown) {} int length; int bitrate; @@ -55,9 +49,13 @@ public: int channels; int bitsPerSample; bool encrypted; - Format format; + Codec codec; }; +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + MP4::AudioProperties::AudioProperties(File *file, MP4::Atoms *atoms, ReadStyle style) : d(new PropertiesPrivate()) { @@ -105,14 +103,20 @@ MP4::AudioProperties::isEncrypted() const return d->encrypted; } +MP4::AudioProperties::Codec +MP4::AudioProperties::codec() const +{ + return d->codec; +} + String MP4::AudioProperties::toString() const { String format; - if(d->format == PropertiesPrivate::AAC) { + if(d->codec == AAC) { format = "AAC"; } - else if(d->format == PropertiesPrivate::ALAC) { + else if(d->codec == ALAC) { format = "ALAC"; } else { @@ -125,6 +129,10 @@ MP4::AudioProperties::toString() const return desc.toString(", "); } +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + void MP4::AudioProperties::read(File *file, Atoms *atoms) { @@ -193,7 +201,7 @@ MP4::AudioProperties::read(File *file, Atoms *atoms) file->seek(atom->offset); data = file->readBlock(atom->length); if(data.mid(20, 4) == "mp4a") { - d->format = PropertiesPrivate::AAC; + d->codec = AAC; d->channels = data.toInt16BE(40); d->bitsPerSample = data.toInt16BE(42); d->sampleRate = data.toUInt32BE(46); @@ -214,7 +222,7 @@ MP4::AudioProperties::read(File *file, Atoms *atoms) } } else if (data.mid(20, 4) == "alac") { - d->format = PropertiesPrivate::ALAC; + d->codec = ALAC; if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); d->channels = data.at(73); diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h index 99de9dd0..53f6c01f 100644 --- a/taglib/mp4/mp4properties.h +++ b/taglib/mp4/mp4properties.h @@ -40,6 +40,12 @@ namespace TagLib { class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties { public: + enum Codec { + Unknown = 0, + AAC, + ALAC + }; + AudioProperties(File *file, Atoms *atoms, ReadStyle style = Average); virtual ~AudioProperties(); @@ -50,6 +56,9 @@ namespace TagLib { virtual int bitsPerSample() const; bool isEncrypted() const; + //! Audio codec used in the MP4 file + Codec codec() const; + String toString() const; private: diff --git a/taglib/mpeg/id3v1/id3v1tag.h b/taglib/mpeg/id3v1/id3v1tag.h index 46813b3f..05e00521 100644 --- a/taglib/mpeg/id3v1/id3v1tag.h +++ b/taglib/mpeg/id3v1/id3v1tag.h @@ -153,14 +153,14 @@ namespace TagLib { /*! * Returns the genre in number. * - * /note Normally 255 indicates that this tag contains no genre. + * \note Normally 255 indicates that this tag contains no genre. */ TagLib::uint genreNumber() const; /*! * Sets the genre in number to \a i. * - * /note Valid value is from 0 up to 255. Normally 255 indicates that + * \note Valid value is from 0 up to 255. Normally 255 indicates that * this tag contains no genre. */ void setGenreNumber(TagLib::uint i); diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 60c6d18b..2f1d73e6 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -325,7 +325,7 @@ namespace static const TagLib::uint frameTranslationSize = 51; static const char *frameTranslation[][2] = { // Text information frames - { "TALB", "ALBUM"}, + { "TALB", "ALBUM" }, { "TBPM", "BPM" }, { "TCOM", "COMPOSER" }, { "TCON", "GENRE" }, @@ -388,7 +388,7 @@ namespace //{ "USLT", "LYRICS" }, handled specially }; - static const TagLib::uint txxxFrameTranslationSize = 7; + static const TagLib::uint txxxFrameTranslationSize = 8; static const char *txxxFrameTranslation[][2] = { { "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, { "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, diff --git a/taglib/mpeg/id3v2/id3v2tag.h b/taglib/mpeg/id3v2/id3v2tag.h index c58d4d86..905ecc9b 100644 --- a/taglib/mpeg/id3v2/id3v2tag.h +++ b/taglib/mpeg/id3v2/id3v2tag.h @@ -89,7 +89,7 @@ namespace TagLib { /*! * Encode a ByteVector with the data from \a s. * - * /note Not implemented intentionally. Always returns empty \s ByteVector. + * \note Not implemented intentionally. Always returns empty \s ByteVector. */ virtual ByteVector render(const String &s) const; }; diff --git a/taglib/riff/aiff/aiffproperties.cpp b/taglib/riff/aiff/aiffproperties.cpp index 6d103e83..25012ecd 100644 --- a/taglib/riff/aiff/aiffproperties.cpp +++ b/taglib/riff/aiff/aiffproperties.cpp @@ -136,11 +136,8 @@ void RIFF::AIFF::AudioProperties::read(const ByteVector &data) d->bitrate = static_cast((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; - if(data.size() < 23) { - debug("RIFF::AIFF::AudioProperties::read() - \"COMM\" chunk is too short for AIFF-C."); - return; + if(data.size() >= 23) { + d->compressionType = data.mid(18, 4); + d->compressionName = String(data.mid(23, static_cast(data[22]))); } - - d->compressionType = data.mid(18, 4); - d->compressionName = String(data.mid(23, static_cast(data[22]))); } diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index 78335690..34dde282 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -28,6 +28,7 @@ #include #include "rifffile.h" +#include #include using namespace TagLib; @@ -69,6 +70,20 @@ RIFF::File::~File() delete d; } +bool RIFF::File::isValidChunkName(const ByteVector &name) // static +{ + if(name.size() != 4) + return false; + + for(ByteVector::ConstIterator it = name.begin(); it != name.end(); ++it) { + const uchar c = static_cast(*it); + if(c < 32 || 127 < c) + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// @@ -253,22 +268,6 @@ void RIFF::File::removeChunk(const ByteVector &name) // private members //////////////////////////////////////////////////////////////////////////////// -namespace -{ - bool isValidChunkID(const ByteVector &name) - { - if(name.size() != 4) { - return false; - } - for(int i = 0; i < 4; i++) { - if(name[i] < 32 || name[i] > 127) { - return false; - } - } - return true; - } -} - void RIFF::File::read() { d->type = readBlock(4); @@ -289,13 +288,13 @@ void RIFF::File::read() else chunkSize = readBlock(4).toUInt32LE(0); - if(!isValidChunkID(chunkName)) { + if(!isValidChunkName(chunkName)) { debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID"); setValid(false); break; } - if(tell() + chunkSize > uint(length())) { + if(static_cast(tell()) + chunkSize > static_cast(length())) { debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); setValid(false); break; diff --git a/taglib/riff/rifffile.h b/taglib/riff/rifffile.h index 6c2a9357..2a759f8c 100644 --- a/taglib/riff/rifffile.h +++ b/taglib/riff/rifffile.h @@ -51,6 +51,11 @@ namespace TagLib { */ virtual ~File(); + /*! + * Returns whether or not \a name is valid as a chunk name. + */ + static bool isValidChunkName(const ByteVector &name); + protected: File(FileName file, ByteOrder endianness); diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp index fac8785c..9857f489 100644 --- a/taglib/riff/wav/infotag.cpp +++ b/taglib/riff/wav/infotag.cpp @@ -26,27 +26,12 @@ #include #include +#include "rifffile.h" #include "infotag.h" using namespace TagLib; using namespace RIFF::Info; -namespace -{ - bool isValidChunkID(const ByteVector &name) - { - if(name.size() != 4) - return false; - - for(int i = 0; i < 4; i++) { - if(name[i] < 32 || 127 < name[i]) - return false; - } - - return true; - } -} - class RIFF::Info::Tag::TagPrivate { public: @@ -186,6 +171,11 @@ bool RIFF::Info::Tag::isEmpty() const return d->fieldListMap.isEmpty(); } +FieldListMap RIFF::Info::Tag::fieldListMap() const +{ + return d->fieldListMap; +} + String RIFF::Info::Tag::fieldText(const ByteVector &id) const { if(d->fieldListMap.contains(id)) @@ -197,7 +187,7 @@ String RIFF::Info::Tag::fieldText(const ByteVector &id) const void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) { // id must be four-byte long pure ASCII string. - if(!isValidChunkID(id)) + if(!RIFF::File::isValidChunkName(id)) return; if(!s.isEmpty()) diff --git a/taglib/riff/wav/infotag.h b/taglib/riff/wav/infotag.h index a812e87e..ff01af7a 100644 --- a/taglib/riff/wav/infotag.h +++ b/taglib/riff/wav/infotag.h @@ -38,7 +38,8 @@ namespace TagLib { class File; - //! A RIFF Info tag implementation. + //! A RIFF INFO tag implementation. + namespace RIFF { namespace Info { @@ -76,14 +77,15 @@ namespace TagLib { virtual ByteVector render(const String &s) const; }; - //! The main class in the ID3v2 implementation + //! The main class in the RIFF INFO tag implementation /*! - * This is the main class in the INFO tag implementation. RIFF INFO tag is a - * metadata format found in WAV audio and AVI video files. Though it is a part - * of Microsoft/IBM's RIFF specification, the author could not find the official - * documents about it. So, this implementation is referring to unofficial documents - * online and some applications' behaviors especially Windows Explorer. + * This is the main class in the INFO tag implementation. RIFF INFO tag is + * a metadata format found in WAV audio and AVI video files. Though it is a + * part of Microsoft/IBM's RIFF specification, the author could not find the + * official documents about it. So, this implementation is referring to + * unofficial documents on the web and some applications' behaviors especially + * Windows Explorer. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { @@ -120,6 +122,18 @@ namespace TagLib { virtual bool isEmpty() const; + /*! + * Returns a copy of the internal fields of the tag. The returned map directly + * reflects the contents of the "INFO" chunk. + * + * \note Modifying this map does not affect the tag's internal data. + * Use setFieldText() and removeField() instead. + * + * \see setFieldText() + * \see removeField() + */ + FieldListMap fieldListMap() const; + /* * Gets the value of the field with the ID \a id. */ diff --git a/taglib/toolkit/taglib.h b/taglib/toolkit/taglib.h index 723de570..dbbdc81c 100644 --- a/taglib/toolkit/taglib.h +++ b/taglib/toolkit/taglib.h @@ -27,8 +27,8 @@ #define TAGLIB_H #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 8 -#define TAGLIB_PATCH_VERSION 0 +#define TAGLIB_MINOR_VERSION 9 +#define TAGLIB_PATCH_VERSION 1 #if (defined(_MSC_VER) && _MSC_VER >= 1600) #define TAGLIB_CONSTRUCT_BITSET(x) static_cast(x) diff --git a/taglib/toolkit/tbytevector.cpp b/taglib/toolkit/tbytevector.cpp index 796cf6da..4a02dbc7 100644 --- a/taglib/toolkit/tbytevector.cpp +++ b/taglib/toolkit/tbytevector.cpp @@ -27,11 +27,13 @@ #include #endif +#include #include #include #include #include #include +#include #include "tstring.h" #include "tdebug.h" @@ -464,60 +466,41 @@ ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &wit if(pattern.size() == 0 || pattern.size() > size()) return *this; - const size_t withSize = with.size(); + const size_t withSize = with.size(); const size_t patternSize = pattern.size(); + const ptrdiff_t diff = withSize - patternSize; + size_t offset = 0; + while (true) + { + offset = find(pattern, offset); + if(offset == npos) + break; - if(withSize == patternSize) { - // I think this case might be common enough to optimize it detach(); - offset = find(pattern); - while(offset != npos) { - ::memcpy(DATA(d) + offset, DATA(with.d), withSize); - offset = find(pattern, offset + withSize); - } - return *this; - } - // calculate new size: - size_t newSize = 0; - for(;;) { - const size_t next = find(pattern, offset); - if(next == npos) { - if(offset == 0) - // pattern not found, do nothing: - return *this; - newSize += size() - offset; + if(diff < 0) { + ::memmove( + data() + offset + withSize, + data() + offset + patternSize, + size() - offset - patternSize); + resize(size() + diff); + } + else if(diff > 0) { + resize(size() + diff); + ::memmove( + data() + offset + withSize, + data() + offset + patternSize, + size() - diff - offset - patternSize); + } + + ::memcpy(data() + offset, with.data(), with.size()); + + offset += withSize; + if(offset > size() - patternSize) break; - } - newSize += (next - offset) + withSize; - offset = next + patternSize; } - // new private data of appropriate size: - ByteVectorPrivate newData(newSize, '\0'); - char *target = &(*newData.data)[0]; - const char *source = DATA(d); - - // copy modified data into new private data: - offset = 0; - for(;;) { - const size_t next = find(pattern, offset); - if(next == npos) { - ::memcpy(target, source + offset, size() - offset); - break; - } - const size_t chunkSize = next - offset; - ::memcpy(target, source + offset, chunkSize); - target += chunkSize; - ::memcpy(target, DATA(with.d), withSize); - target += withSize; - offset += chunkSize + patternSize; - } - - // replace private data: - *d = newData; - return *this; } diff --git a/taglib/toolkit/tdebug.cpp b/taglib/toolkit/tdebug.cpp index 71350af7..acdb6cc2 100644 --- a/taglib/toolkit/tdebug.cpp +++ b/taglib/toolkit/tdebug.cpp @@ -29,44 +29,13 @@ #include "tdebug.h" #include "tstring.h" +#include "tutils.h" #include "tdebuglistener.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. @@ -88,7 +57,8 @@ namespace TagLib 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/tiostream.cpp b/taglib/toolkit/tiostream.cpp index 69f01227..d847ad60 100644 --- a/taglib/toolkit/tiostream.cpp +++ b/taglib/toolkit/tiostream.cpp @@ -41,8 +41,12 @@ namespace bool supportsUnicode() { +#ifdef UNICODE + return true; +#else const FARPROC p = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW"); return (p != NULL); +#endif } // Indicates whether the system supports Unicode file names. diff --git a/taglib/toolkit/tiostream.h b/taglib/toolkit/tiostream.h index 571019a0..fe1ce673 100644 --- a/taglib/toolkit/tiostream.h +++ b/taglib/toolkit/tiostream.h @@ -28,15 +28,12 @@ #include "tbytevector.h" -#ifdef _WIN32 -# include "tstring.h" -# include "tdebug.h" -#endif - namespace TagLib { #ifdef _WIN32 + class String; + class TAGLIB_EXPORT FileName { public: diff --git a/taglib/toolkit/trefcounter.h b/taglib/toolkit/trefcounter.h index 9c927e81..424f6d49 100644 --- a/taglib/toolkit/trefcounter.h +++ b/taglib/toolkit/trefcounter.h @@ -35,6 +35,7 @@ */ namespace TagLib { + class TAGLIB_EXPORT RefCounter { public: @@ -49,7 +50,9 @@ namespace TagLib class RefCounterPrivate; RefCounterPrivate *d; }; + } #endif // DO_NOT_DOCUMENT #endif + diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 25d8db1c..03c350cd 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -36,7 +36,6 @@ #include "tutils.h" #include -#include #include #ifdef HAVE_STD_CODECVT @@ -156,11 +155,6 @@ public: { } - StringPrivate(const std::wstring &s) - : data(new std::wstring(s)) - { - } - StringPrivate(size_t n, wchar_t c) : data(new std::wstring(n, c)) { @@ -268,6 +262,9 @@ String::String(const ByteVector &v, Type t) copyFromUTF8(v.data(), v.size()); else copyFromUTF16(v.data(), v.size(), t); + + // If we hit a null in the ByteVector, shrink the string again. + d->data->resize(::wcslen(d->data->c_str())); } //////////////////////////////////////////////////////////////////////////////// @@ -390,7 +387,7 @@ String String::upper() const { static const int shift = 'A' - 'a'; - String s(*this); + String s(*d->data); for(Iterator it = s.begin(); it != s.end(); ++it) { if(*it >= 'a' && *it <= 'z') *it = *it + shift; @@ -559,30 +556,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[](size_t i) @@ -672,54 +646,43 @@ String &String::operator=(const String &s) String &String::operator=(const std::string &s) { - d->data.reset(new std::wstring()); - copyFromLatin1(s.c_str(), s.length()); - + *this = String(s); return *this; } String &String::operator=(const std::wstring &s) { - d->data.reset(new std::wstring(s)); + *this = String(s); return *this; } String &String::operator=(const wchar_t *s) { - d->data.reset(new std::wstring()); - copyFromUTF16(s, ::wcslen(s), WCharByteOrder); - + *this = String(s); return *this; } String &String::operator=(char c) { - d->data.reset(new std::wstring(1, c)); + *this = String(c); return *this; } String &String::operator=(wchar_t c) { - d->data.reset(new std::wstring(1, c)); + *this = String(c); return *this; } String &String::operator=(const char *s) { - d->data.reset(new std::wstring()); - copyFromLatin1(s, ::strlen(s)); - + *this = String(s); return *this; } String &String::operator=(const ByteVector &v) { - d->data.reset(new std::wstring()); - copyFromLatin1(v.data(), v.size()); - - // If we hit a null in the ByteVector, shrink the string again. - d->data->resize(::wcslen(d->data->c_str())); - + *this = String(v); return *this; } diff --git a/taglib/toolkit/tstring.h b/taglib/toolkit/tstring.h index 32860e19..23714d45 100644 --- a/taglib/toolkit/tstring.h +++ b/taglib/toolkit/tstring.h @@ -129,11 +129,21 @@ namespace TagLib { /*! * Makes a deep copy of the data in \a s. + * + * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or + * UTF16LE, when used with other codecs it will simply print a warning and + * exit. UTF16BE or UTF16LE is automatically chosen as default according + * to the CPU byte order */ String(const std::wstring &s, Type t = WCharByteOrder); /*! * Makes a deep copy of the data in \a s. + * + * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or + * UTF16LE, when used with other codecs it will simply print a warning and + * exit. UTF16BE or UTF16LE is automatically chosen as default according + * to the CPU byte order */ String(const wchar_t *s, Type t = WCharByteOrder); @@ -147,8 +157,13 @@ namespace TagLib { /*! * Makes a deep copy of the data in \a c. + * + * \note This should only be used with the 16-bit codecs UTF16, UTF16BE or + * UTF16LE, when used with other codecs it will simply print a warning and + * exit. UTF16BE or UTF16LE is automatically chosen as default according + * to the CPU byte order */ - String(wchar_t c, Type t = Latin1); + String(wchar_t c, Type t = WCharByteOrder); /*! @@ -161,9 +176,6 @@ namespace TagLib { /*! * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when - * used with other codecs it will simply print a warning and exit. */ String(const ByteVector &v, Type t = Latin1); @@ -219,7 +231,7 @@ namespace TagLib { * The returned pointer remains valid until this String instance is destroyed * or any other method of this String is called. * - * /note This returns a pointer to the String's internal data without any + * \note This returns a pointer to the String's internal data without any * conversions. * * \see toWString() diff --git a/taglib/toolkit/tutils.h b/taglib/toolkit/tutils.h index e6660065..e4e6c953 100644 --- a/taglib/toolkit/tutils.h +++ b/taglib/toolkit/tutils.h @@ -31,7 +31,7 @@ #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header #ifdef HAVE_CONFIG_H -#include +# include #endif #if defined(HAVE_MSC_BYTESWAP) @@ -44,6 +44,10 @@ # include #endif +#include "tstring.h" +#include +#include + namespace TagLib { namespace Utils @@ -144,6 +148,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; + } #ifdef SYSTEM_BYTEORDER diff --git a/taglib/toolkit/unicode.h b/taglib/toolkit/unicode.h index b9de0ea2..ebf1915d 100644 --- a/taglib/toolkit/unicode.h +++ b/taglib/toolkit/unicode.h @@ -106,6 +106,11 @@ 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 @@ -114,10 +119,10 @@ namespace Unicode { -typedef unsigned long UTF32; /* at least 32 bits */ -typedef wchar_t UTF16; /* TagLib assumes that wchar_t is sufficient for UTF-16. */ +typedef unsigned long UTF32; /* at least 32 bits */ +typedef wchar_t UTF16; /* TagLib assumes that wchar_t is sufficient for UTF-16. */ typedef unsigned char UTF8; /* typically 8 bits */ -typedef unsigned char Boolean; /* 0 or 1 */ +typedef unsigned char Boolean; /* 0 or 1 */ typedef enum { conversionOK = 0, /* conversion successful */ diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index 9dc023cc..941d7281 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -112,8 +112,9 @@ TagLib::uint WavPack::AudioProperties::sampleFrames() const namespace { - const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000, - 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; + static const unsigned int sample_rates[] = { + 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 }; } #define BYTES_STORED 3 diff --git a/taglib/wavpack/wavpackproperties.h b/taglib/wavpack/wavpackproperties.h index 06553a05..a02720e0 100644 --- a/taglib/wavpack/wavpackproperties.h +++ b/taglib/wavpack/wavpackproperties.h @@ -65,7 +65,12 @@ namespace TagLib { virtual int length() const; virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. 0 means unknown or custom. + */ virtual int sampleRate() const; + virtual int channels() const; /*! diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5012065e..a9b3f813 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -56,6 +56,7 @@ SET(test_runner_SRCS test_ape.cpp test_apetag.cpp test_wav.cpp + test_info.cpp test_wavpack.cpp test_mp4.cpp test_mp4item.cpp diff --git a/tests/test_bytevector.cpp b/tests/test_bytevector.cpp index 7965f531..eb844fcf 100644 --- a/tests/test_bytevector.cpp +++ b/tests/test_bytevector.cpp @@ -232,6 +232,11 @@ public: a.replace(ByteVector("ab"), ByteVector()); CPPUNIT_ASSERT_EQUAL(ByteVector("cdf"), a); } + { + ByteVector a("abcdabf"); + a.replace(ByteVector("bf"), ByteVector("x")); + CPPUNIT_ASSERT_EQUAL(ByteVector("abcdax"), a); + } } }; diff --git a/tests/test_id3v1.cpp b/tests/test_id3v1.cpp index 1d0a4974..7db2dbbf 100644 --- a/tests/test_id3v1.cpp +++ b/tests/test_id3v1.cpp @@ -1,7 +1,10 @@ #include #include +#include +#include #include #include +#include "utils.h" using namespace std; using namespace TagLib; @@ -16,8 +19,20 @@ public: void testStripWhiteSpace() { - ID3v1::StringHandler h; - CPPUNIT_ASSERT_EQUAL(String("Foo"), h.parse(ByteVector("Foo "))); + ScopedFileCopy copy("xing", ".mp3"); + string newname = copy.fileName(); + + { + MPEG::File f(newname.c_str()); + f.ID3v1Tag(true)->setArtist("Artist "); + f.save(); + } + + { + MPEG::File f(newname.c_str()); + CPPUNIT_ASSERT(f.ID3v1Tag(false)); + CPPUNIT_ASSERT_EQUAL(String("Artist"), f.ID3v1Tag(false)->artist()); + } } }; diff --git a/tests/test_info.cpp b/tests/test_info.cpp index f76fd67a..8e0d7154 100644 --- a/tests/test_info.cpp +++ b/tests/test_info.cpp @@ -1,9 +1,7 @@ -#include #include #include #include -#include -#include +#include #include "utils.h" using namespace std; @@ -23,7 +21,13 @@ public: CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); tag.setTitle("Test title 1"); + tag.setFieldText("TEST", "Dummy Text"); + CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title()); + + RIFF::Info::FieldListMap map = tag.fieldListMap(); + CPPUNIT_ASSERT_EQUAL(String("Test title 1"), map["INAM"]); + CPPUNIT_ASSERT_EQUAL(String("Dummy Text"), map["TEST"]); } void testNumericFields() diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index d7ea4a32..e38c4a6b 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -39,6 +39,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, ((MP4::AudioProperties *)f.audioProperties())->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::AAC, ((MP4::AudioProperties *)f.audioProperties())->codec()); } void testPropertiesALAC() @@ -49,6 +50,7 @@ public: CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT_EQUAL(16, ((MP4::AudioProperties *)f.audioProperties())->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(MP4::AudioProperties::ALAC, ((MP4::AudioProperties *)f.audioProperties())->codec()); } void testCheckValid() diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 0e2807ef..10dbbcd0 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -41,14 +41,28 @@ class TestString : public CppUnit::TestFixture CPPUNIT_TEST(testAppendCharDetach); CPPUNIT_TEST(testAppendStringDetach); CPPUNIT_TEST(testToInt); + CPPUNIT_TEST(testFromInt); CPPUNIT_TEST(testSubstr); CPPUNIT_TEST(testNewline); + CPPUNIT_TEST(testUpper); CPPUNIT_TEST_SUITE_END(); public: void testString() { + // Needs to know the system byte order for some Unicode tests. + bool littleEndian; + { + union { + int i; + char c; + } u; + + u.i = 1; + littleEndian = (u.c == 1) ? true : false; + } + String s = "taglib string"; ByteVector v = "taglib string"; CPPUNIT_ASSERT(v == s.data(String::Latin1)); @@ -72,8 +86,28 @@ public: String unicode2(unicode.to8Bit(true), String::UTF8); CPPUNIT_ASSERT(unicode == unicode2); - String unicode3(L"\u65E5\u672C\u8A9E"); - CPPUNIT_ASSERT(*(unicode3.toCWString() + 1) == L'\u672C'); + String unicode3(L"\u65E5\u672C\u8A9E"); + CPPUNIT_ASSERT(*(unicode3.toCWString() + 1) == L'\u672C'); + + String unicode4(L"\u65e5\u672c\u8a9e"); + CPPUNIT_ASSERT(unicode4[1] == L'\u672c'); + + String unicode5(L"\u65e5\u672c\u8a9e", String::UTF16BE); + CPPUNIT_ASSERT(unicode5[1] == (littleEndian ? L'\u2c67' : L'\u672c')); + + String unicode6(L"\u65e5\u672c\u8a9e", String::UTF16LE); + CPPUNIT_ASSERT(unicode6[1] == (littleEndian ? L'\u672c' : L'\u2c67')); + + wstring stduni = L"\u65e5\u672c\u8a9e"; + + String unicode7(stduni); + CPPUNIT_ASSERT(unicode7[1] == L'\u672c'); + + String unicode8(stduni, String::UTF16BE); + CPPUNIT_ASSERT(unicode8[1] == (littleEndian ? L'\u2c67' : L'\u672c')); + + String unicode9(stduni, String::UTF16LE); + CPPUNIT_ASSERT(unicode9[1] == (littleEndian ? L'\u672c' : L'\u2c67')); CPPUNIT_ASSERT(strcmp(String::number(0).toCString(), "0") == 0); CPPUNIT_ASSERT(strcmp(String::number(12345678).toCString(), "12345678") == 0); @@ -215,6 +249,12 @@ public: CPPUNIT_ASSERT_EQUAL(String("-123aa").toInt(), -123); } + void testFromInt() + { + CPPUNIT_ASSERT_EQUAL(String("123"), String::number(123)); + CPPUNIT_ASSERT_EQUAL(String("-123"), String::number(-123)); + } + void testSubstr() { CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2)); @@ -237,6 +277,14 @@ public: CPPUNIT_ASSERT_EQUAL(L'\x0d', String(crlf)[3]); CPPUNIT_ASSERT_EQUAL(L'\x0a', String(crlf)[4]); } + + void testUpper() + { + String s1 = "tagLIB 012 strING"; + String s2 = s1.upper(); + CPPUNIT_ASSERT_EQUAL(String("tagLIB 012 strING"), s1); + CPPUNIT_ASSERT_EQUAL(String("TAGLIB 012 STRING"), s2); + } };