diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nsid3v2/frame.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/replicant/nsid3v2/frame.cpp')
-rw-r--r-- | Src/replicant/nsid3v2/frame.cpp | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/Src/replicant/nsid3v2/frame.cpp b/Src/replicant/nsid3v2/frame.cpp new file mode 100644 index 00000000..12cdd1c6 --- /dev/null +++ b/Src/replicant/nsid3v2/frame.cpp @@ -0,0 +1,786 @@ +#include "frame.h" +#include "util.h" +#ifdef _WIN32 +#include "zlib/zlib.h" +#else +#include "zlib/zlib.h" +#endif +#include "frames.h" +#include <string.h> +#include <stdlib.h> +#include "nu/ByteReader.h" +#include "nu/ByteWriter.h" +#include "foundation/error.h" +#include "nsid3v2.h" + +/* === ID3v2 common === */ +ID3v2::Frame::Frame() +{ + data = 0; + data_size = 0; +} + +ID3v2::Frame::~Frame() +{ + free(data); +} + +int ID3v2::Frame::GetData(const void **_data, size_t *data_len) const +{ + if (data) + { + *_data = data; + *data_len = data_size; + return NErr_Success; + } + else + return NErr_NullPointer; +} + +size_t ID3v2::Frame::GetDataSize() const +{ + return data_size; +} + +int ID3v2::Frame::NewData(size_t new_len, void **_data, size_t *_data_len) +{ + // we DO NOT update the header, as its meant to hold the original data + void *new_data = realloc(data, new_len); + if (new_data) + { + data = new_data; + data_size = new_len; + *_data = data; + *_data_len = data_size; + return NErr_Success; + } + else + return NErr_OutOfMemory; +} + +bool ID3v2::Frame::Encrypted() const +{ + return false; +} + +bool ID3v2::Frame::Compressed() const +{ + return false; +} + +bool ID3v2::Frame::Grouped() const +{ + return false; +} + +bool ID3v2::Frame::ReadOnly() const +{ + return false; +} + +bool ID3v2::Frame::FrameUnsynchronised() const +{ + return false; +} + +bool ID3v2::Frame::DataLengthIndicated() const +{ + return false; +} + +bool ID3v2::Frame::TagAlterPreservation() const +{ + return false; +} + +bool ID3v2::Frame::FileAlterPreservation() const +{ + return false; +} + +static inline void Advance(const void *&data, size_t &len, size_t amount) +{ + data = (const uint8_t *)data + amount; + len -= amount; +} + +static inline void AdvanceBoth(const void *&data, size_t &len, size_t &len2, size_t amount) +{ + data = (const uint8_t *)data + amount; + len -= amount; + len2 -= amount; +} + + +/* === ID3v2.2 === */ +ID3v2_2::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags) +{ +} + +ID3v2_2::Frame::Frame(const FrameHeader &_header) : header(_header) +{ +} + +int ID3v2_2::Frame::Parse(const void *_data, size_t len, size_t *read) +{ + *read = 0; + data_size = header.FrameSize(); // size of frame AFTER re-synchronization + + /* check to make sure that we have enough input data to read the data */ + if (header.Unsynchronised()) + { + /* this is tricky, because the stored size reflects after re-synchronization, + but the incoming data is unsynchronized */ + if (ID3v2::Util::UnsynchronisedInputSize(_data, data_size) > len) + return 1; + } + else if (data_size > len) + return 1; + + /* allocate memory (real data_size) */ + data = malloc(data_size); + if (!data) + return 1; + + /* === Read the data === */ + if (header.Unsynchronised()) + { + *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size); + } + else // normal data + { + memcpy(data, _data, data_size); + *read += data_size; + } + + return NErr_Success; +} + + +int ID3v2_2::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const +{ + ID3v2_2::FrameHeader new_header(header, tag_header); + // TODO: for now, we're not going to deal with compression + new_header.SetSize(data_size); + + uint32_t current_length=0; + new_header.SerializedSize(¤t_length); + + if (new_header.Unsynchronised()) + { + current_length += ID3v2::Util::SynchronisedSize(data, data_size); + } + else + { + current_length += new_header.FrameSize(); + } + + *length = current_length; + return NErr_Success; +} + +int ID3v2_2::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const +{ + size_t current_length = FrameHeader::SIZE; + uint8_t *data_ptr = (uint8_t *)output; + ID3v2_2::FrameHeader new_header(header, tag_header); + new_header.SetSize(data_size); + + // write frame header + new_header.Serialize(data_ptr); + data_ptr += FrameHeader::SIZE; + if (new_header.Unsynchronised()) + { + current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size); + } + else + { + memcpy(data_ptr, data, data_size); + current_length += data_size; + } + *written = current_length; + return NErr_Success; +} + + +const int8_t *ID3v2_2::Frame::GetIdentifier() const +{ + return header.GetIdentifier(); +} + + +/* === ID3v2.3 === */ +ID3v2_3::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags) +{ +} + +ID3v2_3::Frame::Frame(const FrameHeader &_header) : header(_header) +{ +} + +/* helper function +reads num_bytes from input into output, dealing with re-synchronization and length checking +increments input pointer +increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized +decrements input_len by bytes read +decrements output_len by bytes written +*/ +bool ID3v2_3::Frame::ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const +{ + /* verify that we have enough data in the frame */ + if (num_bytes > frame_len) + return false; + + /* verify that we have enough data in the buffer */ + size_t bytes_to_read; + if (header.Unsynchronised()) + bytes_to_read = ID3v2::Util::UnsynchronisedInputSize(input, num_bytes); + else + bytes_to_read = num_bytes; + + if (bytes_to_read > input_len) + return false; + + /* read data */ + if (header.Unsynchronised()) + { + *bytes_read += ID3v2::Util::SynchroniseTo(&output, input, num_bytes); + } + else + { + *bytes_read += num_bytes; + memcpy(output, input, num_bytes); + } + + /* increment input pointer */ + input = (const uint8_t *)input + bytes_to_read; + + /* decrement sizes */ + frame_len -= num_bytes; + input_len -= bytes_to_read; + return true; +} + +/* benski> this function is a bit complex +we have two things to worry about, and can have any combination of the two +1) Is the data 'unsynchronized' +2) Is the data compressed (zlib) + +we keep track of three sizes: +len - number of bytes in input buffer +data_size - number of bytes of output data buffer +frame_size - number of bytes of data in frame AFTER re-synchronization + +frame_size==data_size when compression is OFF +*/ +int ID3v2_3::Frame::Parse(const void *_data, size_t len, size_t *read) +{ + *read = 0; + size_t frame_size = header.FrameSize(); // size of frame AFTER re-synchronization + + if (header.Compressed()) + { + // read 4 bytes of decompressed size + uint8_t raw_size[4]; + if (ReadData(raw_size, _data, len, frame_size, 4, read) == false) + return 1; + + bytereader_value_t byte_reader; + bytereader_init(&byte_reader, raw_size, 4); + + data_size = bytereader_read_u32_be(&byte_reader); + } + + /* Check for group identity. If this exists, we'll store it separate from the raw data */ + if (header.Grouped()) + { + // read 1 byte for group identity + if (ReadData(&group_identity, _data, len, frame_size, 1, read) == false) + return 1; + } + + if (!header.Compressed()) + { + data_size = frame_size; + } + + /* check to make sure that we have enough input data to read the data */ + if (!header.Compressed() && header.Unsynchronised()) + { + /* this is tricky, because the stored size reflects after re-synchronization, + but the incoming data is unsynchronized */ + if (ID3v2::Util::UnsynchronisedInputSize(_data, data_size) > len) + return 1; + } + else if (frame_size > len) + return 1; + + /* allocate memory (real data_size) */ + data = malloc(data_size); + if (!data) + return NErr_OutOfMemory; + + /* === Read the data === */ + if (header.Compressed()) + { + if (header.Unsynchronised()) // compressed AND unsynchronized.. what a pain!! + { + // TODO: combined re-synchronization + inflation + void *temp = malloc(frame_size); + if (!temp) + return NErr_OutOfMemory; + + *read += ID3v2::Util::UnsynchroniseTo(temp, _data, frame_size); + + uLongf uncompressedSize = data_size; + int ret = uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)temp, frame_size); + free(temp); + if (ret != Z_OK) + return 1; + } + else + { + uLongf uncompressedSize = data_size; + if (uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)_data, frame_size) != Z_OK) + return 1; + *read += frame_size; + } + } + else if (header.Unsynchronised()) + { + *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size); + } + else // normal data + { + memcpy(data, _data, data_size); + *read += data_size; + } + + return NErr_Success; +} + +int ID3v2_3::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const +{ + ID3v2_3::FrameHeader new_header(header, tag_header); + // TODO: for now, we're not going to deal with compression + new_header.ClearCompressed(); + new_header.SetSize(data_size); + + uint32_t current_length=0; + new_header.SerializedSize(¤t_length); + + if (new_header.Unsynchronised()) + { + if (new_header.Compressed()) + { + uint8_t data_length[4]; + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, data_length, 4); + bytewriter_write_u32_be(&byte_writer, data_size); + current_length += ID3v2::Util::SynchronisedSize(&data_length, 4); + } + + if (new_header.Grouped()) + current_length += ID3v2::Util::SynchronisedSize(&group_identity, 1); + current_length += ID3v2::Util::SynchronisedSize(data, data_size); + } + else + { + current_length += new_header.FrameSize(); + } + + *length = current_length; + return NErr_Success; +} + +int ID3v2_3::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const +{ + size_t current_length = FrameHeaderBase::SIZE; + uint8_t *data_ptr = (uint8_t *)output; + ID3v2_3::FrameHeader new_header(header, tag_header); + // TODO: for now, we're not going to deal with compression + new_header.ClearCompressed(); + new_header.SetSize(data_size); + + // write frame header + uint32_t header_size; + new_header.Serialize(data_ptr, &header_size); + data_ptr += header_size; + if (new_header.Unsynchronised()) + { + if (new_header.Compressed()) + { + uint8_t data_length[4]; + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, data_length, 4); + bytewriter_write_u32_be(&byte_writer, data_size); + current_length += ID3v2::Util::SynchroniseTo(data_ptr, &data_length, 4); + data_ptr+=4; + } + + if (new_header.Grouped()) + current_length += ID3v2::Util::SynchroniseTo(data_ptr++, &group_identity, 1); + current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size); + } + else + { + if (new_header.Compressed()) + { + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, data_ptr, 4); + bytewriter_write_u32_be(&byte_writer, data_size); + data_ptr+=4; + } + + if (new_header.Grouped()) + { + *data_ptr++ = group_identity; + current_length++; + } + memcpy(data_ptr, data, data_size); + current_length += data_size; + } + *written = current_length; + return NErr_Success; +} + +const int8_t *ID3v2_3::Frame::GetIdentifier() const +{ + return header.GetIdentifier(); +} + +bool ID3v2_3::Frame::Encrypted() const +{ + return header.Encrypted(); +} + +bool ID3v2_3::Frame::Compressed() const +{ + return header.Compressed(); +} + +bool ID3v2_3::Frame::Grouped() const +{ + return header.Grouped(); +} + +bool ID3v2_3::Frame::ReadOnly() const +{ + return header.ReadOnly(); +} + +bool ID3v2_3::Frame::TagAlterPreservation() const +{ + return header.TagAlterPreservation(); +} + +bool ID3v2_3::Frame::FileAlterPreservation() const +{ + return header.FileAlterPreservation(); +} + + +/* === ID3v2.4 === */ +ID3v2_4::Frame::Frame(const ID3v2::Header &_header, const int8_t *id, int flags) : header(_header, id, flags) +{ +} + +ID3v2_4::Frame::Frame(const FrameHeader &_header) : header(_header) +{ +} + +/* helper function +reads num_bytes from input into output, dealing with re-synchronization and length checking +increments input pointer +increments bytes_read value by number of input bytes read (different from num_bytes when data is unsynchronized +decrements input_len by bytes read +decrements output_len by bytes written +*/ +bool ID3v2_4::Frame::ReadData(void *output, const void *&input, size_t &input_len, size_t &frame_len, size_t num_bytes, size_t *bytes_read) const +{ + /* verify that we have enough data in the frame */ + if (num_bytes > frame_len) + return false; + + /* verify that we have enough data in the buffer */ + size_t bytes_to_read = num_bytes; + + if (bytes_to_read > input_len) + return false; + + /* read data */ + + *bytes_read += num_bytes; + memcpy(output, input, num_bytes); + + /* increment input pointer */ + input = (const uint8_t *)input + bytes_to_read; + + /* decrement sizes */ + frame_len -= num_bytes; + input_len -= bytes_to_read; + return true; +} + +/* benski> this function is a bit complex +we have two things to worry about, and can have any combination of the two +1) Is the data 'unsynchronized' +2) Is the data compressed (zlib) + +we keep track of three sizes: +len - number of bytes in input buffer +data_size - number of bytes of output data buffer +frame_size - number of bytes of data in frame AFTER re-synchronization + +frame_size==data_size when compression is OFF +*/ +int ID3v2_4::Frame::Parse(const void *_data, size_t len, size_t *read) +{ + *read = 0; + size_t frame_size = header.FrameSize(); + + // TODO: if frame_size >= 128, verify size. iTunes v2.4 parser bug ... + + + /* Check for group identity. If this exists, we'll store it separate from the raw data */ + /* Note: ID3v2.4 puts group identity BEFORE data length indicator, where as v2.3 has it the other way */ + if (header.Grouped()) + { + // read 1 byte for group identity + if (ReadData(&group_identity, _data, len, frame_size, 1, read) == false) + return 1; + } + + if (header.Compressed() || header.DataLengthIndicated()) + { + // read 4 bytes of decompressed size + uint8_t raw_size[4]; + if (ReadData(raw_size, _data, len, frame_size, 4, read) == false) + return 1; + + bytereader_value_t byte_reader; + bytereader_init(&byte_reader, raw_size, 4); + + data_size = bytereader_read_u32_be(&byte_reader); + } + + if (!(header.Compressed() || header.DataLengthIndicated())) + { + data_size = frame_size; + } + + /* check to make sure that we have enough input data to read the data */ + + if (frame_size > len) + return 1; + + if (!header.Compressed() && header.Unsynchronised()) + { + data_size = ID3v2::Util::UnsynchronisedOutputSize(_data, frame_size); + } + + /* allocate memory (real data_size) */ + data = malloc(data_size); + if (!data) + return NErr_OutOfMemory; + + /* === Read the data === */ + if (header.Compressed()) + { + if (header.Unsynchronised()) // compressed AND unsynchronized.. what a pain!! + { + // TODO: combined re-synchronization + inflation + size_t sync_size = ID3v2::Util::UnsynchronisedOutputSize(_data, frame_size); + void *temp = malloc(sync_size); + if (!temp) + return NErr_OutOfMemory; + + *read += ID3v2::Util::UnsynchroniseTo(temp, _data, sync_size); + + uLongf uncompressedSize = data_size; + int ret = uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)temp, sync_size); + /* TODO: realloc and set data_size to uncompressedSize if uncompressedSize was actually lower */ + free(temp); + if (ret != Z_OK) + return 1; + } + else + { + uLongf uncompressedSize = data_size; + if (uncompress((Bytef *)data, &uncompressedSize, (const Bytef *)_data, frame_size) != Z_OK) + return 1; + /* TODO: realloc and set data_size to uncompressedSize if uncompressedSize was actually lower */ + *read += frame_size; + } + } + else if (header.Unsynchronised()) + { + *read += ID3v2::Util::UnsynchroniseTo(data, _data, data_size); + } + else // normal data + { + memcpy(data, _data, data_size); + *read += data_size; + } + + return 0; +} + +int ID3v2_4::Frame::SerializedSize(uint32_t *length, const ID3v2::Header &tag_header, int flags) const +{ + ID3v2_4::FrameHeader new_header(header, tag_header); + // TODO: for now, we're not going to deal with compression + new_header.ClearCompressed(); + switch(flags & Serialize_UnsynchronizeMask) + { + case Serialize_Unsynchronize: + // TODO: + break; + case Serialize_NoUnsynchronize: + new_header.ClearUnsynchronized(); + break; + } + + // TODO: this doesn't handle compression + if (new_header.Unsynchronised()) + { + size_t unsynchronized_data_size = ID3v2::Util::SynchronisedSize(data, data_size); + new_header.SetSize(unsynchronized_data_size); + } + else + { + new_header.SetSize(data_size); + } + + + size_t current_length = ID3v2_4::FrameHeader::SIZE; + + if (new_header.Unsynchronised()) + { + if (new_header.DataLengthIndicated() || new_header.Compressed()) + { + current_length += 4; + } + + if (new_header.Grouped()) + current_length += ID3v2::Util::SynchronisedSize(&group_identity, 1); + current_length += ID3v2::Util::SynchronisedSize(data, data_size); + } + else + { + current_length += new_header.FrameSize(); + } + + *length = current_length; + return NErr_Success; +} + +int ID3v2_4::Frame::Serialize(void *output, uint32_t *written, const ID3v2::Header &tag_header, int flags) const +{ + size_t current_length = ID3v2_4::FrameHeader::SIZE; + uint8_t *data_ptr = (uint8_t *)output; + ID3v2_4::FrameHeader new_header(header, tag_header); + // TODO: for now, we're not going to deal with compression + new_header.ClearCompressed(); + switch(flags & Serialize_UnsynchronizeMask) + { + case Serialize_Unsynchronize: + // TODO: + break; + case Serialize_NoUnsynchronize: + new_header.ClearUnsynchronized(); + break; + } + + // TODO: this doesn't handle compression + if (new_header.Unsynchronised()) + { + size_t unsynchronized_data_size = ID3v2::Util::SynchronisedSize(data, data_size); + new_header.SetSize(unsynchronized_data_size); + } + else + { + new_header.SetSize(data_size); + } + + // write frame header + uint32_t header_size; + new_header.Serialize(data_ptr, &header_size); + data_ptr += header_size; + + if (new_header.Compressed() || new_header.DataLengthIndicated()) + { + bytewriter_s byte_writer; + bytewriter_init(&byte_writer, data_ptr, 4); + bytewriter_write_u32_be(&byte_writer, ID3v2::Util::Int32To28(data_size)); + data_ptr+=4; + current_length+=4; + } + + if (new_header.Unsynchronised()) + { + + if (Grouped()) + current_length += ID3v2::Util::SynchroniseTo(data_ptr++, &group_identity, 1); + current_length += ID3v2::Util::SynchroniseTo(data_ptr, data, data_size); + } + else + { + + if (new_header.Grouped()) + { + *data_ptr++ = group_identity; + current_length++; + } + memcpy(data_ptr, data, data_size); + current_length += data_size; + } + *written = current_length; + return NErr_Success; +} + +const int8_t *ID3v2_4::Frame::GetIdentifier() const +{ + return header.GetIdentifier(); +} + +bool ID3v2_4::Frame::Encrypted() const +{ + return header.Encrypted(); +} + +bool ID3v2_4::Frame::Compressed() const +{ + return header.Compressed(); +} + +bool ID3v2_4::Frame::Grouped() const +{ + return header.Grouped(); +} + +bool ID3v2_4::Frame::ReadOnly() const +{ + return header.ReadOnly(); +} + +bool ID3v2_4::Frame::FrameUnsynchronised() const +{ + return header.FrameUnsynchronised(); +} + +bool ID3v2_4::Frame::DataLengthIndicated() const +{ + return header.DataLengthIndicated(); +} + + +bool ID3v2_4::Frame::TagAlterPreservation() const +{ + return header.TagAlterPreservation(); +} + +bool ID3v2_4::Frame::FileAlterPreservation() const +{ + return header.FileAlterPreservation(); +} |