aboutsummaryrefslogtreecommitdiff
path: root/Src/replicant/nsapev2/item.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/replicant/nsapev2/item.cpp')
-rw-r--r--Src/replicant/nsapev2/item.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/Src/replicant/nsapev2/item.cpp b/Src/replicant/nsapev2/item.cpp
new file mode 100644
index 00000000..2d5184ea
--- /dev/null
+++ b/Src/replicant/nsapev2/item.cpp
@@ -0,0 +1,235 @@
+#include "item.h"
+#include "flags.h"
+#include "util.h"
+#include "nu/ByteWriter.h"
+#include "nu/strsafe.h"
+#include "nu/ByteReader.h"
+#include "nsapev2/nsapev2.h"
+#include <stdlib.h>
+
+/*
+http://wiki.hydrogenaudio.org/index.php?title=APE_Tag_Item
+
+Item layout:
+[0-3] length of value field (little endian)
+[4-7] flags (little endian)
+[null terminated] key
+[length] value
+*/
+
+APEv2::Item::Item()
+{
+ len=0;
+ flags=0;
+ key=0;
+ value=0;
+}
+
+APEv2::Item::~Item()
+{
+ free(key);
+ free(value);
+}
+
+int APEv2::Item::Read(bytereader_t byte_reader)
+{
+ if (bytereader_size(byte_reader) < 8)
+ return NErr_NeedMoreData;
+
+ /* read fixed-size fields */
+ len = bytereader_read_u32_le(byte_reader);
+ flags = bytereader_read_u32_le(byte_reader);
+
+ /* find the null terminator */
+ size_t key_len = bytereader_find_zero(byte_reader);
+
+ /* make sure we didn't hit the end of our buffer */
+ if (key_len == bytereader_size(byte_reader))
+ return NErr_Insufficient;
+
+ /* check for empty key and also check for integer overflow */
+ if (key_len == 0 || key_len+1 == 0)
+ return NErr_Error;
+
+ key = (char *)malloc(key_len+1);
+ if (key)
+ {
+ bytereader_read_n(byte_reader, key, key_len+1); /* read key and terminator*/
+
+ if (bytereader_size(byte_reader) < len) /* make sure we have room for the value! */
+ {
+ free(key);
+ key=0;
+ return NErr_NeedMoreData;
+ }
+
+ value = (char *)malloc(len);
+ if (value)
+ {
+ bytereader_read_n(byte_reader, value, len); /* read value */
+ return NErr_Success;
+ }
+ else
+ {
+ free(key);
+ key=0;
+ return NErr_OutOfMemory;
+ }
+ }
+ else
+ return NErr_OutOfMemory;
+
+}
+
+bool APEv2::Item::IsReadOnly()
+{
+ return flags & FLAG_READONLY;
+}
+
+bool APEv2::Item::KeyMatch(const char *key_to_compare, int compare)
+{
+ if (!key || !*key)
+ return false;
+
+ switch (compare)
+ {
+ case ITEM_KEY_COMPARE_CASE_INSENSITIVE:
+#ifdef _WIN32
+ return !_stricmp(key_to_compare, key);
+#else
+ return !strcasecmp(key_to_compare, key);
+#endif
+ case ITEM_KEY_COMPARE_CASE_SENSITIVE:
+ return !strcmp(key_to_compare, key);
+ default:
+ return false;
+ }
+}
+
+int APEv2::Item::Get(const void **data, size_t *datalen) const
+{
+ if (!value || !len)
+ return NErr_Empty;
+ *data = value;
+ *datalen = len;
+ return NErr_Success;
+}
+
+int APEv2::Item::Set(nx_string_t string)
+{
+ if (!value)
+ return NErr_BadParameter;
+
+ flags &= ~nsapev2_item_type_mask;
+ flags |= nsapev2_item_type_utf8;
+
+ size_t bytes;
+ int ret = NXStringGetBytesSize(&bytes, string, nx_charset_utf8, 0);
+ if (ret != NErr_DirectPointer && ret != NErr_Success)
+ return ret;
+
+ void *new_value = malloc(bytes);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ size_t bytes_copied;
+ ret = NXStringGetBytes(&bytes_copied, string, new_value, bytes, nx_charset_utf8, 0);
+ if (ret != NErr_Success)
+ {
+ free(new_value);
+ return ret;
+ }
+
+ free(value);
+ value=new_value;
+ len=(uint32_t)bytes_copied;
+ return NErr_Success;
+}
+
+int APEv2::Item::Set(const void *data, size_t datalen, int data_type)
+{
+ if (!data || !datalen)
+ return NErr_Error;
+
+ // set data type for this item
+ flags &= ~nsapev2_item_type_mask;
+ flags |= data_type;
+
+ void *new_value = realloc(value, datalen);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ value=new_value;
+ len=(uint32_t)datalen;
+ memcpy(value, data, len);
+ return NErr_Success;
+}
+
+ int APEv2::Item::New(size_t datalen, int data_type, void **bytes)
+ {
+ if (!datalen)
+ return NErr_Error;
+
+ // set data type for this item
+ flags &= ~nsapev2_item_type_mask;
+ flags |= data_type;
+
+ void *new_value = realloc(value, datalen);
+ if (!new_value)
+ return NErr_OutOfMemory;
+
+ value=new_value;
+
+ len=(uint32_t)datalen;
+ *bytes = value;
+ return NErr_Success;
+ }
+
+int APEv2::Item::SetKey(const char *tag)
+{
+ if (!tag || !*tag)
+ return NErr_Error;
+
+ char *new_key = strdup(tag);
+ if (!new_key)
+ return NErr_OutOfMemory;
+
+ free(key);
+ key = new_key;
+ return NErr_Success;
+}
+
+int APEv2::Item::GetKey(const char **tag) const
+{
+ if (!key)
+ return NErr_Error;
+ *tag = key;
+ return NErr_Success;
+}
+
+size_t APEv2::Item::EncodeSize() const
+{
+ return 4 /* size */ + 4 /* flags */ + strlen(key) + 1 /* NULL separator */ + len;
+}
+
+uint32_t APEv2::Item::GetFlags() const
+{
+ return flags;
+}
+
+int APEv2::Item::Encode(bytewriter_t byte_writer) const
+{
+ if (!key || !value || !len)
+ return NErr_Error;
+
+ if (bytewriter_size(byte_writer) < EncodeSize())
+ return NErr_Insufficient;
+
+ bytewriter_write_u32_le(byte_writer, len);
+ bytewriter_write_u32_le(byte_writer, flags);
+ bytewriter_write_n(byte_writer, key, strlen(key) + 1);
+ bytewriter_write_n(byte_writer, value, len);
+
+ return NErr_Success;
+}
+