void id3::v2::render(ID3_Writer& writer, const ID3_TagImpl& tag) { // There has to be at least one frame for there to be a tag... if (tag.NumFrames() == 0) { ID3D_WARNING( "id3::v2::render(): no frames to render" ); return; } ID3D_NOTICE( "id3::v2::render(): rendering" ); ID3_TagHeader hdr; hdr.SetSpec(tag.GetSpec()); hdr.SetExtended(tag.GetExtended()); hdr.SetExperimental(tag.GetExperimental()); // set up the encryption and grouping IDs // ... String frms; io::StringWriter frmWriter(frms); if (!tag.GetUnsync()) { ID3D_NOTICE( "id3::v2::render(): rendering frames" ); renderFrames(frmWriter, tag); hdr.SetUnsync(false); } else { ID3D_NOTICE( "id3::v2::render(): rendering unsynced frames" ); io::UnsyncedWriter uw(frmWriter); renderFrames(uw, tag); uw.flush(); ID3D_NOTICE( "id3::v2::render(): numsyncs = " << uw.getNumSyncs() ); hdr.SetUnsync(uw.getNumSyncs() > 0); } size_t frmSize = frms.size(); if (frmSize == 0) { ID3D_WARNING( "id3::v2::render(): rendered frame size is 0 bytes" ); return; } // zero the remainder of the buffer so that our padding bytes are zero luint nPadding = tag.PaddingSize(frmSize); ID3D_NOTICE( "id3::v2::render(): padding size = " << nPadding ); hdr.SetDataSize(frmSize + nPadding); hdr.Render(writer); writer.writeChars(frms.data(), frms.size()); for (size_t i = 0; i < nPadding; ++i) { if (writer.writeChar('\0') == ID3_Writer::END_OF_WRITER) { break; } } }
luint ID3_Tag::Size( void ) { luint bytesUsed = 0; ID3_Elem *cur = frameList; ID3_TagHeader header; header.SetVersion ( m_nVersion, m_nRevision ); bytesUsed += header.Size(); while( cur ) { // PL if( ( cur->frame->GetID() <= 0 ) || (cur->frame->GetID() > ID3FID_CRYPTOREG )) { // TRACE( "Invalide FrameID\n" ); cur = cur->next; continue; } // End PL if( cur->frame ) { cur->frame->SetVersion ( m_nVersion, m_nRevision ); bytesUsed += cur->frame->Size(); } cur = cur->next; } // add 30% for sync if ( m_bSyncOn ) bytesUsed += bytesUsed / 3; bytesUsed += PaddingSize ( bytesUsed ); return bytesUsed; }
size_t ID3_TagImpl::Size() const { if (this->NumFrames() == 0) { return 0; } ID3_TagHeader hdr; hdr.SetSpec(this->GetSpec()); size_t bytesUsed = hdr.Size(); size_t frameBytes = 0; for (const_iterator cur = _frames.begin(); cur != _frames.end(); ++cur) { if (*cur) { (*cur)->SetSpec(this->GetSpec()); frameBytes += (*cur)->Size(); } } if (!frameBytes) { return 0; } bytesUsed += frameBytes; // add 30% for sync if (this->GetUnsync()) { bytesUsed += bytesUsed / 3; } bytesUsed += this->PaddingSize(bytesUsed); return bytesUsed; }
luint ID3_Tag::Render( uchar *buffer ) { luint bytesUsed = 0; if ( buffer ) { ID3_Elem *cur = frameList; ID3_TagHeader header; SetVersion ( ID3_TAGVERSION, ID3_TAGREVISION ); header.SetVersion ( m_nVersion, m_nRevision ); bytesUsed += header.Size(); // set up the encryption and grouping IDs // ... while( cur ) { // PL // Check that frame has a valid FrameID if( ( cur->frame->GetID() <= 0 ) || (cur->frame->GetID() > ID3FID_CRYPTOREG )) { // TRACE( "Invalide FrameID\n" ); cur = cur->next; continue; } // End PL if ( cur->frame ) { cur->frame->compression = m_bCompression; cur->frame->SetVersion ( m_nVersion, m_nRevision ); bytesUsed += cur->frame->Render ( &buffer[ bytesUsed ] ); } cur = cur->next; } if ( m_bSyncOn ) { uchar *tempz; luint newTagSize; newTagSize = GetUnSyncSize ( &buffer[ header.Size() ], bytesUsed - header.Size() ); if ( newTagSize > 0 && ( newTagSize + header.Size() ) > bytesUsed ) { #ifdef _DEBUG // ASSERT( newTagSize < MAX_ALLOC ); // PL #endif if ( tempz = new uchar[ newTagSize ] ) { UnSync ( tempz, newTagSize, &buffer[ header.Size() ], bytesUsed - header.Size() ); header.SetFlags ( ID3HF_UNSYNC ); memcpy ( &buffer[ header.Size() ], tempz, newTagSize ); bytesUsed = newTagSize + header.Size(); delete[] tempz; } else ID3_THROW ( ID3E_NoMemory ); } } // zero the remainder of the buffer so that our // padding bytes are zero for ( luint i = 0; i < PaddingSize ( bytesUsed ); i++ ) buffer[ bytesUsed + i ] = 0; bytesUsed += PaddingSize ( bytesUsed ); header.SetDataSize ( bytesUsed - header.Size() ); header.Render ( buffer ); } else ID3_THROW ( ID3E_NoBuffer ); // set the flag which says that the tag hasn't changed m_bHasChanged = false; return bytesUsed; }
bool id3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader) { ID3_Reader::pos_type beg = reader.getCur(); io::ExitTrigger et(reader); ID3_TagHeader hdr; io::WindowedReader wr(reader, ID3_TagHeader::SIZE); if (!hdr.Parse(wr) || wr.getCur() == beg) { ID3D_NOTICE( "id3::v2::parse(): parsing header failes" ); return false; } if (hdr.GetExtended()) { hdr.ParseExtended(reader); } tag.SetSpec(hdr.GetSpec()); size_t dataSize = hdr.GetDataSize(); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): dataSize = " << dataSize); wr.setWindow(wr.getCur(), dataSize); et.setExitPos(wr.getEnd()); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window beg = " << wr.getBeg() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window cur = " << wr.getCur() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): data window end = " << wr.getEnd() ); tag.SetExtended(hdr.GetExtended()); if (!hdr.GetUnsync()) { tag.SetUnsync(false); parseFrames(tag, wr); } else { // The buffer has been unsynced. It will have to be resynced to be // readable. This has to be done a character at a time. // // The original reader may be reading in characters from a file. Doing // this a character at a time is quite slow. To improve performance, read // in the entire buffer into a string, then create an UnsyncedReader from // the string. // // It might be better to implement a BufferedReader so that the details // of this can be abstracted away behind a class tag.SetUnsync(true); BString raw = io::readAllBinary(wr); io::BStringReader bsr(raw); io::UnsyncedReader ur(bsr); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync beg = " << ur.getBeg() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync cur = " << ur.getCur() ); ID3D_NOTICE( "ID3_TagImpl::Parse(ID3_Reader&): unsync end = " << ur.getEnd() ); // Now read the UnsyncedReader into another string, and parse the frames // from the string. This is done so that 1. the unsynced reader is // unsynced exactly once, removing the possibility of multiple unsyncings // of the same string, and 2) so that calls to readChars aren't done a // character at a time for every call BString synced = io::readAllBinary(ur); io::BStringReader sr(synced); parseFrames(tag, sr); } return true; }