54 Commits

Author SHA1 Message Date
Urs Fleisch
9914519d43 Update NEWS for v1.13beta 2022-09-19 18:07:31 +02:00
Urs Fleisch
f80d11ed2a MP4: Fix heap-buffer-overflow in mp4properties.cpp (#1058) 2022-09-05 06:32:53 +02:00
Urs Fleisch
4e7f844ea6 Correctly parse ID3v2.4.0 multiple strings with single BOM (#1055)
Some ID3v2.4.0 frames such as text information frames support multiple strings
separated by the termination code of the character encoding. If the encoding
is $01 UTF-16 with BOM, all strings shall have the same byte order. In the
multi strings written by TagLib, all string elements of such a multi string
have a BOM. However, I have often seen tags where a BOM exists only at the
beginning, i.e. at the start of the first string. In such a case, TagLib will
only return a list with the first string and a second empty string. This
commit will detect such cases and parse the strings without BOM according to
the BOM of the first string.
2022-08-01 09:27:35 +02:00
Stephen F. Booth
50b89ad19a Fix ID3v2 version handling for embedded frames 2022-07-24 15:28:45 +02:00
Urs Fleisch
0470c2894d Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame
Also use "WORK" for ASF "WM/ContentGroupDescription", as it corresponds
to "TIT1" according to
https://docs.microsoft.com/en-us/windows/win32/wmformat/id3-tag-support.
Add property "COMPILATION" for ID3v2 "TCMP" to be consistent with
MP4 "cpil".
2022-03-15 17:52:44 +01:00
James D. Smith
10721b4b41 Test for ID3v1 false positives. 2022-03-13 08:11:23 +01:00
James D. Smith
cebaf9a8e2 Utils: Fix potential ID3v1 false positive in the presence of an APE tag. 2022-03-13 08:11:23 +01:00
Urs Fleisch
197d2a684b Correctly parse MP4 non-full meta atoms (#1041)
There are m4a files with regular (non-full) meta atoms. When such
a meta atom is not correctly parsed, the subsequent atoms are not
recognized and offsets will not be adjusted when atoms are added,
which will corrupt the MP4 file.

This change will look behind the meta atom to check if the next
atom follows directly, i.e. without the four bytes with version
and flags as they exist in full atoms. In such a case, these
four bytes will not be skipped.

Witnesses of this strange format specification are
https://leo-van-stee.github.io/
https://github.com/axiomatic-systems/Bento4/blob/v1.6.0-639/Source/C%2B%2B/Core/Ap4ContainerAtom.cpp#L60
2022-03-12 07:33:32 +01:00
Urs Fleisch
e255e749e8 fileref: Adapt formatting to TagLib code style 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
a7eb7735ee fileref: Reduce variables scope 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
0de8b45612 tests: Fix stream based resolver test 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
bdd8ff2af0 fileref: Fall back on file name resolvers
when no stream resolver is available
2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
82964ba176 detectByResolvers: Don't mandate an actual file to be present
The main point of IOStream is to be able to use taglib with some content
that can't be fopen()'ed, for instance a network file, or a local file
on a system that doesn't support opening a file directly through fopen
& such.
This commit adds an overload for detectByResolvers that will stick to
using an IOStream all the way.
2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
f7887e7235 FileTypeResolver: Add a StreamTypeResolver interface 2022-02-27 07:11:53 +01:00
Urs Fleisch
8ab618da18 Add tests checking if looking up tags creates map entries (#931) 2022-02-09 20:07:13 +01:00
Urs Fleisch
c98fba7cc4 APE: Do not create map entry if looked up tag does not exist (#931) 2022-02-09 20:07:13 +01:00
whatdoineed2do/Ray
52bf85f8ca FLAC: Do not create map entry if looked up tag does not exist (#931) 2022-02-09 20:07:13 +01:00
Urs Fleisch
a4bb904a01 Map: Add method value() to look up without creating entry 2022-02-09 20:07:13 +01:00
Scott Wheeler
05486d00bf We don't need to export these symbols 2022-01-15 13:52:05 +01:00
Urs Fleisch
c4a0855f42 Add MP4::File::strip() to remove meta atom from file
This changes the modifications from the last commit in order to
achieve the following behavior: MP4::File::save() works in the
same way as before, i.e. it will never shrink the file and will
make space from removed items available as padding in the form of
a "free" atom. To completely remove the "meta" atom from the file,
a new method strip() is introduced, which can be used in the same
way as its MPEG::File::strip() counterpart.
2022-01-06 18:07:15 +01:00
Urs Fleisch
2cb7973162 Remove MP4 meta atom when empty tag is saved
Currently, MP4 tags can only grow. If items are removed, they are
just replaced by padding in the form of "free" atoms. This change
will remove the whole "meta" atom when an MP4 tag without items
is saved. This will make it possible, to bring the file back to
its pristine state without metadata.
2022-01-06 18:07:15 +01:00
Dzmitry Neviadomski
ff8a9ea831 Add -Wall compiler flag to Clang compilers.
Use CMAKE's `CMAKE_<LANG>_COMPILER_ID`, available from 3.0.2.
See: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
2021-12-31 07:51:38 +01:00
ivan tkachenko
fd66b0d3b6 Add compile_commands.json to gitignore
Other entries were copied from KDE/marble repo as well.

See also: https://invent.kde.org/sdk/kdesrc-build/-/issues/86
2021-12-08 19:24:52 +01:00
Rosen Penev
f581615110 clang-tidy: replace static_cast with dynamic_cast (#1021)
* clang-tidy: replace static_cast with dynamic_cast

Found with cppcoreguidelines-pro-type-static-cast-downcast

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-07-09 21:47:36 +02:00
Rosen Penev
07eb81e88a add missing const
Avoids using a non const global variable.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-07-08 20:38:27 +02:00
Rosen Penev
c6f1e2750e use std::pair instead of 2D C array (#1020)
* use std::pair instead of 2D C array

Mostly the same. Except there's now an association between both values.

make_pair has to be used since there's no uniform initialization.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 08:16:46 +02:00
Rosen Penev
a8884c2b17 clang-tidy: don't use else after return
Found with readability-else-after-return

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
76f00c5a8a clang-tidy: simplify boolean return
Found with readability-simplify-boolean-expr

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
68ac7c3106 clang-tidy: use C++ casting
Found with google-readability-casting

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
38d1d4c21c clang-tidy: add ending namespace comments
Found with google-readability-namespace-comments

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
b77e828d4b clang-tidy: remove redundant initializations
Found with readability-redundant-member-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Urs Fleisch
17e299350a Detect by contents if file detected by extension is invalid (#1011)
This is the case for Opus files with an ogg extension.
2021-05-28 20:25:05 +02:00
Oleg Antonyan
1d24bd3399 Fix "error: duplicate ‘volatile’" on systems without HAVE_GCC_ATOMIC in 1.12 (#1012)
* fix error: duplicate volatile

* fix volatile ATOMIC_INT in constructor
2021-05-13 16:41:35 +02:00
bobsayshilol
4971f8fb03 ID3v2: Address review comments
Change the size check to use isEmpty() as requested.
2021-05-02 12:14:27 +02:00
bobsayshilol
d74689cb93 ASF: Bounds check the size of each header object
UBSan spotted an integer overflow on the line `dataPos += size`, so add
a bounds check to the size that we read.
2021-05-02 12:14:27 +02:00
bobsayshilol
51ae5748cb ID3v2: Return early from decode() on invalid data
The while loop in this function assumes that `data.end() - 1` is less
than `data.end()`, which isn't the case if `data` is empty since
`data.end()` can be a nullptr.
2021-05-02 12:14:27 +02:00
bobsayshilol
f2eb331696 MPC: Fix heap-buffer-overflow in readSV7()
If `data` is an allocation of only 3 bytes (MP+) then `data[3]` is a
read past the end of the buffer.
2021-05-02 12:14:27 +02:00
bobsayshilol
5f6bbb20e7 APE: Fix typo in valLength 2021-05-02 12:14:27 +02:00
bobsayshilol
03d03f782e APE: Bounds check the length of values
`pos`, `valLegnth`, and `data.size()` are all unsigned types so we have
to do a little dance to correctly bounds check them without overflow.

Without this we can get stuck in an infinite loop due to 'pos'
overflowing back to the start of the data.
2021-05-02 12:14:27 +02:00
bobsayshilol
1d3e080f04 ID3v2: Bounds check a vector's size
If the provided vector is empty then `data[0]` dereferences a nullptr,
so add a check that this won't be the case before reading the encoding.
2021-05-02 12:14:27 +02:00
bobsayshilol
3391bd80c4 FLAC: Validate the size of a read
Without this we can crash trying to dereference parts of `header`.
2021-05-02 12:14:27 +02:00
Julius Brüggemann
1644c0dd87 Add CMake option for building with ZLib 2021-03-29 20:21:59 +02:00
Scott Wheeler
85cc41082c Update to more modern macOS instructions.
Closes #1005
2021-03-22 11:36:36 +00:00
Jörg Krause
b5cd4c40e2 Drop leftovers WITH_ASF and WITH_MP4
The options WITH_ASF and WITH_MP4 where remove in commit
dd846904cb back in 2011. It's time to
remove the leftovers :-)
2021-03-06 14:54:31 +01:00
Urs Fleisch
932d45259c Fix taglib-config for cross compiling (#998) 2021-03-06 14:52:25 +01:00
Urs Fleisch
844f07d32d Make tests work with BUILD_SHARED_LIBS=ON on Windows 2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
304ab20174 Install taglib.png icon into html folder structure
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
243dd863d7 Install examples after we selected to build them
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
d77e2aee0b Just set CMAKE_MODULE_PATH instead of list(APPEND), drop ENABLE_STATIC err
ENABLE_STATIC error has been there since 2014, that is long enough.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
1e636957ab Use FeatureSummary for a nice CMake summary output
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
507a42871c Use GNUInstallDirs
Well-established CMake standard for installation directories.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
01348fb619 Move finding ZLIB to root CMakeLists.txt
Small line decrease, but also easier to read what is happening.
Now all dependencies can be read from the root CMakeLists.txt file.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
9e0a7f7adb Use CMake's CTest which includes BUILD_TESTING option
BUILD_TESTING is default enabled, which is a good default anyway.
Move the CppUnit check to the root CMakeLists.txt, simpler and clearer.
BUILD_TESTS is obsolete.

The need for BUILD_SHARED_LIBS=OFF for testing is not clear, it works on Linux.
But I kept it in the instructions for now.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Scott Wheeler
340ec9932a SVG for API doc diagrams makes a lot more sense in 2021
This also makes things look non-crappy on high-DPI monitors.
2021-02-16 12:00:52 +00:00
89 changed files with 1216 additions and 758 deletions

5
.gitignore vendored
View File

@@ -47,3 +47,8 @@ CMakeFiles/
taglib.h.stamp
taglib.xcodeproj
CMakeScripts
/.clang-format
/compile_commands.json
.clangd
.cache
.idea

View File

@@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(taglib)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(DEFINED ENABLE_STATIC)
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
endif()
include(CTest)
include(FeatureSummary)
include(GNUInstallDirs)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(APPLE)
@@ -32,7 +32,6 @@ if(ENABLE_CCACHE)
endif()
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(BUILD_BINDINGS "Build the bindings" ON)
@@ -46,15 +45,11 @@ endif()
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
## the following are directories where stuff will be installed to
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
@@ -90,9 +85,9 @@ endif()
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
# 3. If any interfaces have been added since the last public release, then increment age.
# 4. If any interfaces have been removed since the last public release, then set age to 0.
set(TAGLIB_SOVERSION_CURRENT 19)
set(TAGLIB_SOVERSION_CURRENT 20)
set(TAGLIB_SOVERSION_REVISION 0)
set(TAGLIB_SOVERSION_AGE 18)
set(TAGLIB_SOVERSION_AGE 19)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
@@ -100,40 +95,46 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(ConfigureChecks.cmake)
if(${ZLIB_FOUND})
set(ZLIB_LIBRARIES_FLAGS -lz)
# Determine whether zlib is installed.
option(WITH_ZLIB "Build with ZLIB" ON)
if(WITH_ZLIB)
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
set(ZLIB_LIBRARIES_FLAGS -lz)
else()
set(HAVE_ZLIB 0)
endif()
endif()
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
set(HAVE_ZLIB 1)
set(HAVE_ZLIB_SOURCE 1)
endif()
else()
set(HAVE_ZLIB 0)
endif()
if(NOT WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
if(WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${BIN_INSTALL_DIR}")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
if(NOT BUILD_FRAMEWORK)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
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)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
if(WITH_ASF)
set(TAGLIB_WITH_ASF TRUE)
endif()
if(WITH_MP4)
set(TAGLIB_WITH_MP4 TRUE)
endif()
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
@@ -147,9 +148,13 @@ if(BUILD_BINDINGS)
add_subdirectory(bindings)
endif()
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
enable_testing()
add_subdirectory(tests)
if(BUILD_TESTING)
find_package(CppUnit)
if(CppUnit_FOUND)
add_subdirectory(tests)
else()
message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.")
endif()
endif()
if(BUILD_EXAMPLES)
@@ -157,7 +162,7 @@ if(BUILD_EXAMPLES)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
file(COPY doc/taglib.png DESTINATION doc)
file(COPY doc/taglib.png DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/doc/html)
add_custom_target(docs doxygen)
# uninstall target
@@ -166,3 +171,5 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_C
if(NOT TARGET uninstall)
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@@ -176,27 +176,6 @@ check_cxx_source_compiles("
}
" HAVE_ISO_STRDUP)
# Determine whether zlib is installed.
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.
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
find_package(CppUnit)
if(NOT CppUnit_FOUND)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
endif()
endif()
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM WINRT 1)

View File

@@ -169,9 +169,7 @@ SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DO_NOT_DOCUMENT \
DOXYGEN \
WITH_MP4 \
WITH_ASF
DOXYGEN
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
@@ -196,7 +194,7 @@ INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DOT_IMAGE_FORMAT = png
DOT_IMAGE_FORMAT = svg
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024

View File

@@ -13,6 +13,12 @@ In order to build the included examples, use the `BUILD_EXAMPLES` option:
cmake -DBUILD_EXAMPLES=ON [...]
If you want to build TagLib without ZLib, you can use
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
make
sudo make install
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
@@ -22,23 +28,25 @@ Mac OS X
On Mac OS X, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
a framework with Mac OS X 10.10 as the deployment target:
cmake -DCMAKE_BUILD_TYPE=Release \
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
For a 10.10 static library, use:
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_INSTALL_PREFIX="<folder you want to install to>"
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
After `make`, and `make install`, add `libtag.` to your XCode project, and add
the include folder to the project's User Header Search Paths.
@@ -72,6 +80,7 @@ Useful configuration options used with CMake (GUI and/or command line):
| option | description |
| ----------------------- | ----------- |
| `WITH_ZLIB=` | Whether to build with ZLib |
| `ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
| `ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
| `ZLIB_LIBRARY=` | Where to find ZLib's Library.
@@ -167,7 +176,7 @@ Unit Tests
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. To build the tests, include
the option `-DBUILD_TESTS=ON -DBUILD_SHARED_LIBS=OFF` when running cmake.
the option `-DBUILD_TESTING=ON` when running cmake.
The test suite has a custom target in the build system, so you can run
the tests using make:
@@ -183,7 +192,7 @@ Windows MinGW:
- `mingw32-make; mingw32-make install DESTDIR=/path/to/install/dir`
* Build TagLib with testing enabled:
- ```
cmake -G "MinGW Makefiles" -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF \
cmake -G "MinGW Makefiles" -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF \
-DCPPUNIT_INCLUDE_DIR=/path/to/cppunit/include \
-DCPPUNIT_LIBRARIES=/path/to/cppunit/lib/libcppunit.a \
-DCPPUNIT_INSTALLED_VERSION=1.15.1
@@ -200,7 +209,7 @@ Windows MSVS:
- It may fail, but the needed libraries should be available in src\cppunit\DebugDll.
* Build TagLib with testing enabled:
- ```
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
-DCPPUNIT_INCLUDE_DIR=\path\to\cppunit\include
-DCPPUNIT_LIBRARIES=\path\to\cppunit\DebugDll\cppunitd_dll.lib

24
NEWS
View File

@@ -1,3 +1,27 @@
==========================
* Added interface StreamTypeResolver to support streams which cannot be
fopen()'ed, e.g. network files.
* Added MP4::File::strip() to remove meta atom from MP4 file.
* Added Map::value() to look up without creating entry.
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
use property "WORK" for ASF "WM/ContentGroupDescription",
use property "COMPILATION" for ID3v2 "TCMP" frame.
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
tags.
* Fixed detection of invalid file by extension when correct type can be
detected by contents.
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
tag does not exist.
* Fixed parsing of MP4 non-full meta atoms.
* Fixed potential ID3v1 false positive in the presence of an APE tag.
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
* Fixed several smaller issues reported by clang-tidy.
TagLib 1.12 (Feb 16, 2021)
==========================

View File

@@ -58,18 +58,18 @@ set_target_properties(tag_c PROPERTIES
VERSION 0.0.0
SOVERSION 0
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
INSTALL_NAME_DIR ${CMAKE_INSTALL_LIBDIR}
)
install(TARGETS tag_c
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
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib
)
if(NOT BUILD_FRAMEWORK)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif()

View File

@@ -69,7 +69,7 @@ namespace
{
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
}
}
} // namespace
void taglib_set_strings_unicode(BOOL unicode)
{

View File

@@ -1,12 +1,12 @@
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
libdir=${CMAKE_INSTALL_FULL_LIBDIR}
includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}
Name: TagLib C Bindings
Description: Audio meta-data library (C bindings)
Requires: taglib
Version: ${TAGLIB_LIB_VERSION_STRING}
Libs: -L${LIB_INSTALL_DIR} -ltag_c
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
Libs: -L${CMAKE_INSTALL_FULL_LIBDIR} -ltag_c
Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib

View File

@@ -33,7 +33,7 @@ ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
# how can we find cppunit version?
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)

View File

@@ -12,7 +12,7 @@
<table border="0" width="100%">
<tr>
<td width="1">
<img src="../taglib.png">
<img src="taglib.png">
</td>
<td>
<div id="intro">

View File

@@ -38,3 +38,8 @@ target_link_libraries(framelist tag)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@@ -14,10 +14,20 @@ EOH
exit 1;
}
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@LIB_INSTALL_DIR@
includedir=@INCLUDE_INSTALL_DIR@
# Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot.
prefix=
exec_prefix=
if test -z "$prefix"; then
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
else
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
fi
if test -z "$exec_prefix"; then
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
else
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
fi
flags=""
@@ -38,7 +48,7 @@ do
echo @TAGLIB_LIB_VERSION_STRING@
;;
--prefix)
echo $prefix
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
;;
*)
echo "$0: unknown option $1"

View File

@@ -27,8 +27,8 @@ goto theend
* to allow for static, shared or debug builds.
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
:doit
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR} -I${INCLUDE_INSTALL_DIR}/taglib
if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}

View File

@@ -1,11 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@LIB_INSTALL_DIR@
includedir=@INCLUDE_INSTALL_DIR@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: TagLib
Description: Audio meta-data library
Requires:
Requires:
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
Cflags: -I${includedir} -I${includedir}/taglib

View File

@@ -344,7 +344,7 @@ endif()
set_target_properties(tag PROPERTIES
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
INSTALL_NAME_DIR ${CMAKE_INSTALL_LIBDIR}
DEFINE_SYMBOL MAKE_TAGLIB_LIB
LINK_INTERFACE_LIBRARIES ""
PUBLIC_HEADER "${tag_HDRS}"
@@ -365,8 +365,8 @@ 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
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib
)

View File

@@ -135,8 +135,7 @@ unsigned int APE::Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + size();
else
return d->tagSize;
return d->tagSize;
}
void APE::Footer::setTagSize(unsigned int s)
@@ -158,8 +157,7 @@ ByteVector APE::Footer::renderHeader() const
{
if(!d->headerPresent)
return ByteVector();
else
return render(true);
return render(true);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -219,8 +219,7 @@ String APE::Item::toString() const
{
if(d->type == Text && !isEmpty())
return d->text.front();
else
return String();
return String();
}
bool APE::Item::isEmpty() const
@@ -229,9 +228,7 @@ bool APE::Item::isEmpty() const
case Text:
if(d->text.isEmpty())
return true;
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
return d->text.size() == 1 && d->text.front().isEmpty();
case Binary:
case Locator:
return d->value.isEmpty();

View File

@@ -70,7 +70,7 @@ namespace
return true;
}
}
} // namespace
class APE::Tag::TagPrivate
{
@@ -91,13 +91,11 @@ public:
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
TagLib::Tag(),
d(new TagPrivate())
{
d->file = file;
@@ -118,51 +116,44 @@ ByteVector APE::Tag::fileIdentifier()
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String();
return d->itemListMap["TITLE"].values().toString();
Item value = d->itemListMap.value("TITLE");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String();
return d->itemListMap["ARTIST"].values().toString();
Item value = d->itemListMap.value("ARTIST");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String();
return d->itemListMap["ALBUM"].values().toString();
Item value = d->itemListMap.value("ALBUM");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String();
return d->itemListMap["COMMENT"].values().toString();
Item value = d->itemListMap.value("COMMENT");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String();
return d->itemListMap["GENRE"].values().toString();
Item value = d->itemListMap.value("GENRE");
return value.isEmpty() ? String() : value.values().toString();
}
unsigned int APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
Item value = d->itemListMap.value("YEAR");
return value.isEmpty() ? 0 : value.toString().toInt();
}
unsigned int APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
Item value = d->itemListMap.value("TRACK");
return value.isEmpty() ? 0 : value.toString().toInt();
}
void APE::Tag::setTitle(const String &s)
@@ -210,16 +201,18 @@ namespace
{
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
// usual, APE
const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
{"DATE", "YEAR" },
{"ALBUMARTIST", "ALBUM ARTIST"},
{"DISCNUMBER", "DISC" },
{"REMIXER", "MIXARTIST" },
{"RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS" },
{"RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE" }};
// usual, APE
const std::pair<const char *, const char *> keyConversions[] = {
std::make_pair("TRACKNUMBER", "TRACK"),
std::make_pair("DATE", "YEAR"),
std::make_pair("ALBUMARTIST", "ALBUM ARTIST"),
std::make_pair("DISCNUMBER", "DISC"),
std::make_pair("REMIXER", "MIXARTIST"),
std::make_pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
std::make_pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
};
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
}
} // namespace
PropertyMap APE::Tag::properties() const
{
@@ -235,8 +228,8 @@ PropertyMap APE::Tag::properties() const
else {
// Some tags need to be handled specially
for(size_t i = 0; i < keyConversionsSize; ++i) {
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
if(tagName == keyConversions[i].second)
tagName = keyConversions[i].first;
}
properties[tagName].append(it->second.toStringList());
}
@@ -257,9 +250,9 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
// see comment in properties()
for(size_t i = 0; i < keyConversionsSize; ++i)
if(properties.contains(keyConversions[i][0])) {
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
properties.erase(keyConversions[i][0]);
if(properties.contains(keyConversions[i].first)) {
properties.insert(keyConversions[i].second, properties[keyConversions[i].first]);
properties.erase(keyConversions[i].first);
}
// first check if tags need to be removed completely
@@ -419,7 +412,12 @@ void APE::Tag::parse(const ByteVector &data)
}
const unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLegnth = data.toUInt(pos, false);
const unsigned int valLength = data.toUInt(pos, false);
if(valLength >= data.size() || pos > data.size() - valLength) {
debug("APE::Tag::parse() - Invalid val length. Stopped parsing.");
return;
}
if(keyLength >= MinKeyLength
&& keyLength <= MaxKeyLength
@@ -434,6 +432,6 @@ void APE::Tag::parse(const ByteVector &data)
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
}
pos += keyLength + valLegnth + 9;
pos += keyLength + valLength + 9;
}
}

View File

@@ -312,7 +312,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromShort(data.size(), false) +
data;
}
@@ -321,7 +321,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;

View File

@@ -97,7 +97,7 @@ namespace
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
}
} // namespace
class ASF::File::FilePrivate::BaseObject
{
@@ -194,7 +194,7 @@ private:
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
if(size > 24 && size <= (unsigned int)(file->length()))
if(size > 24 && size <= static_cast<unsigned int>(file->length()))
data = file->readBlock(size - 24);
else
data = ByteVector();
@@ -384,7 +384,7 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
}
bool ok;
long long size = readQWORD(file, &ok);
if(!ok) {
if(!ok || size < 0 || size > dataSize - dataPos) {
file->setValid(false);
break;
}
@@ -400,7 +400,7 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
else {
obj = new UnknownObject(guid);
}
obj->parse(file, (unsigned int)size);
obj->parse(file, static_cast<unsigned int>(size));
objects.append(obj);
dataPos += size;
}
@@ -656,7 +656,7 @@ void ASF::File::read()
setValid(false);
break;
}
long size = (long)readQWORD(this, &ok);
long size = static_cast<long>(readQWORD(this, &ok));
if(!ok) {
setValid(false);
break;

View File

@@ -137,7 +137,7 @@ ByteVector ASF::Picture::render() const
return ByteVector();
return
ByteVector((char)d->type) +
ByteVector(static_cast<char>(d->type)) +
ByteVector::fromUInt(d->picture.size(), false) +
renderString(d->mimeType) +
renderString(d->description) +
@@ -150,7 +150,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
if(bytes.size() < 9)
return;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
d->type = static_cast<Type>(bytes[0]); ++pos;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);

View File

@@ -40,7 +40,6 @@ public:
};
ASF::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
@@ -95,8 +94,7 @@ unsigned int ASF::Tag::track() const
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
if(attr.type() == ASF::Attribute::DWordType)
return attr.toUInt();
else
return attr.toString().toInt();
return attr.toString().toInt();
}
if(d->attributeListMap.contains("WM/Track"))
return d->attributeListMap["WM/Track"][0].toUInt();
@@ -212,65 +210,65 @@ bool ASF::Tag::isEmpty() const
namespace
{
const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "WM/AlbumArtist", "ALBUMARTIST" },
{ "WM/Composer", "COMPOSER" },
{ "WM/Writer", "LYRICIST" },
{ "WM/Conductor", "CONDUCTOR" },
{ "WM/ModifiedBy", "REMIXER" },
{ "WM/Year", "DATE" },
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
{ "WM/Producer", "PRODUCER" },
{ "WM/ContentGroupDescription", "GROUPING" },
{ "WM/SubTitle", "SUBTITLE" },
{ "WM/SetSubTitle", "DISCSUBTITLE" },
{ "WM/TrackNumber", "TRACKNUMBER" },
{ "WM/PartOfSet", "DISCNUMBER" },
{ "WM/Genre", "GENRE" },
{ "WM/BeatsPerMinute", "BPM" },
{ "WM/Mood", "MOOD" },
{ "WM/ISRC", "ISRC" },
{ "WM/Lyrics", "LYRICS" },
{ "WM/Media", "MEDIA" },
{ "WM/Publisher", "LABEL" },
{ "WM/CatalogNo", "CATALOGNUMBER" },
{ "WM/Barcode", "BARCODE" },
{ "WM/EncodedBy", "ENCODEDBY" },
{ "WM/AlbumSortOrder", "ALBUMSORT" },
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
{ "WM/ArtistSortOrder", "ARTISTSORT" },
{ "WM/TitleSortOrder", "TITLESORT" },
{ "WM/Script", "SCRIPT" },
{ "WM/Language", "LANGUAGE" },
{ "WM/ARTISTS", "ARTISTS" },
{ "ASIN", "ASIN" },
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz/Album Release Country", "RELEASECOUNTRY" },
{ "MusicBrainz/Album Status", "RELEASESTATUS" },
{ "MusicBrainz/Album Type", "RELEASETYPE" },
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
{ "MusicIP/PUID", "MUSICIP_PUID" },
{ "Acoustid/Id", "ACOUSTID_ID" },
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
const std::pair<const char *, const char *> keyTranslation[] = {
std::make_pair("WM/AlbumTitle", "ALBUM"),
std::make_pair("WM/AlbumArtist", "ALBUMARTIST"),
std::make_pair("WM/Composer", "COMPOSER"),
std::make_pair("WM/Writer", "LYRICIST"),
std::make_pair("WM/Conductor", "CONDUCTOR"),
std::make_pair("WM/ModifiedBy", "REMIXER"),
std::make_pair("WM/Year", "DATE"),
std::make_pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
std::make_pair("WM/Producer", "PRODUCER"),
std::make_pair("WM/ContentGroupDescription", "WORK"),
std::make_pair("WM/SubTitle", "SUBTITLE"),
std::make_pair("WM/SetSubTitle", "DISCSUBTITLE"),
std::make_pair("WM/TrackNumber", "TRACKNUMBER"),
std::make_pair("WM/PartOfSet", "DISCNUMBER"),
std::make_pair("WM/Genre", "GENRE"),
std::make_pair("WM/BeatsPerMinute", "BPM"),
std::make_pair("WM/Mood", "MOOD"),
std::make_pair("WM/ISRC", "ISRC"),
std::make_pair("WM/Lyrics", "LYRICS"),
std::make_pair("WM/Media", "MEDIA"),
std::make_pair("WM/Publisher", "LABEL"),
std::make_pair("WM/CatalogNo", "CATALOGNUMBER"),
std::make_pair("WM/Barcode", "BARCODE"),
std::make_pair("WM/EncodedBy", "ENCODEDBY"),
std::make_pair("WM/AlbumSortOrder", "ALBUMSORT"),
std::make_pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
std::make_pair("WM/ArtistSortOrder", "ARTISTSORT"),
std::make_pair("WM/TitleSortOrder", "TITLESORT"),
std::make_pair("WM/Script", "SCRIPT"),
std::make_pair("WM/Language", "LANGUAGE"),
std::make_pair("WM/ARTISTS", "ARTISTS"),
std::make_pair("ASIN", "ASIN"),
std::make_pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
std::make_pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
std::make_pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
std::make_pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
std::make_pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
std::make_pair("MusicBrainz/Album Status", "RELEASESTATUS"),
std::make_pair("MusicBrainz/Album Type", "RELEASETYPE"),
std::make_pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
std::make_pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
std::make_pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
std::make_pair("MusicIP/PUID", "MUSICIP_PUID"),
std::make_pair("Acoustid/Id", "ACOUSTID_ID"),
std::make_pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
};
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
String translateKey(const String &key)
{
for(size_t i = 0; i < keyTranslationSize; ++i) {
if(key == keyTranslation[i][0])
return keyTranslation[i][1];
if(key == keyTranslation[i].first)
return keyTranslation[i].second;
}
return String();
}
}
} // namespace
PropertyMap ASF::Tag::properties() const
{
@@ -324,9 +322,8 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
for(size_t i = 0; i < keyTranslationSize; i++) {
reverseKeyMap[keyTranslation[i].second] = keyTranslation[i].first;
}
}

View File

@@ -49,32 +49,31 @@ using namespace TagLib;
#define VIRTUAL_FUNCTION_WORKAROUND(function_name, default_value) \
if(dynamic_cast<const APE::Properties*>(this)) \
return dynamic_cast<const APE::Properties*>(this)->function_name(); \
else if(dynamic_cast<const ASF::Properties*>(this)) \
if(dynamic_cast<const ASF::Properties*>(this)) \
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const FLAC::Properties*>(this)) \
if(dynamic_cast<const FLAC::Properties*>(this)) \
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MP4::Properties*>(this)) \
if(dynamic_cast<const MP4::Properties*>(this)) \
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPC::Properties*>(this)) \
if(dynamic_cast<const MPC::Properties*>(this)) \
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
else if(dynamic_cast<const MPEG::Properties*>(this)) \
if(dynamic_cast<const MPEG::Properties*>(this)) \
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
else if(dynamic_cast<const TrueAudio::Properties*>(this)) \
if(dynamic_cast<const TrueAudio::Properties*>(this)) \
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
else if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
else if(dynamic_cast<const Vorbis::Properties*>(this)) \
if(dynamic_cast<const Vorbis::Properties*>(this)) \
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
else if(dynamic_cast<const WavPack::Properties*>(this)) \
if(dynamic_cast<const WavPack::Properties*>(this)) \
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
else \
return (default_value);
return (default_value);
class AudioProperties::AudioPropertiesPrivate
{

View File

@@ -84,6 +84,22 @@ namespace
return 0;
}
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
for(ResolverList::ConstIterator it = fileTypeResolvers.begin();
it != fileTypeResolvers.end(); ++it) {
if(const FileRef::StreamTypeResolver *streamResolver =
dynamic_cast<const FileRef::StreamTypeResolver*>(*it)) {
if(File *file = streamResolver->createFileFromStream(
stream, readAudioProperties, audioPropertiesStyle))
return file;
}
}
return 0;
}
// Detect the file type based on the file extension.
File* detectByExtension(IOStream *stream, bool readAudioProperties,
@@ -109,41 +125,51 @@ namespace
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
File *file = 0;
if(ext == "MP3")
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "FLAC")
return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(ext == "OGG")
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "FLAC")
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(ext == "MPC")
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WV")
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SPX")
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OPUS")
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "TTA")
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WMA" || ext == "ASF")
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WAV")
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "APE")
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "S3M")
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "IT")
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "XM")
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
// if file is not valid, leave it to content-based detection.
if(file) {
if(file->isValid())
return file;
delete file;
}
return 0;
}
@@ -189,8 +215,7 @@ namespace
if(file) {
if(file->isValid())
return file;
else
delete file;
delete file;
}
return 0;
@@ -267,13 +292,12 @@ namespace
return 0;
}
}
} // namespace
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate() :
RefCounter(),
file(0),
stream(0) {}
@@ -472,6 +496,12 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// Try user-defined stream resolvers.
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try user-defined resolvers.
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);

View File

@@ -108,6 +108,16 @@ namespace TagLib {
audioPropertiesStyle = AudioProperties::Average) const = 0;
};
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
virtual File *createFileFromStream(IOStream *stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
};
/*!
* Creates a null FileRef.
*/

View File

@@ -55,7 +55,7 @@ namespace
const long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
} // namespace
class FLAC::File::FilePrivate
{
@@ -497,6 +497,11 @@ void FLAC::File::scan()
seek(nextBlockOffset);
const ByteVector header = readBlock(4);
if(header.size() != 4) {
debug("FLAC::File::scan() -- Failed to read a block header");
setValid(false);
return;
}
// Header format (from spec):
// <1> Last-metadata-block flag

View File

@@ -37,7 +37,7 @@ class IT::File::FilePrivate
{
public:
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: tag(), properties(propertiesStyle)
: properties(propertiesStyle)
{
}
@@ -113,7 +113,7 @@ bool IT::File::save()
// write comment as instrument and sample names:
StringList lines = d->tag.comment().split("\n");
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
seek(192L + length + (static_cast<long>(i) << 2));
unsigned long instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -128,14 +128,14 @@ bool IT::File::save()
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
unsigned long sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
seek(sampleOffset + 20);
if((unsigned int)(i + instrumentCount) < lines.size())
if(static_cast<unsigned int>(i + instrumentCount) < lines.size())
writeString(lines[i + instrumentCount], 25);
else
writeString(String(), 25);
@@ -152,7 +152,7 @@ bool IT::File::save()
// terminating NUL but it does not hurt to add one:
if(message.size() > 7999)
message.resize(7999);
message.append((char)0);
message.append(static_cast<char>(0));
unsigned short special = 0;
unsigned short messageLength = 0;
@@ -239,7 +239,7 @@ void IT::File::read(bool)
seek(messageOffset);
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
int index = messageBytes.find((char) 0);
int index = messageBytes.find(static_cast<char>(0));
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
@@ -258,7 +258,7 @@ void IT::File::read(bool)
// But this always gives 64 channels for all my files anyway.
// Strangely VLC does report other values. I wonder how VLC
// gets it's values.
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
++channels;
}
d->properties.setChannels(channels);
@@ -280,7 +280,7 @@ void IT::File::read(bool)
// e.g. VLC seems to interpret a nil as a space. I
// don't know what is the proper behaviour.
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
seek(192L + length + (static_cast<long>(i) << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@@ -296,7 +296,7 @@ void IT::File::read(bool)
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
READ_U32L_AS(sampleOffset);
seek(sampleOffset);

View File

@@ -49,7 +49,7 @@ bool Mod::FileBase::readString(String &s, unsigned long size)
{
ByteVector data(readBlock(size));
if(data.size() < size) return false;
int index = data.find((char) 0);
int index = data.find(static_cast<char>(0));
if(index > -1)
{
data.resize(index);

View File

@@ -44,7 +44,6 @@ public:
};
Mod::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}

View File

@@ -31,7 +31,7 @@
using namespace TagLib;
const char *MP4::Atom::containers[11] = {
const char *const MP4::Atom::containers[11] = {
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
@@ -85,7 +85,25 @@ MP4::Atom::Atom(File *file)
for(int i = 0; i < numContainers; i++) {
if(name == containers[i]) {
if(name == "meta") {
file->seek(4, File::Current);
long posAfterMeta = file->tell();
ByteVector nextSize = file->readBlock(8).mid(4, 4);
static const char *const metaChildrenNames[] = {
"hdlr", "ilst", "mhdr", "ctry", "lang"
};
bool metaIsFullAtom = true;
for(size_t j = 0;
j < sizeof(metaChildrenNames) / sizeof(metaChildrenNames[0]);
++j) {
if(nextSize == metaChildrenNames[j]) {
// meta is not a full atom (i.e. not followed by version, flags). It
// is followed by the size and type of the first child atom.
metaIsFullAtom = false;
break;
}
}
// Only skip next four bytes, which contain version and flags, if meta
// is a full atom.
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
}
else if(name == "stsd") {
file->seek(8, File::Current);

View File

@@ -74,7 +74,7 @@ namespace TagLib {
typedef TagLib::List<AtomData> AtomDataList;
class Atom
class TAGLIB_EXPORT Atom
{
public:
Atom(File *file);
@@ -88,11 +88,11 @@ namespace TagLib {
AtomList children;
private:
static const int numContainers = 11;
static const char *containers[11];
static const char *const containers[11];
};
//! Root-level atoms
class Atoms
class TAGLIB_EXPORT Atoms
{
public:
Atoms(File *file);

View File

@@ -34,7 +34,6 @@ class MP4::CoverArt::CoverArtPrivate : public RefCounter
{
public:
CoverArtPrivate() :
RefCounter(),
format(MP4::CoverArt::JPEG) {}
Format format;

View File

@@ -49,7 +49,7 @@ namespace
return true;
}
}
} // namespace
class MP4::File::FilePrivate
{
@@ -175,6 +175,26 @@ MP4::File::save()
return d->tag->save();
}
bool
MP4::File::strip(int tags)
{
if(readOnly()) {
debug("MP4::File::strip() - Cannot strip tags from a read only file.");
return false;
}
if(!isValid()) {
debug("MP4::File::strip() -- Cannot strip tags from an invalid file.");
return false;
}
if(tags & MP4) {
return d->tag->strip();
}
return true;
}
bool
MP4::File::hasMP4Tag() const
{

View File

@@ -48,6 +48,19 @@ namespace TagLib {
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for strip() and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches MP4 tags.
MP4 = 0x0001,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs an MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
@@ -114,6 +127,15 @@ namespace TagLib {
*/
bool save();
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns true if the tags are
* successfully stripped.
*
* \note This will update the file immediately.
*/
bool strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an MP4 tag, or the
* file has a Metadata Item List (ilst) atom.

View File

@@ -34,7 +34,6 @@ class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() :
RefCounter(),
valid(true),
atomDataType(TypeUndefined) {}

View File

@@ -50,7 +50,7 @@ namespace
return totalLength;
}
}
} // namespace
class MP4::Properties::PropertiesPrivate
{
@@ -188,7 +188,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
const unsigned int version = data[8];
const unsigned int version = data.at(8);
long long unit;
long long length;
if(version == 1) {
@@ -222,13 +222,13 @@ MP4::Properties::read(File *file, Atoms *atoms)
d->channels = data.toShort(40U);
d->bitsPerSample = data.toShort(42U);
d->sampleRate = data.toUInt(46U);
if(data.containsAt("esds", 56) && data[64] == 0x03) {
if(data.containsAt("esds", 56) && data.at(64) == 0x03) {
unsigned int pos = 65;
if(data.containsAt("\x80\x80\x80", pos)) {
pos += 3;
}
pos += 4;
if(data[pos] == 0x04) {
if(data.at(pos) == 0x04) {
pos += 1;
if(data.containsAt("\x80\x80\x80", pos)) {
pos += 3;

View File

@@ -84,7 +84,7 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
if (val.type == TypeUTF8) {
addItem(atom->name, StringList(String(val.data, String::UTF8)));
} else {
addItem(atom->name, (int)(val.data.toShort()));
addItem(atom->name, static_cast<int>(val.data.toShort()));
}
}
}
@@ -140,7 +140,7 @@ MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm)
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
return result;
}
else if(i == 1 && name != "name") {
if(i == 1 && name != "name") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
return result;
}
@@ -177,7 +177,7 @@ MP4::Tag::parseInt(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, (int)data[0].toShort());
addItem(atom->name, static_cast<int>(data[0].toShort()));
}
}
@@ -213,7 +213,7 @@ MP4::Tag::parseGnre(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
int idx = (int)data[0].toShort();
int idx = static_cast<int>(data[0].toShort());
if(idx > 0) {
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
}
@@ -541,6 +541,19 @@ MP4::Tag::save()
return true;
}
bool
MP4::Tag::strip()
{
d->items.clear();
AtomList path = d->atoms->path("moov", "udta", "meta", "ilst");
if(path.size() == 4) {
saveExisting(ByteVector(), path);
}
return true;
}
void
MP4::Tag::updateParents(const AtomList &path, long delta, int ignore)
{
@@ -699,20 +712,40 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
}
long delta = data.size() - length;
if(delta > 0 || (delta < 0 && delta > -8)) {
data.append(padIlst(data));
delta = data.size() - length;
}
else if(delta < 0) {
data.append(padIlst(data, -delta - 8));
delta = 0;
}
if(!data.isEmpty()) {
if(delta > 0 || (delta < 0 && delta > -8)) {
data.append(padIlst(data));
delta = data.size() - length;
}
else if(delta < 0) {
data.append(padIlst(data, -delta - 8));
delta = 0;
}
d->file->insert(data, offset, length);
d->file->insert(data, offset, length);
if(delta) {
updateParents(path, delta, 1);
updateOffsets(delta, offset);
if(delta) {
updateParents(path, delta, 1);
updateOffsets(delta, offset);
}
}
else {
// Strip meta if data is empty, only the case when called from strip().
MP4::Atom *udta = *(--it);
AtomList &udtaChildren = udta->children;
AtomList::Iterator metaIt = udtaChildren.find(meta);
if(metaIt != udtaChildren.end()) {
offset = meta->offset;
delta = - meta->length;
udtaChildren.erase(metaIt);
d->file->removeBlock(meta->offset, meta->length);
delete meta;
if(delta) {
updateParents(path, delta, 2);
updateOffsets(delta, offset);
}
}
}
}
@@ -871,89 +904,89 @@ bool MP4::Tag::contains(const String &key) const
namespace
{
const char *keyTranslation[][2] = {
{ "\251nam", "TITLE" },
{ "\251ART", "ARTIST" },
{ "\251alb", "ALBUM" },
{ "\251cmt", "COMMENT" },
{ "\251gen", "GENRE" },
{ "\251day", "DATE" },
{ "\251wrt", "COMPOSER" },
{ "\251grp", "GROUPING" },
{ "aART", "ALBUMARTIST" },
{ "trkn", "TRACKNUMBER" },
{ "disk", "DISCNUMBER" },
{ "cpil", "COMPILATION" },
{ "tmpo", "BPM" },
{ "cprt", "COPYRIGHT" },
{ "\251lyr", "LYRICS" },
{ "\251too", "ENCODEDBY" },
{ "soal", "ALBUMSORT" },
{ "soaa", "ALBUMARTISTSORT" },
{ "soar", "ARTISTSORT" },
{ "sonm", "TITLESORT" },
{ "soco", "COMPOSERSORT" },
{ "sosn", "SHOWSORT" },
{ "shwm", "SHOWWORKMOVEMENT" },
{ "pgap", "GAPLESSPLAYBACK" },
{ "pcst", "PODCAST" },
{ "catg", "PODCASTCATEGORY" },
{ "desc", "PODCASTDESC" },
{ "egid", "PODCASTID" },
{ "purl", "PODCASTURL" },
{ "tves", "TVEPISODE" },
{ "tven", "TVEPISODEID" },
{ "tvnn", "TVNETWORK" },
{ "tvsn", "TVSEASON" },
{ "tvsh", "TVSHOW" },
{ "\251wrk", "WORK" },
{ "\251mvn", "MOVEMENTNAME" },
{ "\251mvi", "MOVEMENTNUMBER" },
{ "\251mvc", "MOVEMENTCOUNT" },
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY" },
{ "----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS" },
{ "----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE" },
{ "----:com.apple.iTunes:ARTISTS", "ARTISTS" },
{ "----:com.apple.iTunes:originaldate", "ORIGINALDATE" },
{ "----:com.apple.iTunes:ASIN", "ASIN" },
{ "----:com.apple.iTunes:LABEL", "LABEL" },
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
{ "----:com.apple.iTunes:MIXER", "MIXER" },
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
{ "----:com.apple.iTunes:MOOD", "MOOD" },
{ "----:com.apple.iTunes:ISRC", "ISRC" },
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
const std::pair<const char *, const char *> keyTranslation[] = {
std::make_pair("\251nam", "TITLE"),
std::make_pair("\251ART", "ARTIST"),
std::make_pair("\251alb", "ALBUM"),
std::make_pair("\251cmt", "COMMENT"),
std::make_pair("\251gen", "GENRE"),
std::make_pair("\251day", "DATE"),
std::make_pair("\251wrt", "COMPOSER"),
std::make_pair("\251grp", "GROUPING"),
std::make_pair("aART", "ALBUMARTIST"),
std::make_pair("trkn", "TRACKNUMBER"),
std::make_pair("disk", "DISCNUMBER"),
std::make_pair("cpil", "COMPILATION"),
std::make_pair("tmpo", "BPM"),
std::make_pair("cprt", "COPYRIGHT"),
std::make_pair("\251lyr", "LYRICS"),
std::make_pair("\251too", "ENCODEDBY"),
std::make_pair("soal", "ALBUMSORT"),
std::make_pair("soaa", "ALBUMARTISTSORT"),
std::make_pair("soar", "ARTISTSORT"),
std::make_pair("sonm", "TITLESORT"),
std::make_pair("soco", "COMPOSERSORT"),
std::make_pair("sosn", "SHOWSORT"),
std::make_pair("shwm", "SHOWWORKMOVEMENT"),
std::make_pair("pgap", "GAPLESSPLAYBACK"),
std::make_pair("pcst", "PODCAST"),
std::make_pair("catg", "PODCASTCATEGORY"),
std::make_pair("desc", "PODCASTDESC"),
std::make_pair("egid", "PODCASTID"),
std::make_pair("purl", "PODCASTURL"),
std::make_pair("tves", "TVEPISODE"),
std::make_pair("tven", "TVEPISODEID"),
std::make_pair("tvnn", "TVNETWORK"),
std::make_pair("tvsn", "TVSEASON"),
std::make_pair("tvsh", "TVSHOW"),
std::make_pair("\251wrk", "WORK"),
std::make_pair("\251mvn", "MOVEMENTNAME"),
std::make_pair("\251mvi", "MOVEMENTNUMBER"),
std::make_pair("\251mvc", "MOVEMENTCOUNT"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Album Release Country", "RELEASECOUNTRY"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Album Status", "RELEASESTATUS"),
std::make_pair("----:com.apple.iTunes:MusicBrainz Album Type", "RELEASETYPE"),
std::make_pair("----:com.apple.iTunes:ARTISTS", "ARTISTS"),
std::make_pair("----:com.apple.iTunes:originaldate", "ORIGINALDATE"),
std::make_pair("----:com.apple.iTunes:ASIN", "ASIN"),
std::make_pair("----:com.apple.iTunes:LABEL", "LABEL"),
std::make_pair("----:com.apple.iTunes:LYRICIST", "LYRICIST"),
std::make_pair("----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR"),
std::make_pair("----:com.apple.iTunes:REMIXER", "REMIXER"),
std::make_pair("----:com.apple.iTunes:ENGINEER", "ENGINEER"),
std::make_pair("----:com.apple.iTunes:PRODUCER", "PRODUCER"),
std::make_pair("----:com.apple.iTunes:DJMIXER", "DJMIXER"),
std::make_pair("----:com.apple.iTunes:MIXER", "MIXER"),
std::make_pair("----:com.apple.iTunes:SUBTITLE", "SUBTITLE"),
std::make_pair("----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE"),
std::make_pair("----:com.apple.iTunes:MOOD", "MOOD"),
std::make_pair("----:com.apple.iTunes:ISRC", "ISRC"),
std::make_pair("----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER"),
std::make_pair("----:com.apple.iTunes:BARCODE", "BARCODE"),
std::make_pair("----:com.apple.iTunes:SCRIPT", "SCRIPT"),
std::make_pair("----:com.apple.iTunes:LANGUAGE", "LANGUAGE"),
std::make_pair("----:com.apple.iTunes:LICENSE", "LICENSE"),
std::make_pair("----:com.apple.iTunes:MEDIA", "MEDIA"),
};
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
String translateKey(const String &key)
{
for(size_t i = 0; i < keyTranslationSize; ++i) {
if(key == keyTranslation[i][0])
return keyTranslation[i][1];
if(key == keyTranslation[i].first)
return keyTranslation[i].second;
}
return String();
}
}
} // namespace
PropertyMap MP4::Tag::properties() const
{
@@ -998,9 +1031,8 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
for(size_t i = 0; i < keyTranslationSize; i++) {
reverseKeyMap[keyTranslation[i].second] = keyTranslation[i].first;
}
}

View File

@@ -102,6 +102,11 @@ namespace TagLib {
*/
bool contains(const String &key) const;
/*!
* Saves the associated file with the tag stripped.
*/
bool strip();
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);

View File

@@ -201,7 +201,7 @@ namespace
// This array looks weird, but the same as original MusePack code found at:
// https://www.musepack.net/index.php?pg=src
const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
}
} // namespace
void MPC::Properties::readSV8(File *file, long streamLength)
{
@@ -298,6 +298,9 @@ void MPC::Properties::readSV8(File *file, long streamLength)
void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
{
if(data.startsWith("MP+")) {
if(data.size() < 4)
return;
d->version = data[3] & 15;
if(d->version < 7)
return;
@@ -317,22 +320,22 @@ void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
// convert gain info
if(d->trackGain != 0) {
int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5);
int tmp = static_cast<int>((64.82 - static_cast<short>(d->trackGain) / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->trackGain = tmp;
}
if(d->albumGain != 0) {
int tmp = (int)((64.82 - d->albumGain / 100.) * 256. + .5);
int tmp = static_cast<int>((64.82 - d->albumGain / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->albumGain = tmp;
}
if (d->trackPeak != 0)
d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5);
d->trackPeak = static_cast<int>(log10(static_cast<double>(d->trackPeak)) * 20 * 256 + .5);
if (d->albumPeak != 0)
d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5);
d->albumPeak = static_cast<int>(log10(static_cast<double>(d->albumPeak)) * 20 * 256 + .5);
bool trueGapless = (gapless >> 31) & 0x0001;
if(trueGapless) {

View File

@@ -29,7 +29,7 @@ using namespace TagLib;
namespace
{
const wchar_t *genres[] = {
const wchar_t *const genres[] = {
L"Blues",
L"Classic Rock",
L"Country",
@@ -224,7 +224,7 @@ namespace
L"Psybient"
};
const int genresSize = sizeof(genres) / sizeof(genres[0]);
}
} // namespace
StringList ID3v1::genreList()
{
@@ -250,8 +250,7 @@ String ID3v1::genre(int i)
{
if(i >= 0 && i < genresSize)
return String(genres[i]); // always make a copy
else
return String();
return String();
}
int ID3v1::genreIndex(const String &name)

View File

@@ -76,8 +76,7 @@ ByteVector ID3v1::StringHandler::render(const String &s) const
{
if(s.isLatin1())
return s.data(String::Latin1);
else
return ByteVector();
return ByteVector();
}
////////////////////////////////////////////////////////////////////////////////
@@ -85,13 +84,11 @@ ByteVector ID3v1::StringHandler::render(const String &s) const
////////////////////////////////////////////////////////////////////////////////
ID3v1::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
ID3v1::Tag::Tag(File *file, long tagOffset) :
TagLib::Tag(),
d(new TagPrivate())
{
d->file = file;

View File

@@ -144,7 +144,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data)
return;
}
d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
d->type = static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(data[pos++]);
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);
@@ -205,7 +205,7 @@ void AttachedPictureFrameV22::parseFields(const ByteVector &data)
d->mimeType = "image/" + fixedString;
}
d->type = (TagLib::ID3v2::AttachedPictureFrame::Type)data[pos++];
d->type = static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(data[pos++]);
d->description = readStringField(data, d->textEncoding, &pos);
d->data = data.mid(pos);

View File

@@ -294,8 +294,10 @@ ByteVector ChapterFrame::renderFields() const
data.append(ByteVector::fromUInt(d->startOffset, true));
data.append(ByteVector::fromUInt(d->endOffset, true));
FrameList l = d->embeddedFrameList;
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) {
(*it)->header()->setVersion(header()->version());
data.append((*it)->render());
}
return data;
}

View File

@@ -117,6 +117,11 @@ void OwnershipFrame::parseFields(const ByteVector &data)
{
int pos = 0;
// Need at least 1 byte for the encoding
if(data.isEmpty()) {
return;
}
// Get the text encoding
d->textEncoding = String::Type(data[0]);
pos += 1;

View File

@@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
d->rating = 0;
d->counter = 0;
if(pos < size) {
d->rating = (unsigned char)(data[pos++]);
d->rating = static_cast<unsigned char>(data[pos++]);
if(pos < size) {
d->counter = data.toUInt(static_cast<unsigned int>(pos));
}

View File

@@ -180,7 +180,7 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
// Each channel is at least 4 bytes.
while(pos <= (int)data.size() - 4) {
while(pos <= static_cast<int>(data.size()) - 4) {
ChannelType type = ChannelType(data[pos]);
pos += 1;

View File

@@ -75,7 +75,7 @@ namespace {
}
return l;
}
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// public methods
@@ -274,7 +274,7 @@ TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag)
++it)
{
TableOfContentsFrame *frame = dynamic_cast<TableOfContentsFrame *>(*it);
if(frame && frame->isTopLevel() == true)
if(frame && frame->isTopLevel())
return frame;
}
@@ -336,7 +336,7 @@ ByteVector TableOfContentsFrame::renderFields() const
if(d->isOrdered)
flags += 1;
data.append(flags);
data.append((char)(entryCount()));
data.append(static_cast<char>(entryCount()));
ByteVectorList::ConstIterator it = d->childElements.begin();
while(it != d->childElements.end()) {
data.append(*it);
@@ -344,8 +344,10 @@ ByteVector TableOfContentsFrame::renderFields() const
it++;
}
FrameList l = d->embeddedFrameList;
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) {
(*it)->header()->setVersion(header()->version());
data.append((*it)->render());
}
return data;
}

View File

@@ -125,22 +125,22 @@ void TextIdentificationFrame::setTextEncoding(String::Type encoding)
namespace
{
// array of allowed TIPL prefixes and their corresponding key value
const char* involvedPeople[][2] = {
{"ARRANGER", "ARRANGER"},
{"ENGINEER", "ENGINEER"},
{"PRODUCER", "PRODUCER"},
{"DJ-MIX", "DJMIXER"},
{"MIX", "MIXER"},
const std::pair<const char *, const char *> involvedPeople[] = {
std::make_pair("ARRANGER", "ARRANGER"),
std::make_pair("ENGINEER", "ENGINEER"),
std::make_pair("PRODUCER", "PRODUCER"),
std::make_pair("DJ-MIX", "DJMIXER"),
std::make_pair("MIX", "MIXER"),
};
const size_t involvedPeopleSize = sizeof(involvedPeople) / sizeof(involvedPeople[0]);
}
} // namespace
const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static
{
static KeyConversionMap m;
if(m.isEmpty()) {
for(size_t i = 0; i < involvedPeopleSize; ++i)
m.insert(involvedPeople[i][1], involvedPeople[i][0]);
m.insert(involvedPeople[i].second, involvedPeople[i].first);
}
return m;
}
@@ -218,12 +218,32 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
// append those split values to the list and make sure that the new string's
// type is the same specified for this frame
unsigned short firstBom = 0;
for(ByteVectorList::ConstIterator it = l.begin(); it != l.end(); it++) {
if(!(*it).isEmpty()) {
if(d->textEncoding == String::Latin1)
if(d->textEncoding == String::Latin1) {
d->fieldList.append(Tag::latin1StringHandler()->parse(*it));
else
d->fieldList.append(String(*it, d->textEncoding));
}
else {
String::Type textEncoding = d->textEncoding;
if(textEncoding == String::UTF16) {
if(it == l.begin()) {
firstBom = it->mid(0, 2).toUShort();
}
else {
unsigned short subsequentBom = it->mid(0, 2).toUShort();
if(subsequentBom != 0xfeff && subsequentBom != 0xfffe) {
if(firstBom == 0xfeff) {
textEncoding = String::UTF16BE;
}
else if(firstBom == 0xfffe) {
textEncoding = String::UTF16LE;
}
}
}
}
d->fieldList.append(String(*it, textEncoding));
}
}
}
}
@@ -274,8 +294,8 @@ PropertyMap TextIdentificationFrame::makeTIPLProperties() const
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
bool found = false;
for(size_t i = 0; i < involvedPeopleSize; ++i)
if(*it == involvedPeople[i][0]) {
map.insert(involvedPeople[i][1], (++it)->split(","));
if(*it == involvedPeople[i].first) {
map.insert(involvedPeople[i].second, (++it)->split(","));
found = true;
break;
}

View File

@@ -74,7 +74,7 @@ namespace
}
return true;
}
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// static methods
@@ -94,8 +94,7 @@ ByteVector Frame::textDelimiter(String::Type t)
{
if(t == String::UTF16 || t == String::UTF16BE || t == String::UTF16LE)
return ByteVector(2, '\0');
else
return ByteVector(1, '\0');
return ByteVector(1, '\0');
}
const String Frame::instrumentPrefix("PERFORMER:");
@@ -117,11 +116,11 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) //
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
frame->setText(values);
return frame;
} else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value
} if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
frame->setUrl(values.front());
return frame;
} else if(frameID == "PCST") {
} if(frameID == "PCST") {
return new PodcastFrame();
}
}
@@ -166,16 +165,14 @@ ByteVector Frame::frameID() const
{
if(d->header)
return d->header->frameID();
else
return ByteVector();
return ByteVector();
}
unsigned int Frame::size() const
{
if(d->header)
return d->header->frameSize();
else
return 0;
return 0;
}
void Frame::setData(const ByteVector &data)
@@ -309,10 +306,8 @@ String::Type Frame::checkEncoding(const StringList &fields, String::Type encodin
debug("Frame::checkEncoding() -- Rendering using UTF8.");
return String::UTF8;
}
else {
debug("Frame::checkEncoding() -- Rendering using UTF16.");
return String::UTF16;
}
debug("Frame::checkEncoding() -- Rendering using UTF16.");
return String::UTF16;
}
}
@@ -326,120 +321,121 @@ String::Type Frame::checkTextEncoding(const StringList &fields, String::Type enc
namespace
{
const char *frameTranslation[][2] = {
const std::pair<const char *, const char *> frameTranslation[] = {
// Text information frames
{ "TALB", "ALBUM"},
{ "TBPM", "BPM" },
{ "TCOM", "COMPOSER" },
{ "TCON", "GENRE" },
{ "TCOP", "COPYRIGHT" },
{ "TDEN", "ENCODINGTIME" },
{ "TDLY", "PLAYLISTDELAY" },
{ "TDOR", "ORIGINALDATE" },
{ "TDRC", "DATE" },
// { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
{ "TDRL", "RELEASEDATE" },
{ "TDTG", "TAGGINGDATE" },
{ "TENC", "ENCODEDBY" },
{ "TEXT", "LYRICIST" },
{ "TFLT", "FILETYPE" },
//{ "TIPL", "INVOLVEDPEOPLE" }, handled separately
{ "TIT1", "CONTENTGROUP" }, // 'Work' in iTunes
{ "TIT2", "TITLE"},
{ "TIT3", "SUBTITLE" },
{ "TKEY", "INITIALKEY" },
{ "TLAN", "LANGUAGE" },
{ "TLEN", "LENGTH" },
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
{ "TMED", "MEDIA" },
{ "TMOO", "MOOD" },
{ "TOAL", "ORIGINALALBUM" },
{ "TOFN", "ORIGINALFILENAME" },
{ "TOLY", "ORIGINALLYRICIST" },
{ "TOPE", "ORIGINALARTIST" },
{ "TOWN", "OWNER" },
{ "TPE1", "ARTIST"},
{ "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
{ "TPE3", "CONDUCTOR" },
{ "TPE4", "REMIXER" }, // could also be ARRANGER
{ "TPOS", "DISCNUMBER" },
{ "TPRO", "PRODUCEDNOTICE" },
{ "TPUB", "LABEL" },
{ "TRCK", "TRACKNUMBER" },
{ "TRSN", "RADIOSTATION" },
{ "TRSO", "RADIOSTATIONOWNER" },
{ "TSOA", "ALBUMSORT" },
{ "TSOC", "COMPOSERSORT" },
{ "TSOP", "ARTISTSORT" },
{ "TSOT", "TITLESORT" },
{ "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
{ "TSRC", "ISRC" },
{ "TSSE", "ENCODING" },
std::make_pair("TALB", "ALBUM"),
std::make_pair("TBPM", "BPM"),
std::make_pair("TCOM", "COMPOSER"),
std::make_pair("TCON", "GENRE"),
std::make_pair("TCOP", "COPYRIGHT"),
std::make_pair("TDEN", "ENCODINGTIME"),
std::make_pair("TDLY", "PLAYLISTDELAY"),
std::make_pair("TDOR", "ORIGINALDATE"),
std::make_pair("TDRC", "DATE"),
// std::make_pair("TRDA", "DATE"), // id3 v2.3, replaced by TDRC in v2.4
// std::make_pair("TDAT", "DATE"), // id3 v2.3, replaced by TDRC in v2.4
// std::make_pair("TYER", "DATE"), // id3 v2.3, replaced by TDRC in v2.4
// std::make_pair("TIME", "DATE"), // id3 v2.3, replaced by TDRC in v2.4
std::make_pair("TDRL", "RELEASEDATE"),
std::make_pair("TDTG", "TAGGINGDATE"),
std::make_pair("TENC", "ENCODEDBY"),
std::make_pair("TEXT", "LYRICIST"),
std::make_pair("TFLT", "FILETYPE"),
//std::make_pair("TIPL", "INVOLVEDPEOPLE"), handled separately
std::make_pair("TIT1", "WORK"), // 'Work' in iTunes
std::make_pair("TIT2", "TITLE"),
std::make_pair("TIT3", "SUBTITLE"),
std::make_pair("TKEY", "INITIALKEY"),
std::make_pair("TLAN", "LANGUAGE"),
std::make_pair("TLEN", "LENGTH"),
//std::make_pair("TMCL", "MUSICIANCREDITS"), handled separately
std::make_pair("TMED", "MEDIA"),
std::make_pair("TMOO", "MOOD"),
std::make_pair("TOAL", "ORIGINALALBUM"),
std::make_pair("TOFN", "ORIGINALFILENAME"),
std::make_pair("TOLY", "ORIGINALLYRICIST"),
std::make_pair("TOPE", "ORIGINALARTIST"),
std::make_pair("TOWN", "OWNER"),
std::make_pair("TPE1", "ARTIST"),
std::make_pair("TPE2", "ALBUMARTIST"), // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
std::make_pair("TPE3", "CONDUCTOR"),
std::make_pair("TPE4", "REMIXER"), // could also be ARRANGER
std::make_pair("TPOS", "DISCNUMBER"),
std::make_pair("TPRO", "PRODUCEDNOTICE"),
std::make_pair("TPUB", "LABEL"),
std::make_pair("TRCK", "TRACKNUMBER"),
std::make_pair("TRSN", "RADIOSTATION"),
std::make_pair("TRSO", "RADIOSTATIONOWNER"),
std::make_pair("TSOA", "ALBUMSORT"),
std::make_pair("TSOC", "COMPOSERSORT"),
std::make_pair("TSOP", "ARTISTSORT"),
std::make_pair("TSOT", "TITLESORT"),
std::make_pair("TSO2", "ALBUMARTISTSORT"), // non-standard, used by iTunes
std::make_pair("TSRC", "ISRC"),
std::make_pair("TSSE", "ENCODING"),
// URL frames
{ "WCOP", "COPYRIGHTURL" },
{ "WOAF", "FILEWEBPAGE" },
{ "WOAR", "ARTISTWEBPAGE" },
{ "WOAS", "AUDIOSOURCEWEBPAGE" },
{ "WORS", "RADIOSTATIONWEBPAGE" },
{ "WPAY", "PAYMENTWEBPAGE" },
{ "WPUB", "PUBLISHERWEBPAGE" },
//{ "WXXX", "URL"}, handled specially
std::make_pair("WCOP", "COPYRIGHTURL"),
std::make_pair("WOAF", "FILEWEBPAGE"),
std::make_pair("WOAR", "ARTISTWEBPAGE"),
std::make_pair("WOAS", "AUDIOSOURCEWEBPAGE"),
std::make_pair("WORS", "RADIOSTATIONWEBPAGE"),
std::make_pair("WPAY", "PAYMENTWEBPAGE"),
std::make_pair("WPUB", "PUBLISHERWEBPAGE"),
//std::make_pair("WXXX", "URL"), handled specially
// Other frames
{ "COMM", "COMMENT" },
//{ "USLT", "LYRICS" }, handled specially
std::make_pair("COMM", "COMMENT"),
//std::make_pair("USLT", "LYRICS"), handled specially
// Apple iTunes proprietary frames
{ "PCST", "PODCAST" },
{ "TCAT", "PODCASTCATEGORY" },
{ "TDES", "PODCASTDESC" },
{ "TGID", "PODCASTID" },
{ "WFED", "PODCASTURL" },
{ "MVNM", "MOVEMENTNAME" },
{ "MVIN", "MOVEMENTNUMBER" },
{ "GRP1", "GROUPING" },
std::make_pair("PCST", "PODCAST"),
std::make_pair("TCAT", "PODCASTCATEGORY"),
std::make_pair("TDES", "PODCASTDESC"),
std::make_pair("TGID", "PODCASTID"),
std::make_pair("WFED", "PODCASTURL"),
std::make_pair("MVNM", "MOVEMENTNAME"),
std::make_pair("MVIN", "MOVEMENTNUMBER"),
std::make_pair("GRP1", "GROUPING"),
std::make_pair("TCMP", "COMPILATION"),
};
const size_t frameTranslationSize = sizeof(frameTranslation) / sizeof(frameTranslation[0]);
const char *txxxFrameTranslation[][2] = {
{ "MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID" },
{ "MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID" },
{ "MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MUSICBRAINZ ALBUM RELEASE COUNTRY", "RELEASECOUNTRY" },
{ "MUSICBRAINZ ALBUM STATUS", "RELEASESTATUS" },
{ "MUSICBRAINZ ALBUM TYPE", "RELEASETYPE" },
{ "MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MUSICBRAINZ RELEASE TRACK ID", "MUSICBRAINZ_RELEASETRACKID" },
{ "MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID" },
{ "ACOUSTID ID", "ACOUSTID_ID" },
{ "ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT" },
{ "MUSICIP PUID", "MUSICIP_PUID" },
const std::pair<const char *, const char *> txxxFrameTranslation[] = {
std::make_pair("MUSICBRAINZ ALBUM ID", "MUSICBRAINZ_ALBUMID"),
std::make_pair("MUSICBRAINZ ARTIST ID", "MUSICBRAINZ_ARTISTID"),
std::make_pair("MUSICBRAINZ ALBUM ARTIST ID", "MUSICBRAINZ_ALBUMARTISTID"),
std::make_pair("MUSICBRAINZ ALBUM RELEASE COUNTRY", "RELEASECOUNTRY"),
std::make_pair("MUSICBRAINZ ALBUM STATUS", "RELEASESTATUS"),
std::make_pair("MUSICBRAINZ ALBUM TYPE", "RELEASETYPE"),
std::make_pair("MUSICBRAINZ RELEASE GROUP ID", "MUSICBRAINZ_RELEASEGROUPID"),
std::make_pair("MUSICBRAINZ RELEASE TRACK ID", "MUSICBRAINZ_RELEASETRACKID"),
std::make_pair("MUSICBRAINZ WORK ID", "MUSICBRAINZ_WORKID"),
std::make_pair("ACOUSTID ID", "ACOUSTID_ID"),
std::make_pair("ACOUSTID FINGERPRINT", "ACOUSTID_FINGERPRINT"),
std::make_pair("MUSICIP PUID", "MUSICIP_PUID"),
};
const size_t txxxFrameTranslationSize = sizeof(txxxFrameTranslation) / sizeof(txxxFrameTranslation[0]);
// list of deprecated frames and their successors
const char *deprecatedFrames[][2] = {
{"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3)
{"TDAT", "TDRC"}, // 2.3 -> 2.4
{"TYER", "TDRC"}, // 2.3 -> 2.4
{"TIME", "TDRC"}, // 2.3 -> 2.4
const std::pair<const char *, const char *> deprecatedFrames[] = {
std::make_pair("TRDA", "TDRC"), // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3)
std::make_pair("TDAT", "TDRC"), // 2.3 -> 2.4
std::make_pair("TYER", "TDRC"), // 2.3 -> 2.4
std::make_pair("TIME", "TDRC"), // 2.3 -> 2.4
};
const size_t deprecatedFramesSize = sizeof(deprecatedFrames) / sizeof(deprecatedFrames[0]);
}
} // namespace
String Frame::frameIDToKey(const ByteVector &id)
{
ByteVector id24 = id;
for(size_t i = 0; i < deprecatedFramesSize; ++i) {
if(id24 == deprecatedFrames[i][0]) {
id24 = deprecatedFrames[i][1];
if(id24 == deprecatedFrames[i].first) {
id24 = deprecatedFrames[i].second;
break;
}
}
for(size_t i = 0; i < frameTranslationSize; ++i) {
if(id24 == frameTranslation[i][0])
return frameTranslation[i][1];
if(id24 == frameTranslation[i].first)
return frameTranslation[i].second;
}
return String();
}
@@ -448,8 +444,8 @@ ByteVector Frame::keyToFrameID(const String &s)
{
const String key = s.upper();
for(size_t i = 0; i < frameTranslationSize; ++i) {
if(key == frameTranslation[i][1])
return frameTranslation[i][0];
if(key == frameTranslation[i].second)
return frameTranslation[i].first;
}
return ByteVector();
}
@@ -458,8 +454,8 @@ String Frame::txxxToKey(const String &description)
{
const String d = description.upper();
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
if(d == txxxFrameTranslation[i][0])
return txxxFrameTranslation[i][1];
if(d == txxxFrameTranslation[i].first)
return txxxFrameTranslation[i].second;
}
return d;
}
@@ -468,8 +464,8 @@ String Frame::keyToTXXX(const String &s)
{
const String key = s.upper();
for(size_t i = 0; i < txxxFrameTranslationSize; ++i) {
if(key == txxxFrameTranslation[i][1])
return txxxFrameTranslation[i][0];
if(key == txxxFrameTranslation[i].second)
return txxxFrameTranslation[i].first;
}
return s;
}
@@ -486,19 +482,19 @@ PropertyMap Frame::asProperties() const
if(id == "TXXX")
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
// Apple proprietary WFED (Podcast URL), MVNM (Movement Name), MVIN (Movement Number), GRP1 (Grouping) are in fact text frames.
else if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN" || id == "GRP1")
if(id[0] == 'T' || id == "WFED" || id == "MVNM" || id == "MVIN" || id == "GRP1")
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
else if(id == "WXXX")
if(id == "WXXX")
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
else if(id[0] == 'W')
if(id[0] == 'W')
return dynamic_cast< const UrlLinkFrame* >(this)->asProperties();
else if(id == "COMM")
if(id == "COMM")
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
else if(id == "USLT")
if(id == "USLT")
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
else if(id == "UFID")
if(id == "UFID")
return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties();
else if(id == "PCST")
if(id == "PCST")
return dynamic_cast< const PodcastFrame* >(this)->asProperties();
PropertyMap m;
m.unsupportedData().append(id);

View File

@@ -55,6 +55,8 @@ namespace TagLib {
{
friend class Tag;
friend class FrameFactory;
friend class TableOfContentsFrame;
friend class ChapterFrame;
public:

View File

@@ -85,7 +85,7 @@ namespace
frame->setText(newfields);
}
}
} // namespace
class FrameFactory::FrameFactoryPrivate
{
@@ -268,11 +268,9 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, const Header *tagHe
if(frameID != "WXXX") {
return new UrlLinkFrame(data, header);
}
else {
UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
d->setTextEncoding(f);
return f;
}
UserUrlLinkFrame *f = new UserUrlLinkFrame(data, header);
d->setTextEncoding(f);
return f;
}
// Unsynchronized lyric/text transcription (frames 4.8)
@@ -342,19 +340,20 @@ void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
{
TextIdentificationFrame *tdrc =
dynamic_cast<TextIdentificationFrame *>(tag->frameList("TDRC").front());
UnknownFrame *tdat = static_cast<UnknownFrame *>(tag->frameList("TDAT").front());
UnknownFrame *tdat = dynamic_cast<UnknownFrame *>(tag->frameList("TDAT").front());
if(tdrc &&
tdrc->fieldList().size() == 1 &&
tdrc->fieldList().front().size() == 4 &&
tdat &&
tdat->data().size() >= 5)
{
String date(tdat->data().mid(1), String::Type(tdat->data()[0]));
if(date.length() == 4) {
tdrc->setText(tdrc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2));
if(tag->frameList("TIME").size() == 1) {
UnknownFrame *timeframe = static_cast<UnknownFrame *>(tag->frameList("TIME").front());
if(timeframe->data().size() >= 5) {
UnknownFrame *timeframe = dynamic_cast<UnknownFrame *>(tag->frameList("TIME").front());
if(timeframe && timeframe->data().size() >= 5) {
String time(timeframe->data().mid(1), String::Type(timeframe->data()[0]));
if(time.length() == 4) {
tdrc->setText(tdrc->toString() + 'T' + time.substr(0, 2) + ':' + time.substr(2, 2));
@@ -394,90 +393,90 @@ FrameFactory::~FrameFactory()
namespace
{
// Frame conversion table ID3v2.2 -> 2.4
const char *frameConversion2[][2] = {
{ "BUF", "RBUF" },
{ "CNT", "PCNT" },
{ "COM", "COMM" },
{ "CRA", "AENC" },
{ "ETC", "ETCO" },
{ "GEO", "GEOB" },
{ "IPL", "TIPL" },
{ "MCI", "MCDI" },
{ "MLL", "MLLT" },
{ "POP", "POPM" },
{ "REV", "RVRB" },
{ "SLT", "SYLT" },
{ "STC", "SYTC" },
{ "TAL", "TALB" },
{ "TBP", "TBPM" },
{ "TCM", "TCOM" },
{ "TCO", "TCON" },
{ "TCP", "TCMP" },
{ "TCR", "TCOP" },
{ "TDY", "TDLY" },
{ "TEN", "TENC" },
{ "TFT", "TFLT" },
{ "TKE", "TKEY" },
{ "TLA", "TLAN" },
{ "TLE", "TLEN" },
{ "TMT", "TMED" },
{ "TOA", "TOAL" },
{ "TOF", "TOFN" },
{ "TOL", "TOLY" },
{ "TOR", "TDOR" },
{ "TOT", "TOAL" },
{ "TP1", "TPE1" },
{ "TP2", "TPE2" },
{ "TP3", "TPE3" },
{ "TP4", "TPE4" },
{ "TPA", "TPOS" },
{ "TPB", "TPUB" },
{ "TRC", "TSRC" },
{ "TRD", "TDRC" },
{ "TRK", "TRCK" },
{ "TS2", "TSO2" },
{ "TSA", "TSOA" },
{ "TSC", "TSOC" },
{ "TSP", "TSOP" },
{ "TSS", "TSSE" },
{ "TST", "TSOT" },
{ "TT1", "TIT1" },
{ "TT2", "TIT2" },
{ "TT3", "TIT3" },
{ "TXT", "TOLY" },
{ "TXX", "TXXX" },
{ "TYE", "TDRC" },
{ "UFI", "UFID" },
{ "ULT", "USLT" },
{ "WAF", "WOAF" },
{ "WAR", "WOAR" },
{ "WAS", "WOAS" },
{ "WCM", "WCOM" },
{ "WCP", "WCOP" },
{ "WPB", "WPUB" },
{ "WXX", "WXXX" },
const std::pair<const char *, const char *> frameConversion2[] = {
std::make_pair("BUF", "RBUF"),
std::make_pair("CNT", "PCNT"),
std::make_pair("COM", "COMM"),
std::make_pair("CRA", "AENC"),
std::make_pair("ETC", "ETCO"),
std::make_pair("GEO", "GEOB"),
std::make_pair("IPL", "TIPL"),
std::make_pair("MCI", "MCDI"),
std::make_pair("MLL", "MLLT"),
std::make_pair("POP", "POPM"),
std::make_pair("REV", "RVRB"),
std::make_pair("SLT", "SYLT"),
std::make_pair("STC", "SYTC"),
std::make_pair("TAL", "TALB"),
std::make_pair("TBP", "TBPM"),
std::make_pair("TCM", "TCOM"),
std::make_pair("TCO", "TCON"),
std::make_pair("TCP", "TCMP"),
std::make_pair("TCR", "TCOP"),
std::make_pair("TDY", "TDLY"),
std::make_pair("TEN", "TENC"),
std::make_pair("TFT", "TFLT"),
std::make_pair("TKE", "TKEY"),
std::make_pair("TLA", "TLAN"),
std::make_pair("TLE", "TLEN"),
std::make_pair("TMT", "TMED"),
std::make_pair("TOA", "TOAL"),
std::make_pair("TOF", "TOFN"),
std::make_pair("TOL", "TOLY"),
std::make_pair("TOR", "TDOR"),
std::make_pair("TOT", "TOAL"),
std::make_pair("TP1", "TPE1"),
std::make_pair("TP2", "TPE2"),
std::make_pair("TP3", "TPE3"),
std::make_pair("TP4", "TPE4"),
std::make_pair("TPA", "TPOS"),
std::make_pair("TPB", "TPUB"),
std::make_pair("TRC", "TSRC"),
std::make_pair("TRD", "TDRC"),
std::make_pair("TRK", "TRCK"),
std::make_pair("TS2", "TSO2"),
std::make_pair("TSA", "TSOA"),
std::make_pair("TSC", "TSOC"),
std::make_pair("TSP", "TSOP"),
std::make_pair("TSS", "TSSE"),
std::make_pair("TST", "TSOT"),
std::make_pair("TT1", "TIT1"),
std::make_pair("TT2", "TIT2"),
std::make_pair("TT3", "TIT3"),
std::make_pair("TXT", "TOLY"),
std::make_pair("TXX", "TXXX"),
std::make_pair("TYE", "TDRC"),
std::make_pair("UFI", "UFID"),
std::make_pair("ULT", "USLT"),
std::make_pair("WAF", "WOAF"),
std::make_pair("WAR", "WOAR"),
std::make_pair("WAS", "WOAS"),
std::make_pair("WCM", "WCOM"),
std::make_pair("WCP", "WCOP"),
std::make_pair("WPB", "WPUB"),
std::make_pair("WXX", "WXXX"),
// Apple iTunes nonstandard frames
{ "PCS", "PCST" },
{ "TCT", "TCAT" },
{ "TDR", "TDRL" },
{ "TDS", "TDES" },
{ "TID", "TGID" },
{ "WFD", "WFED" },
{ "MVN", "MVNM" },
{ "MVI", "MVIN" },
{ "GP1", "GRP1" },
std::make_pair("PCS", "PCST"),
std::make_pair("TCT", "TCAT"),
std::make_pair("TDR", "TDRL"),
std::make_pair("TDS", "TDES"),
std::make_pair("TID", "TGID"),
std::make_pair("WFD", "WFED"),
std::make_pair("MVN", "MVNM"),
std::make_pair("MVI", "MVIN"),
std::make_pair("GP1", "GRP1"),
};
const size_t frameConversion2Size = sizeof(frameConversion2) / sizeof(frameConversion2[0]);
// Frame conversion table ID3v2.3 -> 2.4
const char *frameConversion3[][2] = {
{ "TORY", "TDOR" },
{ "TYER", "TDRC" },
{ "IPLS", "TIPL" },
const std::pair<const char *, const char *> frameConversion3[] = {
std::make_pair("TORY", "TDOR"),
std::make_pair("TYER", "TDRC"),
std::make_pair("IPLS", "TIPL"),
};
const size_t frameConversion3Size = sizeof(frameConversion3) / sizeof(frameConversion3[0]);
}
} // namespace
bool FrameFactory::updateFrame(Frame::Header *header) const
{
@@ -504,8 +503,8 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
// the frames to their 4 byte ID3v2.4 equivalent.
for(size_t i = 0; i < frameConversion2Size; ++i) {
if(frameID == frameConversion2[i][0]) {
header->setFrameID(frameConversion2[i][1]);
if(frameID == frameConversion2[i].first) {
header->setFrameID(frameConversion2[i].second);
break;
}
}
@@ -528,8 +527,8 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
}
for(size_t i = 0; i < frameConversion3Size; ++i) {
if(frameID == frameConversion3[i][0]) {
header->setFrameID(frameConversion3[i][1]);
if(frameID == frameConversion3[i].first) {
header->setFrameID(frameConversion3[i].second);
break;
}
}

View File

@@ -137,8 +137,7 @@ unsigned int Header::completeTagSize() const
{
if(d->footerPresent)
return d->tagSize + size() + Footer::size();
else
return d->tagSize + size();
return d->tagSize + size();
}
void Header::setTagSize(unsigned int s)

View File

@@ -74,6 +74,10 @@ ByteVector SynchData::fromUInt(unsigned int value)
ByteVector SynchData::decode(const ByteVector &data)
{
if (data.size() == 0) {
return ByteVector();
}
// We have this optimized method instead of using ByteVector::replace(),
// since it makes a great difference when decoding huge unsynchronized frames.

View File

@@ -64,7 +64,7 @@ namespace
}
return false;
}
}
} // namespace
class ID3v2::Tag::TagPrivate
{
@@ -120,14 +120,12 @@ String Latin1StringHandler::parse(const ByteVector &data) const
////////////////////////////////////////////////////////////////////////////////
ID3v2::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
d->factory = FrameFactory::instance();
}
ID3v2::Tag::Tag(File *file, long tagOffset, const FrameFactory *factory) :
TagLib::Tag(),
d(new TagPrivate())
{
d->factory = factory;
@@ -187,8 +185,10 @@ String ID3v2::Tag::genre() const
// should be separated by " / " instead of " ". For the moment to keep
// the behavior the same as released versions it is being left with " ".
if(d->frameListMap["TCON"].isEmpty() ||
!dynamic_cast<TextIdentificationFrame *>(d->frameListMap["TCON"].front()))
const FrameList &tconFrames = d->frameListMap["TCON"];
TextIdentificationFrame *f;
if(tconFrames.isEmpty() ||
!(f = dynamic_cast<TextIdentificationFrame *>(tconFrames.front())))
{
return String();
}
@@ -199,9 +199,6 @@ String ID3v2::Tag::genre() const
// appended to the genre string. Multiple fields will be appended as the
// string is built.
TextIdentificationFrame *f = static_cast<TextIdentificationFrame *>(
d->frameListMap["TCON"].front());
StringList fields = f->fieldList();
StringList genres;

View File

@@ -93,7 +93,7 @@ namespace
AudioProperties *audioProperties() const { return 0; }
bool save() { return false; }
};
}
} // namespace
bool MPEG::File::isSupported(IOStream *stream)
{

View File

@@ -41,10 +41,9 @@ namespace
{
if(page->header()->lastPacketCompleted())
return page->firstPacketIndex() + page->packetCount();
else
return page->firstPacketIndex() + page->packetCount() - 1;
return page->firstPacketIndex() + page->packetCount() - 1;
}
}
} // namespace
class Ogg::File::FilePrivate
{

View File

@@ -61,13 +61,11 @@ public:
////////////////////////////////////////////////////////////////////////////////
Ogg::XiphComment::XiphComment() :
TagLib::Tag(),
d(new XiphCommentPrivate())
{
}
Ogg::XiphComment::XiphComment(const ByteVector &data) :
TagLib::Tag(),
d(new XiphCommentPrivate())
{
parse(data);
@@ -80,35 +78,34 @@ Ogg::XiphComment::~XiphComment()
String Ogg::XiphComment::title() const
{
if(d->fieldListMap["TITLE"].isEmpty())
return String();
return d->fieldListMap["TITLE"].toString();
StringList value = d->fieldListMap.value("TITLE");
return value.isEmpty() ? String() : value.toString();
}
String Ogg::XiphComment::artist() const
{
if(d->fieldListMap["ARTIST"].isEmpty())
return String();
return d->fieldListMap["ARTIST"].toString();
StringList value = d->fieldListMap.value("ARTIST");
return value.isEmpty() ? String() : value.toString();
}
String Ogg::XiphComment::album() const
{
if(d->fieldListMap["ALBUM"].isEmpty())
return String();
return d->fieldListMap["ALBUM"].toString();
StringList value = d->fieldListMap.value("ALBUM");
return value.isEmpty() ? String() : value.toString();
}
String Ogg::XiphComment::comment() const
{
if(!d->fieldListMap["DESCRIPTION"].isEmpty()) {
StringList value = d->fieldListMap.value("DESCRIPTION");
if(!value.isEmpty()) {
d->commentField = "DESCRIPTION";
return d->fieldListMap["DESCRIPTION"].toString();
return value.toString();
}
if(!d->fieldListMap["COMMENT"].isEmpty()) {
value = d->fieldListMap.value("COMMENT");
if(!value.isEmpty()) {
d->commentField = "COMMENT";
return d->fieldListMap["COMMENT"].toString();
return value.toString();
}
return String();
@@ -116,26 +113,29 @@ String Ogg::XiphComment::comment() const
String Ogg::XiphComment::genre() const
{
if(d->fieldListMap["GENRE"].isEmpty())
return String();
return d->fieldListMap["GENRE"].toString();
StringList value = d->fieldListMap.value("GENRE");
return value.isEmpty() ? String() : value.toString();
}
unsigned int Ogg::XiphComment::year() const
{
if(!d->fieldListMap["DATE"].isEmpty())
return d->fieldListMap["DATE"].front().toInt();
if(!d->fieldListMap["YEAR"].isEmpty())
return d->fieldListMap["YEAR"].front().toInt();
StringList value = d->fieldListMap.value("DATE");
if(!value.isEmpty())
return value.front().toInt();
value = d->fieldListMap.value("YEAR");
if(!value.isEmpty())
return value.front().toInt();
return 0;
}
unsigned int Ogg::XiphComment::track() const
{
if(!d->fieldListMap["TRACKNUMBER"].isEmpty())
return d->fieldListMap["TRACKNUMBER"].front().toInt();
if(!d->fieldListMap["TRACKNUM"].isEmpty())
return d->fieldListMap["TRACKNUM"].front().toInt();
StringList value = d->fieldListMap.value("TRACKNUMBER");
if(!value.isEmpty())
return value.front().toInt();
value = d->fieldListMap.value("TRACKNUM");
if(!value.isEmpty())
return value.front().toInt();
return 0;
}
@@ -157,7 +157,7 @@ void Ogg::XiphComment::setAlbum(const String &s)
void Ogg::XiphComment::setComment(const String &s)
{
if(d->commentField.isEmpty()) {
if(!d->fieldListMap["DESCRIPTION"].isEmpty())
if(!d->fieldListMap.value("DESCRIPTION").isEmpty())
d->commentField = "DESCRIPTION";
else
d->commentField = "COMMENT";
@@ -324,7 +324,7 @@ void Ogg::XiphComment::removeAllFields()
bool Ogg::XiphComment::contains(const String &key) const
{
return !d->fieldListMap[key.upper()].isEmpty();
return !d->fieldListMap.value(key.upper()).isEmpty();
}
void Ogg::XiphComment::removePicture(FLAC::Picture *picture, bool del)

View File

@@ -35,6 +35,11 @@
#include "flacpicture.h"
#include "taglib_export.h"
#ifdef _MSC_VER
// Explained at end of tpropertymap.cpp
extern template class TAGLIB_EXPORT TagLib::Map<TagLib::String, TagLib::StringList>;
#endif
namespace TagLib {
namespace Ogg {

View File

@@ -71,14 +71,12 @@ ByteVector RIFF::Info::StringHandler::render(const String &s) const
////////////////////////////////////////////////////////////////////////////////
RIFF::Info::Tag::Tag(const ByteVector &data) :
TagLib::Tag(),
d(new TagPrivate())
{
parse(data);
}
RIFF::Info::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
@@ -178,8 +176,7 @@ String RIFF::Info::Tag::fieldText(const ByteVector &id) const
{
if(d->fieldListMap.contains(id))
return String(d->fieldListMap[id]);
else
return String();
return String();
}
void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s)
@@ -221,8 +218,7 @@ ByteVector RIFF::Info::Tag::render() const
if(data.size() == 4)
return ByteVector();
else
return data;
return data;
}
void RIFF::Info::Tag::setStringHandler(const StringHandler *handler)

View File

@@ -129,12 +129,12 @@ bool S3M::File::save()
StringList lines = d->tag.comment().split("\n");
// write comment as sample names:
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(96L + length + ((long)i << 1));
seek(96L + length + (static_cast<long>(i) << 1));
unsigned short instrumentOffset = 0;
if(!readU16L(instrumentOffset))
return false;
seek(((long)instrumentOffset << 4) + 48);
seek((static_cast<long>(instrumentOffset) << 4) + 48);
if(i < lines.size())
writeString(lines[i], 27);
@@ -214,10 +214,10 @@ void S3M::File::read(bool)
// instead samples (SCRS).
StringList comment;
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(96L + length + ((long)i << 1));
seek(96L + length + (static_cast<long>(i) << 1));
READ_U16L_AS(sampleHeaderOffset);
seek((long)sampleHeaderOffset << 4);
seek(static_cast<long>(sampleHeaderOffset) << 4);
READ_BYTE_AS(sampleType);
READ_STRING_AS(dosFileName, 13);

View File

@@ -120,16 +120,16 @@ PropertyMap TagUnion::properties() const
if(dynamic_cast<const ID3v1::Tag *>(d->tags[i]))
return dynamic_cast<const ID3v1::Tag *>(d->tags[i])->properties();
else if(dynamic_cast<const ID3v2::Tag *>(d->tags[i]))
if(dynamic_cast<const ID3v2::Tag *>(d->tags[i]))
return dynamic_cast<const ID3v2::Tag *>(d->tags[i])->properties();
else if(dynamic_cast<const APE::Tag *>(d->tags[i]))
if(dynamic_cast<const APE::Tag *>(d->tags[i]))
return dynamic_cast<const APE::Tag *>(d->tags[i])->properties();
else if(dynamic_cast<const Ogg::XiphComment *>(d->tags[i]))
if(dynamic_cast<const Ogg::XiphComment *>(d->tags[i]))
return dynamic_cast<const Ogg::XiphComment *>(d->tags[i])->properties();
else if(dynamic_cast<const RIFF::Info::Tag *>(d->tags[i]))
if(dynamic_cast<const RIFF::Info::Tag *>(d->tags[i]))
return dynamic_cast<const RIFF::Info::Tag *>(d->tags[i])->properties();
}
}

View File

@@ -38,11 +38,22 @@ long Utils::findID3v1(File *file)
if(!file->isValid())
return -1;
file->seek(-128, File::End);
const long p = file->tell();
// Differentiate between a match of APEv2 magic and a match of ID3v1 magic.
if(file->readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
if (file->length() >= 131) {
file->seek(-131, File::End);
const long p = file->tell() + 3;
const TagLib::ByteVector data = file->readBlock(8);
if(data.containsAt(ID3v1::Tag::fileIdentifier(), 3) && (data != APE::Tag::fileIdentifier()))
return p;
} else {
file->seek(-128, File::End);
const long p = file->tell();
if(file->readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
}
return -1;
}

View File

@@ -29,7 +29,7 @@
#include "taglib_config.h"
#define TAGLIB_MAJOR_VERSION 1
#define TAGLIB_MINOR_VERSION 12
#define TAGLIB_MINOR_VERSION 13
#define TAGLIB_PATCH_VERSION 0
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) || defined(__clang__)

View File

@@ -145,8 +145,7 @@ T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst)
if(swap)
return Utils::byteSwap(tmp);
else
return tmp;
return tmp;
}
template <class T>
@@ -242,14 +241,12 @@ long double toFloat80(const ByteVector &v, size_t offset)
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);
val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63);
}
if(negative)
return -val;
else
return val;
return val;
}
class ByteVector::ByteVectorPrivate
@@ -300,8 +297,7 @@ ByteVector ByteVector::fromCString(const char *s, unsigned int length)
{
if(length == 0xffffffff)
return ByteVector(s, static_cast<unsigned int>(::strlen(s)));
else
return ByteVector(s, length);
return ByteVector(s, length);
}
ByteVector ByteVector::fromUInt(unsigned int value, bool mostSignificantByteFirst)
@@ -443,8 +439,7 @@ int ByteVector::rfind(const ByteVector &pattern, unsigned int offset, int byteAl
if(pos == -1)
return -1;
else
return size() - pos - pattern.size();
return size() - pos - pattern.size();
}
bool ByteVector::containsAt(const ByteVector &pattern, unsigned int offset, unsigned int patternOffset, unsigned int patternLength) const
@@ -843,8 +838,7 @@ bool ByteVector::operator<(const ByteVector &v) const
const int result = ::memcmp(data(), v.data(), std::min(size(), v.size()));
if(result != 0)
return result < 0;
else
return size() < v.size();
return size() < v.size();
}
bool ByteVector::operator>(const ByteVector &v) const
@@ -925,8 +919,8 @@ ByteVector ByteVector::fromBase64(const ByteVector & input)
ByteVector output(len);
const unsigned char * src = (const unsigned char*) input.data();
unsigned char * dst = (unsigned char*) output.data();
const unsigned char * src = reinterpret_cast<const unsigned char*>(input.data());
unsigned char * dst = reinterpret_cast<unsigned char*>(output.data());
while(4 <= len) {
@@ -976,7 +970,7 @@ ByteVector ByteVector::fromBase64(const ByteVector & input)
// Only return output if we processed all bytes
if(len == 0) {
output.resize(static_cast<unsigned int>(dst - (unsigned char*) output.data()));
output.resize(static_cast<unsigned int>(dst - reinterpret_cast<unsigned char*>(output.data())));
return output;
}
return ByteVector();
@@ -1030,7 +1024,7 @@ void ByteVector::detach()
ByteVector().swap(*this);
}
}
}
} // namespace TagLib
////////////////////////////////////////////////////////////////////////////////
// related functions

View File

@@ -70,7 +70,7 @@ ByteVectorList ByteVectorList::split(const ByteVector &v, const ByteVector &patt
// public members
////////////////////////////////////////////////////////////////////////////////
ByteVectorList::ByteVectorList() : List<ByteVector>()
ByteVectorList::ByteVectorList()
{
}

View File

@@ -59,6 +59,6 @@ namespace TagLib
debugListener->printMessage(msg);
}
}
}
} // namespace TagLib
#endif

View File

@@ -61,7 +61,7 @@ namespace
};
DefaultListener defaultListener;
}
} // namespace
namespace TagLib
{
@@ -82,4 +82,4 @@ namespace TagLib
else
debugListener = &defaultListener;
}
}
} // namespace TagLib

View File

@@ -185,42 +185,41 @@ PropertyMap File::setProperties(const PropertyMap &properties)
{
if(dynamic_cast<APE::File* >(this))
return dynamic_cast<APE::File* >(this)->setProperties(properties);
else if(dynamic_cast<FLAC::File* >(this))
if(dynamic_cast<FLAC::File* >(this))
return dynamic_cast<FLAC::File* >(this)->setProperties(properties);
else if(dynamic_cast<IT::File* >(this))
if(dynamic_cast<IT::File* >(this))
return dynamic_cast<IT::File* >(this)->setProperties(properties);
else if(dynamic_cast<Mod::File* >(this))
if(dynamic_cast<Mod::File* >(this))
return dynamic_cast<Mod::File* >(this)->setProperties(properties);
else if(dynamic_cast<MPC::File* >(this))
if(dynamic_cast<MPC::File* >(this))
return dynamic_cast<MPC::File* >(this)->setProperties(properties);
else if(dynamic_cast<MPEG::File* >(this))
if(dynamic_cast<MPEG::File* >(this))
return dynamic_cast<MPEG::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::FLAC::File* >(this))
if(dynamic_cast<Ogg::FLAC::File* >(this))
return dynamic_cast<Ogg::FLAC::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Speex::File* >(this))
if(dynamic_cast<Ogg::Speex::File* >(this))
return dynamic_cast<Ogg::Speex::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Opus::File* >(this))
if(dynamic_cast<Ogg::Opus::File* >(this))
return dynamic_cast<Ogg::Opus::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Vorbis::File* >(this))
if(dynamic_cast<Ogg::Vorbis::File* >(this))
return dynamic_cast<Ogg::Vorbis::File* >(this)->setProperties(properties);
else if(dynamic_cast<RIFF::AIFF::File* >(this))
if(dynamic_cast<RIFF::AIFF::File* >(this))
return dynamic_cast<RIFF::AIFF::File* >(this)->setProperties(properties);
else if(dynamic_cast<RIFF::WAV::File* >(this))
if(dynamic_cast<RIFF::WAV::File* >(this))
return dynamic_cast<RIFF::WAV::File* >(this)->setProperties(properties);
else if(dynamic_cast<S3M::File* >(this))
if(dynamic_cast<S3M::File* >(this))
return dynamic_cast<S3M::File* >(this)->setProperties(properties);
else if(dynamic_cast<TrueAudio::File* >(this))
if(dynamic_cast<TrueAudio::File* >(this))
return dynamic_cast<TrueAudio::File* >(this)->setProperties(properties);
else if(dynamic_cast<WavPack::File* >(this))
if(dynamic_cast<WavPack::File* >(this))
return dynamic_cast<WavPack::File* >(this)->setProperties(properties);
else if(dynamic_cast<XM::File* >(this))
if(dynamic_cast<XM::File* >(this))
return dynamic_cast<XM::File* >(this)->setProperties(properties);
else if(dynamic_cast<MP4::File* >(this))
if(dynamic_cast<MP4::File* >(this))
return dynamic_cast<MP4::File* >(this)->setProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
if(dynamic_cast<ASF::File* >(this))
return dynamic_cast<ASF::File* >(this)->setProperties(properties);
else
return tag()->setProperties(properties);
return tag()->setProperties(properties);
}
ByteVector File::readBlock(unsigned long length)

View File

@@ -124,7 +124,7 @@ namespace
}
#endif // _WIN32
}
} // namespace
class FileStream::FileStreamPrivate
{
@@ -250,7 +250,7 @@ void FileStream::insert(const ByteVector &data, unsigned long start, unsigned lo
writeBlock(data);
return;
}
else if(data.size() < replace) {
if(data.size() < replace) {
seek(start);
writeBlock(data);
removeBlock(start + data.size(), replace - data.size());

View File

@@ -153,6 +153,14 @@ namespace TagLib {
*/
Map<Key, T> &erase(const Key &key);
/*!
* Returns the value associated with \a key.
*
* If the map does not contain \a key, it returns defaultValue.
* If no defaultValue is specified, it returns a default-constructed value.
*/
const T value(const Key &key, const T &defaultValue = T()) const;
/*!
* Returns a reference to the value associated with \a key.
*

View File

@@ -155,6 +155,13 @@ unsigned int Map<Key, T>::size() const
return static_cast<unsigned int>(d->map.size());
}
template <class Key, class T>
const T Map<Key, T>::value(const Key &key, const T &defaultValue) const
{
ConstIterator it = d->map.find(key);
return it != d->map.end() ? it->second : defaultValue;
}
template <class Key, class T>
const T &Map<Key, T>::operator[](const Key &key) const
{

View File

@@ -28,7 +28,7 @@
using namespace TagLib;
PropertyMap::PropertyMap() : SimplePropertyMap()
PropertyMap::PropertyMap()
{
}
@@ -117,6 +117,12 @@ PropertyMap &PropertyMap::merge(const PropertyMap &other)
return *this;
}
const StringList PropertyMap::value(const String &key,
const StringList &defaultValue) const
{
return SimplePropertyMap::value(key.upper(), defaultValue);
}
const StringList &PropertyMap::operator[](const String &key) const
{
return SimplePropertyMap::operator[](key.upper());
@@ -177,3 +183,12 @@ const StringList &PropertyMap::unsupportedData() const
{
return unsupported;
}
#ifdef _MSC_VER
// When building with shared libraries and tests, MSVC will fail with
// "already defined in test_opus.obj" as soon as operator[] of
// Ogg::FieldListMap is used because this will instantiate the same template
// Map<String, StringList>. Therefore this template is instantiated here
// and declared extern in the headers using it.
template class TagLib::Map<TagLib::String, TagLib::StringList>;
#endif

View File

@@ -29,6 +29,11 @@
#include "tmap.h"
#include "tstringlist.h"
#ifdef _MSC_VER
// Explained at end of tpropertymap.cpp
extern template class TAGLIB_EXPORT TagLib::Map<TagLib::String, TagLib::StringList>;
#endif
namespace TagLib {
typedef Map<String,StringList> SimplePropertyMap;
@@ -186,6 +191,15 @@ namespace TagLib {
*/
PropertyMap &merge(const PropertyMap &other);
/*!
* Returns the value associated with \a key.
*
* If the map does not contain \a key, it returns defaultValue.
* If no defaultValue is specified, it returns an empty string list.
*/
const StringList value(const String &key,
const StringList &defaultValue = StringList()) const;
/*!
* Returns a reference to the value associated with \a key.
*

View File

@@ -52,7 +52,7 @@
# define ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
# define ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
#else
# define ATOMIC_INT volatile int
# define ATOMIC_INT int
# define ATOMIC_INC(x) (++x)
# define ATOMIC_DEC(x) (--x)
#endif
@@ -93,4 +93,4 @@ namespace TagLib
{
return static_cast<int>(d->refCount);
}
}
} // namespace TagLib

View File

@@ -44,8 +44,7 @@ namespace
{
if(Utils::systemByteOrder() == Utils::LittleEndian)
return String::UTF16LE;
else
return String::UTF16BE;
return String::UTF16BE;
}
// Converts a Latin-1 string into UTF-16(without BOM/CPU byte order)
@@ -134,15 +133,15 @@ namespace
data[i] = c;
}
}
}
} // namespace
namespace TagLib {
class String::StringPrivate : public RefCounter
{
public:
StringPrivate() :
RefCounter() {}
StringPrivate()
{}
/*!
* Stores string in UTF-16. The byte order depends on the CPU endian.
@@ -342,10 +341,8 @@ StringList String::split(const String &separator) const
list.append(substr(index, size() - index));
break;
}
else {
list.append(substr(index, sep - index));
index = sep + separator.size();
}
list.append(substr(index, sep - index));
index = sep + separator.size();
}
return list;
}
@@ -362,8 +359,7 @@ String String::substr(unsigned int position, unsigned int n) const
{
if(position == 0 && n >= size())
return *this;
else
return String(d->data.substr(position, n));
return String(d->data.substr(position, n));
}
String &String::append(const String &s)
@@ -712,7 +708,7 @@ void String::detach()
////////////////////////////////////////////////////////////////////////////////
const String::Type String::WCharByteOrder = wcharByteOrder();
}
} // namespace TagLib
////////////////////////////////////////////////////////////////////////////////
// related non-member functions

View File

@@ -55,7 +55,7 @@ StringList StringList::split(const String &s, const String &pattern)
// public members
////////////////////////////////////////////////////////////////////////////////
StringList::StringList() : List<String>()
StringList::StringList()
{
}
@@ -65,12 +65,12 @@ StringList::StringList(const StringList &l) : List<String>(l)
}
StringList::StringList(const String &s) : List<String>()
StringList::StringList(const String &s)
{
append(s);
}
StringList::StringList(const ByteVectorList &bl, String::Type t) : List<String>()
StringList::StringList(const ByteVectorList &bl, String::Type t)
{
ByteVectorList::ConstIterator i = bl.begin();
for(;i != bl.end(); i++) {

View File

@@ -110,6 +110,6 @@ namespace TagLib {
* \related TagLib::StringList
* Send the StringList to an output stream.
*/
std::ostream &operator<<(std::ostream &s, const TagLib::StringList &l);
std::ostream TAGLIB_EXPORT &operator<<(std::ostream &s, const TagLib::StringList &l);
#endif

View File

@@ -39,7 +39,7 @@ namespace TagLib {
/*!
* Returns whether or not zlib is installed and ready to use.
*/
bool isAvailable();
bool TAGLIB_EXPORT isAvailable();
/*!
* Decompress \a data by zlib.

View File

@@ -255,7 +255,7 @@ namespace
return getMetaDataChunk(block, ID_DSD_BLOCK);
}
}
} // namespace
void WavPack::Properties::read(File *file, long streamLength)
{

View File

@@ -35,6 +35,9 @@
using namespace TagLib;
using namespace XM;
namespace
{
/*!
* The Reader classes are helpers to make handling of the stripped XM
* format more easy. In the stripped XM format certain header sizes might
@@ -130,7 +133,7 @@ public:
{
ByteVector data = file.readBlock(std::min(m_size, limit));
unsigned int count = data.size();
int index = data.find((char) 0);
int index = data.find(static_cast<char>(0));
if(index > -1) {
data.resize(index);
}
@@ -343,11 +346,13 @@ private:
List<Reader*> m_readers;
};
} // namespace
class XM::File::FilePrivate
{
public:
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: tag(), properties(propertiesStyle)
: properties(propertiesStyle)
{
}
@@ -534,7 +539,7 @@ void XM::File::read(bool)
.u16L(bpmSpeed);
unsigned int count = header.read(*this, headerSize - 4U);
unsigned int size = std::min(headerSize - 4U, (unsigned long)header.size());
unsigned int size = std::min(headerSize - 4U, static_cast<unsigned long>(header.size()));
READ_ASSERT(count == size);

Binary file not shown.

View File

@@ -29,6 +29,7 @@
#include <tstringlist.h>
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <apefile.h>
#include <apetag.h>
#include <tdebug.h>
#include <cppunit/extensions/HelperMacros.h>
@@ -46,6 +47,7 @@ class TestAPETag : public CppUnit::TestFixture
CPPUNIT_TEST(testPropertyInterface2);
CPPUNIT_TEST(testInvalidKeys);
CPPUNIT_TEST(testTextBinary);
CPPUNIT_TEST(testID3v1Collision);
CPPUNIT_TEST_SUITE_END();
public:
@@ -165,6 +167,24 @@ public:
CPPUNIT_ASSERT_EQUAL(ByteVector(), item.binaryData());
}
void testID3v1Collision()
{
ScopedFileCopy copy("no-tags", ".mpc");
string newname = copy.fileName();
{
APE::File f(newname.c_str());
f.APETag(true)->setArtist("Filltointersect ");
f.APETag()->setTitle("Filltointersect ");
f.save();
}
{
APE::File f(newname.c_str());
CPPUNIT_ASSERT(!f.hasID3v1Tag());
}
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestAPETag);

View File

@@ -328,7 +328,7 @@ public:
tags["DISCSUBTITLE"] = StringList("Disc Subtitle");
tags["ENCODEDBY"] = StringList("Encoded by");
tags["GENRE"] = StringList("Genre");
tags["GROUPING"] = StringList("Grouping");
tags["WORK"] = StringList("Grouping");
tags["ISRC"] = StringList("UKAAA0500001");
tags["LABEL"] = StringList("Label");
tags["LANGUAGE"] = StringList("eng");

View File

@@ -60,6 +60,20 @@ namespace
return new Ogg::Vorbis::File(fileName);
}
};
class DummyStreamResolver : public FileRef::StreamTypeResolver
{
public:
virtual File *createFile(FileName, bool, AudioProperties::ReadStyle) const
{
return 0;
}
virtual File *createFileFromStream(IOStream *s, bool, AudioProperties::ReadStyle) const
{
return new MP4::File(s);
}
};
}
class TestFileRef : public CppUnit::TestFixture
@@ -387,6 +401,15 @@ public:
FileRef f(TEST_FILE_PATH_C("xing.mp3"));
CPPUNIT_ASSERT(dynamic_cast<Ogg::Vorbis::File *>(f.file()) != NULL);
}
DummyStreamResolver streamResolver;
FileRef::addFileTypeResolver(&streamResolver);
{
FileStream s(TEST_FILE_PATH_C("xing.mp3"));
FileRef f(&s);
CPPUNIT_ASSERT(dynamic_cast<MP4::File *>(f.file()) != NULL);
}
}
};

View File

@@ -199,7 +199,14 @@ public:
sl.append("Foo");
sl.append("Bar");
f.setText(sl);
CPPUNIT_ASSERT_EQUAL((unsigned int)(4+4+2+1+6+2+6), f.render().size());
ByteVector data = f.render();
CPPUNIT_ASSERT_EQUAL((unsigned int)(4+4+2+1+6+2+6), data.size());
ByteVector noBomBeData("TPE1\x00\x00\x00\x0f\x00\x00\x02"
"\0F\0o\0o\0\0"
"\0B\0a\0r", 25);
CPPUNIT_ASSERT_EQUAL(noBomBeData, data);
f.setData(data);
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString());
}
void testUTF16Delimiter()
@@ -209,7 +216,32 @@ public:
sl.append("Foo");
sl.append("Bar");
f.setText(sl);
CPPUNIT_ASSERT_EQUAL((unsigned int)(4+4+2+1+8+2+8), f.render().size());
ByteVector data = f.render();
CPPUNIT_ASSERT_EQUAL((unsigned int)(4+4+2+1+8+2+8), data.size());
ByteVector multiBomLeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xff\xfe"
"F\0o\0o\0\0\0" "\xff\xfe"
"B\0a\0r\0", 29);
CPPUNIT_ASSERT_EQUAL(multiBomLeData, data);
f.setData(data);
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString());
ByteVector multiBomBeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xfe\xff"
"\0F\0o\0o\0\0" "\xfe\xff"
"\0B\0a\0r", 29);
f.setData(multiBomBeData);
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString());
ByteVector singleBomLeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xff\xfe"
"F\0o\0o\0\0\0"
"B\0a\0r\0", 27);
f.setData(singleBomLeData);
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString());
ByteVector singleBomBeData("TPE1\x00\x00\x00\x13\x00\x00\x01\xfe\xff"
"\0F\0o\0o\0\0"
"\0B\0a\0r", 27);
f.setData(singleBomBeData);
CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.toString());
}
void testBrokenFrame1()

View File

@@ -65,6 +65,8 @@ class TestMP4 : public CppUnit::TestFixture
CPPUNIT_TEST(testRepeatedSave);
CPPUNIT_TEST(testWithZeroLengthAtom);
CPPUNIT_TEST(testEmptyValuesRemoveItems);
CPPUNIT_TEST(testRemoveMetadata);
CPPUNIT_TEST(testNonFullMetaAtom);
CPPUNIT_TEST_SUITE_END();
public:
@@ -654,6 +656,58 @@ public:
CPPUNIT_ASSERT_EQUAL(zeroUInt, tag->track());
CPPUNIT_ASSERT(!tag->contains("trkn"));
}
void testRemoveMetadata()
{
ScopedFileCopy copy("no-tags", ".m4a");
{
MP4::File f(copy.fileName().c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasMP4Tag());
MP4::Tag *tag = f.tag();
CPPUNIT_ASSERT(tag->isEmpty());
tag->setTitle("TITLE");
f.save();
}
{
MP4::File f(copy.fileName().c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.hasMP4Tag());
CPPUNIT_ASSERT(!f.tag()->isEmpty());
f.strip();
}
{
MP4::File f(copy.fileName().c_str());
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(!f.hasMP4Tag());
CPPUNIT_ASSERT(f.tag()->isEmpty());
CPPUNIT_ASSERT(fileEqual(
copy.fileName(),
TEST_FILE_PATH_C("no-tags.m4a")));
}
}
void testNonFullMetaAtom()
{
{
MP4::File f(TEST_FILE_PATH_C("non-full-meta.m4a"));
CPPUNIT_ASSERT(f.isValid());
CPPUNIT_ASSERT(f.hasMP4Tag());
CPPUNIT_ASSERT(f.tag()->contains("covr"));
MP4::CoverArtList l = f.tag()->item("covr").toCoverArtList();
CPPUNIT_ASSERT_EQUAL((unsigned int)2, l.size());
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, l[0].format());
CPPUNIT_ASSERT_EQUAL((unsigned int)79, l[0].data().size());
CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::JPEG, l[1].format());
CPPUNIT_ASSERT_EQUAL((unsigned int)287, l[1].data().size());
PropertyMap properties = f.properties();
CPPUNIT_ASSERT_EQUAL(StringList("Test Artist!!!!"), properties["ARTIST"]);
CPPUNIT_ASSERT_EQUAL(StringList("FAAC 1.24"), properties["ENCODEDBY"]);
}
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);

View File

@@ -314,10 +314,11 @@ public:
tags["CATALOGNUMBER"] = StringList("Catalog Number");
tags["COMMENT"] = StringList("Comment");
tags["COMMENT:CDESC"] = StringList("Comment with Description");
tags["COMPILATION"] = StringList("1");
tags["COMPOSER"] = StringList("Composer");
tags["COMPOSERSORT"] = StringList("Composer Sort");
tags["CONDUCTOR"] = StringList("Conductor");
tags["CONTENTGROUP"] = StringList("Content Group");
tags["WORK"] = StringList("Content Group");
tags["COPYRIGHT"] = StringList("2021 Copyright");
tags["COPYRIGHTURL"] = StringList("Copyright URL");
tags["DATE"] = StringList("2021-01-03 12:29:23");

View File

@@ -25,7 +25,13 @@
#include <tpropertymap.h>
#include <tag.h>
#include <apetag.h>
#include <asftag.h>
#include <id3v1tag.h>
#include <id3v2tag.h>
#include <infotag.h>
#include <mp4tag.h>
#include <xiphcomment.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
@@ -35,6 +41,13 @@ class TestPropertyMap : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(TestPropertyMap);
CPPUNIT_TEST(testInvalidKeys);
CPPUNIT_TEST(testGetSetApe);
CPPUNIT_TEST(testGetSetAsf);
CPPUNIT_TEST(testGetSetId3v1);
CPPUNIT_TEST(testGetSetId3v2);
CPPUNIT_TEST(testGetSetInfo);
CPPUNIT_TEST(testGetSetMp4);
CPPUNIT_TEST(testGetSetXiphComment);
CPPUNIT_TEST(testGetSet);
CPPUNIT_TEST_SUITE_END();
@@ -60,9 +73,10 @@ public:
}
void testGetSet()
template <typename T>
void tagGetSet()
{
ID3v1::Tag tag;
T tag;
tag.setTitle("Test Title");
tag.setArtist("Test Artist");
@@ -88,6 +102,7 @@ public:
CPPUNIT_ASSERT_EQUAL(String("Test Title 2"), tag.title());
CPPUNIT_ASSERT_EQUAL(String("Test Artist 2"), tag.artist());
CPPUNIT_ASSERT_EQUAL(5U, tag.track());
CPPUNIT_ASSERT(!tag.isEmpty());
PropertyMap props = tag.properties();
CPPUNIT_ASSERT_EQUAL(StringList("Test Artist 2"), props.find("ARTIST")->second);
@@ -104,9 +119,83 @@ public:
tag.setProperties(PropertyMap());
CPPUNIT_ASSERT(tag.isEmpty());
CPPUNIT_ASSERT(tag.properties().isEmpty());
CPPUNIT_ASSERT_EQUAL(String(""), tag.title());
CPPUNIT_ASSERT_EQUAL(String(""), tag.artist());
CPPUNIT_ASSERT_EQUAL(String(""), tag.album());
CPPUNIT_ASSERT_EQUAL(String(""), tag.comment());
CPPUNIT_ASSERT_EQUAL(String(""), tag.genre());
CPPUNIT_ASSERT_EQUAL(0U, tag.track());
CPPUNIT_ASSERT(tag.isEmpty());
CPPUNIT_ASSERT(tag.properties().isEmpty());
}
void testGetSetId3v1()
{
tagGetSet<ID3v1::Tag>();
}
void testGetSetId3v2()
{
tagGetSet<ID3v2::Tag>();
}
void testGetSetXiphComment()
{
tagGetSet<Ogg::XiphComment>();
}
void testGetSetApe()
{
tagGetSet<APE::Tag>();
}
void testGetSetAsf()
{
tagGetSet<ASF::Tag>();
}
void testGetSetMp4()
{
tagGetSet<MP4::Tag>();
}
void testGetSetInfo()
{
tagGetSet<RIFF::Info::Tag>();
}
void testGetSet()
{
PropertyMap props;
props["Title"] = String("Test Title");
StringList artists("Artist 1");
artists.append("Artist 2");
props.insert("Artist", artists);
CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.value("TITLE"));
CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.value("Title"));
CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props["TITLE"]);
CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props["Title"]);
CPPUNIT_ASSERT(props.contains("title"));
CPPUNIT_ASSERT_EQUAL(StringList("Test Title"), props.find("TITLE")->second);
CPPUNIT_ASSERT_EQUAL(2U, props.size());
CPPUNIT_ASSERT(!props.isEmpty());
props.clear();
CPPUNIT_ASSERT(props.isEmpty());
CPPUNIT_ASSERT_EQUAL(StringList(), props.value("TITLE"));
CPPUNIT_ASSERT_EQUAL(StringList(), props.value("Title"));
CPPUNIT_ASSERT_EQUAL(artists, props.value("Title", artists));
CPPUNIT_ASSERT(!props.contains("title"));
CPPUNIT_ASSERT(props.find("TITLE") == props.end());
CPPUNIT_ASSERT_EQUAL(0U, props.size());
CPPUNIT_ASSERT(props.isEmpty());
CPPUNIT_ASSERT_EQUAL(StringList(), props["TITLE"]);
CPPUNIT_ASSERT_EQUAL(StringList(), props["Title"]);
CPPUNIT_ASSERT(props.contains("title"));
CPPUNIT_ASSERT(props.find("TITLE") != props.end());
CPPUNIT_ASSERT_EQUAL(1U, props.size());
CPPUNIT_ASSERT(!props.isEmpty());
}
};