mirror of
https://github.com/taglib/taglib.git
synced 2026-09-23 01:46:59 -04:00
Merge branch 'master' into merge-master-to-taglib2
Conflicts: ConfigureChecks.cmake config.h.cmake taglib/CMakeLists.txt taglib/ape/apeproperties.cpp taglib/fileref.cpp taglib/mp4/mp4atom.cpp taglib/mpc/mpcproperties.cpp taglib/mpeg/id3v2/frames/chapterframe.cpp taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp taglib/mpeg/id3v2/id3v2frame.cpp taglib/mpeg/id3v2/id3v2framefactory.cpp taglib/mpeg/id3v2/id3v2tag.cpp taglib/mpeg/mpegfile.cpp taglib/mpeg/mpegfile.h taglib/riff/aiff/aifffile.cpp taglib/riff/aiff/aiffproperties.cpp taglib/riff/aiff/aiffproperties.h taglib/riff/wav/infotag.cpp taglib/riff/wav/wavfile.cpp taglib/riff/wav/wavproperties.cpp taglib/toolkit/tdebug.cpp taglib/toolkit/tstring.cpp taglib/toolkit/tutils.h tests/test_aiff.cpp tests/test_id3v2.cpp tests/test_wav.cpp
This commit is contained in:
@@ -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)
|
||||
|
||||
+9
-12
@@ -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));
|
||||
|
||||
+18
-26
@@ -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))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+8
-14
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-14
@@ -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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+44
-68
@@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
MPCKSH
|
||||
Binary file not shown.
Binary file not shown.
+39
-14
@@ -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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
+15
-1
@@ -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);
|
||||
|
||||
+36
-2
@@ -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);
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+29
-1
@@ -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);
|
||||
|
||||
+88
-3
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user