aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nsid3v2/frameheader.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/replicant/nsid3v2/frameheader.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/replicant/nsid3v2/frameheader.cpp')
-rw-r--r--Src/replicant/nsid3v2/frameheader.cpp403
1 files changed, 403 insertions, 0 deletions
diff --git a/Src/replicant/nsid3v2/frameheader.cpp b/Src/replicant/nsid3v2/frameheader.cpp
new file mode 100644
index 00000000..3ffa63f9
--- /dev/null
+++ b/Src/replicant/nsid3v2/frameheader.cpp
@@ -0,0 +1,403 @@
+#include "frameheader.h"
+#include "util.h"
+#include "values.h"
+#include "nu/ByteReader.h"
+#include "nu/ByteWriter.h"
+#include <string.h>
+#include "foundation/error.h"
+/* === ID3v2 common === */
+ID3v2::FrameHeader::FrameHeader(const ID3v2::Header &_header) : tagHeader(_header)
+{
+}
+
+static bool CharOK(int8_t c)
+{
+ if (c >= '0' && c <= '9')
+ return true;
+
+ if (c >= 'A' && c <= 'Z')
+ return true;
+
+ return false;
+}
+
+/* === ID3v2.2 === */
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2_2::FrameHeader &frame_header, const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+ frameHeaderData = frame_header.frameHeaderData;
+}
+
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2::FrameHeader(_header)
+{
+ memcpy(&frameHeaderData.id, id, 3);
+ frameHeaderData.id[3]=0;
+ memset(&frameHeaderData.size, 0, 3);
+}
+
+ID3v2_2::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2::FrameHeader(_header)
+{
+ char temp_data[FrameHeader::SIZE];
+ if (tagHeader.Unsynchronised())
+ {
+ ID3v2::Util::UnsynchroniseTo(temp_data, data, sizeof(temp_data));
+ data = temp_data;
+ }
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeader::SIZE);
+
+ bytereader_read_n(&byte_reader, &frameHeaderData.id, 3);
+ frameHeaderData.id[3]=0;
+ bytereader_read_n(&byte_reader, &frameHeaderData.size, 3);
+}
+
+bool ID3v2_2::FrameHeader::IsValid() const
+{
+ if (CharOK(frameHeaderData.id[0])
+ && CharOK(frameHeaderData.id[1])
+ && CharOK(frameHeaderData.id[2]))
+ return true;
+
+ return false;
+}
+
+const int8_t *ID3v2_2::FrameHeader::GetIdentifier() const
+{
+ return frameHeaderData.id;
+}
+
+bool ID3v2_2::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised();
+}
+
+uint32_t ID3v2_2::FrameHeader::FrameSize() const
+{
+ return (frameHeaderData.size[0] << 16) | (frameHeaderData.size[1] << 8) | (frameHeaderData.size[2]);
+}
+
+void ID3v2_2::FrameHeader::SetSize(uint32_t data_size)
+{
+ frameHeaderData.size[0] = data_size >> 16;
+ frameHeaderData.size[1] = data_size >> 8;
+ frameHeaderData.size[2] = data_size;
+}
+
+int ID3v2_2::FrameHeader::SerializedSize(uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t data[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ *written = ID3v2::Util::SynchronisedSize(data, SIZE);
+ }
+ else
+ {
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+int ID3v2_2::FrameHeader::Serialize(void *data) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t temp[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, temp, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ ID3v2::Util::SynchroniseTo(data, temp, SIZE);
+ }
+ else
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, frameHeaderData.id, 3);
+ bytewriter_write_n(&byte_writer, frameHeaderData.size, 3);
+ }
+ return NErr_Success;
+}
+
+/* === ID3v2.3+ common === */
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2_3::FrameHeaderBase &frame_header_base, const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+ memcpy(id, frame_header_base.id, 4);
+ size=frame_header_base.size;
+ flags[0] = frame_header_base.flags[0];
+ flags[1] = frame_header_base.flags[1];
+}
+
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2::Header &_header) : ID3v2::FrameHeader(_header)
+{
+}
+
+ID3v2_3::FrameHeaderBase::FrameHeaderBase(const ID3v2::Header &_header, const int8_t *_id, int _flags) : ID3v2::FrameHeader(_header)
+{
+ memcpy(id, _id, 4);
+ size=0;
+ // TODO: flags
+ flags[0]=0;
+ flags[1]=0;
+}
+
+const int8_t *ID3v2_3::FrameHeaderBase::GetIdentifier() const
+{
+ return id;
+}
+
+
+bool ID3v2_3::FrameHeaderBase::IsValid() const
+{
+ if (CharOK(id[0])
+ && CharOK(id[1])
+ && CharOK(id[2])
+ && CharOK(id[3]))
+ return true;
+
+ return false;
+}
+
+
+
+/* === ID3v2.3 === */
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2_3::FrameHeader &frame_header, const ID3v2::Header &tag_header) : ID3v2_3::FrameHeaderBase(frame_header, tag_header)
+{
+}
+
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2_3::FrameHeaderBase(_header, id, flags)
+{
+}
+
+ID3v2_3::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2_3::FrameHeaderBase(_header)
+{
+ char temp_data[FrameHeaderBase::SIZE];
+ if (tagHeader.Unsynchronised())
+ {
+ ID3v2::Util::UnsynchroniseTo(temp_data, data, sizeof(temp_data));
+ data = temp_data;
+ }
+
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeaderBase::SIZE);
+
+ bytereader_read_n(&byte_reader, &id, 4);
+ size = bytereader_read_u32_be(&byte_reader);
+ bytereader_read_n(&byte_reader, &flags, 2);
+}
+
+int ID3v2_3::FrameHeaderBase::SerializedSize(uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t data[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = ID3v2::Util::SynchronisedSize(data, SIZE);
+ }
+ else
+ {
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+int ID3v2_3::FrameHeaderBase::Serialize(void *data, uint32_t *written) const
+{
+ if (tagHeader.Unsynchronised())
+ {
+ uint8_t temp[SIZE];
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, temp, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = ID3v2::Util::SynchroniseTo(data, temp, SIZE);
+ }
+ else
+ {
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = SIZE;
+ }
+ return NErr_Success;
+}
+
+uint32_t ID3v2_3::FrameHeader::FrameSize() const
+{
+ return size;
+}
+
+bool ID3v2_3::FrameHeader::ReadOnly() const
+{
+ return !!(flags[0] & (1<<5));
+}
+
+bool ID3v2_3::FrameHeader::Encrypted() const
+{
+ return !!(flags[1] & (1<<6));
+}
+
+bool ID3v2_3::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised();
+}
+
+bool ID3v2_3::FrameHeader::Grouped() const
+{
+ return !!(flags[1] & (1 << 5));
+}
+
+bool ID3v2_3::FrameHeader::Compressed() const
+{
+ return !!(flags[1] & (1 << 7));
+}
+
+bool ID3v2_3::FrameHeader::TagAlterPreservation() const
+{
+ return !!(flags[0] & (1<<7));
+}
+
+bool ID3v2_3::FrameHeader::FileAlterPreservation() const
+{
+ return !!(flags[0] & (1<<6));
+}
+
+void ID3v2_3::FrameHeader::ClearCompressed()
+{
+ flags[1] &= ~(1 << 7);
+}
+
+void ID3v2_3::FrameHeader::SetSize(uint32_t data_size)
+{
+ if (Compressed())
+ data_size+=4;
+ if (Grouped())
+ data_size++;
+ size = data_size;
+}
+
+/* === ID3v2.4 === */
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2_4::FrameHeader &frame_header, const ID3v2::Header &tag_header) : ID3v2_3::FrameHeaderBase(frame_header, tag_header)
+{
+}
+
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2::Header &_header, const int8_t *id, int flags) : ID3v2_3::FrameHeaderBase(_header, id, flags)
+{
+}
+
+ID3v2_4::FrameHeader::FrameHeader(const ID3v2::Header &_header, const void *data) : ID3v2_3::FrameHeaderBase(_header)
+{
+ bytereader_value_t byte_reader;
+ bytereader_init(&byte_reader, data, FrameHeaderBase::SIZE);
+
+ bytereader_read_n(&byte_reader, &id, 4);
+ size = bytereader_read_u32_be(&byte_reader);
+ bytereader_read_n(&byte_reader, &flags, 2);
+}
+
+uint32_t ID3v2_4::FrameHeader::FrameSize() const
+{
+ // many programs write non-syncsafe sizes (iTunes is the biggest culprit)
+ // so we'll try to detect it. unfortunately this isn't foolproof
+ // ID3v2_4::Frame will have some additional checks
+ int mask = size & 0x80808080;
+ if (mask)
+ return size;
+ else
+ return ID3v2::Util::Int28To32(size);
+}
+
+bool ID3v2_4::FrameHeader::ReadOnly() const
+{
+ return !!(flags[0] & (1<<4));
+}
+
+bool ID3v2_4::FrameHeader::Encrypted() const
+{
+ return !!(flags[1] & (1<<3));
+}
+
+bool ID3v2_4::FrameHeader::Unsynchronised() const
+{
+ return tagHeader.Unsynchronised() || !!(flags[1] & (1 << 1));
+}
+
+bool ID3v2_4::FrameHeader::FrameUnsynchronised() const
+{
+ return !!(flags[1] & (1 << 1));
+}
+
+bool ID3v2_4::FrameHeader::DataLengthIndicated() const
+{
+ return !!(flags[1] & (1 << 0));
+}
+
+bool ID3v2_4::FrameHeader::Compressed() const
+{
+ return !!(flags[1] & (1 << 3));
+}
+
+bool ID3v2_4::FrameHeader::Grouped() const
+{
+ return !!(flags[1] & (1 << 6));
+}
+
+bool ID3v2_4::FrameHeader::TagAlterPreservation() const
+{
+ return !!(flags[0] & (1<<6));
+}
+
+bool ID3v2_4::FrameHeader::FileAlterPreservation() const
+{
+ return !!(flags[0] & (1<<5));
+}
+
+void ID3v2_4::FrameHeader::ClearUnsynchronized()
+{
+ flags[1] &= ~(1 << 1);
+}
+
+void ID3v2_4::FrameHeader::ClearCompressed()
+{
+ flags[1] &= ~(1 << 3);
+}
+
+void ID3v2_4::FrameHeader::SetSize(uint32_t data_size)
+{
+ if (Compressed() || DataLengthIndicated())
+ data_size+=4;
+ if (Grouped())
+ data_size++;
+ size = ID3v2::Util::Int32To28(data_size);
+}
+
+int ID3v2_4::FrameHeader::SerializedSize(uint32_t *written) const
+{
+ *written = SIZE;
+ return NErr_Success;
+}
+
+int ID3v2_4::FrameHeader::Serialize(void *data, uint32_t *written) const
+{
+ bytewriter_s byte_writer;
+ bytewriter_init(&byte_writer, data, SIZE);
+ bytewriter_write_n(&byte_writer, id, 4);
+ bytewriter_write_u32_be(&byte_writer, size);
+ bytewriter_write_u8(&byte_writer, flags[0]);
+ bytewriter_write_u8(&byte_writer, flags[1]);
+ *written = SIZE;
+ return NErr_Success;
+}