Added ID3 searching
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5683 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
d719abce64
commit
6660f8e910
200
apps/dbtree.c
200
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,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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 *));
|
||||
|
|
Loading…
Reference in New Issue