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