From 4e7eee3d589d9ec526611e8301a82ece8b3dd629 Mon Sep 17 00:00:00 2001 From: zagor Date: Thu, 27 Jan 2005 22:21:08 +0000 Subject: [PATCH] Added ID3 searching git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5683 a1c6a512-1295-4272-9138-f99709370657 --- apps/dbtree.c | 196 +++++++++++++++++++++++++++++++++---------- apps/dbtree.h | 3 +- apps/lang/english.lang | 30 +++++++ firmware/SOURCES | 1 + firmware/common/strcasestr.c | 122 +++++++++++++++++++++++++++ firmware/include/string.h | 1 + 6 files changed, 310 insertions(+), 43 deletions(-) create mode 100644 firmware/common/strcasestr.c diff --git a/apps/dbtree.c b/apps/dbtree.c index 6925fd3f2..5b30863f3 100644 --- a/apps/dbtree.c +++ b/apps/dbtree.c @@ -16,6 +16,7 @@ * KIND, either express or implied. * ****************************************************************************/ +#define _GNU_SOURCE #include #include #include "file.h" @@ -40,6 +41,7 @@ #include "dbtree.h" #include "icons.h" #include "lang.h" +#include "keyboard.h" #ifdef LITTLE_ENDIAN #include @@ -60,6 +62,9 @@ static int artistlen, initialized = 0; static int db_play_folder(struct tree_context* c); +static int db_search(struct tree_context* c, char* string); + +static char searchstring[32]; int db_init(void) { @@ -78,45 +83,35 @@ int db_init(void) ptr[1] != 'D' || ptr[2] != 'B') { - DEBUGF("File is not a rockbox id3 database, aborting\n"); + splash(HZ,true,"Not a rockbox ID3 database!"); return -1; } version = BE32(buf[0]) & 0xff; if (version != ID3DB_VERSION) { - DEBUGF("Unsupported database version %d, aborting.\n"); + splash(HZ,true,"Unsupported database version %d!", version); return -1; } - 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); if (songstart > albumstart || albumstart > artiststart) { - DEBUGF("Corrupt id3db database, aborting.\n"); + splash(HZ,true,"Corrupt ID3 database!"); return -1; } @@ -157,11 +152,33 @@ int db_load(struct tree_context* c) switch (table) { case root: { - static const int tables[] = {allartists, allalbums, allsongs}; + static const int tables[] = {allartists, allalbums, allsongs, + search }; char* nbuf = (char*)nptr; char* labels[] = { str(LANG_ID3DB_ARTISTS), str(LANG_ID3DB_ALBUMS), - str(LANG_ID3DB_SONGS)}; + str(LANG_ID3DB_SONGS), + str(LANG_ID3DB_SEARCH)}; + + for (i=0; i < 4; i++) { + strcpy(nbuf, labels[i]); + dptr[0] = (unsigned int)nbuf; + dptr[1] = tables[i]; + nbuf += strlen(nbuf) + 1; + dptr += 2; + } + c->dirlength = c->filesindir = i; + return i; + } + + case search: { + static const int tables[] = {searchartists, + searchalbums, + searchsongs}; + char* nbuf = (char*)nptr; + char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS), + str(LANG_ID3DB_SEARCH_ALBUMS), + str(LANG_ID3DB_SEARCH_SONGS)}; for (i=0; i < 3; i++) { strcpy(nbuf, labels[i]); @@ -173,12 +190,26 @@ int db_load(struct tree_context* c) c->dirlength = c->filesindir = i; return i; } - + + case searchartists: + case searchalbums: + case searchsongs: + i = db_search(c, searchstring); + c->dirlength = c->filesindir = i; + if (c->dirfull) { + splash(HZ, true, "%s %s", + str(LANG_SHOWDIR_ERROR_BUFFER), + str(LANG_SHOWDIR_ERROR_FULL)); + c->dirfull = false; + } + else + splash(HZ, true, str(LANG_ID3DB_MATCHES), i); + return i; + case allsongs: offset = songstart + c->firstpos * (songlen + 12); itemcount = songcount; stringlen = songlen; - c->dentry_size = 3; break; case allalbums: @@ -229,7 +260,6 @@ int db_load(struct tree_context* c) offset = safeplace[0]; itemcount = songarraylen; stringlen = songlen; - c->dentry_size = 3; break; default: @@ -257,7 +287,7 @@ int db_load(struct tree_context* c) if (max_items > itemcount) { max_items = itemcount; } - + for ( i=0; i < max_items; i++ ) { int rc, skip=0; int intbuf[4]; @@ -267,7 +297,6 @@ int db_load(struct tree_context* c) c->dirlength = i; break; } - //DEBUGF("Seeking to %x\n", safeplace[i]); lseek(fd, safeplace[i], SEEK_SET); offset = safeplace[i]; } @@ -286,16 +315,13 @@ int db_load(struct tree_context* c) switch (table) { case allsongs: case songs4album: - /* save offset of this song */ - dptr[1] = offset; - rc = read(fd, intbuf, 12); if (rc < 12) { DEBUGF("%d read(%d) returned %d\n", i, 12, rc); return -1; } /* save offset of filename */ - dptr[2] = BE32(intbuf[2]); + dptr[1] = BE32(intbuf[2]); break; case allalbums: @@ -332,11 +358,95 @@ int db_load(struct tree_context* c) return i; } +static int db_search(struct tree_context* c, char* string) +{ + int i, count, size, hits=0; + long start; + + char* nptr = c->name_buffer; + const char* end_of_nbuf = nptr + c->name_buffer_size; + + unsigned long* dptr = c->dircache; + const long dcachesize = global_settings.max_files_in_dir * + sizeof(struct entry); + + switch (c->currtable) { + case searchartists: + start = artiststart; + count = artistcount; + size = artistlen + albumarraylen * 4; + break; + + case searchalbums: + start = albumstart; + count = albumcount; + size = albumlen + 4 + songarraylen * 4; + break; + + case searchsongs: + start = songstart; + count = songcount; + size = songlen + 12; + break; + + default: + DEBUGF("Invalid table %d\n", c->currtable); + return 0; + } + + lseek(fd, start, SEEK_SET); + + for (i=0; icurrtable == searchsongs) { + /* store offset of filename */ + dptr[1] = BE32(*((long*)(nptr + songlen + 8))); + } + else + /* store offset of database record */ + dptr[1] = start + i * size; + + dptr += 2; + + /* limit dir buffer */ + if ((void*)(dptr + c->dentry_size) > + (void*)(c->dircache + dcachesize)) + { + c->dirfull = true; + break; + } + + nptr += strlen(nptr) + 1; + while ((unsigned long)nptr & 3) + nptr++; + + /* limit name buffer */ + if ((void*)nptr + size > (void*)end_of_nbuf) { + c->dirfull = true; + break; + } + } + } + + return hits; +} + int db_enter(struct tree_context* c) { int rc = 0; - int newextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1]; + int offset = (c->dircursor + c->dirstart) * c->dentry_size + 1; + int newextra = ((int*)c->dircache)[offset]; + if (c->dirlevel >= MAX_DIR_LEVELS) + return 0; + c->dirpos[c->dirlevel] = c->dirstart; c->cursorpos[c->dirlevel] = c->dircursor; c->table_history[c->dirlevel] = c->currtable; @@ -351,24 +461,36 @@ int db_enter(struct tree_context* c) break; case allartists: + case searchartists: c->currtable = albums4artist; c->currextra = newextra; break; case allalbums: case albums4artist: + case searchalbums: c->currtable = songs4album; c->currextra = newextra; break; case allsongs: case songs4album: + case searchsongs: c->dirlevel--; if (db_play_folder(c) >= 0) rc = 3; break; + + case search: + rc = kbd_input(searchstring, sizeof(searchstring)); + if (rc == -1 || !searchstring[0]) + c->dirlevel--; + else + c->currtable = newextra; + break; default: + c->dirlevel--; break; } @@ -401,7 +523,7 @@ static int db_play_folder(struct tree_context* c) /* TODO: add support for very long tables */ for (i=0; i < c->filesindir; i++) { - int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 2]; + int pathoffset = ((int*)c->dircache)[i * c->dentry_size + 1]; lseek(fd, pathoffset, SEEK_SET); rc = read(fd, buf, sizeof(buf)); if (rc < songlen) { @@ -424,6 +546,9 @@ static int db_play_folder(struct tree_context* c) #ifdef HAVE_LCD_BITMAP const char* db_get_icon(struct tree_context* c) +#else +int db_get_icon(struct tree_context* c) +#endif { int icon; @@ -431,6 +556,7 @@ const char* db_get_icon(struct tree_context* c) { case allsongs: case songs4album: + case searchsongs: icon = File; break; @@ -439,23 +565,9 @@ const char* db_get_icon(struct tree_context* c) break; } +#ifdef HAVE_LCD_BITMAP return bitmap_icons_6x8[icon]; -} #else -int db_get_icon(struct tree_context* c) -{ - int icon; - switch (c->currtable) - { - case allsongs: - case songs4album: - icon = File; - break; - - default: - icon = Folder; - break; - } return icon; -} #endif +} diff --git a/apps/dbtree.h b/apps/dbtree.h index b0c726881..5cf2e4fee 100644 --- a/apps/dbtree.h +++ b/apps/dbtree.h @@ -22,7 +22,8 @@ #include "tree.h" enum table { invalid, root, allsongs, allalbums, allartists, - albums4artist, songs4album }; + albums4artist, songs4album, + search, searchartists, searchalbums, searchsongs }; int db_init(void); int db_enter(struct tree_context* c); diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 05903601d..795bde69f 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -2895,3 +2895,33 @@ desc: ID3 virtual folder name eng: "Songs" voice: "" new: + +id: LANG_ID3DB_SEARCH +desc: ID3 virtual folder name +eng: "Search" +voice: "" +new: + +id: LANG_ID3DB_SEARCH_ARTISTS +desc: ID3 virtual folder name +eng: "Search Artists" +voice: "" +new: + +id: LANG_ID3DB_SEARCH_ALBUMS +desc: ID3 virtual folder name +eng: "Search Albums" +voice: "" +new: + +id: LANG_ID3DB_SEARCH_SONGS +desc: ID3 virtual folder name +eng: "Search Songs" +voice: "" +new: + +id: LANG_ID3DB_MATCHES +desc: ID3 virtual folder name +eng: "Found %d matches" +voice: "" +new: diff --git a/firmware/SOURCES b/firmware/SOURCES index c3db4e163..92ba69d17 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -17,6 +17,7 @@ common/qsort.c common/random.c common/sprintf.c common/strcasecmp.c +common/strcasestr.c common/strcat.c common/strchr.c common/strcmp.c diff --git a/firmware/common/strcasestr.c b/firmware/common/strcasestr.c new file mode 100644 index 000000000..095eebdd6 --- /dev/null +++ b/firmware/common/strcasestr.c @@ -0,0 +1,122 @@ +/* Return the offset of one string within another. + Copyright (C) 1994,1996,1997,1998,1999,2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * My personal strstr() implementation that beats most other algorithms. + * Until someone tells me otherwise, I assume that this is the + * fastest implementation of strstr() in C. + * I deliberately chose not to comment it. You should have at least + * as much fun trying to understand it, as I had to write it :-). + * + * Stephen R. van den Berg, berg@pool.informatik.rwth-aachen.de*/ + +/* Faster looping by precalculating bl, bu, cl, cu before looping. + * 2004 Apr 08 Jose Da Silva, digital@joescat@com */ + +#include +#include + +typedef unsigned chartype; + +char* strcasestr (const char* phaystack, const char* pneedle) +{ + const unsigned char *haystack, *needle; + chartype bl, bu, cl, cu; + + haystack = (const unsigned char *) phaystack; + needle = (const unsigned char *) pneedle; + + bl = tolower (*needle); + if (bl != '\0') + { + bu = toupper (bl); + haystack--;/* possible ANSI violation */ + do + { + cl = *++haystack; + if (cl == '\0') + goto ret0; + } + while ((cl != bl) && (cl != bu)); + + cl = tolower (*++needle); + if (cl == '\0') + goto foundneedle; + cu = toupper (cl); + ++needle; + goto jin; + + for (;;) + { + chartype a; + const unsigned char *rhaystack, *rneedle; + + do + { + a = *++haystack; + if (a == '\0') + goto ret0; + if ((a == bl) || (a == bu)) + break; + a = *++haystack; + if (a == '\0') + goto ret0; + shloop: + ; + } + while ((a != bl) && (a != bu)); + + jin: a = *++haystack; + if (a == '\0') + goto ret0; + + if ((a != cl) && (a != cu)) + goto shloop; + + rhaystack = haystack-- + 1; + rneedle = needle; + a = tolower (*rneedle); + + if (tolower (*rhaystack) == (int) a) + do + { + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = tolower (*++needle); + if (tolower (*rhaystack) != (int) a) + break; + if (a == '\0') + goto foundneedle; + ++rhaystack; + a = tolower (*++needle); + } + while (tolower (*rhaystack) == (int) a); + + needle = rneedle;/* took the register-poor approach */ + + if (a == '\0') + break; + } + } + foundneedle: + return (char*) haystack; + ret0: + return 0; +} diff --git a/firmware/include/string.h b/firmware/include/string.h index e9aa24c53..713a87569 100644 --- a/firmware/include/string.h +++ b/firmware/include/string.h @@ -40,6 +40,7 @@ char *_EXFUN(strpbrk,(const char *, const char *)); char *_EXFUN(strrchr,(const char *, int)); size_t _EXFUN(strspn,(const char *, const char *)); char *_EXFUN(strstr,(const char *, const char *)); +char *_EXFUN(strcasestr,(const char *, const char *)); #ifndef _REENT_ONLY char *_EXFUN(strtok,(char *, const char *)); -- 2.11.4.GIT