summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2006-03-26 11:33:42 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2006-03-26 11:33:42 +0000
commit7c4e0c8730d5b076d4db4206361bc38d5256a23f (patch)
tree43382ae25de9bfa0bbabdff7d51c32b651ad47b5 /apps
parent50d40ea43409745bc828e56af5e3879ea6b48cf1 (diff)
downloadrockbox-7c4e0c8730d5b076d4db4206361bc38d5256a23f.tar.gz
rockbox-7c4e0c8730d5b076d4db4206361bc38d5256a23f.zip
Initial version of tagcache! There are still some bugs in the engine
and much more problems with the UI. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9256 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES6
-rw-r--r--apps/codecs.c6
-rw-r--r--apps/codecs.h10
-rw-r--r--apps/debug_menu.c39
-rw-r--r--apps/lang/english.lang42
-rw-r--r--apps/main.c27
-rw-r--r--apps/metadata.c11
-rw-r--r--apps/metadata.h1
-rw-r--r--apps/onplay.c4
-rw-r--r--apps/plugin.c10
-rw-r--r--apps/plugin.h14
-rw-r--r--apps/plugins/Makefile3
-rw-r--r--apps/plugins/SOURCES2
-rw-r--r--apps/settings.c5
-rw-r--r--apps/settings.h1
-rw-r--r--apps/settings_menu.c22
-rw-r--r--apps/tagcache.c1868
-rw-r--r--apps/tagcache.h78
-rw-r--r--apps/tagtree.c536
-rw-r--r--apps/tagtree.h41
-rw-r--r--apps/tree.c31
-rw-r--r--apps/tree.h1
22 files changed, 2696 insertions, 62 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index e872531408..4c479b7a60 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -25,8 +25,7 @@ status.c
25talk.c 25talk.c
26#endif 26#endif
27tree.c 27tree.c
28dbtree.c 28tagtree.c
29database.c
30filetree.c 29filetree.c
31 30
32screen_access.c 31screen_access.c
@@ -69,7 +68,6 @@ recorder/recording.c
69#if CONFIG_CODEC == SWCODEC 68#if CONFIG_CODEC == SWCODEC
70pcmbuf.c 69pcmbuf.c
71playback.c 70playback.c
72metadata.c
73codecs.c 71codecs.c
74dsp.c 72dsp.c
75eq.c 73eq.c
@@ -81,3 +79,5 @@ eq_arm.S
81#endif 79#endif
82eq_menu.c 80eq_menu.c
83#endif 81#endif
82metadata.c
83tagcache.c
diff --git a/apps/codecs.c b/apps/codecs.c
index 298e5e1964..95a07539b9 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -177,12 +177,6 @@ struct codec_api ci = {
177 audio_flush_and_reload_tracks, 177 audio_flush_and_reload_tracks,
178 audio_get_file_pos, 178 audio_get_file_pos,
179 179
180 /* tag database */
181 &tagdbheader,
182 &tagdb_fd,
183 &tagdb_initialized,
184 tagdb_init,
185
186 /* misc */ 180 /* misc */
187 srand, 181 srand,
188 rand, 182 rand,
diff --git a/apps/codecs.h b/apps/codecs.h
index d28afc7292..50ab4cda0e 100644
--- a/apps/codecs.h
+++ b/apps/codecs.h
@@ -85,12 +85,12 @@
85#define CODEC_MAGIC 0x52434F44 /* RCOD */ 85#define CODEC_MAGIC 0x52434F44 /* RCOD */
86 86
87/* increase this every time the api struct changes */ 87/* increase this every time the api struct changes */
88#define CODEC_API_VERSION 5 88#define CODEC_API_VERSION 6
89 89
90/* update this to latest version if a change to the api struct breaks 90/* update this to latest version if a change to the api struct breaks
91 backwards compatibility (and please take the opportunity to sort in any 91 backwards compatibility (and please take the opportunity to sort in any
92 new function which are "waiting" at the end of the function table) */ 92 new function which are "waiting" at the end of the function table) */
93#define CODEC_MIN_API_VERSION 5 93#define CODEC_MIN_API_VERSION 6
94 94
95/* codec return codes */ 95/* codec return codes */
96enum codec_status { 96enum codec_status {
@@ -248,12 +248,6 @@ struct codec_api {
248 void (*audio_flush_and_reload_tracks)(void); 248 void (*audio_flush_and_reload_tracks)(void);
249 int (*audio_get_file_pos)(void); 249 int (*audio_get_file_pos)(void);
250 250
251 /* tag database */
252 struct tagdb_header *tagdbheader;
253 int *tagdb_fd;
254 int *tagdb_initialized;
255 int (*tagdb_init) (void);
256
257 /* misc */ 251 /* misc */
258 void (*srand)(unsigned int seed); 252 void (*srand)(unsigned int seed);
259 int (*rand)(void); 253 int (*rand)(void);
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 9ab505061e..c62f65e33d 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -49,6 +49,8 @@
49#include "screens.h" 49#include "screens.h"
50#include "misc.h" 50#include "misc.h"
51#include "splash.h" 51#include "splash.h"
52#include "dircache.h"
53#include "tagcache.h"
52#include "lcd-remote.h" 54#include "lcd-remote.h"
53 55
54#ifdef HAVE_LCD_BITMAP 56#ifdef HAVE_LCD_BITMAP
@@ -1855,6 +1857,10 @@ static bool dbg_dircache_info(void)
1855 dircache_get_build_ticks() / HZ); 1857 dircache_get_build_ticks() / HZ);
1856 lcd_puts(0, line++, buf); 1858 lcd_puts(0, line++, buf);
1857 1859
1860 snprintf(buf, sizeof(buf), "Entry count: %d",
1861 dircache_get_entry_count());
1862 lcd_puts(0, line++, buf);
1863
1858 lcd_update(); 1864 lcd_update();
1859 1865
1860 switch (button_get_w_tmo(HZ/2)) 1866 switch (button_get_w_tmo(HZ/2))
@@ -1871,6 +1877,38 @@ static bool dbg_dircache_info(void)
1871 1877
1872#endif /* HAVE_DIRCACHE */ 1878#endif /* HAVE_DIRCACHE */
1873 1879
1880static bool dbg_tagcache_info(void)
1881{
1882 bool done = false;
1883 int line;
1884 char buf[32];
1885
1886 lcd_setmargins(0, 0);
1887 lcd_setfont(FONT_SYSFIXED);
1888
1889 while (!done)
1890 {
1891 line = 0;
1892
1893 lcd_clear_display();
1894 snprintf(buf, sizeof(buf), "Current progress: %d%%",
1895 tagcache_get_progress());
1896 lcd_puts(0, line++, buf);
1897
1898 lcd_update();
1899
1900 switch (button_get_w_tmo(HZ/2))
1901 {
1902 case SETTINGS_OK:
1903 case SETTINGS_CANCEL:
1904 done = true;
1905 break;
1906 }
1907 }
1908
1909 return false;
1910}
1911
1874#if CONFIG_CPU == SH7034 1912#if CONFIG_CPU == SH7034
1875bool dbg_save_roms(void) 1913bool dbg_save_roms(void)
1876{ 1914{
@@ -2014,6 +2052,7 @@ bool debug_menu(void)
2014#ifdef HAVE_DIRCACHE 2052#ifdef HAVE_DIRCACHE
2015 { "View dircache info", dbg_dircache_info }, 2053 { "View dircache info", dbg_dircache_info },
2016#endif 2054#endif
2055 { "View tagcache info", dbg_tagcache_info },
2017#ifdef HAVE_LCD_BITMAP 2056#ifdef HAVE_LCD_BITMAP
2018 { "View audio thread", dbg_audio_thread }, 2057 { "View audio thread", dbg_audio_thread },
2019#ifdef PM_DEBUG 2058#ifdef PM_DEBUG
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 868b20646e..9923fa9caf 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3838,3 +3838,45 @@ desc: Backlight behaviour setting
3838eng: "First keypress enables backlight only" 3838eng: "First keypress enables backlight only"
3839voice: "First keypress enables backlight only" 3839voice: "First keypress enables backlight only"
3840new: 3840new:
3841
3842id: LANG_ID3DB_GENRES
3843desc: in tag cache
3844eng: "Genres"
3845voice: "Genres"
3846new:
3847
3848id: LANG_TAGCACHE
3849desc: in tag cache settings
3850eng: "Tag cache"
3851voice: "Tag cache"
3852new:
3853
3854id: LANG_TAGCACHE_INIT
3855desc: while initializing tagcache on boot
3856eng: "Committing tagcache"
3857voice:
3858new:
3859
3860id: LANG_TAGCACHE_DISK
3861desc: in tag cache settings
3862eng: "Keep on disk"
3863voice: "Keep on disk"
3864new:
3865
3866id: LANG_TAGCACHE_RAM
3867desc: in tag cache settings
3868eng: "Load to ram"
3869voice: "Load to ram"
3870new:
3871
3872id: LANG_TAGCACHE_FORCE_UPDATE
3873desc: in tag cache settings
3874eng: "Force tag cache update"
3875voice: "Force tag cache update"
3876new:
3877
3878id: LANG_TAGCACHE_FORCE_UPDATE_SPLASH
3879desc: in tag cache settings
3880eng: "Updating in background"
3881voice: "Updating in background"
3882new:
diff --git a/apps/main.c b/apps/main.c
index 09c1e4bea5..915aec27d3 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -60,6 +60,7 @@
60#include "misc.h" 60#include "misc.h"
61#include "database.h" 61#include "database.h"
62#include "dircache.h" 62#include "dircache.h"
63#include "tagcache.h"
63#include "lang.h" 64#include "lang.h"
64#include "string.h" 65#include "string.h"
65#include "splash.h" 66#include "splash.h"
@@ -138,6 +139,29 @@ void init_dircache(void)
138# define init_dircache(...) 139# define init_dircache(...)
139#endif 140#endif
140 141
142void init_tagcache(void)
143{
144 int font_w, font_h;
145
146#ifdef HAVE_LCD_BITMAP
147 /* Print "Scanning disk..." to the display. */
148 lcd_getstringsize("A", &font_w, &font_h);
149 lcd_putsxy((LCD_WIDTH/2) - ((strlen(str(LANG_TAGCACHE_INIT))*font_w)/2),
150 LCD_HEIGHT-font_h*3, str(LANG_TAGCACHE_INIT));
151 lcd_update();
152#endif
153
154 tagcache_init();
155
156#ifdef HAVE_LCD_BITMAP
157 /* Clean the text when we are done. */
158 lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
159 lcd_fillrect(0, LCD_HEIGHT-font_h*3, LCD_WIDTH, font_h);
160 lcd_set_drawmode(DRMODE_SOLID);
161 lcd_update();
162#endif
163}
164
141#ifdef SIMULATOR 165#ifdef SIMULATOR
142 166
143void init(void) 167void init(void)
@@ -162,6 +186,7 @@ void init(void)
162 gui_sync_wps_init(); 186 gui_sync_wps_init();
163 settings_apply(); 187 settings_apply();
164 init_dircache(); 188 init_dircache();
189 init_tagcache();
165 sleep(HZ/2); 190 sleep(HZ/2);
166 tree_init(); 191 tree_init();
167 playlist_init(); 192 playlist_init();
@@ -350,6 +375,7 @@ void init(void)
350 375
351 376
352 init_dircache(); 377 init_dircache();
378 init_tagcache();
353 gui_sync_wps_init(); 379 gui_sync_wps_init();
354 settings_apply(); 380 settings_apply();
355 381
@@ -379,7 +405,6 @@ void init(void)
379#endif 405#endif
380 talk_init(); 406 talk_init();
381 /* runtime database has to be initialized after audio_init() */ 407 /* runtime database has to be initialized after audio_init() */
382 rundb_init();
383 cpu_boost(false); 408 cpu_boost(false);
384 409
385#ifdef AUTOROCK 410#ifdef AUTOROCK
diff --git a/apps/metadata.c b/apps/metadata.c
index 5481cbef58..09ccb3928d 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -68,6 +68,7 @@ static const struct format_list formats[] =
68 { AFMT_MPA_L2, "mp2" }, 68 { AFMT_MPA_L2, "mp2" },
69 { AFMT_MPA_L2, "mpa" }, 69 { AFMT_MPA_L2, "mpa" },
70 { AFMT_MPA_L3, "mp3" }, 70 { AFMT_MPA_L3, "mp3" },
71#if CONFIG_CODEC == SWCODEC
71 { AFMT_OGG_VORBIS, "ogg" }, 72 { AFMT_OGG_VORBIS, "ogg" },
72 { AFMT_PCM_WAV, "wav" }, 73 { AFMT_PCM_WAV, "wav" },
73 { AFMT_FLAC, "flac" }, 74 { AFMT_FLAC, "flac" },
@@ -80,8 +81,10 @@ static const struct format_list formats[] =
80 { AFMT_SHN, "shn" }, 81 { AFMT_SHN, "shn" },
81 { AFMT_AIFF, "aif" }, 82 { AFMT_AIFF, "aif" },
82 { AFMT_AIFF, "aiff" }, 83 { AFMT_AIFF, "aiff" },
84#endif
83}; 85};
84 86
87#if CONFIG_CODEC == SWCODEC
85static const unsigned short a52_bitrates[] = 88static const unsigned short a52_bitrates[] =
86{ 89{
87 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 90 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
@@ -1246,6 +1249,7 @@ static bool get_musepack_metadata(int fd, struct mp3entry *id3)
1246 id3->bitrate = id3->filesize*8/id3->length; 1249 id3->bitrate = id3->filesize*8/id3->length;
1247 return true; 1250 return true;
1248} 1251}
1252#endif /* CONFIG_CODEC == SWCODEC */
1249 1253
1250static bool get_aiff_metadata(int fd, struct mp3entry* id3) 1254static bool get_aiff_metadata(int fd, struct mp3entry* id3)
1251{ 1255{
@@ -1318,7 +1322,7 @@ static bool get_aiff_metadata(int fd, struct mp3entry* id3)
1318} 1322}
1319 1323
1320/* Simple file type probing by looking at the filename extension. */ 1324/* Simple file type probing by looking at the filename extension. */
1321static unsigned int probe_file_format(const char *filename) 1325unsigned int probe_file_format(const char *filename)
1322{ 1326{
1323 char *suffix; 1327 char *suffix;
1324 unsigned int i; 1328 unsigned int i;
@@ -1349,9 +1353,11 @@ static unsigned int probe_file_format(const char *filename)
1349bool get_metadata(struct track_info* track, int fd, const char* trackname, 1353bool get_metadata(struct track_info* track, int fd, const char* trackname,
1350 bool v1first) 1354 bool v1first)
1351{ 1355{
1356#if CONFIG_CODEC == SWCODEC
1352 unsigned char* buf; 1357 unsigned char* buf;
1353 unsigned long totalsamples; 1358 unsigned long totalsamples;
1354 int i; 1359 int i;
1360#endif
1355 1361
1356 /* Take our best guess at the codec type based on file extension */ 1362 /* Take our best guess at the codec type based on file extension */
1357 track->id3.codectype = probe_file_format(trackname); 1363 track->id3.codectype = probe_file_format(trackname);
@@ -1369,6 +1375,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
1369 1375
1370 break; 1376 break;
1371 1377
1378#if CONFIG_CODEC == SWCODEC
1372 case AFMT_FLAC: 1379 case AFMT_FLAC:
1373 if (!get_flac_metadata(fd, &(track->id3))) 1380 if (!get_flac_metadata(fd, &(track->id3)))
1374 { 1381 {
@@ -1519,6 +1526,7 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
1519 } 1526 }
1520 /* TODO: read the id3v2 header if it exists */ 1527 /* TODO: read the id3v2 header if it exists */
1521 break; 1528 break;
1529#endif /* CONFIG_CODEC == SWCODEC */
1522 1530
1523 case AFMT_AIFF: 1531 case AFMT_AIFF:
1524 if (!get_aiff_metadata(fd, &(track->id3))) 1532 if (!get_aiff_metadata(fd, &(track->id3)))
@@ -1543,3 +1551,4 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
1543 1551
1544 return true; 1552 return true;
1545} 1553}
1554
diff --git a/apps/metadata.h b/apps/metadata.h
index 1e07a18277..f790146041 100644
--- a/apps/metadata.h
+++ b/apps/metadata.h
@@ -22,6 +22,7 @@
22 22
23#include "playback.h" 23#include "playback.h"
24 24
25unsigned int probe_file_format(const char *filename);
25bool get_metadata(struct track_info* track, int fd, const char* trackname, 26bool get_metadata(struct track_info* track, int fd, const char* trackname,
26 bool v1first); 27 bool v1first);
27 28
diff --git a/apps/onplay.c b/apps/onplay.c
index 2beb374956..5dcbe6e13e 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -820,12 +820,12 @@ int onplay(char* file, int attr, int from)
820 items[i].desc = ID2P(LANG_MENU_SHOW_ID3_INFO); 820 items[i].desc = ID2P(LANG_MENU_SHOW_ID3_INFO);
821 items[i].function = browse_id3; 821 items[i].function = browse_id3;
822 i++; 822 i++;
823 if(rundb_initialized) 823/* if(rundb_initialized)
824 { 824 {
825 items[i].desc = ID2P(LANG_MENU_SET_RATING); 825 items[i].desc = ID2P(LANG_MENU_SET_RATING);
826 items[i].function = set_rating; 826 items[i].function = set_rating;
827 i++; 827 i++;
828 } 828 }*/
829 } 829 }
830 830
831#ifdef HAVE_MULTIVOLUME 831#ifdef HAVE_MULTIVOLUME
diff --git a/apps/plugin.c b/apps/plugin.c
index 4395195d0f..51e071020a 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -333,16 +333,6 @@ static const struct plugin_api rockbox_api = {
333#endif 333#endif
334#endif /* !SIMULATOR && CONFIG_CODEC != SWCODEC */ 334#endif /* !SIMULATOR && CONFIG_CODEC != SWCODEC */
335 335
336 /* tag database */
337 &tagdbheader,
338 &tagdb_fd,
339 &tagdb_initialized,
340 tagdb_init,
341 /* runtime database */
342 &rundbheader,
343 &rundb_fd,
344 &rundb_initialized,
345
346 /* menu */ 336 /* menu */
347 menu_init, 337 menu_init,
348 menu_exit, 338 menu_exit,
diff --git a/apps/plugin.h b/apps/plugin.h
index 6584dbeed7..99dd3dc6ea 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -99,12 +99,12 @@
99#define PLUGIN_MAGIC 0x526F634B /* RocK */ 99#define PLUGIN_MAGIC 0x526F634B /* RocK */
100 100
101/* increase this every time the api struct changes */ 101/* increase this every time the api struct changes */
102#define PLUGIN_API_VERSION 13 102#define PLUGIN_API_VERSION 14
103 103
104/* update this to latest version if a change to the api struct breaks 104/* update this to latest version if a change to the api struct breaks
105 backwards compatibility (and please take the opportunity to sort in any 105 backwards compatibility (and please take the opportunity to sort in any
106 new function which are "waiting" at the end of the function table) */ 106 new function which are "waiting" at the end of the function table) */
107#define PLUGIN_MIN_API_VERSION 13 107#define PLUGIN_MIN_API_VERSION 14
108 108
109/* plugin return codes */ 109/* plugin return codes */
110enum plugin_status { 110enum plugin_status {
@@ -387,16 +387,6 @@ struct plugin_api {
387#endif 387#endif
388#endif 388#endif
389 389
390 /* tag database */
391 struct tagdb_header *tagdbheader;
392 int *tagdb_fd;
393 int *tagdb_initialized;
394 int (*tagdb_init) (void);
395 /* runtime database */
396 struct rundb_header *rundbheader;
397 int *rundb_fd;
398 int *rundb_initialized;
399
400 /* menu */ 390 /* menu */
401 int (*menu_init)(const struct menu_item* mitems, int count, 391 int (*menu_init)(const struct menu_item* mitems, int count,
402 int (*callback)(int, int), 392 int (*callback)(int, int),
diff --git a/apps/plugins/Makefile b/apps/plugins/Makefile
index 54917ab658..586eb605da 100644
--- a/apps/plugins/Makefile
+++ b/apps/plugins/Makefile
@@ -60,7 +60,8 @@ DIRS = .
60# Subdirs containing multi-file plugins 60# Subdirs containing multi-file plugins
61 61
62#for all targets 62#for all targets
63SUBDIRS += searchengine databox 63# SUBDIRS += searchengine databox
64SUBDIRS += databox
64 65
65#for any recorder, iRiver or iPod model 66#for any recorder, iRiver or iPod model
66ifneq (,$(strip $(foreach tgt,RECORDER IRIVER IPOD_COLOR IPOD_VIDEO GIGABEAT,$(findstring $(tgt),$(TARGET))))) 67ifneq (,$(strip $(foreach tgt,RECORDER IRIVER IPOD_COLOR IPOD_VIDEO GIGABEAT,$(findstring $(tgt),$(TARGET)))))
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 5ba983e4f7..68d7929b81 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -15,7 +15,7 @@ metronome.c
15#endif 15#endif
16mosaique.c 16mosaique.c
17rockbox_flash.c 17rockbox_flash.c
18search.c 18/* search.c */
19snow.c 19snow.c
20sort.c 20sort.c
21stats.c 21stats.c
diff --git a/apps/settings.c b/apps/settings.c
index 8c4e3dea71..6fe9665086 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -565,6 +565,11 @@ static const struct bit_entry hd_bits[] =
565 {LCD_DEPTH,S_O(fg_color),LCD_DEFAULT_FG,"foreground color","rgb"}, 565 {LCD_DEPTH,S_O(fg_color),LCD_DEFAULT_FG,"foreground color","rgb"},
566 {LCD_DEPTH,S_O(bg_color),LCD_DEFAULT_BG,"background color","rgb"}, 566 {LCD_DEPTH,S_O(bg_color),LCD_DEFAULT_BG,"background color","rgb"},
567#endif 567#endif
568
569#ifdef HAVE_DIRCACHE
570 {1, S_O(tagcache_ram), 0, "tagcache_ram", off_on },
571#endif
572
568 /* If values are just added to the end, no need to bump the version. */ 573 /* If values are just added to the end, no need to bump the version. */
569 /* new stuff to be added at the end */ 574 /* new stuff to be added at the end */
570 575
diff --git a/apps/settings.h b/apps/settings.h
index 424341f340..fb8e11ffde 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -419,6 +419,7 @@ struct user_settings
419#ifdef HAVE_DIRCACHE 419#ifdef HAVE_DIRCACHE
420 bool dircache; /* enable directory cache */ 420 bool dircache; /* enable directory cache */
421 int dircache_size; /* directory cache structure last size, 22 bits */ 421 int dircache_size; /* directory cache structure last size, 22 bits */
422 bool tagcache_ram; /* tag cache mode (0=disabled,1=disk,2=ram) */
422#endif 423#endif
423 int default_codepage; /* set default codepage for tag conversion */ 424 int default_codepage; /* set default codepage for tag conversion */
424#ifdef HAVE_REMOTE_LCD 425#ifdef HAVE_REMOTE_LCD
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index ab0e21d098..e381a14595 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -49,6 +49,7 @@
49#include "database.h" 49#include "database.h"
50#include "dir.h" 50#include "dir.h"
51#include "dircache.h" 51#include "dircache.h"
52#include "tagcache.h"
52#include "rbunicode.h" 53#include "rbunicode.h"
53#include "splash.h" 54#include "splash.h"
54#include "yesno.h" 55#include "yesno.h"
@@ -1259,15 +1260,15 @@ static bool next_folder(void)
1259static bool runtimedb(void) 1260static bool runtimedb(void)
1260{ 1261{
1261 bool rc; 1262 bool rc;
1262 bool old = global_settings.runtimedb; 1263// bool old = global_settings.runtimedb;
1263 1264
1264 rc = set_bool( str(LANG_RUNTIMEDB_ACTIVE), 1265 rc = set_bool( str(LANG_RUNTIMEDB_ACTIVE),
1265 &global_settings.runtimedb ); 1266 &global_settings.runtimedb );
1266 if (old && !global_settings.runtimedb) 1267/* if (old && !global_settings.runtimedb)
1267 rundb_shutdown(); 1268 rundb_shutdown();
1268 if (!old && global_settings.runtimedb) 1269 if (!old && global_settings.runtimedb)
1269 rundb_init(); 1270 rundb_init();
1270 1271*/
1271 return rc; 1272 return rc;
1272} 1273}
1273 1274
@@ -1479,6 +1480,7 @@ static bool beep(void)
1479} 1480}
1480#endif 1481#endif
1481 1482
1483
1482#ifdef HAVE_DIRCACHE 1484#ifdef HAVE_DIRCACHE
1483static bool dircache(void) 1485static bool dircache(void)
1484{ 1486{
@@ -1497,6 +1499,16 @@ static bool dircache(void)
1497 return result; 1499 return result;
1498} 1500}
1499 1501
1502static bool tagcache_ram(void)
1503{
1504 bool result = set_bool_options(str(LANG_TAGCACHE),
1505 &global_settings.tagcache_ram,
1506 STR(LANG_TAGCACHE_RAM),
1507 STR(LANG_TAGCACHE_DISK),
1508 NULL);
1509
1510 return result;
1511}
1500#endif /* HAVE_DIRCACHE */ 1512#endif /* HAVE_DIRCACHE */
1501 1513
1502static bool playback_settings_menu(void) 1514static bool playback_settings_menu(void)
@@ -1523,6 +1535,10 @@ static bool playback_settings_menu(void)
1523#endif 1535#endif
1524 { ID2P(LANG_ID3_ORDER), id3_order }, 1536 { ID2P(LANG_ID3_ORDER), id3_order },
1525 { ID2P(LANG_NEXT_FOLDER), next_folder }, 1537 { ID2P(LANG_NEXT_FOLDER), next_folder },
1538#ifdef HAVE_DIRCACHE
1539 { ID2P(LANG_TAGCACHE), tagcache_ram },
1540#endif
1541 { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_force_update },
1526 { ID2P(LANG_RUNTIMEDB_ACTIVE), runtimedb }, 1542 { ID2P(LANG_RUNTIMEDB_ACTIVE), runtimedb },
1527 }; 1543 };
1528 1544
diff --git a/apps/tagcache.c b/apps/tagcache.c
new file mode 100644
index 0000000000..6c6dd00a60
--- /dev/null
+++ b/apps/tagcache.c
@@ -0,0 +1,1868 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdio.h>
21#include "thread.h"
22#include "kernel.h"
23#include "system.h"
24#include "logf.h"
25#include "string.h"
26#include "usb.h"
27#include "dircache.h"
28#include "metadata.h"
29#include "id3.h"
30#include "settings.h"
31#include "splash.h"
32#include "lang.h"
33#include "tagcache.h"
34
35/* External reference to the big audiobuffer. */
36extern char *audiobuf, *audiobufend;
37
38/* Tag Cache thread. */
39static struct event_queue tagcache_queue;
40static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)];
41static const char tagcache_thread_name[] = "tagcache";
42
43/* Previous path when scanning directory tree recursively. */
44static char curpath[MAX_PATH*2];
45static long curpath_size = sizeof(curpath);
46
47/* Used when removing duplicates. */
48static char *tempbuf; /* Allocated when needed. */
49static long tempbufidx; /* Current location in buffer. */
50static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */
51static long tempbuf_left; /* Buffer space left. */
52static long tempbuf_pos;
53
54/* Tags we want to be unique (loaded to the tempbuf). */
55static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_title };
56
57/* Queue commands. */
58#define Q_STOP_SCAN 0
59#define Q_START_SCAN 1
60#define Q_FORCE_UPDATE 2
61
62/* Tag database files. */
63#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/tagcache_tmp.tcd"
64#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/tagcache_idx.tcd"
65#define TAGCACHE_FILE_INDEX ROCKBOX_DIR "/tagcache_%d.tcd"
66
67/* Tag database structures. */
68#define TAGCACHE_MAGIC 0x01020316
69
70/* Variable-length tag entry in tag files. */
71struct tagfile_entry {
72 short tag_length;
73 char tag_data[0];
74};
75
76/* Fixed-size tag entry in master db index. */
77struct index_entry {
78 long tag_seek[TAG_COUNT];
79};
80
81/* Header is the same in every file. */
82struct tagcache_header {
83 long magic;
84 long datasize;
85 long entry_count;
86};
87
88#ifdef HAVE_TC_RAMCACHE
89/* Header is created when loading database to ram. */
90struct ramcache_header {
91 struct tagcache_header h;
92 struct index_entry *indices;
93 char *tags[TAG_COUNT];
94 int entry_count[TAG_COUNT];
95};
96
97static struct ramcache_header *hdr;
98static bool ramcache = false;
99static long tagcache_size = 0;
100#endif
101
102/**
103 * Full tag entries stored in a temporary file waiting
104 * for commit to the cache. */
105struct temp_file_entry {
106 long tag_offset[TAG_COUNT];
107 short tag_length[TAG_COUNT];
108
109 long data_length;
110};
111
112struct tempbuf_id {
113 int id;
114 struct tempbuf_id *next;
115};
116
117struct tempbuf_searchidx {
118 struct tempbuf_id *id;
119 char *str;
120 int seek;
121};
122
123
124/* Used when building the temporary file. */
125static int cachefd = -1, filenametag_fd;
126static int total_entry_count = 0;
127static int data_size = 0;
128static int processed_dir_count;
129
130#ifdef HAVE_TC_RAMCACHE
131static struct index_entry *find_entry_ram(const char *filename,
132 const struct dircache_entry *dc)
133{
134 static long last_pos = 0;
135 int counter = 0;
136 int i;
137
138 /* Check if we tagcache is loaded into ram. */
139 if (!ramcache)
140 return NULL;
141
142 if (dc == NULL)
143 dc = dircache_get_entry_ptr(filename);
144
145 if (dc == NULL)
146 {
147 logf("tagcache: file not found.");
148 return NULL;
149 }
150
151 try_again:
152
153 if (last_pos > 0)
154 i = last_pos;
155 else
156 i = 0;
157
158 for (; i < hdr->h.entry_count - last_pos; i++)
159 {
160 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
161 {
162 last_pos = MAX(0, i - 3);
163 return &hdr->indices[i];
164 }
165
166 if (++counter == 100)
167 {
168 yield();
169 counter = 0;
170 }
171 }
172
173 if (last_pos > 0)
174 {
175 last_pos = 0;
176 goto try_again;
177 }
178
179 return NULL;
180}
181#endif
182
183static struct index_entry *find_entry_disk(const char *filename, bool retrieve)
184{
185 static struct index_entry idx;
186 static long last_pos = -1;
187 long pos_history[POS_HISTORY_COUNT];
188 long pos_history_idx = 0;
189 struct tagcache_header tch;
190 bool found = false;
191 struct tagfile_entry tfe;
192 int masterfd, fd = filenametag_fd;
193 char buf[MAX_PATH];
194 int i;
195 int pos = -1;
196
197 if (fd < 0)
198 {
199 last_pos = -1;
200 return NULL;
201 }
202
203 check_again:
204
205 if (last_pos > 0)
206 lseek(fd, last_pos, SEEK_SET);
207 else
208 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
209
210 while (true)
211 {
212 pos = lseek(fd, 0, SEEK_CUR);
213 for (i = pos_history_idx-1; i >= 0; i--)
214 pos_history[i+1] = pos_history[i];
215 pos_history[0] = pos;
216
217 if (read(fd, &tfe, sizeof(struct tagfile_entry)) !=
218 sizeof(struct tagfile_entry))
219 {
220 break ;
221 }
222
223 if (tfe.tag_length >= (long)sizeof(buf))
224 {
225 logf("too long tag");
226 close(fd);
227 last_pos = -1;
228 return NULL;
229 }
230
231 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
232 {
233 logf("read error #2");
234 close(fd);
235 last_pos = -1;
236 return NULL;
237 }
238
239 if (!strcasecmp(filename, buf))
240 {
241 last_pos = pos_history[pos_history_idx];
242 found = true;
243 break ;
244 }
245
246 if (pos_history_idx < POS_HISTORY_COUNT - 1)
247 pos_history_idx++;
248 }
249
250 /* Not found? */
251 if (!found)
252 {
253 if (last_pos > 0)
254 {
255 last_pos = -1;
256 logf("seek again");
257 goto check_again;
258 }
259 //close(fd);
260 return NULL;
261 }
262
263 if (!retrieve)
264 {
265 /* Just return a valid pointer without a valid entry. */
266 return &idx;
267 }
268
269 /* Found. Now read the index_entry (if requested). */
270 masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
271 if (masterfd < 0)
272 {
273 logf("open fail");
274 return NULL;
275 }
276
277 if (read(fd, &tch, sizeof(struct tagcache_header)) !=
278 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
279 {
280 logf("header error");
281 return NULL;
282 }
283
284 for (i = 0; i < tch.entry_count; i++)
285 {
286 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
287 sizeof(struct index_entry))
288 {
289 logf("read error #3");
290 close(fd);
291 return NULL;
292 }
293
294 if (idx.tag_seek[tag_filename] == pos)
295 break ;
296 }
297 close(masterfd);
298
299 /* Not found? */
300 if (i == tch.entry_count)
301 {
302 logf("not found!");
303 return NULL;
304 }
305
306 return &idx;
307}
308
309static bool build_lookup_list(struct tagcache_search *tcs)
310{
311 struct tagcache_header header;
312 struct index_entry entry;
313 int masterfd;
314 int i;
315
316 tcs->seek_list_count = 0;
317
318#ifdef HAVE_TC_RAMCACHE
319 if (tcs->ramsearch)
320 {
321 int j;
322
323 for (i = tcs->seek_pos; i < hdr->h.entry_count - tcs->seek_pos; i++)
324 {
325 if (tcs->seek_list_count == SEEK_LIST_SIZE)
326 break ;
327
328 for (j = 0; j < tcs->filter_count; j++)
329 {
330 if (hdr->indices[i].tag_seek[tcs->filter_tag[j]] !=
331 tcs->filter_seek[j])
332 break ;
333 }
334
335 if (j < tcs->filter_count)
336 continue ;
337
338 /* Add to the seek list if not already there. */
339 for (j = 0; j < tcs->seek_list_count; j++)
340 {
341 if (tcs->seek_list[j] == hdr->indices[i].tag_seek[tcs->type])
342 break ;
343 }
344
345 /* Lets add it. */
346 if (j == tcs->seek_list_count)
347 {
348 tcs->seek_list[tcs->seek_list_count] =
349 hdr->indices[i].tag_seek[tcs->type];
350 tcs->seek_list_count++;
351 }
352 }
353
354 tcs->seek_pos = i;
355
356 return tcs->seek_list_count > 0;
357 }
358#endif
359
360 masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
361 if (masterfd < 0)
362 {
363 logf("cannot open master index");
364 return false;
365 }
366
367 /* Load the header. */
368 if (read(masterfd, &header, sizeof(struct tagcache_header)) !=
369 sizeof(struct tagcache_header) || header.magic != TAGCACHE_MAGIC)
370 {
371 logf("read error");
372 close(masterfd);
373 return false;
374 }
375
376 lseek(masterfd, tcs->seek_pos * sizeof(struct index_entry) +
377 sizeof(struct tagcache_header), SEEK_SET);
378
379 while (read(masterfd, &entry, sizeof(struct index_entry)) ==
380 sizeof(struct index_entry))
381 {
382 if (tcs->seek_list_count == SEEK_LIST_SIZE)
383 break ;
384
385 for (i = 0; i < tcs->filter_count; i++)
386 {
387 if (entry.tag_seek[tcs->filter_tag[i]] != tcs->filter_seek[i])
388 break ;
389 }
390
391 tcs->seek_pos++;
392
393 if (i < tcs->filter_count)
394 continue ;
395
396 /* Add to the seek list if not already there. */
397 for (i = 0; i < tcs->seek_list_count; i++)
398 {
399 if (tcs->seek_list[i] == entry.tag_seek[tcs->type])
400 break ;
401 }
402
403 /* Lets add it. */
404 if (i == tcs->seek_list_count)
405 {
406 tcs->seek_list[tcs->seek_list_count] =
407 entry.tag_seek[tcs->type];
408 tcs->seek_list_count++;
409 }
410
411 }
412 close(masterfd);
413
414 return tcs->seek_list_count > 0;
415}
416
417bool tagcache_search(struct tagcache_search *tcs, int tag)
418{
419 struct tagcache_header h;
420 char buf[MAX_PATH];
421
422 if (tcs->valid)
423 tagcache_search_finish(tcs);
424
425 tcs->position = sizeof(struct tagcache_header);
426 tcs->fd = -1;
427 tcs->type = tag;
428 tcs->seek_pos = 0;
429 tcs->seek_list_count = 0;
430 tcs->filter_count = 0;
431 tcs->valid = true;
432
433#ifndef HAVE_TC_RAMCACHE
434 tcs->ramsearch = false;
435#else
436 tcs->ramsearch = ramcache;
437 if (tcs->ramsearch)
438 {
439 tcs->entry_count = hdr->entry_count[tcs->type];
440 }
441 else
442#endif
443 {
444 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tcs->type);
445 tcs->fd = open(buf, O_RDONLY);
446 if (tcs->fd < 0)
447 {
448 logf("failed to open index");
449 return false;
450 }
451
452 /* Check the header. */
453 if (read(tcs->fd, &h, sizeof(struct tagcache_header)) !=
454 sizeof(struct tagcache_header) || h.magic != TAGCACHE_MAGIC)
455 {
456 logf("incorrect header");
457 return false;
458 }
459 }
460
461 return true;
462}
463
464bool tagcache_search_add_filter(struct tagcache_search *tcs,
465 int tag, int seek)
466{
467 if (tcs->filter_count == TAGCACHE_MAX_FILTERS)
468 return false;
469
470 tcs->filter_tag[tcs->filter_count] = tag;
471 tcs->filter_seek[tcs->filter_count] = seek;
472 tcs->filter_count++;
473
474 return true;
475}
476
477bool tagcache_get_next(struct tagcache_search *tcs)
478{
479 static char buf[MAX_PATH];
480 struct tagfile_entry entry;
481
482 if (!tcs->valid)
483 return false;
484
485 if (tcs->fd < 0
486#ifdef HAVE_TC_RAMCACHE
487 && !tcs->ramsearch
488#endif
489 )
490 return false;
491
492 /* Relative fetch. */
493 if (tcs->filter_count > 0)
494 {
495 /* Check for end of list. */
496 if (tcs->seek_list_count == 0)
497 {
498 /* Try to fetch more. */
499 if (!build_lookup_list(tcs))
500 return false;
501 }
502
503 tcs->seek_list_count--;
504
505 /* Seek stream to the correct position and continue to direct fetch. */
506 if (!tcs->ramsearch)
507 lseek(tcs->fd, tcs->seek_list[tcs->seek_list_count], SEEK_SET);
508 else
509 tcs->position = tcs->seek_list[tcs->seek_list_count];
510 }
511
512 /* Direct fetch. */
513#ifdef HAVE_TC_RAMCACHE
514 if (tcs->ramsearch)
515 {
516 struct tagfile_entry *ep;
517
518 if (tcs->entry_count == 0)
519 {
520 tcs->valid = false;
521 return false;
522 }
523 tcs->entry_count--;
524 tcs->result_seek = tcs->position;
525
526 if (tcs->type == tag_filename)
527 {
528 dircache_copy_path((struct dircache_entry *)tcs->position,
529 buf, sizeof buf);
530 tcs->result = buf;
531 tcs->result_len = strlen(buf) + 1;
532
533 return true;
534 }
535
536 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->position];
537 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
538 tcs->result = ep->tag_data;
539 tcs->result_len = ep->tag_length;
540
541 return true;
542 }
543 else
544#endif
545 {
546 tcs->result_seek = lseek(tcs->fd, 0, SEEK_CUR);
547 if (read(tcs->fd, &entry, sizeof(struct tagfile_entry)) !=
548 sizeof(struct tagfile_entry))
549 {
550 /* End of data. */
551 tcs->valid = false;
552 return false;
553 }
554 }
555
556 if (entry.tag_length > (long)sizeof(buf))
557 {
558 tcs->valid = false;
559 logf("too long tag");
560 return false;
561 }
562
563 if (read(tcs->fd, buf, entry.tag_length) != entry.tag_length)
564 {
565 tcs->valid = false;
566 logf("read error");
567 return false;
568 }
569
570 tcs->result = buf;
571 tcs->result_len = entry.tag_length;
572
573 return true;
574}
575
576#if 0
577void tagcache_modify(struct tagcache_search *tcs, int type, const char *text)
578{
579 struct tagentry *entry;
580
581 if (tcs->type != tag_title)
582 return ;
583
584 /* We will need reserve buffer for this. */
585 if (tcs->ramcache)
586 {
587 struct tagfile_entry *ep;
588
589 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->result_seek];
590 tcs->seek_list[tcs->seek_list_count];
591 }
592
593 entry = find_entry_ram(
594
595}
596#endif
597
598void tagcache_search_finish(struct tagcache_search *tcs)
599{
600 if (tcs->fd >= 0)
601 {
602 close(tcs->fd);
603 tcs->fd = -1;
604 tcs->ramsearch = false;
605 tcs->valid = false;
606 }
607}
608
609#ifdef HAVE_TC_RAMCACHE
610struct tagfile_entry *get_tag(struct index_entry *entry, int tag)
611{
612 return (struct tagfile_entry *)&hdr->tags[tag][entry->tag_seek[tag]];
613}
614
615bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
616{
617 struct index_entry *entry;
618
619 /* Find the corresponding entry in tagcache. */
620 entry = find_entry_ram(filename, NULL);
621 if (entry == NULL || !ramcache)
622 return false;
623
624 id3->title = get_tag(entry, tag_title)->tag_data;
625 id3->artist = get_tag(entry, tag_artist)->tag_data;
626 id3->album = get_tag(entry, tag_album)->tag_data;
627 id3->genre_string = get_tag(entry, tag_genre)->tag_data;
628
629 return true;
630}
631#endif
632
633static inline void write_item(const char *item)
634{
635 int len = strlen(item) + 1;
636
637 data_size += len;
638 write(cachefd, item, len);
639}
640
641inline void check_if_empty(char **tag)
642{
643 if (tag == NULL || *tag == NULL || *tag[0] == '\0')
644 *tag = "Unknown";
645}
646
647#define CRC_BUF_LEN 8
648
649#ifdef HAVE_TC_RAMCACHE
650static void add_tagcache(const char *path, const struct dircache_entry *dc)
651#else
652static void add_tagcache(const char *path)
653#endif
654{
655 struct track_info track;
656 struct temp_file_entry entry;
657 bool ret;
658 int fd;
659 //uint32_t crcbuf[CRC_BUF_LEN];
660
661 if (cachefd < 0)
662 return ;
663
664 /* Check if the file is supported. */
665 if (probe_file_format(path) == AFMT_UNKNOWN)
666 return ;
667
668 /* Check if the file is already cached. */
669#ifdef HAVE_TC_RAMCACHE
670 if (ramcache)
671 {
672 if (find_entry_ram(path, dc))
673 return ;
674 }
675 else
676#endif
677 {
678 if (find_entry_disk(path, false))
679 return ;
680 }
681
682 fd = open(path, O_RDONLY);
683 if (fd < 0)
684 {
685 logf("open fail: %s", path);
686 return ;
687 }
688
689 memset(&track, 0, sizeof(struct track_info));
690 ret = get_metadata(&track, fd, path, false);
691 close(fd);
692
693 if (!ret)
694 {
695 track.id3.title = (char *)path;
696 track.id3.artist = "Unknown";
697 track.id3.album = "Unknown";
698 track.id3.genre_string = "Unknown";
699 }
700 else
701 check_if_empty(&track.id3.title);
702 check_if_empty(&track.id3.artist);
703 check_if_empty(&track.id3.album);
704 check_if_empty(&track.id3.genre_string);
705
706 entry.tag_length[tag_filename] = strlen(path) + 1;
707 entry.tag_length[tag_title] = strlen(track.id3.title) + 1;
708 entry.tag_length[tag_artist] = strlen(track.id3.artist) + 1;
709 entry.tag_length[tag_album] = strlen(track.id3.album) + 1;
710 entry.tag_length[tag_genre] = strlen(track.id3.genre_string) + 1;
711
712 entry.tag_offset[tag_filename] = 0;
713 entry.tag_offset[tag_title] = entry.tag_offset[tag_filename] + entry.tag_length[tag_filename];
714 entry.tag_offset[tag_artist] = entry.tag_offset[tag_title] + entry.tag_length[tag_title];
715 entry.tag_offset[tag_album] = entry.tag_offset[tag_artist] + entry.tag_length[tag_artist];
716 entry.tag_offset[tag_genre] = entry.tag_offset[tag_album] + entry.tag_length[tag_album];
717 entry.data_length = entry.tag_offset[tag_genre] + entry.tag_length[tag_genre];
718
719 write(cachefd, &entry, sizeof(struct temp_file_entry));
720 write_item(path);
721 write_item(track.id3.title);
722 write_item(track.id3.artist);
723 write_item(track.id3.album);
724 write_item(track.id3.genre_string);
725 total_entry_count++;
726}
727
728static void remove_files(void)
729{
730 int i;
731 char buf[MAX_PATH];
732
733 remove(TAGCACHE_FILE_MASTER);
734 for (i = 0; i < TAG_COUNT; i++)
735 {
736 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
737 remove(buf);
738 }
739}
740
741static bool tempbuf_insert(char *str, int id)
742{
743 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
744 int len = strlen(str)+1;
745
746 /* Insert it to the buffer. */
747 tempbuf_left -= len + sizeof(struct tempbuf_id);
748 if (tempbuf_left - 4 < 0 || tempbufidx >= TAGFILE_MAX_ENTRIES-1)
749 return false;
750
751 index[tempbufidx].id = (struct tempbuf_id *)&tempbuf[tempbuf_pos];
752#ifdef ROCKBOX_STRICT_ALIGN
753 /* Make sure the entry is long aligned. */
754 if ((long)index[tempbufidx].id & 0x03)
755 {
756 int fix = 4 - ((long)id & 0x03);
757 tempbuf_left -= fix;
758 tempbuf_pos += fix;
759 index[tempbufidx].id = (struct tempbuf_id *)((
760 (long)index[tempbufidx].id & ~0x03) + 0x04);
761 }
762#endif
763 index[tempbufidx].id->id = id;
764 index[tempbufidx].id->next = NULL;
765 tempbuf_pos += sizeof(struct tempbuf_id);
766
767 index[tempbufidx].seek = -1;
768 index[tempbufidx].str = &tempbuf[tempbuf_pos];
769 memcpy(index[tempbufidx].str, str, len);
770 tempbuf_pos += len;
771 tempbufidx++;
772
773 return true;
774}
775
776static bool tempbuf_unique_insert(char *str, int id)
777{
778 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
779 struct tempbuf_id *idp;
780 int i;
781
782 /* Check if string already exists. */
783 for (i = 0; i < tempbufidx; i++)
784 {
785 if (!strcasecmp(str, index[i].str))
786 {
787 tempbuf_left -= sizeof(struct tempbuf_id);
788 if (tempbuf_left < 0)
789 return false;
790
791 idp = index[i].id;
792 while (idp->next != NULL)
793 idp = idp->next;
794
795 idp->next = (struct tempbuf_id *)&tempbuf[tempbuf_pos];
796 idp = idp->next;
797 idp->id = id;
798 idp->next = NULL;
799 tempbuf_pos += sizeof(struct tempbuf_id);
800
801 return true;
802 }
803 }
804
805 return tempbuf_insert(str, id);
806}
807
808static int compare(const void *p1, const void *p2)
809{
810 struct tempbuf_searchidx *e1 = (struct tempbuf_searchidx *)p1;
811 struct tempbuf_searchidx *e2 = (struct tempbuf_searchidx *)p2;
812
813 /*
814 if (!strncasecmp("the ", e1, 4))
815 e1 = &e1[4];
816 if (!strncasecmp("the ", e2, 4))
817 e2 = &e2[4];
818 */
819
820 return strncasecmp(e1->str, e2->str, MAX_PATH);
821}
822
823static int tempbuf_sort(int fd)
824{
825 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
826 struct tagfile_entry fe;
827 int i;
828
829 qsort(index, tempbufidx, sizeof(struct tempbuf_searchidx), compare);
830
831 for (i = 0; i < tempbufidx; i++)
832 {
833 index[i].seek = lseek(fd, 0, SEEK_CUR);
834 fe.tag_length = strlen(index[i].str) + 1;
835 if (write(fd, &fe, sizeof(struct tagfile_entry)) !=
836 sizeof(struct tagfile_entry))
837 {
838 logf("tempbuf_sort: write error #1");
839 return -1;
840 }
841
842 if (write(fd, index[i].str, fe.tag_length) !=
843 fe.tag_length)
844 {
845 logf("tempbuf_sort: write error #2");
846 return -2;
847 }
848 }
849
850 return i;
851}
852
853
854static struct tempbuf_searchidx* tempbuf_locate(int id)
855{
856 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
857 struct tempbuf_id *idp;
858 int i;
859
860 /* Check if string already exists. */
861 for (i = 0; i < tempbufidx; i++)
862 {
863 idp = index[i].id;
864 while (idp != NULL)
865 {
866 if (idp->id == id)
867 return &index[i];
868 idp = idp->next;
869 }
870 }
871
872 return NULL;
873}
874
875
876static int tempbuf_find_location(int id)
877{
878 struct tempbuf_searchidx *entry;
879
880 entry = tempbuf_locate(id);
881 if (entry == NULL)
882 return -1;
883
884 return entry->seek;
885}
886
887static bool is_unique_tag(int type)
888{
889 int i;
890
891 for (i = 0; i < (int)(sizeof(unique_tags)/sizeof(unique_tags[0])); i++)
892 {
893 if (type == unique_tags[i])
894 return true;
895 }
896
897 return false;
898}
899
900static bool build_index(int index_type, struct tagcache_header *h, int tmpfd)
901{
902 int i;
903 struct tagcache_header tch;
904 struct index_entry idx;
905 char buf[MAX_PATH];
906 int fd = -1, masterfd;
907 bool error = false;
908 int init;
909 int masterfd_pos;
910
911 logf("Building index: %d", index_type);
912
913 tempbufidx = 0;
914 tempbuf_pos = TAGFILE_MAX_ENTRIES * sizeof(struct tempbuf_searchidx);
915 tempbuf_left = tempbuf_size - tempbuf_pos;
916 if (tempbuf_left < 0)
917 {
918 logf("Buffer way too small!");
919 return false;
920 }
921
922 /* Open the index file, which contains the tag names. */
923 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, index_type);
924 fd = open(buf, O_RDWR);
925
926 if (fd >= 0)
927 {
928 /* Read the header. */
929 if (read(fd, &tch, sizeof(struct tagcache_header)) !=
930 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
931 {
932 logf("header error");
933 close(fd);
934 return false;
935 }
936
937 /**
938 * If tag file contains unique tags (sorted index), we will load
939 * it entirely into memory so we can resort it later for use with
940 * chunked browsing.
941 */
942 if (is_unique_tag(index_type))
943 {
944 for (i = 0; i < tch.entry_count; i++)
945 {
946 struct tagfile_entry entry;
947 int loc = lseek(fd, 0, SEEK_CUR);
948
949 if (read(fd, &entry, sizeof(struct tagfile_entry))
950 != sizeof(struct tagfile_entry))
951 {
952 logf("read error");
953 close(fd);
954 return false;
955 }
956
957 if (entry.tag_length >= (int)sizeof(buf))
958 {
959 logf("too long tag");
960 close(fd);
961 return false;
962 }
963
964 if (read(fd, buf, entry.tag_length) != entry.tag_length)
965 {
966 logf("read error #2");
967 close(fd);
968 return false;
969 }
970
971 /**
972 * Save the tag and tag id in the memory buffer. Tag id
973 * is saved so we can later reindex the master lookup
974 * table when the index gets resorted.
975 */
976 tempbuf_insert(buf, loc + TAGFILE_MAX_ENTRIES);
977 }
978 }
979 else
980 tempbufidx = tch.entry_count;
981 }
982 else
983 {
984 /**
985 * Creating new index file to store the tags. No need to preload
986 * anything whether the index type is sorted or not.
987 */
988 fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC);
989 if (fd < 0)
990 {
991 logf("%s open fail", buf);
992 return false;
993 }
994
995 tch.magic = TAGCACHE_MAGIC;
996 tch.entry_count = 0;
997 tch.datasize = 0;
998
999 if (write(fd, &tch, sizeof(struct tagcache_header)) !=
1000 sizeof(struct tagcache_header))
1001 {
1002 logf("header write failed");
1003 close(fd);
1004 return false;
1005 }
1006 }
1007
1008 /* Loading the tag lookup file as "master file". */
1009 logf("Loading index file");
1010 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
1011
1012 if (masterfd < 0)
1013 {
1014 logf("Creating new index");
1015 masterfd = open(TAGCACHE_FILE_MASTER, O_WRONLY | O_CREAT | O_TRUNC);
1016
1017 if (masterfd < 0)
1018 {
1019 logf("Failure to create index file");
1020 close(fd);
1021 return false;
1022 }
1023
1024 /* Write the header (write real values later). */
1025 tch = *h;
1026 tch.entry_count = 0;
1027 tch.datasize = 0;
1028 write(masterfd, &tch, sizeof(struct tagcache_header));
1029 init = true;
1030 masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
1031 }
1032 else
1033 {
1034 /**
1035 * Master file already exists so we need to process the current
1036 * file first.
1037 */
1038 init = false;
1039
1040 if (read(masterfd, &tch, sizeof(struct tagcache_header)) !=
1041 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC)
1042 {
1043 logf("header error");
1044 close(fd);
1045 close(masterfd);
1046 return false;
1047 }
1048
1049 /**
1050 * If we reach end of the master file, we need to expand it to
1051 * hold new tags. If the current index is not sorted, we can
1052 * simply append new data to end of the file.
1053 * However, if the index is sorted, we need to update all tag
1054 * pointers in the master file for the current index.
1055 */
1056 masterfd_pos = lseek(masterfd, tch.entry_count * sizeof(struct index_entry),
1057 SEEK_CUR);
1058 if (masterfd_pos == filesize(masterfd))
1059 {
1060 logf("appending...");
1061 init = true;
1062 }
1063 }
1064
1065 /**
1066 * Load new unique tags in memory to be sorted later and added
1067 * to the master lookup file.
1068 */
1069 if (is_unique_tag(index_type))
1070 {
1071 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
1072 /* h is the header of the temporary file containing new tags. */
1073 for (i = 0; i < h->entry_count; i++)
1074 {
1075 struct temp_file_entry entry;
1076
1077 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
1078 sizeof(struct temp_file_entry))
1079 {
1080 logf("read fail #1");
1081 error = true;
1082 goto error_exit;
1083 }
1084
1085 /* Read data. */
1086 if (entry.tag_length[index_type] >= (long)sizeof(buf))
1087 {
1088 logf("too long entry!");
1089 error = true;
1090 goto error_exit;
1091 }
1092
1093 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
1094 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
1095 entry.tag_length[index_type])
1096 {
1097 logf("read fail #3");
1098 error = true;
1099 goto error_exit;
1100 }
1101
1102 if (!tempbuf_unique_insert(buf, i))
1103 {
1104 logf("insert error");
1105 error = true;
1106 goto error_exit;
1107 }
1108
1109 /* Skip to next. */
1110 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
1111 entry.tag_length[index_type], SEEK_CUR);
1112 }
1113
1114 /* Sort the buffer data and write it to the index file. */
1115 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
1116 i = tempbuf_sort(fd);
1117 if (i < 0)
1118 goto error_exit;
1119 logf("sorted %d tags", i);
1120
1121 /**
1122 * Now update all indexes in the master lookup file.
1123 */
1124 lseek(masterfd, sizeof(struct tagcache_header), SEEK_SET);
1125 for (i = 0; i < tch.entry_count; i++)
1126 {
1127 int loc = lseek(masterfd, 0, SEEK_CUR);
1128
1129 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
1130 sizeof(struct index_entry))
1131 {
1132 logf("read fail #2");
1133 error = true;
1134 goto error_exit ;
1135 }
1136 idx.tag_seek[index_type] = tempbuf_find_location(
1137 idx.tag_seek[index_type]+TAGFILE_MAX_ENTRIES);
1138 if (idx.tag_seek[index_type] < 0)
1139 {
1140 logf("update error: %d/%d", i, tch.entry_count);
1141 error = true;
1142 goto error_exit;
1143 }
1144
1145 /* Write back the updated index. */
1146 lseek(masterfd, loc, SEEK_SET);
1147 if (write(masterfd, &idx, sizeof(struct index_entry)) !=
1148 sizeof(struct index_entry))
1149 {
1150 logf("write fail");
1151 error = true;
1152 goto error_exit;
1153 }
1154 }
1155 }
1156
1157 /**
1158 * Walk through the temporary file containing the new tags.
1159 */
1160 // build_normal_index(h, tmpfd, masterfd, idx);
1161 lseek(masterfd, masterfd_pos, SEEK_SET);
1162 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
1163 lseek(fd, 0, SEEK_END);
1164 for (i = 0; i < h->entry_count; i++)
1165 {
1166 if (init)
1167 {
1168 memset(&idx, 0, sizeof(struct index_entry));
1169 }
1170 else
1171 {
1172 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
1173 sizeof(struct index_entry))
1174 {
1175 logf("read fail #2");
1176 error = true;
1177 break ;
1178 }
1179 lseek(masterfd, -sizeof(struct index_entry), SEEK_CUR);
1180 }
1181
1182 /* Read entry headers. */
1183 if (!is_unique_tag(index_type))
1184 {
1185 struct temp_file_entry entry;
1186 struct tagfile_entry fe;
1187
1188 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
1189 sizeof(struct temp_file_entry))
1190 {
1191 logf("read fail #1");
1192 error = true;
1193 break ;
1194 }
1195
1196 /* Read data. */
1197 if (entry.tag_length[index_type] >= (int)sizeof(buf))
1198 {
1199 logf("too long entry!");
1200 logf("length=%d", entry.tag_length[index_type]);
1201 logf("pos=0x%02x", lseek(tmpfd, 0, SEEK_CUR));
1202 error = true;
1203 break ;
1204 }
1205
1206 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
1207 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
1208 entry.tag_length[index_type])
1209 {
1210 logf("read fail #3");
1211 logf("offset=0x%02x", entry.tag_offset[index_type]);
1212 logf("length=0x%02x", entry.tag_length[index_type]);
1213 error = true;
1214 break ;
1215 }
1216
1217 /* Write to index file. */
1218 idx.tag_seek[index_type] = lseek(fd, 0, SEEK_CUR);
1219 fe.tag_length = entry.tag_length[index_type];
1220 write(fd, &fe, sizeof(struct tagfile_entry));
1221 write(fd, buf, fe.tag_length);
1222 tempbufidx++;
1223
1224 /* Skip to next. */
1225 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
1226 entry.tag_length[index_type], SEEK_CUR);
1227 }
1228 else
1229 {
1230 /* Locate the correct entry from the sorted array. */
1231 idx.tag_seek[index_type] = tempbuf_find_location(i);
1232 if (idx.tag_seek[index_type] < 0)
1233 {
1234 logf("entry not found (%d)");
1235 error = true;
1236 break ;
1237 }
1238 }
1239
1240
1241 /* Write index. */
1242 if (write(masterfd, &idx, sizeof(struct index_entry)) !=
1243 sizeof(struct index_entry))
1244 {
1245 logf("tagcache: write fail #4");
1246 error = true;
1247 break ;
1248 }
1249
1250 }
1251
1252 /* Finally write the uniqued tag index file. */
1253 if (is_unique_tag(index_type))
1254 {
1255 tch.magic = TAGCACHE_MAGIC;
1256 tch.entry_count = tempbufidx;
1257 tch.datasize = lseek(fd, 0, SEEK_END) - sizeof(struct tagcache_header);
1258 lseek(fd, 0, SEEK_SET);
1259 write(fd, &tch, sizeof(struct tagcache_header));
1260 }
1261 else
1262 {
1263 tch.magic = TAGCACHE_MAGIC;
1264 tch.entry_count = tempbufidx;
1265 tch.datasize = lseek(fd, 0, SEEK_CUR) - sizeof(struct tagcache_header);
1266 lseek(fd, 0, SEEK_SET);
1267 write(fd, &tch, sizeof(struct tagcache_header));
1268 }
1269
1270 error_exit:
1271
1272 close(fd);
1273 close(masterfd);
1274
1275 return !error;
1276}
1277
1278static bool commit(void)
1279{
1280 struct tagcache_header header, header_old;
1281 int i, len, rc;
1282 int tmpfd;
1283 int masterfd;
1284
1285 logf("committing tagcache");
1286
1287 tmpfd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
1288 if (tmpfd < 0)
1289 {
1290 logf("nothing to commit");
1291 return true;
1292 }
1293
1294 /* Load the header. */
1295 len = sizeof(struct tagcache_header);
1296 rc = read(tmpfd, &header, len);
1297
1298 if (header.magic != TAGCACHE_MAGIC || rc != len)
1299 {
1300 logf("incorrect header");
1301 close(tmpfd);
1302 remove_files();
1303 remove(TAGCACHE_FILE_TEMP);
1304 return false;
1305 }
1306
1307 if (header.entry_count == 0)
1308 {
1309 logf("nothing to commit");
1310 close(tmpfd);
1311 remove(TAGCACHE_FILE_TEMP);
1312 return true;
1313 }
1314
1315 if (tempbuf_size == 0)
1316 {
1317 logf("delaying commit until next boot");
1318 close(tmpfd);
1319 return false;
1320 }
1321
1322 logf("commit %d entries...", header.entry_count);
1323
1324 /* Now create the index files. */
1325 for (i = 0; i < TAG_COUNT; i++)
1326 {
1327 if (!build_index(i, &header, tmpfd))
1328 {
1329 logf("tagcache failed init");
1330 remove_files();
1331 return false;
1332 }
1333 }
1334
1335 close(tmpfd);
1336
1337 /* Update the master index headers. */
1338 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
1339 if (masterfd < 0)
1340 {
1341 logf("failed to open master index");
1342 return false;
1343 }
1344
1345 if (read(masterfd, &header_old, sizeof(struct tagcache_header))
1346 != sizeof(struct tagcache_header) ||
1347 header_old.magic != TAGCACHE_MAGIC)
1348 {
1349 logf("incorrect header");
1350 close(masterfd);
1351 remove_files();
1352 return false;
1353 }
1354
1355 header.entry_count += header_old.entry_count;
1356 header.datasize += header_old.datasize;
1357
1358 lseek(masterfd, 0, SEEK_SET);
1359 write(masterfd, &header, sizeof(struct tagcache_header));
1360 close(masterfd);
1361
1362 logf("tagcache committed");
1363 remove(TAGCACHE_FILE_TEMP);
1364
1365 return true;
1366}
1367
1368static void allocate_tempbuf(void)
1369{
1370 /* Yeah, malloc would be really nice now :) */
1371 tempbuf = (char *)(((long)audiobuf & ~0x03) + 0x04);
1372 tempbuf_size = (int)audiobufend - (int)audiobuf - 4;
1373 audiobuf += tempbuf_size;
1374}
1375
1376static void free_tempbuf(void)
1377{
1378 if (tempbuf_size == 0)
1379 return ;
1380
1381 audiobuf -= tempbuf_size;
1382 tempbuf = NULL;
1383 tempbuf_size = 0;
1384}
1385
1386#ifdef HAVE_TC_RAMCACHE
1387static bool allocate_tagcache(void)
1388{
1389 int rc, len;
1390 int fd;
1391
1392 hdr = NULL;
1393
1394 fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
1395 if (fd < 0)
1396 {
1397 logf("no tagcache file found.");
1398 return false;
1399 }
1400
1401 /* Load the header. */
1402 hdr = (struct ramcache_header *)audiobuf;
1403 memset(hdr, 0, sizeof(struct ramcache_header));
1404 len = sizeof(struct tagcache_header);
1405 rc = read(fd, &hdr->h, len);
1406 close(fd);
1407
1408 if (hdr->h.magic != TAGCACHE_MAGIC || rc != len)
1409 {
1410 logf("incorrect header");
1411 remove_files();
1412 hdr = NULL;
1413 return false;
1414 }
1415
1416 hdr->indices = (struct index_entry *)(hdr + 1);
1417
1418 /* Now calculate the required cache size. */
1419 tagcache_size = hdr->h.datasize +
1420 sizeof(struct index_entry) * hdr->h.entry_count +
1421 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
1422 logf("tagcache: %d bytes allocated.", tagcache_size);
1423 logf("at: 0x%04x", audiobuf);
1424 audiobuf += (long)((tagcache_size & ~0x03) + 0x04);
1425
1426 return true;
1427}
1428
1429static bool load_tagcache(void)
1430{
1431 struct tagcache_header *tch;
1432 long bytesleft = tagcache_size;
1433 struct index_entry *idx;
1434 int rc, fd;
1435 char *p;
1436 int i;
1437
1438 /* We really need the dircache for this. */
1439 while (!dircache_is_enabled())
1440 sleep(HZ);
1441
1442 logf("loading tagcache to ram...");
1443
1444 fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
1445 if (fd < 0)
1446 {
1447 logf("tagcache open failed");
1448 return false;
1449 }
1450
1451 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
1452
1453 idx = hdr->indices;
1454
1455 /* Load the master index table. */
1456 for (i = 0; i < hdr->h.entry_count; i++)
1457 {
1458 rc = read(fd, idx, sizeof(struct index_entry));
1459 if (rc != sizeof(struct index_entry))
1460 {
1461 logf("read error #1");
1462 close(fd);
1463 return false;
1464 }
1465
1466 bytesleft -= sizeof(struct index_entry);
1467 if (bytesleft < 0 || ((long)idx - (long)hdr->indices) >= tagcache_size)
1468 {
1469 logf("too big tagcache.");
1470 close(fd);
1471 return false;
1472 }
1473
1474 idx++;
1475 }
1476
1477 close(fd);
1478
1479 /* Load the tags. */
1480 p = (char *)idx;
1481 for (i = 0; i < TAG_COUNT; i++)
1482 {
1483 struct tagfile_entry *fe;
1484 char buf[MAX_PATH];
1485
1486 //p = ((void *)p+1);
1487 p = (char *)((long)p & ~0x03) + 0x04;
1488 hdr->tags[i] = p;
1489
1490 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
1491 fd = open(buf, O_RDONLY);
1492
1493 if (fd < 0)
1494 {
1495 logf("%s open fail", buf);
1496 return false;
1497 }
1498
1499 /* Check the header. */
1500 tch = (struct tagcache_header *)p;
1501 rc = read(fd, tch, sizeof(struct tagcache_header));
1502 p += rc;
1503 if (rc != sizeof(struct tagcache_header) ||
1504 tch->magic != TAGCACHE_MAGIC)
1505 {
1506 logf("incorrect header");
1507 close(fd);
1508 return false;
1509 }
1510
1511 for (hdr->entry_count[i] = 0;
1512 hdr->entry_count[i] < tch->entry_count;
1513 hdr->entry_count[i]++)
1514 {
1515 yield();
1516 fe = (struct tagfile_entry *)p;
1517 rc = read(fd, fe, sizeof(struct tagfile_entry));
1518 if (rc != sizeof(struct tagfile_entry))
1519 {
1520 /* End of lookup table. */
1521 logf("read error");
1522 close(fd);
1523 return false;
1524 }
1525
1526 /* We have a special handling for the filename tags. */
1527 if (i == tag_filename)
1528 {
1529 const struct dircache_entry *dc;
1530
1531 if (fe->tag_length >= (long)sizeof(buf)-1)
1532 {
1533 logf("too long filename");
1534 close(fd);
1535 return false;
1536 }
1537
1538 rc = read(fd, buf, fe->tag_length);
1539 if (rc != fe->tag_length)
1540 {
1541 logf("read error #3");
1542 close(fd);
1543 return false;
1544 }
1545
1546 dc = dircache_get_entry_ptr(buf);
1547 if (dc == NULL)
1548 {
1549 logf("Entry no longer valid.");
1550 logf("-> %s", buf);
1551 continue ;
1552 }
1553
1554 hdr->indices[hdr->entry_count[i]].tag_seek[tag_filename]
1555 = (long)dc;
1556
1557 continue ;
1558 }
1559
1560 bytesleft -= sizeof(struct tagfile_entry) + fe->tag_length;
1561 if (bytesleft < 0)
1562 {
1563 logf("too big tagcache.");
1564 close(fd);
1565 return false;
1566 }
1567
1568 p = fe->tag_data;
1569 rc = read(fd, fe->tag_data, fe->tag_length);
1570 p += rc;
1571
1572 if (rc != fe->tag_length)
1573 {
1574 logf("read error #4");
1575 logf("rc=0x%04x", rc); // 0x431
1576 logf("len=0x%04x", fe->tag_length); // 0x4000
1577 logf("pos=0x%04x", lseek(fd, 0, SEEK_CUR)); // 0x433
1578 logf("i=0x%02x", i); // 0x00
1579 close(fd);
1580 return false;
1581 }
1582 }
1583 close(fd);
1584 }
1585
1586 logf("tagcache loaded into ram!");
1587
1588 return true;
1589}
1590#endif
1591
1592static bool check_dir(const char *dirname)
1593{
1594 DIRCACHED *dir;
1595 int len;
1596 int success = false;
1597
1598 dir = opendir_cached(dirname);
1599 if (!dir)
1600 {
1601 logf("tagcache: opendir_cached() failed");
1602 return false;
1603 }
1604
1605 /* Recursively scan the dir. */
1606 while (queue_empty(&tagcache_queue))
1607 {
1608 struct dircache_entry *entry;
1609
1610 entry = readdir_cached(dir);
1611
1612 if (entry == NULL)
1613 {
1614 success = true;
1615 break ;
1616 }
1617
1618 if (!strcmp(entry->d_name, ".") ||
1619 !strcmp(entry->d_name, ".."))
1620 continue;
1621
1622 yield();
1623
1624 len = strlen(curpath);
1625 snprintf(&curpath[len], curpath_size - len, "/%s",
1626 entry->d_name);
1627
1628 processed_dir_count++;
1629 if (entry->attribute & ATTR_DIRECTORY)
1630 check_dir(curpath);
1631 else
1632#ifdef HAVE_TC_RAMCACHE
1633 add_tagcache(curpath, dir->internal_entry);
1634#else
1635 add_tagcache(curpath);
1636#endif
1637
1638 curpath[len] = '\0';
1639 }
1640
1641 closedir_cached(dir);
1642
1643 return success;
1644}
1645
1646static void build_tagcache(void)
1647{
1648 struct tagcache_header header;
1649 bool ret;
1650 char buf[MAX_PATH];
1651
1652 curpath[0] = '\0';
1653 data_size = 0;
1654 total_entry_count = 0;
1655 processed_dir_count = 0;
1656
1657 logf("updating tagcache");
1658
1659 cachefd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
1660 if (cachefd >= 0)
1661 {
1662 logf("skipping, cache already waiting for commit");
1663 close(cachefd);
1664 return ;
1665 }
1666
1667 cachefd = open(TAGCACHE_FILE_TEMP, O_RDWR | O_CREAT | O_TRUNC);
1668 if (cachefd < 0)
1669 {
1670 logf("master file open failed");
1671 return ;
1672 }
1673
1674 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag_filename);
1675 filenametag_fd = open(buf, O_RDONLY);
1676
1677 if (filenametag_fd >= 0)
1678 {
1679 if (read(filenametag_fd, &header, sizeof(struct tagcache_header)) !=
1680 sizeof(struct tagcache_header) || header.magic != TAGCACHE_MAGIC)
1681 {
1682 logf("header error");
1683 close(filenametag_fd);
1684 filenametag_fd = -1;
1685 }
1686 }
1687
1688
1689 cpu_boost(true);
1690
1691 /* Scan for new files. */
1692 memset(&header, 0, sizeof(struct tagcache_header));
1693 write(cachefd, &header, sizeof(struct tagcache_header));
1694
1695 //strcpy(curpath, "/Best");
1696 ret = check_dir("/");
1697
1698 /* Write the header. */
1699 header.magic = TAGCACHE_MAGIC;
1700 header.datasize = data_size;
1701 header.entry_count = total_entry_count;
1702 lseek(cachefd, 0, SEEK_SET);
1703 write(cachefd, &header, sizeof(struct tagcache_header));
1704 close(cachefd);
1705
1706 if (filenametag_fd >= 0)
1707 {
1708 close(filenametag_fd);
1709 filenametag_fd = -1;
1710 }
1711
1712 if (!ret)
1713 {
1714 logf("Aborted.");
1715 cpu_boost(false);
1716 return ;
1717 }
1718
1719 /* Commit changes to the database. */
1720 if (commit())
1721 {
1722 remove(TAGCACHE_FILE_TEMP);
1723 logf("tagcache built!");
1724 }
1725
1726 cpu_boost(false);
1727}
1728
1729#ifdef HAVE_TC_RAMCACHE
1730static void load_ramcache(void)
1731{
1732 if (!hdr)
1733 return ;
1734
1735 cpu_boost(true);
1736
1737 /* At first we should load the cache (if exists). */
1738 ramcache = load_tagcache();
1739
1740 if (!ramcache)
1741 {
1742 hdr = NULL;
1743 remove_files();
1744 }
1745
1746 cpu_boost(false);
1747}
1748#endif
1749
1750static void tagcache_thread(void)
1751{
1752 struct event ev;
1753 bool check_done = false;
1754
1755 while (1)
1756 {
1757 queue_wait_w_tmo(&tagcache_queue, &ev, HZ);
1758
1759 switch (ev.id)
1760 {
1761 case Q_START_SCAN:
1762 check_done = false;
1763 break ;
1764
1765 case Q_FORCE_UPDATE:
1766 //remove_files();
1767 build_tagcache();
1768 break ;
1769
1770#ifdef HAVE_TC_RAMCACHE
1771 case SYS_TIMEOUT:
1772 if (check_done || !dircache_is_enabled())
1773 break ;
1774
1775 if (!ramcache && global_settings.tagcache_ram)
1776 load_ramcache();
1777
1778 if (global_settings.tagcache_ram)
1779 build_tagcache();
1780
1781 check_done = true;
1782 break ;
1783#endif
1784
1785 case Q_STOP_SCAN:
1786 break ;
1787
1788 case SYS_POWEROFF:
1789 break ;
1790
1791#ifndef SIMULATOR
1792 case SYS_USB_CONNECTED:
1793 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1794 usb_wait_for_disconnect(&tagcache_queue);
1795 break ;
1796#endif
1797 }
1798 }
1799}
1800
1801int tagcache_get_progress(void)
1802{
1803 int total_count = -1;
1804
1805#ifdef HAVE_DIRCACHE
1806 if (dircache_is_enabled())
1807 {
1808 total_count = dircache_get_entry_count();
1809 }
1810 else
1811 {
1812 if (hdr)
1813 total_count = hdr->h.entry_count;
1814 }
1815#endif
1816
1817 if (total_count < 0)
1818 return -1;
1819
1820 return processed_dir_count * 100 / total_count;
1821}
1822
1823void tagcache_start_scan(void)
1824{
1825 queue_post(&tagcache_queue, Q_START_SCAN, 0);
1826}
1827
1828bool tagcache_force_update(void)
1829{
1830 queue_post(&tagcache_queue, Q_FORCE_UPDATE, 0);
1831 gui_syncsplash(HZ*2, true, str(LANG_TAGCACHE_FORCE_UPDATE_SPLASH));
1832
1833 return false;
1834}
1835
1836void tagcache_stop_scan(void)
1837{
1838 queue_post(&tagcache_queue, Q_STOP_SCAN, 0);
1839}
1840
1841#ifdef HAVE_TC_RAMCACHE
1842bool tagcache_is_ramcache(void)
1843{
1844 return ramcache;
1845}
1846#endif
1847
1848void tagcache_init(void)
1849{
1850 /* If the previous cache build/update was interrupted, commit
1851 * the changes first. */
1852 cpu_boost(true);
1853 allocate_tempbuf();
1854 commit();
1855 free_tempbuf();
1856 cpu_boost(false);
1857
1858#ifdef HAVE_TC_RAMCACHE
1859 /* Allocate space for the tagcache if found on disk. */
1860 allocate_tagcache();
1861#endif
1862
1863 queue_init(&tagcache_queue);
1864 create_thread(tagcache_thread, tagcache_stack,
1865 sizeof(tagcache_stack), tagcache_thread_name);
1866}
1867
1868
diff --git a/apps/tagcache.h b/apps/tagcache.h
new file mode 100644
index 0000000000..a405764644
--- /dev/null
+++ b/apps/tagcache.h
@@ -0,0 +1,78 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _TAGCACHE_H
20#define _TAGCACHE_H
21
22#include "id3.h"
23
24enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
25 tag_filename/*, tag_checksum*/ };
26#define TAG_COUNT 5
27
28#ifdef HAVE_DIRCACHE
29#define HAVE_TC_RAMCACHE 1
30#endif
31
32/* Allow a little drift to the filename ordering. */
33#define POS_HISTORY_COUNT 4
34
35/* How many entries we can create in one tag file (for sorting). */
36#define TAGFILE_MAX_ENTRIES 20000
37
38#define SEEK_LIST_SIZE 50
39#define TAGCACHE_MAX_FILTERS 3
40
41struct tagcache_search {
42 /* For internal use only. */
43 int fd;
44 long seek_list[SEEK_LIST_SIZE];
45 long filter_tag[TAGCACHE_MAX_FILTERS];
46 long filter_seek[TAGCACHE_MAX_FILTERS];
47 int filter_count;
48 int seek_list_count;
49 int seek_pos;
50 long position;
51 int entry_count;
52 bool valid;
53
54 /* Exported variables. */
55 bool ramsearch;
56 int type;
57 char *result;
58 int result_len;
59 long result_seek;
60};
61
62bool tagcache_fill_tags(struct mp3entry *id3, const char *filename);
63bool tagcache_search(struct tagcache_search *tcs, int tag);
64bool tagcache_search_add_filter(struct tagcache_search *tcs,
65 int tag, int seek);
66bool tagcache_get_next(struct tagcache_search *tcs);
67void tagcache_search_finish(struct tagcache_search *tcs);
68
69int tagcache_get_progress(void);
70#ifdef HAVE_TC_RAMCACHE
71bool tagcache_is_ramcache(void);
72#endif
73void tagcache_init(void);
74void tagcache_start_scan(void);
75void tagcache_stop_scan(void);
76bool tagcache_force_update(void);
77
78#endif
diff --git a/apps/tagtree.c b/apps/tagtree.c
new file mode 100644
index 0000000000..cb4135c204
--- /dev/null
+++ b/apps/tagtree.c
@@ -0,0 +1,536 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20/**
21 * Basic structure on this file was copied from dbtree.c and modified to
22 * support the tag cache interface.
23 */
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include "system.h"
28#include "kernel.h"
29#include "splash.h"
30#include "icons.h"
31#include "tree.h"
32#include "settings.h"
33#include "tagcache.h"
34#include "tagtree.h"
35#include "lang.h"
36#include "logf.h"
37#include "playlist.h"
38#include "keyboard.h"
39#include "gui/list.h"
40
41#define CHUNKED_NEXT -2
42
43static int tagtree_play_folder(struct tree_context* c);
44static int tagtree_search(struct tree_context* c, char* string);
45
46static char searchstring[32];
47struct tagentry {
48 char *name;
49 int newtable;
50 int extraseek;
51};
52
53static struct tagcache_search tcs;
54
55int tagtree_load(struct tree_context* c)
56{
57 int i;
58 int namebufused = 0;
59 struct tagentry *dptr = (struct tagentry *)c->dircache;
60
61 int table = c->currtable;
62 int extra = c->currextra;
63 int extra2 = c->currextra2;
64
65 c->dentry_size = sizeof(struct tagentry);
66
67 if (!table)
68 {
69 c->dirfull = false;
70 table = root;
71 c->currtable = table;
72 }
73
74 if (c->dirfull)
75 table = chunked_next;
76
77 switch (table) {
78 case root: {
79 static const int tables[] = {allartists, allalbums, allgenres, allsongs,
80 search };
81 unsigned char* labels[] = { str(LANG_ID3DB_ARTISTS),
82 str(LANG_ID3DB_ALBUMS),
83 str(LANG_ID3DB_GENRES),
84 str(LANG_ID3DB_SONGS),
85 str(LANG_ID3DB_SEARCH)};
86
87 for (i = 0; i < 5; i++) {
88 dptr->name = &c->name_buffer[namebufused];
89 dptr->newtable = tables[i];
90 strcpy(dptr->name, (char *)labels[i]);
91 namebufused += strlen(dptr->name) + 1;
92 dptr++;
93 }
94 c->dirlength = c->filesindir = i;
95 return i;
96 }
97
98 case search: {
99 static const int tables[] = {searchartists,
100 searchalbums,
101 searchsongs};
102 unsigned char* labels[] = { str(LANG_ID3DB_SEARCH_ARTISTS),
103 str(LANG_ID3DB_SEARCH_ALBUMS),
104 str(LANG_ID3DB_SEARCH_SONGS)};
105
106 for (i = 0; i < 3; i++) {
107 dptr->name = &c->name_buffer[namebufused];
108 dptr->newtable = tables[i];
109 strcpy(dptr->name, (char *)labels[i]);
110 namebufused += strlen(dptr->name) + 1;
111 dptr++;
112 }
113 c->dirlength = c->filesindir = i;
114 return i;
115 }
116
117 case searchartists:
118 case searchalbums:
119 case searchsongs:
120 i = tagtree_search(c, searchstring);
121 c->dirlength = c->filesindir = i;
122 if (c->dirfull) {
123 gui_syncsplash(HZ, true, (unsigned char *)"%s %s",
124 str(LANG_SHOWDIR_ERROR_BUFFER),
125 str(LANG_SHOWDIR_ERROR_FULL));
126 c->dirfull = false;
127 }
128 else
129 gui_syncsplash(HZ, true, str(LANG_ID3DB_MATCHES), i);
130 return i;
131
132 case allsongs:
133 logf("songs..");
134 tagcache_search(&tcs, tag_title);
135 break;
136
137 case allgenres:
138 logf("genres..");
139 tagcache_search(&tcs, tag_genre);
140 break;
141
142 case allalbums:
143 logf("albums..");
144 tagcache_search(&tcs, tag_album);
145 break;
146
147 case allartists:
148 logf("artists..");
149 tagcache_search(&tcs, tag_artist);
150 break;
151
152 case artist4genres:
153 logf("artist4genres..");
154 tagcache_search(&tcs, tag_artist);
155 tagcache_search_add_filter(&tcs, tag_genre, extra);
156 break;
157
158 case albums4artist:
159 logf("albums4artist..");
160 tagcache_search(&tcs, tag_album);
161 tagcache_search_add_filter(&tcs, tag_artist, extra);
162 break;
163
164 case songs4album:
165 logf("songs4album..");
166 tagcache_search(&tcs, tag_title);
167 tagcache_search_add_filter(&tcs, tag_album, extra);
168 if (extra2 > 0)
169 tagcache_search_add_filter(&tcs, tag_artist, extra2);
170 break;
171
172 case songs4artist:
173 logf("songs4artist..");
174 tagcache_search(&tcs, tag_title);
175 tagcache_search_add_filter(&tcs, tag_artist, extra);
176 break;
177
178 case chunked_next:
179 logf("chunked next...");
180 break;
181
182 default:
183 logf("Unsupported table %d\n", table);
184 return -1;
185 }
186
187 i = 0;
188 namebufused = 0;
189 c->dirfull = false;
190 while (tagcache_get_next(&tcs))
191 {
192 dptr->newtable = tcs.result_seek;
193 if (!tcs.ramsearch)
194 {
195 dptr->name = &c->name_buffer[namebufused];
196 namebufused += tcs.result_len;
197 if (namebufused > c->name_buffer_size)
198 {
199 logf("buffer full, 1 entry missed.");
200 c->dirfull = true;
201 break ;
202 }
203 strcpy(dptr->name, tcs.result);
204 }
205 else
206 dptr->name = tcs.result;
207 dptr++;
208 i++;
209
210 /**
211 * Estimate when we are running out of space so we can stop
212 * and enabled chunked browsing without missing entries.
213 */
214 if (i >= global_settings.max_files_in_dir - 1
215 || namebufused + 200 > c->name_buffer_size)
216 {
217 c->dirfull = true;
218 break ;
219 }
220
221 }
222
223 if (c->dirfull)
224 {
225 dptr->name = "===>";
226 dptr->newtable = chunked_next;
227 dptr++;
228 i++;
229 }
230 else
231 tagcache_search_finish(&tcs);
232
233 c->dirlength = c->filesindir = i;
234
235 return i;
236}
237
238static int tagtree_search(struct tree_context* c, char* string)
239{
240 struct tagentry *dptr = (struct tagentry *)c->dircache;
241 int hits = 0;
242 int namebufused = 0;
243
244 switch (c->currtable) {
245 case searchartists:
246 tagcache_search(&tcs, tag_artist);
247 break;
248
249 case searchalbums:
250 tagcache_search(&tcs, tag_album);
251 break;
252
253 case searchsongs:
254 tagcache_search(&tcs, tag_title);
255 break;
256
257 default:
258 logf("Invalid table %d\n", c->currtable);
259 return 0;
260 }
261
262 while (tagcache_get_next(&tcs))
263 {
264 if (!strcasestr(tcs.result, string))
265 continue ;
266
267 if (!tcs.ramsearch)
268 {
269 dptr->name = &c->name_buffer[namebufused];
270 namebufused += tcs.result_len;
271 strcpy(dptr->name, tcs.result);
272 }
273 else
274 dptr->name = tcs.result;
275
276 dptr->newtable = tcs.result_seek;
277 dptr++;
278 hits++;
279 }
280
281 tagcache_search_finish(&tcs);
282
283 return hits;
284}
285
286int tagtree_enter(struct tree_context* c)
287{
288 int rc = 0;
289 struct tagentry *dptr = (struct tagentry *)c->dircache;
290 int newextra;
291
292 dptr += c->selected_item;
293
294 if (dptr->newtable == chunked_next)
295 {
296 c->selected_item=0;
297 gui_synclist_select_item(&tree_lists, c->selected_item);
298 return 0;
299 }
300
301 c->dirfull = false;
302 newextra = dptr->newtable;
303
304 if (c->dirlevel >= MAX_DIR_LEVELS)
305 return 0;
306
307 c->selected_item_history[c->dirlevel]=c->selected_item;
308 c->table_history[c->dirlevel] = c->currtable;
309 c->extra_history[c->dirlevel] = c->currextra;
310 c->pos_history[c->dirlevel] = c->firstpos;
311 c->dirlevel++;
312
313 switch (c->currtable) {
314 case root:
315 c->currtable = newextra;
316 c->currextra = newextra;
317 break;
318
319 case allartists:
320 case searchartists:
321 c->currtable = albums4artist;
322 c->currextra = newextra;
323 break;
324
325 case allgenres:
326 c->currtable = artist4genres;
327 c->currextra = newextra;
328 break;
329
330 case artist4genres:
331 c->currtable = albums4artist;
332 c->currextra = newextra;
333 break;
334
335 case allalbums:
336 c->currtable = songs4album;
337 c->currextra = newextra;
338 c->currextra2 = -1;
339 break;
340 case albums4artist:
341 case searchalbums:
342 c->currtable = songs4album;
343 c->currextra2 = c->currextra;
344 c->currextra = newextra;
345 break;
346
347 case allsongs:
348 case songs4album:
349 case songs4artist:
350 case searchsongs:
351 c->dirlevel--;
352 if (tagtree_play_folder(c) >= 0)
353 rc = 2;
354 break;
355
356 case search:
357 rc = kbd_input(searchstring, sizeof(searchstring));
358 if (rc == -1 || !searchstring[0])
359 c->dirlevel--;
360 else
361 c->currtable = newextra;
362 break;
363
364 default:
365 c->dirlevel--;
366 break;
367 }
368 c->selected_item=0;
369 gui_synclist_select_item(&tree_lists, c->selected_item);
370
371 return rc;
372}
373
374void tagtree_exit(struct tree_context* c)
375{
376 c->dirfull = false;
377 c->dirlevel--;
378 c->selected_item=c->selected_item_history[c->dirlevel];
379 gui_synclist_select_item(&tree_lists, c->selected_item);
380 c->currtable = c->table_history[c->dirlevel];
381 c->currextra = c->extra_history[c->dirlevel];
382 c->firstpos = c->pos_history[c->dirlevel];
383
384 /* Just to be sure when chunked browsing is used. */
385 tagcache_search_finish(&tcs);
386}
387
388int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
389{
390 struct tagentry *entry = (struct tagentry *)c->dircache;
391
392 entry += c->selected_item;
393
394 tagcache_search(&tcs, tag_filename);
395 tagcache_search_add_filter(&tcs, tag_title, entry->newtable);
396
397 if (!tagcache_get_next(&tcs))
398 {
399 tagcache_search_finish(&tcs);
400 return -1;
401 }
402
403 strncpy(buf, tcs.result, buflen-1);
404 tagcache_search_finish(&tcs);
405
406 return 0;
407}
408
409#if 0
410bool tagtree_rename_tag(struct tree_context *c, const char *newtext)
411{
412 struct tagentry *dptr = (struct tagentry *)c->dircache;
413 int extra, extra2;
414 int type;
415
416 dptr += c->selected_item;
417 extra = dptr->newtable;
418 extra2 = dptr->extraseek;
419
420 switch (c->currtable) {
421 case allgenres:
422 tagcache_search(&tcs, tag_title);
423 tagcache_search_add_filter(&tcs, tag_genre, extra);
424 type = tag_genre;
425 break;
426
427 case allalbums:
428 tagcache_search(&tcs, tag_title);
429 tagcache_search_add_filter(&tcs, tag_album, extra);
430 type = tag_album;
431 break;
432
433 case allartists:
434 tagcache_search(&tcs, tag_title);
435 tagcache_search_add_filter(&tcs, tag_artist, extra);
436 type = tag_artist;
437 break;
438
439 case artist4genres:
440 tagcache_search(&tcs, tag_title);
441 tagcache_search_add_filter(&tcs, tag_genre, extra);
442 type = tag_artist;
443 break;
444
445 case albums4artist:
446 tagcache_search(&tcs, tag_title);
447 tagcache_search_add_filter(&tcs, tag_album, extra);
448 tagcache_search_add_filter(&tcs, tag_artist, extra2);
449 type = tag_album;
450 break;
451
452 default:
453 logf("wrong table");
454 return false;
455 }
456
457 while (tagcache_get_next(&tcs))
458 {
459 // tagcache_modify(&tcs, type, newtext);
460 }
461
462 tagcache_search_finish(&tcs);
463 return true;
464}
465#endif
466
467static int tagtree_play_folder(struct tree_context* c)
468{
469 struct tagentry *entry = (struct tagentry *)c->dircache;
470 int i;
471
472 if (playlist_create(NULL, NULL) < 0) {
473 logf("Failed creating playlist\n");
474 return -1;
475 }
476
477#ifdef HAVE_ADJUSTABLE_CPU_FREQ
478 cpu_boost(true);
479#endif
480
481 for (i=0; i < c->filesindir; i++) {
482 tagcache_search(&tcs, tag_filename);
483 tagcache_search_add_filter(&tcs, tag_title, entry[i].newtable);
484
485 if (!tagcache_get_next(&tcs))
486 {
487 tagcache_search_finish(&tcs);
488 continue ;
489 }
490 playlist_insert_track(NULL, tcs.result, PLAYLIST_INSERT, false);
491 tagcache_search_finish(&tcs);
492 }
493
494#ifdef HAVE_ADJUSTABLE_CPU_FREQ
495 cpu_boost(false);
496#endif
497
498 if (global_settings.playlist_shuffle)
499 c->selected_item = playlist_shuffle(current_tick, c->selected_item);
500 if (!global_settings.play_selected)
501 c->selected_item = 0;
502 gui_synclist_select_item(&tree_lists, c->selected_item);
503
504 playlist_start(c->selected_item,0);
505
506 return 0;
507}
508
509#ifdef HAVE_LCD_BITMAP
510const char* tagtree_get_icon(struct tree_context* c)
511#else
512int tagtree_get_icon(struct tree_context* c)
513#endif
514{
515 int icon;
516
517 switch (c->currtable)
518 {
519 case allsongs:
520 case songs4album:
521 case songs4artist:
522 case searchsongs:
523 icon = Icon_Audio;
524 break;
525
526 default:
527 icon = Icon_Folder;
528 break;
529 }
530
531#ifdef HAVE_LCD_BITMAP
532 return (char *)bitmap_icons_6x8[icon];
533#else
534 return icon;
535#endif
536}
diff --git a/apps/tagtree.h b/apps/tagtree.h
new file mode 100644
index 0000000000..7eafb7061d
--- /dev/null
+++ b/apps/tagtree.h
@@ -0,0 +1,41 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _TAGTREE_H
20#define _TAGTREE_H
21
22#include "tagcache.h"
23#include "tree.h"
24
25enum table { invalid, root, allsongs, allalbums, allartists, allgenres,
26 albums4artist, songs4album, songs4artist, artist4genres,
27 search, searchartists, searchalbums, searchsongs,
28 chunked_next };
29
30int tagtree_enter(struct tree_context* c);
31void tagtree_exit(struct tree_context* c);
32int tagtree_load(struct tree_context* c);
33#ifdef HAVE_LCD_BITMAP
34const char* tagtree_get_icon(struct tree_context* c);
35#else
36int tagtree_get_icon(struct tree_context* c);
37#endif
38int tagtree_get_filename(struct tree_context* c, char *buf, int buflen);
39
40#endif
41
diff --git a/apps/tree.c b/apps/tree.c
index b11d28f1f8..5efacd5993 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -57,10 +57,11 @@
57#include "filetypes.h" 57#include "filetypes.h"
58#include "misc.h" 58#include "misc.h"
59#include "filetree.h" 59#include "filetree.h"
60#include "dbtree.h" 60#include "tagtree.h"
61#include "recorder/recording.h" 61#include "recorder/recording.h"
62#include "rtc.h" 62#include "rtc.h"
63#include "dircache.h" 63#include "dircache.h"
64#include "tagcache.h"
64#include "yesno.h" 65#include "yesno.h"
65 66
66/* gui api */ 67/* gui api */
@@ -164,7 +165,8 @@ char * tree_get_filename(int selected_item, void * data, char *buffer)
164 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; 165 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
165 166
166 if (id3db) { 167 if (id3db) {
167 name = ((char**)local_tc->dircache)[selected_item * local_tc->dentry_size]; 168 char **buf = local_tc->dircache;
169 name = buf[selected_item * (local_tc->dentry_size/sizeof(int))];
168 } 170 }
169 else { 171 else {
170 struct entry* dc = local_tc->dircache; 172 struct entry* dc = local_tc->dircache;
@@ -189,7 +191,7 @@ void tree_get_fileicon(int selected_item, void * data, ICON * icon)
189 struct tree_context * local_tc=(struct tree_context *)data; 191 struct tree_context * local_tc=(struct tree_context *)data;
190 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; 192 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
191 if (id3db) { 193 if (id3db) {
192 *icon = (ICON)db_get_icon(&tc); 194 *icon = (ICON)tagtree_get_icon(&tc);
193 } 195 }
194 else { 196 else {
195 struct entry* dc = local_tc->dircache; 197 struct entry* dc = local_tc->dircache;
@@ -267,6 +269,7 @@ struct tree_context* tree_get_context(void)
267int tree_get_file_position(char * filename) 269int tree_get_file_position(char * filename)
268{ 270{
269 int i; 271 int i;
272
270 /* use lastfile to determine the selected item (default=0) */ 273 /* use lastfile to determine the selected item (default=0) */
271 for (i=0; i < tc.filesindir; i++) 274 for (i=0; i < tc.filesindir; i++)
272 { 275 {
@@ -292,7 +295,7 @@ static int update_dir(void)
292 tc.currextra != lastextra || 295 tc.currextra != lastextra ||
293 tc.firstpos != lastfirstpos) 296 tc.firstpos != lastfirstpos)
294 { 297 {
295 if (db_load(&tc) < 0) 298 if (tagtree_load(&tc) < 0)
296 return -1; 299 return -1;
297 300
298 lasttable = tc.currtable; 301 lasttable = tc.currtable;
@@ -494,7 +497,7 @@ static bool check_changed_id3mode(bool currmode)
494 currmode = global_settings.dirfilter == SHOW_ID3DB; 497 currmode = global_settings.dirfilter == SHOW_ID3DB;
495 if (currmode) { 498 if (currmode) {
496 curr_context=CONTEXT_ID3DB; 499 curr_context=CONTEXT_ID3DB;
497 db_load(&tc); 500 tagtree_load(&tc);
498 } 501 }
499 else 502 else
500 { 503 {
@@ -600,7 +603,7 @@ static bool dirbrowse(void)
600 if ( numentries == 0 ) 603 if ( numentries == 0 )
601 break; 604 break;
602 605
603 switch (id3db?db_enter(&tc):ft_enter(&tc)) 606 switch (id3db?tagtree_enter(&tc):ft_enter(&tc))
604 { 607 {
605 case 1: reload_dir = true; break; 608 case 1: reload_dir = true; break;
606 case 2: start_wps = true; break; 609 case 2: start_wps = true; break;
@@ -624,7 +627,7 @@ static bool dirbrowse(void)
624 break; 627 break;
625 628
626 if (id3db) 629 if (id3db)
627 db_exit(&tc); 630 tagtree_exit(&tc);
628 else 631 else
629 if (ft_exit(&tc) == 3) 632 if (ft_exit(&tc) == 3)
630 exit_func = true; 633 exit_func = true;
@@ -732,6 +735,7 @@ static bool dirbrowse(void)
732 restore = true; 735 restore = true;
733 736
734 id3db = check_changed_id3mode(id3db); 737 id3db = check_changed_id3mode(id3db);
738 reload_dir = true;
735 break; 739 break;
736 } 740 }
737 741
@@ -772,7 +776,7 @@ static bool dirbrowse(void)
772 case songs4artist: 776 case songs4artist:
773 case searchsongs: 777 case searchsongs:
774 attr=TREE_ATTR_MPA; 778 attr=TREE_ATTR_MPA;
775 db_get_filename(&tc, buf, sizeof(buf)); 779 tagtree_get_filename(&tc, buf, sizeof(buf));
776 break; 780 break;
777 } 781 }
778 } 782 }
@@ -906,6 +910,7 @@ static bool dirbrowse(void)
906 lastextra = -1; 910 lastextra = -1;
907 reload_root = false; 911 reload_root = false;
908 } 912 }
913
909 if (! reload_dir ) 914 if (! reload_dir )
910 { 915 {
911 gui_synclist_select_item(&tree_lists, 0); 916 gui_synclist_select_item(&tree_lists, 0);
@@ -935,6 +940,7 @@ static bool dirbrowse(void)
935 need_update = true; 940 need_update = true;
936 reload_dir = false; 941 reload_dir = false;
937 } 942 }
943
938 if(need_update) { 944 if(need_update) {
939 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists); 945 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists);
940 need_update=false; 946 need_update=false;
@@ -1177,8 +1183,6 @@ void tree_init(void)
1177 memset(&tc, 0, sizeof(tc)); 1183 memset(&tc, 0, sizeof(tc));
1178 tc.dirfilter = &global_settings.dirfilter; 1184 tc.dirfilter = &global_settings.dirfilter;
1179 1185
1180 tagdb_init();
1181
1182 tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files; 1186 tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files;
1183 tc.name_buffer = buffer_alloc(tc.name_buffer_size); 1187 tc.name_buffer = buffer_alloc(tc.name_buffer_size);
1184 1188
@@ -1331,9 +1335,9 @@ void ft_play_filename(char *dir, char *file)
1331/* These two functions are called by the USB and shutdown handlers */ 1335/* These two functions are called by the USB and shutdown handlers */
1332void tree_flush(void) 1336void tree_flush(void)
1333{ 1337{
1334 rundb_shutdown(); 1338 tagcache_stop_scan();
1335 tagdb_shutdown();
1336 playlist_shutdown(); 1339 playlist_shutdown();
1340
1337#ifdef HAVE_DIRCACHE 1341#ifdef HAVE_DIRCACHE
1338 if (global_settings.dircache) 1342 if (global_settings.dircache)
1339 { 1343 {
@@ -1351,8 +1355,6 @@ void tree_flush(void)
1351 1355
1352void tree_restore(void) 1356void tree_restore(void)
1353{ 1357{
1354 tagdb_init();
1355 rundb_init();
1356#ifdef HAVE_DIRCACHE 1358#ifdef HAVE_DIRCACHE
1357 if (global_settings.dircache) 1359 if (global_settings.dircache)
1358 { 1360 {
@@ -1376,5 +1378,6 @@ void tree_restore(void)
1376 gui_textarea_clear(&screens[i]); 1378 gui_textarea_clear(&screens[i]);
1377 } 1379 }
1378 } 1380 }
1381 tagcache_start_scan();
1379#endif 1382#endif
1380} 1383}
diff --git a/apps/tree.h b/apps/tree.h
index 9b4888c46a..fe903250d7 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -213,6 +213,7 @@ struct tree_context {
213 int extra_history[MAX_DIR_LEVELS]; /* db use */ 213 int extra_history[MAX_DIR_LEVELS]; /* db use */
214 int currtable; /* db use */ 214 int currtable; /* db use */
215 int currextra; /* db use */ 215 int currextra; /* db use */
216 int currextra2; /* db use */
216 /* A big buffer with plenty of entry structs, 217 /* A big buffer with plenty of entry structs,
217 * contains all files and dirs in the current 218 * contains all files and dirs in the current
218 * dir (with filters applied) */ 219 * dir (with filters applied) */