1#include <algorithm>
2
3#include "All.h"
4#include "APETag.h"
5#include "ID3Genres.h"
6#include "CharacterHelper.h"
7#include "IO.h"
8#include IO_HEADER_FILE
9
10/*****************************************************************************************
11CAPETagField
12*****************************************************************************************/
13
14CAPETagField::CAPETagField(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes, int nFlags)
15{
16    // field name
17    m_spFieldNameUTF16.Assign(new str_utf16 [wcslen(pFieldName) + 1], TRUE);
18    memcpy(m_spFieldNameUTF16, pFieldName, (wcslen(pFieldName) + 1) * sizeof(str_utf16));
19
20    // data (we'll always allocate two extra bytes and memset to 0 so we're safely NULL terminated)
21    m_nFieldValueBytes = max(nFieldBytes, 0);
22    m_spFieldValue.Assign(new char [m_nFieldValueBytes + 2], TRUE);
23    memset(m_spFieldValue, 0, m_nFieldValueBytes + 2);
24    if (m_nFieldValueBytes > 0)
25        memcpy(m_spFieldValue, pFieldValue, m_nFieldValueBytes);
26
27    // flags
28    m_nFieldFlags = nFlags;
29}
30
31CAPETagField::~CAPETagField()
32{
33}
34
35int CAPETagField::GetFieldSize()
36{
37    CSmartPtr<char> spFieldNameANSI(GetANSIFromUTF16(m_spFieldNameUTF16), TRUE);
38    return (strlen(spFieldNameANSI) + 1) + m_nFieldValueBytes + 4 + 4;
39}
40
41const str_utf16 * CAPETagField::GetFieldName()
42{
43    return m_spFieldNameUTF16;
44}
45
46const char * CAPETagField::GetFieldValue()
47{
48    return m_spFieldValue;
49}
50
51int CAPETagField::GetFieldValueSize()
52{
53    return m_nFieldValueBytes;
54}
55
56int CAPETagField::GetFieldFlags()
57{
58    return m_nFieldFlags;
59}
60
61int CAPETagField::SaveField(char * pBuffer)
62{
63    *((int *) pBuffer) = m_nFieldValueBytes;
64    pBuffer += 4;
65    *((int *) pBuffer) = m_nFieldFlags;
66    pBuffer += 4;
67
68    CSmartPtr<char> spFieldNameANSI((char *) GetANSIFromUTF16(m_spFieldNameUTF16), TRUE);
69    strcpy(pBuffer, spFieldNameANSI);
70    pBuffer += strlen(spFieldNameANSI) + 1;
71
72    memcpy(pBuffer, m_spFieldValue, m_nFieldValueBytes);
73
74    return GetFieldSize();
75}
76
77
78/*****************************************************************************************
79CAPETag
80*****************************************************************************************/
81
82CAPETag::CAPETag(const str_utf16 * pFilename, BOOL bAnalyze)
83{
84    m_spIO.Assign(new IO_CLASS_NAME);
85    m_spIO->Open(pFilename);
86
87    m_bAnalyzed = FALSE;
88    m_nFields = 0;
89    m_nTagBytes = 0;
90    m_bIgnoreReadOnly = FALSE;
91
92    if (bAnalyze)
93    {
94        Analyze();
95    }
96}
97
98CAPETag::CAPETag(CIO * pIO, BOOL bAnalyze)
99{
100    m_spIO.Assign(pIO, FALSE, FALSE); // we don't own the IO source
101    m_bAnalyzed = FALSE;
102    m_nFields = 0;
103    m_nTagBytes = 0;
104
105    if (bAnalyze)
106    {
107        Analyze();
108    }
109}
110
111CAPETag::~CAPETag()
112{
113    ClearFields();
114}
115
116int CAPETag::GetTagBytes()
117{
118    if (m_bAnalyzed == FALSE) { Analyze(); }
119
120    return m_nTagBytes;
121}
122
123CAPETagField * CAPETag::GetTagField(int nIndex)
124{
125    if (m_bAnalyzed == FALSE) { Analyze(); }
126
127    if ((nIndex >= 0) && (nIndex < m_nFields))
128    {
129        return m_aryFields[nIndex];
130    }
131
132    return NULL;
133}
134
135int CAPETag::Save(BOOL bUseOldID3)
136{
137    if (Remove(FALSE) != ERROR_SUCCESS)
138        return -1;
139
140    if (m_nFields == 0) { return ERROR_SUCCESS; }
141
142    int nRetVal = -1;
143
144    if (bUseOldID3 == FALSE)
145    {
146        int z = 0;
147
148        // calculate the size of the whole tag
149        int nFieldBytes = 0;
150        for (z = 0; z < m_nFields; z++)
151            nFieldBytes += m_aryFields[z]->GetFieldSize();
152
153        // sort the fields
154        SortFields();
155
156        // build the footer
157        APE_TAG_FOOTER APETagFooter(m_nFields, nFieldBytes);
158
159        // make a buffer for the tag
160        int nTotalTagBytes = APETagFooter.GetTotalTagBytes();
161        CSmartPtr<char> spRawTag(new char [nTotalTagBytes], TRUE);
162
163        // save the fields
164        int nLocation = 0;
165        for (z = 0; z < m_nFields; z++)
166            nLocation += m_aryFields[z]->SaveField(&spRawTag[nLocation]);
167
168        // add the footer to the buffer
169        memcpy(&spRawTag[nLocation], &APETagFooter, APE_TAG_FOOTER_BYTES);
170        nLocation += APE_TAG_FOOTER_BYTES;
171
172        // dump the tag to the I/O source
173        nRetVal = WriteBufferToEndOfIO(spRawTag, nTotalTagBytes);
174    }
175    else
176    {
177        // build the ID3 tag
178        ID3_TAG ID3Tag;
179        CreateID3Tag(&ID3Tag);
180        nRetVal = WriteBufferToEndOfIO(&ID3Tag, sizeof(ID3_TAG));
181    }
182
183    return nRetVal;
184}
185
186int CAPETag::WriteBufferToEndOfIO(void * pBuffer, int nBytes)
187{
188    int nOriginalPosition = m_spIO->GetPosition();
189
190    unsigned int nBytesWritten = 0;
191    m_spIO->Seek(0, FILE_END);
192
193    int nRetVal = m_spIO->Write(pBuffer, nBytes, &nBytesWritten);
194
195    m_spIO->Seek(nOriginalPosition, FILE_BEGIN);
196
197    return nRetVal;
198}
199
200int CAPETag::Analyze()
201{
202    // clean-up
203    ID3_TAG ID3Tag;
204    ClearFields();
205    m_nTagBytes = 0;
206
207    m_bAnalyzed = TRUE;
208
209    // store the original location
210    int nOriginalPosition = m_spIO->GetPosition();
211
212    // check for a tag
213    unsigned int nBytesRead;
214    int nRetVal;
215    m_bHasID3Tag = FALSE;
216    m_bHasAPETag = FALSE;
217    m_nAPETagVersion = -1;
218    m_spIO->Seek(-ID3_TAG_BYTES, FILE_END);
219    nRetVal = m_spIO->Read((unsigned char *) &ID3Tag, sizeof(ID3_TAG), &nBytesRead);
220
221    if ((nBytesRead == sizeof(ID3_TAG)) && (nRetVal == 0))
222    {
223        if (ID3Tag.Header[0] == 'T' && ID3Tag.Header[1] == 'A' && ID3Tag.Header[2] == 'G')
224        {
225            m_bHasID3Tag = TRUE;
226            m_nTagBytes += ID3_TAG_BYTES;
227        }
228    }
229
230    // set the fields
231    if (m_bHasID3Tag)
232    {
233        SetFieldID3String(APE_TAG_FIELD_ARTIST, ID3Tag.Artist, 30);
234        SetFieldID3String(APE_TAG_FIELD_ALBUM, ID3Tag.Album, 30);
235        SetFieldID3String(APE_TAG_FIELD_TITLE, ID3Tag.Title, 30);
236        SetFieldID3String(APE_TAG_FIELD_COMMENT, ID3Tag.Comment, 28);
237        SetFieldID3String(APE_TAG_FIELD_YEAR, ID3Tag.Year, 4);
238
239        char cTemp[16]; sprintf(cTemp, "%d", ID3Tag.Track);
240        SetFieldString(APE_TAG_FIELD_TRACK, cTemp, FALSE);
241
242        if ((ID3Tag.Genre == GENRE_UNDEFINED) || (ID3Tag.Genre >= GENRE_COUNT))
243            SetFieldString(APE_TAG_FIELD_GENRE, APE_TAG_GENRE_UNDEFINED);
244        else
245            SetFieldString(APE_TAG_FIELD_GENRE, g_ID3Genre[ID3Tag.Genre]);
246    }
247
248    // try loading the APE tag
249    if (m_bHasID3Tag == FALSE)
250    {
251        APE_TAG_FOOTER APETagFooter;
252        m_spIO->Seek(-int(APE_TAG_FOOTER_BYTES), FILE_END);
253        nRetVal = m_spIO->Read((unsigned char *) &APETagFooter, APE_TAG_FOOTER_BYTES, &nBytesRead);
254        if ((nBytesRead == APE_TAG_FOOTER_BYTES) && (nRetVal == 0))
255        {
256            if (APETagFooter.GetIsValid(FALSE))
257            {
258                m_bHasAPETag = TRUE;
259                m_nAPETagVersion = APETagFooter.GetVersion();
260
261                int nRawFieldBytes = APETagFooter.GetFieldBytes();
262                m_nTagBytes += APETagFooter.GetTotalTagBytes();
263
264                CSmartPtr<char> spRawTag(new char [nRawFieldBytes], TRUE);
265                m_spIO->Seek(-(APETagFooter.GetTotalTagBytes() - APETagFooter.GetFieldsOffset()), FILE_END);
266                nRetVal = m_spIO->Read((unsigned char *) spRawTag.GetPtr(), nRawFieldBytes, &nBytesRead);
267
268                if ((nRetVal == 0) && (nRawFieldBytes == int(nBytesRead)))
269                {
270                    // parse out the raw fields
271                    int nLocation = 0;
272                    for (int z = 0; z < APETagFooter.GetNumberFields(); z++)
273                    {
274                        int nMaximumFieldBytes = nRawFieldBytes - nLocation;
275
276                        int nBytes = 0;
277                        if (LoadField(&spRawTag[nLocation], nMaximumFieldBytes, &nBytes) != ERROR_SUCCESS)
278                        {
279                            // if LoadField(...) fails, it means that the tag is corrupt (accidently or intentionally)
280                            // we'll just bail out -- leaving the fields we've already set
281                            break;
282                        }
283                        nLocation += nBytes;
284                    }
285                }
286            }
287        }
288    }
289
290    // restore the file pointer
291    m_spIO->Seek(nOriginalPosition, FILE_BEGIN);
292
293    return ERROR_SUCCESS;
294}
295
296int CAPETag::ClearFields()
297{
298    for (int z = 0; z < m_nFields; z++)
299    {
300        SAFE_DELETE(m_aryFields[z])
301    }
302
303    m_nFields = 0;
304
305    return ERROR_SUCCESS;
306}
307
308int CAPETag::GetTagFieldIndex(const str_utf16 * pFieldName)
309{
310    if (m_bAnalyzed == FALSE) { Analyze(); }
311    if (pFieldName == NULL) return -1;
312
313    for (int z = 0; z < m_nFields; z++)
314    {
315        if (wcsicmp(m_aryFields[z]->GetFieldName(), pFieldName) == 0)
316            return z;
317    }
318
319    return -1;
320
321}
322
323CAPETagField * CAPETag::GetTagField(const str_utf16 * pFieldName)
324{
325    int nIndex = GetTagFieldIndex(pFieldName);
326    return (nIndex != -1) ? m_aryFields[nIndex] : NULL;
327}
328
329#if 0
330int CAPETag::GetFieldString(const str_utf16 * pFieldName, str_ansi * pBuffer, int * pBufferCharacters, BOOL bUTF8Encode)
331{
332    int nOriginalCharacters = *pBufferCharacters;
333    str_utf16 * pUTF16 = new str_utf16 [*pBufferCharacters + 1];
334    pUTF16[0] = 0;
335
336    int nRetVal = GetFieldString(pFieldName, pUTF16, pBufferCharacters);
337    if (nRetVal == ERROR_SUCCESS)
338    {
339        CSmartPtr<str_ansi> spANSI(bUTF8Encode ? (str_ansi *) GetUTF8FromUTF16(pUTF16) : GetANSIFromUTF16(pUTF16), TRUE);
340        if (int(strlen(spANSI)) > nOriginalCharacters)
341        {
342            memset(pBuffer, 0, nOriginalCharacters * sizeof(str_ansi));
343            *pBufferCharacters = 0;
344            nRetVal = ERROR_UNDEFINED;
345        }
346        else
347        {
348            strcpy(pBuffer, spANSI);
349            *pBufferCharacters = strlen(spANSI);
350        }
351    }
352
353    delete [] pUTF16;
354
355    return nRetVal;
356}
357#endif
358
359
360int CAPETag::GetFieldString(const str_utf16 * pFieldName, str_utf16 * pBuffer, int * pBufferCharacters)
361{
362    if (m_bAnalyzed == FALSE) { Analyze(); }
363
364    int nRetVal = ERROR_UNDEFINED;
365
366    if (*pBufferCharacters > 0)
367    {
368        CAPETagField * pAPETagField = GetTagField(pFieldName);
369        if (pAPETagField == NULL)
370        {
371            // the field doesn't exist -- return an empty string
372            memset(pBuffer, 0, *pBufferCharacters * sizeof(str_utf16));
373            *pBufferCharacters = 0;
374        }
375        else if (pAPETagField->GetIsUTF8Text() || (m_nAPETagVersion < 2000))
376        {
377            // get the value in UTF-16 format
378            CSmartPtr<str_utf16> spUTF16;
379            if (m_nAPETagVersion >= 2000)
380                spUTF16.Assign(GetUTF16FromUTF8((str_utf8 *) pAPETagField->GetFieldValue()), TRUE);
381            else
382                spUTF16.Assign(GetUTF16FromANSI(pAPETagField->GetFieldValue()), TRUE);
383
384            // get the number of characters
385            int nCharacters = (wcslen(spUTF16) + 1);
386            if (nCharacters > *pBufferCharacters)
387            {
388                // we'll fail here, because it's not clear what would get returned (null termination, size, etc.)
389                // and we really don't want to cause buffer overruns on the client side
390                *pBufferCharacters = nCharacters;
391            }
392            else
393            {
394                // just copy in
395                *pBufferCharacters = nCharacters;
396                memcpy(pBuffer, spUTF16.GetPtr(), *pBufferCharacters * sizeof(str_utf16));
397                nRetVal = ERROR_SUCCESS;
398            }
399        }
400        else
401        {
402            // memset the whole buffer to NULL (so everything left over is NULL terminated)
403            memset(pBuffer, 0, *pBufferCharacters * sizeof(str_utf16));
404
405            // do a binary dump (need to convert from wchar's to bytes)
406            int nBufferBytes = (*pBufferCharacters - 1) * sizeof(str_utf16);
407            nRetVal = GetFieldBinary(pFieldName, pBuffer, &nBufferBytes);
408            *pBufferCharacters = (nBufferBytes / sizeof(str_utf16)) + 1;
409        }
410    }
411
412    return nRetVal;
413}
414
415int CAPETag::GetFieldBinary(const str_utf16 * pFieldName, void * pBuffer, int * pBufferBytes)
416{
417    if (m_bAnalyzed == FALSE) { Analyze(); }
418
419    int nRetVal = ERROR_UNDEFINED;
420
421    if (*pBufferBytes > 0)
422    {
423        CAPETagField * pAPETagField = GetTagField(pFieldName);
424        if (pAPETagField == NULL)
425        {
426            memset(pBuffer, 0, *pBufferBytes);
427            *pBufferBytes = 0;
428        }
429        else
430        {
431            if (pAPETagField->GetFieldValueSize() > *pBufferBytes)
432            {
433                // we'll fail here, because partial data may be worse than no data
434                memset(pBuffer, 0, *pBufferBytes);
435                *pBufferBytes = pAPETagField->GetFieldValueSize();
436            }
437            else
438            {
439                // memcpy
440                *pBufferBytes = pAPETagField->GetFieldValueSize();
441                memcpy(pBuffer, pAPETagField->GetFieldValue(), *pBufferBytes);
442                nRetVal = ERROR_SUCCESS;
443            }
444        }
445    }
446
447    return nRetVal;
448}
449
450int CAPETag::CreateID3Tag(ID3_TAG * pID3Tag)
451{
452    // error check
453    if (pID3Tag == NULL) { return -1; }
454    if (m_bAnalyzed == FALSE) { Analyze(); }
455    if (m_nFields == 0) { return -1; }
456
457    // empty
458    ZeroMemory(pID3Tag, ID3_TAG_BYTES);
459
460    // header
461    pID3Tag->Header[0] = 'T'; pID3Tag->Header[1] = 'A'; pID3Tag->Header[2] = 'G';
462
463    // standard fields
464    GetFieldID3String(APE_TAG_FIELD_ARTIST, pID3Tag->Artist, 30);
465    GetFieldID3String(APE_TAG_FIELD_ALBUM, pID3Tag->Album, 30);
466    GetFieldID3String(APE_TAG_FIELD_TITLE, pID3Tag->Title, 30);
467    GetFieldID3String(APE_TAG_FIELD_COMMENT, pID3Tag->Comment, 28);
468    GetFieldID3String(APE_TAG_FIELD_YEAR, pID3Tag->Year, 4);
469
470    // track number
471    str_utf16 cBuffer[256] = { 0 }; int nBufferCharacters = 255;
472    GetFieldString(APE_TAG_FIELD_TRACK, cBuffer, &nBufferCharacters);
473    pID3Tag->Track = (unsigned char) _wtoi(cBuffer);
474
475    // genre
476    cBuffer[0] = 0; nBufferCharacters = 255;
477    GetFieldString(APE_TAG_FIELD_GENRE, cBuffer, &nBufferCharacters);
478
479    // convert the genre string to an index
480    pID3Tag->Genre = 255;
481    int nGenreIndex = 0;
482    BOOL bFound = FALSE;
483    while ((nGenreIndex < GENRE_COUNT) && (bFound == FALSE))
484    {
485        if (_wcsicmp(cBuffer, g_ID3Genre[nGenreIndex]) == 0)
486        {
487            pID3Tag->Genre = nGenreIndex;
488            bFound = TRUE;
489        }
490
491        nGenreIndex++;
492    }
493
494    return ERROR_SUCCESS;
495}
496
497int CAPETag::LoadField(const char * pBuffer, int nMaximumBytes, int * pBytes)
498{
499    // set bytes to 0
500    if (pBytes) *pBytes = 0;
501
502    // size and flags
503    int nLocation = 0;
504    int nFieldValueSize = *((int *) &pBuffer[nLocation]);
505    nLocation += 4;
506    int nFieldFlags = *((int *) &pBuffer[nLocation]);
507    nLocation += 4;
508
509    // safety check (so we can't get buffer overflow attacked)
510    int nMaximumRead = nMaximumBytes - 8 - nFieldValueSize;
511    BOOL bSafe = TRUE;
512    for (int z = 0; (z < nMaximumRead) && (bSafe == TRUE); z++)
513    {
514        int nCharacter = pBuffer[nLocation + z];
515        if (nCharacter == 0)
516            break;
517        if ((nCharacter < 0x20) || (nCharacter > 0x7E))
518            bSafe = FALSE;
519    }
520    if (bSafe == FALSE)
521        return -1;
522
523    // name
524    int nNameCharacters = strlen(&pBuffer[nLocation]);
525    CSmartPtr<str_utf8> spNameUTF8(new str_utf8 [nNameCharacters + 1], TRUE);
526    memcpy(spNameUTF8, &pBuffer[nLocation], (nNameCharacters + 1) * sizeof(str_utf8));
527    nLocation += nNameCharacters + 1;
528    CSmartPtr<str_utf16> spNameUTF16(GetUTF16FromUTF8(spNameUTF8.GetPtr()), TRUE);
529
530    // value
531    CSmartPtr<char> spFieldBuffer(new char [nFieldValueSize], TRUE);
532    memcpy(spFieldBuffer, &pBuffer[nLocation], nFieldValueSize);
533    nLocation += nFieldValueSize;
534
535    // update the bytes
536    if (pBytes) *pBytes = nLocation;
537
538    // set
539    return SetFieldBinary(spNameUTF16.GetPtr(), spFieldBuffer, nFieldValueSize, nFieldFlags);
540}
541
542int CAPETag::SetFieldString(const str_utf16 * pFieldName, const str_utf16 * pFieldValue)
543{
544    // remove if empty
545    if ((pFieldValue == NULL) || (wcslen(pFieldValue) <= 0))
546        return RemoveField(pFieldName);
547
548    // UTF-8 encode the value and call the UTF-8 SetField(...)
549    CSmartPtr<str_utf8> spFieldValueUTF8(GetUTF8FromUTF16((str_utf16 *) pFieldValue), TRUE);
550    return SetFieldString(pFieldName, (const char *) spFieldValueUTF8.GetPtr(), TRUE);
551}
552
553int CAPETag::SetFieldString(const str_utf16 * pFieldName, const char * pFieldValue, BOOL bAlreadyUTF8Encoded)
554{
555    // remove if empty
556    if ((pFieldValue == NULL) || (strlen(pFieldValue) <= 0))
557        return RemoveField(pFieldName);
558
559    // get the length and call the binary SetField(...)
560    if (bAlreadyUTF8Encoded == FALSE)
561    {
562        CSmartPtr<char> spUTF8((char *) GetUTF8FromANSI(pFieldValue), TRUE);
563        int nFieldBytes = strlen(spUTF8.GetPtr());
564        return SetFieldBinary(pFieldName, spUTF8.GetPtr(), nFieldBytes, TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8);
565    }
566    else
567    {
568        int nFieldBytes = strlen(pFieldValue);
569        return SetFieldBinary(pFieldName, pFieldValue, nFieldBytes, TAG_FIELD_FLAG_DATA_TYPE_TEXT_UTF8);
570    }
571}
572
573int CAPETag::SetFieldBinary(const str_utf16 * pFieldName, const void * pFieldValue, int nFieldBytes, int nFieldFlags)
574{
575    if (m_bAnalyzed == FALSE) { Analyze(); }
576    if (pFieldName == NULL) return -1;
577
578    // check to see if we're trying to remove the field (by setting it to NULL or an empty string)
579    BOOL bRemoving = (pFieldValue == NULL) || (nFieldBytes <= 0);
580
581    // get the index
582    int nFieldIndex = GetTagFieldIndex(pFieldName);
583    if (nFieldIndex != -1)
584    {
585        // existing field
586
587        // fail if we're read-only (and not ignoring the read-only flag)
588        if ((m_bIgnoreReadOnly == FALSE) && (m_aryFields[nFieldIndex]->GetIsReadOnly()))
589            return -1;
590
591        // erase the existing field
592        SAFE_DELETE(m_aryFields[nFieldIndex])
593
594        if (bRemoving)
595        {
596            return RemoveField(nFieldIndex);
597        }
598    }
599    else
600    {
601        if (bRemoving)
602            return ERROR_SUCCESS;
603
604        nFieldIndex = m_nFields;
605        m_nFields++;
606    }
607
608    // create the field and add it to the field array
609    m_aryFields[nFieldIndex] = new CAPETagField(pFieldName, pFieldValue, nFieldBytes, nFieldFlags);
610
611    return ERROR_SUCCESS;
612}
613
614int CAPETag::RemoveField(int nIndex)
615{
616    if ((nIndex >= 0) && (nIndex < m_nFields))
617    {
618        SAFE_DELETE(m_aryFields[nIndex])
619        memmove(&m_aryFields[nIndex], &m_aryFields[nIndex + 1], (256 - nIndex - 1) * sizeof(CAPETagField *));
620        m_nFields--;
621        return ERROR_SUCCESS;
622    }
623
624    return -1;
625}
626
627int CAPETag::RemoveField(const str_utf16 * pFieldName)
628{
629    return RemoveField(GetTagFieldIndex(pFieldName));
630}
631
632int CAPETag::Remove(BOOL bUpdate)
633{
634    // variables
635    unsigned int nBytesRead = 0;
636    int nRetVal = 0;
637    int nOriginalPosition = m_spIO->GetPosition();
638
639    BOOL bID3Removed = TRUE;
640    BOOL bAPETagRemoved = TRUE;
641
642    BOOL bFailedToRemove = FALSE;
643
644    while (bID3Removed || bAPETagRemoved)
645    {
646        bID3Removed = FALSE;
647        bAPETagRemoved = FALSE;
648
649        // ID3 tag
650        if (m_spIO->GetSize() > ID3_TAG_BYTES)
651        {
652            char cTagHeader[3];
653            m_spIO->Seek(-ID3_TAG_BYTES, FILE_END);
654            nRetVal = m_spIO->Read(cTagHeader, 3, &nBytesRead);
655            if ((nRetVal == 0) && (nBytesRead == 3))
656            {
657                if (strncmp(cTagHeader, "TAG", 3) == 0)
658                {
659                    m_spIO->Seek(-ID3_TAG_BYTES, FILE_END);
660                    if (m_spIO->SetEOF() != 0)
661                        bFailedToRemove = TRUE;
662                    else
663                        bID3Removed = TRUE;
664                }
665            }
666        }
667
668
669        // APE Tag
670        if (m_spIO->GetSize() > APE_TAG_FOOTER_BYTES && bFailedToRemove == FALSE)
671        {
672            APE_TAG_FOOTER APETagFooter;
673            m_spIO->Seek(-int(APE_TAG_FOOTER_BYTES), FILE_END);
674            nRetVal = m_spIO->Read(&APETagFooter, APE_TAG_FOOTER_BYTES, &nBytesRead);
675            if ((nRetVal == 0) && (nBytesRead == APE_TAG_FOOTER_BYTES))
676            {
677                if (APETagFooter.GetIsValid(TRUE))
678                {
679                    m_spIO->Seek(-APETagFooter.GetTotalTagBytes(), FILE_END);
680
681                    if (m_spIO->SetEOF() != 0)
682                        bFailedToRemove = TRUE;
683                    else
684                        bAPETagRemoved = TRUE;
685                }
686            }
687        }
688
689    }
690
691    m_spIO->Seek(nOriginalPosition, FILE_BEGIN);
692
693    if (bUpdate && bFailedToRemove == FALSE)
694    {
695        Analyze();
696    }
697
698    return bFailedToRemove ? -1 : 0;
699}
700
701int CAPETag::SetFieldID3String(const str_utf16 * pFieldName, const char * pFieldValue, int nBytes)
702{
703    // allocate a buffer and terminate it
704    CSmartPtr<str_ansi> spBuffer(new str_ansi [nBytes + 1], TRUE);
705    spBuffer[nBytes] = 0;
706
707    // make a capped copy of the string
708    memcpy(spBuffer.GetPtr(), pFieldValue, nBytes);
709
710    // remove trailing white-space
711    char * pEnd = &spBuffer[nBytes];
712    while (((*pEnd == ' ') || (*pEnd == 0)) && pEnd >= &spBuffer[0]) { *pEnd-- = 0; }
713
714    // set the field
715    SetFieldString(pFieldName, spBuffer, FALSE);
716
717    return ERROR_SUCCESS;
718}
719
720int CAPETag::GetFieldID3String(const str_utf16 * pFieldName, char * pBuffer, int nBytes)
721{
722    int nBufferCharacters = 255; str_utf16 cBuffer[256] = {0};
723    GetFieldString(pFieldName, cBuffer, &nBufferCharacters);
724
725    CSmartPtr<str_ansi> spBufferANSI(GetANSIFromUTF16(cBuffer), TRUE);
726
727    memset(pBuffer, 0, nBytes);
728    strncpy(pBuffer, spBufferANSI.GetPtr(), nBytes);
729
730    return ERROR_SUCCESS;
731}
732
733int CAPETag::SortFields()
734{
735    // sort the tag fields by size (so that the smallest fields are at the front of the tag)
736    qsort(m_aryFields, m_nFields, sizeof(CAPETagField *), CompareFields);
737
738    return ERROR_SUCCESS;
739}
740
741int CAPETag::CompareFields(const void * pA, const void * pB)
742{
743    CAPETagField * pFieldA = *((CAPETagField **) pA);
744    CAPETagField * pFieldB = *((CAPETagField **) pB);
745
746    return (pFieldA->GetFieldSize() - pFieldB->GetFieldSize());
747}
748