From 8a5de5fec96171e739442b0601047d93079e3179 Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Mon, 17 Jan 2005 11:39:46 +0000 Subject: Added ID3 database support. Still very early. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5575 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 2 + apps/dbtree.c | 343 ++++++++++++++++ apps/dbtree.h | 39 ++ apps/filetree.c | 509 +++++++++++++++++++++++ apps/filetree.h | 31 ++ apps/playlist.c | 24 +- apps/tree.c | 1221 +++++++++++++++++++------------------------------------ apps/tree.h | 33 +- 8 files changed, 1388 insertions(+), 814 deletions(-) create mode 100644 apps/dbtree.c create mode 100644 apps/dbtree.h create mode 100644 apps/filetree.c create mode 100644 apps/filetree.h (limited to 'apps') diff --git a/apps/SOURCES b/apps/SOURCES index c834839474..edb3bb3dd2 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -24,6 +24,8 @@ sound_menu.c status.c talk.c tree.c +dbtree.c +filetree.c wps-display.c wps.c #ifdef HAVE_LCD_CHARCELLS diff --git a/apps/dbtree.c b/apps/dbtree.c new file mode 100644 index 0000000000..f9bd950057 --- /dev/null +++ b/apps/dbtree.c @@ -0,0 +1,343 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include "file.h" +#include "screens.h" +#include "kernel.h" +#include "tree.h" +#include "lcd.h" +#include "font.h" +#include "settings.h" +#include "icons.h" +#include "status.h" +#include "debug.h" +#include "button.h" +#include "menu.h" +#include "main_menu.h" +#include "mpeg.h" +#include "misc.h" +#include "ata.h" +#include "wps.h" +#include "filetypes.h" +#include "applimits.h" +#include "dbtree.h" +#include "icons.h" + +#ifdef LITTLE_ENDIAN +#include +#define BE32(_x_) htonl(_x_) +#else +#define BE32(_x_) _x_ +#endif + +static int fd; + +static int + songstart, albumstart, artiststart, + songcount, albumcount, artistcount, + songlen, songarraylen, + albumlen, albumarraylen, + artistlen; + +int db_init(void) +{ + unsigned int version; + unsigned int buf[12]; + + fd = open(ROCKBOX_DIR "/rockbox.id3db", O_RDONLY); + if (fd < 0) { + DEBUGF("Failed opening database\n"); + return -1; + } + read(fd, buf, 48); + + version = BE32(buf[0]) & 0xff; + DEBUGF("Version: RDB%d\n", version); + + songstart = BE32(buf[1]); + songcount = BE32(buf[2]); + songlen = BE32(buf[3]); + DEBUGF("Number of songs: %d\n", songcount); + DEBUGF("Songstart: %x\n", songstart); + DEBUGF("Songlen: %d\n", songlen); + + albumstart = BE32(buf[4]); + albumcount = BE32(buf[5]); + albumlen = BE32(buf[6]); + songarraylen = BE32(buf[7]); + DEBUGF("Number of albums: %d\n", albumcount); + DEBUGF("Albumstart: %x\n", albumstart); + DEBUGF("Albumlen: %d\n", albumlen); + + artiststart = BE32(buf[8]); + artistcount = BE32(buf[9]); + artistlen = BE32(buf[10]); + albumarraylen = BE32(buf[11]); + DEBUGF("Number of artists: %d\n", artistcount); + DEBUGF("Artiststart: %x\n", artiststart); + DEBUGF("Artistlen: %d\n", artistlen); + + return 0; +} + +int db_load(struct tree_context* c, bool* dir_buffer_full) +{ + int i, offset, len, rc; + int dcachesize = global_settings.max_files_in_dir * sizeof(struct entry); + int max_items, itemcount, stringlen; + unsigned int* nptr = (void*) c->name_buffer; + unsigned int* dptr = c->dircache; + unsigned int* safeplace = NULL; + + int table = c->currtable; + int extra = c->currextra; + c->dentry_size = 2 * sizeof(int); + + DEBUGF("db_load(%d, %x)\n", table, extra); + + if (!table) { + table = allartists; + c->currtable = table; + } + + switch (table) { + case allsongs: + offset = songstart; + itemcount = songcount; + stringlen = songlen; + break; + + case allalbums: + offset = albumstart; + itemcount = albumcount; + stringlen = albumlen; + break; + + case allartists: + offset = artiststart; + itemcount = artistcount; + stringlen = artistlen; + break; + + case albums: + /* 'extra' is offset to the artist */ + len = albumarraylen * 4; + safeplace = (void*)(c->name_buffer + c->name_buffer_size - len); + //DEBUGF("Seeking to %x\n", extra + artistlen); + lseek(fd, extra + artistlen, SEEK_SET); + rc = read(fd, safeplace, len); + if (rc < len) + return -1; + +#ifdef LITTLE_ENDIAN + for (i=0; iname_buffer + c->name_buffer_size - len); + //DEBUGF("Seeking to %x\n", extra + albumlen + 4); + lseek(fd, extra + albumlen + 4, SEEK_SET); + rc = read(fd, safeplace, len); + if (rc < len) + return -1; + +#ifdef LITTLE_ENDIAN + for (i=0; identry_size; + + if (!safeplace) { + //DEBUGF("Seeking to %x\n", offset); + lseek(fd, offset, SEEK_SET); + } + + /* name_buffer (nptr) contains only names, null terminated. + the first word of dcache (dptr) is a pointer to the name, + the rest is table specific. see below. */ + + if (itemcount > max_items) + if (dir_buffer_full) + *dir_buffer_full = true; + + if (max_items > itemcount) { + max_items = itemcount; + } + + for ( i=0; i < max_items; i++ ) { + int rc, skip=0; + + if (safeplace) { + if (!safeplace[i]) + break; + //DEBUGF("Seeking to %x\n", safeplace[i]); + lseek(fd, safeplace[i], SEEK_SET); + offset = safeplace[i]; + } + + /* read name */ + rc = read(fd, nptr, stringlen); + if (rc < stringlen) + { + DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc); + return -1; + } + + /* store name pointer in dir cache */ + dptr[0] = (unsigned int)nptr; + + switch (table) { + case songs: + case allsongs: + /* save offset of this song */ + skip = 12; + dptr[1] = offset; + break; + + case allalbums: + case albums: + /* save offset of this album */ + skip = songarraylen * 4 + 4; + dptr[1] = offset; + break; + + case allartists: + /* save offset of this artist */ + skip = albumarraylen * 4; + dptr[1] = offset; + break; + } + + //DEBUGF("%x: %s\n", dptr[1], dptr[0]); + + if (skip) + lseek(fd, skip, SEEK_CUR); + + /* next name is stored immediately after this */ + nptr = (void*)nptr + strlen((char*)nptr) + 1; + if ((void*)nptr > (void*)c->name_buffer + c->name_buffer_size) { + DEBUGF("Name buffer overflow (%d)\n",i); + break; + } + dptr = (void*)dptr + c->dentry_size; + + if (!safeplace) + offset += stringlen + skip; + } + + c->filesindir = i; + + return i; +} + +void db_enter(struct tree_context* c) +{ + switch (c->currtable) { + case allartists: + case albums: + c->dirpos[c->dirlevel] = c->dirstart; + c->cursorpos[c->dirlevel] = c->dircursor; + c->table_history[c->dirlevel] = c->currtable; + c->extra_history[c->dirlevel] = c->currextra; + c->dirlevel++; + break; + + default: + break; + } + + switch (c->currtable) { + case allartists: + c->currtable = albums; + c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1]; + break; + + case albums: + c->currtable = songs; + c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1]; + break; + + case songs: + splash(HZ,true,"No playing implemented yet"); +#if 0 + /* find filenames, build playlist, play */ + playlist_create(NULL,NULL); +#endif + break; + + default: + break; + } + + c->dirstart = c->dircursor = 0; +} + +void db_exit(struct tree_context* c) +{ + c->dirlevel--; + c->dirstart = c->dirpos[c->dirlevel]; + c->dircursor = c->cursorpos[c->dirlevel]; + c->currtable = c->table_history[c->dirlevel]; + c->currextra = c->extra_history[c->dirlevel]; +} + +#ifdef HAVE_LCD_BITMAP +const char* db_get_icon(struct tree_context* c) +{ + int icon; + + switch (c->currtable) + { + case allsongs: + case songs: + icon = File; + break; + + default: + icon = Folder; + break; + } + + return bitmap_icons_6x8[icon]; +} +#else +int db_get_icon(struct tree_context* c) +{ + (void)c; + return Folder; +} +#endif diff --git a/apps/dbtree.h b/apps/dbtree.h new file mode 100644 index 0000000000..7664715775 --- /dev/null +++ b/apps/dbtree.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef DBTREE_H +#define DBTREE_H + +#include "tree.h" + +enum table { invalid, allsongs, allalbums, allartists, albums, songs }; + +int db_init(void); +void db_enter(struct tree_context* c); +void db_exit(struct tree_context* c); +int db_load(struct tree_context* c, + bool* dir_buffer_full); +#ifdef HAVE_LCD_BITMAP +const char* db_get_icon(struct tree_context* c); +#else +int db_get_icon(struct tree_context* c); +#endif + + +#endif + diff --git a/apps/filetree.c b/apps/filetree.c new file mode 100644 index 0000000000..98d59b9e9a --- /dev/null +++ b/apps/filetree.c @@ -0,0 +1,509 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bookmark.h" +#include "tree.h" +#include "settings.h" +#include "filetypes.h" +#include "talk.h" +#include "playlist.h" +#include "wps-display.h" +#include "lang.h" +#include "language.h" +#include "screens.h" +#include "plugin.h" +#include "rolo.h" + +static int boot_size = 0; +static int boot_cluster; +extern bool boot_changed; + +int ft_build_playlist(struct tree_context* c, int start_index) +{ + int i; + int start=start_index; + + struct entry *dircache = c->dircache; + + for(i = 0;i < c->filesindir;i++) + { + if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) + { + DEBUGF("Adding %s\n", dircache[i].name); + if (playlist_add(dircache[i].name) < 0) + break; + } + else + { + /* Adjust the start index when se skip non-MP3 entries */ + if(i < start) + start_index--; + } + } + + return start_index; +} + +/* walk a directory and check all dircache entries if a .talk file exists */ +static void check_file_thumbnails(struct tree_context* c) +{ + int i; + struct dirent *entry; + struct entry* dircache = c->dircache; + DIR *dir; + + dir = opendir(c->currdir); + if(!dir) + return; + + for (i=0; i < c->filesindir; i++) /* mark all files as non talking, except the .talk ones */ + { + if (dircache[i].attr & ATTR_DIRECTORY) + continue; /* we're not touching directories */ + + if (strcasecmp(file_thumbnail_ext, + &dircache[i].name[strlen(dircache[i].name) + - strlen(file_thumbnail_ext)])) + { /* no .talk file */ + dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; /* clear */ + } + else + { /* .talk file, we later let them speak themselves */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set */ + } + } + + while((entry = readdir(dir)) != 0) /* walk directory */ + { + int ext_pos; + + ext_pos = strlen(entry->d_name) - strlen(file_thumbnail_ext); + if (ext_pos <= 0 /* too short to carry ".talk" */ + || (entry->attribute & ATTR_DIRECTORY) /* no file */ + || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext)) + { /* or doesn't end with ".talk", no candidate */ + continue; + } + + /* terminate the (disposable) name in dir buffer, + this truncates off the ".talk" without needing an extra buffer */ + entry->d_name[ext_pos] = '\0'; + + /* search corresponding file in dir cache */ + for (i=0; i < c->filesindir; i++) + { + if (!strcasecmp(dircache[i].name, entry->d_name)) + { /* match */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ + break; /* exit search loop, because we found it */ + } + } + } + closedir(dir); +} + +/* support function for qsort() */ +static int compare(const void* p1, const void* p2) +{ + struct entry* e1 = (struct entry*)p1; + struct entry* e2 = (struct entry*)p2; + int criteria; + + if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) + { /* two directories */ + criteria = global_settings.sort_dir; + } + else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY)) + { /* two files */ + criteria = global_settings.sort_file; + } + else /* dir and file, dir goes first */ + return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY ); + + switch(criteria) + { + case 3: /* sort type */ + { + int t1 = e1->attr & TREE_ATTR_MASK; + int t2 = e2->attr & TREE_ATTR_MASK; + + if (!t1) /* unknown type */ + t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */ + if (!t2) /* unknown type */ + t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */ + + if (t1 - t2) /* if different */ + return t1 - t2; + /* else fall through to alphabetical sorting */ + } + case 0: /* sort alphabetically */ + if (global_settings.sort_case) + return strncmp(e1->name, e2->name, MAX_PATH); + else + return strncasecmp(e1->name, e2->name, MAX_PATH); + + case 1: /* sort date */ + return e1->time_write - e2->time_write; + + case 2: /* sort date, newest first */ + return e2->time_write - e1->time_write; + } + return 0; /* never reached */ +} + +/* load and sort directory into dircache. returns NULL on failure. */ +int ft_load(struct tree_context* c, bool *buffer_full) +{ + extern char lastdir[]; /* from tree.c */ + int i; + DIR *dir = opendir(c->currdir); + if(!dir) + return -1; /* not a directory */ + + int name_buffer_used = 0; + c->dirsindir = 0; + if (buffer_full) + *buffer_full = false; + + for ( i=0; i < global_settings.max_files_in_dir; i++ ) { + int len; + struct dirent *entry = readdir(dir); + struct entry* dptr = (struct entry*)(c->dircache + i * sizeof(struct entry)); + if (!entry) + break; + + len = strlen(entry->d_name); + + /* skip directories . and .. */ + if ((entry->attribute & ATTR_DIRECTORY) && + (((len == 1) && + (!strncmp(entry->d_name, ".", 1))) || + ((len == 2) && + (!strncmp(entry->d_name, "..", 2))))) { + i--; + continue; + } + + /* Skip FAT volume ID */ + if (entry->attribute & ATTR_VOLUME_ID) { + i--; + continue; + } + + /* filter out dotfiles and hidden files */ + if (*c->dirfilter != SHOW_ALL && + ((entry->d_name[0]=='.') || + (entry->attribute & ATTR_HIDDEN))) { + i--; + continue; + } + + dptr->attr = entry->attribute; + + /* check for known file types */ + if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) + dptr->attr |= filetype_get_attr(entry->d_name); + + /* memorize/compare details about the boot file */ + if ((c->currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) { + if (boot_size) { + if ((entry->size != boot_size) || + (entry->startcluster != boot_cluster)) + boot_changed = true; + } + boot_size = entry->size; + boot_cluster = entry->startcluster; + } + + /* filter out non-visible files */ + if (!(dptr->attr & ATTR_DIRECTORY) && ( + (*c->dirfilter == SHOW_PLAYLIST && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || + ((*c->dirfilter == SHOW_MUSIC && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || + (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) || + (*c->dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) || + (*c->dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) || + (*c->dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) || + (*c->dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) || + (*c->dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) || + (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK))) + { + i--; + continue; + } + + if (len > c->name_buffer_size - name_buffer_used - 1) { + /* Tell the world that we ran out of buffer space */ + if (buffer_full) + *buffer_full = true; + break; + } + dptr->name = &c->name_buffer[name_buffer_used]; + dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */ + strcpy(dptr->name,entry->d_name); + name_buffer_used += len + 1; + + if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */ + c->dirsindir++; + } + c->filesindir = i; + closedir(dir); + + strcpy(lastdir, c->currdir); + + qsort(c->dircache,i,sizeof(struct entry),compare); + + /* If thumbnail talking is enabled, make an extra run to mark files with + associated thumbnails, so we don't do unsuccessful spinups later. */ + if (global_settings.talk_file == 3) + check_file_thumbnails(c); /* map .talk to ours */ + + return 0; +} + +int ft_enter(struct tree_context* c) +{ + int rc = 0; + char buf[MAX_PATH]; + struct entry *dircache = c->dircache; + struct entry* file = &dircache[c->dircursor + c->dirstart]; + bool reload_root = false; + bool reload_dir = false; + bool start_wps = false; + bool exit_func = false; + + if (c->currdir[1]) + snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); + else + snprintf(buf,sizeof(buf),"/%s",file->name); + + if (file->attr & ATTR_DIRECTORY) { + memcpy(c->currdir, buf, sizeof(c->currdir)); + if ( c->dirlevel < MAX_DIR_LEVELS ) { + c->dirpos[c->dirlevel] = c->dirstart; + c->cursorpos[c->dirlevel] = c->dircursor; + } + c->dirlevel++; + c->dircursor=0; + c->dirstart=0; + } + else { + int seed = current_tick; + bool play = false; + int start_index=0; + + lcd_stop_scroll(); + switch ( file->attr & TREE_ATTR_MASK ) { + case TREE_ATTR_M3U: + if (bookmark_autoload(buf)) + break; + + if (playlist_create(c->currdir, file->name) != -1) + { + if (global_settings.playlist_shuffle) + playlist_shuffle(seed, -1); + start_index = 0; + playlist_start(start_index,0); + play = true; + } + break; + + case TREE_ATTR_MPA: + if (bookmark_autoload(c->currdir)) + break; + + if (playlist_create(c->currdir, NULL) != -1) + { + start_index = + ft_build_playlist(c, c->dircursor + c->dirstart); + if (global_settings.playlist_shuffle) + { + start_index = playlist_shuffle(seed, start_index); + + /* when shuffling dir.: play all files + even if the file selected by user is + not the first one */ + if (!global_settings.play_selected) + start_index = 0; + } + + playlist_start(start_index, 0); + play = true; + } + break; + + /* wps config file */ + case TREE_ATTR_WPS: + wps_load(buf,true); + set_file(buf, global_settings.wps_file, + MAX_FILENAME); + break; + + case TREE_ATTR_CFG: + if (!settings_load_config(buf)) + break; + lcd_clear_display(); + lcd_puts(0,0,str(LANG_SETTINGS_LOADED1)); + lcd_puts(0,1,str(LANG_SETTINGS_LOADED2)); +#ifdef HAVE_LCD_BITMAP + lcd_update(); +#endif + sleep(HZ/2); + break; + + case TREE_ATTR_BMARK: + bookmark_load(buf, false); + reload_dir = true; + break; + + case TREE_ATTR_LNG: + if(!lang_load(buf)) { + extern bool language_changed; /* from settings_menu.c */ + + set_file(buf, global_settings.lang_file, + MAX_FILENAME); + talk_init(); /* use voice of same language */ + splash(HZ, true, str(LANG_LANGUAGE_LOADED)); + language_changed = true; + } + break; + +#ifdef HAVE_LCD_BITMAP + case TREE_ATTR_FONT: + font_load(buf); + set_file(buf, global_settings.font_file, MAX_FILENAME); + break; +#endif + +#ifndef SIMULATOR + /* firmware file */ + case TREE_ATTR_MOD: + rolo_load(buf); + break; +#endif + + /* plugin file */ + case TREE_ATTR_ROCK: + if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED) + { + if(*c->dirfilter > NUM_FILTER_MODES) + /* leave sub-browsers after usb, doing + otherwise might be confusing to the user */ + exit_func = true; + else + reload_root = true; + } + break; + + default: + { + char* plugin = filetype_get_plugin(file); + if (plugin) + { + if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED) + reload_root = true; + } + break; + } + } + + if ( play ) { + if ( global_settings.resume ) { + /* the resume_index must always be the index in the + shuffled list in case shuffle is enabled */ + global_settings.resume_index = start_index; + global_settings.resume_offset = 0; + settings_save(); + } + + start_wps = true; + } + else { + if (*c->dirfilter > NUM_FILTER_MODES && + *c->dirfilter != SHOW_FONT && + *c->dirfilter != SHOW_PLUGINS) + { + exit_func = true; + } + } + } + + if (reload_dir) + rc = 1; + if (reload_root) + rc = 2; + if (start_wps) + rc = 3; + if (exit_func) + rc = 4; + + return rc; +} + +int ft_exit(struct tree_context* c) +{ + extern char lastfile[]; /* from tree.c */ + char buf[MAX_PATH]; + int rc = 0; + bool exit_func = false; + + int i = strlen(c->currdir); + if (i>1) { + while (c->currdir[i-1]!='/') + i--; + strcpy(buf,&c->currdir[i]); + if (i==1) + c->currdir[i]=0; + else + c->currdir[i-1]=0; + + if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1) + exit_func = true; + + c->dirlevel--; + if ( c->dirlevel < MAX_DIR_LEVELS ) { + c->dirstart = c->dirpos[c->dirlevel]; + c->dircursor = c->cursorpos[c->dirlevel]; + } + else + c->dirstart = c->dircursor = 0; + + if (c->dirstart == -1) + strcpy(lastfile, buf); + } + else + { + if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1) + exit_func = true; + } + + if (exit_func) + rc = 4; + + return rc; +} diff --git a/apps/filetree.h b/apps/filetree.h new file mode 100644 index 0000000000..c1ba5681a8 --- /dev/null +++ b/apps/filetree.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Björn Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef FILETREE_H +#define FILETREE_H +#include "tree.h" + +int ft_load(struct tree_context* c, bool *buffer_full); +int ft_play_filenumber(int pos, int attr); +int ft_play_dirname(int start_index); +void ft_play_filename(char *dir, char *file); +int ft_enter(struct tree_context* c); +int ft_exit(struct tree_context* c); +int ft_build_playlist(struct tree_context* c, int start_index); + +#endif diff --git a/apps/playlist.c b/apps/playlist.c index 3b96225b17..1a68a052ba 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -84,7 +84,7 @@ #include "atoi.h" #include "misc.h" #include "button.h" -#include "tree.h" +#include "filetree.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" #include "widgets.h" @@ -537,16 +537,18 @@ static int add_directory_to_playlist(struct playlist_info* playlist, char *count_str; int result = 0; int num_files = 0; - bool buffer_full = false; int i; - int dirfilter = SHOW_ALL; + int dirfilter = global_settings.dirfilter; struct entry *files; /* use the tree browser dircache to load files */ - files = load_and_sort_directory(dirname, &dirfilter, &num_files, - &buffer_full); + global_settings.dirfilter = SHOW_ALL; + struct tree_context* tc = tree_get_context(); + strncpy(tc->currdir, dirname, sizeof(tc->currdir)); + num_files = ft_load(tc, NULL); + files = (struct entry*) tc->dircache; - if(!files) + if(!num_files) { splash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); return 0; @@ -582,9 +584,10 @@ static int add_directory_to_playlist(struct playlist_info* playlist, break; /* we now need to reload our current directory */ - files = load_and_sort_directory(dirname, &dirfilter, &num_files, - &buffer_full); - if (!files) + strncpy(tc->currdir, dirname, sizeof(tc->currdir)); + num_files = ft_load(tc, NULL); + files = (struct entry*) tc->dircache; + if (!num_files) { result = -1; break; @@ -627,6 +630,9 @@ static int add_directory_to_playlist(struct playlist_info* playlist, } } + /* restore dirfilter */ + global_settings.dirfilter = dirfilter; + return result; } diff --git a/apps/tree.c b/apps/tree.c index 9b4f6898de..eb1f7c3b74 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -57,18 +57,13 @@ #include "talk.h" #include "filetypes.h" #include "misc.h" +#include "filetree.h" +#include "dbtree.h" #ifdef HAVE_LCD_BITMAP #include "widgets.h" -#define BOOTFILE "ajbrec.ajz" -#else -#define BOOTFILE "archos.mod" #endif -/* This flag is set by dirbrowse() if a new language is loaded. - It is defined in settings_menu.c */ -extern bool language_changed; - /* a table for the know file types */ const struct filetype filetypes[] = { { ".mp3", TREE_ATTR_MPA, File, VOICE_EXT_MPA }, @@ -92,33 +87,17 @@ const struct filetype filetypes[] = { #endif /* #ifndef SIMULATOR */ }; -/* Boot value of global_settings.max_files_in_dir */ -static int max_files_in_dir; - -static char *name_buffer; -static int name_buffer_size; /* Size of allocated buffer */ -static int name_buffer_length; /* Currently used amount */ - -static struct entry *dircache; - -static int dircursor; -static int dirstart; -static int dirlevel; -static int filesindir; -static int dirsindir; /* how many of the dircache entries are directories */ -static int dirpos[MAX_DIR_LEVELS]; -static int cursorpos[MAX_DIR_LEVELS]; -static char lastdir[MAX_PATH]; -static char lastfile[MAX_PATH]; -static char currdir[MAX_PATH]; -static char currdir_save[MAX_PATH]; +static struct tree_context tc; + +bool boot_changed = false; + +char lastfile[MAX_PATH]; +char lastdir[MAX_PATH]; + static bool reload_dir = false; -static int boot_size = 0; -static int boot_cluster; -static bool boot_changed = false; static bool start_wps = false; -static bool dirbrowse(const char *root, const int *dirfilter); +static bool dirbrowse(void); bool check_rockboxdir(void) { @@ -140,11 +119,13 @@ void browse_root(void) filetype_init(); check_rockboxdir(); + strcpy(tc.currdir, "/"); + #ifndef SIMULATOR - dirbrowse("/", &global_settings.dirfilter); + dirbrowse(); #else - if (!dirbrowse("/", &global_settings.dirfilter)) { + if (!dirbrowse()) { DEBUGF("No filesystem found. Have you forgotten to create it?\n"); } #endif @@ -156,11 +137,16 @@ void tree_get_filetypes(const struct filetype** types, int* count) *count = sizeof(filetypes) / sizeof(*filetypes); } +struct tree_context* tree_get_context(void) +{ + return &tc; +} + #ifdef HAVE_LCD_BITMAP /* pixel margins */ #define MARGIN_X (global_settings.scrollbar && \ - filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \ + tc.filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \ CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0) #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0) @@ -169,7 +155,7 @@ void tree_get_filetypes(const struct filetype** types, int* count) #define LINE_Y (global_settings.statusbar ? 1 : 0) #define CURSOR_X (global_settings.scrollbar && \ - filesindir > tree_max_on_screen ? 1 : 0) + tc.filesindir > tree_max_on_screen ? 1 : 0) #define CURSOR_Y 0 /* the cursor is not positioned in regard to the margins, so this is the amount of lines we add to the cursor Y position to position @@ -197,158 +183,8 @@ void tree_get_filetypes(const struct filetype** types, int* count) /* talkbox hovering delay, to avoid immediate disk activity */ #define HOVER_DELAY (HZ/2) -static int build_playlist(int start_index) -{ - int i; - int start=start_index; - - for(i = 0;i < filesindir;i++) - { - if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) - { - DEBUGF("Adding %s\n", dircache[i].name); - if (playlist_add(dircache[i].name) < 0) - break; - } - else - { - /* Adjust the start index when se skip non-MP3 entries */ - if(i < start) - start_index--; - } - } - - return start_index; -} - -static int play_filenumber(int pos, int attr) -{ - /* try to find a voice ID for the extension, if known */ - unsigned int j; - int ext_id = -1; /* default to none */ - for (j=0; jattr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) - { /* two directories */ - criteria = global_settings.sort_dir; - if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME) - { /* a volume identifier is involved */ - if (e1->attr & ATTR_VOLUME && e2->attr & ATTR_VOLUME) - criteria = 0; /* two volumes: sort alphabetically */ - else /* only one is a volume: volume first */ - return (e2->attr & ATTR_VOLUME) - (e1->attr & ATTR_VOLUME); - } - } - else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY)) - { /* two files */ - criteria = global_settings.sort_file; - } - else /* dir and file, dir goes first */ - return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY ); - - switch(criteria) - { - case 3: /* sort type */ - { - int t1 = e1->attr & TREE_ATTR_MASK; - int t2 = e2->attr & TREE_ATTR_MASK; - - if (!t1) /* unknown type */ - t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */ - if (!t2) /* unknown type */ - t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */ - - if (t1 - t2) /* if different */ - return t1 - t2; - /* else fall through to alphabetical sorting */ - } - case 0: /* sort alphabetically */ - if (global_settings.sort_case) - return strncmp(e1->name, e2->name, MAX_PATH); - else - return strncasecmp(e1->name, e2->name, MAX_PATH); - case 1: /* sort date */ - return e1->time_write - e2->time_write; - case 2: /* sort date, newest first */ - return e2->time_write - e1->time_write; - } - return 0; /* never reached */ -} - -static void showfileline(int line, int direntry, bool scroll, const int *dirfilter) -{ - char* name = dircache[direntry].name; int xpos = LINE_X; char* dotpos = NULL; @@ -358,8 +194,9 @@ static void showfileline(int line, int direntry, bool scroll, const int *dirfilt #endif /* if any file filter is on, strip the extension */ - if (*dirfilter != SHOW_ALL && - !(dircache[direntry].attr & ATTR_DIRECTORY)) + if (*tc.dirfilter != SHOW_ID3DB && + *tc.dirfilter != SHOW_ALL && + !(attr & ATTR_DIRECTORY)) { dotpos = strrchr(name, '.'); if (dotpos) { @@ -383,207 +220,6 @@ static void showfileline(int line, int direntry, bool scroll, const int *dirfilt *dotpos = '.'; } -/* walk a directory and check all dircache entries if a .talk file exists */ -void check_file_thumbnails(const char *dirname, int num_files) -{ - int i; - struct dirent *entry; - DIR *dir; - - dir = opendir(dirname); - if(!dir) - return; - - for (i=0; id_name) - strlen(file_thumbnail_ext); - if (ext_pos <= 0 /* too short to carry ".talk" */ - || (entry->attribute & ATTR_DIRECTORY) /* no file */ - || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext)) - { /* or doesn't end with ".talk", no candidate */ - continue; - } - - /* terminate the (disposable) name in dir buffer, - this truncates off the ".talk" without needing an extra buffer */ - entry->d_name[ext_pos] = '\0'; - - /* search corresponding file in dir cache */ - for (i=0; id_name)) - { /* match */ - dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ - break; /* exit search loop, because we found it */ - } - } - } - closedir(dir); -} - -/* check all dircache directories if they contain a "_dirname.talk" file */ -#if 0 /* not practical, this is too slow */ -void check_dir_thumbnails(const char *dirname, int num_files) -{ - int i; - int fd; - char clipfile[MAX_PATH]; - - for (i=0; i= 0) - { /* there is one */ - dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ - close(fd); - } - else - { /* none found, clear the flag */ - dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; - } - } -} -#endif /* #if 0 */ - -/* load sorted directory into dircache. returns NULL on failure. */ -struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter, - int *num_files, bool *buffer_full) -{ - int i; - - DIR *dir = opendir(dirname); - if(!dir) - return NULL; /* not a directory */ - - name_buffer_length = 0; - dirsindir = 0; - *buffer_full = false; - - for ( i=0; i < max_files_in_dir; i++ ) { - int len; - struct dirent *entry = readdir(dir); - struct entry* dptr = &dircache[i]; - if (!entry) - break; - - len = strlen(entry->d_name); - - /* skip directories . and .. */ - if ((entry->attribute & ATTR_DIRECTORY) && - (((len == 1) && - (!strncmp(entry->d_name, ".", 1))) || - ((len == 2) && - (!strncmp(entry->d_name, "..", 2))))) { - i--; - continue; - } - - /* Skip FAT volume ID */ - if (entry->attribute & ATTR_VOLUME_ID) { - i--; - continue; - } - - /* filter out dotfiles and hidden files */ - if (*dirfilter != SHOW_ALL && - ((entry->d_name[0]=='.') || - (entry->attribute & ATTR_HIDDEN))) { - i--; - continue; - } - - dptr->attr = entry->attribute; - - /* check for known file types */ - if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) - dptr->attr |= filetype_get_attr(entry->d_name); - - /* memorize/compare details about the boot file */ - if ((currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) { - if (boot_size) { - if ((entry->size != boot_size) || - (entry->startcluster != boot_cluster)) - boot_changed = true; - } - boot_size = entry->size; - boot_cluster = entry->startcluster; - } - - /* filter out non-visible files */ - if (!(dptr->attr & ATTR_DIRECTORY) && ( - (*dirfilter == SHOW_PLAYLIST && - (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || - ((*dirfilter == SHOW_MUSIC && - (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) && - (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || - (*dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) || - (*dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) || - (*dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) || - (*dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) || - (*dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) || - (*dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) || - (*dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK))) - { - i--; - continue; - } - - if (len > name_buffer_size - name_buffer_length - 1) { - /* Tell the world that we ran out of buffer space */ - *buffer_full = true; - break; - } - dptr->name = &name_buffer[name_buffer_length]; - dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */ - strcpy(dptr->name,entry->d_name); - name_buffer_length += len + 1; - - if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */ - dirsindir++; - } - *num_files = i; - closedir(dir); - strncpy(lastdir,dirname,sizeof(lastdir)); - lastdir[sizeof(lastdir)-1] = 0; - qsort(dircache,i,sizeof(struct entry),compare); - - /* If thumbnail talking is enabled, make an extra run to mark files with - associated thumbnails, so we don't do unsuccessful spinups later. */ - if (global_settings.talk_file == 3) - check_file_thumbnails(dirname, i); /* map .talk to ours */ -#if 0 /* not practical, this is too slow */ - if (global_settings.talk_dir == 3) - check_dir_thumbnails(dirname, i); /* try in the directories */ -#endif /* #if 0 */ - return dircache; -} - #ifdef HAVE_LCD_BITMAP static int recalc_screen_height(void) { @@ -604,11 +240,12 @@ static int recalc_screen_height(void) } #endif -static int showdir(const char *path, int start, const int *dirfilter) +static int showdir(void) { + struct entry *dircache = tc.dircache; int i; int tree_max_on_screen; - bool dir_buffer_full; + bool dir_buffer_full = false; #ifdef HAVE_LCD_BITMAP const char* icon; @@ -622,24 +259,31 @@ static int showdir(const char *path, int start, const int *dirfilter) int icon; tree_max_on_screen = TREE_MAX_ON_SCREEN; #endif + int start = tc.dirstart; + bool id3db = global_settings.dirfilter == SHOW_ID3DB; - /* new dir? cache it */ - if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) { - if (!load_and_sort_directory(path, dirfilter, - &filesindir, &dir_buffer_full)) + /* new file dir? load it */ + if (id3db) { + if (db_load(&tc, &dir_buffer_full) < 0) return -1; + } + else { + if (strncmp(tc.currdir, lastdir, sizeof(lastdir)) || reload_dir) { + if (ft_load(&tc, &dir_buffer_full) < 0) + return -1; + } + } - if ( dir_buffer_full || filesindir == max_files_in_dir ) { + if ( dir_buffer_full || tc.filesindir == global_settings.max_files_in_dir ) { #ifdef HAVE_LCD_CHARCELLS - lcd_double_height(false); + lcd_double_height(false); #endif - lcd_clear_display(); - lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER)); - lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL)); - lcd_update(); - sleep(HZ*2); - lcd_clear_display(); - } + lcd_clear_display(); + lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER)); + lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL)); + lcd_update(); + sleep(HZ*2); + lcd_clear_display(); } if (start == -1) @@ -649,8 +293,10 @@ static int showdir(const char *path, int start, const int *dirfilter) /* use lastfile to determine start (default=0) */ start = 0; - for (i=0; i= filesindir) + while (start + tc.dircursor >= tc.filesindir) { - if(start) + if (start) start--; else - if(dircursor) - dircursor--; + if (tc.dircursor) + tc.dircursor--; } - dirstart = start; + tc.dirstart = start; } #ifdef HAVE_LCD_CHARCELLS @@ -698,11 +344,24 @@ static int showdir(const char *path, int start, const int *dirfilter) lcd_setfont(FONT_UI); #endif - for ( i=start; i < start+tree_max_on_screen; i++ ) { - if ( i >= filesindir ) - break; - icon = filetype_get_icon(dircache[i].attr); + for ( i=start; i < start+tree_max_on_screen && i < tc.filesindir; i++ ) { + int line = i - start; + char* name; + int attr = 0; + + if (id3db) { + name = ((char**)tc.dircache)[i * 2]; + icon = db_get_icon(&tc); + } + else { + struct entry* dc = tc.dircache; + struct entry* e = &dc[i]; + name = e->name; + attr = e->attr; + icon = filetype_get_icon(dircache[i].attr); + } + if (icon && global_settings.show_icons) { #ifdef HAVE_LCD_BITMAP @@ -720,18 +379,18 @@ static int showdir(const char *path, int start, const int *dirfilter) #endif } - showfileline(i-start, i, false, dirfilter); /* no scroll */ + showfileline(line, name, attr, false); /* no scroll */ } #ifdef HAVE_LCD_BITMAP - if (global_settings.scrollbar && (filesindir > tree_max_on_screen)) + if (global_settings.scrollbar && (tc.filesindir > tree_max_on_screen)) scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1, - tree_max_on_screen * line_height, filesindir, start, + tree_max_on_screen * line_height, tc.filesindir, start, start + tree_max_on_screen, VERTICAL); #if CONFIG_KEYPAD == RECORDER_PAD if(global_settings.buttonbar) { - buttonbar_set(*dirfilter < NUM_FILTER_MODES ? + buttonbar_set(*tc.dirfilter < NUM_FILTER_MODES ? str(LANG_DIRBROWSE_F1) : (unsigned char *) "", str(LANG_DIRBROWSE_F2), str(LANG_DIRBROWSE_F3)); @@ -741,7 +400,7 @@ static int showdir(const char *path, int start, const int *dirfilter) #endif status_draw(true); - return filesindir; + return tc.filesindir; } static bool ask_resume(bool ask_once) @@ -826,12 +485,12 @@ void resume_directory(const char *dir) { bool buffer_full; - if (!load_and_sort_directory(dir, &global_settings.dirfilter, &filesindir, - &buffer_full)) + strcpy(tc.currdir, dir); + if (!ft_load(&tc, &buffer_full)) return; lastdir[0] = 0; - build_playlist(0); + ft_build_playlist(&tc, 0); } /* Returns the current working directory and also writes cwd to buf if @@ -839,10 +498,10 @@ void resume_directory(const char *dir) char *getcwd(char *buf, int size) { if (!buf) - return currdir; + return tc.currdir; else if (size > 0) { - strncpy(buf, currdir, size); + strncpy(buf, tc.currdir, size); return buf; } else @@ -888,41 +547,54 @@ void set_current_file(char *path) if (name) { *name = 0; - strcpy(currdir, path); + strcpy(tc.currdir, path); *name = '/'; name++; } else { - strcpy(currdir, "/"); + strcpy(tc.currdir, "/"); name = path+1; } strcpy(lastfile, name); - dircursor = 0; - dirstart = -1; + tc.dircursor = 0; + tc.dirstart = -1; - if (strncmp(currdir,lastdir,sizeof(lastdir))) + if (strncmp(tc.currdir,lastdir,sizeof(lastdir))) { - dirlevel = 0; - dirpos[dirlevel] = -1; - cursorpos[dirlevel] = 0; + tc.dirlevel = 0; + tc.dirpos[tc.dirlevel] = -1; + tc.cursorpos[tc.dirlevel] = 0; /* use '/' to calculate dirlevel */ for (i=1; i NUM_FILTER_MODES && numentries==0) + + if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0) { splash(HZ*2, true, str(LANG_NO_FILES)); return false; /* No files found for rockbox_browser() */ } - update_all = true; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); while(1) { - struct entry* file = &dircache[dircursor+dirstart]; + struct entry *dircache = tc.dircache; bool restore = false; @@ -1003,7 +674,7 @@ static bool dirbrowse(const char *root, const int *dirfilter) (button & BUTTON_REL)) stop = true; break; - } + } } restore = true; @@ -1012,48 +683,71 @@ static bool dirbrowse(const char *root, const int *dirfilter) #endif switch ( button ) { - case TREE_EXIT: - case TREE_EXIT | BUTTON_REPEAT: -#ifdef TREE_RC_EXIT - case TREE_RC_EXIT: +#ifdef TREE_ENTER + case TREE_ENTER: + case TREE_ENTER | BUTTON_REPEAT: #endif - i=strlen(currdir); - if (i>1) { - while (currdir[i-1]!='/') - i--; - strcpy(buf,&currdir[i]); - if (i==1) - currdir[i]=0; - else - currdir[i-1]=0; - - if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1) - exit_func = true; +#ifdef TREE_RC_ENTER + case TREE_RC_ENTER: +#endif + case TREE_RUN: +#ifdef TREE_RUN_PRE + if ((button == TREE_RUN) && + (lastbutton != TREE_RUN_PRE)) + break; +#endif + if ( !numentries ) + break; - dirlevel--; - if ( dirlevel < MAX_DIR_LEVELS ) { - dirstart = dirpos[dirlevel]; - dircursor = cursorpos[dirlevel]; + if (id3db) + db_enter(&tc); + else { + switch (ft_enter(&tc)) + { + case 1: reload_dir = true; break; + case 2: reload_root = true; break; + case 3: start_wps = true; break; + case 4: exit_func = true; break; + default: break; } - else - dirstart = dircursor = 0; - if (dirstart == -1) - strcpy(lastfile, buf); +#ifdef HAVE_LCD_BITMAP + /* maybe we have a new font */ + tree_max_on_screen = recalc_screen_height(); +#endif + } - restore = true; + /* make sure cursor is on screen */ + while ( tc.dircursor > tree_max_on_screen ) + { + tc.dircursor--; + tc.dirstart++; } + + restore = true; + break; + + case TREE_EXIT: + case TREE_EXIT | BUTTON_REPEAT: +#ifdef TREE_RC_EXIT + case TREE_RC_EXIT: +#endif + if (!tc.dirlevel) + break; + + if (id3db) + db_exit(&tc); else - { - if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1) + if (ft_exit(&tc) == 4) exit_func = true; - } + + restore = true; break; #ifdef TREE_OFF #ifndef HAVE_SW_POWEROFF case TREE_OFF: - if (*dirfilter < NUM_FILTER_MODES) + if (*tc.dirfilter < NUM_FILTER_MODES) { /* Stop the music if it is playing, else show the shutdown screen */ @@ -1078,238 +772,37 @@ static bool dirbrowse(const char *root, const int *dirfilter) break; #endif -#ifdef TREE_ENTER - case TREE_ENTER: - case TREE_ENTER | BUTTON_REPEAT: -#endif -#ifdef TREE_RC_ENTER - case TREE_RC_ENTER: -#endif - case TREE_RUN: -#ifdef TREE_RUN_PRE - if ((button == TREE_RUN) && - (lastbutton != TREE_RUN_PRE)) - break; -#endif - if ( !numentries ) - break; - if (currdir[1]) - snprintf(buf,sizeof(buf),"%s/%s",currdir, file->name); - else - snprintf(buf,sizeof(buf),"/%s",file->name); - - if (file->attr & ATTR_DIRECTORY) { - memcpy(currdir,buf,sizeof(currdir)); - if ( dirlevel < MAX_DIR_LEVELS ) { - dirpos[dirlevel] = dirstart; - cursorpos[dirlevel] = dircursor; - } - dirlevel++; - dircursor=0; - dirstart=0; - } - else { - int seed = current_tick; - bool play = false; - int start_index=0; - - lcd_stop_scroll(); - switch ( file->attr & TREE_ATTR_MASK ) { - case TREE_ATTR_M3U: - if (bookmark_autoload(buf)) - { - restore = true; - break; - } - - if (playlist_create(currdir, file->name) != -1) - { - if (global_settings.playlist_shuffle) - playlist_shuffle(seed, -1); - start_index = 0; - playlist_start(start_index,0); - play = true; - } - break; - - case TREE_ATTR_MPA: - if (bookmark_autoload(currdir)) - { - restore = true; - break; - } - - if (playlist_create(currdir, NULL) != -1) - { - start_index = - build_playlist(dircursor+dirstart); - if (global_settings.playlist_shuffle) - { - start_index = - playlist_shuffle(seed,start_index); - - /* when shuffling dir.: play all files - even if the file selected by user is - not the first one */ - if (!global_settings.play_selected) - start_index = 0; - } - - playlist_start(start_index, 0); - play = true; - } - break; - - /* wps config file */ - case TREE_ATTR_WPS: - wps_load(buf,true); - set_file(buf, global_settings.wps_file, - MAX_FILENAME); - restore = true; - break; - - case TREE_ATTR_CFG: - if (!settings_load_config(buf)) - break; - lcd_clear_display(); - lcd_puts(0,0,str(LANG_SETTINGS_LOADED1)); - lcd_puts(0,1,str(LANG_SETTINGS_LOADED2)); -#ifdef HAVE_LCD_BITMAP - lcd_update(); - - /* maybe we have a new font */ - tree_max_on_screen = recalc_screen_height(); - /* make sure cursor is on screen */ - while ( dircursor > tree_max_on_screen ) - { - dircursor--; - dirstart++; - } -#endif - sleep(HZ/2); - restore = true; - break; - - case TREE_ATTR_BMARK: - bookmark_load(buf, false); - restore = true; - reload_dir = true; - break; - - case TREE_ATTR_LNG: - if(!lang_load(buf)) { - set_file(buf, global_settings.lang_file, - MAX_FILENAME); - talk_init(); /* use voice of same language */ - splash(HZ, true, str(LANG_LANGUAGE_LOADED)); - restore = true; - - language_changed = true; - } - break; - -#ifdef HAVE_LCD_BITMAP - case TREE_ATTR_FONT: - font_load(buf); - set_file(buf, global_settings.font_file, - MAX_FILENAME); - - tree_max_on_screen = recalc_screen_height(); - /* make sure cursor is on screen */ - while ( dircursor > tree_max_on_screen ) { - dircursor--; - dirstart++; - } - restore = true; - break; -#endif - -#ifndef SIMULATOR - /* firmware file */ - case TREE_ATTR_MOD: - rolo_load(buf); - break; -#endif - - /* plugin file */ - case TREE_ATTR_ROCK: - if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED) - { - if(*dirfilter > NUM_FILTER_MODES) - /* leave sub-browsers after usb, doing - otherwise might be confusing to the user */ - exit_func = true; - else - reload_root = true; - } - else - restore = true; - break; - - default: - { - char* plugin = filetype_get_plugin(file); - if (plugin) - { - if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED) - reload_root = true; - else - restore = true; - } - break; - } - } - - if ( play ) { - if ( global_settings.resume ) { - /* the resume_index must always be the index in the - shuffled list in case shuffle is enabled */ - global_settings.resume_index = start_index; - global_settings.resume_offset = 0; - settings_save(); - } - - start_wps = true; - } - else if (*dirfilter > NUM_FILTER_MODES && - *dirfilter != SHOW_FONT && - *dirfilter != SHOW_PLUGINS) - exit_func = true; - } - restore = true; - break; - case TREE_PREV: case TREE_PREV | BUTTON_REPEAT: #ifdef TREE_RC_PREV case TREE_RC_PREV: case TREE_RC_PREV | BUTTON_REPEAT: #endif - if(filesindir) { - if(dircursor) { - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); - dircursor--; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + if(tc.filesindir) { + if(tc.dircursor) { + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false); + tc.dircursor--; + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } else { - if (dirstart) { - dirstart--; - numentries = showdir(currdir, dirstart, dirfilter); + if (tc.dirstart) { + tc.dirstart--; + numentries = showdir(); update_all=true; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } else { if (numentries < tree_max_on_screen) { - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false); - dircursor = numentries - 1; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, + tc.dircursor = numentries - 1; + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } else { - dirstart = numentries - tree_max_on_screen; - dircursor = tree_max_on_screen - 1; - numentries = showdir(currdir, dirstart, dirfilter); + tc.dirstart = numentries - tree_max_on_screen; + tc.dircursor = tree_max_on_screen - 1; + numentries = showdir(); update_all = true; put_cursorxy(CURSOR_X, CURSOR_Y + tree_max_on_screen - 1, true); @@ -1326,32 +819,32 @@ static bool dirbrowse(const char *root, const int *dirfilter) case TREE_RC_NEXT: case TREE_RC_NEXT | BUTTON_REPEAT: #endif - if(filesindir) + if(tc.filesindir) { - if (dircursor + dirstart + 1 < numentries ) { - if(dircursor+1 < tree_max_on_screen) { - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); - dircursor++; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + if (tc.dircursor + tc.dirstart + 1 < numentries ) { + if(tc.dircursor+1 < tree_max_on_screen) { + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false); + tc.dircursor++; + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } else { - dirstart++; - numentries = showdir(currdir, dirstart, dirfilter); + tc.dirstart++; + numentries = showdir(); update_all = true; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } } else { if(numentries < tree_max_on_screen) { - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); - dirstart = dircursor = 0; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false); + tc.dirstart = tc.dircursor = 0; + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } else { - dirstart = dircursor = 0; - numentries = showdir(currdir, dirstart, dirfilter); + tc.dirstart = tc.dircursor = 0; + numentries = showdir(); update_all=true; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); } } need_update = true; @@ -1361,26 +854,26 @@ static bool dirbrowse(const char *root, const int *dirfilter) #ifdef TREE_PGUP case TREE_PGUP: case TREE_PGUP | BUTTON_REPEAT: - if ( dirstart ) { - dirstart -= tree_max_on_screen; - if ( dirstart < 0 ) - dirstart = 0; + if ( tc.dirstart ) { + tc.dirstart -= tree_max_on_screen; + if ( tc.dirstart < 0 ) + tc.dirstart = 0; } else - dircursor = 0; + tc.dircursor = 0; restore = true; break; case TREE_PGDN: case TREE_PGDN | BUTTON_REPEAT: - if ( dirstart < numentries - tree_max_on_screen ) { - dirstart += tree_max_on_screen; - if ( dirstart > + if ( tc.dirstart < numentries - tree_max_on_screen ) { + tc.dirstart += tree_max_on_screen; + if ( tc.dirstart > numentries - tree_max_on_screen ) - dirstart = numentries - tree_max_on_screen; + tc.dirstart = numentries - tree_max_on_screen; } else - dircursor = numentries - dirstart - 1; + tc.dircursor = numentries - tc.dirstart - 1; restore = true; break; #endif @@ -1390,12 +883,15 @@ static bool dirbrowse(const char *root, const int *dirfilter) if (lastbutton != TREE_MENU_PRE) break; #endif - if (*dirfilter < NUM_FILTER_MODES) + /* don't enter menu from plugin browser */ + if (*tc.dirfilter < NUM_FILTER_MODES) { lcd_stop_scroll(); if (main_menu()) reload_root = true; restore = true; + + id3db = check_changed_id3mode(id3db); } break; @@ -1405,7 +901,7 @@ static bool dirbrowse(const char *root, const int *dirfilter) break; #endif /* don't enter wps from plugin browser etc */ - if (*dirfilter < NUM_FILTER_MODES) + if (*tc.dirfilter < NUM_FILTER_MODES) { if (mpeg_status() & MPEG_STATUS_PLAY) { @@ -1421,16 +917,20 @@ static bool dirbrowse(const char *root, const int *dirfilter) #ifdef BUTTON_F2 case BUTTON_F2: - if (*dirfilter < NUM_FILTER_MODES) + /* don't enter f2 from plugin browser */ + if (*tc.dirfilter < NUM_FILTER_MODES) { if (quick_screen(CONTEXT_TREE, BUTTON_F2)) reload_root = true; restore = true; + + id3db = check_changed_id3mode(id3db); break; } case BUTTON_F3: - if (*dirfilter < NUM_FILTER_MODES) + /* don't enter f3 from plugin browser */ + if (*tc.dirfilter < NUM_FILTER_MODES) { if (quick_screen(CONTEXT_TREE, BUTTON_F3)) reload_root = true; @@ -1452,12 +952,12 @@ static bool dirbrowse(const char *root, const int *dirfilter) else { if (currdir[1]) snprintf(buf, sizeof buf, "%s/%s", - currdir, dircache[dircursor+dirstart].name); + currdir, dircache[tc.dircursor+tc.dirstart].name); else snprintf(buf, sizeof buf, "/%s", - dircache[dircursor+dirstart].name); + dircache[tc.dircursor+tc.dirstart].name); onplay_result = onplay(buf, - dircache[dircursor+dirstart].attr); + dircache[tc.dircursor+tc.dirstart].attr); } switch (onplay_result) @@ -1485,7 +985,7 @@ static bool dirbrowse(const char *root, const int *dirfilter) if (dircache[lasti].attr & ATTR_DIRECTORY) { DEBUGF("Playing directory thumbnail: %s", currdir); - res = play_dirname(lasti); + res = ft_play_dirname(lasti); if (res < 0) /* failed, not existing */ { /* say the number instead, as a fallback */ talk_id(VOICE_DIR, false); @@ -1495,10 +995,10 @@ static bool dirbrowse(const char *root, const int *dirfilter) else { DEBUGF("Playing file thumbnail: %s/%s%s\n", - currdir, dircache[lasti].name, file_thumbnail_ext); + currdir, dircache[lasti].name, file_thumbnail_ext); /* no fallback necessary, we knew in advance that the file exists */ - play_filename(currdir, dircache[lasti].name); + ft_play_filename(currdir, dircache[lasti].name); } thumbnail_time = -1; /* job done */ } @@ -1508,7 +1008,7 @@ static bool dirbrowse(const char *root, const int *dirfilter) default: if(default_event_handler(button) == SYS_USB_CONNECTED) { - if(*dirfilter > NUM_FILTER_MODES) + if(*tc.dirfilter > NUM_FILTER_MODES) /* leave sub-browsers after usb, doing otherwise might be confusing to the user */ exit_func = true; @@ -1538,22 +1038,22 @@ static bool dirbrowse(const char *root, const int *dirfilter) /* do we need to rescan dir? */ if (reload_dir || reload_root || - lastfilter != *dirfilter || + lastfilter != *tc.dirfilter || lastsortcase != global_settings.sort_case) { if ( reload_root ) { strcpy(currdir, "/"); - dirlevel = 0; + tc.dirlevel = 0; reload_root = false; } if (! reload_dir ) { - dircursor = 0; - dirstart = 0; + tc.dircursor = 0; + tc.dirstart = 0; lastdir[0] = 0; } - lastfilter = *dirfilter; + lastfilter = *tc.dirfilter; lastsortcase = global_settings.sort_case; restore = true; while (button_get(false)); /* clear button queue */ @@ -1571,9 +1071,9 @@ static bool dirbrowse(const char *root, const int *dirfilter) /* We need to adjust if the number of lines on screen have changed because of a status bar change */ - if(CURSOR_Y+LINE_Y+dircursor>tree_max_on_screen) { - dirstart++; - dircursor--; + if(CURSOR_Y+LINE_Y+tc.dircursor>tree_max_on_screen) { + tc.dirstart++; + tc.dircursor--; } #ifdef HAVE_LCD_BITMAP /* the sub-screen might've ruined the margins */ @@ -1581,72 +1081,98 @@ static bool dirbrowse(const char *root, const int *dirfilter) icon */ lcd_setfont(FONT_UI); #endif - numentries = showdir(currdir, dirstart, dirfilter); + numentries = showdir(); update_all = true; - put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); + put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true); need_update = true; reload_dir = false; } if ( numentries && need_update) { - i = dirstart+dircursor; + i = tc.dirstart+tc.dircursor; /* if MP3 filter is on, cut off the extension */ if(lasti!=i || restore) { + char* name; + int attr = 0; + + if (id3db) + name = ((char**)tc.dircache)[lasti * 2]; + else { + struct entry* dc = tc.dircache; + struct entry* e = &dc[lasti]; + name = e->name; + attr = e->attr; + } + lcd_stop_scroll(); /* So if lastdircursor and dircursor differ, and then full screen was not refreshed, restore the previous line */ - if ((lastdircursor != dircursor) && !update_all ) { - showfileline(lastdircursor, lasti, false, dirfilter); /* no scroll */ + if ((lastdircursor != tc.dircursor) && !update_all ) { + showfileline(lastdircursor, name, attr, false); /* no scroll */ } lasti=i; - lastdircursor=dircursor; + lastdircursor=tc.dircursor; thumbnail_time = -1; /* cancel whatever we were about to say */ - showfileline(dircursor, i, true, dirfilter); /* scroll please */ + if (id3db) + name = ((char**)tc.dircache)[lasti * 2]; + else { + struct entry* dc = tc.dircache; + struct entry* e = &dc[lasti]; + name = e->name; + attr = e->attr; + } + showfileline(tc.dircursor, name, attr, true); /* scroll please */ need_update = true; if (dircache[i].attr & ATTR_DIRECTORY) /* directory? */ { /* play directory thumbnail */ - if (global_settings.talk_dir == 3) /* hover */ - { /* "schedule" a thumbnail, to have a little dalay */ - thumbnail_time = current_tick + HOVER_DELAY; - } - else if (global_settings.talk_dir == 1) /* dirs as numbers */ - { - talk_id(VOICE_DIR, false); - talk_number(i+1, true); - } - else if (global_settings.talk_dir == 2) /* dirs spelled */ - { - talk_spell(dircache[i].name, false); + switch (global_settings.talk_dir) { + case 1: /* dirs as numbers */ + talk_id(VOICE_DIR, false); + talk_number(i+1, true); + break; + + case 2: /* dirs spelled */ + talk_spell(dircache[i].name, false); + break; + + case 3: /* thumbnail clip */ + /* "schedule" a thumbnail, to have a little dalay */ + thumbnail_time = current_tick + HOVER_DELAY; + break; + + default: + break; } } else /* file */ { - int voicemethod = global_settings.talk_file; - if (voicemethod == 3) /* thumbnail clip */ - { /* "schedule" a thumbnail, to have a little delay */ - if (dircache[i].attr & TREE_ATTR_THUMBNAIL) - { - thumbnail_time = current_tick + HOVER_DELAY; - } - else - { /* say the number as fallback */ - voicemethod = 1; - } - } - if (voicemethod == 1) /* files as numbers */ - { - play_filenumber(i-dirsindir+1, - dircache[i].attr & TREE_ATTR_MASK); - } - else if (voicemethod == 2) /* files spelled */ - { - talk_spell(dircache[i].name, false); + switch (global_settings.talk_file) { + case 1: /* files as numbers */ + ft_play_filenumber(i-tc.dirsindir+1, + dircache[i].attr & TREE_ATTR_MASK); + break; + + case 2: /* files spelled */ + talk_spell(dircache[i].name, false); + break; + + case 3: /* thumbnail clip */ + /* "schedule" a thumbnail, to have a little delay */ + if (dircache[i].attr & TREE_ATTR_THUMBNAIL) + thumbnail_time = current_tick + HOVER_DELAY; + else + /* spell the number as fallback */ + talk_spell(dircache[i].name, false); + break; + + default: + break; } } } @@ -1752,7 +1278,7 @@ bool create_playlist(void) char filename[MAX_PATH]; snprintf(filename, sizeof filename, "%s.m3u", - currdir[1] ? currdir : "/root"); + tc.currdir[1] ? tc.currdir : "/root"); lcd_clear_display(); lcd_puts(0,0,str(LANG_CREATING)); @@ -1763,7 +1289,8 @@ bool create_playlist(void) if (fd < 0) return false; - snprintf(filename, sizeof(filename), "%s", currdir[1] ? currdir : "/"); + snprintf(filename, sizeof(filename), "%s", + tc.currdir[1] ? tc.currdir : "/"); plsize = 0; add_dir(filename, sizeof(filename), fd); close(fd); @@ -1775,37 +1302,51 @@ bool create_playlist(void) bool rockbox_browse(const char *root, int dirfilter) { bool rc; - int dircursor_save = dircursor; - int dirstart_save = dirstart; - int dirlevel_save = dirlevel; - int dirpos_save = dirpos[0]; - int cursorpos_save = cursorpos[0]; - - memcpy(currdir_save, currdir, sizeof(currdir)); + int dircursor_save = tc.dircursor; + int dirstart_save = tc.dirstart; + int dirlevel_save = tc.dirlevel; + int dirpos_save = tc.dirpos[0]; + int cursorpos_save = tc.cursorpos[0]; + int* dirfilter_save = tc.dirfilter; + static char currdir_save[MAX_PATH]; + + memcpy(currdir_save, tc.currdir, sizeof(tc.currdir)); reload_dir = true; + memcpy(tc.currdir, root, sizeof(tc.currdir)); start_wps = false; - rc = dirbrowse(root, &dirfilter); - memcpy(currdir, currdir_save, sizeof(currdir)); + tc.dirfilter = &dirfilter; + + rc = dirbrowse(); + memcpy(tc.currdir, currdir_save, sizeof(tc.currdir)); reload_dir = true; - dirstart = dirstart_save; - cursorpos[0] = cursorpos_save; - dirlevel = dirlevel_save; - dircursor = dircursor_save; - dirpos[0] = dirpos_save; + tc.dirstart = dirstart_save; + tc.cursorpos[0] = cursorpos_save; + tc.dirlevel = dirlevel_save; + tc.dircursor = dircursor_save; + tc.dirpos[0] = dirpos_save; + tc.dirfilter = dirfilter_save; return false; } void tree_init(void) { + /* initialize tree context struct */ + memset(&tc, 0, sizeof(tc)); + tc.dirfilter = &global_settings.dirfilter; + + db_init(); + /* We copy the settings value in case it is changed by the user. We can't use it until the next reboot. */ - max_files_in_dir = global_settings.max_files_in_dir; - name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files_in_dir; + int max_files = global_settings.max_files_in_dir; - name_buffer = buffer_alloc(name_buffer_size); - dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry)); + tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files; + tc.name_buffer = buffer_alloc(tc.name_buffer_size); + + tc.dircache_size = max_files * sizeof(struct entry); + tc.dircache = buffer_alloc(tc.dircache_size); } void bookmark_play(char *resume_file, int index, int offset, int seed, @@ -1875,3 +1416,77 @@ void bookmark_play(char *resume_file, int index, int offset, int seed, start_wps=true; } + +int ft_play_filenumber(int pos, int attr) +{ + /* try to find a voice ID for the extension, if known */ + unsigned int j; + int ext_id = -1; /* default to none */ + for (j=0; j +#include +#include #if CONFIG_KEYPAD == IRIVER_H100_PAD #define TREE_NEXT BUTTON_DOWN @@ -95,6 +97,12 @@ #endif +#ifdef HAVE_LCD_BITMAP +#define BOOTFILE "ajbrec.ajz" +#else +#define BOOTFILE "archos.mod" +#endif + struct entry { short attr; /* FAT attributes + file type flags */ unsigned long time_write; /* Last write time */ @@ -108,6 +116,28 @@ struct filetype { int voiceclip; }; +/* browser context for file or db */ +struct tree_context { + int dirlevel; + int dircursor; + int dirstart; + int dirpos[MAX_DIR_LEVELS]; + int cursorpos[MAX_DIR_LEVELS]; + char currdir[MAX_PATH]; /* file use */ + int *dirfilter; /* file use */ + int filesindir; + int dirsindir; /* file use */ + int table_history[MAX_DIR_LEVELS]; /* db use */ + int extra_history[MAX_DIR_LEVELS]; /* db use */ + int currtable; /* db use */ + int currextra; /* db use */ + + void* dircache; + int dircache_size; + char* name_buffer; + int name_buffer_size; + int dentry_size; +}; /* using attribute bits not used by FAT (FAT uses lower 7) */ @@ -134,8 +164,7 @@ bool create_playlist(void); void resume_directory(const char *dir); char *getcwd(char *buf, int size); void reload_directory(void); -struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter, - int *num_files, bool *buffer_full); bool check_rockboxdir(void); +struct tree_context* tree_get_context(void); #endif -- cgit v1.2.3