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