summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorNiels Laukens <niobos@rockbox.org>2005-07-06 11:03:20 +0000
committerNiels Laukens <niobos@rockbox.org>2005-07-06 11:03:20 +0000
commitd1c294c17de95615b7af428da938b686830b42df (patch)
tree950080f5b6c9503c090df6e4f0929f13eae8891e /apps
parent5e9f52f6d1f3356bc6df75a675e1a2d5cdbf9d77 (diff)
downloadrockbox-d1c294c17de95615b7af428da938b686830b42df.tar.gz
rockbox-d1c294c17de95615b7af428da938b686830b42df.zip
Initial import of tagdb
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7039 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/tagdb/Makefile32
-rw-r--r--apps/tagdb/README9
-rw-r--r--apps/tagdb/album.c454
-rw-r--r--apps/tagdb/album.h103
-rw-r--r--apps/tagdb/array_buffer.c667
-rw-r--r--apps/tagdb/array_buffer.h159
-rw-r--r--apps/tagdb/artist.c370
-rw-r--r--apps/tagdb/artist.h100
-rw-r--r--apps/tagdb/config.h39
-rw-r--r--apps/tagdb/db.c603
-rw-r--r--apps/tagdb/db.h37
-rw-r--r--apps/tagdb/file.c268
-rw-r--r--apps/tagdb/file.h84
-rw-r--r--apps/tagdb/header.c121
-rw-r--r--apps/tagdb/header.h39
-rw-r--r--apps/tagdb/main.c115
-rw-r--r--apps/tagdb/malloc.c131
-rw-r--r--apps/tagdb/malloc.h16
-rw-r--r--apps/tagdb/parser.c218
-rw-r--r--apps/tagdb/song.c450
-rw-r--r--apps/tagdb/song.h93
-rw-r--r--apps/tagdb/tag_dummy.c11
-rw-r--r--apps/tagdb/tag_dummy.h3
-rw-r--r--apps/tagdb/unique.c16
-rw-r--r--apps/tagdb/unique.h6
25 files changed, 4144 insertions, 0 deletions
diff --git a/apps/tagdb/Makefile b/apps/tagdb/Makefile
new file mode 100644
index 0000000000..7f6ed63e22
--- /dev/null
+++ b/apps/tagdb/Makefile
@@ -0,0 +1,32 @@
1OBJECTS = main.o db.o array_buffer.o unique.o malloc.o \
2 header.o artist.o album.o song.o file.o \
3 tag_dummy.o
4
5all : tagdb parser
6
7tagdb : $(OBJECTS)
8 $(CC) -o tagdb $(OBJECTS)
9
10parser: parser.o malloc.o
11 $(CC) -o parser parser.o malloc.o
12
13main.o : main.c config.h
14
15db.o : db.c db.h config.h
16
17array_buffer.o : array_buffer.c array_buffer.h config.h
18unique.o : unique.c unique.h
19malloc.o : malloc.c malloc.h config.h
20
21header.o : header.c header.h config.h
22artist.o : artist.c artist.h config.h
23album.o : album.c album.h config.h
24song.o : song.c song.h config.h
25file.o : file.c file.h config.h
26
27tag_dummy.o : tag_dummy.c tag_dummy.h config.h
28
29parser.o : parser.c config.h
30
31clean :
32 rm -rf *.o tagdb parser
diff --git a/apps/tagdb/README b/apps/tagdb/README
new file mode 100644
index 0000000000..90bf20a017
--- /dev/null
+++ b/apps/tagdb/README
@@ -0,0 +1,9 @@
1The code is currently a working mess... needs cleanup
2also it should be transformed into rockbox-format (header in each file).
3
4things that work:
5* DB creation
6
7things that don't work (yet)
8* Sorting
9* reading files to parse the tags
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
13static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill);
14
15struct 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
41int 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
54static 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
101inline 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
105int 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
174int 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
286int 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
353inline 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
361struct 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
374inline 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
379inline 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
389int album_size_destruct(struct album_size *s) {
390 assert(s != NULL);
391 // nothing to do...
392 free(s);
393 return ERR_NONE;
394}
395
396int 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
412static 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
441int 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}
diff --git a/apps/tagdb/album.h b/apps/tagdb/album.h
new file mode 100644
index 0000000000..08995a2940
--- /dev/null
+++ b/apps/tagdb/album.h
@@ -0,0 +1,103 @@
1#ifndef __ALBUM_H__
2#define __ALBUM_H__
3
4#include "config.h"
5#include <stdio.h>
6
7struct album_entry {
8 char* name; // album name
9 char* key; // key for sorting/searching: album___artist___directory
10 uint32_t artist; // pointer to artist
11 uint32_t *song; // song-pointers
12 struct album_size {
13 uint32_t name_len; // length of this field (must be mulitple of 4)
14 uint32_t song_count; // number of song pointers
15 } size; // keeps the size of this thing
16 unsigned char flag; // flags
17};
18
19struct album_entry* new_album_entry(const uint32_t name_len, const uint32_t song_count);
20/* Creates a new album_entry with the specified sizes
21 * Returns a pointer to the structure on success,
22 * NULL when malloc() fails
23 */
24
25int album_entry_destruct(struct album_entry *e);
26/* Destructs the given album_entry and free()'s it's memory
27 * returns ERR_NONE on success (can never fail)
28 */
29
30inline int album_entry_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count);
31/* Change the size of the entry
32 * returns ERR_NONE on succes
33 * ERR_MALLOC when malloc() fails
34 */
35
36int album_entry_serialize(FILE *fd, const struct album_entry *e);
37/* Serializes the entry in the file at the current position
38 * returns ERR_NONE on success
39 * ERR_FILE on fwrite() failure
40 */
41
42int album_entry_unserialize(struct album_entry* *e, FILE *fd);
43/* Unserializes an entry from file into a new structure
44 * The address of the structure is saved into *e
45 * returns ERR_NONE on success
46 * ERR_MALLOC on malloc() failure
47 * ERR_FILE on fread() failure
48 */
49
50int album_entry_write(FILE *fd, struct album_entry *e, struct album_size *s);
51/* Writes the entry to file in the final form
52 * returns ERR_NONE on success
53 * ERR_FILE on fwrite() failure
54 * ERR_MALLOC when e could not be resized due to malloc() problems
55 * If s is smaller than e, s is used!!!
56 */
57
58inline int album_entry_compare(const struct album_entry *a, const struct album_entry *b);
59/* Compares 2 entries
60 * When a < b it returns <0
61 * a = b 0
62 * a > b >0
63 */
64
65struct album_size* new_album_size();
66/* Creates a new size structure
67 * returns a pointer to the structure on success,
68 * NULL on malloc() failure
69 */
70
71inline uint32_t album_size_get_length(const struct album_size *size);
72/* Calculates the length of the entry when written by album_entry_write()
73 * returns the length on success (can never fail)
74 */
75
76inline int album_size_max(struct album_size *s, const struct album_entry *e);
77/* Updates the album_size structure to contain the maximal lengths of either
78 * the original entry in s, or the entry e
79 * returns ERR_NONE on success (can never fail)
80 */
81
82int album_size_destruct(struct album_size *s);
83/* destructs the album_size structure
84 * returns ERR_NONE on success (can never fail)
85 */
86
87
88int album_entry_add_song_mem(struct album_entry *e, struct album_size *s, const uint32_t song);
89/* Adds the song to the array
90 * returns ERR_NONE on success
91 * ERR_MALLOC on malloc() failure
92 */
93
94int album_entry_add_song_file(FILE *fd, struct album_entry *e, struct album_size *s, const uint32_t song);
95/* Adds the song to the serialized entry in the file
96 * When this fails, the entry is invalidated and the function returns
97 * ERR_NO_INPLACE_UPDATE
98 * returns ERR_NONE on success
99 * ERR_NO_INPLACE_UPDATE (see above)
100 * ERR_FILE on fwrite() failure
101 */
102
103#endif
diff --git a/apps/tagdb/array_buffer.c b/apps/tagdb/array_buffer.c
new file mode 100644
index 0000000000..24772d6bc9
--- /dev/null
+++ b/apps/tagdb/array_buffer.c
@@ -0,0 +1,667 @@
1#include "malloc.h" // malloc() and free()
2
3#include "array_buffer.h"
4#include "unique.h"
5
6static int add_mem(struct array_buffer *b, void *e);
7static int add_file(struct array_buffer *b, void *e);
8
9static int update_entry_mem(struct array_buffer *b, const uint32_t index, uint32_t item);
10static int update_entry_file(struct array_buffer *b, const uint32_t index, uint32_t item);
11
12static int find_entry_mem(struct array_buffer *b, const void *needle, uint32_t *index);
13static int find_entry_file(struct array_buffer *b, const void *needle, uint32_t *index);
14
15static int sort_mem(struct array_buffer *b);
16static int sort_mem_merge_blocks(uint32_t *dest, uint32_t *s1, uint32_t s1_l, uint32_t *s2, uint32_t s2_l, struct array_buffer *b);
17static int sort_mem_merge(uint32_t *dest, uint32_t *src, struct array_buffer *b, uint32_t blocksize);
18static int sort_file(struct array_buffer *b);
19
20struct array_buffer* new_array_buffer( int (*cmp)(const void *a, const void *b),
21 int (*serialize)(FILE *fd, const void *e),
22 int (*unserialize)(void **e, FILE *fd),
23 uint32_t (*get_length)(const void *size),
24 int (*write)(FILE *fd, void *e, const void *size),
25 int (*destruct)(void *e),
26 char* file_name,
27 void* max_size,
28 int (*max_size_update)(void *max_size, const void *e),
29 int (*max_size_destruct)(void *max_size),
30 int (*add_item_mem)(void *e, void *s, uint32_t item),
31 int (*add_item_file)(FILE *fd, void *e, void *s, uint32_t item),
32 int (*pre_write)(void *e, void *s)
33 ) {
34 struct array_buffer *b;
35 b = (struct array_buffer*)malloc(sizeof(struct array_buffer));
36 if( b == NULL ) {
37 DEBUGF("new_array_buffer: failed to allocate memory\n");
38 return NULL;
39 }
40
41 b->count = 0;
42 b->array = NULL;
43 b->sort = NULL;
44
45 b->file_name = file_name;
46
47 b->fd = NULL;
48
49 b->cmp = cmp;
50 b->serialize = serialize;
51 b->unserialize = unserialize;
52 b->get_length = get_length;
53 b->write = write;
54 b->destruct = destruct;
55
56 b->max_size = max_size;
57 b->max_size_update = max_size_update;
58 b->max_size_destruct = max_size_destruct;
59
60 b->add_item_mem = add_item_mem;
61 b->add_item_file = add_item_file;
62
63 b->pre_write = pre_write;
64
65 return b;
66}
67
68int array_buffer_destruct(struct array_buffer *b, const int free_file_name) {
69 assert(b != NULL);
70
71 if( b->fd == NULL ) {
72 if( b->destruct == NULL ) {
73 DEBUGF("array_buffer_destruct: no destruct() function registered\n");
74 return ERR_MALLOC;
75 }
76 //we have memory to clean up
77 // iterate over all stored objects:
78 for(; b->count > 0; b->count--) {
79 if( b->destruct(b->array[b->count-1].mem) ) {
80 DEBUGF("array_buffer_destruct: failed to destruct item[%u]\n", b->count-1);
81 return ERR_MALLOC;
82 }
83 }
84 }
85 free(b->array);
86
87 if( b->fd != NULL ) {
88 // we have a file to clean up
89 if( fclose(b->fd) != 0 ) {
90 DEBUGF("array_buffer_destruct: fclose() failed\n");
91 return ERR_FILE;
92 }
93 b->fd = NULL;
94
95 // remove that file
96 if( remove(b->file_name) != 0 ) {
97 DEBUGF("array_buffer_destruct: remove() failed\n");
98 return ERR_FILE;
99 }
100 }
101 if( free_file_name ) {
102 free(b->file_name);
103 b->file_name = NULL;
104 }
105
106 free(b->sort);
107 b->sort = NULL;
108
109 // free the max_size
110 if( b->max_size != NULL ) {
111 if( b->max_size_destruct == NULL ) {
112 DEBUGF("array_buffer_destruct: no max_size_destruct() function registered\n");
113 return 1;
114 }
115
116 if( b->max_size_destruct(b->max_size) ) {
117 DEBUGF("array_buffer_destruct: failed to destruct max_size\n");
118 return ERR_MALLOC;
119 }
120 b->max_size = NULL;
121 }
122
123 free(b);
124
125 return ERR_NONE;
126}
127
128int array_buffer_switch_to_file(struct array_buffer *b) {
129 uint32_t i;
130 long offset;
131
132 assert(b != NULL);
133
134 if(b->file_name == NULL) {
135 DEBUGF("array_buffer_switch_to_file: no file_name, failing...\n");
136 return ERR_MALLOC;
137 }
138
139 if( b->fd != NULL ) {
140 DEBUGF("array_buffer_switch_to_file: already in file, failing...\n");
141 return ERR_MALLOC;
142 }
143
144 // function calls exist?
145 if( b->serialize == NULL || b->unserialize == NULL ) {
146 DEBUGF("array_buffer_switch_to_file: serialize() and/or unserialize() function(s) not registered\n");
147 return ERR_INVALID;
148 }
149
150 // since we got here, we are VERY short on memory
151 // We cannot do any memory allocation before free()ing some
152 // The filename is already allocated in the constructor
153
154 // open the file
155 b->fd = fopen(b->file_name, "w+");
156 if( b->fd == NULL ) {
157 DEBUGF("array_buffer_switch_to_file: failed to fopen() file\n");
158 return ERR_FILE;
159 }
160
161 for(i=0; i<b->count; i++) {
162 offset = ftell(b->fd);
163 if( offset == -1 ) {
164 DEBUGF("array_buffer_switch_to_file: ftell() failed\n");
165 return ERR_FILE;
166 }
167
168 if( b->serialize(b->fd, b->array[i].mem) ) {
169 DEBUGF("array_buffer_switch_to_file: serialize() failed on item[%u], ignoring...\n", i);
170 }
171 b->destruct(b->array[i].mem);
172
173 b->array[i].file_offset = offset;
174 }
175
176 return ERR_NONE;
177}
178
179static int add_mem(struct array_buffer *b, void *e) {
180 assert(b != NULL);
181 assert(e != NULL);
182
183 // just copy over the pointer
184 b->array[b->count].mem = e;
185
186 return ERR_NONE;
187}
188
189static int add_file(struct array_buffer *b, void *e) {
190 int rc;
191
192 assert(b != NULL);
193 assert(e != NULL);
194
195 if( fseek(b->fd, 0, SEEK_END) != 0 ) {
196 DEBUGF("add_file: could not seek to end of file\n");
197 return ERR_FILE;
198 }
199 if(( b->array[b->count].file_offset = ftell(b->fd) ) == -1) {
200 DEBUGF("add_file: ftell() failed to get file_offset\n");
201 return ERR_FILE;
202 }
203
204 if(( rc = b->serialize(b->fd, e) )) {
205 DEBUGF("add_file: could not serialize entry\n");
206 return rc;
207 }
208 if( b->destruct(e) ) {
209 DEBUGF("add_file: could not destruct entry, ignoring... (memory leak)\n");
210 }
211 return ERR_NONE;
212}
213
214int array_buffer_add(struct array_buffer *b, void *e, uint32_t *index) {
215 void* temp;
216 int rc;
217
218 assert(b != NULL);
219 assert(e != NULL);
220
221 // allow the object to update the max_size
222 // Do this first, so if it fails we can just return without cleanup to do
223 if( b->max_size_update != NULL ) {
224 if(( rc = b->max_size_update(b->max_size, e) )) {
225 DEBUGF("array_buffer_add: could not update max_size, failing...\n");
226 return rc;
227 }
228 }
229
230 // we need to enlarge the array[]
231 temp = (void*)realloc(b->array, sizeof(*b->array)*(b->count+1));
232 while( temp == NULL ) {
233 DEBUGF("array_buffer_add: failed to enlarge index_map[]. Switching to file\n");
234 if(( rc = array_buffer_switch_to_file(b) )) {
235 DEBUGF("array_buffer_add: failed to switch to file, failing...\n");
236 return rc;
237 }
238 // now retry
239 temp = (void*)realloc(b->array, sizeof(*b->array)*(b->count+1));
240 }
241 b->array = (union entry*)temp;
242
243 if( b->fd == NULL ) { // we are in memory
244 rc = add_mem(b, e);
245 if( rc == ERR_MALLOC ) {
246 DEBUGF("array_buffer_add: failed to add in memory due to malloc() trouble, switching to file\n");
247 if(( rc = array_buffer_switch_to_file(b) )) {
248 DEBUGF("array_buffer_add: failed to switch to file, failing...\n");
249 return rc;
250 }
251 // fall out and catch next if
252 }
253 } // NOT else, so we can catch the fall-through
254 if( b->fd != NULL) {
255 if(( rc = add_file(b, e) )) {
256 DEBUGF("array_buffer_add: failed to add in file, failing...\n");
257 return rc;
258 }
259 }
260
261 // count and index-stuff
262 if(index != NULL) *index = b->count;
263 b->count++;
264
265 return ERR_NONE;
266}
267
268inline uint32_t array_buffer_get_next_index(struct array_buffer *b) {
269 assert( b != NULL );
270 return b->count;
271}
272
273static int update_entry_mem(struct array_buffer *b, const uint32_t index, const uint32_t item) {
274 int rc;
275
276 assert(b != NULL);
277 assert(index < b->count);
278
279 if( (rc = b->add_item_mem(b->array[index].mem, b->max_size, item)) ) {
280 DEBUGF("update_entry_mem: failed to update entry\n");
281 return rc;
282 }
283
284 return ERR_NONE;
285}
286
287static int update_entry_file(struct array_buffer *b, const uint32_t index, uint32_t item) {
288/* uint32_t i, index;
289 void *e;
290 int rc;
291 long prev_file_offset;*/
292
293 assert(b != NULL);
294 assert(index < b->count);
295
296 printf("TODO: update entry in file\n");
297
298 return 10; // TODO
299/*
300 rewind(b->fd);
301
302 rc = ERR_NOTFOUND;
303 for(i=0; i<b->count; i++) {
304 prev_file_offset = ftell(b->fd); // keep this file-position
305 if( prev_file_offset == -1 ) {
306 DEBUGF("file_entry_add_file: ftell() failed\n");
307 return ERR_FILE;
308 }
309
310 if( (rc = b->unserialize(&e, b->fd)) ) {
311 DEBUGF("find_entry_add_file: unserialize failed\n");
312 return rc;
313 }
314
315 if( b->cmp(e, needle) == 0 ) { // found
316 if( fseek(b->fd, prev_file_offset, SEEK_SET) ) {
317 DEBUGF("file_entry_add_file: fseek() to entry[%u] failed\n", i);
318 return ERR_FILE;
319 }
320
321 rc = b->add_item_file(b->fd, e, b->max_size, item);
322 if( !( rc == ERR_NONE || rc == ERR_NO_INPLACE_UPDATE )) {
323 DEBUGF("find_entry_add_mem: failed to add item\n");
324 return rc;
325 }
326
327 break; // stop looping
328 }
329
330 b->destruct(e);
331 }
332
333 // seek to the end
334 if( fseek(b->fd, 0, SEEK_END) != 0) {
335 DEBUGF("find_entry_add_file: fseek(SEEK_END) failed\n");
336 return ERR_FILE;
337 }
338
339 // We either succeded, deleted the entry or didn't find it:
340 if( rc == ERR_NOTFOUND ) {
341 return rc; // quit
342 } else if( rc == ERR_NONE ) {
343 b->destruct(e); // delete the entry and quit
344 return rc;
345 }
346
347 // we could not update inplace
348 // the entry is deleted, update it and add it again
349 if( (rc = b->add_item_mem(e, b->max_size, item)) ) {
350 DEBUGF("find_entry_add_file: failed to add item in mem\n");
351 return rc;
352 }
353
354 if( (rc = array_buffer_add(b, e, &index) ) ) {
355 DEBUGF("find_entry_add_file: failed to re-add item to array");
356 return rc;
357 }
358
359 // the entry is now re-added, but with another index number...
360 // change the index_map to reflect this:
361 b->index_map[i] = index;
362
363 return ERR_NONE;*/
364}
365
366int array_buffer_entry_update(struct array_buffer *b, const uint32_t index, uint32_t item) {
367 assert(b != NULL);
368
369 if(index >= b->count) {
370 DEBUGF("array_buffer_entry_update: index out of bounds\n");
371 return ERR_INVALID;
372 }
373
374 if( b->fd == NULL ) {
375 return update_entry_mem(b, index, item);
376 } else {
377 return update_entry_file(b, index, item);
378 }
379}
380
381static int find_entry_mem(struct array_buffer *b, const void *needle, uint32_t *index) {
382 uint32_t i;
383
384 assert(b != NULL);
385 assert(needle != NULL);
386 assert(index != NULL);
387
388 for(i=0; i<b->count; i++) {
389 if( b->cmp(b->array[i].mem, needle) == 0 ) { // found
390 *index = i;
391 return ERR_NONE;
392 }
393 }
394 return ERR_NOTFOUND;
395}
396
397static int find_entry_file(struct array_buffer *b, const void *needle, uint32_t *index) {
398 uint32_t i;
399 void *e;
400 int rc;
401 long prev_file_offset;
402
403 assert(b != NULL);
404 assert(needle != NULL);
405 assert(index != NULL);
406
407 // We do this search in the order of the entries in file.
408 // After we found one, we look for the index of that offset
409 // (in memory).
410 // This will (PROBABELY: TODO) be faster than random-access the file
411 rewind(b->fd);
412
413 for(i=0; i<b->count; i++) {
414 prev_file_offset = ftell(b->fd); // keep this file-position
415 if( prev_file_offset == -1 ) {
416 DEBUGF("file_entry_add_file: ftell() failed\n");
417 return ERR_FILE;
418 }
419
420 if( (rc = b->unserialize(&e, b->fd)) ) {
421 DEBUGF("find_entry_add_file: unserialize failed\n");
422 return rc;
423 }
424
425 if( b->cmp(e, needle) == 0 ) { // found
426 if( fseek(b->fd, prev_file_offset, SEEK_SET) ) {
427 DEBUGF("file_entry_add_file: fseek() to entry[%u] failed\n", i);
428 return ERR_FILE;
429 }
430
431 b->destruct(e);
432 break; // out of the for() loop
433 }
434
435 b->destruct(e);
436 }
437
438 if( i == b->count ) {
439 // we didn't find anything
440 return ERR_NOTFOUND;
441 }
442
443 // we found an entry, look for the index number of that offset:
444 for(i=0; i<b->count; i++) {
445 if(prev_file_offset == b->array[i].file_offset) {
446 // found
447 *index = i;
448 return ERR_NONE;
449 }
450 }
451
452 // we should never get here
453 DEBUGF("find_entry_file: found entry in file, but doens't match an index\n");
454 return ERR_INVALID;
455}
456
457int array_buffer_find_entry(struct array_buffer *b, const void *needle, uint32_t *index) {
458 assert(b != NULL);
459 assert(needle != NULL);
460 assert(index != NULL); // TODO: if it is null, do the search but trash the index
461
462 if( b->fd == NULL ) {
463 return find_entry_mem(b, needle, index);
464 } else {
465 return find_entry_file(b, needle, index);
466 }
467}
468
469/*
470static int sort_mem_merge_blocks(uint32_t *dest, const uint32_t *s1, const uint32_t s1_l, const uint32_t *s2, const uint32_t s2_l, struct array_buffer *b) {
471// merges the 2 blocks at s1 (with s1_l items) and s2 (with s2_l items)
472// together in dest
473 uint32_t *s1_max, s2_max;
474
475#define CMP(a, b) b->cmp( b->entry[a].mem, b->entry[b].mem )
476
477 s1_max = s1 + s1_l;
478 s2_max = s2 + s2_l;
479 while( s1 < s1_max || s2 < s2_max ) {
480 while( s1 < s1_max && ( s2 == s2_max || CMP(s1, s2) <= 0 ) ) // s1 is smaller than s2 (or s2 is used up)
481 *(dest++) = s1++; // copy and move to next
482 while( s2 < s2_max && ( s1 == s1_max || CMP(s1, s2) > 0 ) ) // s2 smaller
483 *(dest++) = s2++;
484 }
485
486 return ERR_NONE;
487}
488
489#define MIN(a, b) ( (a) <= (b) ? (a) : (b) )
490static int sort_mem_merge(uint32_t *dest, uint32_t *src, struct array_buffer *b, uint32_t blocksize) {
491// does 1 merge from src[] into dest[]
492// asumes there are sorted blocks in src[] of size blocksize
493 assert( dest != NULL);
494 assert( src != NULL );
495
496 assert( b->count > blocksize );
497
498 // TODO
499}
500*/
501
502static int sort_mem(struct array_buffer *b) {
503 uint32_t *tmp, blocksize;
504
505 assert(b != NULL);
506
507 tmp = (uint32_t*)malloc(sizeof(uint32_t)*b->count);
508 if( tmp == NULL ) {
509 DEBUGF("sort_mem: could not malloc() for second sort[] array\n");
510 return ERR_MALLOC;
511 }
512
513 for( blocksize = 1; blocksize < b->count; blocksize++) {
514 b->sort[blocksize] = blocksize; // 1-1 map TODO
515 }
516
517 free(tmp);
518
519 return ERR_NONE;
520}
521
522static int sort_file(struct array_buffer *b) {
523 printf("TODO: file-sorting\n"); // TODO
524 return ERR_INVALID;
525}
526
527int array_buffer_sort(struct array_buffer *b) {
528 int rc;
529
530 assert(b != NULL);
531
532 b->sort = (uint32_t*)malloc(sizeof(uint32_t)*b->count);
533 if( b->sort == NULL ) {
534 DEBUGF("array_buffer_sort: could not malloc() sort[] array\n");
535 return ERR_MALLOC;
536 }
537
538 if( b->fd == NULL ) { // in memory
539 rc = sort_mem(b);
540 if( rc == ERR_MALLOC ) {
541 if(( rc = array_buffer_switch_to_file(b) )) {
542 DEBUGF("array_buffer_sort: could not switch to file mode\n");
543 return rc;
544 }
545 return sort_file(b);
546 } else if( rc ) {
547 DEBUGF("array_buffer_sort: could not sort array\n");
548 return rc;
549 }
550 return ERR_NONE;
551 } else {
552 return sort_file(b);
553 }
554}
555
556uint32_t array_buffer_get_offset(struct array_buffer *b, const uint32_t index) {
557 uint32_t offset;
558
559 assert(b != NULL);
560
561 if( index >= b->count ) {
562 DEBUGF("array_buffer_get_offset: index out of bounds\n");
563 return (uint32_t)0xffffffff;
564 }
565
566 // what is the (max) length of 1 item
567 if( b->get_length == NULL ) {
568 DEBUGF("array_buffer_get_offset: get_length() function not registered\n");
569 return (uint32_t)0xffffffff;
570 }
571 offset = b->get_length(b->max_size);
572
573 // multiply that by the number of items before me
574 if( b->sort == NULL ) { // easy, we are unsorted
575 offset *= index;
576 } else {
577 uint32_t i;
578 for(i=0; i<b->count; i++) {
579 if( b->sort[i] == index )
580 break;
581 }
582 if( i == b->count ) {
583 DEBUGF("array_buffer_get_offset: index does not appeat in sorted list\n");
584 return ERR_INVALID;
585 }
586 offset *= i; // that many items are before me
587 }
588 return offset;
589}
590
591uint32_t array_buffer_get_length(struct array_buffer *b) {
592 uint32_t length;
593
594 assert(b != NULL);
595
596 // what is the (max) length of 1 item
597 if( b->get_length == NULL ) {
598 DEBUGF("array_buffer_get_offset: get_length() function not registered\n");
599 return (uint32_t)0xffffffff;
600 }
601 length = b->get_length(b->max_size);
602
603 // multiply that by the number of items
604 length *= b->count;
605 return length;
606}
607
608int array_buffer_write(FILE *fd, struct array_buffer *b) {
609 uint32_t i;
610 int rc;
611
612 assert(b != NULL);
613 assert(fd != NULL);
614
615 // check if the functions exist
616 if( b->write == NULL ) {
617 DEBUGF("array_buffer_write: write() function not registered\n");
618 return ERR_INVALID;
619 }
620 // if the array is in file
621 // serialize and unserialize will exist, since they're checked
622 // in the array_buffer_switch_to_file()
623
624 if( b->fd != NULL ) {
625 rewind(b->fd); // seek to the beginning
626 }
627
628 for(i=0; i<b->count; i++) { // for each element
629 void* item;
630 uint32_t j;
631
632 // go through the sort-array and see which item should go next
633 if(b->sort != NULL) {
634 j = b->sort[i];
635 } else j = i;
636
637 // get the item in memory
638 if( b->fd == NULL ) { // it already is im memory, fetch the pointer
639 item = b->array[j].mem;
640 } else {
641 // since it's sorted, we shouldn't have to seek
642 if( (rc = b->unserialize(&item, b->fd)) ) {
643 DEBUGF("array_buffer_write: could not unserialize item[%u], failing...\n", i);
644 return rc;
645 }
646 }
647
648 if(b->pre_write != NULL && ( rc = b->pre_write(item, b->max_size) )) {
649 DEBUGF("array_buffer_write: pre_write function failed, failing...\n");
650 return rc;
651 }
652
653 // write item to file
654 if(( rc = b->write(fd, item, b->max_size) )) {
655 DEBUGF("array_buffer_write: could not write item[%u], failing...\n", i);
656 return rc;
657 }
658
659 // put it back where it came from
660 if( b->fd != NULL ) {
661 b->destruct(item);
662 }
663 }
664
665 return ERR_NONE;
666}
667
diff --git a/apps/tagdb/array_buffer.h b/apps/tagdb/array_buffer.h
new file mode 100644
index 0000000000..6dccefe917
--- /dev/null
+++ b/apps/tagdb/array_buffer.h
@@ -0,0 +1,159 @@
1#ifndef __ARRAY_BUFFER_H__
2#define __ARRAY_BUFFER_H__
3
4#include "config.h"
5#include <stdio.h>
6#include <stdint.h>
7
8struct array_buffer {
9 uint32_t count; // how much items doe we have?
10
11 union entry {
12 void* mem;
13 long file_offset;
14 } *array; // where is the data?
15 // This array will always point to the same data
16 // after sorting the position of the data may be canged
17 // but this array will also be canged accordingly
18
19 uint32_t *sort; // In what order should we put the entries on disk?
20
21 char* file_name; // filename
22 FILE *fd; // file where entries are being kept. (NULL if in mem)
23
24 int (*cmp)(const void *a, const void *b); // compare a to b, should return:
25 // a < b ==> <0
26 // a = b ==> 0
27 // a > b ==> >0
28
29 int (*serialize)(FILE *fd, const void *e); // serialize e into fd
30 int (*unserialize)(void **e, FILE *fd); // unserialize the entry in fd
31
32 uint32_t (*get_length)(const void *size); // get's the length
33 int (*write)(FILE *fd, void *e, const void *size); // write e to file
34
35 int (*destruct)(void *e); // destruct object
36
37 void *max_size; // keep the current maximal size
38 int (*max_size_update)(void *max_size, const void *e); // update the max_size
39 int (*max_size_destruct)(void *max_size); // destruct the size-object
40
41 int (*add_item_mem)(void *e, void *s, uint32_t item);
42 int (*add_item_file)(FILE *fd, void *e, void *s, uint32_t item);
43
44 int (*pre_write)(void *e, void *s); // do whatever you want, just before the entry is wrtiiten
45};
46
47struct array_buffer* new_array_buffer( int (*cmp)(const void *a, const void *b),
48 int (*serialize)(FILE *fd, const void *e),
49 int (*unserialize)(void **e, FILE *fd),
50 uint32_t (*get_length)(const void *size),
51 int (*write)(FILE *fd, void *e, const void *size),
52 int (*destruct)(void *e),
53 char* file_name,
54 void* max_size,
55 int (*max_size_update)(void *max_size, const void *e),
56 int (*max_size_destruct)(void *max_size),
57 int (*add_item_mem)(void *e, void *s, uint32_t item),
58 int (*add_item_file)(FILE *fd, void *e, void *s, uint32_t item),
59 int (*pre_write)(void *e, void *s)
60 );
61/* This makes a new array_buffer
62 * - cmp() is the compare function used to sort: after sort cmp(item[i], item[i+1])<=0
63 * - serialize() should put the entry into the file at the current location, return 0 on success
64 * - unserialize() should read an entry from file and return the entry in memory.
65 * return 0 on success, 1 on malloc() failures, 2 on fread() errors,
66 * anything else on other errors
67 * - get_length() calculates the length of the entry as it will be written by write()
68 * - write() should write the entry to file in it's final format
69 * - destruct() should free all memory assigned to e (including e itself)
70 *
71 * - file_name should contain a filename that can be used as extra storage if needed
72 * if malloc()'s fail, the array is automaticaly converted to file-mode
73 * and array_buffer retries the operation.
74 * by not setting file_name=NULL malloc() failures will result in call
75 * failures
76 *
77 * - max_size may be an object to record the maximal size \
78 * - max_size_update() will be called on each add() to update the max_size-structure | may be NULL
79 * - max_size_destroy() should destroy the given max_size object /
80 *
81 * - add_item_mem() add item to the entry when it is in memory (may be NULL)
82 * - add_item_file() add item to the serialized entry at the current file position.
83 * the entry itself is also given in e for convenience.
84 * If the add cannot be done in-place the function should
85 * - invalidate the serialized entry
86 * - return ERR_NO_INPLACE_UPDATE
87 * The add will be done in memory and re-added to the end of the
88 * array (mey be NULL)
89 * both functions must update the s-structure to reflect the maximal entry
90 *
91 * - pre_write() is called right before the entry is written to disk in the write() call (may be NULL)
92 *
93 * It returns that buffer on succes, NULL otherwise
94 * NULL indicates a memory-allocation failure
95 */
96
97int array_buffer_destruct(struct array_buffer *b, const int free_file_name);
98/* Destructs the buffer:
99 * - destructs all containing elements using the supplied destruct() function
100 * - free()'s all allocations
101 * - optionaly free()'s the file_name
102 * - free()'s b itself
103 */
104
105int array_buffer_switch_to_file(struct array_buffer *b);
106/* Asks the buffer to switch to file mode
107 * returns 0 on success, 1 on failure
108 */
109
110inline uint32_t array_buffer_get_next_index(struct array_buffer *b);
111/* Returns the index that will be given to the next added entry
112 */
113
114int array_buffer_add(struct array_buffer *b, void *e, uint32_t *index);
115/* Adds entry e to the buffer.
116 * If index!=NULL *index will contain a unique number for the entry
117 *
118 * Returns 0 on succes, 1 otherwise
119 * Once an entry is added, the caller should not use the pointer (e) anymore,
120 * since array_buffer may swap the entry out to file
121 */
122
123int array_buffer_entry_update(struct array_buffer *b, const uint32_t index, uint32_t item);
124/* Updates entry index with item, either in memory or in file, depending on the current
125 * state of the array
126 * Returns ERR_NONE on success
127 * ERR_MALLOC on malloc() failure
128 * ERR_FILE on fread(), fwrite(), fseek() problems
129 */
130
131int array_buffer_find_entry(struct array_buffer *b, const void *needle, uint32_t *index);
132/* This looks for an entry that is equal to needle (i.e. that cmp(e, needle) returns 0)
133 * Returns ERR_NONE on success (the entry is found)
134 * ERR_NOTFOUNF when needle was not found,
135 * ERR_MALLOC on malloc() failure
136 * ERR_FILE on fread(), fwrite() of other file() failures
137 */
138
139int array_buffer_sort(struct array_buffer *b);
140/*
141 */
142
143uint32_t array_buffer_get_offset(struct array_buffer *b, const uint32_t index);
144/* Returns the offset of item[index] when it would be written by the
145 * array_buffer_write() call.
146 * Useful to get offsets after sorting!
147 */
148
149uint32_t array_buffer_get_length(struct array_buffer *b);
150/* Returns the total number of bytes array_buffer_write()
151 * would write to the file
152 */
153
154int array_buffer_write(FILE *fd, struct array_buffer *b);
155/* Iterate over each element and write it to file
156 * returns 0 on success, 1 on failure
157 */
158
159#endif
diff --git a/apps/tagdb/artist.c b/apps/tagdb/artist.c
new file mode 100644
index 0000000000..82db81df2f
--- /dev/null
+++ b/apps/tagdb/artist.c
@@ -0,0 +1,370 @@
1#include "malloc.h" // realloc() and free()
2#include <string.h> // strncasecmp()
3
4#include "artist.h"
5
6// how is our flag organized?
7#define FLAG(deleted, spare) ( 0xC0 | (deleted?0x10:0x00) | (spare & 0x0F) )
8#define FLAG_VALID(flag) ((flag & 0xE0) == 0xC0)
9#define FLAG_DELETED(flag) (flag & 0x10)
10#define FLAG_SPARE(flag) (flag & 0x0F)
11
12static int do_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count, const int zero_fill);
13
14struct artist_entry* new_artist_entry(const uint32_t name_len, const uint32_t album_count) {
15 // start by allocating memory
16 struct artist_entry *e = (struct artist_entry*)malloc(sizeof(struct artist_entry));
17 if( e == NULL ) {
18 DEBUGF("new_artist_entry: could not allocate memory\n");
19 return NULL;
20 }
21
22 // We begin empty
23 e->name = NULL;
24 e->size.name_len = 0;
25 e->album = NULL;
26 e->size.album_count = 0;
27 e->flag = FLAG(0, 0);
28
29 // and resize to the requested size
30 if( do_resize(e, name_len, album_count, 1) ) {
31 free(e);
32 return NULL;
33 }
34 return e;
35}
36
37int artist_entry_destruct(struct artist_entry *e) {
38 assert(e != NULL);
39 assert(FLAG_VALID(e->flag));
40
41 free(e->name);
42 free(e->album);
43
44 free(e);
45
46 return ERR_NONE;
47}
48
49static int do_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count, const int zero_fill) {
50 void* temp;
51
52 assert(e != NULL);
53 assert(FLAG_VALID(e->flag));
54
55 // begin with name
56 if( name_len != e->size.name_len ) {
57 temp = realloc(e->name, name_len);
58 if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer
59 DEBUGF("artist_entry_resize: out of memory to resize name\n");
60 return ERR_MALLOC;
61 }
62 e->name = (char*)temp;
63
64 // if asked, fill it with zero's
65 if( zero_fill ) {
66 uint32_t i;
67 for(i=e->size.name_len; i<name_len; i++)
68 e->name[i] = (char)0x00;
69 }
70
71 e->size.name_len = name_len;
72 }
73
74 // now the album
75 if( album_count != e->size.album_count ) {
76 temp = realloc(e->album, album_count * sizeof(*e->album));
77 if(temp == NULL && album_count > 0) { // if realloc(,0) don't complain about NULL-pointer
78 DEBUGF("artist_entry_resize: out of memory to resize album[]\n");
79 return ERR_MALLOC;
80 }
81 e->album = (uint32_t*)temp;
82
83 // if asked, fill it with zero's
84 if( zero_fill ) {
85 uint32_t i;
86 for(i=e->size.album_count; i<album_count; i++)
87 e->album[i] = (uint32_t)0x00000000;
88 }
89
90 e->size.album_count = album_count;
91 }
92
93 return ERR_NONE;
94}
95
96int artist_entry_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count) {
97 return do_resize(e, name_len, album_count, 1);
98}
99
100int artist_entry_serialize(FILE *fd, const struct artist_entry *e) {
101 assert(fd != NULL);
102 assert(e != NULL);
103 assert(FLAG_VALID(e->flag));
104
105 if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing
106 return ERR_NONE;
107 }
108
109 // First byte we write is a flag-byte to indicate this is a valid record
110 if( fwrite(&e->flag, 1, 1, fd) != 1 ) {
111 DEBUGF("artist_entry_serialize: failed to write flag-byte\n");
112 return ERR_FILE;
113 }
114
115 // First we write the length of the name field
116 if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) {
117 DEBUGF("artist_entry_serialize: failed to write name_len\n");
118 return ERR_FILE;
119 }
120
121 // now the name field itself
122 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
123 DEBUGF("artist_entry_serialize: failed to write name\n");
124 return ERR_FILE;
125 }
126
127 // count of albums
128 if( fwrite(&e->size.album_count, sizeof(e->size.album_count), 1, fd) != 1 ) {
129 DEBUGF("artist_entry_serialize: failed to write album_count\n");
130 return ERR_FILE;
131 }
132
133 // album[] itself
134 if( fwrite(e->album, sizeof(*e->album), e->size.album_count, fd) != e->size.album_count ) {
135 DEBUGF("artist_entry_serialize: failed to write albums\n");
136 return ERR_FILE;
137 }
138
139 return ERR_NONE;
140}
141
142int artist_entry_unserialize(struct artist_entry **e, FILE *fd) {
143 uint32_t length;
144 unsigned char flag;
145
146 assert(e != NULL);
147 assert(fd != NULL);
148
149 // First byte we read is flag-byte
150 if( fread(&flag, 1, 1, fd) != 1 ) {
151 DEBUGF("artist_entry_unserialize: failed to read flag-byte\n");
152 return ERR_FILE;
153 }
154
155 // See what we have:
156 if( ! FLAG_VALID(flag) ) {
157 DEBUGF("artist_entry_unserialize: flag-byte not found\n");
158 return ERR_INVALID;
159 }
160
161 // Allocate memory
162 *e = new_artist_entry(0, 0);
163 if( *e == NULL ) {
164 DEBUGF("artist_entry_unserialize: could not create new artist_entry\n");
165 return ERR_MALLOC;
166 }
167
168 (*e)->flag = flag;
169
170 // First we read the length of the name field
171 if( fread(&length, sizeof(length), 1, fd) != 1 ) {
172 DEBUGF("artist_entry_unserialize: failed to read name_len\n");
173 artist_entry_destruct(*e);
174 return ERR_FILE;
175 }
176
177 // allocate memory for the upcomming name-field
178 if( do_resize((*e), length, 0, 0) ) {
179 DEBUGF("artist_entry_unserialize: failed to allocate memory for name\n");
180 artist_entry_destruct(*e);
181 return ERR_MALLOC;
182 }
183
184 // read it in
185 if( fread((*e)->name, 1, (*e)->size.name_len, fd) != (*e)->size.name_len ) {
186 DEBUGF("artist_entry_unserialize: failed to read name\n");
187 artist_entry_destruct(*e);
188 return ERR_FILE;
189 }
190
191 if( FLAG_DELETED(flag) ) {
192 // all there is... free some memory
193 if( do_resize(*e, 0, 0, 0) ) {
194 DEBUGF("artist_entry_unserialize: couldn't free() name\n");
195 return ERR_MALLOC;
196 }
197 return ERR_NONE;
198 }
199
200 // Next the count of albums
201 if( fread(&length, sizeof(length), 1, fd) != 1 ) {
202 DEBUGF("artist_entry_unserialize: failed to read album_count\n");
203 artist_entry_destruct(*e);
204 return ERR_FILE;
205 }
206
207 // allocate memory for the upcomming name-field
208 if( do_resize(*e, (*e)->size.name_len, length, 0) ) {
209 DEBUGF("artist_entry_unserialize: failed to allocate memory for album[]\n");
210 artist_entry_destruct(*e);
211 return ERR_MALLOC;
212 }
213
214 // read it in
215 if( fread((*e)->album, sizeof(*(*e)->album), (*e)->size.album_count, fd) != (*e)->size.album_count ) {
216 DEBUGF("artist_entry_unserialize: failed to read albums\n");
217 artist_entry_destruct(*e);
218 return ERR_FILE;
219 }
220
221 return ERR_NONE;
222}
223
224int artist_entry_write(FILE *fd, const struct artist_entry *e, const struct artist_size *s) {
225 uint32_t i, be;
226 char pad = 0x00;
227
228 assert(fd != NULL);
229 assert(e != NULL);
230 assert(FLAG_VALID(e->flag));
231
232 if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing
233 return ERR_NONE;
234 }
235
236 // artist name
237 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
238 DEBUGF("artist_entry_write: failed to write name\n");
239 return ERR_FILE;
240 }
241 // padd the rest
242 i = e->size.name_len;
243 while( s != NULL && s->name_len > i) {
244 if( fwrite(&pad, 1, 1, fd) == 1 ) {
245 i++;
246 continue;
247 } else {
248 DEBUGF("artist_entry_write: failed to padd name\n");
249 return ERR_FILE;
250 }
251 }
252
253 // album offsets, but in BIG ENDIAN!
254 // so we need to iterate over each item to convert it
255 for(i=0; i<e->size.album_count; i++) {
256 be = BE32(e->album[i]);
257 if( fwrite(&be, sizeof(be), 1, fd) != 1 ) {
258 DEBUGF("artist_entry_write: failed to write album[%d]\n", i);
259 return ERR_FILE;
260 }
261 }
262 // padd the rest
263 be = BE32(0x00000000);
264 for(; s != NULL && i<s->album_count; i++) {
265 if( fwrite(&be, sizeof(be), 1, fd) != 1 ) {
266 DEBUGF("artist_entry_write: failed to padd album[]\n");
267 return ERR_FILE;
268 }
269 }
270
271 return ERR_NONE;
272}
273
274inline int artist_entry_compare(const struct artist_entry *a, const struct artist_entry *b) {
275 assert(a != NULL);
276 assert(b != NULL);
277 if( a->name == NULL || b->name == NULL )
278 return 1; // never match on no-names
279 return strcasecmp(a->name, b->name);
280}
281
282struct artist_size* new_artist_size() {
283 struct artist_size *s;
284 s = (struct artist_size*)malloc(sizeof(struct artist_size));
285 if( s == NULL ) {
286 DEBUGF("new_artist_size: failed to allocate memory\n");
287 return NULL;
288 }
289 s->name_len = 0;
290 s->album_count = 0;
291
292 return s;
293}
294
295inline uint32_t artist_size_get_length(const struct artist_size *size) {
296 assert(size != NULL);
297 return size->name_len + 4*size->album_count;
298}
299
300inline int artist_size_max(struct artist_size *s, const struct artist_entry *e) {
301 s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len );
302 s->album_count = ( s->album_count >= e->size.album_count ? s->album_count : e->size.album_count );
303 return ERR_NONE;
304}
305
306int artist_size_destruct(struct artist_size *s) {
307 assert(s != NULL);
308 // nothing to do...
309 free(s);
310 return ERR_NONE;
311}
312
313int artist_entry_add_album_mem(struct artist_entry *e, struct artist_size *s, const uint32_t album) {
314 assert(e != NULL);
315 assert(FLAG_VALID(e->flag));
316
317 if( do_resize(e, e->size.name_len, e->size.album_count+1, 0) ) {
318 DEBUGF("artist_entry_add_song_mem: failed to resize album[]\n");
319 return ERR_MALLOC;
320 }
321
322 e->album[e->size.album_count-1] = album;
323
324 if(s != NULL) artist_size_max(s, e); // can't fail
325
326 return ERR_NONE;
327}
328
329static int delete_serialized(FILE *fd, struct artist_entry *e) {
330// the entry should be both, in memory and in file at the current location
331// this function will mark the file-entry as deleted
332 uint32_t size;
333 unsigned char flag;
334 // overwrite the beginning of the serialized data:
335 assert(fd != NULL);
336 assert(e != NULL);
337 assert(FLAG_VALID(e->flag));
338
339 flag = FLAG(1, 0); // mark as deleted
340
341 // First byte we write is the flag-byte to indicate this is a deleted
342 if( fwrite(&flag, 1, 1, fd) != 1 ) {
343 DEBUGF("artist_entry_delete_serialized: failed to write flag-byte\n");
344 return ERR_FILE;
345 }
346
347 // Then we write the length of the COMPLETE entry
348 size = artist_size_get_length(&e->size) + 4; // 4 = overhead for the album[]
349 if( fwrite(&size, sizeof(size), 1, fd) != 1 ) {
350 DEBUGF("artist_entry_delete_serialized: failed to write len\n");
351 return ERR_FILE;
352 }
353
354 return ERR_NONE;
355}
356
357int artist_entry_add_album_file(FILE *fd, struct artist_entry *e, struct artist_size *s, const uint32_t album) {
358 assert(fd != NULL);
359 assert(e != NULL);
360 assert(FLAG_VALID(e->flag));
361
362 DEBUGF("artist_entry_add_song_file() called\n");
363
364 if( delete_serialized(fd, e) ) {
365 DEBUGF("artist_entry_add_album_file: could not mark as deleted\n");
366 return ERR_FILE;
367 }
368
369 return ERR_NO_INPLACE_UPDATE;
370}
diff --git a/apps/tagdb/artist.h b/apps/tagdb/artist.h
new file mode 100644
index 0000000000..c741594936
--- /dev/null
+++ b/apps/tagdb/artist.h
@@ -0,0 +1,100 @@
1#ifndef __ARTIST_H__
2#define __ARTIST_H__
3
4#include "config.h"
5#include <stdio.h>
6#include <stdint.h>
7
8struct artist_entry {
9 char* name; // artist name
10 uint32_t *album; // album-pointers
11 struct artist_size {
12 uint32_t name_len; // length of this field (must be mulitple of 4)
13 uint32_t album_count; // number of album pointers
14 } size;
15 unsigned char flag; // flags
16};
17
18struct artist_entry* new_artist_entry(const uint32_t name_len, const uint32_t album_count);
19/* Creates a new artist_entry with the specified sizes
20 * Returns a pointer to the structure on success,
21 * NULL on failure
22*/
23
24int artist_entry_destruct(struct artist_entry *e);
25/* Destructs the given artist_entry and free()'s it's memory
26 * returns ERR_NONE on success (can't fail)
27 */
28
29int artist_entry_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count);
30/* Change the size of the entry
31 * returns ERR_NONE on succes
32 * ERR_MALLOC on malloc() failure
33 */
34
35int artist_entry_serialize(FILE *fd, const struct artist_entry *e);
36/* Serializes the entry in the file at the current position
37 * returns ERR_NONE on success
38 * ERR_FILE on fwrite() failure
39 */
40
41int artist_entry_unserialize(struct artist_entry* *e, FILE *fd);
42/* Unserializes an entry from file into a new structure
43 * The address of the structure is saved into *e
44 * returns ERR_NONE on success
45 * ERR_MALLOC on malloc() failure
46 * ERR_FILE on fread() failure
47 */
48
49int artist_entry_write(FILE *fd, const struct artist_entry *e, const struct artist_size *s);
50/* Writes the entry to file in the final form
51 * returns ERR_NONE on success
52 * ERR_FILE on fwrite() failure
53 */
54
55inline int artist_entry_compare(const struct artist_entry *a, const struct artist_entry *b);
56/* Compares 2 entries
57 * When a < b it returns <0
58 * a = b 0
59 * a > b >0
60 */
61
62struct artist_size* new_artist_size();
63/* Creates a new size structure
64 * returns a pointer to the structure on success,
65 * NULL on failure
66 */
67
68inline uint32_t artist_size_get_length(const struct artist_size *size);
69/* Calculates the length of the entry when written by artist_entry_write()
70 * returns the length on success (can't fail)
71 */
72
73inline int artist_size_max(struct artist_size *s, const struct artist_entry *e);
74/* Updates the artist_size structure to contain the maximal lengths of either
75 * the original entry in s, or the entry e
76 * returns ERR_NONE on success (can't fail)
77 */
78
79int artist_size_destruct(struct artist_size *s);
80/* destructs the artist_size structure
81 * returns ERR_NONE on success (can't fail)
82 */
83
84
85int artist_entry_add_album_mem(struct artist_entry *e, struct artist_size *s, const uint32_t album);
86/* Adds the album to the array
87 * returns ERR_NONE on success
88 * ERR_MALLOC on malloc() failure
89 */
90
91int artist_entry_add_album_file(FILE *fd, struct artist_entry *e, struct artist_size *s, const uint32_t album);
92/* Adds the album to the serialized entry in the file
93 * When this fails, the entry is invalidated and the function returns
94 * ERR_NO_INPLACE_UPDATE
95 * returns ERR_NONE on success
96 * ERR_NO_INPLACE_UPDATE (see above)
97 * ERR_FILE on fread()/fwrite() error
98 */
99
100#endif
diff --git a/apps/tagdb/config.h b/apps/tagdb/config.h
new file mode 100644
index 0000000000..86461349e3
--- /dev/null
+++ b/apps/tagdb/config.h
@@ -0,0 +1,39 @@
1#ifndef __CONFIG_H // Include me only once
2#define __CONFIG_H
3
4// DEBUGF will print in debug mode:
5#ifdef DEBUG
6#define DEBUGF(...) fprintf (stderr, __VA_ARGS__)
7#define DEBUGT(...) fprintf (stdout, __VA_ARGS__)
8#else //DEBUG
9#define DEBUGF(...)
10#endif //DEBUG
11
12
13#define OS_LINUX // architecture: LINUX, ROCKBOX, WINDOWS
14#define ROCKBOX_LITTLE_ENDIAN // we are intel... little-endian
15
16
17#ifdef ROCKBOX_LITTLE_ENDIAN
18#define BE32(_x_) ((( (_x_) & 0xff000000) >> 24) | \
19 (( (_x_) & 0x00ff0000) >> 8) | \
20 (( (_x_) & 0x0000ff00) << 8) | \
21 (( (_x_) & 0x000000ff) << 24))
22#define BE16(_x_) ( (( (_x_) & 0xff00) >> 8) | (( (_x_) & 0x00ff) << 8))
23#else
24#define BE32(_x_) _x_
25#define BE16(_x_) _x_
26#endif
27
28#include <stdint.h>
29
30#define ERR_NONE 0 // no error
31#define ERR_NOTFOUND -1 // entry not found
32#define ERR_MALLOC 1 // memory allocation failed
33#define ERR_FILE 2 // file operation failed
34#define ERR_INVALID 3 // something is invalid
35#define ERR_NO_INPLACE_UPDATE 4 // can't update in this place
36
37#include <assert.h>
38
39#endif
diff --git a/apps/tagdb/db.c b/apps/tagdb/db.c
new file mode 100644
index 0000000000..1c84b2b75c
--- /dev/null
+++ b/apps/tagdb/db.c
@@ -0,0 +1,603 @@
1#include <string.h> // strlen() strcpy() strcat()
2
3#include "malloc.h"
4#include "db.h"
5#include "header.h"
6
7#include "artist.h"
8#include "album.h"
9#include "song.h"
10#include "file.h"
11
12#include "tag_dummy.h"
13
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
16
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 } \
24 }
25
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; \
37 }
38// Catches all errors: if it's a MALLOC one, try to free memory,
39// if it's another one, return the code
40
41static int fill_artist_offsets(struct artist_entry *e, struct artist_size *max_s);
42static int fill_album_offsets(struct album_entry *e, struct album_size *max_s);
43static int fill_song_offsets(struct song_entry *e, struct song_size *max_s);
44static int fill_file_offsets(struct file_entry *e, struct file_size *max_s);
45
46static int do_add(const struct tag_info *t);
47
48static int tag_empty_get(struct tag_info *t);
49/* Adds "<no artist tag>" and "<no album tag>" if they're empty
50 */
51
52static int free_ram();
53static char in_file = 0;
54
55static int do_write(FILE *fd);
56
57static struct array_buffer *artists;
58static struct array_buffer *albums;
59static struct array_buffer *songs;
60static struct array_buffer *files;
61static uint32_t artist_start=0, album_start=0, song_start=0, file_start=0;
62static uint32_t artist_entry_len, album_entry_len, song_entry_len, file_entry_len;
63static char *artists_file, *albums_file, *songs_file, *files_file;
64
65int db_construct() {
66 void *max_size;
67
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 // );
81
82 if(!( max_size = (void*)new_artist_size() )) {
83 DEBUGF("new_db: new_artist_size() failed\n");
84 return ERR_MALLOC;
85 }
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;
102 }
103 if(!( artists_file = malloc(12) )) { // artists.tmp
104 DEBUGF("new_db: could not malloc() for artists[] file_name\n");
105 return ERR_MALLOC;
106 }
107 strcpy(artists_file, "artists.tmp");
108
109 if(!( max_size = (void*)new_album_size() )) {
110 DEBUGF("new_db: new_album_size() failed\n");
111 return ERR_MALLOC;
112 }
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;
129 }
130 if(!( albums_file = malloc(11) )) { // albums.tmp
131 DEBUGF("new_db: could not malloc() for albums[] file_name\n");
132 return ERR_MALLOC;
133 }
134 strcpy(albums_file, "albums.tmp");
135
136 if(!( max_size = (void*)new_song_size() )) {
137 DEBUGF("new_db: new_song_size() failed\n");
138 return ERR_MALLOC;
139 }
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;
156 }
157 if(!( songs_file = malloc(10) )) { // songs.tmp
158 DEBUGF("new_db: could not malloc() for songs[] file_name\n");
159 return ERR_MALLOC;
160 }
161 strcpy(songs_file, "songs.tmp");
162
163 if(!( max_size = (void*)new_file_size() )) {
164 DEBUGF("new_db: new_file_size() failed\n");
165 return ERR_MALLOC;
166 }
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;
183 }
184 if(!( files_file = malloc(10) )) { // files.tmp
185 DEBUGF("new_db: could not malloc() for files[] file_name\n");
186 return ERR_MALLOC;
187 }
188 strcpy(files_file, "files.tmp");
189
190 return ERR_NONE;
191}
192
193int db_destruct() {
194 int rc;
195
196 CATCH_ERR( array_buffer_destruct(artists, 1) );
197 artists = NULL;
198 free(artists_file);
199 artists_file = NULL;
200
201 CATCH_ERR( array_buffer_destruct(albums, 1) );
202 albums = NULL;
203 free(albums_file);
204 albums_file = NULL;
205
206 CATCH_ERR( array_buffer_destruct(songs, 1) );
207 songs = NULL;
208 free(songs_file);
209 songs_file = NULL;
210
211 CATCH_ERR( array_buffer_destruct(files, 1) );
212 files = NULL;
213 free(files_file);
214 files_file = NULL;
215
216 return ERR_NONE;
217}
218
219static 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;
225
226 // create file
227 CATCH_MALLOC_NULL( file = new_file_entry( CEIL32BIT( strlen(t->directory) + 1 + strlen(t->filename) + 1 ) ) ); // "dir"."/"."file"."\0"
228
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
236
237 // add
238 CATCH_ERR( array_buffer_add(files, file, &filen) );
239
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;
259 }
260
261
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;
290 }
291
292
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) );
297 }
298
299
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) );
315
316 return ERR_NONE;
317}
318
319static int tag_empty_get(struct tag_info *t) {
320 assert( t != NULL );
321
322 if( t->song == NULL ) {
323 CATCH_MALLOC_NULL( t->song = (char*)malloc(14) );
324 strcpy(t->song, "<no song tag>");
325 }
326 if( t->genre == NULL ) {
327 CATCH_MALLOC_NULL( t->genre = (char*)malloc(15) );
328 strcpy(t->genre, "<no genre tag>");
329 }
330 if( t->artist == NULL ) {
331 CATCH_MALLOC_NULL( t->artist = (char*)malloc(16) );
332 strcpy(t->artist, "<no artist tag>");
333 }
334 if( t->album == NULL ) {
335 CATCH_MALLOC_NULL( t->album = (char*)malloc(15) );
336 strcpy(t->album, "<no album tag>");
337 }
338
339 return ERR_NONE;
340}
341
342int 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;
346
347 assert(file_path != NULL);
348
349 // Create a new tag_info structure
350 CATCH_MALLOC_NULL( t = new_tag_info() );
351
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 /
361 }
362 CATCH_MALLOC_NULL( t->filename = malloc(strlen(basename)+1) ); // +1 for the '\0' termination
363 strcpy(t->filename, basename);
364
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
371 }
372 }
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);
380 }
381
382 // restore the file_path to it's original state
383 if( dir != NULL) *(basename-1) = '/';
384
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);
392
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;
397 }
398
399 // fill in empty tags with "<no ... tag>"
400 CATCH_ERR( tag_empty_get(t) );
401
402 // all filled in, now add it
403 CATCH_ERR( do_add(t) );
404
405 tag_info_destruct(t); // we won't need it anymore
406
407 return ERR_NONE;
408}
409
410static 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;
444 }
445}
446
447static int fill_artist_offsets(struct artist_entry *e, struct artist_size *max_s) {
448 uint32_t i;
449
450 assert(e != NULL);
451 assert(album_start != 0);
452
453 for(i=0; i<e->size.album_count; i++) {
454 e->album[i] = album_start + e->album[i] * album_entry_len;
455 }
456 return ERR_NONE;
457}
458
459static int fill_album_offsets(struct album_entry *e, struct album_size *max_s) {
460 uint32_t i;
461
462 assert(e != NULL);
463 assert(song_start != 0);
464
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;
468 }
469 return ERR_NONE;
470}
471
472static int fill_song_offsets(struct song_entry *e, struct song_size *max_s) {
473
474 assert(e != NULL);
475 assert(artist_start != 0);
476 assert(album_start != 0);
477 assert(file_start != 0);
478
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;
483}
484
485static int fill_file_offsets(struct file_entry *e, struct file_size *max_s) {
486
487 assert(e != NULL);
488 assert(song_start != 0);
489
490 e->song = song_start + e->song * song_entry_len;
491 return ERR_NONE;
492}
493
494static int do_write(FILE *fd) {
495 int rc;
496 struct header h;
497
498 assert(fd != NULL);
499
500 // make a header
501 h.magic[0] = 'R'; h.magic[1] = 'D'; h.magic[2] = 'B';
502 h.version = 0x03;
503
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);
508
509 h.artist_count = artists->count;
510 h.album_count = albums->count;
511 h.song_count = songs->count;
512 h.file_count = files->count;
513
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;
519
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);
524
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;
527
528 h.flags.reserved = 0;
529 h.flags.rundb_dirty = 1;
530
531 // write the header
532 CATCH_ERR( header_write(fd, &h) );
533
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) );
539
540 return ERR_NONE;
541}
542
543int 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) );
550
551 CATCH_ERR( do_write(fd) );
552
553 return ERR_NONE;
554}
555
556struct 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;
562 }
563
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;
575
576 return t;
577}
578
579int tag_info_destruct(struct tag_info *t) {
580 assert(t != NULL);
581
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;
599
600 free(t);
601
602 return ERR_NONE;
603}
diff --git a/apps/tagdb/db.h b/apps/tagdb/db.h
new file mode 100644
index 0000000000..be29581a09
--- /dev/null
+++ b/apps/tagdb/db.h
@@ -0,0 +1,37 @@
1#ifndef __DB_H__
2#define __DB_H__
3
4#include "config.h"
5#include <stdio.h>
6
7#include "array_buffer.h"
8
9struct tag_info {
10 char* directory;
11 char* filename; // \0 terminated string's
12 char* song;
13 char* artist;
14 char* album;
15 char* genre;
16 uint16_t bitrate;
17 uint16_t year;
18 uint32_t playtime;
19 uint16_t track;
20 uint16_t samplerate;
21};
22
23int db_construct();
24
25int db_destruct();
26
27int db_add(char* file_path, const char* strip_path, const char* add_path);
28
29int db_sort();
30
31int db_write(FILE *fd);
32
33struct tag_info* new_tag_info();
34
35int tag_info_destruct(struct tag_info *t);
36
37#endif
diff --git a/apps/tagdb/file.c b/apps/tagdb/file.c
new file mode 100644
index 0000000000..de641fec38
--- /dev/null
+++ b/apps/tagdb/file.c
@@ -0,0 +1,268 @@
1#include "malloc.h" // realloc() and free()
2#include <string.h> // strncasecmp()
3
4#include "file.h"
5
6// how is our flag organized?
7#define FLAG ( 0xBF )
8#define FLAG_VALID(flag) (flag == 0xBF)
9
10static int do_resize(struct file_entry *e, const uint32_t name_len, const int zero_fill);
11
12struct file_entry* new_file_entry(const uint32_t name_len) {
13 // Start my allocating memory
14 struct file_entry *e = (struct file_entry*)malloc(sizeof(struct file_entry));
15 if( e == NULL ) {
16 DEBUGF("new_file_entry: could not allocate memory\n");
17 return NULL;
18 }
19
20 // We begin empty
21 e->name = NULL;
22 e->size.name_len = 0;
23
24 e->hash = 0;
25 e->song = 0;
26 e->rundb = 0;
27
28 e->flag = FLAG;
29
30 // and resize to the requested size
31 if( do_resize(e, name_len, 1) ) {
32 free(e);
33 return NULL;
34 }
35
36 return e;
37}
38
39int file_entry_destruct(struct file_entry *e) {
40 assert(e != NULL);
41 assert(FLAG_VALID(e->flag));
42
43 free(e->name);
44
45 free(e);
46
47 return ERR_NONE;
48}
49
50static int do_resize(struct file_entry *e, const uint32_t name_len, const int zero_fill) {
51 void* temp;
52
53 assert(e != NULL);
54 assert(FLAG_VALID(e->flag));
55
56 if( name_len != e->size.name_len ) {
57 temp = realloc(e->name, name_len);
58 if(temp == NULL) {
59 DEBUGF("file_entry_resize: out of memory to resize name\n");
60 return ERR_MALLOC;
61 }
62 e->name = (char*)temp;
63
64 // if asked, fill it with zero's
65 if( zero_fill ) {
66 uint32_t i;
67 for(i=e->size.name_len; i<name_len; i++)
68 e->name[i] = (char)0x00;
69 }
70
71 e->size.name_len = name_len;
72 }
73
74 return ERR_NONE;
75}
76
77inline int file_entry_resize(struct file_entry *e, const uint32_t name_len) {
78 return do_resize(e, name_len, 1);
79}
80
81int file_entry_serialize(FILE *fd, const struct file_entry *e) {
82 assert(fd != NULL);
83 assert(e != NULL);
84 assert(FLAG_VALID(e->flag));
85
86 // First byte we write is a flag-byte to indicate this is a valid record
87 if( fwrite(&e->flag, 1, 1, fd) != 1 ) {
88 DEBUGF("file_entry_serialize: failed to write flag-byte\n");
89 return ERR_FILE;
90 }
91
92 // First we write the length of the name field
93 if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) {
94 DEBUGF("file_entry_serialize: failed to write name_len\n");
95 return ERR_FILE;
96 }
97 // now the name field itself
98 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
99 DEBUGF("file_entry_serialize: failed to write name\n");
100 return ERR_FILE;
101 }
102
103 // hash field
104 if( fwrite(&e->hash, sizeof(e->hash), 1, fd) != 1 ) {
105 DEBUGF("file_entry_serialize: failed to write hash\n");
106 return ERR_FILE;
107 }
108
109 // song field
110 if( fwrite(&e->song, sizeof(e->song), 1, fd) != 1 ) {
111 DEBUGF("file_entry_serialize: failed to write song\n");
112 return ERR_FILE;
113 }
114
115 // rundb field
116 if( fwrite(&e->rundb, sizeof(e->rundb), 1, fd) != 1 ) {
117 DEBUGF("file_entry_serialize: failed to write rundb\n");
118 return ERR_FILE;
119 }
120
121 return ERR_NONE;
122}
123
124int file_entry_unserialize(struct file_entry **dest, FILE *fd) {
125 uint32_t length;
126 struct file_entry *e;
127
128 assert(dest != NULL);
129 assert(fd != NULL);
130
131 // Allocate memory
132 e = new_file_entry(0);
133 if( e == NULL ) {
134 DEBUGF("file_entry_unserialize: could not create new file_entry\n");
135 return ERR_MALLOC;
136 }
137
138 // First we read the length of the name field
139 if( fread(&length, sizeof(length), 1, fd) != 1 ) {
140 DEBUGF("file_entry_unserialize: failed to read name_len\n");
141 file_entry_destruct(e);
142 return ERR_FILE;
143 }
144
145 // allocate memory for the upcomming name-field
146 if( do_resize(e, length, 0) ) {
147 DEBUGF("file_entry_unserialize: failed to allocate memory for name\n");
148 file_entry_destruct(e);
149 return ERR_MALLOC;
150 }
151
152 // read it in
153 if( fread(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
154 DEBUGF("file_entry_unserialize: failed to read name\n");
155 file_entry_destruct(e);
156 return ERR_FILE;
157 }
158
159 // hash field
160 if( fread(&e->hash, sizeof(e->hash), 1, fd) != 1 ) {
161 DEBUGF("file_entry_unserialize: failed to read hash\n");
162 file_entry_destruct(e);
163 return ERR_FILE;
164 }
165
166 // song field
167 if( fread(&e->song, sizeof(e->song), 1, fd) != 1 ) {
168 DEBUGF("file_entry_unserialize: failed to read song\n");
169 file_entry_destruct(e);
170 return ERR_FILE;
171 }
172
173 // rundb field
174 if( fread(&e->rundb, sizeof(e->rundb), 1, fd) != 1 ) {
175 DEBUGF("file_entry_unserialize: failed to read rundb\n");
176 file_entry_destruct(e);
177 return ERR_FILE;
178 }
179
180 *dest = e;
181 return ERR_NONE;
182}
183
184int file_entry_write(FILE *fd, struct file_entry *e, struct file_size *s) {
185 uint32_t be32;
186 char pad = 0x00;
187
188 assert(fd != NULL);
189 assert(e != NULL);
190 assert(FLAG_VALID(e->flag));
191
192 // file name
193 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
194 DEBUGF("file_entry_write: failed to write name\n");
195 return ERR_FILE;
196 }
197 // pad the rest
198 be32 = e->size.name_len; // abuse be32 as counter
199 while( s != NULL && s->name_len > be32) {
200 if( fwrite(&pad, 1, 1, fd) == 1 ) {
201 be32++;
202 } else {
203 DEBUGF("file_entry_write: failed to pad name\n");
204 return ERR_FILE;
205 }
206 }
207
208 // hash
209 be32 = BE32(e->hash);
210 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
211 DEBUGF("file_entry_write: failed to write hash\n");
212 return ERR_FILE;
213 }
214
215 // song
216 be32 = BE32(e->song);
217 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
218 DEBUGF("file_entry_write: failed to write song\n");
219 return ERR_FILE;
220 }
221
222 // rundb
223 be32 = BE32(e->rundb);
224 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
225 DEBUGF("file_entry_write: failed to write rundb\n");
226 return ERR_FILE;
227 }
228
229 return ERR_NONE;
230}
231
232inline int file_entry_compare(const struct file_entry *a, const struct file_entry *b) {
233 assert(a != NULL);
234 assert(b != NULL);
235 return strncasecmp(a->name, b->name, (a->size.name_len <= b->size.name_len ? a->size.name_len : b->size.name_len) );
236}
237
238struct file_size* new_file_size() {
239 struct file_size *s;
240 s = (struct file_size*)malloc(sizeof(struct file_size));
241 if( s == NULL ) {
242 DEBUGF("new_file_size: failed to allocate memory\n");
243 return NULL;
244 }
245 s->name_len = 0;
246
247 return s;
248}
249
250inline uint32_t file_size_get_length(const struct file_size *size) {
251 assert(size != NULL);
252 return size->name_len + 12;
253}
254
255inline int file_size_max(struct file_size *s, const struct file_entry *e) {
256 assert(s != NULL);
257 assert(e != NULL);
258 assert(FLAG_VALID(e->flag));
259 s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len );
260 return ERR_NONE;
261}
262
263int file_size_destruct(struct file_size *s) {
264 assert(s != NULL);
265 // nothing to do...
266 free(s);
267 return ERR_NONE;
268}
diff --git a/apps/tagdb/file.h b/apps/tagdb/file.h
new file mode 100644
index 0000000000..d2538a7569
--- /dev/null
+++ b/apps/tagdb/file.h
@@ -0,0 +1,84 @@
1#ifndef __FILE_H__
2#define __FILE_H__
3
4#include "config.h"
5#include <stdio.h>
6#include <stdint.h>
7
8struct file_entry {
9 char* name; // song name
10
11 uint32_t hash;
12 uint32_t song; // pointer to song
13 uint32_t rundb; // pointer to rundb
14
15 struct file_size {
16 uint32_t name_len; // must be mulitple of 4
17 } size;
18 unsigned char flag; // flags
19};
20
21struct file_entry* new_file_entry(const uint32_t name_len);
22/* Creates a new file_entry with the specified sizes
23 * Returns a pointer to the structure on success,
24 * NULL on failure
25 */
26
27int file_entry_destruct(struct file_entry *e);
28/* Destructs the given file_entry and free()'s it's memory
29 * returns 0 on success, 1 on failure
30 */
31
32inline int file_entry_resize(struct file_entry *e, const uint32_t name_len);
33/* Change the size of the entry
34 * returns 0 on succes, 1 on failure
35 */
36
37int file_entry_serialize(FILE *fd, const struct file_entry *e);
38/* Serializes the entry in the file at the current position
39 * returns 0 on success, 1 on failure
40 */
41
42int file_entry_unserialize(struct file_entry* *e, FILE *fd);
43/* Unserializes an entry from file into a new structure
44 * The address of the structure is saved into *e
45 * returns 0 on success
46 * 1 on malloc() failure
47 * 2 on fread() failure
48 */
49
50int file_entry_write(FILE *fd, struct file_entry *e, struct file_size *s);
51/* Writes the entry to file in the final form
52 * returns 0 (0) on success, 1 (1) on failure
53 */
54
55inline int file_entry_compare(const struct file_entry *a, const struct file_entry *b);
56/* Compares 2 entries
57 * When a < b it returns <0
58 * a = b 0
59 * a > b >0
60 */
61
62struct file_size* new_file_size();
63/* Creates a new size structure
64 * returns a pointer to the structure on success,
65 * NULL on failure
66 */
67
68inline uint32_t file_size_get_length(const struct file_size *size);
69/* Calculates the length of the entry when written by file_entry_write()
70 * returns the length on success, 0xffffffff on failure
71 */
72
73inline int file_size_max(struct file_size *s, const struct file_entry *e);
74/* Updates the file_size structure to contain the maximal lengths of either
75 * the original entry in s, or the entry e
76 * returns 0 on success, 1 on failure
77 */
78
79int file_size_destruct(struct file_size *s);
80/* destructs the file_size structure
81 * returns 0 on success, 1 on failure
82 */
83
84#endif
diff --git a/apps/tagdb/header.c b/apps/tagdb/header.c
new file mode 100644
index 0000000000..01f973824b
--- /dev/null
+++ b/apps/tagdb/header.c
@@ -0,0 +1,121 @@
1
2#include <stdio.h>
3
4#include "header.h"
5
6int header_write(FILE *fd, const struct header *h) {
7// Write the header to file
8 uint32_t be;
9
10 if( fwrite(h->magic, 3, 1, fd) != 1 ) {
11 DEBUGF("header_write: failed to write magic[3]\n");
12 return ERR_FILE;
13 }
14 if( fwrite(&h->version, 1, 1, fd) != 1 ) {
15 DEBUGF("header_write: failed to write version\n");
16 return ERR_FILE;
17 }
18
19 be = BE32(h->artist_start);
20 if( fwrite(&be, 4, 1, fd) != 1 ) {
21 DEBUGF("header_write: failed to write artist_start\n");
22 return ERR_FILE;
23 }
24
25 be = BE32(h->album_start);
26 if( fwrite(&be, 4, 1, fd) != 1 ) {
27 DEBUGF("header_write: failed to write album_start\n");
28 return ERR_FILE;
29 }
30
31 be = BE32(h->song_start);
32 if( fwrite(&be, 4, 1, fd) != 1 ) {
33 DEBUGF("header_write: failed to write song_start\n");
34 return ERR_FILE;
35 }
36
37 be = BE32(h->file_start);
38 if( fwrite(&be, 4, 1, fd) != 1 ) {
39 DEBUGF("header_write: failed to write file_start\n");
40 return ERR_FILE;
41 }
42
43
44 be = BE32(h->artist_count);
45 if( fwrite(&be, 4, 1, fd) != 1 ) {
46 DEBUGF("header_write: failed to write artist_count\n");
47 return ERR_FILE;
48 }
49
50 be = BE32(h->album_count);
51 if( fwrite(&be, 4, 1, fd) != 1 ) {
52 DEBUGF("header_write: failed to write album_count\n");
53 return ERR_FILE;
54 }
55
56 be = BE32(h->song_count);
57 if( fwrite(&be, 4, 1, fd) != 1 ) {
58 DEBUGF("header_write: failed to write song_count\n");
59 return ERR_FILE;
60 }
61
62 be = BE32(h->file_count);
63 if( fwrite(&be, 4, 1, fd) != 1 ) {
64 DEBUGF("header_write: failed to write file_count\n");
65 return ERR_FILE;
66 }
67
68
69 be = BE32(h->artist_len);
70 if( fwrite(&be, 4, 1, fd) != 1 ) {
71 DEBUGF("header_write: failed to write artist_len\n");
72 return ERR_FILE;
73 }
74
75 be = BE32(h->album_len);
76 if( fwrite(&be, 4, 1, fd) != 1 ) {
77 DEBUGF("header_write: failed to write album_len\n");
78 return ERR_FILE;
79 }
80
81 be = BE32(h->song_len);
82 if( fwrite(&be, 4, 1, fd) != 1 ) {
83 DEBUGF("header_write: failed to write song_len\n");
84 return ERR_FILE;
85 }
86
87 be = BE32(h->genre_len);
88 if( fwrite(&be, 4, 1, fd) != 1 ) {
89 DEBUGF("header_write: failed to write genre_len\n");
90 return ERR_FILE;
91 }
92
93 be = BE32(h->file_len);
94 if( fwrite(&be, 4, 1, fd) != 1 ) {
95 DEBUGF("header_write: failed to write file_len\n");
96 return ERR_FILE;
97 }
98
99
100 be = BE32(h->song_array_count);
101 if( fwrite(&be, 4, 1, fd) != 1 ) {
102 DEBUGF("header_write: failed to write song_array_count\n");
103 return ERR_FILE;
104 }
105
106 be = BE32(h->album_array_count);
107 if( fwrite(&be, 4, 1, fd) != 1 ) {
108 DEBUGF("header_write: failed to write album_array_count\n");
109 return ERR_FILE;
110 }
111
112
113 be = BE32( (h->flags.reserved << 1) | (h->flags.rundb_dirty) );
114 if( fwrite(&be, 4, 1, fd) != 1 ) {
115 DEBUGF("header_write: failed to write flags\n");
116 return ERR_FILE;
117 }
118
119
120 return ERR_NONE;
121}
diff --git a/apps/tagdb/header.h b/apps/tagdb/header.h
new file mode 100644
index 0000000000..08a563ec72
--- /dev/null
+++ b/apps/tagdb/header.h
@@ -0,0 +1,39 @@
1#ifndef __HEADER_H__
2#define __HEADER_H__
3
4#include "config.h"
5
6#define HEADER_SIZE 68
7
8struct header {
9 char magic[3]; // (four bytes: 'R' 'D' 'B' and a byte for version. This is version 2. (0x02)
10 unsigned char version;
11
12 uint32_t artist_start; // File Offset to the artist table(starting from 0)
13 uint32_t album_start; // File Offset to the album table(starting from 0)
14 uint32_t song_start; // File Offset of the song table(starting from 0)
15 uint32_t file_start; // File Offset to the filename table(starting from 0)
16
17 uint32_t artist_count; // Number of artists
18 uint32_t album_count; // Number of albums
19 uint32_t song_count; // Number of songs
20 uint32_t file_count; // Number of File Entries, this is needed for the binary search.
21
22 uint32_t artist_len; // Max Length of the artist name field
23 uint32_t album_len; // Max Length of the album name field
24 uint32_t song_len; // Max Length of the song name field
25 uint32_t genre_len; // Max Length of the genre field
26 uint32_t file_len; // Max Length of the filename field.
27
28 uint32_t song_array_count; // Number of entries in songs-per-album array
29 uint32_t album_array_count; // Number of entries in albums-per-artist array
30
31 struct {
32 unsigned reserved : 31; // must be 0
33 unsigned rundb_dirty : 1; // if the TagDatabase in unsynchronized with the RuntimeDatabase, 0 if synchronized.
34 } flags;
35};
36
37int header_write(FILE *fd, const struct header *header);
38
39#endif
diff --git a/apps/tagdb/main.c b/apps/tagdb/main.c
new file mode 100644
index 0000000000..61a0330c81
--- /dev/null
+++ b/apps/tagdb/main.c
@@ -0,0 +1,115 @@
1#include "config.h"
2
3#include <stdio.h>
4#include <string.h> // strcmp()
5#include <dirent.h> // opendir() readdir() closedir()
6#include <sys/stat.h> // IS_DIR
7
8#include "malloc.h"
9#include "db.h"
10
11extern int out_of_memory;
12
13// dir-is-album: all files in the dir ARE the same album, use the first name found.
14// dir-is-album-name: if no tag found, use the dir's instead of "<no album tag>"
15//
16// files in different dirs are ALWAYS different albums
17
18static char* strip_path = NULL;
19static char* add_path = NULL;
20
21static int iterate_dir(char* dir);
22/* Iterates over each item in the given directory
23 * calls add_file() on each file
24 * calls iterate_directory() on each directory (recursively)
25 */
26
27static int iterate_dir(char* dir) {
28 DIR *d;
29 struct dirent *e;
30 struct stat s;
31 int rc;
32
33 assert(dir != NULL);
34
35 if(!( d = opendir(dir) )) {
36 DEBUGF("iterate_dir: could not open directory \"%s\"\n", dir);
37 return ERR_FILE;
38 }
39
40 while(( e = readdir(d) )) {
41 char *path;
42
43 if( strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0 )
44 continue; // we don't want to descend or loop around...
45
46 path = malloc(strlen(dir) + 1 + strlen(e->d_name) + 1); // "dir/d_name\0"
47 if( path == NULL ) {
48 DEBUGF("iterate_dir: could not malloc() directory-entry-name\n");
49 return ERR_MALLOC;
50 }
51 strcpy(path, dir);
52 strcat(path, "/");
53 strcat(path, e->d_name);
54#if defined OS_LINUX
55 if( stat(path, &s) ) {
56 DEBUGF("iterate_dir: could not stat(\"%s\")\n", path);
57 return ERR_FILE;
58 }
59
60 if( S_ISDIR(s.st_mode) ) {
61#elif defined OS_ROCKBOX
62#error "Rockbox: not yet implemented: don't know how to list directory"
63 if( false ) {
64#elif defined OS_WINDOWS
65 if( false ) {
66#error "Windows: not yet implemented: don't know how to list directory"
67#else
68 if( false ) {
69#error "No OS specified: don't know how to list directory"
70#endif
71 if(( rc = iterate_dir(path) )) {
72 closedir(d);
73 return rc;
74 }
75 } else {
76 if(( rc = db_add(path, strip_path, add_path) )) {
77 closedir(d);
78 return rc;
79 }
80 }
81 free(path);
82 }
83
84 if( closedir(d) ) {
85 DEBUGF("iterate_dir: could not close directory \"%s\", ignoring...\n", dir);
86 }
87
88 return ERR_NONE;
89}
90
91int main(int argc, char* argv[]) {
92 FILE *fd;
93
94 if( argc != 2 ) {
95 printf("usage: ./songdb dir\n");
96 return 1;
97 }
98
99 strip_path = "/home/niels/";
100 add_path = "TEST/";
101
102 db_construct();
103
104 iterate_dir(argv[1]);
105
106 fd = fopen("xxx.db", "w");
107 db_write(fd);
108 fclose(fd);
109
110 db_destruct();
111
112 malloc_stats();
113
114 return 0;
115}
diff --git a/apps/tagdb/malloc.c b/apps/tagdb/malloc.c
new file mode 100644
index 0000000000..78d24f9d4e
--- /dev/null
+++ b/apps/tagdb/malloc.c
@@ -0,0 +1,131 @@
1#include "config.h"
2#include "malloc.h"
3
4#undef malloc
5#undef free
6#undef realloc
7
8#undef DEBUGF
9#define DEBUGF(...)
10
11#include <stdio.h>
12#include <stdlib.h>
13
14static size_t total=0;
15static size_t max_total=0;
16
17struct size_array {
18 void *ptr;
19 size_t size;
20} sizes[1000];
21#define NOT_FOUND 1001
22static unsigned long count=0;
23
24int out_of_memory = 1000000;
25
26void *do_malloc(size_t size) {
27 void *ret;
28 if(total + size > out_of_memory) {
29 DEBUGF("malloc(%d), total=%d: FAILED: simulating out-of-memory\n", size, total+size);
30 return NULL;
31 }
32
33 ret = malloc(size);
34 if( ret == NULL ) {
35 DEBUGF("malloc(%d), total=%d FAILED\n", size, total+size);
36 return NULL;
37 } else {
38 total += size;
39 max_total = ( total > max_total ? total : max_total );
40 sizes[count].ptr = ret;
41 sizes[count].size = size;
42 DEBUGF("malloc(%d), total=%d OK => 0x%08lx (%lu)\n", size, total, (unsigned long)ret, count);
43 count++;
44 if(count == NOT_FOUND) {
45 fprintf(stderr, "MALLOC MEMORY FULL!!!!!!! FAILING\n");
46 free(ret);
47 count--;
48 return NULL;
49 }
50 return ret;
51 }
52}
53
54static unsigned long find(void* ptr) {
55 unsigned long i;
56 for(i=0; i<count; i++) {
57 if( ptr == sizes[i].ptr ) {
58 return i;
59 }
60 }
61 return NOT_FOUND;
62}
63
64void do_free(void *ptr) {
65 unsigned long i;
66
67 i = find(ptr);
68 if( i == NOT_FOUND ) {
69 DEBUGF("free(%08lx) (?) ptr unknown\n", (unsigned long)ptr);
70 free(ptr);
71 } else {
72 total -= sizes[i].size;
73 DEBUGF("free(%08lx) (%lu, %dbytes) => total=%u\n", (unsigned long)ptr, i, sizes[i].size, total);
74 free(ptr);
75 sizes[i].ptr = NULL; // delete
76 sizes[i].size = 0;
77 }
78}
79
80void *do_realloc(void *ptr, size_t size) {
81 void *ret;
82 unsigned long i;
83
84 if( ptr == NULL ) {
85 DEBUGF("realloc()=>");
86 return do_malloc(size);
87 }
88
89 i = find(ptr);
90
91 if( i == NOT_FOUND ) {
92 DEBUGF("realloc(%08lx, %d) (?) ptr unknown ", (unsigned long)ptr, size);
93 } else {
94 DEBUGF("realloc(%08lx, %d) (%lu, %dbytes) => total=%d ", (unsigned long)ptr, size, i, sizes[i].size, total+size-sizes[i].size);
95 }
96
97 if(total + size - sizes[i].size > out_of_memory) {
98 DEBUGF("FAILED: simulating out-of-memory\n");
99 return NULL;
100 }
101
102 ret = realloc(ptr, size);
103 if( ret == NULL && size != 0) { // realloc(x, 0) returns NULL, but is valid!
104 DEBUGF("FAILED\n");
105 } else {
106 total += size - sizes[i].size;
107 max_total = ( total > max_total ? total : max_total );
108 sizes[i].ptr = ret; // update the ptr if realloc changed it
109 sizes[i].size = size;
110 DEBUGF("=> %08lx\n", (unsigned long)ret);
111 }
112 return ret;
113}
114
115void malloc_stats() {
116 unsigned long i, j;
117
118 printf("malloc stats:\n");
119 printf(" Total number of allocated items: %lu\n", count);
120 printf(" Current number of allocated items: ");
121 j=0;
122 for(i=0; i<count; i++) {
123 if( sizes[i].ptr != NULL) {
124 printf("%lu ", i);
125 j++;
126 }
127 }
128 printf("=> %lu items\n", j);
129 printf(" Maximum amount of allocated memory: %dbytes\n", max_total);
130 printf(" Current amount of allocated memory: %dbytes\n", total);
131}
diff --git a/apps/tagdb/malloc.h b/apps/tagdb/malloc.h
new file mode 100644
index 0000000000..c8c885bf6f
--- /dev/null
+++ b/apps/tagdb/malloc.h
@@ -0,0 +1,16 @@
1#ifndef __MALLOC_H__
2#define __MALLOC_H__
3
4#include <stdlib.h>
5
6#define malloc do_malloc
7#define free do_free
8#define realloc do_realloc
9
10void *do_malloc(size_t size);
11void do_free(void *ptr);
12void *do_realloc(void *ptr, size_t size);
13
14void malloc_stats();
15
16#endif
diff --git a/apps/tagdb/parser.c b/apps/tagdb/parser.c
new file mode 100644
index 0000000000..1d251dcbe3
--- /dev/null
+++ b/apps/tagdb/parser.c
@@ -0,0 +1,218 @@
1#include <stdio.h>
2#include <stdint.h>
3#include <stdlib.h>
4
5#include "config.h"
6
7int errno;
8
9int read_failure(FILE *fd) {
10 fprintf(stderr, "Could not read from file: errno: %u ", errno);
11 if( feof(fd) ) fprintf(stderr, "EOF");
12 fprintf(stderr, "\n");
13 return 1;
14}
15
16int mem_failure() {
17 fprintf(stderr, "Could not (re)allocate memory\n");
18 return 1;
19}
20
21int main(int argc, char *argv[]) {
22 FILE *fd;
23 uint32_t artist_start, album_start, song_start, file_start;
24 uint32_t artist_count, album_count, song_count, file_count;
25 uint32_t artist_len, album_array_count;
26 uint32_t album_len, song_array_count;
27 uint32_t song_len, genre_len;
28 uint32_t file_len;
29#define header_start 0
30#define header_len 68
31
32 uint32_t i, j;
33 char *ct1 = NULL, *ct2 = NULL; // char temp 1 and 2
34 uint32_t it = 0; // integer temp
35
36 // input validation
37 if( argc != 2 ) {
38 fprintf(stderr, "usage: parser dbfile\n");
39 return 1;
40 }
41
42 // open file
43 fd = fopen(argv[1], "r");
44 if( fd == NULL ) {
45 fprintf(stderr, "Could not open file \"%s\"\n", argv[1]);
46 return 1;
47 }
48
49 // read the header
50 ct1 = realloc(ct1, 4); if( ct1 == NULL ) return mem_failure();
51 if( fread(ct1, 4, 1, fd) != 1 ) return read_failure(fd);
52 if( ct1[0] != 'R' || ct1[1] != 'D' || ct1[2] != 'B' ) {
53 printf("No header found\n");
54 return 1;
55 }
56 if( ct1[3] != 0x03 ) {
57 printf("Not version 3\n");
58 return 1;
59 }
60
61 if( fread(&artist_start, 4, 1, fd) != 1 ) return read_failure(fd); artist_start = BE32(artist_start);
62 if( fread(&album_start, 4, 1, fd) != 1 ) return read_failure(fd); album_start = BE32(album_start);
63 if( fread(&song_start, 4, 1, fd) != 1 ) return read_failure(fd); song_start = BE32(song_start);
64 if( fread(&file_start, 4, 1, fd) != 1 ) return read_failure(fd); file_start = BE32(file_start);
65
66 if( fread(&artist_count, 4, 1, fd) != 1 ) return read_failure(fd); artist_count = BE32(artist_count);
67 if( fread(&album_count, 4, 1, fd) != 1 ) return read_failure(fd); album_count = BE32(album_count);
68 if( fread(&song_count, 4, 1, fd) != 1 ) return read_failure(fd); song_count = BE32(song_count);
69 if( fread(&file_count, 4, 1, fd) != 1 ) return read_failure(fd); file_count = BE32(file_count);
70
71 if( fread(&artist_len, 4, 1, fd) != 1 ) return read_failure(fd); artist_len = BE32(artist_len);
72 if( fread(&album_len, 4, 1, fd) != 1 ) return read_failure(fd); album_len = BE32(album_len);
73 if( fread(&song_len, 4, 1, fd) != 1 ) return read_failure(fd); song_len = BE32(song_len);
74 if( fread(&genre_len, 4, 1, fd) != 1 ) return read_failure(fd); genre_len = BE32(genre_len);
75 if( fread(&file_len, 4, 1, fd) != 1 ) return read_failure(fd); file_len = BE32(file_len);
76
77 if( fread(&song_array_count, 4, 1, fd) != 1 ) return read_failure(fd); song_array_count = BE32(song_array_count);
78 if( fread(&album_array_count, 4, 1, fd) != 1 ) return read_failure(fd); album_array_count = BE32(album_array_count);
79
80 if( fread(ct1, 4, 1, fd) != 1 ) return read_failure(fd);
81
82 // print header info
83 printf("HEADER");
84 printf("\n Artist start: 0x%08x = %u", artist_start, artist_start);
85 if( artist_start != header_start + header_len )
86 printf(" should be 0x%08x = %u", header_start + header_len, header_start + header_len);
87 printf("\n Album start: 0x%08x = %u", album_start, album_start);
88 if( album_start != artist_start + artist_count*(artist_len + 4*album_array_count) )
89 printf(" should be 0x%08x = %u", artist_start + artist_count*(artist_len + 4*album_array_count),
90 artist_start + artist_count*(artist_len + 4*album_array_count));
91 printf("\n Song start: 0x%08x = %u", song_start, song_start);
92 if( song_start != album_start + album_count*(album_len + 4 + 4*song_array_count) )
93 printf(" should be 0x%08x = %u", album_start + album_count*(album_len + 4 + 4*song_array_count),
94 album_start + album_count*(album_len + 4 + 4*song_array_count));
95 printf("\n File start: 0x%08x = %u", file_start, file_start);
96 if( file_start != song_start + song_count*(song_len + genre_len + 24) )
97 printf(" should be 0x%08x = %u", song_start + song_count*(song_len + genre_len + 24),
98 song_start + song_count*(song_len + genre_len + 24));
99
100 printf("\n Artist count: 0x%08x = %u\n", artist_count, artist_count);
101 printf(" Album count: 0x%08x = %u\n", album_count, album_count);
102 printf(" Song count: 0x%08x = %u\n", song_count, song_count);
103 printf(" File count: 0x%08x = %u\n", file_count, file_count);
104
105 printf(" Artist len: 0x%08x = %u\n", artist_len, artist_len);
106 printf(" Album len: 0x%08x = %u\n", album_len, album_len);
107 printf(" Song len: 0x%08x = %u\n", song_len, song_len);
108 printf(" Genre len: 0x%08x = %u\n", genre_len, genre_len);
109 printf(" File len: 0x%08x = %u\n", file_len, file_len);
110
111 printf(" Song[] count: 0x%08x = %u\n", song_array_count, song_array_count);
112 printf(" Album[] count: 0x%08x = %u\n", album_array_count, album_array_count);
113
114 printf(" Reserved: 0x%08x\n", ct1[0] & 0xFFFFFFFE);
115 printf(" Rundb dirty: 0x%01x\n", ct1[3] & 0x01);
116
117 // iterate over artists:
118 ct1 = realloc(ct1, artist_len); if( ct1 == NULL && artist_count!=0 ) return mem_failure();
119 for(i=0; i < artist_count; i++) {
120 printf("ARTIST %u/%u (offset 0x%08lx)\n", i, artist_count, (unsigned long)ftell(fd));
121
122 if( fread(ct1, artist_len, 1, fd) != 1 ) return read_failure(fd);
123 printf(" Name: \"%s\"\n", ct1);
124
125 printf(" Albums:\n");
126 for(j=0; j < album_array_count; j++) {
127 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
128 printf(" Offset 0x%08x = ", it);
129 if(it != 0) {
130 printf("item %u\n", (it - album_start) / (album_len + 4 + 4*song_array_count));
131 } else {
132 printf("padding\n");
133 }
134 }
135 }
136
137 // iterate over albums:
138 ct1 = realloc(ct1, album_len); if( ct1 == NULL && album_count!=0) return mem_failure();
139 for(i=0; i < album_count; i++) {
140 printf("ALBUM %u/%u (offset 0x%08lx)\n", i, album_count, (unsigned long)ftell(fd));
141
142 if( fread(ct1, album_len, 1, fd) != 1 ) return read_failure(fd);
143 printf(" Name: \"%s\"\n", ct1);
144
145 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
146 printf(" Artist offset: 0x%08x = item %u\n", it, (it - artist_start) / (artist_len + 4*album_array_count));
147
148 printf(" Songs:\n");
149 for(j=0; j < song_array_count; j++) {
150 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
151 printf(" Offset 0x%08x = ", it);
152 if(it != 0) {
153 printf("item %u\n", (it - song_start) / (song_len + genre_len + 24));
154 } else {
155 printf("padding\n");
156 }
157 }
158 }
159
160 // iterate over songs:
161 ct1 = realloc(ct1, song_len); if( ct1 == NULL && song_count!=0) return mem_failure();
162 ct2 = realloc(ct2, genre_len); if( ct2 == NULL && song_count!=0) return mem_failure();
163 for(i=0; i < song_count; i++) {
164 printf("SONG %u/%u (offset 0x%08lx)\n", i, song_count, (unsigned long)ftell(fd));
165
166 if( fread(ct1, song_len, 1, fd) != 1 ) return read_failure(fd);
167 printf(" Name: \"%s\"\n", ct1);
168
169 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
170 printf(" Artist offset: 0x%08x = item %u\n", it, (it - artist_start) / (artist_len + 4*album_array_count));
171
172 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
173 printf(" Album offset: 0x%08x = item %u\n", it, (it - album_start) / (album_len + 4 + 4*song_array_count));
174
175 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
176 printf(" File offset: 0x%08x = item %u\n", it, (it - file_start) / (file_len + 12));
177
178 if( fread(ct2, genre_len, 1, fd) != 1 ) return read_failure(fd);
179 printf(" Genre: \"%s\"\n", ct2);
180
181 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
182 printf(" Bitrate: 0x%04x = %u\n", (it & 0xFFFF0000) >> 16, (it & 0xFFFF0000) >> 16);
183 printf(" Year: 0x%04x = %u\n", it & 0x0000FFFF, it & 0x0000FFFF);
184
185 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
186 printf(" Playtime: 0x%08x = %u\n", it, it);
187
188 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
189 printf(" Track: 0x%04x = %u\n", (it & 0xFFFF0000) >> 16, (it & 0xFFFF0000) >> 16);
190 printf(" Samplerate: 0x%04x = %u\n", it & 0x0000FFFF, it & 0x0000FFFF);
191 }
192
193 // iterate over file:
194 ct1 = realloc(ct1, file_len); if( ct1 == NULL && file_count!=0) return mem_failure();
195 for(i=0; i < file_count; i++) {
196 printf("FILE %u/%u (offset 0x%08lx)\n", i, file_count, (unsigned long)ftell(fd));
197
198 if( fread(ct1, file_len, 1, fd) != 1 ) return read_failure(fd);
199 printf(" Name: \"%s\"\n", ct1);
200
201 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
202 printf(" Hash: 0x%08x = %u\n", it, it);
203
204 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
205 printf(" Song offset: 0x%08x = item %u\n", it, (it - song_start) / (song_len + genre_len + 24));
206
207 if( fread(&it, 4, 1, fd) != 1 ) return read_failure(fd); it = BE32(it);
208 printf(" Rundb offset: 0x%08x = %u\n", it, it);
209 }
210
211 // close the file
212 if( fclose(fd) != 0 ) {
213 fprintf(stderr, "Could not close file\n");
214 return 1;
215 }
216
217 return 0;
218}
diff --git a/apps/tagdb/song.c b/apps/tagdb/song.c
new file mode 100644
index 0000000000..16ae385eda
--- /dev/null
+++ b/apps/tagdb/song.c
@@ -0,0 +1,450 @@
1#include "malloc.h" // realloc() and free()
2#include <string.h> // strncasecmp()
3
4#include "song.h"
5
6// how is our flag organized?
7#define FLAG ( 0xCF )
8#define FLAG_VALID(flag) (flag == 0xCF)
9
10static int do_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len, const int zero_fill);
11
12struct song_entry* new_song_entry(const uint32_t name_len, const uint32_t genre_len) {
13 // Start my allocating memory
14 struct song_entry *e = (struct song_entry*)malloc(sizeof(struct song_entry));
15 if( e == NULL ) {
16 DEBUGF("new_song_entry: could not allocate memory\n");
17 return NULL;
18 }
19
20 // We begin empty
21 e->name = NULL;
22 e->size.name_len = 0;
23
24 e->artist = 0;
25 e->album = 0;
26 e->file = 0;
27
28 e->genre = NULL;
29 e->size.genre_len = 0;
30
31 e->bitrate = 0;
32 e->year = 0;
33 e->playtime = 0;
34 e->track = 0;
35 e->samplerate = 0;
36
37 e->flag = FLAG;
38
39 // and resize to the requested size
40 if( do_resize(e, name_len, genre_len, 1) ) {
41 free(e);
42 return NULL;
43 }
44 return e;
45}
46
47int song_entry_destruct(struct song_entry *e) {
48 assert(e != NULL);
49 assert(FLAG_VALID(e->flag));
50
51 free(e->name);
52 free(e->genre);
53
54 free(e);
55
56 return ERR_NONE;
57}
58
59static int do_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len, const int zero_fill) {
60 void* temp;
61
62 assert(e != NULL);
63 assert(FLAG_VALID(e->flag));
64
65 // begin with name
66 if( name_len != e->size.name_len ) {
67 temp = realloc(e->name, name_len);
68 if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer
69 DEBUGF("song_entry_resize: out of memory to resize name\n");
70 return ERR_MALLOC;
71 }
72 e->name = (char*)temp;
73
74 // if asked, fill it with zero's
75 if( zero_fill ) {
76 uint32_t i;
77 for(i=e->size.name_len; i<name_len; i++)
78 e->name[i] = (char)0x00;
79 }
80
81 e->size.name_len = name_len;
82 }
83
84 // now the genre
85 if( genre_len != e->size.genre_len ) {
86 temp = realloc(e->genre, genre_len);
87 if(temp == NULL && genre_len > 0) { // if realloc(,0) don't complain about NULL-pointer
88 DEBUGF("song_entry_resize: out of memory to resize genre\n");
89 return ERR_MALLOC;
90 }
91 e->genre = (char*)temp;
92
93 // if asked, fill it with zero's
94 if( zero_fill ) {
95 uint32_t i;
96 for(i=e->size.genre_len; i<genre_len; i++)
97 e->genre[i] = (char)0x00;
98 }
99
100 e->size.genre_len = genre_len;
101 }
102
103 return ERR_NONE;
104}
105
106inline int song_entry_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len) {
107 return do_resize(e, name_len, genre_len, 1);
108}
109
110int song_entry_serialize(FILE *fd, const struct song_entry *e) {
111 assert(fd != NULL);
112 assert(e != NULL);
113 assert(FLAG_VALID(e->flag));
114
115 // First byte we write is a flag-byte to indicate this is a valid record
116 if( fwrite(&e->flag, 1, 1, fd) != 1 ) {
117 DEBUGF("song_entry_serialize: failed to write flag-byte\n");
118 return ERR_FILE;
119 }
120
121 // Write the length of the name field
122 if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) {
123 DEBUGF("song_entry_serialize: failed to write name_len\n");
124 return ERR_FILE;
125 }
126
127 // now the name field itself
128 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
129 DEBUGF("song_entry_serialize: failed to write name\n");
130 return ERR_FILE;
131 }
132
133 // Artist field
134 if( fwrite(&e->artist, sizeof(e->artist), 1, fd) != 1 ) {
135 DEBUGF("song_entry_serialize: failed to write artist\n");
136 return ERR_FILE;
137 }
138
139 // Album field
140 if( fwrite(&e->album, sizeof(e->album), 1, fd) != 1 ) {
141 DEBUGF("song_entry_serialize: failed to write album\n");
142 return ERR_FILE;
143 }
144
145 // File field
146 if( fwrite(&e->file, sizeof(e->file), 1, fd) != 1 ) {
147 DEBUGF("song_entry_serialize: failed to write file\n");
148 return ERR_FILE;
149 }
150
151 // length of genre field
152 if( fwrite(&e->size.genre_len, sizeof(e->size.genre_len), 1, fd) != 1 ) {
153 DEBUGF("song_entry_serialize: failed to write genre_len\n");
154 return ERR_FILE;
155 }
156
157 // genre itself
158 if( fwrite(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
159 DEBUGF("song_entry_serialize: failed to write genre\n");
160 return ERR_FILE;
161 }
162
163 // Bitrate field
164 if( fwrite(&e->bitrate, sizeof(e->bitrate), 1, fd) != 1 ) {
165 DEBUGF("song_entry_serialize: failed to write bitrate\n");
166 return ERR_FILE;
167 }
168
169 // Year field
170 if( fwrite(&e->year, sizeof(e->year), 1, fd) != 1 ) {
171 DEBUGF("song_entry_serialize: failed to write year\n");
172 return ERR_FILE;
173 }
174
175 // Playtime field
176 if( fwrite(&e->playtime, sizeof(e->playtime), 1, fd) != 1 ) {
177 DEBUGF("song_entry_serialize: failed to write playtime\n");
178 return ERR_FILE;
179 }
180
181 // Track field
182 if( fwrite(&e->track, sizeof(e->track), 1, fd) != 1 ) {
183 DEBUGF("song_entry_serialize: failed to write track\n");
184 return ERR_FILE;
185 }
186
187 // Samplerate field
188 if( fwrite(&e->samplerate, sizeof(e->samplerate), 1, fd) != 1 ) {
189 DEBUGF("song_entry_serialize: failed to write samplerate\n");
190 return ERR_FILE;
191 }
192
193 return ERR_NONE;
194}
195
196int song_entry_unserialize(struct song_entry **dest, FILE *fd) {
197 uint32_t length;
198 struct song_entry* e;
199
200 assert(dest != NULL);
201 assert(fd != NULL);
202
203 // Allocate memory
204 e = new_song_entry(0, 0);
205 if( e == NULL ) {
206 DEBUGF("song_entry_unserialize: could not create new song_entry\n");
207 return ERR_MALLOC;
208 }
209
210 // First we read the length of the name field
211 if( fread(&length, sizeof(length), 1, fd) != 1 ) {
212 DEBUGF("song_entry_unserialize: failed to read name_len\n");
213 song_entry_destruct(e);
214 return ERR_FILE;
215 }
216
217 // allocate memory for the upcomming name-field
218 if( do_resize(e, length, 0, 0) ) {
219 DEBUGF("song_entry_unserialize: failed to allocate memory for name\n");
220 song_entry_destruct(e);
221 return ERR_MALLOC;
222 }
223
224 // read it in
225 if( fread(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
226 DEBUGF("song_entry_unserialize: failed to read name\n");
227 song_entry_destruct(e);
228 return ERR_FILE;
229 }
230
231 // Artist field
232 if( fread(&e->artist, sizeof(e->artist), 1, fd) != 1 ) {
233 DEBUGF("song_entry_unserialize: failed to read artist\n");
234 song_entry_destruct(e);
235 return ERR_FILE;
236 }
237
238 // Album field
239 if( fread(&e->album, sizeof(e->album), 1, fd) != 1 ) {
240 DEBUGF("song_entry_unserialize: failed to read album\n");
241 song_entry_destruct(e);
242 return ERR_FILE;
243 }
244
245 // File field
246 if( fread(&e->file, sizeof(e->file), 1, fd) != 1 ) {
247 DEBUGF("song_entry_unserialize: failed to read file\n");
248 song_entry_destruct(e);
249 return ERR_FILE;
250 }
251
252 // Next the length of genre
253 if( fread(&length, sizeof(length), 1, fd) != 1 ) {
254 DEBUGF("song_entry_unserialize: failed to read genre_len\n");
255 song_entry_destruct(e);
256 return ERR_FILE;
257 }
258
259 // allocate memory for the upcomming name-field
260 if( do_resize(e, e->size.name_len, length, 0) ) {
261 DEBUGF("song_entry_unserialize: failed to allocate memory for song\n");
262 song_entry_destruct(e);
263 return ERR_MALLOC;
264 }
265
266 // read it in
267 if( fread(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
268 DEBUGF("song_entry_unserialize: failed to read genre\n");
269 song_entry_destruct(e);
270 return ERR_FILE;
271 }
272
273 // Bitrate field
274 if( fread(&e->bitrate, sizeof(e->bitrate), 1, fd) != 1 ) {
275 DEBUGF("song_entry_unserialize: failed to read bitrate\n");
276 song_entry_destruct(e);
277 return ERR_FILE;
278 }
279
280 // Year field
281 if( fread(&e->year, sizeof(e->year), 1, fd) != 1 ) {
282 DEBUGF("song_entry_unserialize: failed to read year\n");
283 song_entry_destruct(e);
284 return ERR_FILE;
285 }
286
287 // Playtime field
288 if( fread(&e->playtime, sizeof(e->playtime), 1, fd) != 1 ) {
289 DEBUGF("song_entry_unserialize: failed to read playtime\n");
290 song_entry_destruct(e);
291 return ERR_FILE;
292 }
293
294 // Track field
295 if( fread(&e->track, sizeof(e->track), 1, fd) != 1 ) {
296 DEBUGF("song_entry_unserialize: failed to read track\n");
297 song_entry_destruct(e);
298 return ERR_FILE;
299 }
300
301 // Samplerate field
302 if( fread(&e->samplerate, sizeof(e->samplerate), 1, fd) != 1 ) {
303 DEBUGF("song_entry_unserialize: failed to read samplerate\n");
304 song_entry_destruct(e);
305 return ERR_FILE;
306 }
307
308 *dest = e;
309 return ERR_NONE;
310}
311
312int song_entry_write(FILE *fd, struct song_entry *e, struct song_size *s) {
313 uint32_t be32;
314 uint16_t be16;
315 char pad = 0x00;
316
317 assert(fd != NULL);
318 assert(e != NULL);
319 assert(FLAG_VALID(e->flag));
320
321 // song name
322 if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) {
323 DEBUGF("song_entry_write: failed to write name\n");
324 return ERR_FILE;
325 }
326 // pad the rest (abuse be32 for counter)
327 be32 = e->size.name_len;
328 while( s != NULL && s->name_len > be32) {
329 if( fwrite(&pad, 1, 1, fd) == 1 ) {
330 be32++;
331 } else {
332 DEBUGF("genre_entry_write: failed to pad name\n");
333 return ERR_FILE;
334 }
335 }
336
337 // artist
338 be32 = BE32(e->artist);
339 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
340 DEBUGF("song_entry_write: failed to write artist\n");
341 return ERR_FILE;
342 }
343
344 // album
345 be32 = BE32(e->album);
346 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
347 DEBUGF("song_entry_write: failed to write album\n");
348 return ERR_FILE;
349 }
350
351 // file
352 be32 = BE32(e->file);
353 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
354 DEBUGF("song_entry_write: failed to write file\n");
355 return ERR_FILE;
356 }
357
358 // genre
359 if( fwrite(e->genre, 1, e->size.genre_len, fd) != e->size.genre_len ) {
360 DEBUGF("song_entry_write: failed to write genre\n");
361 return ERR_FILE;
362 }
363 // pad the rest (abuse be32 for counter)
364 be32 = e->size.genre_len;
365 while( s != NULL && s->genre_len > be32) {
366 if( fwrite(&pad, 1, 1, fd) == 1 ) {
367 be32++;
368 } else {
369 DEBUGF("genre_entry_write: failed to pad genre\n");
370 return ERR_FILE;
371 }
372 }
373
374 // bitrate
375 be16 = BE16(e->bitrate);
376 if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
377 DEBUGF("song_entry_write: failed to write bitrate\n");
378 return ERR_FILE;
379 }
380
381 // year
382 be16 = BE16(e->year);
383 if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
384 DEBUGF("song_entry_write: failed to write year\n");
385 return ERR_FILE;
386 }
387
388 // playtime
389 be32 = BE32(e->playtime);
390 if( fwrite(&be32, sizeof(be32), 1, fd) != 1 ) {
391 DEBUGF("song_entry_write: failed to write playtime\n");
392 return ERR_FILE;
393 }
394
395 // track
396 be16 = BE16(e->track);
397 if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
398 DEBUGF("song_entry_write: failed to write track\n");
399 return ERR_FILE;
400 }
401
402 // samplerate
403 be16 = BE16(e->samplerate);
404 if( fwrite(&be16, sizeof(be16), 1, fd) != 1 ) {
405 DEBUGF("song_entry_write: failed to write samplerate\n");
406 return ERR_FILE;
407 }
408
409 return ERR_NONE;
410}
411
412inline int song_entry_compare(const struct song_entry *a, const struct song_entry *b) {
413 assert(a != NULL);
414 assert(b != NULL);
415 return strncasecmp(a->name, b->name, (a->size.name_len <= b->size.name_len ? a->size.name_len : b->size.name_len) );
416}
417
418struct song_size* new_song_size() {
419 struct song_size *s;
420 s = (struct song_size*)malloc(sizeof(struct song_size));
421 if( s == NULL ) {
422 DEBUGF("new_song_size: failed to allocate memory\n");
423 return NULL;
424 }
425 s->name_len = 0;
426 s->genre_len = 0;
427
428 return s;
429}
430
431inline uint32_t song_size_get_length(const struct song_size *size) {
432 assert(size != NULL);
433 return size->name_len + size->genre_len + 6*4;
434}
435
436inline int song_size_max(struct song_size *s, const struct song_entry *e) {
437 assert(s != NULL);
438 assert(e != NULL);
439 assert(FLAG_VALID(e->flag));
440 s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len );
441 s->genre_len = ( s->genre_len >= e->size.genre_len ? s->genre_len : e->size.genre_len );
442 return ERR_NONE;
443}
444
445int song_size_destruct(struct song_size *s) {
446 assert(s != NULL);
447 // nothing to do...
448 free(s);
449 return ERR_NONE;
450}
diff --git a/apps/tagdb/song.h b/apps/tagdb/song.h
new file mode 100644
index 0000000000..1be81ccf0a
--- /dev/null
+++ b/apps/tagdb/song.h
@@ -0,0 +1,93 @@
1#ifndef __SONG_H__
2#define __SONG_H__
3
4#include "config.h"
5#include <stdio.h>
6#include <stdint.h>
7
8struct song_entry {
9 char* name; // song name
10
11 uint32_t artist; // pointer to artist
12 uint32_t album; // pointer to album
13 uint32_t file; // pointer to file
14
15 char* genre; // genre
16
17 uint16_t bitrate; // bitrate (-1 = VBR or unknown)
18 uint16_t year;
19 uint32_t playtime; // in seconds
20 uint16_t track;
21 uint16_t samplerate; // in Hz
22
23 struct song_size {
24 uint32_t name_len; // must be mulitple of 4
25 uint32_t genre_len; // must be multiple of 4
26 } size;
27 unsigned char flag; // flags
28};
29
30struct song_entry* new_song_entry(const uint32_t name_len, const uint32_t genre_len);
31/* Creates a new song_entry with the specified sizes
32 * Returns a pointer to the structure on success,
33 * NULL on failure
34 */
35
36int song_entry_destruct(struct song_entry *e);
37/* Destructs the given song_entry and free()'s it's memory
38 * returns 0 on success, 1 on failure
39 */
40
41inline int song_entry_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len);
42/* Change the size of the entry
43 * returns 0 on succes, 1 on failure
44 */
45
46int song_entry_serialize(FILE *fd, const struct song_entry *e);
47/* Serializes the entry in the file at the current position
48 * returns 0 on success, 1 on failure
49 */
50
51int song_entry_unserialize(struct song_entry* *e, FILE *fd);
52/* Unserializes an entry from file into a new structure
53 * The address of the structure is saved into *e
54 * returns 0 on success
55 * 1 on malloc() failure
56 * 2 on fread() failure
57 */
58
59int song_entry_write(FILE *fd, struct song_entry *e, struct song_size *s);
60/* Writes the entry to file in the final form
61 * returns 0 (0) on success, 1 (1) on failure
62 */
63
64inline int song_entry_compare(const struct song_entry *a, const struct song_entry *b);
65/* Compares 2 entries
66 * When a < b it returns <0
67 * a = b 0
68 * a > b >0
69 */
70
71struct song_size* new_song_size();
72/* Creates a new size structure
73 * returns a pointer to the structure on success,
74 * NULL on failure
75 */
76
77inline uint32_t song_size_get_length(const struct song_size *size);
78/* Calculates the length of the entry when written by song_entry_write()
79 * returns the length on success, 0xffffffff on failure
80 */
81
82inline int song_size_max(struct song_size *s, const struct song_entry *e);
83/* Updates the song_size structure to contain the maximal lengths of either
84 * the original entry in s, or the entry e
85 * returns 0 on success, 1 on failure
86 */
87
88int song_size_destruct(struct song_size *s);
89/* destructs the song_size structure
90 * returns 0 on success, 1 on failure
91 */
92
93#endif
diff --git a/apps/tagdb/tag_dummy.c b/apps/tagdb/tag_dummy.c
new file mode 100644
index 0000000000..f0125f32ea
--- /dev/null
+++ b/apps/tagdb/tag_dummy.c
@@ -0,0 +1,11 @@
1#include "config.h"
2#include "malloc.h"
3
4#include "tag_dummy.h"
5#include <string.h>
6
7int tag_dummy(char *file, struct tag_info *t) {
8 t->song = malloc(strlen(file)+1);
9 strcpy(t->song, file);
10 return ERR_NONE;
11}
diff --git a/apps/tagdb/tag_dummy.h b/apps/tagdb/tag_dummy.h
new file mode 100644
index 0000000000..856a0a5a2e
--- /dev/null
+++ b/apps/tagdb/tag_dummy.h
@@ -0,0 +1,3 @@
1#include "db.h"
2
3int tag_dummy(char *file, struct tag_info *t);
diff --git a/apps/tagdb/unique.c b/apps/tagdb/unique.c
new file mode 100644
index 0000000000..471f59e67f
--- /dev/null
+++ b/apps/tagdb/unique.c
@@ -0,0 +1,16 @@
1#include "unique.h"
2
3#include <string.h>
4#include <stdio.h>
5
6char *create_unique_name(char *buffer, const char *prefix, const char *suffix, int digits) {
7 static unsigned long i=0;
8
9 strcpy(buffer, prefix);
10 sprintf(buffer+strlen(prefix), "%05lu", i);
11 strcat(buffer, suffix);
12
13 i++;
14
15 return buffer;
16}
diff --git a/apps/tagdb/unique.h b/apps/tagdb/unique.h
new file mode 100644
index 0000000000..03dc261141
--- /dev/null
+++ b/apps/tagdb/unique.h
@@ -0,0 +1,6 @@
1#ifndef __UNIQUE_H__
2#define __UNIQUE_H__
3
4char *create_unique_name(char *buffer, const char *prefix, const char *suffix, int digits);
5
6#endif