1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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;
}
|