Added support for very large tables in ID3 database.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5595 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Björn Stenberg 2005-01-18 22:45:00 +00:00
parent f9c06226d6
commit 84c7d88021
7 changed files with 194 additions and 121 deletions

View File

@ -121,18 +121,21 @@ int db_init(void)
return 0;
}
int db_load(struct tree_context* c, bool* dir_buffer_full)
int db_load(struct tree_context* c)
{
int i, offset, len, rc;
int i, offset, 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 safeplacelen = 0;
int table = c->currtable;
int extra = c->currextra;
char* end_of_nbuf = c->name_buffer + c->name_buffer_size;
if (!initialized) {
DEBUGF("ID3 database is not initialized.\n");
c->filesindir = 0;
@ -140,8 +143,9 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
}
c->dentry_size = 2 * sizeof(int);
DEBUGF("db_load(%d, %x)\n", table, extra);
c->dirfull = false;
DEBUGF("db_load(%d, %x, %d)\n", table, extra, c->firstpos);
if (!table) {
table = allartists;
@ -150,51 +154,50 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
switch (table) {
case allsongs:
offset = songstart;
offset = songstart + c->firstpos * (songlen + 12);
itemcount = songcount;
stringlen = songlen;
break;
case allalbums:
offset = albumstart;
offset = albumstart +
c->firstpos * (albumlen + 4 + songarraylen * 4);
itemcount = albumcount;
stringlen = albumlen;
break;
case allartists:
offset = artiststart;
offset = artiststart +
c->firstpos * (artistlen + albumarraylen * 4);
itemcount = artistcount;
stringlen = artistlen;
break;
case albums:
case albums4artist:
/* '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);
safeplacelen = albumarraylen * 4;
safeplace = (void*)(end_of_nbuf - safeplacelen);
lseek(fd, extra + artistlen, SEEK_SET);
rc = read(fd, safeplace, len);
if (rc < len)
rc = read(fd, safeplace, safeplacelen);
if (rc < safeplacelen)
return -1;
#ifdef LITTLE_ENDIAN
for (i=0; i<albumarraylen; i++)
safeplace[i] = BE32(safeplace[i]);
#endif
offset = safeplace[0];
itemcount = albumarraylen;
stringlen = albumlen;
break;
case songs:
case songs4album:
/* 'extra' is offset to the album */
len = songarraylen * 4;
safeplace = (void*)(c->name_buffer + c->name_buffer_size - len);
//DEBUGF("Seeking to %x\n", extra + albumlen + 4);
safeplacelen = songarraylen * 4;
safeplace = (void*)(end_of_nbuf - safeplacelen);
lseek(fd, extra + albumlen + 4, SEEK_SET);
rc = read(fd, safeplace, len);
if (rc < len)
rc = read(fd, safeplace, safeplacelen);
if (rc < safeplacelen)
return -1;
#ifdef LITTLE_ENDIAN
@ -211,6 +214,10 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
return -1;
}
max_items = dcachesize / c->dentry_size;
end_of_nbuf -= safeplacelen;
c->dirlength = itemcount;
itemcount -= c->firstpos;
if (!safeplace) {
//DEBUGF("Seeking to %x\n", offset);
@ -222,8 +229,7 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
the rest is table specific. see below. */
if (itemcount > max_items)
if (dir_buffer_full)
*dir_buffer_full = true;
c->dirfull = true;
if (max_items > itemcount) {
max_items = itemcount;
@ -233,8 +239,10 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
int rc, skip=0;
if (safeplace) {
if (!safeplace[i])
if (!safeplace[i]) {
c->dirlength = i;
break;
}
//DEBUGF("Seeking to %x\n", safeplace[i]);
lseek(fd, safeplace[i], SEEK_SET);
offset = safeplace[i];
@ -252,15 +260,15 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
dptr[0] = (unsigned int)nptr;
switch (table) {
case songs:
case allsongs:
case songs4album:
/* save offset of this song */
skip = 12;
dptr[1] = offset;
break;
case allalbums:
case albums:
case albums4artist:
/* save offset of this album */
skip = songarraylen * 4 + 4;
dptr[1] = offset;
@ -273,15 +281,14 @@ int db_load(struct tree_context* c, bool* dir_buffer_full)
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) {
if ((void*)nptr > (void*)end_of_nbuf) {
DEBUGF("Name buffer overflow (%d)\n",i);
c->dirfull = true;
break;
}
dptr = (void*)dptr + c->dentry_size;
@ -299,11 +306,12 @@ void db_enter(struct tree_context* c)
{
switch (c->currtable) {
case allartists:
case albums:
case albums4artist:
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->pos_history[c->dirlevel] = c->firstpos;
c->dirlevel++;
break;
@ -313,16 +321,16 @@ void db_enter(struct tree_context* c)
switch (c->currtable) {
case allartists:
c->currtable = albums;
c->currtable = albums4artist;
c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1];
break;
case albums:
c->currtable = songs;
case albums4artist:
c->currtable = songs4album;
c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1];
break;
case songs:
case songs4album:
splash(HZ,true,"No playing implemented yet");
#if 0
/* find filenames, build playlist, play */
@ -334,7 +342,7 @@ void db_enter(struct tree_context* c)
break;
}
c->dirstart = c->dircursor = 0;
c->dirstart = c->dircursor = c->firstpos = 0;
}
void db_exit(struct tree_context* c)
@ -344,6 +352,7 @@ void db_exit(struct tree_context* c)
c->dircursor = c->cursorpos[c->dirlevel];
c->currtable = c->table_history[c->dirlevel];
c->currextra = c->extra_history[c->dirlevel];
c->firstpos = c->pos_history[c->dirlevel];
}
#ifdef HAVE_LCD_BITMAP
@ -354,7 +363,7 @@ const char* db_get_icon(struct tree_context* c)
switch (c->currtable)
{
case allsongs:
case songs:
case songs4album:
icon = File;
break;
@ -372,7 +381,7 @@ int db_get_icon(struct tree_context* c)
switch (c->currtable)
{
case allsongs:
case songs:
case songs4album:
icon = File;
break;

View File

@ -21,13 +21,13 @@
#include "tree.h"
enum table { invalid, allsongs, allalbums, allartists, albums, songs };
enum table { invalid, allsongs, allalbums, allartists,
albums4artist, songs4album };
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);
int db_load(struct tree_context* c);
#ifdef HAVE_LCD_BITMAP
const char* db_get_icon(struct tree_context* c);
#else

View File

@ -184,9 +184,8 @@ static int compare(const void* p1, const void* p2)
}
/* load and sort directory into dircache. returns NULL on failure. */
int ft_load(struct tree_context* c, bool *buffer_full)
int ft_load(struct tree_context* c)
{
extern char lastdir[]; /* from tree.c */
int i;
int name_buffer_used = 0;
DIR *dir = opendir(c->currdir);
@ -194,13 +193,13 @@ int ft_load(struct tree_context* c, bool *buffer_full)
return -1; /* not a directory */
c->dirsindir = 0;
if (buffer_full)
*buffer_full = false;
c->dirfull = 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));
struct entry* dptr =
(struct entry*)(c->dircache + i * sizeof(struct entry));
if (!entry)
break;
@ -268,8 +267,7 @@ int ft_load(struct tree_context* c, bool *buffer_full)
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;
c->dirfull = true;
break;
}
dptr->name = &c->name_buffer[name_buffer_used];
@ -281,10 +279,9 @@ int ft_load(struct tree_context* c, bool *buffer_full)
c->dirsindir++;
}
c->filesindir = i;
c->dirlength = 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

View File

@ -20,7 +20,7 @@
#define FILETREE_H
#include "tree.h"
int ft_load(struct tree_context* c, bool *buffer_full);
int ft_load(struct tree_context* c);
int ft_play_filenumber(int pos, int attr);
int ft_play_dirname(int start_index);
void ft_play_filename(char *dir, char *file);

View File

@ -545,7 +545,7 @@ static int add_directory_to_playlist(struct playlist_info* playlist,
/* use the tree browser dircache to load files */
global_settings.dirfilter = SHOW_ALL;
strncpy(tc->currdir, dirname, sizeof(tc->currdir));
num_files = ft_load(tc, NULL);
num_files = ft_load(tc);
files = (struct entry*) tc->dircache;
if(!num_files)
@ -585,7 +585,7 @@ static int add_directory_to_playlist(struct playlist_info* playlist,
/* we now need to reload our current directory */
strncpy(tc->currdir, dirname, sizeof(tc->currdir));
num_files = ft_load(tc, NULL);
num_files = ft_load(tc);
files = (struct entry*) tc->dircache;
if (!num_files)
{

View File

@ -92,7 +92,9 @@ static struct tree_context tc;
bool boot_changed = false;
char lastfile[MAX_PATH];
char lastdir[MAX_PATH];
static char lastdir[MAX_PATH];
static int lasttable, lastextra, lastfirstpos;
static int max_files = 0;
static bool reload_dir = false;
@ -245,10 +247,9 @@ static int showdir(void)
struct entry *dircache = tc.dircache;
int i;
int tree_max_on_screen;
bool dir_buffer_full = false;
int start = tc.dirstart;
bool id3db = global_settings.dirfilter == SHOW_ID3DB;
bool newdir = false;
#ifdef HAVE_LCD_BITMAP
const char* icon;
int line_height;
@ -264,17 +265,30 @@ static int showdir(void)
/* new file dir? load it */
if (id3db) {
if (db_load(&tc, &dir_buffer_full) < 0)
return -1;
if (tc.currtable != lasttable ||
tc.currextra != lastextra ||
tc.firstpos != lastfirstpos)
{
if (db_load(&tc) < 0)
return -1;
lasttable = tc.currtable;
lastextra = tc.currextra;
lastfirstpos = tc.firstpos;
newdir = true;
}
}
else {
if (strncmp(tc.currdir, lastdir, sizeof(lastdir)) || reload_dir) {
if (ft_load(&tc, &dir_buffer_full) < 0)
if (ft_load(&tc) < 0)
return -1;
strcpy(lastdir, tc.currdir);
newdir = true;
}
}
if ( dir_buffer_full || tc.filesindir == global_settings.max_files_in_dir ) {
if (newdir && !id3db &&
(tc.dirfull || tc.filesindir == global_settings.max_files_in_dir) )
{
#ifdef HAVE_LCD_CHARCELLS
lcd_double_height(false);
#endif
@ -383,10 +397,11 @@ static int showdir(void)
}
#ifdef HAVE_LCD_BITMAP
if (global_settings.scrollbar && (tc.filesindir > tree_max_on_screen))
if (global_settings.scrollbar && (tc.dirlength > tree_max_on_screen))
scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
tree_max_on_screen * line_height, tc.filesindir, start,
start + tree_max_on_screen, VERTICAL);
tree_max_on_screen * line_height, tc.dirlength,
start + tc.firstpos,
start + tc.firstpos + tree_max_on_screen, VERTICAL);
#if CONFIG_KEYPAD == RECORDER_PAD
if(global_settings.buttonbar) {
@ -483,10 +498,8 @@ static bool ask_resume(bool ask_once)
/* load tracks from specified directory to resume play */
void resume_directory(const char *dir)
{
bool buffer_full;
strcpy(tc.currdir, dir);
if (!ft_load(&tc, &buffer_full))
if (!ft_load(&tc))
return;
lastdir[0] = 0;
@ -586,10 +599,10 @@ static bool check_changed_id3mode(bool currmode)
if (currmode != (global_settings.dirfilter == SHOW_ID3DB)) {
currmode = global_settings.dirfilter == SHOW_ID3DB;
if (currmode) {
db_load(&tc, NULL);
db_load(&tc);
}
else
ft_load(&tc, NULL);
ft_load(&tc);
}
return currmode;
}
@ -624,6 +637,10 @@ static bool dirbrowse(void)
tc.dircursor=0;
tc.dirstart=0;
tc.dirlevel=0;
tc.firstpos=0;
lasttable = -1;
lastextra = -1;
lastfirstpos = 0;
if (*tc.dirfilter < NUM_FILTER_MODES)
start_resume(true);
@ -778,39 +795,59 @@ static bool dirbrowse(void)
case TREE_RC_PREV:
case TREE_RC_PREV | BUTTON_REPEAT:
#endif
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 (tc.dirstart) {
if (!tc.filesindir)
break;
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 (tc.dirstart || tc.firstpos) {
if (tc.dirstart)
tc.dirstart--;
numentries = showdir();
update_all=true;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
else {
if (numentries < tree_max_on_screen) {
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
false);
tc.dircursor = numentries - 1;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
true);
if (tc.firstpos > max_files/2) {
tc.firstpos -= max_files/2;
tc.dirstart += max_files/2;
tc.dirstart--;
}
else {
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);
tc.dirstart = tc.firstpos - 1;
tc.firstpos = 0;
}
}
restore = true;
}
else {
if (numentries < tree_max_on_screen) {
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
false);
tc.dircursor = numentries - 1;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
true);
}
else if (id3db && tc.dirfull) {
/* load last dir segment */
/* use max_files/2 in case names are longer than
AVERAGE_FILE_LENGTH */
tc.firstpos = tc.dirlength - max_files/2;
tc.dirstart = tc.firstpos;
tc.dircursor = tree_max_on_screen - 1;
numentries = showdir();
update_all = true;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
true);
}
else {
tc.dirstart = numentries - tree_max_on_screen;
tc.dircursor = tree_max_on_screen - 1;
restore = true;
}
}
need_update = true;
}
need_update = true;
break;
case TREE_NEXT:
@ -819,46 +856,67 @@ static bool dirbrowse(void)
case TREE_RC_NEXT:
case TREE_RC_NEXT | BUTTON_REPEAT:
#endif
if(tc.filesindir)
{
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 {
tc.dirstart++;
numentries = showdir();
update_all = true;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
if (!tc.filesindir)
break;
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 {
if(numentries < tree_max_on_screen) {
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false);
tc.dirstart = tc.dircursor = 0;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
else {
tc.dirstart = tc.dircursor = 0;
numentries = showdir();
update_all=true;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
tc.dirstart++;
restore = true;
}
need_update = true;
}
else if (id3db && (tc.firstpos || tc.dirfull)) {
if (tc.dircursor + tc.dirstart + tc.firstpos + 1 >= tc.dirlength) {
/* wrap and load first dir segment */
tc.firstpos = tc.dirstart = tc.dircursor = 0;
}
else {
/* load next dir segment */
tc.firstpos += tc.dirstart;
tc.dirstart = 0;
}
restore = true;
}
else {
if(numentries < tree_max_on_screen) {
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false);
tc.dirstart = tc.dircursor = 0;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
else {
tc.dirstart = tc.dircursor = 0;
numentries = showdir();
update_all=true;
put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
}
}
need_update = true;
break;
#ifdef TREE_PGUP
case TREE_PGUP:
case TREE_PGUP | BUTTON_REPEAT:
if ( tc.dirstart ) {
if (tc.dirstart) {
tc.dirstart -= tree_max_on_screen;
if ( tc.dirstart < 0 )
tc.dirstart = 0;
}
else if (tc.firstpos) {
if (tc.firstpos > max_files/2) {
tc.firstpos -= max_files/2;
tc.dirstart += max_files/2;
tc.dirstart -= tree_max_on_screen;
}
else {
tc.dirstart = tc.firstpos - tree_max_on_screen;
tc.firstpos = 0;
}
}
else
tc.dircursor = 0;
restore = true;
@ -868,10 +926,14 @@ static bool dirbrowse(void)
case TREE_PGDN | BUTTON_REPEAT:
if ( tc.dirstart < numentries - tree_max_on_screen ) {
tc.dirstart += tree_max_on_screen;
if ( tc.dirstart >
numentries - tree_max_on_screen )
if ( tc.dirstart > numentries - tree_max_on_screen )
tc.dirstart = numentries - tree_max_on_screen;
}
else if (id3db && tc.dirfull) {
/* load next dir segment */
tc.firstpos += tc.dirstart;
tc.dirstart = 0;
}
else
tc.dircursor = numentries - tc.dirstart - 1;
restore = true;
@ -1334,7 +1396,7 @@ void tree_init(void)
{
/* We copy the settings value in case it is changed by the user. We can't
use it until the next reboot. */
int max_files = global_settings.max_files_in_dir;
max_files = global_settings.max_files_in_dir;
/* initialize tree context struct */
memset(&tc, 0, sizeof(tc));

View File

@ -121,12 +121,16 @@ struct tree_context {
int dirlevel;
int dircursor;
int dirstart;
int firstpos; /* which dir entry is on first
position in dir buffer */
int pos_history[MAX_DIR_LEVELS];
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 dirlength; /* total number of entries in dir, incl. those not loaded */
int table_history[MAX_DIR_LEVELS]; /* db use */
int extra_history[MAX_DIR_LEVELS]; /* db use */
int currtable; /* db use */
@ -137,6 +141,7 @@ struct tree_context {
char* name_buffer;
int name_buffer_size;
int dentry_size;
bool dirfull;
};
/* using attribute bits not used by FAT (FAT uses lower 7) */