return readBlock(d->chunks[i].size);
}
-void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
+void RIFF::File::setChunkData(uint i, const ByteVector &data)
{
- if(d->chunks.size() == 0) {
- debug("RIFF::File::setChunkData - No valid chunks found.");
- return;
- }
+ // First we update the global size
- for(uint i = 0; i < d->chunks.size(); i++) {
- if(d->chunks[i].name == name) {
+ d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
+ insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
- // First we update the global size
+ // Now update the specific chunk
- d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
- insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
+ writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
- // Now update the specific chunk
+ d->chunks[i].size = data.size();
+ d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
- writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8);
+ // Now update the internal offsets
- d->chunks[i].size = data.size();
- d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
+ 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;
+}
- // Now update the internal offsets
+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(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;
+ if(alwaysCreate && name != "LIST") {
+ debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks.");
+ return;
+ }
- return;
+ if(!alwaysCreate) {
+ for(uint i = 0; i < d->chunks.size(); i++) {
+ if(d->chunks[i].name == name) {
+ setChunkData(i, data);
+ return;
+ }
}
}
// Now add the chunk to the file
- writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
+ long len = length();
+ writeChunk(name, data, offset, std::max<long>(0, length() - offset), (offset & 1) ? 1 : 0);
// And update our internal structure
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
////////////////////////////////////////////////////////////////////////////////
*/
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
* created after the existing chunks.
*
+ * \note If \a alwaysCreate is true, a new chunk is created regardless of
+ * existence of chunk \a name. It should be used for only "LIST" chunks.
+ *
+ * \warning This will update the file immediately.
+ */
+ void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate = false);
+
+ /*!
+ * 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 setChunkData(const ByteVector &name, const ByteVector &data);
+ void removeChunk(const ByteVector &name);
private:
File(const File &);
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ 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::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);
+}
+
+
+
+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()
+{
+}
+
+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;
+}
+
+void RIFF::Info::Tag::parse(const ByteVector &data)
+{
+ uint p = 4;
+ while(p < data.size()) {
+ uint size = data.mid(p + 4, 4).toUInt(false);
+ d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
+
+ p += ((size + 1) & ~1) + 8;
+ }
+}
+
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ 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/ *
+ ***************************************************************************/
+
+#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 refering 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
* 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 ")
{
-
}
~FilePrivate()
{
delete properties;
- delete tag;
}
Properties *properties;
- ID3v2::Tag *tag;
+
ByteVector tagChunkID;
+
+ TagUnion tag;
};
////////////////////////////////////////////////////////////////////////////////
delete d;
}
-ID3v2::Tag *RIFF::WAV::File::tag() const
+Tag *RIFF::WAV::File::tag() const
{
- return d->tag;
+ return &d->tag;
+}
+
+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 d->tag.properties();
}
PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties)
{
- return d->tag->setProperties(properties);
+ return d->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.");
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));
+ }
+
+ 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);
+ }
+ }
return true;
}
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)));
}
- 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));
+ }
}
+ 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){
+ uint chunkId = findInfoTagChunk();
+ if(chunkId != -1)
+ removeChunk(chunkId);
+ }
+}
+
+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 -1;
}
#include "rifffile.h"
#include "id3v2tag.h"
+#include "infotag.h"
#include "wavproperties.h"
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
/*!
* Returns the Tag for this file.
*/
- virtual ID3v2::Tag *tag() const;
+ virtual Tag *tag() const;
+
+ ID3v2::Tag *ID3v2Tag() const;
+
+ Info::Tag *InfoTag() const;
/*!
* Implements the unified property interface -- export function.
*/
virtual bool save();
+ bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4);
+
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;
};
CPPUNIT_TEST(testMP4_3);
CPPUNIT_TEST(testTrueAudio);
CPPUNIT_TEST(testAPE);
+ CPPUNIT_TEST(testWav);
CPPUNIT_TEST_SUITE_END();
public:
fileRefSave("no-tags", ".3g2");
}
+ void testWav()
+ {
+ fileRefSave("empty", ".wav");
+ }
+
void testOGA_FLAC()
{
FileRef *f = new FileRef(TEST_FILE_PATH_C("empty_flac.oga"));
void testAPE()
{
- fileRefSave("mac-399.ape", ".ape");
+ fileRefSave("mac-399", ".ape");
}
};
--- /dev/null
+#include <cppunit/extensions/HelperMacros.h>
+#include <string>
+#include <stdio.h>
+#include <infotag.h>
+#include <tpropertymap.h>
+#include <tdebug.h>
+#include "utils.h"
+
+using namespace std;
+using namespace TagLib;
+
+class TestInfoTag : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(TestInfoTag);
+ CPPUNIT_TEST(testTitle);
+ CPPUNIT_TEST(testNumericFields);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testTitle()
+ {
+ RIFF::Info::Tag tag;
+
+ CPPUNIT_ASSERT_EQUAL(String(""), tag.title());
+ tag.setTitle("Test title 1");
+ CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title());
+ }
+
+ void testNumericFields()
+ {
+ RIFF::Info::Tag tag;
+
+ CPPUNIT_ASSERT_EQUAL((uint)0, tag.track());
+ tag.setTrack(1234);
+ CPPUNIT_ASSERT_EQUAL((uint)1234, tag.track());
+ CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT"));
+
+ CPPUNIT_ASSERT_EQUAL((uint)0, tag.year());
+ tag.setYear(1234);
+ CPPUNIT_ASSERT_EQUAL((uint)1234, tag.year());
+ CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD"));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag);