From 8636eb749a1d61b268e5e223b1915be5926de0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Tue, 28 Jul 2009 18:29:39 +0000 Subject: [PATCH] Handle RIFF chunk padding and ignore trailing garbage This is based on patches by Marc Halbruegge, but those only deal with read-only cases. The code now also correctly adds padding to RIFF chunks, and calculates offsets in chunkData taking the padding into account. BUG:171957 BUG:175781 git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1003745 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- taglib/riff/rifffile.cpp | 38 ++++++++++++++++-- tests/CMakeLists.txt | 4 ++ tests/data/empty.aiff | Bin 0 -> 5936 bytes tests/test_aiff.cpp | 32 ++++++++++++++++ tests/test_riff.cpp | 81 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 tests/data/empty.aiff create mode 100644 tests/test_aiff.cpp create mode 100644 tests/test_riff.cpp diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index 2aca44aa..25ada552 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -49,6 +49,7 @@ public: std::vector chunkNames; std::vector chunkOffsets; std::vector chunkSizes; + std::vector chunkPadding; }; //////////////////////////////////////////////////////////////////////////////// @@ -101,7 +102,7 @@ ByteVector RIFF::File::chunkData(uint i) long begin = 12 + 8; for(uint it = 0; it < i; it++) - begin += 8 + d->chunkSizes[it]; + begin += 8 + d->chunkSizes[it] + d->chunkPadding[it]; seek(begin); @@ -128,12 +129,15 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) // Now update the specific chunk - writeChunk(name, data, d->chunkOffsets[i] - 8, d->chunkSizes[i] + 8); + writeChunk(name, data, d->chunkOffsets[i] - 8, d->chunkSizes[i] + d->chunkPadding[i] + 8); + + d->chunkSizes[i] = data.size(); + d->chunkPadding[i] = (data.size() & 0x01) ? 1 : 0; // Now update the internal offsets for(i++; i < d->chunkNames.size(); i++) - d->chunkOffsets[i] += sizeDifference; + d->chunkOffsets[i] = d->chunkOffsets[i-1] + 8 + d->chunkSizes[i-1] + d->chunkPadding[i-1]; return; } @@ -158,16 +162,38 @@ void RIFF::File::read() d->size = readBlock(4).toUInt(bigEndian); d->format = readBlock(4); - while(tell() < length()) { + // + 8: chunk header at least, fix for additional junk bytes + while(tell() + 8 <= length()) { ByteVector chunkName = readBlock(4); uint chunkSize = readBlock(4).toUInt(bigEndian); + if(tell() + chunkSize > length()) { + // something wrong + break; + } + d->chunkNames.push_back(chunkName); d->chunkSizes.push_back(chunkSize); d->chunkOffsets.push_back(tell()); seek(chunkSize, Current); + + // check padding + char paddingSize = 0; + long uPosNotPadded = tell(); + if((uPosNotPadded & 0x01) != 0) { + ByteVector iByte = readBlock(1); + if((iByte.size() != 1) || (iByte[0] != 0)) { + // not well formed, re-seek + seek(uPosNotPadded, Beginning); + } + else { + paddingSize = 1; + } + } + d->chunkPadding.push_back(paddingSize); + } } @@ -177,5 +203,9 @@ void RIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, ByteVector combined = 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); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6afefe6d..41b1129f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4 + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/riff + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/riff/aiff ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/trueaudio ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg ) @@ -27,6 +29,8 @@ SET(test_runner_SRCS test_id3v1.cpp test_id3v2.cpp test_xiphcomment.cpp + test_aiff.cpp + test_riff.cpp ) IF(WITH_MP4) SET(test_runner_SRCS ${test_runner_SRCS} test_mp4.cpp) diff --git a/tests/data/empty.aiff b/tests/data/empty.aiff new file mode 100644 index 0000000000000000000000000000000000000000..849b762da5a8d2b1a366ea8f9d3daa87be7a194c GIT binary patch literal 5936 zcmZvgVQwTv423(HNysh|Li~U$a10O-i66^KWUs@GI3K&&Bs;v%pWW3FjFg$~>ay$k z`Po&S`}cpo+qNHm{Q39$`(NI_%k}o%cC~F^|FwPl(>H(rn)`en9{$MH_mAGy_x0sz zyScn<*V}fvT)yP`^Y+!{al5VYr|q1vZ98T3_VOH$*UY_K+QU_zo-%&QFHdgD@;TnF za>wXZ=GI^QGY3oV^T~Ja>pSx@`n27}{wBYtdWsM3z;u&U&i#iM{krI_O z-E&1cShdOuJrgt&b*~+-=is^xzH>-?$rV|9X-x3RO0RK(Zsl)(nwh7AuZ3<WvJdIXi!x_L_8YF1(~TQkBaalwL`7+%Jk_{SIQmHOgoqJ+?7(IA7am# zGe2!_E+3<6_1kJ~JlH&y`jd~cSotZ&Gf#~=&J|j?2hsTmF|v}L`_)L%&l%w zF+R5fTW6)YsURwMDoLeU&BO{u6*_7&IuWXy&V0=y#$&Zrjw{P!0h3seAFj;9q3Ysi z#oYR+EMZ|bE905fRZ=WW-j_$cPlifo<%bzWSk+pqapfK8d0s1U(;ro0_Qic<9qlZJ zu`h2GcR6SlSH#vGn5_%f^cueXtlr?f;OcsqS{$xHj8ARlTJNo+sxeR%(;};N=qRPW z!ZKMI1mMscD^AB%SEDudgHQCd_@md!ZS#{cVypUL)xO8WOKSaNY93oLs14XdhvP}> z@lx^Nsd2d*KE@YXTeen#ow~Yt=67ClMYgPu)tAhmTuj_mv1O?uuq@TlZ$X~h+v8FA0_OjEip56-DN zuD+z!=Co73%bPpwbFbHHR;GgJu%7rB0p;#JYlWM9FD=8#WA9F=3`=Hk-gvQ`K8MDqx2SL&(V{=p%9`L8`@~HIUm^C1KWce^NcdL%@ zY_?q$%*<6yM&oYsln6P$z)^EmtW`~vPphJ;eCKm|py)s~Cp;sT!@*glA7Q*6AlY!iay zPHdXtmMw=DSTP!p)%LVMK$`>)AUR+4Ytl^@x-ps zn!(SWMeJ9sM|S00O^h1AHCeN!CxYUSfHMQ1dtJ2_N2OEk6W1tC#%i7T@V#v^Ej#S9 zo}j2w?fFigVsTXJsA=U?wfYV`HI8lVeeKN*$dV|HR$F#6xfrEeHxuWqZ~Rmq=Mj}U zc&0M-i^bl1JGVT^UvX&lR+Tbpq-~)k^SDa<6^qp~$`dEi`p&Kb$k*`b)v7^tVI&}t zT@{wQMukp2HPP$ScQUU~{3i!xS-BZS5Zy4fuHqmc?^W`u1}w$wsSJhMBZ#8oZ>)Rd z?Q6VD#O}=gpT=ra3kwkzoOIL4@VOCim9PHz0wQ8U>$X#OC5t;kZmb-lE zC^MMlA6Ca6HRsRPN6#C*$TwAM9g|<)WbWdF5$d0q#8JOlM+|#cZd;hguaS-BMg#fX z^T+&Owp8hjgvtyGJ z;)OYww1SCDotP2)$T>jguA^lD-ij;sWMbtljgwqwxoT5JLHWAtp^0^l13eM?*0IhI zSj)ILr^17#5KTRms)(sa&v{RM6aSufJP{CUD<`uGQ86^i@Z7^{^ay#CbFK6~im4vc z46CCWX~y>);Hj8CvOM~kb=(oxyTn9=4O&rkR2wW)ZCz1$26I$bRDxAv?6 literal 0 HcmV?d00001 diff --git a/tests/test_aiff.cpp b/tests/test_aiff.cpp new file mode 100644 index 00000000..be94b07f --- /dev/null +++ b/tests/test_aiff.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestAIFF : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestAIFF); + CPPUNIT_TEST(testReading); + CPPUNIT_TEST_SUITE_END(); + +public: + + void testReading() + { + string filename = copyFile("empty", ".aiff"); + + RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(689, f->audioProperties()->bitrate()); + + deleteFile(filename); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF); diff --git a/tests/test_riff.cpp b/tests/test_riff.cpp new file mode 100644 index 00000000..34ab84ec --- /dev/null +++ b/tests/test_riff.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class PublicRIFF : public RIFF::File +{ +public: + PublicRIFF(FileName file) : RIFF::File(file, BigEndian) {}; + TagLib::uint chunkCount() { return RIFF::File::chunkCount(); }; + TagLib::uint chunkOffset(TagLib::uint i) { return RIFF::File::chunkOffset(i); }; + ByteVector chunkName(TagLib::uint i) { return RIFF::File::chunkName(i); }; + ByteVector chunkData(TagLib::uint i) { return RIFF::File::chunkData(i); }; + void setChunkData(const ByteVector &name, const ByteVector &data) { + RIFF::File::setChunkData(name, data); + }; + virtual TagLib::Tag* tag() const { return 0; }; + virtual TagLib::AudioProperties* audioProperties() const { return 0;}; + virtual bool save() { return false; }; +}; + +class TestRIFF : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestRIFF); + CPPUNIT_TEST(testPadding); + CPPUNIT_TEST_SUITE_END(); + +public: + + void testPadding() + { + string filename = copyFile("empty", ".aiff"); + + PublicRIFF *f = new PublicRIFF(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(2)); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(0x1728 + 8), f->chunkOffset(2)); + + f->setChunkData("TEST", "foo"); + delete f; + + f = new PublicRIFF(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(2)); + CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f->chunkData(2)); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(0x1728 + 8), f->chunkOffset(2)); + + f->setChunkData("SSND", "abcd"); + + CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(1)); + CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f->chunkData(1)); + + f->seek(f->chunkOffset(1)); + CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f->readBlock(4)); + + CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(2)); + CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f->chunkData(2)); + + f->seek(f->chunkOffset(2)); + CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f->readBlock(3)); + + delete f; + + f = new PublicRIFF(filename.c_str()); + + CPPUNIT_ASSERT_EQUAL(ByteVector("SSND"), f->chunkName(1)); + CPPUNIT_ASSERT_EQUAL(ByteVector("abcd"), f->chunkData(1)); + + CPPUNIT_ASSERT_EQUAL(ByteVector("TEST"), f->chunkName(2)); + CPPUNIT_ASSERT_EQUAL(ByteVector("foo"), f->chunkData(2)); + + deleteFile(filename); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestRIFF);