Added note about 2.5 being Archos only
[kugel-rb.git] / apps / dbtree.c
bloba9d90b348b2c9eb32d9bd293f1387a2ec8896180
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 "playlist.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"
46 #include "list.h"
47 #include "splash.h"
49 static int db_play_folder(struct tree_context* c);
50 static int db_search(struct tree_context* c, char* string);
52 static char searchstring[32];
54 int db_load(struct tree_context* c)
56 int i, offset, rc;
57 int dcachesize = global_settings.max_files_in_dir * sizeof(struct entry);
58 int itemcount, stringlen, hits=0;
59 unsigned long* nptr = (void*) c->name_buffer;
60 unsigned long* dptr = c->dircache;
61 unsigned long* safeplace = NULL;
62 int safeplacelen = 0;
64 int table = c->currtable;
65 int extra = c->currextra;
67 char* end_of_nbuf = c->name_buffer + c->name_buffer_size;
69 if (!tagdb_initialized) {
70 DEBUGF("ID3 database is not initialized.\n");
71 c->filesindir = 0;
72 return 0;
75 c->dentry_size = 2;
76 c->dirfull = false;
78 DEBUGF("db_load() table: %d extra: 0x%x firstpos: %d\n", table, extra,
79 c->firstpos);
81 if (!table) {
82 table = root;
83 c->currtable = table;
86 switch (table) {
87 case root: {
88 static const int tables[] = {allartists, allalbums, allsongs,
89 search };
90 char* nbuf = (char*)nptr;
91 unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS),
92 str(LANG_ID3DB_ALBUMS),
93 str(LANG_ID3DB_SONGS),
94 str(LANG_ID3DB_SEARCH)};
95 DEBUGF("dbload table root\n");
96 for (i=0; i < 4; i++) {
97 strcpy(nbuf, (char *)labels[i]);
98 dptr[0] = (unsigned long)nbuf;
99 dptr[1] = tables[i];
100 nbuf += strlen(nbuf) + 1;
101 dptr += 2;
103 c->dirlength = c->filesindir = i;
104 return i;
107 case search: {
108 static const int tables[] = {searchartists,
109 searchalbums,
110 searchsongs};
111 char* nbuf = (char*)nptr;
112 unsigned char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
113 str(LANG_ID3DB_SEARCH_ALBUMS),
114 str(LANG_ID3DB_SEARCH_SONGS)};
115 DEBUGF("dbload table search\n");
116 for (i=0; i < 3; i++) {
117 strcpy(nbuf, (char *)labels[i]);
118 dptr[0] = (unsigned long)nbuf;
119 dptr[1] = tables[i];
120 nbuf += strlen(nbuf) + 1;
121 dptr += 2;
123 c->dirlength = c->filesindir = i;
124 return i;
127 case searchartists:
128 case searchalbums:
129 case searchsongs:
130 DEBUGF("dbload table searchsongs/searchartists/searchalbums\n");
131 i = db_search(c, searchstring);
132 c->dirlength = c->filesindir = i;
133 if (c->dirfull) {
134 gui_syncsplash(HZ, true, (unsigned char *)"%s %s",
135 str(LANG_SHOWDIR_ERROR_BUFFER),
136 str(LANG_SHOWDIR_ERROR_FULL));
137 c->dirfull = false;
139 else
140 gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
141 return i;
143 case allsongs:
144 DEBUGF("dbload table allsongs\n");
145 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
146 itemcount = tagdbheader.songcount;
147 stringlen = tagdbheader.songlen;
148 break;
150 case allalbums:
151 DEBUGF("dbload table allalbums\n");
152 offset = tagdbheader.albumstart + c->firstpos * ALBUMENTRY_SIZE;
153 itemcount = tagdbheader.albumcount;
154 stringlen = tagdbheader.albumlen;
155 break;
157 case allartists:
158 DEBUGF("dbload table allartists\n");
159 offset = tagdbheader.artiststart + c->firstpos * ARTISTENTRY_SIZE;
160 itemcount = tagdbheader.artistcount;
161 stringlen = tagdbheader.artistlen;
162 break;
164 case albums4artist:
165 DEBUGF("dbload table albums4artist\n");
166 /* 'extra' is offset to the artist */
167 safeplacelen = tagdbheader.albumarraylen * 4;
168 safeplace = (void*)(end_of_nbuf - safeplacelen);
169 lseek(tagdb_fd, extra + tagdbheader.artistlen, SEEK_SET);
170 rc = read(tagdb_fd, safeplace, safeplacelen);
171 if (rc < safeplacelen)
172 return -1;
174 #ifdef ROCKBOX_LITTLE_ENDIAN
175 for (i=0; i<tagdbheader.albumarraylen; i++)
176 safeplace[i] = BE32(safeplace[i]);
177 #endif
178 offset = safeplace[0];
179 itemcount = tagdbheader.albumarraylen;
180 stringlen = tagdbheader.albumlen;
181 break;
183 case songs4album:
184 DEBUGF("dbload table songs4album\n");
185 /* 'extra' is offset to the album */
186 safeplacelen = tagdbheader.songarraylen * 4;
187 safeplace = (void*)(end_of_nbuf - safeplacelen);
188 lseek(tagdb_fd, extra + tagdbheader.albumlen + 4, SEEK_SET);
189 rc = read(tagdb_fd, safeplace, safeplacelen);
190 if (rc < safeplacelen)
191 return -1;
193 #ifdef ROCKBOX_LITTLE_ENDIAN
194 for (i=0; i<tagdbheader.songarraylen; i++) {
195 safeplace[i] = BE32(safeplace[i]);
196 DEBUGF("db_load songs4album song %d: 0x%x\n",i,safeplace[i]);
198 #endif
199 offset = safeplace[0];
200 itemcount = tagdbheader.songarraylen;
201 stringlen = tagdbheader.songlen;
202 break;
204 case songs4artist:
205 DEBUGF("dbload table songs4artist\n");
206 /* 'extra' is offset to the artist, used as filter */
207 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
208 itemcount = tagdbheader.songcount;
209 stringlen = tagdbheader.songlen;
210 break;
212 default:
213 DEBUGF("Unsupported table %d\n", table);
214 return -1;
216 end_of_nbuf -= safeplacelen;
218 c->dirlength = itemcount;
219 itemcount -= c->firstpos;
221 if (!safeplace)
222 lseek(tagdb_fd, offset, SEEK_SET);
224 /* name_buffer (nptr) contains only names, null terminated.
225 the first word of dcache (dptr) is a pointer to the name,
226 the rest is table specific. see below. */
228 for ( i=0; i < itemcount; i++ ) {
229 int rc, skip=0;
230 int intbuf[4];
232 if (safeplace) {
233 if (!safeplace[i]) {
234 c->dirlength = i;
235 break;
237 lseek(tagdb_fd, safeplace[i], SEEK_SET);
238 offset = safeplace[i];
241 /* read name */
242 rc = read(tagdb_fd, nptr, stringlen);
243 if (rc < stringlen)
245 DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc);
246 return -1;
249 switch (table) {
250 case allsongs:
251 case songs4album:
252 case songs4artist:
253 rc = read(tagdb_fd, intbuf, 12);
254 /* skip the rest of the song info */
255 skip = SONGENTRY_SIZE-stringlen-12;
256 if (rc < 12) {
257 DEBUGF("%d read(%d) returned %d\n", i, 12, rc);
258 return -1;
260 /* continue to next song if wrong artist */
261 if (table == songs4artist && (int)BE32(intbuf[0]) != extra) {
262 lseek(tagdb_fd, skip, SEEK_CUR);
263 continue;
266 /* save offset of filename */
267 dptr[1] = BE32(intbuf[2]);
268 break;
270 case allalbums:
271 case albums4artist:
272 /* save offset of this album */
273 skip = tagdbheader.songarraylen * 4 + 4;
274 dptr[1] = offset;
275 break;
277 case allartists:
278 /* save offset of this artist */
279 skip = tagdbheader.albumarraylen * 4;
280 dptr[1] = offset;
281 break;
284 /* store name pointer in dir cache */
285 dptr[0] = (unsigned long)nptr;
287 if (skip)
288 lseek(tagdb_fd, skip, SEEK_CUR);
290 hits++;
292 if(table==songs4artist)
293 c->dirlength=hits;
295 /* next name is stored immediately after this */
296 nptr = (void*)nptr + strlen((char*)nptr) + 1;
297 if ((void*)nptr + stringlen > (void*)end_of_nbuf) {
298 c->dirfull = true;
299 break;
302 /* limit dir buffer */
303 dptr = (void*)dptr + c->dentry_size * sizeof(int);
304 if ((void*)(dptr + c->dentry_size) >
305 (void*)(c->dircache + dcachesize))
307 c->dirfull = true;
308 break;
311 if (!safeplace)
312 offset += stringlen + skip;
315 if (c->currtable == albums4artist && !c->dirfull) {
316 strcpy((char*)nptr, (char *)str(LANG_ID3DB_ALL_SONGS));
317 dptr[0] = (unsigned long)nptr;
318 dptr[1] = extra; /* offset to artist */
319 hits++;
322 c->filesindir = hits;
324 return hits;
327 static int db_search(struct tree_context* c, char* string)
329 int i, count, size, hits=0;
330 long start;
332 char* nptr = c->name_buffer;
333 const char* end_of_nbuf = nptr + c->name_buffer_size;
335 unsigned long* dptr = c->dircache;
336 const long dcachesize = global_settings.max_files_in_dir *
337 sizeof(struct entry);
339 switch (c->currtable) {
340 case searchartists:
341 start = tagdbheader.artiststart;
342 count = tagdbheader.artistcount;
343 size = ARTISTENTRY_SIZE;
344 break;
346 case searchalbums:
347 start = tagdbheader.albumstart;
348 count = tagdbheader.albumcount;
349 size = ALBUMENTRY_SIZE;
350 break;
352 case searchsongs:
353 start = tagdbheader.songstart;
354 count = tagdbheader.songcount;
355 size = SONGENTRY_SIZE;
356 break;
358 default:
359 DEBUGF("Invalid table %d\n", c->currtable);
360 return 0;
363 lseek(tagdb_fd, start, SEEK_SET);
365 for (i=0; i<count; i++) {
366 if (read(tagdb_fd, nptr, size) < size) {
367 DEBUGF("Short read(%d) in db_search()\n",size);
368 break;
370 if (strcasestr(nptr, string)) {
371 hits++;
373 dptr[0] = (unsigned long)nptr;
374 if (c->currtable == searchsongs) {
375 /* store offset of filename */
376 dptr[1] = BE32(*((long*)(nptr + tagdbheader.songlen + 8)));
378 else
379 /* store offset of database record */
380 dptr[1] = start + i * size;
382 dptr += 2;
384 /* limit dir buffer */
385 if ((void*)(dptr + c->dentry_size) >
386 (void*)(c->dircache + dcachesize))
388 c->dirfull = true;
389 break;
392 nptr += strlen(nptr) + 1;
393 while ((unsigned long)nptr & 3)
394 nptr++;
396 /* limit name buffer */
397 if ((void*)nptr + size > (void*)end_of_nbuf) {
398 c->dirfull = true;
399 break;
404 return hits;
407 int db_enter(struct tree_context* c)
409 int rc = 0;
410 int offset = (c->selected_item) * c->dentry_size + 1;
411 int newextra = ((int*)c->dircache)[offset];
413 if (c->dirlevel >= MAX_DIR_LEVELS)
414 return 0;
416 c->selected_item_history[c->dirlevel]=c->selected_item;
417 c->table_history[c->dirlevel] = c->currtable;
418 c->extra_history[c->dirlevel] = c->currextra;
419 c->pos_history[c->dirlevel] = c->firstpos;
420 c->dirlevel++;
422 switch (c->currtable) {
423 case root:
424 c->currtable = newextra;
425 c->currextra = newextra;
426 break;
428 case allartists:
429 case searchartists:
430 c->currtable = albums4artist;
431 c->currextra = newextra;
432 break;
434 case allalbums:
435 case albums4artist:
436 case searchalbums:
437 /* virtual <all albums> entry points to the artist,
438 all normal entries point to the album */
439 if (newextra < tagdbheader.albumstart)
440 c->currtable = songs4artist;
441 else
442 c->currtable = songs4album;
444 c->currextra = newextra;
445 break;
447 case allsongs:
448 case songs4album:
449 case songs4artist:
450 case searchsongs:
451 c->dirlevel--;
452 if (db_play_folder(c) >= 0)
453 rc = 2;
454 break;
456 case search:
457 rc = kbd_input(searchstring, sizeof(searchstring));
458 if (rc == -1 || !searchstring[0])
459 c->dirlevel--;
460 else
461 c->currtable = newextra;
462 break;
464 default:
465 c->dirlevel--;
466 break;
468 c->selected_item=0;
469 gui_synclist_select_item(&tree_lists, c->selected_item);
471 return rc;
474 void db_exit(struct tree_context* c)
476 c->dirlevel--;
477 c->selected_item=c->selected_item_history[c->dirlevel];
478 gui_synclist_select_item(&tree_lists, c->selected_item);
479 c->currtable = c->table_history[c->dirlevel];
480 c->currextra = c->extra_history[c->dirlevel];
481 c->firstpos = c->pos_history[c->dirlevel];
484 int db_get_filename(struct tree_context* c, char *buf, int buflen)
486 int rc;
487 int pathoffset = ((int*)c->dircache)[c->selected_item * c->dentry_size + 1];
489 lseek(tagdb_fd, pathoffset, SEEK_SET);
490 rc = read(tagdb_fd, buf, buflen);
492 if (rc < tagdbheader.songlen) {
493 DEBUGF("short path read(%ld) = %d\n", sizeof(buf), rc);
494 return -2;
496 return 0;
499 static int db_play_folder(struct tree_context* c)
501 char buf[MAX_PATH];
502 int rc, i;
504 if (playlist_create(NULL, NULL) < 0) {
505 DEBUGF("Failed creating playlist\n");
506 return -1;
509 /* TODO: add support for very long tables */
511 for (i=0; i < c->filesindir; i++) {
512 int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 1];
513 lseek(tagdb_fd, pathoffset, SEEK_SET);
514 rc = read(tagdb_fd, buf, sizeof(buf));
515 if (rc < tagdbheader.songlen) {
516 DEBUGF("short path read(%ld) = %d\n", sizeof(buf), rc);
517 return -2;
520 playlist_insert_track(NULL, buf, PLAYLIST_INSERT, false);
523 if (global_settings.playlist_shuffle)
524 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
525 if (!global_settings.play_selected)
526 c->selected_item = 0;
527 gui_synclist_select_item(&tree_lists, c->selected_item);
529 playlist_start(c->selected_item,0);
531 return 0;
534 #ifdef HAVE_LCD_BITMAP
535 const char* db_get_icon(struct tree_context* c)
536 #else
537 int db_get_icon(struct tree_context* c)
538 #endif
540 int icon;
542 switch (c->currtable)
544 case allsongs:
545 case songs4album:
546 case songs4artist:
547 case searchsongs:
548 icon = Icon_Audio;
549 break;
551 default:
552 icon = Icon_Folder;
553 break;
556 #ifdef HAVE_LCD_BITMAP
557 return (char *)bitmap_icons_6x8[icon];
558 #else
559 return icon;
560 #endif