mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Merge pull request #507 from TsudaKageyu/merge-master-to-taglib2
Merge master to taglib2
This commit is contained in:
commit
ab2389819e
21
.editorconfig
Normal file
21
.editorconfig
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 &&
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
BIN
tests/data/duplicate_id3v2.mp3
Normal file
BIN
tests/data/duplicate_id3v2.mp3
Normal file
Binary file not shown.
BIN
tests/data/excessive_alloc.aif
Normal file
BIN
tests/data/excessive_alloc.aif
Normal file
Binary file not shown.
BIN
tests/data/excessive_alloc.mp3
Normal file
BIN
tests/data/excessive_alloc.mp3
Normal file
Binary file not shown.
BIN
tests/data/infloop.mpc
Normal file
BIN
tests/data/infloop.mpc
Normal file
Binary file not shown.
BIN
tests/data/infloop.wav
Normal file
BIN
tests/data/infloop.wav
Normal file
Binary file not shown.
BIN
tests/data/longloop.ape
Normal file
BIN
tests/data/longloop.ape
Normal file
Binary file not shown.
BIN
tests/data/segfault.aif
Normal file
BIN
tests/data/segfault.aif
Normal file
Binary file not shown.
BIN
tests/data/segfault.mpc
Normal file
BIN
tests/data/segfault.mpc
Normal file
Binary file not shown.
BIN
tests/data/segfault.oga
Normal file
BIN
tests/data/segfault.oga
Normal file
Binary file not shown.
BIN
tests/data/segfault.wav
Normal file
BIN
tests/data/segfault.wav
Normal file
Binary file not shown.
1
tests/data/segfault2.mpc
Normal file
1
tests/data/segfault2.mpc
Normal file
@ -0,0 +1 @@
|
||||
MPCKSH
|
BIN
tests/data/zerodiv.ape
Normal file
BIN
tests/data/zerodiv.ape
Normal file
Binary file not shown.
BIN
tests/data/zerodiv.mpc
Normal file
BIN
tests/data/zerodiv.mpc
Normal file
Binary file not shown.
@ -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());
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user