Added ID3 searching

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5683 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Björn Stenberg 2005-01-27 22:21:08 +00:00
parent d719abce64
commit 6660f8e910
6 changed files with 312 additions and 45 deletions

View File

@ -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,31 +546,17 @@ static int db_play_folder(struct tree_context* c)
#ifdef HAVE_LCD_BITMAP
const char* db_get_icon(struct tree_context* c)
{
int icon;
switch (c->currtable)
{
case allsongs:
case songs4album:
icon = File;
break;
default:
icon = Folder;
break;
}
return bitmap_icons_6x8[icon];
}
#else
int db_get_icon(struct tree_context* c)
#endif
{
int icon;
switch (c->currtable)
{
case allsongs:
case songs4album:
case searchsongs:
icon = File;
break;
@ -456,6 +564,10 @@ int db_get_icon(struct tree_context* c)
icon = Folder;
break;
}
#ifdef HAVE_LCD_BITMAP
return bitmap_icons_6x8[icon];
#else
return icon;
}
#endif
}

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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;
}

View File

@ -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 *));