From 84c7d8802106266c94d0a30827dff418a2fcac6d Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Tue, 18 Jan 2005 22:45:00 +0000 Subject: Added support for very large tables in ID3 database. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5595 a1c6a512-1295-4272-9138-f99709370657 --- apps/dbtree.c | 81 +++++++++++++---------- apps/dbtree.h | 6 +- apps/filetree.c | 15 ++--- apps/filetree.h | 2 +- apps/playlist.c | 4 +- apps/tree.c | 202 ++++++++++++++++++++++++++++++++++++-------------------- apps/tree.h | 5 ++ 7 files changed, 194 insertions(+), 121 deletions(-) (limited to 'apps') diff --git a/apps/dbtree.c b/apps/dbtree.c index 68a211a836..abe8aed647 100644 --- a/apps/dbtree.c +++ b/apps/dbtree.c @@ -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; iname_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; diff --git a/apps/dbtree.h b/apps/dbtree.h index 414aa6f19f..5fa09b0eaf 100644 --- a/apps/dbtree.h +++ b/apps/dbtree.h @@ -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 diff --git a/apps/filetree.c b/apps/filetree.c index 09c2a2dcc0..a46ffbb2bc 100644 --- a/apps/filetree.c +++ b/apps/filetree.c @@ -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 diff --git a/apps/filetree.h b/apps/filetree.h index fcf8b64af2..143b75ad66 100644 --- a/apps/filetree.h +++ b/apps/filetree.h @@ -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); diff --git a/apps/playlist.c b/apps/playlist.c index aa8415a86f..4506ec5f54 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -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) { diff --git a/apps/tree.c b/apps/tree.c index b844060322..2ba4585047 100644 --- a/apps/tree.c +++ b/apps/tree.c @@ -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)); diff --git a/apps/tree.h b/apps/tree.h index 87e0361e50..87364bfd5a 100644 --- a/apps/tree.h +++ b/apps/tree.h @@ -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) */ -- cgit v1.2.3