Merge pull request #507 from TsudaKageyu/merge-master-to-taglib2

Merge master to taglib2
This commit is contained in:
Lukáš Lalinský 2015-05-15 19:46:52 -07:00
commit ab2389819e
51 changed files with 615 additions and 290 deletions

21
.editorconfig Normal file
View File

@ -0,0 +1,21 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Non-specified newlines with a newline ending every file
[*]
insert_final_newline = true
# 2 space indentation
[*.{h,cpp,tcc,cmake}]
indent_style = space
indent_size = 2
# Trim traling whitespaces
[*.{h,cpp,tcc,cmake}]
trim_trailing_whitespace = true
# UTF-8 without BOM
[*]
charset = utf-8

View File

@ -277,16 +277,6 @@ check_cxx_source_compiles("
}
" HAVE_ISO_STRDUP)
# Determine whether your compiler supports codecvt.
check_cxx_source_compiles("
#include <codecvt>
int main() {
std::codecvt_utf8_utf16<wchar_t> x;
return 0;
}
" HAVE_STD_CODECVT)
# Determine whether zlib is installed.
if(NOT ZLIB_SOURCE)

View File

@ -13,6 +13,15 @@
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP_16 1
#cmakedefine HAVE_GCC_BYTESWAP_32 1
#cmakedefine HAVE_GCC_BYTESWAP_64 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
#cmakedefine HAVE_MSC_BYTESWAP 1
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_STD_ATOMIC 1
#cmakedefine HAVE_BOOST_ATOMIC 1
@ -25,15 +34,6 @@
#cmakedefine HAVE_STD_SMART_PTR 1
#cmakedefine HAVE_BOOST_SMART_PTR 1
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP_16 1
#cmakedefine HAVE_GCC_BYTESWAP_32 1
#cmakedefine HAVE_GCC_BYTESWAP_64 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
#cmakedefine HAVE_MSC_BYTESWAP 1
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports snprintf or sprintf_s. */
#cmakedefine HAVE_SNPRINTF 1
#cmakedefine HAVE_SPRINTF_S 1
@ -41,9 +41,6 @@
/* Defined if your compiler supports ISO _strdup. */
#cmakedefine HAVE_ISO_STRDUP 1
/* Defined if your compiler supports codecvt */
#cmakedefine HAVE_STD_CODECVT 1
/* Defined if you have libz */
#cmakedefine HAVE_ZLIB 1

View File

@ -330,9 +330,14 @@ set(toolkit_SRCS
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
toolkit/unicode.cpp
)
if(NOT WIN32)
set(unicode_SRCS
toolkit/unicode.cpp
)
endif()
if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c
@ -340,7 +345,6 @@ if(HAVE_ZLIB_SOURCE)
${ZLIB_SOURCE}/inffast.c
${ZLIB_SOURCE}/inflate.c
${ZLIB_SOURCE}/inftrees.c
${ZLIB_SOURCE}/uncompr.c
${ZLIB_SOURCE}/zutil.c
)
endif()
@ -351,13 +355,14 @@ set(tag_LIB_SRCS
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS}
${unicode_SRCS} ${zlib_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
audioproperties.cpp
)
add_library(tag ${tag_LIB_SRCS} ${zlib_SRCS} ${tag_HDRS})
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
if(ZLIB_FOUND)
target_link_libraries(tag ${ZLIB_LIBRARIES})

View File

@ -61,7 +61,7 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
APE::AudioProperties::AudioProperties(File *file, ReadStyle style) :
APE::AudioProperties::AudioProperties(File *file, ReadStyle style) :
d(new PropertiesPrivate())
{
read(file);
@ -219,11 +219,19 @@ void APE::AudioProperties::analyzeOld(File *file)
else
blocksPerFrame = 9216;
d->channels = header.toUInt16LE(4);
d->channels = header.toUInt16LE(4);
d->sampleRate = header.toUInt32LE(6);
const uint finalFrameBlocks = header.toUInt32LE(22);
const uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? static_cast<int>(file->length() * 8L / d->length / 1000) : 0;
uint totalBlocks = 0;
if(totalFrames > 0)
totalBlocks = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
if(d->sampleRate > 0)
d->length = totalBlocks / d->sampleRate;
if(d->length > 0)
d->bitrate = ((file->length() * 8L) / d->length) / 1000;
}

View File

@ -371,10 +371,13 @@ ByteVector APE::Tag::render() const
void APE::Tag::parse(const ByteVector &data)
{
uint pos = 0;
// 11 bytes is the minimum size for an APE item
if(data.size() < 11)
return;
uint pos = 0;
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
APE::Item item;
item.parse(data.mid(pos));

View File

@ -63,12 +63,12 @@ namespace
ResolverList fileTypeResolvers;
SHARED_PTR<File> create(
FileName fileName,
FileName fileName,
bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
SHARED_PTR<File> file;
for(ResolverConstIterator it = fileTypeResolvers.begin(); it != fileTypeResolvers.end(); ++it)
for(ResolverConstIterator it = fileTypeResolvers.begin(); it != fileTypeResolvers.end(); ++it)
{
file.reset((*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle));
if(file)
@ -146,23 +146,17 @@ namespace
}
}
class FileRef::FileRefPrivate
class FileRef::FileRefPrivate
{
public:
FileRefPrivate()
: file()
{
}
FileRefPrivate() :
file() {}
FileRefPrivate(File *f)
: file(f)
{
}
FileRefPrivate(const SHARED_PTR<File> &f)
: file(f)
{
}
FileRefPrivate(File *f) :
file(f) {}
FileRefPrivate(const SHARED_PTR<File> &f) :
file(f) {}
SHARED_PTR<File> file;
};
@ -171,25 +165,23 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef()
: d(new FileRefPrivate())
FileRef::FileRef() :
d(new FileRefPrivate())
{
}
FileRef::FileRef(FileName fileName,
bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
: d(new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle)))
FileRef::FileRef(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle style) :
d(new FileRefPrivate(create(fileName, readAudioProperties, style)))
{
}
FileRef::FileRef(File *file)
: d(new FileRefPrivate(file))
FileRef::FileRef(File *file) :
d(new FileRefPrivate(file))
{
}
FileRef::FileRef(const FileRef &ref)
: d(new FileRefPrivate(ref.d->file))
FileRef::FileRef(const FileRef &ref) :
d(new FileRefPrivate(ref.d->file))
{
}

View File

@ -23,6 +23,10 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
@ -50,20 +54,10 @@ MP4::Atom::Atom(File *file)
length = header.toUInt32BE(0);
if (length == 1) {
const long long longLength = file->readBlock(8).toInt64BE(0);
if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
// The atom has a 64-bit length, but it's actually a 32-bit value
length = (long)longLength;
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
}
if (length < 8) {
if(length == 1)
length = file->readBlock(8).toInt64BE(0);
if(length < 8) {
debug("MP4: Invalid atom size");
length = 0;
file->seek(0, File::End);

View File

@ -83,7 +83,7 @@ namespace TagLib {
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
AtomList findall(const char *name, bool recursive = false);
offset_t offset;
long length;
offset_t length;
TagLib::ByteVector name;
AtomList children;
private:

View File

@ -147,35 +147,43 @@ int MPC::AudioProperties::albumPeak() const
namespace
{
unsigned long readSize(File *file, size_t &sizelength)
unsigned long readSize(File *file, size_t &sizeLength, bool &eof)
{
sizeLength = 0;
eof = false;
unsigned char tmp;
unsigned long size = 0;
do {
ByteVector b = file->readBlock(1);
const ByteVector b = file->readBlock(1);
if(b.isEmpty()) {
eof = true;
break;
}
tmp = b[0];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
sizeLength++;
} while((tmp & 0x80));
return size;
}
unsigned long readSize(const ByteVector &data, size_t &sizelength)
unsigned long readSize(const ByteVector &data, size_t &pos)
{
unsigned char tmp;
unsigned long size = 0;
unsigned long pos = 0;
do {
tmp = data[pos++];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80) && (pos < data.size()));
return size;
}
static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
// This array looks weird, but the same as original MusePack code found at:
// https://www.musepack.net/index.php?pg=src
static const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
}
void MPC::AudioProperties::readSV8(File *file, offset_t streamLength)
@ -183,40 +191,73 @@ void MPC::AudioProperties::readSV8(File *file, offset_t streamLength)
bool readSH = false, readRG = false;
while(!readSH && !readRG) {
ByteVector packetType = file->readBlock(2);
size_t packetSizeLength = 0;
size_t packetSize = readSize(file, packetSizeLength);
size_t dataSize = packetSize - 2 - packetSizeLength;
const ByteVector packetType = file->readBlock(2);
size_t packetSizeLength;
bool eof;
const size_t packetSize = readSize(file, packetSizeLength, eof);
if(eof) {
debug("MPC::Properties::readSV8() - Reached to EOF.");
break;
}
const size_t dataSize = packetSize - 2 - packetSizeLength;
const ByteVector data = file->readBlock(dataSize);
if(data.size() != dataSize) {
debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size.");
break;
}
if(packetType == "SH") {
// Stream Header
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
ByteVector data = file->readBlock(dataSize);
if(dataSize <= 5) {
debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse.");
break;
}
readSH = true;
size_t pos = 4;
d->version = data[pos];
pos += 1;
d->sampleFrames = readSize(data.mid(pos), pos);
uint begSilence = readSize(data.mid(pos), pos);
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt16BE(pos)));
d->sampleFrames = readSize(data, pos);
if(pos > dataSize - 3) {
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
ulong begSilence = readSize(data, pos);
if(pos > dataSize - 2) {
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
const ushort flags = data.toUInt16BE(pos);
pos += 2;
d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]];
d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1;
d->sampleRate = sftable[(flags >> 13) & 0x07];
d->channels = ((flags >> 4) & 0x0F) + 1;
if((d->sampleFrames - begSilence) != 0)
d->bitrate = static_cast<int>(streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence));
d->bitrate = d->bitrate / 1000;
d->length = (d->sampleFrames - begSilence) / d->sampleRate;
const uint frameCount = d->sampleFrames - begSilence;
if(frameCount != 0 && d->sampleRate != 0) {
d->bitrate = (int)(streamLength * 8.0 * d->sampleRate / frameCount / 1000);
d->length = frameCount / d->sampleRate;
}
}
else if (packetType == "RG") {
// Replay Gain
// http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
ByteVector data = file->readBlock(dataSize);
if(dataSize <= 9) {
debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse.");
break;
}
readRG = true;
int replayGainVersion = data[0];
@ -306,4 +347,3 @@ void MPC::AudioProperties::readSV7(const ByteVector &data, offset_t streamLength
if(!d->bitrate)
d->bitrate = d->length > 0 ? static_cast<int>(streamLength * 8L / d->length / 1000) : 0;
}

View File

@ -44,10 +44,10 @@ public:
const ID3v2::Header *tagHeader;
ByteVector elementID;
uint startTime;
uint endTime;
uint startOffset;
uint endOffset;
TagLib::uint startTime;
TagLib::uint endTime;
TagLib::uint startOffset;
TagLib::uint endOffset;
FrameListMap embeddedFrameListMap;
FrameList embeddedFrameList;
};
@ -64,8 +64,8 @@ ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &dat
setData(data);
}
ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT,
const uint &sO, const uint &eO, const FrameList &eF) :
ChapterFrame::ChapterFrame(const ByteVector &eID, const TagLib::uint &sT, const TagLib::uint &eT,
const TagLib::uint &sO, const TagLib::uint &eO, const FrameList &eF) :
ID3v2::Frame("CHAP")
{
d = new ChapterFramePrivate;
@ -89,22 +89,22 @@ ByteVector ChapterFrame::elementID() const
return d->elementID;
}
uint ChapterFrame::startTime() const
TagLib::uint ChapterFrame::startTime() const
{
return d->startTime;
}
uint ChapterFrame::endTime() const
TagLib::uint ChapterFrame::endTime() const
{
return d->endTime;
}
uint ChapterFrame::startOffset() const
TagLib::uint ChapterFrame::startOffset() const
{
return d->startOffset;
}
uint ChapterFrame::endOffset() const
TagLib::uint ChapterFrame::endOffset() const
{
return d->endOffset;
}
@ -116,22 +116,22 @@ void ChapterFrame::setElementID(const ByteVector &eID)
d->elementID.append(char(0));
}
void ChapterFrame::setStartTime(const uint &sT)
void ChapterFrame::setStartTime(const TagLib::uint &sT)
{
d->startTime = sT;
}
void ChapterFrame::setEndTime(const uint &eT)
void ChapterFrame::setEndTime(const TagLib::uint &eT)
{
d->endTime = eT;
}
void ChapterFrame::setStartOffset(const uint &sO)
void ChapterFrame::setStartOffset(const TagLib::uint &sO)
{
d->startOffset = sO;
}
void ChapterFrame::setEndOffset(const uint &eO)
void ChapterFrame::setEndOffset(const TagLib::uint &eO)
{
d->endOffset = eO;
}
@ -232,6 +232,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
pos += 4;
size -= pos;
while(embPos < size - header()->size()) {
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), d->tagHeader);
if(!frame)

View File

@ -94,7 +94,7 @@ bool TableOfContentsFrame::isOrdered() const
return d->isOrdered;
}
uint TableOfContentsFrame::entryCount() const
TagLib::uint TableOfContentsFrame::entryCount() const
{
return d->childElements.size();
}
@ -229,7 +229,7 @@ TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag)
void TableOfContentsFrame::parseFields(const ByteVector &data)
{
uint size = data.size();
TagLib::uint size = data.size();
if(size < 6) {
debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by "
"null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated "
@ -243,8 +243,9 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
d->elementID.append(char(0));
d->isTopLevel = (data.at(pos) & 2) > 0;
d->isOrdered = (data.at(pos++) & 1) > 0;
uint entryCount = data.at(pos++);
for(uint i = 0; i < entryCount; i++) {
TagLib::uint entryCount = data.at(pos++);
for(TagLib::uint i = 0; i < entryCount; i++)
{
ByteVector childElementID = readStringField(data, String::Latin1, pos).data(String::Latin1);
childElementID.append(char(0));
d->childElements.append(childElementID);

View File

@ -254,12 +254,49 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const
if(d->header->compression() &&
!d->header->encryption())
{
ByteVector data(frameDataLength);
uLongf uLongTmp = static_cast<uLongf>(frameDataLength);
::uncompress((Bytef *) data.data(),
(uLongf *) &uLongTmp,
(Bytef *) frameData.data() + frameDataOffset,
size());
if(frameData.size() <= frameDataOffset) {
debug("Compressed frame doesn't have enough data to decode");
return ByteVector();
}
z_stream stream = {};
if(inflateInit(&stream) != Z_OK)
return ByteVector();
stream.avail_in = (uLongf) frameData.size() - frameDataOffset;
stream.next_in = (Bytef *) frameData.data() + frameDataOffset;
static const size_t chunkSize = 1024;
ByteVector data;
ByteVector chunk(chunkSize);
do {
stream.avail_out = (uLongf) chunk.size();
stream.next_out = (Bytef *) chunk.data();
int result = inflate(&stream, Z_NO_FLUSH);
if(result == Z_STREAM_ERROR ||
result == Z_NEED_DICT ||
result == Z_DATA_ERROR ||
result == Z_MEM_ERROR)
{
if(result != Z_STREAM_ERROR)
inflateEnd(&stream);
debug("Error reading compressed stream");
return ByteVector();
}
data.append(stream.avail_out == 0 ? chunk : chunk.mid(0, chunk.size() - stream.avail_out));
} while(stream.avail_out == 0);
inflateEnd(&stream);
if(frameDataLength != data.size())
debug("frameDataLength does not match the data length returned by zlib");
return data;
}
else

View File

@ -278,7 +278,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
return new UnknownFrame(data, header);
}
void FrameFactory::rebuildAggregateFrames(Tag *tag) const
void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
{
if(tag->header()->majorVersion() < 4 &&
tag->frameList("TDRC").size() == 1 &&

View File

@ -78,7 +78,7 @@ namespace TagLib {
* have been deprecated and can't be upgraded directly.
*/
// BIC: Make virtual
void rebuildAggregateFrames(Tag *tag) const;
void rebuildAggregateFrames(ID3v2::Tag *tag) const;
/*!
* Returns the default text encoding for text frames. If setTextEncoding()

View File

@ -52,7 +52,7 @@ using namespace ID3v2;
class ID3v2::Tag::TagPrivate
{
public:
TagPrivate()
TagPrivate()
: file(0)
, tagOffset(-1)
, extendedHeader(0)
@ -91,6 +91,11 @@ namespace
const TagLib::StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler;
namespace
{
const uint DefaultPaddingSize = 1024;
}
////////////////////////////////////////////////////////////////////////////////
// Latin1StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
@ -607,15 +612,21 @@ ByteVector ID3v2::Tag::render(int version) const
// Compute the amount of padding, and append that to tagData.
size_t paddingSize = 0;
const size_t originalSize = d->header.tagSize();
size_t paddingSize = DefaultPaddingSize;
if(tagData.size() < originalSize)
paddingSize = originalSize - tagData.size();
else
paddingSize = 1024;
if(d->file && tagData.size() < d->header.tagSize()) {
paddingSize = d->header.tagSize() - tagData.size();
tagData.append(ByteVector(paddingSize, char(0)));
// Padding won't increase beyond 1% of the file size.
if(paddingSize > DefaultPaddingSize) {
const ulonglong threshold = d->file->length() / 100;
if(paddingSize > threshold)
paddingSize = DefaultPaddingSize;
}
}
tagData.append(ByteVector(paddingSize, '\0'));
// Set the version and data size.
d->header.setMajorVersion(version);

View File

@ -374,7 +374,7 @@ offset_t MPEG::File::previousFrameOffset(offset_t position)
ByteVector buffer;
while (position > 0) {
size_t size = position < static_cast<offset_t>(bufferSize())
size_t size = position < static_cast<offset_t>(bufferSize())
? static_cast<size_t>(position) : bufferSize();
position -= size;
@ -401,9 +401,24 @@ offset_t MPEG::File::firstFrameOffset()
{
offset_t position = 0;
if(ID3v2Tag())
if(ID3v2Tag()) {
position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize();
// Skip duplicate ID3v2 tags.
// Workaround for some faulty files that have duplicate ID3v2 tags.
// Combination of EAC and LAME creates such files when configured incorrectly.
long location;
while((location = findID3v2(position)) >= 0) {
seek(location);
const ID3v2::Header header(readBlock(ID3v2::Header::size()));
position = location + header.completeTagSize();
debug("MPEG::File::firstFrameOffset() - Duplicate ID3v2 tag found.");
}
}
return nextFrameOffset(position);
}
@ -435,7 +450,7 @@ void MPEG::File::read(bool readProperties, AudioProperties::ReadStyle properties
{
// Look for an ID3v2 tag
d->ID3v2Location = findID3v2();
d->ID3v2Location = findID3v2(0);
if(d->ID3v2Location >= 0) {
@ -478,7 +493,7 @@ void MPEG::File::read(bool readProperties, AudioProperties::ReadStyle properties
ID3v1Tag(true);
}
offset_t MPEG::File::findID3v2()
offset_t MPEG::File::findID3v2(offset_t offset)
{
// This method is based on the contents of TagLib::File::find(), but because
// of some subtlteies -- specifically the need to look for the bit pattern of
@ -501,9 +516,9 @@ offset_t MPEG::File::findID3v2()
const offset_t originalPosition = tell();
// Start the search at the beginning of the file.
// Start the search at the offset.
seek(0);
seek(offset);
// This loop is the crux of the find method. There are three cases that we
// want to account for:
@ -514,7 +529,7 @@ offset_t MPEG::File::findID3v2()
// (2) The search pattern is wholly contained within the current buffer.
//
// (3) The current buffer ends with a partial match of the pattern. We will
// note this for use in the next itteration, where we will check for the rest
// note this for use in the next iteration, where we will check for the rest
// of the pattern.
while(true)
@ -532,7 +547,7 @@ offset_t MPEG::File::findID3v2()
const size_t patternOffset = (bufferSize() - previousPartialMatch);
if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
seek(originalPosition);
return bufferOffset - bufferSize() + previousPartialMatch;
return offset + bufferOffset - bufferSize() + previousPartialMatch;
}
}
@ -541,7 +556,7 @@ offset_t MPEG::File::findID3v2()
const size_t location = buffer.find(ID3v2::Header::fileIdentifier());
if(location != ByteVector::npos) {
seek(originalPosition);
return bufferOffset + location;
return offset + bufferOffset + location;
}
size_t firstSynchByte = buffer.find(char(uchar(255)));

View File

@ -208,8 +208,8 @@ namespace TagLib {
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@ -227,8 +227,8 @@ namespace TagLib {
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@ -246,8 +246,8 @@ namespace TagLib {
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@ -336,7 +336,7 @@ namespace TagLib {
File &operator=(const File &);
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
offset_t findID3v2();
offset_t findID3v2(offset_t offset);
offset_t findID3v1();
void findAPE();

View File

@ -103,7 +103,7 @@ PropertyMap Ogg::FLAC::File::properties() const
PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
}
FLAC::AudioProperties *Ogg::FLAC::File::audioProperties() const
{
@ -211,29 +211,30 @@ void Ogg::FLAC::File::scan()
offset_t overhead = 0;
ByteVector metadataHeader = packet(ipacket);
if(metadataHeader.isNull())
if(metadataHeader.isEmpty())
return;
ByteVector header;
if (!metadataHeader.startsWith("fLaC")) {
if(!metadataHeader.startsWith("fLaC")) {
// FLAC 1.1.2+
if (metadataHeader.mid(1,4) != "FLAC") return;
if(metadataHeader.mid(1, 4) != "FLAC")
return;
if (metadataHeader[5] != 1) return; // not version 1
if(metadataHeader[5] != 1)
return; // not version 1
metadataHeader = metadataHeader.mid(13);
}
else {
// FLAC 1.1.0 & 1.1.1
metadataHeader = packet(++ipacket);
if(metadataHeader.isNull())
return;
}
header = metadataHeader.mid(0,4);
ByteVector header = metadataHeader.mid(0, 4);
if(header.size() != 4) {
debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
return;
}
// Header format (from spec):
// <1> Last-metadata-block flag
// <7> BLOCK_TYPE
@ -262,11 +263,12 @@ void Ogg::FLAC::File::scan()
while(!lastBlock) {
metadataHeader = packet(++ipacket);
if(metadataHeader.isNull())
return;
header = metadataHeader.mid(0, 4);
if(header.size() != 4) {
debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header");
return;
}
blockType = header[0] & 0x7f;
lastBlock = (header[0] & 0x80) != 0;
length = header.toUInt24BE(1);

View File

@ -39,7 +39,8 @@ public:
FilePrivate() :
properties(0),
tag(0),
tagChunkID("ID3 ")
tagChunkID("ID3 "),
hasID3v2(false)
{
}
@ -53,6 +54,8 @@ public:
AudioProperties *properties;
ID3v2::Tag *tag;
ByteVector tagChunkID;
bool hasID3v2;
};
////////////////////////////////////////////////////////////////////////////////
@ -100,7 +103,6 @@ PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties)
return d->tag->setProperties(properties);
}
RIFF::AIFF::AudioProperties *RIFF::AIFF::File::audioProperties() const
{
return d->properties;
@ -119,10 +121,15 @@ bool RIFF::AIFF::File::save()
}
setChunkData(d->tagChunkID, d->tag->render());
d->hasID3v2 = true;
return true;
}
bool RIFF::AIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
////////////////////////////////////////////////////////////////////////////////
// private members
@ -134,6 +141,7 @@ void RIFF::AIFF::File::read(bool readProperties, AudioProperties::ReadStyle prop
if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") {
d->tagChunkID = chunkName(i);
d->tag = new ID3v2::Tag(this, chunkOffset(i));
d->hasID3v2 = true;
}
else if(chunkName(i) == "COMM" && readProperties)
d->properties = new AudioProperties(chunkData(i), propertiesStyle);

View File

@ -131,6 +131,7 @@ void RIFF::AIFF::AudioProperties::read(const ByteVector &data)
d->channels = data.toInt16BE(0);
d->sampleFrames = data.toUInt32BE(2);
d->sampleWidth = data.toInt16BE(6);
const long double sampleRate = data.toFloat80BE(8);
d->sampleRate = static_cast<int>(sampleRate);
d->bitrate = static_cast<int>((sampleRate * d->sampleWidth * d->channels) / 1000.0);

View File

@ -47,7 +47,7 @@ namespace TagLib {
{
public:
/*!
* Creates an instance of AIFF::AudioProperties with the data read from
* Creates an instance of AIFF::AudioProperties with the data read from
* the ByteVector \a data.
*/
AudioProperties(const ByteVector &data, ReadStyle style);

View File

@ -244,9 +244,15 @@ void RIFF::Info::Tag::parse(const ByteVector &data)
size_t p = 4;
while(p < data.size()) {
const uint size = data.toUInt32LE(p + 4);
d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
if(size > data.size() - p - 8)
break;
const ByteVector id = data.mid(p, 4);
if(RIFF::File::isValidChunkName(id)) {
const String text = TagPrivate::stringHandler->parse(data.mid(p + 8, size));
d->fieldListMap[id] = text;
}
p += ((size + 1) & ~1) + 8;
}
}

View File

@ -57,7 +57,7 @@ public:
}
AudioProperties *properties;
ByteVector tagChunkID;
DoubleTagUnion tag;
@ -146,21 +146,30 @@ bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version)
if(stripOthers)
strip(static_cast<TagTypes>(AllTags & ~tags));
ID3v2::Tag *id3v2tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
if((tags & ID3v2) && !id3v2tag->isEmpty()) {
setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version));
d->hasID3v2 = true;
const ID3v2::Tag *id3v2tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
if(tags & ID3v2) {
if(d->hasID3v2) {
removeChunk(d->tagChunkID);
d->hasID3v2 = false;
}
if(!id3v2tag->isEmpty()) {
setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version));
d->hasID3v2 = true;
}
}
Info::Tag *infotag = d->tag.access<Info::Tag>(InfoIndex, false);
if((tags & Info) && !infotag->isEmpty()) {
int chunkId = findInfoTagChunk();
if(chunkId != -1)
setChunkData(chunkId, infotag->render());
else
setChunkData("LIST", infotag->render(), true);
const Info::Tag *infotag = d->tag.access<Info::Tag>(InfoIndex, false);
if(tags & Info) {
if(d->hasInfo) {
removeChunk(findInfoTagChunk());
d->hasInfo = false;
}
d->hasInfo = true;
if(!infotag->isEmpty()) {
setChunkData("LIST", infotag->render(), true);
d->hasInfo = true;
}
}
return true;
@ -239,6 +248,6 @@ TagLib::uint RIFF::WAV::File::findInfoTagChunk()
return i;
}
}
return TagLib::uint(-1);
}

View File

@ -54,7 +54,7 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
RIFF::WAV::AudioProperties::AudioProperties(const ByteVector &data, uint streamLength,
RIFF::WAV::AudioProperties::AudioProperties(const ByteVector &data, uint streamLength,
ReadStyle style) :
d(new PropertiesPrivate())
{
@ -108,7 +108,7 @@ TagLib::uint RIFF::WAV::AudioProperties::format() const
void RIFF::WAV::AudioProperties::read(const ByteVector &data, uint streamLength)
{
if(data.size() < 16) {
debug("RIFF::WAV::AudioProperties::read() - \"fmt \" chunk is too short.");
debug("RIFF::WAV::AudioProperties::read() - \"fmt \" chunk is too short for WAV.");
return;
}

View File

@ -31,11 +31,10 @@
#include "tstring.h"
#include "tutils.h"
#include "tdebuglistener.h"
#include "tutils.h"
#include <bitset>
using namespace TagLib;
namespace TagLib
{
// The instance is defined in tdebuglistener.cpp.
@ -44,7 +43,7 @@ namespace TagLib
void debug(const String &s)
{
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
debugListener->printMessage("TagLib: " + s + "\n");
#endif
@ -54,11 +53,11 @@ namespace TagLib
{
#if !defined(NDEBUG) || defined(TRACE_IN_RELEASE)
for(size_t i = 0; i < v.size(); ++i)
for(size_t i = 0; i < v.size(); ++i)
{
std::string bits = std::bitset<8>(v[i]).to_string();
String msg = Utils::formatString(
"*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n",
"*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n",
i, v[i], v[i], v[i], bits.c_str());
debugListener->printMessage(msg);

View File

@ -63,12 +63,12 @@ namespace
debug("unicodeToAnsi() - Should not be used on WinNT systems.");
}
const int len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
const int len = ::WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
if(len == 0)
return std::string();
std::string str(len, '\0');
WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL);
::WideCharToMultiByte(CP_ACP, 0, wstr, -1, &str[0], len, NULL, NULL);
return str;
}
@ -145,12 +145,12 @@ String FileName::toString() const
return String(d->data->wname);
}
else if(!d->data->name.empty()) {
const int len = MultiByteToWideChar(CP_ACP, 0, d->data->name.c_str(), -1, NULL, 0);
const int len = ::MultiByteToWideChar(CP_ACP, 0, d->data->name.c_str(), -1, NULL, 0);
if(len == 0)
return String::null;
std::vector<wchar_t> buf(len);
MultiByteToWideChar(CP_ACP, 0, d->data->name.c_str(), -1, &buf[0], len);
::MultiByteToWideChar(CP_ACP, 0, d->data->name.c_str(), -1, &buf[0], len);
return String(&buf[0]);
}
@ -159,7 +159,6 @@ String FileName::toString() const
}
}
#endif // _WIN32
////////////////////////////////////////////////////////////////////////////////

View File

@ -41,105 +41,80 @@
#include <cerrno>
#include <climits>
#ifdef HAVE_STD_CODECVT
# include <codecvt>
#ifdef _WIN32
# include <windows.h>
#else
# include "unicode.h"
#endif
namespace
{
using namespace TagLib;
void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength)
inline size_t UTF16toUTF8(
const wchar_t *src, size_t srcLength, char *dst, size_t dstLength)
{
#ifdef HAVE_STD_CODECVT
size_t len = 0;
typedef std::codecvt_utf8_utf16<wchar_t> utf8_utf16_t;
#ifdef _WIN32
using namespace TagLib;
const wchar_t *srcBegin = src;
const wchar_t *srcEnd = srcBegin + srcLength;
char *dstBegin = dst;
char *dstEnd = dstBegin + dstLength;
std::mbstate_t st;
const wchar_t *source;
char *target;
memset(&st, 0, sizeof(st));
std::codecvt_base::result result = utf8_utf16_t().out(
st, srcBegin, srcEnd, source, dstBegin, dstEnd, target);
if(result != utf8_utf16_t::ok) {
debug("String::UTF16toUTF8() - Unicode conversion error.");
}
len = ::WideCharToMultiByte(CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL);
#else
using namespace Unicode;
using namespace TagLib;
const Unicode::UTF16 *srcBegin = src;
const Unicode::UTF16 *srcEnd = srcBegin + srcLength;
const UTF16 *srcBegin = src;
const UTF16 *srcEnd = srcBegin + srcLength;
Unicode::UTF8 *dstBegin = reinterpret_cast<Unicode::UTF8*>(dst);
Unicode::UTF8 *dstEnd = dstBegin + dstLength;
UTF8 *dstBegin = reinterpret_cast<UTF8*>(dst);
UTF8 *dstEnd = dstBegin + dstLength;
Unicode::ConversionResult result = Unicode::ConvertUTF16toUTF8(
&srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion);
ConversionResult result = ConvertUTF16toUTF8(
&srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::UTF16toUTF8() - Unicode conversion error.");
}
if(result == conversionOK)
len = dstBegin - reinterpret_cast<UTF8*>(dst);
#endif
if(len == 0)
debug("String::UTF16toUTF8() - Unicode conversion error.");
return len;
}
void UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength)
inline size_t UTF8toUTF16(
const char *src, size_t srcLength, wchar_t *dst, size_t dstLength)
{
#ifdef HAVE_STD_CODECVT
size_t len = 0;
typedef std::codecvt_utf8_utf16<wchar_t> utf8_utf16_t;
#ifdef _WIN32
using namespace TagLib;
const char *srcBegin = src;
const char *srcEnd = srcBegin + srcLength;
wchar_t *dstBegin = dst;
wchar_t *dstEnd = dstBegin + dstLength;
std::mbstate_t st;
const char *source;
wchar_t *target;
memset(&st, 0, sizeof(st));
std::codecvt_base::result result = utf8_utf16_t().in(
st, srcBegin, srcEnd, source, dstBegin, dstEnd, target);
if(result != utf8_utf16_t::ok) {
debug("String::UTF8toUTF16() - Unicode conversion error.");
}
len = ::MultiByteToWideChar(CP_UTF8, 0, src, srcLength, dst, dstLength);
#else
using namespace Unicode;
using namespace TagLib;
const Unicode::UTF8 *srcBegin = reinterpret_cast<const Unicode::UTF8*>(src);
const Unicode::UTF8 *srcEnd = srcBegin + srcLength;
const UTF8 *srcBegin = reinterpret_cast<const UTF8*>(src);
const UTF8 *srcEnd = srcBegin + srcLength;
Unicode::UTF16 *dstBegin = dst;
Unicode::UTF16 *dstEnd = dstBegin + dstLength;
UTF16 *dstBegin = dst;
UTF16 *dstEnd = dstBegin + dstLength;
Unicode::ConversionResult result = Unicode::ConvertUTF8toUTF16(
&srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion);
ConversionResult result = ConvertUTF8toUTF16(
&srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion);
if(result != Unicode::conversionOK) {
debug("String::UTF8toUTF16() - Unicode conversion error.");
}
if(result == conversionOK)
len = dstBegin - dst;
#endif
if(len == 0)
debug("String::UTF8toUTF16() - Unicode conversion error.");
return len;
}
}
@ -412,8 +387,9 @@ ByteVector String::data(Type t) const
{
ByteVector v(size() * 4 + 1, 0);
UTF16toUTF8(d->data->c_str(), d->data->size(), v.data(), v.size());
v.resize(::strlen(v.data()));
const size_t len = UTF16toUTF8(
d->data->c_str(), d->data->size(), v.data(), v.size());
v.resize(len);
return v;
}
@ -690,8 +666,8 @@ void String::copyFromUTF8(const char *s, size_t length)
d->data->resize(length);
if(length > 0) {
UTF8toUTF16(s, length, &(*d->data)[0], d->data->size());
d->data->resize(::wcslen(d->data->c_str()));
const size_t len = UTF8toUTF16(s, length, &(*d->data)[0], d->data->size());
d->data->resize(len);
}
}

View File

@ -106,11 +106,6 @@
bit mask & shift operations.
------------------------------------------------------------------------ */
// Workaround for when MSVC doesn't have wchar_t as a built-in type.
#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
# include <wchar.h>
#endif
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tests/data/infloop.mpc Normal file

Binary file not shown.

BIN
tests/data/infloop.wav Normal file

Binary file not shown.

BIN
tests/data/longloop.ape Normal file

Binary file not shown.

BIN
tests/data/segfault.aif Normal file

Binary file not shown.

BIN
tests/data/segfault.mpc Normal file

Binary file not shown.

BIN
tests/data/segfault.oga Normal file

Binary file not shown.

BIN
tests/data/segfault.wav Normal file

Binary file not shown.

1
tests/data/segfault2.mpc Normal file
View File

@ -0,0 +1 @@
MPCKSH

BIN
tests/data/zerodiv.ape Normal file

Binary file not shown.

BIN
tests/data/zerodiv.mpc Normal file

Binary file not shown.

View File

@ -1,9 +1,9 @@
#include <cppunit/extensions/HelperMacros.h>
#include <string>
#include <stdio.h>
#include <tag.h>
#include <tbytevectorlist.h>
#include <aifffile.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
using namespace std;
@ -13,32 +13,57 @@ class TestAIFF : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(TestAIFF);
CPPUNIT_TEST(testReading);
CPPUNIT_TEST(testSaveID3v2);
CPPUNIT_TEST(testAiffCProperties);
CPPUNIT_TEST(testFuzzedFile1);
CPPUNIT_TEST(testFuzzedFile2);
CPPUNIT_TEST_SUITE_END();
public:
void testReading()
{
ScopedFileCopy copy("empty", ".aiff");
string filename = copy.fileName();
RIFF::AIFF::File f(TEST_FILE_PATH_C("empty.aiff"));
CPPUNIT_ASSERT_EQUAL(705, f.audioProperties()->bitrate());
}
RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str());
CPPUNIT_ASSERT_EQUAL(705, f->audioProperties()->bitrate());
CPPUNIT_ASSERT(!f->audioProperties()->isAiffC());
delete f;
void testSaveID3v2()
{
ScopedFileCopy copy("empty", ".aiff");
string newname = copy.fileName();
{
RIFF::AIFF::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasID3v2Tag());
f.tag()->setTitle(L"TitleXXX");
f.save();
}
{
RIFF::AIFF::File f(newname.c_str());
CPPUNIT_ASSERT(f.hasID3v2Tag());
CPPUNIT_ASSERT_EQUAL(String(L"TitleXXX"), f.tag()->title());
}
}
void testAiffCProperties()
{
ScopedFileCopy copy("alaw", ".aifc");
string filename = copy.fileName();
RIFF::AIFF::File f(TEST_FILE_PATH_C("alaw.aifc"));
CPPUNIT_ASSERT(f.audioProperties()->isAiffC());
CPPUNIT_ASSERT(f.audioProperties()->compressionType() == "ALAW");
CPPUNIT_ASSERT(f.audioProperties()->compressionName() == "SGI CCITT G.711 A-law");
}
RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str());
CPPUNIT_ASSERT(f->audioProperties()->isAiffC());
CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f->audioProperties()->compressionType());
CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f->audioProperties()->compressionName());
delete f;
void testFuzzedFile1()
{
RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif"));
CPPUNIT_ASSERT(!f.isValid());
}
void testFuzzedFile2()
{
RIFF::AIFF::File f(TEST_FILE_PATH_C("excessive_alloc.aif"));
CPPUNIT_ASSERT(!f.isValid());
}
};

View File

@ -1,10 +1,10 @@
#include <cppunit/extensions/HelperMacros.h>
#include <string>
#include <stdio.h>
#include <tag.h>
#include <tstringlist.h>
#include <tbytevectorlist.h>
#include <apefile.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
using namespace std;
@ -16,6 +16,8 @@ class TestAPE : public CppUnit::TestFixture
CPPUNIT_TEST(testProperties399);
CPPUNIT_TEST(testProperties396);
CPPUNIT_TEST(testProperties390);
CPPUNIT_TEST(testFuzzedFile1);
CPPUNIT_TEST(testFuzzedFile2);
CPPUNIT_TEST_SUITE_END();
public:
@ -47,6 +49,18 @@ public:
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
}
void testFuzzedFile1()
{
APE::File f(TEST_FILE_PATH_C("longloop.ape"));
CPPUNIT_ASSERT(f.isValid());
}
void testFuzzedFile2()
{
APE::File f(TEST_FILE_PATH_C("zerodiv.ape"));
CPPUNIT_ASSERT(f.isValid());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestAPE);

View File

@ -93,6 +93,7 @@ class TestID3v2 : public CppUnit::TestFixture
CPPUNIT_TEST(testRenderChapterFrame);
CPPUNIT_TEST(testParseTableOfContentsFrame);
CPPUNIT_TEST(testRenderTableOfContentsFrame);
CPPUNIT_TEST(testShrinkPadding);
CPPUNIT_TEST_SUITE_END();
public:
@ -201,7 +202,7 @@ public:
"JPG"
"\x01"
"d\x00"
"\x00", 18);
"\x00", 14);
ID3v2::Header header;
header.setMajorVersion(2);
ID3v2::AttachedPictureFrame *frame =
@ -224,7 +225,7 @@ public:
"JPG"
"\x01"
"d\x00"
"\x00", 18);
"\x00", 14);
ID3v2::Header header;
header.setMajorVersion(2);
ID3v2::AttachedPictureFrame *frame =
@ -1016,6 +1017,39 @@ public:
f.render());
}
void testShrinkPadding()
{
ScopedFileCopy copy("xing", ".mp3");
string newname = copy.fileName();
{
MPEG::File f(newname.c_str());
ID3v2::Tag *tag = f.ID3v2Tag(true);
ID3v2::TextIdentificationFrame *frame1 = new ID3v2::TextIdentificationFrame("TIT2");
frame1->setText("Title");
tag->addFrame(frame1);
ID3v2::AttachedPictureFrame *frame2 = new ID3v2::AttachedPictureFrame();
frame2->setPicture(ByteVector(100 * 1024, '\xff'));
tag->addFrame(frame2);
f.save();
CPPUNIT_ASSERT(f.length() > 100 * 1024);
}
{
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
ID3v2::Tag *tag = f.ID3v2Tag();
tag->removeFrames("APIC");
f.save();
CPPUNIT_ASSERT(f.length() < 10 * 1024);
}
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2);

View File

@ -155,7 +155,7 @@ public:
MP4::Atoms *atoms = new MP4::Atoms(f);
MP4::Atom *moov = atoms->atoms[0];
CPPUNIT_ASSERT_EQUAL(long(77), moov->length);
CPPUNIT_ASSERT_EQUAL(offset_t(77), moov->length);
f->tag()->itemListMap()["pgap"] = true;
f->save();
@ -169,7 +169,7 @@ public:
atoms = new MP4::Atoms(f);
moov = atoms->atoms[0];
// original size + 'pgap' size + padding
CPPUNIT_ASSERT_EQUAL(long(77 + 25 + 974), moov->length);
CPPUNIT_ASSERT_EQUAL(offset_t(77 + 25 + 974), moov->length);
delete atoms;
delete f;
}

View File

@ -1,10 +1,10 @@
#include <cppunit/extensions/HelperMacros.h>
#include <string>
#include <stdio.h>
#include <tag.h>
#include <tstringlist.h>
#include <tbytevectorlist.h>
#include <mpcfile.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
using namespace std;
@ -17,6 +17,10 @@ class TestMPC : public CppUnit::TestFixture
CPPUNIT_TEST(testPropertiesSV7);
CPPUNIT_TEST(testPropertiesSV5);
CPPUNIT_TEST(testPropertiesSV4);
CPPUNIT_TEST(testFuzzedFile1);
CPPUNIT_TEST(testFuzzedFile2);
CPPUNIT_TEST(testFuzzedFile3);
CPPUNIT_TEST(testFuzzedFile4);
CPPUNIT_TEST_SUITE_END();
public:
@ -61,6 +65,30 @@ public:
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
}
void testFuzzedFile1()
{
MPC::File f(TEST_FILE_PATH_C("zerodiv.mpc"));
CPPUNIT_ASSERT(f.isValid());
}
void testFuzzedFile2()
{
MPC::File f(TEST_FILE_PATH_C("infloop.mpc"));
CPPUNIT_ASSERT(f.isValid());
}
void testFuzzedFile3()
{
MPC::File f(TEST_FILE_PATH_C("segfault.mpc"));
CPPUNIT_ASSERT(f.isValid());
}
void testFuzzedFile4()
{
MPC::File f(TEST_FILE_PATH_C("segfault2.mpc"));
CPPUNIT_ASSERT(f.isValid());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMPC);

View File

@ -16,6 +16,8 @@ class TestMPEG : public CppUnit::TestFixture
CPPUNIT_TEST(testSaveID3v24);
CPPUNIT_TEST(testSaveID3v24WrongParam);
CPPUNIT_TEST(testSaveID3v23);
CPPUNIT_TEST(testDuplicateID3v2);
CPPUNIT_TEST(testFuzzedFile);
CPPUNIT_TEST_SUITE_END();
public:
@ -92,6 +94,25 @@ public:
}
}
void testDuplicateID3v2()
{
ScopedFileCopy copy("duplicate_id3v2", ".mp3");
string newname = copy.fileName();
MPEG::File f(newname.c_str());
// duplicate_id3v2.mp3 has duplicate ID3v2 tags.
// Sample rate will be 32000 if can't skip the second tag.
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
}
void testFuzzedFile()
{
MPEG::File f(TEST_FILE_PATH_C("excessive_alloc.mp3"));
CPPUNIT_ASSERT(f.isValid());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG);

View File

@ -15,6 +15,7 @@ class TestOggFLAC : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(TestOggFLAC);
CPPUNIT_TEST(testFramingBit);
CPPUNIT_TEST(testFuzzedFile);
CPPUNIT_TEST_SUITE_END();
public:
@ -39,6 +40,12 @@ public:
delete f;
}
void testFuzzedFile()
{
Ogg::FLAC::File f(TEST_FILE_PATH_C("segfault.oga"));
CPPUNIT_ASSERT(!f.isValid());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestOggFLAC);

View File

@ -1,9 +1,11 @@
#include <cppunit/extensions/HelperMacros.h>
#include <string>
#include <stdio.h>
#include <tag.h>
#include <id3v2tag.h>
#include <infotag.h>
#include <tbytevectorlist.h>
#include <wavfile.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
using namespace std;
@ -14,8 +16,12 @@ class TestWAV : public CppUnit::TestFixture
CPPUNIT_TEST_SUITE(TestWAV);
CPPUNIT_TEST(testLength);
CPPUNIT_TEST(testZeroSizeDataChunk);
CPPUNIT_TEST(testID3v2Tag);
CPPUNIT_TEST(testInfoTag);
CPPUNIT_TEST(testStripTags);
CPPUNIT_TEST(testFormat);
CPPUNIT_TEST(testFuzzedFile1);
CPPUNIT_TEST(testFuzzedFile2);
CPPUNIT_TEST_SUITE_END();
public:
@ -23,14 +29,80 @@ public:
void testLength()
{
RIFF::WAV::File f(TEST_FILE_PATH_C("empty.wav"));
CPPUNIT_ASSERT_EQUAL(true, f.isValid());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
}
void testZeroSizeDataChunk()
{
RIFF::WAV::File f(TEST_FILE_PATH_C("zero-size-chunk.wav"));
CPPUNIT_ASSERT_EQUAL(false, f.isValid());
CPPUNIT_ASSERT(!f.isValid());
}
void testID3v2Tag()
{
ScopedFileCopy copy("empty", ".wav");
string filename = copy.fileName();
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
f.ID3v2Tag()->setTitle(L"Title");
f.ID3v2Tag()->setArtist(L"Artist");
f.save();
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.ID3v2Tag()->title());
CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.ID3v2Tag()->artist());
f.ID3v2Tag()->setTitle(L"");
f.ID3v2Tag()->setArtist(L"");
f.save();
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->title());
CPPUNIT_ASSERT_EQUAL(String(L""), f.ID3v2Tag()->artist());
}
}
void testInfoTag()
{
ScopedFileCopy copy("empty", ".wav");
string filename = copy.fileName();
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
f.InfoTag()->setTitle(L"Title");
f.InfoTag()->setArtist(L"Artist");
f.save();
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT_EQUAL(String(L"Title"), f.InfoTag()->title());
CPPUNIT_ASSERT_EQUAL(String(L"Artist"), f.InfoTag()->artist());
f.InfoTag()->setTitle(L"");
f.InfoTag()->setArtist(L"");
f.save();
}
{
RIFF::WAV::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->title());
CPPUNIT_ASSERT_EQUAL(String(L""), f.InfoTag()->artist());
}
}
void testStripTags()
@ -80,6 +152,19 @@ public:
CPPUNIT_ASSERT_EQUAL(true, f2.isValid());
CPPUNIT_ASSERT_EQUAL((uint)6, f2.audioProperties()->format());
}
void testFuzzedFile1()
{
RIFF::WAV::File f1(TEST_FILE_PATH_C("infloop.wav"));
CPPUNIT_ASSERT(!f1.isValid());
}
void testFuzzedFile2()
{
RIFF::WAV::File f2(TEST_FILE_PATH_C("segfault.wav"));
CPPUNIT_ASSERT(f2.isValid());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestWAV);