HAVE_ADJUSTABLE_CPU_FREQ isn't defined for simulators, so we don't have to check...
[Rockbox.git] / apps / dbtree.c
blobbc98491d9eec721251589c8cec1475e19fffa816
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, str(LANG_SHOWDIR_BUFFER_FULL));
135 c->dirfull = false;
137 else
138 gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
139 return i;
141 case allsongs:
142 DEBUGF("dbload table allsongs\n");
143 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
144 itemcount = tagdbheader.songcount;
145 stringlen = tagdbheader.songlen;
146 break;
148 case allalbums:
149 DEBUGF("dbload table allalbums\n");
150 offset = tagdbheader.albumstart + c->firstpos * ALBUMENTRY_SIZE;
151 itemcount = tagdbheader.albumcount;
152 stringlen = tagdbheader.albumlen;
153 break;
155 case allartists:
156 DEBUGF("dbload table allartists\n");
157 offset = tagdbheader.artiststart + c->firstpos * ARTISTENTRY_SIZE;
158 itemcount = tagdbheader.artistcount;
159 stringlen = tagdbheader.artistlen;
160 break;
162 case albums4artist:
163 DEBUGF("dbload table albums4artist\n");
164 /* 'extra' is offset to the artist */
165 safeplacelen = tagdbheader.albumarraylen * 4;
166 safeplace = (void*)(end_of_nbuf - safeplacelen);
167 lseek(tagdb_fd, extra + tagdbheader.artistlen, SEEK_SET);
168 rc = read(tagdb_fd, safeplace, safeplacelen);
169 if (rc < safeplacelen)
170 return -1;
172 #ifdef ROCKBOX_LITTLE_ENDIAN
173 for (i=0; i<tagdbheader.albumarraylen; i++)
174 safeplace[i] = BE32(safeplace[i]);
175 #endif
176 offset = safeplace[0];
177 itemcount = tagdbheader.albumarraylen;
178 stringlen = tagdbheader.albumlen;
179 break;
181 case songs4album:
182 DEBUGF("dbload table songs4album\n");
183 /* 'extra' is offset to the album */
184 safeplacelen = tagdbheader.songarraylen * 4;
185 safeplace = (void*)(end_of_nbuf - safeplacelen);
186 lseek(tagdb_fd, extra + tagdbheader.albumlen + 4, SEEK_SET);
187 rc = read(tagdb_fd, safeplace, safeplacelen);
188 if (rc < safeplacelen)
189 return -1;
191 #ifdef ROCKBOX_LITTLE_ENDIAN
192 for (i=0; i<tagdbheader.songarraylen; i++) {
193 safeplace[i] = BE32(safeplace[i]);
194 DEBUGF("db_load songs4album song %d: 0x%x\n",i,safeplace[i]);
196 #endif
197 offset = safeplace[0];
198 itemcount = tagdbheader.songarraylen;
199 stringlen = tagdbheader.songlen;
200 break;
202 case songs4artist:
203 DEBUGF("dbload table songs4artist\n");
204 /* 'extra' is offset to the artist, used as filter */
205 offset = tagdbheader.songstart + c->firstpos * SONGENTRY_SIZE;
206 itemcount = tagdbheader.songcount;
207 stringlen = tagdbheader.songlen;
208 break;
210 default:
211 DEBUGF("Unsupported table %d\n", table);
212 return -1;
214 end_of_nbuf -= safeplacelen;
216 c->dirlength = itemcount;
217 itemcount -= c->firstpos;
219 if (!safeplace)
220 lseek(tagdb_fd, offset, SEEK_SET);
222 /* name_buffer (nptr) contains only names, null terminated.
223 the first word of dcache (dptr) is a pointer to the name,
224 the rest is table specific. see below. */
226 for ( i=0; i < itemcount; i++ ) {
227 int rc, skip=0;
228 int intbuf[4];
230 if (safeplace) {
231 if (!safeplace[i]) {
232 c->dirlength = i;
233 break;
235 lseek(tagdb_fd, safeplace[i], SEEK_SET);
236 offset = safeplace[i];
239 /* read name */
240 rc = read(tagdb_fd, nptr, stringlen);
241 if (rc < stringlen)
243 DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc);
244 return -1;
247 switch (table) {
248 case allsongs:
249 case songs4album:
250 case songs4artist:
251 rc = read(tagdb_fd, intbuf, 12);
252 /* skip the rest of the song info */
253 skip = SONGENTRY_SIZE-stringlen-12;
254 if (rc < 12) {
255 DEBUGF("%d read(%d) returned %d\n", i, 12, rc);
256 return -1;
258 /* continue to next song if wrong artist */
259 if (table == songs4artist && (int)BE32(intbuf[0]) != extra) {
260 lseek(tagdb_fd, skip, SEEK_CUR);
261 continue;
264 /* save offset of filename */
265 dptr[1] = BE32(intbuf[2]);
266 break;
268 case allalbums:
269 case albums4artist:
270 /* save offset of this album */
271 skip = tagdbheader.songarraylen * 4 + 4;
272 dptr[1] = offset;
273 break;
275 case allartists:
276 /* save offset of this artist */
277 skip = tagdbheader.albumarraylen * 4;
278 dptr[1] = offset;
279 break;
282 /* store name pointer in dir cache */
283 dptr[0] = (unsigned long)nptr;
285 if (skip)
286 lseek(tagdb_fd, skip, SEEK_CUR);
288 hits++;
290 if(table==songs4artist)
291 c->dirlength=hits;
293 /* next name is stored immediately after this */
294 nptr = (void*)nptr + strlen((char*)nptr) + 1;
295 if ((void*)nptr + stringlen > (void*)end_of_nbuf) {
296 c->dirfull = true;
297 break;
300 /* limit dir buffer */
301 dptr = (void*)dptr + c->dentry_size * sizeof(int);
302 if ((void*)(dptr + c->dentry_size) >
303 (void*)(c->dircache + dcachesize))
305 c->dirfull = true;
306 break;
309 if (!safeplace)
310 offset += stringlen + skip;
313 if (c->currtable == albums4artist && !c->dirfull) {
314 strcpy((char*)nptr, (char *)str(LANG_ID3DB_ALL_SONGS));
315 dptr[0] = (unsigned long)nptr;
316 dptr[1] = extra; /* offset to artist */
317 hits++;
320 c->filesindir = hits;
322 return hits;
325 static int db_search(struct tree_context* c, char* string)
327 int i, count, size, hits=0;
328 long start;
330 char* nptr = c->name_buffer;
331 const char* end_of_nbuf = nptr + c->name_buffer_size;
333 unsigned long* dptr = c->dircache;
334 const long dcachesize = global_settings.max_files_in_dir *
335 sizeof(struct entry);
337 switch (c->currtable) {
338 case searchartists:
339 start = tagdbheader.artiststart;
340 count = tagdbheader.artistcount;
341 size = ARTISTENTRY_SIZE;
342 break;
344 case searchalbums:
345 start = tagdbheader.albumstart;
346 count = tagdbheader.albumcount;
347 size = ALBUMENTRY_SIZE;
348 break;
350 case searchsongs:
351 start = tagdbheader.songstart;
352 count = tagdbheader.songcount;
353 size = SONGENTRY_SIZE;
354 break;
356 default:
357 DEBUGF("Invalid table %d\n", c->currtable);
358 return 0;
361 lseek(tagdb_fd, start, SEEK_SET);
363 for (i=0; i<count; i++) {
364 if (read(tagdb_fd, nptr, size) < size) {
365 DEBUGF("Short read(%d) in db_search()\n",size);
366 break;
368 if (strcasestr(nptr, string)) {
369 hits++;
371 dptr[0] = (unsigned long)nptr;
372 if (c->currtable == searchsongs) {
373 /* store offset of filename */
374 dptr[1] = BE32(*((long*)(nptr + tagdbheader.songlen + 8)));
376 else
377 /* store offset of database record */
378 dptr[1] = start + i * size;
380 dptr += 2;
382 /* limit dir buffer */
383 if ((void*)(dptr + c->dentry_size) >
384 (void*)(c->dircache + dcachesize))
386 c->dirfull = true;
387 break;
390 nptr += strlen(nptr) + 1;
391 while ((unsigned long)nptr & 3)
392 nptr++;
394 /* limit name buffer */
395 if ((void*)nptr + size > (void*)end_of_nbuf) {
396 c->dirfull = true;
397 break;
402 return hits;
405 int db_enter(struct tree_context* c)
407 int rc = 0;
408 int offset = (c->selected_item) * c->dentry_size + 1;
409 int newextra = ((int*)c->dircache)[offset];
411 if (c->dirlevel >= MAX_DIR_LEVELS)
412 return 0;
414 c->selected_item_history[c->dirlevel]=c->selected_item;
415 c->table_history[c->dirlevel] = c->currtable;
416 c->extra_history[c->dirlevel] = c->currextra;
417 c->pos_history[c->dirlevel] = c->firstpos;
418 c->dirlevel++;
420 switch (c->currtable) {
421 case root:
422 c->currtable = newextra;
423 c->currextra = newextra;
424 break;
426 case allartists:
427 case searchartists:
428 c->currtable = albums4artist;
429 c->currextra = newextra;
430 break;
432 case allalbums:
433 case albums4artist:
434 case searchalbums:
435 /* virtual <all albums> entry points to the artist,
436 all normal entries point to the album */
437 if (newextra < tagdbheader.albumstart)
438 c->currtable = songs4artist;
439 else
440 c->currtable = songs4album;
442 c->currextra = newextra;
443 break;
445 case allsongs:
446 case songs4album:
447 case songs4artist:
448 case searchsongs:
449 c->dirlevel--;
450 if (db_play_folder(c) >= 0)
451 rc = 2;
452 break;
454 case search:
455 rc = kbd_input(searchstring, sizeof(searchstring));
456 if (rc == -1 || !searchstring[0])
457 c->dirlevel--;
458 else
459 c->currtable = newextra;
460 break;
462 default:
463 c->dirlevel--;
464 break;
466 c->selected_item=0;
467 gui_synclist_select_item(&tree_lists, c->selected_item);
469 return rc;
472 void db_exit(struct tree_context* c)
474 c->dirlevel--;
475 c->selected_item=c->selected_item_history[c->dirlevel];
476 gui_synclist_select_item(&tree_lists, c->selected_item);
477 c->currtable = c->table_history[c->dirlevel];
478 c->currextra = c->extra_history[c->dirlevel];
479 c->firstpos = c->pos_history[c->dirlevel];
482 int db_get_filename(struct tree_context* c, char *buf, int buflen)
484 int rc;
485 int pathoffset = ((int*)c->dircache)[c->selected_item * c->dentry_size + 1];
487 lseek(tagdb_fd, pathoffset, SEEK_SET);
488 rc = read(tagdb_fd, buf, buflen);
490 if (rc < tagdbheader.songlen) {
491 DEBUGF("short path read(%ld) = %d\n", sizeof(buf), rc);
492 return -2;
494 return 0;
497 static int db_play_folder(struct tree_context* c)
499 char buf[MAX_PATH];
500 int rc, i;
502 if (playlist_create(NULL, NULL) < 0) {
503 DEBUGF("Failed creating playlist\n");
504 return -1;
507 /* TODO: add support for very long tables */
509 for (i=0; i < c->filesindir; i++) {
510 int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 1];
511 lseek(tagdb_fd, pathoffset, SEEK_SET);
512 rc = read(tagdb_fd, buf, sizeof(buf));
513 if (rc < tagdbheader.songlen) {
514 DEBUGF("short path read(%ld) = %d\n", sizeof(buf), rc);
515 return -2;
518 playlist_insert_track(NULL, buf, PLAYLIST_INSERT, false);
521 if (global_settings.playlist_shuffle)
522 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
523 if (!global_settings.play_selected)
524 c->selected_item = 0;
525 gui_synclist_select_item(&tree_lists, c->selected_item);
527 playlist_start(c->selected_item,0);
529 return 0;
532 #ifdef HAVE_LCD_BITMAP
533 const char* db_get_icon(struct tree_context* c)
534 #else
535 int db_get_icon(struct tree_context* c)
536 #endif
538 int icon;
540 switch (c->currtable)
542 case allsongs:
543 case songs4album:
544 case songs4artist:
545 case searchsongs:
546 icon = Icon_Audio;
547 break;
549 default:
550 icon = Icon_Folder;
551 break;
554 #ifdef HAVE_LCD_BITMAP
555 return (char *)bitmap_icons_6x8[icon];
556 #else
557 return icon;
558 #endif