diff options
author | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-01 17:29:44 +0000 |
---|---|---|
committer | Michiel Van Der Kolk <not.valid@email.address> | 2005-07-01 17:29:44 +0000 |
commit | c735ed79142a0260fc05d58cb0672e5d1720a26a (patch) | |
tree | 3fd67eb9fb598e9b8bc0c3003ec3bf037379ebb4 /apps/database.c | |
parent | 4ec80704d5f34a167d45db7539f47b12cc23c59e (diff) | |
download | rockbox-c735ed79142a0260fc05d58cb0672e5d1720a26a.tar.gz rockbox-c735ed79142a0260fc05d58cb0672e5d1720a26a.zip |
First runtime database support, self repairing, only playcount works for now,
which is still rather crude; playcount gets increased even if the song started playback but was skipped... track rating should be trivial to add, autorating also works since its based on playcount.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6969 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/database.c')
-rw-r--r-- | apps/database.c | 217 |
1 files changed, 193 insertions, 24 deletions
diff --git a/apps/database.c b/apps/database.c index c753362cd1..06bac4a10c 100644 --- a/apps/database.c +++ b/apps/database.c | |||
@@ -43,14 +43,26 @@ | |||
43 | #include "keyboard.h" | 43 | #include "keyboard.h" |
44 | #include "database.h" | 44 | #include "database.h" |
45 | #include "autoconf.h" | 45 | #include "autoconf.h" |
46 | #include "playback.h" | ||
47 | #include "logf.h" | ||
46 | 48 | ||
47 | #undef NEW_DB_CODE | 49 | /* internal functions */ |
48 | 50 | void writetagdbheader(void); | |
49 | #ifdef NEW_DB_CODE | 51 | void writefentry(void); |
52 | void getfentrybyoffset(int offset); | ||
53 | void update_fentryoffsets(int start, int end); | ||
54 | void writerundbheader(void); | ||
55 | void getrundbentrybyoffset(int offset); | ||
56 | void writerundbentry(void); | ||
57 | int getfentrybyfilename(char *fname); | ||
58 | int getfentrybyhash(int hash); | ||
59 | int deletefentry(char *fname); | ||
60 | int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes); | ||
61 | int tagdb_shiftup(int targetoffset, int startingoffset, int bytes); | ||
62 | |||
50 | static char sbuf[1024]; | 63 | static char sbuf[1024]; |
51 | static struct file_entry fe; | 64 | static struct file_entry fe; |
52 | static int currentfeoffset, currentferecord; | 65 | static int currentfeoffset, currentferecord; |
53 | #endif | ||
54 | 66 | ||
55 | int tagdb_fd = -1; | 67 | int tagdb_fd = -1; |
56 | int tagdb_initialized = 0; | 68 | int tagdb_initialized = 0; |
@@ -63,7 +75,7 @@ int tagdb_init(void) | |||
63 | int i, *p; | 75 | int i, *p; |
64 | #endif | 76 | #endif |
65 | 77 | ||
66 | tagdb_fd = open(ROCKBOX_DIR "/rockbox.id3db", O_RDONLY); | 78 | tagdb_fd = open(ROCKBOX_DIR "/rockbox.tagdb", O_RDWR); |
67 | if (tagdb_fd < 0) { | 79 | if (tagdb_fd < 0) { |
68 | DEBUGF("Failed opening database\n"); | 80 | DEBUGF("Failed opening database\n"); |
69 | return -1; | 81 | return -1; |
@@ -111,19 +123,26 @@ void tagdb_shutdown(void) | |||
111 | 123 | ||
112 | /* NOTE: All these functions below are yet untested. */ | 124 | /* NOTE: All these functions below are yet untested. */ |
113 | 125 | ||
114 | #ifdef NEW_DB_CODE | ||
115 | |||
116 | /*** TagDatabase code ***/ | 126 | /*** TagDatabase code ***/ |
117 | 127 | ||
118 | void writetagdbheader() { | 128 | void writetagdbheader(void) { |
119 | lseek(tagdb_fd,0,SEEK_SET); | 129 | lseek(tagdb_fd,0,SEEK_SET); |
120 | write(tagdb_fd, &tagdbheader, 68); | 130 | write(tagdb_fd, &tagdbheader, 68); |
131 | fsync(tagdb_fd); | ||
132 | } | ||
133 | |||
134 | void writefentry(void) { | ||
135 | lseek(tagdb_fd,currentfeoffset,SEEK_SET); | ||
136 | write(tagdb_fd,sbuf,tagdbheader.filelen); | ||
137 | write(tagdb_fd,&fe.hash,12); | ||
138 | fsync(tagdb_fd); | ||
121 | } | 139 | } |
122 | 140 | ||
123 | void getfentrybyoffset(int offset) { | 141 | void getfentrybyoffset(int offset) { |
142 | memset(&fe,0,sizeof(struct file_entry)); | ||
124 | lseek(tagdb_fd,offset,SEEK_SET); | 143 | lseek(tagdb_fd,offset,SEEK_SET); |
125 | fread(tagdb_fd,sbuf,tagdbheader.filelen); | 144 | read(tagdb_fd,sbuf,tagdbheader.filelen); |
126 | fread(tagdb_fd,&fe+sizeof(char *),12); | 145 | read(tagdb_fd,&fe.hash,12); |
127 | fe.name=sbuf; | 146 | fe.name=sbuf; |
128 | currentfeoffset=offset; | 147 | currentfeoffset=offset; |
129 | currentferecord=(offset-tagdbheader.filestart)/FILEENTRY_SIZE; | 148 | currentferecord=(offset-tagdbheader.filestart)/FILEENTRY_SIZE; |
@@ -138,7 +157,7 @@ int getfentrybyfilename(char *fname) { | |||
138 | int mid=(min+max)/2; | 157 | int mid=(min+max)/2; |
139 | int compare; | 158 | int compare; |
140 | getfentrybyrecord(mid); | 159 | getfentrybyrecord(mid); |
141 | compare=strcasecmp(fname,fe.name)); | 160 | compare=strcasecmp(fname,fe.name); |
142 | if(compare==0) | 161 | if(compare==0) |
143 | return 1; | 162 | return 1; |
144 | else if(compare<0) | 163 | else if(compare<0) |
@@ -164,7 +183,7 @@ int deletefentry(char *fname) { | |||
164 | return 0; | 183 | return 0; |
165 | int restrecord = currentferecord+1; | 184 | int restrecord = currentferecord+1; |
166 | if(currentferecord!=tagdbheader.filecount) /* file is not last entry */ | 185 | if(currentferecord!=tagdbheader.filecount) /* file is not last entry */ |
167 | shiftdown(FILERECORD2OFFSET(currentferecord),FILERECORD2OFFSET(restrecord),(tagdbheader.filecount-restrecord)*FILEENTRY_SIZE); | 186 | tagdb_shiftdown(FILERECORD2OFFSET(currentferecord),FILERECORD2OFFSET(restrecord),(tagdbheader.filecount-restrecord)*FILEENTRY_SIZE); |
168 | ftruncate(tagdb_fd,lseek(tagdb_fd,0,SEEK_END)-FILEENTRY_SIZE); | 187 | ftruncate(tagdb_fd,lseek(tagdb_fd,0,SEEK_END)-FILEENTRY_SIZE); |
169 | tagdbheader.filecount--; | 188 | tagdbheader.filecount--; |
170 | update_fentryoffsets(restrecord,tagdbheader.filecount); | 189 | update_fentryoffsets(restrecord,tagdbheader.filecount); |
@@ -172,9 +191,9 @@ int deletefentry(char *fname) { | |||
172 | return 1; | 191 | return 1; |
173 | } | 192 | } |
174 | 193 | ||
175 | int update_fentryoffsets(int start, int end) { | 194 | void update_fentryoffsets(int start, int end) { |
176 | int i; | 195 | int i; |
177 | for(int i=start;i<end;i++) { | 196 | for(i=start;i<end;i++) { |
178 | getfentrybyrecord(i); | 197 | getfentrybyrecord(i); |
179 | if(fe.songentry!=-1) { | 198 | if(fe.songentry!=-1) { |
180 | int p; | 199 | int p; |
@@ -187,7 +206,7 @@ int update_fentryoffsets(int start, int end) { | |||
187 | } | 206 | } |
188 | } | 207 | } |
189 | if(fe.rundbentry!=-1) { | 208 | if(fe.rundbentry!=-1) { |
190 | splash(HZ*2, "o.o.. found a rundbentry? o.o; didn't update it, update the code o.o;"); | 209 | splash(HZ*2,true, "o.o.. found a rundbentry? o.o; didn't update it, update the code o.o;"); |
191 | } | 210 | } |
192 | } | 211 | } |
193 | } | 212 | } |
@@ -195,18 +214,18 @@ int update_fentryoffsets(int start, int end) { | |||
195 | int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes) { | 214 | int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes) { |
196 | int amount; | 215 | int amount; |
197 | if(targetoffset>=startingoffset) { | 216 | if(targetoffset>=startingoffset) { |
198 | splash(HZ*2,"Woah. no beeping way. (tagdb_shiftdown)"); | 217 | splash(HZ*2,true,"Woah. no beeping way. (tagdb_shiftdown)"); |
199 | return 0; | 218 | return 0; |
200 | } | 219 | } |
201 | lseek(tagdb_fd,startingoffset,SEEK_SET); | 220 | lseek(tagdb_fd,startingoffset,SEEK_SET); |
202 | while(amount=read(tagdb_fd,sbuf,bytes > 1024 ? 1024 : bytes)) { | 221 | while((amount=read(tagdb_fd,sbuf,(bytes > 1024) ? 1024 : bytes))) { |
203 | int written; | 222 | int written; |
204 | startingoffset+=amount; | 223 | startingoffset+=amount; |
205 | lseek(tagdb_fd,targetoffset,SEEK_SET); | 224 | lseek(tagdb_fd,targetoffset,SEEK_SET); |
206 | written=write(tagdb_fd,sbuf,amount); | 225 | written=write(tagdb_fd,sbuf,amount); |
207 | targetoffset+=written; | 226 | targetoffset+=written; |
208 | if(amount!=written) { | 227 | if(amount!=written) { |
209 | splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftdown)"); | 228 | splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftdown)"); |
210 | return 0; | 229 | return 0; |
211 | } | 230 | } |
212 | lseek(tagdb_fd,startingoffset,SEEK_SET); | 231 | lseek(tagdb_fd,startingoffset,SEEK_SET); |
@@ -218,9 +237,8 @@ int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes) { | |||
218 | int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) { | 237 | int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) { |
219 | int amount,amount2; | 238 | int amount,amount2; |
220 | int readpos,writepos,filelen; | 239 | int readpos,writepos,filelen; |
221 | int ok; | ||
222 | if(targetoffset<=startingoffset) { | 240 | if(targetoffset<=startingoffset) { |
223 | splash(HZ*2,"Um. no. (tagdb_shiftup)"); | 241 | splash(HZ*2,true,"Um. no. (tagdb_shiftup)"); |
224 | return 0; | 242 | return 0; |
225 | } | 243 | } |
226 | filelen=lseek(tagdb_fd,0,SEEK_END); | 244 | filelen=lseek(tagdb_fd,0,SEEK_END); |
@@ -232,13 +250,13 @@ int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) { | |||
232 | lseek(tagdb_fd,readpos,SEEK_SET); | 250 | lseek(tagdb_fd,readpos,SEEK_SET); |
233 | amount2=read(tagdb_fd,sbuf,amount); | 251 | amount2=read(tagdb_fd,sbuf,amount); |
234 | if(amount2!=amount) { | 252 | if(amount2!=amount) { |
235 | splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); | 253 | splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); |
236 | return 0; | 254 | return 0; |
237 | } | 255 | } |
238 | lseek(tagdb_fd,writepos,SEEK_SET); | 256 | lseek(tagdb_fd,writepos,SEEK_SET); |
239 | amount=write(tagdb_fd,sbuf,amount2); | 257 | amount=write(tagdb_fd,sbuf,amount2); |
240 | if(amount2!=amount) { | 258 | if(amount2!=amount) { |
241 | splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); | 259 | splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); |
242 | return 0; | 260 | return 0; |
243 | } | 261 | } |
244 | bytes-=amount; | 262 | bytes-=amount; |
@@ -246,17 +264,168 @@ int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) { | |||
246 | if(bytes==0) | 264 | if(bytes==0) |
247 | return 1; | 265 | return 1; |
248 | else { | 266 | else { |
249 | splash(HZ*2,"Something went wrong, >.>;; (tagdb_shiftup)"); | 267 | splash(HZ*2,true,"Something went wrong, >.>;; (tagdb_shiftup)"); |
250 | return 0; | 268 | return 0; |
251 | } | 269 | } |
252 | } | 270 | } |
253 | 271 | ||
254 | /*** end TagDatabase code ***/ | 272 | /*** end TagDatabase code ***/ |
255 | 273 | ||
274 | int rundb_fd = -1; | ||
275 | int rundb_initialized = 0; | ||
276 | struct rundb_header rundbheader; | ||
277 | |||
278 | static int valid_file, currentreoffset,rundbsize; | ||
279 | static struct rundb_entry rundbentry; | ||
280 | |||
256 | /*** RuntimeDatabase code ***/ | 281 | /*** RuntimeDatabase code ***/ |
257 | 282 | ||
283 | void rundb_track_changed(struct track_info *ti) { | ||
284 | increaseplaycount(); | ||
285 | logf("rundb new track: %s", ti->id3.path); | ||
286 | loadruntimeinfo(ti->id3.path); | ||
287 | } | ||
258 | 288 | ||
289 | int rundb_init(void) | ||
290 | { | ||
291 | unsigned char* ptr = (char*)&rundbheader.version; | ||
292 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
293 | int i, *p; | ||
294 | #endif | ||
259 | 295 | ||
260 | /*** end RuntimeDatabase code ***/ | 296 | if(!tagdb_initialized) /* forget it.*/ |
297 | return -1; | ||
298 | |||
299 | rundb_fd = open(ROCKBOX_DIR "/rockbox.rundb", O_CREAT|O_RDWR); | ||
300 | if (rundb_fd < 0) { | ||
301 | DEBUGF("Failed opening database\n"); | ||
302 | return -1; | ||
303 | } | ||
304 | if(read(rundb_fd, &rundbheader, 8)!=8) { | ||
305 | ptr[0]=ptr[1]='R'; | ||
306 | ptr[2]='D'; | ||
307 | ptr[3]=0x1; | ||
308 | rundbheader.entrycount=0; | ||
309 | writerundbheader(); | ||
310 | } | ||
311 | |||
312 | if (ptr[0] != 'R' || | ||
313 | ptr[1] != 'R' || | ||
314 | ptr[2] != 'D') | ||
315 | { | ||
316 | splash(HZ,true,"Not a rockbox runtime database!"); | ||
317 | return -1; | ||
318 | } | ||
319 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
320 | p=(int *)&rundbheader; | ||
321 | for(i=0;i<2;i++) { | ||
322 | *p=BE32(*p); | ||
323 | p++; | ||
324 | } | ||
325 | #endif | ||
326 | if ( (rundbheader.version&0xFF) != RUNDB_VERSION) | ||
327 | { | ||
328 | splash(HZ,true,"Unsupported runtime database version %d!", rundbheader.version&0xFF); | ||
329 | return -1; | ||
330 | } | ||
331 | |||
332 | rundb_initialized = 1; | ||
333 | audio_set_track_changed_event(&rundb_track_changed); | ||
334 | memset(&rundbentry,0,sizeof(struct rundb_entry)); | ||
335 | rundbsize=lseek(rundb_fd,0,SEEK_END); | ||
336 | return 0; | ||
337 | } | ||
261 | 338 | ||
339 | void writerundbheader(void) { | ||
340 | lseek(rundb_fd,0,SEEK_SET); | ||
341 | write(rundb_fd, &rundbheader, 8); | ||
342 | fsync(rundb_fd); | ||
343 | } | ||
344 | |||
345 | #define getrundbentrybyrecord(_x_) getrundbentrybyoffset(8+_x_*20) | ||
346 | |||
347 | void getrundbentrybyoffset(int offset) { | ||
348 | lseek(rundb_fd,offset,SEEK_SET); | ||
349 | read(rundb_fd,&rundbentry,20); | ||
350 | currentreoffset=offset; | ||
351 | #ifdef ROCKBOX_LITTLE_ENDIAN | ||
352 | rundbentry.fileentry=BE32(rundbentry.fileentry); | ||
353 | rundbentry.hash=BE32(rundbentry.hash); | ||
354 | rundbentry.rating=BE16(rundbentry.rating); | ||
355 | rundbentry.voladjust=BE16(rundbentry.voladjust); | ||
356 | rundbentry.playcount=BE32(rundbentry.playcount); | ||
357 | rundbentry.lastplayed=BE32(rundbentry.lastplayed); | ||
262 | #endif | 358 | #endif |
359 | } | ||
360 | |||
361 | int getrundbentrybyhash(int hash) { | ||
362 | int min=0; | ||
363 | for(min=0;min<rundbheader.entrycount;min++) { | ||
364 | getrundbentrybyrecord(min); | ||
365 | if(hash==rundbentry.hash) | ||
366 | return 1; | ||
367 | } | ||
368 | memset(&rundbentry,0,sizeof(struct rundb_entry)); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | void writerundbentry(void) { | ||
373 | if(rundbentry.hash==0) // 0 = invalid rundb info. | ||
374 | return; | ||
375 | lseek(rundb_fd,currentreoffset,SEEK_SET); | ||
376 | write(rundb_fd,&rundbentry,20); | ||
377 | fsync(rundb_fd); | ||
378 | } | ||
379 | |||
380 | void loadruntimeinfo(char *filename) { | ||
381 | memset(&rundbentry,0,sizeof(struct rundb_entry)); | ||
382 | valid_file=0; | ||
383 | if(!getfentrybyfilename(filename)) | ||
384 | return; /* file is not in tagdatabase, could not load. */ | ||
385 | valid_file=1; | ||
386 | if(fe.rundbentry!=-1&&fe.rundbentry<rundbsize) { | ||
387 | logf("load rundbentry: 0x%x",fe.rundbentry); | ||
388 | getrundbentrybyoffset(fe.rundbentry); | ||
389 | if(fe.hash!=rundbentry.hash) { | ||
390 | logf("Rundb: Hash mismatch. trying to repair entry.",fe.hash,rundbentry.hash); | ||
391 | addrundbentry(); | ||
392 | } | ||
393 | } | ||
394 | else // add new rundb entry. | ||
395 | addrundbentry(); | ||
396 | } | ||
397 | |||
398 | void addrundbentry() { | ||
399 | // first search for an entry with an equal hash. | ||
400 | if(getrundbentrybyhash(fe.hash)) { | ||
401 | logf("Found existing rundb entry: 0x%x",currentreoffset); | ||
402 | fe.rundbentry=currentreoffset; | ||
403 | writefentry(); | ||
404 | return; | ||
405 | } | ||
406 | rundbheader.entrycount++; | ||
407 | writerundbheader(); | ||
408 | fe.rundbentry=currentreoffset=lseek(rundb_fd,0,SEEK_END); | ||
409 | logf("Add rundb entry: 0x%x hash: 0x%x",fe.rundbentry,fe.hash); | ||
410 | rundbentry.hash=fe.hash; | ||
411 | rundbentry.fileentry=currentfeoffset; | ||
412 | writefentry(); | ||
413 | writerundbentry(); | ||
414 | rundbsize=lseek(rundb_fd,0,SEEK_END); | ||
415 | } | ||
416 | |||
417 | void increaseplaycount(void) { | ||
418 | if(rundbentry.hash==0) // 0 = invalid rundb info. | ||
419 | return; | ||
420 | rundbentry.playcount++; | ||
421 | writerundbentry(); | ||
422 | } | ||
423 | |||
424 | void setrating(int rating) { | ||
425 | if(rundbentry.hash==0) // 0 = invalid rundb info. | ||
426 | return; | ||
427 | rundbentry.rating=rating; | ||
428 | writerundbentry(); | ||
429 | } | ||
430 | |||
431 | /*** end RuntimeDatabase code ***/ | ||