diff options
Diffstat (limited to 'apps/database.c')
-rw-r--r-- | apps/database.c | 530 |
1 files changed, 0 insertions, 530 deletions
diff --git a/apps/database.c b/apps/database.c deleted file mode 100644 index df069538a2..0000000000 --- a/apps/database.c +++ /dev/null | |||
@@ -1,530 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 by Michiel van der Kolk | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #define _GNU_SOURCE | ||
20 | #include <stdio.h> | ||
21 | #include <string.h> | ||
22 | #include "file.h" | ||
23 | #include "screens.h" | ||
24 | #include "kernel.h" | ||
25 | #include "tree.h" | ||
26 | #include "lcd.h" | ||
27 | #include "font.h" | ||
28 | #include "settings.h" | ||
29 | #include "icons.h" | ||
30 | #include "status.h" | ||
31 | #include "debug.h" | ||
32 | #include "button.h" | ||
33 | #include "menu.h" | ||
34 | #include "main_menu.h" | ||
35 | #include "mpeg.h" | ||
36 | #include "misc.h" | ||
37 | #include "ata.h" | ||
38 | #include "filetypes.h" | ||
39 | #include "applimits.h" | ||
40 | #include "icons.h" | ||
41 | #include "lang.h" | ||
42 | #include "keyboard.h" | ||
43 | #include "database.h" | ||
44 | #include "autoconf.h" | ||
45 | #include "splash.h" | ||
46 | |||
47 | #if CONFIG_CODEC == SWCODEC | ||
48 | #include "playback.h" | ||
49 | #else | ||
50 | #include "mpeg.h" | ||
51 | #endif | ||
52 | |||
53 | #include "logf.h" | ||
54 | |||
55 | /* internal functions */ | ||
56 | void writetagdbheader(void); | ||
57 | void writefentry(struct mp3entry *id); | ||
58 | void getfentrybyoffset(struct mp3entry *id,int offset); | ||
59 | void update_fentryoffsets(int start, int end); | ||
60 | void writerundbheader(void); | ||
61 | void getrundbentrybyoffset(struct mp3entry *id,int offset); | ||
62 | void writerundbentry(struct mp3entry *id); | ||
63 | int getfentrybyfilename(struct mp3entry *id); | ||
64 | void clearfileentryinfo(struct mp3entry *id); | ||
65 | void clearruntimeinfo(struct mp3entry *id); | ||
66 | int findrundbentry(struct mp3entry *id); | ||
67 | |||
68 | int getfentrybyhash(int hash); | ||
69 | int deletefentry(char *fname); | ||
70 | int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes); | ||
71 | int tagdb_shiftup(int targetoffset, int startingoffset, int bytes); | ||
72 | |||
73 | static char sbuf[MAX_PATH]; | ||
74 | |||
75 | int tagdb_fd = -1; | ||
76 | int tagdb_initialized = 0; | ||
77 | struct tagdb_header tagdbheader; | ||
78 | |||
79 | int tagdb_init(void) | ||
80 | { | ||
81 | unsigned char* ptr = (unsigned char*)&tagdbheader.version; | ||
82 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
83 | int i, *p; | ||
84 | #endif | ||
85 | |||
86 | tagdb_fd = open(ROCKBOX_DIR "/rockbox.tagdb", O_RDWR); | ||
87 | if (tagdb_fd < 0) { | ||
88 | DEBUGF("Failed opening database\n"); | ||
89 | return -1; | ||
90 | } | ||
91 | read(tagdb_fd, &tagdbheader, 68); | ||
92 | |||
93 | if (ptr[0] != 'R' || | ||
94 | ptr[1] != 'D' || | ||
95 | ptr[2] != 'B') | ||
96 | { | ||
97 | gui_syncsplash(HZ, true, | ||
98 | (unsigned char *)"Not a rockbox ID3 database!"); | ||
99 | return -1; | ||
100 | } | ||
101 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
102 | p=(int *)&tagdbheader; | ||
103 | for(i=0;i<17;i++) { | ||
104 | *p=BE32(*p); | ||
105 | p++; | ||
106 | } | ||
107 | #endif | ||
108 | if ( (tagdbheader.version&0xFF) != TAGDB_VERSION) | ||
109 | { | ||
110 | gui_syncsplash(HZ, true, | ||
111 | (unsigned char *)"Unsupported database version %d!", | ||
112 | tagdbheader.version&0xFF); | ||
113 | return -1; | ||
114 | } | ||
115 | |||
116 | if (tagdbheader.songstart > tagdbheader.filestart || | ||
117 | tagdbheader.albumstart > tagdbheader.songstart || | ||
118 | tagdbheader.artiststart > tagdbheader.albumstart) | ||
119 | { | ||
120 | gui_syncsplash(HZ, true, (unsigned char *)"Corrupt ID3 database!"); | ||
121 | return -1; | ||
122 | } | ||
123 | |||
124 | tagdb_initialized = 1; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | void tagdb_shutdown(void) | ||
129 | { | ||
130 | if (tagdb_fd >= 0) | ||
131 | close(tagdb_fd); | ||
132 | tagdb_initialized = 0; | ||
133 | } | ||
134 | |||
135 | /* NOTE: All these functions below are yet untested. */ | ||
136 | |||
137 | /*** TagDatabase code ***/ | ||
138 | |||
139 | void writetagdbheader(void) | ||
140 | { | ||
141 | lseek(tagdb_fd,0,SEEK_SET); | ||
142 | write(tagdb_fd, &tagdbheader, 68); | ||
143 | fsync(tagdb_fd); | ||
144 | } | ||
145 | |||
146 | void writefentry(struct mp3entry *id) | ||
147 | { long temp; | ||
148 | if(!id->fileentryoffset) | ||
149 | return; | ||
150 | lseek(tagdb_fd,id->fileentryoffset,SEEK_SET); | ||
151 | write(tagdb_fd,id->path,tagdbheader.filelen); | ||
152 | temp=id->filehash; | ||
153 | write(tagdb_fd,&temp,4); | ||
154 | temp=id->songentryoffset; | ||
155 | write(tagdb_fd,&temp,4); | ||
156 | temp=id->rundbentryoffset; | ||
157 | write(tagdb_fd,&temp,4); | ||
158 | } | ||
159 | |||
160 | void getfentrybyoffset(struct mp3entry *id,int offset) | ||
161 | { | ||
162 | clearfileentryinfo(id); | ||
163 | lseek(tagdb_fd,offset,SEEK_SET); | ||
164 | read(tagdb_fd,sbuf,tagdbheader.filelen); | ||
165 | id->fileentryoffset=offset; | ||
166 | read(tagdb_fd,&id->filehash,4); | ||
167 | read(tagdb_fd,&id->songentryoffset,4); | ||
168 | read(tagdb_fd,&id->rundbentryoffset,4); | ||
169 | } | ||
170 | |||
171 | #define getfentrybyrecord(_y_,_x_) getfentrybyoffset(_y_,FILERECORD2OFFSET(_x_)) | ||
172 | |||
173 | int getfentrybyfilename(struct mp3entry *id) | ||
174 | { | ||
175 | int min=0; | ||
176 | int max=tagdbheader.filecount; | ||
177 | while(min<max) { | ||
178 | int mid=(min+max)/2; | ||
179 | int compare; | ||
180 | getfentrybyrecord(id,mid); | ||
181 | compare=strcasecmp(id->path,sbuf); | ||
182 | if(compare==0) | ||
183 | return 1; | ||
184 | else if(compare<0) | ||
185 | max=mid; | ||
186 | else | ||
187 | min=mid+1; | ||
188 | } | ||
189 | clearfileentryinfo(id); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | #if 0 | ||
194 | int getfentrybyhash(int hash) | ||
195 | { | ||
196 | int min=0; | ||
197 | for(min=0;min<tagdbheader.filecount;min++) { | ||
198 | getfentrybyrecord(min); | ||
199 | if(hash==fe.hash) | ||
200 | return 1; | ||
201 | } | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | int deletefentry(char *fname) | ||
206 | { | ||
207 | if(!getfentrybyfilename(fname)) | ||
208 | return 0; | ||
209 | int restrecord = currentferecord+1; | ||
210 | if(currentferecord!=tagdbheader.filecount) /* file is not last entry */ | ||
211 | tagdb_shiftdown(FILERECORD2OFFSET(currentferecord), | ||
212 | FILERECORD2OFFSET(restrecord), | ||
213 | (tagdbheader.filecount-restrecord)*FILEENTRY_SIZE); | ||
214 | ftruncate(tagdb_fd,lseek(tagdb_fd,0,SEEK_END)-FILEENTRY_SIZE); | ||
215 | tagdbheader.filecount--; | ||
216 | update_fentryoffsets(restrecord,tagdbheader.filecount); | ||
217 | writetagdbheader(); | ||
218 | return 1; | ||
219 | } | ||
220 | |||
221 | void update_fentryoffsets(int start, int end) | ||
222 | { | ||
223 | int i; | ||
224 | for(i=start;i<end;i++) { | ||
225 | getfentrybyrecord(i); | ||
226 | if(fe.songentry!=-1) { | ||
227 | int p; | ||
228 | lseek(tagdb_fd,fe.songentry+tagdbheader.songlen+8,SEEK_SET); | ||
229 | read(tagdb_fd,&p,sizeof(int)); | ||
230 | if(p!=currentfeoffset) { | ||
231 | p=currentfeoffset; | ||
232 | lseek(tagdb_fd,fe.songentry+tagdbheader.songlen+8,SEEK_SET); | ||
233 | write(tagdb_fd,&p,sizeof(int)); | ||
234 | } | ||
235 | } | ||
236 | if(fe.rundbentry!=-1) { | ||
237 | gui_syncsplash(HZ*2,true, "o.o.. found a rundbentry? o.o; didn't update " | ||
238 | "it, update the code o.o;"); | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes) | ||
244 | { | ||
245 | int amount; | ||
246 | if(targetoffset>=startingoffset) { | ||
247 | gui_syncsplash(HZ*2,true,"Woah. no beeping way. (tagdb_shiftdown)"); | ||
248 | return 0; | ||
249 | } | ||
250 | lseek(tagdb_fd,startingoffset,SEEK_SET); | ||
251 | while((amount=read(tagdb_fd,sbuf,(bytes > 1024) ? 1024 : bytes))) { | ||
252 | int written; | ||
253 | startingoffset+=amount; | ||
254 | lseek(tagdb_fd,targetoffset,SEEK_SET); | ||
255 | written=write(tagdb_fd,sbuf,amount); | ||
256 | targetoffset+=written; | ||
257 | if(amount!=written) { | ||
258 | gui_syncsplash(HZ*2,true,"Something went very wrong. expect database " | ||
259 | "corruption. (tagdb_shiftdown)"); | ||
260 | return 0; | ||
261 | } | ||
262 | lseek(tagdb_fd,startingoffset,SEEK_SET); | ||
263 | bytes-=amount; | ||
264 | } | ||
265 | return 1; | ||
266 | } | ||
267 | |||
268 | int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) | ||
269 | { | ||
270 | int amount,amount2; | ||
271 | int readpos,writepos,filelen; | ||
272 | if(targetoffset<=startingoffset) { | ||
273 | gui_syncsplash(HZ*2,true,"Um. no. (tagdb_shiftup)"); | ||
274 | return 0; | ||
275 | } | ||
276 | filelen=lseek(tagdb_fd,0,SEEK_END); | ||
277 | readpos=startingoffset+bytes; | ||
278 | do { | ||
279 | amount=bytes>1024 ? 1024 : bytes; | ||
280 | readpos-=amount; | ||
281 | writepos=readpos+targetoffset-startingoffset; | ||
282 | lseek(tagdb_fd,readpos,SEEK_SET); | ||
283 | amount2=read(tagdb_fd,sbuf,amount); | ||
284 | if(amount2!=amount) { | ||
285 | gui_syncsplash(HZ*2,true,"Something went very wrong. expect database " | ||
286 | "corruption. (tagdb_shiftup)"); | ||
287 | return 0; | ||
288 | } | ||
289 | lseek(tagdb_fd,writepos,SEEK_SET); | ||
290 | amount=write(tagdb_fd,sbuf,amount2); | ||
291 | if(amount2!=amount) { | ||
292 | gui_syncsplash(HZ*2,true,"Something went very wrong. expect database " | ||
293 | "corruption. (tagdb_shiftup)"); | ||
294 | return 0; | ||
295 | } | ||
296 | bytes-=amount; | ||
297 | } while (amount>0); | ||
298 | if(bytes==0) | ||
299 | return 1; | ||
300 | else { | ||
301 | gui_syncsplash(HZ*2,true,"Something went wrong, >.>;; (tagdb_shiftup)"); | ||
302 | return 0; | ||
303 | } | ||
304 | } | ||
305 | #endif | ||
306 | |||
307 | /*** end TagDatabase code ***/ | ||
308 | |||
309 | int rundb_fd = -1; | ||
310 | int rundb_initialized = 0; | ||
311 | struct rundb_header rundbheader; | ||
312 | |||
313 | static long rundbsize; | ||
314 | |||
315 | /*** RuntimeDatabase code ***/ | ||
316 | |||
317 | void rundb_unbuffer_track(struct mp3entry *id, bool last_track) { | ||
318 | writeruntimeinfo(id); | ||
319 | if(last_track) { | ||
320 | fsync(rundb_fd); | ||
321 | fsync(tagdb_fd); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | void rundb_track_change(struct mp3entry *id) { | ||
326 | id->playcount++; | ||
327 | } | ||
328 | |||
329 | void rundb_buffer_track(struct mp3entry *id, bool last_track) { | ||
330 | loadruntimeinfo(id); | ||
331 | if(last_track) { | ||
332 | fsync(rundb_fd); | ||
333 | fsync(tagdb_fd); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | int rundb_init(void) | ||
338 | { | ||
339 | unsigned char* ptr = (unsigned char*)&rundbheader.version; | ||
340 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
341 | int i, *p; | ||
342 | #endif | ||
343 | if(!tagdb_initialized) /* forget it.*/ | ||
344 | return -1; | ||
345 | |||
346 | if(!global_settings.runtimedb) /* user doesn't care */ | ||
347 | return -1; | ||
348 | |||
349 | rundb_fd = open(ROCKBOX_DIR "/rockbox.rundb", O_CREAT|O_RDWR); | ||
350 | if (rundb_fd < 0) { | ||
351 | DEBUGF("Failed opening database\n"); | ||
352 | return -1; | ||
353 | } | ||
354 | if(read(rundb_fd, &rundbheader, 8)!=8) { | ||
355 | ptr[0]=ptr[1]='R'; | ||
356 | ptr[2]='D'; | ||
357 | ptr[3]=0x1; | ||
358 | rundbheader.entrycount=0; | ||
359 | writerundbheader(); | ||
360 | } | ||
361 | |||
362 | if (ptr[0] != 'R' || | ||
363 | ptr[1] != 'R' || | ||
364 | ptr[2] != 'D') | ||
365 | { | ||
366 | gui_syncsplash(HZ, true, | ||
367 | (unsigned char *)"Not a rockbox runtime database!"); | ||
368 | return -1; | ||
369 | } | ||
370 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
371 | p=(int *)&rundbheader; | ||
372 | for(i=0;i<2;i++) { | ||
373 | *p=BE32(*p); | ||
374 | p++; | ||
375 | } | ||
376 | #endif | ||
377 | if ( (rundbheader.version&0xFF) != RUNDB_VERSION) | ||
378 | { | ||
379 | gui_syncsplash(HZ, true, (unsigned char *) | ||
380 | "Unsupported runtime database version %d!", | ||
381 | rundbheader.version&0xFF); | ||
382 | return -1; | ||
383 | } | ||
384 | |||
385 | rundb_initialized = 1; | ||
386 | audio_set_track_buffer_event(&rundb_buffer_track); | ||
387 | audio_set_track_changed_event(&rundb_track_change); | ||
388 | audio_set_track_unbuffer_event(&rundb_unbuffer_track); | ||
389 | logf("rundb inited."); | ||
390 | |||
391 | rundbsize=lseek(rundb_fd,0,SEEK_END); | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | void rundb_shutdown(void) | ||
396 | { | ||
397 | if (rundb_fd >= 0) | ||
398 | close(rundb_fd); | ||
399 | rundb_initialized = 0; | ||
400 | audio_set_track_buffer_event(NULL); | ||
401 | audio_set_track_unbuffer_event(NULL); | ||
402 | audio_set_track_changed_event(NULL); | ||
403 | } | ||
404 | |||
405 | void writerundbheader(void) | ||
406 | { | ||
407 | lseek(rundb_fd,0,SEEK_SET); | ||
408 | write(rundb_fd, &rundbheader, 8); | ||
409 | } | ||
410 | |||
411 | #define getrundbentrybyrecord(_y_,_x_) getrundbentrybyoffset(_y_,8+_x_*20) | ||
412 | |||
413 | void getrundbentrybyoffset(struct mp3entry *id,int offset) { | ||
414 | lseek(rundb_fd,offset+4,SEEK_SET); // skip fileentry offset | ||
415 | id->rundbentryoffset=offset; | ||
416 | read(rundb_fd,&id->rundbhash,4); | ||
417 | read(rundb_fd,&id->rating,2); | ||
418 | read(rundb_fd,&id->voladjust,2); | ||
419 | read(rundb_fd,&id->playcount,4); | ||
420 | read(rundb_fd,&id->lastplayed,4); | ||
421 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
422 | id->rundbhash=BE32(id->rundbhash); | ||
423 | id->rating=BE16(id->rating); | ||
424 | id->voladjust=BE16(id->voladjust); | ||
425 | id->playcount=BE32(id->playcount); | ||
426 | id->lastplayed=BE32(id->lastplayed); | ||
427 | #endif | ||
428 | } | ||
429 | |||
430 | int getrundbentrybyhash(struct mp3entry *id) | ||
431 | { | ||
432 | int min=0; | ||
433 | for(min=0;min<rundbheader.entrycount;min++) { | ||
434 | getrundbentrybyrecord(id,min); | ||
435 | if(id->filehash==id->rundbhash) | ||
436 | return 1; | ||
437 | } | ||
438 | clearruntimeinfo(id); | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | void writerundbentry(struct mp3entry *id) | ||
443 | { | ||
444 | if(id->rundbhash==0) /* 0 = invalid rundb info. */ | ||
445 | return; | ||
446 | lseek(rundb_fd,id->rundbentryoffset,SEEK_SET); | ||
447 | write(rundb_fd,&id->fileentryoffset,4); | ||
448 | write(rundb_fd,&id->rundbhash,4); | ||
449 | write(rundb_fd,&id->rating,2); | ||
450 | write(rundb_fd,&id->voladjust,2); | ||
451 | write(rundb_fd,&id->playcount,4); | ||
452 | write(rundb_fd,&id->lastplayed,4); | ||
453 | } | ||
454 | |||
455 | void writeruntimeinfo(struct mp3entry *id) { | ||
456 | logf("rundb write"); | ||
457 | if(!id->rundbhash) | ||
458 | addrundbentry(id); | ||
459 | else | ||
460 | writerundbentry(id); | ||
461 | } | ||
462 | |||
463 | void clearfileentryinfo(struct mp3entry *id) { | ||
464 | id->fileentryoffset=0; | ||
465 | id->filehash=0; | ||
466 | id->songentryoffset=0; | ||
467 | id->rundbentryoffset=0; | ||
468 | } | ||
469 | |||
470 | void clearruntimeinfo(struct mp3entry *id) { | ||
471 | id->rundbhash=0; | ||
472 | id->rating=0; | ||
473 | id->voladjust=0; | ||
474 | id->playcount=0; | ||
475 | id->lastplayed=0; | ||
476 | } | ||
477 | |||
478 | void loadruntimeinfo(struct mp3entry *id) | ||
479 | { | ||
480 | logf("rundb load"); | ||
481 | clearruntimeinfo(id); | ||
482 | clearfileentryinfo(id); | ||
483 | if(!getfentrybyfilename(id)) { | ||
484 | logf("tagdb fail: %s",id->path); | ||
485 | return; /* file is not in tagdatabase, could not load. */ | ||
486 | } | ||
487 | if(id->rundbentryoffset!=-1 && id->rundbentryoffset<rundbsize) { | ||
488 | logf("load rundbentry: 0x%x", id->rundbentryoffset); | ||
489 | getrundbentrybyoffset(id, id->rundbentryoffset); | ||
490 | if(id->filehash != id->rundbhash) { | ||
491 | logf("Rundb: Hash mismatch. trying to repair entry.", | ||
492 | id->filehash, id->rundbhash); | ||
493 | findrundbentry(id); | ||
494 | } | ||
495 | } | ||
496 | else | ||
497 | #ifdef ROCKBOX_HAS_LOGF | ||
498 | if(!findrundbentry(id)) | ||
499 | logf("rundb:no entry and not found."); | ||
500 | #else | ||
501 | findrundbentry(id); | ||
502 | #endif | ||
503 | } | ||
504 | |||
505 | int findrundbentry(struct mp3entry *id) { | ||
506 | if(getrundbentrybyhash(id)) { | ||
507 | logf("Found existing rundb entry: 0x%x",id->rundbentryoffset); | ||
508 | writefentry(id); | ||
509 | return 1; | ||
510 | } | ||
511 | clearruntimeinfo(id); | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | void addrundbentry(struct mp3entry *id) | ||
516 | { | ||
517 | /* first search for an entry with an equal hash. */ | ||
518 | /* if(findrundbentry(id)) | ||
519 | return; disabled cause this SHOULD have been checked at the buffer event.. */ | ||
520 | rundbheader.entrycount++; | ||
521 | writerundbheader(); | ||
522 | id->rundbentryoffset=lseek(rundb_fd,0,SEEK_END); | ||
523 | logf("Add rundb entry: 0x%x hash: 0x%x",id->rundbentryoffset,id->filehash); | ||
524 | id->rundbhash=id->filehash; | ||
525 | writefentry(id); | ||
526 | writerundbentry(id); | ||
527 | rundbsize=lseek(rundb_fd,0,SEEK_END); | ||
528 | } | ||
529 | |||
530 | /*** end RuntimeDatabase code ***/ | ||