lots of old and new edits
[kugel-rb.git] / apps / tagdb / album.c
blob53b3660c900752fb2c602c6bf0df7a3ec443b203
1 #include "malloc.h" // realloc() and free()
2 #include <strings.h> // strncasecmp()
3 #include <string.h> // strlen()
5 #include "album.h"
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)
13 static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill);
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;
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;
31 e->flag = FLAG(0, 0);
33 // and resize to the requested size
34 if( do_resize(e, name_len, song_count, 1) ) {
35 free(e);
36 return NULL;
38 return e;
41 int album_entry_destruct(struct album_entry *e) {
42 assert(e != NULL);
43 assert(FLAG_VALID(e->flag));
45 free(e->name);
46 free(e->key);
47 free(e->song);
49 free(e);
51 return ERR_NONE;
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;
57 assert(e != NULL);
58 assert(FLAG_VALID(e->flag));
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;
67 e->name = (char*)temp;
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;
76 e->size.name_len = name_len;
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;
86 e->song = (uint32_t*)temp;
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;
95 e->size.song_count = song_count;
98 return ERR_NONE;
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);
105 int album_entry_serialize(FILE *fd, const struct album_entry *e) {
106 uint32_t length;
108 assert(e != NULL);
109 assert(FLAG_VALID(e->flag));
110 assert(fd != NULL);
112 if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing
113 return ERR_NONE;
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;
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;
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;
134 // the key-field (if present)
135 if( e->key != NULL ) {
136 length = strlen(e->key);
137 } else {
138 length = 0;
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;
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;
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;
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;
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;
171 return ERR_NONE;
174 int album_entry_unserialize(struct album_entry **e, FILE *fd) {
175 uint32_t length;
176 unsigned char flag;
178 assert(e != NULL);
179 assert(fd != NULL);
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;
187 // See what we have:
188 if( ! FLAG_VALID(flag) ) {
189 DEBUGF("album_entry_unserialize: flag-byte is invalid\n");
190 return ERR_INVALID;
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;
200 (*e)->flag = flag; // we had a valid entry, copy it over
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;
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;
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;
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;
229 return ERR_NONE;
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;
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;
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;
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;
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;
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;
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;
283 return ERR_NONE;
286 int album_entry_write(FILE *fd, struct album_entry *e, struct album_size *s) {
287 uint32_t i, be;
288 char pad = 0x00;
290 assert(e != NULL);
291 assert(FLAG_VALID(e->flag));
292 assert(fd != NULL);
294 if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing
295 return ERR_NONE;
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;
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;
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;
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;
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;
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;
350 return 0;
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);
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;
368 s->name_len = 0;
369 s->song_count = 0;
371 return s;
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;
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));
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;
389 int album_size_destruct(struct album_size *s) {
390 assert(s != NULL);
391 // nothing to do...
392 free(s);
393 return ERR_NONE;
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));
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;
405 e->song[e->size.song_count-1] = song;
407 if( s != NULL) album_size_max(s, e); // can't fail
409 return ERR_NONE;
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;
418 assert(fd != NULL);
419 assert(e != NULL);
420 assert(FLAG_VALID(e->flag));
422 // overwrite the beginning of the serialized data:
423 flag = FLAG(1, 0); // set the delete flag, clear the spare flags
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;
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;
438 return ERR_NONE;
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));
446 DEBUGF("album_entry_add_song_file() called\n");
448 if( delete_serialized(fd, e) ) {
449 DEBUGF("album_entry_add_song_file: could not mark as deleted\n");
450 return ERR_FILE;
453 return ERR_NO_INPLACE_UPDATE;