Detect APE tags, emit according messages and update README
It is likely not worth adding support for APE tags at this point. However, it still makes sense to acknowledge the presence of them despite not being actually supported to avoid possible confusion. With this change, APE tags at the beginning of the file will be dropped when applying changes as the container offset is updated when skipping the tag. I suppose that makes sense considering putting those tags at the beginning is not recommended anyways. The diag messages and README have been updated accordingly.
This commit is contained in:
parent
9e4d221bb1
commit
0a2b948f26
|
@ -7,11 +7,19 @@ The tag library can read and write the following tag formats:
|
|||
* iTunes-style MP4/M4A tags (MP4-DASH is supported)
|
||||
* ID3v1 and ID3v2 tags
|
||||
* conversion between ID3v1 and different versions of ID3v2 is possible
|
||||
* mainly for use in MP3 files but can be added to any kind of file
|
||||
* Vorbis, Opus and FLAC comments in Ogg streams
|
||||
* cover art via "METADATA_BLOCK_PICTURE" is supported
|
||||
* Vorbis comments and "METADATA_BLOCK_PICTURE" in raw FLAC streams
|
||||
* Matroska/WebM tags and attachments
|
||||
|
||||
Further remarks:
|
||||
|
||||
* Unsupported file contents (such as unsupported tag formats) are *generally* preserved as-is.
|
||||
* Note that APE tags are *not* supported. APE tags in the beginning of a file are strongly
|
||||
unrecommended and thus discarded when applying changes. APE tags at the end of the file
|
||||
are preserved as-is when applying changes.
|
||||
|
||||
## File layout options
|
||||
### Tag position
|
||||
The library allows you to choose whether tags should be placed at the beginning or at
|
||||
|
|
|
@ -254,6 +254,23 @@ startParsingSignature:
|
|||
static_cast<OggContainer *>(m_container.get())->setChecksumValidationEnabled(isForcingFullParse());
|
||||
break;
|
||||
case ContainerFormat::Unknown:
|
||||
case ContainerFormat::ApeTag:
|
||||
// skip APE tag if the specified size makes sense at all
|
||||
if (m_containerFormat == ContainerFormat::ApeTag) {
|
||||
if (const auto apeEnd = m_containerOffset + 32 + LE::toUInt32(buff + 12); apeEnd <= static_cast<std::streamoff>(size())) {
|
||||
// take record of APE tag
|
||||
diag.emplace_back(DiagLevel::Critical,
|
||||
argsToString("Found an APE tag at the beginning of the file at offset ", m_containerOffset,
|
||||
". This tag format is not supported and the tag will therefore be ignored. It will NOT be preserved when saving as "
|
||||
"placing an APE tag at the beginning of a file is strongly unrecommended."),
|
||||
context);
|
||||
// continue reading signature
|
||||
m_containerOffset = apeEnd;
|
||||
goto startParsingSignature;
|
||||
}
|
||||
m_containerFormat = ContainerFormat::Unknown;
|
||||
}
|
||||
|
||||
// check for magic numbers at odd offsets
|
||||
// -> check for tar (magic number at offset 0x101)
|
||||
if (size() > 0x107) {
|
||||
|
@ -378,12 +395,14 @@ void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &prog
|
|||
static const string context("parsing tag");
|
||||
|
||||
// check for ID3v1 tag
|
||||
if (size() >= 128) {
|
||||
auto effectiveSize = static_cast<std::streamoff>(size());
|
||||
if (effectiveSize >= 128) {
|
||||
m_id3v1Tag = make_unique<Id3v1Tag>();
|
||||
try {
|
||||
stream().seekg(-128, ios_base::end);
|
||||
stream().seekg(effectiveSize - 128, std::ios_base::beg);
|
||||
m_id3v1Tag->parse(stream(), diag);
|
||||
m_fileStructureFlags += MediaFileStructureFlags::ActualExistingId3v1Tag;
|
||||
effectiveSize -= 128;
|
||||
} catch (const NoDataFoundException &) {
|
||||
m_id3v1Tag.reset();
|
||||
} catch (const OperationAbortedException &) {
|
||||
|
@ -395,6 +414,22 @@ void MediaFileInfo::parseTags(Diagnostics &diag, AbortableProgressFeedback &prog
|
|||
}
|
||||
}
|
||||
|
||||
// check for APE tag at the end of the file (APE tags a the beginning are already covered when parsing the container format)
|
||||
if (constexpr auto apeHeaderSize = 32; effectiveSize >= apeHeaderSize) {
|
||||
const auto footerOffset = effectiveSize - apeHeaderSize;
|
||||
char buffer[apeHeaderSize];
|
||||
stream().seekg(footerOffset, std::ios_base::beg);
|
||||
stream().read(buffer, sizeof(buffer));
|
||||
if (BE::toUInt64(buffer) == 0x4150455441474558ul /* APETAGEX */) {
|
||||
// take record of APE tag
|
||||
const auto tagSize = static_cast<std::streamoff>(LE::toUInt32(buffer + 12));
|
||||
diag.emplace_back(DiagLevel::Warning,
|
||||
argsToString("Found an APE tag at the end of the file at offset ", (footerOffset - tagSize),
|
||||
". This tag format is not supported and the tag will therefore be ignored. It will be preserved when saving as-is."),
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
// check for ID3v2 tags: the offsets of the ID3v2 tags have already been parsed when parsing the container format
|
||||
m_id3v2Tags.clear();
|
||||
for (const auto offset : m_actualId3v2TagOffsets) {
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace TagParser {
|
|||
* \brief Holds 64-bit signatures.
|
||||
*/
|
||||
enum Sig64 : std::uint64_t {
|
||||
ApeTag = 0x4150455441474558ul, // APETAGEX
|
||||
Ar = 0x213C617263683E0A,
|
||||
Asf1 = 0x3026B2758E66CF11ul,
|
||||
Asf2 = 0xA6D900AA0062CE6Cul,
|
||||
|
@ -126,6 +127,8 @@ ContainerFormat parseSignature(std::string_view buffer)
|
|||
}
|
||||
// return corresponding container format
|
||||
switch (sig) { // check 64-bit signatures
|
||||
case ApeTag:
|
||||
return ContainerFormat::ApeTag;
|
||||
case Ar:
|
||||
return ContainerFormat::Ar;
|
||||
case Asf1:
|
||||
|
@ -490,6 +493,8 @@ std::string_view containerFormatName(ContainerFormat containerFormat)
|
|||
return "Zstandard compressed file";
|
||||
case ContainerFormat::Id2v2Tag:
|
||||
return "ID3v2 tag";
|
||||
case ContainerFormat::ApeTag:
|
||||
return "APE tag";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ enum class ContainerFormat : unsigned int {
|
|||
Zip, /**< ZIP archive */
|
||||
Aiff, /**< Audio Interchange File Format */
|
||||
Zstd, /**< Zstandard-compressed data */
|
||||
ApeTag, /**< APE tag */
|
||||
};
|
||||
|
||||
TAG_PARSER_EXPORT ContainerFormat parseSignature(const char *buffer, std::size_t bufferSize);
|
||||
|
|
Loading…
Reference in New Issue