1#ifndef APE_APETAG_H 2#define APE_APETAG_H 3 4#include "NoWindows.h" 5#include "SmartPtr.h" 6 7#include <cstring> 8 9class CIO; 10 11/***************************************************************************************** 12APETag version history / supported formats 13 141.0 (1000) - Original APE tag spec. Fully supported by this code. 152.0 (2000) - Refined APE tag spec (better streaming support, UTF encoding). Fully supported by this code. 16 17Notes: 18 - also supports reading of ID3v1.1 tags 19 - all saving done in the APE Tag format using CURRENT_APE_TAG_VERSION 20*****************************************************************************************/ 21 22/***************************************************************************************** 23APETag layout 24 251) Header - APE_TAG_FOOTER (optional) (32 bytes) 262) Fields (array): 27 Value Size (4 bytes) 28 Flags (4 bytes) 29 Field Name (? ANSI bytes -- requires NULL terminator -- in range of 0x20 (space) to 0x7E (tilde)) 30 Value ([Value Size] bytes) 313) Footer - APE_TAG_FOOTER (32 bytes) 32*****************************************************************************************/ 33 34/***************************************************************************************** 35Notes 36 37-When saving images, store the filename (no directory -- i.e. Cover.jpg) in UTF-8 followed 38by a null terminator, followed by the image data. 39*****************************************************************************************/ 40 41/***************************************************************************************** 42The version of the APE tag 43*****************************************************************************************/ 44#define CURRENT_APE_TAG_VERSION 2000 45 46/***************************************************************************************** 47"Standard" APE tag fields 48*****************************************************************************************/ 49#define APE_TAG_FIELD_TITLE "Title" 50#define APE_TAG_FIELD_ARTIST "Artist" 51#define APE_TAG_FIELD_ALBUM "Album" 52#define APE_TAG_FIELD_COMMENT "Comment" 53#define APE_TAG_FIELD_YEAR "Year" 54#define APE_TAG_FIELD_TRACK "Track" 55#define APE_TAG_FIELD_GENRE "Genre" 56#define APE_TAG_FIELD_COVER_ART_FRONT "Cover Art (front)" 57#define APE_TAG_FIELD_NOTES "Notes" 58#define APE_TAG_FIELD_LYRICS "Lyrics" 59#define APE_TAG_FIELD_COPYRIGHT "Copyright" 60#define APE_TAG_FIELD_BUY_URL "Buy UR" 61#define APE_TAG_FIELD_ARTIST_URL "Artist UR" 62#define APE_TAG_FIELD_PUBLISHER_URL "Publisher UR" 63#define APE_TAG_FIELD_FILE_URL "File UR" 64#define APE_TAG_FIELD_COPYRIGHT_URL "Copyright UR" 65#define APE_TAG_FIELD_MJ_METADATA "Media Jukebox Metadata" 66#define APE_TAG_FIELD_TOOL_NAME "Tool Name" 67#define APE_TAG_FIELD_TOOL_VERSION "Tool Version" 68#define APE_TAG_FIELD_PEAK_LEVEL "Peak Level" 69#define APE_TAG_FIELD_REPLAY_GAIN_RADIO "Replay Gain (radio)" 70#define APE_TAG_FIELD_REPLAY_GAIN_ALBUM "Replay Gain (album)" 71#define APE_TAG_FIELD_COMPOSER "Composer" 72#define APE_TAG_FIELD_KEYWORDS "Keywords" 73 74/***************************************************************************************** 75Standard APE tag field values 76*****************************************************************************************/ 77#define APE_TAG_GENRE_UNDEFINED "Undefined" 78 79/***************************************************************************************** 80ID3 v1.1 tag 81*****************************************************************************************/ 82#define ID3_TAG_BYTES 128 83struct ID3_TAG 84{ 85 char Header[3]; // should equal 'TAG' 86 char Title[30]; // title 87 char Artist[30]; // artist 88 char Album[30]; // album 89 char Year[4]; // year 90 char Comment[29]; // comment 91 unsigned char Track; // track 92 unsigned char Genre; // genre 93}; 94 95/***************************************************************************************** 96Footer (and header) flags 97*****************************************************************************************/ 98#define APE_TAG_FLAG_CONTAINS_HEADER (1 << 31) 99#define APE_TAG_FLAG_CONTAINS_FOOTER (1 << 30) 100#define APE_TAG_FLAG_IS_HEADER (1 << 29) 101 102#define APE_TAG_FLAGS_DEFAULT (APE_TAG_FLAG_CONTAINS_FOOTER) 103 104/***************************************************************************************** 105Tag field flags 106*****************************************************************************************/ 107#define TAG_FIELD_FLAG_READ_ONLY (1 << 0) 108 109#define TAG_FIELD_FLAG_DATA_TYPE_MASK (6) 110#define TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8 (0 << 1) 111#define TAG_FIELD_FLAG_DATA_TYPE_BINARY (1 << 1) 112#define TAG_FIELD_FLAG_DATA_TYPE_EXTERNAL_INFO (2 << 1) 113#define TAG_FIELD_FLAG_DATA_TYPE_RESERVED (3 << 1) 114 115/***************************************************************************************** 116The footer at the end of APE tagged files (can also optionally be at the front of the tag) 117*****************************************************************************************/ 118#define APE_TAG_FOOTER_BYTES 32 119 120class APE_TAG_FOOTER 121{ 122protected: 123 124 char m_cID[8]; // should equal 'APETAGEX' 125 int m_nVersion; // equals CURRENT_APE_TAG_VERSION 126 int m_nSize; // the complete size of the tag, including this footer (excludes header) 127 int m_nFields; // the number of fields in the tag 128 int m_nFlags; // the tag flags 129 char m_cReserved[8]; // reserved for later use (must be zero) 130 131public: 132 133 APE_TAG_FOOTER(int nFields = 0, int nFieldBytes = 0) 134 { 135 memcpy(m_cID, "APETAGEX", 8); 136 memset(m_cReserved, 0, 8); 137 m_nFields = nFields; 138 m_nFlags = APE_TAG_FLAGS_DEFAULT; 139 m_nSize = nFieldBytes + APE_TAG_FOOTER_BYTES; 140 m_nVersion = CURRENT_APE_TAG_VERSION; 141 } 142 143 int GetTotalTagBytes() { return m_nSize + (GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0); } 144 int GetFieldBytes() { return m_nSize - APE_TAG_FOOTER_BYTES; } 145 int GetFieldsOffset() { return GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0; } 146 int GetNumberFields() { return m_nFields; } 147 BOOL GetHasHeader() { return (m_nFlags & APE_TAG_FLAG_CONTAINS_HEADER) ? TRUE : FALSE; } 148 BOOL GetIsHeader() { return (m_nFlags & APE_TAG_FLAG_IS_HEADER) ? TRUE : FALSE; } 149 int GetVersion() { return m_nVersion; } 150 151 BOOL GetIsValid(BOOL bAllowHeader) 152 { 153 BOOL bValid = (strncmp(m_cID, "APETAGEX", 8) == 0) && 154 (m_nVersion <= CURRENT_APE_TAG_VERSION) && 155 (m_nFields <= 65536) && 156 (GetFieldBytes() <= (1024 * 1024 * 16)); 157 158 if (bValid && (bAllowHeader == FALSE) && GetIsHeader()) 159 bValid = FALSE; 160 161 return bValid ? TRUE : FALSE; 162 } 163}; 164 165/***************************************************************************************** 166CAPETagField class (an APE tag is an array of these) 167*****************************************************************************************/ 168class CAPETagField 169{ 170public: 171 172 // create a tag field (use nFieldBytes = -1 for null-terminated strings) 173 CAPETagField(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes = -1, int nFlags = 0); 174 175 // destructor 176 ~CAPETagField(); 177 178 // gets the size of the entire field in bytes (name, value, and metadata) 179 int GetFieldSize(); 180 181 // get the name of the field 182 const str_utf16 * GetFieldName(); 183 184 // get the value of the field 185 const char * GetFieldValue(); 186 187 // get the size of the value (in bytes) 188 int GetFieldValueSize(); 189 190 // get any special flags 191 int GetFieldFlags(); 192 193 // output the entire field to a buffer (GetFieldSize() bytes) 194 int SaveField(char * pBuffer); 195 196 // checks to see if the field is read-only 197 BOOL GetIsReadOnly() { return (m_nFieldFlags & TAG_FIELD_FLAG_READ_ONLY) ? TRUE : FALSE; } 198 BOOL GetIsUTF8Text() { return ((m_nFieldFlags & TAG_FIELD_FLAG_DATA_TYPE_MASK) == TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8) ? TRUE : FALSE; } 199 200 // set helpers (use with EXTREME caution) 201 void SetFieldFlags(int nFlags) { m_nFieldFlags = nFlags; } 202 203private: 204 205 CSmartPtr<str_utf16> m_spFieldNameUTF16; 206 CSmartPtr<char> m_spFieldValue; 207 int m_nFieldFlags; 208 int m_nFieldValueBytes; 209}; 210 211/***************************************************************************************** 212CAPETag class 213*****************************************************************************************/ 214class CAPETag 215{ 216public: 217 218 // create an APE tag 219 // bAnalyze determines whether it will analyze immediately or on the first request 220 // be careful with multiple threads / file pointer movement if you don't analyze immediately 221 CAPETag(CIO * pIO, BOOL bAnalyze = TRUE); 222 CAPETag(const str_utf16 * pFilename, BOOL bAnalyze = TRUE); 223 224 // SHINTA: All virtual functions make virtual 225 226 // destructor 227 virtual ~CAPETag(); 228 229 // save the tag to the I/O source (bUseOldID3 forces it to save as an ID3v1.1 tag instead of an APE tag) 230 virtual int Save(BOOL bUseOldID3 = FALSE); 231 232 // removes any tags from the file (bUpdate determines whether is should re-analyze after removing the tag) 233 virtual int Remove(BOOL bUpdate = TRUE); 234 235 // sets the value of a field (use nFieldBytes = -1 for null terminated strings) 236 // note: using NULL or "" for a string type will remove the field 237 virtual int SetFieldString(const str_utf16 * pFieldName, const str_utf16 * pFieldValue); 238 virtual int SetFieldString(const str_utf16 * pFieldName, const char * pFieldValue, BOOL bAlreadyUTF8Encoded); 239 virtual int SetFieldBinary(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes, int nFieldFlags); 240 241 // gets the value of a field (returns -1 and an empty buffer if the field doesn't exist) 242 virtual int GetFieldBinary(const str_utf16 * pFieldName, void * pBuffer, int * pBufferBytes); 243 virtual int GetFieldString(const str_utf16 * pFieldName, str_utf16 * pBuffer, int * pBufferCharacters); 244// virtual int GetFieldString(const str_utf16 * pFieldName, str_ansi * pBuffer, int * pBufferCharacters, BOOL bUTF8Encode = FALSE); 245 246 // remove a specific field 247 virtual int RemoveField(const str_utf16 * pFieldName); 248 virtual int RemoveField(int nIndex); 249 250 // clear all the fields 251 virtual int ClearFields(); 252 253 // get the total tag bytes in the file from the last analyze 254 // need to call Save() then Analyze() to update any changes 255 virtual int GetTagBytes(); 256 257 // fills in an ID3_TAG using the current fields (useful for quickly converting the tag) 258 virtual int CreateID3Tag(ID3_TAG * pID3Tag); 259 260 // see whether the file has an ID3 or APE tag 261 virtual BOOL GetHasID3Tag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasID3Tag; } 262 virtual BOOL GetHasAPETag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasAPETag; } 263 virtual int GetAPETagVersion() { return GetHasAPETag() ? m_nAPETagVersion : -1; } 264 265 // gets a desired tag field (returns NULL if not found) 266 // again, be careful, because this a pointer to the actual field in this class 267 virtual CAPETagField * GetTagField(const str_utf16 * pFieldName); 268 virtual CAPETagField * GetTagField(int nIndex); 269 270 // options 271 virtual void SetIgnoreReadOnly(BOOL bIgnoreReadOnly) { m_bIgnoreReadOnly = bIgnoreReadOnly; } 272 273private: 274 275 // private functions 276 int Analyze(); 277 int GetTagFieldIndex(const str_utf16 * pFieldName); 278 int WriteBufferToEndOfIO(void * pBuffer, int nBytes); 279 int LoadField(const char * pBuffer, int nMaximumBytes, int * pBytes); 280 int SortFields(); 281 static int CompareFields(const void * pA, const void * pB); 282 283 // helper set / get field functions 284 int SetFieldID3String(const str_utf16 * pFieldName, const char * pFieldValue, int nBytes); 285 int GetFieldID3String(const str_utf16 * pFieldName, char * pBuffer, int nBytes); 286 287 // private data 288 CSmartPtr<CIO> m_spIO; 289 BOOL m_bAnalyzed; 290 int m_nTagBytes; 291 int m_nFields; 292 CAPETagField * m_aryFields[256]; 293 BOOL m_bHasAPETag; 294 int m_nAPETagVersion; 295 BOOL m_bHasID3Tag; 296 BOOL m_bIgnoreReadOnly; 297}; 298 299#endif // #ifndef APE_APETAG_H 300 301