diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2006-03-26 11:33:42 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2006-03-26 11:33:42 +0000 |
commit | 7c4e0c8730d5b076d4db4206361bc38d5256a23f (patch) | |
tree | 43382ae25de9bfa0bbabdff7d51c32b651ad47b5 /apps | |
parent | 50d40ea43409745bc828e56af5e3879ea6b48cf1 (diff) | |
download | rockbox-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/SOURCES | 6 | ||||
-rw-r--r-- | apps/codecs.c | 6 | ||||
-rw-r--r-- | apps/codecs.h | 10 | ||||
-rw-r--r-- | apps/debug_menu.c | 39 | ||||
-rw-r--r-- | apps/lang/english.lang | 42 | ||||
-rw-r--r-- | apps/main.c | 27 | ||||
-rw-r--r-- | apps/metadata.c | 11 | ||||
-rw-r--r-- | apps/metadata.h | 1 | ||||
-rw-r--r-- | apps/onplay.c | 4 | ||||
-rw-r--r-- | apps/plugin.c | 10 | ||||
-rw-r--r-- | apps/plugin.h | 14 | ||||
-rw-r--r-- | apps/plugins/Makefile | 3 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 2 | ||||
-rw-r--r-- | apps/settings.c | 5 | ||||
-rw-r--r-- | apps/settings.h | 1 | ||||
-rw-r--r-- | apps/settings_menu.c | 22 | ||||
-rw-r--r-- | apps/tagcache.c | 1868 | ||||
-rw-r--r-- | apps/tagcache.h | 78 | ||||
-rw-r--r-- | apps/tagtree.c | 536 | ||||
-rw-r--r-- | apps/tagtree.h | 41 | ||||
-rw-r--r-- | apps/tree.c | 31 | ||||
-rw-r--r-- | apps/tree.h | 1 |
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 | |||
25 | talk.c | 25 | talk.c |
26 | #endif | 26 | #endif |
27 | tree.c | 27 | tree.c |
28 | dbtree.c | 28 | tagtree.c |
29 | database.c | ||
30 | filetree.c | 29 | filetree.c |
31 | 30 | ||
32 | screen_access.c | 31 | screen_access.c |
@@ -69,7 +68,6 @@ recorder/recording.c | |||
69 | #if CONFIG_CODEC == SWCODEC | 68 | #if CONFIG_CODEC == SWCODEC |
70 | pcmbuf.c | 69 | pcmbuf.c |
71 | playback.c | 70 | playback.c |
72 | metadata.c | ||
73 | codecs.c | 71 | codecs.c |
74 | dsp.c | 72 | dsp.c |
75 | eq.c | 73 | eq.c |
@@ -81,3 +79,5 @@ eq_arm.S | |||
81 | #endif | 79 | #endif |
82 | eq_menu.c | 80 | eq_menu.c |
83 | #endif | 81 | #endif |
82 | metadata.c | ||
83 | tagcache.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 */ |
96 | enum codec_status { | 96 | enum 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 | ||
1880 | static 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 |
1875 | bool dbg_save_roms(void) | 1913 | bool 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 | |||
3838 | eng: "First keypress enables backlight only" | 3838 | eng: "First keypress enables backlight only" |
3839 | voice: "First keypress enables backlight only" | 3839 | voice: "First keypress enables backlight only" |
3840 | new: | 3840 | new: |
3841 | |||
3842 | id: LANG_ID3DB_GENRES | ||
3843 | desc: in tag cache | ||
3844 | eng: "Genres" | ||
3845 | voice: "Genres" | ||
3846 | new: | ||
3847 | |||
3848 | id: LANG_TAGCACHE | ||
3849 | desc: in tag cache settings | ||
3850 | eng: "Tag cache" | ||
3851 | voice: "Tag cache" | ||
3852 | new: | ||
3853 | |||
3854 | id: LANG_TAGCACHE_INIT | ||
3855 | desc: while initializing tagcache on boot | ||
3856 | eng: "Committing tagcache" | ||
3857 | voice: | ||
3858 | new: | ||
3859 | |||
3860 | id: LANG_TAGCACHE_DISK | ||
3861 | desc: in tag cache settings | ||
3862 | eng: "Keep on disk" | ||
3863 | voice: "Keep on disk" | ||
3864 | new: | ||
3865 | |||
3866 | id: LANG_TAGCACHE_RAM | ||
3867 | desc: in tag cache settings | ||
3868 | eng: "Load to ram" | ||
3869 | voice: "Load to ram" | ||
3870 | new: | ||
3871 | |||
3872 | id: LANG_TAGCACHE_FORCE_UPDATE | ||
3873 | desc: in tag cache settings | ||
3874 | eng: "Force tag cache update" | ||
3875 | voice: "Force tag cache update" | ||
3876 | new: | ||
3877 | |||
3878 | id: LANG_TAGCACHE_FORCE_UPDATE_SPLASH | ||
3879 | desc: in tag cache settings | ||
3880 | eng: "Updating in background" | ||
3881 | voice: "Updating in background" | ||
3882 | new: | ||
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 | ||
142 | void 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 | ||
143 | void init(void) | 167 | void 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 | ||
85 | static const unsigned short a52_bitrates[] = | 88 | static 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 | ||
1250 | static bool get_aiff_metadata(int fd, struct mp3entry* id3) | 1254 | static 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. */ |
1321 | static unsigned int probe_file_format(const char *filename) | 1325 | unsigned 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) | |||
1349 | bool get_metadata(struct track_info* track, int fd, const char* trackname, | 1353 | bool 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 | ||
25 | unsigned int probe_file_format(const char *filename); | ||
25 | bool get_metadata(struct track_info* track, int fd, const char* trackname, | 26 | bool 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 */ |
110 | enum plugin_status { | 110 | enum 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 |
63 | SUBDIRS += searchengine databox | 63 | # SUBDIRS += searchengine databox |
64 | SUBDIRS += databox | ||
64 | 65 | ||
65 | #for any recorder, iRiver or iPod model | 66 | #for any recorder, iRiver or iPod model |
66 | ifneq (,$(strip $(foreach tgt,RECORDER IRIVER IPOD_COLOR IPOD_VIDEO GIGABEAT,$(findstring $(tgt),$(TARGET))))) | 67 | ifneq (,$(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 |
16 | mosaique.c | 16 | mosaique.c |
17 | rockbox_flash.c | 17 | rockbox_flash.c |
18 | search.c | 18 | /* search.c */ |
19 | snow.c | 19 | snow.c |
20 | sort.c | 20 | sort.c |
21 | stats.c | 21 | stats.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) | |||
1259 | static bool runtimedb(void) | 1260 | static 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 |
1483 | static bool dircache(void) | 1485 | static bool dircache(void) |
1484 | { | 1486 | { |
@@ -1497,6 +1499,16 @@ static bool dircache(void) | |||
1497 | return result; | 1499 | return result; |
1498 | } | 1500 | } |
1499 | 1501 | ||
1502 | static 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 | ||
1502 | static bool playback_settings_menu(void) | 1514 | static 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. */ | ||
36 | extern char *audiobuf, *audiobufend; | ||
37 | |||
38 | /* Tag Cache thread. */ | ||
39 | static struct event_queue tagcache_queue; | ||
40 | static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)]; | ||
41 | static const char tagcache_thread_name[] = "tagcache"; | ||
42 | |||
43 | /* Previous path when scanning directory tree recursively. */ | ||
44 | static char curpath[MAX_PATH*2]; | ||
45 | static long curpath_size = sizeof(curpath); | ||
46 | |||
47 | /* Used when removing duplicates. */ | ||
48 | static char *tempbuf; /* Allocated when needed. */ | ||
49 | static long tempbufidx; /* Current location in buffer. */ | ||
50 | static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */ | ||
51 | static long tempbuf_left; /* Buffer space left. */ | ||
52 | static long tempbuf_pos; | ||
53 | |||
54 | /* Tags we want to be unique (loaded to the tempbuf). */ | ||
55 | static 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. */ | ||
71 | struct tagfile_entry { | ||
72 | short tag_length; | ||
73 | char tag_data[0]; | ||
74 | }; | ||
75 | |||
76 | /* Fixed-size tag entry in master db index. */ | ||
77 | struct index_entry { | ||
78 | long tag_seek[TAG_COUNT]; | ||
79 | }; | ||
80 | |||
81 | /* Header is the same in every file. */ | ||
82 | struct 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. */ | ||
90 | struct 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 | |||
97 | static struct ramcache_header *hdr; | ||
98 | static bool ramcache = false; | ||
99 | static long tagcache_size = 0; | ||
100 | #endif | ||
101 | |||
102 | /** | ||
103 | * Full tag entries stored in a temporary file waiting | ||
104 | * for commit to the cache. */ | ||
105 | struct temp_file_entry { | ||
106 | long tag_offset[TAG_COUNT]; | ||
107 | short tag_length[TAG_COUNT]; | ||
108 | |||
109 | long data_length; | ||
110 | }; | ||
111 | |||
112 | struct tempbuf_id { | ||
113 | int id; | ||
114 | struct tempbuf_id *next; | ||
115 | }; | ||
116 | |||
117 | struct tempbuf_searchidx { | ||
118 | struct tempbuf_id *id; | ||
119 | char *str; | ||
120 | int seek; | ||
121 | }; | ||
122 | |||
123 | |||
124 | /* Used when building the temporary file. */ | ||
125 | static int cachefd = -1, filenametag_fd; | ||
126 | static int total_entry_count = 0; | ||
127 | static int data_size = 0; | ||
128 | static int processed_dir_count; | ||
129 | |||
130 | #ifdef HAVE_TC_RAMCACHE | ||
131 | static 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 | |||
183 | static 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 | |||
309 | static 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 | |||
417 | bool 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 | |||
464 | bool 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 | |||
477 | bool 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 | ||
577 | void 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 | |||
598 | void 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 | ||
610 | struct 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 | |||
615 | bool 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 | |||
633 | static 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 | |||
641 | inline 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 | ||
650 | static void add_tagcache(const char *path, const struct dircache_entry *dc) | ||
651 | #else | ||
652 | static 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 | |||
728 | static 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 | |||
741 | static 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 | |||
776 | static 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 | |||
808 | static 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 | |||
823 | static 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 | |||
854 | static 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 | |||
876 | static 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 | |||
887 | static 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 | |||
900 | static 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 | |||
1278 | static 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 | |||
1368 | static 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 | |||
1376 | static 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 | ||
1387 | static 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 | |||
1429 | static 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 | |||
1592 | static 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 | |||
1646 | static 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 | ||
1730 | static 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 | |||
1750 | static 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 | |||
1801 | int 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 | |||
1823 | void tagcache_start_scan(void) | ||
1824 | { | ||
1825 | queue_post(&tagcache_queue, Q_START_SCAN, 0); | ||
1826 | } | ||
1827 | |||
1828 | bool 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 | |||
1836 | void tagcache_stop_scan(void) | ||
1837 | { | ||
1838 | queue_post(&tagcache_queue, Q_STOP_SCAN, 0); | ||
1839 | } | ||
1840 | |||
1841 | #ifdef HAVE_TC_RAMCACHE | ||
1842 | bool tagcache_is_ramcache(void) | ||
1843 | { | ||
1844 | return ramcache; | ||
1845 | } | ||
1846 | #endif | ||
1847 | |||
1848 | void 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 | |||
24 | enum 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 | |||
41 | struct 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 | |||
62 | bool tagcache_fill_tags(struct mp3entry *id3, const char *filename); | ||
63 | bool tagcache_search(struct tagcache_search *tcs, int tag); | ||
64 | bool tagcache_search_add_filter(struct tagcache_search *tcs, | ||
65 | int tag, int seek); | ||
66 | bool tagcache_get_next(struct tagcache_search *tcs); | ||
67 | void tagcache_search_finish(struct tagcache_search *tcs); | ||
68 | |||
69 | int tagcache_get_progress(void); | ||
70 | #ifdef HAVE_TC_RAMCACHE | ||
71 | bool tagcache_is_ramcache(void); | ||
72 | #endif | ||
73 | void tagcache_init(void); | ||
74 | void tagcache_start_scan(void); | ||
75 | void tagcache_stop_scan(void); | ||
76 | bool 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 | |||
43 | static int tagtree_play_folder(struct tree_context* c); | ||
44 | static int tagtree_search(struct tree_context* c, char* string); | ||
45 | |||
46 | static char searchstring[32]; | ||
47 | struct tagentry { | ||
48 | char *name; | ||
49 | int newtable; | ||
50 | int extraseek; | ||
51 | }; | ||
52 | |||
53 | static struct tagcache_search tcs; | ||
54 | |||
55 | int 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 | |||
238 | static 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 | |||
286 | int 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 | |||
374 | void 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 | |||
388 | int 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 | ||
410 | bool 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 | |||
467 | static 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 | ||
510 | const char* tagtree_get_icon(struct tree_context* c) | ||
511 | #else | ||
512 | int 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 | |||
25 | enum table { invalid, root, allsongs, allalbums, allartists, allgenres, | ||
26 | albums4artist, songs4album, songs4artist, artist4genres, | ||
27 | search, searchartists, searchalbums, searchsongs, | ||
28 | chunked_next }; | ||
29 | |||
30 | int tagtree_enter(struct tree_context* c); | ||
31 | void tagtree_exit(struct tree_context* c); | ||
32 | int tagtree_load(struct tree_context* c); | ||
33 | #ifdef HAVE_LCD_BITMAP | ||
34 | const char* tagtree_get_icon(struct tree_context* c); | ||
35 | #else | ||
36 | int tagtree_get_icon(struct tree_context* c); | ||
37 | #endif | ||
38 | int 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) | |||
267 | int tree_get_file_position(char * filename) | 269 | int 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 */ |
1332 | void tree_flush(void) | 1336 | void 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 | ||
1352 | void tree_restore(void) | 1356 | void 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) */ |