#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 /* 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; }