diff options
-rw-r--r-- | apps/tagdb/Makefile | 32 | ||||
-rw-r--r-- | apps/tagdb/README | 9 | ||||
-rw-r--r-- | apps/tagdb/album.c | 454 | ||||
-rw-r--r-- | apps/tagdb/album.h | 103 | ||||
-rw-r--r-- | apps/tagdb/array_buffer.c | 667 | ||||
-rw-r--r-- | apps/tagdb/array_buffer.h | 159 | ||||
-rw-r--r-- | apps/tagdb/artist.c | 370 | ||||
-rw-r--r-- | apps/tagdb/artist.h | 100 | ||||
-rw-r--r-- | apps/tagdb/config.h | 39 | ||||
-rw-r--r-- | apps/tagdb/db.c | 603 | ||||
-rw-r--r-- | apps/tagdb/db.h | 37 | ||||
-rw-r--r-- | apps/tagdb/file.c | 268 | ||||
-rw-r--r-- | apps/tagdb/file.h | 84 | ||||
-rw-r--r-- | apps/tagdb/header.c | 121 | ||||
-rw-r--r-- | apps/tagdb/header.h | 39 | ||||
-rw-r--r-- | apps/tagdb/main.c | 115 | ||||
-rw-r--r-- | apps/tagdb/malloc.c | 131 | ||||
-rw-r--r-- | apps/tagdb/malloc.h | 16 | ||||
-rw-r--r-- | apps/tagdb/parser.c | 218 | ||||
-rw-r--r-- | apps/tagdb/song.c | 450 | ||||
-rw-r--r-- | apps/tagdb/song.h | 93 | ||||
-rw-r--r-- | apps/tagdb/tag_dummy.c | 11 | ||||
-rw-r--r-- | apps/tagdb/tag_dummy.h | 3 | ||||
-rw-r--r-- | apps/tagdb/unique.c | 16 | ||||
-rw-r--r-- | apps/tagdb/unique.h | 6 |
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 @@ | |||
1 | OBJECTS = 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 | |||
5 | all : tagdb parser | ||
6 | |||
7 | tagdb : $(OBJECTS) | ||
8 | $(CC) -o tagdb $(OBJECTS) | ||
9 | |||
10 | parser: parser.o malloc.o | ||
11 | $(CC) -o parser parser.o malloc.o | ||
12 | |||
13 | main.o : main.c config.h | ||
14 | |||
15 | db.o : db.c db.h config.h | ||
16 | |||
17 | array_buffer.o : array_buffer.c array_buffer.h config.h | ||
18 | unique.o : unique.c unique.h | ||
19 | malloc.o : malloc.c malloc.h config.h | ||
20 | |||
21 | header.o : header.c header.h config.h | ||
22 | artist.o : artist.c artist.h config.h | ||
23 | album.o : album.c album.h config.h | ||
24 | song.o : song.c song.h config.h | ||
25 | file.o : file.c file.h config.h | ||
26 | |||
27 | tag_dummy.o : tag_dummy.c tag_dummy.h config.h | ||
28 | |||
29 | parser.o : parser.c config.h | ||
30 | |||
31 | clean : | ||
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 @@ | |||
1 | The code is currently a working mess... needs cleanup | ||
2 | also it should be transformed into rockbox-format (header in each file). | ||
3 | |||
4 | things that work: | ||
5 | * DB creation | ||
6 | |||
7 | things 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 | |||
13 | static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill); | ||
14 | |||
15 | struct album_entry* new_album_entry(const uint32_t name_len, const uint32_t song_count) { | ||
16 | // Start my allocating memory | ||
17 | struct album_entry *e = (struct album_entry*)malloc(sizeof(struct album_entry)); | ||
18 | if( e == NULL ) { | ||
19 | DEBUGF("new_album_entry: could not allocate memory\n"); | ||
20 | return NULL; | ||
21 | } | ||
22 | |||
23 | // We begin empty | ||
24 | e->name = NULL; | ||
25 | e->size.name_len = 0; | ||
26 | e->key = NULL; | ||
27 | e->artist = 0; | ||
28 | e->song = NULL; | ||
29 | e->size.song_count = 0; | ||
30 | |||
31 | e->flag = FLAG(0, 0); | ||
32 | |||
33 | // and resize to the requested size | ||
34 | if( do_resize(e, name_len, song_count, 1) ) { | ||
35 | free(e); | ||
36 | return NULL; | ||
37 | } | ||
38 | return e; | ||
39 | } | ||
40 | |||
41 | int album_entry_destruct(struct album_entry *e) { | ||
42 | assert(e != NULL); | ||
43 | assert(FLAG_VALID(e->flag)); | ||
44 | |||
45 | free(e->name); | ||
46 | free(e->key); | ||
47 | free(e->song); | ||
48 | |||
49 | free(e); | ||
50 | |||
51 | return ERR_NONE; | ||
52 | } | ||
53 | |||
54 | static int do_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count, const int zero_fill) { | ||
55 | void* temp; | ||
56 | |||
57 | assert(e != NULL); | ||
58 | assert(FLAG_VALID(e->flag)); | ||
59 | |||
60 | // begin with name | ||
61 | if( name_len != e->size.name_len ) { | ||
62 | temp = realloc(e->name, name_len); | ||
63 | if(temp == NULL && name_len > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
64 | DEBUGF("do_resize: out of memory to resize name\n"); | ||
65 | return ERR_MALLOC; | ||
66 | } | ||
67 | e->name = (char*)temp; | ||
68 | |||
69 | // if asked, fill it with zero's | ||
70 | if( zero_fill ) { | ||
71 | uint32_t i; | ||
72 | for(i=e->size.name_len; i<name_len; i++) | ||
73 | e->name[i] = (char)0x00; | ||
74 | } | ||
75 | |||
76 | e->size.name_len = name_len; | ||
77 | } | ||
78 | |||
79 | // now the song[] | ||
80 | if( song_count != e->size.song_count ) { | ||
81 | temp = realloc(e->song, song_count * sizeof(*e->song)); | ||
82 | if(temp == NULL && song_count > 0) { // if realloc(,0) don't complain about NULL-pointer | ||
83 | DEBUGF("album_entry_resize: out of memory to resize song[]\n"); | ||
84 | return ERR_MALLOC; | ||
85 | } | ||
86 | e->song = (uint32_t*)temp; | ||
87 | |||
88 | // if asked, fill it with zero's | ||
89 | if( zero_fill ) { | ||
90 | uint32_t i; | ||
91 | for(i=e->size.song_count; i<song_count; i++) | ||
92 | e->song[i] = (uint32_t)0x00000000; | ||
93 | } | ||
94 | |||
95 | e->size.song_count = song_count; | ||
96 | } | ||
97 | |||
98 | return ERR_NONE; | ||
99 | } | ||
100 | |||
101 | inline int album_entry_resize(struct album_entry *e, const uint32_t name_len, const uint32_t song_count) { | ||
102 | return do_resize(e, name_len, song_count, 1); | ||
103 | } | ||
104 | |||
105 | int album_entry_serialize(FILE *fd, const struct album_entry *e) { | ||
106 | uint32_t length; | ||
107 | |||
108 | assert(e != NULL); | ||
109 | assert(FLAG_VALID(e->flag)); | ||
110 | assert(fd != NULL); | ||
111 | |||
112 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
113 | return ERR_NONE; | ||
114 | } | ||
115 | |||
116 | // First byte we write is a flag-byte | ||
117 | if( fwrite(&e->flag, 1, 1, fd) != 1 ) { | ||
118 | DEBUGF("album_entry_serialize: failed to write flag-byte\n"); | ||
119 | return ERR_FILE; | ||
120 | } | ||
121 | |||
122 | // First we write the length of the name field | ||
123 | if( fwrite(&e->size.name_len, sizeof(e->size.name_len), 1, fd) != 1 ) { | ||
124 | DEBUGF("album_entry_serialize: failed to write name_len\n"); | ||
125 | return ERR_FILE; | ||
126 | } | ||
127 | |||
128 | // now the name field itself | ||
129 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
130 | DEBUGF("album_entry_serialize: failed to write name\n"); | ||
131 | return ERR_FILE; | ||
132 | } | ||
133 | |||
134 | // the key-field (if present) | ||
135 | if( e->key != NULL ) { | ||
136 | length = strlen(e->key); | ||
137 | } else { | ||
138 | length = 0; | ||
139 | } | ||
140 | // length (always, 0 if not present) | ||
141 | if( fwrite(&length, sizeof(length), 1, fd) != 1 ) { | ||
142 | DEBUGF("album_entry_serialize: failed to write length of key\n"); | ||
143 | return ERR_FILE; | ||
144 | } | ||
145 | if( e->key != NULL ) { | ||
146 | // key itself | ||
147 | if( fwrite(e->key, 1, length, fd) != length ) { | ||
148 | DEBUGF("album_entry_serialize: failed to write key\n"); | ||
149 | return ERR_FILE; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | // Artist field | ||
154 | if( fwrite(&e->artist, sizeof(e->artist), 1, fd) != 1 ) { | ||
155 | DEBUGF("album_entry_serialize: failed to write artist\n"); | ||
156 | return ERR_FILE; | ||
157 | } | ||
158 | |||
159 | // count of songs | ||
160 | if( fwrite(&e->size.song_count, sizeof(e->size.song_count), 1, fd) != 1 ) { | ||
161 | DEBUGF("album_entry_serialize: failed to write song_count\n"); | ||
162 | return ERR_FILE; | ||
163 | } | ||
164 | |||
165 | // song[] itself | ||
166 | if( fwrite(e->song, sizeof(*e->song), e->size.song_count, fd) != e->size.song_count ) { | ||
167 | DEBUGF("album_entry_serialize: failed to write songs\n"); | ||
168 | return ERR_FILE; | ||
169 | } | ||
170 | |||
171 | return ERR_NONE; | ||
172 | } | ||
173 | |||
174 | int album_entry_unserialize(struct album_entry **e, FILE *fd) { | ||
175 | uint32_t length; | ||
176 | unsigned char flag; | ||
177 | |||
178 | assert(e != NULL); | ||
179 | assert(fd != NULL); | ||
180 | |||
181 | // First byte we read are the flags | ||
182 | if( fread(&flag, 1, 1, fd) != 1 ) { | ||
183 | DEBUGF("album_entry_unserialize: failed to read flag-byte\n"); | ||
184 | return ERR_FILE; | ||
185 | } | ||
186 | |||
187 | // See what we have: | ||
188 | if( ! FLAG_VALID(flag) ) { | ||
189 | DEBUGF("album_entry_unserialize: flag-byte is invalid\n"); | ||
190 | return ERR_INVALID; | ||
191 | } | ||
192 | |||
193 | // Allocate memory | ||
194 | *e = new_album_entry(0, 0); | ||
195 | if( *e == NULL ) { | ||
196 | DEBUGF("album_entry_unserialize: could not create new album_entry\n"); | ||
197 | return ERR_MALLOC; | ||
198 | } | ||
199 | |||
200 | (*e)->flag = flag; // we had a valid entry, copy it over | ||
201 | |||
202 | // First we read the length of the name field | ||
203 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
204 | DEBUGF("album_entry_unserialize: failed to read name_len\n"); | ||
205 | album_entry_destruct(*e); | ||
206 | return ERR_FILE; | ||
207 | } | ||
208 | |||
209 | // allocate memory for the upcomming name-field | ||
210 | if( do_resize(*e, length, 0, 0) ) { | ||
211 | DEBUGF("album_entry_unserialize: failed to allocate memory for name\n"); | ||
212 | album_entry_destruct(*e); | ||
213 | return ERR_MALLOC; | ||
214 | } | ||
215 | |||
216 | // read it in | ||
217 | if( fread((*e)->name, 1, (*e)->size.name_len, fd) != (*e)->size.name_len ) { | ||
218 | DEBUGF("album_entry_unserialize: failed to read name\n"); | ||
219 | album_entry_destruct(*e); | ||
220 | return ERR_FILE; | ||
221 | } | ||
222 | |||
223 | if( FLAG_DELETED(flag) ) { | ||
224 | // all there is... free some memory | ||
225 | if( do_resize(*e, 0, 0, 0) ) { | ||
226 | DEBUGF("album_entry_unserialize: couldn't free() name\n"); | ||
227 | return ERR_MALLOC; | ||
228 | } | ||
229 | return ERR_NONE; | ||
230 | } | ||
231 | |||
232 | // maybe a key-field | ||
233 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
234 | DEBUGF("album_entry_unserialize: failed to read length of key\n"); | ||
235 | album_entry_destruct(*e); | ||
236 | return ERR_FILE; | ||
237 | } | ||
238 | |||
239 | if( length > 0 ) { | ||
240 | // allocate memory | ||
241 | if( ((*e)->key = malloc(length)) == NULL ) { | ||
242 | DEBUGF("album_entry_unserialize: failed to allocate memory for key\n"); | ||
243 | album_entry_destruct(*e); | ||
244 | return ERR_MALLOC; | ||
245 | } | ||
246 | |||
247 | // read it | ||
248 | if( fread((*e)->key, 1, length, fd) != length ) { | ||
249 | DEBUGF("album_entry_unserialize: failed to read key\n"); | ||
250 | album_entry_destruct(*e); | ||
251 | return ERR_FILE; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | // next the artist field | ||
256 | if( fread(&(*e)->artist, sizeof((*e)->artist), 1, fd) != 1 ) { | ||
257 | DEBUGF("album_entry_unserialize: failed to read artist\n"); | ||
258 | album_entry_destruct(*e); | ||
259 | return ERR_FILE; | ||
260 | } | ||
261 | |||
262 | // Next the count of songs | ||
263 | if( fread(&length, sizeof(length), 1, fd) != 1 ) { | ||
264 | DEBUGF("album_entry_unserialize: failed to read song_count\n"); | ||
265 | album_entry_destruct(*e); | ||
266 | return ERR_FILE; | ||
267 | } | ||
268 | |||
269 | // allocate memory for the upcomming name-field | ||
270 | if( do_resize(*e, (*e)->size.name_len, length, 0) ) { | ||
271 | DEBUGF("album_entry_unserialize: failed to allocate memory for song[]\n"); | ||
272 | album_entry_destruct(*e); | ||
273 | return ERR_MALLOC; | ||
274 | } | ||
275 | |||
276 | // read it in | ||
277 | if( fread((*e)->song, sizeof(*(*e)->song), (*e)->size.song_count, fd) != (*e)->size.song_count ) { | ||
278 | DEBUGF("album_entry_unserialize: failed to read songs\n"); | ||
279 | album_entry_destruct(*e); | ||
280 | return ERR_FILE; | ||
281 | } | ||
282 | |||
283 | return ERR_NONE; | ||
284 | } | ||
285 | |||
286 | int album_entry_write(FILE *fd, struct album_entry *e, struct album_size *s) { | ||
287 | uint32_t i, be; | ||
288 | char pad = 0x00; | ||
289 | |||
290 | assert(e != NULL); | ||
291 | assert(FLAG_VALID(e->flag)); | ||
292 | assert(fd != NULL); | ||
293 | |||
294 | if( FLAG_DELETED(e->flag) ) { // we are deleted, do nothing | ||
295 | return ERR_NONE; | ||
296 | } | ||
297 | |||
298 | // resize-write to size *s | ||
299 | // First check if we are not reducing the size... | ||
300 | if( s != NULL && ( s->name_len < e->size.name_len || s->song_count < e->size.song_count ) ) { | ||
301 | // just do it in 2 steps | ||
302 | if( do_resize(e, s->name_len, s->song_count, 0) ) { | ||
303 | DEBUGF("album_entry_write: failed to reduce size of entry, failing...\n"); | ||
304 | return ERR_MALLOC; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | // album name | ||
309 | if( fwrite(e->name, 1, e->size.name_len, fd) != e->size.name_len ) { | ||
310 | DEBUGF("album_entry_write: failed to write name\n"); | ||
311 | return ERR_FILE; | ||
312 | } | ||
313 | // pad the rest | ||
314 | i = e->size.name_len; | ||
315 | while( s != NULL && s->name_len > i) { | ||
316 | if( fwrite(&pad, 1, 1, fd) == 1 ) { | ||
317 | i++; | ||
318 | continue; | ||
319 | } else { | ||
320 | DEBUGF("album_entry_write: failed to pad name\n"); | ||
321 | return ERR_FILE; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | // artist | ||
326 | be = BE32(e->artist); | ||
327 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
328 | DEBUGF("album_entry_write: failed to write artist\n"); | ||
329 | return ERR_FILE; | ||
330 | } | ||
331 | |||
332 | // song offsets, but in BIG ENDIAN! | ||
333 | // so we need to iterate over each item to convert it | ||
334 | for(i=0; i<e->size.song_count; i++) { | ||
335 | be = BE32(e->song[i]); | ||
336 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
337 | DEBUGF("album_entry_write: failed to write song[%d]\n", i); | ||
338 | return ERR_FILE; | ||
339 | } | ||
340 | } | ||
341 | // pad the rest | ||
342 | be = BE32(0x00000000); | ||
343 | for(; s != NULL && i<s->song_count; i++) { | ||
344 | if( fwrite(&be, sizeof(be), 1, fd) != 1 ) { | ||
345 | DEBUGF("album_entry_write: failed to pad song[]\n"); | ||
346 | return ERR_FILE; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | inline int album_entry_compare(const struct album_entry *a, const struct album_entry *b) { | ||
354 | assert(a != NULL); | ||
355 | assert(b != NULL); | ||
356 | assert(a->key != NULL); | ||
357 | assert(b->key != NULL); | ||
358 | return strcasecmp(a->key, b->key); | ||
359 | } | ||
360 | |||
361 | struct album_size* new_album_size() { | ||
362 | struct album_size *s; | ||
363 | s = (struct album_size*)malloc(sizeof(struct album_size)); | ||
364 | if( s == NULL ) { | ||
365 | DEBUGF("new_album_size: failed to allocate memory\n"); | ||
366 | return NULL; | ||
367 | } | ||
368 | s->name_len = 0; | ||
369 | s->song_count = 0; | ||
370 | |||
371 | return s; | ||
372 | } | ||
373 | |||
374 | inline uint32_t album_size_get_length(const struct album_size *size) { | ||
375 | assert(size != NULL); | ||
376 | return size->name_len + 4 + 4*size->song_count; | ||
377 | } | ||
378 | |||
379 | inline int album_size_max(struct album_size *s, const struct album_entry *e) { | ||
380 | assert(s != NULL); | ||
381 | assert(e != NULL); | ||
382 | assert(FLAG_VALID(e->flag)); | ||
383 | |||
384 | s->name_len = ( s->name_len >= e->size.name_len ? s->name_len : e->size.name_len ); | ||
385 | s->song_count = ( s->song_count >= e->size.song_count ? s->song_count : e->size.song_count ); | ||
386 | return ERR_NONE; | ||
387 | } | ||
388 | |||
389 | int album_size_destruct(struct album_size *s) { | ||
390 | assert(s != NULL); | ||
391 | // nothing to do... | ||
392 | free(s); | ||
393 | return ERR_NONE; | ||
394 | } | ||
395 | |||
396 | int album_entry_add_song_mem(struct album_entry *e, struct album_size *s, const uint32_t song) { | ||
397 | assert(e != NULL); | ||
398 | assert(FLAG_VALID(e->flag)); | ||
399 | |||
400 | if( do_resize(e, e->size.name_len, e->size.song_count+1, 0) ) { | ||
401 | DEBUGF("album_entry_add_song_mem: failed to resize song[]\n"); | ||
402 | return ERR_MALLOC; | ||
403 | } | ||
404 | |||
405 | e->song[e->size.song_count-1] = song; | ||
406 | |||
407 | if( s != NULL) album_size_max(s, e); // can't fail | ||
408 | |||
409 | return ERR_NONE; | ||
410 | } | ||
411 | |||
412 | static int delete_serialized(FILE *fd, struct album_entry *e) { | ||
413 | // the entry should be both, in memory and in file at the current location | ||
414 | // this function will mark the file-entry as deleted | ||
415 | uint32_t size; | ||
416 | unsigned char flag; | ||
417 | |||
418 | assert(fd != NULL); | ||
419 | assert(e != NULL); | ||
420 | assert(FLAG_VALID(e->flag)); | ||
421 | |||
422 | // overwrite the beginning of the serialized data: | ||
423 | flag = FLAG(1, 0); // set the delete flag, clear the spare flags | ||
424 | |||
425 | // First byte we write is the flag-byte to indicate this is a deleted | ||
426 | if( fwrite(&flag, 1, 1, fd) != 1 ) { | ||
427 | DEBUGF("album_entry_delete_serialized: failed to write flag-byte\n"); | ||
428 | return ERR_FILE; | ||
429 | } | ||
430 | |||
431 | // Then we write the length of the COMPLETE entry | ||
432 | size = album_size_get_length(&e->size) + 4; // 4 = overhead for the song[] | ||
433 | if( fwrite(&size, sizeof(size), 1, fd) != 1 ) { | ||
434 | DEBUGF("album_entry_delete_serialized: failed to write len\n"); | ||
435 | return ERR_FILE; | ||
436 | } | ||
437 | |||
438 | return ERR_NONE; | ||
439 | } | ||
440 | |||
441 | int album_entry_add_song_file(FILE *fd, struct album_entry *e, struct album_size *s, const uint32_t song) { | ||
442 | assert(fd != NULL); | ||
443 | assert(e != NULL); | ||
444 | assert(FLAG_VALID(e->flag)); | ||
445 | |||
446 | DEBUGF("album_entry_add_song_file() called\n"); | ||
447 | |||
448 | if( delete_serialized(fd, e) ) { | ||
449 | DEBUGF("album_entry_add_song_file: could not mark as deleted\n"); | ||
450 | return ERR_FILE; | ||
451 | } | ||
452 | |||
453 | return ERR_NO_INPLACE_UPDATE; | ||
454 | } | ||
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 | |||
7 | struct 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 | |||
19 | struct 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 | |||
25 | int 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 | |||
30 | inline 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 | |||
36 | int 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 | |||
42 | int 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 | |||
50 | int 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 | |||
58 | inline 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 | |||
65 | struct 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 | |||
71 | inline 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 | |||
76 | inline 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 | |||
82 | int 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 | |||
88 | int 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 | |||
94 | int 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 | |||
6 | static int add_mem(struct array_buffer *b, void *e); | ||
7 | static int add_file(struct array_buffer *b, void *e); | ||
8 | |||
9 | static int update_entry_mem(struct array_buffer *b, const uint32_t index, uint32_t item); | ||
10 | static int update_entry_file(struct array_buffer *b, const uint32_t index, uint32_t item); | ||
11 | |||
12 | static int find_entry_mem(struct array_buffer *b, const void *needle, uint32_t *index); | ||
13 | static int find_entry_file(struct array_buffer *b, const void *needle, uint32_t *index); | ||
14 | |||
15 | static int sort_mem(struct array_buffer *b); | ||
16 | static 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); | ||
17 | static int sort_mem_merge(uint32_t *dest, uint32_t *src, struct array_buffer *b, uint32_t blocksize); | ||
18 | static int sort_file(struct array_buffer *b); | ||
19 | |||
20 | struct 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 | |||
68 | int 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 | |||
128 | int 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 | |||
179 | static 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 | |||
189 | static 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 | |||
214 | int 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 | |||
268 | inline uint32_t array_buffer_get_next_index(struct array_buffer *b) { | ||
269 | assert( b != NULL ); | ||
270 | return b->count; | ||
271 | } | ||
272 | |||
273 | static 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 | |||
287 | static 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 | |||
366 | int 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 | |||
381 | static 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 | |||
397 | static 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 | |||
457 | int 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 | /* | ||
470 | static 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) ) | ||
490 | static 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 | |||
502 | static 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 | |||
522 | static int sort_file(struct array_buffer *b) { | ||
523 | printf("TODO: file-sorting\n"); // TODO | ||
524 | return ERR_INVALID; | ||
525 | } | ||
526 | |||
527 | int 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 | |||
556 | uint32_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 | |||
591 | uint32_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 | |||
608 | int 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 | |||
8 | struct 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 | |||
47 | struct 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 | |||
97 | int 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 | |||
105 | int 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 | |||
110 | inline 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 | |||
114 | int 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 | |||
123 | int 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 | |||
131 | int 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 | |||
139 | int array_buffer_sort(struct array_buffer *b); | ||
140 | /* | ||
141 | */ | ||
142 | |||
143 | uint32_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 | |||
149 | uint32_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 | |||
154 | int 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 | |||
12 | static int do_resize(struct artist_entry *e, const uint32_t name_len, const uint32_t album_count, const int zero_fill); | ||
13 | |||
14 | struct 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 | |||
37 | int 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 | |||
49 | static 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 | |||
96 | int 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 | |||
100 | int 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 | |||
142 | int 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 | |||
224 | int 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 | |||
274 | inline 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 | |||
282 | struct 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 | |||
295 | inline 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 | |||
300 | inline 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 | |||
306 | int artist_size_destruct(struct artist_size *s) { | ||
307 | assert(s != NULL); | ||
308 | // nothing to do... | ||
309 | free(s); | ||
310 | return ERR_NONE; | ||
311 | } | ||
312 | |||
313 | int 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 | |||
329 | static 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 | |||
357 | int 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 | |||
8 | struct 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 | |||
18 | struct 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 | |||
24 | int 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 | |||
29 | int 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 | |||
35 | int 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 | |||
41 | int 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 | |||
49 | int 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 | |||
55 | inline 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 | |||
62 | struct 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 | |||
68 | inline 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 | |||
73 | inline 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 | |||
79 | int 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 | |||
85 | int 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 | |||
91 | int 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 | |||
41 | static int fill_artist_offsets(struct artist_entry *e, struct artist_size *max_s); | ||
42 | static int fill_album_offsets(struct album_entry *e, struct album_size *max_s); | ||
43 | static int fill_song_offsets(struct song_entry *e, struct song_size *max_s); | ||
44 | static int fill_file_offsets(struct file_entry *e, struct file_size *max_s); | ||
45 | |||
46 | static int do_add(const struct tag_info *t); | ||
47 | |||
48 | static int tag_empty_get(struct tag_info *t); | ||
49 | /* Adds "<no artist tag>" and "<no album tag>" if they're empty | ||
50 | */ | ||
51 | |||
52 | static int free_ram(); | ||
53 | static char in_file = 0; | ||
54 | |||
55 | static int do_write(FILE *fd); | ||
56 | |||
57 | static struct array_buffer *artists; | ||
58 | static struct array_buffer *albums; | ||
59 | static struct array_buffer *songs; | ||
60 | static struct array_buffer *files; | ||
61 | static uint32_t artist_start=0, album_start=0, song_start=0, file_start=0; | ||
62 | static uint32_t artist_entry_len, album_entry_len, song_entry_len, file_entry_len; | ||
63 | static char *artists_file, *albums_file, *songs_file, *files_file; | ||
64 | |||
65 | int 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 | |||
193 | int 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 | |||
219 | static int do_add(const struct tag_info *t) { | ||
220 | struct artist_entry *artist; uint32_t artistn; | ||
221 | struct album_entry *album; uint32_t albumn; | ||
222 | struct song_entry *song; uint32_t songn; | ||
223 | struct file_entry *file; uint32_t filen; | ||
224 | int rc; | ||
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 | |||
319 | static 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 | |||
342 | int db_add(char* file_path, const char* strip_path, const char* add_path) { | ||
343 | char *basename, *dir; | ||
344 | struct tag_info *t; | ||
345 | int rc; | ||
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 | |||
410 | static int free_ram() { | ||
411 | // put things in file that we won't need to search a lot: | ||
412 | // files[] and songs[] are write only | ||
413 | // artists[] and albums[] should stay in memory as long as possible | ||
414 | // albums[] is updated for every song; | ||
415 | // artists[] for every album: artists[] will be the first to loose ram... | ||
416 | if(!( in_file & 0x01 )) { // files[] is still in ram | ||
417 | in_file |= 0x01; | ||
418 | // switch files[] to file-mode | ||
419 | files->file_name = files_file; | ||
420 | files_file = NULL; // since array_buffer will clean this up | ||
421 | return array_buffer_switch_to_file(files); | ||
422 | } else if(!( in_file & 0x02 )) { // song[] is still in ram | ||
423 | in_file |= 0x02; | ||
424 | // switch songs[] to file-mode | ||
425 | songs->file_name = songs_file; | ||
426 | songs_file = NULL; // since array_buffer will clean this up | ||
427 | return array_buffer_switch_to_file(songs); | ||
428 | } else if(!( in_file & 0x04 )) { // artists[] is still in ram | ||
429 | in_file |= 0x04; | ||
430 | // switch artists[] to file-mode | ||
431 | artists->file_name = artists_file; | ||
432 | artists_file = NULL; // since array_buffer will clean this up | ||
433 | return array_buffer_switch_to_file(artists); | ||
434 | } else if(!( in_file & 0x08 )) { // albums[] is still in ram | ||
435 | in_file |= 0x08; | ||
436 | // switch albums[] to file-mode | ||
437 | albums->file_name = albums_file; | ||
438 | albums_file = NULL; // since array_buffer will clean this up | ||
439 | return array_buffer_switch_to_file(albums); | ||
440 | } else { | ||
441 | // all is already in file mode, sorry... | ||
442 | DEBUGF("free_ram: everything is already in file-mode, cannot free more ram, sorry...\n"); | ||
443 | return ERR_MALLOC; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | static 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 | |||
459 | static 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 | |||
472 | static 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 | |||
485 | static 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 | |||
494 | static 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 | |||
543 | int db_write(FILE *fd) { | ||
544 | int rc; | ||
545 | // sort everything | ||
546 | CATCH_ERR( array_buffer_sort(artists) ); | ||
547 | CATCH_ERR( array_buffer_sort(albums) ); | ||
548 | CATCH_ERR( array_buffer_sort(songs) ); | ||
549 | CATCH_ERR( array_buffer_sort(files) ); | ||
550 | |||
551 | CATCH_ERR( do_write(fd) ); | ||
552 | |||
553 | return ERR_NONE; | ||
554 | } | ||
555 | |||
556 | struct tag_info* new_tag_info() { | ||
557 | struct tag_info *t; | ||
558 | t = malloc(sizeof(struct tag_info)); | ||
559 | if( t == NULL ) { | ||
560 | DEBUGF("new_tag_info: could not malloc() for tag_info\n"); | ||
561 | return NULL; | ||
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 | |||
579 | int 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 | |||
9 | struct 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 | |||
23 | int db_construct(); | ||
24 | |||
25 | int db_destruct(); | ||
26 | |||
27 | int db_add(char* file_path, const char* strip_path, const char* add_path); | ||
28 | |||
29 | int db_sort(); | ||
30 | |||
31 | int db_write(FILE *fd); | ||
32 | |||
33 | struct tag_info* new_tag_info(); | ||
34 | |||
35 | int 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 | |||
10 | static int do_resize(struct file_entry *e, const uint32_t name_len, const int zero_fill); | ||
11 | |||
12 | struct 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 | |||
39 | int 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 | |||
50 | static 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 | |||
77 | inline int file_entry_resize(struct file_entry *e, const uint32_t name_len) { | ||
78 | return do_resize(e, name_len, 1); | ||
79 | } | ||
80 | |||
81 | int 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 | |||
124 | int 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 | |||
184 | int 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 | |||
232 | inline 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 | |||
238 | struct 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 | |||
250 | inline uint32_t file_size_get_length(const struct file_size *size) { | ||
251 | assert(size != NULL); | ||
252 | return size->name_len + 12; | ||
253 | } | ||
254 | |||
255 | inline 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 | |||
263 | int 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 | |||
8 | struct 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 | |||
21 | struct 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 | |||
27 | int 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 | |||
32 | inline 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 | |||
37 | int 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 | |||
42 | int 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 | |||
50 | int 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 | |||
55 | inline 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 | |||
62 | struct 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 | |||
68 | inline 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 | |||
73 | inline 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 | |||
79 | int 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 | |||
6 | int 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 | |||
8 | struct 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 | |||
37 | int 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 | |||
11 | extern 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 | |||
18 | static char* strip_path = NULL; | ||
19 | static char* add_path = NULL; | ||
20 | |||
21 | static 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 | |||
27 | static 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 | |||
91 | int 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 | |||
14 | static size_t total=0; | ||
15 | static size_t max_total=0; | ||
16 | |||
17 | struct size_array { | ||
18 | void *ptr; | ||
19 | size_t size; | ||
20 | } sizes[1000]; | ||
21 | #define NOT_FOUND 1001 | ||
22 | static unsigned long count=0; | ||
23 | |||
24 | int out_of_memory = 1000000; | ||
25 | |||
26 | void *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 | |||
54 | static 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 | |||
64 | void 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 | |||
80 | void *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 | |||
115 | void 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 | |||
10 | void *do_malloc(size_t size); | ||
11 | void do_free(void *ptr); | ||
12 | void *do_realloc(void *ptr, size_t size); | ||
13 | |||
14 | void 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 | |||
7 | int errno; | ||
8 | |||
9 | int 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 | |||
16 | int mem_failure() { | ||
17 | fprintf(stderr, "Could not (re)allocate memory\n"); | ||
18 | return 1; | ||
19 | } | ||
20 | |||
21 | int 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 | |||
10 | static int do_resize(struct song_entry *e, const uint32_t name_len, const uint32_t genre_len, const int zero_fill); | ||
11 | |||
12 | struct 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 | |||
47 | int 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 | |||
59 | static 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 | |||
106 | inline 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 | |||
110 | int 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 | |||
196 | int 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 | |||
312 | int 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 | |||
412 | inline 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 | |||
418 | struct 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 | |||
431 | inline 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 | |||
436 | inline 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 | |||
445 | int 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 | |||
8 | struct 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 | |||
30 | struct 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 | |||
36 | int 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 | |||
41 | inline 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 | |||
46 | int 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 | |||
51 | int 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 | |||
59 | int 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 | |||
64 | inline 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 | |||
71 | struct 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 | |||
77 | inline 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 | |||
82 | inline 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 | |||
88 | int 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 | |||
7 | int 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 | |||
3 | int 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 | |||
6 | char *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 | |||
4 | char *create_unique_name(char *buffer, const char *prefix, const char *suffix, int digits); | ||
5 | |||
6 | #endif | ||