Merge branch 'master' into merge-master-to-taglib2

Conflicts:
	ConfigureChecks.cmake
	taglib/CMakeLists.txt
	taglib/riff/aiff/aiffproperties.cpp
	taglib/toolkit/tbytevector.cpp
	taglib/toolkit/tbytevector.h
	taglib/toolkit/tfilestream.cpp
	taglib/toolkit/tstring.cpp
	taglib/toolkit/tutils.h
	tests/test_apetag.cpp
	tests/test_bytevector.cpp
	tests/test_flac.cpp
	tests/test_id3v2.cpp
	tests/test_propertymap.cpp
This commit is contained in:
Tsuda Kageyu 2014-07-24 09:39:08 +09:00
commit f93397fa7b
32 changed files with 2030 additions and 581 deletions

View File

@ -6,6 +6,8 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
endif()
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
option(ENABLE_STATIC "Make static version of libtag" OFF)
if(ENABLE_STATIC)
add_definitions(-DTAGLIB_STATIC)
@ -85,6 +87,11 @@ if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
endif()
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
set(HAVE_ZLIB 1)
set(HAVE_ZLIB_SOURCE 1)
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)

View File

@ -6,8 +6,9 @@ include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXSourceCompiles)
include(TestBigEndian)
include(TestFloatFormat)
# Check if the size of integral types are suitable.
# Check if the size of numeric types are suitable.
check_type_size("short" SIZEOF_SHORT)
if(NOT ${SIZEOF_SHORT} EQUAL 2)
@ -29,6 +30,16 @@ if(${SIZEOF_WCHAR_T} LESS 2)
MESSAGE(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
check_type_size("float" SIZEOF_FLOAT)
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
MESSAGE(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
endif()
check_type_size("double" SIZEOF_DOUBLE)
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
MESSAGE(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Determine the CPU byte order.
test_big_endian(IS_BIG_ENDIAN)
@ -39,6 +50,17 @@ else()
set(SYSTEM_BYTEORDER 2)
endif()
# Check if the format of floating point types are suitable.
test_float_format(FP_IEEE754)
if(${FP_IEEE754} EQUAL 1)
set(FLOAT_BYTEORDER 1)
elseif(${FP_IEEE754} EQUAL 2)
set(FLOAT_BYTEORDER 2)
else()
MESSAGE(FATAL_ERROR "TagLib requires that floating point types are IEEE754 compliant.")
endif()
# Determine which kind of byte swap functions your compiler supports.
# GCC's __builtin_bswap* should be checked individually
@ -254,11 +276,13 @@ check_cxx_source_compiles("
# Determine whether zlib is installed.
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
endif()
# Determine whether CppUnit is installed.

View File

@ -72,6 +72,8 @@ Useful configuration options used with CMake (GUI and/or Command line):
Assumes parent of: \include and \lib.
ZLIB_INCLUDE_DIR= Where to find ZLib's Include directory.
ZLIB_LIBRARY= Where to find ZLib's Library.
ZLIB_SOURCE= Where to find ZLib's Source Code.
Alternative to ZLIB_INCLUDE_DIR and ZLIB_LIBRARY.
CMAKE_INSTALL_PREFIX= Where to install Taglib.
CMAKE_BUILD_TYPE= Release, Debug, etc ... (Not available in MSVC)

13
cmake/TestFloatFormat.c Normal file
View File

@ -0,0 +1,13 @@
int main()
{
double bin1[] = {
// "*TAGLIB*" encoded as a little-endian floating-point number
(double)3.9865557444897601e-105, (double)0.0
};
float bin2[] = {
// "*TL*" encoded as a little-endian floating-point number
(float)1.81480400e-013, (float)0.0
};
return 0;
}

View File

@ -0,0 +1,60 @@
# Returns 1 if IEEE754 little-endian, 2 if IEEE754 big-endian, otherwise 0.
MACRO(TEST_FLOAT_FORMAT FP_IEEE754)
IF(NOT FP_IEEE754)
TRY_COMPILE(HAVE_${FP_IEEE754} "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}/cmake/TestFloatFormat.c"
COPY_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin")
SET(FP_IEEE754 0)
IF(HAVE_${FP_IEEE754})
# dont match first/last letter because of string rounding errors :-)
FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
DOUBLE_IEEE754_LE LIMIT_COUNT 1 REGEX "TAGLIB")
FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
DOUBLE_IEEE754_BE LIMIT_COUNT 1 REGEX "BILGAT")
FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
FLOAT_IEEE754_LE LIMIT_COUNT 1 REGEX "TL")
FILE(STRINGS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestFloatFormat.bin"
FLOAT_IEEE754_BE LIMIT_COUNT 1 REGEX "LT")
IF(DOUBLE_IEEE754_LE AND FLOAT_IEEE754_LE)
SET(FP_IEEE754_LE 1)
ENDIF()
IF(DOUBLE_IEEE754_BE AND FLOAT_IEEE754_BE)
SET(FP_IEEE754_BE 1)
ENDIF()
# OS X Universal binaries will contain both strings, set it to the host
IF(FP_IEEE754_LE AND FP_IEEE754_BE)
IF(CMAKE_SYSTEM_PROCESSOR MATCHES powerpc)
SET(FP_IEEE754_LE FALSE)
SET(FP_IEEE754_BE TRUE)
ELSE()
SET(FP_IEEE754_LE TRUE)
SET(FP_IEEE754_BE FALSE)
ENDIF()
ENDIF()
IF(FP_IEEE754_LE)
SET(FP_IEEE754 1)
ELSEIF(FP_IEEE754_BE)
SET(FP_IEEE754 2)
ENDIF()
ENDIF()
# just some informational output for the user
IF(FP_IEEE754_LE)
MESSAGE(STATUS "Checking the floating point format - IEEE754 (LittleEndian)")
ELSEIF(FP_IEEE754_BE)
MESSAGE(STATUS "Checking the floating point format - IEEE754 (BigEndian)")
ELSE()
MESSAGE(STATUS "Checking the floating point format - Not IEEE754 or failed to detect.")
ENDIF()
SET(FP_IEEE754 "${${FP_IEEE754}}" CACHE INTERNAL "Result of TEST_FLOAT_FORMAT" FORCE)
ENDIF()
ENDMACRO(TEST_FLOAT_FORMAT FP_IEEE754)

View File

@ -1,9 +1,13 @@
/* config.h. Generated by cmake from config.h.cmake */
/* Indicates the byte order of your target system */
/* Integer byte order of your target system */
/* 1 if little-endian, 2 if big-endian. */
#cmakedefine SYSTEM_BYTEORDER ${SYSTEM_BYTEORDER}
/* IEEE754 byte order of your target system. */
/* 1 if little-endian, 2 if big-endian. */
#cmakedefine FLOAT_BYTEORDER ${FLOAT_BYTEORDER}
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP_16 1
#cmakedefine HAVE_GCC_BYTESWAP_32 1

View File

@ -31,6 +31,8 @@ include_directories(
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
elseif(HAVE_ZLIB_SOURCE)
include_directories(${ZLIB_SOURCE})
endif()
set(tag_HDRS
@ -82,6 +84,8 @@ set(tag_HDRS
mpeg/id3v2/frames/unknownframe.h
mpeg/id3v2/frames/unsynchronizedlyricsframe.h
mpeg/id3v2/frames/urllinkframe.h
mpeg/id3v2/frames/chapterframe.h
mpeg/id3v2/frames/tableofcontentsframe.h
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
@ -182,6 +186,8 @@ set(frames_SRCS
mpeg/id3v2/frames/unknownframe.cpp
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
mpeg/id3v2/frames/urllinkframe.cpp
mpeg/id3v2/frames/chapterframe.cpp
mpeg/id3v2/frames/tableofcontentsframe.cpp
)
set(ogg_SRCS
@ -300,6 +306,16 @@ set(dsf_SRCS
dsf/dsfproperties.cpp
)
set(ebml_SRCS
ebml/ebmlfile.cpp
ebml/ebmlelement.cpp
)
set(matroska_SRCS
ebml/matroska/ebmlmatroskafile.cpp
ebml/matroska/ebmlmatroskaaudio.cpp
)
set(toolkit_SRCS
toolkit/tstring.cpp
toolkit/tstringlist.cpp
@ -317,15 +333,17 @@ set(toolkit_SRCS
toolkit/unicode.cpp
)
set(ebml_SRCS
ebml/ebmlfile.cpp
ebml/ebmlelement.cpp
)
set(matroska_SRCS
ebml/matroska/ebmlmatroskafile.cpp
ebml/matroska/ebmlmatroskaaudio.cpp
)
if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c
${ZLIB_SOURCE}/crc32.c
${ZLIB_SOURCE}/inffast.c
${ZLIB_SOURCE}/inflate.c
${ZLIB_SOURCE}/inftrees.c
${ZLIB_SOURCE}/uncompr.c
${ZLIB_SOURCE}/zutil.c
)
endif()
set(tag_LIB_SRCS
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
@ -339,10 +357,10 @@ set(tag_LIB_SRCS
audioproperties.cpp
)
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
add_library(tag ${tag_LIB_SRCS} ${zlib_SRCS} ${tag_HDRS})
if(ZLIB_FOUND)
target_link_libraries(tag ${ZLIB_LIBRARIES})
target_link_libraries(tag ${ZLIB_LIBRARIES})
endif()
set_target_properties(tag PROPERTIES
@ -358,10 +376,9 @@ if(BUILD_FRAMEWORK)
endif()
install(TARGETS tag
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
)

View File

@ -89,35 +89,35 @@ String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String::null;
return d->itemListMap["TITLE"].toString();
return d->itemListMap["TITLE"].values().toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String::null;
return d->itemListMap["ARTIST"].toString();
return d->itemListMap["ARTIST"].values().toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String::null;
return d->itemListMap["ALBUM"].toString();
return d->itemListMap["ALBUM"].values().toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String::null;
return d->itemListMap["COMMENT"].toString();
return d->itemListMap["COMMENT"].values().toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String::null;
return d->itemListMap["GENRE"].toString();
return d->itemListMap["GENRE"].values().toString();
}
TagLib::uint APE::Tag::year() const

View File

@ -30,7 +30,7 @@ using namespace TagLib;
namespace TagLib {
namespace ID3v1 {
static const int genresSize = 148;
static const int genresSize = 192;
static const String genres[] = {
"Blues",
"Classic Rock",
@ -179,7 +179,51 @@ namespace TagLib {
"Thrash Metal",
"Anime",
"Jpop",
"Synthpop"
"Synthpop",
"Abstract",
"Art Rock",
"Baroque",
"Bhangra",
"Big Beat",
"Breakbeat",
"Chillout",
"Downtempo",
"Dub",
"EBM",
"Eclectic",
"Electro",
"Electroclash",
"Emo",
"Experimental",
"Garage",
"Global",
"IDM",
"Illbient",
"Industro-Goth",
"Jam Band",
"Krautrock",
"Leftfield",
"Lounge",
"Math Rock",
"New Romantic",
"Nu-Breakz",
"Post-Punk",
"Post-Rock",
"Psytrance",
"Shoegaze",
"Space Rock",
"Trop Rock",
"World Music",
"Neoclassical",
"Audiobook",
"Audio Theatre",
"Neue Deutsche Welle",
"Podcast",
"Indie Rock",
"G-Funk",
"Dubstep",
"Garage Rock",
"Psybient"
};
}
}

View File

@ -49,7 +49,7 @@ namespace TagLib {
/*!
* Returns the name of the genre at \a index in the ID3v1 genre list. If
* \a index is out of range -- less than zero or greater than 146 -- a null
* \a index is out of range -- less than zero or greater than 191 -- a null
* string will be returned.
*/
String TAGLIB_EXPORT genre(int index);

View File

@ -0,0 +1,266 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <stdio.h>
#include "chapterframe.h"
using namespace TagLib;
using namespace ID3v2;
class ChapterFrame::ChapterFramePrivate
{
public:
ByteVector elementID;
uint startTime;
uint endTime;
uint startOffset;
uint endOffset;
const FrameFactory *factory;
FrameListMap embeddedFrameListMap;
FrameList embeddedFrameList;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
ChapterFrame::ChapterFrame(const ByteVector &data) :
ID3v2::Frame(data)
{
d = new ChapterFramePrivate;
d->factory = FrameFactory::instance();
setData(data);
}
ChapterFrame::ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO, const FrameList &eF) :
ID3v2::Frame("CHAP")
{
d = new ChapterFramePrivate;
d->elementID = eID;
d->startTime = sT;
d->endTime = eT;
d->startOffset = sO;
d->endOffset = eO;
FrameList l = eF;
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
addEmbeddedFrame(*it);
d->factory = FrameFactory::instance();
}
ChapterFrame::~ChapterFrame()
{
delete d;
}
ByteVector ChapterFrame::elementID() const
{
return d->elementID;
}
uint ChapterFrame::startTime() const
{
return d->startTime;
}
uint ChapterFrame::endTime() const
{
return d->endTime;
}
uint ChapterFrame::startOffset() const
{
return d->startOffset;
}
uint ChapterFrame::endOffset() const
{
return d->endOffset;
}
void ChapterFrame::setElementID(const ByteVector &eID)
{
d->elementID = eID;
if(eID.at(eID.size() - 1) != char(0))
d->elementID.append(char(0));
}
void ChapterFrame::setStartTime(const uint &sT)
{
d->startTime = sT;
}
void ChapterFrame::setEndTime(const uint &eT)
{
d->endTime = eT;
}
void ChapterFrame::setStartOffset(const uint &sO)
{
d->startOffset = sO;
}
void ChapterFrame::setEndOffset(const uint &eO)
{
d->endOffset = eO;
}
const FrameListMap &ChapterFrame::embeddedFrameListMap() const
{
return d->embeddedFrameListMap;
}
const FrameList &ChapterFrame::embeddedFrameList() const
{
return d->embeddedFrameList;
}
const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const
{
return d->embeddedFrameListMap[frameID];
}
void ChapterFrame::addEmbeddedFrame(Frame *frame)
{
d->embeddedFrameList.append(frame);
d->embeddedFrameListMap[frame->frameID()].append(frame);
}
void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del)
{
// remove the frame from the frame list
FrameList::Iterator it = d->embeddedFrameList.find(frame);
d->embeddedFrameList.erase(it);
// ...and from the frame list map
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
d->embeddedFrameListMap[frame->frameID()].erase(it);
// ...and delete as desired
if(del)
delete frame;
}
void ChapterFrame::removeEmbeddedFrames(const ByteVector &id)
{
FrameList l = d->embeddedFrameListMap[id];
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
removeEmbeddedFrame(*it, true);
}
String ChapterFrame::toString() const
{
return String::null;
}
PropertyMap ChapterFrame::asProperties() const
{
PropertyMap map;
map.unsupportedData().append(frameID() + String("/") + d->elementID);
return map;
}
ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static
{
ID3v2::FrameList comments = tag->frameList("CHAP");
for(ID3v2::FrameList::ConstIterator it = comments.begin();
it != comments.end();
++it)
{
ChapterFrame *frame = dynamic_cast<ChapterFrame *>(*it);
if(frame && frame->elementID() == eID)
return frame;
}
return 0;
}
void ChapterFrame::parseFields(const ByteVector &data)
{
uint size = data.size();
if(size < 18) {
debug("A CHAP frame must contain at least 18 bytes (1 byte element ID terminated by null and 4x4 bytes for start and end time and offset).");
return;
}
size_t pos = 0, embPos = 0;
d->elementID = readStringField(data, String::Latin1, pos).data(String::Latin1);
d->elementID.append(char(0));
d->startTime = data.toUInt32BE(pos);
pos += 4;
d->endTime = data.toUInt32BE(pos);
pos += 4;
d->startOffset = data.toUInt32BE(pos);
pos += 4;
d->endOffset = data.toUInt32BE(pos);
pos += 4;
size -= pos;
while((uint)embPos < size - Frame::headerSize(4))
{
Frame *frame = d->factory->createFrame(data.mid(pos + embPos));
if(!frame)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
delete frame;
return;
}
embPos += frame->size() + Frame::headerSize(4);
addEmbeddedFrame(frame);
}
}
ByteVector ChapterFrame::renderFields() const
{
ByteVector data;
data.append(d->elementID);
data.append(ByteVector::fromUInt32BE(d->startTime));
data.append(ByteVector::fromUInt32BE(d->endTime));
data.append(ByteVector::fromUInt32BE(d->startOffset));
data.append(ByteVector::fromUInt32BE(d->endOffset));
FrameList l = d->embeddedFrameList;
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
data.append((*it)->render());
return data;
}
ChapterFrame::ChapterFrame(const ByteVector &data, Header *h) :
Frame(h)
{
d = new ChapterFramePrivate;
d->factory = FrameFactory::instance();
parseFields(fieldData(data));
}

View File

@ -0,0 +1,242 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_CHAPTERFRAME
#define TAGLIB_CHAPTERFRAME
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
/*!
* This is an implementation of ID3v2 chapter frames. The purpose of this
* frame is to describe a single chapter within an audio file.
*/
//! An implementation of ID3v2 chapter frames
class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame
{
friend class FrameFactory;
public:
/*!
* Creates a chapter frame based on \a data.
*/
ChapterFrame(const ByteVector &data);
/*!
* Creates a chapter frame with the element ID \a eID,
* start time \a sT, end time \a eT, start offset \a sO,
* end offset \a eO and embedded frames, that are in \a eF.
*/
ChapterFrame(const ByteVector &eID, const uint &sT, const uint &eT, const uint &sO, const uint &eO, const FrameList &eF);
/*!
* Destroys the frame.
*/
~ChapterFrame();
/*!
* Returns the element ID of the frame. Element ID
* is a null terminated string, however it's not human-readable.
*
* \see setElementID()
*/
ByteVector elementID() const;
/*!
* Returns time of chapter's start (in miliseconds).
*
* \see setStartTime()
*/
uint startTime() const;
/*!
* Returns time of chapter's end (in miliseconds).
*
* \see setEndTime()
*/
uint endTime() const;
/*!
* Returns zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's start.
*
* \note If returned value is 0xFFFFFFFF, start time should be used instead.
* \see setStartOffset()
*/
uint startOffset() const;
/*!
* Returns zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's end.
*
* \note If returned value is 0xFFFFFFFF, end time should be used instead.
* \see setEndOffset()
*/
uint endOffset() const;
/*!
* Sets the element ID of the frame to \a eID. If \a eID isn't
* null terminated, a null char is appended automatically.
*
* \see elementID()
*/
void setElementID(const ByteVector &eID);
/*!
* Sets time of chapter's start (in miliseconds) to \a sT.
*
* \see startTime()
*/
void setStartTime(const uint &sT);
/*!
* Sets time of chapter's end (in miliseconds) to \a eT.
*
* \see endTime()
*/
void setEndTime(const uint &eT);
/*!
* Sets zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's start to \a sO.
*
* \see startOffset()
*/
void setStartOffset(const uint &sO);
/*!
* Sets zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's end to \a eO.
*
* \see endOffset()
*/
void setEndOffset(const uint &eO);
/*!
* Returns a reference to the frame list map. This is an FrameListMap of
* all of the frames embedded in the CHAP frame.
*
* This is the most convenient structure for accessing the CHAP frame's
* embedded frames. Many frame types allow multiple instances of the same
* frame type so this is a map of lists. In most cases however there will
* only be a single frame of a certain type.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*
* \see embeddedFrameList()
*/
const FrameListMap &embeddedFrameListMap() const;
/*!
* Returns a reference to the embedded frame list. This is an FrameList
* of all of the frames embedded in the CHAP frame in the order that they
* were parsed.
*
* This can be useful if for example you want iterate over the CHAP frame's
* embedded frames in the order that they occur in the CHAP frame.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*/
const FrameList &embeddedFrameList() const;
/*!
* Returns the embedded frame list for frames with the id \a frameID
* or an empty list if there are no embedded frames of that type. This
* is just a convenience and is equivalent to:
*
* \code
* embeddedFrameListMap()[frameID];
* \endcode
*
* \see embeddedFrameListMap()
*/
const FrameList &embeddedFrameList(const ByteVector &frameID) const;
/*!
* Add an embedded frame to the CHAP frame. At this point the CHAP frame
* takes ownership of the embedded frame and will handle freeing its memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void addEmbeddedFrame(Frame *frame);
/*!
* Remove an embedded frame from the CHAP frame. If \a del is true the frame's
* memory will be freed; if it is false, it must be deleted by the user.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrame(Frame *frame, bool del = true);
/*!
* Remove all embedded frames of type \a id from the CHAP frame and free their
* memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrames(const ByteVector &id);
virtual String toString() const;
PropertyMap asProperties() const;
/*!
* CHAP frames each have a unique element ID. This searches for a CHAP
* frame with the element ID \a eID and returns a pointer to it. This
* can be used to link CTOC and CHAP frames together.
*
* \see elementID()
*/
static ChapterFrame *findByElementID(const Tag *tag, const ByteVector &eID);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
ChapterFrame(const ChapterFrame &);
ChapterFrame &operator=(const ChapterFrame &);
ChapterFrame(const ByteVector &data, Header *h);
class ChapterFramePrivate;
ChapterFramePrivate *d;
};
}
}
#endif

View File

@ -0,0 +1,293 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include "tableofcontentsframe.h"
using namespace TagLib;
using namespace ID3v2;
class TableOfContentsFrame::TableOfContentsFramePrivate
{
public:
ByteVector elementID;
bool isTopLevel;
bool isOrdered;
ByteVectorList childElements;
const FrameFactory *factory;
FrameListMap embeddedFrameListMap;
FrameList embeddedFrameList;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data) :
ID3v2::Frame(data)
{
d = new TableOfContentsFramePrivate;
d->factory = FrameFactory::instance();
setData(data);
}
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch, const FrameList &eF) :
ID3v2::Frame("CTOC")
{
d = new TableOfContentsFramePrivate;
d->elementID = eID;
d->childElements = ch;
FrameList l = eF;
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
addEmbeddedFrame(*it);
d->factory = FrameFactory::instance();
}
TableOfContentsFrame::~TableOfContentsFrame()
{
delete d;
}
ByteVector TableOfContentsFrame::elementID() const
{
return d->elementID;
}
bool TableOfContentsFrame::isTopLevel() const
{
return d->isTopLevel;
}
bool TableOfContentsFrame::isOrdered() const
{
return d->isOrdered;
}
uint TableOfContentsFrame::entryCount() const
{
return d->childElements.size();
}
ByteVectorList TableOfContentsFrame::childElements() const
{
return d->childElements;
}
void TableOfContentsFrame::setElementID(const ByteVector &eID)
{
d->elementID = eID;
if(eID.at(eID.size() - 1) != char(0))
d->elementID.append(char(0));
}
void TableOfContentsFrame::setIsTopLevel(const bool &t)
{
d->isTopLevel = t;
}
void TableOfContentsFrame::setIsOrdered(const bool &o)
{
d->isOrdered = o;
}
void TableOfContentsFrame::setChildElements(const ByteVectorList &l)
{
d->childElements = l;
}
void TableOfContentsFrame::addChildElement(const ByteVector &cE)
{
d->childElements.append(cE);
}
void TableOfContentsFrame::removeChildElement(const ByteVector &cE)
{
ByteVectorList::Iterator it = d->childElements.find(cE);
d->childElements.erase(it);
}
const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const
{
return d->embeddedFrameListMap;
}
const FrameList &TableOfContentsFrame::embeddedFrameList() const
{
return d->embeddedFrameList;
}
const FrameList &TableOfContentsFrame::embeddedFrameList(const ByteVector &frameID) const
{
return d->embeddedFrameListMap[frameID];
}
void TableOfContentsFrame::addEmbeddedFrame(Frame *frame)
{
d->embeddedFrameList.append(frame);
d->embeddedFrameListMap[frame->frameID()].append(frame);
}
void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del)
{
// remove the frame from the frame list
FrameList::Iterator it = d->embeddedFrameList.find(frame);
d->embeddedFrameList.erase(it);
// ...and from the frame list map
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
d->embeddedFrameListMap[frame->frameID()].erase(it);
// ...and delete as desired
if(del)
delete frame;
}
void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id)
{
FrameList l = d->embeddedFrameListMap[id];
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
removeEmbeddedFrame(*it, true);
}
String TableOfContentsFrame::toString() const
{
return String::null;
}
PropertyMap TableOfContentsFrame::asProperties() const
{
PropertyMap map;
map.unsupportedData().append(frameID() + String("/") + d->elementID);
return map;
}
TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static
{
ID3v2::FrameList tablesOfContents = tag->frameList("CTOC");
for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin();
it != tablesOfContents.end();
++it)
{
TableOfContentsFrame *frame = dynamic_cast<TableOfContentsFrame *>(*it);
if(frame && frame->elementID() == eID)
return frame;
}
return 0;
}
TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) // static
{
ID3v2::FrameList tablesOfContents = tag->frameList("CTOC");
for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin();
it != tablesOfContents.end();
++it)
{
TableOfContentsFrame *frame = dynamic_cast<TableOfContentsFrame *>(*it);
if(frame && frame->isTopLevel() == true)
return frame;
}
return 0;
}
void TableOfContentsFrame::parseFields(const ByteVector &data)
{
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 by null.");
return;
}
size_t pos = 0, embPos = 0;
d->elementID = readStringField(data, String::Latin1, pos).data(String::Latin1);
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++)
{
ByteVector childElementID = readStringField(data, String::Latin1, pos).data(String::Latin1);
childElementID.append(char(0));
d->childElements.append(childElementID);
}
size -= pos;
while((uint)embPos < size - Frame::headerSize(4))
{
Frame *frame = d->factory->createFrame(data.mid(pos + embPos));
if(!frame)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
delete frame;
return;
}
embPos += frame->size() + Frame::headerSize(4);
addEmbeddedFrame(frame);
}
}
ByteVector TableOfContentsFrame::renderFields() const
{
ByteVector data;
data.append(d->elementID);
char flags = 0;
if(d->isTopLevel)
flags += 2;
if(d->isOrdered)
flags += 1;
data.append(flags);
data.append((char)(entryCount()));
ByteVectorList::ConstIterator it = d->childElements.begin();
while(it != d->childElements.end()) {
data.append(*it);
it++;
}
FrameList l = d->embeddedFrameList;
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
data.append((*it)->render());
return data;
}
TableOfContentsFrame::TableOfContentsFrame(const ByteVector &data, Header *h) :
Frame(h)
{
d = new TableOfContentsFramePrivate;
d->factory = FrameFactory::instance();
parseFields(fieldData(data));
}

View File

@ -0,0 +1,255 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_TABLEOFCONTENTSFRAME
#define TAGLIB_TABLEOFCONTENTSFRAME
#include "id3v2tag.h"
#include "id3v2frame.h"
namespace TagLib {
namespace ID3v2 {
/*!
* This is an implementation of ID3v2 table of contents frames. Purpose
* of this frame is to allow a table of contents to be defined.
*/
//! An implementation of ID3v2 table of contents frames
class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame
{
friend class FrameFactory;
public:
/*!
* Creates a table of contents frame based on \a data.
*/
TableOfContentsFrame(const ByteVector &data);
/*!
* Creates a table of contents frame with the element ID \a eID,
* the child elements \a ch and embedded frames, that are in \a eF.
*/
TableOfContentsFrame(const ByteVector &eID, const ByteVectorList &ch, const FrameList &eF);
/*!
* Destroys the frame.
*/
~TableOfContentsFrame();
/*!
* Returns the elementID of the frame. Element ID
* is a null terminated string, however it's not human-readable.
*
* \see setElementID()
*/
ByteVector elementID() const;
/*!
* Returns true, if the frame is top-level (doen't have
* any parent CTOC frame).
*
* \see setIsTopLevel()
*/
bool isTopLevel() const;
/*!
* Returns true, if the child elements list entries
* are ordered.
*
* \see setIsOrdered()
*/
bool isOrdered() const;
/*!
* Returns count of child elements of the frame. It allways
* corresponds to size of child elements list.
*
* \see childElements()
*/
uint entryCount() const;
/*!
* Returns list of child elements of the frame.
*
* \see setChildElements()
*/
ByteVectorList childElements() const;
/*!
* Sets the elementID of the frame to \a eID. If \a eID isn't
* null terminated, a null char is appended automatically.
*
* \see elementID()
*/
void setElementID(const ByteVector &eID);
/*!
* Sets, if the frame is top-level (doen't have
* any parent CTOC frame).
*
* \see isTopLevel()
*/
void setIsTopLevel(const bool &t);
/*!
* Sets, if the child elements list entries
* are ordered.
*
* \see isOrdered()
*/
void setIsOrdered(const bool &o);
/*!
* Sets list of child elements of the frame to \a l.
*
* \see childElements()
*/
void setChildElements(const ByteVectorList &l);
/*!
* Adds \a cE to list of child elements of the frame.
*
* \see childElements()
*/
void addChildElement(const ByteVector &cE);
/*!
* Removes \a cE to list of child elements of the frame.
*
* \see childElements()
*/
void removeChildElement(const ByteVector &cE);
/*!
* Returns a reference to the frame list map. This is an FrameListMap of
* all of the frames embedded in the CTOC frame.
*
* This is the most convenient structure for accessing the CTOC frame's
* embedded frames. Many frame types allow multiple instances of the same
* frame type so this is a map of lists. In most cases however there will
* only be a single frame of a certain type.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*
* \see embeddedFrameList()
*/
const FrameListMap &embeddedFrameListMap() const;
/*!
* Returns a reference to the embedded frame list. This is an FrameList
* of all of the frames embedded in the CTOC frame in the order that they
* were parsed.
*
* This can be useful if for example you want iterate over the CTOC frame's
* embedded frames in the order that they occur in the CTOC frame.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*/
const FrameList &embeddedFrameList() const;
/*!
* Returns the embedded frame list for frames with the id \a frameID
* or an empty list if there are no embedded frames of that type. This
* is just a convenience and is equivalent to:
*
* \code
* embeddedFrameListMap()[frameID];
* \endcode
*
* \see embeddedFrameListMap()
*/
const FrameList &embeddedFrameList(const ByteVector &frameID) const;
/*!
* Add an embedded frame to the CTOC frame. At this point the CTOC frame
* takes ownership of the embedded frame and will handle freeing its memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void addEmbeddedFrame(Frame *frame);
/*!
* Remove an embedded frame from the CTOC frame. If \a del is true the frame's
* memory will be freed; if it is false, it must be deleted by the user.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrame(Frame *frame, bool del = true);
/*!
* Remove all embedded frames of type \a id from the CTOC frame and free their
* memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrames(const ByteVector &id);
virtual String toString() const;
PropertyMap asProperties() const;
/*!
* CTOC frames each have a unique element ID. This searches for a CTOC
* frame with the element ID \a eID and returns a pointer to it. This
* can be used to link together parent and child CTOC frames.
*
* \see elementID()
*/
static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID);
/*!
* CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't
* any frame, which contains this frame in its child elements list). Only a single frame
* within tag can be top-level. This searches for a top-level CTOC frame.
*
* \see isTopLevel()
*/
static TableOfContentsFrame *findTopLevel(const Tag *tag);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
TableOfContentsFrame(const TableOfContentsFrame &);
TableOfContentsFrame &operator=(const TableOfContentsFrame &);
TableOfContentsFrame(const ByteVector &data, Header *h);
class TableOfContentsFramePrivate;
TableOfContentsFramePrivate *d;
};
}
}
#endif

View File

@ -47,6 +47,8 @@
#include "frames/ownershipframe.h"
#include "frames/synchronizedlyricsframe.h"
#include "frames/eventtimingcodesframe.h"
#include "frames/chapterframe.h"
#include "frames/tableofcontentsframe.h"
using namespace TagLib;
using namespace ID3v2;
@ -274,6 +276,16 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
d->setTextEncoding(f);
return f;
}
// Chapter (ID3v2 chapters 1.0)
if(frameID == "CHAP")
return new ChapterFrame(data, header);
// Table of contents (ID3v2 chapters 1.0)
if(frameID == "CTOC")
return new TableOfContentsFrame(data, header);
return new UnknownFrame(data, header);
}

View File

@ -63,33 +63,33 @@ String Ogg::XiphComment::title() const
{
if(d->fieldListMap["TITLE"].isEmpty())
return String::null;
return d->fieldListMap["TITLE"].front();
return d->fieldListMap["TITLE"].toString();
}
String Ogg::XiphComment::artist() const
{
if(d->fieldListMap["ARTIST"].isEmpty())
return String::null;
return d->fieldListMap["ARTIST"].front();
return d->fieldListMap["ARTIST"].toString();
}
String Ogg::XiphComment::album() const
{
if(d->fieldListMap["ALBUM"].isEmpty())
return String::null;
return d->fieldListMap["ALBUM"].front();
return d->fieldListMap["ALBUM"].toString();
}
String Ogg::XiphComment::comment() const
{
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
d->commentField = "DESCRIPTION";
return d->fieldListMap["DESCRIPTION"].front();
return d->fieldListMap["DESCRIPTION"].toString();
}
if(!d->fieldListMap["COMMENT"].isEmpty()) {
d->commentField = "COMMENT";
return d->fieldListMap["COMMENT"].front();
return d->fieldListMap["COMMENT"].toString();
}
return String::null;
@ -99,7 +99,7 @@ String Ogg::XiphComment::genre() const
{
if(d->fieldListMap["GENRE"].isEmpty())
return String::null;
return d->fieldListMap["GENRE"].front();
return d->fieldListMap["GENRE"].toString();
}
TagLib::uint Ogg::XiphComment::year() const

View File

@ -102,7 +102,7 @@ static const uint crcTable[256] = {
};
/*!
* A templatized straightforward find that works with the types
* A templatized straightforward find that works with the types
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
*/
template <class TIterator>
@ -111,7 +111,7 @@ size_t findChar(
char c, size_t offset, size_t byteAlign)
{
const size_t dataSize = dataEnd - dataBegin;
if(dataSize == 0 || offset > dataSize - 1)
if(offset + 1 > dataSize)
return ByteVector::npos;
// n % 0 is invalid
@ -128,7 +128,7 @@ size_t findChar(
}
/*!
* A templatized KMP find that works with the types
* A templatized KMP find that works with the types
* std::vector<char>::iterator and std::vector<char>::reverse_iterator.
*/
template <class TIterator>
@ -139,7 +139,7 @@ size_t findVector(
{
const size_t dataSize = dataEnd - dataBegin;
const size_t patternSize = patternEnd - patternBegin;
if(patternSize > dataSize || offset > dataSize - 1)
if(patternSize == 0 || offset + patternSize > dataSize)
return ByteVector::npos;
// n % 0 is invalid
@ -195,7 +195,7 @@ inline T toNumber(const ByteVector &v, size_t offset)
{
static const bool swap = (ENDIAN != Utils::SystemByteOrder);
if(LENGTH >= sizeof(T) && offset + LENGTH <= v.size())
if(LENGTH >= sizeof(T) && offset + LENGTH <= v.size())
{
// Uses memcpy instead of reinterpret_cast to avoid an alignment exception.
T tmp;
@ -227,56 +227,125 @@ inline T toNumber(const ByteVector &v, size_t offset)
template <typename T, ByteOrder ENDIAN>
inline ByteVector fromNumber(T value)
{
static const bool swap = (ENDIAN != Utils::SystemByteOrder);
if(swap)
if (ENDIAN != Utils::SystemByteOrder)
value = Utils::byteSwap(value);
return ByteVector(reinterpret_cast<const char *>(&value), sizeof(T));
}
class ByteVector::ByteVectorPrivate
template <typename TFloat, typename TInt, ByteOrder ENDIAN>
TFloat toFloat(const ByteVector &v, size_t offset)
{
if (offset > v.size() - sizeof(TInt)) {
debug("toFloat() - offset is out of range. Returning 0.");
return 0.0;
}
union {
TInt i;
TFloat f;
} tmp;
::memcpy(&tmp, v.data() + offset, sizeof(TInt));
if(ENDIAN != Utils::FloatByteOrder)
tmp.i = Utils::byteSwap(tmp.i);
return tmp.f;
}
template <typename TFloat, typename TInt, ByteOrder ENDIAN>
ByteVector fromFloat(TFloat value)
{
union {
TInt i;
TFloat f;
} tmp;
tmp.f = value;
if(ENDIAN != Utils::FloatByteOrder)
tmp.i = Utils::byteSwap(tmp.i);
return ByteVector(reinterpret_cast<char *>(&tmp), sizeof(TInt));
}
template <ByteOrder ENDIAN>
long double toFloat80(const ByteVector &v, size_t offset)
{
if(offset > v.size() - 10) {
debug("toFloat80() - offset is out of range. Returning 0.");
return 0.0;
}
uchar bytes[10];
::memcpy(bytes, v.data() + offset, 10);
if(ENDIAN == LittleEndian) {
std::swap(bytes[0], bytes[9]);
std::swap(bytes[1], bytes[8]);
std::swap(bytes[2], bytes[7]);
std::swap(bytes[3], bytes[6]);
std::swap(bytes[4], bytes[5]);
}
// 1-bit sign
const bool negative = ((bytes[0] & 0x80) != 0);
// 15-bit exponent
const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1];
// 64-bit fraction. Leading 1 is explicit.
const ulonglong fraction
= (static_cast<ulonglong>(bytes[2]) << 56)
| (static_cast<ulonglong>(bytes[3]) << 48)
| (static_cast<ulonglong>(bytes[4]) << 40)
| (static_cast<ulonglong>(bytes[5]) << 32)
| (static_cast<ulonglong>(bytes[6]) << 24)
| (static_cast<ulonglong>(bytes[7]) << 16)
| (static_cast<ulonglong>(bytes[8]) << 8)
| (static_cast<ulonglong>(bytes[9]));
long double val;
if(exponent == 0 && fraction == 0)
val = 0;
else {
if(exponent == 0x7FFF) {
debug("toFloat80() - can't handle the infinity or NaN. Returning 0.");
return 0.0;
}
else
val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63);
}
if(negative)
return -val;
else
return val;
}
class ByteVector::ByteVectorPrivate
{
public:
ByteVectorPrivate()
: data(new std::vector<char>())
, offset(0)
, length(0)
{
}
ByteVectorPrivate() :
data(new std::vector<char>()),
offset(0),
length(0) {}
ByteVectorPrivate(ByteVectorPrivate* d, size_t o, size_t l)
: data(d->data)
, offset(d->offset + o)
, length(l)
{
}
ByteVectorPrivate(ByteVectorPrivate* d, size_t o, size_t l) :
data(d->data),
offset(d->offset + o),
length(l) {}
ByteVectorPrivate(size_t l, char c)
: data(new std::vector<char>(l, c))
, offset(0)
, length(l)
{
}
ByteVectorPrivate(size_t l, char c) :
data(new std::vector<char>(l, c)),
offset(0),
length(l) {}
ByteVectorPrivate(const char *s, size_t l)
: data(new std::vector<char>(s, s + l))
, offset(0)
, length(l)
{
}
void detach()
{
if(!data.unique()) {
data.reset(new std::vector<char>(data->begin() + offset, data->begin() + offset + length));
offset = 0;
}
}
ByteVectorPrivate(const char *s, size_t l) :
data(new std::vector<char>(s, s + l)),
offset(0),
length(l) {}
~ByteVectorPrivate()
{
}
~ByteVectorPrivate() {}
SHARED_PTR<std::vector<char> > data;
size_t offset;
@ -329,42 +398,62 @@ ByteVector ByteVector::fromUInt64BE(ulonglong value)
return fromNumber<ulonglong, BigEndian>(value);
}
ByteVector ByteVector::fromFloat32LE(float value)
{
return fromFloat<float, uint, LittleEndian>(value);
}
ByteVector ByteVector::fromFloat32BE(float value)
{
return fromFloat<float, uint, BigEndian>(value);
}
ByteVector ByteVector::fromFloat64LE(double value)
{
return fromFloat<double, ulonglong, LittleEndian>(value);
}
ByteVector ByteVector::fromFloat64BE(double value)
{
return fromFloat<double, ulonglong, BigEndian>(value);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ByteVector::ByteVector()
: d(new ByteVectorPrivate())
ByteVector::ByteVector() :
d(new ByteVectorPrivate())
{
}
ByteVector::ByteVector(size_t size, char value)
: d(new ByteVectorPrivate(size, value))
ByteVector::ByteVector(size_t size, char value) :
d(new ByteVectorPrivate(size, value))
{
}
ByteVector::ByteVector(const ByteVector &v)
: d(new ByteVectorPrivate(*v.d))
ByteVector::ByteVector(const ByteVector &v) :
d(new ByteVectorPrivate(*v.d))
{
}
ByteVector::ByteVector(const ByteVector &v, size_t offset, size_t length)
: d(new ByteVectorPrivate(v.d, offset, length))
ByteVector::ByteVector(const ByteVector &v, size_t offset, size_t length) :
d(new ByteVectorPrivate(v.d, offset, length))
{
}
ByteVector::ByteVector(char c)
: d(new ByteVectorPrivate(1, c))
ByteVector::ByteVector(char c) :
d(new ByteVectorPrivate(1, c))
{
}
ByteVector::ByteVector(const char *data, size_t length)
: d(new ByteVectorPrivate(data, length))
ByteVector::ByteVector(const char *data, size_t length) :
d(new ByteVectorPrivate(data, length))
{
}
ByteVector::ByteVector(const char *data)
: d(new ByteVectorPrivate(data, ::strlen(data)))
ByteVector::ByteVector(const char *data) :
d(new ByteVectorPrivate(data, ::strlen(data)))
{
}
@ -445,9 +534,9 @@ bool ByteVector::containsAt(
// do some sanity checking -- all of these things are needed for the search to be valid
const size_t compareLength = patternLength - patternOffset;
if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0)
if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0)
return false;
return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0);
}
@ -469,7 +558,7 @@ ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &wit
const size_t withSize = with.size();
const size_t patternSize = pattern.size();
const ptrdiff_t diff = withSize - patternSize;
size_t offset = 0;
while (true)
{
@ -481,16 +570,16 @@ ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &wit
if(diff < 0) {
::memmove(
data() + offset + withSize,
data() + offset + patternSize,
data() + offset + withSize,
data() + offset + patternSize,
size() - offset - patternSize);
resize(size() + diff);
}
else if(diff > 0) {
resize(size() + diff);
::memmove(
data() + offset + withSize,
data() + offset + patternSize,
data() + offset + withSize,
data() + offset + patternSize,
size() - diff - offset - patternSize);
}
@ -545,7 +634,7 @@ ByteVector &ByteVector::append(char c)
ByteVector &ByteVector::clear()
{
detach();
*d = *ByteVector::null.d;
*d = *ByteVector::null.d;
return *this;
}
@ -676,206 +765,36 @@ long long ByteVector::toInt64LE(size_t offset) const
long long ByteVector::toInt64BE(size_t offset) const
{
return static_cast<long long>(toNumber<ulonglong, 8, BigEndian>(*this, offset));
}
}
float ByteVector::toFloat32LE(size_t offset) const
{
return toFloat<float, uint, LittleEndian>(*this, offset);
}
float ByteVector::toFloat32BE(size_t offset) const
{
typedef std::numeric_limits<float> Limits;
return toFloat<float, uint, BigEndian>(*this, offset);
}
if(offset > size() - 4) {
debug("ByteVector::toFloat32BE() - offset is out of range. Returning 0.");
return 0.0;
}
if(Limits::is_iec559 && Limits::digits == 24) {
// float is 32-bit wide and IEEE754 compliant.
union {
uint i;
float f;
} tmp = {};
::memcpy(&tmp, data() + offset, 4);
if(Utils::SystemByteOrder == LittleEndian)
tmp.i = Utils::byteSwap(tmp.i);
return tmp.f;
}
else {
// float is not 32-bit or not IEEE754. Very unlikely in practice.
const uchar *bytes = reinterpret_cast<const uchar*>(data() + offset);
// 1-bit sign
const bool negative = ((bytes[0] & 0x80) != 0);
// 8-bit exponent
const int exponent = ((bytes[0] & 0x7F) << 1) | (bytes[1] >> 7);
// 24-bit fraction. Leading 1 is implied.
const uint fraction
= (1U << 23)
| (static_cast<uint>(bytes[1] & 0x7f) << 16)
| (static_cast<uint>(bytes[2]) << 8)
| (static_cast<uint>(bytes[3]));
float val;
if(exponent == 0 && fraction == 0)
val = 0;
else {
if(exponent == 0xFF) {
debug("ByteVector::toFloat32BE() - can't handle the infinity or NaN. Returning 0.");
return 0.0;
}
else
val = ::ldexp(static_cast<float>(fraction), exponent - 127 - 23);
}
if(negative)
return -val;
else
return val;
}
double ByteVector::toFloat64LE(size_t offset) const
{
return toFloat<double, ulonglong, LittleEndian>(*this, offset);
}
double ByteVector::toFloat64BE(size_t offset) const
{
typedef std::numeric_limits<double> Limits;
return toFloat<double, ulonglong, BigEndian>(*this, offset);
}
if(offset > size() - 8) {
debug("ByteVector::toFloat64BE() - offset is out of range. Returning 0.");
return 0.0;
}
if(Limits::is_iec559 && Limits::digits == 53) {
// double is 64-bit wide and IEEE754 compliant.
union {
ulonglong i;
double f;
} tmp = {};
::memcpy(&tmp, data() + offset, 8);
if(Utils::SystemByteOrder == LittleEndian)
tmp.i = Utils::byteSwap(tmp.i);
return tmp.f;
}
else {
// double is not 64-bit or not IEEE754. Very unlikely in practice.
const uchar *bytes = reinterpret_cast<const uchar*>(data() + offset);
// 1-bit sign
const bool negative = ((bytes[0] & 0x80) != 0);
// 11-bit exponent
const int exponent = ((bytes[0] & 0x7F) << 4) | (bytes[1] >> 4);
// 53-bit fraction. Leading 1 is implied.
const ulonglong fraction
= (1ULL << 52)
| (static_cast<ulonglong>(bytes[1] & 0x0F) << 48)
| (static_cast<ulonglong>(bytes[2]) << 40)
| (static_cast<ulonglong>(bytes[3]) << 32)
| (static_cast<ulonglong>(bytes[4]) << 24)
| (static_cast<ulonglong>(bytes[5]) << 16)
| (static_cast<ulonglong>(bytes[6]) << 8)
| (static_cast<ulonglong>(bytes[7]));
double val;
if(exponent == 0 && fraction == 0)
val = 0;
else {
if(exponent == 0x7FF) {
debug("ByteVector::toFloat64BE() - can't handle the infinity or NaN. Returning 0.");
return 0.0;
}
else
val = ::ldexp(static_cast<double>(fraction), exponent - 1023 - 52);
}
if(negative)
return -val;
else
return val;
}
long double ByteVector::toFloat80LE(size_t offset) const
{
return toFloat80<LittleEndian>(*this, offset);
}
long double ByteVector::toFloat80BE(size_t offset) const
{
typedef std::numeric_limits<long double> Limits;
if(offset > size() - 10) {
debug("ByteVector::toFloat80BE() - offset is out of range. Returning 0.");
return 0.0;
}
if(Limits::is_iec559 && Limits::digits == 64) {
// long double is 80-bit wide and IEEE754 compliant.
union {
uchar c[10];
long double f;
} tmp = {};
::memcpy(&tmp, data() + offset, 10);
if(Utils::SystemByteOrder == LittleEndian) {
std::swap(tmp.c[0], tmp.c[9]);
std::swap(tmp.c[1], tmp.c[8]);
std::swap(tmp.c[2], tmp.c[7]);
std::swap(tmp.c[3], tmp.c[6]);
std::swap(tmp.c[4], tmp.c[5]);
}
return tmp.f;
}
else {
// long double is not 80-bit or not IEEE754.
// GCC on ARM, MSVC, etc. will go this way.
const uchar *bytes = reinterpret_cast<const uchar*>(data() + offset);
// 1-bit sign
const bool negative = ((bytes[0] & 0x80) != 0);
// 15-bit exponent
const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1];
// 64-bit fraction. Leading 1 is explicit.
const ulonglong fraction
= (static_cast<ulonglong>(bytes[2]) << 56)
| (static_cast<ulonglong>(bytes[3]) << 48)
| (static_cast<ulonglong>(bytes[4]) << 40)
| (static_cast<ulonglong>(bytes[5]) << 32)
| (static_cast<ulonglong>(bytes[6]) << 24)
| (static_cast<ulonglong>(bytes[7]) << 16)
| (static_cast<ulonglong>(bytes[8]) << 8)
| (static_cast<ulonglong>(bytes[9]));
long double val;
if(exponent == 0 && fraction == 0)
val = 0;
else {
if(exponent == 0x7FFF) {
debug("ByteVector::toFloat80BE() - can't handle the infinity or NaN. Returning 0.");
return 0.0;
}
else
val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63);
}
if(negative)
return -val;
else
return val;
}
return toFloat80<BigEndian>(*this, offset);
}
const char &ByteVector::operator[](size_t index) const
@ -939,7 +858,6 @@ ByteVector ByteVector::operator+(const ByteVector &v) const
ByteVector &ByteVector::operator=(const ByteVector &v)
{
*d = *v.d;
return *this;
}
@ -975,7 +893,12 @@ ByteVector ByteVector::toHex() const
void ByteVector::detach()
{
d->detach();
if(!d->data.unique()) {
std::vector<char>::const_iterator begin = d->data->begin() + d->offset;
std::vector<char>::const_iterator end = begin + d->length;
d->data.reset(new std::vector<char>(begin, end));
d->offset = 0;
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -326,7 +326,7 @@ namespace TagLib {
* 24-bit big-endian integer.
*/
uint toUInt24BE(size_t offset) const;
/*!
* Converts the 4 bytes at \a offset of the vector to a uint as an unsigned
* 32-bit little-endian integer.
@ -334,7 +334,7 @@ namespace TagLib {
* \see fromUInt32LE()
*/
uint toUInt32LE(size_t offset) const;
/*!
* Converts the 4 bytes at \a offset of the vector to a ushort as an unsigned
* 32-bit big-endian integer.
@ -342,7 +342,7 @@ namespace TagLib {
* \see fromUInt32BE()
*/
uint toUInt32BE(size_t offset) const;
/*!
* Converts the 8 bytes at \a offset of the vector to a long long as a signed
* 64-bit little-endian integer.
@ -350,7 +350,7 @@ namespace TagLib {
* \see fromUInt64LE()
*/
long long toInt64LE(size_t offset) const;
/*!
* Converts the 8 bytes at \a offset of the vector to a long long as a signed
* 64-bit big-endian integer.
@ -359,12 +359,24 @@ namespace TagLib {
*/
long long toInt64BE(size_t offset) const;
/*
* Converts the 4 bytes at \a offset of the vector to a float as an IEEE754
* 32-bit little-endian floating point number.
*/
float toFloat32LE(size_t offset) const;
/*
* Converts the 4 bytes at \a offset of the vector to a float as an IEEE754
* 32-bit big-endian floating point number.
*/
float toFloat32BE(size_t offset) const;
/*
* Converts the 8 bytes at \a offset of the vector to a double as an IEEE754
* 64-bit little-endian floating point number.
*/
double toFloat64LE(size_t offset) const;
/*
* Converts the 8 bytes at \a offset of the vector to a double as an IEEE754
* 64-bit big-endian floating point number.
@ -372,8 +384,16 @@ namespace TagLib {
double toFloat64BE(size_t offset) const;
/*
* Converts the 10 bytes at \a offset of the vector to a long double as an IEEE754
* 80-bit big-endian floating point number.
* Converts the 10 bytes at \a offset of the vector to a long double as an
* IEEE754 80-bit little-endian floating point number.
*
* \note This may compromise the precision depends on the size of long double.
*/
long double toFloat80LE(size_t offset) const;
/*
* Converts the 10 bytes at \a offset of the vector to a long double as an
* IEEE754 80-bit big-endian floating point number.
*
* \note This may compromise the precision depends on the size of long double.
*/
@ -383,7 +403,7 @@ namespace TagLib {
* Creates a 2 byte ByteVector based on \a value as an unsigned 16-bit
* little-endian integer.
*
* \note If \a value is larger than 16-bit, the lowest 16 bits are used.
* \note If \a value is larger than 16-bit, the lowest 16 bits are used.
* \see toUInt16LE()
*/
static ByteVector fromUInt16LE(size_t value);
@ -392,7 +412,7 @@ namespace TagLib {
* Creates a 2 byte ByteVector based on \a value as an unsigned 16-bit
* big-endian integer.
*
* \note If \a value is larger than 16-bit, the lowest 16 bits are used.
* \note If \a value is larger than 16-bit, the lowest 16 bits are used.
* \see toUInt16BE()
*/
static ByteVector fromUInt16BE(size_t value);
@ -401,7 +421,7 @@ namespace TagLib {
* Creates a 4 byte ByteVector based on \a value as an unsigned 32-bit
* little-endian integer.
*
* \note If \a value is larger than 32-bit, the lowest 32 bits are used.
* \note If \a value is larger than 32-bit, the lowest 32 bits are used.
* \see toUInt32LE()
*/
static ByteVector fromUInt32LE(size_t value);
@ -410,7 +430,7 @@ namespace TagLib {
* Creates a 4 byte ByteVector based on \a value as an unsigned 32-bit
* big-endian integer.
*
* \note If \a value is larger than 32-bit, the lowest 32 bits are used.
* \note If \a value is larger than 32-bit, the lowest 32 bits are used.
* \see toUInt32BE()
*/
static ByteVector fromUInt32BE(size_t value);
@ -431,6 +451,38 @@ namespace TagLib {
*/
static ByteVector fromUInt64BE(ulonglong value);
/*!
* Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit
* little-endian floating point number.
*
* \see fromFloat32BE()
*/
static ByteVector fromFloat32LE(float value);
/*!
* Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit
* big-endian floating point number.
*
* \see fromFloat32LE()
*/
static ByteVector fromFloat32BE(float value);
/*!
* Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit
* little-endian floating point number.
*
* \see fromFloat64BE()
*/
static ByteVector fromFloat64LE(double value);
/*!
* Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit
* big-endian floating point number.
*
* \see fromFloat64LE()
*/
static ByteVector fromFloat64BE(double value);
/*!
* Returns a ByteVector based on the CString \a s.
*/
@ -507,7 +559,7 @@ namespace TagLib {
static const ByteVector null;
/*!
* When used as the value for a \a length or \a patternLength parameter
* When used as the value for a \a length or \a patternLength parameter
* in ByteVector's member functions, means "until the end of the data".
* As a return value, it is usually used to indicate no matches.
*/

View File

@ -36,7 +36,7 @@
using namespace TagLib;
namespace
namespace
{
#ifdef _WIN32
@ -79,7 +79,7 @@ namespace
DWORD length;
if(WriteFile(file, buffer.data(), static_cast<DWORD>(buffer.size()), &length, NULL))
return static_cast<size_t>(length);
else
else
return 0;
}
@ -122,12 +122,10 @@ namespace
class FileStream::FileStreamPrivate
{
public:
FileStreamPrivate(const FileName &fileName)
: file(InvalidFileHandle)
, name(fileName)
, readOnly(true)
{
}
FileStreamPrivate(const FileName &fileName) :
file(InvalidFileHandle),
name(fileName),
readOnly(true) {}
FileHandle file;
FileNameHandle name;
@ -138,8 +136,8 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FileStream::FileStream(FileName fileName, bool openReadOnly)
: d(new FileStreamPrivate(fileName))
FileStream::FileStream(FileName fileName, bool openReadOnly) :
d(new FileStreamPrivate(fileName))
{
// First try with read / write mode, if that fails, fall back to read only.
@ -151,13 +149,13 @@ FileStream::FileStream(FileName fileName, bool openReadOnly)
else
d->file = openFile(fileName, true);
if(d->file == InvalidFileHandle)
if(d->file == InvalidFileHandle)
{
# ifdef _WIN32
debug("Could not open file " + fileName.toString());
# else
debug("Could not open file " + String(static_cast<const char *>(d->name)));
# endif
# endif
}
}
@ -192,7 +190,7 @@ ByteVector FileStream::readBlock(size_t length)
const size_t count = readFile(d->file, buffer);
buffer.resize(count);
return buffer;
}
@ -262,7 +260,7 @@ void FileStream::insert(const ByteVector &data, offset_t start, size_t replace)
{
// Seek to the current read position and read the data that we're about
// to overwrite. Appropriately increment the readPosition.
seek(readPosition);
const size_t bytesRead = readFile(d->file, aboutToOverwrite);
aboutToOverwrite.resize(bytesRead);
@ -288,7 +286,7 @@ void FileStream::insert(const ByteVector &data, offset_t start, size_t replace)
writePosition += buffer.size();
// Make the current buffer the data that we read in the beginning.
buffer = aboutToOverwrite;
}
}
@ -368,7 +366,12 @@ void FileStream::seek(offset_t offset, Position p)
LARGE_INTEGER liOffset;
liOffset.QuadPart = offset;
SetLastError(NO_ERROR);
SetFilePointer(d->file, liOffset.LowPart, &liOffset.HighPart, whence);
if(GetLastError() == ERROR_NEGATIVE_SEEK) {
SetLastError(NO_ERROR);
SetFilePointer(d->file, 0, NULL, FILE_BEGIN);
}
if(GetLastError() != NO_ERROR) {
debug("File::seek() -- Failed to set the file pointer.");
}
@ -399,7 +402,7 @@ void FileStream::seek(offset_t offset, Position p)
fseek(d->file, static_cast<long>(offset), whence);
# endif
# endif
#endif
}
@ -424,6 +427,7 @@ offset_t FileStream::tell() const
LARGE_INTEGER position;
position.QuadPart = 0;
SetLastError(NO_ERROR);
position.LowPart = SetFilePointer(
d->file, position.LowPart, &position.HighPart, FILE_CURRENT);
if(GetLastError() == NO_ERROR) {
@ -444,7 +448,7 @@ offset_t FileStream::tell() const
return static_cast<offset_t>(ftell(d->file));
# endif
# endif
#endif
}
@ -461,6 +465,7 @@ offset_t FileStream::length()
LARGE_INTEGER fileSize;
fileSize.QuadPart = 0;
SetLastError(NO_ERROR);
fileSize.LowPart = GetFileSize(d->file, reinterpret_cast<LPDWORD>(&fileSize.HighPart));
if(GetLastError() == NO_ERROR) {
return fileSize.QuadPart;
@ -500,6 +505,8 @@ void FileStream::truncate(offset_t length)
const offset_t currentPos = tell();
seek(length);
SetLastError(NO_ERROR);
SetEndOfFile(d->file);
if(GetLastError() != NO_ERROR) {
debug("File::truncate() -- Failed to truncate the file.");

View File

@ -34,10 +34,10 @@ using namespace TagLib;
# include "tsmartptr.h"
# include <windows.h>
namespace
namespace
{
// Check if the running system has CreateFileW() function.
// Windows9x systems don't have CreateFileW() or can't accept Unicode file names.
// Windows9x systems don't have CreateFileW() or can't accept Unicode file names.
bool supportsUnicode()
{
@ -50,11 +50,11 @@ namespace
}
// Indicates whether the system supports Unicode file names.
const bool SystemSupportsUnicode = supportsUnicode();
const bool SystemSupportsUnicode = supportsUnicode();
// Converts a UTF-16 string into a local encoding.
// This function should only be used in Windows9x systems which don't support
// This function should only be used in Windows9x systems which don't support
// Unicode file names.
std::string unicodeToAnsi(const wchar_t *wstr)
@ -83,21 +83,19 @@ namespace
class FileName::FileNamePrivate
{
public:
FileNamePrivate()
: data(new FileNameData())
{
}
FileNamePrivate() :
data(new FileNameData()) {}
SHARED_PTR<FileNameData> data;
};
FileName::FileName()
: d(new FileNamePrivate())
FileName::FileName() :
d(new FileNamePrivate())
{
}
FileName::FileName(const wchar_t *name)
: d(new FileNamePrivate())
FileName::FileName(const wchar_t *name) :
d(new FileNamePrivate())
{
// If WinNT, stores a Unicode string into wname directly.
// If Win9x, converts and stores it into name to avoid calling Unicode version functions.
@ -108,14 +106,14 @@ FileName::FileName(const wchar_t *name)
d->data->name = unicodeToAnsi(name);
}
FileName::FileName(const char *name)
: d(new FileNamePrivate())
FileName::FileName(const char *name) :
d(new FileNamePrivate())
{
d->data->name = name;
}
FileName::FileName(const FileName &name)
: d(new FileNamePrivate())
FileName::FileName(const FileName &name) :
d(new FileNamePrivate())
{
*d = *name.d;
}
@ -131,21 +129,21 @@ FileName &FileName::operator=(const FileName &name)
return *this;
}
const std::wstring &FileName::wstr() const
{
return d->data->wname;
const std::wstring &FileName::wstr() const
{
return d->data->wname;
}
const std::string &FileName::str() const
{
const std::string &FileName::str() const
{
return d->data->name;
}
}
String FileName::toString() const
{
if(!d->data->wname.empty()) {
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);
if(len == 0)

View File

@ -48,11 +48,6 @@
namespace
{
inline unsigned short combine(unsigned char c1, unsigned char c2)
{
return (c1 << 8) | c2;
}
void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength)
{
#ifdef HAVE_STD_CODECVT
@ -151,10 +146,8 @@ namespace TagLib {
class String::StringPrivate
{
public:
StringPrivate()
: data(new std::wstring())
{
}
StringPrivate() :
data(new std::wstring()) {}
/*!
* Stores string in UTF-16. The byte order depends on the CPU endian.
@ -174,18 +167,18 @@ const size_t String::npos = std::wstring::npos;
////////////////////////////////////////////////////////////////////////////////
String::String()
: d(new StringPrivate())
String::String() :
d(new StringPrivate())
{
}
String::String(const String &s)
: d(new StringPrivate(*s.d))
String::String(const String &s) :
d(new StringPrivate(*s.d))
{
}
String::String(const std::string &s, Type t)
: d(new StringPrivate())
String::String(const std::string &s, Type t) :
d(new StringPrivate())
{
if(t == Latin1)
copyFromLatin1(s.c_str(), s.length());
@ -196,8 +189,8 @@ String::String(const std::string &s, Type t)
}
}
String::String(const std::wstring &s, Type t)
: d(new StringPrivate())
String::String(const std::wstring &s, Type t) :
d(new StringPrivate())
{
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(s.c_str(), s.length(), t);
@ -206,8 +199,8 @@ String::String(const std::wstring &s, Type t)
}
}
String::String(const wchar_t *s, Type t)
: d(new StringPrivate())
String::String(const wchar_t *s, Type t) :
d(new StringPrivate())
{
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(s, ::wcslen(s), t);
@ -216,8 +209,8 @@ String::String(const wchar_t *s, Type t)
}
}
String::String(const char *s, Type t)
: d(new StringPrivate())
String::String(const char *s, Type t) :
d(new StringPrivate())
{
if(t == Latin1)
copyFromLatin1(s, ::strlen(s));
@ -228,8 +221,8 @@ String::String(const char *s, Type t)
}
}
String::String(wchar_t c, Type t)
: d(new StringPrivate())
String::String(wchar_t c, Type t) :
d(new StringPrivate())
{
if(t == UTF16 || t == UTF16BE || t == UTF16LE)
copyFromUTF16(&c, 1, t);
@ -238,8 +231,8 @@ String::String(wchar_t c, Type t)
}
}
String::String(char c, Type t)
: d(new StringPrivate())
String::String(char c, Type t) :
d(new StringPrivate())
{
if(t == Latin1)
copyFromLatin1(&c, 1);
@ -250,8 +243,8 @@ String::String(char c, Type t)
}
}
String::String(const ByteVector &v, Type t)
: d(new StringPrivate())
String::String(const ByteVector &v, Type t) :
d(new StringPrivate())
{
if(v.isEmpty())
return;
@ -746,7 +739,12 @@ void String::copyFromUTF16(const char *s, size_t length, Type t)
d->data->resize(length / 2);
for(size_t i = 0; i < length / 2; ++i) {
(*d->data)[i] = swap ? combine(*s, *(s + 1)) : combine(*(s + 1), *s);
ushort c;
::memcpy(&c, s, 2);
if(swap)
c = Utils::byteSwap(c);
(*d->data)[i] = static_cast<wchar_t>(c);
s += 2;
}
}

View File

@ -47,6 +47,7 @@
#include "tstring.h"
#include <cstdio>
#include <cstdarg>
#include <cstring>
namespace TagLib
{
@ -148,13 +149,13 @@ namespace TagLib
#endif
}
inline String formatString(const char *format, ...)
{
// Sufficient buffer size for the current internal uses.
// Consider changing this value when you use this function.
static const size_t BufferSize = 128;
static const size_t BufferSize = 128;
va_list args;
va_start(args, format);
@ -194,11 +195,11 @@ namespace TagLib
# if SYSTEM_BYTEORDER == 1
const ByteOrder SystemByteOrder = LittleEndian;
const ByteOrder SystemByteOrder = LittleEndian;
# else
const ByteOrder SystemByteOrder = BigEndian;
const ByteOrder SystemByteOrder = BigEndian;
# endif
@ -217,8 +218,40 @@ namespace TagLib
else
return BigEndian;
}
const ByteOrder SystemByteOrder = systemByteOrder();
const ByteOrder SystemByteOrder = systemByteOrder();
#endif
#ifdef FLOAT_BYTEORDER
# if FLOAT_BYTEORDER == 1
const ByteOrder FloatByteOrder = LittleEndian;
# else
const ByteOrder FloatByteOrder = BigEndian;
# endif
#else
inline ByteOrder floatByteOrder()
{
double bin[] = {
// "*TAGLIB*" encoded as a little-endian floating-point number
(double) 3.9865557444897601e-105, (double) 0.0
};
char *str = (char*)&bin[0];
if(strncmp(&str[1], "TAGLIB", 6) == 0)
return LittleEndian;
else
return BigEndian;
}
const ByteOrder FloatByteOrder = floatByteOrder();
#endif
}

View File

@ -51,8 +51,8 @@ public:
dict["TRACKNUMBER"].append("17");
tag.setProperties(dict);
CPPUNIT_ASSERT_EQUAL(String("17"), tag.itemListMap()["TRACK"].values()[0]);
CPPUNIT_ASSERT_EQUAL((size_t)2u, tag.itemListMap()["ARTIST"].values().size());
CPPUNIT_ASSERT_EQUAL(String("artist 1"), tag.artist());
CPPUNIT_ASSERT_EQUAL((size_t)2, tag.itemListMap()["ARTIST"].values().size());
CPPUNIT_ASSERT_EQUAL(String("artist 1 artist 2"), tag.artist());
CPPUNIT_ASSERT_EQUAL(17u, tag.track());
}
@ -98,19 +98,19 @@ public:
CPPUNIT_ASSERT(unsuccessful.contains("A"));
CPPUNIT_ASSERT(unsuccessful.contains("MP+"));
}
void testTextBinary()
{
APE::Item item = APE::Item("DUMMY", "Test Text");
CPPUNIT_ASSERT_EQUAL(String("Test Text"), item.toString());
CPPUNIT_ASSERT_EQUAL(ByteVector::null, item.binaryData());
ByteVector data("Test Data");
item.setBinaryData(data);
CPPUNIT_ASSERT(item.values().isEmpty());
CPPUNIT_ASSERT_EQUAL(String::null, item.toString());
CPPUNIT_ASSERT_EQUAL(data, item.binaryData());
item.setValue("Test Text 2");
CPPUNIT_ASSERT_EQUAL(String("Test Text 2"), item.toString());
CPPUNIT_ASSERT_EQUAL(ByteVector::null, item.binaryData());

View File

@ -22,6 +22,7 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cstring>
#include <tbytevector.h>
#include <tbytevectorlist.h>
#include <cppunit/extensions/HelperMacros.h>
@ -79,7 +80,7 @@ public:
CPPUNIT_ASSERT(i.containsAt(j, 6, 1));
CPPUNIT_ASSERT(i.containsAt(j, 6, 1, 3));
}
void testFind1()
{
CPPUNIT_ASSERT_EQUAL((size_t)4, ByteVector("....SggO."). find("SggO"));
@ -92,6 +93,12 @@ public:
CPPUNIT_ASSERT_EQUAL(ByteVector::npos, ByteVector("....SggO."). find("SggO", 6));
CPPUNIT_ASSERT_EQUAL(ByteVector::npos, ByteVector("....SggO."). find("SggO", 7));
CPPUNIT_ASSERT_EQUAL(ByteVector::npos, ByteVector("....SggO."). find("SggO", 8));
// Intentional out-of-bounds access.
ByteVector v("0123456789x");
v.resize(10);
v.data()[10] = 'x';
CPPUNIT_ASSERT_EQUAL(ByteVector::npos, v.find("789x", 7));
}
void testFind2()
@ -147,6 +154,7 @@ public:
void testNumericCoversion()
{
// n = { 0x00, 0x88, 0x11, 0x99, ..., 0x77, 0xFF }
ByteVector n(16, 0);
for(size_t i = 0; i < 8; ++i) {
n[i * 2 ] = static_cast<unsigned char>(0x11 * i);
@ -167,7 +175,7 @@ public:
CPPUNIT_ASSERT(n.toInt64BE(3) == -7412174897536512939ll);
CPPUNIT_ASSERT(n.toInt64LE(6) == -1268082884489200845ll);
CPPUNIT_ASSERT(n.toInt64BE(4) == 2497865822736504285ll);
CPPUNIT_ASSERT(ByteVector::fromUInt16LE(n.toInt16LE(5)) == n.mid(5, 2));
CPPUNIT_ASSERT(ByteVector::fromUInt16BE(n.toInt16BE(9)) == n.mid(9, 2));
CPPUNIT_ASSERT(ByteVector::fromUInt32LE(n.toUInt32LE(4)) == n.mid(4, 4));
@ -179,18 +187,42 @@ public:
CPPUNIT_ASSERT(ByteVector::fromUInt32LE(287454020) == ByteVector::fromUInt32BE(1144201745));
CPPUNIT_ASSERT(ByteVector::fromUInt64LE(1234605615291183940) == ByteVector::fromUInt64BE(4914309075945333265));
const uchar PI32[] = { 0x00, 0x40, 0x49, 0x0f, 0xdb };
const uchar PI64[] = { 0x00, 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18 };
const uchar PI80[] = { 0x00, 0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc0, 0x00 };
const uchar PI32LE[] = { 0x00, 0xdb, 0x0f, 0x49, 0x40 };
const uchar PI32BE[] = { 0x00, 0x40, 0x49, 0x0f, 0xdb };
const uchar PI64LE[] = { 0x00, 0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40 };
const uchar PI64BE[] = { 0x00, 0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18 };
const uchar PI80LE[] = { 0x00, 0x00, 0xc0, 0x68, 0x21, 0xa2, 0xda, 0x0f, 0xc9, 0x00, 0x40 };
const uchar PI80BE[] = { 0x00, 0x40, 0x00, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc0, 0x00 };
ByteVector pi32(reinterpret_cast<const char*>(PI32), 5);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi32.toFloat32BE(1) * 10000));
ByteVector pi32le(reinterpret_cast<const char*>(PI32LE), 5);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi32le.toFloat32LE(1) * 10000));
ByteVector pi64(reinterpret_cast<const char*>(PI64), 9);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi64.toFloat64BE(1) * 10000));
ByteVector pi32be(reinterpret_cast<const char*>(PI32BE), 5);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi32be.toFloat32BE(1) * 10000));
ByteVector pi80(reinterpret_cast<const char*>(PI80), 11);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi80.toFloat80BE(1) * 10000));
ByteVector pi64le(reinterpret_cast<const char*>(PI64LE), 9);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi64le.toFloat64LE(1) * 10000));
ByteVector pi64be(reinterpret_cast<const char*>(PI64BE), 9);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi64be.toFloat64BE(1) * 10000));
ByteVector pi80le(reinterpret_cast<const char*>(PI80LE), 11);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi80le.toFloat80LE(1) * 10000));
ByteVector pi80be(reinterpret_cast<const char*>(PI80BE), 11);
CPPUNIT_ASSERT_EQUAL(31415, static_cast<int>(pi80be.toFloat80BE(1) * 10000));
ByteVector pi32le2 = ByteVector::fromFloat32LE(pi32le.toFloat32LE(1));
CPPUNIT_ASSERT(memcmp(pi32le.data() + 1, pi32le2.data(), 4) == 0);
ByteVector pi32be2 = ByteVector::fromFloat32BE(pi32be.toFloat32BE(1));
CPPUNIT_ASSERT(memcmp(pi32be.data() + 1, pi32be2.data(), 4) == 0);
ByteVector pi64le2 = ByteVector::fromFloat64LE(pi64le.toFloat64LE(1));
CPPUNIT_ASSERT(memcmp(pi64le.data() + 1, pi64le2.data(), 8) == 0);
ByteVector pi64be2 = ByteVector::fromFloat64BE(pi64be.toFloat64BE(1));
CPPUNIT_ASSERT(memcmp(pi64be.data() + 1, pi64be2.data(), 8) == 0);
}
void testReplace()

View File

@ -91,6 +91,7 @@ public:
newpic->setData("JPEG data");
f->addPicture(newpic);
f->save();
delete f;
f = new FLAC::File(newname.c_str());
lst = f->pictureList();
@ -115,6 +116,7 @@ public:
CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType());
CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description());
CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data());
delete f;
}
void testReplacePicture()
@ -138,6 +140,7 @@ public:
f->removePictures();
f->addPicture(newpic);
f->save();
delete f;
f = new FLAC::File(newname.c_str());
lst = f->pictureList();
@ -152,6 +155,7 @@ public:
CPPUNIT_ASSERT_EQUAL(String("image/jpeg"), pic->mimeType());
CPPUNIT_ASSERT_EQUAL(String("new image"), pic->description());
CPPUNIT_ASSERT_EQUAL(ByteVector("JPEG data"), pic->data());
delete f;
}
void testRemoveAllPictures()
@ -165,10 +169,12 @@ public:
f->removePictures();
f->save();
delete f;
f = new FLAC::File(newname.c_str());
lst = f->pictureList();
CPPUNIT_ASSERT_EQUAL(size_t(0), lst.size());
delete f;
}
void testRepeatedSave()
@ -185,10 +191,12 @@ public:
tag->setTitle("NEW TITLE 2");
f->save();
CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), tag->title());
delete f;
f = new FLAC::File(newname.c_str());
tag = f->tag();
CPPUNIT_ASSERT_EQUAL(String("NEW TITLE 2"), tag->title());
delete f;
}
void testSaveMultipleValues()
@ -209,6 +217,7 @@ public:
CPPUNIT_ASSERT_EQUAL(size_t(2), m["ARTIST"].size());
CPPUNIT_ASSERT_EQUAL(String("artist 1"), m["ARTIST"][0]);
CPPUNIT_ASSERT_EQUAL(String("artist 2"), m["ARTIST"][1]);
delete f;
}
void testDict()
@ -230,13 +239,14 @@ public:
CPPUNIT_ASSERT_EQUAL(size_t(2), dict["ARTIST"].size());
CPPUNIT_ASSERT_EQUAL(String("artøst 1"), dict["ARTIST"][0]);
CPPUNIT_ASSERT_EQUAL(String("artöst 2"), dict["ARTIST"][1]);
delete f;
}
void testInvalid()
{
ScopedFileCopy copy("silence-44-s", ".flac");
PropertyMap map;
map["H\xc4\xd6"] = String("bla");
map[L"H\x00c4\x00d6"] = String("bla");
FLAC::File f(copy.fileName().c_str());
PropertyMap invalid = f.setProperties(map);
CPPUNIT_ASSERT_EQUAL(size_t(1), invalid.size());

View File

@ -4,12 +4,9 @@
#include <string>
#include <stdio.h>
// so evil :(
#define protected public
#include <id3v2tag.h>
#include <mpegfile.h>
#include <id3v2frame.h>
#undef protected
#include <uniquefileidentifierframe.h>
#include <textidentificationframe.h>
#include <attachedpictureframe.h>
@ -22,6 +19,8 @@
#include <urllinkframe.h>
#include <ownershipframe.h>
#include <unknownframe.h>
#include <chapterframe.h>
#include <tableofcontentsframe.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <cppunit/extensions/HelperMacros.h>
@ -36,8 +35,8 @@ class PublicFrame : public ID3v2::Frame
PublicFrame() : ID3v2::Frame(ByteVector("XXXX\0\0\0\0\0\0", 10)) {}
String readStringField(const ByteVector &data, String::Type encoding)
{
size_t position = 0;
return ID3v2::Frame::readStringField(data, encoding, position);
size_t position = 0;
return ID3v2::Frame::readStringField(data, encoding, position);
}
virtual String toString() const { return String::null; }
virtual void parseFields(const ByteVector &) {}
@ -90,6 +89,10 @@ class TestID3v2 : public CppUnit::TestFixture
CPPUNIT_TEST(testPropertyInterface2);
CPPUNIT_TEST(testDeleteFrame);
CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2);
CPPUNIT_TEST(testParseChapterFrame);
CPPUNIT_TEST(testRenderChapterFrame);
CPPUNIT_TEST(testParseTableOfContentsFrame);
CPPUNIT_TEST(testRenderTableOfContentsFrame);
CPPUNIT_TEST_SUITE_END();
public:
@ -103,13 +106,23 @@ public:
void testDowngradeUTF8ForID3v23()
{
ID3v2::TextIdentificationFrame f(ByteVector("TPE1"), String::UTF8);
ScopedFileCopy copy("xing", ".mp3");
string newname = copy.fileName();
ID3v2::TextIdentificationFrame *f
= new ID3v2::TextIdentificationFrame(ByteVector("TPE1"), String::UTF8);
StringList sl;
sl.append("Foo");
f.setText(sl);
f.header()->setVersion(3);
ByteVector data = f.render();
f->setText(sl);
MPEG::File file(newname.c_str());
file.ID3v2Tag(true)->addFrame(f);
file.save(MPEG::File::ID3v2, true, 3);
CPPUNIT_ASSERT_EQUAL(true, file.hasID3v2Tag());
ByteVector data = f->render();
CPPUNIT_ASSERT_EQUAL((size_t)(4+4+2+1+6+2), data.size());
ID3v2::TextIdentificationFrame f2(data);
CPPUNIT_ASSERT_EQUAL(sl, f2.fieldList());
CPPUNIT_ASSERT_EQUAL(String::UTF16, f2.textEncoding());
@ -295,13 +308,16 @@ public:
f->setRating(200);
f->setCounter(3);
MPEG::File foo(newname.c_str());
foo.ID3v2Tag()->addFrame(f);
foo.save();
MPEG::File bar(newname.c_str());
CPPUNIT_ASSERT_EQUAL(String("email@example.com"), dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->email());
CPPUNIT_ASSERT_EQUAL(200, dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->rating());
{
MPEG::File foo(newname.c_str());
foo.ID3v2Tag()->addFrame(f);
foo.save();
}
{
MPEG::File bar(newname.c_str());
CPPUNIT_ASSERT_EQUAL(String("email@example.com"), dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->email());
CPPUNIT_ASSERT_EQUAL(200, dynamic_cast<ID3v2::PopularimeterFrame *>(bar.ID3v2Tag()->frameList("POPM").front())->rating());
}
}
// http://bugs.kde.org/show_bug.cgi?id=150481
@ -402,7 +418,7 @@ public:
"http://example.com", 33), // URL
f.render());
}
void testParseOwnershipFrame()
{
ID3v2::OwnershipFrame f(
@ -547,13 +563,17 @@ public:
ScopedFileCopy copy("xing", ".mp3");
string newname = copy.fileName();
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(String::UTF16);
MPEG::File foo(newname.c_str());
foo.strip();
foo.tag()->setComment("Test comment!");
foo.save();
MPEG::File bar(newname.c_str());
CPPUNIT_ASSERT_EQUAL(String("Test comment!"), bar.tag()->comment());
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(defaultEncoding);
{
MPEG::File foo(newname.c_str());
foo.strip();
foo.tag()->setComment("Test comment!");
foo.save();
}
{
MPEG::File bar(newname.c_str());
CPPUNIT_ASSERT_EQUAL(String("Test comment!"), bar.tag()->comment());
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(defaultEncoding);
}
}
void testUpdateGenre23_1()
@ -634,57 +654,60 @@ public:
string newname = copy.fileName();
ID3v2::TextIdentificationFrame *tf;
MPEG::File foo(newname.c_str());
tf = new ID3v2::TextIdentificationFrame("TDOR", String::Latin1);
tf->setText("2011-03-16");
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TDRC", String::Latin1);
tf->setText("2012-04-17T12:01");
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TMCL", String::Latin1);
tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2"));
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1);
tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4"));
foo.ID3v2Tag()->addFrame(tf);
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDRL", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDTG", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TMOO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TPRO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOA", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOT", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSST", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOP", String::Latin1));
foo.save(MPEG::File::AllTags, true, 3);
MPEG::File bar(newname.c_str());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDOR").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2011"), tf->fieldList().front());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDRC").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2012"), tf->fieldList().front());
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TIPL").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(8), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]);
CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]);
CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]);
CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]);
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]);
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]);
CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]);
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]);
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDRL"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDTG"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TMOO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TPRO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOA"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOT"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSST"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOP"));
{
MPEG::File foo(newname.c_str());
tf = new ID3v2::TextIdentificationFrame("TDOR", String::Latin1);
tf->setText("2011-03-16");
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TDRC", String::Latin1);
tf->setText("2012-04-17T12:01");
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TMCL", String::Latin1);
tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2"));
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1);
tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4"));
foo.ID3v2Tag()->addFrame(tf);
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDRL", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TDTG", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TMOO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TPRO", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOA", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOT", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSST", String::Latin1));
foo.ID3v2Tag()->addFrame(new ID3v2::TextIdentificationFrame("TSOP", String::Latin1));
foo.save(MPEG::File::AllTags, true, 3);
}
{
MPEG::File bar(newname.c_str());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDOR").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2011"), tf->fieldList().front());
tf = static_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TDRC").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(1), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("2012"), tf->fieldList().front());
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TIPL").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(size_t(8), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]);
CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]);
CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]);
CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]);
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]);
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]);
CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]);
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]);
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDRL"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TDTG"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TMOO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TPRO"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOA"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOT"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSST"));
CPPUNIT_ASSERT(!bar.ID3v2Tag()->frameListMap().contains("TSOP"));
}
}
void testCompressedFrameWithBrokenLength()
@ -694,7 +717,7 @@ public:
#ifdef HAVE_ZLIB
ID3v2::AttachedPictureFrame *frame
ID3v2::AttachedPictureFrame *frame
= dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(f.ID3v2Tag()->frameListMap()["APIC"].front());
CPPUNIT_ASSERT(frame);
CPPUNIT_ASSERT_EQUAL(String("image/bmp"), frame->mimeType());
@ -707,13 +730,13 @@ public:
// Skip the test if ZLIB is not installed.
// The message "Compressed frames are currently not supported." will be displayed.
ID3v2::UnknownFrame *frame
ID3v2::UnknownFrame *frame
= dynamic_cast<TagLib::ID3v2::UnknownFrame*>(f.ID3v2Tag()->frameListMap()["APIC"].front());
CPPUNIT_ASSERT(frame);
#endif
}
void testW000()
{
MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false);
@ -825,40 +848,157 @@ public:
{
ScopedFileCopy copy("rare_frames", ".mp3");
string newname = copy.fileName();
MPEG::File f(newname.c_str());
ID3v2::Tag *t = f.ID3v2Tag();
ID3v2::Frame *frame = t->frameList("TCON")[0];
CPPUNIT_ASSERT_EQUAL((size_t)1u, t->frameList("TCON").size());
t->removeFrame(frame, true);
f.save(MPEG::File::ID3v2);
MPEG::File f2(newname.c_str());
t = f2.ID3v2Tag();
CPPUNIT_ASSERT(t->frameList("TCON").isEmpty());
{
MPEG::File f(newname.c_str());
ID3v2::Tag *t = f.ID3v2Tag();
ID3v2::Frame *frame = t->frameList("TCON")[0];
CPPUNIT_ASSERT_EQUAL(size_t(1), t->frameList("TCON").size());
t->removeFrame(frame, true);
f.save(MPEG::File::ID3v2);
}
{
MPEG::File f2(newname.c_str());
ID3v2::Tag *t = f2.ID3v2Tag();
CPPUNIT_ASSERT(t->frameList("TCON").isEmpty());
}
}
void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2()
{
ScopedFileCopy copy("xing", ".mp3");
string newname = copy.fileName();
{
MPEG::File foo(newname.c_str());
foo.tag()->setArtist("Artist");
foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2);
}
{
MPEG::File bar(newname.c_str());
bar.ID3v2Tag()->removeFrames("TPE1");
// Should strip ID3v1 here and not add old values to ID3v2 again
bar.save(MPEG::File::ID3v2, true);
}
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1"));
}
void testParseChapterFrame()
{
ID3v2::ChapterFrame f(
ByteVector("CHAP" // Frame ID
"\x00\x00\x00\x20" // Frame size
"\x00\x00" // Frame flags
"\x43\x00" // Element ID
"\x00\x00\x00\x03" // Start time
"\x00\x00\x00\x05" // End time
"\x00\x00\x00\x02" // Start offset
"\x00\x00\x00\x03" // End offset
"TIT2" // Embedded frame ID
"\x00\x00\x00\x04" // Embedded frame size
"\x00\x00" // Embedded frame flags
"\x00" // TIT2 frame text encoding
"CH1", 42)); // Chapter title
CPPUNIT_ASSERT_EQUAL(ByteVector("\x43\x00", 2),
f.elementID());
CPPUNIT_ASSERT((uint)0x03 == f.startTime());
CPPUNIT_ASSERT((uint)0x05 == f.endTime());
CPPUNIT_ASSERT((uint)0x02 == f.startOffset());
CPPUNIT_ASSERT((uint)0x03 == f.endOffset());
CPPUNIT_ASSERT((uint)0x01 == f.embeddedFrameList().size());
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1);
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "CH1");
}
void testRenderChapterFrame()
{
ID3v2::ChapterFrame f("CHAP");
f.setElementID(ByteVector("\x43\x00", 2));
f.setStartTime(3);
f.setEndTime(5);
f.setStartOffset(2);
f.setEndOffset(3);
ID3v2::TextIdentificationFrame eF("TIT2");
eF.setText("CH1");
f.addEmbeddedFrame(&eF);
CPPUNIT_ASSERT_EQUAL(
ByteVector("CHAP" // Frame ID
"\x00\x00\x00\x20" // Frame size
"\x00\x00" // Frame flags
"\x43\x00" // Element ID
"\x00\x00\x00\x03" // Start time
"\x00\x00\x00\x05" // End time
"\x00\x00\x00\x02" // Start offset
"\x00\x00\x00\x03" // End offset
"TIT2" // Embedded frame ID
"\x00\x00\x00\x04" // Embedded frame size
"\x00\x00" // Embedded frame flags
"\x00" // TIT2 frame text encoding
"CH1", 42), // Chapter title
f.render());
}
void testParseTableOfContentsFrame()
{
ID3v2::TableOfContentsFrame f(
ByteVector("CTOC" // Frame ID
"\x00\x00\x00\x16" // Frame size
"\x00\x00" // Frame flags
"\x54\x00" // Element ID
"\x01" // CTOC flags
"\x02" // Entry count
"\x43\x00" // First entry
"\x44\x00" // Second entry
"TIT2" // Embedded frame ID
"\x00\x00\x00\x04" // Embedded frame size
"\x00\x00" // Embedded frame flags
"\x00" // TIT2 frame text encoding
"TC1", 32)); // Table of contents title
CPPUNIT_ASSERT_EQUAL(ByteVector("\x54\x00", 2),
f.elementID());
CPPUNIT_ASSERT(!f.isTopLevel());
CPPUNIT_ASSERT(f.isOrdered());
CPPUNIT_ASSERT((uint)0x02 == f.entryCount());
CPPUNIT_ASSERT_EQUAL(ByteVector("\x43\x00", 2),
f.childElements()[0]);
CPPUNIT_ASSERT_EQUAL(ByteVector("\x44\x00", 2),
f.childElements()[1]);
CPPUNIT_ASSERT((uint)0x01 == f.embeddedFrameList().size());
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2").size() == 1);
CPPUNIT_ASSERT(f.embeddedFrameList("TIT2")[0]->toString() == "TC1");
}
void testRenderTableOfContentsFrame()
{
ID3v2::TableOfContentsFrame f("CTOC");
f.setElementID(ByteVector("\x54\x00", 2));
f.setIsTopLevel(false);
f.setIsOrdered(true);
f.addChildElement(ByteVector("\x43\x00", 2));
f.addChildElement(ByteVector("\x44\x00", 2));
ID3v2::TextIdentificationFrame eF("TIT2");
eF.setText("TC1");
f.addEmbeddedFrame(&eF);
CPPUNIT_ASSERT_EQUAL(
ByteVector("CTOC" // Frame ID
"\x00\x00\x00\x16" // Frame size
"\x00\x00" // Frame flags
"\x54\x00" // Element ID
"\x01" // CTOC flags
"\x02" // Entry count
"\x43\x00" // First entry
"\x44\x00" // Second entry
"TIT2" // Embedded frame ID
"\x00\x00\x00\x04" // Embedded frame size
"\x00\x00" // Embedded frame flags
"\x00" // TIT2 frame text encoding
"TC1", 32), // Table of contents title
f.render());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2);

View File

@ -18,85 +18,90 @@ class TestMatroska : public CppUnit::TestFixture
CPPUNIT_TEST(testInsertAndExtract);
CPPUNIT_TEST(testAudioProperties);
CPPUNIT_TEST_SUITE_END();
public:
void testPredefined()
{
ScopedFileCopy copy("matroska", ".mka");
string filename = copy.fileName();
EBML::Matroska::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
PropertyMap pm = f.properties();
PropertyMap::Iterator i = pm.find("ENCODER");
CPPUNIT_ASSERT(i != f.properties().end());
CPPUNIT_ASSERT(i != pm.end());
CPPUNIT_ASSERT_EQUAL(String("Lavf54.63.104"), i->second.front());
}
void testInsertAndExtract()
{
ScopedFileCopy copy("matroska", ".mka");
string filename = copy.fileName();
EBML::Matroska::File f1(filename.c_str());
CPPUNIT_ASSERT(f1.isValid());
Tag* t = f1.tag();
CPPUNIT_ASSERT(t != 0);
t->setTitle("Seconds of Silence");
t->setArtist("Nobody");
t->setAlbum("TagLib Test Suite");
t->setComment("Well, there's nothing to say - a few special signs: ©’…ä–€ſ");
t->setGenre("Air");
t->setYear(2013);
t->setTrack(15);
CPPUNIT_ASSERT(f1.save());
EBML::Matroska::File f2(filename.c_str());
CPPUNIT_ASSERT(f2.isValid());
t = f2.tag();
CPPUNIT_ASSERT(t != 0);
CPPUNIT_ASSERT_EQUAL(String("Seconds of Silence"), t->title());
CPPUNIT_ASSERT_EQUAL(String("Nobody"), t->artist());
CPPUNIT_ASSERT_EQUAL(String("TagLib Test Suite"), t->album());
CPPUNIT_ASSERT_EQUAL(String("Well, there's nothing to say - a few special signs: ©’…ä–€ſ"), t->comment());
CPPUNIT_ASSERT_EQUAL(String("Air"), t->genre());
CPPUNIT_ASSERT_EQUAL(2013u, t->year());
CPPUNIT_ASSERT_EQUAL(15u, t->track());
PropertyMap pm = f2.properties();
pm.erase("COMMENT");
f2.setProperties(pm);
CPPUNIT_ASSERT(f2.save());
EBML::Matroska::File f3(filename.c_str());
CPPUNIT_ASSERT(f3.isValid());
pm = f3.properties();
PropertyMap::Iterator i = pm.find("GENRE");
CPPUNIT_ASSERT(i != pm.end());
CPPUNIT_ASSERT_EQUAL(String("Air"), i->second.front());
{
EBML::Matroska::File f1(filename.c_str());
CPPUNIT_ASSERT(f1.isValid());
Tag* t = f1.tag();
CPPUNIT_ASSERT(t != 0);
t->setTitle("Seconds of Silence");
t->setArtist("Nobody");
t->setAlbum("TagLib Test Suite");
t->setComment("Well, there's nothing to say - a few special signs: ©’…ä–€ſ");
t->setGenre("Air");
t->setYear(2013);
t->setTrack(15);
CPPUNIT_ASSERT(f1.save());
}
{
EBML::Matroska::File f2(filename.c_str());
CPPUNIT_ASSERT(f2.isValid());
Tag* t = f2.tag();
CPPUNIT_ASSERT(t != 0);
CPPUNIT_ASSERT_EQUAL(String("Seconds of Silence"), t->title());
CPPUNIT_ASSERT_EQUAL(String("Nobody"), t->artist());
CPPUNIT_ASSERT_EQUAL(String("TagLib Test Suite"), t->album());
CPPUNIT_ASSERT_EQUAL(String("Well, there's nothing to say - a few special signs: ©’…ä–€ſ"), t->comment());
CPPUNIT_ASSERT_EQUAL(String("Air"), t->genre());
CPPUNIT_ASSERT_EQUAL(2013u, t->year());
CPPUNIT_ASSERT_EQUAL(15u, t->track());
PropertyMap pm = f2.properties();
pm.erase("COMMENT");
f2.setProperties(pm);
CPPUNIT_ASSERT(f2.save());
}
{
EBML::Matroska::File f3(filename.c_str());
CPPUNIT_ASSERT(f3.isValid());
PropertyMap pm = f3.properties();
PropertyMap::Iterator i = pm.find("GENRE");
CPPUNIT_ASSERT(i != pm.end());
CPPUNIT_ASSERT_EQUAL(String("Air"), i->second.front());
}
}
void testAudioProperties()
{
ScopedFileCopy copy("matroska", ".mka");
string filename = copy.fileName();
EBML::Matroska::File f(filename.c_str());
CPPUNIT_ASSERT(f.isValid());
AudioProperties* a = f.audioProperties();
CPPUNIT_ASSERT(a != 0);
// Not a very nice assertion...
CPPUNIT_ASSERT_EQUAL(a->length(), 0);
// Bitrate is not nice and thus not tested.

View File

@ -158,6 +158,7 @@ public:
f->tag()->itemListMap()["pgap"] = true;
f->save();
delete f;
f = new MP4::File(filename.c_str());
CPPUNIT_ASSERT_EQUAL(true, f->tag()->itemListMap()["cpil"].toBool());
@ -167,6 +168,7 @@ public:
moov = atoms->atoms[0];
// original size + 'pgap' size + padding
CPPUNIT_ASSERT_EQUAL(long(77 + 25 + 974), moov->length);
delete f;
}
void testGnre()
@ -231,7 +233,7 @@ public:
void testProperties()
{
MP4::File f(TEST_FILE_PATH_C("has-tags.m4a"));
PropertyMap tags = f.properties();
CPPUNIT_ASSERT_EQUAL(StringList("Test Artist"), tags["ARTIST"]);

View File

@ -32,18 +32,21 @@ public:
string newname = copy.fileName();
String xxx = ByteVector(254, 'X');
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag());
{
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 4);
CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 4);
CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
}
{
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
}
}
void testSaveID3v24WrongParam()
@ -52,15 +55,18 @@ public:
string newname = copy.fileName();
String xxx = ByteVector(254, 'X');
MPEG::File f(newname.c_str());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 8);
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
{
MPEG::File f(newname.c_str());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 8);
}
{
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
}
}
void testSaveID3v23()
@ -69,18 +75,21 @@ public:
string newname = copy.fileName();
String xxx = ByteVector(254, 'X');
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag());
{
MPEG::File f(newname.c_str());
CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 3);
CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
f.tag()->setTitle(xxx);
f.tag()->setArtist("Artist A");
f.save(MPEG::File::AllTags, true, 3);
CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag());
}
{
MPEG::File f2(newname.c_str());
CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f2.ID3v2Tag()->header()->majorVersion());
CPPUNIT_ASSERT_EQUAL(String("Artist A"), f2.tag()->artist());
CPPUNIT_ASSERT_EQUAL(xxx, f2.tag()->title());
}
}
};

View File

@ -11,11 +11,11 @@ public:
{
TagLib::PropertyMap map1;
CPPUNIT_ASSERT(map1.isEmpty());
map1["\xc4\xd6\xdc"].append("test");
CPPUNIT_ASSERT_EQUAL(map1.size(), (size_t)1u);
map1[L"\x00c4\x00d6\x00dc"].append("test");
CPPUNIT_ASSERT_EQUAL(map1.size(), (size_t)1);
TagLib::PropertyMap map2;
map2["\xc4\xd6\xdc"].append("test");
map2[L"\x00c4\x00d6\x00dc"].append("test");
CPPUNIT_ASSERT(map1 == map2);
CPPUNIT_ASSERT(map1.contains(map2));
@ -24,6 +24,7 @@ public:
CPPUNIT_ASSERT(map2.contains(map1));
map2["\xc4\xd6\xdc"].append("test 2");
map2[L"\x00c4\x00d6\x00dc"].append("test 2");
CPPUNIT_ASSERT(!map2.contains(map1));
}

View File

@ -99,7 +99,7 @@ public:
String unicode6(L"\u65e5\u672c\u8a9e", String::UTF16LE);
CPPUNIT_ASSERT(unicode6[1] == (littleEndian ? L'\u672c' : L'\u2c67'));
wstring stduni = L"\u65e5\u672c\u8a9e";
std::wstring stduni = L"\u65e5\u672c\u8a9e";
String unicode7(stduni);
CPPUNIT_ASSERT(unicode7[1] == L'\u672c');

View File

@ -29,8 +29,8 @@ inline string copyFile(const string &filename, const string &ext)
string newname = string(tempnam(NULL, NULL)) + ext;
string oldname = testFilePath(filename) + ext;
#ifdef _WIN32
CopyFile(oldname.c_str(), newname.c_str(), FALSE);
SetFileAttributes(newname.c_str(), GetFileAttributes(newname.c_str()) & ~FILE_ATTRIBUTE_READONLY);
CopyFileA(oldname.c_str(), newname.c_str(), FALSE);
SetFileAttributesA(newname.c_str(), GetFileAttributesA(newname.c_str()) & ~FILE_ATTRIBUTE_READONLY);
#else
char buffer[4096];
int bytes;