Fix for ff/rw in long MP3 files.
[kugel-rb.git] / apps / dbtree.c
blob263abf4082ce4a6d5ba0d9ae2b2a3fdffc3f2d64
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Björn Stenberg
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 "wps.h"
39 #include "filetypes.h"
40 #include "applimits.h"
41 #include "dbtree.h"
42 #include "icons.h"
43 #include "lang.h"
44 #include "keyboard.h"
45 #include "autoconf.h"
47 static int db_play_folder(struct tree_context* c);
48 static int db_search(struct tree_context* c, char* string);
50 static char searchstring[32];
52 int db_load(struct tree_context* c)
54 int i, offset, rc;
55 int dcachesize = global_settings.max_files_in_dir * sizeof(struct entry);
56 int itemcount, stringlen, hits=0;
57 unsigned long* nptr = (void*) c->name_buffer;
58 unsigned long* dptr = c->dircache;
59 unsigned long* safeplace = NULL;
60 int safeplacelen = 0;
62 int table = c->currtable;
63 int extra = c->currextra;
65 char* end_of_nbuf = c->name_buffer + c->name_buffer_size;
67 if (!tagdb_initialized) {
68 DEBUGF("ID3 database is not initialized.\n");
69 c->filesindir = 0;
70 return 0;
73 c->dentry_size = 2;
74 c->dirfull = false;
76 DEBUGF("db_load() table: %d extra: 0x%x firstpos: %d\n", table, extra, c->firstpos);
78 if (!table) {
79 table = root;
80 c->currtable = table;
83 switch (table) {
84 case root: {
85 static const int tables[] = {allartists, allalbums, allsongs,
86 search };
87 char* nbuf = (char*)nptr;
88 char* labels[] = { str(LANG_ID3DB_ARTISTS),
89 str(LANG_ID3DB_ALBUMS),
90 str(LANG_ID3DB_SONGS),
91 str(LANG_ID3DB_SEARCH)};
92 DEBUGF("dbload table root\n");
93 for (i=0; i < 4; i++) {
94 strcpy(nbuf, labels[i]);
95 dptr[0] = (unsigned long)nbuf;
96 dptr[1] = tables[i];
97 nbuf += strlen(nbuf) + 1;
98 dptr += 2;
100 c->dirlength = c->filesindir = i;
101 return i;
104 case search: {
105 static const int tables[] = {searchartists,
106 searchalbums,
107 searchsongs};
108 char* nbuf = (char*)nptr;
109 char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
110 str(LANG_ID3DB_SEARCH_ALBUMS),
111 str(LANG_ID3DB_SEARCH_SONGS)};
112 DEBUGF("dbload table search\n");
113 for (i=0; i < 3; i++) {
114 strcpy(nbuf, labels[i]);
115 dptr[0] = (unsigned long)nbuf;
116 dptr[1] = tables[i];
117 nbuf += strlen(nbuf) + 1;
118 dptr += 2;
120 c->dirlength = c->filesindir = i;
121 return i;
124 case searchartists:
125 case searchalbums:
126 case searchsongs:
127 DEBUGF("dbload table searchsongs/searchartists/searchalbums\n");
128 i = db_search(c, searchstring);
129 c->dirlength = c->filesindir = i;
130 if (c->dirfull) {
131 splash(HZ, true, "%s %s",
132 str(LANG_SHOWDIR_ERROR_BUFFER),
133 str(LANG_SHOWDIR_ERROR_FULL));
134 c->dirfull = false;
136 else
137 splash(HZ, true, str(LANG_ID3DB_MATCHES), i);
138 return i;
140 case allsongs:
141 DEBUGF("dbload table allsongs\n");
142 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
143 itemcount = tagdbheader.songcount;
144 stringlen = tagdbheader.songlen;
145 break;
147 case allalbums:
148 DEBUGF("dbload table allalbums\n");
149 offset = tagdbheader.albumstart + c->firstpos * ALBUMENTRY_SIZE;
150 itemcount = tagdbheader.albumcount;
151 stringlen = tagdbheader.albumlen;
152 break;
154 case allartists:
155 DEBUGF("dbload table allartists\n");
156 offset = tagdbheader.artiststart + c->firstpos * ARTISTENTRY_SIZE;
157 itemcount = tagdbheader.artistcount;
158 stringlen = tagdbheader.artistlen;
159 break;
161 case albums4artist:
162 DEBUGF("dbload table albums4artist\n");
163 /* 'extra' is offset to the artist */
164 safeplacelen = tagdbheader.albumarraylen * 4;
165 safeplace = (void*)(end_of_nbuf - safeplacelen);
166 lseek(tagdb_fd, extra + tagdbheader.artistlen, SEEK_SET);
167 rc = read(tagdb_fd, safeplace, safeplacelen);
168 if (rc < safeplacelen)
169 return -1;
171 #ifdef ROCKBOX_LITTLE_ENDIAN
172 for (i=0; i<tagdbheader.albumarraylen; i++)
173 safeplace[i] = BE32(safeplace[i]);
174 #endif
175 offset = safeplace[0];
176 itemcount = tagdbheader.albumarraylen;
177 stringlen = tagdbheader.albumlen;
178 break;
180 case songs4album:
181 DEBUGF("dbload table songs4album\n");
182 /* 'extra' is offset to the album */
183 safeplacelen = tagdbheader.songarraylen * 4;
184 safeplace = (void*)(end_of_nbuf - safeplacelen);
185 lseek(tagdb_fd, extra + tagdbheader.albumlen + 4, SEEK_SET);
186 rc = read(tagdb_fd, safeplace, safeplacelen);
187 if (rc < safeplacelen)
188 return -1;
190 #ifdef ROCKBOX_LITTLE_ENDIAN
191 for (i=0; i<tagdbheader.songarraylen; i++) {
192 safeplace[i] = BE32(safeplace[i]);
193 DEBUGF("db_load songs4album song %d: 0x%x\n",i,safeplace[i]);
195 #endif
196 offset = safeplace[0];
197 itemcount = tagdbheader.songarraylen;
198 stringlen = tagdbheader.songlen;
199 break;
201 case songs4artist:
202 DEBUGF("dbload table songs4artist\n");
203 /* 'extra' is offset to the artist, used as filter */
204 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
205 itemcount = tagdbheader.songcount;
206 stringlen = tagdbheader.songlen;
207 break;
209 default:
210 DEBUGF("Unsupported table %d\n", table);
211 return -1;
213 end_of_nbuf -= safeplacelen;
215 c->dirlength = itemcount;
216 itemcount -= c->firstpos;
218 if (!safeplace)
219 lseek(tagdb_fd, offset, SEEK_SET);
221 /* name_buffer (nptr) contains only names, null terminated.
222 the first word of dcache (dptr) is a pointer to the name,
223 the rest is table specific. see below. */
225 for ( i=0; i < itemcount; i++ ) {
226 int rc, skip=0;
227 int intbuf[4];
229 if (safeplace) {
230 if (!safeplace[i]) {
231 c->dirlength = i;
232 break;
234 lseek(tagdb_fd, safeplace[i], SEEK_SET);
235 offset = safeplace[i];
238 /* read name */
239 rc = read(tagdb_fd, nptr, stringlen);
240 if (rc < stringlen)
242 DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc);
243 return -1;
246 switch (table) {
247 case allsongs:
248 case songs4album:
249 case songs4artist:
250 rc = read(tagdb_fd, intbuf, 12);
251 skip = SONGENTRY_SIZE-stringlen-12; /* skip the rest of the song info */
252 if (rc < 12) {
253 DEBUGF("%d read(%d) returned %d\n", i, 12, rc);
254 return -1;
256 /* continue to next song if wrong artist */
257 if (table == songs4artist && (int)BE32(intbuf[0]) != extra) {
258 lseek(tagdb_fd, skip, SEEK_CUR);
259 continue;
262 /* save offset of filename */
263 dptr[1] = BE32(intbuf[2]);
264 break;
266 case allalbums:
267 case albums4artist:
268 /* save offset of this album */
269 skip = tagdbheader.songarraylen * 4 + 4;
270 dptr[1] = offset;
271 break;
273 case allartists:
274 /* save offset of this artist */
275 skip = tagdbheader.albumarraylen * 4;
276 dptr[1] = offset;
277 break;
280 /* store name pointer in dir cache */
281 dptr[0] = (unsigned long)nptr;
283 if (skip)
284 lseek(tagdb_fd, skip, SEEK_CUR);
286 hits++;
288 if(table==songs4artist)
289 c->dirlength=hits;
291 /* next name is stored immediately after this */
292 nptr = (void*)nptr + strlen((char*)nptr) + 1;
293 if ((void*)nptr + stringlen > (void*)end_of_nbuf) {
294 c->dirfull = true;
295 break;
298 /* limit dir buffer */
299 dptr = (void*)dptr + c->dentry_size * sizeof(int);
300 if ((void*)(dptr + c->dentry_size) >
301 (void*)(c->dircache + dcachesize))
303 c->dirfull = true;
304 break;
307 if (!safeplace)
308 offset += stringlen + skip;
311 if (c->currtable == albums4artist && !c->dirfull) {
312 strcpy((char*)nptr, str(LANG_ID3DB_ALL_SONGS));
313 dptr[0] = (unsigned long)nptr;
314 dptr[1] = extra; /* offset to artist */
315 hits++;
318 c->filesindir = hits;
320 return hits;
323 static int db_search(struct tree_context* c, char* string)
325 int i, count, size, hits=0;
326 long start;
328 char* nptr = c->name_buffer;
329 const char* end_of_nbuf = nptr + c->name_buffer_size;
331 unsigned long* dptr = c->dircache;
332 const long dcachesize = global_settings.max_files_in_dir *
333 sizeof(struct entry);
335 switch (c->currtable) {
336 case searchartists:
337 start = tagdbheader.artiststart;
338 count = tagdbheader.artistcount;
339 size = ARTISTENTRY_SIZE;
340 break;
342 case searchalbums:
343 start = tagdbheader.albumstart;
344 count = tagdbheader.albumcount;
345 size = ALBUMENTRY_SIZE;
346 break;
348 case searchsongs:
349 start = tagdbheader.songstart;
350 count = tagdbheader.songcount;
351 size = SONGENTRY_SIZE;
352 break;
354 default:
355 DEBUGF("Invalid table %d\n", c->currtable);
356 return 0;
359 lseek(tagdb_fd, start, SEEK_SET);
361 for (i=0; i<count; i++) {
362 if (read(tagdb_fd, nptr, size) < size) {
363 DEBUGF("Short read(%d) in db_search()\n",size);
364 break;
366 if (strcasestr(nptr, string)) {
367 hits++;
369 dptr[0] = (unsigned long)nptr;
370 if (c->currtable == searchsongs) {
371 /* store offset of filename */
372 dptr[1] = BE32(*((long*)(nptr + tagdbheader.songlen + 8)));
374 else
375 /* store offset of database record */
376 dptr[1] = start + i * size;
378 dptr += 2;
380 /* limit dir buffer */
381 if ((void*)(dptr + c->dentry_size) >
382 (void*)(c->dircache + dcachesize))
384 c->dirfull = true;
385 break;
388 nptr += strlen(nptr) + 1;
389 while ((unsigned long)nptr & 3)
390 nptr++;
392 /* limit name buffer */
393 if ((void*)nptr + size > (void*)end_of_nbuf) {
394 c->dirfull = true;
395 break;
400 return hits;
403 int db_enter(struct tree_context* c)
405 int rc = 0;
406 int offset = (c->dircursor + c->dirstart) * c->dentry_size + 1;
407 int newextra = ((int*)c->dircache)[offset];
409 if (c->dirlevel >= MAX_DIR_LEVELS)
410 return 0;
412 c->dirpos[c->dirlevel] = c->dirstart;
413 c->cursorpos[c->dirlevel] = c->dircursor;
414 c->table_history[c->dirlevel] = c->currtable;
415 c->extra_history[c->dirlevel] = c->currextra;
416 c->pos_history[c->dirlevel] = c->firstpos;
417 c->dirlevel++;
419 switch (c->currtable) {
420 case root:
421 c->currtable = newextra;
422 c->currextra = newextra;
423 break;
425 case allartists:
426 case searchartists:
427 c->currtable = albums4artist;
428 c->currextra = newextra;
429 break;
431 case allalbums:
432 case albums4artist:
433 case searchalbums:
434 /* virtual <all albums> entry points to the artist,
435 all normal entries point to the album */
436 if (newextra < tagdbheader.albumstart)
437 c->currtable = songs4artist;
438 else
439 c->currtable = songs4album;
441 c->currextra = newextra;
442 break;
444 case allsongs:
445 case songs4album:
446 case songs4artist:
447 case searchsongs:
448 c->dirlevel--;
449 if (db_play_folder(c) >= 0)
450 rc = 2;
451 break;
453 case search:
454 rc = kbd_input(searchstring, sizeof(searchstring));
455 if (rc == -1 || !searchstring[0])
456 c->dirlevel--;
457 else
458 c->currtable = newextra;
459 break;
461 default:
462 c->dirlevel--;
463 break;
466 c->dirstart = c->dircursor = c->firstpos = 0;
468 return rc;
471 void db_exit(struct tree_context* c)
473 c->dirlevel--;
474 c->dirstart = c->dirpos[c->dirlevel];
475 c->dircursor = c->cursorpos[c->dirlevel];
476 c->currtable = c->table_history[c->dirlevel];
477 c->currextra = c->extra_history[c->dirlevel];
478 c->firstpos = c->pos_history[c->dirlevel];
481 static int db_play_folder(struct tree_context* c)
483 char buf[MAX_PATH];
484 int rc, i;
485 int filenum = c->dircursor + c->dirstart;
487 if (playlist_create(NULL, NULL) < 0) {
488 DEBUGF("Failed creating playlist\n");
489 return -1;
492 /* TODO: add support for very long tables */
494 for (i=0; i < c->filesindir; i++) {
495 int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 1];
496 lseek(tagdb_fd, pathoffset, SEEK_SET);
497 rc = read(tagdb_fd, buf, sizeof(buf));
498 if (rc < tagdbheader.songlen) {
499 DEBUGF("short path read(%ld) = %d\n", sizeof(buf), rc);
500 return -2;
503 playlist_insert_track(NULL, buf, PLAYLIST_INSERT, false);
506 if (global_settings.playlist_shuffle)
507 filenum = playlist_shuffle(current_tick, filenum);
508 if (!global_settings.play_selected)
509 filenum = 0;
511 playlist_start(filenum,0);
513 return 0;
516 #ifdef HAVE_LCD_BITMAP
517 const char* db_get_icon(struct tree_context* c)
518 #else
519 int db_get_icon(struct tree_context* c)
520 #endif
522 int icon;
524 switch (c->currtable)
526 case allsongs:
527 case songs4album:
528 case songs4artist:
529 case searchsongs:
530 icon = File;
531 break;
533 default:
534 icon = Folder;
535 break;
538 #ifdef HAVE_LCD_BITMAP
539 return bitmap_icons_6x8[icon];
540 #else
541 return icon;
542 #endif