xref: /haiku/src/add-ons/media/plugins/ape_reader/MAClib/APETag.cpp (revision 053cc0d4fe060114342762e1e8dbbf86a9cad259)
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 /*****************************************************************************************
11 CAPETagField
12 *****************************************************************************************/
13 
CAPETagField(const str_utf16 * pFieldName,const void * pFieldValue,int nFieldBytes,int nFlags)14 CAPETagField::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 
~CAPETagField()31 CAPETagField::~CAPETagField()
32 {
33 }
34 
GetFieldSize()35 int CAPETagField::GetFieldSize()
36 {
37     CSmartPtr<char> spFieldNameANSI(GetANSIFromUTF16(m_spFieldNameUTF16), TRUE);
38     return (strlen(spFieldNameANSI) + 1) + m_nFieldValueBytes + 4 + 4;
39 }
40 
GetFieldName()41 const str_utf16 * CAPETagField::GetFieldName()
42 {
43     return m_spFieldNameUTF16;
44 }
45 
GetFieldValue()46 const char * CAPETagField::GetFieldValue()
47 {
48     return m_spFieldValue;
49 }
50 
GetFieldValueSize()51 int CAPETagField::GetFieldValueSize()
52 {
53     return m_nFieldValueBytes;
54 }
55 
GetFieldFlags()56 int CAPETagField::GetFieldFlags()
57 {
58     return m_nFieldFlags;
59 }
60 
SaveField(char * pBuffer)61 int 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 /*****************************************************************************************
79 CAPETag
80 *****************************************************************************************/
81 
CAPETag(const str_utf16 * pFilename,BOOL bAnalyze)82 CAPETag::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 
CAPETag(CIO * pIO,BOOL bAnalyze)98 CAPETag::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 
~CAPETag()111 CAPETag::~CAPETag()
112 {
113     ClearFields();
114 }
115 
GetTagBytes()116 int CAPETag::GetTagBytes()
117 {
118     if (m_bAnalyzed == FALSE) { Analyze(); }
119 
120     return m_nTagBytes;
121 }
122 
GetTagField(int nIndex)123 CAPETagField * 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 
Save(BOOL bUseOldID3)135 int 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 
WriteBufferToEndOfIO(void * pBuffer,int nBytes)186 int 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 
Analyze()200 int 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 
ClearFields()296 int 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 
GetTagFieldIndex(const str_utf16 * pFieldName)308 int 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 
GetTagField(const str_utf16 * pFieldName)323 CAPETagField * 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
330 int 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 
GetFieldString(const str_utf16 * pFieldName,str_utf16 * pBuffer,int * pBufferCharacters)360 int 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 
GetFieldBinary(const str_utf16 * pFieldName,void * pBuffer,int * pBufferBytes)415 int 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 
CreateID3Tag(ID3_TAG * pID3Tag)450 int 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 
LoadField(const char * pBuffer,int nMaximumBytes,int * pBytes)497 int 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 
SetFieldString(const str_utf16 * pFieldName,const str_utf16 * pFieldValue)542 int 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 
SetFieldString(const str_utf16 * pFieldName,const char * pFieldValue,BOOL bAlreadyUTF8Encoded)553 int 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 
SetFieldBinary(const str_utf16 * pFieldName,const void * pFieldValue,int nFieldBytes,int nFieldFlags)573 int 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 
RemoveField(int nIndex)614 int 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 
RemoveField(const str_utf16 * pFieldName)627 int CAPETag::RemoveField(const str_utf16 * pFieldName)
628 {
629     return RemoveField(GetTagFieldIndex(pFieldName));
630 }
631 
Remove(BOOL bUpdate)632 int 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 
SetFieldID3String(const str_utf16 * pFieldName,const char * pFieldValue,int nBytes)701 int 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 
GetFieldID3String(const str_utf16 * pFieldName,char * pBuffer,int nBytes)720 int 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 
SortFields()733 int 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 
CompareFields(const void * pA,const void * pB)741 int 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