#ifndef APE_APETAG_H
#define APE_APETAG_H
#include "NoWindows.h"
#include "SmartPtr.h"
#include <cstring>
class CIO;
#define CURRENT_APE_TAG_VERSION 2000
#define APE_TAG_FIELD_TITLE "Title"
#define APE_TAG_FIELD_ARTIST "Artist"
#define APE_TAG_FIELD_ALBUM "Album"
#define APE_TAG_FIELD_COMMENT "Comment"
#define APE_TAG_FIELD_YEAR "Year"
#define APE_TAG_FIELD_TRACK "Track"
#define APE_TAG_FIELD_GENRE "Genre"
#define APE_TAG_FIELD_COVER_ART_FRONT "Cover Art (front)"
#define APE_TAG_FIELD_NOTES "Notes"
#define APE_TAG_FIELD_LYRICS "Lyrics"
#define APE_TAG_FIELD_COPYRIGHT "Copyright"
#define APE_TAG_FIELD_BUY_URL "Buy UR"
#define APE_TAG_FIELD_ARTIST_URL "Artist UR"
#define APE_TAG_FIELD_PUBLISHER_URL "Publisher UR"
#define APE_TAG_FIELD_FILE_URL "File UR"
#define APE_TAG_FIELD_COPYRIGHT_URL "Copyright UR"
#define APE_TAG_FIELD_MJ_METADATA "Media Jukebox Metadata"
#define APE_TAG_FIELD_TOOL_NAME "Tool Name"
#define APE_TAG_FIELD_TOOL_VERSION "Tool Version"
#define APE_TAG_FIELD_PEAK_LEVEL "Peak Level"
#define APE_TAG_FIELD_REPLAY_GAIN_RADIO "Replay Gain (radio)"
#define APE_TAG_FIELD_REPLAY_GAIN_ALBUM "Replay Gain (album)"
#define APE_TAG_FIELD_COMPOSER "Composer"
#define APE_TAG_FIELD_KEYWORDS "Keywords"
#define APE_TAG_GENRE_UNDEFINED "Undefined"
#define ID3_TAG_BYTES 128
struct ID3_TAG
{
char Header[3];
char Title[30];
char Artist[30];
char Album[30];
char Year[4];
char Comment[29];
unsigned char Track;
unsigned char Genre;
};
#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31)
#define APE_TAG_FLAG_CONTAINS_FOOTER (1 << 30)
#define APE_TAG_FLAG_IS_HEADER (1 << 29)
#define APE_TAG_FLAGS_DEFAULT (APE_TAG_FLAG_CONTAINS_FOOTER)
#define TAG_FIELD_FLAG_READ_ONLY (1 << 0)
#define TAG_FIELD_FLAG_DATA_TYPE_MASK (6)
#define TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8 (0 << 1)
#define TAG_FIELD_FLAG_DATA_TYPE_BINARY (1 << 1)
#define TAG_FIELD_FLAG_DATA_TYPE_EXTERNAL_INFO (2 << 1)
#define TAG_FIELD_FLAG_DATA_TYPE_RESERVED (3 << 1)
#define APE_TAG_FOOTER_BYTES 32
class APE_TAG_FOOTER
{
protected:
char m_cID[8];
int m_nVersion;
int m_nSize;
int m_nFields;
int m_nFlags;
char m_cReserved[8];
public:
APE_TAG_FOOTER(int nFields = 0, int nFieldBytes = 0)
{
memcpy(m_cID, "APETAGEX", 8);
memset(m_cReserved, 0, 8);
m_nFields = nFields;
m_nFlags = APE_TAG_FLAGS_DEFAULT;
m_nSize = nFieldBytes + APE_TAG_FOOTER_BYTES;
m_nVersion = CURRENT_APE_TAG_VERSION;
}
int GetTotalTagBytes() { return m_nSize + (GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0); }
int GetFieldBytes() { return m_nSize - APE_TAG_FOOTER_BYTES; }
int GetFieldsOffset() { return GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0; }
int GetNumberFields() { return m_nFields; }
BOOL GetHasHeader() { return (m_nFlags & APE_TAG_FLAG_CONTAINS_HEADER) ? TRUE : FALSE; }
BOOL GetIsHeader() { return (m_nFlags & APE_TAG_FLAG_IS_HEADER) ? TRUE : FALSE; }
int GetVersion() { return m_nVersion; }
BOOL GetIsValid(BOOL bAllowHeader)
{
BOOL bValid = (strncmp(m_cID, "APETAGEX", 8) == 0) &&
(m_nVersion <= CURRENT_APE_TAG_VERSION) &&
(m_nFields <= 65536) &&
(GetFieldBytes() <= (1024 * 1024 * 16));
if (bValid && (bAllowHeader == FALSE) && GetIsHeader())
bValid = FALSE;
return bValid ? TRUE : FALSE;
}
};
class CAPETagField
{
public:
CAPETagField(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes = -1, int nFlags = 0);
~CAPETagField();
int GetFieldSize();
const str_utf16 * GetFieldName();
const char * GetFieldValue();
int GetFieldValueSize();
int GetFieldFlags();
int SaveField(char * pBuffer);
BOOL GetIsReadOnly() { return (m_nFieldFlags & TAG_FIELD_FLAG_READ_ONLY) ? TRUE : FALSE; }
BOOL GetIsUTF8Text() { return ((m_nFieldFlags & TAG_FIELD_FLAG_DATA_TYPE_MASK) == TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8) ? TRUE : FALSE; }
void SetFieldFlags(int nFlags) { m_nFieldFlags = nFlags; }
private:
CSmartPtr<str_utf16> m_spFieldNameUTF16;
CSmartPtr<char> m_spFieldValue;
int m_nFieldFlags;
int m_nFieldValueBytes;
};
class CAPETag
{
public:
CAPETag(CIO * pIO, BOOL bAnalyze = TRUE);
CAPETag(const str_utf16 * pFilename, BOOL bAnalyze = TRUE);
virtual ~CAPETag();
virtual int Save(BOOL bUseOldID3 = FALSE);
virtual int Remove(BOOL bUpdate = TRUE);
virtual int SetFieldString(const str_utf16 * pFieldName, const str_utf16 * pFieldValue);
virtual int SetFieldString(const str_utf16 * pFieldName, const char * pFieldValue, BOOL bAlreadyUTF8Encoded);
virtual int SetFieldBinary(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes, int nFieldFlags);
virtual int GetFieldBinary(const str_utf16 * pFieldName, void * pBuffer, int * pBufferBytes);
virtual int GetFieldString(const str_utf16 * pFieldName, str_utf16 * pBuffer, int * pBufferCharacters);
virtual int RemoveField(const str_utf16 * pFieldName);
virtual int RemoveField(int nIndex);
virtual int ClearFields();
virtual int GetTagBytes();
virtual int CreateID3Tag(ID3_TAG * pID3Tag);
virtual BOOL GetHasID3Tag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasID3Tag; }
virtual BOOL GetHasAPETag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasAPETag; }
virtual int GetAPETagVersion() { return GetHasAPETag() ? m_nAPETagVersion : -1; }
virtual CAPETagField * GetTagField(const str_utf16 * pFieldName);
virtual CAPETagField * GetTagField(int nIndex);
virtual void SetIgnoreReadOnly(BOOL bIgnoreReadOnly) { m_bIgnoreReadOnly = bIgnoreReadOnly; }
private:
int Analyze();
int GetTagFieldIndex(const str_utf16 * pFieldName);
int WriteBufferToEndOfIO(void * pBuffer, int nBytes);
int LoadField(const char * pBuffer, int nMaximumBytes, int * pBytes);
int SortFields();
static int CompareFields(const void * pA, const void * pB);
int SetFieldID3String(const str_utf16 * pFieldName, const char * pFieldValue, int nBytes);
int GetFieldID3String(const str_utf16 * pFieldName, char * pBuffer, int nBytes);
CSmartPtr<CIO> m_spIO;
BOOL m_bAnalyzed;
int m_nTagBytes;
int m_nFields;
CAPETagField * m_aryFields[256];
BOOL m_bHasAPETag;
int m_nAPETagVersion;
BOOL m_bHasID3Tag;
BOOL m_bIgnoreReadOnly;
};
#endif