mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Fix writing of new RIFF chunks at even positions
If the last chunk had an odd size, the new chunk would have been written at odd position, which is incorrect. This is based on the patch by Jens Dyffort, but I ended up changing the implementation to correctly handle subsequential updates to the file. The whole RIFF code really needs to be rewritten in a different way... BUG:243954 git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1220223 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
parent
ad0494bb7e
commit
c21fd955ff
@ -74,6 +74,11 @@ RIFF::File::File(FileName file, Endianness endianness) : TagLib::File(file)
|
||||
read();
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::File::riffSize() const
|
||||
{
|
||||
return d->size;
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::File::chunkCount() const
|
||||
{
|
||||
return d->chunkNames.size();
|
||||
@ -89,6 +94,11 @@ TagLib::uint RIFF::File::chunkOffset(uint i) const
|
||||
return d->chunkOffsets[i];
|
||||
}
|
||||
|
||||
TagLib::uint RIFF::File::chunkPadding(uint i) const
|
||||
{
|
||||
return d->chunkPadding[i];
|
||||
}
|
||||
|
||||
ByteVector RIFF::File::chunkName(uint i) const
|
||||
{
|
||||
if(i >= chunkCount())
|
||||
@ -116,8 +126,7 @@ ByteVector RIFF::File::chunkData(uint i)
|
||||
|
||||
void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
if(d->chunkNames.size() == 0)
|
||||
{
|
||||
if(d->chunkNames.size() == 0) {
|
||||
debug("RIFF::File::setChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
@ -125,12 +134,10 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
for(uint i = 0; i < d->chunkNames.size(); i++) {
|
||||
if(d->chunkNames[i] == name) {
|
||||
|
||||
int sizeDifference = data.size() - d->chunkSizes[i];
|
||||
|
||||
// First we update the global size
|
||||
|
||||
insert(ByteVector::fromUInt(d->size + sizeDifference,
|
||||
d->endianness == BigEndian), 4, 4);
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunkSizes[i] + d->chunkPadding[i]);
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
@ -148,11 +155,30 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one. First update
|
||||
// the global size:
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
|
||||
insert(ByteVector::fromUInt(d->size + data.size() + 8, d->endianness == BigEndian), 4, 4);
|
||||
writeChunk(name, data, d->chunkOffsets.back() + d->chunkSizes.back());
|
||||
uint i = d->chunkNames.size() - 1;
|
||||
ulong offset = d->chunkOffsets[i] + d->chunkSizes[i];
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += (offset & 1) + data.size() + 8;
|
||||
insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4);
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0);
|
||||
|
||||
// And update our internal structure
|
||||
|
||||
if (offset & 1) {
|
||||
d->chunkPadding[i] = 1;
|
||||
offset++;
|
||||
}
|
||||
d->chunkNames.push_back(name);
|
||||
d->chunkSizes.push_back(data.size());
|
||||
d->chunkOffsets.push_back(offset + 8);
|
||||
d->chunkPadding.push_back((data.size() & 0x01) ? 1 : 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -203,13 +229,16 @@ void RIFF::File::read()
|
||||
}
|
||||
|
||||
void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
ulong offset, ulong replace)
|
||||
ulong offset, ulong replace, uint leadingPadding)
|
||||
{
|
||||
ByteVector combined = name;
|
||||
ByteVector combined;
|
||||
if(leadingPadding) {
|
||||
combined.append(ByteVector(leadingPadding, '\x00'));
|
||||
}
|
||||
combined.append(name);
|
||||
combined.append(ByteVector::fromUInt(data.size(), d->endianness == BigEndian));
|
||||
combined.append(data);
|
||||
if((data.size() & 0x01) != 0) {
|
||||
// padding
|
||||
combined.append('\x00');
|
||||
}
|
||||
insert(combined, offset, replace);
|
||||
|
@ -57,6 +57,11 @@ namespace TagLib {
|
||||
|
||||
File(FileName file, Endianness endianness);
|
||||
|
||||
/*!
|
||||
* \return The size of the main RIFF chunk.
|
||||
*/
|
||||
uint riffSize() const;
|
||||
|
||||
/*!
|
||||
* \return The number of chunks in the file.
|
||||
*/
|
||||
@ -72,6 +77,11 @@ namespace TagLib {
|
||||
*/
|
||||
uint chunkDataSize(uint i) const;
|
||||
|
||||
/*!
|
||||
* \return The size of the padding after the chunk (can be either 0 or 1).
|
||||
*/
|
||||
uint chunkPadding(uint i) const;
|
||||
|
||||
/*!
|
||||
* \return The name of the specified chunk, for instance, "COMM" or "ID3 "
|
||||
*/
|
||||
@ -99,7 +109,8 @@ namespace TagLib {
|
||||
|
||||
void read();
|
||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
ulong offset, ulong replace = 0);
|
||||
ulong offset, ulong replace = 0,
|
||||
uint leadingPadding = 0);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
|
BIN
tests/data/noise.aif
Normal file
BIN
tests/data/noise.aif
Normal file
Binary file not shown.
BIN
tests/data/noise_odd.aif
Normal file
BIN
tests/data/noise_odd.aif
Normal file
Binary file not shown.
@ -13,8 +13,10 @@ class PublicRIFF : public RIFF::File
|
||||
{
|
||||
public:
|
||||
PublicRIFF(FileName file) : RIFF::File(file, BigEndian) {};
|
||||
TagLib::uint riffSize() { return RIFF::File::riffSize(); };
|
||||
TagLib::uint chunkCount() { return RIFF::File::chunkCount(); };
|
||||
TagLib::uint chunkOffset(TagLib::uint i) { return RIFF::File::chunkOffset(i); };
|
||||
TagLib::uint chunkPadding(TagLib::uint i) { return RIFF::File::chunkPadding(i); };
|
||||
TagLib::uint chunkDataSize(TagLib::uint i) { return RIFF::File::chunkDataSize(i); };
|
||||
ByteVector chunkName(TagLib::uint i) { return RIFF::File::chunkName(i); };
|
||||
ByteVector chunkData(TagLib::uint i) { return RIFF::File::chunkData(i); };
|
||||
@ -30,6 +32,9 @@ class TestRIFF : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestRIFF);
|
||||
CPPUNIT_TEST(testPadding);
|
||||
CPPUNIT_TEST(testLastChunkAtEvenPosition);
|
||||
CPPUNIT_TEST(testLastChunkAtEvenPosition2);
|
||||
CPPUNIT_TEST(testLastChunkAtEvenPosition3);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -77,6 +82,117 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f->chunkData(2));
|
||||
}
|
||||
|
||||
void testLastChunkAtEvenPosition()
|
||||
{
|
||||
ScopedFileCopy copy("noise", ".aif");
|
||||
string filename = copy.fileName();
|
||||
|
||||
PublicRIFF *f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0xff0 + 8), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4400), f->length());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4399 - 8), f->riffSize());
|
||||
f->setChunkData("TEST", "abcd");
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4412 - 8), f->riffSize());
|
||||
delete f;
|
||||
|
||||
f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4412), f->length());
|
||||
delete f;
|
||||
}
|
||||
|
||||
void testLastChunkAtEvenPosition2()
|
||||
{
|
||||
ScopedFileCopy copy("noise_odd", ".aif");
|
||||
string filename = copy.fileName();
|
||||
|
||||
PublicRIFF *f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0xff0 + 8), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4399), f->length());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4399 - 8), f->riffSize());
|
||||
f->setChunkData("TEST", "abcd");
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4412 - 8), f->riffSize());
|
||||
delete f;
|
||||
|
||||
f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4412), f->length());
|
||||
delete f;
|
||||
}
|
||||
|
||||
void testLastChunkAtEvenPosition3()
|
||||
{
|
||||
ScopedFileCopy copy("noise_odd", ".aif");
|
||||
string filename = copy.fileName();
|
||||
|
||||
PublicRIFF *f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0xff0 + 8), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4399), f->length());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4399 - 8), f->riffSize());
|
||||
f->setChunkData("TEST", "abc");
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4411 - 8), f->riffSize());
|
||||
delete f;
|
||||
|
||||
f = new PublicRIFF(filename.c_str());
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4088), f->chunkOffset(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(311), f->chunkDataSize(2));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(2));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4408), f->chunkOffset(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f->chunkDataSize(3));
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(3));
|
||||
CPPUNIT_ASSERT_EQUAL(TagLib::uint(1), f->chunkPadding(3));
|
||||
CPPUNIT_ASSERT_EQUAL(long(4412), f->length());
|
||||
delete f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestRIFF);
|
||||
|
Loading…
Reference in New Issue
Block a user