1 #include <string.h> // strlen() strcpy() strcat()
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; \
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
32 #define CATCH_ERR(expr) \
33 CATCH_MALLOC_ERR(rc = expr); \
34 if( rc != ERR_NONE ) { \
35 DEBUGF("catch_err: " #expr ": failed\n"); \
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
;
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),
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)
82 if(!( max_size
= (void*)new_artist_size() )) {
83 DEBUGF("new_db: new_artist_size() failed\n");
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
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
100 DEBUGF("new_db: new_array_buffer() failed on artists[]\n");
103 if(!( artists_file
= malloc(12) )) { // artists.tmp
104 DEBUGF("new_db: could not malloc() for artists[] file_name\n");
107 strcpy(artists_file
, "artists.tmp");
109 if(!( max_size
= (void*)new_album_size() )) {
110 DEBUGF("new_db: new_album_size() failed\n");
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
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
127 DEBUGF("new_db: new_array_buffer() failed on albums[]\n");
130 if(!( albums_file
= malloc(11) )) { // albums.tmp
131 DEBUGF("new_db: could not malloc() for albums[] file_name\n");
134 strcpy(albums_file
, "albums.tmp");
136 if(!( max_size
= (void*)new_song_size() )) {
137 DEBUGF("new_db: new_song_size() failed\n");
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
148 (int (*)(void *max_size
, const void *e
)) song_size_max
,
149 (int (*)(void *max_size
)) song_size_destruct
,
152 (int (*)(void *e
, void *s
)) fill_song_offsets
154 DEBUGF("new_db: new_array_buffer() failed on songs[]\n");
157 if(!( songs_file
= malloc(10) )) { // songs.tmp
158 DEBUGF("new_db: could not malloc() for songs[] file_name\n");
161 strcpy(songs_file
, "songs.tmp");
163 if(!( max_size
= (void*)new_file_size() )) {
164 DEBUGF("new_db: new_file_size() failed\n");
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
,
175 (int (*)(void *max_size
, const void *e
)) file_size_max
,
176 (int (*)(void *max_size
)) file_size_destruct
,
179 (int (*)(void *e
, void *s
)) fill_file_offsets
181 DEBUGF("new_db: new_array_buffer() failed on files[]\n");
184 if(!( files_file
= malloc(10) )) { // files.tmp
185 DEBUGF("new_db: could not malloc() for files[] file_name\n");
188 strcpy(files_file
, "files.tmp");
196 CATCH_ERR( array_buffer_destruct(artists
, 1) );
201 CATCH_ERR( array_buffer_destruct(albums
, 1) );
206 CATCH_ERR( array_buffer_destruct(songs
, 1) );
211 CATCH_ERR( array_buffer_destruct(files
, 1) );
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
;
227 CATCH_MALLOC_NULL( file
= new_file_entry( CEIL32BIT( strlen(t
->directory
) + 1 + strlen(t
->filename
) + 1 ) ) ); // "dir"."/"."file"."\0"
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
238 CATCH_ERR( array_buffer_add(files
, file
, &filen
) );
241 CATCH_MALLOC_NULL( artist
= new_artist_entry( CEIL32BIT_LEN(t
->artist
), 0) );
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
);
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
257 DEBUGF("do_add: could not search for artist in artists[]\n");
263 CATCH_MALLOC_NULL( album
= new_album_entry(0,0) );
265 CATCH_MALLOC_NULL( album
->key
= malloc( strlen(t
->album
) + 3 + strlen(t
->artist
) + 3 + strlen(t
->directory
) + 1 ) );
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
);
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
) );
288 DEBUGF("do_add: could not search for album in albums[]\n");
293 if( album
!= NULL
&& artist
== NULL
) {
294 // we have a new album from an already existing artist
296 CATCH_ERR( array_buffer_entry_update(artists
, artistn
, albumn
) );
301 CATCH_MALLOC_NULL( song
= new_song_entry( CEIL32BIT_LEN(t
->song
), CEIL32BIT_LEN(t
->genre
)) );
303 strcpy(song
->name
, t
->song
);
304 song
->artist
= artistn
;
305 song
->album
= albumn
;
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
;
314 CATCH_ERR( array_buffer_add(songs
, song
, NULL
) );
319 static int tag_empty_get(struct tag_info
*t
) {
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>");
342 int db_add(char* file_path
, const char* strip_path
, const char* add_path
) {
343 char *basename
, *dir
;
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
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
);
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
);
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
);
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) = '/';
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
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
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
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
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
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
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
);
441 // all is already in file mode, sorry...
442 DEBUGF("free_ram: everything is already in file-mode, cannot free more ram, sorry...\n");
447 static int fill_artist_offsets(struct artist_entry
*e
, struct artist_size
*max_s
) {
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
;
459 static int fill_album_offsets(struct album_entry
*e
, struct album_size
*max_s
) {
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
;
472 static int fill_song_offsets(struct song_entry
*e
, struct song_size
*max_s
) {
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
;
485 static int fill_file_offsets(struct file_entry
*e
, struct file_size
*max_s
) {
488 assert(song_start
!= 0);
490 e
->song
= song_start
+ e
->song
* song_entry_len
;
494 static int do_write(FILE *fd
) {
501 h
.magic
[0] = 'R'; h
.magic
[1] = 'D'; h
.magic
[2] = 'B';
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;
532 CATCH_ERR( header_write(fd
, &h
) );
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
) );
543 int db_write(FILE *fd
) {
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
) );
556 struct tag_info
* new_tag_info() {
558 t
= malloc(sizeof(struct tag_info
));
560 DEBUGF("new_tag_info: could not malloc() for tag_info\n");
579 int tag_info_destruct(struct tag_info
*t
) {