Use a newer taglib (taglib master at 89fcab5669013bd46b0ef7b7f6efbb8a21cd1ceb)

Fixes issue 3446
This commit is contained in:
David Sansome 2013-08-18 12:39:33 +10:00
parent 2c0b505b7b
commit 4411b25e86
111 changed files with 4770 additions and 1585 deletions

View File

@ -7,6 +7,15 @@ math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSI
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(TestBigEndian)
test_big_endian(IS_BIG_ENDIAN)
if(NOT IS_BIG_ENDIAN)
add_definitions(-DSYSTEM_BYTEORDER=1)
else()
add_definitions(-DSYSTEM_BYTEORDER=2)
endif()
configure_file(taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@ -21,6 +30,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/mp4
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
@ -60,6 +70,8 @@ set(tag_HDRS
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpropertymap.h
toolkit/trefcounter.h
toolkit/tdebuglistener.h
mpeg/mpegfile.h
mpeg/mpegproperties.h
mpeg/mpegheader.h
@ -94,6 +106,8 @@ set(tag_HDRS
ogg/flac/oggflacfile.h
ogg/speex/speexfile.h
ogg/speex/speexproperties.h
ogg/opus/opusfile.h
ogg/opus/opusproperties.h
flac/flacfile.h
flac/flacpicture.h
flac/flacproperties.h
@ -114,6 +128,7 @@ set(tag_HDRS
riff/aiff/aiffproperties.h
riff/wav/wavfile.h
riff/wav/wavproperties.h
riff/wav/infotag.h
asf/asffile.h
asf/asfproperties.h
asf/asftag.h
@ -230,6 +245,11 @@ set(speex_SRCS
ogg/speex/speexproperties.cpp
)
set(opus_SRCS
ogg/opus/opusfile.cpp
ogg/opus/opusproperties.cpp
)
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
@ -255,6 +275,7 @@ set(aiff_SRCS
set(wav_SRCS
riff/wav/wavfile.cpp
riff/wav/wavproperties.cpp
riff/wav/infotag.cpp
)
set(mod_SRCS
@ -290,6 +311,8 @@ set(toolkit_SRCS
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
toolkit/unicode.cpp
)
@ -297,7 +320,7 @@ set(tag_LIB_SRCS
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_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}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp

View File

@ -47,7 +47,7 @@ using namespace TagLib;
namespace
{
enum { ApeAPEIndex, ApeID3v1Index };
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
}
class APE::File::FilePrivate
@ -90,14 +90,16 @@ APE::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
APE::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
APE::File::~File()
@ -129,12 +131,9 @@ void APE::File::removeUnsupportedProperties(const StringList &properties)
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
else
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const
@ -236,6 +235,16 @@ void APE::File::strip(int tags)
}
}
bool APE::File::hasAPETag() const
{
return d->hasAPE;
}
bool APE::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

View File

@ -59,7 +59,7 @@ namespace TagLib {
//! An implementation of TagLib::File with APE specific methods
/*!
* This implements and provides an interface APE WavPack files to the
* This implements and provides an interface for APE files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to APE files.
@ -84,20 +84,22 @@ namespace TagLib {
};
/*!
* Contructs an WavPack file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an APE file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an WavPack file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an APE file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -128,10 +130,11 @@ namespace TagLib {
/*!
* Implements the unified property interface -- import function.
* As for the export, only one tag is taken into account. If the file
* has no tag at all, APE will be created.
* Creates an APEv2 tag if necessary. A pontentially existing ID3v1
* tag will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
@ -149,27 +152,38 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
* new ID3v1 tag will be placed after it.
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist.
* an APE tag if one does not exist and returns a valid pointer.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasAPETag()
*/
APE::Tag *APETag(bool create = false);
@ -183,6 +197,20 @@ namespace TagLib {
*/
void strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -177,19 +177,19 @@ void APE::Footer::parse(const ByteVector &data)
// Read the version number
d->version = data.mid(8, 4).toUInt(false);
d->version = data.toUInt(8, false);
// Read the tag size
d->tagSize = data.mid(12, 4).toUInt(false);
d->tagSize = data.toUInt(12, false);
// Read the item count
d->itemCount = data.mid(16, 4).toUInt(false);
d->itemCount = data.toUInt(16, false);
// Read the flags
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false)));
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];

View File

@ -125,6 +125,7 @@ void APE::Item::setBinaryData(const ByteVector &value)
{
d->type = Binary;
d->value = value;
d->text.clear();
}
ByteVector APE::Item::value() const
@ -137,31 +138,35 @@ ByteVector APE::Item::value() const
void APE::Item::setKey(const String &key)
{
d->key = key;
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->type = Text;
d->text = value;
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::setValues(const StringList &value)
{
d->type = Text;
d->text = value;
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::appendValue(const String &value)
{
d->type = Text;
d->text.append(value);
d->type = Text;
d->text.append(value);
d->value.clear();
}
void APE::Item::appendValues(const StringList &values)
{
d->type = Text;
d->text.append(values);
d->type = Text;
d->text.append(values);
d->value.clear();
}
int APE::Item::size() const
@ -200,7 +205,10 @@ StringList APE::Item::values() const
String APE::Item::toString() const
{
return isEmpty() ? String::null : d->text.front();
if(d->type == Text && !isEmpty())
return d->text.front();
else
return String::null;
}
bool APE::Item::isEmpty() const
@ -229,18 +237,20 @@ void APE::Item::parse(const ByteVector &data)
return;
}
uint valueLength = data.mid(0, 4).toUInt(false);
uint flags = data.mid(4, 4).toUInt(false);
const uint valueLength = data.toUInt(0, false);
const uint flags = data.toUInt(4, false);
d->key = String(data.mid(8), String::UTF8);
d->value = data.mid(8 + d->key.size() + 1, valueLength);
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(Text == d->type)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
if(Text == d->type)
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else
d->value = value;
}
ByteVector APE::Item::render() const

View File

@ -97,7 +97,7 @@ namespace TagLib {
/*!
* Returns the binary value.
* If the item type is not \a Binary, the returned contents are undefined
* If the item type is not \a Binary, always returns an empty ByteVector.
*/
ByteVector binaryData() const;
@ -152,8 +152,9 @@ namespace TagLib {
int size() const;
/*!
* Returns the value as a single string. In case of multiple strings,
* the first is returned.
* Returns the value as a single string. In case of multiple strings,
* the first is returned. If the data type is not \a Text, always returns
* an empty String.
*/
String toString() const;
@ -163,7 +164,8 @@ namespace TagLib {
#endif
/*!
* Returns the list of text values.
* Returns the list of text values. If the data type is not \a Text, always
* returns an empty StringList.
*/
StringList values() const;

View File

@ -125,10 +125,10 @@ void APE::Properties::read()
// Then we read the header common for all versions of APE
d->file->seek(offset);
ByteVector commonHeader=d->file->readBlock(6);
ByteVector commonHeader = d->file->readBlock(6);
if(!commonHeader.startsWith("MAC "))
return;
d->version = commonHeader.mid(4).toUInt(false);
d->version = commonHeader.toUShort(4, false);
if(d->version >= 3980) {
analyzeCurrent();
@ -182,7 +182,7 @@ void APE::Properties::analyzeCurrent()
// Read the descriptor
d->file->seek(2, File::Current);
ByteVector descriptor = d->file->readBlock(44);
uint descriptorBytes = descriptor.mid(0,4).toUInt(false);
const uint descriptorBytes = descriptor.toUInt(0, false);
if ((descriptorBytes - 52) > 0)
d->file->seek(descriptorBytes - 52, File::Current);
@ -191,14 +191,14 @@ void APE::Properties::analyzeCurrent()
ByteVector header = d->file->readBlock(24);
// Get the APE info
d->channels = header.mid(18, 2).toShort(false);
d->sampleRate = header.mid(20, 4).toUInt(false);
d->bitsPerSample = header.mid(16, 2).toShort(false);
d->channels = header.toShort(18, false);
d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.toShort(16, false);
//d->compressionLevel =
uint totalFrames = header.mid(12, 4).toUInt(false);
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
const uint totalFrames = header.toUInt(12, false);
const uint blocksPerFrame = header.toUInt(4, false);
const uint finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
@ -207,13 +207,13 @@ void APE::Properties::analyzeCurrent()
void APE::Properties::analyzeOld()
{
ByteVector header = d->file->readBlock(26);
uint totalFrames = header.mid(18, 4).toUInt(false);
const uint totalFrames = header.toUInt(18, false);
// Fail on 0 length APE files (catches non-finalized APE files)
if(totalFrames == 0)
return;
short compressionLevel = header.mid(0, 2).toShort(false);
const short compressionLevel = header.toShort(0, false);
uint blocksPerFrame;
if(d->version >= 3950)
blocksPerFrame = 73728 * 4;
@ -221,10 +221,11 @@ void APE::Properties::analyzeOld()
blocksPerFrame = 73728;
else
blocksPerFrame = 9216;
d->channels = header.mid(4, 2).toShort(false);
d->sampleRate = header.mid(6, 4).toUInt(false);
uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->channels = header.toShort(4, false);
d->sampleRate = header.toUInt(6, false);
const uint finalFrameBlocks = header.toUInt(22, false);
const uint totalBlocks
= totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "asfattribute.h"
#include "asffile.h"

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
#include "asffile.h"
#include "asftag.h"
@ -186,7 +183,8 @@ ByteVector ASF::File::FilePropertiesObject::guid()
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
{
BaseObject::parse(file, size);
file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
file->d->properties->setLength(
(int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L));
}
ByteVector ASF::File::StreamPropertiesObject::guid()
@ -197,9 +195,9 @@ ByteVector ASF::File::StreamPropertiesObject::guid()
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
{
BaseObject::parse(file, size);
file->d->properties->setChannels(data.mid(56, 2).toShort(false));
file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
file->d->properties->setChannels(data.toShort(56, false));
file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000);
}
ByteVector ASF::File::ContentDescriptionObject::guid()
@ -372,14 +370,16 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper
: TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
ASF::File::~File()
@ -401,6 +401,21 @@ ASF::Tag *ASF::File::tag() const
return d->tag;
}
PropertyMap ASF::File::properties() const
{
return d->tag->properties();
}
void ASF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties;

View File

@ -48,30 +48,27 @@ namespace TagLib {
public:
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@ -90,6 +87,22 @@ namespace TagLib {
*/
virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the ASF audio properties for this file.
*/
@ -103,7 +116,6 @@ namespace TagLib {
virtual bool save();
private:
int readBYTE(bool *ok = 0);
int readWORD(bool *ok = 0);
unsigned int readDWORD(bool *ok = 0);

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "asfattribute.h"
#include "asffile.h"
#include "asfpicture.h"
@ -149,7 +146,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
return;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
uint dataLen = bytes.mid(pos, 4).toUInt(false); pos+=4;
const uint dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include "asfproperties.h"

View File

@ -23,10 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tpropertymap.h>
#include "asftag.h"
using namespace TagLib;
@ -196,3 +193,162 @@ bool ASF::Tag::isEmpty() const
d->attributeListMap.isEmpty();
}
static const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "WM/Composer", "COMPOSER" },
{ "WM/Writer", "WRITER" },
{ "WM/Conductor", "CONDUCTOR" },
{ "WM/ModifiedBy", "REMIXER" },
{ "WM/Year", "DATE" },
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
{ "WM/Producer", "PRODUCER" },
{ "WM/ContentGroupDescription", "GROUPING" },
{ "WM/SubTitle", "SUBTITLE" },
{ "WM/SetSubTitle", "DISCSUBTITLE" },
{ "WM/TrackNumber", "TRACKNUMBER" },
{ "WM/PartOfSet", "DISCNUMBER" },
{ "WM/Genre", "GENRE" },
{ "WM/BeatsPerMinute", "BPM" },
{ "WM/Mood", "MOOD" },
{ "WM/ISRC", "ISRC" },
{ "WM/Lyrics", "LYRICS" },
{ "WM/Media", "MEDIA" },
{ "WM/Publisher", "LABEL" },
{ "WM/CatalogNo", "CATALOGNUMBER" },
{ "WM/Barcode", "BARCODE" },
{ "WM/EncodedBy", "ENCODEDBY" },
{ "WM/AlbumSortOrder", "ALBUMSORT" },
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
{ "WM/ArtistSortOrder", "ARTISTSORT" },
{ "WM/TitleSortOrder", "TITLESORT" },
{ "WM/Script", "SCRIPT" },
{ "WM/Language", "LANGUAGE" },
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
{ "MusicIP/PUID", "MUSICIP_PUID" },
{ "Acoustid/Id", "ACOUSTID_ID" },
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
};
PropertyMap ASF::Tag::properties() const
{
static Map<String, String> keyMap;
if(keyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
}
}
PropertyMap props;
if(!d->title.isEmpty()) {
props["TITLE"] = d->title;
}
if(!d->artist.isEmpty()) {
props["ARTIST"] = d->artist;
}
if(!d->copyright.isEmpty()) {
props["COPYRIGHT"] = d->copyright;
}
if(!d->comment.isEmpty()) {
props["COMMENT"] = d->comment;
}
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
for(; it != d->attributeListMap.end(); ++it) {
if(keyMap.contains(it->first)) {
String key = keyMap[it->first];
AttributeList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
if(key == "TRACKNUMBER") {
if(it2->type() == ASF::Attribute::DWordType)
props.insert(key, String::number(it2->toUInt()));
else
props.insert(key, it2->toString());
}
else {
props.insert(key, it2->toString());
}
}
}
else {
props.unsupportedData().append(it->first);
}
}
return props;
}
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
{
StringList::ConstIterator it = props.begin();
for(; it != props.end(); ++it)
d->attributeListMap.erase(*it);
}
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
}
}
PropertyMap origProps = properties();
PropertyMap::ConstIterator it = origProps.begin();
for(; it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
if(it->first == "TITLE") {
d->title = String::null;
}
else if(it->first == "ARTIST") {
d->artist = String::null;
}
else if(it->first == "COMMENT") {
d->comment = String::null;
}
else if(it->first == "COPYRIGHT") {
d->copyright = String::null;
}
else {
d->attributeListMap.erase(reverseKeyMap[it->first]);
}
}
}
PropertyMap ignoredProps;
it = props.begin();
for(; it != props.end(); ++it) {
if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first];
removeItem(name);
StringList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
addAttribute(name, *it2);
}
}
else if(it->first == "TITLE") {
d->title = it->second.toString();
}
else if(it->first == "ARTIST") {
d->artist = it->second.toString();
}
else if(it->first == "COMMENT") {
d->comment = it->second.toString();
}
else if(it->first == "COPYRIGHT") {
d->copyright = it->second.toString();
}
else {
ignoredProps.insert(it->first, it->second);
}
}
return ignoredProps;
}

View File

@ -176,6 +176,10 @@ namespace TagLib {
*/
void addAttribute(const String &name, const Attribute &attribute);
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
private:
class TagPrivate;

View File

@ -27,13 +27,10 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tfile.h>
#include <tstring.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "fileref.h"
#include "asffile.h"
@ -45,6 +42,7 @@
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "opusfile.h"
#include "trueaudiofile.h"
#include "aifffile.h"
#include "wavfile.h"
@ -217,21 +215,28 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
// Ok, this is really dumb for now, but it works for testing.
String s;
String ext;
{
#ifdef _WIN32
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
String s = fileName.toString();
#else
s = fileName;
#endif
String s = fileName;
#endif
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
}
// If this list is updated, the method defaultFileExtensions() should also be
// updated. However at some point that list should be created at the same time
// that a default file type resolver is created.
int pos = s.rfind(".");
if(pos != -1) {
String ext = s.substr(pos + 1).upper();
if(!ext.isEmpty()) {
if(ext == "MP3")
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
@ -252,6 +257,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")

View File

@ -70,7 +70,8 @@ public:
~FilePrivate()
{
for(uint i = 0; i < blocks.size(); i++) {
uint size = blocks.size();
for(uint i = 0; i < size; i++) {
delete blocks[i];
}
delete properties;
@ -108,7 +109,8 @@ FLAC::File::File(FileName file, bool readProperties,
TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
@ -117,7 +119,8 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
@ -126,7 +129,8 @@ FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
FLAC::File::~File()
@ -164,14 +168,7 @@ void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
if(d->hasXiphComment)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->setProperties(properties);
else if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->setProperties(properties);
else
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
}
FLAC::Properties *FLAC::File::audioProperties() const
@ -402,7 +399,7 @@ void FLAC::File::scan()
char blockType = header[0] & 0x7f;
bool isLastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt();
uint length = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
@ -431,10 +428,10 @@ void FLAC::File::scan()
header = readBlock(4);
blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt();
length = header.toUInt(1U, 3U);
ByteVector data = readBlock(length);
if(data.size() != length) {
if(data.size() != length || length == 0) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
@ -564,3 +561,17 @@ void FLAC::File::removePictures()
d->blocks = newBlocks;
}
bool FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
}
bool FLAC::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool FLAC::File::hasID3v2Tag() const
{
return d->hasID3v2;
}

View File

@ -67,9 +67,10 @@ namespace TagLib {
{
public:
/*!
* Contructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
@ -78,12 +79,13 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an APE file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
@ -91,15 +93,16 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a FLAC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
@ -133,8 +136,10 @@ namespace TagLib {
/*!
* Implements the unified property interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, a XiphComment will be created.
* This always creates a Xiph comment, if none exists. The return value
* relates to the Xiph comment only.
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/
PropertyMap setProperties(const PropertyMap &);
@ -156,39 +161,57 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist.
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist.
* If \a create is false (the default) this returns a null pointer
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the XiphComment for the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this returns a null pointer
* if there is no valid XiphComment. If \a create is true it will create
* a XiphComment if one does not exist.
* a XiphComment if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has a XiphComment. Use hasXiphComment() to check if the
* file on disk actually has a XiphComment.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasXiphComment()
*/
Ogg::XiphComment *xiphComment(bool create = false);
@ -241,6 +264,27 @@ namespace TagLib {
*/
void addPicture(Picture *picture);
/*!
* Returns whether or not the file on disk actually has a XiphComment.
*
* \see xiphComment()
*/
bool hasXiphComment() const;
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "flacmetadatablock.h"

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "flacpicture.h"
@ -82,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false;
}
int pos = 0;
d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt());
uint pos = 0;
d->type = FLAC::Picture::Type(data.toUInt(pos));
pos += 4;
uint mimeTypeLength = data.mid(pos, 4).toUInt();
uint mimeTypeLength = data.toUInt(pos);
pos += 4;
if(pos + mimeTypeLength + 24 > data.size()) {
debug("Invalid picture block.");
@ -93,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
pos += mimeTypeLength;
uint descriptionLength = data.mid(pos, 4).toUInt();
uint descriptionLength = data.toUInt(pos);
pos += 4;
if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block.");
@ -101,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
pos += descriptionLength;
d->width = data.mid(pos, 4).toUInt();
d->width = data.toUInt(pos);
pos += 4;
d->height = data.mid(pos, 4).toUInt();
d->height = data.toUInt(pos);
pos += 4;
d->colorDepth = data.mid(pos, 4).toUInt();
d->colorDepth = data.toUInt(pos);
pos += 4;
d->numColors = data.mid(pos, 4).toUInt();
d->numColors = data.toUInt(pos);
pos += 4;
uint dataLength = data.mid(pos, 4).toUInt();
uint dataLength = data.toUInt(pos);
pos += 4;
if(pos + dataLength > data.size()) {
debug("Invalid picture block.");

View File

@ -124,7 +124,7 @@ void FLAC::Properties::read()
return;
}
int pos = 0;
uint pos = 0;
// Minimum block size (in samples)
pos += 2;
@ -138,7 +138,7 @@ void FLAC::Properties::read()
// Maximum frame size (in bytes)
pos += 3;
uint flags = d->data.mid(pos, 4).toUInt(true);
uint flags = d->data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12;
@ -149,7 +149,7 @@ void FLAC::Properties::read()
// stream length in samples. (Audio files measured in days)
unsigned long long hi = flags & 0xf;
unsigned long long lo = d->data.mid(pos, 4).toUInt(true);
unsigned long long lo = d->data.toUInt(pos, true);
pos += 4;
d->sampleFrames = (hi << 32) | lo;

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include <tstring.h>

View File

@ -45,7 +45,8 @@ IT::File::File(FileName file, bool readProperties,
Mod::FileBase(file),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
IT::File::File(IOStream *stream, bool readProperties,
@ -53,7 +54,8 @@ IT::File::File(IOStream *stream, bool readProperties,
Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
IT::File::~File()

View File

@ -36,23 +36,27 @@ namespace TagLib {
class TAGLIB_EXPORT File : public Mod::FileBase {
public:
/*!
* Contructs a Impulse Tracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a Impulse Tracker file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a Impulse Tracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a Impulse Tracker file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stram, bool readProperties = true,
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);

View File

@ -45,7 +45,8 @@ Mod::File::File(FileName file, bool readProperties,
Mod::FileBase(file),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
Mod::File::File(IOStream *stream, bool readProperties,
@ -53,7 +54,8 @@ Mod::File::File(IOStream *stream, bool readProperties,
Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
Mod::File::~File()

View File

@ -37,18 +37,22 @@ namespace TagLib {
{
public:
/*!
* Contructs a Protracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a Protracker file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a Protracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a Protracker file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
@ -52,10 +48,10 @@ MP4::Atom::Atom(File *file)
return;
}
length = header.mid(0, 4).toUInt();
length = header.toUInt();
if (length == 1) {
long long longLength = file->readBlock(8).toLongLong();
const long long longLength = file->readBlock(8).toLongLong();
if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
// The atom has a 64-bit length, but it's actually a 32-bit value
length = (long)longLength;

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4coverart.h"
using namespace TagLib;

View File

@ -42,10 +42,11 @@ namespace TagLib {
* This describes the image type.
*/
enum Format {
JPEG = TypeJPEG,
PNG = TypePNG,
BMP = TypeBMP,
GIF = TypeGIF
JPEG = TypeJPEG,
PNG = TypePNG,
BMP = TypeBMP,
GIF = TypeGIF,
Unknown = TypeImplicit,
};
CoverArt(Format format, const ByteVector &data);

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include <tpropertymap.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "mp4file.h"
@ -67,14 +64,16 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a
: TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, audioPropertiesStyle);
if(isOpen())
read(readProperties, audioPropertiesStyle);
}
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
: TagLib::File(stream)
{
d = new FilePrivate;
read(readProperties, audioPropertiesStyle);
if(isOpen())
read(readProperties, audioPropertiesStyle);
}
MP4::File::~File()
@ -88,6 +87,21 @@ MP4::File::tag() const
return d->tag;
}
PropertyMap MP4::File::properties() const
{
return d->tag->properties();
}
void MP4::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
MP4::Properties *
MP4::File::audioProperties() const
{

View File

@ -49,27 +49,25 @@ namespace TagLib {
{
public:
/*!
* Contructs a MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
File(FileName file, bool readProperties = true,
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Contructs a MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
* Constructs an MP4 file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@ -88,6 +86,22 @@ namespace TagLib {
*/
Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MP4 audio properties for this file.
*/

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4item.h"
using namespace TagLib;

View File

@ -23,10 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include "mp4file.h"
@ -96,8 +92,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
long long unit = data.mid(28, 8).toLongLong();
long long length = data.mid(36, 8).toLongLong();
const long long unit = data.toLongLong(28U);
const long long length = data.toLongLong(36U);
d->length = unit ? int(length / unit) : 0;
}
else {
@ -105,8 +101,8 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unsigned int unit = data.mid(20, 4).toUInt();
unsigned int length = data.mid(24, 4).toUInt();
const unsigned int unit = data.toUInt(20U);
const unsigned int length = data.toUInt(24U);
d->length = unit ? length / unit : 0;
}
@ -118,11 +114,11 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
file->seek(atom->offset);
data = file->readBlock(atom->length);
if(data.mid(20, 4) == "mp4a") {
d->channels = data.mid(40, 2).toShort();
d->bitsPerSample = data.mid(42, 2).toShort();
d->sampleRate = data.mid(46, 4).toUInt();
d->channels = data.toShort(40U);
d->bitsPerSample = data.toShort(42U);
d->sampleRate = data.toUInt(46U);
if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
long pos = 65;
uint pos = 65;
if(data.mid(pos, 3) == "\x80\x80\x80") {
pos += 3;
}
@ -133,16 +129,16 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
pos += 3;
}
pos += 10;
d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000;
d->bitrate = (data.toUInt(pos) + 500) / 1000;
}
}
}
else if (data.mid(20, 4) == "alac") {
if (atom->length == 88 && data.mid(56, 4) == "alac") {
d->bitsPerSample = data.at(69);
d->channels = data.at(73);
d->bitrate = data.mid(80, 4).toUInt() / 1000;
d->sampleRate = data.mid(84, 4).toUInt();
d->channels = data.at(73);
d->bitrate = data.toUInt(80U) / 1000;
d->sampleRate = data.toUInt(84U);
}
}

View File

@ -23,12 +23,9 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include <tpropertymap.h>
#include "mp4atom.h"
#include "mp4tag.h"
#include "id3v1genres.h"
@ -113,9 +110,9 @@ MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, boo
int i = 0;
unsigned int pos = 0;
while(pos < data.size()) {
int length = data.mid(pos, 4).toUInt();
const int length = static_cast<int>(data.toUInt(pos));
ByteVector name = data.mid(pos + 4, 4);
int flags = data.mid(pos + 8, 4).toUInt();
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(freeForm && i < 2) {
if(i == 0 && name != "mean") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
@ -158,7 +155,7 @@ MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, (int)data[0].toShort());
addItem(atom->name, (int)data[0].toShort());
}
}
@ -167,7 +164,7 @@ MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, data[0].toUInt());
addItem(atom->name, data[0].toUInt());
}
}
@ -176,7 +173,7 @@ MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, data[0].toLongLong());
addItem(atom->name, data[0].toLongLong());
}
}
@ -185,7 +182,7 @@ MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, (uchar)data[0].at(0));
addItem(atom->name, (uchar)data[0].at(0));
}
}
@ -195,8 +192,8 @@ MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
ByteVectorList data = parseData(atom, file);
if(data.size()) {
int idx = (int)data[0].toShort();
if(!d->items.contains("\251gen") && idx > 0) {
d->items.insert("\251gen", StringList(ID3v1::genre(idx - 1)));
if(idx > 0) {
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
}
}
}
@ -206,9 +203,9 @@ MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
int a = data[0].mid(2, 2).toShort();
int b = data[0].mid(4, 2).toShort();
d->items.insert(atom->name, MP4::Item(a, b));
const int a = data[0].toShort(2U);
const int b = data[0].toShort(4U);
addItem(atom->name, MP4::Item(a, b));
}
}
@ -218,7 +215,7 @@ MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
ByteVectorList data = parseData(atom, file);
if(data.size()) {
bool value = data[0].size() ? data[0][0] != '\0' : false;
d->items.insert(atom->name, value);
addItem(atom->name, value);
}
}
@ -231,7 +228,7 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
for(unsigned int i = 0; i < data.size(); i++) {
value.append(String(data[i], String::UTF8));
}
d->items.insert(atom->name, value);
addItem(atom->name, value);
}
}
@ -255,7 +252,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
}
Item item(value);
item.setAtomDataType(type);
d->items.insert(name, item);
addItem(name, item);
}
else {
ByteVectorList value;
@ -264,7 +261,7 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
}
Item item(value);
item.setAtomDataType(type);
d->items.insert(name, item);
addItem(name, item);
}
}
}
@ -276,21 +273,24 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
ByteVector data = file->readBlock(atom->length - 8);
unsigned int pos = 0;
while(pos < data.size()) {
int length = data.mid(pos, 4).toUInt();
const int length = static_cast<int>(data.toUInt(pos));
ByteVector name = data.mid(pos + 4, 4);
int flags = data.mid(pos + 8, 4).toUInt();
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(name != "data") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
}
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF) {
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) {
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
data.mid(pos + 16, length - 16)));
}
else {
debug("MP4: Unknown covr format " + String::number(flags));
}
pos += length;
}
if(value.size() > 0)
d->items.insert(atom->name, value);
addItem(atom->name, value);
}
ByteVector
@ -526,11 +526,11 @@ MP4::Tag::updateOffsets(long delta, long offset)
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.mid(0, 4).toUInt();
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
int pos = 4;
uint pos = 4;
while(count--) {
long o = data.mid(pos, 4).toUInt();
long o = static_cast<long>(data.toUInt(pos));
if(o > offset) {
o += delta;
}
@ -547,11 +547,11 @@ MP4::Tag::updateOffsets(long delta, long offset)
}
d->file->seek(atom->offset + 12);
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.mid(0, 4).toUInt();
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
int pos = 4;
uint pos = 4;
while(count--) {
long long o = data.mid(pos, 8).toLongLong();
long long o = data.toLongLong(pos);
if(o > offset) {
o += delta;
}
@ -570,10 +570,10 @@ MP4::Tag::updateOffsets(long delta, long offset)
atom->offset += delta;
}
d->file->seek(atom->offset + 9);
ByteVector data = d->file->readBlock(atom->offset - 9);
unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
ByteVector data = d->file->readBlock(atom->length - 9);
const unsigned int flags = data.toUInt(0, 3, true);
if(flags & 1) {
long long o = data.mid(7, 8).toLongLong();
long long o = data.toLongLong(7U);
if(o > offset) {
o += delta;
}
@ -756,3 +756,162 @@ MP4::Tag::itemListMap()
return d->items;
}
static const char *keyTranslation[][2] = {
{ "\251nam", "TITLE" },
{ "\251ART", "ARTIST" },
{ "\251alb", "ALBUM" },
{ "\251cmt", "COMMENT" },
{ "\251gen", "GENRE" },
{ "\251day", "DATE" },
{ "\251wrt", "COMPOSER" },
{ "\251grp", "GROUPING" },
{ "trkn", "TRACKNUMBER" },
{ "disk", "DISCNUMBER" },
{ "cpil", "COMPILATION" },
{ "tmpo", "BPM" },
{ "cprt", "COPYRIGHT" },
{ "\251lyr", "LYRICS" },
{ "\251too", "ENCODEDBY" },
{ "soal", "ALBUMSORT" },
{ "soaa", "ALBUMARTISTSORT" },
{ "soar", "ARTISTSORT" },
{ "sonm", "TITLESORT" },
{ "soco", "COMPOSERSORT" },
{ "sosn", "SHOWSORT" },
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "----:com.apple.iTunes:ASIN", "ASIN" },
{ "----:com.apple.iTunes:LABEL", "LABEL" },
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
{ "----:com.apple.iTunes:MIXER", "MIXER" },
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
{ "----:com.apple.iTunes:MOOD", "MOOD" },
{ "----:com.apple.iTunes:ISRC", "ISRC" },
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
};
PropertyMap MP4::Tag::properties() const
{
static Map<String, String> keyMap;
if(keyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
}
}
PropertyMap props;
MP4::ItemListMap::ConstIterator it = d->items.begin();
for(; it != d->items.end(); ++it) {
if(keyMap.contains(it->first)) {
String key = keyMap[it->first];
if(key == "TRACKNUMBER" || key == "DISCNUMBER") {
MP4::Item::IntPair ip = it->second.toIntPair();
String value = String::number(ip.first);
if(ip.second) {
value += "/" + String::number(ip.second);
}
props[key] = value;
}
else if(key == "BPM") {
props[key] = String::number(it->second.toInt());
}
else if(key == "COMPILATION") {
props[key] = String::number(it->second.toBool());
}
else {
props[key] = it->second.toStringList();
}
}
else {
props.unsupportedData().append(it->first);
}
}
return props;
}
void MP4::Tag::removeUnsupportedProperties(const StringList &props)
{
StringList::ConstIterator it = props.begin();
for(; it != props.end(); ++it)
d->items.erase(*it);
}
PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
}
}
PropertyMap origProps = properties();
PropertyMap::ConstIterator it = origProps.begin();
for(; it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
d->items.erase(reverseKeyMap[it->first]);
}
}
PropertyMap ignoredProps;
it = props.begin();
for(; it != props.end(); ++it) {
if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first];
if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") {
int first = 0, second = 0;
StringList parts = StringList::split(it->second.front(), "/");
if(parts.size() > 0) {
first = parts[0].toInt();
if(parts.size() > 1) {
second = parts[1].toInt();
}
d->items[name] = MP4::Item(first, second);
}
}
else if(it->first == "BPM") {
int value = it->second.front().toInt();
d->items[name] = MP4::Item(value);
}
else if(it->first == "COMPILATION") {
bool value = (it->second.front().toInt() != 0);
d->items[name] = MP4::Item(value);
}
else {
d->items[name] = it->second;
}
}
else {
ignoredProps.insert(it->first, it->second);
}
}
return ignoredProps;
}
void MP4::Tag::addItem(const String &name, const Item &value)
{
if(!d->items.contains(name)) {
d->items.insert(name, value);
}
else {
debug("MP4: Ignoring duplicate atom \"" + name + "\"");
}
}

View File

@ -67,6 +67,10 @@ namespace TagLib {
ItemListMap &itemListMap();
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
private:
AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
@ -101,6 +105,8 @@ namespace TagLib {
void saveNew(TagLib::ByteVector &data);
void saveExisting(TagLib::ByteVector &data, AtomList &path);
void addItem(const String &name, const Item &value);
class TagPrivate;
TagPrivate *d;
};

View File

@ -39,7 +39,7 @@ using namespace TagLib;
namespace
{
enum { MPCAPEIndex, MPCID3v1Index };
enum { MPCAPEIndex = 0, MPCID3v1Index = 1 };
}
class MPC::File::FilePrivate
@ -94,14 +94,16 @@ MPC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
MPC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
MPC::File::~File()
@ -133,15 +135,11 @@ void MPC::File::removeUnsupportedProperties(const StringList &properties)
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->setProperties(properties);
else
return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
if(d->hasID3v1)
d->tag.access<APE::Tag>(MPCID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(MPCAPEIndex, true)->setProperties(properties);
}
MPC::Properties *MPC::File::audioProperties() const
{
return d->properties;
@ -258,6 +256,15 @@ void MPC::File::remove(int tags)
strip(tags);
}
bool MPC::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool MPC::File::hasAPETag() const
{
return d->hasAPE;
}
////////////////////////////////////////////////////////////////////////////////
// private members

View File

@ -84,20 +84,22 @@ namespace TagLib {
};
/*!
* Contructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an MPC file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -124,8 +126,8 @@ namespace TagLib {
/*!
* Implements the unified property interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, an APE tag will be created.
* Affects only the APEv2 tag which will be created if necessary.
* If an ID3v1 tag exists, it will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
@ -143,28 +145,39 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
* new ID3v1 tag will be placed after it.
* If \a create is false (the default) this returns a null pointer
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* a APE tag if one does not exist. If there is already an ID3v1 tag, thes
* new APE tag will be placed before it.
* an APE tag if one does not exist and returns a valid pointer. If
* there already be an ID3v1 tag, the new APE tag will be placed before it.
*
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasAPETag()
*/
APE::Tag *APETag(bool create = false);
@ -185,6 +198,20 @@ namespace TagLib {
*/
void remove(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -207,7 +207,7 @@ void MPC::Properties::readSV8(File *file)
d->sampleFrames = readSize(data.mid(pos), pos);
ulong begSilence = readSize(data.mid(pos), pos);
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(pos, 2).toUShort(true)));
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true)));
pos += 2;
d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]];
@ -228,10 +228,10 @@ void MPC::Properties::readSV8(File *file)
int replayGainVersion = data[0];
if(replayGainVersion == 1) {
d->trackGain = data.mid(1, 2).toUInt(true);
d->trackPeak = data.mid(3, 2).toUInt(true);
d->albumGain = data.mid(5, 2).toUInt(true);
d->albumPeak = data.mid(7, 2).toUInt(true);
d->trackGain = data.toShort(1, true);
d->trackPeak = data.toShort(3, true);
d->albumGain = data.toShort(5, true);
d->albumPeak = data.toShort(7, true);
}
}
@ -252,18 +252,18 @@ void MPC::Properties::readSV7(const ByteVector &data)
if(d->version < 7)
return;
d->totalFrames = data.mid(4, 4).toUInt(false);
d->totalFrames = data.toUInt(4, false);
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(8, 4).toUInt(false)));
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false)));
d->sampleRate = sftable[flags[17] * 2 + flags[16]];
d->channels = 2;
uint gapless = data.mid(5, 4).toUInt(false);
uint gapless = data.toUInt(5, false);
d->trackGain = data.mid(14,2).toShort(false);
d->trackPeak = data.mid(12,2).toUInt(false);
d->albumGain = data.mid(18,2).toShort(false);
d->albumPeak = data.mid(16,2).toUInt(false);
d->trackGain = data.toShort(14, false);
d->trackPeak = data.toShort(12, false);
d->albumGain = data.toShort(18, false);
d->albumPeak = data.toShort(16, false);
// convert gain info
if(d->trackGain != 0) {
@ -293,7 +293,7 @@ void MPC::Properties::readSV7(const ByteVector &data)
d->sampleFrames = d->totalFrames * 1152 - 576;
}
else {
uint headerData = data.mid(0, 4).toUInt(false);
uint headerData = data.toUInt(0, false);
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
@ -301,9 +301,9 @@ void MPC::Properties::readSV7(const ByteVector &data)
d->channels = 2;
if(d->version >= 5)
d->totalFrames = data.mid(4, 4).toUInt(false);
d->totalFrames = data.toUInt(4, false);
else
d->totalFrames = data.mid(6, 2).toUInt(false);
d->totalFrames = data.toUShort(6, false);
d->sampleFrames = d->totalFrames * 1152 - 576;
}

View File

@ -182,16 +182,26 @@ void ID3v1::Tag::setGenre(const String &s)
d->genre = ID3v1::genreIndex(s);
}
void ID3v1::Tag::setYear(uint i)
void ID3v1::Tag::setYear(TagLib::uint i)
{
d->year = i > 0 ? String::number(i) : String::null;
}
void ID3v1::Tag::setTrack(uint i)
void ID3v1::Tag::setTrack(TagLib::uint i)
{
d->track = i < 256 ? i : 0;
}
TagLib::uint ID3v1::Tag::genreNumber() const
{
return d->genre;
}
void ID3v1::Tag::setGenreNumber(TagLib::uint i)
{
d->genre = i < 256 ? i : 255;
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{
if (handler)

View File

@ -140,16 +140,31 @@ namespace TagLib {
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual TagLib::uint year() const;
virtual TagLib::uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual void setYear(TagLib::uint i);
virtual void setTrack(TagLib::uint i);
/*!
* Returns the genre in number.
*
* /note Normally 255 indicates that this tag contains no genre.
*/
TagLib::uint genreNumber() const;
/*!
* Sets the genre in number to \a i.
*
* /note Valid value is from 0 up to 255. Normally 255 indicates that
* this tag contains no genre.
*/
void setGenreNumber(TagLib::uint i);
/*!
* Sets the string handler that decides how the ID3v1 data will be

View File

@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
if(pos < size) {
d->rating = (unsigned char)(data[pos++]);
if(pos < size) {
d->counter = data.mid(pos, 4).toUInt();
d->counter = data.toUInt(static_cast<uint>(pos));
}
}
}

View File

@ -191,7 +191,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
ChannelData &channel = d->channels[type];
channel.volumeAdjustment = data.mid(pos, 2).toShort();
channel.volumeAdjustment = data.toShort(static_cast<uint>(pos));
pos += 2;
channel.peakVolume.bitsRepresentingPeak = data[pos];

View File

@ -381,18 +381,12 @@ void UserTextIdentificationFrame::setDescription(const String &s)
PropertyMap UserTextIdentificationFrame::asProperties() const
{
String tagName = description();
PropertyMap map;
String key = tagName.upper();
if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
map.unsupportedData().append(L"TXXX/" + description());
else {
StringList v = fieldList();
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
if(*it != description())
map.insert(key, *it);
}
String tagName = txxxToKey(description());
StringList v = fieldList();
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
if(it != v.begin())
map.insert(tagName, *it);
return map;
}

View File

@ -24,8 +24,10 @@
***************************************************************************/
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include "id3v2tag.h"
#include "uniquefileidentifierframe.h"
using namespace TagLib;
@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const
return String::null;
}
PropertyMap UniqueFileIdentifierFrame::asProperties() const
{
PropertyMap map;
if(d->owner == "http://musicbrainz.org") {
map.insert("MUSICBRAINZ_TRACKID", String(d->identifier));
}
else {
map.unsupportedData().append(frameID() + String("/") + d->owner);
}
return map;
}
UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static
{
ID3v2::FrameList comments = tag->frameList("UFID");
for(ID3v2::FrameList::ConstIterator it = comments.begin();
it != comments.end();
++it)
{
UniqueFileIdentifierFrame *frame = dynamic_cast<UniqueFileIdentifierFrame *>(*it);
if(frame && frame->owner() == o)
return frame;
}
return 0;
}
void UniqueFileIdentifierFrame::parseFields(const ByteVector &data)
{
if(data.size() < 1) {

View File

@ -94,6 +94,16 @@ namespace TagLib {
virtual String toString() const;
PropertyMap asProperties() const;
/*!
* UFID frames each have a unique owner. This searches for a UFID
* frame with the owner \a o and returns a pointer to it.
*
* \see owner()
*/
static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;

View File

@ -44,6 +44,7 @@
#include "frames/urllinkframe.h"
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/commentsframe.h"
#include "frames/uniquefileidentifierframe.h"
#include "frames/unknownframe.h"
using namespace TagLib;
@ -120,16 +121,20 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
frame->setText(values);
return frame;
} else if(values.size() == 1){ // URL frame (not WXXX); support only one value
} else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
frame->setUrl(values.front());
return frame;
}
}
if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) {
UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8));
return frame;
}
// now we check if it's one of the "special" cases:
// -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS)
if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame();
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame(String::UTF8);
frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size()));
frame->setText(values.front());
return frame;
@ -144,12 +149,14 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
// -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT)
if((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1){
CommentsFrame *frame = new CommentsFrame(String::UTF8);
frame->setDescription(key == "COMMENT" ? key : key.substr(commentPrefix.size()));
if (key != "COMMENT"){
frame->setDescription(key.substr(commentPrefix.size()));
}
frame->setText(values.front());
return frame;
}
// if non of the above cases apply, we use a TXXX frame with the key as description
return new UserTextIdentificationFrame(key, values, String::UTF8);
return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8);
}
Frame::~Frame()
@ -348,7 +355,7 @@ static const char *frameTranslation[][2] = {
{ "TLAN", "LANGUAGE" },
{ "TLEN", "LENGTH" },
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
{ "TMED", "MEDIATYPE" },
{ "TMED", "MEDIA" },
{ "TMOO", "MOOD" },
{ "TOAL", "ORIGINALALBUM" },
{ "TOFN", "ORIGINALFILENAME" },
@ -361,7 +368,7 @@ static const char *frameTranslation[][2] = {
{ "TPE4", "REMIXER" }, // could also be ARRANGER
{ "TPOS", "DISCNUMBER" },
{ "TPRO", "PRODUCEDNOTICE" },
{ "TPUB", "PUBLISHER" },
{ "TPUB", "LABEL" },
{ "TRCK", "TRACKNUMBER" },
{ "TRSN", "RADIOSTATION" },
{ "TRSO", "RADIOSTATIONOWNER" },
@ -385,6 +392,18 @@ static const char *frameTranslation[][2] = {
//{ "USLT", "LYRICS" }, handled specially
};
static const TagLib::uint txxxFrameTranslationSize = 7;
static const char *txxxFrameTranslation[][2] = {
{ "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "Acoustid Id", "ACOUSTID_ID" },
{ "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" },
{ "MusicIP PUID", "MUSICIP_PUID" },
};
Map<ByteVector, String> &idMap()
{
static Map<ByteVector, String> m;
@ -394,6 +413,18 @@ Map<ByteVector, String> &idMap()
return m;
}
Map<String, String> &txxxMap()
{
static Map<String, String> m;
if(m.isEmpty()) {
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
String key = String(txxxFrameTranslation[i][0]).upper();
m[key] = txxxFrameTranslation[i][1];
}
}
return m;
}
// list of deprecated frames and their successors
static const TagLib::uint deprecatedFramesSize = 4;
static const char *deprecatedFrames[][2] = {
@ -433,6 +464,26 @@ ByteVector Frame::keyToFrameID(const String &s)
return ByteVector::null;
}
String Frame::txxxToKey(const String &description)
{
Map<String, String> &m = txxxMap();
String d = description.upper();
if(m.contains(d))
return m[d];
return d;
}
String Frame::keyToTXXX(const String &s)
{
static Map<String, String> m;
if(m.isEmpty())
for(size_t i = 0; i < txxxFrameTranslationSize; ++i)
m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0];
if(m.contains(s.upper()))
return m[s];
return s;
}
PropertyMap Frame::asProperties() const
{
if(dynamic_cast< const UnknownFrame *>(this)) {
@ -454,6 +505,8 @@ PropertyMap Frame::asProperties() const
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
else if(id == "USLT")
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
else if(id == "UFID")
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
PropertyMap m;
m.unsupportedData().append(id);
return m;
@ -589,7 +642,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
return;
}
d->frameSize = data.mid(3, 3).toUInt();
d->frameSize = data.toUInt(3, 3, true);
break;
}
@ -617,7 +670,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
// Set the size -- the frame size is the four bytes starting at byte four in
// the frame header (structure 4)
d->frameSize = data.mid(4, 4).toUInt();
d->frameSize = data.toUInt(4U);
{ // read the first byte of flags
std::bitset<8> flags(data[8]);
@ -664,7 +717,7 @@ void Frame::Header::setData(const ByteVector &data, uint version)
// iTunes writes v2.4 tags with v2.3-like frame sizes
if(d->frameSize > 127) {
if(!isValidFrameID(data.mid(d->frameSize + 10, 4))) {
unsigned int uintSize = data.mid(4, 4).toUInt();
unsigned int uintSize = data.toUInt(4U);
if(isValidFrameID(data.mid(uintSize + 10, 4))) {
d->frameSize = uintSize;
}

View File

@ -274,6 +274,15 @@ namespace TagLib {
*/
static String frameIDToKey(const ByteVector &);
/*!
* Returns an appropriate TXXX frame description for the given free-form tag key.
*/
static String keyToTXXX(const String &);
/*!
* Returns a free-form tag name for the given ID3 frame description.
*/
static String txxxToKey(const String &);
/*!
* This helper function splits the PropertyMap \a original into three ProperytMaps

View File

@ -99,7 +99,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
// A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
// characters. Also make sure that there is data in the frame.
if(!frameID.size() == (version < 3 ? 3 : 4) ||
if(frameID.size() != (version < 3 ? 3 : 4) ||
header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) ||
header->frameSize() > data.size())
{
@ -107,6 +107,17 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
return 0;
}
#ifndef NO_ITUNES_HACKS
if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') {
// iTunes v2.3 tags store v2.2 frames - convert now
frameID = frameID.mid(0, 3);
header->setFrameID(frameID);
header->setVersion(2);
updateFrame(header);
header->setVersion(3);
}
#endif
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
delete header;
@ -125,7 +136,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
// TagLib doesn't mess with encrypted frames, so just treat them
// as unknown frames.
#if HAVE_ZLIB == 0
#if !defined(HAVE_ZLIB) || HAVE_ZLIB == 0
if(header->compression()) {
debug("Compressed frames are currently not supported.");
return new UnknownFrame(data, header);

View File

@ -49,7 +49,14 @@ TagLib::uint SynchData::toUInt(const ByteVector &data)
// Invalid data; assume this was created by some buggy software that just
// put normal integers here rather than syncsafe ones, and try it that
// way.
sum = (data.size() > 4) ? data.mid(0, 4).toUInt() : data.toUInt();
if(data.size() >= 4) {
sum = data.toUInt(0, true);
}
else {
ByteVector tmp(data);
tmp.resize(4);
sum = tmp.toUInt(0, true);
}
}
return sum;

View File

@ -23,6 +23,10 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tfile.h>
#include "id3v2tag.h"
@ -379,10 +383,12 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
if (dynamic_cast<const UnknownFrame *>(*fit) != 0)
removeFrame(*fit);
} else if(it->size() == 4){
}
else if(it->size() == 4){
ByteVector id = it->data(String::Latin1);
removeFrames(id);
} else {
}
else {
ByteVector id = it->substr(0,4).data(String::Latin1);
if(it->size() <= 5)
continue; // invalid specification
@ -396,6 +402,8 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
frame = CommentsFrame::findByDescription(this, description);
else if(id == "USLT")
frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
else if(id == "UFID")
frame = UniqueFileIdentifierFrame::findByOwner(this, description);
if(frame)
removeFrame(frame);
}

View File

@ -156,16 +156,13 @@ void MPEG::File::removeUnsupportedProperties(const StringList &properties)
else if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
}
PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
else if(d->hasAPE)
return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
else
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
if(d->hasID3v1)
// update ID3v1 tag if it exists, but ignore the return value
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
MPEG::Properties *MPEG::File::audioProperties() const
@ -189,6 +186,11 @@ bool MPEG::File::save(int tags, bool stripOthers)
}
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
{
return save(tags, stripOthers, id3v2Version, true);
}
bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags)
{
if(tags == NoTags && stripOthers)
return strip(AllTags);
@ -206,14 +208,19 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
return false;
}
// Create the tags if we've been asked to. Copy the values from the tag that
// does exist into the new tag, except if the existing tag is to be stripped.
// Create the tags if we've been asked to.
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if (duplicateTags) {
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
// Copy the values from the tag that does exist into the new tag,
// except if the existing tag is to be stripped.
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
}
bool success = true;
@ -437,6 +444,21 @@ long MPEG::File::lastFrameOffset()
return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length());
}
bool MPEG::File::hasID3v1Tag() const
{
return d->hasID3v1;
}
bool MPEG::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
bool MPEG::File::hasAPETag() const
{
return d->hasAPE;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@ -634,9 +656,6 @@ void MPEG::File::findAPE()
bool MPEG::File::secondSynchByte(char byte)
{
if(uchar(byte) == 0xff)
return false;
std::bitset<8> b(byte);
// check to see if the byte matches 111xxxxx

View File

@ -71,9 +71,10 @@ namespace TagLib {
};
/*!
* Contructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
@ -82,28 +83,31 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored. The frames will be created using
* Constructs an MPEG file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an MPEG file from \a stream. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored. The frames will be created using
* \a frameFactory.
* Constructs an MPEG file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -133,7 +137,7 @@ namespace TagLib {
virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* Implements the reading part of the unified property interface.
* If the file contains more than one tag, only the
* first one (in the order ID3v2, APE, ID3v1) will be converted to the
* PropertyMap.
@ -143,9 +147,12 @@ namespace TagLib {
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified tag dictionary interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, ID3v2 will be created.
* Implements the writing part of the unified tag dictionary interface.
* In order to avoid problems with deprecated tag formats, this method
* always creates an ID3v2 tag if necessary.
* If an ID3v1 tag exists, it will be updated as well, within the
* limitations of that format.
* The returned PropertyMap refers to the ID3v2 tag only.
*/
PropertyMap setProperties(const PropertyMap &);
@ -210,42 +217,78 @@ namespace TagLib {
// BIC: combine with the above method
bool save(int tags, bool stripOthers, int id3v2Version);
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* If \a stripOthers is true this strips all tags not included in the mask,
* but does not modify them in memory, so later calls to save() which make
* use of these tags will remain valid. This also strips empty tags.
*
* The \a id3v2Version parameter specifies the version of the saved
* ID3v2 tag. It can be either 4 or 3.
*
* If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 --
* exists this will duplicate its content into the other tag.
*/
// BIC: combine with the above method
bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags);
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* A tag will always be returned, regardless of whether there is a
* tag in the file or not. Use ID3v2::Tag::isEmpty() to check if
* the tag contains no data.
* If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* A tag will always be returned, regardless of whether there is a
* tag in the file or not. Use Tag::isEmpty() to check if
* the tag contains no data.
* If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasID3v1Tag()
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist.
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasAPETag()
*/
APE::Tag *APETag(bool create = false);
@ -301,6 +344,27 @@ namespace TagLib {
*/
long lastFrameOffset();
/*!
* Returns whether or not the file on disk actually has an ID3v1 tag.
*
* \see ID3v1Tag()
*/
bool hasID3v1Tag() const;
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -28,6 +28,7 @@
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mpegheader.h"

View File

@ -108,8 +108,8 @@ void MPEG::XingHeader::parse(const ByteVector &data)
return;
}
d->frames = data.mid(8, 4).toUInt();
d->size = data.mid(12, 4).toUInt();
d->frames = data.toUInt(8U);
d->size = data.toUInt(12U);
d->valid = true;
}

View File

@ -26,6 +26,7 @@
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <xiphcomment.h>
#include "oggflacfile.h"
@ -72,14 +73,16 @@ Ogg::FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Ogg::FLAC::File::~File()
@ -92,6 +95,16 @@ Ogg::XiphComment *Ogg::FLAC::File::tag() const
return d->comment;
}
PropertyMap Ogg::FLAC::File::properties() const
{
return d->comment->properties();
}
PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Properties *Ogg::FLAC::File::audioProperties() const
{
return d->properties;
@ -124,6 +137,11 @@ bool Ogg::FLAC::File::save()
return Ogg::File::save();
}
bool Ogg::FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@ -228,7 +246,7 @@ void Ogg::FLAC::File::scan()
char blockType = header[0] & 0x7f;
bool lastBlock = (header[0] & 0x80) != 0;
uint length = header.mid(1, 3).toUInt();
uint length = header.toUInt(1, 3, true);
overhead += length;
// Sanity: First block should be the stream_info metadata
@ -238,7 +256,7 @@ void Ogg::FLAC::File::scan()
return;
}
d->streamInfoData = metadataHeader.mid(4,length);
d->streamInfoData = metadataHeader.mid(4, length);
// Search through the remaining metadata
@ -251,7 +269,7 @@ void Ogg::FLAC::File::scan()
header = metadataHeader.mid(0, 4);
blockType = header[0] & 0x7f;
lastBlock = (header[0] & 0x80) != 0;
length = header.mid(1, 3).toUInt();
length = header.toUInt(1, 3, true);
overhead += length;
if(blockType == 1) {

View File

@ -64,20 +64,22 @@ namespace TagLib {
{
public:
/*!
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read using \a propertiesStyle.
* If false, \a propertiesStyle is ignored.
* Constructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read using \a propertiesStyle.
* If false, \a propertiesStyle is ignored.
* Constructs an Ogg/FLAC file from \a stream. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -89,6 +91,16 @@ namespace TagLib {
/*!
* Returns the Tag for this file. This will always be a XiphComment.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has a XiphComment. Use hasXiphComment() to check if
* the file on disk actually has a XiphComment.
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasXiphComment()
*/
virtual XiphComment *tag() const;
@ -98,6 +110,20 @@ namespace TagLib {
*/
virtual Properties *audioProperties() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Save the file. This will primarily save and update the XiphComment.
* Returns true if the save is successful.
@ -110,6 +136,13 @@ namespace TagLib {
*/
long streamLength();
/*!
* Returns whether or not the file on disk actually has a XiphComment.
*
* \see tag()
*/
bool hasXiphComment() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -82,9 +82,7 @@ namespace TagLib {
protected:
/*!
* Contructs an Ogg file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an Ogg file from \a file.
*
* \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec
@ -93,9 +91,7 @@ namespace TagLib {
File(FileName file);
/*!
* Contructs an Ogg file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an Ogg file from \a stream.
*
* \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec

View File

@ -255,9 +255,9 @@ void Ogg::PageHeader::read()
d->firstPageOfStream = flags.test(1);
d->lastPageOfStream = flags.test(2);
d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
d->streamSerialNumber = data.mid(14, 4).toUInt(false);
d->pageSequenceNumber = data.mid(18, 4).toUInt(false);
d->absoluteGranularPosition = data.toLongLong(6, false);
d->streamSerialNumber = data.toUInt(14, false);
d->pageSequenceNumber = data.toUInt(18, false);
// Byte number 27 is the number of page segments, which is the only variable
// length portion of the page header. After reading the number of page

139
3rdparty/taglib/ogg/opus/opusfile.cpp vendored Normal file
View File

@ -0,0 +1,139 @@
/***************************************************************************
copyright : (C) 2012 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include "opusfile.h"
using namespace TagLib;
using namespace TagLib::Ogg;
class Opus::File::FilePrivate
{
public:
FilePrivate() :
comment(0),
properties(0) {}
~FilePrivate()
{
delete comment;
delete properties;
}
Ogg::XiphComment *comment;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) :
Ogg::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) :
Ogg::File(stream),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
Opus::File::~File()
{
delete d;
}
Ogg::XiphComment *Opus::File::tag() const
{
return d->comment;
}
PropertyMap Opus::File::properties() const
{
return d->comment->properties();
}
PropertyMap Opus::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Opus::Properties *Opus::File::audioProperties() const
{
return d->properties;
}
bool Opus::File::save()
{
if(!d->comment)
d->comment = new Ogg::XiphComment;
setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false));
return Ogg::File::save();
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
ByteVector opusHeaderData = packet(0);
if(!opusHeaderData.startsWith("OpusHead")) {
setValid(false);
debug("Opus::File::read() -- invalid Opus identification header");
return;
}
ByteVector commentHeaderData = packet(1);
if(!commentHeaderData.startsWith("OpusTags")) {
setValid(false);
debug("Opus::File::read() -- invalid Opus tags header");
return;
}
d->comment = new Ogg::XiphComment(commentHeaderData.mid(8));
if(readProperties)
d->properties = new Properties(this, propertiesStyle);
}

124
3rdparty/taglib/ogg/opus/opusfile.h vendored Normal file
View File

@ -0,0 +1,124 @@
/***************************************************************************
copyright : (C) 2012 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_OPUSFILE_H
#define TAGLIB_OPUSFILE_H
#include "oggfile.h"
#include "xiphcomment.h"
#include "opusproperties.h"
namespace TagLib {
namespace Ogg {
//! A namespace containing classes for Opus metadata
namespace Opus {
//! An implementation of Ogg::File with Opus specific methods
/*!
* This is the central class in the Ogg Opus metadata processing collection
* of classes. It's built upon Ogg::File which handles processing of the Ogg
* logical bitstream and breaking it down into pages which are handled by
* the codec implementations, in this case Opus specifically.
*/
class TAGLIB_EXPORT File : public Ogg::File
{
public:
/*!
* Constructs an Opus file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an Opus file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the XiphComment for this file. XiphComment implements the tag
* interface, so this serves as the reimplementation of
* TagLib::File::tag().
*/
virtual Ogg::XiphComment *tag() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Opus::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
virtual bool save();
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View File

@ -0,0 +1,161 @@
/***************************************************************************
copyright : (C) 2012 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include <oggpageheader.h>
#include "opusproperties.h"
#include "opusfile.h"
using namespace TagLib;
using namespace TagLib::Ogg;
class Opus::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(File *f, ReadStyle s) :
file(f),
style(s),
length(0),
inputSampleRate(0),
channels(0),
opusVersion(0) {}
File *file;
ReadStyle style;
int length;
int inputSampleRate;
int channels;
int opusVersion;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(file, style);
read();
}
Opus::Properties::~Properties()
{
delete d;
}
int Opus::Properties::length() const
{
return d->length;
}
int Opus::Properties::bitrate() const
{
return 0;
}
int Opus::Properties::sampleRate() const
{
// Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz,
// so there is no single sample rate. Let's assume it's the highest
// possible.
return 48000;
}
int Opus::Properties::channels() const
{
return d->channels;
}
int Opus::Properties::inputSampleRate() const
{
return d->inputSampleRate;
}
int Opus::Properties::opusVersion() const
{
return d->opusVersion;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Opus::Properties::read()
{
// Get the identification header from the Ogg implementation.
// http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1
ByteVector data = d->file->packet(0);
// *Magic Signature*
uint pos = 8;
// *Version* (8 bits, unsigned)
d->opusVersion = uchar(data.at(pos));
pos += 1;
// *Output Channel Count* 'C' (8 bits, unsigned)
d->channels = uchar(data.at(pos));
pos += 1;
// *Pre-skip* (16 bits, unsigned, little endian)
const ushort preSkip = data.toUShort(pos, false);
pos += 2;
// *Input Sample Rate* (32 bits, unsigned, little endian)
d->inputSampleRate = data.toUInt(pos, false);
pos += 4;
// *Output Gain* (16 bits, signed, little endian)
pos += 2;
// *Channel Mapping Family* (8 bits, unsigned)
pos += 1;
const Ogg::PageHeader *first = d->file->firstPageHeader();
const Ogg::PageHeader *last = d->file->lastPageHeader();
if(first && last) {
long long start = first->absoluteGranularPosition();
long long end = last->absoluteGranularPosition();
if(start >= 0 && end >= 0)
d->length = (int) ((end - start - preSkip) / 48000);
else {
debug("Opus::Properties::read() -- The PCM values for the start or "
"end of this file was incorrect.");
}
}
else
debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages.");
}

View File

@ -0,0 +1,96 @@
/***************************************************************************
copyright : (C) 2012 by Lukáš Lalinský
email : lalinsky@gmail.com
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
(original Vorbis implementation)
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_OPUSPROPERTIES_H
#define TAGLIB_OPUSPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace Ogg {
namespace Opus {
class File;
//! An implementation of audio property reading for Ogg Opus
/*!
* This reads the data from an Ogg Opus stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of Opus::Properties with the data read from the
* Opus::File \a file.
*/
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this Opus::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
virtual int length() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
/*!
* The Opus codec supports decoding at multiple sample rates, there is no
* single sample rate of the encoded stream. This returns the sample rate
* of the original audio stream.
*/
int inputSampleRate() const;
/*!
* Returns the Opus version, currently "0" (as specified by the spec).
*/
int opusVersion() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
}
#endif

View File

@ -31,6 +31,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include "speexfile.h"
@ -62,14 +63,16 @@ Speex::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Speex::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Speex::File::~File()
@ -82,6 +85,16 @@ Ogg::XiphComment *Speex::File::tag() const
return d->comment;
}
PropertyMap Speex::File::properties() const
{
return d->comment->properties();
}
PropertyMap Speex::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Speex::Properties *Speex::File::audioProperties() const
{
return d->properties;

View File

@ -56,20 +56,22 @@ namespace TagLib {
{
public:
/*!
* Contructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a Speex file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -86,12 +88,26 @@ namespace TagLib {
*/
virtual Ogg::XiphComment *tag() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Speex::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
virtual bool save();
private:

View File

@ -113,32 +113,32 @@ void Speex::Properties::read()
ByteVector data = d->file->packet(0);
int pos = 28;
uint pos = 28;
// speex_version_id; /**< Version for Speex (for checking compatibility) */
d->speexVersion = data.mid(pos, 4).toUInt(false);
d->speexVersion = data.toUInt(pos, false);
pos += 4;
// header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
pos += 4;
// rate; /**< Sampling rate used */
d->sampleRate = data.mid(pos, 4).toUInt(false);
d->sampleRate = data.toUInt(pos, false);
pos += 4;
// mode; /**< Mode used (0 for narrowband, 1 for wideband) */
d->mode = data.mid(pos, 4).toUInt(false);
d->mode = data.toUInt(pos, false);
pos += 4;
// mode_bitstream_version; /**< Version ID of the bit-stream */
pos += 4;
// nb_channels; /**< Number of channels encoded */
d->channels = data.mid(pos, 4).toUInt(false);
d->channels = data.toUInt(pos, false);
pos += 4;
// bitrate; /**< Bit-rate used */
d->bitrate = data.mid(pos, 4).toUInt(false);
d->bitrate = data.toUInt(pos, false);
pos += 4;
// frame_size; /**< Size of frames */
@ -146,7 +146,7 @@ void Speex::Properties::read()
pos += 4;
// vbr; /**< 1 for a VBR encoding, 0 otherwise */
d->vbr = data.mid(pos, 4).toUInt(false) == 1;
d->vbr = data.toUInt(pos, false) == 1;
pos += 4;
// frames_per_packet; /**< Number of frames stored per Ogg packet */

View File

@ -67,14 +67,16 @@ Vorbis::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Vorbis::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
if(isOpen())
read(readProperties, propertiesStyle);
}
Vorbis::File::~File()

View File

@ -63,20 +63,22 @@ namespace TagLib {
{
public:
/*!
* Contructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a Vorbis file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);

View File

@ -133,7 +133,7 @@ void Vorbis::Properties::read()
ByteVector data = d->file->packet(0);
int pos = 0;
uint pos = 0;
if(data.mid(pos, 7) != vorbisSetupHeaderID) {
debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
@ -142,22 +142,22 @@ void Vorbis::Properties::read()
pos += 7;
d->vorbisVersion = data.mid(pos, 4).toUInt(false);
d->vorbisVersion = data.toUInt(pos, false);
pos += 4;
d->channels = uchar(data[pos]);
pos += 1;
d->sampleRate = data.mid(pos, 4).toUInt(false);
d->sampleRate = data.toUInt(pos, false);
pos += 4;
d->bitrateMaximum = data.mid(pos, 4).toUInt(false);
d->bitrateMaximum = data.toUInt(pos, false);
pos += 4;
d->bitrateNominal = data.mid(pos, 4).toUInt(false);
d->bitrateNominal = data.toUInt(pos, false);
pos += 4;
d->bitrateMinimum = data.mid(pos, 4).toUInt(false);
d->bitrateMinimum = data.toUInt(pos, false);
// TODO: Later this should be only the "fast" mode.
d->bitrate = d->bitrateNominal;

View File

@ -340,7 +340,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
uint pos = 0;
uint vendorLength = data.mid(0, 4).toUInt(false);
const uint vendorLength = data.toUInt(0, false);
pos += 4;
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
@ -348,7 +348,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
// Next the number of fields in the comment vector.
uint commentFields = data.mid(pos, 4).toUInt(false);
const uint commentFields = data.toUInt(pos, false);
pos += 4;
if(commentFields > (data.size() - 8) / 4) {
@ -360,7 +360,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
// Each comment field is in the format "KEY=value" in a UTF8 string and has
// 4 bytes before the text starts that gives the length.
uint commentLength = data.mid(pos, 4).toUInt(false);
const uint commentLength = data.toUInt(pos, false);
pos += 4;
String comment = String(data.mid(pos, commentLength), String::UTF8);

View File

@ -90,6 +90,11 @@ PropertyMap RIFF::AIFF::File::properties() const
return d->tag->properties();
}
void RIFF::AIFF::File::removeUnsupportedProperties(const StringList &unsupported)
{
d->tag->removeUnsupportedProperties(unsupported);
}
PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
@ -118,6 +123,7 @@ bool RIFF::AIFF::File::save()
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

View File

@ -58,20 +58,22 @@ namespace TagLib {
{
public:
/*!
* Contructs an AIFF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an AIFF file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an AIFF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an AIFF file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -83,6 +85,12 @@ namespace TagLib {
/*!
* Returns the Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \see hasID3v2Tag()
*/
virtual ID3v2::Tag *tag() const;
@ -92,6 +100,8 @@ namespace TagLib {
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
@ -109,6 +119,13 @@ namespace TagLib {
*/
virtual bool save();
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
private:
File(const File &);
File &operator=(const File &);

View File

@ -39,7 +39,7 @@
#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)
static double ConvertFromIeeeExtended(unsigned char *bytes)
static double ConvertFromIeeeExtended(const TagLib::uchar *bytes)
{
double f;
int expon;
@ -150,10 +150,10 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const
void RIFF::AIFF::Properties::read(const ByteVector &data)
{
d->channels = data.mid(0, 2).toShort();
d->sampleFrames = data.mid(2, 4).toUInt();
d->sampleWidth = data.mid(6, 2).toShort();
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<unsigned char *>(data.mid(8, 10).data()));
d->channels = data.toShort(0U);
d->sampleFrames = data.toUInt(2U);
d->sampleWidth = data.toShort(6U);
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<const uchar *>(data.data() + 8));
d->sampleRate = (int)sampleRate;
d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;

View File

@ -138,34 +138,49 @@ ByteVector RIFF::File::chunkData(uint i)
return readBlock(d->chunks[i].size);
}
void RIFF::File::setChunkData(uint i, const ByteVector &data)
{
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
// Now update the specific chunk
writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
for(i++; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
}
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
{
setChunkData(name, data, false);
}
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate)
{
if(d->chunks.size() == 0) {
debug("RIFF::File::setChunkData - No valid chunks found.");
return;
}
for(uint i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
if(alwaysCreate && name != "LIST") {
debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks.");
return;
}
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
// Now update the specific chunk
writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
for(i++; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding;
return;
if(!alwaysCreate) {
for(uint i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
setChunkData(i, data);
return;
}
}
}
@ -181,7 +196,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
// Now add the chunk to the file
writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
writeChunk(name, data, offset, std::max<long>(0, length() - offset), (offset & 1) ? 1 : 0);
// And update our internal structure
@ -199,6 +214,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
d->chunks.push_back(chunk);
}
void RIFF::File::removeChunk(uint i)
{
if(i >= d->chunks.size())
return;
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
d->chunks.erase(d->chunks.begin() + i);
}
void RIFF::File::removeChunk(const ByteVector &name)
{
std::vector<Chunk> newChunks;
for(size_t i = 0; i < d->chunks.size(); ++i) {
if(d->chunks[i].name == name)
removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8);
else
newChunks.push_back(d->chunks[i]);
}
d->chunks.swap(newChunks);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

View File

@ -95,6 +95,13 @@ namespace TagLib {
*/
ByteVector chunkData(uint i);
/*!
* Sets the data for the the specified chunk to \a data.
*
* \warning This will update the file immediately.
*/
void setChunkData(uint i, const ByteVector &data);
/*!
* Sets the data for the chunk \a name to \a data. If a chunk with the
* given name already exists it will be overwritten, otherwise it will be
@ -104,6 +111,34 @@ namespace TagLib {
*/
void setChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the chunk \a name to \a data. If a chunk with the
* given name already exists it will be overwritten, otherwise it will be
* created after the existing chunks.
*
* \note If \a alwaysCreate is true, a new chunk is created regardless of
* whether or not the chunk \a name exists. It should only be used for
* "LIST" chunks.
*
* \warning This will update the file immediately.
*/
void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate);
/*!
* Removes the specified chunk.
*
* \warning This will update the file immediately.
*/
void removeChunk(uint i);
/*!
* Removes the chunk \a name.
*
* \warning This will update the file immediately.
* \warning This removes all the chunks with the given name.
*/
void removeChunk(const ByteVector &name);
private:
File(const File &);
File &operator=(const File &);

261
3rdparty/taglib/riff/wav/infotag.cpp vendored Normal file
View File

@ -0,0 +1,261 @@
/***************************************************************************
copyright : (C) 2012 by Tsuda Kageyu
email : wheeler@kde.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include <tfile.h>
#include "infotag.h"
using namespace TagLib;
using namespace RIFF::Info;
namespace {
static bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4)
return false;
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 127)
return false;
}
return true;
}
}
class RIFF::Info::Tag::TagPrivate
{
public:
TagPrivate()
{}
FieldListMap fieldListMap;
static const StringHandler *stringHandler;
};
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
StringHandler::StringHandler()
{
}
StringHandler::~StringHandler()
{
}
String RIFF::Info::StringHandler::parse(const ByteVector &data) const
{
return String(data, String::UTF8);
}
ByteVector RIFF::Info::StringHandler::render(const String &s) const
{
return s.data(String::UTF8);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
static const StringHandler defaultStringHandler;
const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler;
RIFF::Info::Tag::Tag(const ByteVector &data)
: TagLib::Tag()
, d(new TagPrivate())
{
parse(data);
}
RIFF::Info::Tag::Tag()
: TagLib::Tag()
, d(new TagPrivate())
{
}
RIFF::Info::Tag::~Tag()
{
delete d;
}
String RIFF::Info::Tag::title() const
{
return fieldText("INAM");
}
String RIFF::Info::Tag::artist() const
{
return fieldText("IART");
}
String RIFF::Info::Tag::album() const
{
return fieldText("IPRD");
}
String RIFF::Info::Tag::comment() const
{
return fieldText("ICMT");
}
String RIFF::Info::Tag::genre() const
{
return fieldText("IGNR");
}
TagLib::uint RIFF::Info::Tag::year() const
{
return fieldText("ICRD").substr(0, 4).toInt();
}
TagLib::uint RIFF::Info::Tag::track() const
{
return fieldText("IPRT").toInt();
}
void RIFF::Info::Tag::setTitle(const String &s)
{
setFieldText("INAM", s);
}
void RIFF::Info::Tag::setArtist(const String &s)
{
setFieldText("IART", s);
}
void RIFF::Info::Tag::setAlbum(const String &s)
{
setFieldText("IPRD", s);
}
void RIFF::Info::Tag::setComment(const String &s)
{
setFieldText("ICMT", s);
}
void RIFF::Info::Tag::setGenre(const String &s)
{
setFieldText("IGNR", s);
}
void RIFF::Info::Tag::setYear(uint i)
{
if(i != 0)
setFieldText("ICRD", String::number(i));
else
d->fieldListMap.erase("ICRD");
}
void RIFF::Info::Tag::setTrack(uint i)
{
if(i != 0)
setFieldText("IPRT", String::number(i));
else
d->fieldListMap.erase("IPRT");
}
bool RIFF::Info::Tag::isEmpty() const
{
return d->fieldListMap.isEmpty();
}
String RIFF::Info::Tag::fieldText(const ByteVector &id) const
{
if(d->fieldListMap.contains(id))
return String(d->fieldListMap[id]);
else
return String();
}
void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
{
// id must be four-byte long pure ascii string.
if(!isValidChunkID(id))
return;
if(!s.isEmpty())
d->fieldListMap[id] = s;
else
removeField(id);
}
void RIFF::Info::Tag::removeField(const ByteVector &id)
{
if(d->fieldListMap.contains(id))
d->fieldListMap.erase(id);
}
ByteVector RIFF::Info::Tag::render() const
{
ByteVector data("INFO");
FieldListMap::ConstIterator it = d->fieldListMap.begin();
for(; it != d->fieldListMap.end(); ++it) {
ByteVector text = TagPrivate::stringHandler->render(it->second);
if(text.isEmpty())
continue;
data.append(it->first);
data.append(ByteVector::fromUInt(text.size() + 1, false));
data.append(text);
do {
data.append('\0');
} while(data.size() & 1);
}
if(data.size() == 4)
return ByteVector();
else
return data;
}
void RIFF::Info::Tag::setStringHandler(const StringHandler *handler)
{
if(handler)
TagPrivate::stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void RIFF::Info::Tag::parse(const ByteVector &data)
{
uint p = 4;
while(p < data.size()) {
const uint size = data.toUInt(p + 4, false);
d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
p += ((size + 1) & ~1) + 8;
}
}

180
3rdparty/taglib/riff/wav/infotag.h vendored Normal file
View File

@ -0,0 +1,180 @@
/***************************************************************************
copyright : (C) 2012 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_INFOTAG_H
#define TAGLIB_INFOTAG_H
#include "tag.h"
#include "tmap.h"
#include "tstring.h"
#include "tstringlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
class File;
//! A RIFF Info tag implementation.
namespace RIFF {
namespace Info {
typedef Map<ByteVector, String> FieldListMap;
//! A abstraction for the string to data encoding in Info tags.
/*!
* RIFF Info tag has no clear definitions about character encodings.
* In practice, local encoding of each system is largely used and UTF-8 is
* popular too.
*
* Here is an option to read and write tags in your preferrd encoding
* by subclassing this class, reimplementing parse() and render() and setting
* your reimplementation as the default with Info::Tag::setStringHandler().
*
* \see ID3v1::Tag::setStringHandler()
*/
class TAGLIB_EXPORT StringHandler
{
public:
StringHandler();
~StringHandler();
/*!
* Decode a string from \a data. The default implementation assumes that
* \a data is an UTF-8 character array.
*/
virtual String parse(const ByteVector &data) const;
/*!
* Encode a ByteVector with the data from \a s. The default implementation
* assumes that \a s is an UTF-8 string.
*/
virtual ByteVector render(const String &s) const;
};
//! The main class in the ID3v2 implementation
/*!
* This is the main class in the INFO tag implementation. RIFF INFO tag is a
* metadata format found in WAV audio and AVI video files. Though it is a part
* of Microsoft/IBM's RIFF specification, the author could not find the official
* documents about it. So, this implementation is referring to unofficial documents
* online and some applications' behaviors especially Windows Explorer.
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
/*!
* Constructs an empty Info tag.
*/
Tag();
/*!
* Constructs an Info tag read from \a data which is contents of "LIST" chunk.
*/
Tag(const ByteVector &data);
virtual ~Tag();
// Reimplementations
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual bool isEmpty() const;
/*
* Gets the value of the field with the ID \a id.
*/
String fieldText(const ByteVector &id) const;
/*
* Sets the value of the field with the ID \a id to \a s.
* If the field does not exist, it is created.
* If \s is empty, the field is removed.
*
* \note fieldId must be four-byte long pure ASCII string. This function
* performs nothing if fieldId is invalid.
*/
void setFieldText(const ByteVector &id, const String &s);
/*
* Removes the field with the ID \a id.
*/
void removeField(const ByteVector &id);
/*!
* Render the tag back to binary data, suitable to be written to disk.
*
* \note Returns empty ByteVector is the tag contains no fields.
*/
ByteVector render() const;
/*!
* Sets the string handler that decides how the text data will be
* converted to and from binary data.
* If the parameter \a handler is null, the previous handler is
* released and default UTF-8 handler is restored.
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
*
* \see StringHandler
*/
static void setStringHandler(const StringHandler *handler);
protected:
/*!
* Pareses the body of the tag in \a data.
*/
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}}
}
#endif

View File

@ -23,36 +23,47 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include "tbytevector.h"
#include "tdebug.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "wavfile.h"
#include "id3v2tag.h"
#include "infotag.h"
#include "tagunion.h"
using namespace TagLib;
namespace
{
enum { ID3v2Index = 0, InfoIndex = 1 };
}
class RIFF::WAV::File::FilePrivate
{
public:
FilePrivate() :
properties(0),
tag(0),
tagChunkID("ID3 ")
tagChunkID("ID3 "),
hasID3v2(false),
hasInfo(false)
{
}
~FilePrivate()
{
delete properties;
delete tag;
}
Properties *properties;
ID3v2::Tag *tag;
ByteVector tagChunkID;
TagUnion tag;
bool hasID3v2;
bool hasInfo;
};
////////////////////////////////////////////////////////////////////////////////
@ -82,26 +93,45 @@ RIFF::WAV::File::~File()
ID3v2::Tag *RIFF::WAV::File::tag() const
{
return d->tag;
return ID3v2Tag();
}
ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
}
RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const
{
return d->tag.access<RIFF::Info::Tag>(InfoIndex, false);
}
PropertyMap RIFF::WAV::File::properties() const
{
return d->tag->properties();
return tag()->properties();
}
void RIFF::WAV::File::removeUnsupportedProperties(const StringList &unsupported)
{
tag()->removeUnsupportedProperties(unsupported);
}
PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
return tag()->setProperties(properties);
}
RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
{
return d->properties;
}
bool RIFF::WAV::File::save()
{
return RIFF::WAV::File::save(AllTags);
}
bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
{
if(readOnly()) {
debug("RIFF::WAV::File::save() -- File is read only.");
@ -113,11 +143,43 @@ bool RIFF::WAV::File::save()
return false;
}
setChunkData(d->tagChunkID, d->tag->render());
if(stripOthers)
strip(static_cast<TagTypes>(AllTags & ~tags));
ID3v2::Tag *id3v2tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
if(!id3v2tag->isEmpty()) {
if(tags & ID3v2) {
setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version));
d->hasID3v2 = true;
}
}
Info::Tag *infotag = d->tag.access<Info::Tag>(InfoIndex, false);
if(!infotag->isEmpty()) {
if(tags & Info) {
int chunkId = findInfoTagChunk();
if(chunkId != -1)
setChunkData(chunkId, infotag->render());
else
setChunkData("LIST", infotag->render(), true);
d->hasInfo = true;
}
}
return true;
}
bool RIFF::WAV::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
bool RIFF::WAV::File::hasInfoTag() const
{
return d->hasInfo;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@ -127,19 +189,56 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties
ByteVector formatData;
uint streamLength = 0;
for(uint i = 0; i < chunkCount(); i++) {
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
String name = chunkName(i);
if(name == "ID3 " || name == "id3 ") {
d->tagChunkID = chunkName(i);
d->tag = new ID3v2::Tag(this, chunkOffset(i));
d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i)));
d->hasID3v2 = true;
}
else if(chunkName(i) == "fmt " && readProperties)
else if(name == "fmt " && readProperties)
formatData = chunkData(i);
else if(chunkName(i) == "data" && readProperties)
else if(name == "data" && readProperties)
streamLength = chunkDataSize(i);
else if(name == "LIST") {
ByteVector data = chunkData(i);
ByteVector type = data.mid(0, 4);
if(type == "INFO") {
d->tag.set(InfoIndex, new RIFF::Info::Tag(data));
d->hasInfo = true;
}
}
}
if (!d->tag[ID3v2Index])
d->tag.set(ID3v2Index, new ID3v2::Tag);
if (!d->tag[InfoIndex])
d->tag.set(InfoIndex, new RIFF::Info::Tag);
if(!formatData.isEmpty())
d->properties = new Properties(formatData, streamLength, propertiesStyle);
if(!d->tag)
d->tag = new ID3v2::Tag;
}
void RIFF::WAV::File::strip(TagTypes tags)
{
if(tags & ID3v2)
removeChunk(d->tagChunkID);
if(tags & Info){
TagLib::uint chunkId = findInfoTagChunk();
if(chunkId != TagLib::uint(-1))
removeChunk(chunkId);
}
}
TagLib::uint RIFF::WAV::File::findInfoTagChunk()
{
for(uint i = 0; i < chunkCount(); ++i) {
if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") {
return i;
}
}
return TagLib::uint(-1);
}

View File

@ -28,6 +28,7 @@
#include "rifffile.h"
#include "id3v2tag.h"
#include "infotag.h"
#include "wavproperties.h"
namespace TagLib {
@ -57,21 +58,34 @@ namespace TagLib {
class TAGLIB_EXPORT File : public TagLib::RIFF::File
{
public:
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches ID3v2 tags.
ID3v2 = 0x0001,
//! Matches Info tags.
Info = 0x0002,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Contructs an WAV file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a WAV file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an WAV file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs a WAV file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@ -82,9 +96,34 @@ namespace TagLib {
virtual ~File();
/*!
* Returns the Tag for this file.
* Returns the ID3v2 Tag for this file.
*
* \note This method does not return all the tags for this file for
* backward compatibility. Will be fixed in TagLib 2.0.
*/
virtual ID3v2::Tag *tag() const;
ID3v2::Tag *tag() const;
/*!
* Returns the ID3v2 Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
* file on disk actually has an ID3v2 tag.
*
* \see hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag() const;
/*!
* Returns the RIFF INFO Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the
* file on disk actually has a RIFF INFO tag.
*
* \see hasInfoTag()
*/
Info::Tag *InfoTag() const;
/*!
* Implements the unified property interface -- export function.
@ -92,6 +131,8 @@ namespace TagLib {
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
@ -109,12 +150,35 @@ namespace TagLib {
*/
virtual bool save();
bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4);
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has a RIFF INFO tag.
*
* \see InfoTag()
*/
bool hasInfoTag() const;
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void strip(TagTypes tags);
/*!
* Returns the index of the chunk that its name is "LIST" and list type is "INFO".
*/
uint findInfoTagChunk();
class FilePrivate;
FilePrivate *d;
};

View File

@ -115,12 +115,12 @@ TagLib::uint RIFF::WAV::Properties::sampleFrames() const
void RIFF::WAV::Properties::read(const ByteVector &data)
{
d->format = data.mid(0, 2).toShort(false);
d->channels = data.mid(2, 2).toShort(false);
d->sampleRate = data.mid(4, 4).toUInt(false);
d->sampleWidth = data.mid(14, 2).toShort(false);
d->format = data.toShort(0, false);
d->channels = data.toShort(2, false);
d->sampleRate = data.toUInt(4, false);
d->sampleWidth = data.toShort(14, false);
uint byteRate = data.mid(8, 4).toUInt(false);
const uint byteRate = data.toUInt(8, false);
d->bitrate = byteRate * 8 / 1000;
d->length = byteRate > 0 ? d->streamLength / byteRate : 0;

View File

@ -47,7 +47,8 @@ S3M::File::File(FileName file, bool readProperties,
Mod::FileBase(file),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
S3M::File::File(IOStream *stream, bool readProperties,
@ -55,7 +56,8 @@ S3M::File::File(IOStream *stream, bool readProperties,
Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle))
{
read(readProperties);
if(isOpen())
read(readProperties);
}
S3M::File::~File()

View File

@ -36,18 +36,22 @@ namespace TagLib {
class TAGLIB_EXPORT File : public Mod::FileBase {
public:
/*!
* Contructs a ScreamTracker III file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a ScreamTracker III from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a ScreamTracker III file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
* Constructs a ScreamTracker III file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.

View File

@ -40,8 +40,4 @@
#define TAGLIB_EXPORT
#endif
#ifndef TAGLIB_NO_CONFIG
#include "taglib_config.h"
#endif
#endif

View File

@ -26,8 +26,10 @@
#ifndef TAGLIB_H
#define TAGLIB_H
#include "taglib_config.h"
#define TAGLIB_MAJOR_VERSION 1
#define TAGLIB_MINOR_VERSION 7
#define TAGLIB_MINOR_VERSION 8
#define TAGLIB_PATCH_VERSION 0
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1))
@ -44,23 +46,6 @@
#include <string>
#ifdef __APPLE__
# include <libkern/OSAtomic.h>
# define TAGLIB_ATOMIC_MAC
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# define NOMINMAX
# include <windows.h>
# define TAGLIB_ATOMIC_WIN
#elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \
&& (defined(__i386__) || defined(__i486__) || defined(__i586__) || \
defined(__i686__) || defined(__x86_64) || defined(__ia64)) \
&& !defined(__INTEL_COMPILER)
# define TAGLIB_ATOMIC_GCC
#elif defined(__ia64) && defined(__INTEL_COMPILER)
# include <ia64intrin.h>
# define TAGLIB_ATOMIC_GCC
#endif
//! A namespace for all TagLib related classes and functions
/*!
@ -75,10 +60,13 @@ namespace TagLib {
class String;
typedef wchar_t wchar;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef wchar_t wchar; // Assumed to be sufficient to store a UTF-16 char.
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long long ulonglong;
// long/ulong can be either 32-bit or 64-bit wide.
typedef unsigned long ulong;
/*!
@ -86,50 +74,6 @@ namespace TagLib {
* so I'm providing something here that should be constant.
*/
typedef std::basic_string<wchar> wstring;
#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
/*!
* \internal
* This is just used as a base class for shared classes in TagLib.
*
* \warning This <b>is not</b> part of the TagLib public API!
*/
class RefCounter
{
public:
RefCounter() : refCount(1) {}
#ifdef TAGLIB_ATOMIC_MAC
void ref() { OSAtomicIncrement32Barrier(const_cast<int32_t*>(&refCount)); }
bool deref() { return ! OSAtomicDecrement32Barrier(const_cast<int32_t*>(&refCount)); }
int32_t count() { return refCount; }
private:
volatile int32_t refCount;
#elif defined(TAGLIB_ATOMIC_WIN)
void ref() { InterlockedIncrement(&refCount); }
bool deref() { return ! InterlockedDecrement(&refCount); }
long count() { return refCount; }
private:
volatile long refCount;
#elif defined(TAGLIB_ATOMIC_GCC)
void ref() { __sync_add_and_fetch(&refCount, 1); }
bool deref() { return ! __sync_sub_and_fetch(&refCount, 1); }
int count() { return refCount; }
private:
volatile int refCount;
#else
void ref() { refCount++; }
bool deref() { return ! --refCount; }
int count() { return refCount; }
private:
uint refCount;
#endif
};
#endif // DO_NOT_DOCUMENT
}
/*!
@ -143,7 +87,7 @@ namespace TagLib {
* - A clean, high level, C++ API to handling audio meta data.
* - Format specific APIs for advanced API users.
* - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats.
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis and Speex file formats.
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats.
* - Basic audio file properties such as length, sample rate, etc.
* - Long term binary and source compatibility.
* - Extensible design, notably the ability to add other formats or extend current formats as a library user.

View File

@ -23,230 +23,327 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#include <cstdio>
#include <cstring>
#include <tstring.h>
#include <tdebug.h>
#include <string.h>
#include "trefcounter.h"
#include "tutils.h"
#include "tbytevector.h"
// This is a bit ugly to keep writing over and over again.
// A rather obscure feature of the C++ spec that I hadn't thought of that makes
// working with C libs much more effecient. There's more here:
// working with C libs much more efficient. There's more here:
//
// http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
#define DATA(x) (&(x->data[0]))
#define DATA(x) (&(x->data->data[0]))
namespace TagLib {
static const char hexTable[17] = "0123456789abcdef";
static const uint crcTable[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
static const char hexTable[17] = "0123456789abcdef";
/*!
* A templatized KMP find that works both with a ByteVector and a ByteVectorMirror.
*/
template <class Vector>
int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
{
if(pattern.size() > v.size() || offset > v.size() - 1)
return -1;
// Let's go ahead and special case a pattern of size one since that's common
// and easy to make fast.
if(pattern.size() == 1) {
char p = pattern[0];
for(uint i = offset; i < v.size(); i++) {
if(v[i] == p && (i - offset) % byteAlign == 0)
return i;
}
return -1;
}
uchar lastOccurrence[256];
for(uint i = 0; i < 256; ++i)
lastOccurrence[i] = uchar(pattern.size());
for(uint i = 0; i < pattern.size() - 1; ++i)
lastOccurrence[uchar(pattern[i])] = uchar(pattern.size() - i - 1);
for(uint i = pattern.size() - 1 + offset; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
int iBuffer = i;
int iPattern = pattern.size() - 1;
while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
--iBuffer;
--iPattern;
}
if(-1 == iPattern && (iBuffer + 1 - offset) % byteAlign == 0)
return iBuffer + 1;
}
static const uint crcTable[256] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/*!
* A templatized straightforward find that works with the types
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
*/
template <class TIterator>
int findChar(
const TIterator dataBegin, const TIterator dataEnd,
char c, uint offset, int byteAlign)
{
const size_t dataSize = dataEnd - dataBegin;
if(dataSize == 0 || offset > dataSize - 1)
return -1;
// n % 0 is invalid
if(byteAlign == 0)
return -1;
for(TIterator it = dataBegin + offset; it < dataEnd; it += byteAlign) {
if(*it == c)
return (it - dataBegin);
}
/*!
* Wraps the accessors to a ByteVector to make the search algorithm access the
* elements in reverse.
*
* \see vectorFind()
* \see ByteVector::rfind()
*/
class ByteVectorMirror
{
public:
ByteVectorMirror(const ByteVector &source) : v(source) {}
char operator[](int index) const
{
return v[v.size() - index - 1];
}
char at(int index) const
{
return v.at(v.size() - index - 1);
}
ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
{
return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
}
uint size() const
{
return v.size();
}
int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
{
ByteVectorMirror v(*this);
if(offset > 0) {
offset = size() - offset - pattern.size();
if(offset >= size())
offset = 0;
}
const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
// If the offset is zero then we need to adjust the location in the search
// to be appropriately reversed. If not we need to account for the fact
// that the recursive call (called from the above line) has already ajusted
// for this but that the normal templatized find above will add the offset
// to the returned value.
//
// This is a little confusing at first if you don't first stop to think
// through the logic involved in the forward search.
if(pos == -1)
return -1;
return size() - pos - pattern.size();
}
private:
const ByteVector &v;
};
template <class T>
T toNumber(const std::vector<char> &data, bool mostSignificantByteFirst)
{
T sum = 0;
if(data.size() <= 0) {
debug("ByteVectorMirror::toNumber<T>() -- data is empty, returning 0");
return sum;
}
uint size = sizeof(T);
uint last = data.size() > size ? size - 1 : data.size() - 1;
for(uint i = 0; i <= last; i++)
sum |= (T) uchar(data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
return sum;
}
template <class T>
ByteVector fromNumber(T value, bool mostSignificantByteFirst)
{
int size = sizeof(T);
ByteVector v(size, 0);
for(int i = 0; i < size; i++)
v[i] = uchar(value >> ((mostSignificantByteFirst ? size - 1 - i : i) * 8) & 0xff);
return v;
}
return -1;
}
using namespace TagLib;
/*!
* A templatized KMP find that works with the types
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
*/
template <class TIterator>
int findVector(
const TIterator dataBegin, const TIterator dataEnd,
const TIterator patternBegin, const TIterator patternEnd,
uint offset, int byteAlign)
{
const size_t dataSize = dataEnd - dataBegin;
const size_t patternSize = patternEnd - patternBegin;
if(patternSize > dataSize || offset > dataSize - 1)
return -1;
// n % 0 is invalid
if(byteAlign == 0)
return -1;
// Special case that pattern contains just single char.
if(patternSize == 1)
return findChar(dataBegin, dataEnd, *patternBegin, offset, byteAlign);
size_t lastOccurrence[256];
for(size_t i = 0; i < 256; ++i)
lastOccurrence[i] = patternSize;
for(size_t i = 0; i < patternSize - 1; ++i)
lastOccurrence[static_cast<uchar>(*(patternBegin + i))] = patternSize - i - 1;
TIterator it = dataBegin + patternSize - 1 + offset;
while(true)
{
TIterator itBuffer = it;
TIterator itPattern = patternBegin + patternSize - 1;
while(*itBuffer == *itPattern)
{
if(itPattern == patternBegin)
{
if((itBuffer - dataBegin - offset) % byteAlign == 0)
return (itBuffer - dataBegin);
else
break;
}
--itBuffer;
--itPattern;
}
const size_t step = lastOccurrence[static_cast<uchar>(*it)];
if(dataEnd - step <= it)
break;
it += step;
}
return -1;
}
template <class T>
T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignificantByteFirst)
{
if(offset >= v.size()) {
debug("toNumber<T>() -- No data to convert. Returning 0.");
return 0;
}
length = std::min(length, v.size() - offset);
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<uchar>(v[offset + i])) << shift;
}
return sum;
}
template <class T>
T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst)
{
if(offset + sizeof(T) > v.size())
return toNumber<T>(v, offset, v.size() - offset, mostSignificantByteFirst);
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
T tmp;
::memcpy(&tmp, v.data() + offset, sizeof(T));
#if SYSTEM_BYTEORDER == 1
const bool swap = mostSignificantByteFirst;
#else
const bool swap != mostSignificantByteFirst;
#endif
if(swap)
return byteSwap(tmp);
else
return tmp;
}
template <class T>
ByteVector fromNumber(T value, bool mostSignificantByteFirst)
{
const size_t size = sizeof(T);
#if SYSTEM_BYTEORDER == 1
const bool swap = mostSignificantByteFirst;
#else
const bool swap != mostSignificantByteFirst;
#endif
if(swap)
value = byteSwap(value);
return ByteVector(reinterpret_cast<const char *>(&value), size);
}
class DataPrivate : public RefCounter
{
public:
DataPrivate()
{
}
DataPrivate(const std::vector<char> &v, uint offset, uint length)
: data(v.begin() + offset, v.begin() + offset + length)
{
}
// A char* can be an iterator.
DataPrivate(const char *begin, const char *end)
: data(begin, end)
{
}
DataPrivate(uint len, char c)
: data(len, c)
{
}
std::vector<char> data;
};
class ByteVector::ByteVectorPrivate : public RefCounter
{
public:
ByteVectorPrivate() : RefCounter(), size(0) {}
ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v), size(v.size()) {}
ByteVectorPrivate(TagLib::uint len, char value) : RefCounter(), data(len, value), size(len) {}
ByteVectorPrivate()
: RefCounter()
, data(new DataPrivate())
, offset(0)
, length(0)
{
}
std::vector<char> data;
ByteVectorPrivate(ByteVectorPrivate *d, uint o, uint l)
: RefCounter()
, data(d->data)
, offset(d->offset + o)
, length(l)
{
data->ref();
}
// std::vector<T>::size() is very slow, so we'll cache the value
ByteVectorPrivate(const std::vector<char> &v, uint o, uint l)
: RefCounter()
, data(new DataPrivate(v, o, l))
, offset(0)
, length(l)
{
}
uint size;
ByteVectorPrivate(uint l, char c)
: RefCounter()
, data(new DataPrivate(l, c))
, offset(0)
, length(l)
{
}
ByteVectorPrivate(const char *s, uint l)
: RefCounter()
, data(new DataPrivate(s, s + l))
, offset(0)
, length(l)
{
}
void detach()
{
if(data->count() > 1) {
data->deref();
data = new DataPrivate(data->data, offset, length);
offset = 0;
}
}
~ByteVectorPrivate()
{
if(data->deref())
delete data;
}
ByteVectorPrivate &operator=(const ByteVectorPrivate &x)
{
if(&x != this)
{
if(data->deref())
delete data;
data = x.data;
data->ref();
}
return *this;
}
DataPrivate *data;
uint offset;
uint length;
};
////////////////////////////////////////////////////////////////////////////////
@ -257,14 +354,10 @@ ByteVector ByteVector::null;
ByteVector ByteVector::fromCString(const char *s, uint length)
{
ByteVector v;
if(length == 0xffffffff)
v.setData(s);
return ByteVector(s, ::strlen(s));
else
v.setData(s, length);
return v;
return ByteVector(s, length);
}
ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
@ -274,12 +367,12 @@ ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
ByteVector ByteVector::fromShort(short value, bool mostSignificantByteFirst)
{
return fromNumber<short>(value, mostSignificantByteFirst);
return fromNumber<ushort>(value, mostSignificantByteFirst);
}
ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
{
return fromNumber<long long>(value, mostSignificantByteFirst);
return fromNumber<unsigned long long>(value, mostSignificantByteFirst);
}
////////////////////////////////////////////////////////////////////////////////
@ -287,37 +380,39 @@ ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFir
////////////////////////////////////////////////////////////////////////////////
ByteVector::ByteVector()
: d(new ByteVectorPrivate())
{
d = new ByteVectorPrivate;
}
ByteVector::ByteVector(uint size, char value)
: d(new ByteVectorPrivate(size, value))
{
d = new ByteVectorPrivate(size, value);
}
ByteVector::ByteVector(const ByteVector &v) : d(v.d)
ByteVector::ByteVector(const ByteVector &v)
: d(v.d)
{
d->ref();
}
ByteVector::ByteVector(char c)
ByteVector::ByteVector(const ByteVector &v, uint offset, uint length)
: d(new ByteVectorPrivate(v.d, offset, length))
{
}
ByteVector::ByteVector(char c)
: d(new ByteVectorPrivate(1, c))
{
d = new ByteVectorPrivate;
d->data.push_back(c);
d->size = 1;
}
ByteVector::ByteVector(const char *data, uint length)
: d(new ByteVectorPrivate(data, length))
{
d = new ByteVectorPrivate;
setData(data, length);
}
ByteVector::ByteVector(const char *data)
: d(new ByteVectorPrivate(data, ::strlen(data)))
{
d = new ByteVectorPrivate;
setData(data);
}
ByteVector::~ByteVector()
@ -326,74 +421,68 @@ ByteVector::~ByteVector()
delete d;
}
ByteVector &ByteVector::setData(const char *data, uint length)
ByteVector &ByteVector::setData(const char *s, uint length)
{
detach();
resize(length);
if(length > 0)
::memcpy(DATA(d), data, length);
*this = ByteVector(s, length);
return *this;
}
ByteVector &ByteVector::setData(const char *data)
{
return setData(data, ::strlen(data));
*this = ByteVector(data);
return *this;
}
char *ByteVector::data()
{
detach();
return size() > 0 ? DATA(d) : 0;
return size() > 0 ? (DATA(d) + d->offset) : 0;
}
const char *ByteVector::data() const
{
return size() > 0 ? DATA(d) : 0;
return size() > 0 ? (DATA(d) + d->offset) : 0;
}
ByteVector ByteVector::mid(uint index, uint length) const
{
ByteVector v;
index = std::min(index, size());
length = std::min(length, size() - index);
if(index > size())
return v;
ConstIterator endIt;
if(length < size() - index)
endIt = d->data.begin() + index + length;
else
endIt = d->data.end();
v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
v.d->size = v.d->data.size();
return v;
return ByteVector(*this, index, length);
}
char ByteVector::at(uint index) const
{
return index < size() ? d->data[index] : 0;
return index < size() ? DATA(d)[d->offset + index] : 0;
}
int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
{
return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
return findVector<ConstIterator>(
begin(), end(), pattern.begin(), pattern.end(), offset, byteAlign);
}
int ByteVector::find(char c, uint offset, int byteAlign) const
{
return findChar<ConstIterator>(begin(), end(), c, offset, byteAlign);
}
int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
{
// Ok, this is a little goofy, but pretty cool after it sinks in. Instead of
// reversing the find method's Boyer-Moore search algorithm I created a "mirror"
// for a ByteVector to reverse the behavior of the accessors.
if(offset > 0) {
offset = size() - offset - pattern.size();
if(offset >= size())
offset = 0;
}
ByteVectorMirror v(*this);
ByteVectorMirror p(pattern);
const int pos = findVector<ConstReverseIterator>(
rbegin(), rend(), pattern.rbegin(), pattern.rend(), offset, byteAlign);
return v.find(p, offset, byteAlign);
if(pos == -1)
return -1;
else
return size() - pos - pattern.size();
}
bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
@ -402,18 +491,11 @@ bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint pattern
patternLength = pattern.size();
// do some sanity checking -- all of these things are needed for the search to be valid
if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
const uint compareLength = patternLength - patternOffset;
if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0)
return false;
// loop through looking for a mismatch
for(uint i = 0; i < patternLength - patternOffset; i++) {
if(at(i + offset) != pattern[i + patternOffset])
return false;
}
return true;
return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0);
}
bool ByteVector::startsWith(const ByteVector &pattern) const
@ -511,74 +593,92 @@ int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
ByteVector &ByteVector::append(const ByteVector &v)
{
if(v.d->size == 0)
return *this; // Simply return if appending nothing.
if(v.d->length != 0)
{
detach();
detach();
uint originalSize = d->size;
resize(d->size + v.d->size);
::memcpy(DATA(d) + originalSize, DATA(v.d), v.size());
uint originalSize = size();
resize(originalSize + v.size());
::memcpy(data() + originalSize, v.data(), v.size());
}
return *this;
}
ByteVector &ByteVector::clear()
{
detach();
d->data.clear();
d->size = 0;
*this = ByteVector();
return *this;
}
TagLib::uint ByteVector::size() const
{
return d->size;
return d->length;
}
ByteVector &ByteVector::resize(uint size, char padding)
{
if(d->size < size) {
d->data.reserve(size);
d->data.insert(d->data.end(), size - d->size, padding);
if(size != d->length) {
detach();
d->data->data.resize(d->offset + size, padding);
d->length = size;
}
else
d->data.erase(d->data.begin() + size, d->data.end());
d->size = size;
return *this;
}
ByteVector::Iterator ByteVector::begin()
{
return d->data.begin();
return d->data->data.begin() + d->offset;
}
ByteVector::ConstIterator ByteVector::begin() const
{
return d->data.begin();
return d->data->data.begin() + d->offset;
}
ByteVector::Iterator ByteVector::end()
{
return d->data.end();
return d->data->data.begin() + d->offset + d->length;
}
ByteVector::ConstIterator ByteVector::end() const
{
return d->data.end();
return d->data->data.begin() + d->offset + d->length;
}
ByteVector::ReverseIterator ByteVector::rbegin()
{
std::vector<char> &v = d->data->data;
return v.rbegin() + (v.size() - (d->offset + d->length));
}
ByteVector::ConstReverseIterator ByteVector::rbegin() const
{
std::vector<char> &v = d->data->data;
return v.rbegin() + (v.size() - (d->offset + d->length));
}
ByteVector::ReverseIterator ByteVector::rend()
{
std::vector<char> &v = d->data->data;
return v.rbegin() + (v.size() - d->offset);
}
ByteVector::ConstReverseIterator ByteVector::rend() const
{
std::vector<char> &v = d->data->data;
return v.rbegin() + (v.size() - d->offset);
}
bool ByteVector::isNull() const
{
return d == null.d;
return (d == null.d);
}
bool ByteVector::isEmpty() const
{
return d->data.size() == 0;
return (d->length == 0);
}
TagLib::uint ByteVector::checksum() const
@ -591,42 +691,66 @@ TagLib::uint ByteVector::checksum() const
TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
{
return toNumber<uint>(d->data, mostSignificantByteFirst);
return toNumber<uint>(*this, 0, mostSignificantByteFirst);
}
TagLib::uint ByteVector::toUInt(uint offset, bool mostSignificantByteFirst) const
{
return toNumber<uint>(*this, offset, mostSignificantByteFirst);
}
TagLib::uint ByteVector::toUInt(uint offset, uint length, bool mostSignificantByteFirst) const
{
return toNumber<uint>(*this, offset, length, mostSignificantByteFirst);
}
short ByteVector::toShort(bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst);
}
short ByteVector::toShort(uint offset, bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst);
}
unsigned short ByteVector::toUShort(bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(d->data, mostSignificantByteFirst);
return toNumber<unsigned short>(*this, 0, mostSignificantByteFirst);
}
unsigned short ByteVector::toUShort(uint offset, bool mostSignificantByteFirst) const
{
return toNumber<unsigned short>(*this, offset, mostSignificantByteFirst);
}
long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
{
return toNumber<unsigned long long>(d->data, mostSignificantByteFirst);
return toNumber<unsigned long long>(*this, 0, mostSignificantByteFirst);
}
long long ByteVector::toLongLong(uint offset, bool mostSignificantByteFirst) const
{
return toNumber<unsigned long long>(*this, offset, mostSignificantByteFirst);
}
const char &ByteVector::operator[](int index) const
{
return d->data[index];
return d->data->data[d->offset + index];
}
char &ByteVector::operator[](int index)
{
detach();
return d->data[index];
return d->data->data[d->offset + index];
}
bool ByteVector::operator==(const ByteVector &v) const
{
if(d->size != v.d->size)
if(size() != v.size())
return false;
return ::memcmp(data(), v.data(), size()) == 0;
return (::memcmp(data(), v.data(), size()) == 0);
}
bool ByteVector::operator!=(const ByteVector &v) const
@ -636,10 +760,10 @@ bool ByteVector::operator!=(const ByteVector &v) const
bool ByteVector::operator==(const char *s) const
{
if(d->size != ::strlen(s))
if(size() != ::strlen(s))
return false;
return ::memcmp(data(), s, d->size) == 0;
return (::memcmp(data(), s, size()) == 0);
}
bool ByteVector::operator!=(const char *s) const
@ -649,8 +773,7 @@ bool ByteVector::operator!=(const char *s) const
bool ByteVector::operator<(const ByteVector &v) const
{
int result = ::memcmp(data(), v.data(), d->size < v.d->size ? d->size : v.d->size);
const int result = ::memcmp(data(), v.data(), std::min(size(), v.size()));
if(result != 0)
return result < 0;
else
@ -697,12 +820,12 @@ ByteVector &ByteVector::operator=(const char *data)
ByteVector ByteVector::toHex() const
{
ByteVector encoded(size() * 2);
char *p = encoded.data();
uint j = 0;
for(uint i = 0; i < size(); i++) {
unsigned char c = d->data[i];
encoded[j++] = hexTable[(c >> 4) & 0x0F];
encoded[j++] = hexTable[(c ) & 0x0F];
unsigned char c = data()[i];
*p++ = hexTable[(c >> 4) & 0x0F];
*p++ = hexTable[(c ) & 0x0F];
}
return encoded;
@ -714,17 +837,24 @@ ByteVector ByteVector::toHex() const
void ByteVector::detach()
{
if(d->data->count() > 1) {
d->data->deref();
d->data = new DataPrivate(d->data->data, d->offset, d->length);
d->offset = 0;
}
if(d->count() > 1) {
d->deref();
d = new ByteVectorPrivate(d->data);
d = new ByteVectorPrivate(d->data->data, d->offset, d->length);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
std::ostream &operator<<(std::ostream &s, const ByteVector &v)
std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v)
{
for(TagLib::uint i = 0; i < v.size(); i++)
s << v[i];

View File

@ -48,6 +48,8 @@ namespace TagLib {
#ifndef DO_NOT_DOCUMENT
typedef std::vector<char>::iterator Iterator;
typedef std::vector<char>::const_iterator ConstIterator;
typedef std::vector<char>::reverse_iterator ReverseIterator;
typedef std::vector<char>::const_reverse_iterator ConstReverseIterator;
#endif
/*!
@ -62,12 +64,17 @@ namespace TagLib {
ByteVector(uint size, char value = 0);
/*!
* Contructs a byte vector that is a copy of \a v.
* Constructs a byte vector that is a copy of \a v.
*/
ByteVector(const ByteVector &v);
/*!
* Contructs a byte vector that contains \a c.
* Constructs a byte vector that is a copy of \a v.
*/
ByteVector(const ByteVector &v, uint offset, uint length);
/*!
* Constructs a byte vector that contains \a c.
*/
ByteVector(char c);
@ -135,6 +142,14 @@ namespace TagLib {
*/
int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
/*!
* Searches the char for \a c starting at \a offset and returns
* the offset. Returns \a npos if the pattern was not found. If \a byteAlign is
* specified the pattern will only be matched if it starts on a byte divisible
* by \a byteAlign (starting from \a offset).
*/
int find(char c, uint offset = 0, int byteAlign = 1) const;
/*!
* Searches the ByteVector for \a pattern starting from either the end of the
* vector or \a offset and returns the offset. Returns -1 if the pattern was
@ -222,6 +237,26 @@ namespace TagLib {
*/
ConstIterator end() const;
/*!
* Returns a ReverseIterator that points to the front of the vector.
*/
ReverseIterator rbegin();
/*!
* Returns a ConstReverseIterator that points to the front of the vector.
*/
ConstReverseIterator rbegin() const;
/*!
* Returns a ReverseIterator that points to the back of the vector.
*/
ReverseIterator rend();
/*!
* Returns a ConstReverseIterator that points to the back of the vector.
*/
ConstReverseIterator rend() const;
/*!
* Returns true if the vector is null.
*
@ -256,7 +291,32 @@ namespace TagLib {
uint toUInt(bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 2 bytes of the vector to a short.
* Converts the 4 bytes at \a offset of the vector to an unsigned integer.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see fromUInt()
*/
uint toUInt(uint offset, bool mostSignificantByteFirst = true) const;
/*!
* Converts the \a length bytes at \a offset of the vector to an unsigned
* integer. If \a length is larger than 4, the excess is ignored.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
* 0x01000000 == 1.
*
* \see fromUInt()
*/
uint toUInt(uint offset, uint length, bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 2 bytes of the vector to a (signed) short.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
@ -266,6 +326,17 @@ namespace TagLib {
*/
short toShort(bool mostSignificantByteFirst = true) const;
/*!
* Converts the 2 bytes at \a offset of the vector to a (signed) short.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
*
* \see fromShort()
*/
short toShort(uint offset, bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 2 bytes of the vector to a unsigned short.
*
@ -277,6 +348,17 @@ namespace TagLib {
*/
unsigned short toUShort(bool mostSignificantByteFirst = true) const;
/*!
* Converts the 2 bytes at \a offset of the vector to a unsigned short.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 $01 == 0x0001 == 1, if false, $01 00 == 0x01000000 == 1.
*
* \see fromShort()
*/
unsigned short toUShort(uint offset, bool mostSignificantByteFirst = true) const;
/*!
* Converts the first 8 bytes of the vector to a (signed) long long.
*
@ -289,6 +371,18 @@ namespace TagLib {
*/
long long toLongLong(bool mostSignificantByteFirst = true) const;
/*!
* Converts the 8 bytes at \a offset of the vector to a (signed) long long.
*
* If \a mostSignificantByteFirst is true this will operate left to right
* evaluating the integer. For example if \a mostSignificantByteFirst is
* true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
* if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
*
* \see fromUInt()
*/
long long toLongLong(uint offset, bool mostSignificantByteFirst = true) const;
/*!
* Creates a 4 byte ByteVector based on \a value. If
* \a mostSignificantByteFirst is true, then this will operate left to right
@ -413,7 +507,6 @@ namespace TagLib {
class ByteVectorPrivate;
ByteVectorPrivate *d;
};
}
/*!

View File

@ -23,33 +23,77 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef NDEBUG
#include <iostream>
#include <bitset>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "tdebug.h"
#include "tstring.h"
#include "tdebuglistener.h"
#include <bitset>
#include <cstdio>
#include <cstdarg>
using namespace TagLib;
void TagLib::debug(const String &s)
namespace
{
std::cerr << "TagLib: " << s << std::endl;
}
String format(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
void TagLib::debugData(const ByteVector &v)
{
for(uint i = 0; i < v.size(); i++) {
char buf[256];
std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i])
<< std::endl;
#if defined(HAVE_SNPRINTF)
std::bitset<8> b(v[i]);
vsnprintf(buf, sizeof(buf), fmt, args);
for(int j = 0; j < 8; j++)
std::cout << i << ":" << j << " " << b.test(j) << std::endl;
#elif defined(HAVE_SPRINTF_S)
std::cout << std::endl;
vsprintf_s(buf, fmt, args);
#else
// Be careful. May cause a buffer overflow.
vsprintf(buf, fmt, args);
#endif
va_end(args);
return String(buf);
}
}
namespace TagLib
{
// The instance is defined in tdebuglistener.cpp.
extern DebugListener *debugListener;
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 = format("*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n",
i, v[i], v[i], v[i], bits.c_str());
debugListener->printMessage(msg);
}
#endif
}
}

View File

@ -32,11 +32,11 @@ namespace TagLib {
class ByteVector;
#ifndef DO_NOT_DOCUMENT
#ifndef NDEBUG
/*!
* A simple function that prints debugging output to cerr if debugging is
* not disabled.
* 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
* symbols in your build if TagLib is built with NDEBUG defined and your
@ -45,7 +45,7 @@ namespace TagLib {
* \internal
*/
void debug(const String &s);
/*!
* For debugging binary data.
*
@ -56,16 +56,7 @@ namespace TagLib {
* \internal
*/
void debugData(const ByteVector &v);
#else
// Define these to an empty statement if debugging is disabled.
#define debug(x)
#define debugData(x)
#endif
#endif
}
#endif
#endif

View File

@ -0,0 +1,85 @@
/***************************************************************************
copyright : (C) 2013 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "tdebuglistener.h"
#include <iostream>
#include <bitset>
#ifdef _WIN32
# include <windows.h>
#endif
using namespace TagLib;
namespace
{
class DefaultListener : public DebugListener
{
public:
virtual void printMessage(const String &msg)
{
#ifdef _WIN32
const wstring wstr = msg.toWString();
const int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
if(len != 0) {
std::vector<char> buf(len);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buf[0], len, NULL, NULL);
std::cerr << std::string(&buf[0]);
}
#else
std::cerr << msg;
#endif
}
};
DefaultListener defaultListener;
}
namespace TagLib
{
DebugListener *debugListener = &defaultListener;
DebugListener::DebugListener()
{
}
DebugListener::~DebugListener()
{
}
void setDebugListener(DebugListener *listener)
{
if(listener)
debugListener = listener;
else
debugListener = &defaultListener;
}
}

View File

@ -0,0 +1,74 @@
/***************************************************************************
copyright : (C) 2013 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DEBUGLISTENER_H
#define TAGLIB_DEBUGLISTENER_H
#include "taglib_export.h"
#include "tstring.h"
namespace TagLib
{
//! An abstraction for the listener to the debug messages.
/*!
* This class enables you to handle the debug messages in your preferred
* way by subclassing this class, reimplementing printMessage() and setting
* your reimplementation as the default with setDebugListener().
*
* \see setDebugListener()
*/
class TAGLIB_EXPORT DebugListener
{
public:
DebugListener();
virtual ~DebugListener();
/*!
* When overridden in a derived class, redirects \a msg to your preferred
* channel such as stderr, Windows debugger or so forth.
*/
virtual void printMessage(const String &msg) = 0;
private:
// Noncopyable
DebugListener(const DebugListener &);
DebugListener &operator=(const DebugListener &);
};
/*!
* Sets the listener that decides how the debug messages are redirected.
* If the parameter \a listener is null, the previous listener is released
* and default stderr listener is restored.
*
* \note The caller is responsible for deleting the previous listener
* as needed after it is released.
*
* \see DebugListener
*/
TAGLIB_EXPORT void setDebugListener(DebugListener *listener);
}
#endif

View File

@ -29,21 +29,14 @@
#include "tdebug.h"
#include "tpropertymap.h"
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#ifdef _WIN32
# include <wchar.h>
# include <windows.h>
# include <io.h>
# define ftruncate _chsize
#else
# include <stdio.h>
# include <unistd.h>
#endif
#include <stdlib.h>
#ifndef R_OK
# define R_OK 4
#endif
@ -60,6 +53,7 @@
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "opusfile.h"
#include "trueaudiofile.h"
#include "aifffile.h"
#include "wavfile.h"
@ -68,9 +62,19 @@
#include "s3mfile.h"
#include "itfile.h"
#include "xmfile.h"
#include "mp4file.h"
using namespace TagLib;
namespace
{
#ifdef _WIN32
const TagLib::uint BufferSize = 8192;
#else
const TagLib::uint BufferSize = 1024;
#endif
}
class File::FilePrivate
{
public:
@ -79,7 +83,6 @@ public:
IOStream *stream;
bool streamOwner;
bool valid;
static const uint bufferSize = 1024;
};
File::FilePrivate::FilePrivate(IOStream *stream, bool owner) :
@ -135,6 +138,8 @@ PropertyMap File::properties() const
return dynamic_cast<const Ogg::FLAC::File* >(this)->properties();
if(dynamic_cast<const Ogg::Speex::File* >(this))
return dynamic_cast<const Ogg::Speex::File* >(this)->properties();
if(dynamic_cast<const Ogg::Opus::File* >(this))
return dynamic_cast<const Ogg::Opus::File* >(this)->properties();
if(dynamic_cast<const Ogg::Vorbis::File* >(this))
return dynamic_cast<const Ogg::Vorbis::File* >(this)->properties();
if(dynamic_cast<const RIFF::AIFF::File* >(this))
@ -149,12 +154,10 @@ PropertyMap File::properties() const
return dynamic_cast<const WavPack::File* >(this)->properties();
if(dynamic_cast<const XM::File* >(this))
return dynamic_cast<const XM::File* >(this)->properties();
// no specialized implementation available -> use generic one
// - ASF: ugly format, largely undocumented, not worth implementing
// dict interface ...
// - MP4: taglib's MP4::Tag does not really support anything beyond
// the basic implementation, therefor we use just the default Tag
// interface
if(dynamic_cast<const MP4::File* >(this))
return dynamic_cast<const MP4::File* >(this)->properties();
if(dynamic_cast<const ASF::File* >(this))
return dynamic_cast<const ASF::File* >(this)->properties();
return tag()->properties();
}
@ -170,24 +173,20 @@ void File::removeUnsupportedProperties(const StringList &properties)
dynamic_cast<MPC::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<MPEG::File* >(this))
dynamic_cast<MPEG::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<Ogg::FLAC::File* >(this))
dynamic_cast<Ogg::FLAC::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<Ogg::Speex::File* >(this))
dynamic_cast<Ogg::Speex::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<Ogg::Vorbis::File* >(this))
dynamic_cast<Ogg::Vorbis::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<RIFF::AIFF::File* >(this))
dynamic_cast<RIFF::AIFF::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<RIFF::WAV::File* >(this))
dynamic_cast<RIFF::WAV::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<S3M::File* >(this))
dynamic_cast<S3M::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<TrueAudio::File* >(this))
dynamic_cast<TrueAudio::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<WavPack::File* >(this))
dynamic_cast<WavPack::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<XM::File* >(this))
dynamic_cast<XM::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<MP4::File* >(this))
dynamic_cast<MP4::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
dynamic_cast<ASF::File* >(this)->removeUnsupportedProperties(properties);
else
tag()->removeUnsupportedProperties(properties);
}
@ -210,6 +209,8 @@ PropertyMap File::setProperties(const PropertyMap &properties)
return dynamic_cast<Ogg::FLAC::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Speex::File* >(this))
return dynamic_cast<Ogg::Speex::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Opus::File* >(this))
return dynamic_cast<Ogg::Opus::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Vorbis::File* >(this))
return dynamic_cast<Ogg::Vorbis::File* >(this)->setProperties(properties);
else if(dynamic_cast<RIFF::AIFF::File* >(this))
@ -224,6 +225,10 @@ PropertyMap File::setProperties(const PropertyMap &properties)
return dynamic_cast<WavPack::File* >(this)->setProperties(properties);
else if(dynamic_cast<XM::File* >(this))
return dynamic_cast<XM::File* >(this)->setProperties(properties);
else if(dynamic_cast<MP4::File* >(this))
return dynamic_cast<MP4::File* >(this)->setProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
return dynamic_cast<ASF::File* >(this)->setProperties(properties);
else
return tag()->setProperties(properties);
}
@ -240,7 +245,7 @@ void File::writeBlock(const ByteVector &data)
long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
if(!d->stream || pattern.size() > d->bufferSize)
if(!d->stream || pattern.size() > bufferSize())
return -1;
// The position in the file that the current buffer starts at.
@ -281,20 +286,20 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be
// then check for "before". The order is important because it gives priority
// to "real" matches.
for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
// (1) previous partial match
if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
const int patternOffset = (d->bufferSize - previousPartialMatch);
if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
const int patternOffset = (bufferSize() - previousPartialMatch);
if(buffer.containsAt(pattern, 0, patternOffset)) {
seek(originalPosition);
return bufferOffset - d->bufferSize + previousPartialMatch;
return bufferOffset - bufferSize() + previousPartialMatch;
}
}
if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(bufferSize()) > beforePreviousPartialMatch) {
const int beforeOffset = (bufferSize() - beforePreviousPartialMatch);
if(buffer.containsAt(before, 0, beforeOffset)) {
seek(originalPosition);
return -1;
@ -321,7 +326,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be
if(!before.isNull())
beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);
bufferOffset += d->bufferSize;
bufferOffset += bufferSize();
}
// Since we hit the end of the file, reset the status before continuing.
@ -336,7 +341,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be
long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
{
if(!d->stream || pattern.size() > d->bufferSize)
if(!d->stream || pattern.size() > bufferSize())
return -1;
// The position in the file that the current buffer starts at.
@ -360,17 +365,17 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b
long bufferOffset;
if(fromOffset == 0) {
seek(-1 * int(d->bufferSize), End);
seek(-1 * int(bufferSize()), End);
bufferOffset = tell();
}
else {
seek(fromOffset + -1 * int(d->bufferSize), Beginning);
seek(fromOffset + -1 * int(bufferSize()), Beginning);
bufferOffset = tell();
}
// See the notes in find() for an explanation of this algorithm.
for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
// TODO: (1) previous partial match
@ -389,7 +394,7 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b
// TODO: (3) partial match
bufferOffset -= d->bufferSize;
bufferOffset -= bufferSize();
seek(bufferOffset);
}
@ -488,7 +493,7 @@ bool File::isWritable(const char *file)
TagLib::uint File::bufferSize()
{
return FilePrivate::bufferSize;
return BufferSize;
}
void File::setValid(bool valid)

View File

@ -80,12 +80,14 @@ namespace TagLib {
/*!
* Exports the tags of the file as dictionary mapping (human readable) tag
* names (Strings) to StringLists of tag values. Calls the according specialization
* in the File subclasses.
* names (uppercase Strings) to StringLists of tag values. Calls the according
* specialization in the File subclasses.
* For each metadata object of the file that could not be parsed into the PropertyMap
* format, the returend 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
* tag) only the most "modern" one will be exported (ID3v2 in this case).
* BIC: Will be made virtual in future releases.
*/
PropertyMap properties() const;
@ -105,9 +107,15 @@ namespace TagLib {
* If some value(s) could not be written imported to the specific metadata format,
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
* indicating that no problems occured.
* With file types that support several tag formats (for instance, MP3 files can have
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
* be taken into account for the return value of this function.
* See the documentation of the subclass implementations for detailed descriptions.
* BIC: will become pure virtual in the future
*/
PropertyMap setProperties(const PropertyMap &properties);
/*!
* Returns a pointer to this file's audio properties. This should be
* reimplemented in the concrete subclasses. If no audio properties were
@ -205,7 +213,7 @@ namespace TagLib {
bool isOpen() const;
/*!
* Returns true if the file is open and readble.
* Returns true if the file is open and readable.
*/
bool isValid() const;

View File

@ -27,137 +27,145 @@
#include "tstring.h"
#include "tdebug.h"
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#ifdef _WIN32
# include <wchar.h>
# include <windows.h>
# include <io.h>
# define ftruncate _chsize
#else
# include <stdio.h>
# include <unistd.h>
#endif
#include <stdlib.h>
#ifndef R_OK
# define R_OK 4
#endif
#ifndef W_OK
# define W_OK 2
#endif
using namespace TagLib;
namespace
{
#ifdef _WIN32
typedef FileName FileNameHandle;
// Uses Win32 native API instead of POSIX API to reduce the resource consumption.
#else
typedef FileName FileNameHandle;
typedef HANDLE FileHandle;
struct FileNameHandle : public std::string
{
FileNameHandle(FileName name) : std::string(name) {}
operator FileName () const { return c_str(); }
};
const TagLib::uint BufferSize = 8192;
const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE;
#endif
namespace {
FILE *openFile(const FileName &path, bool readOnly)
inline FileHandle openFile(const FileName &path, bool readOnly)
{
// Calls a proper variation of fopen() depending on the compiling environment.
const DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE);
#if defined(_WIN32)
# if defined(_MSC_VER) && (_MSC_VER >= 1400)
// Visual C++ 2005 or later.
FILE *file;
errno_t err;
if(wcslen(path) > 0)
err = _wfopen_s(&file, path, readOnly ? L"rb" : L"rb+");
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
err = fopen_s(&file, path, readOnly ? "rb" : "rb+");
if(err == 0)
return file;
else
return NULL;
# else // defined(_MSC_VER) && (_MSC_VER >= 1400)
// Visual C++.NET 2003 or earlier.
if(wcslen(path) > 0)
return _wfopen(path, readOnly ? L"rb" : L"rb+");
else
return fopen(path, readOnly ? "rb" : "rb+");
# endif // defined(_MSC_VER) && (_MSC_VER >= 1400)
#else // defined(_WIN32)
// Non-Win32
return fopen(path, readOnly ? "rb" : "rb+");
#endif // defined(_WIN32)
return InvalidFileHandle;
}
inline void closeFile(FileHandle file)
{
CloseHandle(file);
}
inline size_t readFile(FileHandle file, ByteVector &buffer)
{
DWORD length;
if(ReadFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, NULL))
return static_cast<size_t>(length);
else
return 0;
}
inline size_t writeFile(FileHandle file, const ByteVector &buffer)
{
DWORD length;
if(WriteFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, NULL))
return static_cast<size_t>(length);
else
return 0;
}
#else // _WIN32
struct FileNameHandle : public std::string
{
FileNameHandle(FileName name) : std::string(name) {}
operator FileName () const { return c_str(); }
};
typedef FILE* FileHandle;
const TagLib::uint BufferSize = 8192;
const FileHandle InvalidFileHandle = 0;
inline FileHandle openFile(const FileName &path, bool readOnly)
{
return fopen(path, readOnly ? "rb" : "rb+");
}
inline void closeFile(FileHandle file)
{
fclose(file);
}
inline size_t readFile(FileHandle file, ByteVector &buffer)
{
return fread(buffer.data(), sizeof(char), buffer.size(), file);
}
inline size_t writeFile(FileHandle file, const ByteVector &buffer)
{
return fwrite(buffer.data(), sizeof(char), buffer.size(), file);
}
#endif // _WIN32
}
class FileStream::FileStreamPrivate
{
public:
FileStreamPrivate(FileName fileName, bool openReadOnly);
FILE *file;
FileNameHandle name;
bool readOnly;
ulong size;
static const uint bufferSize = 1024;
};
FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openReadOnly) :
file(0),
name(fileName),
readOnly(true),
size(0)
{
// First try with read / write mode, if that fails, fall back to read only.
if(!openReadOnly)
file = openFile(name, false);
if(file)
readOnly = false;
else
file = openFile(name, true);
if(!file) {
debug("Could not open file " + String((const char *) name));
FileStreamPrivate(const FileName &fileName)
: file(InvalidFileHandle)
, name(fileName)
, readOnly(true)
{
}
}
FileHandle file;
FileNameHandle name;
bool readOnly;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FileStream::FileStream(FileName file, bool openReadOnly)
FileStream::FileStream(FileName fileName, bool openReadOnly)
: d(new FileStreamPrivate(fileName))
{
d = new FileStreamPrivate(file, openReadOnly);
// First try with read / write mode, if that fails, fall back to read only.
if(!openReadOnly)
d->file = openFile(fileName, false);
if(d->file != InvalidFileHandle)
d->readOnly = false;
else
d->file = openFile(fileName, true);
if(d->file == InvalidFileHandle)
{
# ifdef _WIN32
debug("Could not open file " + fileName.toString());
# else
debug("Could not open file " + String(static_cast<const char *>(d->name)));
# endif
}
}
FileStream::~FileStream()
{
if(d->file)
fclose(d->file);
if(isOpen())
closeFile(d->file);
delete d;
}
@ -168,43 +176,52 @@ FileName FileStream::name() const
ByteVector FileStream::readBlock(ulong length)
{
if(!d->file) {
debug("FileStream::readBlock() -- Invalid File");
if(!isOpen()) {
debug("File::readBlock() -- invalid file.");
return ByteVector::null;
}
if(length == 0)
return ByteVector::null;
if(length > FileStreamPrivate::bufferSize &&
length > ulong(FileStream::length()))
{
length = FileStream::length();
}
const ulong streamLength = static_cast<ulong>(FileStream::length());
if(length > bufferSize() && length > streamLength)
length = streamLength;
ByteVector v(static_cast<uint>(length));
const int count = fread(v.data(), sizeof(char), length, d->file);
v.resize(count);
return v;
ByteVector buffer(static_cast<uint>(length));
const size_t count = readFile(d->file, buffer);
buffer.resize(static_cast<uint>(count));
return buffer;
}
void FileStream::writeBlock(const ByteVector &data)
{
if(!d->file)
return;
if(d->readOnly) {
debug("File::writeBlock() -- attempted to write to a file that is not writable");
if(!isOpen()) {
debug("File::writeBlock() -- invalid file.");
return;
}
fwrite(data.data(), sizeof(char), data.size(), d->file);
if(readOnly()) {
debug("File::writeBlock() -- read only file.");
return;
}
writeFile(d->file, data);
}
void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
{
if(!d->file)
if(!isOpen()) {
debug("File::insert() -- invalid file.");
return;
}
if(readOnly()) {
debug("File::insert() -- read only file.");
return;
}
if(data.size() == replace) {
seek(start);
@ -212,10 +229,10 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
return;
}
else if(data.size() < replace) {
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());
return;
}
// Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
@ -238,71 +255,50 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace)
long readPosition = start + replace;
long writePosition = start;
ByteVector buffer;
ByteVector buffer = data;
ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));
// This is basically a special case of the loop below. Here we're just
// doing the same steps as below, but since we aren't using the same buffer
// size -- instead we're using the tag size -- this has to be handled as a
// special case. We're also using File::writeBlock() just for the tag.
// That's a bit slower than using char *'s so, we're only doing it here.
seek(readPosition);
int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
readPosition += bufferLength;
seek(writePosition);
writeBlock(data);
writePosition += data.size();
buffer = aboutToOverwrite;
// In case we've already reached the end of file...
buffer.resize(bytesRead);
// Ok, here's the main loop. We want to loop until the read fails, which
// means that we hit the end of the file.
while(!buffer.isEmpty()) {
while(true)
{
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.
seek(readPosition);
bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
const size_t bytesRead = readFile(d->file, aboutToOverwrite);
aboutToOverwrite.resize(bytesRead);
readPosition += bufferLength;
// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.
if(ulong(bytesRead) < bufferLength)
if(bytesRead < bufferLength)
clear();
// Seek to the write position and write our buffer. Increment the
// writePosition.
seek(writePosition);
fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);
writeBlock(buffer);
// We hit the end of the file.
if(bytesRead == 0)
break;
writePosition += buffer.size();
// Make the current buffer the data that we read in the beginning.
buffer = aboutToOverwrite;
// Again, we need this for the last write. We don't want to write garbage
// at the end of our file, so we need to set the buffer size to the amount
// that we actually read.
bufferLength = bytesRead;
}
}
void FileStream::removeBlock(ulong start, ulong length)
{
if(!d->file)
if(!isOpen()) {
debug("File::removeBlock() -- invalid file.");
return;
}
ulong bufferLength = bufferSize();
@ -311,23 +307,26 @@ void FileStream::removeBlock(ulong start, ulong length)
ByteVector buffer(static_cast<uint>(bufferLength));
ulong bytesRead = 1;
while(bytesRead != 0) {
for(size_t bytesRead = -1; bytesRead != 0;)
{
seek(readPosition);
bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
bytesRead = readFile(d->file, buffer);
readPosition += bytesRead;
// Check to see if we just read the last block. We need to call clear()
// if we did so that the last write succeeds.
if(bytesRead < bufferLength)
if(bytesRead < buffer.size()) {
clear();
buffer.resize(bytesRead);
}
seek(writePosition);
fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
writeFile(d->file, buffer);
writePosition += bytesRead;
}
truncate(writePosition);
}
@ -338,58 +337,125 @@ bool FileStream::readOnly() const
bool FileStream::isOpen() const
{
return (d->file != NULL);
return (d->file != InvalidFileHandle);
}
void FileStream::seek(long offset, Position p)
{
if(!d->file) {
debug("File::seek() -- trying to seek in a file that isn't opened.");
if(!isOpen()) {
debug("File::seek() -- invalid file.");
return;
}
#ifdef _WIN32
DWORD whence;
switch(p) {
case Beginning:
fseek(d->file, offset, SEEK_SET);
whence = FILE_BEGIN;
break;
case Current:
fseek(d->file, offset, SEEK_CUR);
whence = FILE_CURRENT;
break;
case End:
fseek(d->file, offset, SEEK_END);
whence = FILE_END;
break;
default:
debug("FileStream::seek() -- Invalid Position value.");
return;
}
SetFilePointer(d->file, offset, NULL, whence);
if(GetLastError() != NO_ERROR) {
debug("File::seek() -- Failed to set the file pointer.");
}
#else
int whence;
switch(p) {
case Beginning:
whence = SEEK_SET;
break;
case Current:
whence = SEEK_CUR;
break;
case End:
whence = SEEK_END;
break;
default:
debug("FileStream::seek() -- Invalid Position value.");
return;
}
fseek(d->file, offset, whence);
#endif
}
void FileStream::clear()
{
#ifdef _WIN32
// NOP
#else
clearerr(d->file);
#endif
}
long FileStream::tell() const
{
#ifdef _WIN32
const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT);
if(GetLastError() == NO_ERROR) {
return static_cast<long>(position);
}
else {
debug("File::tell() -- Failed to get the file pointer.");
return 0;
}
#else
return ftell(d->file);
#endif
}
long FileStream::length()
{
// Do some caching in case we do multiple calls.
if(d->size > 0)
return d->size;
if(!d->file)
if(!isOpen()) {
debug("File::length() -- invalid file.");
return 0;
}
long curpos = tell();
#ifdef _WIN32
const DWORD fileSize = GetFileSize(d->file, NULL);
if(GetLastError() == NO_ERROR) {
return static_cast<ulong>(fileSize);
}
else {
debug("File::length() -- Failed to get the file size.");
return 0;
}
#else
const long curpos = tell();
seek(0, End);
long endpos = tell();
const long endpos = tell();
seek(curpos, Beginning);
d->size = endpos;
return endpos;
#endif
}
////////////////////////////////////////////////////////////////////////////////
@ -398,20 +464,29 @@ long FileStream::length()
void FileStream::truncate(long length)
{
#ifdef _WIN32
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
const long currentPos = tell();
ftruncate(_fileno(d->file), length);
seek(length);
SetEndOfFile(d->file);
if(GetLastError() != NO_ERROR) {
debug("File::truncate() -- Failed to truncate the file.");
}
seek(currentPos);
#else
ftruncate(fileno(d->file), length);
const int error = ftruncate(fileno(d->file), length);
if(error != 0) {
debug("FileStream::truncate() -- Coundn't truncate the file.");
}
#endif
}
TagLib::uint FileStream::bufferSize()
{
return FileStreamPrivate::bufferSize;
return BufferSize;
}

View File

@ -27,6 +27,111 @@
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()
{
const FARPROC p = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW");
return (p != NULL);
}
// Indicates whether the system supports Unicode file names.
const bool SystemSupportsUnicode = supportsUnicode();
// Converts a UTF-16 string into a local encoding.
// This function should only be used in Windows9x systems which don't support
// Unicode file names.
std::string unicodeToAnsi(const wchar_t *wstr)
{
if(SystemSupportsUnicode) {
debug("unicodeToAnsi() - Should not be used on WinNT systems.");
}
const int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
if(len == 0)
return std::string();
std::string str(len, '\0');
WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL);
return str;
}
}
// 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.
FileName::FileName(const wchar_t *name)
: m_name (SystemSupportsUnicode ? "" : unicodeToAnsi(name))
, m_wname(SystemSupportsUnicode ? name : L"")
{
}
FileName::FileName(const char *name)
: m_name(name)
{
}
FileName::FileName(const FileName &name)
: m_name (name.m_name)
, m_wname(name.m_wname)
{
}
FileName::operator const wchar_t *() const
{
return m_wname.c_str();
}
FileName::operator const char *() const
{
return m_name.c_str();
}
const std::wstring &FileName::wstr() const
{
return m_wname;
}
const std::string &FileName::str() const
{
return m_name;
}
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::null;
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::null;
}
}
#endif // _WIN32
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

View File

@ -36,13 +36,22 @@ namespace TagLib {
class TAGLIB_EXPORT FileName
{
public:
FileName(const wchar_t *name) : m_wname(name) {}
FileName(const char *name) : m_name(name) {}
operator const wchar_t *() const { return m_wname.c_str(); }
operator const char *() const { return m_name.c_str(); }
FileName(const wchar_t *name);
FileName(const char *name);
FileName(const FileName &name);
operator const wchar_t *() const;
operator const char *() const;
const std::wstring &wstr() const;
const std::string &str() const;
String toString() const;
private:
std::string m_name;
std::wstring m_wname;
const std::string m_name;
const std::wstring m_wname;
};
#else
typedef const char *FileName;

View File

@ -24,6 +24,7 @@
***************************************************************************/
#include <algorithm>
#include "trefcounter.h"
namespace TagLib {

View File

@ -23,6 +23,8 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "trefcounter.h"
namespace TagLib {
////////////////////////////////////////////////////////////////////////////////

View File

@ -40,6 +40,65 @@ namespace TagLib {
* Note that most metadata formats pose additional conditions on the tag keys. The
* most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of
* length between 2 and 16.
*
* This class can contain any tags, but here is a list of "well-known" tags that
* you might want to use:
*
* Basic tags:
*
* - TITLE
* - ALBUM
* - ARTIST
* - ALBUMARTIST
* - SUBTITLE
* - TRACKNUMBER
* - DISCNUMBER
* - DATE
* - ORIGINALDATE
* - GENRE
* - COMMENT
*
* Sort names:
*
* - TITLESORT
* - ALBUMSORT
* - ARTISTSORT
* - ALBUMARTISTSORT
*
* Credits:
*
* - COMPOSER
* - LYRICIST
* - CONDUCTOR
* - REMIXER
* - PERFORMER:<XXXX>
*
* Other tags:
*
* - ISRC
* - ASIN
* - BPM
* - COPYRIGHT
* - ENCODEDBY
* - MOOD
* - COMMENT
* - MEDIA
* - LABEL
* - CATALOGNUMBER
* - BARCODE
*
* MusicBrainz identifiers:
*
* - MUSICBRAINZ_TRACKID
* - MUSICBRAINZ_ALBUMID
* - MUSICBRAINZ_RELEASEGROUPID
* - MUSICBRAINZ_WORKID
* - MUSICBRAINZ_ARTISTID
* - MUSICBRAINZ_ALBUMARTISTID
* - ACOUSTID_ID
* - ACOUSTID_FINGERPRINT
* - MUSICIP_PUID
*
*/
class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap

108
3rdparty/taglib/toolkit/trefcounter.cpp vendored Normal file
View File

@ -0,0 +1,108 @@
/***************************************************************************
copyright : (C) 2013 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "trefcounter.h"
#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)
#elif defined(HAVE_GCC_ATOMIC)
# define ATOMIC_INT int
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
#elif defined(HAVE_WIN_ATOMIC)
# if !defined(NOMINMAX)
# define NOMINMAX
# endif
# include <windows.h>
# define ATOMIC_INT long
# define ATOMIC_INC(x) InterlockedIncrement(&x)
# define ATOMIC_DEC(x) InterlockedDecrement(&x)
#elif defined(HAVE_MAC_ATOMIC)
# include <libkern/OSAtomic.h>
# define ATOMIC_INT int32_t
# define ATOMIC_INC(x) OSAtomicIncrement32Barrier(&x)
# define ATOMIC_DEC(x) OSAtomicDecrement32Barrier(&x)
#elif defined(HAVE_IA64_ATOMIC)
# include <ia64intrin.h>
# define ATOMIC_INT int
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
#else
# define ATOMIC_INT int
# define ATOMIC_INC(x) (++x)
# define ATOMIC_DEC(x) (--x)
#endif
namespace TagLib
{
class RefCounter::RefCounterPrivate
{
public:
RefCounterPrivate() : refCount(1) {}
void ref() { ATOMIC_INC(refCount); }
bool deref() { return (ATOMIC_DEC(refCount) == 0); }
int count() const { return refCount; }
volatile ATOMIC_INT refCount;
};
RefCounter::RefCounter()
: d(new RefCounterPrivate())
{
}
RefCounter::~RefCounter()
{
delete d;
}
void RefCounter::ref()
{
d->ref();
}
bool RefCounter::deref()
{
return d->deref();
}
int RefCounter::count() const
{
return d->count();
}
}

58
3rdparty/taglib/toolkit/trefcounter.h vendored Normal file
View File

@ -0,0 +1,58 @@
/***************************************************************************
copyright : (C) 2013 by Tsuda Kageyu
email : tsuda.kageyu@gmail.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_REFCOUNTER_H
#define TAGLIB_REFCOUNTER_H
#include "taglib_export.h"
#include "taglib.h"
#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
/*!
* \internal
* This is just used as a base class for shared classes in TagLib.
*
* \warning This <b>is not</b> part of the TagLib public API!
*/
namespace TagLib
{
class TAGLIB_EXPORT RefCounter
{
public:
RefCounter();
virtual ~RefCounter();
void ref();
bool deref();
int count() const;
private:
class RefCounterPrivate;
RefCounterPrivate *d;
};
}
#endif // DO_NOT_DOCUMENT
#endif

View File

@ -23,176 +23,251 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
// This class assumes that std::basic_string<T> has a contiguous and null-terminated buffer.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "tstring.h"
#include "unicode.h"
#include "tdebug.h"
#include "tstringlist.h"
#include "trefcounter.h"
#include "tutils.h"
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string.h>
#ifdef HAVE_STD_CODECVT
# include <codecvt>
#else
# include "unicode.h"
#endif
namespace TagLib {
inline unsigned short byteSwap(unsigned short x)
{
return (((x) >> 8) & 0xff) | (((x) & 0xff) << 8);
}
namespace
{
inline unsigned short combine(unsigned char c1, unsigned char c2)
{
return (c1 << 8) | c2;
}
void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength)
{
#ifdef HAVE_STD_CODECVT
typedef std::codecvt_utf8_utf16<wchar_t> utf8_utf16_t;
using namespace TagLib;
const wchar_t *srcBegin = src;
const wchar_t *srcEnd = srcBegin + srcLength;
char *dstBegin = dst;
char *dstEnd = dstBegin + dstLength;
std::mbstate_t st;
const wchar_t *source;
char *target;
memset(&st, 0, sizeof(st));
std::codecvt_base::result result = utf8_utf16_t().out(
st, srcBegin, srcEnd, source, dstBegin, dstEnd, target);
if(result != utf8_utf16_t::ok) {
debug("String::copyFromUTF8() - Unicode conversion error.");
}
#else
using namespace Unicode;
using namespace TagLib;
const Unicode::UTF16 *srcBegin = src;
const Unicode::UTF16 *srcEnd = srcBegin + srcLength;
Unicode::UTF8 *dstBegin = reinterpret_cast<Unicode::UTF8*>(dst);
Unicode::UTF8 *dstEnd = dstBegin + dstLength;
Unicode::ConversionResult result = Unicode::ConvertUTF16toUTF8(
&srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::to8Bit() - Unicode conversion error.");
}
#endif
}
void UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength)
{
#ifdef HAVE_STD_CODECVT
typedef std::codecvt_utf8_utf16<wchar_t> utf8_utf16_t;
using namespace TagLib;
const char *srcBegin = src;
const char *srcEnd = srcBegin + srcLength;
wchar_t *dstBegin = dst;
wchar_t *dstEnd = dstBegin + dstLength;
std::mbstate_t st;
const char *source;
wchar_t *target;
memset(&st, 0, sizeof(st));
std::codecvt_base::result result = utf8_utf16_t().in(
st, srcBegin, srcEnd, source, dstBegin, dstEnd, target);
if(result != utf8_utf16_t::ok) {
debug("String::copyFromUTF8() - Unicode conversion error.");
}
#else
using namespace Unicode;
using namespace TagLib;
const Unicode::UTF8 *srcBegin = reinterpret_cast<const Unicode::UTF8*>(src);
const Unicode::UTF8 *srcEnd = srcBegin + srcLength;
Unicode::UTF16 *dstBegin = dst;
Unicode::UTF16 *dstEnd = dstBegin + dstLength;
Unicode::ConversionResult result = Unicode::ConvertUTF8toUTF16(
&srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::copyFromUTF8() - Unicode conversion error.");
}
#endif
}
}
using namespace TagLib;
namespace TagLib {
class String::StringPrivate : public RefCounter
{
public:
StringPrivate(const wstring &s) :
RefCounter(),
data(s),
CString(0) {}
StringPrivate() :
RefCounter(),
CString(0) {}
~StringPrivate() {
delete [] CString;
StringPrivate()
: RefCounter()
{
}
wstring data;
StringPrivate(const wstring &s)
: RefCounter()
, data(s)
{
}
StringPrivate(uint n, wchar_t c)
: RefCounter()
, data(static_cast<size_t>(n), c)
{
}
/*!
* This is only used to hold the a pointer to the most recent value of
* toCString.
* Stores string in UTF-16. The byte order depends on the CPU endian.
*/
char *CString;
TagLib::wstring data;
/*!
* This is only used to hold the the most recent value of toCString().
*/
std::string cstring;
};
String String::null;
////////////////////////////////////////////////////////////////////////////////
String::String()
String::String()
: d(new StringPrivate())
{
d = new StringPrivate;
}
String::String(const String &s) : d(s.d)
String::String(const String &s)
: d(s.d)
{
d->ref();
}
String::String(const std::string &s, Type t)
: d(new StringPrivate())
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
if(t == Latin1)
copyFromLatin1(&s[0], s.length());
else if(t == String::UTF8)
copyFromUTF8(&s[0], s.length());
else {
debug("String::String() -- A std::string should not contain UTF16.");
return;
}
int length = s.length();
d->data.resize(length);
wstring::iterator targetIt = d->data.begin();
for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
*targetIt = uchar(*it);
++targetIt;
}
prepare(t);
}
String::String(const wstring &s, Type t)
: d(new StringPrivate())
{
d = new StringPrivate(s);
prepare(t);
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(s.c_str(), s.length(), t);
else {
debug("String::String() -- A TagLib::wstring should not contain Latin1 or UTF-8.");
}
}
String::String(const wchar_t *s, Type t)
: d(new StringPrivate())
{
d = new StringPrivate(s);
prepare(t);
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(s, ::wcslen(s), t);
else {
debug("String::String() -- A const wchar_t * should not contain Latin1 or UTF-8.");
}
}
String::String(const char *s, Type t)
: d(new StringPrivate())
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
if(t == Latin1)
copyFromLatin1(s, ::strlen(s));
else if(t == String::UTF8)
copyFromUTF8(s, ::strlen(s));
else {
debug("String::String() -- A const char * should not contain UTF16.");
return;
}
int length = ::strlen(s);
d->data.resize(length);
wstring::iterator targetIt = d->data.begin();
for(int i = 0; i < length; i++) {
*targetIt = uchar(s[i]);
++targetIt;
}
prepare(t);
}
String::String(wchar_t c, Type t)
: d(new StringPrivate())
{
d = new StringPrivate;
d->data += c;
prepare(t);
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(&c, 1, t);
else {
debug("String::String() -- A const wchar_t should not contain Latin1 or UTF-8.");
}
}
String::String(char c, Type t)
: d(new StringPrivate(1, static_cast<uchar>(c)))
{
d = new StringPrivate;
if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
debug("String::String() -- A std::string should not contain UTF16.");
return;
if(t != Latin1 && t != UTF8) {
debug("String::String() -- A char should not contain UTF16.");
}
d->data += uchar(c);
prepare(t);
}
String::String(const ByteVector &v, Type t)
: d(new StringPrivate())
{
d = new StringPrivate;
if(v.isEmpty())
return;
if(t == Latin1 || t == UTF8) {
int length = 0;
d->data.resize(v.size());
wstring::iterator targetIt = d->data.begin();
for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
*targetIt = uchar(*it);
++targetIt;
++length;
}
d->data.resize(length);
}
else {
d->data.resize(v.size() / 2);
wstring::iterator targetIt = d->data.begin();
for(ByteVector::ConstIterator it = v.begin();
it != v.end() && it + 1 != v.end() && combine(*it, *(it + 1));
it += 2)
{
*targetIt = combine(*it, *(it + 1));
++targetIt;
}
}
prepare(t);
if(t == Latin1)
copyFromLatin1(v.data(), v.size());
else if(t == UTF8)
copyFromUTF8(v.data(), v.size());
else
copyFromUTF16(v.data(), v.size(), t);
}
////////////////////////////////////////////////////////////////////////////////
@ -206,46 +281,23 @@ String::~String()
std::string String::to8Bit(bool unicode) const
{
std::string s;
s.resize(d->data.size());
if(!unicode) {
s.resize(d->data.size());
std::string::iterator targetIt = s.begin();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*targetIt = char(*it);
*targetIt = static_cast<char>(*it);
++targetIt;
}
return s;
}
else {
s.resize(d->data.size() * 4 + 1);
const int outputBufferSize = d->data.size() * 3 + 1;
Unicode::UTF16 *sourceBuffer = new Unicode::UTF16[d->data.size() + 1];
Unicode::UTF8 *targetBuffer = new Unicode::UTF8[outputBufferSize];
for(unsigned int i = 0; i < d->data.size(); i++)
sourceBuffer[i] = Unicode::UTF16(d->data[i]);
const Unicode::UTF16 *source = sourceBuffer;
Unicode::UTF8 *target = targetBuffer;
Unicode::ConversionResult result =
Unicode::ConvertUTF16toUTF8(&source, sourceBuffer + d->data.size(),
&target, targetBuffer + outputBufferSize,
Unicode::lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::to8Bit() - Unicode conversion error.");
UTF16toUTF8(&d->data[0], d->data.size(), &s[0], s.size());
s.resize(::strlen(s.c_str()));
}
int newSize = target - targetBuffer;
s.resize(newSize);
targetBuffer[newSize] = 0;
s = (char *) targetBuffer;
delete [] sourceBuffer;
delete [] targetBuffer;
return s;
}
@ -256,22 +308,13 @@ TagLib::wstring String::toWString() const
const char *String::toCString(bool unicode) const
{
delete [] d->CString;
d->cstring = to8Bit(unicode);
return d->cstring.c_str();
}
std::string buffer = to8Bit(unicode);
d->CString = new char[buffer.size() + 1];
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
strcpy_s(d->CString, buffer.size() + 1, buffer.c_str());
#else
strcpy(d->CString, buffer.c_str());
#endif
return d->CString;
const wchar_t *String::toCWString() const
{
return d->data.c_str();
}
String::Iterator String::begin()
@ -296,23 +339,12 @@ String::ConstIterator String::end() const
int String::find(const String &s, int offset) const
{
wstring::size_type position = d->data.find(s.d->data, offset);
if(position != wstring::npos)
return position;
else
return -1;
return d->data.find(s.d->data, offset);
}
int String::rfind(const String &s, int offset) const
{
wstring::size_type position =
d->data.rfind(s.d->data, offset == -1 ? wstring::npos : offset);
if(position != wstring::npos)
return position;
else
return -1;
return d->data.rfind(s.d->data, offset);
}
StringList String::split(const String &separator) const
@ -345,9 +377,7 @@ bool String::startsWith(const String &s) const
String String::substr(uint position, uint n) const
{
String s;
s.d->data = d->data.substr(position, n);
return s;
return String(d->data.substr(position, n));
}
String &String::append(const String &s)
@ -395,67 +425,75 @@ bool String::isNull() const
ByteVector String::data(Type t) const
{
ByteVector v;
switch(t) {
switch(t)
{
case Latin1:
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
v.append(char(*it));
break;
}
{
ByteVector v(size(), 0);
char *p = v.data();
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
*p++ = static_cast<char>(*it);
return v;
}
case UTF8:
{
std::string s = to8Bit(true);
v.setData(s.c_str(), s.length());
break;
}
{
ByteVector v(size() * 4 + 1, 0);
UTF16toUTF8(&d->data[0], d->data.size(), v.data(), v.size());
v.resize(::strlen(v.data()));
return v;
}
case UTF16:
{
// Assume that if we're doing UTF16 and not UTF16BE that we want little
// endian encoding. (Byte Order Mark)
{
ByteVector v(2 + size() * 2, 0);
char *p = v.data();
v.append(char(0xff));
v.append(char(0xfe));
// Assume that if we're doing UTF16 and not UTF16BE that we want little
// endian encoding. (Byte Order Mark)
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*p++ = '\xff';
*p++ = '\xfe';
char c1 = *it & 0xff;
char c2 = *it >> 8;
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*p++ = static_cast<char>(*it & 0xff);
*p++ = static_cast<char>(*it >> 8);
}
v.append(c1);
v.append(c2);
return v;
}
break;
}
case UTF16BE:
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
{
ByteVector v(size() * 2, 0);
char *p = v.data();
char c1 = *it >> 8;
char c2 = *it & 0xff;
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*p++ = static_cast<char>(*it >> 8);
*p++ = static_cast<char>(*it & 0xff);
}
v.append(c1);
v.append(c2);
return v;
}
break;
}
case UTF16LE:
{
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
{
ByteVector v(size() * 2, 0);
char *p = v.data();
char c1 = *it & 0xff;
char c2 = *it >> 8;
for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
*p++ = static_cast<char>(*it & 0xff);
*p++ = static_cast<char>(*it >> 8);
}
v.append(c1);
v.append(c2);
return v;
}
default:
{
debug("String::data() - Invalid Type value.");
return ByteVector();
}
break;
}
}
return v;
}
int String::toInt() const
@ -530,37 +568,35 @@ bool String::isAscii() const
String String::number(int n) // static
{
if(n == 0)
return String("0");
static const size_t BufferSize = 11; // Sufficient to store "-214748364".
static const char *Format = "%d";
String charStack;
char buffer[BufferSize];
int length;
bool negative = n < 0;
#if defined(HAVE_SNPRINTF)
if(negative)
n = n * -1;
length = snprintf(buffer, BufferSize, Format, n);
while(n > 0) {
int remainder = n % 10;
charStack += char(remainder + '0');
n = (n - remainder) / 10;
}
#elif defined(HAVE_SPRINTF_S)
String s;
length = sprintf_s(buffer, Format, n);
if(negative)
s += '-';
#else
for(int i = charStack.d->data.size() - 1; i >= 0; i--)
s += charStack.d->data[i];
length = sprintf(buffer, Format, n);
return s;
#endif
if(length > 0)
return String(buffer);
else
return String::null;
}
TagLib::wchar &String::operator[](int i)
{
detach();
return d->data[i];
}
@ -638,14 +674,7 @@ String &String::operator=(const std::string &s)
delete d;
d = new StringPrivate;
d->data.resize(s.size());
wstring::iterator targetIt = d->data.begin();
for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
*targetIt = uchar(*it);
++targetIt;
}
copyFromLatin1(s.c_str(), s.length());
return *this;
}
@ -662,6 +691,7 @@ String &String::operator=(const wchar_t *s)
{
if(d->deref())
delete d;
d = new StringPrivate(s);
return *this;
}
@ -670,8 +700,8 @@ String &String::operator=(char c)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data += uchar(c);
d = new StringPrivate(1, static_cast<uchar>(c));
return *this;
}
@ -679,8 +709,8 @@ String &String::operator=(wchar_t c)
{
if(d->deref())
delete d;
d = new StringPrivate;
d->data += c;
d = new StringPrivate(1, c);
return *this;
}
@ -690,15 +720,7 @@ String &String::operator=(const char *s)
delete d;
d = new StringPrivate;
int length = ::strlen(s);
d->data.resize(length);
wstring::iterator targetIt = d->data.begin();
for(int i = 0; i < length; i++) {
*targetIt = uchar(s[i]);
++targetIt;
}
copyFromLatin1(s, ::strlen(s));
return *this;
}
@ -709,20 +731,10 @@ String &String::operator=(const ByteVector &v)
delete d;
d = new StringPrivate;
d->data.resize(v.size());
wstring::iterator targetIt = d->data.begin();
uint i = 0;
for(ByteVector::ConstIterator it = v.begin(); it != v.end() && (*it); ++it) {
*targetIt = uchar(*it);
++targetIt;
++i;
}
copyFromLatin1(v.data(), v.size());
// If we hit a null in the ByteVector, shrink the string again.
d->data.resize(i);
d->data.resize(::wcslen(d->data.c_str()));
return *this;
}
@ -748,68 +760,95 @@ void String::detach()
// private members
////////////////////////////////////////////////////////////////////////////////
void String::prepare(Type t)
void String::copyFromLatin1(const char *s, size_t length)
{
switch(t) {
case UTF16:
{
if(d->data.size() >= 1 && (d->data[0] == 0xfeff || d->data[0] == 0xfffe)) {
bool swap = d->data[0] != 0xfeff;
d->data.erase(d->data.begin(), d->data.begin() + 1);
if(swap) {
for(uint i = 0; i < d->data.size(); i++)
d->data[i] = byteSwap((unsigned short)d->data[i]);
}
}
d->data.resize(length);
for(size_t i = 0; i < length; ++i)
d->data[i] = static_cast<uchar>(s[i]);
}
void String::copyFromUTF8(const char *s, size_t length)
{
d->data.resize(length);
UTF8toUTF16(s, length, &d->data[0], d->data.size());
d->data.resize(::wcslen(d->data.c_str()));
}
void String::copyFromUTF16(const wchar_t *s, size_t length, Type t)
{
bool swap;
if(t == 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::prepare() - Invalid UTF16 string.");
d->data.erase(d->data.begin(), d->data.end());
}
break;
}
case UTF8:
{
int bufferSize = d->data.size() + 1;
Unicode::UTF8 *sourceBuffer = new Unicode::UTF8[bufferSize];
Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize];
unsigned int i = 0;
for(; i < d->data.size(); i++)
sourceBuffer[i] = Unicode::UTF8(d->data[i]);
sourceBuffer[i] = 0;
const Unicode::UTF8 *source = sourceBuffer;
Unicode::UTF16 *target = targetBuffer;
Unicode::ConversionResult result =
Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize,
&target, targetBuffer + bufferSize,
Unicode::lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::prepare() - Unicode conversion error.");
debug("String::copyFromUTF16() - Invalid UTF16 string.");
return;
}
int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0;
d->data.resize(newSize);
for(int i = 0; i < newSize; i++)
d->data[i] = targetBuffer[i];
delete [] sourceBuffer;
delete [] targetBuffer;
break;
s++;
length--;
}
case UTF16LE:
{
for(uint i = 0; i < d->data.size(); i++)
d->data[i] = byteSwap((unsigned short)d->data[i]);
break;
else
swap = (t != WCharByteOrder);
d->data.resize(length);
memcpy(&d->data[0], s, length * sizeof(wchar_t));
if(swap) {
for(size_t i = 0; i < length; ++i)
d->data[i] = byteSwap(static_cast<ushort>(s[i]));
}
default:
break;
}
void String::copyFromUTF16(const char *s, size_t length, Type t)
{
bool swap;
if(t == UTF16) {
if(length < 2) {
debug("String::copyFromUTF16() - Invalid UTF16 string.");
return;
}
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
ushort 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);
d->data.resize(length / 2);
for(size_t i = 0; i < length / 2; ++i) {
d->data[i] = swap ? combine(*s, *(s + 1)) : combine(*(s + 1), *s);
s += 2;
}
}
#if SYSTEM_BYTEORDER == 1
const String::Type String::WCharByteOrder = String::UTF16LE;
#else
const String::Type String::WCharByteOrder = String::UTF16BE;
#endif
}
////////////////////////////////////////////////////////////////////////////////
@ -818,27 +857,28 @@ void String::prepare(Type t)
const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2)
{
String s(s1);
TagLib::String s(s1);
s.append(s2);
return s;
}
const TagLib::String operator+(const char *s1, const TagLib::String &s2)
{
String s(s1);
TagLib::String s(s1);
s.append(s2);
return s;
}
const TagLib::String operator+(const TagLib::String &s1, const char *s2)
{
String s(s1);
TagLib::String s(s1);
s.append(s2);
return s;
}
std::ostream &operator<<(std::ostream &s, const String &str)
std::ostream &operator<<(std::ostream &s, const TagLib::String &str)
{
s << str.to8Bit();
return s;
}

View File

@ -63,8 +63,8 @@ namespace TagLib {
/*!
* This is an implicitly shared \e wide string. For storage it uses
* TagLib::wstring, but as this is an <i>implementation detail</i> this of
* course could change. Strings are stored internally as UTF-16BE. (Without
* the BOM (Byte Order Mark)
* course could change. Strings are stored internally as UTF-16(without BOM/
* CPU byte order)
*
* The use of implicit sharing means that copying a string is cheap, the only
* \e cost comes into play when the copy is modified. Prior to that the string
@ -135,12 +135,12 @@ namespace TagLib {
/*!
* Makes a deep copy of the data in \a s.
*/
String(const wstring &s, Type t = UTF16BE);
String(const wstring &s, Type t = WCharByteOrder);
/*!
* Makes a deep copy of the data in \a s.
*/
String(const wchar_t *s, Type t = UTF16BE);
String(const wchar_t *s, Type t = WCharByteOrder);
/*!
* Makes a deep copy of the data in \a c.
@ -178,34 +178,59 @@ namespace TagLib {
virtual ~String();
/*!
* If \a unicode if false (the default) this will return a \e Latin1 encoded
* std::string. If it is true the returned std::wstring will be UTF-8
* encoded.
* Returns a deep copy of this String as an std::string. The returned string
* is encoded in UTF8 if \a unicode is true, otherwise Latin1.
*
* \see toCString()
*/
std::string to8Bit(bool unicode = false) const;
/*!
* Returns a wstring version of the TagLib string as a wide string.
* Returns a deep copy of this String as a wstring. The returned string is
* encoded in UTF-16 (without BOM/CPU byte order).
*
* \see toCWString()
*/
wstring toWString() const;
/*!
* Creates and returns a C-String based on the data. This string is still
* owned by the String (class) and as such should not be deleted by the user.
* Creates and returns a standard C-style (null-terminated) version of this
* String. The returned string is encoded in UTF8 if \a unicode is true,
* otherwise Latin1.
*
* The returned string is still owned by this String and should not be deleted
* by the user.
*
* If \a unicode if false (the default) this string will be encoded in
* \e Latin1. If it is true the returned C-String will be UTF-8 encoded.
* The returned pointer remains valid until this String instance is destroyed
* or toCString() is called again.
*
* This string remains valid until the String instance is destroyed or
* another export method is called.
*
* \warning This however has the side effect that this C-String will remain
* in memory <b>in addition to</b> other memory that is consumed by the
* \warning This however has the side effect that the returned string will remain
* in memory <b>in addition to</b> other memory that is consumed by this
* String instance. So, this method should not be used on large strings or
* where memory is critical.
* where memory is critical. Consider using to8Bit() instead to avoid it.
*
* \see to8Bit()
*/
const char *toCString(bool unicode = false) const;
/*!
* Returns a standard C-style (null-terminated) wide character version of
* this String. The returned string is encoded in UTF-16 (without BOM/CPU byte
* order).
*
* The returned string is still owned by this String and should not be deleted
* by the user.
*
* The returned pointer remains valid until this String instance is destroyed
* or any other method of this String is called.
*
* /note This returns a pointer to the String's internal data without any
* conversions.
*
* \see toWString()
*/
const wchar_t *toCWString() const;
/*!
* Returns an iterator pointing to the beginning of the string.
*/
@ -306,7 +331,7 @@ namespace TagLib {
/*!
* Convert the string to an integer.
*
* Returns the integer if the conversion was successfull or 0 if the
* Returns the integer if the conversion was successful or 0 if the
* string does not represent a number.
*/
// BIC: merge with the method below
@ -451,17 +476,39 @@ namespace TagLib {
private:
/*!
* This checks to see if the string is in \e UTF-16 (with BOM) or \e UTF-8
* format and if so converts it to \e UTF-16BE for internal use. \e Latin1
* does not require conversion since it is a subset of \e UTF-16BE and
* \e UTF16-BE requires no conversion since it is used internally.
* Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order)
* and copies it to the internal buffer.
*/
void prepare(Type t);
void copyFromLatin1(const char *s, size_t length);
/*!
* Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order)
* and copies it to the internal buffer.
*/
void copyFromUTF8(const char *s, size_t length);
/*!
* Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into
* \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer.
*/
void copyFromUTF16(const wchar_t *s, size_t length, Type t);
/*!
* Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into
* \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer.
*/
void copyFromUTF16(const char *s, size_t length, Type t);
/*!
* Indicates which byte order of UTF-16 is used to store strings internally.
*
* \note \e String::UTF16BE or \e String::UTF16LE
*/
static const Type WCharByteOrder;
class StringPrivate;
StringPrivate *d;
};
}
/*!

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