#include <id3v2tag.h>
#include <id3v2header.h>
#include <id3v1tag.h>
+#include <apefooter.h>
+#include <apetag.h>
#include <tdebug.h>
#include <bitset>
ID3v2Tag(0),
ID3v2Location(-1),
ID3v2OriginalSize(0),
+ APETag(0),
+ APELocation(-1),
+ APEOriginalSize(0),
ID3v1Tag(0),
ID3v1Location(-1),
tag(0),
hasID3v2(false),
hasID3v1(false),
+ hasAPE(false),
properties(0) {}
~FilePrivate() {
long ID3v2Location;
uint ID3v2OriginalSize;
+ APE::Tag *APETag;
+ long APELocation;
+ uint APEOriginalSize;
+
ID3v1::Tag *ID3v1Tag;
long ID3v1Location;
MPEGTag *tag;
- // These indicate whether the file *on disk* has an ID3v[1/2] tag, not if
+ // These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasID3v2;
bool hasID3v1;
+ bool hasAPE;
Properties *properties;
};
bool MPEG::File::save()
{
- return save(ID3v1 | ID3v2);
+ return save(AllTags);
}
bool MPEG::File::save(int tags)
if(tags == NoTags)
return strip(AllTags);
- if(!d->ID3v2Tag && !d->ID3v1Tag) {
+ if(!d->ID3v2Tag && !d->ID3v1Tag && !d->APETag) {
- if(d->hasID3v1 || d->hasID3v2)
+ if(d->hasID3v1 || d->hasID3v2 || d->hasAPE)
return strip(AllTags);
return true;
// Create the tags if we've been asked to. Copy the values from the tag that
// does exist into the new tag.
- if(tags & ID3v2 && d->ID3v1Tag)
+ if((tags & ID3v2) && d->ID3v1Tag)
Tag::duplicate(d->ID3v1Tag, ID3v2Tag(true), false);
- if(tags & ID3v1 && d->ID3v2Tag)
+ if((tags & ID3v1) && d->ID3v2Tag)
Tag::duplicate(d->ID3v2Tag, ID3v1Tag(true), false);
bool success = true;
else if(d->hasID3v1)
success = strip(ID3v1, false) && success;
+ // Dont save an APE-tag unless one has been created
+ if((APE & tags) && d->APETag) {
+ if(d->hasAPE)
+ insert(d->APETag->render(), d->APELocation, d->APEOriginalSize);
+ else {
+ if(d->hasID3v1) {
+ insert(d->APETag->render(), d->ID3v1Location, 0);
+ d->APEOriginalSize = d->APETag->footer()->completeTagSize();
+ d->hasAPE = true;
+ d->APELocation = d->ID3v1Location;
+ d->ID3v1Location += d->APEOriginalSize;
+ }
+ else {
+ seek(0, End);
+ d->APELocation = tell();
+ writeBlock(d->APETag->render());
+ d->APEOriginalSize = d->APETag->footer()->completeTagSize();
+ d->hasAPE = true;
+ }
+ }
+ }
+ else if(d->hasAPE)
+ success = strip(APE, false) && success;
+
return success;
}
return d->ID3v1Tag;
}
+APE::Tag *MPEG::File::APETag(bool create)
+{
+ if(!create || d->APETag)
+ return d->APETag;
+
+ // no APE tag exists and we've been asked to create one
+
+ d->APETag = new APE::Tag;
+ return d->APETag;
+}
+
bool MPEG::File::strip(int tags)
{
return strip(tags, true);
return false;
}
- if(tags & ID3v2 && d->hasID3v2) {
+ if((tags & ID3v2) && d->hasID3v2) {
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->ID3v2Location = -1;
d->ID3v2OriginalSize = 0;
d->ID3v1Location = findID3v1();
}
- if(tags & ID3v1 && d->hasID3v1) {
+ if((tags & ID3v1) && d->hasID3v1) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
d->hasID3v1 = false;
}
}
+ if((tags & APE) && d->hasAPE) {
+ removeBlock(d->APELocation, d->APEOriginalSize);
+ d->APELocation = -1;
+ d->hasAPE = false;
+ if(d->hasID3v1) {
+ if (d->ID3v1Location > d->APELocation)
+ d->ID3v1Location -= d->APEOriginalSize;
+ }
+ if(freeMemory) {
+ delete d->APETag;
+ d->APETag = 0;
+ }
+ }
+
return true;
}
d->hasID3v1 = true;
}
+ // Look for an APE tag
+
+ d->APELocation = findAPE();
+
+ if(d->APELocation >= 0) {
+
+ d->APETag = new APE::Tag(this, d->APELocation);
+
+ d->APEOriginalSize = d->APETag->footer()->completeTagSize();
+
+ d->hasAPE = true;
+ }
+
if(readProperties)
d->properties = new Properties(this, propertiesStyle);
}
return -1;
}
+long MPEG::File::findAPE()
+{
+ if(isValid()) {
+ if (d->hasID3v1)
+ seek(-160, End);
+ else
+ seek(-32, End);
+
+ long p = tell();
+
+ if(readBlock(8) == APE::Tag::fileIdentifier())
+ return p;
+ }
+ return -1;
+}
+
bool MPEG::File::secondSynchByte(char byte)
{
if(uchar(byte) == 0xff)
namespace ID3v2 { class Tag; class FrameFactory; }
namespace ID3v1 { class Tag; }
+ namespace APE { class Tag; }
//! An implementation of TagLib::File with MPEG (MP3) specific methods
ID3v1 = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
+ //! Matches APE tags.
+ APE = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
*
* \see ID3v1Tag()
* \see ID3v2Tag()
+ * \see APETag()
*/
virtual Tag *tag() const;
*/
ID3v1::Tag *ID3v1Tag(bool create = false);
+ /*!
+ * Returns a pointer to the APE tag of the file.
+ *
+ * If \a create is false (the default) this will return a null pointer
+ * if there is no valid APE tag. If \a create is true it will create
+ * an APE tag if one does not exist.
+ *
+ * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ APE::Tag *APETag(bool create = false);
+
/*!
* 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
*
* This is equivalent to strip(tags, true)
*
- * \note This will also invalidate pointers to the ID3v2 and ID3v1 tags
+ * \note This will also invalidate pointers to the ID3 and APE tags
* as their memory will be freed.
*/
bool strip(int tags = AllTags);
* file. By default it strips all tags. It returns true if the tags are
* successfully stripped.
*
- * If \a freeMemory is true the ID3v1 and ID3v2 tags will be deleted and
+ * If \a freeMemory is true the ID3 and APE tags will be deleted and
* pointers to them will be invalidated.
*/
// BIC: merge with the method above
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
long findID3v2();
long findID3v1();
+ long findAPE();
/*!
* MPEG frames can be recognized by the bit pattern 11111111 111, so the