diff --git a/NEWS b/NEWS index e4d1c9a5..1b3751bd 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ * Better handling of duplicate ID3v2 tags in all kinds of files. * Better handling of duplicate tags in WAV files. * Fixed crash when calling File::properties() after strip(). + * Fixed possible file corruptions when saving ASF files. * Marked ByteVector::null and ByteVector::isNull() deprecated. * Marked String::null and ByteVector::isNull() deprecated. * Many smaller bug fixes and performance improvements. diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index 02fe83c3..a5e5ea84 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -50,7 +50,7 @@ public: class MetadataLibraryObject; FilePrivate(): - size(0), + headerSize(0), tag(0), properties(0), contentDescriptionObject(0), @@ -68,7 +68,7 @@ public: delete properties; } - unsigned long long size; + unsigned long long headerSize; ASF::Tag *tag; ASF::Properties *properties; @@ -556,6 +556,10 @@ bool ASF::File::save() d->headerExtensionObject->objects.append(d->metadataLibraryObject); } + d->extendedContentDescriptionObject->attributeData.clear(); + d->metadataObject->attributeData.clear(); + d->metadataLibraryObject->attributeData.clear(); + const AttributeListMap allAttributes = d->tag->attributeListMap(); for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) { @@ -591,8 +595,14 @@ bool ASF::File::save() data.append((*it)->render(this)); } - data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; - insert(data, 0, (TagLib::ulong)d->size); + seek(16); + writeBlock(ByteVector::fromLongLong(data.size() + 30, false)); + writeBlock(ByteVector::fromUInt(d->objects.size(), false)); + writeBlock(ByteVector("\x01\x02", 2)); + + insert(data, 30, static_cast(d->headerSize - 30)); + + d->headerSize = data.size() + 30; return true; } @@ -617,7 +627,7 @@ void ASF::File::read() d->properties = new ASF::Properties(); bool ok; - d->size = readQWORD(this, &ok); + d->headerSize = readQWORD(this, &ok); if(!ok) { setValid(false); return; diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h index f1ae431f..b674da79 100644 --- a/taglib/asf/asffile.h +++ b/taglib/asf/asffile.h @@ -112,9 +112,6 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. - * - * \warning In the current implementation, it's dangerous to call save() - * repeatedly. At worst it will corrupt the file. */ virtual bool save(); diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp index 663ae583..37614b0c 100644 --- a/tests/test_asf.cpp +++ b/tests/test_asf.cpp @@ -25,6 +25,7 @@ class TestASF : public CppUnit::TestFixture CPPUNIT_TEST(testSavePicture); CPPUNIT_TEST(testSaveMultiplePictures); CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testRepeatedSave); CPPUNIT_TEST_SUITE_END(); public: @@ -270,6 +271,21 @@ public: CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); } + void testRepeatedSave() + { + ScopedFileCopy copy("silence-1", ".wma"); + + { + ASF::File f(copy.fileName().c_str()); + f.tag()->setTitle(std::string(128 * 1024, 'X').c_str()); + f.save(); + CPPUNIT_ASSERT_EQUAL(297578L, f.length()); + f.tag()->setTitle(std::string(16 * 1024, 'X').c_str()); + f.save(); + CPPUNIT_ASSERT_EQUAL(68202L, f.length()); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestASF);