diff --git a/taglib/mp4/mp4atom.cpp b/taglib/mp4/mp4atom.cpp index bfc6d5ee..6148f371 100644 --- a/taglib/mp4/mp4atom.cpp +++ b/taglib/mp4/mp4atom.cpp @@ -40,62 +40,72 @@ namespace { }; } // namespace -MP4::Atom::Atom(File *file) - : offset(file->tell()) +class MP4::Atom::AtomPrivate { - children.setAutoDelete(true); +public: + explicit AtomPrivate(offset_t ofs) : offset(ofs) {} + offset_t offset; + offset_t length { 0 }; + TagLib::ByteVector name; + AtomList children; +}; + +MP4::Atom::Atom(File *file) + : d(std::make_unique(file->tell())) +{ + d->children.setAutoDelete(true); ByteVector header = file->readBlock(8); if(header.size() != 8) { // The atom header must be 8 bytes long, otherwise there is either // trailing garbage or the file is truncated debug("MP4: Couldn't read 8 bytes of data for atom header"); - length = 0; + d->length = 0; file->seek(0, File::End); return; } - length = header.toUInt(); + d->length = header.toUInt(); - if(length == 0) { + if(d->length == 0) { // The last atom which extends to the end of the file. - length = file->length() - offset; + d->length = file->length() - d->offset; } - else if(length == 1) { + else if(d->length == 1) { // The atom has a 64-bit length. const long long longLength = file->readBlock(8).toLongLong(); if(longLength <= LONG_MAX) { // The actual length fits in long. That's always the case if long is 64-bit. - length = static_cast(longLength); + d->length = static_cast(longLength); } else { debug("MP4: 64-bit atoms are not supported"); - length = 0; + d->length = 0; file->seek(0, File::End); return; } } - if(length < 8 || length > file->length() - offset) { + if(d->length < 8 || d->length > file->length() - d->offset) { debug("MP4: Invalid atom size"); - length = 0; + d->length = 0; file->seek(0, File::End); return; } - name = header.mid(4, 4); + d->name = header.mid(4, 4); for(int i = 0; i < 4; ++i) { - const char ch = name.at(i); + const char ch = d->name.at(i); if((ch < ' ' || ch > '~') && ch != '\251') { debug("MP4: Invalid atom type"); - length = 0; + d->length = 0; file->seek(0, File::End); } } for(auto c : containers) { - if(name == c) { - if(name == "meta") { + if(d->name == c) { + if(d->name == "meta") { offset_t posAfterMeta = file->tell(); static constexpr std::array metaChildrenNames { "hdlr", "ilst", "mhdr", "ctry", "lang" @@ -108,20 +118,20 @@ MP4::Atom::Atom(File *file) // is a full atom. file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0)); } - else if(name == "stsd") { + else if(d->name == "stsd") { file->seek(8, File::Current); } - while(file->tell() < offset + length) { + while(file->tell() < d->offset + d->length) { auto child = new MP4::Atom(file); - children.append(child); - if(child->length == 0) + d->children.append(child); + if(child->d->length == 0) return; } return; } } - file->seek(offset + length); + file->seek(d->offset + d->length); } MP4::Atom::~Atom() = default; @@ -132,17 +142,17 @@ MP4::Atom::find(const char *name1, const char *name2, const char *name3, const c if(name1 == nullptr) { return this; } - auto it = std::find_if(children.cbegin(), children.cend(), - [&name1](Atom *child) { return child->name == name1; }); - return it != children.cend() ? (*it)->find(name2, name3, name4) : nullptr; + auto it = std::find_if(d->children.cbegin(), d->children.cend(), + [&name1](Atom *child) { return child->d->name == name1; }); + return it != d->children.cend() ? (*it)->find(name2, name3, name4) : nullptr; } MP4::AtomList MP4::Atom::findall(const char *name, bool recursive) { MP4::AtomList result; - for(const auto &child : std::as_const(children)) { - if(child->name == name) { + for(const auto &child : std::as_const(d->children)) { + if(child->d->name == name) { result.append(child); } if(recursive) { @@ -159,22 +169,70 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const if(name1 == nullptr) { return true; } - auto it = std::find_if(children.cbegin(), children.cend(), - [&name1](Atom *child) { return child->name == name1; }); - return it != children.cend() ? (*it)->path(path, name2, name3) : false; + auto it = std::find_if(d->children.cbegin(), d->children.cend(), + [&name1](Atom *child) { return child->d->name == name1; }); + return it != d->children.cend() ? (*it)->path(path, name2, name3) : false; } -MP4::Atoms::Atoms(File *file) +void MP4::Atom::addToOffset(offset_t delta) { - atoms.setAutoDelete(true); + d->offset += delta; +} + +void MP4::Atom::prependChild(Atom *atom) +{ + d->children.prepend(atom); +} + +bool MP4::Atom::removeChild(Atom *meta) +{ + auto it = d->children.find(meta); + if(it != d->children.end()) { + d->children.erase(it); + return true; + } + return false; +} + +offset_t MP4::Atom::offset() const +{ + return d->offset; +} + +offset_t MP4::Atom::length() const +{ + return d->length; +} + +const ByteVector &MP4::Atom::name() const +{ + return d->name; +} + +const MP4::AtomList &MP4::Atom::children() const +{ + return d->children; +} + + +class MP4::Atoms::AtomsPrivate +{ +public: + AtomList atoms; +}; + +MP4::Atoms::Atoms(File *file) : + d(std::make_unique()) +{ + d->atoms.setAutoDelete(true); file->seek(0, File::End); offset_t end = file->tell(); file->seek(0); while(file->tell() + 8 <= end) { auto atom = new MP4::Atom(file); - atoms.append(atom); - if (atom->length == 0) + d->atoms.append(atom); + if (atom->length() == 0) break; } } @@ -184,18 +242,18 @@ MP4::Atoms::~Atoms() = default; MP4::Atom * MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4) { - auto it = std::find_if(atoms.cbegin(), atoms.cend(), - [&name1](Atom *atom) { return atom->name == name1; }); - return it != atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr; + auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(), + [&name1](Atom *atom) { return atom->name() == name1; }); + return it != d->atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr; } MP4::AtomList MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4) { MP4::AtomList path; - auto it = std::find_if(atoms.cbegin(), atoms.cend(), - [&name1](Atom *atom) { return atom->name == name1; }); - if(it != atoms.cend()) { + auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(), + [&name1](Atom *atom) { return atom->name() == name1; }); + if(it != d->atoms.cend()) { if(!(*it)->path(path, name2, name3, name4)) { path.clear(); } @@ -203,3 +261,45 @@ MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const } return path; } + +namespace +{ + bool checkValid(const MP4::AtomList &list) + { + return std::none_of(list.begin(), list.end(), + [](const auto &a) { return a->length() == 0 || !checkValid(a->children()); }); + } +} // namespace + +bool MP4::Atoms::checkRootLevelAtoms() +{ + bool moovValid = false; + for(auto it = d->atoms.begin(); it != d->atoms.end(); ++it) { + bool invalid = (*it)->length() == 0 || !checkValid((*it)->children()); + if(!moovValid && !invalid && (*it)->name() == "moov") { + moovValid = true; + } + if(invalid) { + if(moovValid && (*it)->name() != "moof") { + // Only the root level atoms "moov" and (if present) "moof" are + // modified. If they are valid, ignore following invalid root level + // atoms as trailing garbage. + while(it != d->atoms.end()) { + delete *it; + it = d->atoms.erase(it); + } + return true; + } + else { + return false; + } + } + } + + return true; +} + +const MP4::AtomList &MP4::Atoms::atoms() const +{ + return d->atoms; +}; diff --git a/taglib/mp4/mp4atom.h b/taglib/mp4/mp4atom.h index d536d9b4..a365c4a4 100644 --- a/taglib/mp4/mp4atom.h +++ b/taglib/mp4/mp4atom.h @@ -84,10 +84,17 @@ namespace TagLib { Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr); AtomList findall(const char *name, bool recursive = false); - offset_t offset; - offset_t length; - TagLib::ByteVector name; - AtomList children; + void addToOffset(offset_t delta); + void prependChild(Atom *atom); + bool removeChild(Atom *meta); + offset_t offset() const; + offset_t length() const; + const ByteVector &name() const; + const AtomList &children() const; + + private: + class AtomPrivate; + std::unique_ptr d; }; //! Root-level atoms @@ -100,7 +107,12 @@ namespace TagLib { Atoms &operator=(const Atoms &) = delete; Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr); - AtomList atoms; + bool checkRootLevelAtoms(); + const AtomList &atoms() const; + + private: + class AtomsPrivate; + std::unique_ptr d; }; } // namespace MP4 } // namespace TagLib diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index a14db53d..ae80210f 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -33,44 +33,6 @@ using namespace TagLib; -namespace -{ - bool checkValid(const MP4::AtomList &list) - { - return std::none_of(list.begin(), list.end(), - [](const auto &a) { return a->length == 0 || !checkValid(a->children); }); - } - - bool checkRootLevelAtoms(MP4::AtomList &list) - { - bool moovValid = false; - for(auto it = list.begin(); it != list.end(); ++it) { - bool invalid = (*it)->length == 0 || !checkValid((*it)->children); - if(!moovValid && !invalid && (*it)->name == "moov") { - moovValid = true; - } - if(invalid) { - if(moovValid && (*it)->name != "moof") { - // Only the root level atoms "moov" and (if present) "moof" are - // modified. If they are valid, ignore following invalid root level - // atoms as trailing garbage. - while(it != list.end()) { - delete *it; - it = list.erase(it); - } - return true; - } - else { - return false; - } - } - } - - return true; - } - -} // namespace - class MP4::File::FilePrivate { public: @@ -156,7 +118,7 @@ MP4::File::read(bool readProperties) return; d->atoms = std::make_unique(this); - if(!checkRootLevelAtoms(d->atoms->atoms)) { + if(!d->atoms->checkRootLevelAtoms()) { setValid(false); return; } diff --git a/taglib/mp4/mp4itemfactory.cpp b/taglib/mp4/mp4itemfactory.cpp index d9885282..e89e5844 100644 --- a/taglib/mp4/mp4itemfactory.cpp +++ b/taglib/mp4/mp4itemfactory.cpp @@ -57,7 +57,7 @@ ItemFactory *ItemFactory::instance() std::pair ItemFactory::parseItem( const Atom *atom, const ByteVector &data) const { - auto handlerType = handlerTypeForName(atom->name); + auto handlerType = handlerTypeForName(atom->name()); switch(handlerType) { case ItemHandlerType::Unknown: break; @@ -87,7 +87,7 @@ std::pair ItemFactory::parseItem( case ItemHandlerType::Text: return parseText(atom, data); } - return {atom->name, Item()}; + return {atom->name(), Item()}; } ByteVector ItemFactory::renderItem( @@ -211,7 +211,7 @@ std::pair ItemFactory::itemToProperty( case ItemHandlerType::UInt: return {key, String::number(item.toUInt())}; case ItemHandlerType::LongLong: - return {key, String::number(item.toLongLong())}; + return {key, String::fromLongLong(item.toLongLong())}; case ItemHandlerType::Byte: return {key, String::number(item.toByte())}; case ItemHandlerType::Bool: @@ -449,7 +449,7 @@ std::pair ItemFactory::parseInt( { ByteVectorList data = parseData(atom, bytes); return { - atom->name, + atom->name(), !data.isEmpty() ? Item(static_cast(data[0].toShort())) : Item() }; } @@ -461,12 +461,12 @@ std::pair ItemFactory::parseTextOrInt( if(!data.isEmpty()) { AtomData val = data[0]; return { - atom->name, + atom->name(), val.type == TypeUTF8 ? Item(StringList(String(val.data, String::UTF8))) : Item(static_cast(val.data.toShort())) }; } - return {atom->name, Item()}; + return {atom->name(), Item()}; } std::pair ItemFactory::parseUInt( @@ -474,7 +474,7 @@ std::pair ItemFactory::parseUInt( { ByteVectorList data = parseData(atom, bytes); return { - atom->name, + atom->name(), !data.isEmpty() ? Item(data[0].toUInt()) : Item() }; } @@ -484,7 +484,7 @@ std::pair ItemFactory::parseLongLong( { ByteVectorList data = parseData(atom, bytes); return { - atom->name, + atom->name(), !data.isEmpty() ?Item (data[0].toLongLong()) : Item() }; } @@ -494,7 +494,7 @@ std::pair ItemFactory::parseByte( { ByteVectorList data = parseData(atom, bytes); return { - atom->name, + atom->name(), !data.isEmpty() ? Item(static_cast(data[0].at(0))) : Item() }; } @@ -522,9 +522,9 @@ std::pair ItemFactory::parseIntPair( if(!data.isEmpty()) { const int a = data[0].toShort(2U); const int b = data[0].toShort(4U); - return {atom->name, Item(a, b)}; + return {atom->name(), Item(a, b)}; } - return {atom->name, Item()}; + return {atom->name(), Item()}; } std::pair ItemFactory::parseBool( @@ -533,9 +533,9 @@ std::pair ItemFactory::parseBool( ByteVectorList data = parseData(atom, bytes); if(!data.isEmpty()) { bool value = !data[0].isEmpty() && data[0][0] != '\0'; - return {atom->name, Item(value)}; + return {atom->name(), Item(value)}; } - return {atom->name, Item()}; + return {atom->name(), Item()}; } std::pair ItemFactory::parseText( @@ -547,9 +547,9 @@ std::pair ItemFactory::parseText( for(const auto &byte : data) { value.append(String(byte, String::UTF8)); } - return {atom->name, Item(value)}; + return {atom->name(), Item(value)}; } - return {atom->name, Item()}; + return {atom->name(), Item()}; } std::pair ItemFactory::parseFreeForm( @@ -591,7 +591,7 @@ std::pair ItemFactory::parseFreeForm( return {name, item}; } } - return {atom->name, Item()}; + return {atom->name(), Item()}; } std::pair ItemFactory::parseCovr( @@ -623,7 +623,7 @@ std::pair ItemFactory::parseCovr( pos += length; } return { - atom->name, + atom->name(), !value.isEmpty() ? Item(value) : Item() }; } diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index 88ed26ca..3f8d8d19 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -39,14 +39,14 @@ namespace { long long totalLength = 0; for(const auto &atom : list) { - offset_t length = atom->length; + offset_t length = atom->length(); if(length == 0) return 0; // for safety, see checkValid() in mp4file.cpp - if(atom->name == "mdat") + if(atom->name() == "mdat") totalLength += length; - totalLength += calculateMdatLength(atom->children); + totalLength += calculateMdatLength(atom->children()); } return totalLength; @@ -144,8 +144,8 @@ MP4::Properties::read(File *file, Atoms *atoms) return; } trak = track; - file->seek(hdlr->offset); - data = file->readBlock(hdlr->length); + file->seek(hdlr->offset()); + data = file->readBlock(hdlr->length()); if(data.containsAt("soun", 16)) { break; } @@ -162,8 +162,8 @@ MP4::Properties::read(File *file, Atoms *atoms) return; } - file->seek(mdhd->offset); - data = file->readBlock(mdhd->length); + file->seek(mdhd->offset()); + data = file->readBlock(mdhd->length()); const unsigned int version = data.at(8); long long unit; @@ -187,8 +187,8 @@ MP4::Properties::read(File *file, Atoms *atoms) if(length == 0) { // No length found in the media header (mdhd), try the movie header (mvhd) if(MP4::Atom *mvhd = moov->find("mvhd")) { - file->seek(mvhd->offset); - data = file->readBlock(mvhd->length); + file->seek(mvhd->offset()); + data = file->readBlock(mvhd->length()); if(data.size() >= 24 + 4) { unit = data.toUInt(20U); length = data.toUInt(24U); @@ -203,8 +203,8 @@ MP4::Properties::read(File *file, Atoms *atoms) return; } - file->seek(atom->offset); - data = file->readBlock(atom->length); + file->seek(atom->offset()); + data = file->readBlock(atom->length()); if(data.containsAt("mp4a", 20)) { d->codec = AAC; d->channels = data.toShort(40U); @@ -228,13 +228,13 @@ MP4::Properties::read(File *file, Atoms *atoms) } else { d->bitrate = static_cast( - (calculateMdatLength(atoms->atoms) * 8) / d->length); + (calculateMdatLength(atoms->atoms()) * 8) / d->length); } } } } else if(data.containsAt("alac", 20)) { - if(atom->length == 88 && data.containsAt("alac", 56)) { + if(atom->length() == 88 && data.containsAt("alac", 56)) { d->codec = ALAC; d->bitsPerSample = data.at(69); d->channels = data.at(73); @@ -245,7 +245,7 @@ MP4::Properties::read(File *file, Atoms *atoms) // There are files which do not contain a nominal bitrate, e.g. those // generated by refalac64.exe. Calculate the bitrate from the audio // data size (mdat atoms) and the duration. - d->bitrate = static_cast((calculateMdatLength(atoms->atoms) * 8) / d->length); + d->bitrate = static_cast((calculateMdatLength(atoms->atoms()) * 8) / d->length); } } } diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index 635ece9d..4bfe9908 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -71,9 +71,9 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms, return; } - for(const auto &atom : std::as_const(ilst->children)) { - file->seek(atom->offset + 8); - ByteVector data = d->file->readBlock(atom->length - 8); + for(const auto &atom : ilst->children()) { + file->seek(atom->offset() + 8); + ByteVector data = d->file->readBlock(atom->length() - 8); const auto &[name, itm] = d->factory->parseItem(atom, data); if (itm.isValid()) { addItem(name, itm); @@ -141,19 +141,19 @@ MP4::Tag::updateParents(const AtomList &path, offset_t delta, int ignore) std::advance(itEnd, 0 - ignore); for(auto it = path.begin(); it != itEnd; ++it) { - d->file->seek((*it)->offset); + d->file->seek((*it)->offset()); long size = d->file->readBlock(4).toUInt(); // 64-bit if (size == 1) { d->file->seek(4, File::Current); // Skip name long long longSize = d->file->readBlock(8).toLongLong(); // Seek the offset of the 64-bit size - d->file->seek((*it)->offset + 8); + d->file->seek((*it)->offset() + 8); d->file->writeBlock(ByteVector::fromLongLong(longSize + delta)); } // 32-bit else { - d->file->seek((*it)->offset); + d->file->seek((*it)->offset()); d->file->writeBlock(ByteVector::fromUInt(static_cast(size + delta))); } } @@ -166,13 +166,13 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset) if(moov) { const MP4::AtomList stco = moov->findall("stco", true); for(const auto &atom : stco) { - if(atom->offset > offset) { - atom->offset += delta; + if(atom->offset() > offset) { + atom->addToOffset(delta); } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); + d->file->seek(atom->offset() + 12); + ByteVector data = d->file->readBlock(atom->length() - 12); unsigned int count = data.toUInt(); - d->file->seek(atom->offset + 16); + d->file->seek(atom->offset() + 16); unsigned int pos = 4; while(count--) { auto o = static_cast(data.toUInt(pos)); @@ -186,13 +186,13 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset) const MP4::AtomList co64 = moov->findall("co64", true); for(const auto &atom : co64) { - if(atom->offset > offset) { - atom->offset += delta; + if(atom->offset() > offset) { + atom->addToOffset(delta); } - d->file->seek(atom->offset + 12); - ByteVector data = d->file->readBlock(atom->length - 12); + d->file->seek(atom->offset() + 12); + ByteVector data = d->file->readBlock(atom->length() - 12); unsigned int count = data.toUInt(); - d->file->seek(atom->offset + 16); + d->file->seek(atom->offset() + 16); unsigned int pos = 4; while(count--) { long long o = data.toLongLong(pos); @@ -209,18 +209,18 @@ MP4::Tag::updateOffsets(offset_t delta, offset_t offset) if(moof) { const MP4::AtomList tfhd = moof->findall("tfhd", true); for(const auto &atom : tfhd) { - if(atom->offset > offset) { - atom->offset += delta; + if(atom->offset() > offset) { + atom->addToOffset(delta); } - d->file->seek(atom->offset + 9); - ByteVector data = d->file->readBlock(atom->length - 9); + d->file->seek(atom->offset() + 9); + ByteVector data = d->file->readBlock(atom->length() - 9); const unsigned int flags = data.toUInt(0, 3, true); if(flags & 1) { long long o = data.toLongLong(7U); if(o > offset) { o += delta; } - d->file->seek(atom->offset + 16); + d->file->seek(atom->offset() + 16); d->file->writeBlock(ByteVector::fromLongLong(o)); } } @@ -241,7 +241,7 @@ MP4::Tag::saveNew(ByteVector data) data = renderAtom("udta", data); } - offset_t offset = path.back()->offset + 8; + offset_t offset = path.back()->offset() + 8; d->file->insert(data, offset, 0); updateParents(path, data.size()); @@ -250,7 +250,7 @@ MP4::Tag::saveNew(ByteVector data) // Insert the newly created atoms into the tree to keep it up-to-date. d->file->seek(offset); - path.back()->children.prepend(new Atom(d->file)); + path.back()->prependChild(new Atom(d->file)); } void @@ -259,27 +259,27 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path) auto it = path.end(); MP4::Atom *ilst = *(--it); - offset_t offset = ilst->offset; - offset_t length = ilst->length; + offset_t offset = ilst->offset(); + offset_t length = ilst->length(); MP4::Atom *meta = *(--it); - auto index = meta->children.cfind(ilst); + auto index = meta->children().cfind(ilst); // check if there is an atom before 'ilst', and possibly use it as padding - if(index != meta->children.cbegin()) { + if(index != meta->children().cbegin()) { auto prevIndex = std::prev(index); MP4::Atom *prev = *prevIndex; - if(prev->name == "free") { - offset = prev->offset; - length += prev->length; + if(prev->name() == "free") { + offset = prev->offset(); + length += prev->length(); } } // check if there is an atom after 'ilst', and possibly use it as padding auto nextIndex = std::next(index); - if(nextIndex != meta->children.cend()) { + if(nextIndex != meta->children().cend()) { MP4::Atom *next = *nextIndex; - if(next->name == "free") { - length += next->length; + if(next->name() == "free") { + length += next->length(); } } @@ -304,13 +304,10 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path) else { // Strip meta if data is empty, only the case when called from strip(). MP4::Atom *udta = *std::prev(it); - AtomList &udtaChildren = udta->children; - auto metaIt = udtaChildren.find(meta); - if(metaIt != udtaChildren.end()) { - offset = meta->offset; - delta = - meta->length; - udtaChildren.erase(metaIt); - d->file->removeBlock(meta->offset, meta->length); + if(udta->removeChild(meta)) { + offset = meta->offset(); + delta = - meta->length(); + d->file->removeBlock(meta->offset(), meta->length()); delete meta; if(delta) { diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 8e14e9a4..ece346a6 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -523,6 +523,11 @@ String String::number(int n) // static return Utils::formatString("%d", n); } +String String::fromLongLong(long long n) // static +{ + return Utils::formatString("%lld", n); +} + wchar_t &String::operator[](int i) { detach(); diff --git a/taglib/toolkit/tstring.h b/taglib/toolkit/tstring.h index c4caf098..a9e34160 100644 --- a/taglib/toolkit/tstring.h +++ b/taglib/toolkit/tstring.h @@ -378,6 +378,11 @@ namespace TagLib { */ static String number(int n); + /*! + * Converts the base-10 integer \a n to a string. + */ + static String fromLongLong(long long n); + /*! * Returns a reference to the character at position \a i. */ diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index 79cb8645..c83210fb 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -238,8 +238,8 @@ public: MP4::Atoms a(&f); MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; - f.seek(stco->offset + 12); - ByteVector data = f.readBlock(stco->length - 12); + f.seek(stco->offset() + 12); + ByteVector data = f.readBlock(stco->length() - 12); unsigned int count = data.mid(0, 4).toUInt(); int pos = 4; while (count--) { @@ -257,8 +257,8 @@ public: MP4::Atoms a(&f); MP4::Atom *stco = a.find("moov")->findall("stco", true)[0]; - f.seek(stco->offset + 12); - ByteVector data = f.readBlock(stco->length - 12); + f.seek(stco->offset() + 12); + ByteVector data = f.readBlock(stco->length() - 12); unsigned int count = data.mid(0, 4).toUInt(); int pos = 4, i = 0; while (count--) { @@ -323,8 +323,8 @@ public: CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemMap()["cpil"].toBool()); MP4::Atoms atoms(&f); - MP4::Atom *moov = atoms.atoms[0]; - CPPUNIT_ASSERT_EQUAL(static_cast(77), moov->length); + MP4::Atom *moov = atoms.atoms()[0]; + CPPUNIT_ASSERT_EQUAL(static_cast(77), moov->length()); f.tag()->setItem("pgap", true); f.save(); @@ -335,9 +335,9 @@ public: CPPUNIT_ASSERT_EQUAL(true, f.tag()->item("pgap").toBool()); MP4::Atoms atoms(&f); - MP4::Atom *moov = atoms.atoms[0]; + MP4::Atom *moov = atoms.atoms()[0]; // original size + 'pgap' size + padding - CPPUNIT_ASSERT_EQUAL(static_cast(77 + 25 + 974), moov->length); + CPPUNIT_ASSERT_EQUAL(static_cast(77 + 25 + 974), moov->length()); } }