Fix b&w LCD targets.
[maemo-rb.git] / apps / database.c
blobdf069538a2ef9225e517e00c1442b918c7b742c1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Michiel van der Kolk
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
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"
47 #if CONFIG_CODEC == SWCODEC
48 #include "playback.h"
49 #else
50 #include "mpeg.h"
51 #endif
53 #include "logf.h"
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);
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);
73 static char sbuf[MAX_PATH];
75 int tagdb_fd = -1;
76 int tagdb_initialized = 0;
77 struct tagdb_header tagdbheader;
79 int tagdb_init(void)
81 unsigned char* ptr = (unsigned char*)&tagdbheader.version;
82 #ifdef ROCKBOX_LITTLE_ENDIAN
83 int i, *p;
84 #endif
86 tagdb_fd = open(ROCKBOX_DIR "/rockbox.tagdb", O_RDWR);
87 if (tagdb_fd < 0) {
88 DEBUGF("Failed opening database\n");
89 return -1;
91 read(tagdb_fd, &tagdbheader, 68);
93 if (ptr[0] != 'R' ||
94 ptr[1] != 'D' ||
95 ptr[2] != 'B')
97 gui_syncsplash(HZ, true,
98 (unsigned char *)"Not a rockbox ID3 database!");
99 return -1;
101 #ifdef ROCKBOX_LITTLE_ENDIAN
102 p=(int *)&tagdbheader;
103 for(i=0;i<17;i++) {
104 *p=BE32(*p);
105 p++;
107 #endif
108 if ( (tagdbheader.version&0xFF) != TAGDB_VERSION)
110 gui_syncsplash(HZ, true,
111 (unsigned char *)"Unsupported database version %d!",
112 tagdbheader.version&0xFF);
113 return -1;
116 if (tagdbheader.songstart > tagdbheader.filestart ||
117 tagdbheader.albumstart > tagdbheader.songstart ||
118 tagdbheader.artiststart > tagdbheader.albumstart)
120 gui_syncsplash(HZ, true, (unsigned char *)"Corrupt ID3 database!");
121 return -1;
124 tagdb_initialized = 1;
125 return 0;
128 void tagdb_shutdown(void)
130 if (tagdb_fd >= 0)
131 close(tagdb_fd);
132 tagdb_initialized = 0;
135 /* NOTE: All these functions below are yet untested. */
137 /*** TagDatabase code ***/
139 void writetagdbheader(void)
141 lseek(tagdb_fd,0,SEEK_SET);
142 write(tagdb_fd, &tagdbheader, 68);
143 fsync(tagdb_fd);
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);
160 void getfentrybyoffset(struct mp3entry *id,int offset)
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);
171 #define getfentrybyrecord(_y_,_x_) getfentrybyoffset(_y_,FILERECORD2OFFSET(_x_))
173 int getfentrybyfilename(struct mp3entry *id)
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;
189 clearfileentryinfo(id);
190 return 0;
193 #if 0
194 int getfentrybyhash(int hash)
196 int min=0;
197 for(min=0;min<tagdbheader.filecount;min++) {
198 getfentrybyrecord(min);
199 if(hash==fe.hash)
200 return 1;
202 return 0;
205 int deletefentry(char *fname)
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;
221 void update_fentryoffsets(int start, int end)
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));
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;");
243 int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes)
245 int amount;
246 if(targetoffset>=startingoffset) {
247 gui_syncsplash(HZ*2,true,"Woah. no beeping way. (tagdb_shiftdown)");
248 return 0;
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;
262 lseek(tagdb_fd,startingoffset,SEEK_SET);
263 bytes-=amount;
265 return 1;
268 int tagdb_shiftup(int targetoffset, int startingoffset, int bytes)
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;
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;
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;
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;
305 #endif
307 /*** end TagDatabase code ***/
309 int rundb_fd = -1;
310 int rundb_initialized = 0;
311 struct rundb_header rundbheader;
313 static long rundbsize;
315 /*** RuntimeDatabase code ***/
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);
325 void rundb_track_change(struct mp3entry *id) {
326 id->playcount++;
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);
337 int rundb_init(void)
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;
346 if(!global_settings.runtimedb) /* user doesn't care */
347 return -1;
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;
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();
362 if (ptr[0] != 'R' ||
363 ptr[1] != 'R' ||
364 ptr[2] != 'D')
366 gui_syncsplash(HZ, true,
367 (unsigned char *)"Not a rockbox runtime database!");
368 return -1;
370 #ifdef ROCKBOX_LITTLE_ENDIAN
371 p=(int *)&rundbheader;
372 for(i=0;i<2;i++) {
373 *p=BE32(*p);
374 p++;
376 #endif
377 if ( (rundbheader.version&0xFF) != RUNDB_VERSION)
379 gui_syncsplash(HZ, true, (unsigned char *)
380 "Unsupported runtime database version %d!",
381 rundbheader.version&0xFF);
382 return -1;
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.");
391 rundbsize=lseek(rundb_fd,0,SEEK_END);
392 return 0;
395 void rundb_shutdown(void)
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);
405 void writerundbheader(void)
407 lseek(rundb_fd,0,SEEK_SET);
408 write(rundb_fd, &rundbheader, 8);
411 #define getrundbentrybyrecord(_y_,_x_) getrundbentrybyoffset(_y_,8+_x_*20)
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
430 int getrundbentrybyhash(struct mp3entry *id)
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;
438 clearruntimeinfo(id);
439 return 0;
442 void writerundbentry(struct mp3entry *id)
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);
455 void writeruntimeinfo(struct mp3entry *id) {
456 logf("rundb write");
457 if(!id->rundbhash)
458 addrundbentry(id);
459 else
460 writerundbentry(id);
463 void clearfileentryinfo(struct mp3entry *id) {
464 id->fileentryoffset=0;
465 id->filehash=0;
466 id->songentryoffset=0;
467 id->rundbentryoffset=0;
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;
478 void loadruntimeinfo(struct mp3entry *id)
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. */
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);
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
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;
511 clearruntimeinfo(id);
512 return 0;
515 void addrundbentry(struct mp3entry *id)
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);
530 /*** end RuntimeDatabase code ***/