Added music playing from ID3 browser.
[kugel-rb.git] / apps / dbtree.c
blob6925fd3f298a6a16fda3fe4f9b3306a7d1ce741d
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 #include <stdio.h>
20 #include <string.h>
21 #include "file.h"
22 #include "screens.h"
23 #include "kernel.h"
24 #include "tree.h"
25 #include "lcd.h"
26 #include "font.h"
27 #include "settings.h"
28 #include "icons.h"
29 #include "status.h"
30 #include "debug.h"
31 #include "button.h"
32 #include "menu.h"
33 #include "main_menu.h"
34 #include "mpeg.h"
35 #include "misc.h"
36 #include "ata.h"
37 #include "wps.h"
38 #include "filetypes.h"
39 #include "applimits.h"
40 #include "dbtree.h"
41 #include "icons.h"
42 #include "lang.h"
44 #ifdef LITTLE_ENDIAN
45 #include <netinet/in.h>
46 #define BE32(_x_) htonl(_x_)
47 #else
48 #define BE32(_x_) _x_
49 #endif
51 #define ID3DB_VERSION 1
53 static int fd;
55 static int
56 songstart, albumstart, artiststart,
57 songcount, albumcount, artistcount,
58 songlen, songarraylen,
59 albumlen, albumarraylen,
60 artistlen, initialized = 0;
62 static int db_play_folder(struct tree_context* c);
64 int db_init(void)
66 unsigned int version;
67 unsigned int buf[12];
68 unsigned char* ptr = (char*)buf;
70 fd = open(ROCKBOX_DIR "/rockbox.id3db", O_RDONLY);
71 if (fd < 0) {
72 DEBUGF("Failed opening database\n");
73 return -1;
75 read(fd, buf, 48);
77 if (ptr[0] != 'R' ||
78 ptr[1] != 'D' ||
79 ptr[2] != 'B')
81 DEBUGF("File is not a rockbox id3 database, aborting\n");
82 return -1;
85 version = BE32(buf[0]) & 0xff;
86 if (version != ID3DB_VERSION)
88 DEBUGF("Unsupported database version %d, aborting.\n");
89 return -1;
91 DEBUGF("Version: RDB%d\n", version);
93 songstart = BE32(buf[1]);
94 songcount = BE32(buf[2]);
95 songlen = BE32(buf[3]);
96 DEBUGF("Number of songs: %d\n", songcount);
97 DEBUGF("Songstart: %x\n", songstart);
98 DEBUGF("Songlen: %d\n", songlen);
100 albumstart = BE32(buf[4]);
101 albumcount = BE32(buf[5]);
102 albumlen = BE32(buf[6]);
103 songarraylen = BE32(buf[7]);
104 DEBUGF("Number of albums: %d\n", albumcount);
105 DEBUGF("Albumstart: %x\n", albumstart);
106 DEBUGF("Albumlen: %d\n", albumlen);
108 artiststart = BE32(buf[8]);
109 artistcount = BE32(buf[9]);
110 artistlen = BE32(buf[10]);
111 albumarraylen = BE32(buf[11]);
112 DEBUGF("Number of artists: %d\n", artistcount);
113 DEBUGF("Artiststart: %x\n", artiststart);
114 DEBUGF("Artistlen: %d\n", artistlen);
116 if (songstart > albumstart ||
117 albumstart > artiststart)
119 DEBUGF("Corrupt id3db database, aborting.\n");
120 return -1;
123 initialized = 1;
124 return 0;
127 int db_load(struct tree_context* c)
129 int i, offset, rc;
130 int dcachesize = global_settings.max_files_in_dir * sizeof(struct entry);
131 int max_items, itemcount, stringlen;
132 unsigned int* nptr = (void*) c->name_buffer;
133 unsigned int* dptr = c->dircache;
134 unsigned int* safeplace = NULL;
135 int safeplacelen = 0;
137 int table = c->currtable;
138 int extra = c->currextra;
140 char* end_of_nbuf = c->name_buffer + c->name_buffer_size;
142 if (!initialized) {
143 DEBUGF("ID3 database is not initialized.\n");
144 c->filesindir = 0;
145 return 0;
148 c->dentry_size = 2;
149 c->dirfull = false;
151 DEBUGF("db_load(%d, %x, %d)\n", table, extra, c->firstpos);
153 if (!table) {
154 table = root;
155 c->currtable = table;
158 switch (table) {
159 case root: {
160 static const int tables[] = {allartists, allalbums, allsongs};
161 char* nbuf = (char*)nptr;
162 char* labels[] = { str(LANG_ID3DB_ARTISTS),
163 str(LANG_ID3DB_ALBUMS),
164 str(LANG_ID3DB_SONGS)};
166 for (i=0; i < 3; i++) {
167 strcpy(nbuf, labels[i]);
168 dptr[0] = (unsigned int)nbuf;
169 dptr[1] = tables[i];
170 nbuf += strlen(nbuf) + 1;
171 dptr += 2;
173 c->dirlength = c->filesindir = i;
174 return i;
177 case allsongs:
178 offset = songstart + c->firstpos * (songlen + 12);
179 itemcount = songcount;
180 stringlen = songlen;
181 c->dentry_size = 3;
182 break;
184 case allalbums:
185 offset = albumstart +
186 c->firstpos * (albumlen + 4 + songarraylen * 4);
187 itemcount = albumcount;
188 stringlen = albumlen;
189 break;
191 case allartists:
192 offset = artiststart +
193 c->firstpos * (artistlen + albumarraylen * 4);
194 itemcount = artistcount;
195 stringlen = artistlen;
196 break;
198 case albums4artist:
199 /* 'extra' is offset to the artist */
200 safeplacelen = albumarraylen * 4;
201 safeplace = (void*)(end_of_nbuf - safeplacelen);
202 lseek(fd, extra + artistlen, SEEK_SET);
203 rc = read(fd, safeplace, safeplacelen);
204 if (rc < safeplacelen)
205 return -1;
207 #ifdef LITTLE_ENDIAN
208 for (i=0; i<albumarraylen; i++)
209 safeplace[i] = BE32(safeplace[i]);
210 #endif
211 offset = safeplace[0];
212 itemcount = albumarraylen;
213 stringlen = albumlen;
214 break;
216 case songs4album:
217 /* 'extra' is offset to the album */
218 safeplacelen = songarraylen * 4;
219 safeplace = (void*)(end_of_nbuf - safeplacelen);
220 lseek(fd, extra + albumlen + 4, SEEK_SET);
221 rc = read(fd, safeplace, safeplacelen);
222 if (rc < safeplacelen)
223 return -1;
225 #ifdef LITTLE_ENDIAN
226 for (i=0; i<songarraylen; i++)
227 safeplace[i] = BE32(safeplace[i]);
228 #endif
229 offset = safeplace[0];
230 itemcount = songarraylen;
231 stringlen = songlen;
232 c->dentry_size = 3;
233 break;
235 default:
236 DEBUGF("Unsupported table %d\n", table);
237 return -1;
239 max_items = dcachesize / (c->dentry_size * sizeof(int));
240 end_of_nbuf -= safeplacelen;
242 c->dirlength = itemcount;
243 itemcount -= c->firstpos;
245 if (!safeplace) {
246 //DEBUGF("Seeking to %x\n", offset);
247 lseek(fd, offset, SEEK_SET);
250 /* name_buffer (nptr) contains only names, null terminated.
251 the first word of dcache (dptr) is a pointer to the name,
252 the rest is table specific. see below. */
254 if (itemcount > max_items)
255 c->dirfull = true;
257 if (max_items > itemcount) {
258 max_items = itemcount;
261 for ( i=0; i < max_items; i++ ) {
262 int rc, skip=0;
263 int intbuf[4];
265 if (safeplace) {
266 if (!safeplace[i]) {
267 c->dirlength = i;
268 break;
270 //DEBUGF("Seeking to %x\n", safeplace[i]);
271 lseek(fd, safeplace[i], SEEK_SET);
272 offset = safeplace[i];
275 /* read name */
276 rc = read(fd, nptr, stringlen);
277 if (rc < stringlen)
279 DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc);
280 return -1;
283 /* store name pointer in dir cache */
284 dptr[0] = (unsigned int)nptr;
286 switch (table) {
287 case allsongs:
288 case songs4album:
289 /* save offset of this song */
290 dptr[1] = offset;
292 rc = read(fd, intbuf, 12);
293 if (rc < 12) {
294 DEBUGF("%d read(%d) returned %d\n", i, 12, rc);
295 return -1;
297 /* save offset of filename */
298 dptr[2] = BE32(intbuf[2]);
299 break;
301 case allalbums:
302 case albums4artist:
303 /* save offset of this album */
304 skip = songarraylen * 4 + 4;
305 dptr[1] = offset;
306 break;
308 case allartists:
309 /* save offset of this artist */
310 skip = albumarraylen * 4;
311 dptr[1] = offset;
312 break;
315 if (skip)
316 lseek(fd, skip, SEEK_CUR);
318 /* next name is stored immediately after this */
319 nptr = (void*)nptr + strlen((char*)nptr) + 1;
320 if ((void*)nptr + stringlen > (void*)end_of_nbuf) {
321 c->dirfull = true;
322 break;
324 dptr = (void*)dptr + c->dentry_size * sizeof(int);
326 if (!safeplace)
327 offset += stringlen + skip;
330 c->filesindir = i;
332 return i;
335 int db_enter(struct tree_context* c)
337 int rc = 0;
338 int newextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1];
340 c->dirpos[c->dirlevel] = c->dirstart;
341 c->cursorpos[c->dirlevel] = c->dircursor;
342 c->table_history[c->dirlevel] = c->currtable;
343 c->extra_history[c->dirlevel] = c->currextra;
344 c->pos_history[c->dirlevel] = c->firstpos;
345 c->dirlevel++;
347 switch (c->currtable) {
348 case root:
349 c->currtable = newextra;
350 c->currextra = newextra;
351 break;
353 case allartists:
354 c->currtable = albums4artist;
355 c->currextra = newextra;
356 break;
358 case allalbums:
359 case albums4artist:
360 c->currtable = songs4album;
361 c->currextra = newextra;
362 break;
364 case allsongs:
365 case songs4album:
366 c->dirlevel--;
367 if (db_play_folder(c) >= 0)
368 rc = 3;
369 break;
371 default:
372 break;
375 c->dirstart = c->dircursor = c->firstpos = 0;
377 return rc;
380 void db_exit(struct tree_context* c)
382 c->dirlevel--;
383 c->dirstart = c->dirpos[c->dirlevel];
384 c->dircursor = c->cursorpos[c->dirlevel];
385 c->currtable = c->table_history[c->dirlevel];
386 c->currextra = c->extra_history[c->dirlevel];
387 c->firstpos = c->pos_history[c->dirlevel];
390 static int db_play_folder(struct tree_context* c)
392 char buf[MAX_PATH];
393 int rc, i;
394 int filenum = c->dircursor + c->dirstart;
396 if (playlist_create(NULL, NULL) < 0) {
397 DEBUGF("Failed creating playlist\n");
398 return -1;
401 /* TODO: add support for very long tables */
403 for (i=0; i < c->filesindir; i++) {
404 int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 2];
405 lseek(fd, pathoffset, SEEK_SET);
406 rc = read(fd, buf, sizeof(buf));
407 if (rc < songlen) {
408 DEBUGF("short path read(%d) = %d\n", sizeof(buf), rc);
409 return -2;
412 playlist_insert_track(NULL, buf, PLAYLIST_INSERT, false);
415 if (global_settings.playlist_shuffle)
416 filenum = playlist_shuffle(current_tick, filenum);
417 if (!global_settings.play_selected)
418 filenum = 0;
420 playlist_start(filenum,0);
422 return 0;
425 #ifdef HAVE_LCD_BITMAP
426 const char* db_get_icon(struct tree_context* c)
428 int icon;
430 switch (c->currtable)
432 case allsongs:
433 case songs4album:
434 icon = File;
435 break;
437 default:
438 icon = Folder;
439 break;
442 return bitmap_icons_6x8[icon];
444 #else
445 int db_get_icon(struct tree_context* c)
447 int icon;
448 switch (c->currtable)
450 case allsongs:
451 case songs4album:
452 icon = File;
453 break;
455 default:
456 icon = Folder;
457 break;
459 return icon;
461 #endif