1 #ifndef APE_APETAG_H 2 #define APE_APETAG_H 3 4 #include "NoWindows.h" 5 #include "SmartPtr.h" 6 7 #include <cstring> 8 9 class CIO; 10 11 /***************************************************************************************** 12 APETag version history / supported formats 13 14 1.0 (1000) - Original APE tag spec. Fully supported by this code. 15 2.0 (2000) - Refined APE tag spec (better streaming support, UTF encoding). Fully supported by this code. 16 17 Notes: 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 /***************************************************************************************** 23 APETag layout 24 25 1) Header - APE_TAG_FOOTER (optional) (32 bytes) 26 2) 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) 31 3) Footer - APE_TAG_FOOTER (32 bytes) 32 *****************************************************************************************/ 33 34 /***************************************************************************************** 35 Notes 36 37 -When saving images, store the filename (no directory -- i.e. Cover.jpg) in UTF-8 followed 38 by a null terminator, followed by the image data. 39 *****************************************************************************************/ 40 41 /***************************************************************************************** 42 The 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 /***************************************************************************************** 75 Standard APE tag field values 76 *****************************************************************************************/ 77 #define APE_TAG_GENRE_UNDEFINED "Undefined" 78 79 /***************************************************************************************** 80 ID3 v1.1 tag 81 *****************************************************************************************/ 82 #define ID3_TAG_BYTES 128 83 struct 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 /***************************************************************************************** 96 Footer (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 /***************************************************************************************** 105 Tag 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 /***************************************************************************************** 116 The 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 120 class APE_TAG_FOOTER 121 { 122 protected: 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 131 public: 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 GetTotalTagBytes()143 int GetTotalTagBytes() { return m_nSize + (GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0); } GetFieldBytes()144 int GetFieldBytes() { return m_nSize - APE_TAG_FOOTER_BYTES; } GetFieldsOffset()145 int GetFieldsOffset() { return GetHasHeader() ? APE_TAG_FOOTER_BYTES : 0; } GetNumberFields()146 int GetNumberFields() { return m_nFields; } GetHasHeader()147 BOOL GetHasHeader() { return (m_nFlags & APE_TAG_FLAG_CONTAINS_HEADER) ? TRUE : FALSE; } GetIsHeader()148 BOOL GetIsHeader() { return (m_nFlags & APE_TAG_FLAG_IS_HEADER) ? TRUE : FALSE; } GetVersion()149 int GetVersion() { return m_nVersion; } 150 GetIsValid(BOOL bAllowHeader)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 /***************************************************************************************** 166 CAPETagField class (an APE tag is an array of these) 167 *****************************************************************************************/ 168 class CAPETagField 169 { 170 public: 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 GetIsReadOnly()197 BOOL GetIsReadOnly() { return (m_nFieldFlags & TAG_FIELD_FLAG_READ_ONLY) ? TRUE : FALSE; } GetIsUTF8Text()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) SetFieldFlags(int nFlags)201 void SetFieldFlags(int nFlags) { m_nFieldFlags = nFlags; } 202 203 private: 204 205 CSmartPtr<str_utf16> m_spFieldNameUTF16; 206 CSmartPtr<char> m_spFieldValue; 207 int m_nFieldFlags; 208 int m_nFieldValueBytes; 209 }; 210 211 /***************************************************************************************** 212 CAPETag class 213 *****************************************************************************************/ 214 class CAPETag 215 { 216 public: 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 GetHasID3Tag()261 virtual BOOL GetHasID3Tag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasID3Tag; } GetHasAPETag()262 virtual BOOL GetHasAPETag() { if (m_bAnalyzed == FALSE) { Analyze(); } return m_bHasAPETag; } GetAPETagVersion()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 SetIgnoreReadOnly(BOOL bIgnoreReadOnly)271 virtual void SetIgnoreReadOnly(BOOL bIgnoreReadOnly) { m_bIgnoreReadOnly = bIgnoreReadOnly; } 272 273 private: 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