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(TAGLIB_SOVERSION_CURRENT 17)
@ -45,6 +47,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_SOURCE_DIR}/3rdparty
)
if(ZLIB_FOUND)
@ -53,10 +56,6 @@ elseif(HAVE_ZLIB_SOURCE)
include_directories(${ZLIB_SOURCE})
endif()
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
include_directories(${Boost_INCLUDE_DIR})
endif()
set(tag_HDRS
tag.h
fileref.h
@ -333,12 +332,6 @@ set(toolkit_SRCS
toolkit/tzlib.cpp
)
if(NOT WIN32)
set(unicode_SRCS
toolkit/unicode.cpp
)
endif()
if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c
@ -355,7 +348,7 @@ set(tag_LIB_SRCS
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${unicode_SRCS} ${zlib_SRCS}
${zlib_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
@ -365,18 +358,10 @@ set(tag_LIB_SRCS
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})
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
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}

View File

@ -83,6 +83,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////

View File

@ -211,6 +211,15 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

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

View File

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

View File

@ -113,7 +113,7 @@ namespace TagLib
/*!
* 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.

View File

@ -27,6 +27,7 @@
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
#include <tagutils.h>
#include "asffile.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*/)
{
file->d->contentDescriptionObject = this;
const int titleLength = readWORD(file);
const int artistLength = 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*/)
{
file->d->extendedContentDescriptionObject = this;
int count = readWORD(file);
while(count--) {
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*/)
{
file->d->metadataObject = this;
int count = readWORD(file);
while(count--) {
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*/)
{
file->d->metadataLibraryObject = this;
int count = readWORD(file);
while(count--) {
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*/)
{
file->d->headerExtensionObject = this;
file->seek(18, File::Current);
long long dataSize = readDWORD(file);
long long dataPos = 0;
@ -394,10 +390,12 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
}
BaseObject *obj;
if(guid == metadataGuid) {
obj = new MetadataObject();
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
}
else if(guid == metadataLibraryGuid) {
obj = new MetadataLibraryObject();
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
}
else {
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
////////////////////////////////////////////////////////////////////////////////
@ -616,9 +626,8 @@ void ASF::File::read()
if(!isValid())
return;
ByteVector guid = readBlock(16);
if(guid != headerGuid) {
debug("ASF: Not an ASF file.");
if(readBlock(16) != headerGuid) {
debug("ASF::File::read(): Not an ASF file.");
setValid(false);
return;
}
@ -639,8 +648,10 @@ void ASF::File::read()
}
seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
for(int i = 0; i < numObjects; i++) {
guid = readBlock(16);
const ByteVector guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
@ -652,19 +663,24 @@ void ASF::File::read()
}
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
obj = new FilePrivate::FilePropertiesObject();
filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
}
else if(guid == streamPropertiesGuid) {
obj = new FilePrivate::StreamPropertiesObject();
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
}
else if(guid == contentDescriptionGuid) {
obj = new FilePrivate::ContentDescriptionObject();
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
}
else if(guid == extendedContentDescriptionGuid) {
obj = new FilePrivate::ExtendedContentDescriptionObject();
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
}
else if(guid == headerExtensionGuid) {
obj = new FilePrivate::HeaderExtensionObject();
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
}
else if(guid == codecListGuid) {
obj = new FilePrivate::CodecListObject();
@ -680,4 +696,10 @@ void ASF::File::read()
obj->parse(this, size);
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();
/*!
* 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:
void read();

View File

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

View File

@ -43,6 +43,39 @@
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
{
@ -59,98 +92,12 @@ AudioProperties::~AudioProperties()
int AudioProperties::lengthInSeconds() const
{
// This is an ugly workaround but we can't add a virtual function.
// 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;
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
}
int AudioProperties::lengthInMilliseconds() const
{
// This is an ugly workaround but we can't add a virtual function.
// 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;
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -28,6 +28,7 @@
***************************************************************************/
#include <tfile.h>
#include <tfilestream.h>
#include <tstring.h>
#include <tdebug.h>
#include <trefcounter.h>
@ -59,49 +60,14 @@ namespace
typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers;
// Templatized internal functions. T should be String or IOStream*.
// Detect the file type by user-defined resolvers.
template <typename T>
FileName toFileName(T arg)
{
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)
File *detectByResolvers(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(arg, readProperties, style);
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
@ -109,18 +75,15 @@ namespace
return 0;
}
template <typename T>
File* createInternal(T arg, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
// Detect the file type based on the file extension.
File* detectByExtension(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
const String s = toFileName(arg).toString();
const String s = stream->name().toString();
#else
const String s(toFileName(arg));
const String s(stream->name());
#endif
String ext;
@ -135,49 +98,163 @@ namespace
if(ext.isEmpty())
return 0;
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
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")
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") {
/* .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())
return file;
delete file;
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
}
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")
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
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")
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
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")
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
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
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")
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0;
}
@ -186,15 +263,18 @@ namespace
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate(File *f) :
FileRefPrivate() :
RefCounter(),
file(f) {}
file(0),
stream(0) {}
~FileRefPrivate() {
delete file;
delete stream;
}
File *file;
File *file;
IOStream *stream;
};
////////////////////////////////////////////////////////////////////////////////
@ -202,24 +282,27 @@ public:
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() :
d(new FileRefPrivate(0))
d(new FileRefPrivate())
{
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
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) :
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
d(new FileRefPrivate())
{
parse(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(File *file) :
d(new FileRefPrivate(file))
d(new FileRefPrivate())
{
d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
@ -341,3 +424,51 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
{
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,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
private:
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
class FileRefPrivate;
FileRefPrivate *d;
};

View File

@ -95,6 +95,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////
@ -168,8 +180,8 @@ bool FLAC::File::save()
}
// Create new vorbis comments
Tag::duplicate(&d->tag, xiphComment(true), false);
if(!hasXiphComment())
Tag::duplicate(&d->tag, xiphComment(true), false);
d->xiphCommentData = xiphComment()->render(false);
@ -198,7 +210,6 @@ bool FLAC::File::save()
}
// 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 paddingLength = originalLength - data.size() - 4;
@ -298,11 +309,7 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
if(!create || d->tag[FlacID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
@ -507,7 +514,9 @@ void FLAC::File::scan()
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");
setValid(false);
return;

View File

@ -318,6 +318,15 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

@ -50,14 +50,14 @@ public:
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);
}

View File

@ -39,11 +39,10 @@ public:
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;
//debug(String(data.toHex()));
d->data = data;
}

View File

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

View File

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

View File

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

View File

@ -54,24 +54,25 @@ MP4::Atom::Atom(File *file)
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();
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);
}
else {
if(longLength <= LONG_MAX) {
// The atom has a 64-bit length, but it's actually a 31-bit value
length = static_cast<long>(longLength);
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
}
if(length < 8) {
debug("MP4: Invalid atom size");
length = 0;

View File

@ -26,6 +26,8 @@
#include <tdebug.h>
#include <tstring.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "mp4file.h"
@ -69,6 +71,22 @@ public:
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) :
TagLib::File(file),
d(new FilePrivate())

View File

@ -120,6 +120,15 @@ namespace TagLib {
*/
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:
void read(bool readProperties);

View File

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

View File

@ -75,6 +75,19 @@ public:
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
////////////////////////////////////////////////////////////////////////////////

View File

@ -214,6 +214,15 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

@ -48,14 +48,16 @@ public:
// 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);
}
@ -169,9 +171,10 @@ ByteVector AttachedPictureFrame::renderFields() const
// 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));
}

View File

@ -37,7 +37,11 @@ class ChapterFrame::ChapterFramePrivate
{
public:
ChapterFramePrivate() :
tagHeader(0)
tagHeader(0),
startTime(0),
endTime(0),
startOffset(0),
endOffset(0)
{
embeddedFrameList.setAutoDelete(true);
}
@ -57,9 +61,9 @@ public:
////////////////////////////////////////////////////////////////////////////////
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
ID3v2::Frame(data)
ID3v2::Frame(data),
d(new ChapterFramePrivate())
{
d = new ChapterFramePrivate;
d->tagHeader = tagHeader;
setData(data);
}
@ -68,10 +72,9 @@ ChapterFrame::ChapterFrame(const ByteVector &elementID,
unsigned int startTime, unsigned int endTime,
unsigned int startOffset, unsigned int endOffset,
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
// specifically include the null byte.
@ -198,7 +201,7 @@ String ChapterFrame::toString() const
s += ", start offset: " + String::number(d->startOffset);
if(d->endOffset != 0xFFFFFFFF)
s += ", start offset: " + String::number(d->endOffset);
s += ", end offset: " + String::number(d->endOffset);
if(!d->embeddedFrameList.isEmpty()) {
StringList frameIDs;
@ -298,9 +301,9 @@ ByteVector ChapterFrame::renderFields() const
}
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) :
Frame(h)
Frame(h),
d(new ChapterFramePrivate())
{
d = new ChapterFramePrivate;
d->tagHeader = tagHeader;
parseFields(fieldData(data));
}

View File

@ -48,15 +48,17 @@ public:
// 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;
}
CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
CommentsFrame::CommentsFrame(const ByteVector &data) :
Frame(data),
d(new CommentsFramePrivate())
{
d = new CommentsFramePrivate;
setData(data);
}
@ -188,8 +190,9 @@ ByteVector CommentsFrame::renderFields() const
// 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));
}

View File

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

View File

@ -50,14 +50,16 @@ public:
// 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);
}
@ -177,8 +179,9 @@ ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
// 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));
}

View File

@ -45,15 +45,17 @@ public:
// 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;
}
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data)
OwnershipFrame::OwnershipFrame(const ByteVector &data) :
Frame(data),
d(new OwnershipFramePrivate())
{
d = new OwnershipFramePrivate;
setData(data);
}
@ -161,8 +163,9 @@ ByteVector OwnershipFrame::renderFields() const
// 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));
}

View File

@ -38,9 +38,10 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame() : Frame("PCST")
PodcastFrame::PodcastFrame() :
Frame("PCST"),
d(new PodcastFramePrivate())
{
d = new PodcastFramePrivate;
d->fieldData = ByteVector(4, '\0');
}
@ -72,8 +73,9 @@ ByteVector PodcastFrame::renderFields() const
// 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));
}

View File

@ -43,14 +43,16 @@ public:
// 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);
}
@ -130,8 +132,9 @@ ByteVector PopularimeterFrame::renderFields() const
// 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));
}

View File

@ -45,14 +45,16 @@ public:
// 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);
}
@ -121,8 +123,9 @@ ByteVector PrivateFrame::renderFields() const
// 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));
}

View File

@ -51,14 +51,16 @@ public:
// 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);
}
@ -223,8 +225,9 @@ ByteVector RelativeVolumeFrame::renderFields() const
// 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));
}

View File

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

View File

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

View File

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

View File

@ -45,16 +45,16 @@ public:
////////////////////////////////////////////////////////////////////////////////
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) :
Frame(type)
Frame(type),
d(new TextIdentificationFramePrivate())
{
d = new TextIdentificationFramePrivate;
d->textEncoding = encoding;
}
TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
Frame(data)
Frame(data),
d(new TextIdentificationFramePrivate())
{
d = new TextIdentificationFramePrivate;
setData(data);
}
@ -252,9 +252,10 @@ ByteVector TextIdentificationFrame::renderFields() const
// 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));
}

View File

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

View File

@ -38,9 +38,10 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data)
UnknownFrame::UnknownFrame(const ByteVector &data) :
Frame(data),
d(new UnknownFramePrivate())
{
d = new UnknownFramePrivate;
setData(data);
}
@ -77,8 +78,9 @@ ByteVector UnknownFrame::renderFields() const
// 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));
}

View File

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

View File

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

View File

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

View File

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

View File

@ -60,6 +60,7 @@ class ID3v2::Tag::TagPrivate
{
public:
TagPrivate() :
factory(0),
file(0),
tagOffset(0),
extendedHeader(0),
@ -286,7 +287,7 @@ void ID3v2::Tag::setGenre(const String &s)
void ID3v2::Tag::setYear(unsigned int i)
{
if(i <= 0) {
if(i == 0) {
removeFrames("TDRC");
return;
}
@ -295,7 +296,7 @@ void ID3v2::Tag::setYear(unsigned int i)
void ID3v2::Tag::setTrack(unsigned int i)
{
if(i <= 0) {
if(i == 0) {
removeFrames("TRCK");
return;
}
@ -618,7 +619,6 @@ ByteVector ID3v2::Tag::render(int version) const
}
// 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 paddingSize = originalSize - (tagData.size() - Header::size());
@ -723,7 +723,7 @@ void ID3v2::Tag::parse(const ByteVector &origData)
if(d->header.extendedHeader()) {
if(!d->extendedHeader)
d->extendedHeader = new ExtendedHeader;
d->extendedHeader = new ExtendedHeader();
d->extendedHeader->setData(data);
if(d->extendedHeader->size() <= data.size()) {
frameDataPosition += d->extendedHeader->size();

View File

@ -76,6 +76,58 @@ public:
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
////////////////////////////////////////////////////////////////////////////////
@ -346,55 +398,50 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
long MPEG::File::nextFrameOffset(long position)
{
bool foundLastSyncPattern = false;
ByteVector buffer;
ByteVector frameSyncBytes(2, '\0');
while(true) {
seek(position);
buffer = readBlock(bufferSize());
if(buffer.size() <= 0)
const ByteVector buffer = readBlock(bufferSize());
if(buffer.isEmpty())
return -1;
if(foundLastSyncPattern && secondSynchByte(buffer[0]))
return position - 1;
for(unsigned int i = 0; i < buffer.size() - 1; i++) {
if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1]))
return position + i;
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 position + i - 1;
}
}
foundLastSyncPattern = firstSyncByte(buffer[buffer.size() - 1]);
position += buffer.size();
position += bufferSize();
}
}
long MPEG::File::previousFrameOffset(long position)
{
bool foundFirstSyncPattern = false;
ByteVector buffer;
ByteVector frameSyncBytes(2, '\0');
while (position > 0) {
long size = std::min<long>(position, bufferSize());
position -= size;
while(position > 0) {
const long bufferLength = std::min<long>(position, bufferSize());
position -= bufferLength;
seek(position);
buffer = readBlock(size);
const ByteVector buffer = readBlock(bufferLength);
if(buffer.size() <= 0)
break;
if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1]))
return position + buffer.size() - 1;
for(int i = buffer.size() - 2; i >= 0; i--) {
if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1]))
return position + i;
for(int i = buffer.size() - 1; i >= 0; --i) {
frameSyncBytes[1] = frameSyncBytes[0];
frameSyncBytes[0] = buffer[i];
if(isFrameSync(frameSyncBytes)) {
const Header header(this, position + i, true);
if(header.isValid())
return position + i + header.frameLength();
}
}
foundFirstSyncPattern = secondSynchByte(buffer[0]);
}
return -1;
}
@ -488,28 +535,41 @@ long MPEG::File::findID3v2()
const ByteVector headerID = ID3v2::Header::fileIdentifier();
seek(0);
const ByteVector data = readBlock(headerID.size());
if(data.size() < headerID.size())
return -1;
if(data == headerID)
if(readBlock(headerID.size()) == headerID)
return 0;
if(firstSyncByte(data[0]) && secondSynchByte(data[1]))
const Header firstHeader(this, 0, true);
if(firstHeader.isValid())
return -1;
// Look for the entire file, if neither an MEPG frame or ID3v2 tag was found
// at the beginning of the file.
// We don't care about the inefficiency of the code, since this is a seldom case.
// Look for an ID3v2 tag until reaching the first valid MPEG frame.
const long tagOffset = find(headerID);
if(tagOffset < 0)
return -1;
ByteVector frameSyncBytes(2, '\0');
ByteVector tagHeaderBytes(3, '\0');
long position = 0;
const long frameOffset = firstFrameOffset();
if(frameOffset < tagOffset)
return -1;
while(true) {
seek(position);
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;
/*!
* 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:
File(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.
if(!firstSyncByte(data[0]) || !secondSynchByte(data[1])) {
if(!isFrameSync(data)) {
debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch.");
return;
}
@ -197,10 +197,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->version = Version2;
else if(versionBits == 3)
d->version = Version1;
else {
debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
else
return;
}
// Set the MPEG layer
@ -212,10 +210,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->layer = 2;
else if(layerBits == 3)
d->layer = 1;
else {
debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
else
return;
}
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];
if(d->bitrate == 0) {
debug("MPEG::Header::parse() -- Invalid bit rate.");
if(d->bitrate == 0)
return;
}
// Set the sample rate
@ -264,7 +258,6 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->sampleRate = sampleRates[d->version][samplerateIndex];
if(d->sampleRate == 0) {
debug("MPEG::Header::parse() -- Invalid sample rate.");
return;
}
@ -311,20 +304,16 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
file->seek(offset + d->frameLength);
const ByteVector nextData = file->readBlock(4);
if(nextData.size() < 4) {
debug("MPEG::Header::parse() -- Could not read the next frame header.");
if(nextData.size() < 4)
return;
}
const unsigned int HeaderMask = 0xfffe0c00;
const unsigned int header = data.toUInt(0, true) & HeaderMask;
const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask;
if(header != nextHeader) {
debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
if(header != nextHeader)
return;
}
}
// 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.
long firstFrameOffset = file->firstFrameOffset();
const long firstFrameOffset = file->firstFrameOffset();
if(firstFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
return;
}
Header firstHeader(file, firstFrameOffset);
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);
}
const Header firstHeader(file, firstFrameOffset, false);
// Check for a VBR header that will help us in gathering information about a
// VBR stream.
@ -207,24 +197,13 @@ void MPEG::Properties::read(File *file)
// Look for the last MPEG audio frame to calculate the stream length.
long lastFrameOffset = file->lastFrameOffset();
const long lastFrameOffset = file->lastFrameOffset();
if(lastFrameOffset < 0) {
debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
return;
}
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 Header lastHeader(file, lastFrameOffset, false);
const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
if(streamLength > 0)
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
* 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.
*
* \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)
{
// 0xFF is possible in theory, but it's very unlikely be a header.
return (byte != 0xFF && ((byte & 0xE0) == 0xE0));
const unsigned char b1 = bytes[offset + 0];
const unsigned char b2 = bytes[offset + 1];
return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0);
}
}

View File

@ -27,6 +27,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <xiphcomment.h>
#include "oggflacfile.h"
@ -65,22 +66,36 @@ public:
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
////////////////////////////////////////////////////////////////////////////////
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())
read(readProperties, propertiesStyle);
}
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())
read(readProperties, propertiesStyle);
}
@ -172,7 +187,7 @@ void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle properties
if(d->hasXiphComment)
d->comment = new Ogg::XiphComment(xiphCommentData());
else
d->comment = new Ogg::XiphComment;
d->comment = new Ogg::XiphComment();
if(readProperties)

View File

@ -127,9 +127,6 @@ namespace TagLib {
/*!
* Save the file. This will primarily save and update the XiphComment.
* 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();
@ -146,6 +143,14 @@ namespace TagLib {
*/
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:
File(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();
packets[i - firstPage->firstPacketIndex()] = packet;
if(firstPage != lastPage && lastPage->packetCount() > 2) {
if(firstPage != lastPage && lastPage->packetCount() > 1) {
ByteVectorList lastPagePackets = lastPage->packets();
lastPagePackets.erase(lastPagePackets.begin());
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;
// 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) {
size_t totalSize = packets.size();
size_t tableSize = 0;
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;
}

View File

@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "opusfile.h"
@ -53,6 +54,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////
@ -101,7 +114,7 @@ Opus::Properties *Opus::File::audioProperties() const
bool Opus::File::save()
{
if(!d->comment)
d->comment = new Ogg::XiphComment;
d->comment = new Ogg::XiphComment();
setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false));

View File

@ -110,12 +110,18 @@ namespace TagLib {
* Save the file.
*
* 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();
/*!
* 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:
File(const File &);
File &operator=(const File &);

View File

@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "speexfile.h"
@ -53,6 +54,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////
@ -101,7 +114,7 @@ Speex::Properties *Speex::File::audioProperties() const
bool Speex::File::save()
{
if(!d->comment)
d->comment = new Ogg::XiphComment;
d->comment = new Ogg::XiphComment();
setPacket(1, d->comment->render());

View File

@ -110,12 +110,18 @@ namespace TagLib {
* Save the file.
*
* 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();
/*!
* 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:
File(const File &);
File &operator=(const File &);

View File

@ -28,10 +28,10 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "vorbisfile.h"
using namespace TagLib;
class Vorbis::File::FilePrivate
@ -59,6 +59,18 @@ namespace TagLib {
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
////////////////////////////////////////////////////////////////////////////////
@ -109,7 +121,7 @@ bool Vorbis::File::save()
ByteVector v(vorbisCommentHeaderID);
if(!d->comment)
d->comment = new Ogg::XiphComment;
d->comment = new Ogg::XiphComment();
v.append(d->comment->render());
setPacket(1, v);

View File

@ -118,12 +118,17 @@ namespace TagLib {
* Save the file.
*
* 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();
/*!
* 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:
File(const File &);
File &operator=(const File &);

View File

@ -261,10 +261,14 @@ bool Ogg::XiphComment::checkKey(const String &key)
{
if(key.size() < 1)
return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
// A key may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
for(String::ConstIterator it = key.begin(); it != key.end(); it++) {
if(*it < 0x20 || *it > 0x7D || *it == 0x3D)
return false;
}
return true;
}
@ -275,11 +279,18 @@ String Ogg::XiphComment::vendorID() const
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)
removeFields(key.upper());
removeFields(upperKey);
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)
@ -436,85 +447,69 @@ void Ogg::XiphComment::parse(const ByteVector &data)
const unsigned int commentLength = data.toUInt(pos, false);
pos += 4;
ByteVector entry = data.mid(pos, commentLength);
const ByteVector entry = data.mid(pos, commentLength);
pos += commentLength;
// Don't go past data end
if(pos > data.size())
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
int sep = entry.find('=');
const int sep = entry.find('=');
if(sep < 1) {
debug("Discarding invalid comment field.");
debug("Ogg::XiphComment::parse() - Discarding a field. Separator not found.");
continue;
}
// Parse key and value
String key = String(entry.mid(0, sep), String::UTF8);
String value = String(entry.mid(sep + 1), String::UTF8);
addField(key, value, false);
// Parse the key
const String key = String(entry.mid(0, sep), String::UTF8).upper();
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 <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "aifffile.h"
@ -53,6 +54,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////

View File

@ -126,6 +126,14 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

@ -95,13 +95,13 @@ unsigned int RIFF::File::riffSize() 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
{
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;
}
@ -111,7 +111,7 @@ unsigned int RIFF::File::chunkDataSize(unsigned int i) const
unsigned int RIFF::File::chunkOffset(unsigned int i) const
{
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;
}
@ -161,19 +161,19 @@ void RIFF::File::setChunkData(unsigned int i, const ByteVector &data)
std::vector<Chunk>::iterator it = d->chunks.begin();
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);
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
for(++it; it != d->chunks.end(); ++it)
it->offset += diff;
it->offset += static_cast<int>(diff);
// 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)
{
if(d->chunks.size() == 0) {
if(d->chunks.empty()) {
debug("RIFF::File::setChunkData - No valid chunks found.");
return;
}
@ -269,7 +269,7 @@ void RIFF::File::removeChunk(unsigned int i)
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)
removeChunk(i);
}
@ -292,7 +292,6 @@ void RIFF::File::read()
d->size = readBlock(4).toUInt(bigEndian);
offset += 8;
seek(offset);
// + 8: chunk header at least, fix for additional junk bytes
while(offset + 8 <= length()) {
@ -307,28 +306,24 @@ void RIFF::File::read()
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)");
setValid(false);
break;
}
offset += 8;
Chunk chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = offset;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = offset + 8;
chunk.padding = 0;
offset += chunk.size;
seek(offset);
offset = chunk.offset + chunk.size;
// Check padding
chunk.padding = 0;
if(offset & 1) {
seek(offset);
const ByteVector iByte = readBlock(1);
if(iByte.size() == 1 && iByte[0] == '\0') {
chunk.padding = 1;

View File

@ -23,10 +23,11 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "tbytevector.h"
#include "tdebug.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include <tbytevector.h>
#include <tdebug.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "wavfile.h"
#include "id3v2tag.h"
@ -60,6 +61,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////

View File

@ -175,6 +175,15 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

@ -64,7 +64,7 @@ public:
S3M::Properties::Properties(AudioProperties::ReadStyle 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 */
#ifndef TAGLIB_TAGLIB_CONFIG_H
#define TAGLIB_TAGLIB_CONFIG_H
/* These values are no longer used. This file is present only for compatibility reasons. */
#define TAGLIB_WITH_ASF 1
#define TAGLIB_WITH_MP4 1
#endif

View File

@ -79,10 +79,9 @@ public:
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[1] = second;
d->tags[2] = third;

View File

@ -77,3 +77,29 @@ long Utils::findAPE(File *file, long id3v1Location)
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
#include <tbytevector.h>
namespace TagLib {
class File;
class IOStream;
namespace Utils {
@ -41,6 +44,9 @@ namespace TagLib {
long findID3v2(File *file);
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_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__)
#define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"")
@ -115,7 +115,7 @@ namespace 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.
*
* 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.
*
* - <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>
*
* \author Scott Wheeler <wheeler@kde.org> et al.
*
* \author <a href="https://github.com/taglib/taglib/blob/master/AUTHORS">TagLib authors</a>.
*/
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -69,39 +69,38 @@ using namespace TagLib;
class File::FilePrivate
{
public:
FilePrivate(IOStream *stream, bool owner);
FilePrivate(IOStream *stream, bool owner) :
stream(stream),
streamOwner(owner),
valid(true) {}
~FilePrivate()
{
if(streamOwner)
delete stream;
}
IOStream *stream;
bool streamOwner;
bool valid;
};
File::FilePrivate::FilePrivate(IOStream *stream, bool owner) :
stream(stream),
streamOwner(owner),
valid(true)
{
}
////////////////////////////////////////////////////////////////////////////////
// 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()
{
if(d->stream && d->streamOwner)
delete d->stream;
delete d;
}

View File

@ -86,7 +86,7 @@ namespace TagLib {
* format, the returned map's unsupportedData() list will contain one entry identifying
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
* 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).
* 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);
if(!path.wstr().empty())
return CreateFileW(path.wstr().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
else if(!path.str().empty())
return CreateFileA(path.str().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
else
return InvalidFileHandle;
#if defined (PLATFORM_WINRT)
return CreateFile2(path.wstr().c_str(), access, FILE_SHARE_READ, OPEN_EXISTING, NULL);
#else
return CreateFileW(path.wstr().c_str(), access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
#endif
}
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.
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);
readPosition += bufferLength;
@ -305,10 +304,10 @@ void FileStream::removeBlock(unsigned long start, unsigned long length)
ByteVector buffer(static_cast<unsigned int>(bufferLength));
for(size_t bytesRead = -1; bytesRead != 0;)
for(unsigned int bytesRead = -1; bytesRead != 0;)
{
seek(readPosition);
bytesRead = readFile(d->file, buffer);
bytesRead = static_cast<unsigned int>(readFile(d->file, buffer));
readPosition += bytesRead;
// 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
DWORD whence;
switch(p) {
case Beginning:
whence = FILE_BEGIN;
break;
case Current:
whence = FILE_CURRENT;
break;
case End:
whence = FILE_END;
break;
default:
if(p != Beginning && p != Current && p != End) {
debug("FileStream::seek() -- Invalid Position value.");
return;
}
SetLastError(NO_ERROR);
SetFilePointer(d->file, offset, NULL, whence);
LARGE_INTEGER liOffset;
liOffset.QuadPart = offset;
const int lastError = GetLastError();
if(lastError != NO_ERROR && lastError != ERROR_NEGATIVE_SEEK)
if(!SetFilePointerEx(d->file, liOffset, NULL, static_cast<DWORD>(p))) {
debug("FileStream::seek() -- Failed to set the file pointer.");
}
#else
@ -410,10 +398,11 @@ long FileStream::tell() const
{
#ifdef _WIN32
SetLastError(NO_ERROR);
const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT);
if(GetLastError() == NO_ERROR) {
return static_cast<long>(position);
const LARGE_INTEGER zero = {};
LARGE_INTEGER position;
if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) && position.QuadPart <= LONG_MAX) {
return static_cast<long>(position.QuadPart);
}
else {
debug("FileStream::tell() -- Failed to get the file pointer.");
@ -436,10 +425,10 @@ long FileStream::length()
#ifdef _WIN32
SetLastError(NO_ERROR);
const DWORD fileSize = GetFileSize(d->file, NULL);
if(GetLastError() == NO_ERROR) {
return static_cast<long>(fileSize);
LARGE_INTEGER fileSize;
if(GetFileSizeEx(d->file, &fileSize) && fileSize.QuadPart <= LONG_MAX) {
return static_cast<long>(fileSize.QuadPart);
}
else {
debug("FileStream::length() -- Failed to get the file size.");
@ -472,9 +461,7 @@ void FileStream::truncate(long length)
seek(length);
SetLastError(NO_ERROR);
SetEndOfFile(d->file);
if(GetLastError() != NO_ERROR) {
if(!SetEndOfFile(d->file)) {
debug("FileStream::truncate() -- Failed to truncate the file.");
}

View File

@ -23,73 +23,49 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef _WIN32
# include <windows.h>
# include <tstring.h>
#endif
#include "tiostream.h"
using namespace TagLib;
#ifdef _WIN32
# include "tstring.h"
# include "tdebug.h"
# include <windows.h>
namespace
{
// Check if the running system has CreateFileW() function.
// Windows9x systems don't have CreateFileW() or can't accept Unicode file names.
bool supportsUnicode()
std::wstring ansiToUnicode(const char *str)
{
#ifdef UNICODE
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);
const int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
if(len == 0)
return std::string();
return std::wstring();
std::string str(len, '\0');
WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL);
std::wstring wstr(len - 1, L'\0');
MultiByteToWideChar(CP_ACP, 0, str, -1, &wstr[0], len);
return str;
return wstr;
}
}
// If WinNT, stores a Unicode string into m_wname directly.
// If Win9x, converts and stores it into m_name to avoid calling Unicode version functions.
// m_name is no longer used, but kept for backward compatibility.
FileName::FileName(const wchar_t *name)
: m_name (SystemSupportsUnicode ? "" : unicodeToAnsi(name))
, m_wname(SystemSupportsUnicode ? name : L"")
FileName::FileName(const wchar_t *name) :
m_name(),
m_wname(name)
{
}
FileName::FileName(const char *name)
: m_name(name)
FileName::FileName(const char *name) :
m_name(),
m_wname(ansiToUnicode(name))
{
}
FileName::FileName(const FileName &name)
: m_name (name.m_name)
, m_wname(name.m_wname)
FileName::FileName(const FileName &name) :
m_name(),
m_wname(name.m_wname)
{
}
@ -115,25 +91,9 @@ const std::string &FileName::str() const
String FileName::toString() const
{
if(!m_wname.empty()) {
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();
}
return String(m_wname.c_str());
}
#endif // _WIN32
////////////////////////////////////////////////////////////////////////////////

View File

@ -229,6 +229,11 @@ namespace TagLib {
*/
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
* the same.

View File

@ -89,9 +89,9 @@ public:
////////////////////////////////////////////////////////////////////////////////
template <class T>
List<T>::List()
List<T>::List() :
d(new ListPrivate<T>())
{
d = new ListPrivate<T>;
}
template <class T>
@ -196,7 +196,7 @@ List<T> &List<T>::clear()
template <class T>
unsigned int List<T>::size() const
{
return d->list.size();
return static_cast<unsigned int>(d->list.size());
}
template <class T>
@ -283,16 +283,18 @@ const T &List<T>::operator[](unsigned int i) const
template <class T>
List<T> &List<T>::operator=(const List<T> &l)
{
if(&l == this)
return *this;
if(d->deref())
delete d;
d = l.d;
d->ref();
List<T>(l).swap(*this);
return *this;
}
template <class T>
void List<T>::swap(List<T> &l)
{
using std::swap;
swap(d, l.d);
}
template <class T>
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);
/*!
* Exchanges the content of this map by the content of \a m.
*/
void swap(Map<Key, T> &m);
protected:
/*
* 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>
Map<Key, T>::Map()
Map<Key, T>::Map() :
d(new MapPrivate<Key, T>())
{
d = new MapPrivate<Key, 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>
unsigned int Map<Key, T>::size() const
{
return d->map.size();
return static_cast<unsigned int>(d->map.size());
}
template <class Key, class T>
@ -171,16 +171,18 @@ T &Map<Key, T>::operator[](const Key &key)
template <class Key, class T>
Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m)
{
if(&m == this)
return *this;
if(d->deref())
delete(d);
d = m.d;
d->ref();
Map<Key, T>(m).swap(*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
////////////////////////////////////////////////////////////////////////////////

View File

@ -31,14 +31,9 @@
#if defined(HAVE_STD_ATOMIC)
# include <atomic>
# define ATOMIC_INT std::atomic<unsigned int>
# define ATOMIC_INC(x) x.fetch_add(1)
# define ATOMIC_DEC(x) (x.fetch_sub(1) - 1)
#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)
# define ATOMIC_INT std::atomic_int
# define ATOMIC_INC(x) (++x)
# define ATOMIC_DEC(x) (--x)
#elif defined(HAVE_GCC_ATOMIC)
# define ATOMIC_INT int
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)

View File

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

View File

@ -23,19 +23,10 @@
* 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 <climits>
#include <cstdio>
#include <cstring>
#ifdef _WIN32
# include <windows.h>
#else
# include "unicode.h"
#endif
#include <utf8-cpp/checked.h>
#include <tdebug.h>
#include <tstringlist.h>
@ -48,70 +39,6 @@ namespace
{
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.
String::Type wcharByteOrder()
{
@ -137,28 +64,61 @@ namespace
{
data.resize(length);
if(length > 0) {
const size_t len = UTF8toUTF16(s, length, &data[0], data.size());
data.resize(len);
try {
const std::wstring::iterator dstEnd = utf8::utf8to16(s, s + length, data.begin());
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
// 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;
if(t == String::UTF16) {
if(length >= 1 && s[0] == 0xfeff)
swap = false; // Same as CPU endian. No need to swap bytes.
else if(length >= 1 && s[0] == 0xfffe)
swap = true; // Not same as CPU endian. Need to swap bytes.
else {
debug("String::copyFromUTF16() - Invalid UTF16 string.");
if(length < 1) {
debug("String::copyFromUTF16() - Invalid UTF16 string. Too short to have a BOM.");
return;
}
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;
}
s++;
length--;
}
else {
@ -166,57 +126,12 @@ namespace
}
data.resize(length);
if(length > 0) {
if(swap) {
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);
for(size_t i = 0; i < length; ++i) {
const unsigned short c = nextUTF16(&s);
if(swap)
c = Utils::byteSwap(c);
data[i] = static_cast<wchar_t>(c);
s += 2;
data[i] = Utils::byteSwap(c);
else
data[i] = c;
}
}
}
@ -229,10 +144,6 @@ public:
StringPrivate() :
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.
*/
@ -332,9 +243,13 @@ String::String(wchar_t 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.");
}
}
@ -350,7 +265,7 @@ String::String(const ByteVector &v, Type t) :
else if(t == UTF8)
copyFromUTF8(d->data, v.data(), v.size());
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.
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
{
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
{
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
@ -445,7 +360,10 @@ bool String::startsWith(const String &s) 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)
@ -464,12 +382,11 @@ String & String::clear()
String String::upper() const
{
String s;
s.d->data.reserve(size());
static int shift = 'A' - 'a';
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) {
for(ConstIterator it = begin(); it != end(); ++it) {
if(*it >= 'a' && *it <= 'z')
s.d->data.push_back(*it + shift);
s.d->data.push_back(*it + 'A' - 'a');
else
s.d->data.push_back(*it);
}
@ -479,7 +396,7 @@ String String::upper() const
unsigned int String::size() const
{
return d->data.size();
return static_cast<unsigned int>(d->data.size());
}
unsigned int String::length() const
@ -506,25 +423,27 @@ ByteVector String::data(Type t) const
ByteVector v(size(), 0);
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);
return v;
}
case UTF8:
if(!d->data.empty())
{
ByteVector v(size() * 4 + 1, 0);
ByteVector v(size() * 4, 0);
const size_t len = UTF16toUTF8(
d->data.c_str(), d->data.size(), v.data(), v.size());
v.resize(len);
try {
const ByteVector::Iterator dstEnd = utf8::utf16to8(begin(), end(), v.begin());
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;
}
else {
return ByteVector();
}
case UTF16:
{
ByteVector v(2 + size() * 2, 0);
@ -535,7 +454,7 @@ ByteVector String::data(Type t) const
*p++ = '\xff';
*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 >> 8);
}
@ -547,7 +466,7 @@ ByteVector String::data(Type t) const
ByteVector v(size() * 2, 0);
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 & 0xff);
}
@ -559,7 +478,7 @@ ByteVector String::data(Type t) const
ByteVector v(size() * 2, 0);
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 >> 8);
}
@ -604,12 +523,12 @@ String String::stripWhiteSpace() const
return String();
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
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
for(ConstIterator it = begin(); it != end(); ++it) {
if(*it >= 256)
return false;
}
@ -618,7 +537,7 @@ bool String::isLatin1() 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)
return false;
}
@ -787,6 +706,12 @@ void String::detach()
if(d->count() > 1)
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();
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;
StringPrivate *d;
};

View File

@ -34,9 +34,7 @@
# include <config.h>
#endif
#if defined(HAVE_BOOST_BYTESWAP)
# include <boost/endian/conversion.hpp>
#elif defined(HAVE_MSC_BYTESWAP)
#if defined(HAVE_MSC_BYTESWAP)
# include <stdlib.h>
#elif defined(HAVE_GLIBC_BYTESWAP)
# include <byteswap.h>
@ -63,11 +61,7 @@ namespace TagLib
*/
inline unsigned short byteSwap(unsigned short x)
{
#if defined(HAVE_BOOST_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint16_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
#if defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap16(x);
@ -99,11 +93,7 @@ namespace TagLib
*/
inline unsigned int byteSwap(unsigned int x)
{
#if defined(HAVE_BOOST_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint32_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
#if defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap32(x);
@ -138,11 +128,7 @@ namespace TagLib
*/
inline unsigned long long byteSwap(unsigned long long x)
{
#if defined(HAVE_BOOST_BYTESWAP)
return boost::endian::endian_reverse(static_cast<uint64_t>(x));
#elif defined(HAVE_GCC_BYTESWAP)
#if defined(HAVE_GCC_BYTESWAP)
return __builtin_bswap64(x);
@ -221,23 +207,6 @@ namespace TagLib
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.
*/
@ -250,7 +219,7 @@ namespace TagLib
};
/*!
* Returns the integer byte order of the system.
* Returns the byte order of the system.
*/
inline ByteOrder systemByteOrder()
{
@ -265,26 +234,6 @@ namespace TagLib
else
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>
#endif
#if defined(HAVE_ZLIB)
#ifdef HAVE_ZLIB
# include <zlib.h>
#elif defined(HAVE_BOOST_ZLIB)
# include <boost/iostreams/filtering_streambuf.hpp>
# include <boost/iostreams/filter/zlib.hpp>
# include <tstring.h>
# include <tdebug.h>
#endif
#include <tstring.h>
#include <tdebug.h>
#include "tzlib.h"
using namespace TagLib;
bool zlib::isAvailable()
{
#if defined(HAVE_ZLIB) || defined(HAVE_BOOST_ZLIB)
#ifdef HAVE_ZLIB
return true;
@ -56,7 +52,7 @@ bool zlib::isAvailable()
ByteVector zlib::decompress(const ByteVector &data)
{
#if defined(HAVE_ZLIB)
#ifdef HAVE_ZLIB
z_stream stream = {};
@ -102,38 +98,6 @@ ByteVector zlib::decompress(const ByteVector &data)
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
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;
};
////////////////////////////////////////////////////////////////////////////////
// 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
////////////////////////////////////////////////////////////////////////////////

View File

@ -235,6 +235,15 @@ namespace TagLib {
*/
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:
File(const File &);
File &operator=(const File &);

View File

@ -71,6 +71,18 @@ public:
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
////////////////////////////////////////////////////////////////////////////////

View File

@ -200,6 +200,14 @@ namespace TagLib {
*/
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:
File(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);
READ_ASSERT(count == std::min(instrumentHeaderSize, (unsigned long)instrument.size() + 4));
unsigned long sampleHeaderSize = 0;
long offset = 0;
if(sampleCount > 0) {
unsigned long sampleHeaderSize = 0;
sumSampleCount += sampleCount;
// wouldn't know which header size to assume otherwise:
READ_ASSERT(instrumentHeaderSize >= count + 4 && readU32L(sampleHeaderSize));

View File

@ -60,7 +60,7 @@ public:
XM::Properties::Properties(AudioProperties::ReadStyle 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