123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- #include "tag.h"
- #include "frameheader.h"
- #include "frames.h"
- #include "foundation/error.h"
- #include <string.h>
- #include <new>
- #include "nsid3v2.h" // for serialize flags
- /* === ID3v2 common === */
- static bool IdentifierMatch(const int8_t *id1, const int8_t *id2)
- {
- return !memcmp(id1, id2, 4);
- }
- ID3v2::Tag::Tag(const ID3v2::Header &_header) : Header(_header)
- {
- }
- void ID3v2::Tag::RemoveFrame(ID3v2::Frame *frame)
- {
- frames.erase(frame);
- delete frame;
- }
- ID3v2::Frame *ID3v2::Tag::FindFirstFrame(const int8_t *id) const
- {
- if (!id)
- return 0;
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- if (IdentifierMatch(itr->GetIdentifier(), id))
- return *itr;
- }
- return 0;
- }
- ID3v2::Frame *ID3v2::Tag::FindNextFrame(const Frame *frame) const
- {
- FrameList::const_iterator itr=frame;
- for (itr++;itr != frames.end();itr++)
- {
- if (IdentifierMatch(itr->GetIdentifier(), frame->GetIdentifier()))
- return *itr;
- }
- return 0;
- }
- void ID3v2::Tag::RemoveFrames(const int8_t *id)
- {
- // TODO: not exactly the fastest way
- Frame *frame;
- while (frame = FindFirstFrame(id))
- frames.erase(frame);
- }
- void ID3v2::Tag::AddFrame(ID3v2::Frame *frame)
- {
- frames.push_back(frame);
- }
- ID3v2::Frame *ID3v2::Tag::EnumerateFrame(const ID3v2::Frame *position) const
- {
- if (!position)
- return frames.front();
- else
- return (ID3v2::Frame *)position->next;
- }
- /* === ID3v2.2 === */
- ID3v2_2::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
- {
- }
- ID3v2_2::Tag::~Tag()
- {
- frames.deleteAll();
- }
- static inline void Advance(const void *&data, size_t &len, size_t amount)
- {
- data = (const uint8_t *)data + amount;
- len -= amount;
- }
- int ID3v2_2::Tag::Parse(const void *data, size_t len)
- {
- /* Is there an extended header? */
- if (Header::HasExtendedHeader())
- {
- size_t read=0;
- if (extendedHeader.Parse(data, len, &read) != 0)
- {
- return 1;
- }
- Advance(data, len, read);
- }
- /* Read each frame */
- while (len >= FrameHeader::SIZE)
- {
- /* if next byte is zero, we've hit the padding area, GTFO */
- if (*(uint8_t *)data == 0x0)
- break;
- /* Read frame header first */
- FrameHeader frame_header(*this, data);
- Advance(data, len, FrameHeader::SIZE);
- if (!frame_header.IsValid())
- return 1;
- /* read frame data */
- Frame *new_frame = new (std::nothrow) Frame(frame_header);
- if (!new_frame)
- return NErr_OutOfMemory;
- size_t read=0;
- if (new_frame->Parse(data, len, &read) == 0)
- {
- Advance(data, len, read);
- frames.push_back(new_frame);
- }
- else
- {
- delete new_frame;
- return 1;
- }
- }
- return 0;
- }
- int ID3v2_2::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
- {
- size_t current_length=0;
- Header new_header(*this);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- current_length += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: deal with extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_2::Frame *frame = (const ID3v2_2::Frame *)*itr;
- int ret = frame->SerializedSize(&written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- }
- switch(flags & SerializedSize_PaddingMask)
- {
- case SerializedSize_Padding:
- current_length += padding_size;
- break;
- case SerializedSize_AbsoluteSize:
- if (current_length < padding_size)
- current_length = padding_size;
- break;
- case SerializedSize_BlockSize:
- {
- uint32_t additional = current_length % padding_size;
- current_length += additional;
- }
- break;
- }
- *length = current_length;
- return NErr_Success;
- }
- int ID3v2_2::Tag::Serialize(void *data, uint32_t len, int flags) const
- {
- uint8_t *data_itr = (uint8_t *)data;
- uint32_t current_length=0;
- // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
- Header new_header(this, len-10);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- new_header.Serialize(data);
- current_length += Header::SIZE;
- data_itr += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: write extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_2::Frame *frame = (const ID3v2_2::Frame *)*itr;
- int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- data_itr += written;
- }
- // write padding
- memset(data_itr, 0, len-current_length);
- return NErr_Success;
- }
- static bool IdentifierMatch3(const int8_t *id1, const int8_t *id2)
- {
- return !memcmp(id1, id2, 3);
- }
- ID3v2_2::Frame *ID3v2_2::Tag::FindFirstFrame(int frame_id) const
- {
- if (!ValidFrameID(frame_id))
- return 0;
- return (ID3v2_2::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v2);
- };
- void ID3v2_2::Tag::RemoveFrames(int frame_id)
- {
- // TODO: not exactly the fastest way
- Frame *frame;
- while (frame = FindFirstFrame(frame_id))
- frames.erase(frame);
- }
- ID3v2_2::Frame *ID3v2_2::Tag::NewFrame(int frame_id, int flags) const
- {
- if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v2)
- return 0;
- return new (std::nothrow) ID3v2_2::Frame(*this, frame_ids[frame_id].v2, flags);
- }
- /* === ID3v2.3 === */
- ID3v2_3::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
- {
- }
- ID3v2_3::Tag::~Tag()
- {
- frames.deleteAll();
- }
- int ID3v2_3::Tag::Parse(const void *data, size_t len)
- {
- /* Is there an extended header? */
- if (Header::HasExtendedHeader())
- {
- size_t read=0;
- if (extendedHeader.Parse(data, len, &read) != 0)
- {
- return 1;
- }
- Advance(data, len, read);
- }
- /* Read each frame */
- while (len >= FrameHeader::SIZE)
- {
- /* if next byte is zero, we've hit the padding area, GTFO */
- if (*(uint8_t *)data == 0x0)
- break;
- /* Read frame header first */
- FrameHeader frame_header(*this, data);
- Advance(data, len, FrameHeader::SIZE);
- if (!frame_header.IsValid())
- return 1;
- /* read frame data */
- Frame *new_frame = new (std::nothrow) Frame(frame_header);
- if (!new_frame)
- return NErr_OutOfMemory;
- size_t read=0;
- if (new_frame->Parse(data, len, &read) == 0)
- {
- Advance(data, len, read);
- frames.push_back(new_frame);
- }
- else
- {
- delete new_frame;
- return 1;
- }
- }
- return 0;
- }
- int ID3v2_3::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
- {
- size_t current_length=0;
- Header new_header(*this);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- current_length += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: deal with extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_3::Frame *frame = (const ID3v2_3::Frame *)*itr;
- int ret = frame->SerializedSize(&written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- }
- switch(flags & SerializedSize_PaddingMask)
- {
- case SerializedSize_Padding:
- current_length += padding_size;
- break;
- case SerializedSize_AbsoluteSize:
- if (current_length < padding_size)
- current_length = padding_size;
- break;
- case SerializedSize_BlockSize:
- {
- uint32_t additional = padding_size - (current_length % padding_size);
- if (additional == padding_size)
- additional=0;
- current_length += additional;
- }
- break;
- }
- *length = current_length;
- return NErr_Success;
- }
- int ID3v2_3::Tag::Serialize(void *data, uint32_t len, int flags) const
- {
- uint8_t *data_itr = (uint8_t *)data;
- uint32_t current_length=0;
- // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
- Header new_header(this, len-10);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- new_header.Serialize(data);
- current_length += Header::SIZE;
- data_itr += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: write extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_3::Frame *frame = (const ID3v2_3::Frame *)*itr;
- int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- data_itr += written;
- }
- // write padding
- memset(data_itr, 0, len-current_length);
- return NErr_Success;
- }
- ID3v2_3::Frame *ID3v2_3::Tag::FindFirstFrame(int frame_id) const
- {
- if (!ValidFrameID(frame_id))
- return 0;
- return (ID3v2_3::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v3);
- };
- void ID3v2_3::Tag::RemoveFrames(int frame_id)
- {
- // TODO: not exactly the fastest way
- Frame *frame;
- while (frame = FindFirstFrame(frame_id))
- frames.erase(frame);
- }
- ID3v2_3::Frame *ID3v2_3::Tag::NewFrame(int frame_id, int flags) const
- {
- if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v3)
- return 0;
- return new (std::nothrow) ID3v2_3::Frame(*this, frame_ids[frame_id].v3, flags);
- }
- /* === ID3v2.4 === */
- ID3v2_4::Tag::Tag(const ID3v2::Header &_header) : ID3v2::Tag(_header), extendedHeader(_header)
- {
- }
- ID3v2_4::Tag::~Tag()
- {
- frames.deleteAll();
- }
- int ID3v2_4::Tag::Parse(const void *data, size_t len)
- {
- /* Is there an extended header? */
- if (Header::HasExtendedHeader())
- {
- size_t read=0;
- if (extendedHeader.Parse(data, len, &read) != 0)
- {
- return 1;
- }
- Advance(data, len, read);
- }
- /* Read each frame */
- while (len >= FrameHeader::SIZE)
- {
- /* if next byte is zero, we've hit the padding area, GTFO */
- if (*(uint8_t *)data == 0x0)
- break;
- /* Read frame header first */
- FrameHeader frame_header(*this, data);
- Advance(data, len, FrameHeader::SIZE);
- if (!frame_header.IsValid())
- return 1;
- /* read frame data */
- Frame *new_frame = new (std::nothrow) Frame(frame_header);
- if (!new_frame)
- return NErr_OutOfMemory;
- size_t read=0;
- if (new_frame->Parse(data, len, &read) == 0)
- {
- Advance(data, len, read);
- frames.push_back(new_frame);
- }
- else
- {
- delete new_frame;
- return 1;
- }
- }
- return 0;
- }
- int ID3v2_4::Tag::SerializedSize(uint32_t *length, uint32_t padding_size, int flags) const
- {
- size_t current_length=0;
-
- Header new_header(*this);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- current_length += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: deal with extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_4::Frame *frame = (const ID3v2_4::Frame *)*itr;
- int ret = frame->SerializedSize(&written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- }
- switch(flags & SerializedSize_PaddingMask)
- {
- case 0:
- /* we can only write a footer if there is no padding */
- if (new_header.FooterValid() || new_header.HasFooter())
- {
- current_length += Header::SIZE;
- }
- break;
- case SerializedSize_Padding:
- current_length += padding_size;
- break;
- case SerializedSize_AbsoluteSize:
- if (current_length < padding_size)
- current_length = padding_size;
- break;
- case SerializedSize_BlockSize:
- {
- uint32_t additional = current_length % padding_size;
- current_length += additional;
- }
- break;
- }
- *length = current_length;
- return NErr_Success;
- }
- int ID3v2_4::Tag::Serialize(void *data, uint32_t len, int flags) const
- {
- uint8_t *data_itr = (uint8_t *)data;
- uint32_t current_length=0;
- // write header. note the passed-in length is guaranteed to be correct, as it was generated by SerializedSize
- bool write_footer=false;
- if ((flags & SerializedSize_PaddingMask) == 0 && (FooterValid() || HasFooter()))
- write_footer=true;
- Header new_header(this, write_footer?(len-20):(len-10));
- new_header.SetFooter(write_footer);
- /* TODO: going to clear this, for now */
- new_header.ClearExtendedHeader();
- switch(flags & Serialize_UnsynchronizeMask)
- {
- case Serialize_Unsynchronize:
- // TODO:
- break;
- case Serialize_NoUnsynchronize:
- new_header.ClearUnsynchronized();
- break;
- }
- new_header.SerializeAsHeader(data);
- current_length += Header::SIZE;
- data_itr += Header::SIZE;
- if (new_header.HasExtendedHeader())
- {
- // TODO: write extended header
- }
- for (FrameList::const_iterator itr=frames.begin();itr != frames.end();itr++)
- {
- uint32_t written;
- const ID3v2_4::Frame *frame = (const ID3v2_4::Frame *)*itr;
- int ret = frame->Serialize((void *)data_itr, &written, new_header, flags);
- if (ret != NErr_Success)
- return ret;
- current_length += written;
- data_itr += written;
- }
- if (write_footer)
- {
- new_header.SerializeAsFooter(data_itr);
- current_length += Header::SIZE;
- data_itr += Header::SIZE;
- }
- // write padding
- memset(data_itr, 0, len-current_length);
- return NErr_Success;
- }
- ID3v2_4::Frame *ID3v2_4::Tag::FindFirstFrame(int frame_id) const
- {
- if (!ValidFrameID(frame_id))
- return 0;
- return (ID3v2_4::Frame *)ID3v2::Tag::FindFirstFrame(frame_ids[frame_id].v4);
- };
- void ID3v2_4::Tag::RemoveFrames(int frame_id)
- {
- // TODO: not exactly the fastest way
- Frame *frame;
- while (frame = FindFirstFrame(frame_id))
- frames.erase(frame);
- }
- ID3v2_4::Frame *ID3v2_4::Tag::NewFrame(int frame_id, int flags) const
- {
- if (!ValidFrameID(frame_id) || !frame_ids[frame_id].v4)
- return 0;
- return new (std::nothrow) ID3v2_4::Frame(*this, frame_ids[frame_id].v4, flags);
- }
|