mirror of
https://github.com/taglib/taglib.git
synced 2026-06-16 03:09:20 -04:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa34afda79 | ||
|
|
942ec58de5 | ||
|
|
082a36147b | ||
|
|
f11b206fe8 | ||
|
|
e37f6ed752 | ||
|
|
d2f20e8d2a | ||
|
|
f194a55c0f | ||
|
|
719187794e | ||
|
|
74b94613a0 | ||
|
|
33d0be110b | ||
|
|
df12b4ffc6 | ||
|
|
d16c24ae21 | ||
|
|
1c35918834 | ||
|
|
d163f36d35 | ||
|
|
590cd4c9f6 | ||
|
|
6c0227ee13 | ||
|
|
9bb57fe0a7 | ||
|
|
3fecdbf428 | ||
|
|
356c7a5d6e | ||
|
|
4b4f70253b | ||
|
|
8b61a06fda | ||
|
|
6801ac2515 | ||
|
|
29d17bb8e9 | ||
|
|
fe8053c7d5 | ||
|
|
eb63ee8ec6 | ||
|
|
e86e5f906b | ||
|
|
60e82e6694 | ||
|
|
fc6e02da35 | ||
|
|
4140c5f2eb | ||
|
|
fc3fc10f60 | ||
|
|
3bc123aed6 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
cmake_install.cmake
|
||||
cmake_uninstall.cmake
|
||||
Makefile
|
||||
CTestTestfile.cmake
|
||||
CMakeFiles/
|
||||
*.so
|
||||
*.so.*
|
||||
@@ -16,6 +18,7 @@ CMakeFiles/
|
||||
/config.h
|
||||
/taglib.pc
|
||||
/tests/test_runner
|
||||
/tests/Testing
|
||||
/taglib_config.h
|
||||
/taglib-config
|
||||
/bindings/c/taglib_c.pc
|
||||
|
||||
@@ -56,9 +56,9 @@ set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VE
|
||||
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
|
||||
# 3. If any interfaces have been added since the last public release, then increment age.
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 12)
|
||||
set(TAGLIB_SOVERSION_CURRENT 13)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 11)
|
||||
set(TAGLIB_SOVERSION_AGE 12)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
|
||||
17
NEWS
17
NEWS
@@ -1,6 +1,21 @@
|
||||
TagLib 1.8 BETA (Jul 14, 2012)
|
||||
TagLib 1.8 (Sep 6, 2012)
|
||||
==============================
|
||||
|
||||
1.8:
|
||||
|
||||
* Added support for OWNE ID3 frames.
|
||||
* Changed key validation in the new PropertyMap API.
|
||||
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
|
||||
the caller is responsible for this.
|
||||
* File objects will also no longer delete the passed IOStream objects. It
|
||||
should be done in the caller code after the File object is no longer
|
||||
used.
|
||||
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
|
||||
latin1-encoded text in ID3v2 frames.
|
||||
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
|
||||
|
||||
1.8 BETA:
|
||||
|
||||
* New API for accessing tags by name.
|
||||
* New abstract I/O stream layer to allow custom I/O handlers.
|
||||
* Support for writing ID3v2.3 tags.
|
||||
|
||||
@@ -65,6 +65,7 @@ set(tag_HDRS
|
||||
mpeg/id3v2/frames/attachedpictureframe.h
|
||||
mpeg/id3v2/frames/commentsframe.h
|
||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.h
|
||||
mpeg/id3v2/frames/ownershipframe.h
|
||||
mpeg/id3v2/frames/popularimeterframe.h
|
||||
mpeg/id3v2/frames/privateframe.h
|
||||
mpeg/id3v2/frames/relativevolumeframe.h
|
||||
@@ -151,6 +152,7 @@ set(frames_SRCS
|
||||
mpeg/id3v2/frames/attachedpictureframe.cpp
|
||||
mpeg/id3v2/frames/commentsframe.cpp
|
||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
|
||||
mpeg/id3v2/frames/ownershipframe.cpp
|
||||
mpeg/id3v2/frames/popularimeterframe.cpp
|
||||
mpeg/id3v2/frames/privateframe.cpp
|
||||
mpeg/id3v2/frames/relativevolumeframe.cpp
|
||||
|
||||
@@ -95,6 +95,9 @@ 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -189,7 +189,7 @@ PropertyMap APE::Tag::properties() const
|
||||
PropertyMap properties;
|
||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
||||
for(; it != itemListMap().end(); ++it) {
|
||||
String tagName = PropertyMap::prepareKey(it->first);
|
||||
String tagName = it->first.upper();
|
||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
||||
// add to unsupportedData
|
||||
if(it->second.type() != Item::Text || tagName.isNull())
|
||||
@@ -227,7 +227,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
StringList toRemove;
|
||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
||||
for(; remIt != itemListMap().end(); ++remIt) {
|
||||
String key = PropertyMap::prepareKey(remIt->first);
|
||||
String key = remIt->first.upper();
|
||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
||||
if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
@@ -238,9 +238,12 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
|
||||
// now sync in the "forward direction"
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
PropertyMap invalid;
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(!checkKey(tagName))
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.size() == 0)
|
||||
removeItem(tagName);
|
||||
else {
|
||||
@@ -252,7 +255,21 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
return PropertyMap();
|
||||
return invalid;
|
||||
}
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < 2 || key.size() > 16)
|
||||
return false;
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
|
||||
// only allow printable ASCII including space (32..127)
|
||||
if (*it < 32 || *it >= 128)
|
||||
return false;
|
||||
String upperKey = key.upper();
|
||||
if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
|
||||
@@ -123,10 +123,17 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function. The same
|
||||
* comments as for the export function apply.
|
||||
* comments as for the export function apply; additionally note that the APE tag
|
||||
* specification requires keys to have between 2 and 16 printable ASCII characters
|
||||
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Check if the given String is a valid APE tag key.
|
||||
*/
|
||||
static bool checkKey(const String&);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the tag's footer.
|
||||
*/
|
||||
|
||||
@@ -348,7 +348,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
else {
|
||||
obj = new UnknownObject(guid);
|
||||
}
|
||||
obj->parse(file, size);
|
||||
obj->parse(file, (unsigned int)size);
|
||||
objects.append(obj);
|
||||
dataPos += size;
|
||||
}
|
||||
@@ -535,7 +535,7 @@ bool ASF::File::save()
|
||||
data.append(d->objects[i]->render(this));
|
||||
}
|
||||
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
|
||||
insert(data, 0, d->size);
|
||||
insert(data, 0, (TagLib::ulong)d->size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace TagLib {
|
||||
*
|
||||
* \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.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
@@ -64,6 +67,9 @@ namespace TagLib {
|
||||
*
|
||||
* \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.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ bool FLAC::File::save()
|
||||
}
|
||||
ByteVector padding = ByteVector::fromUInt(paddingLength);
|
||||
padding.resize(paddingLength + 4);
|
||||
padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag;
|
||||
padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(padding);
|
||||
|
||||
// Write the data to the file
|
||||
|
||||
@@ -97,6 +97,9 @@ namespace TagLib {
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
|
||||
@@ -128,7 +128,7 @@ bool IT::File::save()
|
||||
|
||||
seek(sampleOffset + 20);
|
||||
|
||||
if((i + instrumentCount) < lines.size())
|
||||
if((TagLib::uint)(i + instrumentCount) < lines.size())
|
||||
writeString(lines[i + instrumentCount], 25);
|
||||
else
|
||||
writeString(String::null, 25);
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
|
||||
@@ -94,7 +94,7 @@ int IT::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
ushort IT::Properties::lengthInPatterns() const
|
||||
TagLib::ushort IT::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -104,37 +104,37 @@ bool IT::Properties::stereo() const
|
||||
return d->flags & Stereo;
|
||||
}
|
||||
|
||||
ushort IT::Properties::instrumentCount() const
|
||||
TagLib::ushort IT::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::sampleCount() const
|
||||
TagLib::ushort IT::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::patternCount() const
|
||||
TagLib::ushort IT::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::version() const
|
||||
TagLib::ushort IT::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
ushort IT::Properties::compatibleVersion() const
|
||||
TagLib::ushort IT::Properties::compatibleVersion() const
|
||||
{
|
||||
return d->compatibleVersion;
|
||||
}
|
||||
|
||||
ushort IT::Properties::flags() const
|
||||
TagLib::ushort IT::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
ushort IT::Properties::special() const
|
||||
TagLib::ushort IT::Properties::special() const
|
||||
{
|
||||
return d->special;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
|
||||
@@ -70,7 +70,7 @@ int Mod::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
uint Mod::Properties::instrumentCount() const
|
||||
TagLib::uint Mod::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace TagLib {
|
||||
*
|
||||
* \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.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
|
||||
|
||||
@@ -95,6 +95,9 @@ 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -214,7 +214,7 @@ void MPC::Properties::readSV8(File *file)
|
||||
d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1;
|
||||
|
||||
if((d->sampleFrames - begSilence) != 0)
|
||||
d->bitrate = d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence);
|
||||
d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence));
|
||||
d->bitrate = d->bitrate / 1000;
|
||||
|
||||
d->length = (d->sampleFrames - begSilence) / d->sampleRate;
|
||||
@@ -279,10 +279,10 @@ void MPC::Properties::readSV7(const ByteVector &data)
|
||||
}
|
||||
|
||||
if (d->trackPeak != 0)
|
||||
d->trackPeak = (int)(log10(d->trackPeak) * 20 * 256 + .5);
|
||||
d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5);
|
||||
|
||||
if (d->albumPeak != 0)
|
||||
d->albumPeak = (int)(log10(d->albumPeak) * 20 * 256 + .5);
|
||||
d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5);
|
||||
|
||||
bool trueGapless = (gapless >> 31) & 0x0001;
|
||||
if(trueGapless) {
|
||||
|
||||
@@ -194,10 +194,10 @@ void ID3v1::Tag::setTrack(uint i)
|
||||
|
||||
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
|
||||
{
|
||||
if(TagPrivate::stringHandler != &defaultStringHandler)
|
||||
delete TagPrivate::stringHandler;
|
||||
|
||||
TagPrivate::stringHandler = handler;
|
||||
if (handler)
|
||||
TagPrivate::stringHandler = handler;
|
||||
else
|
||||
TagPrivate::stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -154,6 +154,11 @@ namespace TagLib {
|
||||
/*!
|
||||
* Sets the string handler that decides how the ID3v1 data will be
|
||||
* converted to and from binary data.
|
||||
* If the parameter \a handler is null, the previous handler is
|
||||
* released and default ISO-8859-1 handler is restored.
|
||||
*
|
||||
* \note The caller is responsible for deleting the previous handler
|
||||
* as needed after it is released.
|
||||
*
|
||||
* \see StringHandler
|
||||
*/
|
||||
|
||||
@@ -112,7 +112,7 @@ void CommentsFrame::setTextEncoding(String::Type encoding)
|
||||
|
||||
PropertyMap CommentsFrame::asProperties() const
|
||||
{
|
||||
String key = PropertyMap::prepareKey(description());
|
||||
String key = description().upper();
|
||||
PropertyMap map;
|
||||
if(key.isEmpty() || key == "COMMENT")
|
||||
map.insert("COMMENT", text());
|
||||
@@ -158,8 +158,13 @@ void CommentsFrame::parseFields(const ByteVector &data)
|
||||
ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
|
||||
|
||||
if(l.size() == 2) {
|
||||
d->description = String(l.front(), d->textEncoding);
|
||||
d->text = String(l.back(), d->textEncoding);
|
||||
if(d->textEncoding == String::Latin1) {
|
||||
d->description = Tag::latin1StringHandler()->parse(l.front());
|
||||
d->text = Tag::latin1StringHandler()->parse(l.back());
|
||||
} else {
|
||||
d->description = String(l.front(), d->textEncoding);
|
||||
d->text = String(l.back(), d->textEncoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
162
taglib/mpeg/id3v2/frames/ownershipframe.cpp
Normal file
162
taglib/mpeg/id3v2/frames/ownershipframe.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Rupert Daniel
|
||||
email : rupert@cancelmonday.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 <tdebug.h>
|
||||
|
||||
#include "ownershipframe.h"
|
||||
#include <id3v2tag.h>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
|
||||
class OwnershipFrame::OwnershipFramePrivate
|
||||
{
|
||||
public:
|
||||
String pricePaid;
|
||||
String datePurchased;
|
||||
String seller;
|
||||
String::Type textEncoding;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE")
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
d->textEncoding = encoding;
|
||||
}
|
||||
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data)
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
setData(data);
|
||||
}
|
||||
|
||||
OwnershipFrame::~OwnershipFrame()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String OwnershipFrame::toString() const
|
||||
{
|
||||
return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller;
|
||||
}
|
||||
|
||||
String OwnershipFrame::pricePaid() const
|
||||
{
|
||||
return d->pricePaid;
|
||||
}
|
||||
|
||||
void OwnershipFrame::setPricePaid(const String &s)
|
||||
{
|
||||
d->pricePaid = s;
|
||||
}
|
||||
|
||||
String OwnershipFrame::datePurchased() const
|
||||
{
|
||||
return d->datePurchased;
|
||||
}
|
||||
|
||||
void OwnershipFrame::setDatePurchased(const String &s)
|
||||
{
|
||||
d->datePurchased = s;
|
||||
}
|
||||
|
||||
String OwnershipFrame::seller() const
|
||||
{
|
||||
return d->seller;
|
||||
}
|
||||
|
||||
void OwnershipFrame::setSeller(const String &s)
|
||||
{
|
||||
d->seller = s;
|
||||
}
|
||||
|
||||
String::Type OwnershipFrame::textEncoding() const
|
||||
{
|
||||
return d->textEncoding;
|
||||
}
|
||||
|
||||
void OwnershipFrame::setTextEncoding(String::Type encoding)
|
||||
{
|
||||
d->textEncoding = encoding;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void OwnershipFrame::parseFields(const ByteVector &data)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
// Get the text encoding
|
||||
d->textEncoding = String::Type(data[0]);
|
||||
pos += 1;
|
||||
|
||||
// Read the price paid this is a null terminate string
|
||||
d->pricePaid = readStringField(data, String::Latin1, &pos);
|
||||
|
||||
// If we don't have at least 8 bytes left then don't parse the rest of the
|
||||
// data
|
||||
if(data.size() - pos < 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the date purchased YYYYMMDD
|
||||
d->datePurchased = String(data.mid(pos, 8));
|
||||
pos += 8;
|
||||
|
||||
// Read the seller
|
||||
if(d->textEncoding == String::Latin1)
|
||||
d->seller = Tag::latin1StringHandler()->parse(data.mid(pos));
|
||||
else
|
||||
d->seller = String(data.mid(pos), d->textEncoding);
|
||||
}
|
||||
|
||||
ByteVector OwnershipFrame::renderFields() const
|
||||
{
|
||||
ByteVector v;
|
||||
|
||||
v.append(char(d->textEncoding));
|
||||
v.append(d->pricePaid.data(String::Latin1));
|
||||
v.append(textDelimiter(String::Latin1));
|
||||
v.append(d->datePurchased.data(String::Latin1));
|
||||
v.append(d->seller.data(d->textEncoding));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h)
|
||||
{
|
||||
d = new OwnershipFramePrivate;
|
||||
parseFields(fieldData(data));
|
||||
}
|
||||
151
taglib/mpeg/id3v2/frames/ownershipframe.h
Normal file
151
taglib/mpeg/id3v2/frames/ownershipframe.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2012 by Rupert Daniel
|
||||
email : rupert@cancelmonday.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_OWNERSHIPFRAME_H
|
||||
#define TAGLIB_OWNERSHIPFRAME_H
|
||||
|
||||
#include "id3v2frame.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ID3v2 {
|
||||
|
||||
//! An implementation of ID3v2 "ownership"
|
||||
|
||||
/*!
|
||||
* This implements the ID3v2 ownership (OWNE frame). It consists of
|
||||
* a price paid, a date purchased (YYYYMMDD) and the name of the seller.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT OwnershipFrame : public Frame
|
||||
{
|
||||
friend class FrameFactory;
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Construct an empty ownership frame.
|
||||
*/
|
||||
explicit OwnershipFrame(String::Type encoding = String::Latin1);
|
||||
|
||||
/*!
|
||||
* Construct a ownership based on the data in \a data.
|
||||
*/
|
||||
explicit OwnershipFrame(const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Destroys this OwnershipFrame instance.
|
||||
*/
|
||||
virtual ~OwnershipFrame();
|
||||
|
||||
/*!
|
||||
* Returns the text of this popularimeter.
|
||||
*
|
||||
* \see text()
|
||||
*/
|
||||
virtual String toString() const;
|
||||
|
||||
/*!
|
||||
* Returns the date purchased.
|
||||
*
|
||||
* \see setDatePurchased()
|
||||
*/
|
||||
String datePurchased() const;
|
||||
|
||||
/*!
|
||||
* Set the date purchased.
|
||||
*
|
||||
* \see datePurchased()
|
||||
*/
|
||||
void setDatePurchased(const String &datePurchased);
|
||||
|
||||
/*!
|
||||
* Returns the price paid.
|
||||
*
|
||||
* \see setPricePaid()
|
||||
*/
|
||||
String pricePaid() const;
|
||||
|
||||
/*!
|
||||
* Set the price paid.
|
||||
*
|
||||
* \see pricePaid()
|
||||
*/
|
||||
void setPricePaid(const String &pricePaid);
|
||||
|
||||
/*!
|
||||
* Returns the seller.
|
||||
*
|
||||
* \see setSeller()
|
||||
*/
|
||||
String seller() const;
|
||||
|
||||
/*!
|
||||
* Set the seller.
|
||||
*
|
||||
* \see seller()
|
||||
*/
|
||||
void setSeller(const String &seller);
|
||||
|
||||
/*!
|
||||
* Returns the text encoding that will be used in rendering this frame.
|
||||
* This defaults to the type that was either specified in the constructor
|
||||
* or read from the frame when parsed.
|
||||
*
|
||||
* \see setTextEncoding()
|
||||
* \see render()
|
||||
*/
|
||||
String::Type textEncoding() const;
|
||||
|
||||
/*!
|
||||
* Sets the text encoding to be used when rendering this frame to
|
||||
* \a encoding.
|
||||
*
|
||||
* \see textEncoding()
|
||||
* \see render()
|
||||
*/
|
||||
void setTextEncoding(String::Type encoding);
|
||||
|
||||
protected:
|
||||
// Reimplementations.
|
||||
|
||||
virtual void parseFields(const ByteVector &data);
|
||||
virtual ByteVector renderFields() const;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* The constructor used by the FrameFactory.
|
||||
*/
|
||||
OwnershipFrame(const ByteVector &data, Header *h);
|
||||
OwnershipFrame(const OwnershipFrame &);
|
||||
OwnershipFrame &operator=(const OwnershipFrame &);
|
||||
|
||||
class OwnershipFramePrivate;
|
||||
OwnershipFramePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -213,8 +213,10 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
|
||||
|
||||
for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
|
||||
if(!(*it).isEmpty()) {
|
||||
String s(*it, d->textEncoding);
|
||||
d->fieldList.append(s);
|
||||
if(d->textEncoding == String::Latin1)
|
||||
d->fieldList.append(Tag::latin1StringHandler()->parse(*it));
|
||||
else
|
||||
d->fieldList.append(String(*it, d->textEncoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,7 +291,7 @@ PropertyMap TextIdentificationFrame::makeTMCLProperties() const
|
||||
}
|
||||
StringList l = fieldList();
|
||||
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
|
||||
String instrument = PropertyMap::prepareKey(*it);
|
||||
String instrument = it->upper();
|
||||
if(instrument.isNull()) {
|
||||
// instrument is not a valid key -> frame unsupported
|
||||
map.clear();
|
||||
@@ -382,7 +384,7 @@ PropertyMap UserTextIdentificationFrame::asProperties() const
|
||||
String tagName = description();
|
||||
|
||||
PropertyMap map;
|
||||
String key = map.prepareKey(tagName);
|
||||
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 {
|
||||
|
||||
@@ -116,7 +116,7 @@ void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
|
||||
PropertyMap UnsynchronizedLyricsFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
String key = PropertyMap::prepareKey(description());
|
||||
String key = description().upper();
|
||||
if(key.isEmpty() || key.upper() == "LYRICS")
|
||||
map.insert("LYRICS", text());
|
||||
else if(key.isNull())
|
||||
@@ -158,8 +158,13 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data)
|
||||
ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
|
||||
|
||||
if(l.size() == 2) {
|
||||
d->description = String(l.front(), d->textEncoding);
|
||||
d->text = String(l.back(), d->textEncoding);
|
||||
if(d->textEncoding == String::Latin1) {
|
||||
d->description = Tag::latin1StringHandler()->parse(l.front());
|
||||
d->text = Tag::latin1StringHandler()->parse(l.back());
|
||||
} else {
|
||||
d->description = String(l.front(), d->textEncoding);
|
||||
d->text = String(l.back(), d->textEncoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ void UserUrlLinkFrame::setDescription(const String &s)
|
||||
PropertyMap UserUrlLinkFrame::asProperties() const
|
||||
{
|
||||
PropertyMap map;
|
||||
String key = PropertyMap::prepareKey(description());
|
||||
String key = description().upper();
|
||||
if(key.isEmpty() || key.upper() == "URL")
|
||||
map.insert("URL", url());
|
||||
else if(key.isNull())
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <tdebug.h>
|
||||
#include <tstringlist.h>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
#include "id3v2synchdata.h"
|
||||
#include "tpropertymap.h"
|
||||
@@ -71,7 +72,7 @@ namespace
|
||||
return false;
|
||||
|
||||
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
|
||||
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
|
||||
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -273,7 +274,11 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int
|
||||
if(end < *position)
|
||||
return String::null;
|
||||
|
||||
String str = String(data.mid(*position, end - *position), encoding);
|
||||
String str;
|
||||
if(encoding == String::Latin1)
|
||||
str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position));
|
||||
else
|
||||
str = String(data.mid(*position, end - *position), encoding);
|
||||
|
||||
*position = end + delimiter.size();
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "frames/unsynchronizedlyricsframe.h"
|
||||
#include "frames/popularimeterframe.h"
|
||||
#include "frames/privateframe.h"
|
||||
#include "frames/ownershipframe.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace ID3v2;
|
||||
@@ -107,7 +108,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
}
|
||||
|
||||
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
|
||||
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
|
||||
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
|
||||
delete header;
|
||||
return 0;
|
||||
}
|
||||
@@ -238,6 +239,14 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
|
||||
|
||||
if(frameID == "PRIV")
|
||||
return new PrivateFrame(data, header);
|
||||
|
||||
// Ownership (frames 4.22)
|
||||
|
||||
if(frameID == "OWNE") {
|
||||
OwnershipFrame *f = new OwnershipFrame(data, header);
|
||||
d->setTextEncoding(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
return new UnknownFrame(data, header);
|
||||
}
|
||||
|
||||
@@ -123,8 +123,7 @@ namespace TagLib {
|
||||
FrameFactory();
|
||||
|
||||
/*!
|
||||
* Destroys the frame factory. In most cases this will never be called (as
|
||||
* is typical of singletons).
|
||||
* Destroys the frame factory.
|
||||
*/
|
||||
virtual ~FrameFactory();
|
||||
|
||||
|
||||
@@ -70,8 +70,30 @@ public:
|
||||
|
||||
FrameListMap frameListMap;
|
||||
FrameList frameList;
|
||||
|
||||
static const Latin1StringHandler *stringHandler;
|
||||
};
|
||||
|
||||
static const Latin1StringHandler defaultStringHandler;
|
||||
const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// StringHandler implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Latin1StringHandler::Latin1StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
Latin1StringHandler::~Latin1StringHandler()
|
||||
{
|
||||
}
|
||||
|
||||
String Latin1StringHandler::parse(const ByteVector &data) const
|
||||
{
|
||||
return String(data, String::Latin1);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -584,6 +606,19 @@ ByteVector ID3v2::Tag::render(int version) const
|
||||
return d->header.render() + tagData;
|
||||
}
|
||||
|
||||
Latin1StringHandler const *ID3v2::Tag::latin1StringHandler()
|
||||
{
|
||||
return TagPrivate::stringHandler;
|
||||
}
|
||||
|
||||
void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler)
|
||||
{
|
||||
if(handler)
|
||||
TagPrivate::stringHandler = handler;
|
||||
else
|
||||
TagPrivate::stringHandler = &defaultStringHandler;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -645,8 +680,9 @@ void ID3v2::Tag::parse(const ByteVector &origData)
|
||||
// portion of the frame data.
|
||||
|
||||
if(data.at(frameDataPosition) == 0) {
|
||||
if(d->header.footerPresent())
|
||||
if(d->header.footerPresent()) {
|
||||
debug("Padding *and* a footer found. This is not allowed by the spec.");
|
||||
}
|
||||
|
||||
d->paddingSize = frameDataLength - frameDataPosition;
|
||||
return;
|
||||
|
||||
@@ -57,6 +57,36 @@ namespace TagLib {
|
||||
typedef List<Frame *> FrameList;
|
||||
typedef Map<ByteVector, FrameList> FrameListMap;
|
||||
|
||||
//! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags.
|
||||
|
||||
/*!
|
||||
* ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only
|
||||
* supports genuine ISO-8859-1 by default. However, in practice, non
|
||||
* ISO-8859-1 encodings are often used instead of ISO-8859-1, such as
|
||||
* Windows-1252 for western languages, Shift_JIS for Japanese and so on.
|
||||
*
|
||||
* Here is an option to read such tags by subclassing this class,
|
||||
* reimplementing parse() and setting your reimplementation as the default
|
||||
* with ID3v2::Tag::setStringHandler().
|
||||
*
|
||||
* \note Writing non-ISO-8859-1 tags is not implemented intentionally.
|
||||
* Use UTF-16 or UTF-8 instead.
|
||||
*
|
||||
* \see ID3v2::Tag::setStringHandler()
|
||||
*/
|
||||
class TAGLIB_EXPORT Latin1StringHandler
|
||||
{
|
||||
public:
|
||||
Latin1StringHandler();
|
||||
virtual ~Latin1StringHandler();
|
||||
|
||||
/*!
|
||||
* Decode a string from \a data. The default implementation assumes that
|
||||
* \a data is an ISO-8859-1 (Latin1) character array.
|
||||
*/
|
||||
virtual String parse(const ByteVector &data) const;
|
||||
};
|
||||
|
||||
//! The main class in the ID3v2 implementation
|
||||
|
||||
/*!
|
||||
@@ -323,6 +353,27 @@ namespace TagLib {
|
||||
*/
|
||||
// BIC: combine with the above method
|
||||
ByteVector render(int version) const;
|
||||
|
||||
/*!
|
||||
* Gets the current string handler that decides how the "Latin-1" data
|
||||
* will be converted to and from binary data.
|
||||
*
|
||||
* \see Latin1StringHandler
|
||||
*/
|
||||
static Latin1StringHandler const *latin1StringHandler();
|
||||
|
||||
/*!
|
||||
* Sets the string handler that decides how the "Latin-1" data will be
|
||||
* converted to and from binary data.
|
||||
* If the parameter \a handler is null, the previous handler is
|
||||
* released and default ISO-8859-1 handler is restored.
|
||||
*
|
||||
* \note The caller is responsible for deleting the previous handler
|
||||
* as needed after it is released.
|
||||
*
|
||||
* \see Latin1StringHandler
|
||||
*/
|
||||
static void setLatin1StringHandler(const Latin1StringHandler *handler);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
|
||||
@@ -99,6 +99,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
|
||||
@@ -221,7 +221,7 @@ void MPEG::Properties::read()
|
||||
double length = timePerFrame * d->xingHeader->totalFrames();
|
||||
|
||||
d->length = int(length);
|
||||
d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0;
|
||||
d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0;
|
||||
}
|
||||
else {
|
||||
// Since there was no valid Xing header found, we hope that we're in a constant
|
||||
|
||||
@@ -263,9 +263,9 @@ void Ogg::FLAC::File::scan()
|
||||
d->hasXiphComment = true;
|
||||
d->commentPacket = ipacket;
|
||||
}
|
||||
else if(blockType > 5)
|
||||
else if(blockType > 5) {
|
||||
debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// End of metadata, now comes the datastream
|
||||
|
||||
@@ -75,6 +75,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -100,6 +100,9 @@ namespace TagLib {
|
||||
* \note This constructor is protected since Ogg::File shouldn't be
|
||||
* instantiated directly but rather should be used through the codec
|
||||
* specific subclasses.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream);
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -74,6 +74,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -173,7 +173,7 @@ void Vorbis::Properties::read()
|
||||
long long end = last->absoluteGranularPosition();
|
||||
|
||||
if(start >= 0 && end >= 0 && d->sampleRate > 0)
|
||||
d->length = (end - start) / (long long) d->sampleRate;
|
||||
d->length = (int)((end - start) / (long long) d->sampleRate);
|
||||
else
|
||||
debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
|
||||
"end of this file was incorrect or the sample rate is zero.");
|
||||
|
||||
@@ -205,11 +205,14 @@ PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties)
|
||||
for(StringList::ConstIterator it = toRemove.begin(); it != toRemove.end(); ++it)
|
||||
removeField(*it);
|
||||
|
||||
// now go through keys in \a properties and check that the values match those in the xiph comment */
|
||||
// now go through keys in \a properties and check that the values match those in the xiph comment
|
||||
PropertyMap invalid;
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it)
|
||||
{
|
||||
if(!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) {
|
||||
if(!checkKey(it->first))
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) {
|
||||
const StringList &sl = it->second;
|
||||
if(sl.size() == 0)
|
||||
// zero size string list -> remove the tag with all values
|
||||
@@ -224,7 +227,18 @@ PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties)
|
||||
}
|
||||
}
|
||||
}
|
||||
return PropertyMap();
|
||||
return invalid;
|
||||
}
|
||||
|
||||
bool Ogg::XiphComment::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < 1)
|
||||
return false;
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
|
||||
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
|
||||
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
String Ogg::XiphComment::vendorID() const
|
||||
|
||||
@@ -151,10 +151,18 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* The tags from the given map will be stored one-to-one in the file.
|
||||
* The tags from the given map will be stored one-to-one in the file,
|
||||
* except for invalid keys (less than one character, non-ASCII, or
|
||||
* containing '=' or '~') in which case the according values will
|
||||
* be contained in the returned PropertyMap.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap&);
|
||||
|
||||
/*!
|
||||
* Check if the given String is a valid Xiph comment key.
|
||||
*/
|
||||
static bool checkKey(const String&);
|
||||
|
||||
/*!
|
||||
* Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the
|
||||
* most common case always returns "Xiph.Org libVorbis I 20020717".
|
||||
|
||||
@@ -69,6 +69,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -139,7 +139,7 @@ int RIFF::AIFF::Properties::sampleWidth() const
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
uint RIFF::AIFF::Properties::sampleFrames() const
|
||||
TagLib::uint RIFF::AIFF::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ void RIFF::AIFF::Properties::read(const ByteVector &data)
|
||||
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->sampleRate = sampleRate;
|
||||
d->bitrate = (sampleRate * d->sampleWidth * d->channels) / 1000.0;
|
||||
d->sampleRate = (int)sampleRate;
|
||||
d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
|
||||
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -104,7 +104,7 @@ int RIFF::WAV::Properties::sampleWidth() const
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
uint RIFF::WAV::Properties::sampleFrames() const
|
||||
TagLib::uint RIFF::WAV::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \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,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
|
||||
@@ -88,7 +88,7 @@ int S3M::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::lengthInPatterns() const
|
||||
TagLib::ushort S3M::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -98,27 +98,27 @@ bool S3M::Properties::stereo() const
|
||||
return d->stereo;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::sampleCount() const
|
||||
TagLib::ushort S3M::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::patternCount() const
|
||||
TagLib::ushort S3M::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::flags() const
|
||||
TagLib::ushort S3M::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::trackerVersion() const
|
||||
TagLib::ushort S3M::Properties::trackerVersion() const
|
||||
{
|
||||
return d->trackerVersion;
|
||||
}
|
||||
|
||||
ushort S3M::Properties::fileFormatVersion() const
|
||||
TagLib::ushort S3M::Properties::fileFormatVersion() const
|
||||
{
|
||||
return d->fileFormatVersion;
|
||||
}
|
||||
|
||||
@@ -74,15 +74,17 @@ using namespace TagLib;
|
||||
class File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(IOStream *stream);
|
||||
FilePrivate(IOStream *stream, bool owner);
|
||||
|
||||
IOStream *stream;
|
||||
bool streamOwner;
|
||||
bool valid;
|
||||
static const uint bufferSize = 1024;
|
||||
};
|
||||
|
||||
File::FilePrivate::FilePrivate(IOStream *stream) :
|
||||
File::FilePrivate::FilePrivate(IOStream *stream, bool owner) :
|
||||
stream(stream),
|
||||
streamOwner(owner),
|
||||
valid(true)
|
||||
{
|
||||
}
|
||||
@@ -94,17 +96,17 @@ File::FilePrivate::FilePrivate(IOStream *stream) :
|
||||
File::File(FileName fileName)
|
||||
{
|
||||
IOStream *stream = new FileStream(fileName);
|
||||
d = new FilePrivate(stream);
|
||||
d = new FilePrivate(stream, true);
|
||||
}
|
||||
|
||||
File::File(IOStream *stream)
|
||||
{
|
||||
d = new FilePrivate(stream);
|
||||
d = new FilePrivate(stream, false);
|
||||
}
|
||||
|
||||
File::~File()
|
||||
{
|
||||
if(d->stream)
|
||||
if(d->stream && d->streamOwner)
|
||||
delete d->stream;
|
||||
delete d;
|
||||
}
|
||||
@@ -452,12 +454,32 @@ long File::length()
|
||||
|
||||
bool File::isReadable(const char *file)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
|
||||
return _access_s(file, R_OK) == 0;
|
||||
|
||||
#else
|
||||
|
||||
return access(file, R_OK) == 0;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool File::isWritable(const char *file)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
|
||||
return _access_s(file, W_OK) == 0;
|
||||
|
||||
#else
|
||||
|
||||
return access(file, W_OK) == 0;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -260,6 +260,9 @@ namespace TagLib {
|
||||
/*!
|
||||
* Construct a File object and use the \a stream instance.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note Constructor is protected since this class should only be
|
||||
* instantiated through subclasses.
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,51 @@ struct FileNameHandle : public std::string
|
||||
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
FILE *openFile(const FileName &path, bool readOnly)
|
||||
{
|
||||
// Calls a proper variation of fopen() depending on the compiling environment.
|
||||
|
||||
#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+");
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
class FileStream::FileStreamPrivate
|
||||
{
|
||||
public:
|
||||
@@ -87,41 +132,15 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea
|
||||
{
|
||||
// First try with read / write mode, if that fails, fall back to read only.
|
||||
|
||||
#ifdef _WIN32
|
||||
if(!openReadOnly)
|
||||
file = openFile(name, false);
|
||||
|
||||
if(wcslen((const wchar_t *) fileName) > 0) {
|
||||
if(file)
|
||||
readOnly = false;
|
||||
else
|
||||
file = openFile(name, true);
|
||||
|
||||
if(openReadOnly)
|
||||
file = _wfopen(name, L"rb");
|
||||
else {
|
||||
file = _wfopen(name, L"rb+");
|
||||
|
||||
if(file)
|
||||
readOnly = false;
|
||||
else
|
||||
file = _wfopen(name, L"rb");
|
||||
}
|
||||
|
||||
if(file)
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if(openReadOnly)
|
||||
file = fopen(name, "rb");
|
||||
else {
|
||||
file = fopen(name, "rb+");
|
||||
|
||||
if(file)
|
||||
readOnly = false;
|
||||
else
|
||||
file = fopen(name, "rb");
|
||||
}
|
||||
|
||||
if(!file)
|
||||
{
|
||||
if(!file) {
|
||||
debug("Could not open file " + String((const char *) name));
|
||||
}
|
||||
}
|
||||
@@ -379,7 +398,17 @@ long FileStream::length()
|
||||
|
||||
void FileStream::truncate(long length)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later
|
||||
|
||||
ftruncate(_fileno(d->file), length);
|
||||
|
||||
#else
|
||||
|
||||
ftruncate(fileno(d->file), length);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
TagLib::uint FileStream::bufferSize()
|
||||
|
||||
@@ -34,7 +34,7 @@ PropertyMap::PropertyMap(const PropertyMap &m) : SimplePropertyMap(m), unsupport
|
||||
PropertyMap::PropertyMap(const SimplePropertyMap &m)
|
||||
{
|
||||
for(SimplePropertyMap::ConstIterator it = m.begin(); it != m.end(); ++it){
|
||||
String key = prepareKey(it->first);
|
||||
String key = it->first.upper();
|
||||
if(!key.isNull())
|
||||
insert(it->first, it->second);
|
||||
else
|
||||
@@ -48,10 +48,7 @@ PropertyMap::~PropertyMap()
|
||||
|
||||
bool PropertyMap::insert(const String &key, const StringList &values)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(realKey.isNull())
|
||||
return false;
|
||||
|
||||
String realKey = key.upper();
|
||||
Iterator result = SimplePropertyMap::find(realKey);
|
||||
if(result == end())
|
||||
SimplePropertyMap::insert(realKey, values);
|
||||
@@ -62,9 +59,7 @@ bool PropertyMap::insert(const String &key, const StringList &values)
|
||||
|
||||
bool PropertyMap::replace(const String &key, const StringList &values)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(realKey.isNull())
|
||||
return false;
|
||||
String realKey = key.upper();
|
||||
SimplePropertyMap::erase(realKey);
|
||||
SimplePropertyMap::insert(realKey, values);
|
||||
return true;
|
||||
@@ -72,26 +67,17 @@ bool PropertyMap::replace(const String &key, const StringList &values)
|
||||
|
||||
PropertyMap::Iterator PropertyMap::find(const String &key)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(realKey.isNull())
|
||||
return end();
|
||||
return SimplePropertyMap::find(realKey);
|
||||
return SimplePropertyMap::find(key.upper());
|
||||
}
|
||||
|
||||
PropertyMap::ConstIterator PropertyMap::find(const String &key) const
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(realKey.isNull())
|
||||
return end();
|
||||
return SimplePropertyMap::find(realKey);
|
||||
return SimplePropertyMap::find(key.upper());
|
||||
}
|
||||
|
||||
bool PropertyMap::contains(const String &key) const
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(realKey.isNull())
|
||||
return false;
|
||||
return SimplePropertyMap::contains(realKey);
|
||||
return SimplePropertyMap::contains(key.upper());
|
||||
}
|
||||
|
||||
bool PropertyMap::contains(const PropertyMap &other) const
|
||||
@@ -107,9 +93,7 @@ bool PropertyMap::contains(const PropertyMap &other) const
|
||||
|
||||
PropertyMap &PropertyMap::erase(const String &key)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
if(!realKey.isNull())
|
||||
SimplePropertyMap::erase(realKey);
|
||||
SimplePropertyMap::erase(key.upper());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -122,23 +106,20 @@ PropertyMap &PropertyMap::erase(const PropertyMap &other)
|
||||
|
||||
PropertyMap &PropertyMap::merge(const PropertyMap &other)
|
||||
{
|
||||
for(PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it) {
|
||||
for(PropertyMap::ConstIterator it = other.begin(); it != other.end(); ++it)
|
||||
insert(it->first, it->second);
|
||||
}
|
||||
unsupported.append(other.unsupported);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const StringList &PropertyMap::operator[](const String &key) const
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
return SimplePropertyMap::operator[](realKey);
|
||||
return SimplePropertyMap::operator[](key.upper());
|
||||
}
|
||||
|
||||
StringList &PropertyMap::operator[](const String &key)
|
||||
{
|
||||
String realKey = prepareKey(key);
|
||||
return SimplePropertyMap::operator[](realKey);
|
||||
return SimplePropertyMap::operator[](key.upper());
|
||||
}
|
||||
|
||||
bool PropertyMap::operator==(const PropertyMap &other) const
|
||||
@@ -190,13 +171,3 @@ const StringList &PropertyMap::unsupportedData() const
|
||||
{
|
||||
return unsupported;
|
||||
}
|
||||
|
||||
String PropertyMap::prepareKey(const String &proposed) {
|
||||
if(proposed.isEmpty())
|
||||
return String::null;
|
||||
for (String::ConstIterator it = proposed.begin(); it != proposed.end(); it++)
|
||||
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
|
||||
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
|
||||
return String::null;
|
||||
return proposed.upper();
|
||||
}
|
||||
|
||||
@@ -36,15 +36,10 @@ namespace TagLib {
|
||||
* ("tags") realized as pairs of a case-insensitive key
|
||||
* and a nonempty list of corresponding values, each value being an an arbitrary
|
||||
* unicode String.
|
||||
* The key has the same restrictions as in the vorbis comment specification,
|
||||
* i.e. it must contain at least one character; all printable ASCII characters
|
||||
* except '=' and '~' are allowed.
|
||||
*
|
||||
* In order to be safe with other formats, keep these additional restrictions in mind:
|
||||
*
|
||||
* - APE only allows keys from 2 to 16 printable ASCII characters (including space),
|
||||
* with the exception of these strings: ID3, TAG, OggS, MP+
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap
|
||||
@@ -126,16 +121,17 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns a reference to the value associated with \a key.
|
||||
*
|
||||
* \note: This has undefined behavior if the key is not valid or not
|
||||
* present in the map.
|
||||
* \note: If \a key is not contained in the map, an empty
|
||||
* StringList is returned without error.
|
||||
*/
|
||||
const StringList &operator[](const String &key) const;
|
||||
|
||||
/*!
|
||||
* Returns a reference to the value associated with \a key.
|
||||
*
|
||||
* \note: This has undefined behavior if the key is not valid or not
|
||||
* present in the map.
|
||||
* \note: If \a key is not contained in the map, an empty
|
||||
* StringList is returned. You can also directly add entries
|
||||
* by using this function as an lvalue.
|
||||
*/
|
||||
StringList &operator[](const String &key);
|
||||
|
||||
@@ -168,12 +164,6 @@ namespace TagLib {
|
||||
|
||||
String toString() const;
|
||||
|
||||
/*!
|
||||
* Converts \a proposed into another String suitable to be used as
|
||||
* a key, or returns String::null if this is not possible.
|
||||
*/
|
||||
static String prepareKey(const String &proposed);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
@@ -233,8 +233,9 @@ std::string String::to8Bit(bool unicode) const
|
||||
&target, targetBuffer + outputBufferSize,
|
||||
Unicode::lenientConversion);
|
||||
|
||||
if(result != Unicode::conversionOK)
|
||||
if(result != Unicode::conversionOK) {
|
||||
debug("String::to8Bit() - Unicode conversion error.");
|
||||
}
|
||||
|
||||
int newSize = target - targetBuffer;
|
||||
s.resize(newSize);
|
||||
@@ -259,8 +260,17 @@ const char *String::toCString(bool unicode) const
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -335,9 +345,6 @@ bool String::startsWith(const String &s) const
|
||||
|
||||
String String::substr(uint position, uint n) const
|
||||
{
|
||||
if(n > position + d->data.size())
|
||||
n = d->data.size() - position;
|
||||
|
||||
String s;
|
||||
s.d->data = d->data.substr(position, n);
|
||||
return s;
|
||||
@@ -779,9 +786,9 @@ void String::prepare(Type t)
|
||||
&target, targetBuffer + bufferSize,
|
||||
Unicode::lenientConversion);
|
||||
|
||||
if(result != Unicode::conversionOK)
|
||||
if(result != Unicode::conversionOK) {
|
||||
debug("String::prepare() - Unicode conversion error.");
|
||||
|
||||
}
|
||||
|
||||
int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0;
|
||||
d->data.resize(newSize);
|
||||
|
||||
@@ -155,7 +155,7 @@ ConversionResult ConvertUTF16toUTF8 (
|
||||
case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
|
||||
case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
|
||||
case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
|
||||
case 1: *--target = ch | firstByteMark[bytesToWrite];
|
||||
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
@@ -253,7 +253,7 @@ ConversionResult ConvertUTF8toUTF16 (
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = ch; /* normal case */
|
||||
*target++ = (UTF16)ch; /* normal case */
|
||||
}
|
||||
} else if (ch > UNI_MAX_UTF16) {
|
||||
if (flags == strictConversion) {
|
||||
@@ -270,7 +270,7 @@ ConversionResult ConvertUTF8toUTF16 (
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = (ch >> halfShift) + UNI_SUR_HIGH_START;
|
||||
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (ch & halfMask) + UNI_SUR_LOW_START;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,9 @@ namespace TagLib {
|
||||
* Contructs an TrueAudio 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 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);
|
||||
@@ -109,6 +112,9 @@ namespace TagLib {
|
||||
* 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.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
|
||||
@@ -103,7 +103,7 @@ int TrueAudio::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
uint TrueAudio::Properties::sampleFrames() const
|
||||
TagLib::uint TrueAudio::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
|
||||
@@ -91,6 +91,9 @@ 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.
|
||||
*
|
||||
* \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);
|
||||
|
||||
@@ -117,7 +117,7 @@ int WavPack::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
uint WavPack::Properties::sampleFrames() const
|
||||
TagLib::uint WavPack::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace TagLib {
|
||||
* Contructs a Extended Module 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.
|
||||
*
|
||||
* \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,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
|
||||
@@ -84,47 +84,47 @@ int XM::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
ushort XM::Properties::lengthInPatterns() const
|
||||
TagLib::ushort XM::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
|
||||
ushort XM::Properties::version() const
|
||||
TagLib::ushort XM::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
ushort XM::Properties::restartPosition() const
|
||||
TagLib::ushort XM::Properties::restartPosition() const
|
||||
{
|
||||
return d->restartPosition;
|
||||
}
|
||||
|
||||
ushort XM::Properties::patternCount() const
|
||||
TagLib::ushort XM::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
ushort XM::Properties::instrumentCount() const
|
||||
TagLib::ushort XM::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
uint XM::Properties::sampleCount() const
|
||||
TagLib::uint XM::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
ushort XM::Properties::flags() const
|
||||
TagLib::ushort XM::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
ushort XM::Properties::tempo() const
|
||||
TagLib::ushort XM::Properties::tempo() const
|
||||
{
|
||||
return d->tempo;
|
||||
}
|
||||
|
||||
ushort XM::Properties::bpmSpeed() const
|
||||
TagLib::ushort XM::Properties::bpmSpeed() const
|
||||
{
|
||||
return d->bpmSpeed;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ SET(test_runner_SRCS
|
||||
test_bytevectorlist.cpp
|
||||
test_bytevectorstream.cpp
|
||||
test_string.cpp
|
||||
test_propertymap.cpp
|
||||
test_fileref.cpp
|
||||
test_id3v1.cpp
|
||||
test_id3v2.cpp
|
||||
|
||||
BIN
tests/data/w000.mp3
Normal file
BIN
tests/data/w000.mp3
Normal file
Binary file not shown.
@@ -19,6 +19,7 @@ class TestAPETag : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testIsEmpty2);
|
||||
CPPUNIT_TEST(testPropertyInterface1);
|
||||
CPPUNIT_TEST(testPropertyInterface2);
|
||||
CPPUNIT_TEST(testInvalidKeys);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -76,12 +77,27 @@ public:
|
||||
APE::Item item3 = APE::Item("TRACKNUMBER", "29");
|
||||
tag.setItem("TRACKNUMBER", item3);
|
||||
properties = tag.properties();
|
||||
CPPUNIT_ASSERT_EQUAL(2u, properties["TRACKNUMBER"].size());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), properties["TRACKNUMBER"].size());
|
||||
CPPUNIT_ASSERT_EQUAL(String("17"), properties["TRACKNUMBER"][0]);
|
||||
CPPUNIT_ASSERT_EQUAL(String("29"), properties["TRACKNUMBER"][1]);
|
||||
|
||||
}
|
||||
|
||||
void testInvalidKeys()
|
||||
{
|
||||
PropertyMap properties;
|
||||
properties["A"] = String("invalid key: one character");
|
||||
properties["MP+"] = String("invalid key: forbidden string");
|
||||
properties["A B~C"] = String("valid key: space and tilde");
|
||||
properties["ARTIST"] = String("valid key: normal one");
|
||||
|
||||
APE::Tag tag;
|
||||
PropertyMap unsuccessful = tag.setProperties(properties);
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), unsuccessful.size());
|
||||
CPPUNIT_ASSERT(unsuccessful.contains("A"));
|
||||
CPPUNIT_ASSERT(unsuccessful.contains("MP+"));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestAPETag);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <relativevolumeframe.h>
|
||||
#include <popularimeterframe.h>
|
||||
#include <urllinkframe.h>
|
||||
#include <ownershipframe.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "utils.h"
|
||||
@@ -61,6 +62,8 @@ class TestID3v2 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testRenderUrlLinkFrame);
|
||||
CPPUNIT_TEST(testParseUserUrlLinkFrame);
|
||||
CPPUNIT_TEST(testRenderUserUrlLinkFrame);
|
||||
CPPUNIT_TEST(testParseOwnershipFrame);
|
||||
CPPUNIT_TEST(testRenderOwnershipFrame);
|
||||
CPPUNIT_TEST(testSaveUTF16Comment);
|
||||
CPPUNIT_TEST(testUpdateGenre23_1);
|
||||
CPPUNIT_TEST(testUpdateGenre23_2);
|
||||
@@ -69,8 +72,10 @@ class TestID3v2 : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testDowngradeTo23);
|
||||
// CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together
|
||||
CPPUNIT_TEST(testCompressedFrameWithBrokenLength);
|
||||
CPPUNIT_TEST(testW000);
|
||||
CPPUNIT_TEST(testPropertyInterface);
|
||||
CPPUNIT_TEST(testPropertyInterface2);
|
||||
CPPUNIT_TEST(testDeleteFrame);
|
||||
CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
@@ -384,6 +389,38 @@ public:
|
||||
"http://example.com", 33), // URL
|
||||
f.render());
|
||||
}
|
||||
|
||||
void testParseOwnershipFrame()
|
||||
{
|
||||
ID3v2::OwnershipFrame f(
|
||||
ByteVector("OWNE" // Frame ID
|
||||
"\x00\x00\x00\x19" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x00" // Text encoding
|
||||
"GBP1.99\x00" // Price paid
|
||||
"20120905" // Date of purchase
|
||||
"Beatport", 35)); // Seller
|
||||
CPPUNIT_ASSERT_EQUAL(String("GBP1.99"), f.pricePaid());
|
||||
CPPUNIT_ASSERT_EQUAL(String("20120905"), f.datePurchased());
|
||||
CPPUNIT_ASSERT_EQUAL(String("Beatport"), f.seller());
|
||||
}
|
||||
|
||||
void testRenderOwnershipFrame()
|
||||
{
|
||||
ID3v2::OwnershipFrame f;
|
||||
f.setPricePaid("GBP1.99");
|
||||
f.setDatePurchased("20120905");
|
||||
f.setSeller("Beatport");
|
||||
CPPUNIT_ASSERT_EQUAL(
|
||||
ByteVector("OWNE" // Frame ID
|
||||
"\x00\x00\x00\x19" // Frame size
|
||||
"\x00\x00" // Frame flags
|
||||
"\x00" // Text encoding
|
||||
"GBP1.99\x00" // Price paid
|
||||
"20120905" // Date of purchase
|
||||
"Beatport", 35), // URL
|
||||
f.render());
|
||||
}
|
||||
|
||||
void testItunes24FrameSize()
|
||||
{
|
||||
@@ -551,6 +588,16 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String(""), frame->description());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(86414), frame->picture().size());
|
||||
}
|
||||
|
||||
void testW000()
|
||||
{
|
||||
MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false);
|
||||
CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000"));
|
||||
ID3v2::UrlLinkFrame *frame =
|
||||
dynamic_cast<TagLib::ID3v2::UrlLinkFrame*>(f.ID3v2Tag()->frameListMap()["W000"].front());
|
||||
CPPUNIT_ASSERT(frame);
|
||||
CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url());
|
||||
}
|
||||
|
||||
void testPropertyInterface()
|
||||
{
|
||||
@@ -629,24 +676,40 @@ public:
|
||||
CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty());
|
||||
}
|
||||
|
||||
void testDeleteFrame()
|
||||
{
|
||||
ScopedFileCopy copy("rare_frames", ".mp3");
|
||||
string newname = copy.fileName();
|
||||
MPEG::File f(newname.c_str());
|
||||
ID3v2::Tag *t = f.ID3v2Tag();
|
||||
ID3v2::Frame *frame = t->frameList("TCON")[0];
|
||||
CPPUNIT_ASSERT_EQUAL(1u, t->frameList("TCON").size());
|
||||
t->removeFrame(frame, true);
|
||||
f.save(MPEG::File::ID3v2);
|
||||
|
||||
MPEG::File f2(newname.c_str());
|
||||
t = f2.ID3v2Tag();
|
||||
CPPUNIT_ASSERT(t->frameList("TCON").isEmpty());
|
||||
}
|
||||
|
||||
void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2()
|
||||
{
|
||||
ScopedFileCopy copy("xing", ".mp3");
|
||||
string newname = copy.fileName();
|
||||
|
||||
|
||||
{
|
||||
MPEG::File foo(newname.c_str());
|
||||
foo.tag()->setArtist("Artist");
|
||||
foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
MPEG::File bar(newname.c_str());
|
||||
bar.ID3v2Tag()->removeFrames("TPE1");
|
||||
// Should strip ID3v1 here and not add old values to ID3v2 again
|
||||
bar.save(MPEG::File::ID3v2, true);
|
||||
}
|
||||
|
||||
|
||||
MPEG::File f(newname.c_str());
|
||||
CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1"));
|
||||
}
|
||||
|
||||
32
tests/test_propertymap.cpp
Normal file
32
tests/test_propertymap.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <tpropertymap.h>
|
||||
class TestPropertyMap : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestPropertyMap);
|
||||
CPPUNIT_TEST(testInvalidKeys);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
void testInvalidKeys()
|
||||
{
|
||||
TagLib::PropertyMap map1;
|
||||
CPPUNIT_ASSERT(map1.isEmpty());
|
||||
map1["ÄÖÜ"].append("test");
|
||||
CPPUNIT_ASSERT_EQUAL(map1.size(), 1u);
|
||||
|
||||
TagLib::PropertyMap map2;
|
||||
map2["ÄÖÜ"].append("test");
|
||||
CPPUNIT_ASSERT(map1 == map2);
|
||||
CPPUNIT_ASSERT(map1.contains(map2));
|
||||
|
||||
map2["ARTIST"] = TagLib::String("Test Artist");
|
||||
CPPUNIT_ASSERT(map1 != map2);
|
||||
CPPUNIT_ASSERT(map2.contains(map1));
|
||||
|
||||
map2["ÄÖÜ"].append("test 2");
|
||||
CPPUNIT_ASSERT(!map2.contains(map1));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestPropertyMap);
|
||||
@@ -41,6 +41,7 @@ class TestString : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testAppendCharDetach);
|
||||
CPPUNIT_TEST(testAppendStringDetach);
|
||||
CPPUNIT_TEST(testToInt);
|
||||
CPPUNIT_TEST(testSubstr);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -193,6 +194,13 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String("-123aa").toInt(), -123);
|
||||
}
|
||||
|
||||
void testSubstr()
|
||||
{
|
||||
CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2));
|
||||
CPPUNIT_ASSERT_EQUAL(String("12"), String("0123456").substr(1, 2));
|
||||
CPPUNIT_ASSERT_EQUAL(String("123456"), String("0123456").substr(1, 200));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestString);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <xiphcomment.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include "utils.h"
|
||||
|
||||
@@ -15,6 +16,7 @@ class TestXiphComment : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testSetYear);
|
||||
CPPUNIT_TEST(testTrack);
|
||||
CPPUNIT_TEST(testSetTrack);
|
||||
CPPUNIT_TEST(testInvalidKeys);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@@ -59,6 +61,19 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(String("3"), cmt.fieldListMap()["TRACKNUMBER"].front());
|
||||
}
|
||||
|
||||
void testInvalidKeys()
|
||||
{
|
||||
PropertyMap map;
|
||||
map[""] = String("invalid key: empty string");
|
||||
map["A=B"] = String("invalid key: contains '='");
|
||||
map["A~B"] = String("invalid key: contains '~'");
|
||||
|
||||
Ogg::XiphComment cmt;
|
||||
PropertyMap unsuccessful = cmt.setProperties(map);
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), unsuccessful.size());
|
||||
CPPUNIT_ASSERT(cmt.properties().isEmpty());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestXiphComment);
|
||||
|
||||
Reference in New Issue
Block a user