diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2006-07-15 17:36:25 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2006-07-15 17:36:25 +0000 |
commit | 45dfe2a36f03d1ada7036dedb50fb98d7c5421b2 (patch) | |
tree | 866a57b677dce40ef5b772e1514a9ec6a35f60ea /apps | |
parent | e60cb43a982e03b062bd42dbe260abca299fe14d (diff) | |
download | rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.tar.gz rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.zip |
Initial runtimedb support for tagcache. Only for developers,
statistical data will be lost in future until changelogs has been
implemented.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10217 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/playback.c | 20 | ||||
-rw-r--r-- | apps/settings_menu.c | 11 | ||||
-rw-r--r-- | apps/tagcache.c | 264 | ||||
-rw-r--r-- | apps/tagcache.h | 12 | ||||
-rw-r--r-- | apps/tagnavi.config | 19 | ||||
-rw-r--r-- | apps/tagtree.c | 236 |
6 files changed, 420 insertions, 142 deletions
diff --git a/apps/playback.c b/apps/playback.c index 4fc0c33921..8ffd5889c5 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -2189,11 +2189,13 @@ void audio_thread(void) | |||
2189 | 2189 | ||
2190 | case Q_AUDIO_PLAY: | 2190 | case Q_AUDIO_PLAY: |
2191 | logf("starting..."); | 2191 | logf("starting..."); |
2192 | audio_clear_track_entries(true, false); | ||
2192 | audio_play_start((size_t)ev.data); | 2193 | audio_play_start((size_t)ev.data); |
2193 | break ; | 2194 | break ; |
2194 | 2195 | ||
2195 | case Q_AUDIO_STOP: | 2196 | case Q_AUDIO_STOP: |
2196 | logf("audio_stop"); | 2197 | logf("audio_stop"); |
2198 | audio_clear_track_entries(true, false); | ||
2197 | audio_stop_playback(); | 2199 | audio_stop_playback(); |
2198 | break ; | 2200 | break ; |
2199 | 2201 | ||
@@ -2816,22 +2818,6 @@ void mpeg_id3_options(bool _v1first) | |||
2816 | } | 2818 | } |
2817 | 2819 | ||
2818 | #ifdef ROCKBOX_HAS_LOGF | 2820 | #ifdef ROCKBOX_HAS_LOGF |
2819 | void test_buffer_event(struct mp3entry *id3, bool last_track) | ||
2820 | { | ||
2821 | (void)id3; | ||
2822 | (void)last_track; | ||
2823 | |||
2824 | logf("be:%d%s", last_track, id3->path); | ||
2825 | } | ||
2826 | |||
2827 | void test_unbuffer_event(struct mp3entry *id3, bool last_track) | ||
2828 | { | ||
2829 | (void)id3; | ||
2830 | (void)last_track; | ||
2831 | |||
2832 | logf("ube:%d%s", last_track, id3->path); | ||
2833 | } | ||
2834 | |||
2835 | void test_track_changed_event(struct mp3entry *id3) | 2821 | void test_track_changed_event(struct mp3entry *id3) |
2836 | { | 2822 | { |
2837 | (void)id3; | 2823 | (void)id3; |
@@ -2854,8 +2840,6 @@ static void playback_init(void) | |||
2854 | #endif | 2840 | #endif |
2855 | 2841 | ||
2856 | #ifdef ROCKBOX_HAS_LOGF | 2842 | #ifdef ROCKBOX_HAS_LOGF |
2857 | audio_set_track_buffer_event(test_buffer_event); | ||
2858 | audio_set_track_unbuffer_event(test_unbuffer_event); | ||
2859 | audio_set_track_changed_event(test_track_changed_event); | 2843 | audio_set_track_changed_event(test_track_changed_event); |
2860 | #endif | 2844 | #endif |
2861 | 2845 | ||
diff --git a/apps/settings_menu.c b/apps/settings_menu.c index f3cae84743..f00612a180 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c | |||
@@ -1545,6 +1545,16 @@ static bool tagcache_autoupdate(void) | |||
1545 | return rc; | 1545 | return rc; |
1546 | } | 1546 | } |
1547 | 1547 | ||
1548 | static bool tagcache_runtimedb(void) | ||
1549 | { | ||
1550 | bool rc = set_bool_options(str(LANG_RUNTIMEDB_ACTIVE), | ||
1551 | &global_settings.runtimedb, | ||
1552 | STR(LANG_ON), | ||
1553 | STR(LANG_OFF), | ||
1554 | NULL); | ||
1555 | return rc; | ||
1556 | } | ||
1557 | |||
1548 | static bool tagcache_settings_menu(void) | 1558 | static bool tagcache_settings_menu(void) |
1549 | { | 1559 | { |
1550 | int m; | 1560 | int m; |
@@ -1557,6 +1567,7 @@ static bool tagcache_settings_menu(void) | |||
1557 | { ID2P(LANG_TAGCACHE_AUTOUPDATE), tagcache_autoupdate }, | 1567 | { ID2P(LANG_TAGCACHE_AUTOUPDATE), tagcache_autoupdate }, |
1558 | { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_rebuild }, | 1568 | { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_rebuild }, |
1559 | { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, | 1569 | { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, |
1570 | { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb }, | ||
1560 | }; | 1571 | }; |
1561 | 1572 | ||
1562 | m=menu_init( items, sizeof(items) / sizeof(*items), NULL, | 1573 | m=menu_init( items, sizeof(items) / sizeof(*items), NULL, |
diff --git a/apps/tagcache.c b/apps/tagcache.c index affdc46a66..a5675850bf 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c | |||
@@ -58,7 +58,8 @@ static const int sorted_tags[] = { tag_artist, tag_album, tag_genre, tag_compose | |||
58 | static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer }; | 58 | static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer }; |
59 | 59 | ||
60 | /* Numeric tags (we can use these tags with conditional clauses). */ | 60 | /* Numeric tags (we can use these tags with conditional clauses). */ |
61 | static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate }; | 61 | static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate, |
62 | tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore }; | ||
62 | 63 | ||
63 | /* Status information of the tagcache. */ | 64 | /* Status information of the tagcache. */ |
64 | static struct tagcache_stat stat; | 65 | static struct tagcache_stat stat; |
@@ -179,8 +180,8 @@ bool tagcache_is_sorted_tag(int type) | |||
179 | } | 180 | } |
180 | 181 | ||
181 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) | 182 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
182 | static struct index_entry *find_entry_ram(const char *filename, | 183 | static long find_entry_ram(const char *filename, |
183 | const struct dircache_entry *dc) | 184 | const struct dircache_entry *dc) |
184 | { | 185 | { |
185 | static long last_pos = 0; | 186 | static long last_pos = 0; |
186 | int counter = 0; | 187 | int counter = 0; |
@@ -188,7 +189,7 @@ static struct index_entry *find_entry_ram(const char *filename, | |||
188 | 189 | ||
189 | /* Check if we tagcache is loaded into ram. */ | 190 | /* Check if we tagcache is loaded into ram. */ |
190 | if (!stat.ramcache) | 191 | if (!stat.ramcache) |
191 | return NULL; | 192 | return -1; |
192 | 193 | ||
193 | if (dc == NULL) | 194 | if (dc == NULL) |
194 | dc = dircache_get_entry_ptr(filename); | 195 | dc = dircache_get_entry_ptr(filename); |
@@ -196,7 +197,7 @@ static struct index_entry *find_entry_ram(const char *filename, | |||
196 | if (dc == NULL) | 197 | if (dc == NULL) |
197 | { | 198 | { |
198 | logf("tagcache: file not found."); | 199 | logf("tagcache: file not found."); |
199 | return NULL; | 200 | return -1; |
200 | } | 201 | } |
201 | 202 | ||
202 | try_again: | 203 | try_again: |
@@ -211,7 +212,7 @@ static struct index_entry *find_entry_ram(const char *filename, | |||
211 | if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) | 212 | if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) |
212 | { | 213 | { |
213 | last_pos = MAX(0, i - 3); | 214 | last_pos = MAX(0, i - 3); |
214 | return &hdr->indices[i]; | 215 | return i; |
215 | } | 216 | } |
216 | 217 | ||
217 | if (++counter == 100) | 218 | if (++counter == 100) |
@@ -227,28 +228,28 @@ static struct index_entry *find_entry_ram(const char *filename, | |||
227 | goto try_again; | 228 | goto try_again; |
228 | } | 229 | } |
229 | 230 | ||
230 | return NULL; | 231 | return -1; |
231 | } | 232 | } |
232 | #endif | 233 | #endif |
233 | 234 | ||
234 | static struct index_entry *find_entry_disk(const char *filename, bool retrieve) | 235 | static long find_entry_disk(const char *filename) |
235 | { | 236 | { |
236 | static struct index_entry idx; | ||
237 | static long last_pos = -1; | 237 | static long last_pos = -1; |
238 | long pos_history[POS_HISTORY_COUNT]; | 238 | long pos_history[POS_HISTORY_COUNT]; |
239 | long pos_history_idx = 0; | 239 | long pos_history_idx = 0; |
240 | struct tagcache_header tch; | ||
241 | bool found = false; | 240 | bool found = false; |
242 | struct tagfile_entry tfe; | 241 | struct tagfile_entry tfe; |
243 | int masterfd, fd = filenametag_fd; | 242 | int fd; |
244 | char buf[MAX_PATH]; | 243 | char buf[MAX_PATH]; |
245 | int i; | 244 | int i; |
246 | int pos = -1; | 245 | int pos = -1; |
247 | 246 | ||
247 | fd = filenametag_fd; | ||
248 | if (fd < 0) | 248 | if (fd < 0) |
249 | { | 249 | { |
250 | last_pos = -1; | 250 | last_pos = -1; |
251 | return NULL; | 251 | if ( (fd = open(TAGCACHE_FILE_MASTER, O_RDONLY)) < 0) |
252 | return -1; | ||
252 | } | 253 | } |
253 | 254 | ||
254 | check_again: | 255 | check_again: |
@@ -276,7 +277,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) | |||
276 | logf("too long tag #1"); | 277 | logf("too long tag #1"); |
277 | close(fd); | 278 | close(fd); |
278 | last_pos = -1; | 279 | last_pos = -1; |
279 | return NULL; | 280 | return -2; |
280 | } | 281 | } |
281 | 282 | ||
282 | if (read(fd, buf, tfe.tag_length) != tfe.tag_length) | 283 | if (read(fd, buf, tfe.tag_length) != tfe.tag_length) |
@@ -284,7 +285,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) | |||
284 | logf("read error #2"); | 285 | logf("read error #2"); |
285 | close(fd); | 286 | close(fd); |
286 | last_pos = -1; | 287 | last_pos = -1; |
287 | return NULL; | 288 | return -3; |
288 | } | 289 | } |
289 | 290 | ||
290 | if (!strcasecmp(filename, buf)) | 291 | if (!strcasecmp(filename, buf)) |
@@ -307,90 +308,106 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve) | |||
307 | logf("seek again"); | 308 | logf("seek again"); |
308 | goto check_again; | 309 | goto check_again; |
309 | } | 310 | } |
310 | //close(fd); | 311 | |
311 | return NULL; | 312 | if (fd != filenametag_fd) |
313 | close(fd); | ||
314 | return -4; | ||
312 | } | 315 | } |
313 | 316 | ||
314 | if (!retrieve) | 317 | if (fd != filenametag_fd) |
315 | { | 318 | close(fd); |
316 | /* Just return a valid pointer without a valid entry. */ | 319 | |
317 | return &idx; | 320 | return tfe.idx_id; |
318 | } | 321 | } |
322 | |||
323 | bool tagcache_find_index(struct tagcache_search *tcs, const char *filename) | ||
324 | { | ||
325 | long idx_id = -1; | ||
319 | 326 | ||
320 | /* Found. Now read the index_entry (if requested). */ | 327 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
321 | masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY); | 328 | if (stat.ramcache && dircache_is_enabled()) |
322 | if (masterfd < 0) | 329 | idx_id = find_entry_ram(filename, NULL); |
323 | { | 330 | #endif |
324 | logf("open fail"); | ||
325 | return NULL; | ||
326 | } | ||
327 | 331 | ||
328 | if (read(fd, &tch, sizeof(struct tagcache_header)) != | 332 | if (idx_id < 0) |
329 | sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) | 333 | idx_id = find_entry_disk(filename); |
330 | { | ||
331 | logf("header error"); | ||
332 | return NULL; | ||
333 | } | ||
334 | 334 | ||
335 | for (i = 0; i < tch.entry_count; i++) | 335 | if (idx_id < 0) |
336 | { | 336 | return false; |
337 | if (read(masterfd, &idx, sizeof(struct index_entry)) != | ||
338 | sizeof(struct index_entry)) | ||
339 | { | ||
340 | logf("read error #3"); | ||
341 | close(fd); | ||
342 | return NULL; | ||
343 | } | ||
344 | |||
345 | if (idx.tag_seek[tag_filename] == pos) | ||
346 | break ; | ||
347 | } | ||
348 | close(masterfd); | ||
349 | 337 | ||
350 | /* Not found? */ | 338 | if (!tagcache_search(tcs, tag_filename)) |
351 | if (i == tch.entry_count) | 339 | return false; |
352 | { | ||
353 | logf("not found!"); | ||
354 | return NULL; | ||
355 | } | ||
356 | 340 | ||
357 | return &idx; | 341 | tcs->entry_count = 0; |
342 | tcs->idx_id = idx_id; | ||
343 | |||
344 | return true; | ||
358 | } | 345 | } |
359 | 346 | ||
360 | static long tagcache_get_seek(const struct tagcache_search *tcs, | 347 | static bool tagcache_get_index(const struct tagcache_search *tcs, |
361 | int tag, int idxid) | 348 | int idxid, struct index_entry *idx) |
362 | { | 349 | { |
363 | struct index_entry idx; | ||
364 | |||
365 | #ifdef HAVE_TC_RAMCACHE | 350 | #ifdef HAVE_TC_RAMCACHE |
366 | if (tcs->ramsearch) | 351 | if (tcs->ramsearch) |
367 | { | 352 | { |
368 | if (hdr->indices[idxid].flag & FLAG_DELETED) | 353 | if (hdr->indices[idxid].flag & FLAG_DELETED) |
369 | return false; | 354 | return false; |
370 | 355 | ||
371 | return hdr->indices[idxid].tag_seek[tag]; | 356 | memcpy(idx, &hdr->indices[idxid], sizeof(struct index_entry)); |
357 | return true; | ||
372 | } | 358 | } |
373 | #endif | 359 | #endif |
374 | 360 | ||
375 | lseek(tcs->masterfd, idxid * sizeof(struct index_entry) | 361 | lseek(tcs->masterfd, idxid * sizeof(struct index_entry) |
376 | + sizeof(struct tagcache_header), SEEK_SET); | 362 | + sizeof(struct tagcache_header), SEEK_SET); |
377 | if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) != | 363 | if (read(tcs->masterfd, idx, sizeof(struct index_entry)) != |
378 | sizeof(struct index_entry)) | 364 | sizeof(struct index_entry)) |
379 | { | 365 | { |
380 | logf("read error #3"); | 366 | logf("read error #3"); |
381 | return -4; | 367 | return false; |
368 | } | ||
369 | |||
370 | return true; | ||
371 | } | ||
372 | |||
373 | static long check_virtual_tags(int tag, const struct index_entry *idx) | ||
374 | { | ||
375 | long data = 0; | ||
376 | |||
377 | switch (tag) | ||
378 | { | ||
379 | case tag_virt_autoscore: | ||
380 | if (idx->tag_seek[tag_length] == 0 | ||
381 | || idx->tag_seek[tag_playcount] == 0) | ||
382 | { | ||
383 | data = 0; | ||
384 | } | ||
385 | else | ||
386 | { | ||
387 | data = 100 * idx->tag_seek[tag_playtime] | ||
388 | / idx->tag_seek[tag_length] | ||
389 | / idx->tag_seek[tag_playcount]; | ||
390 | } | ||
391 | break; | ||
392 | |||
393 | default: | ||
394 | data = idx->tag_seek[tag]; | ||
382 | } | 395 | } |
383 | 396 | ||
384 | return idx.tag_seek[tag]; | 397 | return data; |
385 | } | 398 | } |
386 | 399 | ||
387 | long tagcache_get_numeric(const struct tagcache_search *tcs, int tag) | 400 | long tagcache_get_numeric(const struct tagcache_search *tcs, int tag) |
388 | { | 401 | { |
402 | struct index_entry idx; | ||
389 | 403 | ||
390 | if (!tagcache_is_numeric_tag(tag)) | 404 | if (!tagcache_is_numeric_tag(tag)) |
391 | return -1; | 405 | return -1; |
392 | 406 | ||
393 | return tagcache_get_seek(tcs, tag, tcs->idx_id); | 407 | if (!tagcache_get_index(tcs, tcs->idx_id, &idx)) |
408 | return -2; | ||
409 | |||
410 | return check_virtual_tags(tag, &idx); | ||
394 | } | 411 | } |
395 | 412 | ||
396 | static bool check_against_clause(long numeric, const char *str, | 413 | static bool check_against_clause(long numeric, const char *str, |
@@ -459,16 +476,20 @@ static bool build_lookup_list(struct tagcache_search *tcs) | |||
459 | /* Go through all conditional clauses. */ | 476 | /* Go through all conditional clauses. */ |
460 | for (j = 0; j < tcs->clause_count; j++) | 477 | for (j = 0; j < tcs->clause_count; j++) |
461 | { | 478 | { |
462 | int seek = hdr->indices[i].tag_seek[tcs->clause[j]->tag]; | 479 | struct index_entry *idx = &hdr->indices[i]; |
480 | int seek; | ||
463 | char *str = NULL; | 481 | char *str = NULL; |
464 | struct tagfile_entry *entry; | 482 | struct tagfile_entry *entry; |
465 | 483 | ||
484 | seek = check_virtual_tags(tcs->clause[j]->tag, idx); | ||
485 | |||
466 | if (!tagcache_is_numeric_tag(tcs->clause[j]->tag)) | 486 | if (!tagcache_is_numeric_tag(tcs->clause[j]->tag)) |
467 | { | 487 | { |
468 | entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek]; | 488 | entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek]; |
469 | str = entry->tag_data; | 489 | str = entry->tag_data; |
470 | } | 490 | } |
471 | 491 | ||
492 | |||
472 | if (!check_against_clause(seek, str, tcs->clause[j])) | 493 | if (!check_against_clause(seek, str, tcs->clause[j])) |
473 | break ; | 494 | break ; |
474 | } | 495 | } |
@@ -529,9 +550,11 @@ static bool build_lookup_list(struct tagcache_search *tcs) | |||
529 | for (i = 0; i < tcs->clause_count; i++) | 550 | for (i = 0; i < tcs->clause_count; i++) |
530 | { | 551 | { |
531 | struct tagfile_entry tfe; | 552 | struct tagfile_entry tfe; |
532 | int seek = entry.tag_seek[tcs->clause[i]->tag]; | 553 | int seek; |
533 | char str[256]; | 554 | char str[256]; |
534 | 555 | ||
556 | seek = check_virtual_tags(tcs->clause[i]->tag, &entry); | ||
557 | |||
535 | memset(str, 0, sizeof str); | 558 | memset(str, 0, sizeof str); |
536 | if (!tagcache_is_numeric_tag(tcs->clause[i]->tag)) | 559 | if (!tagcache_is_numeric_tag(tcs->clause[i]->tag)) |
537 | { | 560 | { |
@@ -852,9 +875,13 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, | |||
852 | char *buf, long size) | 875 | char *buf, long size) |
853 | { | 876 | { |
854 | struct tagfile_entry tfe; | 877 | struct tagfile_entry tfe; |
878 | struct index_entry idx; | ||
855 | long seek; | 879 | long seek; |
856 | 880 | ||
857 | seek = tagcache_get_seek(tcs, tcs->type, idxid); | 881 | if (!tagcache_get_index(tcs, idxid, &idx)) |
882 | return false; | ||
883 | |||
884 | seek = idx.tag_seek[tcs->type]; | ||
858 | if (seek < 0) | 885 | if (seek < 0) |
859 | { | 886 | { |
860 | logf("Retrieve failed"); | 887 | logf("Retrieve failed"); |
@@ -974,12 +1001,15 @@ static long get_tag_numeric(const struct index_entry *entry, int tag) | |||
974 | bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) | 1001 | bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) |
975 | { | 1002 | { |
976 | struct index_entry *entry; | 1003 | struct index_entry *entry; |
1004 | int idx_id; | ||
977 | 1005 | ||
978 | /* Find the corresponding entry in tagcache. */ | 1006 | /* Find the corresponding entry in tagcache. */ |
979 | entry = find_entry_ram(filename, NULL); | 1007 | idx_id = find_entry_ram(filename, NULL); |
980 | if (entry == NULL || !stat.ramcache) | 1008 | if (idx_id < 0 || !stat.ramcache) |
981 | return false; | 1009 | return false; |
982 | 1010 | ||
1011 | entry = &hdr->indices[idx_id]; | ||
1012 | |||
983 | id3->title = get_tag(entry, tag_title)->tag_data; | 1013 | id3->title = get_tag(entry, tag_title)->tag_data; |
984 | id3->artist = get_tag(entry, tag_artist)->tag_data; | 1014 | id3->artist = get_tag(entry, tag_artist)->tag_data; |
985 | id3->album = get_tag(entry, tag_album)->tag_data; | 1015 | id3->album = get_tag(entry, tag_album)->tag_data; |
@@ -1036,13 +1066,13 @@ static void add_tagcache(const char *path) | |||
1036 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) | 1066 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
1037 | if (stat.ramcache && dircache_is_enabled()) | 1067 | if (stat.ramcache && dircache_is_enabled()) |
1038 | { | 1068 | { |
1039 | if (find_entry_ram(path, dc)) | 1069 | if (find_entry_ram(path, dc) >= 0) |
1040 | return ; | 1070 | return ; |
1041 | } | 1071 | } |
1042 | else | 1072 | else |
1043 | #endif | 1073 | #endif |
1044 | { | 1074 | { |
1045 | if (find_entry_disk(path, false)) | 1075 | if (find_entry_disk(path) >= 0) |
1046 | return ; | 1076 | return ; |
1047 | } | 1077 | } |
1048 | 1078 | ||
@@ -2051,35 +2081,101 @@ static void free_tempbuf(void) | |||
2051 | tempbuf_size = 0; | 2081 | tempbuf_size = 0; |
2052 | } | 2082 | } |
2053 | 2083 | ||
2054 | static bool delete_entry(long idx_id) | 2084 | static int open_master_fd(struct tagcache_header *hdr) |
2055 | { | 2085 | { |
2056 | int fd; | 2086 | int fd; |
2057 | int tag, i; | ||
2058 | struct index_entry idx, myidx; | ||
2059 | struct tagcache_header hdr; | ||
2060 | char buf[MAX_PATH]; | ||
2061 | int in_use[TAG_COUNT]; | ||
2062 | 2087 | ||
2063 | fd = open(TAGCACHE_FILE_MASTER, O_RDWR); | 2088 | fd = open(TAGCACHE_FILE_MASTER, O_RDWR); |
2064 | if (fd < 0) | 2089 | if (fd < 0) |
2065 | { | 2090 | { |
2066 | logf("master file open failed for R/W"); | 2091 | logf("master file open failed for R/W"); |
2067 | return false; | 2092 | return fd; |
2068 | } | 2093 | } |
2069 | 2094 | ||
2070 | /* Check the header. */ | 2095 | /* Check the header. */ |
2071 | read(fd, &hdr, sizeof(struct tagcache_header)); | 2096 | read(fd, hdr, sizeof(struct tagcache_header)); |
2072 | if (hdr.magic != TAGCACHE_MAGIC) | 2097 | if (hdr->magic != TAGCACHE_MAGIC) |
2073 | { | 2098 | { |
2074 | logf("header error"); | 2099 | logf("header error"); |
2100 | close(fd); | ||
2101 | return -2; | ||
2102 | } | ||
2103 | |||
2104 | return fd; | ||
2105 | } | ||
2106 | |||
2107 | bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, | ||
2108 | int tag, long data) | ||
2109 | { | ||
2110 | struct index_entry idx; | ||
2111 | struct tagcache_header myhdr; | ||
2112 | |||
2113 | if (!tagcache_is_numeric_tag(tag)) | ||
2075 | return false; | 2114 | return false; |
2115 | |||
2116 | #ifdef HAVE_TC_RAMCACHE | ||
2117 | /* Update ram entries first. */ | ||
2118 | if (tcs->ramsearch) | ||
2119 | { | ||
2120 | hdr->indices[tcs->idx_id].tag_seek[tag] = data; | ||
2121 | hdr->indices[tcs->idx_id].flag |= FLAG_DIRTYNUM; | ||
2076 | } | 2122 | } |
2123 | #endif | ||
2124 | |||
2125 | /* And now update the db on disk also. */ | ||
2126 | if (tcs->masterfd < 0) | ||
2127 | { | ||
2128 | if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0) | ||
2129 | return false; | ||
2130 | } | ||
2131 | |||
2132 | lseek(tcs->masterfd, tcs->idx_id * sizeof(struct index_entry) | ||
2133 | + sizeof(struct tagcache_header), SEEK_SET); | ||
2134 | if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) | ||
2135 | != sizeof(struct index_entry)) | ||
2136 | { | ||
2137 | logf("read error"); | ||
2138 | return false; | ||
2139 | } | ||
2140 | |||
2141 | idx.flag |= FLAG_DIRTYNUM; | ||
2142 | idx.tag_seek[tag] = data; | ||
2143 | |||
2144 | lseek(tcs->masterfd, -sizeof(struct index_entry), SEEK_CUR); | ||
2145 | if (write(tcs->masterfd, &idx, sizeof(struct index_entry)) | ||
2146 | != sizeof(struct index_entry)) | ||
2147 | { | ||
2148 | logf("write error"); | ||
2149 | return false; | ||
2150 | } | ||
2151 | |||
2152 | return true; | ||
2153 | } | ||
2154 | |||
2155 | static bool delete_entry(long idx_id) | ||
2156 | { | ||
2157 | int fd; | ||
2158 | int tag, i; | ||
2159 | struct index_entry idx, myidx; | ||
2160 | struct tagcache_header myhdr; | ||
2161 | char buf[MAX_PATH]; | ||
2162 | int in_use[TAG_COUNT]; | ||
2163 | |||
2164 | #ifdef HAVE_TC_RAMCACHE | ||
2165 | /* At first mark the entry removed from ram cache. */ | ||
2166 | if (hdr) | ||
2167 | hdr->indices[idx_id].flag |= FLAG_DELETED; | ||
2168 | #endif | ||
2169 | |||
2170 | if ( (fd = open_master_fd(&myhdr) < 0) ) | ||
2171 | return false; | ||
2077 | 2172 | ||
2078 | lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); | 2173 | lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); |
2079 | if (read(fd, &myidx, sizeof(struct index_entry)) | 2174 | if (read(fd, &myidx, sizeof(struct index_entry)) |
2080 | != sizeof(struct index_entry)) | 2175 | != sizeof(struct index_entry)) |
2081 | { | 2176 | { |
2082 | logf("read error"); | 2177 | logf("read error"); |
2178 | close(fd); | ||
2083 | return false; | 2179 | return false; |
2084 | } | 2180 | } |
2085 | 2181 | ||
@@ -2089,6 +2185,7 @@ static bool delete_entry(long idx_id) | |||
2089 | != sizeof(struct index_entry)) | 2185 | != sizeof(struct index_entry)) |
2090 | { | 2186 | { |
2091 | logf("write error"); | 2187 | logf("write error"); |
2188 | close(fd); | ||
2092 | return false; | 2189 | return false; |
2093 | } | 2190 | } |
2094 | 2191 | ||
@@ -2097,7 +2194,7 @@ static bool delete_entry(long idx_id) | |||
2097 | in_use[tag] = 0; | 2194 | in_use[tag] = 0; |
2098 | 2195 | ||
2099 | lseek(fd, sizeof(struct tagcache_header), SEEK_SET); | 2196 | lseek(fd, sizeof(struct tagcache_header), SEEK_SET); |
2100 | for (i = 0; i < hdr.entry_count; i++) | 2197 | for (i = 0; i < myhdr.entry_count; i++) |
2101 | { | 2198 | { |
2102 | if (read(fd, &idx, sizeof(struct index_entry)) | 2199 | if (read(fd, &idx, sizeof(struct index_entry)) |
2103 | != sizeof(struct index_entry)) | 2200 | != sizeof(struct index_entry)) |
@@ -2376,7 +2473,6 @@ static bool load_tagcache(void) | |||
2376 | { | 2473 | { |
2377 | logf("Entry no longer valid."); | 2474 | logf("Entry no longer valid."); |
2378 | logf("-> %s", buf); | 2475 | logf("-> %s", buf); |
2379 | idx->flag |= FLAG_DELETED; | ||
2380 | delete_entry(fe->idx_id); | 2476 | delete_entry(fe->idx_id); |
2381 | continue ; | 2477 | continue ; |
2382 | } | 2478 | } |
@@ -2397,7 +2493,6 @@ static bool load_tagcache(void) | |||
2397 | { | 2493 | { |
2398 | logf("Entry no longer valid."); | 2494 | logf("Entry no longer valid."); |
2399 | logf("-> %s", buf); | 2495 | logf("-> %s", buf); |
2400 | idx->flag |= FLAG_DELETED; | ||
2401 | delete_entry(fe->idx_id); | 2496 | delete_entry(fe->idx_id); |
2402 | continue; | 2497 | continue; |
2403 | } | 2498 | } |
@@ -2808,6 +2903,7 @@ void tagcache_init(void) | |||
2808 | { | 2903 | { |
2809 | stat.initialized = false; | 2904 | stat.initialized = false; |
2810 | stat.commit_step = 0; | 2905 | stat.commit_step = 0; |
2906 | filenametag_fd = -1; | ||
2811 | 2907 | ||
2812 | queue_init(&tagcache_queue); | 2908 | queue_init(&tagcache_queue); |
2813 | create_thread(tagcache_thread, tagcache_stack, | 2909 | create_thread(tagcache_thread, tagcache_stack, |
diff --git a/apps/tagcache.h b/apps/tagcache.h index a82f6e1634..04f9567936 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h | |||
@@ -23,9 +23,11 @@ | |||
23 | 23 | ||
24 | enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, | 24 | enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, |
25 | tag_filename, tag_composer, tag_year, tag_tracknumber, | 25 | tag_filename, tag_composer, tag_year, tag_tracknumber, |
26 | tag_bitrate, tag_length }; | 26 | tag_bitrate, tag_length, tag_playcount, tag_playtime, tag_lastplayed, |
27 | /* Virtual tags */ | ||
28 | tag_virt_autoscore }; | ||
27 | 29 | ||
28 | #define TAG_COUNT 10 | 30 | #define TAG_COUNT 13 |
29 | 31 | ||
30 | /* Allow a little drift to the filename ordering (should not be too high/low). */ | 32 | /* Allow a little drift to the filename ordering (should not be too high/low). */ |
31 | #define POS_HISTORY_COUNT 4 | 33 | #define POS_HISTORY_COUNT 4 |
@@ -34,7 +36,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, | |||
34 | #define IDX_BUF_DEPTH 64 | 36 | #define IDX_BUF_DEPTH 64 |
35 | 37 | ||
36 | /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ | 38 | /* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ |
37 | #define TAGCACHE_MAGIC 0x54434804 | 39 | #define TAGCACHE_MAGIC 0x54434805 |
38 | 40 | ||
39 | /* How much to allocate extra space for ramcache. */ | 41 | /* How much to allocate extra space for ramcache. */ |
40 | #define TAGCACHE_RESERVE 32768 | 42 | #define TAGCACHE_RESERVE 32768 |
@@ -68,6 +70,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, | |||
68 | /* Flags */ | 70 | /* Flags */ |
69 | #define FLAG_DELETED 0x0001 /* Entry has been removed from db */ | 71 | #define FLAG_DELETED 0x0001 /* Entry has been removed from db */ |
70 | #define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */ | 72 | #define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */ |
73 | #define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */ | ||
71 | 74 | ||
72 | enum clause { clause_none, clause_is, clause_gt, clause_gteq, clause_lt, | 75 | enum clause { clause_none, clause_is, clause_gt, clause_gteq, clause_lt, |
73 | clause_lteq, clause_contains, clause_begins_with, clause_ends_with }; | 76 | clause_lteq, clause_contains, clause_begins_with, clause_ends_with }; |
@@ -123,6 +126,7 @@ struct tagcache_search { | |||
123 | bool tagcache_is_numeric_tag(int type); | 126 | bool tagcache_is_numeric_tag(int type); |
124 | bool tagcache_is_unique_tag(int type); | 127 | bool tagcache_is_unique_tag(int type); |
125 | bool tagcache_is_sorted_tag(int type); | 128 | bool tagcache_is_sorted_tag(int type); |
129 | bool tagcache_find_index(struct tagcache_search *tcs, const char *filename); | ||
126 | bool tagcache_search(struct tagcache_search *tcs, int tag); | 130 | bool tagcache_search(struct tagcache_search *tcs, int tag); |
127 | bool tagcache_search_add_filter(struct tagcache_search *tcs, | 131 | bool tagcache_search_add_filter(struct tagcache_search *tcs, |
128 | int tag, int seek); | 132 | int tag, int seek); |
@@ -133,6 +137,8 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid, | |||
133 | char *buf, long size); | 137 | char *buf, long size); |
134 | void tagcache_search_finish(struct tagcache_search *tcs); | 138 | void tagcache_search_finish(struct tagcache_search *tcs); |
135 | long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); | 139 | long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); |
140 | bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, | ||
141 | int tag, long data); | ||
136 | 142 | ||
137 | struct tagcache_stat* tagcache_get_stat(void); | 143 | struct tagcache_stat* tagcache_get_stat(void); |
138 | int tagcache_get_commit_step(void); | 144 | int tagcache_get_commit_step(void); |
diff --git a/apps/tagnavi.config b/apps/tagnavi.config index 2b00d59e1c..285f826425 100644 --- a/apps/tagnavi.config +++ b/apps/tagnavi.config | |||
@@ -1,11 +1,16 @@ | |||
1 | "Artists" artist : album : title | 1 | "Artists" artist : album : title = "%02d. %s" tracknum title |
2 | "Albums" album : title | 2 | "Albums" album : title = "%02d. %s" tracknum title |
3 | "Genres" genre : artist : album : title | 3 | "Genres" genre : artist : album : title = "%02d. %s" tracknum title |
4 | "Composers" composer : album : title | 4 | "Composers" composer : album : title = "%02d. %s" tracknum title |
5 | "Tracks" title | 5 | "Tracks" title |
6 | "Search by artist" artist ? artist ~ "" : album : title | 6 | "Search by artist" artist ? artist ~ "" : album : title = "%02d. %s" tracknum title |
7 | "Search by album" album ? album ~ "" : title | 7 | "Search by album" album ? album ~ "" : title = "%02d. %s" tracknum title |
8 | "Search by title" title ? title ~ "" | 8 | "Search by title" title ? title ~ "" |
9 | "Search by year" artist ? year = "" : album : title | 9 | "Search by year" artist ? year = "" : album : title = "%02d. %s" tracknum title |
10 | "Search by score" title = "(%3d) %s" autoscore title ? autoscore > "" | ||
11 | "Most played tracks" title = "(%d) %s" playcount title ? playcount > "1" | ||
12 | "Never played tracks" artist ? playcount == "0" : album : title = "%02d. %s" tracknum title | ||
13 | "Best tracks" title ? autoscore > "60" & playcount > "1" | ||
10 | "Example 1" artist ? year >= "2000" & artist ^ "a" : album : title | 14 | "Example 1" artist ? year >= "2000" & artist ^ "a" : album : title |
11 | "Example 2" genre ? genre ~ "metal" : artist ? year >= "2000" : album ? year >= "2000" : title | 15 | "Example 2" genre ? genre ~ "metal" : artist ? year >= "2000" : album ? year >= "2000" : title |
16 | "List played tracks" title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0" | ||
diff --git a/apps/tagtree.c b/apps/tagtree.c index ded2426d41..396248d7ce 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include "gui/list.h" | 39 | #include "gui/list.h" |
40 | #include "buffer.h" | 40 | #include "buffer.h" |
41 | #include "atoi.h" | 41 | #include "atoi.h" |
42 | #include "playback.h" | ||
42 | 43 | ||
43 | #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" | 44 | #define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" |
44 | 45 | ||
@@ -47,11 +48,28 @@ static int tagtree_play_folder(struct tree_context* c); | |||
47 | static char searchstring[32]; | 48 | static char searchstring[32]; |
48 | #define MAX_TAGS 5 | 49 | #define MAX_TAGS 5 |
49 | 50 | ||
51 | /* | ||
52 | * "%3d. %s" autoscore title | ||
53 | * | ||
54 | * valid = true | ||
55 | * formatstr = "%-3d. %s" | ||
56 | * tags[0] = tag_autoscore | ||
57 | * tags[1] = tag_title | ||
58 | * tag_count = 2 | ||
59 | */ | ||
60 | struct display_format { | ||
61 | bool valid; | ||
62 | char formatstr[64]; | ||
63 | int tags[MAX_TAGS]; | ||
64 | int tag_count; | ||
65 | }; | ||
66 | |||
50 | struct search_instruction { | 67 | struct search_instruction { |
51 | char name[64]; | 68 | char name[64]; |
52 | int tagorder[MAX_TAGS]; | 69 | int tagorder[MAX_TAGS]; |
53 | int tagorder_count; | 70 | int tagorder_count; |
54 | struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; | 71 | struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; |
72 | struct display_format format[MAX_TAGS]; | ||
55 | int clause_count[MAX_TAGS]; | 73 | int clause_count[MAX_TAGS]; |
56 | int result_seek[MAX_TAGS]; | 74 | int result_seek[MAX_TAGS]; |
57 | }; | 75 | }; |
@@ -121,6 +139,8 @@ static int get_tag(int *tag) | |||
121 | MATCH(tag, buf, "title", tag_title); | 139 | MATCH(tag, buf, "title", tag_title); |
122 | MATCH(tag, buf, "tracknum", tag_tracknumber); | 140 | MATCH(tag, buf, "tracknum", tag_tracknumber); |
123 | MATCH(tag, buf, "year", tag_year); | 141 | MATCH(tag, buf, "year", tag_year); |
142 | MATCH(tag, buf, "playcount", tag_playcount); | ||
143 | MATCH(tag, buf, "autoscore", tag_virt_autoscore); | ||
124 | 144 | ||
125 | logf("NO MATCH: %s\n", buf); | 145 | logf("NO MATCH: %s\n", buf); |
126 | if (buf[0] == '?') | 146 | if (buf[0] == '?') |
@@ -163,7 +183,7 @@ static int get_clause(int *condition) | |||
163 | return 0; | 183 | return 0; |
164 | } | 184 | } |
165 | 185 | ||
166 | static bool add_clause(struct search_instruction *inst, | 186 | static bool add_clause(struct search_instruction *inst, |
167 | int tag, int type, const char *str) | 187 | int tag, int type, const char *str) |
168 | { | 188 | { |
169 | int len = strlen(str); | 189 | int len = strlen(str); |
@@ -206,14 +226,51 @@ static bool add_clause(struct search_instruction *inst, | |||
206 | return true; | 226 | return true; |
207 | } | 227 | } |
208 | 228 | ||
229 | static int get_format_str(struct display_format *fmt) | ||
230 | { | ||
231 | int ret; | ||
232 | |||
233 | memset(fmt, 0, sizeof(struct display_format)); | ||
234 | |||
235 | if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0) | ||
236 | return -10; | ||
237 | |||
238 | while (fmt->tag_count < MAX_TAGS) | ||
239 | { | ||
240 | ret = get_tag(&fmt->tags[fmt->tag_count]); | ||
241 | if (ret < 0) | ||
242 | return -11; | ||
243 | |||
244 | if (ret == 0) | ||
245 | break; | ||
246 | |||
247 | fmt->tag_count++; | ||
248 | } | ||
249 | |||
250 | fmt->valid = true; | ||
251 | |||
252 | return 1; | ||
253 | } | ||
254 | |||
209 | static int get_condition(struct search_instruction *inst) | 255 | static int get_condition(struct search_instruction *inst) |
210 | { | 256 | { |
257 | struct display_format format; | ||
258 | struct display_format *fmt = NULL; | ||
211 | int tag; | 259 | int tag; |
212 | int condition; | 260 | int condition; |
213 | char buf[32]; | 261 | char buf[32]; |
214 | 262 | ||
215 | switch (*strp) | 263 | switch (*strp) |
216 | { | 264 | { |
265 | case '=': | ||
266 | if (get_format_str(&format) < 0) | ||
267 | { | ||
268 | logf("get_format_str() parser failed!"); | ||
269 | return -4; | ||
270 | } | ||
271 | fmt = &format; | ||
272 | break; | ||
273 | |||
217 | case '?': | 274 | case '?': |
218 | case ' ': | 275 | case ' ': |
219 | case '&': | 276 | case '&': |
@@ -225,6 +282,14 @@ static int get_condition(struct search_instruction *inst) | |||
225 | return 0; | 282 | return 0; |
226 | } | 283 | } |
227 | 284 | ||
285 | if (fmt) | ||
286 | { | ||
287 | memcpy(&inst->format[inst->tagorder_count], fmt, | ||
288 | sizeof(struct display_format)); | ||
289 | } | ||
290 | else | ||
291 | inst->format[inst->tagorder_count].valid = false; | ||
292 | |||
228 | if (get_tag(&tag) <= 0) | 293 | if (get_tag(&tag) <= 0) |
229 | return -1; | 294 | return -1; |
230 | 295 | ||
@@ -294,6 +359,62 @@ static int compare(const void *p1, const void *p2) | |||
294 | return strncasecmp(e1->name, e2->name, MAX_PATH); | 359 | return strncasecmp(e1->name, e2->name, MAX_PATH); |
295 | } | 360 | } |
296 | 361 | ||
362 | static void tagtree_buffer_event(struct mp3entry *id3, bool last_track) | ||
363 | { | ||
364 | (void)id3; | ||
365 | (void)last_track; | ||
366 | |||
367 | logf("be:%d%s", last_track, id3->path); | ||
368 | } | ||
369 | |||
370 | static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track) | ||
371 | { | ||
372 | (void)last_track; | ||
373 | long playcount; | ||
374 | long playtime; | ||
375 | long lastplayed; | ||
376 | |||
377 | /* Do not gather data unless proper setting has been enabled. */ | ||
378 | if (!global_settings.runtimedb) | ||
379 | return; | ||
380 | |||
381 | /* Don't process unplayed tracks. */ | ||
382 | if (id3->elapsed == 0) | ||
383 | return; | ||
384 | |||
385 | if (!tagcache_find_index(&tcs, id3->path)) | ||
386 | { | ||
387 | logf("tc stat: not found: %s", id3->path); | ||
388 | return; | ||
389 | } | ||
390 | |||
391 | playcount = tagcache_get_numeric(&tcs, tag_playcount); | ||
392 | playtime = tagcache_get_numeric(&tcs, tag_playtime); | ||
393 | lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); | ||
394 | |||
395 | playcount++; | ||
396 | |||
397 | /* Ignore the last 15s (crossfade etc.) */ | ||
398 | playtime += MIN(id3->length, id3->elapsed + 15 * 1000); | ||
399 | |||
400 | logf("ube:%s", id3->path); | ||
401 | logf("-> %d/%d/%d", last_track, playcount, playtime); | ||
402 | logf("-> %d/%d/%d", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000)); | ||
403 | |||
404 | /* lastplayed not yet supported. */ | ||
405 | |||
406 | if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount) | ||
407 | || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime) | ||
408 | || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, tag_lastplayed)) | ||
409 | { | ||
410 | logf("tc stat: modify failed!"); | ||
411 | tagcache_search_finish(&tcs); | ||
412 | return; | ||
413 | } | ||
414 | |||
415 | tagcache_search_finish(&tcs); | ||
416 | } | ||
417 | |||
297 | void tagtree_init(void) | 418 | void tagtree_init(void) |
298 | { | 419 | { |
299 | int fd; | 420 | int fd; |
@@ -350,6 +471,9 @@ void tagtree_init(void) | |||
350 | close(fd); | 471 | close(fd); |
351 | 472 | ||
352 | audiobuf += sizeof(struct search_instruction) * si_count + 4; | 473 | audiobuf += sizeof(struct search_instruction) * si_count + 4; |
474 | |||
475 | audio_set_track_buffer_event(tagtree_buffer_event); | ||
476 | audio_set_track_unbuffer_event(tagtree_unbuffer_event); | ||
353 | } | 477 | } |
354 | 478 | ||
355 | bool show_search_progress(bool init, int count) | 479 | bool show_search_progress(bool init, int count) |
@@ -445,48 +569,100 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs, | |||
445 | 569 | ||
446 | while (tagcache_get_next(tcs)) | 570 | while (tagcache_get_next(tcs)) |
447 | { | 571 | { |
572 | struct display_format *fmt = &csi->format[extra]; | ||
573 | |||
448 | if (total_count++ < offset) | 574 | if (total_count++ < offset) |
449 | continue; | 575 | continue; |
450 | 576 | ||
451 | dptr->newtable = navibrowse; | 577 | dptr->newtable = navibrowse; |
452 | dptr->extraseek = tcs->result_seek; | 578 | dptr->extraseek = tcs->result_seek; |
453 | if (!tcs->ramsearch || tag == tag_title) | 579 | if (tag == tag_title) |
580 | dptr->newtable = playtrack; | ||
581 | |||
582 | if (!tcs->ramsearch || fmt->valid) | ||
454 | { | 583 | { |
455 | int tracknum = -1; | 584 | char buf[MAX_PATH]; |
585 | int buf_pos = 0; | ||
456 | 586 | ||
457 | dptr->name = &c->name_buffer[namebufused]; | 587 | if (fmt->valid) |
458 | if (tag == tag_title) | ||
459 | { | ||
460 | dptr->newtable = playtrack; | ||
461 | if (c->currtable != allsubentries && c->dirlevel > 1) | ||
462 | tracknum = tagcache_get_numeric(tcs, tag_tracknumber); | ||
463 | } | ||
464 | |||
465 | if (tracknum > 0) | ||
466 | { | 588 | { |
467 | snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s", | 589 | char fmtbuf[8]; |
468 | tracknum, tcs->result); | 590 | bool read_format = false; |
469 | namebufused += strlen(dptr->name) + 1; | 591 | int fmtbuf_pos = 0; |
470 | if (namebufused >= c->name_buffer_size) | 592 | int parpos = 0; |
593 | |||
594 | memset(buf, 0, sizeof buf); | ||
595 | for (i = 0; fmt->formatstr[i] != '\0'; i++) | ||
471 | { | 596 | { |
472 | logf("chunk mode #1: %d", current_entry_count); | 597 | if (fmt->formatstr[i] == '%') |
473 | c->dirfull = true; | 598 | { |
474 | sort = false; | 599 | read_format = true; |
475 | break ; | 600 | fmtbuf_pos = 0; |
601 | if (parpos >= fmt->tag_count) | ||
602 | { | ||
603 | logf("too many format tags"); | ||
604 | return 0; | ||
605 | } | ||
606 | } | ||
607 | |||
608 | if (read_format) | ||
609 | { | ||
610 | fmtbuf[fmtbuf_pos++] = fmt->formatstr[i]; | ||
611 | if (fmtbuf_pos >= (long)sizeof(fmtbuf)) | ||
612 | { | ||
613 | logf("format parse error"); | ||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | if (fmt->formatstr[i] == 's') | ||
618 | { | ||
619 | fmtbuf[fmtbuf_pos] = '\0'; | ||
620 | read_format = false; | ||
621 | snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, tcs->result); | ||
622 | buf_pos += strlen(&buf[buf_pos]); | ||
623 | parpos++; | ||
624 | } | ||
625 | else if (fmt->formatstr[i] == 'd') | ||
626 | { | ||
627 | fmtbuf[fmtbuf_pos] = '\0'; | ||
628 | read_format = false; | ||
629 | snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, | ||
630 | tagcache_get_numeric(tcs, fmt->tags[parpos])); | ||
631 | buf_pos += strlen(&buf[buf_pos]); | ||
632 | parpos++; | ||
633 | } | ||
634 | continue; | ||
635 | } | ||
636 | |||
637 | buf[buf_pos++] = fmt->formatstr[i]; | ||
638 | |||
639 | if (buf_pos - 1 >= (long)sizeof(buf)) | ||
640 | { | ||
641 | logf("buffer overflow"); | ||
642 | return 0; | ||
643 | } | ||
476 | } | 644 | } |
645 | |||
646 | buf[buf_pos++] = '\0'; | ||
477 | } | 647 | } |
648 | |||
649 | dptr->name = &c->name_buffer[namebufused]; | ||
650 | if (fmt->valid) | ||
651 | namebufused += buf_pos; | ||
478 | else | 652 | else |
479 | { | ||
480 | namebufused += tcs->result_len; | 653 | namebufused += tcs->result_len; |
481 | if (namebufused >= c->name_buffer_size) | 654 | |
482 | { | 655 | if (namebufused >= c->name_buffer_size) |
483 | logf("chunk mode #2: %d", current_entry_count); | 656 | { |
484 | c->dirfull = true; | 657 | logf("chunk mode #2: %d", current_entry_count); |
485 | sort = false; | 658 | c->dirfull = true; |
486 | break ; | 659 | sort = false; |
487 | } | 660 | break ; |
488 | strcpy(dptr->name, tcs->result); | ||
489 | } | 661 | } |
662 | if (fmt->valid) | ||
663 | strcpy(dptr->name, buf); | ||
664 | else | ||
665 | strcpy(dptr->name, tcs->result); | ||
490 | } | 666 | } |
491 | else | 667 | else |
492 | dptr->name = tcs->result; | 668 | dptr->name = tcs->result; |