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