aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Wheeler <scott@directededge.com>2019-09-12 06:46:23 +0200
committerScott Wheeler <scott@directededge.com>2019-09-12 07:12:29 +0200
commit088e063bbb9df4aec4f9e74f88f7a71ead9c2119 (patch)
tree5eb6aeeee23ec0d87da55c63a124a9647519f216
parentf1b40de66b1c73fbbd28237c55fb5cb9ae6a8bb6 (diff)
Begin implementing the rest of the File API
This adds: - ID3v2 versions (v3, v4): working - Ability to create missing tags (working for ID3v2, not for DIIN) - Ability to strip tags (incomplete) - Some tests for these features, and other basic functionality (incomplete) There are still a number of major issues in the code, so I'm pushing this out to a feature branch for now. Specifically, the structure of the code does not closely follow the format specification (as in other implementations) and is not internally documented, so it is very difficult to follow. Specifically chunk type names should follow more closely those in the spec. Right now there is only one chunk type (whereas the spec has several), and the notion / implementation of child chunks seems incomplete. Addtionally, before this is merged back to master a number of formatting changes are needed: - Spacing as done in the rest of TagLib - Remove usage of NULL - Usage of TagLib's containers instead of STL containers - Use of std::array rather than C arrays where fixed length containers are needed - More complete test coverage CC #878
-rw-r--r--taglib/dsdiff/dsdifffile.cpp255
-rw-r--r--taglib/dsdiff/dsdifffile.h46
-rw-r--r--tests/test_dsdiff.cpp104
3 files changed, 290 insertions, 115 deletions
diff --git a/taglib/dsdiff/dsdifffile.cpp b/taglib/dsdiff/dsdifffile.cpp
index ac867b2b..fa993ce5 100644
--- a/taglib/dsdiff/dsdifffile.cpp
+++ b/taglib/dsdiff/dsdifffile.cpp
@@ -35,16 +35,28 @@
using namespace TagLib;
-struct Chunk64
-{
- ByteVector name;
- unsigned long long offset;
- unsigned long long size;
- char padding;
-};
-
namespace
{
+ struct Chunk64
+ {
+ ByteVector name;
+ unsigned long long offset;
+ unsigned long long size;
+ char padding;
+ };
+
+ typedef std::vector<Chunk64> ChunkList;
+
+ int chunkIndex(const ChunkList &chunks, const ByteVector &id)
+ {
+ for(int i = 0; i < chunks.size(); i++) {
+ if(chunks[i].name == id)
+ return i;
+ }
+
+ return -1;
+ }
+
enum {
ID3v2Index = 0,
DIINIndex = 1
@@ -81,8 +93,8 @@ public:
ByteVector type;
unsigned long long size;
ByteVector format;
- std::vector<Chunk64> chunks;
- std::vector<Chunk64> childChunks[2];
+ ChunkList chunks;
+ ChunkList childChunks[2];
int childChunkIndex[2];
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in
@@ -142,9 +154,9 @@ TagLib::Tag *DSDIFF::File::tag() const
return &d->tag;
}
-ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
+ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
{
- return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
+ return d->tag.access<ID3v2::Tag>(ID3v2Index, create);
}
bool DSDIFF::File::hasID3v2Tag() const
@@ -152,9 +164,9 @@ bool DSDIFF::File::hasID3v2Tag() const
return d->hasID3v2;
}
-DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
+DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
{
- return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
+ return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
}
bool DSDIFF::File::hasDIINTag() const
@@ -191,6 +203,11 @@ DSDIFF::Properties *DSDIFF::File::audioProperties() const
bool DSDIFF::File::save()
{
+ return save(AllTags);
+}
+
+bool DSDIFF::File::save(TagTypes tags, StripTags strip, ID3v2::Version version)
+{
if(readOnly()) {
debug("DSDIFF::File::save() -- File is read only.");
return false;
@@ -201,35 +218,41 @@ bool DSDIFF::File::save()
return false;
}
+ if(strip == StripOthers || strip == StripAll)
+ File::strip(static_cast<TagTypes>(AllTags & ~tags));
+
// First: save ID3V2 chunk
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
- if(d->isID3InPropChunk) {
- if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
- setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
- d->hasID3v2 = true;
- }
- else {
- // Empty tag: remove it
- setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
- d->hasID3v2 = false;
- }
- }
- else {
- if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
- setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
- d->hasID3v2 = true;
+
+ if(tags & ID3v2 && id3v2Tag) {
+ if(d->isID3InPropChunk) {
+ if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
+ setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
+ d->hasID3v2 = true;
+ }
+ else {
+ // Empty tag: remove it
+ setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
+ d->hasID3v2 = false;
+ }
}
else {
- // Empty tag: remove it
- setRootChunkData(d->id3v2TagChunkID, ByteVector());
- d->hasID3v2 = false;
+ if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
+ setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
+ d->hasID3v2 = true;
+ }
+ else {
+ // Empty tag: remove it
+ setRootChunkData(d->id3v2TagChunkID, ByteVector());
+ d->hasID3v2 = false;
+ }
}
}
// Second: save the DIIN chunk
- if(d->hasDiin) {
- DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
+ DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
+ if(tags & DIIN && diinTag) {
if(!diinTag->title().isEmpty()) {
ByteVector diinTitle;
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
@@ -258,46 +281,77 @@ bool DSDIFF::File::save()
return true;
}
+void DSDIFF::File::strip(TagTypes tags)
+{
+ if(tags & ID3v2) {
+ removeRootChunk("ID3 ");
+ removeRootChunk("id3 ");
+ d->hasID3v2 = false;
+ d->tag.set(ID3v2Index, new ID3v2::Tag);
+
+ /// TODO: needs to also account for ID3v2 tags under the PROP chunk
+ }
+ if(tags & DIIN) {
+ removeRootChunk("DITI");
+ removeRootChunk("DIAR");
+ d->hasDiin = false;
+ d->tag.set(DIINIndex, new DIIN::Tag);
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
-void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
+void DSDIFF::File::removeRootChunk(unsigned int i)
{
- if(data.isEmpty()) {
- // Null data: remove chunk
- // Update global size
- unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
- d->size -= removedChunkTotalSize;
+ unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
+
+ d->size -= chunkSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
- removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
+ removeBlock(d->chunks[i].offset - 12, chunkSize);
// Update the internal offsets
+
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
d->chunks[r].offset = d->chunks[r - 1].offset + 12
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
d->chunks.erase(d->chunks.begin() + i);
- }
- else {
- // Non null data: update chunk
- // First we update the global size
- d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
- insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
+}
- // Now update the specific chunk
- writeChunk(d->chunks[i].name,
- data,
- d->chunks[i].offset - 12,
- d->chunks[i].size + d->chunks[i].padding + 12);
+void DSDIFF::File::removeRootChunk(const ByteVector &id)
+{
+ int i = chunkIndex(d->chunks, id);
- d->chunks[i].size = data.size();
- d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
+ if(i >= 0)
+ removeRootChunk(i);
+}
- // Finally update the internal offsets
- updateRootChunksStructure(i + 1);
+void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
+{
+ if(data.isEmpty()) {
+ removeRootChunk(i);
+ return;
}
+
+ // Non null data: update chunk
+ // First we update the global size
+ d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
+ insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
+
+ // Now update the specific chunk
+ writeChunk(d->chunks[i].name,
+ data,
+ d->chunks[i].offset - 12,
+ d->chunks[i].size + d->chunks[i].padding + 12);
+
+ d->chunks[i].size = data.size();
+ d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
+
+ // Finally update the internal offsets
+ updateRootChunksStructure(i + 1);
}
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
@@ -307,15 +361,15 @@ void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &da
return;
}
- for(unsigned int i = 0; i < d->chunks.size(); i++) {
- if(d->chunks[i].name == name) {
- setRootChunkData(i, data);
- return;
- }
+ int i = chunkIndex(d->chunks, name);
+
+ if(i >= 0) {
+ setRootChunkData(i, data);
+ return;
}
// Couldn't find an existing chunk, so let's create a new one.
- unsigned int i = d->chunks.size() - 1;
+ i = d->chunks.size() - 1;
unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
// First we update the global size
@@ -338,15 +392,12 @@ void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &da
d->chunks.push_back(chunk);
}
-void DSDIFF::File::setChildChunkData(unsigned int i,
- const ByteVector &data,
- unsigned int childChunkNum)
+void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
{
- std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
+ ChunkList &childChunks = d->childChunks[childChunkNum];
- if(data.isEmpty()) {
- // Null data: remove chunk
// Update global size
+
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
d->size -= removedChunkTotalSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
@@ -374,44 +425,54 @@ void DSDIFF::File::setChildChunkData(unsigned int i,
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
childChunks.erase(childChunks.begin() + i);
+}
+
+void DSDIFF::File::setChildChunkData(unsigned int i,
+ const ByteVector &data,
+ unsigned int childChunkNum)
+{
+ ChunkList &childChunks = d->childChunks[childChunkNum];
+
+ if(data.isEmpty()) {
+ removeChildChunk(i, childChunkNum);
+ return;
}
- else {
- // Non null data: update chunk
- // First we update the global size
- d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
- insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
- // And the PROP chunk size
- d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
- - (childChunks[i].size + childChunks[i].padding);
- insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
- d->endianness == BigEndian),
- d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
- // Now update the specific chunk
- writeChunk(childChunks[i].name,
- data,
- childChunks[i].offset - 12,
- childChunks[i].size + childChunks[i].padding + 12);
+ // Non null data: update chunk
+ // First we update the global size
+ d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
+ insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
+ // And the PROP chunk size
+ d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
+ - (childChunks[i].size + childChunks[i].padding);
+ insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
+ d->endianness == BigEndian),
+ d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
- childChunks[i].size = data.size();
- childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
+ // Now update the specific chunk
+ writeChunk(childChunks[i].name,
+ data,
+ childChunks[i].offset - 12,
+ childChunks[i].size + childChunks[i].padding + 12);
- // Now update the internal offsets
- // For child Chunks
- for(i++; i < childChunks.size(); i++)
- childChunks[i].offset = childChunks[i - 1].offset + 12
- + childChunks[i - 1].size + childChunks[i - 1].padding;
+ childChunks[i].size = data.size();
+ childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
- // And for root chunks
- updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
- }
+ // Now update the internal offsets
+ // For child Chunks
+ for(i++; i < childChunks.size(); i++)
+ childChunks[i].offset = childChunks[i - 1].offset + 12
+ + childChunks[i - 1].size + childChunks[i - 1].padding;
+
+ // And for root chunks
+ updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
void DSDIFF::File::setChildChunkData(const ByteVector &name,
const ByteVector &data,
unsigned int childChunkNum)
{
- std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
+ ChunkList &childChunks = d->childChunks[childChunkNum];
if(childChunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
@@ -485,7 +546,7 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
// Update childchunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
- std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
+ ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
if(childChunksToUpdate.size() > 0) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
@@ -494,7 +555,7 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
}
}
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
- std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
+ ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
if(childChunksToUpdate.size() > 0) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
diff --git a/taglib/dsdiff/dsdifffile.h b/taglib/dsdiff/dsdifffile.h
index 9248a3e3..e82b5908 100644
--- a/taglib/dsdiff/dsdifffile.h
+++ b/taglib/dsdiff/dsdifffile.h
@@ -61,6 +61,22 @@ namespace TagLib {
class TAGLIB_EXPORT File : public TagLib::File
{
public:
+
+ /*!
+ * This set of flags is used for various operations and is suitable for
+ * being OR-ed together.
+ */
+ enum TagTypes {
+ //! Empty set. Matches no tag types.
+ NoTags = 0x0000,
+ //! Matches DIIN tags.
+ DIIN = 0x0002,
+ //! Matches ID3v1 tags.
+ ID3v2 = 0x0002,
+ //! Matches all tag types.
+ AllTags = 0xffff
+ };
+
/*!
* Constructs an DSDIFF file from \a file. If \a readProperties is true
* the file's audio properties will also be read.
@@ -114,13 +130,13 @@ namespace TagLib {
*
* \see hasID3v2Tag()
*/
- virtual ID3v2::Tag *ID3v2Tag() const;
+ ID3v2::Tag *ID3v2Tag(bool create = false) const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
- DSDIFF::DIIN::Tag *DIINTag() const;
+ DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
/*!
* Implements the unified property interface -- export function.
@@ -160,15 +176,21 @@ namespace TagLib {
virtual bool save();
/*!
- * Save the file. This will attempt to save all of the tag types that are
- * specified by OR-ing together TagTypes values. The save() method above
- * uses AllTags. This returns true if saving was successful.
+ * Save the file. If \a strip is specified, it is possible to choose if
+ * tags not specified in \a tags should be stripped from the file or
+ * retained. With \a version, it is possible to specify whether ID3v2.4
+ * or ID3v2.3 should be used.
+ */
+ bool save(TagTypes tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
+
+ /*!
+ * This will strip the tags that match the OR-ed together TagTypes from the
+ * file. By default it strips all tags. It returns true if the tags are
+ * successfully stripped.
*
- * This strips all tags not included in the mask, but does not modify them
- * in memory, so later calls to save() which make use of these tags will
- * remain valid. This also strips empty tags.
+ * \note This will update the file immediately.
*/
- bool save(int tags);
+ void strip(TagTypes tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
@@ -179,7 +201,7 @@ namespace TagLib {
/*!
* Returns whether or not the file on disk actually has the DSDIFF
- * Title & Artist tag.
+ * title and artist tags.
*
* \see DIINTag()
*/
@@ -204,6 +226,10 @@ namespace TagLib {
File(const File &);
File &operator=(const File &);
+ void removeRootChunk(const ByteVector &id);
+ void removeRootChunk(unsigned int chunk);
+ void removeChildChunk(unsigned int i, unsigned int chunk);
+
/*!
* Sets the data for the the specified chunk at root level to \a data.
*
diff --git a/tests/test_dsdiff.cpp b/tests/test_dsdiff.cpp
index 0d16af72..20a1207d 100644
--- a/tests/test_dsdiff.cpp
+++ b/tests/test_dsdiff.cpp
@@ -15,6 +15,8 @@ class TestDSDIFF : public CppUnit::TestFixture
CPPUNIT_TEST(testProperties);
CPPUNIT_TEST(testTags);
CPPUNIT_TEST(testSaveID3v2);
+ CPPUNIT_TEST(testSaveID3v23);
+ CPPUNIT_TEST(testStrip);
CPPUNIT_TEST(testRepeatedSave);
CPPUNIT_TEST_SUITE_END();
@@ -49,16 +51,16 @@ public:
CPPUNIT_ASSERT_EQUAL(String("The Artist"), f->tag()->artist());
delete f;
}
-
+
void testSaveID3v2()
{
ScopedFileCopy copy("empty10ms", ".dff");
string newname = copy.fileName();
-
+
{
DSDIFF::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
-
+
f.tag()->setTitle(L"TitleXXX");
f.save();
CPPUNIT_ASSERT(f.hasID3v2Tag());
@@ -67,7 +69,7 @@ public:
DSDIFF::File f(newname.c_str());
CPPUNIT_ASSERT(f.hasID3v2Tag());
CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title());
-
+
f.tag()->setTitle("");
f.save();
CPPUNIT_ASSERT(!f.hasID3v2Tag());
@@ -85,12 +87,100 @@ public:
CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title());
}
}
-
+
+ void testSaveID3v23()
+ {
+ ScopedFileCopy copy("empty10ms", ".dff");
+ string newname = copy.fileName();
+
+ String xxx = ByteVector(254, 'X');
+ {
+ DSDIFF::File f(newname.c_str());
+ CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag());
+
+ f.tag()->setTitle(xxx);
+ f.tag()->setArtist("Artist A");
+ f.save(DSDIFF::File::AllTags, File::StripOthers, ID3v2::v3);
+ CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
+ }
+ {
+ DSDIFF::File f2(newname.c_str());
+ CPPUNIT_ASSERT_EQUAL((unsigned int)3, f2.ID3v2Tag()->header()->majorVersion());
+ CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
+ CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
+ }
+ }
+
+ void testStrip()
+ {
+ {
+ ScopedFileCopy copy("empty10ms", ".dff");
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ f.tag()->setArtist("X");
+ f.save();
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(f.hasID3v2Tag());
+ CPPUNIT_ASSERT(f.hasDIINTag());
+ f.strip();
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(!f.hasID3v2Tag());
+ CPPUNIT_ASSERT(!f.hasDIINTag());
+ }
+ }
+
+ {
+ ScopedFileCopy copy("empty10ms", ".dff");
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ f.ID3v2Tag(true);
+ f.DIINTag(true);
+ f.tag()->setArtist("X");
+ f.save();
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(f.hasID3v2Tag());
+ CPPUNIT_ASSERT(f.hasDIINTag());
+ f.strip(DSDIFF::File::ID3v2);
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(!f.hasID3v2Tag());
+ CPPUNIT_ASSERT(f.hasDIINTag());
+ }
+ }
+
+ {
+ ScopedFileCopy copy("empty10ms", ".dff");
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ f.tag()->setArtist("X");
+ f.save();
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(f.hasID3v2Tag());
+ CPPUNIT_ASSERT(f.hasDIINTag());
+ f.strip(DSDIFF::File::DIIN);
+ }
+ {
+ DSDIFF::File f(copy.fileName().c_str());
+ CPPUNIT_ASSERT(f.hasID3v2Tag());
+ CPPUNIT_ASSERT(!f.hasDIINTag());
+ }
+ }
+ }
+
void testRepeatedSave()
{
ScopedFileCopy copy("empty10ms", ".dff");
string newname = copy.fileName();
-
+
{
DSDIFF::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(String(""), f.tag()->title());
@@ -109,8 +199,6 @@ public:
CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), f.tag()->title());
}
}
-
-
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestDSDIFF);