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