Added note about 2.5 being Archos only
[kugel-rb.git] / apps / tagdb / db.c
blob1c84b2b75c79ddb02366e498c675bf33870eca33
1 #include <string.h> // strlen() strcpy() strcat()
3 #include "malloc.h"
4 #include "db.h"
5 #include "header.h"
7 #include "artist.h"
8 #include "album.h"
9 #include "song.h"
10 #include "file.h"
12 #include "tag_dummy.h"
14 #define CEIL32BIT(x) ( ((x) + 3) & 0xfffffffc )
15 #define CEIL32BIT_LEN(x) CEIL32BIT(strlen(x) + 1) // +1 because we want to store the \0 at least once
17 #define CATCH_MALLOC(condition) \
18 while( condition ) { \
19 int rc_catch_malloc = free_ram(); \
20 if (rc_catch_malloc != ERR_NONE) { \
21 DEBUGF("catch_malloc: " #condition ": could not free memory, failing...\n"); \
22 return rc_catch_malloc; \
23 } \
26 #define CATCH_MALLOC_ERR(expr) CATCH_MALLOC( (expr) == ERR_MALLOC )
27 #define CATCH_MALLOC_NULL(expr) CATCH_MALLOC( (expr) == NULL )
28 // Loop the expression as long as it returns ERR_MALLOC (for CATCH_MALLOC_ERR)
29 // or NULL (for CATCH_MALLOC_NULL)
30 // on each failure, call free_ram() to free some ram. if free_ram() fails, return
31 // the fail-code
32 #define CATCH_ERR(expr) \
33 CATCH_MALLOC_ERR(rc = expr); \
34 if( rc != ERR_NONE ) { \
35 DEBUGF("catch_err: " #expr ": failed\n"); \
36 return rc; \
38 // Catches all errors: if it's a MALLOC one, try to free memory,
39 // if it's another one, return the code
41 static int fill_artist_offsets(struct artist_entry *e, struct artist_size *max_s);
42 static int fill_album_offsets(struct album_entry *e, struct album_size *max_s);
43 static int fill_song_offsets(struct song_entry *e, struct song_size *max_s);
44 static int fill_file_offsets(struct file_entry *e, struct file_size *max_s);
46 static int do_add(const struct tag_info *t);
48 static int tag_empty_get(struct tag_info *t);
49 /* Adds "<no artist tag>" and "<no album tag>" if they're empty
52 static int free_ram();
53 static char in_file = 0;
55 static int do_write(FILE *fd);
57 static struct array_buffer *artists;
58 static struct array_buffer *albums;
59 static struct array_buffer *songs;
60 static struct array_buffer *files;
61 static uint32_t artist_start=0, album_start=0, song_start=0, file_start=0;
62 static uint32_t artist_entry_len, album_entry_len, song_entry_len, file_entry_len;
63 static char *artists_file, *albums_file, *songs_file, *files_file;
65 int db_construct() {
66 void *max_size;
68 // struct array_buffer* new_array_buffer( int (*cmp)(const void *a, const void *b),
69 // int (*serialize)(FILE *fd, const void *e),
70 // int (*unserialize)(void **e, FILE *fd),
71 // uint32_t (*get_length)(const void *size),
72 // int (*write)(FILE *fd, void *e, const void *size),
73 // int (*destruct)(void *e),
74 // char* file_name,
75 // void* max_size,
76 // int (*max_size_update)(void *max_size, const void *e),
77 // int (*max_size_destruct)(void *max_size),
78 // int (*add_item_mem)(void *e, void *s, uint32_t item),
79 // int (*add_item_file)(FILE *fd, void *e, void *s, uint32_t item)
80 // );
82 if(!( max_size = (void*)new_artist_size() )) {
83 DEBUGF("new_db: new_artist_size() failed\n");
84 return ERR_MALLOC;
86 if(!( artists = new_array_buffer( (int (*)(const void *a, const void *b)) artist_entry_compare,
87 (int (*)(FILE *fd, const void *e)) artist_entry_serialize,
88 (int (*)(void **e, FILE *fd)) artist_entry_unserialize,
89 (uint32_t (*)(const void *size)) artist_size_get_length,
90 (int (*)(FILE *fd, void *e, const void *size)) artist_entry_write,
91 (int (*)(void *e)) artist_entry_destruct,
92 NULL, // don't allow to switch to file
93 max_size,
94 (int (*)(void *max_size, const void *e)) artist_size_max,
95 (int (*)(void *max_size)) artist_size_destruct,
96 (int (*)(void *e, void *s, uint32_t item)) artist_entry_add_album_mem,
97 (int (*)(FILE *fd, void *e, void *s, uint32_t item)) artist_entry_add_album_file,
98 (int (*)(void *e, void *s)) fill_artist_offsets
99 ) )) {
100 DEBUGF("new_db: new_array_buffer() failed on artists[]\n");
101 return ERR_MALLOC;
103 if(!( artists_file = malloc(12) )) { // artists.tmp
104 DEBUGF("new_db: could not malloc() for artists[] file_name\n");
105 return ERR_MALLOC;
107 strcpy(artists_file, "artists.tmp");
109 if(!( max_size = (void*)new_album_size() )) {
110 DEBUGF("new_db: new_album_size() failed\n");
111 return ERR_MALLOC;
113 if(!( albums = new_array_buffer( (int (*)(const void *a, const void *b)) album_entry_compare,
114 (int (*)(FILE *fd, const void *e)) album_entry_serialize,
115 (int (*)(void **e, FILE *fd)) album_entry_unserialize,
116 (uint32_t (*)(const void *size)) album_size_get_length,
117 (int (*)(FILE *fd, void *e, const void *size)) album_entry_write,
118 (int (*)(void *e)) album_entry_destruct,
119 NULL, // don't allow to switch to file
120 max_size,
121 (int (*)(void *max_size, const void *e)) album_size_max,
122 (int (*)(void *max_size)) album_size_destruct,
123 (int (*)(void *e, void *s, uint32_t item)) album_entry_add_song_mem,
124 (int (*)(FILE *fd, void *e, void *s, uint32_t item)) album_entry_add_song_file,
125 (int (*)(void *e, void *s)) fill_album_offsets
126 ) )) {
127 DEBUGF("new_db: new_array_buffer() failed on albums[]\n");
128 return ERR_MALLOC;
130 if(!( albums_file = malloc(11) )) { // albums.tmp
131 DEBUGF("new_db: could not malloc() for albums[] file_name\n");
132 return ERR_MALLOC;
134 strcpy(albums_file, "albums.tmp");
136 if(!( max_size = (void*)new_song_size() )) {
137 DEBUGF("new_db: new_song_size() failed\n");
138 return ERR_MALLOC;
140 if(!( songs = new_array_buffer( (int (*)(const void *a, const void *b)) song_entry_compare,
141 (int (*)(FILE *fd, const void *e)) song_entry_serialize,
142 (int (*)(void **e, FILE *fd)) song_entry_unserialize,
143 (uint32_t (*)(const void *size)) song_size_get_length,
144 (int (*)(FILE *fd, void *e, const void *size)) song_entry_write,
145 (int (*)(void *e)) song_entry_destruct,
146 NULL, // may switch to file, but we'd like to know about it
147 max_size,
148 (int (*)(void *max_size, const void *e)) song_size_max,
149 (int (*)(void *max_size)) song_size_destruct,
150 NULL,
151 NULL,
152 (int (*)(void *e, void *s)) fill_song_offsets
153 ) )) {
154 DEBUGF("new_db: new_array_buffer() failed on songs[]\n");
155 return ERR_MALLOC;
157 if(!( songs_file = malloc(10) )) { // songs.tmp
158 DEBUGF("new_db: could not malloc() for songs[] file_name\n");
159 return ERR_MALLOC;
161 strcpy(songs_file, "songs.tmp");
163 if(!( max_size = (void*)new_file_size() )) {
164 DEBUGF("new_db: new_file_size() failed\n");
165 return ERR_MALLOC;
167 if(!( files = new_array_buffer( (int (*)(const void *a, const void *b)) file_entry_compare,
168 (int (*)(FILE *fd, const void *e)) file_entry_serialize,
169 (int (*)(void **e, FILE *fd)) file_entry_unserialize,
170 (uint32_t (*)(const void *size)) file_size_get_length,
171 (int (*)(FILE *fd, void *e, const void *size)) file_entry_write,
172 (int (*)(void *e)) file_entry_destruct,
173 NULL,
174 max_size,
175 (int (*)(void *max_size, const void *e)) file_size_max,
176 (int (*)(void *max_size)) file_size_destruct,
177 NULL,
178 NULL,
179 (int (*)(void *e, void *s)) fill_file_offsets
180 ) )) {
181 DEBUGF("new_db: new_array_buffer() failed on files[]\n");
182 return ERR_MALLOC;
184 if(!( files_file = malloc(10) )) { // files.tmp
185 DEBUGF("new_db: could not malloc() for files[] file_name\n");
186 return ERR_MALLOC;
188 strcpy(files_file, "files.tmp");
190 return ERR_NONE;
193 int db_destruct() {
194 int rc;
196 CATCH_ERR( array_buffer_destruct(artists, 1) );
197 artists = NULL;
198 free(artists_file);
199 artists_file = NULL;
201 CATCH_ERR( array_buffer_destruct(albums, 1) );
202 albums = NULL;
203 free(albums_file);
204 albums_file = NULL;
206 CATCH_ERR( array_buffer_destruct(songs, 1) );
207 songs = NULL;
208 free(songs_file);
209 songs_file = NULL;
211 CATCH_ERR( array_buffer_destruct(files, 1) );
212 files = NULL;
213 free(files_file);
214 files_file = NULL;
216 return ERR_NONE;
219 static int do_add(const struct tag_info *t) {
220 struct artist_entry *artist; uint32_t artistn;
221 struct album_entry *album; uint32_t albumn;
222 struct song_entry *song; uint32_t songn;
223 struct file_entry *file; uint32_t filen;
224 int rc;
226 // create file
227 CATCH_MALLOC_NULL( file = new_file_entry( CEIL32BIT( strlen(t->directory) + 1 + strlen(t->filename) + 1 ) ) ); // "dir"."/"."file"."\0"
229 // fill in file
230 strcpy(file->name, t->directory);
231 strcat(file->name, "/");
232 strcat(file->name, t->filename);
233 file->hash = 0xffffffff; // TODO
234 file->song = songn = array_buffer_get_next_index(songs);
235 file->rundb = 0xffffffff; // TODO
237 // add
238 CATCH_ERR( array_buffer_add(files, file, &filen) );
240 // create artist
241 CATCH_MALLOC_NULL( artist = new_artist_entry( CEIL32BIT_LEN(t->artist), 0) );
242 // fill in
243 strcpy(artist->name, t->artist);
244 // see if it is already in
245 CATCH_MALLOC_ERR( rc = array_buffer_find_entry(artists, artist, &artistn) );
246 if( rc == ERR_NONE ) { // found it
247 // remove our self-made one
248 artist_entry_destruct(artist);
249 artist = NULL;
250 } else if( rc == ERR_NOTFOUND ) { // didn't find it
251 // fill in the rest and add
252 CATCH_ERR( artist_entry_resize(artist, artist->size.name_len, 1) );
253 artist->album[0] = albumn = array_buffer_get_next_index(albums); // if artist isn't in, album will not be in either
254 CATCH_ERR( array_buffer_add(artists, artist, &artistn) );
255 // leave artist != NULL, to indicate that we made a new one
256 } else { //error
257 DEBUGF("do_add: could not search for artist in artists[]\n");
258 return rc;
262 // create album
263 CATCH_MALLOC_NULL( album = new_album_entry(0,0) );
264 // malloc for key
265 CATCH_MALLOC_NULL( album->key = malloc( strlen(t->album) + 3 + strlen(t->artist) + 3 + strlen(t->directory) + 1 ) );
266 // fill in
267 strcpy(album->key, t->album);
268 strcat(album->key, "___");
269 strcat(album->key, t->artist);
270 strcat(album->key, "___");
271 strcat(album->key, t->directory);
272 // see if it is already in
273 CATCH_MALLOC_ERR( rc = array_buffer_find_entry(albums, album, &albumn) );
274 if( rc == ERR_NONE ) { // found it
275 assert(artist == NULL); // make sure artist was found; else we have trouble!
276 // Remove our search-album and add the song to the already existing one
277 album_entry_destruct(album);
278 album = NULL;
279 CATCH_ERR( array_buffer_entry_update(albums, albumn, songn) );
280 } else if( rc == ERR_NOTFOUND ) { // didn't find it
281 // fill in the rest of the info in this album and add it
282 CATCH_ERR( album_entry_resize(album, CEIL32BIT_LEN(t->album), 1 ) );
283 strcpy(album->name, t->album);
284 album->artist = artistn;
285 album->song[0] = songn;
286 CATCH_ERR( array_buffer_add(albums, album, &albumn) );
287 } else { // error
288 DEBUGF("do_add: could not search for album in albums[]\n");
289 return rc;
293 if( album != NULL && artist == NULL ) {
294 // we have a new album from an already existing artist
295 // add it!
296 CATCH_ERR( array_buffer_entry_update(artists, artistn, albumn) );
300 // song
301 CATCH_MALLOC_NULL( song = new_song_entry( CEIL32BIT_LEN(t->song), CEIL32BIT_LEN(t->genre)) );
302 // fill in
303 strcpy(song->name, t->song);
304 song->artist = artistn;
305 song->album = albumn;
306 song->file = filen;
307 strcpy(song->genre, t->genre);
308 song->bitrate = t->bitrate;
309 song->year = t->year;
310 song->playtime = t->playtime;
311 song->track = t->track;
312 song->samplerate = t->samplerate;
313 // add
314 CATCH_ERR( array_buffer_add(songs, song, NULL) );
316 return ERR_NONE;
319 static int tag_empty_get(struct tag_info *t) {
320 assert( t != NULL );
322 if( t->song == NULL ) {
323 CATCH_MALLOC_NULL( t->song = (char*)malloc(14) );
324 strcpy(t->song, "<no song tag>");
326 if( t->genre == NULL ) {
327 CATCH_MALLOC_NULL( t->genre = (char*)malloc(15) );
328 strcpy(t->genre, "<no genre tag>");
330 if( t->artist == NULL ) {
331 CATCH_MALLOC_NULL( t->artist = (char*)malloc(16) );
332 strcpy(t->artist, "<no artist tag>");
334 if( t->album == NULL ) {
335 CATCH_MALLOC_NULL( t->album = (char*)malloc(15) );
336 strcpy(t->album, "<no album tag>");
339 return ERR_NONE;
342 int db_add(char* file_path, const char* strip_path, const char* add_path) {
343 char *basename, *dir;
344 struct tag_info *t;
345 int rc;
347 assert(file_path != NULL);
349 // Create a new tag_info structure
350 CATCH_MALLOC_NULL( t = new_tag_info() );
352 // fill in the file_name
353 basename = strrchr(file_path, '/'); // TODO: add \ for windows
354 if( basename == NULL ) {
355 basename = file_path; // no / in the path, so it's only a filename
356 dir = NULL;
357 } else {
358 dir = file_path;
359 basename[0] = '\0'; // set the / to \0 to split the string
360 basename++; // skip past the /
362 CATCH_MALLOC_NULL( t->filename = malloc(strlen(basename)+1) ); // +1 for the '\0' termination
363 strcpy(t->filename, basename);
365 // convert the path
366 if( strip_path != NULL && strlen(strip_path) > 0) {
367 if( dir == NULL || strncmp(file_path, strip_path, strlen(strip_path)) ) {
368 printf("db_add: could not strip path from \"%s\"\n", file_path);
369 } else {
370 dir += strlen(strip_path); // skip the path to strip
373 if( add_path != NULL ) {
374 CATCH_MALLOC_NULL( t->directory = malloc( strlen(add_path) + strlen(dir) + 1 ) ); // +1 for '\0' termination
375 strcpy(t->directory, add_path);
376 strcat(t->directory, dir);
377 } else {
378 CATCH_MALLOC_NULL( t->directory = malloc( strlen(dir) + 1 ) );
379 strcpy(t->directory, dir);
382 // restore the file_path to it's original state
383 if( dir != NULL) *(basename-1) = '/';
385 // So far we have:
386 // filename
387 // directory
388 // try to get the rest from tag-information:
389 //tag_id3v2_get(file_path, t);
390 //tag_id3v1_get(file_path, t);
391 tag_dummy(file_path, t);
393 // If it is still empty here, skip this file
394 if( t->artist==NULL && t->song==NULL && t->album==NULL && t->genre==NULL) {
395 tag_info_destruct(t); // we won't need it anymore
396 return ERR_NONE;
399 // fill in empty tags with "<no ... tag>"
400 CATCH_ERR( tag_empty_get(t) );
402 // all filled in, now add it
403 CATCH_ERR( do_add(t) );
405 tag_info_destruct(t); // we won't need it anymore
407 return ERR_NONE;
410 static int free_ram() {
411 // put things in file that we won't need to search a lot:
412 // files[] and songs[] are write only
413 // artists[] and albums[] should stay in memory as long as possible
414 // albums[] is updated for every song;
415 // artists[] for every album: artists[] will be the first to loose ram...
416 if(!( in_file & 0x01 )) { // files[] is still in ram
417 in_file |= 0x01;
418 // switch files[] to file-mode
419 files->file_name = files_file;
420 files_file = NULL; // since array_buffer will clean this up
421 return array_buffer_switch_to_file(files);
422 } else if(!( in_file & 0x02 )) { // song[] is still in ram
423 in_file |= 0x02;
424 // switch songs[] to file-mode
425 songs->file_name = songs_file;
426 songs_file = NULL; // since array_buffer will clean this up
427 return array_buffer_switch_to_file(songs);
428 } else if(!( in_file & 0x04 )) { // artists[] is still in ram
429 in_file |= 0x04;
430 // switch artists[] to file-mode
431 artists->file_name = artists_file;
432 artists_file = NULL; // since array_buffer will clean this up
433 return array_buffer_switch_to_file(artists);
434 } else if(!( in_file & 0x08 )) { // albums[] is still in ram
435 in_file |= 0x08;
436 // switch albums[] to file-mode
437 albums->file_name = albums_file;
438 albums_file = NULL; // since array_buffer will clean this up
439 return array_buffer_switch_to_file(albums);
440 } else {
441 // all is already in file mode, sorry...
442 DEBUGF("free_ram: everything is already in file-mode, cannot free more ram, sorry...\n");
443 return ERR_MALLOC;
447 static int fill_artist_offsets(struct artist_entry *e, struct artist_size *max_s) {
448 uint32_t i;
450 assert(e != NULL);
451 assert(album_start != 0);
453 for(i=0; i<e->size.album_count; i++) {
454 e->album[i] = album_start + e->album[i] * album_entry_len;
456 return ERR_NONE;
459 static int fill_album_offsets(struct album_entry *e, struct album_size *max_s) {
460 uint32_t i;
462 assert(e != NULL);
463 assert(song_start != 0);
465 e->artist = artist_start + e->artist * artist_entry_len;
466 for(i=0; i<e->size.song_count; i++) {
467 e->song[i] = song_start + e->song[i] * song_entry_len;
469 return ERR_NONE;
472 static int fill_song_offsets(struct song_entry *e, struct song_size *max_s) {
474 assert(e != NULL);
475 assert(artist_start != 0);
476 assert(album_start != 0);
477 assert(file_start != 0);
479 e->artist = artist_start + e->artist * artist_entry_len;
480 e->album = album_start + e->album * album_entry_len;
481 e->file = file_start + e->file * file_entry_len;
482 return ERR_NONE;
485 static int fill_file_offsets(struct file_entry *e, struct file_size *max_s) {
487 assert(e != NULL);
488 assert(song_start != 0);
490 e->song = song_start + e->song * song_entry_len;
491 return ERR_NONE;
494 static int do_write(FILE *fd) {
495 int rc;
496 struct header h;
498 assert(fd != NULL);
500 // make a header
501 h.magic[0] = 'R'; h.magic[1] = 'D'; h.magic[2] = 'B';
502 h.version = 0x03;
504 h.artist_start = artist_start = HEADER_SIZE;
505 h.album_start = album_start = h.artist_start + array_buffer_get_length(artists); // TODO error check
506 h.song_start = song_start = h.album_start + array_buffer_get_length(albums);
507 h.file_start = file_start = h.song_start + array_buffer_get_length(songs);
509 h.artist_count = artists->count;
510 h.album_count = albums->count;
511 h.song_count = songs->count;
512 h.file_count = files->count;
514 h.artist_len = ((struct artist_size*)artists->max_size)->name_len;
515 h.album_len = ((struct album_size*)albums->max_size)->name_len;
516 h.song_len = ((struct song_size*)songs->max_size)->name_len;
517 h.genre_len = ((struct song_size*)songs->max_size)->genre_len;
518 h.file_len = ((struct file_size*)files->max_size)->name_len;
520 artist_entry_len = artist_size_get_length(artists->max_size); // TODO error check
521 album_entry_len = album_size_get_length(albums->max_size);
522 song_entry_len = song_size_get_length(songs->max_size);
523 file_entry_len = file_size_get_length(files->max_size);
525 h.song_array_count = ((struct album_size*)albums->max_size)->song_count;
526 h.album_array_count = ((struct artist_size*)artists->max_size)->album_count;
528 h.flags.reserved = 0;
529 h.flags.rundb_dirty = 1;
531 // write the header
532 CATCH_ERR( header_write(fd, &h) );
534 // write the arrays
535 CATCH_ERR( array_buffer_write(fd, artists) );
536 CATCH_ERR( array_buffer_write(fd, albums) );
537 CATCH_ERR( array_buffer_write(fd, songs) );
538 CATCH_ERR( array_buffer_write(fd, files) );
540 return ERR_NONE;
543 int db_write(FILE *fd) {
544 int rc;
545 // sort everything
546 CATCH_ERR( array_buffer_sort(artists) );
547 CATCH_ERR( array_buffer_sort(albums) );
548 CATCH_ERR( array_buffer_sort(songs) );
549 CATCH_ERR( array_buffer_sort(files) );
551 CATCH_ERR( do_write(fd) );
553 return ERR_NONE;
556 struct tag_info* new_tag_info() {
557 struct tag_info *t;
558 t = malloc(sizeof(struct tag_info));
559 if( t == NULL ) {
560 DEBUGF("new_tag_info: could not malloc() for tag_info\n");
561 return NULL;
564 t->directory = NULL;
565 t->filename = NULL;
566 t->song = NULL;
567 t->artist = NULL;
568 t->album = NULL;
569 t->genre = NULL;
570 t->bitrate = 0;
571 t->year = 0;
572 t->playtime = 0;
573 t->track = 0;
574 t->samplerate = 0;
576 return t;
579 int tag_info_destruct(struct tag_info *t) {
580 assert(t != NULL);
582 free(t->directory);
583 t->directory = NULL;
584 free(t->filename);
585 t->filename = NULL;
586 free(t->song);
587 t->song = NULL;
588 free(t->artist);
589 t->artist = NULL;
590 free(t->album);
591 t->album = NULL;
592 free(t->genre);
593 t->genre = NULL;
594 t->bitrate = 0;
595 t->year = 0;
596 t->playtime = 0;
597 t->track = 0;
598 t->samplerate = 0;
600 free(t);
602 return ERR_NONE;