summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dbtree.c196
-rw-r--r--apps/dbtree.h3
-rw-r--r--apps/lang/english.lang30
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/common/strcasestr.c122
-rw-r--r--firmware/include/string.h1
6 files changed, 310 insertions, 43 deletions
diff --git a/apps/dbtree.c b/apps/dbtree.c
index 6925fd3f29..5b30863f39 100644
--- a/apps/dbtree.c
+++ b/apps/dbtree.c
@@ -16,6 +16,7 @@
* KIND, either express or implied.
*
****************************************************************************/
+#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include "file.h"
@@ -40,6 +41,7 @@
#include "dbtree.h"
#include "icons.h"
#include "lang.h"
+#include "keyboard.h"
#ifdef LITTLE_ENDIAN
#include <netinet/in.h>
@@ -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; i<count; i++) {
+ if (read(fd, nptr, size) < size) {
+ DEBUGF("Short read(%d) in db_search()\n",size);
+ break;
+ }
+ if (strcasestr(nptr, string)) {
+ hits++;
+
+ dptr[0] = (unsigned long)nptr;
+ if (c->currtable == 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 b0c726881f..5cf2e4feed 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 05903601db..795bde69f8 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 c3db4e163b..92ba69d179 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 0000000000..095eebdd66
--- /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 <string.h>
+#include <ctype.h>
+
+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 e9aa24c530..713a875698 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 *));