Merge remote-tracking branch 'upstream/master' into qt5-update

This commit is contained in:
Jonas Kvinge 2018-06-29 02:38:19 +02:00
commit a5e8eba91c
139 changed files with 2861 additions and 1657 deletions

View File

@ -1,3 +1,5 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor")
set(TAGLIB_SOVERSION_CURRENT 17) set(TAGLIB_SOVERSION_CURRENT 17)
@ -45,6 +47,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/s3m ${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it ${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm ${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_SOURCE_DIR}/3rdparty
) )
if(ZLIB_FOUND) if(ZLIB_FOUND)
@ -53,10 +56,6 @@ elseif(HAVE_ZLIB_SOURCE)
include_directories(${ZLIB_SOURCE}) include_directories(${ZLIB_SOURCE})
endif() endif()
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
include_directories(${Boost_INCLUDE_DIR})
endif()
set(tag_HDRS set(tag_HDRS
tag.h tag.h
fileref.h fileref.h
@ -333,12 +332,6 @@ set(toolkit_SRCS
toolkit/tzlib.cpp toolkit/tzlib.cpp
) )
if(NOT WIN32)
set(unicode_SRCS
toolkit/unicode.cpp
)
endif()
if(HAVE_ZLIB_SOURCE) if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c ${ZLIB_SOURCE}/adler32.c
@ -355,7 +348,7 @@ set(tag_LIB_SRCS
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_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} ${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} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${unicode_SRCS} ${zlib_SRCS} ${zlib_SRCS}
tag.cpp tag.cpp
tagunion.cpp tagunion.cpp
fileref.cpp fileref.cpp
@ -365,18 +358,10 @@ set(tag_LIB_SRCS
add_library(tag STATIC ${tag_LIB_SRCS} ${tag_HDRS}) add_library(tag STATIC ${tag_LIB_SRCS} ${tag_HDRS})
if(ZLIB_FOUND) if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
target_link_libraries(tag ${ZLIB_LIBRARIES}) target_link_libraries(tag ${ZLIB_LIBRARIES})
endif() endif()
if(HAVE_BOOST_ATOMIC)
target_link_libraries(tag ${Boost_ATOMIC_LIBRARY})
endif()
if(HAVE_BOOST_ZLIB)
target_link_libraries(tag ${Boost_IOSTREAMS_LIBRARY} ${Boost_ZLIB_LIBRARY})
endif()
set_target_properties(tag PROPERTIES set_target_properties(tag PROPERTIES
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH} VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR} SOVERSION ${TAGLIB_SOVERSION_MAJOR}

View File

@ -83,6 +83,18 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool APE::File::isSupported(IOStream *stream)
{
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("MAC ") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -211,6 +211,15 @@ namespace TagLib {
*/ */
bool hasID3v1Tag() const; bool hasID3v1Tag() const;
/*!
* Returns whether or not the given \a stream can be opened as an APE
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -47,23 +47,24 @@ using namespace APE;
namespace namespace
{ {
bool isKeyValid(const char *key, size_t length) const unsigned int MinKeyLength = 2;
const unsigned int MaxKeyLength = 255;
bool isKeyValid(const ByteVector &key)
{ {
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 }; const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
if(length < 2 || length > 255)
return false;
// only allow printable ASCII including space (32..126) // only allow printable ASCII including space (32..126)
for(const char *p = key; p < key + length; ++p) { for(ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) {
const int c = static_cast<unsigned char>(*p); const int c = static_cast<unsigned char>(*it);
if(c < 32 || c > 126) if(c < 32 || c > 126)
return false; return false;
} }
const String upperKey = String(key).upper();
for(size_t i = 0; invalidKeys[i] != 0; ++i) { for(size_t i = 0; invalidKeys[i] != 0; ++i) {
if(Utils::equalsIgnoreCase(key, invalidKeys[i])) if(upperKey == invalidKeys[i])
return false; return false;
} }
@ -191,7 +192,7 @@ void APE::Tag::setGenre(const String &s)
void APE::Tag::setYear(unsigned int i) void APE::Tag::setYear(unsigned int i)
{ {
if(i <= 0) if(i == 0)
removeItem("YEAR"); removeItem("YEAR");
else else
addValue("YEAR", String::number(i), true); addValue("YEAR", String::number(i), true);
@ -199,7 +200,7 @@ void APE::Tag::setYear(unsigned int i)
void APE::Tag::setTrack(unsigned int i) void APE::Tag::setTrack(unsigned int i)
{ {
if(i <= 0) if(i == 0)
removeItem("TRACK"); removeItem("TRACK");
else else
addValue("TRACK", String::number(i), true); addValue("TRACK", String::number(i), true);
@ -296,11 +297,10 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
bool APE::Tag::checkKey(const String &key) bool APE::Tag::checkKey(const String &key)
{ {
if(!key.isLatin1()) if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
return false; return false;
const std::string data = key.to8Bit(false); return isKeyValid(key.data(String::UTF8));
return isKeyValid(data.c_str(), data.size());
} }
APE::Footer *APE::Tag::footer() const APE::Footer *APE::Tag::footer() const
@ -419,7 +419,10 @@ void APE::Tag::parse(const ByteVector &data)
const unsigned int keyLength = nullPos - pos - 8; const unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLegnth = data.toUInt(pos, false); const unsigned int valLegnth = data.toUInt(pos, false);
if(isKeyValid(&data[pos + 8], keyLength)){ if(keyLength >= MinKeyLength
&& keyLength <= MaxKeyLength
&& isKeyValid(data.mid(pos + 8, keyLength)))
{
APE::Item item; APE::Item item;
item.parse(data.mid(pos)); item.parse(data.mid(pos));

View File

@ -36,20 +36,16 @@ using namespace TagLib;
class ASF::Attribute::AttributePrivate : public RefCounter class ASF::Attribute::AttributePrivate : public RefCounter
{ {
public: public:
AttributePrivate() AttributePrivate() :
: pictureValue(ASF::Picture::fromInvalid()), pictureValue(ASF::Picture::fromInvalid()),
stream(0), numericValue(0),
language(0) {} stream(0),
language(0) {}
AttributeTypes type; AttributeTypes type;
String stringValue; String stringValue;
ByteVector byteVectorValue; ByteVector byteVectorValue;
ASF::Picture pictureValue; ASF::Picture pictureValue;
union { unsigned long long numericValue;
unsigned int intValue;
unsigned short shortValue;
unsigned long long longLongValue;
bool boolValue;
};
int stream; int stream;
int language; int language;
}; };
@ -95,28 +91,28 @@ ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate()) d(new AttributePrivate())
{ {
d->type = DWordType; d->type = DWordType;
d->intValue = value; d->numericValue = value;
} }
ASF::Attribute::Attribute(unsigned long long value) : ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate()) d(new AttributePrivate())
{ {
d->type = QWordType; d->type = QWordType;
d->longLongValue = value; d->numericValue = value;
} }
ASF::Attribute::Attribute(unsigned short value) : ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate()) d(new AttributePrivate())
{ {
d->type = WordType; d->type = WordType;
d->shortValue = value; d->numericValue = value;
} }
ASF::Attribute::Attribute(bool value) : ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate()) d(new AttributePrivate())
{ {
d->type = BoolType; d->type = BoolType;
d->boolValue = value; d->numericValue = value;
} }
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other) ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
@ -157,22 +153,22 @@ ByteVector ASF::Attribute::toByteVector() const
unsigned short ASF::Attribute::toBool() const unsigned short ASF::Attribute::toBool() const
{ {
return d->shortValue; return d->numericValue ? 1 : 0;
} }
unsigned short ASF::Attribute::toUShort() const unsigned short ASF::Attribute::toUShort() const
{ {
return d->shortValue; return static_cast<unsigned short>(d->numericValue);
} }
unsigned int ASF::Attribute::toUInt() const unsigned int ASF::Attribute::toUInt() const
{ {
return d->intValue; return static_cast<unsigned int>(d->numericValue);
} }
unsigned long long ASF::Attribute::toULongLong() const unsigned long long ASF::Attribute::toULongLong() const
{ {
return d->longLongValue; return static_cast<unsigned long long>(d->numericValue);
} }
ASF::Picture ASF::Attribute::toPicture() const ASF::Picture ASF::Attribute::toPicture() const
@ -212,24 +208,24 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
switch(d->type) { switch(d->type) {
case WordType: case WordType:
d->shortValue = readWORD(&f); d->numericValue = readWORD(&f);
break; break;
case BoolType: case BoolType:
if(kind == 0) { if(kind == 0) {
d->boolValue = (readDWORD(&f) == 1); d->numericValue = (readDWORD(&f) != 0);
} }
else { else {
d->boolValue = (readWORD(&f) == 1); d->numericValue = (readWORD(&f) != 0);
} }
break; break;
case DWordType: case DWordType:
d->intValue = readDWORD(&f); d->numericValue = readDWORD(&f);
break; break;
case QWordType: case QWordType:
d->longLongValue = readQWORD(&f); d->numericValue = readQWORD(&f);
break; break;
case UnicodeType: case UnicodeType:
@ -280,24 +276,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
switch (d->type) { switch (d->type) {
case WordType: case WordType:
data.append(ByteVector::fromShort(d->shortValue, false)); data.append(ByteVector::fromShort(toUShort(), false));
break; break;
case BoolType: case BoolType:
if(kind == 0) { if(kind == 0) {
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false)); data.append(ByteVector::fromUInt(toBool(), false));
} }
else { else {
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false)); data.append(ByteVector::fromShort(toBool(), false));
} }
break; break;
case DWordType: case DWordType:
data.append(ByteVector::fromUInt(d->intValue, false)); data.append(ByteVector::fromUInt(toUInt(), false));
break; break;
case QWordType: case QWordType:
data.append(ByteVector::fromLongLong(d->longLongValue, false)); data.append(ByteVector::fromLongLong(toULongLong(), false));
break; break;
case UnicodeType: case UnicodeType:

View File

@ -113,7 +113,7 @@ namespace TagLib
/*! /*!
* Copies the contents of \a other into this item. * Copies the contents of \a other into this item.
*/ */
ASF::Attribute &operator=(const Attribute &other); Attribute &operator=(const Attribute &other);
/*! /*!
* Exchanges the content of the Attribute by the content of \a other. * Exchanges the content of the Attribute by the content of \a other.

View File

@ -27,6 +27,7 @@
#include <tbytevectorlist.h> #include <tbytevectorlist.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tstring.h> #include <tstring.h>
#include <tagutils.h>
#include "asffile.h" #include "asffile.h"
#include "asftag.h" #include "asftag.h"
@ -258,7 +259,6 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
{ {
file->d->contentDescriptionObject = this;
const int titleLength = readWORD(file); const int titleLength = readWORD(file);
const int artistLength = readWORD(file); const int artistLength = readWORD(file);
const int copyrightLength = readWORD(file); const int copyrightLength = readWORD(file);
@ -299,7 +299,6 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/) void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
{ {
file->d->extendedContentDescriptionObject = this;
int count = readWORD(file); int count = readWORD(file);
while(count--) { while(count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
@ -323,7 +322,6 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/) void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
{ {
file->d->metadataObject = this;
int count = readWORD(file); int count = readWORD(file);
while(count--) { while(count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
@ -347,7 +345,6 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/) void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
{ {
file->d->metadataLibraryObject = this;
int count = readWORD(file); int count = readWORD(file);
while(count--) { while(count--) {
ASF::Attribute attribute; ASF::Attribute attribute;
@ -376,7 +373,6 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/) void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
{ {
file->d->headerExtensionObject = this;
file->seek(18, File::Current); file->seek(18, File::Current);
long long dataSize = readDWORD(file); long long dataSize = readDWORD(file);
long long dataPos = 0; long long dataPos = 0;
@ -394,10 +390,12 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
} }
BaseObject *obj; BaseObject *obj;
if(guid == metadataGuid) { if(guid == metadataGuid) {
obj = new MetadataObject(); file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
} }
else if(guid == metadataLibraryGuid) { else if(guid == metadataLibraryGuid) {
obj = new MetadataLibraryObject(); file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
} }
else { else {
obj = new UnknownObject(guid); obj = new UnknownObject(guid);
@ -473,6 +471,18 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
} }
} }
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool ASF::File::isSupported(IOStream *stream)
{
// An ASF file has to start with the designated GUID.
const ByteVector id = Utils::readHeader(stream, 16, false);
return (id == headerGuid);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -616,9 +626,8 @@ void ASF::File::read()
if(!isValid()) if(!isValid())
return; return;
ByteVector guid = readBlock(16); if(readBlock(16) != headerGuid) {
if(guid != headerGuid) { debug("ASF::File::read(): Not an ASF file.");
debug("ASF: Not an ASF file.");
setValid(false); setValid(false);
return; return;
} }
@ -639,8 +648,10 @@ void ASF::File::read()
} }
seek(2, Current); seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
for(int i = 0; i < numObjects; i++) { for(int i = 0; i < numObjects; i++) {
guid = readBlock(16); const ByteVector guid = readBlock(16);
if(guid.size() != 16) { if(guid.size() != 16) {
setValid(false); setValid(false);
break; break;
@ -652,19 +663,24 @@ void ASF::File::read()
} }
FilePrivate::BaseObject *obj; FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) { if(guid == filePropertiesGuid) {
obj = new FilePrivate::FilePropertiesObject(); filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
} }
else if(guid == streamPropertiesGuid) { else if(guid == streamPropertiesGuid) {
obj = new FilePrivate::StreamPropertiesObject(); streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
} }
else if(guid == contentDescriptionGuid) { else if(guid == contentDescriptionGuid) {
obj = new FilePrivate::ContentDescriptionObject(); d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
} }
else if(guid == extendedContentDescriptionGuid) { else if(guid == extendedContentDescriptionGuid) {
obj = new FilePrivate::ExtendedContentDescriptionObject(); d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
} }
else if(guid == headerExtensionGuid) { else if(guid == headerExtensionGuid) {
obj = new FilePrivate::HeaderExtensionObject(); d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
} }
else if(guid == codecListGuid) { else if(guid == codecListGuid) {
obj = new FilePrivate::CodecListObject(); obj = new FilePrivate::CodecListObject();
@ -680,4 +696,10 @@ void ASF::File::read()
obj->parse(this, size); obj->parse(this, size);
d->objects.append(obj); d->objects.append(obj);
} }
if(!filePropertiesObject || !streamPropertiesObject) {
debug("ASF::File::read(): Missing mandatory header objects.");
setValid(false);
return;
}
} }

View File

@ -115,6 +115,15 @@ namespace TagLib {
*/ */
virtual bool save(); virtual bool save();
/*!
* Returns whether or not the given \a stream can be opened as an ASF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
void read(); void read();

View File

@ -39,10 +39,10 @@ public:
AttributeListMap attributeListMap; AttributeListMap attributeListMap;
}; };
ASF::Tag::Tag() ASF::Tag::Tag() :
: TagLib::Tag() TagLib::Tag(),
d(new TagPrivate())
{ {
d = new TagPrivate;
} }
ASF::Tag::~Tag() ASF::Tag::~Tag()

View File

@ -43,6 +43,39 @@
using namespace TagLib; using namespace TagLib;
// This macro is a workaround for the fact that we can't add virtual functions.
// Should be true virtual functions in taglib2.
#define VIRTUAL_FUNCTION_WORKAROUND(function_name, default_value) \
if(dynamic_cast<const APE::Properties*>(this)) \
return dynamic_cast<const APE::Properties*>(this)->function_name(); \
else if(dynamic_cast<const ASF::Properties*>(this)) \
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const FLAC::Properties*>(this)) \
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MP4::Properties*>(this)) \
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPC::Properties*>(this)) \
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPEG::Properties*>(this)) \
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
else if(dynamic_cast<const TrueAudio::Properties*>(this)) \
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Vorbis::Properties*>(this)) \
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
else if(dynamic_cast<const WavPack::Properties*>(this)) \
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
else \
return (default_value);
class AudioProperties::AudioPropertiesPrivate class AudioProperties::AudioPropertiesPrivate
{ {
@ -59,98 +92,12 @@ AudioProperties::~AudioProperties()
int AudioProperties::lengthInSeconds() const int AudioProperties::lengthInSeconds() const
{ {
// This is an ugly workaround but we can't add a virtual function. VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
// Should be virtual in taglib2.
if(dynamic_cast<const APE::Properties*>(this))
return dynamic_cast<const APE::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const ASF::Properties*>(this))
return dynamic_cast<const ASF::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const FLAC::Properties*>(this))
return dynamic_cast<const FLAC::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MP4::Properties*>(this))
return dynamic_cast<const MP4::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MPC::Properties*>(this))
return dynamic_cast<const MPC::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MPEG::Properties*>(this))
return dynamic_cast<const MPEG::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const TrueAudio::Properties*>(this))
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInSeconds();
else if (dynamic_cast<const RIFF::AIFF::Properties*>(this))
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Vorbis::Properties*>(this))
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const WavPack::Properties*>(this))
return dynamic_cast<const WavPack::Properties*>(this)->lengthInSeconds();
else
return 0;
} }
int AudioProperties::lengthInMilliseconds() const int AudioProperties::lengthInMilliseconds() const
{ {
// This is an ugly workaround but we can't add a virtual function. VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
// Should be virtual in taglib2.
if(dynamic_cast<const APE::Properties*>(this))
return dynamic_cast<const APE::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const ASF::Properties*>(this))
return dynamic_cast<const ASF::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const FLAC::Properties*>(this))
return dynamic_cast<const FLAC::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MP4::Properties*>(this))
return dynamic_cast<const MP4::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MPC::Properties*>(this))
return dynamic_cast<const MPC::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MPEG::Properties*>(this))
return dynamic_cast<const MPEG::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const TrueAudio::Properties*>(this))
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this))
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Vorbis::Properties*>(this))
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const WavPack::Properties*>(this))
return dynamic_cast<const WavPack::Properties*>(this)->lengthInMilliseconds();
else
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -28,6 +28,7 @@
***************************************************************************/ ***************************************************************************/
#include <tfile.h> #include <tfile.h>
#include <tfilestream.h>
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <trefcounter.h> #include <trefcounter.h>
@ -59,49 +60,14 @@ namespace
typedef List<const FileRef::FileTypeResolver *> ResolverList; typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers; ResolverList fileTypeResolvers;
// Templatized internal functions. T should be String or IOStream*. // Detect the file type by user-defined resolvers.
template <typename T> File *detectByResolvers(FileName fileName, bool readAudioProperties,
FileName toFileName(T arg) AudioProperties::ReadStyle audioPropertiesStyle)
{
debug("FileRef::toFileName<T>(): This version should never be called.");
return FileName(L"");
}
template <>
FileName toFileName<IOStream *>(IOStream *arg)
{
return arg->name();
}
template <>
FileName toFileName<FileName>(FileName arg)
{
return arg;
}
template <typename T>
File *resolveFileType(T arg, bool readProperties,
AudioProperties::ReadStyle style)
{
debug("FileRef::resolveFileType<T>(): This version should never be called.");
return 0;
}
template <>
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
AudioProperties::ReadStyle style)
{
return 0;
}
template <>
File *resolveFileType<FileName>(FileName arg, bool readProperties,
AudioProperties::ReadStyle style)
{ {
ResolverList::ConstIterator it = fileTypeResolvers.begin(); ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) { for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(arg, readProperties, style); File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file) if(file)
return file; return file;
} }
@ -109,18 +75,15 @@ namespace
return 0; return 0;
} }
template <typename T> // Detect the file type based on the file extension.
File* createInternal(T arg, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
File* detectByExtension(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32 #ifdef _WIN32
const String s = toFileName(arg).toString(); const String s = stream->name().toString();
#else #else
const String s(toFileName(arg)); const String s(stream->name());
#endif #endif
String ext; String ext;
@ -135,49 +98,163 @@ namespace
if(ext.isEmpty()) if(ext.isEmpty())
return 0; return 0;
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
if(ext == "MP3") if(ext == "MP3")
return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG") if(ext == "OGG")
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle); return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "FLAC")
return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
return 0;
}
// Detect the file type based on the actual content of the stream.
File *detectByContent(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = 0;
if(MPEG::File::isSupported(stream))
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(Ogg::Vorbis::File::isSupported(stream))
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::FLAC::File::isSupported(stream))
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(FLAC::File::isSupported(stream))
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(MPC::File::isSupported(stream))
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(WavPack::File::isSupported(stream))
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Speex::File::isSupported(stream))
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Opus::File::isSupported(stream))
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
else if(TrueAudio::File::isSupported(stream))
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
else if(MP4::File::isSupported(stream))
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ASF::File::isSupported(stream))
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(RIFF::AIFF::File::isSupported(stream))
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(RIFF::WAV::File::isSupported(stream))
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
if(file) {
if(file->isValid())
return file;
else
delete file;
}
return 0;
}
// Internal function that supports FileRef::create().
// This looks redundant, but necessary in order not to change the previous
// behavior of FileRef::create().
File* createInternal(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
#ifdef _WIN32
const String s = fileName.toString();
#else
const String s(fileName);
#endif
String ext;
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
if(ext.isEmpty())
return 0;
if(ext == "MP3")
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") { if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */ /* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle); File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(file->isValid()) if(file->isValid())
return file; return file;
delete file; delete file;
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle); return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
} }
if(ext == "FLAC") if(ext == "FLAC")
return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle); return new FLAC::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC") if(ext == "MPC")
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle); return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV") if(ext == "WV")
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle); return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX") if(ext == "SPX")
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle); return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS") if(ext == "OPUS")
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle); return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA") if(ext == "TTA")
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle); return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V") if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle); return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF") if(ext == "WMA" || ext == "ASF")
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle); return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle); return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV") if(ext == "WAV")
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle); return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE") if(ext == "APE")
return new APE::File(arg, readAudioProperties, audioPropertiesStyle); return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions // module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW") if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(arg, readAudioProperties, audioPropertiesStyle); return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M") if(ext == "S3M")
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle); return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT") if(ext == "IT")
return new IT::File(arg, readAudioProperties, audioPropertiesStyle); return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM") if(ext == "XM")
return new XM::File(arg, readAudioProperties, audioPropertiesStyle); return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0; return 0;
} }
@ -186,15 +263,18 @@ namespace
class FileRef::FileRefPrivate : public RefCounter class FileRef::FileRefPrivate : public RefCounter
{ {
public: public:
FileRefPrivate(File *f) : FileRefPrivate() :
RefCounter(), RefCounter(),
file(f) {} file(0),
stream(0) {}
~FileRefPrivate() { ~FileRefPrivate() {
delete file; delete file;
delete stream;
} }
File *file; File *file;
IOStream *stream;
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -202,24 +282,27 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() : FileRef::FileRef() :
d(new FileRefPrivate(0)) d(new FileRefPrivate())
{ {
} }
FileRef::FileRef(FileName fileName, bool readAudioProperties, FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) : AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle))) d(new FileRefPrivate())
{ {
parse(fileName, readAudioProperties, audioPropertiesStyle);
} }
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) : FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle))) d(new FileRefPrivate())
{ {
parse(stream, readAudioProperties, audioPropertiesStyle);
} }
FileRef::FileRef(File *file) : FileRef::FileRef(File *file) :
d(new FileRefPrivate(file)) d(new FileRefPrivate())
{ {
d->file = file;
} }
FileRef::FileRef(const FileRef &ref) : FileRef::FileRef(const FileRef &ref) :
@ -341,3 +424,51 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
{ {
return createInternal(fileName, readAudioProperties, audioPropertiesStyle); return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
} }
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FileRef::parse(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// Try user-defined resolvers.
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->stream = new FileStream(fileName);
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content.
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Stream have to be closed here if failed to resolve file types.
delete d->stream;
d->stream = 0;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// User-defined resolvers won't work with a stream.
// Try to resolve file types based on the file extension.
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content of the file.
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}

View File

@ -274,8 +274,10 @@ namespace TagLib {
bool readAudioProperties = true, bool readAudioProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average); AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
private: private:
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
class FileRefPrivate; class FileRefPrivate;
FileRefPrivate *d; FileRefPrivate *d;
}; };

View File

@ -95,6 +95,18 @@ public:
bool scanned; bool scanned;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool FLAC::File::isSupported(IOStream *stream)
{
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("fLaC") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -168,8 +180,8 @@ bool FLAC::File::save()
} }
// Create new vorbis comments // Create new vorbis comments
if(!hasXiphComment())
Tag::duplicate(&d->tag, xiphComment(true), false); Tag::duplicate(&d->tag, xiphComment(true), false);
d->xiphCommentData = xiphComment()->render(false); d->xiphCommentData = xiphComment()->render(false);
@ -198,7 +210,6 @@ bool FLAC::File::save()
} }
// Compute the amount of padding, and append that to data. // Compute the amount of padding, and append that to data.
// TODO: Should be calculated in offset_t in taglib2.
long originalLength = d->streamStart - d->flacStart; long originalLength = d->streamStart - d->flacStart;
long paddingLength = originalLength - data.size() - 4; long paddingLength = originalLength - data.size() - 4;
@ -298,11 +309,7 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{ {
if(!create || d->tag[FlacID3v2Index]) return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
} }
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
@ -507,7 +514,9 @@ void FLAC::File::scan()
return; return;
} }
if(blockLength == 0 && blockType != MetadataBlock::Padding) { if(blockLength == 0
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
{
debug("FLAC::File::scan() -- Zero-sized metadata block found"); debug("FLAC::File::scan() -- Zero-sized metadata block found");
setValid(false); setValid(false);
return; return;

View File

@ -318,6 +318,15 @@ namespace TagLib {
*/ */
bool hasID3v2Tag() const; bool hasID3v2Tag() const;
/*!
* Returns whether or not the given \a stream can be opened as a FLAC
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -50,14 +50,14 @@ public:
ByteVector data; ByteVector data;
}; };
FLAC::Picture::Picture() FLAC::Picture::Picture() :
d(new PicturePrivate())
{ {
d = new PicturePrivate;
} }
FLAC::Picture::Picture(const ByteVector &data) FLAC::Picture::Picture(const ByteVector &data) :
d(new PicturePrivate())
{ {
d = new PicturePrivate;
parse(data); parse(data);
} }

View File

@ -39,11 +39,10 @@ public:
ByteVector data; ByteVector data;
}; };
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
d(new UnknownMetadataBlockPrivate())
{ {
d = new UnknownMetadataBlockPrivate;
d->code = code; d->code = code;
//debug(String(data.toHex()));
d->data = data; d->data = data;
} }

View File

@ -70,7 +70,7 @@ public:
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle), AudioProperties(propertiesStyle),
d(new PropertiesPrivate) d(new PropertiesPrivate())
{ {
} }

View File

@ -46,7 +46,7 @@ public:
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle), AudioProperties(propertiesStyle),
d(new PropertiesPrivate) d(new PropertiesPrivate())
{ {
} }

View File

@ -43,9 +43,10 @@ public:
String trackerName; String trackerName;
}; };
Mod::Tag::Tag() : TagLib::Tag() Mod::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{ {
d = new TagPrivate;
} }
Mod::Tag::~Tag() Mod::Tag::~Tag()

View File

@ -54,24 +54,25 @@ MP4::Atom::Atom(File *file)
length = header.toUInt(); length = header.toUInt();
if(length == 1) { if(length == 0) {
// The last atom which extends to the end of the file.
length = file->length() - offset;
}
else if(length == 1) {
// The atom has a 64-bit length.
const long long longLength = file->readBlock(8).toLongLong(); const long long longLength = file->readBlock(8).toLongLong();
if(sizeof(long) == sizeof(long long)) { if(longLength <= LONG_MAX) {
// The actual length fits in long. That's always the case if long is 64-bit.
length = static_cast<long>(longLength); length = static_cast<long>(longLength);
} }
else { else {
if(longLength <= LONG_MAX) { debug("MP4: 64-bit atoms are not supported");
// The atom has a 64-bit length, but it's actually a 31-bit value length = 0;
length = static_cast<long>(longLength); file->seek(0, File::End);
} return;
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
} }
} }
if(length < 8) { if(length < 8) {
debug("MP4: Invalid atom size"); debug("MP4: Invalid atom size");
length = 0; length = 0;

View File

@ -26,6 +26,8 @@
#include <tdebug.h> #include <tdebug.h>
#include <tstring.h> #include <tstring.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include "mp4atom.h" #include "mp4atom.h"
#include "mp4tag.h" #include "mp4tag.h"
#include "mp4file.h" #include "mp4file.h"
@ -69,6 +71,22 @@ public:
MP4::Properties *properties; MP4::Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool MP4::File::isSupported(IOStream *stream)
{
// An MP4 file has to have an "ftyp" box first.
const ByteVector id = Utils::readHeader(stream, 8, false);
return id.containsAt("ftyp", 4);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
TagLib::File(file), TagLib::File(file),
d(new FilePrivate()) d(new FilePrivate())

View File

@ -120,6 +120,15 @@ namespace TagLib {
*/ */
bool hasMP4Tag() const; bool hasMP4Tag() const;
/*!
* Returns whether or not the given \a stream can be opened as an ASF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
void read(bool readProperties); void read(bool readProperties);

View File

@ -71,14 +71,15 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
parseIntPair(atom); parseIntPair(atom);
} }
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" || else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
atom->name == "hdvd") { atom->name == "hdvd" || atom->name == "shwm") {
parseBool(atom); parseBool(atom);
} }
else if(atom->name == "tmpo") { else if(atom->name == "tmpo" || atom->name == "rate" || atom->name == "\251mvi" || atom->name == "\251mvc") {
parseInt(atom); parseInt(atom);
} }
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" || else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") { atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" ||
atom->name == "cmID") {
parseUInt(atom); parseUInt(atom);
} }
else if(atom->name == "plID") { else if(atom->name == "plID") {
@ -93,6 +94,9 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
else if(atom->name == "covr") { else if(atom->name == "covr") {
parseCovr(atom); parseCovr(atom);
} }
else if(atom->name == "purl" || atom->name == "egid") {
parseText(atom, -1);
}
else { else {
parseText(atom); parseText(atom);
} }
@ -472,14 +476,16 @@ MP4::Tag::save()
else if(name == "disk") { else if(name == "disk") {
data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second)); data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second));
} }
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") { else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd" ||
name == "shwm") {
data.append(renderBool(name.data(String::Latin1), it->second)); data.append(renderBool(name.data(String::Latin1), it->second));
} }
else if(name == "tmpo") { else if(name == "tmpo" || name == "rate" || name == "\251mvi" || name == "\251mvc") {
data.append(renderInt(name.data(String::Latin1), it->second)); data.append(renderInt(name.data(String::Latin1), it->second));
} }
else if(name == "tvsn" || name == "tves" || name == "cnID" || else if(name == "tvsn" || name == "tves" || name == "cnID" ||
name == "sfID" || name == "atID" || name == "geID") { name == "sfID" || name == "atID" || name == "geID" ||
name == "cmID") {
data.append(renderUInt(name.data(String::Latin1), it->second)); data.append(renderUInt(name.data(String::Latin1), it->second));
} }
else if(name == "plID") { else if(name == "plID") {
@ -491,6 +497,9 @@ MP4::Tag::save()
else if(name == "covr") { else if(name == "covr") {
data.append(renderCovr(name.data(String::Latin1), it->second)); data.append(renderCovr(name.data(String::Latin1), it->second));
} }
else if(name == "purl" || name == "egid") {
data.append(renderText(name.data(String::Latin1), it->second, TypeImplicit));
}
else if(name.size() == 4){ else if(name.size() == 4){
data.append(renderText(name.data(String::Latin1), it->second)); data.append(renderText(name.data(String::Latin1), it->second));
} }
@ -844,6 +853,11 @@ namespace
{ "sonm", "TITLESORT" }, { "sonm", "TITLESORT" },
{ "soco", "COMPOSERSORT" }, { "soco", "COMPOSERSORT" },
{ "sosn", "SHOWSORT" }, { "sosn", "SHOWSORT" },
{ "shwm", "SHOWWORKMOVEMENT" },
{ "\251wrk", "WORK" },
{ "\251mvn", "MOVEMENTNAME" },
{ "\251mvi", "MOVEMENTNUMBER" },
{ "\251mvc", "MOVEMENTCOUNT" },
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" }, { "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, { "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
@ -897,10 +911,10 @@ PropertyMap MP4::Tag::properties() const
} }
props[key] = value; props[key] = value;
} }
else if(key == "BPM") { else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") {
props[key] = String::number(it->second.toInt()); props[key] = String::number(it->second.toInt());
} }
else if(key == "COMPILATION") { else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT") {
props[key] = String::number(it->second.toBool()); props[key] = String::number(it->second.toBool());
} }
else { else {
@ -942,21 +956,21 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
if(reverseKeyMap.contains(it->first)) { if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first]; String name = reverseKeyMap[it->first];
if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) { if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) {
int first = 0, second = 0;
StringList parts = StringList::split(it->second.front(), "/"); StringList parts = StringList::split(it->second.front(), "/");
if(!parts.isEmpty()) { if(!parts.isEmpty()) {
first = parts[0].toInt(); int first = parts[0].toInt();
int second = 0;
if(parts.size() > 1) { if(parts.size() > 1) {
second = parts[1].toInt(); second = parts[1].toInt();
} }
d->items[name] = MP4::Item(first, second); d->items[name] = MP4::Item(first, second);
} }
} }
else if(it->first == "BPM" && !it->second.isEmpty()) { else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) {
int value = it->second.front().toInt(); int value = it->second.front().toInt();
d->items[name] = MP4::Item(value); d->items[name] = MP4::Item(value);
} }
else if(it->first == "COMPILATION" && !it->second.isEmpty()) { else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) {
bool value = (it->second.front().toInt() != 0); bool value = (it->second.front().toInt() != 0);
d->items[name] = MP4::Item(value); d->items[name] = MP4::Item(value);
} }

View File

@ -75,6 +75,19 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool MPC::File::isSupported(IOStream *stream)
{
// A newer MPC file has to start with "MPCK" or "MP+", but older files don't
// have keys to do a quick check.
const ByteVector id = Utils::readHeader(stream, 4, false);
return (id == "MPCK" || id.startsWith("MP+"));
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -214,6 +214,15 @@ namespace TagLib {
*/ */
bool hasAPETag() const; bool hasAPETag() const;
/*!
* Returns whether or not the given \a stream can be opened as an MPC
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -48,14 +48,16 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC") AttachedPictureFrame::AttachedPictureFrame() :
Frame("APIC"),
d(new AttachedPictureFramePrivate())
{ {
d = new AttachedPictureFramePrivate;
} }
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data) AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) :
Frame(data),
d(new AttachedPictureFramePrivate())
{ {
d = new AttachedPictureFramePrivate;
setData(data); setData(data);
} }
@ -169,9 +171,10 @@ ByteVector AttachedPictureFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h) AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new AttachedPictureFramePrivate())
{ {
d = new AttachedPictureFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -37,7 +37,11 @@ class ChapterFrame::ChapterFramePrivate
{ {
public: public:
ChapterFramePrivate() : ChapterFramePrivate() :
tagHeader(0) tagHeader(0),
startTime(0),
endTime(0),
startOffset(0),
endOffset(0)
{ {
embeddedFrameList.setAutoDelete(true); embeddedFrameList.setAutoDelete(true);
} }
@ -57,9 +61,9 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
ID3v2::Frame(data) ID3v2::Frame(data),
d(new ChapterFramePrivate())
{ {
d = new ChapterFramePrivate;
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
setData(data); setData(data);
} }
@ -68,10 +72,9 @@ ChapterFrame::ChapterFrame(const ByteVector &elementID,
unsigned int startTime, unsigned int endTime, unsigned int startTime, unsigned int endTime,
unsigned int startOffset, unsigned int endOffset, unsigned int startOffset, unsigned int endOffset,
const FrameList &embeddedFrames) : const FrameList &embeddedFrames) :
ID3v2::Frame("CHAP") ID3v2::Frame("CHAP"),
d(new ChapterFramePrivate())
{ {
d = new ChapterFramePrivate;
// setElementID has a workaround for a previously silly API where you had to // setElementID has a workaround for a previously silly API where you had to
// specifically include the null byte. // specifically include the null byte.
@ -198,7 +201,7 @@ String ChapterFrame::toString() const
s += ", start offset: " + String::number(d->startOffset); s += ", start offset: " + String::number(d->startOffset);
if(d->endOffset != 0xFFFFFFFF) if(d->endOffset != 0xFFFFFFFF)
s += ", start offset: " + String::number(d->endOffset); s += ", end offset: " + String::number(d->endOffset);
if(!d->embeddedFrameList.isEmpty()) { if(!d->embeddedFrameList.isEmpty()) {
StringList frameIDs; StringList frameIDs;
@ -298,9 +301,9 @@ ByteVector ChapterFrame::renderFields() const
} }
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) :
Frame(h) Frame(h),
d(new ChapterFramePrivate())
{ {
d = new ChapterFramePrivate;
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -48,15 +48,17 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM") CommentsFrame::CommentsFrame(String::Type encoding) :
Frame("COMM"),
d(new CommentsFramePrivate())
{ {
d = new CommentsFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data) CommentsFrame::CommentsFrame(const ByteVector &data) :
Frame(data),
d(new CommentsFramePrivate())
{ {
d = new CommentsFramePrivate;
setData(data); setData(data);
} }
@ -188,8 +190,9 @@ ByteVector CommentsFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h) CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new CommentsFramePrivate())
{ {
d = new CommentsFramePrivate();
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -46,15 +46,15 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame() : EventTimingCodesFrame::EventTimingCodesFrame() :
Frame("ETCO") Frame("ETCO"),
d(new EventTimingCodesFramePrivate())
{ {
d = new EventTimingCodesFramePrivate;
} }
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) :
Frame(data) Frame(data),
d(new EventTimingCodesFramePrivate())
{ {
d = new EventTimingCodesFramePrivate;
setData(data); setData(data);
} }
@ -136,9 +136,9 @@ ByteVector EventTimingCodesFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) :
: Frame(h) Frame(h),
d(new EventTimingCodesFramePrivate())
{ {
d = new EventTimingCodesFramePrivate();
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -50,14 +50,16 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB") GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() :
Frame("GEOB"),
d(new GeneralEncapsulatedObjectFramePrivate())
{ {
d = new GeneralEncapsulatedObjectFramePrivate;
} }
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data) GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) :
Frame(data),
d(new GeneralEncapsulatedObjectFramePrivate())
{ {
d = new GeneralEncapsulatedObjectFramePrivate;
setData(data); setData(data);
} }
@ -177,8 +179,9 @@ ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h) GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new GeneralEncapsulatedObjectFramePrivate())
{ {
d = new GeneralEncapsulatedObjectFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -45,15 +45,17 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE") OwnershipFrame::OwnershipFrame(String::Type encoding) :
Frame("OWNE"),
d(new OwnershipFramePrivate())
{ {
d = new OwnershipFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data) OwnershipFrame::OwnershipFrame(const ByteVector &data) :
Frame(data),
d(new OwnershipFramePrivate())
{ {
d = new OwnershipFramePrivate;
setData(data); setData(data);
} }
@ -161,8 +163,9 @@ ByteVector OwnershipFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h) OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new OwnershipFramePrivate())
{ {
d = new OwnershipFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -38,9 +38,10 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame() : Frame("PCST") PodcastFrame::PodcastFrame() :
Frame("PCST"),
d(new PodcastFramePrivate())
{ {
d = new PodcastFramePrivate;
d->fieldData = ByteVector(4, '\0'); d->fieldData = ByteVector(4, '\0');
} }
@ -72,8 +73,9 @@ ByteVector PodcastFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h) PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new PodcastFramePrivate())
{ {
d = new PodcastFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -43,14 +43,16 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PopularimeterFrame::PopularimeterFrame() : Frame("POPM") PopularimeterFrame::PopularimeterFrame() :
Frame("POPM"),
d(new PopularimeterFramePrivate())
{ {
d = new PopularimeterFramePrivate;
} }
PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data) PopularimeterFrame::PopularimeterFrame(const ByteVector &data) :
Frame(data),
d(new PopularimeterFramePrivate())
{ {
d = new PopularimeterFramePrivate;
setData(data); setData(data);
} }
@ -130,8 +132,9 @@ ByteVector PopularimeterFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h) PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new PopularimeterFramePrivate())
{ {
d = new PopularimeterFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -45,14 +45,16 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PrivateFrame::PrivateFrame() : Frame("PRIV") PrivateFrame::PrivateFrame() :
Frame("PRIV"),
d(new PrivateFramePrivate())
{ {
d = new PrivateFramePrivate;
} }
PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data) PrivateFrame::PrivateFrame(const ByteVector &data) :
Frame(data),
d(new PrivateFramePrivate())
{ {
d = new PrivateFramePrivate;
setData(data); setData(data);
} }
@ -121,8 +123,9 @@ ByteVector PrivateFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h) PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new PrivateFramePrivate())
{ {
d = new PrivateFramePrivate();
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -51,14 +51,16 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2") RelativeVolumeFrame::RelativeVolumeFrame() :
Frame("RVA2"),
d(new RelativeVolumeFramePrivate())
{ {
d = new RelativeVolumeFramePrivate;
} }
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data) RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) :
Frame(data),
d(new RelativeVolumeFramePrivate())
{ {
d = new RelativeVolumeFramePrivate;
setData(data); setData(data);
} }
@ -223,8 +225,9 @@ ByteVector RelativeVolumeFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h) RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new RelativeVolumeFramePrivate())
{ {
d = new RelativeVolumeFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -52,16 +52,16 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) : SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) :
Frame("SYLT") Frame("SYLT"),
d(new SynchronizedLyricsFramePrivate())
{ {
d = new SynchronizedLyricsFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) : SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) :
Frame(data) Frame(data),
d(new SynchronizedLyricsFramePrivate())
{ {
d = new SynchronizedLyricsFramePrivate;
setData(data); setData(data);
} }
@ -189,7 +189,7 @@ void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
} }
} }
String text = readStringField(data, enc, &pos); String text = readStringField(data, enc, &pos);
if(text.isEmpty() || pos + 4 > end) if(pos + 4 > end)
return; return;
unsigned int time = data.toUInt(pos, true); unsigned int time = data.toUInt(pos, true);
@ -234,9 +234,9 @@ ByteVector SynchronizedLyricsFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) :
: Frame(h) Frame(h),
d(new SynchronizedLyricsFramePrivate())
{ {
d = new SynchronizedLyricsFramePrivate();
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -36,7 +36,9 @@ class TableOfContentsFrame::TableOfContentsFramePrivate
{ {
public: public:
TableOfContentsFramePrivate() : TableOfContentsFramePrivate() :
tagHeader(0) tagHeader(0),
isTopLevel(false),
isOrdered(false)
{ {
embeddedFrameList.setAutoDelete(true); embeddedFrameList.setAutoDelete(true);
} }
@ -80,9 +82,9 @@ namespace {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
ID3v2::Frame(data) ID3v2::Frame(data),
d(new TableOfContentsFramePrivate())
{ {
d = new TableOfContentsFramePrivate;
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
setData(data); setData(data);
} }
@ -90,9 +92,9 @@ TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID,
const ByteVectorList &children, const ByteVectorList &children,
const FrameList &embeddedFrames) : const FrameList &embeddedFrames) :
ID3v2::Frame("CTOC") ID3v2::Frame("CTOC"),
d(new TableOfContentsFramePrivate())
{ {
d = new TableOfContentsFramePrivate;
d->elementID = elementID; d->elementID = elementID;
strip(d->elementID); strip(d->elementID);
d->childElements = children; d->childElements = children;
@ -272,9 +274,9 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
int pos = 0; int pos = 0;
unsigned int embPos = 0; unsigned int embPos = 0;
d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
d->isTopLevel = (data.at(pos) & 2) > 0; d->isTopLevel = (data.at(pos) & 2) != 0;
d->isOrdered = (data.at(pos++) & 1) > 0; d->isOrdered = (data.at(pos++) & 1) != 0;
unsigned int entryCount = data.at(pos++); unsigned int entryCount = static_cast<unsigned char>(data.at(pos++));
for(unsigned int i = 0; i < entryCount; i++) { for(unsigned int i = 0; i < entryCount; i++) {
ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
d->childElements.append(childElementID); d->childElements.append(childElementID);
@ -330,9 +332,9 @@ ByteVector TableOfContentsFrame::renderFields() const
TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader,
const ByteVector &data, Header *h) : const ByteVector &data, Header *h) :
Frame(h) Frame(h),
d(new TableOfContentsFramePrivate())
{ {
d = new TableOfContentsFramePrivate;
d->tagHeader = tagHeader; d->tagHeader = tagHeader;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -29,6 +29,8 @@
#include "id3v2tag.h" #include "id3v2tag.h"
#include "id3v2frame.h" #include "id3v2frame.h"
#include "tbytevectorlist.h"
namespace TagLib { namespace TagLib {
namespace ID3v2 { namespace ID3v2 {

View File

@ -45,16 +45,16 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) : TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) :
Frame(type) Frame(type),
d(new TextIdentificationFramePrivate())
{ {
d = new TextIdentificationFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
Frame(data) Frame(data),
d(new TextIdentificationFramePrivate())
{ {
d = new TextIdentificationFramePrivate;
setData(data); setData(data);
} }
@ -252,9 +252,10 @@ ByteVector TextIdentificationFrame::renderFields() const
// TextIdentificationFrame private members // TextIdentificationFrame private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h) TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new TextIdentificationFramePrivate())
{ {
d = new TextIdentificationFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -45,16 +45,16 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) : UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) :
ID3v2::Frame(data) ID3v2::Frame(data),
d(new UniqueFileIdentifierFramePrivate())
{ {
d = new UniqueFileIdentifierFramePrivate;
setData(data); setData(data);
} }
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) : UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) :
ID3v2::Frame("UFID") ID3v2::Frame("UFID"),
d(new UniqueFileIdentifierFramePrivate())
{ {
d = new UniqueFileIdentifierFramePrivate;
d->owner = owner; d->owner = owner;
d->identifier = id; d->identifier = id;
} }
@ -141,8 +141,8 @@ ByteVector UniqueFileIdentifierFrame::renderFields() const
} }
UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) : UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) :
Frame(h) Frame(h),
d(new UniqueFileIdentifierFramePrivate())
{ {
d = new UniqueFileIdentifierFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -38,9 +38,10 @@ public:
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data) UnknownFrame::UnknownFrame(const ByteVector &data) :
Frame(data),
d(new UnknownFramePrivate())
{ {
d = new UnknownFramePrivate;
setData(data); setData(data);
} }
@ -77,8 +78,9 @@ ByteVector UnknownFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h) UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new UnknownFramePrivate())
{ {
d = new UnknownFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -50,16 +50,16 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) : UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) :
Frame("USLT") Frame("USLT"),
d(new UnsynchronizedLyricsFramePrivate())
{ {
d = new UnsynchronizedLyricsFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) : UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) :
Frame(data) Frame(data),
d(new UnsynchronizedLyricsFramePrivate())
{ {
d = new UnsynchronizedLyricsFramePrivate;
setData(data); setData(data);
} }
@ -118,7 +118,7 @@ PropertyMap UnsynchronizedLyricsFrame::asProperties() const
{ {
PropertyMap map; PropertyMap map;
String key = description().upper(); String key = description().upper();
if(key.isEmpty() || key.upper() == "LYRICS") if(key.isEmpty() || key == "LYRICS")
map.insert("LYRICS", text()); map.insert("LYRICS", text());
else else
map.insert("LYRICS:" + key, text()); map.insert("LYRICS:" + key, text());
@ -190,9 +190,9 @@ ByteVector UnsynchronizedLyricsFrame::renderFields() const
// private members // private members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) :
: Frame(h) Frame(h),
d(new UnsynchronizedLyricsFramePrivate())
{ {
d = new UnsynchronizedLyricsFramePrivate();
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -49,10 +49,14 @@ public:
String description; String description;
}; };
////////////////////////////////////////////////////////////////////////////////
// UrlLinkFrame public members
////////////////////////////////////////////////////////////////////////////////
UrlLinkFrame::UrlLinkFrame(const ByteVector &data) : UrlLinkFrame::UrlLinkFrame(const ByteVector &data) :
Frame(data) Frame(data),
d(new UrlLinkFramePrivate())
{ {
d = new UrlLinkFramePrivate;
setData(data); setData(data);
} }
@ -93,6 +97,10 @@ PropertyMap UrlLinkFrame::asProperties() const
return map; return map;
} }
////////////////////////////////////////////////////////////////////////////////
// UrlLinkFrame protected members
////////////////////////////////////////////////////////////////////////////////
void UrlLinkFrame::parseFields(const ByteVector &data) void UrlLinkFrame::parseFields(const ByteVector &data)
{ {
d->url = String(data); d->url = String(data);
@ -103,24 +111,28 @@ ByteVector UrlLinkFrame::renderFields() const
return d->url.data(String::Latin1); return d->url.data(String::Latin1);
} }
UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h) UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) :
Frame(h),
d(new UrlLinkFramePrivate())
{ {
d = new UrlLinkFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }
////////////////////////////////////////////////////////////////////////////////
// UserUrlLinkFrame public members
////////////////////////////////////////////////////////////////////////////////
UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) : UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) :
UrlLinkFrame("WXXX") UrlLinkFrame("WXXX"),
d(new UserUrlLinkFramePrivate())
{ {
d = new UserUrlLinkFramePrivate;
d->textEncoding = encoding; d->textEncoding = encoding;
} }
UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) : UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) :
UrlLinkFrame(data) UrlLinkFrame(data),
d(new UserUrlLinkFramePrivate())
{ {
d = new UserUrlLinkFramePrivate;
setData(data); setData(data);
} }
@ -158,7 +170,7 @@ PropertyMap UserUrlLinkFrame::asProperties() const
{ {
PropertyMap map; PropertyMap map;
String key = description().upper(); String key = description().upper();
if(key.isEmpty() || key.upper() == "URL") if(key.isEmpty() || key == "URL")
map.insert("URL", url()); map.insert("URL", url());
else else
map.insert("URL:" + key, url()); map.insert("URL:" + key, url());
@ -176,6 +188,10 @@ UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &descript
return 0; return 0;
} }
////////////////////////////////////////////////////////////////////////////////
// UserUrlLinkFrame protected members
////////////////////////////////////////////////////////////////////////////////
void UserUrlLinkFrame::parseFields(const ByteVector &data) void UserUrlLinkFrame::parseFields(const ByteVector &data)
{ {
if(data.size() < 2) { if(data.size() < 2) {
@ -222,8 +238,9 @@ ByteVector UserUrlLinkFrame::renderFields() const
return v; return v;
} }
UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h) UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) :
UrlLinkFrame(data, h),
d(new UserUrlLinkFramePrivate())
{ {
d = new UserUrlLinkFramePrivate;
parseFields(fieldData(data)); parseFields(fieldData(data));
} }

View File

@ -41,9 +41,9 @@ public:
// public methods // public methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ExtendedHeader::ExtendedHeader() ExtendedHeader::ExtendedHeader() :
d(new ExtendedHeaderPrivate())
{ {
d = new ExtendedHeaderPrivate();
} }
ExtendedHeader::~ExtendedHeader() ExtendedHeader::~ExtendedHeader()

View File

@ -111,8 +111,8 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
// check if the key is contained in the key<=>frameID mapping // check if the key is contained in the key<=>frameID mapping
ByteVector frameID = keyToFrameID(key); ByteVector frameID = keyToFrameID(key);
if(!frameID.isEmpty()) { if(!frameID.isEmpty()) {
// Apple proprietary WFED (Podcast URL) is in fact a text frame. // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
if(frameID[0] == 'T' || frameID == "WFED"){ // text frame if(frameID[0] == 'T' || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN"){ // text frame
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8); TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
frame->setText(values); frame->setText(values);
return frame; return frame;
@ -198,15 +198,15 @@ ByteVector Frame::render() const
// protected members // protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Frame::Frame(const ByteVector &data) Frame::Frame(const ByteVector &data) :
d(new FramePrivate())
{ {
d = new FramePrivate;
d->header = new Header(data); d->header = new Header(data);
} }
Frame::Frame(Header *h) Frame::Frame(Header *h) :
d(new FramePrivate())
{ {
d = new FramePrivate;
d->header = h; d->header = h;
} }
@ -392,6 +392,8 @@ namespace
{ "TDES", "PODCASTDESC" }, { "TDES", "PODCASTDESC" },
{ "TGID", "PODCASTID" }, { "TGID", "PODCASTID" },
{ "WFED", "PODCASTURL" }, { "WFED", "PODCASTURL" },
{ "MVNM", "MOVEMENTNAME" },
{ "MVIN", "MOVEMENTNUMBER" },
}; };
const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]); const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]);
@ -474,8 +476,8 @@ PropertyMap Frame::asProperties() const
// workaround until this function is virtual // workaround until this function is virtual
if(id == "TXXX") if(id == "TXXX")
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties(); return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
// Apple proprietary WFED (Podcast URL) is in fact a text frame. // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
else if(id[0] == 'T' || id == "WFED") else if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN")
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties(); return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
else if(id == "WXXX") else if(id == "WXXX")
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties(); return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
@ -571,15 +573,15 @@ unsigned int Frame::Header::size(unsigned int version)
// public members (Frame::Header) // public members (Frame::Header)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Frame::Header::Header(const ByteVector &data, bool synchSafeInts) Frame::Header::Header(const ByteVector &data, bool synchSafeInts) :
d(new HeaderPrivate())
{ {
d = new HeaderPrivate;
setData(data, synchSafeInts); setData(data, synchSafeInts);
} }
Frame::Header::Header(const ByteVector &data, unsigned int version) Frame::Header::Header(const ByteVector &data, unsigned int version) :
d(new HeaderPrivate())
{ {
d = new HeaderPrivate;
setData(data, version); setData(data, version);
} }

View File

@ -198,8 +198,8 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
// Text Identification (frames 4.2) // Text Identification (frames 4.2)
// Apple proprietary WFED (Podcast URL) is in fact a text frame. // Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number) are in fact text frames.
if(frameID.startsWith("T") || frameID == "WFED") { if(frameID.startsWith("T") || frameID == "WFED" || frameID == "MVNM" || frameID == "MVIN") {
TextIdentificationFrame *f = frameID != "TXXX" TextIdentificationFrame *f = frameID != "TXXX"
? new TextIdentificationFrame(data, header) ? new TextIdentificationFrame(data, header)
@ -334,10 +334,11 @@ void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
tag->frameList("TDAT").size() == 1) tag->frameList("TDAT").size() == 1)
{ {
TextIdentificationFrame *tdrc = TextIdentificationFrame *tdrc =
static_cast<TextIdentificationFrame *>(tag->frameList("TDRC").front()); dynamic_cast<TextIdentificationFrame *>(tag->frameList("TDRC").front());
UnknownFrame *tdat = static_cast<UnknownFrame *>(tag->frameList("TDAT").front()); UnknownFrame *tdat = static_cast<UnknownFrame *>(tag->frameList("TDAT").front());
if(tdrc->fieldList().size() == 1 && if(tdrc &&
tdrc->fieldList().size() == 1 &&
tdrc->fieldList().front().size() == 4 && tdrc->fieldList().front().size() == 4 &&
tdat->data().size() >= 5) tdat->data().size() >= 5)
{ {
@ -456,6 +457,8 @@ namespace
{ "TDS", "TDES" }, { "TDS", "TDES" },
{ "TID", "TGID" }, { "TID", "TGID" },
{ "WFD", "WFED" }, { "WFD", "WFED" },
{ "MVN", "MVNM" },
{ "MVI", "MVIN" },
}; };
const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]); const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]);

View File

@ -60,6 +60,7 @@ class ID3v2::Tag::TagPrivate
{ {
public: public:
TagPrivate() : TagPrivate() :
factory(0),
file(0), file(0),
tagOffset(0), tagOffset(0),
extendedHeader(0), extendedHeader(0),
@ -286,7 +287,7 @@ void ID3v2::Tag::setGenre(const String &s)
void ID3v2::Tag::setYear(unsigned int i) void ID3v2::Tag::setYear(unsigned int i)
{ {
if(i <= 0) { if(i == 0) {
removeFrames("TDRC"); removeFrames("TDRC");
return; return;
} }
@ -295,7 +296,7 @@ void ID3v2::Tag::setYear(unsigned int i)
void ID3v2::Tag::setTrack(unsigned int i) void ID3v2::Tag::setTrack(unsigned int i)
{ {
if(i <= 0) { if(i == 0) {
removeFrames("TRCK"); removeFrames("TRCK");
return; return;
} }
@ -618,7 +619,6 @@ ByteVector ID3v2::Tag::render(int version) const
} }
// Compute the amount of padding, and append that to tagData. // Compute the amount of padding, and append that to tagData.
// TODO: Should be calculated in long long in taglib2.
long originalSize = d->header.tagSize(); long originalSize = d->header.tagSize();
long paddingSize = originalSize - (tagData.size() - Header::size()); long paddingSize = originalSize - (tagData.size() - Header::size());
@ -723,7 +723,7 @@ void ID3v2::Tag::parse(const ByteVector &origData)
if(d->header.extendedHeader()) { if(d->header.extendedHeader()) {
if(!d->extendedHeader) if(!d->extendedHeader)
d->extendedHeader = new ExtendedHeader; d->extendedHeader = new ExtendedHeader();
d->extendedHeader->setData(data); d->extendedHeader->setData(data);
if(d->extendedHeader->size() <= data.size()) { if(d->extendedHeader->size() <= data.size()) {
frameDataPosition += d->extendedHeader->size(); frameDataPosition += d->extendedHeader->size();

View File

@ -76,6 +76,58 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
namespace
{
// Dummy file class to make a stream work with MPEG::Header.
class AdapterFile : public TagLib::File
{
public:
AdapterFile(IOStream *stream) : File(stream) {}
Tag *tag() const { return 0; }
AudioProperties *audioProperties() const { return 0; }
bool save() { return false; }
};
}
bool MPEG::File::isSupported(IOStream *stream)
{
if(!stream || !stream->isOpen())
return false;
// An MPEG file has MPEG frame headers. An ID3v2 tag may precede.
// MPEG frame headers are really confusing with irrelevant binary data.
// So we check if a frame header is really valid.
long headerOffset;
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset);
if(buffer.isEmpty())
return false;
const long originalPosition = stream->tell();
AdapterFile file(stream);
for(unsigned int i = 0; i < buffer.size() - 1; ++i) {
if(isFrameSync(buffer, i)) {
const Header header(&file, headerOffset + i, true);
if(header.isValid()) {
stream->seek(originalPosition);
return true;
}
}
}
stream->seek(originalPosition);
return false;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -346,55 +398,50 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
long MPEG::File::nextFrameOffset(long position) long MPEG::File::nextFrameOffset(long position)
{ {
bool foundLastSyncPattern = false; ByteVector frameSyncBytes(2, '\0');
ByteVector buffer;
while(true) { while(true) {
seek(position); seek(position);
buffer = readBlock(bufferSize()); const ByteVector buffer = readBlock(bufferSize());
if(buffer.isEmpty())
if(buffer.size() <= 0)
return -1; return -1;
if(foundLastSyncPattern && secondSynchByte(buffer[0])) for(unsigned int i = 0; i < buffer.size(); ++i) {
return position - 1; frameSyncBytes[0] = frameSyncBytes[1];
frameSyncBytes[1] = buffer[i];
for(unsigned int i = 0; i < buffer.size() - 1; i++) { if(isFrameSync(frameSyncBytes)) {
if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) const Header header(this, position + i - 1, true);
return position + i; if(header.isValid())
return position + i - 1;
}
} }
foundLastSyncPattern = firstSyncByte(buffer[buffer.size() - 1]); position += bufferSize();
position += buffer.size();
} }
} }
long MPEG::File::previousFrameOffset(long position) long MPEG::File::previousFrameOffset(long position)
{ {
bool foundFirstSyncPattern = false; ByteVector frameSyncBytes(2, '\0');
ByteVector buffer;
while (position > 0) { while(position > 0) {
long size = std::min<long>(position, bufferSize()); const long bufferLength = std::min<long>(position, bufferSize());
position -= size; position -= bufferLength;
seek(position); seek(position);
buffer = readBlock(size); const ByteVector buffer = readBlock(bufferLength);
if(buffer.size() <= 0) for(int i = buffer.size() - 1; i >= 0; --i) {
break; frameSyncBytes[1] = frameSyncBytes[0];
frameSyncBytes[0] = buffer[i];
if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1])) if(isFrameSync(frameSyncBytes)) {
return position + buffer.size() - 1; const Header header(this, position + i, true);
if(header.isValid())
for(int i = buffer.size() - 2; i >= 0; i--) { return position + i + header.frameLength();
if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) }
return position + i;
} }
foundFirstSyncPattern = secondSynchByte(buffer[0]);
} }
return -1; return -1;
} }
@ -488,28 +535,41 @@ long MPEG::File::findID3v2()
const ByteVector headerID = ID3v2::Header::fileIdentifier(); const ByteVector headerID = ID3v2::Header::fileIdentifier();
seek(0); seek(0);
if(readBlock(headerID.size()) == headerID)
const ByteVector data = readBlock(headerID.size());
if(data.size() < headerID.size())
return -1;
if(data == headerID)
return 0; return 0;
if(firstSyncByte(data[0]) && secondSynchByte(data[1])) const Header firstHeader(this, 0, true);
if(firstHeader.isValid())
return -1; return -1;
// Look for the entire file, if neither an MEPG frame or ID3v2 tag was found // Look for an ID3v2 tag until reaching the first valid MPEG frame.
// at the beginning of the file.
// We don't care about the inefficiency of the code, since this is a seldom case.
const long tagOffset = find(headerID); ByteVector frameSyncBytes(2, '\0');
if(tagOffset < 0) ByteVector tagHeaderBytes(3, '\0');
return -1; long position = 0;
const long frameOffset = firstFrameOffset(); while(true) {
if(frameOffset < tagOffset) seek(position);
return -1; const ByteVector buffer = readBlock(bufferSize());
if(buffer.isEmpty())
return -1;
return tagOffset; for(unsigned int i = 0; i < buffer.size(); ++i) {
frameSyncBytes[0] = frameSyncBytes[1];
frameSyncBytes[1] = buffer[i];
if(isFrameSync(frameSyncBytes)) {
const Header header(this, position + i - 1, true);
if(header.isValid())
return -1;
}
tagHeaderBytes[0] = tagHeaderBytes[1];
tagHeaderBytes[1] = tagHeaderBytes[2];
tagHeaderBytes[2] = buffer[i];
if(tagHeaderBytes == headerID)
return position + i - 2;
}
position += bufferSize();
}
} }

View File

@ -370,6 +370,15 @@ namespace TagLib {
*/ */
bool hasAPETag() const; bool hasAPETag() const;
/*!
* Returns whether or not the given \a stream can be opened as an MPEG
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -182,7 +182,7 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
// Check for the MPEG synch bytes. // Check for the MPEG synch bytes.
if(!firstSyncByte(data[0]) || !secondSynchByte(data[1])) { if(!isFrameSync(data)) {
debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch."); debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch.");
return; return;
} }
@ -197,10 +197,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->version = Version2; d->version = Version2;
else if(versionBits == 3) else if(versionBits == 3)
d->version = Version1; d->version = Version1;
else { else
debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
return; return;
}
// Set the MPEG layer // Set the MPEG layer
@ -212,10 +210,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->layer = 2; d->layer = 2;
else if(layerBits == 3) else if(layerBits == 3)
d->layer = 1; d->layer = 1;
else { else
debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
return; return;
}
d->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0); d->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0);
@ -244,10 +240,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex]; d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex];
if(d->bitrate == 0) { if(d->bitrate == 0)
debug("MPEG::Header::parse() -- Invalid bit rate.");
return; return;
}
// Set the sample rate // Set the sample rate
@ -264,7 +258,6 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->sampleRate = sampleRates[d->version][samplerateIndex]; d->sampleRate = sampleRates[d->version][samplerateIndex];
if(d->sampleRate == 0) { if(d->sampleRate == 0) {
debug("MPEG::Header::parse() -- Invalid sample rate.");
return; return;
} }
@ -311,20 +304,16 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
file->seek(offset + d->frameLength); file->seek(offset + d->frameLength);
const ByteVector nextData = file->readBlock(4); const ByteVector nextData = file->readBlock(4);
if(nextData.size() < 4) { if(nextData.size() < 4)
debug("MPEG::Header::parse() -- Could not read the next frame header.");
return; return;
}
const unsigned int HeaderMask = 0xfffe0c00; const unsigned int HeaderMask = 0xfffe0c00;
const unsigned int header = data.toUInt(0, true) & HeaderMask; const unsigned int header = data.toUInt(0, true) & HeaderMask;
const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask; const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask;
if(header != nextHeader) { if(header != nextHeader)
debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
return; return;
}
} }
// Now that we're done parsing, set this to be a valid frame. // Now that we're done parsing, set this to be a valid frame.

View File

@ -157,23 +157,13 @@ void MPEG::Properties::read(File *file)
{ {
// Only the first valid frame is required if we have a VBR header. // Only the first valid frame is required if we have a VBR header.
long firstFrameOffset = file->firstFrameOffset(); const long firstFrameOffset = file->firstFrameOffset();
if(firstFrameOffset < 0) { if(firstFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
return; return;
} }
Header firstHeader(file, firstFrameOffset); const Header firstHeader(file, firstFrameOffset, false);
while(!firstHeader.isValid()) {
firstFrameOffset = file->nextFrameOffset(firstFrameOffset + 1);
if(firstFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
return;
}
firstHeader = Header(file, firstFrameOffset);
}
// Check for a VBR header that will help us in gathering information about a // Check for a VBR header that will help us in gathering information about a
// VBR stream. // VBR stream.
@ -207,24 +197,13 @@ void MPEG::Properties::read(File *file)
// Look for the last MPEG audio frame to calculate the stream length. // Look for the last MPEG audio frame to calculate the stream length.
long lastFrameOffset = file->lastFrameOffset(); const long lastFrameOffset = file->lastFrameOffset();
if(lastFrameOffset < 0) { if(lastFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
return; return;
} }
Header lastHeader(file, lastFrameOffset, false); const Header lastHeader(file, lastFrameOffset, false);
while(!lastHeader.isValid()) {
lastFrameOffset = file->previousFrameOffset(lastFrameOffset);
if(lastFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
return;
}
lastHeader = Header(file, lastFrameOffset, false);
}
const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
if(streamLength > 0) if(streamLength > 0)
d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5); d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5);

View File

@ -41,17 +41,17 @@ namespace TagLib
* MPEG frames can be recognized by the bit pattern 11111111 111, so the * MPEG frames can be recognized by the bit pattern 11111111 111, so the
* first byte is easy to check for, however checking to see if the second byte * first byte is easy to check for, however checking to see if the second byte
* starts with \e 111 is a bit more tricky, hence these functions. * starts with \e 111 is a bit more tricky, hence these functions.
*
* \note This does not check the length of the vector, since this is an
* internal utility function.
*/ */
inline bool firstSyncByte(unsigned char byte) inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0)
{ {
return (byte == 0xFF); // 0xFF in the second byte is possible in theory, but it's very unlikely.
}
inline bool secondSynchByte(unsigned char byte) const unsigned char b1 = bytes[offset + 0];
{ const unsigned char b2 = bytes[offset + 1];
// 0xFF is possible in theory, but it's very unlikely be a header. return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0);
return (byte != 0xFF && ((byte & 0xE0) == 0xE0));
} }
} }

View File

@ -27,6 +27,7 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include <xiphcomment.h> #include <xiphcomment.h>
#include "oggflacfile.h" #include "oggflacfile.h"
@ -65,22 +66,36 @@ public:
int commentPacket; int commentPacket;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool Ogg::FLAC::File::isSupported(IOStream *stream)
{
// An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
return (buffer.find("OggS") >= 0 && buffer.find("fLaC") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Ogg::FLAC::File::File(FileName file, bool readProperties, Ogg::FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file) Properties::ReadStyle propertiesStyle) :
Ogg::File(file),
d(new FilePrivate())
{ {
d = new FilePrivate;
if(isOpen()) if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
Ogg::FLAC::File::File(IOStream *stream, bool readProperties, Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream) Properties::ReadStyle propertiesStyle) :
Ogg::File(stream),
d(new FilePrivate())
{ {
d = new FilePrivate;
if(isOpen()) if(isOpen())
read(readProperties, propertiesStyle); read(readProperties, propertiesStyle);
} }
@ -172,7 +187,7 @@ void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle properties
if(d->hasXiphComment) if(d->hasXiphComment)
d->comment = new Ogg::XiphComment(xiphCommentData()); d->comment = new Ogg::XiphComment(xiphCommentData());
else else
d->comment = new Ogg::XiphComment; d->comment = new Ogg::XiphComment();
if(readProperties) if(readProperties)

View File

@ -127,9 +127,6 @@ namespace TagLib {
/*! /*!
* Save the file. This will primarily save and update the XiphComment. * Save the file. This will primarily save and update the XiphComment.
* Returns true if the save is successful. * Returns true if the save is successful.
*
* \warning In the current implementation, it's dangerous to call save()
* repeatedly. It leads to a segfault.
*/ */
virtual bool save(); virtual bool save();
@ -146,6 +143,14 @@ namespace TagLib {
*/ */
bool hasXiphComment() const; bool hasXiphComment() const;
/*!
* Check if the given \a stream can be opened as an Ogg FLAC file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -253,7 +253,7 @@ void Ogg::File::writePacket(unsigned int i, const ByteVector &packet)
ByteVectorList packets = firstPage->packets(); ByteVectorList packets = firstPage->packets();
packets[i - firstPage->firstPacketIndex()] = packet; packets[i - firstPage->firstPacketIndex()] = packet;
if(firstPage != lastPage && lastPage->packetCount() > 2) { if(firstPage != lastPage && lastPage->packetCount() > 1) {
ByteVectorList lastPagePackets = lastPage->packets(); ByteVectorList lastPagePackets = lastPage->packets();
lastPagePackets.erase(lastPagePackets.begin()); lastPagePackets.erase(lastPagePackets.begin());
packets.append(lastPagePackets); packets.append(lastPagePackets);

View File

@ -208,15 +208,15 @@ List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
static const unsigned int SplitSize = 32 * 255; static const unsigned int SplitSize = 32 * 255;
// Force repagination if the packets are too large for a page. // Force repagination if the segment table will exceed the size limit.
if(strategy != Repaginate) { if(strategy != Repaginate) {
size_t totalSize = packets.size(); size_t tableSize = 0;
for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
totalSize += it->size(); tableSize += it->size() / 255 + 1;
if(totalSize > 255 * 255) if(tableSize > 255)
strategy = Repaginate; strategy = Repaginate;
} }

View File

@ -30,6 +30,7 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include "opusfile.h" #include "opusfile.h"
@ -53,6 +54,18 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool Ogg::Opus::File::isSupported(IOStream *stream)
{
// An Opus file has IDs "OggS" and "OpusHead" somewhere.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
return (buffer.find("OggS") >= 0 && buffer.find("OpusHead") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -101,7 +114,7 @@ Opus::Properties *Opus::File::audioProperties() const
bool Opus::File::save() bool Opus::File::save()
{ {
if(!d->comment) if(!d->comment)
d->comment = new Ogg::XiphComment; d->comment = new Ogg::XiphComment();
setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false));

View File

@ -110,12 +110,18 @@ namespace TagLib {
* Save the file. * Save the file.
* *
* This returns true if the save was successful. * This returns true if the save was successful.
*
* \warning In the current implementation, it's dangerous to call save()
* repeatedly. It leads to a segfault.
*/ */
virtual bool save(); virtual bool save();
/*!
* Returns whether or not the given \a stream can be opened as an Opus
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -30,6 +30,7 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include "speexfile.h" #include "speexfile.h"
@ -53,6 +54,18 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool Ogg::Speex::File::isSupported(IOStream *stream)
{
// A Speex file has IDs "OggS" and "Speex " somewhere.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
return (buffer.find("OggS") >= 0 && buffer.find("Speex ") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -101,7 +114,7 @@ Speex::Properties *Speex::File::audioProperties() const
bool Speex::File::save() bool Speex::File::save()
{ {
if(!d->comment) if(!d->comment)
d->comment = new Ogg::XiphComment; d->comment = new Ogg::XiphComment();
setPacket(1, d->comment->render()); setPacket(1, d->comment->render());

View File

@ -110,12 +110,18 @@ namespace TagLib {
* Save the file. * Save the file.
* *
* This returns true if the save was successful. * This returns true if the save was successful.
*
* \warning In the current implementation, it's dangerous to call save()
* repeatedly. It leads to a segfault.
*/ */
virtual bool save(); virtual bool save();
/*!
* Returns whether or not the given \a stream can be opened as a Speex
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -28,10 +28,10 @@
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include "vorbisfile.h" #include "vorbisfile.h"
using namespace TagLib; using namespace TagLib;
class Vorbis::File::FilePrivate class Vorbis::File::FilePrivate
@ -59,6 +59,18 @@ namespace TagLib {
static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 }; static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
} }
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool Vorbis::File::isSupported(IOStream *stream)
{
// An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
return (buffer.find("OggS") >= 0 && buffer.find("\x01vorbis") >= 0);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -109,7 +121,7 @@ bool Vorbis::File::save()
ByteVector v(vorbisCommentHeaderID); ByteVector v(vorbisCommentHeaderID);
if(!d->comment) if(!d->comment)
d->comment = new Ogg::XiphComment; d->comment = new Ogg::XiphComment();
v.append(d->comment->render()); v.append(d->comment->render());
setPacket(1, v); setPacket(1, v);

View File

@ -118,12 +118,17 @@ namespace TagLib {
* Save the file. * Save the file.
* *
* This returns true if the save was successful. * This returns true if the save was successful.
*
* \warning In the current implementation, it's dangerous to call save()
* repeatedly. It leads to a segfault.
*/ */
virtual bool save(); virtual bool save();
/*!
* Check if the given \a stream can be opened as an Ogg Vorbis file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -261,10 +261,14 @@ bool Ogg::XiphComment::checkKey(const String &key)
{ {
if(key.size() < 1) if(key.size() < 1)
return false; return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126) // A key may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
for(String::ConstIterator it = key.begin(); it != key.end(); it++) {
if(*it < 0x20 || *it > 0x7D || *it == 0x3D)
return false; return false;
}
return true; return true;
} }
@ -275,11 +279,18 @@ String Ogg::XiphComment::vendorID() const
void Ogg::XiphComment::addField(const String &key, const String &value, bool replace) void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
{ {
if(!checkKey(key)) {
debug("Ogg::XiphComment::addField() - Invalid key. Field not added.");
return;
}
const String upperKey = key.upper();
if(replace) if(replace)
removeFields(key.upper()); removeFields(upperKey);
if(!key.isEmpty() && !value.isEmpty()) if(!key.isEmpty() && !value.isEmpty())
d->fieldListMap[key.upper()].append(value); d->fieldListMap[upperKey].append(value);
} }
void Ogg::XiphComment::removeField(const String &key, const String &value) void Ogg::XiphComment::removeField(const String &key, const String &value)
@ -436,85 +447,69 @@ void Ogg::XiphComment::parse(const ByteVector &data)
const unsigned int commentLength = data.toUInt(pos, false); const unsigned int commentLength = data.toUInt(pos, false);
pos += 4; pos += 4;
ByteVector entry = data.mid(pos, commentLength); const ByteVector entry = data.mid(pos, commentLength);
pos += commentLength; pos += commentLength;
// Don't go past data end // Don't go past data end
if(pos > data.size()) if(pos > data.size())
break; break;
// Handle Pictures separately
if(entry.startsWith("METADATA_BLOCK_PICTURE=")) {
// We need base64 encoded data including padding
if((entry.size() - 23) > 3 && ((entry.size() - 23) % 4) == 0) {
// Decode base64 picture data
ByteVector picturedata = ByteVector::fromBase64(entry.mid(23));
if(picturedata.size()) {
// Decode Flac Picture
FLAC::Picture * picture = new FLAC::Picture();
if(picture->parse(picturedata)) {
d->pictureList.append(picture);
// continue to next field
continue;
}
else {
delete picture;
debug("Failed to decode FlacPicture block");
}
}
else {
debug("Failed to decode base64 encoded data");
}
}
else {
debug("Invalid base64 encoded data");
}
}
// Handle old picture standard
if(entry.startsWith("COVERART=")) {
if((entry.size() - 9) > 3 && ((entry.size() - 9) % 4) == 0) {
// Decode base64 picture data
ByteVector picturedata = ByteVector::fromBase64(entry.mid(9));
if (picturedata.size()) {
// Assume it's some type of image file
FLAC::Picture * picture = new FLAC::Picture();
picture->setData(picturedata);
picture->setMimeType("image/");
picture->setType(FLAC::Picture::Other);
d->pictureList.append(picture);
// continue to next field
continue;
}
else {
debug("Failed to decode base64 encoded data");
}
}
else {
debug("Invalid base64 encoded data");
}
}
// Check for field separator // Check for field separator
int sep = entry.find('=');
const int sep = entry.find('=');
if(sep < 1) { if(sep < 1) {
debug("Discarding invalid comment field."); debug("Ogg::XiphComment::parse() - Discarding a field. Separator not found.");
continue; continue;
} }
// Parse key and value // Parse the key
String key = String(entry.mid(0, sep), String::UTF8);
String value = String(entry.mid(sep + 1), String::UTF8); const String key = String(entry.mid(0, sep), String::UTF8).upper();
addField(key, value, false); if(!checkKey(key)) {
debug("Ogg::XiphComment::parse() - Discarding a field. Invalid key.");
continue;
}
if(key == "METADATA_BLOCK_PICTURE" || key == "COVERART") {
// Handle Pictures separately
const ByteVector picturedata = ByteVector::fromBase64(entry.mid(sep + 1));
if(picturedata.isEmpty()) {
debug("Ogg::XiphComment::parse() - Discarding a field. Invalid base64 data");
continue;
}
if(key[0] == L'M') {
// Decode FLAC Picture
FLAC::Picture * picture = new FLAC::Picture();
if(picture->parse(picturedata)) {
d->pictureList.append(picture);
}
else {
delete picture;
debug("Ogg::XiphComment::parse() - Failed to decode FLAC Picture block");
}
}
else {
// Assume it's some type of image file
FLAC::Picture * picture = new FLAC::Picture();
picture->setData(picturedata);
picture->setMimeType("image/");
picture->setType(FLAC::Picture::Other);
d->pictureList.append(picture);
}
}
else {
// Parse the text
addField(key, String(entry.mid(sep + 1), String::UTF8), false);
}
} }
} }

View File

@ -28,6 +28,7 @@
#include <id3v2tag.h> #include <id3v2tag.h>
#include <tstringlist.h> #include <tstringlist.h>
#include <tpropertymap.h> #include <tpropertymap.h>
#include <tagutils.h>
#include "aifffile.h" #include "aifffile.h"
@ -53,6 +54,18 @@ public:
bool hasID3v2; bool hasID3v2;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool RIFF::AIFF::File::isSupported(IOStream *stream)
{
// An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC".
const ByteVector id = Utils::readHeader(stream, 12, false);
return (id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8)));
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -126,6 +126,14 @@ namespace TagLib {
*/ */
bool hasID3v2Tag() const; bool hasID3v2Tag() const;
/*!
* Check if the given \a stream can be opened as an AIFF file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -95,13 +95,13 @@ unsigned int RIFF::File::riffSize() const
unsigned int RIFF::File::chunkCount() const unsigned int RIFF::File::chunkCount() const
{ {
return d->chunks.size(); return static_cast<unsigned int>(d->chunks.size());
} }
unsigned int RIFF::File::chunkDataSize(unsigned int i) const unsigned int RIFF::File::chunkDataSize(unsigned int i) const
{ {
if(i >= d->chunks.size()) { if(i >= d->chunks.size()) {
debug("RIFF::File::chunkPadding() - Index out of range. Returning 0."); debug("RIFF::File::chunkDataSize() - Index out of range. Returning 0.");
return 0; return 0;
} }
@ -111,7 +111,7 @@ unsigned int RIFF::File::chunkDataSize(unsigned int i) const
unsigned int RIFF::File::chunkOffset(unsigned int i) const unsigned int RIFF::File::chunkOffset(unsigned int i) const
{ {
if(i >= d->chunks.size()) { if(i >= d->chunks.size()) {
debug("RIFF::File::chunkPadding() - Index out of range. Returning 0."); debug("RIFF::File::chunkOffset() - Index out of range. Returning 0.");
return 0; return 0;
} }
@ -161,19 +161,19 @@ void RIFF::File::setChunkData(unsigned int i, const ByteVector &data)
std::vector<Chunk>::iterator it = d->chunks.begin(); std::vector<Chunk>::iterator it = d->chunks.begin();
std::advance(it, i); std::advance(it, i);
const int originalSize = it->size + it->padding; const long long originalSize = static_cast<long long>(it->size) + it->padding;
writeChunk(it->name, data, it->offset - 8, it->size + it->padding + 8); writeChunk(it->name, data, it->offset - 8, it->size + it->padding + 8);
it->size = data.size(); it->size = data.size();
it->padding = data.size() % 1; it->padding = data.size() % 2;
const int diff = it->size + it->padding - originalSize; const long long diff = static_cast<long long>(it->size) + it->padding - originalSize;
// Now update the internal offsets // Now update the internal offsets
for(++it; it != d->chunks.end(); ++it) for(++it; it != d->chunks.end(); ++it)
it->offset += diff; it->offset += static_cast<int>(diff);
// Update the global size. // Update the global size.
@ -187,7 +187,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate)
{ {
if(d->chunks.size() == 0) { if(d->chunks.empty()) {
debug("RIFF::File::setChunkData - No valid chunks found."); debug("RIFF::File::setChunkData - No valid chunks found.");
return; return;
} }
@ -269,7 +269,7 @@ void RIFF::File::removeChunk(unsigned int i)
void RIFF::File::removeChunk(const ByteVector &name) void RIFF::File::removeChunk(const ByteVector &name)
{ {
for(int i = d->chunks.size() - 1; i >= 0; --i) { for(int i = static_cast<int>(d->chunks.size()) - 1; i >= 0; --i) {
if(d->chunks[i].name == name) if(d->chunks[i].name == name)
removeChunk(i); removeChunk(i);
} }
@ -292,7 +292,6 @@ void RIFF::File::read()
d->size = readBlock(4).toUInt(bigEndian); d->size = readBlock(4).toUInt(bigEndian);
offset += 8; offset += 8;
seek(offset);
// + 8: chunk header at least, fix for additional junk bytes // + 8: chunk header at least, fix for additional junk bytes
while(offset + 8 <= length()) { while(offset + 8 <= length()) {
@ -307,28 +306,24 @@ void RIFF::File::read()
break; break;
} }
if(static_cast<long long>(tell()) + chunkSize > length()) { if(static_cast<long long>(offset) + 8 + chunkSize > length()) {
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)"); debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)");
setValid(false); setValid(false);
break; break;
} }
offset += 8;
Chunk chunk; Chunk chunk;
chunk.name = chunkName; chunk.name = chunkName;
chunk.size = chunkSize; chunk.size = chunkSize;
chunk.offset = offset; chunk.offset = offset + 8;
chunk.padding = 0;
offset += chunk.size; offset = chunk.offset + chunk.size;
seek(offset);
// Check padding // Check padding
chunk.padding = 0;
if(offset & 1) { if(offset & 1) {
seek(offset);
const ByteVector iByte = readBlock(1); const ByteVector iByte = readBlock(1);
if(iByte.size() == 1 && iByte[0] == '\0') { if(iByte.size() == 1 && iByte[0] == '\0') {
chunk.padding = 1; chunk.padding = 1;

View File

@ -23,10 +23,11 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#include "tbytevector.h" #include <tbytevector.h>
#include "tdebug.h" #include <tdebug.h>
#include "tstringlist.h" #include <tstringlist.h>
#include "tpropertymap.h" #include <tpropertymap.h>
#include <tagutils.h>
#include "wavfile.h" #include "wavfile.h"
#include "id3v2tag.h" #include "id3v2tag.h"
@ -60,6 +61,18 @@ public:
bool hasInfo; bool hasInfo;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool RIFF::WAV::File::isSupported(IOStream *stream)
{
// A WAV file has to start with "RIFF????WAVE".
const ByteVector id = Utils::readHeader(stream, 12, false);
return (id.startsWith("RIFF") && id.containsAt("WAVE", 8));
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -175,6 +175,15 @@ namespace TagLib {
*/ */
bool hasInfoTag() const; bool hasInfoTag() const;
/*!
* Returns whether or not the given \a stream can be opened as a WAV
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -64,7 +64,7 @@ public:
S3M::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : S3M::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle), AudioProperties(propertiesStyle),
d(new PropertiesPrivate) d(new PropertiesPrivate())
{ {
} }

View File

@ -1,6 +1,11 @@
/* taglib_config.h. Generated by cmake from taglib_config.h.cmake */ /* taglib_config.h. Generated by cmake from taglib_config.h.cmake */
#ifndef TAGLIB_TAGLIB_CONFIG_H
#define TAGLIB_TAGLIB_CONFIG_H
/* These values are no longer used. This file is present only for compatibility reasons. */ /* These values are no longer used. This file is present only for compatibility reasons. */
#define TAGLIB_WITH_ASF 1 #define TAGLIB_WITH_ASF 1
#define TAGLIB_WITH_MP4 1 #define TAGLIB_WITH_MP4 1
#endif

View File

@ -79,10 +79,9 @@ public:
std::vector<Tag *> tags; std::vector<Tag *> tags;
}; };
TagUnion::TagUnion(Tag *first, Tag *second, Tag *third) TagUnion::TagUnion(Tag *first, Tag *second, Tag *third) :
d(new TagUnionPrivate())
{ {
d = new TagUnionPrivate;
d->tags[0] = first; d->tags[0] = first;
d->tags[1] = second; d->tags[1] = second;
d->tags[2] = third; d->tags[2] = third;

View File

@ -77,3 +77,29 @@ long Utils::findAPE(File *file, long id3v1Location)
return -1; return -1;
} }
ByteVector TagLib::Utils::readHeader(IOStream *stream, unsigned int length,
bool skipID3v2, long *headerOffset)
{
if(!stream || !stream->isOpen())
return ByteVector();
const long originalPosition = stream->tell();
long bufferOffset = 0;
if(skipID3v2) {
stream->seek(0);
const ByteVector data = stream->readBlock(ID3v2::Header::size());
if(data.startsWith(ID3v2::Header::fileIdentifier()))
bufferOffset = ID3v2::Header(data).completeTagSize();
}
stream->seek(bufferOffset);
const ByteVector header = stream->readBlock(length);
stream->seek(originalPosition);
if(headerOffset)
*headerOffset = bufferOffset;
return header;
}

View File

@ -30,9 +30,12 @@
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header #ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
#include <tbytevector.h>
namespace TagLib { namespace TagLib {
class File; class File;
class IOStream;
namespace Utils { namespace Utils {
@ -41,6 +44,9 @@ namespace TagLib {
long findID3v2(File *file); long findID3v2(File *file);
long findAPE(File *file, long id3v1Location); long findAPE(File *file, long id3v1Location);
ByteVector readHeader(IOStream *stream, unsigned int length, bool skipID3v2,
long *headerOffset = 0);
} }
} }

View File

@ -30,7 +30,7 @@
#define TAGLIB_MAJOR_VERSION 1 #define TAGLIB_MAJOR_VERSION 1
#define TAGLIB_MINOR_VERSION 11 #define TAGLIB_MINOR_VERSION 11
#define TAGLIB_PATCH_VERSION 0 #define TAGLIB_PATCH_VERSION 1
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) || defined(__clang__) #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) || defined(__clang__)
#define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") #define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
@ -115,7 +115,7 @@ namespace TagLib {
* *
* \section installing Installing TagLib * \section installing Installing TagLib
* *
* Please see the <a href="http://developer.kde.org/~wheeler/taglib.html">TagLib website</a> for the latest * Please see the <a href="http://taglib.org/">TagLib website</a> for the latest
* downloads. * downloads.
* *
* TagLib can be built using the CMake build system. TagLib installs a taglib-config and pkg-config file to * TagLib can be built using the CMake build system. TagLib installs a taglib-config and pkg-config file to
@ -160,11 +160,10 @@ namespace TagLib {
* *
* Questions about TagLib should be directed to the TagLib mailing list, not directly to the author. * Questions about TagLib should be directed to the TagLib mailing list, not directly to the author.
* *
* - <a href="http://developer.kde.org/~wheeler/taglib/">TagLib Homepage</a> * - <a href="http://taglib.org/">TagLib Homepage</a>
* - <a href="https://mail.kde.org/mailman/listinfo/taglib-devel">TagLib Mailing List (taglib-devel@kde.org)</a> * - <a href="https://mail.kde.org/mailman/listinfo/taglib-devel">TagLib Mailing List (taglib-devel@kde.org)</a>
* *
* \author Scott Wheeler <wheeler@kde.org> et al. * \author <a href="https://github.com/taglib/taglib/blob/master/AUTHORS">TagLib authors</a>.
*
*/ */
#endif #endif

View File

@ -29,7 +29,6 @@
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <cstddef>
#include <tstring.h> #include <tstring.h>
#include <tdebug.h> #include <tdebug.h>
@ -63,7 +62,7 @@ int findChar(
for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) { for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) {
if(*it == c) if(*it == c)
return (it - dataBegin); return static_cast<int>(it - dataBegin);
} }
return -1; return -1;
@ -105,7 +104,7 @@ int findVector(
++itPattern; ++itPattern;
if(itPattern == patternEnd) if(itPattern == patternEnd)
return (it - dataBegin); return static_cast<int>(it - dataBegin);
} }
} }
@ -125,7 +124,7 @@ T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignifica
T sum = 0; T sum = 0;
for(size_t i = 0; i < length; i++) { for(size_t i = 0; i < length; i++) {
const size_t shift = (mostSignificantByteFirst ? length - 1 - i : i) * 8; const size_t shift = (mostSignificantByteFirst ? length - 1 - i : i) * 8;
sum |= static_cast<T>(static_cast<unsigned char>(v[offset + i])) << shift; sum |= static_cast<T>(static_cast<unsigned char>(v[static_cast<int>(offset + i)])) << shift;
} }
return sum; return sum;
@ -176,7 +175,7 @@ TFloat toFloat(const ByteVector &v, size_t offset)
} tmp; } tmp;
::memcpy(&tmp, v.data() + offset, sizeof(TInt)); ::memcpy(&tmp, v.data() + offset, sizeof(TInt));
if(ENDIAN != Utils::floatByteOrder()) if(ENDIAN != Utils::systemByteOrder())
tmp.i = Utils::byteSwap(tmp.i); tmp.i = Utils::byteSwap(tmp.i);
return tmp.f; return tmp.f;
@ -191,7 +190,7 @@ ByteVector fromFloat(TFloat value)
} tmp; } tmp;
tmp.f = value; tmp.f = value;
if(ENDIAN != Utils::floatByteOrder()) if(ENDIAN != Utils::systemByteOrder())
tmp.i = Utils::byteSwap(tmp.i); tmp.i = Utils::byteSwap(tmp.i);
return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt)); return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt));
@ -300,7 +299,7 @@ ByteVector ByteVector::null;
ByteVector ByteVector::fromCString(const char *s, unsigned int length) ByteVector ByteVector::fromCString(const char *s, unsigned int length)
{ {
if(length == 0xffffffff) if(length == 0xffffffff)
return ByteVector(s, ::strlen(s)); return ByteVector(s, static_cast<unsigned int>(::strlen(s)));
else else
return ByteVector(s, length); return ByteVector(s, length);
} }
@ -375,7 +374,7 @@ ByteVector::ByteVector(const char *data, unsigned int length) :
} }
ByteVector::ByteVector(const char *data) : ByteVector::ByteVector(const char *data) :
d(new ByteVectorPrivate(data, ::strlen(data))) d(new ByteVectorPrivate(data, static_cast<unsigned int>(::strlen(data))))
{ {
} }
@ -485,46 +484,60 @@ ByteVector &ByteVector::replace(char oldByte, char newByte)
ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with) ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &with)
{ {
// TODO: This takes O(n!) time in the worst case. Rewrite it to run in O(n) time.
if(pattern.size() == 0 || pattern.size() > size())
return *this;
if(pattern.size() == 1 && with.size() == 1) if(pattern.size() == 1 && with.size() == 1)
return replace(pattern[0], with[0]); return replace(pattern[0], with[0]);
const size_t withSize = with.size(); // Check if there is at least one occurrence of the pattern.
const size_t patternSize = pattern.size();
const ptrdiff_t diff = withSize - patternSize;
size_t offset = 0; int offset = find(pattern, 0);
while (true) { if(offset == -1)
offset = find(pattern, offset); return *this;
if(offset == static_cast<size_t>(-1)) // Use npos in taglib2.
break; if(pattern.size() == with.size()) {
// We think this case might be common enough to optimize it.
detach(); detach();
do
{
::memcpy(data() + offset, with.data(), with.size());
offset = find(pattern, offset + pattern.size());
} while(offset != -1);
}
else {
if(diff < 0) { // Loop once to calculate the result size.
::memmove(
data() + offset + withSize, unsigned int dstSize = size();
data() + offset + patternSize, do
size() - offset - patternSize); {
resize(size() + diff); dstSize += with.size() - pattern.size();
} offset = find(pattern, offset + pattern.size());
else if(diff > 0) { } while(offset != -1);
resize(size() + diff);
::memmove( // Loop again to copy modified data to the new vector.
data() + offset + withSize,
data() + offset + patternSize, ByteVector dst(dstSize);
size() - diff - offset - patternSize); int dstOffset = 0;
offset = 0;
while(true) {
const int next = find(pattern, offset);
if(next == -1) {
::memcpy(dst.data() + dstOffset, data() + offset, size() - offset);
break;
}
::memcpy(dst.data() + dstOffset, data() + offset, next - offset);
dstOffset += next - offset;
::memcpy(dst.data() + dstOffset, with.data(), with.size());
dstOffset += with.size();
offset = next + pattern.size();
} }
::memcpy(data() + offset, with.data(), with.size()); swap(dst);
offset += withSize;
if(offset > size() - patternSize)
break;
} }
return *this; return *this;
@ -963,7 +976,7 @@ ByteVector ByteVector::fromBase64(const ByteVector & input)
// Only return output if we processed all bytes // Only return output if we processed all bytes
if(len == 0) { if(len == 0) {
output.resize(dst - (unsigned char*) output.data()); output.resize(static_cast<unsigned int>(dst - (unsigned char*) output.data()));
return output; return output;
} }
return ByteVector(); return ByteVector();

View File

@ -53,9 +53,9 @@ ByteVectorStream::ByteVectorStreamPrivate::ByteVectorStreamPrivate(const ByteVec
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ByteVectorStream::ByteVectorStream(const ByteVector &data) ByteVectorStream::ByteVectorStream(const ByteVector &data) :
d(new ByteVectorStreamPrivate(data))
{ {
d = new ByteVectorStreamPrivate(data);
} }
ByteVectorStream::~ByteVectorStream() ByteVectorStream::~ByteVectorStream()
@ -137,7 +137,7 @@ void ByteVectorStream::seek(long offset, Position p)
d->position += offset; d->position += offset;
break; break;
case End: case End:
d->position = length() - offset; d->position = length() + offset; // offset is expected to be negative
break; break;
} }
} }

View File

@ -27,6 +27,8 @@
#include <config.h> #include <config.h>
#endif #endif
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
#include "tdebug.h" #include "tdebug.h"
#include "tstring.h" #include "tstring.h"
#include "tdebuglistener.h" #include "tdebuglistener.h"
@ -43,27 +45,20 @@ namespace TagLib
void debug(const String &s) void debug(const String &s)
{ {
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
debugListener->printMessage("TagLib: " + s + "\n"); debugListener->printMessage("TagLib: " + s + "\n");
#endif
} }
void debugData(const ByteVector &v) void debugData(const ByteVector &v)
{ {
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) for(unsigned int i = 0; i < v.size(); ++i) {
const std::string bits = std::bitset<8>(v[i]).to_string();
for(size_t i = 0; i < v.size(); ++i) const String msg = Utils::formatString(
{ "*** [%u] - char '%c' - int %d, 0x%02x, 0b%s\n",
std::string bits = std::bitset<8>(v[i]).to_string();
String msg = Utils::formatString(
"*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n",
i, v[i], v[i], v[i], bits.c_str()); i, v[i], v[i], v[i], bits.c_str());
debugListener->printMessage(msg); debugListener->printMessage(msg);
} }
#endif
} }
} }
#endif

View File

@ -32,10 +32,11 @@ namespace TagLib {
class ByteVector; class ByteVector;
#ifndef DO_NOT_DOCUMENT #ifndef DO_NOT_DOCUMENT
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
/*! /*!
* A simple function that outputs the debug messages to the listener. * A simple function that outputs the debug messages to the listener.
* The default listener redirects the messages to \a stderr when NDEBUG is * The default listener redirects the messages to \a stderr when NDEBUG is
* not defined. * not defined.
* *
* \warning Do not use this outside of TagLib, it could lead to undefined * \warning Do not use this outside of TagLib, it could lead to undefined
@ -45,7 +46,7 @@ namespace TagLib {
* \internal * \internal
*/ */
void debug(const String &s); void debug(const String &s);
/*! /*!
* For debugging binary data. * For debugging binary data.
* *
@ -56,6 +57,13 @@ namespace TagLib {
* \internal * \internal
*/ */
void debugData(const ByteVector &v); void debugData(const ByteVector &v);
#else
#define debug(x) ((void)0)
#define debugData(x) ((void)0)
#endif
} }
#endif #endif

View File

@ -69,39 +69,38 @@ using namespace TagLib;
class File::FilePrivate class File::FilePrivate
{ {
public: public:
FilePrivate(IOStream *stream, bool owner); FilePrivate(IOStream *stream, bool owner) :
stream(stream),
streamOwner(owner),
valid(true) {}
~FilePrivate()
{
if(streamOwner)
delete stream;
}
IOStream *stream; IOStream *stream;
bool streamOwner; bool streamOwner;
bool valid; bool valid;
}; };
File::FilePrivate::FilePrivate(IOStream *stream, bool owner) :
stream(stream),
streamOwner(owner),
valid(true)
{
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
File::File(FileName fileName) File::File(FileName fileName) :
d(new FilePrivate(new FileStream(fileName), true))
{ {
IOStream *stream = new FileStream(fileName);
d = new FilePrivate(stream, true);
} }
File::File(IOStream *stream) File::File(IOStream *stream) :
d(new FilePrivate(stream, false))
{ {
d = new FilePrivate(stream, false);
} }
File::~File() File::~File()
{ {
if(d->stream && d->streamOwner)
delete d->stream;
delete d; delete d;
} }

View File

@ -86,7 +86,7 @@ namespace TagLib {
* format, the returned map's unsupportedData() list will contain one entry identifying * format, the returned map's unsupportedData() list will contain one entry identifying
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties() * that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
* to remove (a subset of) them. * to remove (a subset of) them.
* For files that contain more than one tag (e.g. an MP3 with both an ID3v2 and an ID3v2 * For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
* tag) only the most "modern" one will be exported (ID3v2 in this case). * tag) only the most "modern" one will be exported (ID3v2 in this case).
* BIC: Will be made virtual in future releases. * BIC: Will be made virtual in future releases.
*/ */

View File

@ -51,12 +51,11 @@ namespace
{ {
const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
if(!path.wstr().empty()) #if defined (PLATFORM_WINRT)
return CreateFileW(path.wstr().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); return CreateFile2(path.wstr().c_str(), access, FILE_SHARE_READ, OPEN_EXISTING, NULL);
else if(!path.str().empty()) #else
return CreateFileA(path.str().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); return CreateFileW(path.wstr().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
else #endif
return InvalidFileHandle;
} }
void closeFile(FileHandle file) void closeFile(FileHandle file)
@ -262,7 +261,7 @@ void FileStream::insert(const ByteVector &data, unsigned long start, unsigned lo
// to overwrite. Appropriately increment the readPosition. // to overwrite. Appropriately increment the readPosition.
seek(readPosition); seek(readPosition);
const size_t bytesRead = readFile(d->file, aboutToOverwrite); const unsigned int bytesRead = static_cast<unsigned int>(readFile(d->file, aboutToOverwrite));
aboutToOverwrite.resize(bytesRead); aboutToOverwrite.resize(bytesRead);
readPosition += bufferLength; readPosition += bufferLength;
@ -305,10 +304,10 @@ void FileStream::removeBlock(unsigned long start, unsigned long length)
ByteVector buffer(static_cast<unsigned int>(bufferLength)); ByteVector buffer(static_cast<unsigned int>(bufferLength));
for(size_t bytesRead = -1; bytesRead != 0;) for(unsigned int bytesRead = -1; bytesRead != 0;)
{ {
seek(readPosition); seek(readPosition);
bytesRead = readFile(d->file, buffer); bytesRead = static_cast<unsigned int>(readFile(d->file, buffer));
readPosition += bytesRead; readPosition += bytesRead;
// Check to see if we just read the last block. We need to call clear() // Check to see if we just read the last block. We need to call clear()
@ -347,28 +346,17 @@ void FileStream::seek(long offset, Position p)
#ifdef _WIN32 #ifdef _WIN32
DWORD whence; if(p != Beginning && p != Current && p != End) {
switch(p) {
case Beginning:
whence = FILE_BEGIN;
break;
case Current:
whence = FILE_CURRENT;
break;
case End:
whence = FILE_END;
break;
default:
debug("FileStream::seek() -- Invalid Position value."); debug("FileStream::seek() -- Invalid Position value.");
return; return;
} }
SetLastError(NO_ERROR); LARGE_INTEGER liOffset;
SetFilePointer(d->file, offset, NULL, whence); liOffset.QuadPart = offset;
const int lastError = GetLastError(); if(!SetFilePointerEx(d->file, liOffset, NULL, static_cast<DWORD>(p))) {
if(lastError != NO_ERROR && lastError != ERROR_NEGATIVE_SEEK)
debug("FileStream::seek() -- Failed to set the file pointer."); debug("FileStream::seek() -- Failed to set the file pointer.");
}
#else #else
@ -410,10 +398,11 @@ long FileStream::tell() const
{ {
#ifdef _WIN32 #ifdef _WIN32
SetLastError(NO_ERROR); const LARGE_INTEGER zero = {};
const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT); LARGE_INTEGER position;
if(GetLastError() == NO_ERROR) {
return static_cast<long>(position); if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) && position.QuadPart <= LONG_MAX) {
return static_cast<long>(position.QuadPart);
} }
else { else {
debug("FileStream::tell() -- Failed to get the file pointer."); debug("FileStream::tell() -- Failed to get the file pointer.");
@ -436,10 +425,10 @@ long FileStream::length()
#ifdef _WIN32 #ifdef _WIN32
SetLastError(NO_ERROR); LARGE_INTEGER fileSize;
const DWORD fileSize = GetFileSize(d->file, NULL);
if(GetLastError() == NO_ERROR) { if(GetFileSizeEx(d->file, &fileSize) && fileSize.QuadPart <= LONG_MAX) {
return static_cast<long>(fileSize); return static_cast<long>(fileSize.QuadPart);
} }
else { else {
debug("FileStream::length() -- Failed to get the file size."); debug("FileStream::length() -- Failed to get the file size.");
@ -472,9 +461,7 @@ void FileStream::truncate(long length)
seek(length); seek(length);
SetLastError(NO_ERROR); if(!SetEndOfFile(d->file)) {
SetEndOfFile(d->file);
if(GetLastError() != NO_ERROR) {
debug("FileStream::truncate() -- Failed to truncate the file."); debug("FileStream::truncate() -- Failed to truncate the file.");
} }

View File

@ -23,73 +23,49 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
#ifdef _WIN32
# include <windows.h>
# include <tstring.h>
#endif
#include "tiostream.h" #include "tiostream.h"
using namespace TagLib; using namespace TagLib;
#ifdef _WIN32 #ifdef _WIN32
# include "tstring.h"
# include "tdebug.h"
# include <windows.h>
namespace namespace
{ {
// Check if the running system has CreateFileW() function. std::wstring ansiToUnicode(const char *str)
// Windows9x systems don't have CreateFileW() or can't accept Unicode file names.
bool supportsUnicode()
{ {
#ifdef UNICODE const int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
return true;
#else
const FARPROC p = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW");
return (p != NULL);
#endif
}
// Indicates whether the system supports Unicode file names.
const bool SystemSupportsUnicode = supportsUnicode();
// Converts a UTF-16 string into a local encoding.
// This function should only be used in Windows9x systems which don't support
// Unicode file names.
std::string unicodeToAnsi(const wchar_t *wstr)
{
if(SystemSupportsUnicode) {
debug("unicodeToAnsi() - Should not be used on WinNT systems.");
}
const int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
if(len == 0) if(len == 0)
return std::string(); return std::wstring();
std::string str(len, '\0'); std::wstring wstr(len - 1, L'\0');
WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL); MultiByteToWideChar(CP_ACP, 0, str, -1, &wstr[0], len);
return str; return wstr;
} }
} }
// If WinNT, stores a Unicode string into m_wname directly. // m_name is no longer used, but kept for backward compatibility.
// If Win9x, converts and stores it into m_name to avoid calling Unicode version functions.
FileName::FileName(const wchar_t *name) FileName::FileName(const wchar_t *name) :
: m_name (SystemSupportsUnicode ? "" : unicodeToAnsi(name)) m_name(),
, m_wname(SystemSupportsUnicode ? name : L"") m_wname(name)
{ {
} }
FileName::FileName(const char *name) FileName::FileName(const char *name) :
: m_name(name) m_name(),
m_wname(ansiToUnicode(name))
{ {
} }
FileName::FileName(const FileName &name) FileName::FileName(const FileName &name) :
: m_name (name.m_name) m_name(),
, m_wname(name.m_wname) m_wname(name.m_wname)
{ {
} }
@ -115,25 +91,9 @@ const std::string &FileName::str() const
String FileName::toString() const String FileName::toString() const
{ {
if(!m_wname.empty()) { return String(m_wname.c_str());
return String(m_wname);
}
else if(!m_name.empty()) {
const int len = MultiByteToWideChar(CP_ACP, 0, m_name.c_str(), -1, NULL, 0);
if(len == 0)
return String();
std::vector<wchar_t> buf(len);
MultiByteToWideChar(CP_ACP, 0, m_name.c_str(), -1, &buf[0], len);
return String(&buf[0]);
}
else {
return String();
}
} }
#endif // _WIN32 #endif // _WIN32
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -229,6 +229,11 @@ namespace TagLib {
*/ */
List<T> &operator=(const List<T> &l); List<T> &operator=(const List<T> &l);
/*!
* Exchanges the content of this list by the content of \a l.
*/
void swap(List<T> &l);
/*! /*!
* Compares this list with \a l and returns true if all of the elements are * Compares this list with \a l and returns true if all of the elements are
* the same. * the same.

View File

@ -89,9 +89,9 @@ public:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template <class T> template <class T>
List<T>::List() List<T>::List() :
d(new ListPrivate<T>())
{ {
d = new ListPrivate<T>;
} }
template <class T> template <class T>
@ -196,7 +196,7 @@ List<T> &List<T>::clear()
template <class T> template <class T>
unsigned int List<T>::size() const unsigned int List<T>::size() const
{ {
return d->list.size(); return static_cast<unsigned int>(d->list.size());
} }
template <class T> template <class T>
@ -283,16 +283,18 @@ const T &List<T>::operator[](unsigned int i) const
template <class T> template <class T>
List<T> &List<T>::operator=(const List<T> &l) List<T> &List<T>::operator=(const List<T> &l)
{ {
if(&l == this) List<T>(l).swap(*this);
return *this;
if(d->deref())
delete d;
d = l.d;
d->ref();
return *this; return *this;
} }
template <class T>
void List<T>::swap(List<T> &l)
{
using std::swap;
swap(d, l.d);
}
template <class T> template <class T>
bool List<T>::operator==(const List<T> &l) const bool List<T>::operator==(const List<T> &l) const
{ {

View File

@ -174,6 +174,11 @@ namespace TagLib {
*/ */
Map<Key, T> &operator=(const Map<Key, T> &m); Map<Key, T> &operator=(const Map<Key, T> &m);
/*!
* Exchanges the content of this map by the content of \a m.
*/
void swap(Map<Key, T> &m);
protected: protected:
/* /*
* If this List is being shared via implicit sharing, do a deep copy of the * If this List is being shared via implicit sharing, do a deep copy of the

View File

@ -48,9 +48,9 @@ public:
}; };
template <class Key, class T> template <class Key, class T>
Map<Key, T>::Map() Map<Key, T>::Map() :
d(new MapPrivate<Key, T>())
{ {
d = new MapPrivate<Key, T>;
} }
template <class Key, class T> template <class Key, class T>
@ -152,7 +152,7 @@ Map<Key, T> &Map<Key,T>::erase(const Key &key)
template <class Key, class T> template <class Key, class T>
unsigned int Map<Key, T>::size() const unsigned int Map<Key, T>::size() const
{ {
return d->map.size(); return static_cast<unsigned int>(d->map.size());
} }
template <class Key, class T> template <class Key, class T>
@ -171,16 +171,18 @@ T &Map<Key, T>::operator[](const Key &key)
template <class Key, class T> template <class Key, class T>
Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m) Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m)
{ {
if(&m == this) Map<Key, T>(m).swap(*this);
return *this;
if(d->deref())
delete(d);
d = m.d;
d->ref();
return *this; return *this;
} }
template <class Key, class T>
void Map<Key, T>::swap(Map<Key, T> &m)
{
using std::swap;
swap(d, m.d);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// protected members // protected members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -31,14 +31,9 @@
#if defined(HAVE_STD_ATOMIC) #if defined(HAVE_STD_ATOMIC)
# include <atomic> # include <atomic>
# define ATOMIC_INT std::atomic<unsigned int> # define ATOMIC_INT std::atomic_int
# define ATOMIC_INC(x) x.fetch_add(1) # define ATOMIC_INC(x) (++x)
# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1) # define ATOMIC_DEC(x) (--x)
#elif defined(HAVE_BOOST_ATOMIC)
# include <boost/atomic.hpp>
# define ATOMIC_INT boost::atomic<unsigned int>
# define ATOMIC_INC(x) x.fetch_add(1)
# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1)
#elif defined(HAVE_GCC_ATOMIC) #elif defined(HAVE_GCC_ATOMIC)
# define ATOMIC_INT int # define ATOMIC_INT int
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1) # define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)

View File

@ -30,6 +30,7 @@
#include "taglib.h" #include "taglib.h"
#ifdef __APPLE__ #ifdef __APPLE__
# define OSATOMIC_DEPRECATED 0
# include <libkern/OSAtomic.h> # include <libkern/OSAtomic.h>
# define TAGLIB_ATOMIC_MAC # define TAGLIB_ATOMIC_MAC
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)

View File

@ -23,19 +23,10 @@
* http://www.mozilla.org/MPL/ * * http://www.mozilla.org/MPL/ *
***************************************************************************/ ***************************************************************************/
// This class assumes that std::basic_string<T> has a contiguous and null-terminated buffer.
#include <iostream>
#include <cerrno> #include <cerrno>
#include <climits> #include <climits>
#include <cstdio>
#include <cstring>
#ifdef _WIN32 #include <utf8-cpp/checked.h>
# include <windows.h>
#else
# include "unicode.h"
#endif
#include <tdebug.h> #include <tdebug.h>
#include <tstringlist.h> #include <tstringlist.h>
@ -48,70 +39,6 @@ namespace
{ {
using namespace TagLib; using namespace TagLib;
size_t UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength)
{
size_t len = 0;
#ifdef _WIN32
len = ::WideCharToMultiByte(CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL);
#else
using namespace Unicode;
const UTF16 *srcBegin = src;
const UTF16 *srcEnd = srcBegin + srcLength;
UTF8 *dstBegin = reinterpret_cast<UTF8*>(dst);
UTF8 *dstEnd = dstBegin + dstLength;
ConversionResult result = ConvertUTF16toUTF8(
&srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion);
if(result == conversionOK)
len = dstBegin - reinterpret_cast<UTF8*>(dst);
#endif
if(len == 0)
debug("String::UTF16toUTF8() - Unicode conversion error.");
return len;
}
size_t UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength)
{
size_t len = 0;
#ifdef _WIN32
len = ::MultiByteToWideChar(CP_UTF8, 0, src, srcLength, dst, dstLength);
#else
using namespace Unicode;
const UTF8 *srcBegin = reinterpret_cast<const UTF8*>(src);
const UTF8 *srcEnd = srcBegin + srcLength;
UTF16 *dstBegin = dst;
UTF16 *dstEnd = dstBegin + dstLength;
ConversionResult result = ConvertUTF8toUTF16(
&srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion);
if(result == conversionOK)
len = dstBegin - dst;
#endif
if(len == 0)
debug("String::UTF8toUTF16() - Unicode conversion error.");
return len;
}
// Returns the native format of std::wstring. // Returns the native format of std::wstring.
String::Type wcharByteOrder() String::Type wcharByteOrder()
{ {
@ -137,28 +64,61 @@ namespace
{ {
data.resize(length); data.resize(length);
if(length > 0) { try {
const size_t len = UTF8toUTF16(s, length, &data[0], data.size()); const std::wstring::iterator dstEnd = utf8::utf8to16(s, s + length, data.begin());
data.resize(len); data.resize(dstEnd - data.begin());
} }
catch(const utf8::exception &e) {
const String message(e.what());
debug("String::copyFromUTF8() - UTF8-CPP error: " + message);
data.clear();
}
}
// Helper functions to read a UTF-16 character from an array.
template <typename T>
unsigned short nextUTF16(const T **p);
template <>
unsigned short nextUTF16<wchar_t>(const wchar_t **p)
{
return static_cast<unsigned short>(*(*p)++);
}
template <>
unsigned short nextUTF16<char>(const char **p)
{
union {
unsigned short w;
char c[2];
} u;
u.c[0] = *(*p)++;
u.c[1] = *(*p)++;
return u.w;
} }
// Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into // Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into
// UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. // UTF-16(without BOM/CPU byte order) and copies it to the internal buffer.
void copyFromUTF16(std::wstring &data, const wchar_t *s, size_t length, String::Type t) template <typename T>
void copyFromUTF16(std::wstring &data, const T *s, size_t length, String::Type t)
{ {
bool swap; bool swap;
if(t == String::UTF16) { if(t == String::UTF16) {
if(length >= 1 && s[0] == 0xfeff) if(length < 1) {
swap = false; // Same as CPU endian. No need to swap bytes. debug("String::copyFromUTF16() - Invalid UTF16 string. Too short to have a BOM.");
else if(length >= 1 && s[0] == 0xfffe) return;
swap = true; // Not same as CPU endian. Need to swap bytes. }
else {
debug("String::copyFromUTF16() - Invalid UTF16 string."); const unsigned short bom = nextUTF16(&s);
if(bom == 0xfeff)
swap = false; // Same as CPU endian. No need to swap bytes.
else if(bom == 0xfffe)
swap = true; // Not same as CPU endian. Need to swap bytes.
else {
debug("String::copyFromUTF16() - Invalid UTF16 string. BOM is broken.");
return; return;
} }
s++;
length--; length--;
} }
else { else {
@ -166,57 +126,12 @@ namespace
} }
data.resize(length); data.resize(length);
if(length > 0) { for(size_t i = 0; i < length; ++i) {
if(swap) { const unsigned short c = nextUTF16(&s);
for(size_t i = 0; i < length; ++i)
data[i] = Utils::byteSwap(static_cast<unsigned short>(s[i]));
}
else {
::wmemcpy(&data[0], s, length);
}
}
}
// Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into
// UTF-16(without BOM/CPU byte order) and copies it to the internal buffer.
void copyFromUTF16(std::wstring &data, const char *s, size_t length, String::Type t)
{
bool swap;
if(t == String::UTF16) {
if(length < 2) {
debug("String::copyFromUTF16() - Invalid UTF16 string.");
return;
}
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
unsigned short bom;
::memcpy(&bom, s, 2);
if(bom == 0xfeff)
swap = false; // Same as CPU endian. No need to swap bytes.
else if(bom == 0xfffe)
swap = true; // Not same as CPU endian. Need to swap bytes.
else {
debug("String::copyFromUTF16() - Invalid UTF16 string.");
return;
}
s += 2;
length -= 2;
}
else {
swap = (t != wcharByteOrder());
}
data.resize(length / 2);
for(size_t i = 0; i < length / 2; ++i) {
unsigned short c;
::memcpy(&c, s, 2);
if(swap) if(swap)
c = Utils::byteSwap(c); data[i] = Utils::byteSwap(c);
else
data[i] = static_cast<wchar_t>(c); data[i] = c;
s += 2;
} }
} }
} }
@ -229,10 +144,6 @@ public:
StringPrivate() : StringPrivate() :
RefCounter() {} RefCounter() {}
StringPrivate(unsigned int n, wchar_t c) :
RefCounter(),
data(static_cast<size_t>(n), c) {}
/*! /*!
* Stores string in UTF-16. The byte order depends on the CPU endian. * Stores string in UTF-16. The byte order depends on the CPU endian.
*/ */
@ -332,9 +243,13 @@ String::String(wchar_t c, Type t) :
} }
String::String(char c, Type t) : String::String(char c, Type t) :
d(new StringPrivate(1, static_cast<unsigned char>(c))) d(new StringPrivate())
{ {
if(t != Latin1 && t != UTF8) { if(t == Latin1)
copyFromLatin1(d->data, &c, 1);
else if(t == String::UTF8)
copyFromUTF8(d->data, &c, 1);
else {
debug("String::String() -- char should not contain UTF16."); debug("String::String() -- char should not contain UTF16.");
} }
} }
@ -350,7 +265,7 @@ String::String(const ByteVector &v, Type t) :
else if(t == UTF8) else if(t == UTF8)
copyFromUTF8(d->data, v.data(), v.size()); copyFromUTF8(d->data, v.data(), v.size());
else else
copyFromUTF16(d->data, v.data(), v.size(), t); copyFromUTF16(d->data, v.data(), v.size() / 2, t);
// If we hit a null in the ByteVector, shrink the string again. // If we hit a null in the ByteVector, shrink the string again.
d->data.resize(::wcslen(d->data.c_str())); d->data.resize(::wcslen(d->data.c_str()));
@ -410,12 +325,12 @@ String::ConstIterator String::end() const
int String::find(const String &s, int offset) const int String::find(const String &s, int offset) const
{ {
return d->data.find(s.d->data, offset); return static_cast<int>(d->data.find(s.d->data, offset));
} }
int String::rfind(const String &s, int offset) const int String::rfind(const String &s, int offset) const
{ {
return d->data.rfind(s.d->data, offset); return static_cast<int>(d->data.rfind(s.d->data, offset));
} }
StringList String::split(const String &separator) const StringList String::split(const String &separator) const
@ -445,7 +360,10 @@ bool String::startsWith(const String &s) const
String String::substr(unsigned int position, unsigned int n) const String String::substr(unsigned int position, unsigned int n) const
{ {
return String(d->data.substr(position, n)); if(position == 0 && n >= size())
return *this;
else
return String(d->data.substr(position, n));
} }
String &String::append(const String &s) String &String::append(const String &s)
@ -464,12 +382,11 @@ String & String::clear()
String String::upper() const String String::upper() const
{ {
String s; String s;
s.d->data.reserve(size());
static int shift = 'A' - 'a'; for(ConstIterator it = begin(); it != end(); ++it) {
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) {
if(*it >= 'a' && *it <= 'z') if(*it >= 'a' && *it <= 'z')
s.d->data.push_back(*it + shift); s.d->data.push_back(*it + 'A' - 'a');
else else
s.d->data.push_back(*it); s.d->data.push_back(*it);
} }
@ -479,7 +396,7 @@ String String::upper() const
unsigned int String::size() const unsigned int String::size() const
{ {
return d->data.size(); return static_cast<unsigned int>(d->data.size());
} }
unsigned int String::length() const unsigned int String::length() const
@ -506,25 +423,27 @@ ByteVector String::data(Type t) const
ByteVector v(size(), 0); ByteVector v(size(), 0);
char *p = v.data(); char *p = v.data();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) for(ConstIterator it = begin(); it != end(); ++it)
*p++ = static_cast<char>(*it); *p++ = static_cast<char>(*it);
return v; return v;
} }
case UTF8: case UTF8:
if(!d->data.empty())
{ {
ByteVector v(size() * 4 + 1, 0); ByteVector v(size() * 4, 0);
const size_t len = UTF16toUTF8( try {
d->data.c_str(), d->data.size(), v.data(), v.size()); const ByteVector::Iterator dstEnd = utf8::utf16to8(begin(), end(), v.begin());
v.resize(len); v.resize(static_cast<unsigned int>(dstEnd - v.begin()));
}
catch(const utf8::exception &e) {
const String message(e.what());
debug("String::data() - UTF8-CPP error: " + message);
v.clear();
}
return v; return v;
} }
else {
return ByteVector();
}
case UTF16: case UTF16:
{ {
ByteVector v(2 + size() * 2, 0); ByteVector v(2 + size() * 2, 0);
@ -535,7 +454,7 @@ ByteVector String::data(Type t) const
*p++ = '\xff'; *p++ = '\xff';
*p++ = '\xfe'; *p++ = '\xfe';
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { for(ConstIterator it = begin(); it != end(); ++it) {
*p++ = static_cast<char>(*it & 0xff); *p++ = static_cast<char>(*it & 0xff);
*p++ = static_cast<char>(*it >> 8); *p++ = static_cast<char>(*it >> 8);
} }
@ -547,7 +466,7 @@ ByteVector String::data(Type t) const
ByteVector v(size() * 2, 0); ByteVector v(size() * 2, 0);
char *p = v.data(); char *p = v.data();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { for(ConstIterator it = begin(); it != end(); ++it) {
*p++ = static_cast<char>(*it >> 8); *p++ = static_cast<char>(*it >> 8);
*p++ = static_cast<char>(*it & 0xff); *p++ = static_cast<char>(*it & 0xff);
} }
@ -559,7 +478,7 @@ ByteVector String::data(Type t) const
ByteVector v(size() * 2, 0); ByteVector v(size() * 2, 0);
char *p = v.data(); char *p = v.data();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { for(ConstIterator it = begin(); it != end(); ++it) {
*p++ = static_cast<char>(*it & 0xff); *p++ = static_cast<char>(*it & 0xff);
*p++ = static_cast<char>(*it >> 8); *p++ = static_cast<char>(*it >> 8);
} }
@ -604,12 +523,12 @@ String String::stripWhiteSpace() const
return String(); return String();
const size_t pos2 = d->data.find_last_not_of(WhiteSpaceChars); const size_t pos2 = d->data.find_last_not_of(WhiteSpaceChars);
return substr(pos1, pos2 - pos1 + 1); return substr(static_cast<unsigned int>(pos1), static_cast<unsigned int>(pos2 - pos1 + 1));
} }
bool String::isLatin1() const bool String::isLatin1() const
{ {
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { for(ConstIterator it = begin(); it != end(); ++it) {
if(*it >= 256) if(*it >= 256)
return false; return false;
} }
@ -618,7 +537,7 @@ bool String::isLatin1() const
bool String::isAscii() const bool String::isAscii() const
{ {
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { for(ConstIterator it = begin(); it != end(); ++it) {
if(*it >= 128) if(*it >= 128)
return false; return false;
} }
@ -787,6 +706,12 @@ void String::detach()
if(d->count() > 1) if(d->count() > 1)
String(d->data.c_str()).swap(*this); String(d->data.c_str()).swap(*this);
} }
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
const String::Type String::WCharByteOrder = wcharByteOrder();
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -536,6 +536,13 @@ namespace TagLib {
void detach(); void detach();
private: private:
/*!
* \deprecated This variable is no longer used, but NEVER remove this. It
* may lead to a linkage error.
*/
// BIC: remove
static const Type WCharByteOrder;
class StringPrivate; class StringPrivate;
StringPrivate *d; StringPrivate *d;
}; };

View File

@ -34,9 +34,7 @@
# include <config.h> # include <config.h>
#endif #endif
#if defined(HAVE_BOOST_BYTESWAP) #if defined(HAVE_MSC_BYTESWAP)
# include <boost/endian/conversion.hpp>
#elif defined(HAVE_MSC_BYTESWAP)
# include <stdlib.h> # include <stdlib.h>
#elif defined(HAVE_GLIBC_BYTESWAP) #elif defined(HAVE_GLIBC_BYTESWAP)
# include <byteswap.h> # include <byteswap.h>
@ -63,11 +61,7 @@ namespace TagLib
*/ */
inline unsigned short byteSwap(unsigned short x) inline unsigned short byteSwap(unsigned short x)
{ {
#if defined(HAVE_BOOST_BYTESWAP) #if defined(HAVE_GCC_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint16_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap16(x); return __builtin_bswap16(x);
@ -99,11 +93,7 @@ namespace TagLib
*/ */
inline unsigned int byteSwap(unsigned int x) inline unsigned int byteSwap(unsigned int x)
{ {
#if defined(HAVE_BOOST_BYTESWAP) #if defined(HAVE_GCC_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint32_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap32(x); return __builtin_bswap32(x);
@ -138,11 +128,7 @@ namespace TagLib
*/ */
inline unsigned long long byteSwap(unsigned long long x) inline unsigned long long byteSwap(unsigned long long x)
{ {
#if defined(HAVE_BOOST_BYTESWAP) #if defined(HAVE_GCC_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint64_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap64(x); return __builtin_bswap64(x);
@ -221,23 +207,6 @@ namespace TagLib
return String(); return String();
} }
/*!
* Returns whether the two strings s1 and s2 are equal, ignoring the case of
* the characters.
*
* We took the trouble to define this one here, since there are some
* incompatible variations of case insensitive strcmp().
*/
inline bool equalsIgnoreCase(const char *s1, const char *s2)
{
while(*s1 != '\0' && *s2 != '\0' && ::tolower(*s1) == ::tolower(*s2)) {
s1++;
s2++;
}
return (*s1 == '\0' && *s2 == '\0');
}
/*! /*!
* The types of byte order of the running system. * The types of byte order of the running system.
*/ */
@ -250,7 +219,7 @@ namespace TagLib
}; };
/*! /*!
* Returns the integer byte order of the system. * Returns the byte order of the system.
*/ */
inline ByteOrder systemByteOrder() inline ByteOrder systemByteOrder()
{ {
@ -265,26 +234,6 @@ namespace TagLib
else else
return BigEndian; return BigEndian;
} }
/*!
* Returns the IEEE754 byte order of the system.
*/
inline ByteOrder floatByteOrder()
{
union {
double d;
char c;
} u;
// 1.0 is stored in memory like 0x3FF0000000000000 in canonical form.
// So the first byte is zero if little endian.
u.d = 1.0;
if(u.c == 0)
return LittleEndian;
else
return BigEndian;
}
} }
} }
} }

View File

@ -27,23 +27,19 @@
# include <config.h> # include <config.h>
#endif #endif
#if defined(HAVE_ZLIB) #ifdef HAVE_ZLIB
# include <zlib.h> # include <zlib.h>
#elif defined(HAVE_BOOST_ZLIB) # include <tstring.h>
# include <boost/iostreams/filtering_streambuf.hpp> # include <tdebug.h>
# include <boost/iostreams/filter/zlib.hpp>
#endif #endif
#include <tstring.h>
#include <tdebug.h>
#include "tzlib.h" #include "tzlib.h"
using namespace TagLib; using namespace TagLib;
bool zlib::isAvailable() bool zlib::isAvailable()
{ {
#if defined(HAVE_ZLIB) || defined(HAVE_BOOST_ZLIB) #ifdef HAVE_ZLIB
return true; return true;
@ -56,7 +52,7 @@ bool zlib::isAvailable()
ByteVector zlib::decompress(const ByteVector &data) ByteVector zlib::decompress(const ByteVector &data)
{ {
#if defined(HAVE_ZLIB) #ifdef HAVE_ZLIB
z_stream stream = {}; z_stream stream = {};
@ -102,38 +98,6 @@ ByteVector zlib::decompress(const ByteVector &data)
return outData; return outData;
#elif defined(HAVE_BOOST_ZLIB)
using namespace boost::iostreams;
struct : public sink
{
ByteVector data;
typedef char char_type;
typedef sink_tag category;
std::streamsize write(char const* s, std::streamsize n)
{
const unsigned int originalSize = data.size();
data.resize(static_cast<unsigned int>(originalSize + n));
::memcpy(data.data() + originalSize, s, static_cast<size_t>(n));
return n;
}
} sink;
try {
zlib_decompressor().write(sink, data.data(), data.size());
}
catch(const zlib_error &) {
debug("zlib::decompress() - Error reading compressed stream.");
return ByteVector();
}
return sink.data;
#else #else
return ByteVector(); return ByteVector();

View File

@ -1,303 +0,0 @@
/*******************************************************************************
* *
* THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
* AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
* AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
* *
*******************************************************************************/
/*
* Copyright 2001 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/*
* This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
* the UTF32 conversion functions and to place the appropriate functions
* in their own C++ namespace.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "unicode.h"
#include <stdio.h>
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#define false 0
#define true 1
namespace Unicode {
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... six byte sequence.)
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
UTF32 ch2 = *source;
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x200000) { bytesToWrite = 4;
} else { bytesToWrite = 2;
ch = UNI_REPLACEMENT_CHAR;
}
// printf("bytes to write = %i\n", bytesToWrite);
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static Boolean isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
if (*source > 0xF4) return false;
}
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (source+length > sourceEnd) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
if ((flags == strictConversion) && (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (ch & halfMask) + UNI_SUR_LOW_START;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

View File

@ -1,149 +0,0 @@
#ifndef TAGLIB_UNICODE_H
#define TAGLIB_UNICODE_H
/*******************************************************************************
* *
* THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
* AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
* AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
* *
*******************************************************************************/
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
/*
* Copyright 2001 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/*
* This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
* the UTF32 conversion functions and to place the appropriate functions
* in their own C++ namespace.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several functions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
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 char UTF8; /* typically 8 bits */
typedef unsigned char Boolean; /* 0 or 1 */
typedef enum {
conversionOK = 0, /* conversion successful */
sourceExhausted = 1, /* partial character in source, but hit end */
targetExhausted = 2, /* insuff. room in target for conversion */
sourceIllegal = 3 /* source sequence is illegal/malformed */
} ConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
} // namespace Unicode
/* --------------------------------------------------------------------- */
#endif
#endif

View File

@ -73,6 +73,18 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool TrueAudio::File::isSupported(IOStream *stream)
{
// A TrueAudio file has to start with "TTA". An ID3v2 tag may precede.
const ByteVector id = Utils::readHeader(stream, 3, true);
return (id == "TTA");
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -235,6 +235,15 @@ namespace TagLib {
*/ */
bool hasID3v2Tag() const; bool hasID3v2Tag() const;
/*!
* Returns whether or not the given \a stream can be opened as a TrueAudio
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -71,6 +71,18 @@ public:
Properties *properties; Properties *properties;
}; };
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool WavPack::File::isSupported(IOStream *stream)
{
// A WavPack file has to start with "wvpk".
const ByteVector id = Utils::readHeader(stream, 4, false);
return (id == "wvpk");
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// public members // public members
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -200,6 +200,14 @@ namespace TagLib {
*/ */
bool hasAPETag() const; bool hasAPETag() const;
/*!
* Check if the given \a stream can be opened as a WavPack file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private: private:
File(const File &); File(const File &);
File &operator=(const File &); File &operator=(const File &);

View File

@ -586,9 +586,9 @@ void XM::File::read(bool)
unsigned int count = 4 + instrument.read(*this, instrumentHeaderSize - 4U); unsigned int count = 4 + instrument.read(*this, instrumentHeaderSize - 4U);
READ_ASSERT(count == std::min(instrumentHeaderSize, (unsigned long)instrument.size() + 4)); READ_ASSERT(count == std::min(instrumentHeaderSize, (unsigned long)instrument.size() + 4));
unsigned long sampleHeaderSize = 0;
long offset = 0; long offset = 0;
if(sampleCount > 0) { if(sampleCount > 0) {
unsigned long sampleHeaderSize = 0;
sumSampleCount += sampleCount; sumSampleCount += sampleCount;
// wouldn't know which header size to assume otherwise: // wouldn't know which header size to assume otherwise:
READ_ASSERT(instrumentHeaderSize >= count + 4 && readU32L(sampleHeaderSize)); READ_ASSERT(instrumentHeaderSize >= count + 4 && readU32L(sampleHeaderSize));

View File

@ -60,7 +60,7 @@ public:
XM::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : XM::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle), AudioProperties(propertiesStyle),
d(new PropertiesPrivate) d(new PropertiesPrivate())
{ {
} }

Some files were not shown because too many files have changed in this diff Show More