diff options
Diffstat (limited to 'apps/tagdb/album.c')
-rw-r--r-- | apps/tagdb/album.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/apps/tagdb/album.c b/apps/tagdb/album.c new file mode 100644 index 0000000000..53b3660c90 --- /dev/null +++ b/apps/tagdb/album.c | |||
@@ -0,0 +1,454 @@ | |||
1 | #include "malloc.h" // realloc() and free() | ||
2 | #include <strings.h> // strncasecmp() | ||
3 | #include <string.h> // strlen() | ||
4 | |||
5 | #include "album.h" | ||
6 | |||
7 | // how is our flag organized? | ||
8 | #define FLAG(deleted, spare) ( 0xE0 | (deleted?0x10:0x00) | (spare & 0x0F) ) | ||
9 | #define FLAG_VALID(flag) ((flag & 0xE0) == 0xE0) | ||
10 | #define FLAG_DELETED(flag) (flag & 0x10) | ||
11 | #define FLAG_SPARE(flag) (flag & 0x0F) | ||
12 | |||
13 | static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill); | ||
14 | |||
15 | struct album_entry* new_album_entry(const uint32_t name_len, const uint32_t song_count) { | ||
16 | // Start my allocating memory | ||
17 | struct album_entry *e = (struct album_entry*)malloc(sizeof(struct album_entry)); | ||
18 | if( e == NULL ) { | ||
19 | DEBUGF("new_album_entry: could not allocate memory\n"); | ||
20 | return NULL; | ||
21 | } | ||
22 | |||
23 | // We begin empty | ||
24 | e->name = NULL; | ||
25 | e->size.name_len = 0; | ||
26 | e->key = NULL; | ||
27 | e->artist = 0; | ||
28 | e->song = NULL; | ||
29 | e->size.song_count = 0; | ||
30 | |||
31 | e->flag = FLAG(0, 0); | ||
32 | |||
33 | // and resize to the requested size | ||
34 | if( do_resize(e, name_len, song_count, 1) ) { | ||
35 | free(e); | ||
36 | return NULL; | ||
37 | } | ||
38 | return e; | ||
39 | } | ||
40 | |||
41 | int album_entry_destruct(struct album_entry *e) { | ||
42 | assert(e != NULL); | ||
43 | assert(FLAG_VALID(e->flag)); | ||
44 | |||
45 | free(e->name); | ||
46 | free(e->key); | ||
47 | free(e->song); | ||
48 | |||
49 | free(e); | ||
50 | |||
51 | return ERR_NONE; | ||
52 | } | ||
53 | |||
54 | static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill) { | ||
55 | void* temp; | ||
56 | |||
57 | assert(e != NULL); | ||
58 | assert(FLAG_VALID(e->flag)); | ||
59 | |||
60 | // begin with name | ||
61 | if( name_len != e->size.name_len ) { | ||
62 | temp = realloc(e->name, name_len); | ||
63 | if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
64 | DEBUGF("do_resize: out of memory to resize name\n"); | ||
65 | return ERR_MALLOC; | ||
66 | } | ||
67 | e->name = (char*)temp; | ||
68 | |||
69 | // if asked, fill it with zero's | ||
70 | if( zero_fill ) { | ||
71 | uint32_t i; | ||
72 | for(i=e->size.name_len; i<name_len; i++) | ||
73 | e->name[i] = (char)0x00; | ||
74 | } | ||
75 | |||
76 | e->size.name_len = name_len; | ||
77 | } | ||
78 | |||
79 | // now the song[] | ||
80 | if( song_count != e->size.song_count ) { | ||
81 | temp = realloc(e->song, song_count * sizeof(*e->song)); | ||
82 | if(temp == NULL && song_count > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
83 | DEBUGF("album_entry_resize: out of memory to resize song[]\n"); | ||
84 | return ERR_MALLOC; | ||
85 | } | ||
86 | e->song = (uint32_t*)temp; | ||
87 | |||
88 | // if asked, fill it with zero's | ||
89 | if( zero_fill ) { | ||
90 | uint32_t i; | ||
91 | for(i=e->size.song_count; i<song_count; i++) | ||
92 | e->song[i] = (uint32_t)0x00000000; | ||
93 | } | ||
94 | |||
95 | e->size.song_count = song_count; | ||
96 | } | ||
97 | |||
98 | return ERR_NONE; | ||
99 | } | ||
100 | |||
101 | inline int album_entry_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count) { | ||
102 | return do_resize(e, name_len, song_count, 1); | ||
103 | } | ||
104 | |||
105 | int album_entry_serialize(FILE *fd, const struct album_entry *e) { | ||
106 | uint32_t length; | ||
107 | |||
108 | assert(e != NULL); | ||
109 | assert(FLAG_VALID(e->flag)); | ||
110 | assert(fd != NULL); | ||
111 | |||
112 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
113 | return ERR_NONE; | ||
114 | } | ||
115 | |||
116 | // First byte we write is a flag-byte | ||
117 | if( fwrite(&e->flag, 1, 1, fd) != 1 ) { | ||
118 | DEBUGF("album_entry_serialize: failed to write flag-byte\n"); | ||
119 | return ERR_FILE; | ||
120 | } | ||
121 | |||
122 | // First we write the length of the name field | ||
123 | if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) { | ||
124 | DEBUGF("album_entry_serialize: failed to write name_len\n"); | ||
125 | return ERR_FILE; | ||
126 | } | ||
127 | |||
128 | // now the name field itself | ||
129 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
130 | DEBUGF("album_entry_serialize: failed to write name\n"); | ||
131 | return ERR_FILE; | ||
132 | } | ||
133 | |||
134 | // the key-field (if present) | ||
135 | if( e->key != NULL ) { | ||
136 | length = strlen(e->key); | ||
137 | } else { | ||
138 | length = 0; | ||
139 | } | ||
140 | // length (always, 0 if not present) | ||
141 | if( fwrite(&length, sizeof(length), 1, fd) != 1 ) { | ||
142 | DEBUGF("album_entry_serialize: failed to write length of key\n"); | ||
143 | return ERR_FILE; | ||
144 | } | ||
145 | if( e->key != NULL ) { | ||
146 | // key itself | ||
147 | if( fwrite(e->key, 1, length, fd) != length ) { | ||
148 | DEBUGF("album_entry_serialize: failed to write key\n"); | ||
149 | return ERR_FILE; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | // Artist field | ||
154 | if( fwrite(&e->artist, sizeof(e->artist), 1, fd) != 1 ) { | ||
155 | DEBUGF("album_entry_serialize: failed to write artist\n"); | ||
156 | return ERR_FILE; | ||
157 | } | ||
158 | |||
159 | // count of songs | ||
160 | if( fwrite(&e->size.song_count, sizeof(e->size.song_count), 1, fd) != 1 ) { | ||
161 | DEBUGF("album_entry_serialize: failed to write song_count\n"); | ||
162 | return ERR_FILE; | ||
163 | } | ||
164 | |||
165 | // song[] itself | ||
166 | if( fwrite(e->song, sizeof(*e->song), e->size.song_count, fd) != e->size.song_count ) { | ||
167 | DEBUGF("album_entry_serialize: failed to write songs\n"); | ||
168 | return ERR_FILE; | ||
169 | } | ||
170 | |||
171 | return ERR_NONE; | ||
172 | } | ||
173 | |||
174 | int album_entry_unserialize(struct album_entry **e, FILE *fd) { | ||
175 | uint32_t length; | ||
176 | unsigned char flag; | ||
177 | |||
178 | assert(e != NULL); | ||
179 | assert(fd != NULL); | ||
180 | |||
181 | // First byte we read are the flags | ||
182 | if( fread(&flag, 1, 1, fd) != 1 ) { | ||
183 | DEBUGF("album_entry_unserialize: failed to read flag-byte\n"); | ||
184 | return ERR_FILE; | ||
185 | } | ||
186 | |||
187 | // See what we have: | ||
188 | if( ! FLAG_VALID(flag) ) { | ||
189 | DEBUGF("album_entry_unserialize: flag-byte is invalid\n"); | ||
190 | return ERR_INVALID; | ||
191 | } | ||
192 | |||
193 | // Allocate memory | ||
194 | *e = new_album_entry(0, 0); | ||
195 | if( *e == NULL ) { | ||
196 | DEBUGF("album_entry_unserialize: could not create new album_entry\n"); | ||
197 | return ERR_MALLOC; | ||
198 | } | ||
199 | |||
200 | (*e)->flag = flag; // we had a valid entry, copy it over | ||
201 | |||
202 | // First we read the length of the name field | ||
203 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
204 | DEBUGF("album_entry_unserialize: failed to read name_len\n"); | ||
205 | album_entry_destruct(*e); | ||
206 | return ERR_FILE; | ||
207 | } | ||
208 | |||
209 | // allocate memory for the upcomming name-field | ||
210 | if( do_resize(*e, length, 0, 0) ) { | ||
211 | DEBUGF("album_entry_unserialize: failed to allocate memory for name\n"); | ||
212 | album_entry_destruct(*e); | ||
213 | return ERR_MALLOC; | ||
214 | } | ||
215 | |||
216 | // read it in | ||
217 | if( fread((*e)->name, 1, (*e)->size.name_len, fd) != (*e)->size.name_len ) { | ||
218 | DEBUGF("album_entry_unserialize: failed to read name\n"); | ||
219 | album_entry_destruct(*e); | ||
220 | return ERR_FILE; | ||
221 | } | ||
222 | |||
223 | if( FLAG_DELETED(flag) ) { | ||
224 | // all there is... free some memory | ||
225 | if( do_resize(*e, 0, 0, 0) ) { | ||
226 | DEBUGF("album_entry_unserialize: couldn't free() name\n"); | ||
227 | return ERR_MALLOC; | ||
228 | } | ||
229 | return ERR_NONE; | ||
230 | } | ||
231 | |||
232 | // maybe a key-field | ||
233 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
234 | DEBUGF("album_entry_unserialize: failed to read length of key\n"); | ||
235 | album_entry_destruct(*e); | ||
236 | return ERR_FILE; | ||
237 | } | ||
238 | |||
239 | if( length > 0 ) { | ||
240 | // allocate memory | ||
241 | if( ((*e)->key = malloc(length)) == NULL ) { | ||
242 | DEBUGF("album_entry_unserialize: failed to allocate memory for key\n"); | ||
243 | album_entry_destruct(*e); | ||
244 | return ERR_MALLOC; | ||
245 | } | ||
246 | |||
247 | // read it | ||
248 | if( fread((*e)->key, 1, length, fd) != length ) { | ||
249 | DEBUGF("album_entry_unserialize: failed to read key\n"); | ||
250 | album_entry_destruct(*e); | ||
251 | return ERR_FILE; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | // next the artist field | ||
256 | if( fread(&(*e)->artist, sizeof((*e)->artist), 1, fd) != 1 ) { | ||
257 | DEBUGF("album_entry_unserialize: failed to read artist\n"); | ||
258 | album_entry_destruct(*e); | ||
259 | return ERR_FILE; | ||
260 | } | ||
261 | |||
262 | // Next the count of songs | ||
263 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
264 | DEBUGF("album_entry_unserialize: failed to read song_count\n"); | ||
265 | album_entry_destruct(*e); | ||
266 | return ERR_FILE; | ||
267 | } | ||
268 | |||
269 | // allocate memory for the upcomming name-field | ||
270 | if( do_resize(*e, (*e)->size.name_len, length, 0) ) { | ||
271 | DEBUGF("album_entry_unserialize: failed to allocate memory for song[]\n"); | ||
272 | album_entry_destruct(*e); | ||
273 | return ERR_MALLOC; | ||
274 | } | ||
275 | |||
276 | // read it in | ||
277 | if( fread((*e)->song, sizeof(*(*e)->song), (*e)->size.song_count, fd) != (*e)->size.song_count ) { | ||
278 | DEBUGF("album_entry_unserialize: failed to read songs\n"); | ||
279 | album_entry_destruct(*e); | ||
280 | return ERR_FILE; | ||
281 | } | ||
282 | |||
283 | return ERR_NONE; | ||
284 | } | ||
285 | |||
286 | int album_entry_write(FILE *fd, struct album_entry *e, struct album_size *s) { | ||
287 | uint32_t i, be; | ||
288 | char pad = 0x00; | ||
289 | |||
290 | assert(e != NULL); | ||
291 | assert(FLAG_VALID(e->flag)); | ||
292 | assert(fd != NULL); | ||
293 | |||
294 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
295 | return ERR_NONE; | ||
296 | } | ||
297 | |||
298 | // resize-write to size *s | ||
299 | // First check if we are not reducing the size... | ||
300 | if( s != NULL && ( s->name_len < e->size.name_len || s->song_count < e->size.song_count ) ) { | ||
301 | // just do it in 2 steps | ||
302 | if( do_resize(e, s->name_len, s->song_count, 0) ) { | ||
303 | DEBUGF("album_entry_write: failed to reduce size of entry, failing...\n"); | ||
304 | return ERR_MALLOC; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | // album name | ||
309 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
310 | DEBUGF("album_entry_write: failed to write name\n"); | ||
311 | return ERR_FILE; | ||
312 | } | ||
313 | // pad the rest | ||
314 | i = e->size.name_len; | ||
315 | while( s != NULL && s->name_len > i) { | ||
316 | if( fwrite(&pad, 1, 1, fd) == 1 ) { | ||
317 | i++; | ||
318 | continue; | ||
319 | } else { | ||
320 | DEBUGF("album_entry_write: failed to pad name\n"); | ||
321 | return ERR_FILE; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | // artist | ||
326 | be = BE32(e->artist); | ||
327 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
328 | DEBUGF("album_entry_write: failed to write artist\n"); | ||
329 | return ERR_FILE; | ||
330 | } | ||
331 | |||
332 | // song offsets, but in BIG ENDIAN! | ||
333 | // so we need to iterate over each item to convert it | ||
334 | for(i=0; i<e->size.song_count; i++) { | ||
335 | be = BE32(e->song[i]); | ||
336 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
337 | DEBUGF("album_entry_write: failed to write song[%d]\n", i); | ||
338 | return ERR_FILE; | ||
339 | } | ||
340 | } | ||
341 | // pad the rest | ||
342 | be = BE32(0x00000000); | ||
343 | for(; s != NULL && i<s->song_count; i++) { | ||
344 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
345 | DEBUGF("album_entry_write: failed to pad song[]\n"); | ||
346 | return ERR_FILE; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | inline int album_entry_compare(const struct album_entry *a, const struct album_entry *b) { | ||
354 | assert(a != NULL); | ||
355 | assert(b != NULL); | ||
356 | assert(a->key != NULL); | ||
357 | assert(b->key != NULL); | ||
358 | return strcasecmp(a->key, b->key); | ||
359 | } | ||
360 | |||
361 | struct album_size* new_album_size() { | ||
362 | struct album_size *s; | ||
363 | s = (struct album_size*)malloc(sizeof(struct album_size)); | ||
364 | if( s == NULL ) { | ||
365 | DEBUGF("new_album_size: failed to allocate memory\n"); | ||
366 | return NULL; | ||
367 | } | ||
368 | s->name_len = 0; | ||
369 | s->song_count = 0; | ||
370 | |||
371 | return s; | ||
372 | } | ||
373 | |||
374 | inline uint32_t album_size_get_length(const struct album_size *size) { | ||
375 | assert(size != NULL); | ||
376 | return size->name_len + 4 + 4*size->song_count; | ||
377 | } | ||
378 | |||
379 | inline int album_size_max(struct album_size *s, const struct album_entry *e) { | ||
380 | assert(s != NULL); | ||
381 | assert(e != NULL); | ||
382 | assert(FLAG_VALID(e->flag)); | ||
383 | |||
384 | s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len ); | ||
385 | s->song_count = ( s->song_count >= e->size.song_count ? s->song_count : e->size.song_count ); | ||
386 | return ERR_NONE; | ||
387 | } | ||
388 | |||
389 | int album_size_destruct(struct album_size *s) { | ||
390 | assert(s != NULL); | ||
391 | // nothing to do... | ||
392 | free(s); | ||
393 | return ERR_NONE; | ||
394 | } | ||
395 | |||
396 | int album_entry_add_song_mem(struct album_entry *e, struct album_size *s, const uint32_t song) { | ||
397 | assert(e != NULL); | ||
398 | assert(FLAG_VALID(e->flag)); | ||
399 | |||
400 | if( do_resize(e, e->size.name_len, e->size.song_count+1, 0) ) { | ||
401 | DEBUGF("album_entry_add_song_mem: failed to resize song[]\n"); | ||
402 | return ERR_MALLOC; | ||
403 | } | ||
404 | |||
405 | e->song[e->size.song_count-1] = song; | ||
406 | |||
407 | if( s != NULL) album_size_max(s, e); // can't fail | ||
408 | |||
409 | return ERR_NONE; | ||
410 | } | ||
411 | |||
412 | static int delete_serialized(FILE *fd, struct album_entry *e) { | ||
413 | // the entry should be both, in memory and in file at the current location | ||
414 | // this function will mark the file-entry as deleted | ||
415 | uint32_t size; | ||
416 | unsigned char flag; | ||
417 | |||
418 | assert(fd != NULL); | ||
419 | assert(e != NULL); | ||
420 | assert(FLAG_VALID(e->flag)); | ||
421 | |||
422 | // overwrite the beginning of the serialized data: | ||
423 | flag = FLAG(1, 0); // set the delete flag, clear the spare flags | ||
424 | |||
425 | // First byte we write is the flag-byte to indicate this is a deleted | ||
426 | if( fwrite(&flag, 1, 1, fd) != 1 ) { | ||
427 | DEBUGF("album_entry_delete_serialized: failed to write flag-byte\n"); | ||
428 | return ERR_FILE; | ||
429 | } | ||
430 | |||
431 | // Then we write the length of the COMPLETE entry | ||
432 | size = album_size_get_length(&e->size) + 4; // 4 = overhead for the song[] | ||
433 | if( fwrite(&size, sizeof(size), 1, fd) != 1 ) { | ||
434 | DEBUGF("album_entry_delete_serialized: failed to write len\n"); | ||
435 | return ERR_FILE; | ||
436 | } | ||
437 | |||
438 | return ERR_NONE; | ||
439 | } | ||
440 | |||
441 | int album_entry_add_song_file(FILE *fd, struct album_entry *e, struct album_size *s, const uint32_t song) { | ||
442 | assert(fd != NULL); | ||
443 | assert(e != NULL); | ||
444 | assert(FLAG_VALID(e->flag)); | ||
445 | |||
446 | DEBUGF("album_entry_add_song_file() called\n"); | ||
447 | |||
448 | if( delete_serialized(fd, e) ) { | ||
449 | DEBUGF("album_entry_add_song_file: could not mark as deleted\n"); | ||
450 | return ERR_FILE; | ||
451 | } | ||
452 | |||
453 | return ERR_NO_INPLACE_UPDATE; | ||
454 | } | ||