1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
34 #include "main_menu.h"
38 #include "filetypes.h"
39 #include "applimits.h"
47 #if CONFIG_CODEC == SWCODEC
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
];
76 int tagdb_initialized
= 0;
77 struct tagdb_header tagdbheader
;
81 unsigned char* ptr
= (unsigned char*)&tagdbheader
.version
;
82 #ifdef ROCKBOX_LITTLE_ENDIAN
86 tagdb_fd
= open(ROCKBOX_DIR
"/rockbox.tagdb", O_RDWR
);
88 DEBUGF("Failed opening database\n");
91 read(tagdb_fd
, &tagdbheader
, 68);
97 gui_syncsplash(HZ
, true,
98 (unsigned char *)"Not a rockbox ID3 database!");
101 #ifdef ROCKBOX_LITTLE_ENDIAN
102 p
=(int *)&tagdbheader
;
108 if ( (tagdbheader
.version
&0xFF) != TAGDB_VERSION
)
110 gui_syncsplash(HZ
, true,
111 (unsigned char *)"Unsupported database version %d!",
112 tagdbheader
.version
&0xFF);
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!");
124 tagdb_initialized
= 1;
128 void tagdb_shutdown(void)
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);
146 void writefentry(struct mp3entry
*id
)
148 if(!id
->fileentryoffset
)
150 lseek(tagdb_fd
,id
->fileentryoffset
,SEEK_SET
);
151 write(tagdb_fd
,id
->path
,tagdbheader
.filelen
);
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
)
176 int max
=tagdbheader
.filecount
;
180 getfentrybyrecord(id
,mid
);
181 compare
=strcasecmp(id
->path
,sbuf
);
189 clearfileentryinfo(id
);
194 int getfentrybyhash(int hash
)
197 for(min
=0;min
<tagdbheader
.filecount
;min
++) {
198 getfentrybyrecord(min
);
205 int deletefentry(char *fname
)
207 if(!getfentrybyfilename(fname
))
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
);
221 void update_fentryoffsets(int start
, int end
)
224 for(i
=start
;i
<end
;i
++) {
225 getfentrybyrecord(i
);
226 if(fe
.songentry
!=-1) {
228 lseek(tagdb_fd
,fe
.songentry
+tagdbheader
.songlen
+8,SEEK_SET
);
229 read(tagdb_fd
,&p
,sizeof(int));
230 if(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
)
246 if(targetoffset
>=startingoffset
) {
247 gui_syncsplash(HZ
*2,true,"Woah. no beeping way. (tagdb_shiftdown)");
250 lseek(tagdb_fd
,startingoffset
,SEEK_SET
);
251 while((amount
=read(tagdb_fd
,sbuf
,(bytes
> 1024) ? 1024 : bytes
))) {
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)");
262 lseek(tagdb_fd
,startingoffset
,SEEK_SET
);
268 int tagdb_shiftup(int targetoffset
, int startingoffset
, int bytes
)
271 int readpos
,writepos
,filelen
;
272 if(targetoffset
<=startingoffset
) {
273 gui_syncsplash(HZ
*2,true,"Um. no. (tagdb_shiftup)");
276 filelen
=lseek(tagdb_fd
,0,SEEK_END
);
277 readpos
=startingoffset
+bytes
;
279 amount
=bytes
>1024 ? 1024 : bytes
;
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)");
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)");
301 gui_syncsplash(HZ
*2,true,"Something went wrong, >.>;; (tagdb_shiftup)");
307 /*** end TagDatabase code ***/
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
);
325 void rundb_track_change(struct mp3entry
*id
) {
329 void rundb_buffer_track(struct mp3entry
*id
, bool last_track
) {
339 unsigned char* ptr
= (unsigned char*)&rundbheader
.version
;
340 #ifdef ROCKBOX_LITTLE_ENDIAN
343 if(!tagdb_initialized
) /* forget it.*/
346 if(!global_settings
.runtimedb
) /* user doesn't care */
349 rundb_fd
= open(ROCKBOX_DIR
"/rockbox.rundb", O_CREAT
|O_RDWR
);
351 DEBUGF("Failed opening database\n");
354 if(read(rundb_fd
, &rundbheader
, 8)!=8) {
358 rundbheader
.entrycount
=0;
366 gui_syncsplash(HZ
, true,
367 (unsigned char *)"Not a rockbox runtime database!");
370 #ifdef ROCKBOX_LITTLE_ENDIAN
371 p
=(int *)&rundbheader
;
377 if ( (rundbheader
.version
&0xFF) != RUNDB_VERSION
)
379 gui_syncsplash(HZ
, true, (unsigned char *)
380 "Unsupported runtime database version %d!",
381 rundbheader
.version
&0xFF);
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
);
395 void rundb_shutdown(void)
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
);
430 int getrundbentrybyhash(struct mp3entry
*id
)
433 for(min
=0;min
<rundbheader
.entrycount
;min
++) {
434 getrundbentrybyrecord(id
,min
);
435 if(id
->filehash
==id
->rundbhash
)
438 clearruntimeinfo(id
);
442 void writerundbentry(struct mp3entry
*id
)
444 if(id
->rundbhash
==0) /* 0 = invalid rundb info. */
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
) {
463 void clearfileentryinfo(struct mp3entry
*id
) {
464 id
->fileentryoffset
=0;
466 id
->songentryoffset
=0;
467 id
->rundbentryoffset
=0;
470 void clearruntimeinfo(struct mp3entry
*id
) {
478 void loadruntimeinfo(struct mp3entry
*id
)
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
);
497 #ifdef ROCKBOX_HAS_LOGF
498 if(!findrundbentry(id
))
499 logf("rundb:no entry and not found.");
505 int findrundbentry(struct mp3entry
*id
) {
506 if(getrundbentrybyhash(id
)) {
507 logf("Found existing rundb entry: 0x%x",id
->rundbentryoffset
);
511 clearruntimeinfo(id
);
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
++;
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
;
527 rundbsize
=lseek(rundb_fd
,0,SEEK_END
);
530 /*** end RuntimeDatabase code ***/