void ID3_TagHeader::Render(ID3_Writer& writer) const { writer.writeChars((uchar *) ID, strlen(ID)); writer.writeChar(ID3_V2SpecToVer(ID3V2_LATEST)); writer.writeChar(ID3_V2SpecToRev(ID3V2_LATEST)); // set the flags byte in the header writer.writeChar(static_cast<uchar>(_flags.get() & MASK8)); io::writeUInt28(writer, this->GetDataSize()); //now includes the extended header // now we render the extended header if (_flags.test(HEADER_FLAG_EXTENDED)) { if (this->GetSpec() == ID3V2_4_0) { io::writeUInt28(writer, 6); //write 4 bytes of v2.4.0 ext header containing size '6' io::writeBENumber(writer, 1, 1); //write that it has only one flag byte (value '1') io::writeBENumber(writer, 0, 1); //write flag byte with value '0' } else if (this->GetSpec() == ID3V2_3_0) { io::writeBENumber(writer, 6, sizeof(uint32)); for (size_t i = 0; i < 6; ++i) { if (writer.writeChar('\0') == ID3_Writer::END_OF_WRITER) { break; } } } // else //not implemented } }
size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) { ID3_Writer::pos_type beg = writer.getCur(); size_t size = (data.size() / 2) * 2; if (size == 0) { return 0; } if (bom) { // Write the BOM: 0xFEFF unicode_t BOM = 0xFEFF; writer.writeChars((const unsigned char*) &BOM, 2); } // AF outside if( bom ) for (size_t i = 0; i < size; i += 2) { // AF TODO FIXME PATCH unicode_t ch = (data[i] << 8) | data[i+1]; // AF: SWAP BYTES AND PERFORM unsigned char cast unicode_t ch = (static_cast<unsigned char>(data[i+1]) << 8) | static_cast<unsigned char>(data[i]); writer.writeChars((const unsigned char*) &ch, 2); } return writer.getCur() - beg; }
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; } } }
size_t io::writeTrailingSpaces(ID3_Writer& writer, String buf, size_t len) { ID3_Writer::pos_type beg = writer.getCur(); ID3_Writer::size_type strLen = buf.size(); ID3_Writer::size_type size = min((unsigned int)len, (unsigned int)strLen); writer.writeChars(buf.data(), size); for (; size < len; ++size) { writer.writeChar('\0'); } return writer.getCur() - beg; }
size_t ID3_Tag::Render(ID3_Writer& writer, ID3_TagType tt) const { ID3_Writer::pos_type beg = writer.getCur(); if (ID3TT_ID3V2 & tt) { id3::v2::render(writer, *this); } else if (ID3TT_ID3V1 & tt) { id3::v1::render(writer, *this); } return writer.getCur() - beg; }
size_t io::writeBENumber(ID3_Writer& writer, uint32 val, size_t len) { ID3_Writer::char_type bytes[sizeof(uint32)]; ID3_Writer::size_type size = min<ID3_Reader::size_type>(len, sizeof(uint32)); renderNumber(bytes, val, size); return writer.writeChars(bytes, size); }
size_t io::writeUnicodeString(ID3_Writer& writer, String data, bool bom) { size_t size = writeUnicodeText(writer, data, bom); unicode_t null = NULL_UNICODE; writer.writeChars((const unsigned char*) &null, 2); return size + 2; }
void ID3_FrameHeader::Render(ID3_Writer& writer) const { size_t size = 0; if (NULL == _frame_def) { // TODO: log this ID3D_WARNING( "ID3_FrameHeader::Render(): _frame_def is NULL!" ); return; //ID3_THROW(ID3E_InvalidFrameID); } char *textID; if (_info->frame_bytes_id == strlen(_frame_def->sShortTextID)) { textID = _frame_def->sShortTextID; } else { textID = _frame_def->sLongTextID; } ID3D_NOTICE( "ID3_FrameHeader::Render(): writing " << textID << ", " << (int) _info->frame_bytes_size << " bytes"); writer.writeChars((uchar *) textID, _info->frame_bytes_id); io::writeBENumber(writer, _data_size, _info->frame_bytes_size); io::writeBENumber(writer, _flags.get(), _info->frame_bytes_flags); }
size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) { ID3_Writer::pos_type beg = writer.getCur(); size_t size = (data.size() / 2) * 2; if (size == 0) { return 0; } if (bom) { // Write the BOM: 0xFEFF unicode_t BOM = 0xFEFF; writer.writeChars((const unsigned char*) &BOM, 2); } writer.writeChars(&data[0], size); return writer.getCur() - beg; }
void ID3_TagHeader::Render(ID3_Writer& writer) const { writer.writeChars((uchar *) ID, strlen(ID)); writer.writeChar(ID3_V2SpecToVer(ID3V2_LATEST)); writer.writeChar(ID3_V2SpecToRev(ID3V2_LATEST)); // set the flags byte in the header writer.writeChar(static_cast<uchar>(_flags.get() & MASK8)); io::writeUInt28(writer, this->GetDataSize()); // now we render the extended header if (_flags.test(EXTENDED)) { io::writeBENumber(writer, _info->extended_bytes, sizeof(uint32)); } }
size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) { ID3_Writer::pos_type beg = writer.getCur(); size_t size = (data.size() / 2) * 2; if (size == 0) { return 0; } int is_bom = isBOM(data[0],data[1]); if (!is_bom && bom) { // Write the BOM: 0xFEFF const unsigned char BOMch1 = 0xFE; const unsigned char BOMch2 = 0xFF; writer.writeChars(&BOMch1, 1); writer.writeChars(&BOMch2, 1); } for (size_t i = 0; i < size; i += 2) { if (!i && !bom && is_bom) { // Skip unneeded leading BOM continue; } if (is_bom >= 0) { writer.writeChars(data.data()+i, 1); writer.writeChars(data.data()+i+1, 1); } else { writer.writeChars(data.data()+i+1, 1); writer.writeChars(data.data()+i, 1); } } return writer.getCur() - beg; }
size_t io::writeUnicodeText(ID3_Writer& writer, String data, bool bom) { ID3_Writer::pos_type beg = writer.getCur(); size_t size = (data.size() / 2) * 2; if (size == 0) { return 0; } if (bom) { // Write the BOM: 0xFEFF unicode_t BOM = 0xFEFF; writer.writeChars((const unsigned char*) &BOM, 2); for (size_t i = 0; i < size; i += 2) { unicode_t ch = (data[i] << 8) | data[i+1]; writer.writeChars((const unsigned char*) &ch, 2); } } return writer.getCur() - beg; }
void id3::v1::render(ID3_Writer& writer, const ID3_TagImpl& tag) { writer.writeChars("TAG", 3); io::writeTrailingSpaces(writer, id3::v2::getTitle(tag), ID3_V1_LEN_TITLE); io::writeTrailingSpaces(writer, id3::v2::getArtist(tag), ID3_V1_LEN_ARTIST); io::writeTrailingSpaces(writer, id3::v2::getAlbum(tag), ID3_V1_LEN_ALBUM); io::writeTrailingSpaces(writer, id3::v2::getYear(tag), ID3_V1_LEN_YEAR); size_t track = id3::v2::getTrackNum(tag); String comment = id3::v2::getV1Comment(tag); if (track > 0) { io::writeTrailingSpaces(writer, comment, ID3_V1_LEN_COMMENT - 2); writer.writeChar('\0'); writer.writeChar((char) track); } else { io::writeTrailingSpaces(writer, comment, ID3_V1_LEN_COMMENT); } writer.writeChar((char) id3::v2::getGenreNum(tag)); }
size_t io::writeUInt28(ID3_Writer& writer, uint32 val) { uchar data[sizeof(uint32)]; const unsigned short BITSUSED = 7; const uint32 MAXVAL = MASK(BITSUSED * sizeof(uint32)); val = min(val, MAXVAL); // This loop renders the value to the character buffer in reverse order, as // it is easy to extract the last 7 bits of an integer. This is why the // loop shifts the value of the integer by 7 bits for each iteration. for (size_t i = 0; i < sizeof(uint32); ++i) { // Extract the last BITSUSED bits from val and put it in its appropriate // place in the data buffer data[sizeof(uint32) - i - 1] = static_cast<uchar>(val & MASK(BITSUSED)); // The last BITSUSED bits were extracted from the val. So shift it to the // right by that many bits for the next iteration val >>= BITSUSED; } // Should always render 4 bytes return writer.writeChars(data, sizeof(uint32)); }
void ID3_FrameImpl::Render(ID3_Writer& writer) const { // Return immediately if we have no fields, which (usually) means we're // trying to render a frame which has been Cleared or hasn't been initialized if (!this->NumFields()) { return; } ID3_FrameHeader hdr; const size_t hdr_size = hdr.Size(); // 1. Write out the field data to the buffer, with the assumption that // we won't be decompressing, since this is the usual behavior String flds; io::StringWriter fldWriter(flds); size_t origSize = 0; if (!this->GetCompression()) { renderFields(fldWriter, *this); origSize = flds.size(); ID3D_NOTICE ( "ID3_FrameImpl::Render(): uncompressed fields" ); } else { io::CompressedWriter cr(fldWriter); renderFields(cr, *this); cr.flush(); origSize = cr.getOrigSize(); ID3D_NOTICE ( "ID3_FrameImpl::Render(): compressed fields, orig size = " << origSize ); } size_t fldSize = flds.size(); ID3D_NOTICE ( "ID3_FrameImpl::Render(): field size = " << fldSize ); // No need to not write empty frames, why would we not? They can be used to fill up padding space // which is even recommended in the id3 spec. // if (fldSize == 0) // { // ID3D_WARNING ( "ID3_FrameImpl::Render(): no field data" ); // return; // } // determine which flags need to be set uchar eID = this->GetEncryptionID(), gID = this->GetGroupingID(); ID3_FrameID fid = this->GetID(); if (fid == ID3FID_NOFRAME) { const char *tid = this->GetTextID(); hdr.SetUnknownFrame(tid); } else { hdr.SetFrameID(fid); } hdr.SetEncryption(eID > 0); hdr.SetGrouping(gID > 0); hdr.SetCompression(origSize > fldSize); hdr.SetDataSize(fldSize + ((hdr.GetCompression() ? 4 : 0) + (hdr.GetEncryption() ? 1 : 0) + (hdr.GetGrouping() ? 1 : 0))); // write out the header hdr.Render(writer); if (fldSize != 0) { // No-man's land! Not part of the header, not part of the data if (hdr.GetCompression()) { io::writeBENumber(writer, origSize, sizeof(uint32)); ID3D_NOTICE( "ID3_FrameImpl::Render(): frame is compressed, wrote origSize = " << origSize ); } if (hdr.GetEncryption()) { writer.writeChar(eID); ID3D_NOTICE( "ID3_FrameImpl::Render(): frame is compressed, encryption id = " << eID ); } if (hdr.GetGrouping()) { writer.writeChar(gID); ID3D_NOTICE( "ID3_FrameImpl::Render(): frame is compressed, grouping id = " << gID ); } // Write the field data writer.writeChars(flds.data(), fldSize); } _changed = false; }
void ID3_FieldImpl::RenderBinary(ID3_Writer& writer) const { writer.writeChars(this->GetRawBinary(), this->Size()); }
size_t io::writeText(ID3_Writer& writer, String data) { ID3_Writer::pos_type beg = writer.getCur(); writer.writeChars(data.data(), data.size()); return writer.getCur() - beg; }
size_t io::writeString(ID3_Writer& writer, String data) { size_t size = writeText(writer, data); writer.writeChar('\0'); return size + 1; }