236 lines
4.4 KiB
C++
236 lines
4.4 KiB
C++
|
#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;
|
||
|
}
|
||
|
|