summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/debug_menu.c21
-rw-r--r--apps/dsp.c60
-rw-r--r--apps/filetree.c31
-rw-r--r--apps/filetypes.c22
-rw-r--r--apps/main.c6
-rw-r--r--apps/menus/playback_menu.c2
-rw-r--r--apps/mpeg.c121
-rw-r--r--apps/playback.c76
-rw-r--r--apps/playlist.c95
-rw-r--r--apps/playlist.h9
-rw-r--r--apps/plugin.c6
-rw-r--r--apps/plugin.h2
-rw-r--r--apps/plugins/imageviewer/imageviewer.c2
-rw-r--r--apps/plugins/mikmod/mikmod.c2
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c2
-rw-r--r--apps/plugins/rockpaint.c2
-rw-r--r--apps/scrobbler.c8
-rw-r--r--apps/tagcache.c144
-rw-r--r--apps/tagcache.h4
-rw-r--r--apps/tagtree.c229
-rw-r--r--apps/tagtree.h9
-rw-r--r--apps/tdspeed.c75
-rw-r--r--apps/tdspeed.h3
-rw-r--r--apps/tree.c78
-rw-r--r--apps/tree.h36
-rw-r--r--firmware/buflib.c5
-rw-r--r--firmware/common/dircache.c114
-rw-r--r--firmware/core_alloc.c1
28 files changed, 866 insertions, 299 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index fb8575ec62..6375094225 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -425,11 +425,32 @@ static const char* bf_getname(int selected_item, void *data,
425 return buffer; 425 return buffer;
426} 426}
427 427
428static int bf_action_cb(int action, struct gui_synclist* list)
429{
430 if (action == ACTION_STD_OK)
431 {
432 splash(HZ/1, "Attempting a 64k allocation");
433 int handle = core_alloc("test", 64<<10);
434 splash(HZ/2, (handle > 0) ? "Success":"Fail");
435 /* for some reason simplelist doesn't allow adding items here if
436 * info.get_name is given, so use normal list api */
437 gui_synclist_set_nb_items(list, core_get_num_blocks());
438 if (handle > 0)
439 core_free(handle);
440 action = ACTION_REDRAW;
441 }
442 else if (action == ACTION_NONE)
443 action = ACTION_REDRAW;
444 return action;
445}
446
428static bool dbg_buflib_allocs(void) 447static bool dbg_buflib_allocs(void)
429{ 448{
430 struct simplelist_info info; 449 struct simplelist_info info;
431 simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL); 450 simplelist_info_init(&info, "mem allocs", core_get_num_blocks(), NULL);
432 info.get_name = bf_getname; 451 info.get_name = bf_getname;
452 info.action_callback = bf_action_cb;
453 info.timeout = HZ/2;
433 return simplelist_show_list(&info); 454 return simplelist_show_list(&info);
434} 455}
435 456
diff --git a/apps/dsp.c b/apps/dsp.c
index a728dd75ea..167c043427 100644
--- a/apps/dsp.c
+++ b/apps/dsp.c
@@ -318,30 +318,50 @@ static void tdspeed_setup(struct dsp_config *dspc)
318 resample_buf = big_resample_buf; 318 resample_buf = big_resample_buf;
319} 319}
320 320
321
322static int move_callback(int handle, void* current, void* new)
323{
324 /* TODO */
325 (void)handle;(void)current;;
326 big_sample_buf = new;
327 return BUFLIB_CB_OK;
328}
329
330static struct buflib_callbacks ops = {
331 .move_callback = move_callback,
332 .shrink_callback = NULL,
333};
334
335
321void dsp_timestretch_enable(bool enabled) 336void dsp_timestretch_enable(bool enabled)
322{ 337{
323 /* Hook to set up timestretch buffer on first call to settings_apply() */ 338 /* Hook to set up timestretch buffer on first call to settings_apply() */
324 if (big_sample_buf_count < 0) /* Only do something on first call */ 339 static int handle;
340 if (enabled)
325 { 341 {
326 if (enabled) 342 if (big_sample_buf_count > 0)
327 { 343 return; /* already allocated and enabled */
328 int handle; 344 /* Set up timestretch buffers */
329 /* Set up timestretch buffers */ 345 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO;
330 big_sample_buf_count = SMALL_SAMPLE_BUF_COUNT * RESAMPLE_RATIO; 346 big_sample_buf = small_resample_buf;
331 big_sample_buf = small_resample_buf; 347 handle = core_alloc_ex("resample buf",
332 handle = core_alloc("resample buf", 348 big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t), &ops);
333 big_sample_buf_count * RESAMPLE_RATIO * sizeof(int32_t)); 349 if (handle > 0)
334 if (handle > 0) 350 { /* success, now setup tdspeed */
335 big_resample_buf = core_get_data(handle); 351 big_resample_buf = core_get_data(handle);
336 else 352 tdspeed_init();
337 big_sample_buf_count = 0; 353 tdspeed_setup(&AUDIO_DSP);
338 } 354 }
339 else 355 }
340 { 356 if (!enabled || (handle <= 0)) /* disable */
341 /* Not enabled at startup, "big" buffers will never be available */ 357 {
342 big_sample_buf_count = 0; 358 dsp_set_timestretch(PITCH_SPEED_100);
343 } 359 tdspeed_finish();
344 tdspeed_setup(&AUDIO_DSP); 360 if (handle > 0)
361 core_free(handle);
362 handle = 0;
363 big_sample_buf = NULL;
364 big_sample_buf_count = 0;
345 } 365 }
346} 366}
347 367
@@ -1211,7 +1231,7 @@ int dsp_callback(int msg, intptr_t param)
1211 */ 1231 */
1212int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count) 1232int dsp_process(struct dsp_config *dsp, char *dst, const char *src[], int count)
1213{ 1233{
1214 int32_t *tmp[2]; 1234 static int32_t *tmp[2]; /* tdspeed_doit() needs it static */
1215 static long last_yield; 1235 static long last_yield;
1216 long tick; 1236 long tick;
1217 int written = 0; 1237 int written = 0;
diff --git a/apps/filetree.c b/apps/filetree.c
index 1aee80b6b2..35bb2a8fd0 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -29,6 +29,7 @@
29#include <limits.h> 29#include <limits.h>
30#include "bookmark.h" 30#include "bookmark.h"
31#include "tree.h" 31#include "tree.h"
32#include "core_alloc.h"
32#include "settings.h" 33#include "settings.h"
33#include "filetypes.h" 34#include "filetypes.h"
34#include "talk.h" 35#include "talk.h"
@@ -60,7 +61,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
60 int i; 61 int i;
61 int start=start_index; 62 int start=start_index;
62 63
63 struct entry *entries = c->cache.entries; 64 tree_lock_cache(c);
65 struct entry *entries = tree_get_entries(c);
64 66
65 for(i = 0;i < c->filesindir;i++) 67 for(i = 0;i < c->filesindir;i++)
66 { 68 {
@@ -77,6 +79,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
77 } 79 }
78 } 80 }
79 81
82 tree_unlock_cache(c);
83
80 return start_index; 84 return start_index;
81} 85}
82 86
@@ -127,13 +131,15 @@ static void check_file_thumbnails(struct tree_context* c)
127{ 131{
128 int i; 132 int i;
129 struct dirent *entry; 133 struct dirent *entry;
130 struct entry* entries = c->cache.entries; 134 struct entry* entries;
131 DIR *dir; 135 DIR *dir;
132 136
133 dir = opendir(c->currdir); 137 dir = opendir(c->currdir);
134 if(!dir) 138 if(!dir)
135 return; 139 return;
136 /* mark all files as non talking, except the .talk ones */ 140 /* mark all files as non talking, except the .talk ones */
141 entries = tree_get_entries(c);
142 tree_lock_cache(c);
137 for (i=0; i < c->filesindir; i++) 143 for (i=0; i < c->filesindir; i++)
138 { 144 {
139 if (entries[i].attr & ATTR_DIRECTORY) 145 if (entries[i].attr & ATTR_DIRECTORY)
@@ -177,6 +183,7 @@ static void check_file_thumbnails(struct tree_context* c)
177 } 183 }
178 } 184 }
179 } 185 }
186 tree_unlock_cache(c);
180 closedir(dir); 187 closedir(dir);
181} 188}
182 189
@@ -287,11 +294,11 @@ int ft_load(struct tree_context* c, const char* tempdir)
287 c->dirsindir = 0; 294 c->dirsindir = 0;
288 c->dirfull = false; 295 c->dirfull = false;
289 296
297 tree_lock_cache(c);
290 while ((entry = readdir(dir))) { 298 while ((entry = readdir(dir))) {
291 int len; 299 int len;
292 struct dirinfo info; 300 struct dirinfo info;
293 struct entry* table = c->cache.entries; 301 struct entry* dptr = tree_get_entry_at(c, files_in_dir);
294 struct entry* dptr = &table[files_in_dir];
295 if (!entry) 302 if (!entry)
296 break; 303 break;
297 304
@@ -369,7 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir)
369 376
370 ++files_in_dir; 377 ++files_in_dir;
371 378
372 dptr->name = &c->cache.name_buffer[name_buffer_used]; 379 dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used;
373 dptr->time_write = 380 dptr->time_write =
374 (long)info.wrtdate<<16 | 381 (long)info.wrtdate<<16 |
375 (long)info.wrttime; /* in one # */ 382 (long)info.wrttime; /* in one # */
@@ -384,13 +391,14 @@ int ft_load(struct tree_context* c, const char* tempdir)
384 closedir(dir); 391 closedir(dir);
385 392
386 compare_sort_dir = c->sort_dir; 393 compare_sort_dir = c->sort_dir;
387 qsort(c->cache.entries, files_in_dir, sizeof(struct entry), compare); 394 qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare);
388 395
389 /* If thumbnail talking is enabled, make an extra run to mark files with 396 /* If thumbnail talking is enabled, make an extra run to mark files with
390 associated thumbnails, so we don't do unsuccessful spinups later. */ 397 associated thumbnails, so we don't do unsuccessful spinups later. */
391 if (global_settings.talk_file_clip) 398 if (global_settings.talk_file_clip)
392 check_file_thumbnails(c); /* map .talk to ours */ 399 check_file_thumbnails(c); /* map .talk to ours */
393 400
401 tree_unlock_cache(c);
394 return 0; 402 return 0;
395} 403}
396#ifdef HAVE_LCD_BITMAP 404#ifdef HAVE_LCD_BITMAP
@@ -424,15 +432,15 @@ int ft_enter(struct tree_context* c)
424{ 432{
425 int rc = GO_TO_PREVIOUS; 433 int rc = GO_TO_PREVIOUS;
426 char buf[MAX_PATH]; 434 char buf[MAX_PATH];
427 struct entry* table = c->cache.entries; 435 struct entry* file = tree_get_entry_at(c, c->selected_item);
428 struct entry *file = &table[c->selected_item]; 436 int file_attr = file->attr;
429 437
430 if (c->currdir[1]) 438 if (c->currdir[1])
431 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); 439 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
432 else 440 else
433 snprintf(buf,sizeof(buf),"/%s",file->name); 441 snprintf(buf,sizeof(buf),"/%s",file->name);
434 442
435 if (file->attr & ATTR_DIRECTORY) { 443 if (file_attr & ATTR_DIRECTORY) {
436 memcpy(c->currdir, buf, sizeof(c->currdir)); 444 memcpy(c->currdir, buf, sizeof(c->currdir));
437 if ( c->dirlevel < MAX_DIR_LEVELS ) 445 if ( c->dirlevel < MAX_DIR_LEVELS )
438 c->selected_item_history[c->dirlevel] = c->selected_item; 446 c->selected_item_history[c->dirlevel] = c->selected_item;
@@ -444,7 +452,7 @@ int ft_enter(struct tree_context* c)
444 bool play = false; 452 bool play = false;
445 int start_index=0; 453 int start_index=0;
446 454
447 switch ( file->attr & FILE_ATTR_MASK ) { 455 switch ( file_attr & FILE_ATTR_MASK ) {
448 case FILE_ATTR_M3U: 456 case FILE_ATTR_M3U:
449 if (!bookmark_autoload(buf)) 457 if (!bookmark_autoload(buf))
450 playlist_viewer_ex(buf); 458 playlist_viewer_ex(buf);
@@ -612,7 +620,7 @@ int ft_enter(struct tree_context* c)
612 char *plugin = buf, *argument = NULL, lua_path[MAX_PATH]; 620 char *plugin = buf, *argument = NULL, lua_path[MAX_PATH];
613 int ret; 621 int ret;
614 622
615 if ((file->attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) { 623 if ((file_attr & FILE_ATTR_MASK) == FILE_ATTR_LUA) {
616 snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */ 624 snprintf(lua_path, sizeof(lua_path)-1, "%s/lua.rock", VIEWERS_DIR); /* Use a #define here ? */
617 plugin = lua_path; 625 plugin = lua_path;
618 argument = buf; 626 argument = buf;
@@ -658,6 +666,7 @@ int ft_enter(struct tree_context* c)
658 break; 666 break;
659 } 667 }
660 668
669 struct entry* file = tree_get_entry_at(c, c->selected_item);
661 plugin = filetype_get_plugin(file); 670 plugin = filetype_get_plugin(file);
662 if (plugin) 671 if (plugin)
663 { 672 {
diff --git a/apps/filetypes.c b/apps/filetypes.c
index c52c734a1d..942ff329fe 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -184,6 +184,26 @@ static unsigned char highest_attr = 0;
184static int viewer_count = 0; 184static int viewer_count = 0;
185 185
186static int strdup_handle, strdup_bufsize, strdup_cur_idx; 186static int strdup_handle, strdup_bufsize, strdup_cur_idx;
187static int move_callback(int handle, void* current, void* new)
188{
189 /*could compare to strdup_handle, but ops is only used once */
190 (void)handle;
191 size_t diff = new - current;
192#define FIX_PTR(x) \
193 { if ((void*)x > current && (void*)x < (current+strdup_bufsize)) x+= diff; }
194 for(int i = 0; i < filetype_count; i++)
195 {
196 FIX_PTR(filetypes[i].extension);
197 FIX_PTR(filetypes[i].plugin);
198 }
199 return BUFLIB_CB_OK;
200}
201
202static struct buflib_callbacks ops = {
203 .move_callback = move_callback,
204 .shrink_callback = NULL,
205};
206
187static char *filetypes_strdup(char* string) 207static char *filetypes_strdup(char* string)
188{ 208{
189 char *buffer = core_get_data(strdup_handle) + strdup_cur_idx; 209 char *buffer = core_get_data(strdup_handle) + strdup_cur_idx;
@@ -323,7 +343,7 @@ void filetype_init(void)
323 return; 343 return;
324 344
325 strdup_bufsize = filesize(fd); 345 strdup_bufsize = filesize(fd);
326 strdup_handle = core_alloc("filetypes", strdup_bufsize); 346 strdup_handle = core_alloc_ex("filetypes", strdup_bufsize, &ops);
327 if (strdup_handle <= 0) 347 if (strdup_handle <= 0)
328 return; 348 return;
329 read_builtin_types(); 349 read_builtin_types();
diff --git a/apps/main.c b/apps/main.c
index cc9c9e8d8e..07a8bba44c 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -403,9 +403,6 @@ static void init(void)
403#endif /* CONFIG_CODEC != SWCODEC */ 403#endif /* CONFIG_CODEC != SWCODEC */
404 404
405 scrobbler_init(); 405 scrobbler_init();
406#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN)
407 tdspeed_init();
408#endif /* CONFIG_CODEC == SWCODEC */
409 406
410 audio_init(); 407 audio_init();
411 408
@@ -659,9 +656,6 @@ static void init(void)
659 tree_mem_init(); 656 tree_mem_init();
660 filetype_init(); 657 filetype_init();
661 scrobbler_init(); 658 scrobbler_init();
662#if CONFIG_CODEC == SWCODEC && defined (HAVE_PITCHSCREEN)
663 tdspeed_init();
664#endif /* CONFIG_CODEC == SWCODEC */
665 theme_init_buffer(); 659 theme_init_buffer();
666 660
667#if CONFIG_CODEC != SWCODEC 661#if CONFIG_CODEC != SWCODEC
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 1b1a13a6a5..a219373a8b 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -142,7 +142,7 @@ static int audioscrobbler_callback(int action,const struct menu_item_ex *this_it
142 { 142 {
143 case ACTION_EXIT_MENUITEM: /* on exit */ 143 case ACTION_EXIT_MENUITEM: /* on exit */
144 if (!scrobbler_is_enabled() && global_settings.audioscrobbler) 144 if (!scrobbler_is_enabled() && global_settings.audioscrobbler)
145 splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); 145 scrobbler_init();
146 146
147 if(scrobbler_is_enabled() && !global_settings.audioscrobbler) 147 if(scrobbler_is_enabled() && !global_settings.audioscrobbler)
148 scrobbler_shutdown(); 148 scrobbler_shutdown();
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 595f943545..0b1413e195 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -39,7 +39,7 @@
39#include "mp3_playback.h" 39#include "mp3_playback.h"
40#include "talk.h" 40#include "talk.h"
41#include "sound.h" 41#include "sound.h"
42#include "bitswap.h" 42#include "system.h"
43#include "appevents.h" 43#include "appevents.h"
44#include "playlist.h" 44#include "playlist.h"
45#include "cuesheet.h" 45#include "cuesheet.h"
@@ -140,6 +140,7 @@ static struct cuesheet *curr_cuesheet = NULL;
140static bool checked_for_cuesheet = false; 140static bool checked_for_cuesheet = false;
141 141
142static const char mpeg_thread_name[] = "mpeg"; 142static const char mpeg_thread_name[] = "mpeg";
143static unsigned int audio_thread_id;
143static unsigned int mpeg_errno; 144static unsigned int mpeg_errno;
144 145
145static bool playing = false; /* We are playing an MP3 stream */ 146static bool playing = false; /* We are playing an MP3 stream */
@@ -492,20 +493,81 @@ unsigned long mpeg_get_last_header(void)
492#endif /* !SIMULATOR */ 493#endif /* !SIMULATOR */
493} 494}
494 495
495/* Buffer must not move. And not shrink for now */ 496static void do_stop(void)
496static struct buflib_callbacks ops = { NULL, NULL }; 497{
498 is_playing = false;
499 paused = false;
500
501#ifndef SIMULATOR
502 if (playing)
503 playlist_update_resume_info(audio_current_track());
504
505 stop_playing();
506 mpeg_stop_done = true;
507#else
508 playing = false;
509#endif
510}
511
512static void audio_reset_buffer_noalloc(void* buf, size_t bufsize);
513/* Buffer must not move. */
514static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
515{
516 long offset = audio_current_track()->offset;
517 int status = audio_status();
518 /* TODO: Do it without stopping playback, if possible */
519 /* don't call audio_hard_stop() as it frees this handle */
520 if (thread_self() == audio_thread_id)
521 { /* inline case MPEG_STOP (audio_stop()) response
522 * if we're in the audio thread since audio_stop() otherwise deadlocks */
523 do_stop();
524 }
525 else
526 audio_stop();
527 talk_buffer_steal(); /* we obtain control over the buffer */
528
529 /* we should be free to change the buffer now */
530 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
531 ssize_t size = (ssize_t)old_size - wanted_size;
532 switch (hints & BUFLIB_SHRINK_POS_MASK)
533 {
534 case BUFLIB_SHRINK_POS_BACK:
535 core_shrink(handle, start, size);
536 audio_reset_buffer_noalloc(start, size);
537 break;
538 case BUFLIB_SHRINK_POS_FRONT:
539 core_shrink(handle, start + wanted_size, size);
540 audio_reset_buffer_noalloc(start + wanted_size, size);
541 break;
542 }
543 if (!(status & AUDIO_STATUS_PAUSE))
544 { /* safe to call even from the audio thread (due to queue_post()) */
545 audio_play(offset);
546 }
547
548 return BUFLIB_CB_OK;
549}
550
551static struct buflib_callbacks ops = {
552 .move_callback = NULL,
553 .shrink_callback = shrink_callback,
554};
555
497unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) 556unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size)
498{ 557{
499 (void)talk_buf; /* always grab the voice buffer for now */ 558 (void)talk_buf; /* always grab the voice buffer for now */
500 559
501 if (buffer_size) /* special case for talk_init() */ 560 if (buffer_size) /* special case for talk_init() */
561 audio_hard_stop();
562
563 if (!audiobuf_handle)
502 { 564 {
503 size_t bufsize; 565 size_t bufsize;
504 audio_hard_stop();
505 /* audio_hard_stop() frees audiobuf, so re-aquire */ 566 /* audio_hard_stop() frees audiobuf, so re-aquire */
506 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); 567 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
507 audiobuflen = bufsize; 568 audiobuflen = bufsize;
508 *buffer_size = audiobuflen; 569 if (buffer_size)
570 *buffer_size = audiobuflen;
509 } 571 }
510 mpeg_audiobuf = core_get_data(audiobuf_handle); 572 mpeg_audiobuf = core_get_data(audiobuf_handle);
511 573
@@ -1314,15 +1376,7 @@ static void mpeg_thread(void)
1314 break; 1376 break;
1315 1377
1316 case MPEG_STOP: 1378 case MPEG_STOP:
1317 DEBUGF("MPEG_STOP\n"); 1379 do_stop();
1318 is_playing = false;
1319 paused = false;
1320
1321 if (playing)
1322 playlist_update_resume_info(audio_current_track());
1323
1324 stop_playing();
1325 mpeg_stop_done = true;
1326 break; 1380 break;
1327 1381
1328 case MPEG_PAUSE: 1382 case MPEG_PAUSE:
@@ -2679,19 +2733,29 @@ size_t audio_buffer_available(void)
2679 return core_available(); 2733 return core_available();
2680} 2734}
2681 2735
2682static void audio_reset_buffer(void) 2736static void audio_reset_buffer_noalloc(void* buf, size_t bufsize)
2683{ 2737{
2684 talk_buffer_steal(); /* will use the mp3 buffer */ 2738 talk_buffer_steal(); /* will use the mp3 buffer */
2739 mpeg_audiobuf = buf;
2740 audiobuflen = bufsize;
2741 if (global_settings.cuesheet)
2742 { /* enable cuesheet support */
2743 curr_cuesheet = (struct cuesheet*)mpeg_audiobuf;
2744 mpeg_audiobuf = SKIPBYTES(mpeg_audiobuf, sizeof(struct cuesheet));
2745 audiobuflen -= sizeof(struct cuesheet);
2746 }
2747 talkbuf_init(mpeg_audiobuf);
2748}
2749
2750static void audio_reset_buffer(void)
2751{
2752 size_t bufsize = audiobuflen;
2685 2753
2686 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */ 2754 /* alloc buffer if it's was never allocated or freed by audio_hard_stop() */
2687 if (!audiobuf_handle) 2755 if (!audiobuf_handle)
2688 {
2689 size_t bufsize; /* dont break strict-aliasing */
2690 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops); 2756 audiobuf_handle = core_alloc_maximum("audiobuf", &bufsize, &ops);
2691 mpeg_audiobuf = core_get_data(audiobuf_handle); 2757
2692 audiobuflen = bufsize; 2758 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
2693 }
2694 talkbuf_init(mpeg_audiobuf);
2695} 2759}
2696 2760
2697void audio_play(long offset) 2761void audio_play(long offset)
@@ -2923,13 +2987,6 @@ static void mpeg_thread(void)
2923void audio_init(void) 2987void audio_init(void)
2924{ 2988{
2925 mpeg_errno = 0; 2989 mpeg_errno = 0;
2926 /* cuesheet support */
2927 if (global_settings.cuesheet)
2928 {
2929 int handle = core_alloc("cuesheet", sizeof(struct cuesheet));
2930 if (handle > 0)
2931 curr_cuesheet = core_get_data(handle);
2932 }
2933 2990
2934 talk_init(); 2991 talk_init();
2935 audio_reset_buffer(); 2992 audio_reset_buffer();
@@ -2937,10 +2994,10 @@ void audio_init(void)
2937#ifndef SIMULATOR 2994#ifndef SIMULATOR
2938 queue_init(&mpeg_queue, true); 2995 queue_init(&mpeg_queue, true);
2939#endif /* !SIMULATOR */ 2996#endif /* !SIMULATOR */
2940 create_thread(mpeg_thread, mpeg_stack, 2997 audio_thread_id = create_thread(mpeg_thread, mpeg_stack,
2941 sizeof(mpeg_stack), 0, mpeg_thread_name 2998 sizeof(mpeg_stack), 0, mpeg_thread_name
2942 IF_PRIO(, PRIORITY_SYSTEM) 2999 IF_PRIO(, PRIORITY_SYSTEM)
2943 IF_COP(, CPU)); 3000 IF_COP(, CPU));
2944 3001
2945 memset(trackdata, 0, sizeof(trackdata)); 3002 memset(trackdata, 0, sizeof(trackdata));
2946 3003
diff --git a/apps/playback.c b/apps/playback.c
index e35d652ffb..af077e639a 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -732,8 +732,6 @@ static void scratch_mem_init(void *mem)
732 } 732 }
733} 733}
734 734
735/* Buffer must not move. And not shrink for now */
736static struct buflib_callbacks ops = { NULL, NULL };
737static int audiobuf_handle; 735static int audiobuf_handle;
738static size_t filebuflen; 736static size_t filebuflen;
739 737
@@ -744,8 +742,9 @@ size_t audio_buffer_available(void)
744 return core_available(); 742 return core_available();
745} 743}
746 744
747/* Set up the audio buffer for playback */ 745/* Set up the audio buffer for playback
748static void audio_reset_buffer(void) 746 * filebuflen must be pre-initialized with the maximum size */
747static void audio_reset_buffer_noalloc(void* filebuf)
749{ 748{
750 /* 749 /*
751 * Layout audio buffer as follows: 750 * Layout audio buffer as follows:
@@ -761,11 +760,6 @@ static void audio_reset_buffer(void)
761 760
762 /* Initially set up file buffer as all space available */ 761 /* Initially set up file buffer as all space available */
763 size_t allocsize; 762 size_t allocsize;
764 if (audiobuf_handle > 0)
765 audiobuf_handle = core_free(audiobuf_handle);
766
767 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
768 unsigned char *filebuf = core_get_data(audiobuf_handle);
769 763
770 /* Subtract whatever voice needs */ 764 /* Subtract whatever voice needs */
771 allocsize = talkbuf_init(filebuf); 765 allocsize = talkbuf_init(filebuf);
@@ -830,6 +824,70 @@ bufpanic:
830 panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); 824 panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen);
831} 825}
832 826
827
828/* Buffer must not move. */
829static int shrink_callback(int handle, unsigned hints, void* start, size_t old_size)
830{
831 long offset = audio_current_track()->offset;
832 int status = audio_status();
833 /* TODO: Do it without stopping playback, if possible */
834 /* don't call audio_hard_stop() as it frees this handle */
835 if (thread_self() == audio_thread_id)
836 { /* inline case Q_AUDIO_STOP (audio_hard_stop() response
837 * if we're in the audio thread */
838 audio_stop_playback();
839 queue_clear(&audio_queue);
840 }
841 else
842 audio_queue_send(Q_AUDIO_STOP, 1);
843#ifdef PLAYBACK_VOICE
844 voice_stop();
845#endif
846 /* we should be free to change the buffer now */
847 size_t wanted_size = (hints & BUFLIB_SHRINK_SIZE_MASK);
848 ssize_t size = (ssize_t)old_size - wanted_size;
849 /* set final buffer size before calling audio_reset_buffer_noalloc() */
850 filebuflen = size;
851 switch (hints & BUFLIB_SHRINK_POS_MASK)
852 {
853 case BUFLIB_SHRINK_POS_BACK:
854 core_shrink(handle, start, size);
855 audio_reset_buffer_noalloc(start);
856 break;
857 case BUFLIB_SHRINK_POS_FRONT:
858 core_shrink(handle, start + wanted_size, size);
859 audio_reset_buffer_noalloc(start + wanted_size);
860 break;
861 }
862 if ((status & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
863 {
864 if (thread_self() == audio_thread_id)
865 audio_start_playback(offset, 0); /* inline Q_AUDIO_PLAY */
866 else
867 audio_play(offset);
868 }
869
870 return BUFLIB_CB_OK;
871}
872
873static struct buflib_callbacks ops = {
874 .move_callback = NULL,
875 .shrink_callback = shrink_callback,
876};
877
878static void audio_reset_buffer(void)
879{
880 if (audiobuf_handle > 0)
881 {
882 core_free(audiobuf_handle);
883 audiobuf_handle = 0;
884 }
885 audiobuf_handle = core_alloc_maximum("audiobuf", &filebuflen, &ops);
886 unsigned char *filebuf = core_get_data(audiobuf_handle);
887
888 audio_reset_buffer_noalloc(filebuf);
889}
890
833/* Set the buffer margin to begin rebuffering when 'seconds' from empty */ 891/* Set the buffer margin to begin rebuffering when 'seconds' from empty */
834static void audio_update_filebuf_watermark(int seconds) 892static void audio_update_filebuf_watermark(int seconds)
835{ 893{
diff --git a/apps/playlist.c b/apps/playlist.c
index 77d8141af8..f6dda977f4 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -993,14 +993,14 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
993 unsigned int current = playlist->indices[playlist->index]; 993 unsigned int current = playlist->indices[playlist->index];
994 994
995 if (playlist->amount > 0) 995 if (playlist->amount > 0)
996 qsort(playlist->indices, playlist->amount, 996 qsort((void*)playlist->indices, playlist->amount,
997 sizeof(playlist->indices[0]), compare); 997 sizeof(playlist->indices[0]), compare);
998 998
999#ifdef HAVE_DIRCACHE 999#ifdef HAVE_DIRCACHE
1000 /** We need to re-check the song names from disk because qsort can't 1000 /** We need to re-check the song names from disk because qsort can't
1001 * sort two arrays at once :/ 1001 * sort two arrays at once :/
1002 * FIXME: Please implement a better way to do this. */ 1002 * FIXME: Please implement a better way to do this. */
1003 memset(playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int)); 1003 memset((void*)playlist->filenames, 0xff, playlist->max_playlist_size * sizeof(int));
1004 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); 1004 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1005#endif 1005#endif
1006 1006
@@ -1378,7 +1378,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek,
1378 1378
1379 if (playlist->in_ram && !control_file && max < 0) 1379 if (playlist->in_ram && !control_file && max < 0)
1380 { 1380 {
1381 max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf)); 1381 max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
1382 } 1382 }
1383 else if (max < 0) 1383 else if (max < 0)
1384 { 1384 {
@@ -1534,9 +1534,10 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion)
1534 break; 1534 break;
1535 } 1535 }
1536 1536
1537 files = tc->cache.entries; 1537 files = tree_get_entries(tc);
1538 num_files = tc->filesindir; 1538 num_files = tc->filesindir;
1539 1539
1540 tree_lock_cache(tc);
1540 for (i=0; i<num_files; i++) 1541 for (i=0; i<num_files; i++)
1541 { 1542 {
1542 /* user abort */ 1543 /* user abort */
@@ -1562,6 +1563,7 @@ static int get_next_dir(char *dir, bool is_forward, bool recursion)
1562 start_dir = NULL; 1563 start_dir = NULL;
1563 } 1564 }
1564 } 1565 }
1566 tree_unlock_cache(tc);
1565 1567
1566 if (!exit) 1568 if (!exit)
1567 { 1569 {
@@ -1612,7 +1614,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1612 return -2; 1614 return -2;
1613 } 1615 }
1614 1616
1615 files = tc->cache.entries; 1617 files = tree_get_entries(tc);
1616 num_files = tc->filesindir; 1618 num_files = tc->filesindir;
1617 1619
1618 for (i=0; i<num_files; i++) 1620 for (i=0; i<num_files; i++)
@@ -1629,6 +1631,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1629 if (has_music) 1631 if (has_music)
1630 return 0; 1632 return 0;
1631 1633
1634 tree_lock_cache(tc);
1632 if (has_subdir && recurse) 1635 if (has_subdir && recurse)
1633 { 1636 {
1634 for (i=0; i<num_files; i++) 1637 for (i=0; i<num_files; i++)
@@ -1647,6 +1650,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1647 } 1650 }
1648 } 1651 }
1649 } 1652 }
1653 tree_unlock_cache(tc);
1650 1654
1651 if (result < 0) 1655 if (result < 0)
1652 { 1656 {
@@ -1925,6 +1929,31 @@ static int rotate_index(const struct playlist_info* playlist, int index)
1925} 1929}
1926 1930
1927/* 1931/*
1932 * Need no movement protection since all 3 allocations are not passed to
1933 * other functions which can yield().
1934 */
1935static int move_callback(int handle, void* current, void* new)
1936{
1937 (void)handle;
1938 struct playlist_info* playlist = &current_playlist;
1939 if (current == playlist->indices)
1940 playlist->indices = new;
1941 else if (current == playlist->filenames)
1942 playlist->filenames = new;
1943 /* buffer can possibly point to a new buffer temporarily (playlist_save()).
1944 * just don't overwrite the pointer to that temp buffer */
1945 else if (current == playlist->buffer)
1946 playlist->buffer = new;
1947
1948 return BUFLIB_CB_OK;
1949}
1950
1951
1952static struct buflib_callbacks ops = {
1953 .move_callback = move_callback,
1954 .shrink_callback = NULL,
1955};
1956/*
1928 * Initialize playlist entries at startup 1957 * Initialize playlist entries at startup
1929 */ 1958 */
1930void playlist_init(void) 1959void playlist_init(void)
@@ -1941,20 +1970,23 @@ void playlist_init(void)
1941 playlist->fd = -1; 1970 playlist->fd = -1;
1942 playlist->control_fd = -1; 1971 playlist->control_fd = -1;
1943 playlist->max_playlist_size = global_settings.max_files_in_playlist; 1972 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1944 handle = core_alloc("playlist idx", playlist->max_playlist_size * sizeof(int)); 1973 handle = core_alloc_ex("playlist idx",
1974 playlist->max_playlist_size * sizeof(int), &ops);
1945 playlist->indices = core_get_data(handle); 1975 playlist->indices = core_get_data(handle);
1946 playlist->buffer_size = 1976 playlist->buffer_size =
1947 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; 1977 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1948 handle = core_alloc("playlist buf", playlist->buffer_size); 1978 handle = core_alloc_ex("playlist buf",
1979 playlist->buffer_size, &ops);
1949 playlist->buffer = core_get_data(handle); 1980 playlist->buffer = core_get_data(handle);
1950 playlist->control_mutex = &current_playlist_mutex; 1981 playlist->control_mutex = &current_playlist_mutex;
1951 1982
1952 empty_playlist(playlist, true); 1983 empty_playlist(playlist, true);
1953 1984
1954#ifdef HAVE_DIRCACHE 1985#ifdef HAVE_DIRCACHE
1955 handle = core_alloc("playlist dc", playlist->max_playlist_size * sizeof(int)); 1986 handle = core_alloc_ex("playlist dc",
1987 playlist->max_playlist_size * sizeof(int), &ops);
1956 playlist->filenames = core_get_data(handle); 1988 playlist->filenames = core_get_data(handle);
1957 memset(playlist->filenames, 0xff, 1989 memset((void*)playlist->filenames, 0xff,
1958 playlist->max_playlist_size * sizeof(int)); 1990 playlist->max_playlist_size * sizeof(int));
1959 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack), 1991 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1960 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND) 1992 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
@@ -2404,7 +2436,7 @@ int playlist_add(const char *filename)
2404#endif 2436#endif
2405 playlist->amount++; 2437 playlist->amount++;
2406 2438
2407 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename); 2439 strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
2408 playlist->buffer_end_pos += len; 2440 playlist->buffer_end_pos += len;
2409 playlist->buffer[playlist->buffer_end_pos++] = '\0'; 2441 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2410 2442
@@ -2731,6 +2763,7 @@ int playlist_create_ex(struct playlist_info* playlist,
2731 } 2763 }
2732 2764
2733 playlist->buffer_size = 0; 2765 playlist->buffer_size = 0;
2766 playlist->buffer_handle = -1;
2734 playlist->buffer = NULL; 2767 playlist->buffer = NULL;
2735 playlist->control_mutex = &created_playlist_mutex; 2768 playlist->control_mutex = &created_playlist_mutex;
2736 } 2769 }
@@ -2779,10 +2812,10 @@ int playlist_set_current(struct playlist_info* playlist)
2779 2812
2780 if (playlist->indices && playlist->indices != current_playlist.indices) 2813 if (playlist->indices && playlist->indices != current_playlist.indices)
2781 { 2814 {
2782 memcpy(current_playlist.indices, playlist->indices, 2815 memcpy((void*)current_playlist.indices, (void*)playlist->indices,
2783 playlist->max_playlist_size*sizeof(int)); 2816 playlist->max_playlist_size*sizeof(int));
2784#ifdef HAVE_DIRCACHE 2817#ifdef HAVE_DIRCACHE
2785 memcpy(current_playlist.filenames, playlist->filenames, 2818 memcpy((void*)current_playlist.filenames, (void*)playlist->filenames,
2786 playlist->max_playlist_size*sizeof(int)); 2819 playlist->max_playlist_size*sizeof(int));
2787#endif 2820#endif
2788 } 2821 }
@@ -3358,6 +3391,7 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3358 char tmp_buf[MAX_PATH+1]; 3391 char tmp_buf[MAX_PATH+1];
3359 int result = 0; 3392 int result = 0;
3360 bool overwrite_current = false; 3393 bool overwrite_current = false;
3394 int old_handle = -1;
3361 char* old_buffer = NULL; 3395 char* old_buffer = NULL;
3362 size_t old_buffer_size = 0; 3396 size_t old_buffer_size = 0;
3363 3397
@@ -3380,15 +3414,16 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3380 { 3414 {
3381 /* not enough buffer space to store updated indices */ 3415 /* not enough buffer space to store updated indices */
3382 /* Try to get a buffer */ 3416 /* Try to get a buffer */
3383 old_buffer = playlist->buffer; 3417 old_handle = playlist->buffer_handle;
3418 /* can ignore volatile here, because core_get_data() is called later */
3419 old_buffer = (char*)playlist->buffer;
3384 old_buffer_size = playlist->buffer_size; 3420 old_buffer_size = playlist->buffer_size;
3385 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size); 3421 playlist->buffer = plugin_get_buffer((size_t*)&playlist->buffer_size);
3386 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int))) 3422 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3387 { 3423 {
3388 playlist->buffer = old_buffer;
3389 playlist->buffer_size = old_buffer_size;
3390 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); 3424 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3391 return -1; 3425 result = -1;
3426 goto reset_old_buffer;
3392 } 3427 }
3393 } 3428 }
3394 3429
@@ -3409,12 +3444,8 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3409 if (fd < 0) 3444 if (fd < 0)
3410 { 3445 {
3411 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR)); 3446 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3412 if (old_buffer != NULL) 3447 result = -1;
3413 { 3448 goto reset_old_buffer;
3414 playlist->buffer = old_buffer;
3415 playlist->buffer_size = old_buffer_size;
3416 }
3417 return -1;
3418 } 3449 }
3419 3450
3420 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false); 3451 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
@@ -3514,11 +3545,12 @@ int playlist_save(struct playlist_info* playlist, char *filename)
3514 } 3545 }
3515 3546
3516 cpu_boost(false); 3547 cpu_boost(false);
3517 if (old_buffer != NULL) 3548
3518 { 3549reset_old_buffer:
3519 playlist->buffer = old_buffer; 3550 if (old_handle > 0)
3520 playlist->buffer_size = old_buffer_size; 3551 old_buffer = core_get_data(old_handle);
3521 } 3552 playlist->buffer = old_buffer;
3553 playlist->buffer_size = old_buffer_size;
3522 3554
3523 return result; 3555 return result;
3524} 3556}
@@ -3534,9 +3566,9 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3534 char buf[MAX_PATH+1]; 3566 char buf[MAX_PATH+1];
3535 int result = 0; 3567 int result = 0;
3536 int num_files = 0; 3568 int num_files = 0;
3537 int i; 3569 int i;;
3538 struct entry *files;
3539 struct tree_context* tc = tree_get_context(); 3570 struct tree_context* tc = tree_get_context();
3571 struct tree_cache* cache = &tc->cache;
3540 int old_dirfilter = *(tc->dirfilter); 3572 int old_dirfilter = *(tc->dirfilter);
3541 3573
3542 if (!callback) 3574 if (!callback)
@@ -3552,7 +3584,6 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3552 return -1; 3584 return -1;
3553 } 3585 }
3554 3586
3555 files = tc->cache.entries;
3556 num_files = tc->filesindir; 3587 num_files = tc->filesindir;
3557 3588
3558 /* we've overwritten the dircache so tree browser will need to be 3589 /* we've overwritten the dircache so tree browser will need to be
@@ -3568,6 +3599,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3568 break; 3599 break;
3569 } 3600 }
3570 3601
3602 struct entry *files = core_get_data(cache->entries_handle);
3571 if (files[i].attr & ATTR_DIRECTORY) 3603 if (files[i].attr & ATTR_DIRECTORY)
3572 { 3604 {
3573 if (recurse) 3605 if (recurse)
@@ -3586,8 +3618,7 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
3586 result = -1; 3618 result = -1;
3587 break; 3619 break;
3588 } 3620 }
3589 3621
3590 files = tc->cache.entries;
3591 num_files = tc->filesindir; 3622 num_files = tc->filesindir;
3592 if (!num_files) 3623 if (!num_files)
3593 { 3624 {
diff --git a/apps/playlist.h b/apps/playlist.h
index f14b5c6460..6dd5535df1 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -80,15 +80,16 @@ struct playlist_info
80 int control_fd; /* descriptor of the open control file */ 80 int control_fd; /* descriptor of the open control file */
81 bool control_created; /* has control file been created? */ 81 bool control_created; /* has control file been created? */
82 int dirlen; /* Length of the path to the playlist file */ 82 int dirlen; /* Length of the path to the playlist file */
83 unsigned long *indices; /* array of indices */ 83 volatile unsigned long *indices; /* array of indices */
84 int *filenames; /* Array of dircache indices */ 84 volatile int *filenames; /* Array of dircache indices */
85 int max_playlist_size; /* Max number of files in playlist. Mirror of 85 int max_playlist_size; /* Max number of files in playlist. Mirror of
86 global_settings.max_files_in_playlist */ 86 global_settings.max_files_in_playlist */
87 bool in_ram; /* playlist stored in ram (dirplay) */ 87 bool in_ram; /* playlist stored in ram (dirplay) */
88 int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
88 89
89 union { 90 union {
90 char *buffer; /* buffer for in-ram playlists */ 91 volatile char *buffer;/* buffer for in-ram playlists */
91 int *seek_buf; /* buffer for seeks in real playlists */ 92 int *seek_buf; /* buffer for seeks in real playlists */
92 }; 93 };
93 int buffer_size; /* size of buffer */ 94 int buffer_size; /* size of buffer */
94 int buffer_end_pos; /* last position where buffer was written */ 95 int buffer_end_pos; /* last position where buffer was written */
diff --git a/apps/plugin.c b/apps/plugin.c
index 32b77ad287..43d9e03acf 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -789,6 +789,8 @@ static const struct plugin_api rockbox_api = {
789 789
790 /* new stuff at the end, sort into place next time 790 /* new stuff at the end, sort into place next time
791 the API gets incompatible */ 791 the API gets incompatible */
792 tree_get_entries,
793 tree_get_entry_at,
792}; 794};
793 795
794int plugin_load(const char* plugin, const void* parameter) 796int plugin_load(const char* plugin, const void* parameter)
@@ -865,6 +867,9 @@ int plugin_load(const char* plugin, const void* parameter)
865 lcd_remote_update(); 867 lcd_remote_update();
866#endif 868#endif
867 push_current_activity(ACTIVITY_PLUGIN); 869 push_current_activity(ACTIVITY_PLUGIN);
870 /* some plugins assume the entry cache doesn't move and save pointers to it
871 * they should be fixed properly instead of this lock */
872 tree_lock_cache(tree_get_context());
868 873
869 FOR_NB_SCREENS(i) 874 FOR_NB_SCREENS(i)
870 viewportmanager_theme_enable(i, false, NULL); 875 viewportmanager_theme_enable(i, false, NULL);
@@ -879,6 +884,7 @@ int plugin_load(const char* plugin, const void* parameter)
879 884
880 rc = p_hdr->entry_point(parameter); 885 rc = p_hdr->entry_point(parameter);
881 886
887 tree_unlock_cache(tree_get_context());
882 pop_current_activity(); 888 pop_current_activity();
883 889
884 if (!pfn_tsr_exit) 890 if (!pfn_tsr_exit)
diff --git a/apps/plugin.h b/apps/plugin.h
index d70e5634f9..1d8413f6df 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -926,6 +926,8 @@ struct plugin_api {
926 926
927 /* new stuff at the end, sort into place next time 927 /* new stuff at the end, sort into place next time
928 the API gets incompatible */ 928 the API gets incompatible */
929 struct entry* (*tree_get_entries)(struct tree_context* t);
930 struct entry* (*tree_get_entry_at)(struct tree_context* t, int index);
929}; 931};
930 932
931/* plugin header */ 933/* plugin header */
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 80e1ba41bf..044c835d00 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -136,7 +136,7 @@ static enum image_type image_type = IMAGE_UNKNOWN;
136static void get_pic_list(void) 136static void get_pic_list(void)
137{ 137{
138 struct tree_context *tree = rb->tree_get_context(); 138 struct tree_context *tree = rb->tree_get_context();
139 struct entry *dircache = tree->cache.entries; 139 struct entry *dircache = rb->tree_get_entries(tree);
140 int i; 140 int i;
141 char *pname; 141 char *pname;
142 142
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index dff0fce685..d132f80498 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -185,7 +185,7 @@ bool mod_ext(const char ext[])
185void get_mod_list(void) 185void get_mod_list(void)
186{ 186{
187 struct tree_context *tree = rb->tree_get_context(); 187 struct tree_context *tree = rb->tree_get_context();
188 struct entry *dircache = tree->cache.entries; 188 struct entry *dircache = rb->tree_get_entries(tree);
189 int i; 189 int i;
190 char *pname; 190 char *pname;
191 191
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 156ec019c1..84eae42a75 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -1876,7 +1876,7 @@ static bool is_videofile(const char* file)
1876static bool get_videofile(int direction, char* videofile, size_t bufsize) 1876static bool get_videofile(int direction, char* videofile, size_t bufsize)
1877{ 1877{
1878 struct tree_context *tree = rb->tree_get_context(); 1878 struct tree_context *tree = rb->tree_get_context();
1879 struct entry *dircache = tree->cache.entries; 1879 struct entry *dircache = rb->tree_get_entries(tree);
1880 int i, step, end, found = 0; 1880 int i, step, end, found = 0;
1881 char *videoname = rb->strrchr(videofile, '/') + 1; 1881 char *videoname = rb->strrchr(videofile, '/') + 1;
1882 size_t rest = bufsize - (videoname - videofile) - 1; 1882 size_t rest = bufsize - (videoname - videofile) - 1;
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index add09c7fef..d1cc8f272a 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -948,7 +948,7 @@ static bool browse_fonts( char *dst, int dst_size )
948 948
949 tree = rb->tree_get_context(); 949 tree = rb->tree_get_context();
950 backup = *tree; 950 backup = *tree;
951 dc = tree->cache.entries; 951 dc = rb->tree_get_entries(tree);
952 a = backup.currdir+rb->strlen(backup.currdir)-1; 952 a = backup.currdir+rb->strlen(backup.currdir)-1;
953 if( *a != '/' ) 953 if( *a != '/' )
954 { 954 {
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index a6307d5dd7..78414f3d88 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -255,6 +255,11 @@ int scrobbler_init(void)
255 return -1; 255 return -1;
256 256
257 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); 257 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
258 if (scrobbler_cache <= 0)
259 {
260 logf("SCROOBLER: OOM");
261 return -1;
262 }
258 263
259 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); 264 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
260 cache_pos = 0; 265 cache_pos = 0;
@@ -288,6 +293,9 @@ void scrobbler_shutdown(void)
288 { 293 {
289 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); 294 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
290 scrobbler_initialised = false; 295 scrobbler_initialised = false;
296 /* get rid of the buffer */
297 core_free(scrobbler_cache);
298 scrobbler_cache = 0;
291 } 299 }
292} 300}
293 301
diff --git a/apps/tagcache.c b/apps/tagcache.c
index 753675f906..5ab77264f6 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -222,6 +222,8 @@ struct statefile_header {
222 222
223/* Pointer to allocated ramcache_header */ 223/* Pointer to allocated ramcache_header */
224static struct ramcache_header *ramcache_hdr; 224static struct ramcache_header *ramcache_hdr;
225/* lock entity to temporarily prevent ramcache_hdr from moving */
226static int move_lock;
225#endif 227#endif
226 228
227/** 229/**
@@ -1035,6 +1037,8 @@ static bool check_clauses(struct tagcache_search *tcs,
1035 { 1037 {
1036 tfe = (struct tagfile_entry *) 1038 tfe = (struct tagfile_entry *)
1037 &ramcache_hdr->tags[clause->tag][seek]; 1039 &ramcache_hdr->tags[clause->tag][seek];
1040 /* str points to movable data, but no locking required here,
1041 * as no yield() is following */
1038 str = tfe->tag_data; 1042 str = tfe->tag_data;
1039 } 1043 }
1040 } 1044 }
@@ -1149,9 +1153,11 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1149# endif 1153# endif
1150 ) 1154 )
1151 { 1155 {
1156 move_lock++; /* lock because below makes a pointer to movable data */
1152 for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++) 1157 for (i = tcs->seek_pos; i < current_tcmh.tch.entry_count; i++)
1153 { 1158 {
1154 struct tagcache_seeklist_entry *seeklist; 1159 struct tagcache_seeklist_entry *seeklist;
1160 /* idx points to movable data, don't yield or reload */
1155 struct index_entry *idx = &ramcache_hdr->indices[i]; 1161 struct index_entry *idx = &ramcache_hdr->indices[i];
1156 if (tcs->seek_list_count == SEEK_LIST_SIZE) 1162 if (tcs->seek_list_count == SEEK_LIST_SIZE)
1157 break ; 1163 break ;
@@ -1175,8 +1181,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1175 /* Check for conditions. */ 1181 /* Check for conditions. */
1176 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count)) 1182 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count))
1177 continue; 1183 continue;
1178 1184 /* Add to the seek list if not already in uniq buffer (doesn't yield)*/
1179 /* Add to the seek list if not already in uniq buffer. */
1180 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type])) 1185 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
1181 continue; 1186 continue;
1182 1187
@@ -1187,6 +1192,7 @@ static bool build_lookup_list(struct tagcache_search *tcs)
1187 seeklist->idx_id = i; 1192 seeklist->idx_id = i;
1188 tcs->seek_list_count++; 1193 tcs->seek_list_count++;
1189 } 1194 }
1195 move_lock--;
1190 1196
1191 tcs->seek_pos = i; 1197 tcs->seek_pos = i;
1192 1198
@@ -1538,10 +1544,11 @@ static bool get_next(struct tagcache_search *tcs)
1538 struct tagfile_entry *ep; 1544 struct tagfile_entry *ep;
1539 1545
1540 ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position]; 1546 ep = (struct tagfile_entry *)&ramcache_hdr->tags[tcs->type][tcs->position];
1541 tcs->result = ep->tag_data; 1547 /* don't return ep->tag_data directly as it may move */
1542 tcs->result_len = strlen(tcs->result) + 1; 1548 tcs->result_len = strlcpy(buf, ep->tag_data, sizeof(buf)) + 1;
1549 tcs->result = buf;
1543 tcs->idx_id = ep->idx_id; 1550 tcs->idx_id = ep->idx_id;
1544 tcs->ramresult = true; 1551 tcs->ramresult = false; /* was true before we copied to buf too */
1545 1552
1546 /* Increase position for the next run. This may get overwritten. */ 1553 /* Increase position for the next run. This may get overwritten. */
1547 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length; 1554 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
@@ -1703,15 +1710,34 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
1703 entry = &ramcache_hdr->indices[idx_id]; 1710 entry = &ramcache_hdr->indices[idx_id];
1704 1711
1705 memset(id3, 0, sizeof(struct mp3entry)); 1712 memset(id3, 0, sizeof(struct mp3entry));
1706 1713 char* buf = id3->id3v2buf;
1707 id3->title = get_tag_string(entry, tag_title); 1714 ssize_t remaining = sizeof(id3->id3v2buf);
1708 id3->artist = get_tag_string(entry, tag_artist); 1715
1709 id3->album = get_tag_string(entry, tag_album); 1716 /* this macro sets id3 strings by copying to the id3v2buf */
1710 id3->genre_string = get_tag_string(entry, tag_genre); 1717#define SET(x, y) do \
1711 id3->composer = get_tag_string(entry, tag_composer); 1718 { \
1712 id3->comment = get_tag_string(entry, tag_comment); 1719 if (remaining > 0) \
1713 id3->albumartist = get_tag_string(entry, tag_albumartist); 1720 { \
1714 id3->grouping = get_tag_string(entry, tag_grouping); 1721 x = NULL; /* initialize with null if tag doesn't exist */ \
1722 char* src = get_tag_string(entry, y); \
1723 if (src) \
1724 { \
1725 x = buf; \
1726 size_t len = strlcpy(buf, src, remaining) +1; \
1727 buf += len; remaining -= len; \
1728 } \
1729 } \
1730 } while(0)
1731
1732
1733 SET(id3->title, tag_title);
1734 SET(id3->artist, tag_artist);
1735 SET(id3->album, tag_album);
1736 SET(id3->genre_string, tag_genre);
1737 SET(id3->composer, tag_composer);
1738 SET(id3->comment, tag_comment);
1739 SET(id3->albumartist, tag_albumartist);
1740 SET(id3->grouping, tag_grouping);
1715 1741
1716 id3->length = get_tag_numeric(entry, tag_length, idx_id); 1742 id3->length = get_tag_numeric(entry, tag_length, idx_id);
1717 id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id); 1743 id3->playcount = get_tag_numeric(entry, tag_playcount, idx_id);
@@ -2903,6 +2929,9 @@ static bool commit(void)
2903#ifdef HAVE_DIRCACHE 2929#ifdef HAVE_DIRCACHE
2904 bool dircache_buffer_stolen = false; 2930 bool dircache_buffer_stolen = false;
2905#endif 2931#endif
2932#ifdef HAVE_TC_RAMCACHE
2933 bool ramcache_buffer_stolen = false;
2934#endif
2906 bool local_allocation = false; 2935 bool local_allocation = false;
2907 2936
2908 logf("committing tagcache"); 2937 logf("committing tagcache");
@@ -2976,6 +3005,8 @@ static bool commit(void)
2976 tempbuf = (char *)(ramcache_hdr + 1); 3005 tempbuf = (char *)(ramcache_hdr + 1);
2977 tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128; 3006 tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128;
2978 tempbuf_size &= ~0x03; 3007 tempbuf_size &= ~0x03;
3008 move_lock++;
3009 ramcache_buffer_stolen = true;
2979 } 3010 }
2980#endif 3011#endif
2981 3012
@@ -3072,6 +3103,8 @@ static bool commit(void)
3072#endif 3103#endif
3073 3104
3074#ifdef HAVE_TC_RAMCACHE 3105#ifdef HAVE_TC_RAMCACHE
3106 if (ramcache_buffer_stolen)
3107 move_lock--;
3075 /* Reload tagcache. */ 3108 /* Reload tagcache. */
3076 if (tc_stat.ramcache_allocated > 0) 3109 if (tc_stat.ramcache_allocated > 0)
3077 tagcache_start_scan(); 3110 tagcache_start_scan();
@@ -3689,9 +3722,11 @@ static bool delete_entry(long idx_id)
3689 { 3722 {
3690 struct tagfile_entry *tfe; 3723 struct tagfile_entry *tfe;
3691 int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag]; 3724 int32_t *seek = &ramcache_hdr->indices[idx_id].tag_seek[tag];
3692 3725
3693 tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek]; 3726 tfe = (struct tagfile_entry *)&ramcache_hdr->tags[tag][*seek];
3727 move_lock++; /* protect tfe and seek if crc_32() yield()s */
3694 *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff); 3728 *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff);
3729 move_lock--;
3695 myidx.tag_seek[tag] = *seek; 3730 myidx.tag_seek[tag] = *seek;
3696 } 3731 }
3697 else 3732 else
@@ -3813,6 +3848,30 @@ static bool check_event_queue(void)
3813#endif 3848#endif
3814 3849
3815#ifdef HAVE_TC_RAMCACHE 3850#ifdef HAVE_TC_RAMCACHE
3851
3852static void fix_ramcache(void* old_addr, void* new_addr)
3853{
3854 ptrdiff_t offpos = new_addr - old_addr;
3855 for (int i = 0; i < TAG_COUNT; i++)
3856 ramcache_hdr->tags[i] += offpos;
3857}
3858
3859static int move_cb(int handle, void* current, void* new)
3860{
3861 (void)handle;
3862 if (move_lock > 0)
3863 return BUFLIB_CB_CANNOT_MOVE;
3864
3865 fix_ramcache(current, new);
3866 ramcache_hdr = new;
3867 return BUFLIB_CB_OK;
3868}
3869
3870static struct buflib_callbacks ops = {
3871 .move_callback = move_cb,
3872 .shrink_callback = NULL,
3873};
3874
3816static bool allocate_tagcache(void) 3875static bool allocate_tagcache(void)
3817{ 3876{
3818 struct master_header tcmh; 3877 struct master_header tcmh;
@@ -3833,7 +3892,7 @@ static bool allocate_tagcache(void)
3833 */ 3892 */
3834 tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE + 3893 tc_stat.ramcache_allocated = tcmh.tch.datasize + 256 + TAGCACHE_RESERVE +
3835 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *); 3894 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
3836 int handle = core_alloc("tc ramcache", tc_stat.ramcache_allocated); 3895 int handle = core_alloc_ex("tc ramcache", tc_stat.ramcache_allocated, &ops);
3837 ramcache_hdr = core_get_data(handle); 3896 ramcache_hdr = core_get_data(handle);
3838 memset(ramcache_hdr, 0, sizeof(struct ramcache_header)); 3897 memset(ramcache_hdr, 0, sizeof(struct ramcache_header));
3839 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh); 3898 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
@@ -3871,12 +3930,13 @@ static bool tagcache_dumpload(void)
3871 3930
3872 3931
3873 /* Lets allocate real memory and load it */ 3932 /* Lets allocate real memory and load it */
3874 handle = core_alloc("tc ramcache", shdr.tc_stat.ramcache_allocated); 3933 handle = core_alloc_ex("tc ramcache", shdr.tc_stat.ramcache_allocated, &ops);
3875 ramcache_hdr = core_get_data(handle); 3934 ramcache_hdr = core_get_data(handle);
3935 moev_lock++;
3876 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated); 3936 rc = read(fd, ramcache_hdr, shdr.tc_stat.ramcache_allocated);
3937 move_lock--;
3877 close(fd); 3938 close(fd);
3878 3939
3879 offpos = (long)ramcache_hdr - (long)shdr.hdr;
3880 if (rc != shdr.tc_stat.ramcache_allocated) 3940 if (rc != shdr.tc_stat.ramcache_allocated)
3881 { 3941 {
3882 logf("read failure!"); 3942 logf("read failure!");
@@ -3887,8 +3947,7 @@ static bool tagcache_dumpload(void)
3887 memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat)); 3947 memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat));
3888 3948
3889 /* Now fix the pointers */ 3949 /* Now fix the pointers */
3890 for (i = 0; i < TAG_COUNT; i++) 3950 fix_ramcache(shdr.hdr, ramcache_hdr);
3891 ramcache_hdr->tags[i] += offpos;
3892 3951
3893 /* Load the tagcache master header (should match the actual DB file header). */ 3952 /* Load the tagcache master header (should match the actual DB file header). */
3894 memcpy(&current_tcmh, &shdr.mh, sizeof current_tcmh); 3953 memcpy(&current_tcmh, &shdr.mh, sizeof current_tcmh);
@@ -3919,7 +3978,9 @@ static bool tagcache_dumpsave(void)
3919 write(fd, &shdr, sizeof shdr); 3978 write(fd, &shdr, sizeof shdr);
3920 3979
3921 /* And dump the data too */ 3980 /* And dump the data too */
3981 move_lock++;
3922 write(fd, ramcache_hdr, tc_stat.ramcache_allocated); 3982 write(fd, ramcache_hdr, tc_stat.ramcache_allocated);
3983 move_lock--;
3923 close(fd); 3984 close(fd);
3924 3985
3925 return true; 3986 return true;
@@ -3962,7 +4023,8 @@ static bool load_tagcache(void)
3962 4023
3963 /* Master header copy should already match, this can be redundant to do. */ 4024 /* Master header copy should already match, this can be redundant to do. */
3964 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh); 4025 memcpy(&current_tcmh, &tcmh, sizeof current_tcmh);
3965 4026
4027 move_lock++; /* lock for the reset of the scan, simpler to handle */
3966 idx = ramcache_hdr->indices; 4028 idx = ramcache_hdr->indices;
3967 4029
3968 /* Load the master index table. */ 4030 /* Load the master index table. */
@@ -3972,8 +4034,7 @@ static bool load_tagcache(void)
3972 if (bytesleft < 0) 4034 if (bytesleft < 0)
3973 { 4035 {
3974 logf("too big tagcache."); 4036 logf("too big tagcache.");
3975 close(fd); 4037 goto failure;
3976 return false;
3977 } 4038 }
3978 4039
3979 /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture 4040 /* DEBUG: After tagcache commit and dircache rebuild, hdr-sturcture
@@ -3982,8 +4043,7 @@ static bool load_tagcache(void)
3982 if (rc != sizeof(struct index_entry)) 4043 if (rc != sizeof(struct index_entry))
3983 { 4044 {
3984 logf("read error #10"); 4045 logf("read error #10");
3985 close(fd); 4046 goto failure;
3986 return false;
3987 } 4047 }
3988 4048
3989 idx++; 4049 idx++;
@@ -4010,7 +4070,7 @@ static bool load_tagcache(void)
4010 p += sizeof(struct tagcache_header); 4070 p += sizeof(struct tagcache_header);
4011 4071
4012 if ( (fd = open_tag_fd(tch, tag, false)) < 0) 4072 if ( (fd = open_tag_fd(tch, tag, false)) < 0)
4013 return false; 4073 goto failure_nofd;
4014 4074
4015 for (ramcache_hdr->entry_count[tag] = 0; 4075 for (ramcache_hdr->entry_count[tag] = 0;
4016 ramcache_hdr->entry_count[tag] < tch->entry_count; 4076 ramcache_hdr->entry_count[tag] < tch->entry_count;
@@ -4022,7 +4082,7 @@ static bool load_tagcache(void)
4022 { 4082 {
4023 /* Abort if we got a critical event in queue */ 4083 /* Abort if we got a critical event in queue */
4024 if (check_event_queue()) 4084 if (check_event_queue())
4025 return false; 4085 goto failure;
4026 } 4086 }
4027 4087
4028 fe = (struct tagfile_entry *)p; 4088 fe = (struct tagfile_entry *)p;
@@ -4032,8 +4092,7 @@ static bool load_tagcache(void)
4032 { 4092 {
4033 /* End of lookup table. */ 4093 /* End of lookup table. */
4034 logf("read error #11"); 4094 logf("read error #11");
4035 close(fd); 4095 goto failure;
4036 return false;
4037 } 4096 }
4038 4097
4039 /* We have a special handling for the filename tags. */ 4098 /* We have a special handling for the filename tags. */
@@ -4051,16 +4110,14 @@ static bool load_tagcache(void)
4051 buf[10] = '\0'; 4110 buf[10] = '\0';
4052 logf("TAG:%s", buf); 4111 logf("TAG:%s", buf);
4053 logf("too long filename"); 4112 logf("too long filename");
4054 close(fd); 4113 goto failure;
4055 return false;
4056 } 4114 }
4057 4115
4058 rc = read(fd, buf, fe->tag_length); 4116 rc = read(fd, buf, fe->tag_length);
4059 if (rc != fe->tag_length) 4117 if (rc != fe->tag_length)
4060 { 4118 {
4061 logf("read error #12"); 4119 logf("read error #12");
4062 close(fd); 4120 goto failure;
4063 return false;
4064 } 4121 }
4065 4122
4066 /* Check if the entry has already been removed */ 4123 /* Check if the entry has already been removed */
@@ -4071,15 +4128,13 @@ static bool load_tagcache(void)
4071 if (idx->flag & FLAG_DIRCACHE) 4128 if (idx->flag & FLAG_DIRCACHE)
4072 { 4129 {
4073 logf("internal error!"); 4130 logf("internal error!");
4074 close(fd); 4131 goto failure;
4075 return false;
4076 } 4132 }
4077 4133
4078 if (idx->tag_seek[tag] != pos) 4134 if (idx->tag_seek[tag] != pos)
4079 { 4135 {
4080 logf("corrupt data structures!"); 4136 logf("corrupt data structures!");
4081 close(fd); 4137 goto failure;
4082 return false;
4083 } 4138 }
4084 4139
4085# ifdef HAVE_DIRCACHE 4140# ifdef HAVE_DIRCACHE
@@ -4126,8 +4181,7 @@ static bool load_tagcache(void)
4126 logf("too big tagcache #2"); 4181 logf("too big tagcache #2");
4127 logf("tl: %ld", fe->tag_length); 4182 logf("tl: %ld", fe->tag_length);
4128 logf("bl: %ld", bytesleft); 4183 logf("bl: %ld", bytesleft);
4129 close(fd); 4184 goto failure;
4130 return false;
4131 } 4185 }
4132 4186
4133 p = fe->tag_data; 4187 p = fe->tag_data;
@@ -4141,8 +4195,7 @@ static bool load_tagcache(void)
4141 logf("len=0x%04lx", fe->tag_length); // 0x4000 4195 logf("len=0x%04lx", fe->tag_length); // 0x4000
4142 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433 4196 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433
4143 logf("tag=0x%02x", tag); // 0x00 4197 logf("tag=0x%02x", tag); // 0x00
4144 close(fd); 4198 goto failure;
4145 return false;
4146 } 4199 }
4147 } 4200 }
4148 close(fd); 4201 close(fd);
@@ -4151,7 +4204,14 @@ static bool load_tagcache(void)
4151 tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft; 4204 tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft;
4152 logf("tagcache loaded into ram!"); 4205 logf("tagcache loaded into ram!");
4153 4206
4207 move_lock--;
4154 return true; 4208 return true;
4209
4210failure:
4211 close(fd);
4212failure_nofd:
4213 move_lock--;
4214 return false;
4155} 4215}
4156#endif /* HAVE_TC_RAMCACHE */ 4216#endif /* HAVE_TC_RAMCACHE */
4157 4217
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 393a2905f5..6c13efdd0e 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -190,7 +190,9 @@ struct tagcache_search {
190 190
191 /* Exported variables. */ 191 /* Exported variables. */
192 bool ramsearch; /* Is ram copy of the tagcache being used. */ 192 bool ramsearch; /* Is ram copy of the tagcache being used. */
193 bool ramresult; /* False if result is not static, and must be copied. */ 193 bool ramresult; /* False if result is not static, and must be copied.
194 Currently always false since ramresult buffer is
195 movable */
194 int type; /* The tag type to be searched. Only nonvirtual tags */ 196 int type; /* The tag type to be searched. Only nonvirtual tags */
195 char *result; /* The result data for all tags. */ 197 char *result; /* The result data for all tags. */
196 int result_len; /* Length of the result including \0 */ 198 int result_len; /* Length of the result including \0 */
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 0d4330bac8..5766d2892e 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -53,6 +53,7 @@
53#include "storage.h" 53#include "storage.h"
54#include "dir.h" 54#include "dir.h"
55#include "playback.h" 55#include "playback.h"
56#include "panic.h"
56 57
57#define str_or_empty(x) (x ? x : "(NULL)") 58#define str_or_empty(x) (x ? x : "(NULL)")
58 59
@@ -60,6 +61,17 @@
60 61
61static int tagtree_play_folder(struct tree_context* c); 62static int tagtree_play_folder(struct tree_context* c);
62 63
64/* this needs to be same size as struct entry (tree.h) and name needs to be
65 * the first; so that they're compatible enough to walk arrays of both
66 * derefencing the name member*/
67struct tagentry {
68 char* name;
69 int newtable;
70 int extraseek;
71};
72
73static struct tagentry* tagtree_get_entry(struct tree_context *c, int id);
74
63#define SEARCHSTR_SIZE 256 75#define SEARCHSTR_SIZE 256
64 76
65enum table { 77enum table {
@@ -96,7 +108,7 @@ enum variables {
96 108
97/* Capacity 10 000 entries (for example 10k different artists) */ 109/* Capacity 10 000 entries (for example 10k different artists) */
98#define UNIQBUF_SIZE (64*1024) 110#define UNIQBUF_SIZE (64*1024)
99static long *uniqbuf; 111static long uniqbuf[UNIQBUF_SIZE / sizeof(long)];
100 112
101#define MAX_TAGS 5 113#define MAX_TAGS 5
102#define MAX_MENU_ID_SIZE 32 114#define MAX_MENU_ID_SIZE 32
@@ -163,8 +175,8 @@ struct match
163/* Statusbar text of the current view. */ 175/* Statusbar text of the current view. */
164static char current_title[MAX_TAGS][128]; 176static char current_title[MAX_TAGS][128];
165 177
166static struct menu_root *menus[TAGMENU_MAX_MENUS]; 178static struct menu_root * menus[TAGMENU_MAX_MENUS];
167static struct menu_root *menu; 179static struct menu_root * menu;
168static struct search_instruction *csi; 180static struct search_instruction *csi;
169static const char *strp; 181static const char *strp;
170static int menu_count; 182static int menu_count;
@@ -176,8 +188,74 @@ static int current_entry_count;
176static struct tree_context *tc; 188static struct tree_context *tc;
177 189
178/* a few memory alloc helper */ 190/* a few memory alloc helper */
179static int tagtree_handle; 191static int tagtree_handle, lock_count;
180static size_t tagtree_bufsize, tagtree_buf_used; 192static size_t tagtree_bufsize, tagtree_buf_used;
193
194#define UPDATE(x, y) { x = (typeof(x))((char*)(x) + (y)); }
195static int move_callback(int handle, void* current, void* new)
196{
197 (void)handle; (void)current; (void)new;
198 ptrdiff_t diff = new - current;
199
200 if (lock_count > 0)
201 return BUFLIB_CB_CANNOT_MOVE;
202
203 UPDATE(menu, diff);
204 /* loop over menus */
205 for(int i = 0; i < menu_count; i++)
206 {
207 struct menu_root* menu = menus[i];
208 /* then over the menu_entries of a menu */
209 for(int j = 0; j < menu->itemcount; j++)
210 {
211 struct menu_entry* mentry = menu->items[j];
212 /* then over the search_instructions of each menu_entry */
213 for(int k = 0; k < mentry->si.tagorder_count; k++)
214 {
215 for(int l = 0; l < mentry->si.clause_count[k]; l++)
216 {
217 UPDATE(mentry->si.clause[k][l]->str, diff);
218 UPDATE(mentry->si.clause[k][l], diff);
219 }
220 }
221 UPDATE(menu->items[j], diff);
222 }
223 UPDATE(menus[i], diff);
224 }
225
226 /* now the same game for formats */
227 for(int i = 0; i < format_count; i++)
228 {
229 for(int j = 0; j < formats[i]->clause_count; j++)
230 {
231 UPDATE(formats[i]->clause[j]->str, diff);
232 UPDATE(formats[i]->clause[j], diff);
233 }
234
235 if (formats[i]->formatstr)
236 UPDATE(formats[i]->formatstr, diff);
237
238 UPDATE(formats[i], diff);
239 }
240 return BUFLIB_CB_OK;
241}
242#undef UPDATE
243
244static inline void tagtree_lock(void)
245{
246 lock_count++;
247}
248
249static inline void tagtree_unlock(void)
250{
251 lock_count--;
252}
253
254static struct buflib_callbacks ops = {
255 .move_callback = move_callback,
256 .shrink_callback = NULL,
257};
258
181static void* tagtree_alloc(size_t size) 259static void* tagtree_alloc(size_t size)
182{ 260{
183 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used; 261 char* buf = core_get_data(tagtree_handle) + tagtree_buf_used;
@@ -201,6 +279,7 @@ static char* tagtree_strdup(const char* buf)
201 return dest; 279 return dest;
202} 280}
203 281
282/* save to call without locking */
204static int get_token_str(char *buf, int size) 283static int get_token_str(char *buf, int size)
205{ 284{
206 /* Find the start. */ 285 /* Find the start. */
@@ -510,7 +589,8 @@ static int add_format(const char *buf)
510 { 589 {
511 int clause_count = 0; 590 int clause_count = 0;
512 strp++; 591 strp++;
513 592
593 tagtree_lock();
514 while (1) 594 while (1)
515 { 595 {
516 struct tagcache_search_clause *newclause; 596 struct tagcache_search_clause *newclause;
@@ -529,6 +609,7 @@ static int add_format(const char *buf)
529 609
530 clause_count++; 610 clause_count++;
531 } 611 }
612 tagtree_unlock();
532 613
533 formats[format_count]->clause_count = clause_count; 614 formats[format_count]->clause_count = clause_count;
534 } 615 }
@@ -593,9 +674,14 @@ static int get_condition(struct search_instruction *inst)
593 strp++; 674 strp++;
594 new_clause->type = clause_logical_or; 675 new_clause->type = clause_logical_or;
595 } 676 }
596 else if (!read_clause(new_clause)) 677 else
597 return -1; 678 {
598 679 tagtree_lock();
680 bool ret = read_clause(new_clause);
681 tagtree_unlock();
682 if (!ret)
683 return -1;
684 }
599 inst->clause_count[inst->tagorder_count]++; 685 inst->clause_count[inst->tagorder_count]++;
600 686
601 return 1; 687 return 1;
@@ -616,7 +702,6 @@ static bool parse_search(struct menu_entry *entry, const char *str)
616 struct search_instruction *inst = &entry->si; 702 struct search_instruction *inst = &entry->si;
617 char buf[MAX_PATH]; 703 char buf[MAX_PATH];
618 int i; 704 int i;
619 struct menu_root *new_menu;
620 705
621 strp = str; 706 strp = str;
622 707
@@ -654,8 +739,7 @@ static bool parse_search(struct menu_entry *entry, const char *str)
654 739
655 /* Allocate a new menu unless link is found. */ 740 /* Allocate a new menu unless link is found. */
656 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root)); 741 menus[menu_count] = tagtree_alloc0(sizeof(struct menu_root));
657 new_menu = menus[menu_count]; 742 strlcpy(menus[menu_count]->id, buf, MAX_MENU_ID_SIZE);
658 strlcpy(new_menu->id, buf, MAX_MENU_ID_SIZE);
659 entry->link = menu_count; 743 entry->link = menu_count;
660 ++menu_count; 744 ++menu_count;
661 745
@@ -679,8 +763,11 @@ static bool parse_search(struct menu_entry *entry, const char *str)
679 break ; 763 break ;
680 764
681 logf("tag: %d", inst->tagorder[inst->tagorder_count]); 765 logf("tag: %d", inst->tagorder[inst->tagorder_count]);
682 766
767 tagtree_lock();
683 while ( (ret = get_condition(inst)) > 0 ) ; 768 while ( (ret = get_condition(inst)) > 0 ) ;
769 tagtree_unlock();
770
684 if (ret < 0) 771 if (ret < 0)
685 return false; 772 return false;
686 773
@@ -697,7 +784,7 @@ static int compare(const void *p1, const void *p2)
697{ 784{
698 struct tagentry *e1 = (struct tagentry *)p1; 785 struct tagentry *e1 = (struct tagentry *)p1;
699 struct tagentry *e2 = (struct tagentry *)p2; 786 struct tagentry *e2 = (struct tagentry *)p2;
700 787
701 if (sort_inverse) 788 if (sort_inverse)
702 return strncasecmp(e2->name, e1->name, MAX_PATH); 789 return strncasecmp(e2->name, e1->name, MAX_PATH);
703 790
@@ -1001,11 +1088,11 @@ static int parse_line(int n, char *buf, void *parameters)
1001 if (menu->items[menu->itemcount] == NULL) 1088 if (menu->items[menu->itemcount] == NULL)
1002 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry)); 1089 menu->items[menu->itemcount] = tagtree_alloc0(sizeof(struct menu_entry));
1003 1090
1004 if (!parse_search(menu->items[menu->itemcount], buf)) 1091 tagtree_lock();
1005 return 0; 1092 if (parse_search(menu->items[menu->itemcount], buf))
1006 1093 menu->itemcount++;
1007 menu->itemcount++; 1094 tagtree_unlock();
1008 1095
1009 return 0; 1096 return 0;
1010} 1097}
1011 1098
@@ -1040,15 +1127,20 @@ void tagtree_init(void)
1040 menu_count = 0; 1127 menu_count = 0;
1041 menu = NULL; 1128 menu = NULL;
1042 rootmenu = -1; 1129 rootmenu = -1;
1043 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, NULL); 1130 tagtree_handle = core_alloc_maximum("tagtree", &tagtree_bufsize, &ops);
1044 parse_menu(FILE_SEARCH_INSTRUCTIONS); 1131 parse_menu(FILE_SEARCH_INSTRUCTIONS);
1132
1133 /* safety check since tree.c needs to cast tagentry to entry */
1134 if (sizeof(struct tagentry) != sizeof(struct entry))
1135 panicf("tagentry(%zu) and entry mismatch(%zu)",
1136 sizeof(struct tagentry), sizeof(struct entry));
1137 if (lock_count > 0)
1138 panicf("tagtree locked after parsing");
1045 1139
1046 /* If no root menu is set, assume it's the first single menu 1140 /* If no root menu is set, assume it's the first single menu
1047 * we have. That shouldn't normally happen. */ 1141 * we have. That shouldn't normally happen. */
1048 if (rootmenu < 0) 1142 if (rootmenu < 0)
1049 rootmenu = 0; 1143 rootmenu = 0;
1050
1051 uniqbuf = tagtree_alloc(UNIQBUF_SIZE);
1052 1144
1053 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event); 1145 add_event(PLAYBACK_EVENT_TRACK_BUFFER, false, tagtree_buffer_event);
1054 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event); 1146 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, tagtree_track_finish_event);
@@ -1181,10 +1273,14 @@ static int format_str(struct tagcache_search *tcs, struct display_format *fmt,
1181 return 0; 1273 return 0;
1182} 1274}
1183 1275
1276static struct tagentry* get_entries(struct tree_context *tc)
1277{
1278 return core_get_data(tc->cache.entries_handle);
1279}
1280
1184static int retrieve_entries(struct tree_context *c, int offset, bool init) 1281static int retrieve_entries(struct tree_context *c, int offset, bool init)
1185{ 1282{
1186 struct tagcache_search tcs; 1283 struct tagcache_search tcs;
1187 struct tagentry *dptr = c->cache.entries;
1188 struct display_format *fmt; 1284 struct display_format *fmt;
1189 int i; 1285 int i;
1190 int namebufused = 0; 1286 int namebufused = 0;
@@ -1242,7 +1338,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1242 csi->result_seek[i]); 1338 csi->result_seek[i]);
1243 } 1339 }
1244 } 1340 }
1245 1341
1342 /* because tagcache saves the clauses, we need to lock the buffer
1343 * for the entire duration of the search */
1344 tagtree_lock();
1246 for (i = 0; i <= level; i++) 1345 for (i = 0; i <= level; i++)
1247 { 1346 {
1248 int j; 1347 int j;
@@ -1276,6 +1375,10 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1276 strip = 0; 1375 strip = 0;
1277 } 1376 }
1278 1377
1378 /* lock buflib out due to possible yields */
1379 tree_lock_cache(c);
1380 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1381
1279 if (tag != tag_title && tag != tag_filename) 1382 if (tag != tag_title && tag != tag_filename)
1280 { 1383 {
1281 if (offset == 0) 1384 if (offset == 0)
@@ -1315,6 +1418,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1315 1418
1316 fmt = NULL; 1419 fmt = NULL;
1317 /* Check the format */ 1420 /* Check the format */
1421 tagtree_lock();
1318 for (i = 0; i < format_count; i++) 1422 for (i = 0; i < format_count; i++)
1319 { 1423 {
1320 if (formats[i]->group_id != csi->format_id[level]) 1424 if (formats[i]->group_id != csi->format_id[level])
@@ -1327,6 +1431,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1327 break; 1431 break;
1328 } 1432 }
1329 } 1433 }
1434 tagtree_unlock();
1330 1435
1331 if (strcmp(tcs.result, UNTAGGED) == 0) 1436 if (strcmp(tcs.result, UNTAGGED) == 0)
1332 { 1437 {
@@ -1337,7 +1442,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1337 1442
1338 if (!tcs.ramresult || fmt) 1443 if (!tcs.ramresult || fmt)
1339 { 1444 {
1340 dptr->name = &c->cache.name_buffer[namebufused]; 1445 dptr->name = core_get_data(c->cache.name_buffer_handle)+namebufused;
1341 1446
1342 if (fmt) 1447 if (fmt)
1343 { 1448 {
@@ -1354,6 +1459,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1354 { 1459 {
1355 logf("format_str() failed"); 1460 logf("format_str() failed");
1356 tagcache_search_finish(&tcs); 1461 tagcache_search_finish(&tcs);
1462 tree_unlock_cache(c);
1463 tagtree_unlock();
1357 return 0; 1464 return 0;
1358 } 1465 }
1359 else 1466 else
@@ -1392,6 +1499,8 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1392 if (!show_search_progress(false, total_count)) 1499 if (!show_search_progress(false, total_count))
1393 { /* user aborted */ 1500 { /* user aborted */
1394 tagcache_search_finish(&tcs); 1501 tagcache_search_finish(&tcs);
1502 tree_unlock_cache(c);
1503 tagtree_unlock();
1395 return current_entry_count; 1504 return current_entry_count;
1396 } 1505 }
1397 } 1506 }
@@ -1399,15 +1508,17 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1399 1508
1400 if (sort) 1509 if (sort)
1401 { 1510 {
1402 int entry_size = sizeof(struct tagentry); 1511 struct tagentry *entries = get_entries(c);
1403 qsort(c->cache.entries + special_entry_count * entry_size, 1512 qsort(&entries[special_entry_count],
1404 current_entry_count - special_entry_count, 1513 current_entry_count - special_entry_count,
1405 entry_size, compare); 1514 sizeof(struct tagentry), compare);
1406 } 1515 }
1407 1516
1408 if (!init) 1517 if (!init)
1409 { 1518 {
1410 tagcache_search_finish(&tcs); 1519 tagcache_search_finish(&tcs);
1520 tree_unlock_cache(c);
1521 tagtree_unlock();
1411 return current_entry_count; 1522 return current_entry_count;
1412 } 1523 }
1413 1524
@@ -1422,7 +1533,9 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1422 } 1533 }
1423 1534
1424 tagcache_search_finish(&tcs); 1535 tagcache_search_finish(&tcs);
1425 1536 tree_unlock_cache(c);
1537 tagtree_unlock();
1538
1426 if (!sort && (sort_inverse || sort_limit)) 1539 if (!sort && (sort_inverse || sort_limit))
1427 { 1540 {
1428 splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count); 1541 splashf(HZ*4, ID2P(LANG_SHOWDIR_BUFFER_FULL), total_count);
@@ -1435,7 +1548,7 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1435 1548
1436 if (strip) 1549 if (strip)
1437 { 1550 {
1438 dptr = c->cache.entries; 1551 dptr = get_entries(c);
1439 for (i = special_entry_count; i < current_entry_count; i++, dptr++) 1552 for (i = special_entry_count; i < current_entry_count; i++, dptr++)
1440 { 1553 {
1441 int len = strlen(dptr->name); 1554 int len = strlen(dptr->name);
@@ -1446,14 +1559,14 @@ static int retrieve_entries(struct tree_context *c, int offset, bool init)
1446 dptr->name = &dptr->name[strip]; 1559 dptr->name = &dptr->name[strip];
1447 } 1560 }
1448 } 1561 }
1449 1562
1450 return total_count; 1563 return total_count;
1451 1564
1452} 1565}
1453 1566
1454static int load_root(struct tree_context *c) 1567static int load_root(struct tree_context *c)
1455{ 1568{
1456 struct tagentry *dptr = c->cache.entries; 1569 struct tagentry *dptr = core_get_data(c->cache.entries_handle);
1457 int i; 1570 int i;
1458 1571
1459 tc = c; 1572 tc = c;
@@ -1569,6 +1682,10 @@ int tagtree_enter(struct tree_context* c)
1569 c->pos_history[c->dirlevel] = c->firstpos; 1682 c->pos_history[c->dirlevel] = c->firstpos;
1570 c->dirlevel++; 1683 c->dirlevel++;
1571 1684
1685 /* lock buflib for possible I/O to protect dptr */
1686 tree_lock_cache(c);
1687 tagtree_lock();
1688
1572 switch (c->currtable) { 1689 switch (c->currtable) {
1573 case ROOT: 1690 case ROOT:
1574 c->currextra = newextra; 1691 c->currextra = newextra;
@@ -1634,6 +1751,8 @@ int tagtree_enter(struct tree_context* c)
1634 if (rc < 0 || !searchstring[0]) 1751 if (rc < 0 || !searchstring[0])
1635 { 1752 {
1636 tagtree_exit(c); 1753 tagtree_exit(c);
1754 tree_unlock_cache(c);
1755 tagtree_unlock();
1637 return 0; 1756 return 0;
1638 } 1757 }
1639 if (csi->clause[i][j]->numeric) 1758 if (csi->clause[i][j]->numeric)
@@ -1682,9 +1801,12 @@ int tagtree_enter(struct tree_context* c)
1682 c->dirlevel--; 1801 c->dirlevel--;
1683 break; 1802 break;
1684 } 1803 }
1804
1685 1805
1686 c->selected_item=0; 1806 c->selected_item=0;
1687 gui_synclist_select_item(&tree_lists, c->selected_item); 1807 gui_synclist_select_item(&tree_lists, c->selected_item);
1808 tree_unlock_cache(c);
1809 tagtree_unlock();
1688 1810
1689 return rc; 1811 return rc;
1690} 1812}
@@ -1704,14 +1826,13 @@ void tagtree_exit(struct tree_context* c)
1704int tagtree_get_filename(struct tree_context* c, char *buf, int buflen) 1826int tagtree_get_filename(struct tree_context* c, char *buf, int buflen)
1705{ 1827{
1706 struct tagcache_search tcs; 1828 struct tagcache_search tcs;
1707 struct tagentry *entry; 1829 int extraseek = tagtree_get_entry(c, c->selected_item)->extraseek;
1708 1830
1709 entry = tagtree_get_entry(c, c->selected_item);
1710 1831
1711 if (!tagcache_search(&tcs, tag_filename)) 1832 if (!tagcache_search(&tcs, tag_filename))
1712 return -1; 1833 return -1;
1713 1834
1714 if (!tagcache_retrieve(&tcs, entry->extraseek, tcs.type, buf, buflen)) 1835 if (!tagcache_retrieve(&tcs, extraseek, tcs.type, buf, buflen))
1715 { 1836 {
1716 tagcache_search_finish(&tcs); 1837 tagcache_search_finish(&tcs);
1717 return -2; 1838 return -2;
@@ -1790,9 +1911,9 @@ static bool insert_all_playlist(struct tree_context *c, int position, bool queue
1790 1911
1791bool tagtree_insert_selection_playlist(int position, bool queue) 1912bool tagtree_insert_selection_playlist(int position, bool queue)
1792{ 1913{
1793 struct tagentry *dptr;
1794 char buf[MAX_PATH]; 1914 char buf[MAX_PATH];
1795 int dirlevel = tc->dirlevel; 1915 int dirlevel = tc->dirlevel;
1916 int newtable;
1796 1917
1797 show_search_progress( 1918 show_search_progress(
1798#ifdef HAVE_DISK_STORAGE 1919#ifdef HAVE_DISK_STORAGE
@@ -1804,10 +1925,10 @@ bool tagtree_insert_selection_playlist(int position, bool queue)
1804 1925
1805 1926
1806 /* We need to set the table to allsubentries. */ 1927 /* We need to set the table to allsubentries. */
1807 dptr = tagtree_get_entry(tc, tc->selected_item); 1928 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1808 1929
1809 /* Insert a single track? */ 1930 /* Insert a single track? */
1810 if (dptr->newtable == PLAYTRACK) 1931 if (newtable == PLAYTRACK)
1811 { 1932 {
1812 if (tagtree_get_filename(tc, buf, sizeof buf) < 0) 1933 if (tagtree_get_filename(tc, buf, sizeof buf) < 0)
1813 { 1934 {
@@ -1819,29 +1940,29 @@ bool tagtree_insert_selection_playlist(int position, bool queue)
1819 return true; 1940 return true;
1820 } 1941 }
1821 1942
1822 if (dptr->newtable == NAVIBROWSE) 1943 if (newtable == NAVIBROWSE)
1823 { 1944 {
1824 tagtree_enter(tc); 1945 tagtree_enter(tc);
1825 tagtree_load(tc); 1946 tagtree_load(tc);
1826 dptr = tagtree_get_entry(tc, tc->selected_item); 1947 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1827 } 1948 }
1828 else if (dptr->newtable != ALLSUBENTRIES) 1949 else if (newtable != ALLSUBENTRIES)
1829 { 1950 {
1830 logf("unsupported table: %d", dptr->newtable); 1951 logf("unsupported table: %d", newtable);
1831 return false; 1952 return false;
1832 } 1953 }
1833 1954
1834 /* Now the current table should be allsubentries. */ 1955 /* Now the current table should be allsubentries. */
1835 if (dptr->newtable != PLAYTRACK) 1956 if (newtable != PLAYTRACK)
1836 { 1957 {
1837 tagtree_enter(tc); 1958 tagtree_enter(tc);
1838 tagtree_load(tc); 1959 tagtree_load(tc);
1839 dptr = tagtree_get_entry(tc, tc->selected_item); 1960 newtable = tagtree_get_entry(tc, tc->selected_item)->newtable;
1840 1961
1841 /* And now the newtable should be playtrack. */ 1962 /* And now the newtable should be playtrack. */
1842 if (dptr->newtable != PLAYTRACK) 1963 if (newtable != PLAYTRACK)
1843 { 1964 {
1844 logf("newtable: %d !!", dptr->newtable); 1965 logf("newtable: %d !!", newtable);
1845 tc->dirlevel = dirlevel; 1966 tc->dirlevel = dirlevel;
1846 return false; 1967 return false;
1847 } 1968 }
@@ -1886,9 +2007,9 @@ static int tagtree_play_folder(struct tree_context* c)
1886 return 0; 2007 return 0;
1887} 2008}
1888 2009
1889struct tagentry* tagtree_get_entry(struct tree_context *c, int id) 2010static struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1890{ 2011{
1891 struct tagentry *entry = (struct tagentry *)c->cache.entries; 2012 struct tagentry *entry;
1892 int realid = id - current_offset; 2013 int realid = id - current_offset;
1893 2014
1894 /* Load the next chunk if necessary. */ 2015 /* Load the next chunk if necessary. */
@@ -1905,10 +2026,22 @@ struct tagentry* tagtree_get_entry(struct tree_context *c, int id)
1905 realid = id - current_offset; 2026 realid = id - current_offset;
1906 cpu_boost(false); 2027 cpu_boost(false);
1907 } 2028 }
1908 2029
2030 entry = get_entries(c);
1909 return &entry[realid]; 2031 return &entry[realid];
1910} 2032}
1911 2033
2034char* tagtree_get_entry_name(struct tree_context *c, int id,
2035 char* buf, size_t bufsize)
2036{
2037 struct tagentry *entry = tagtree_get_entry(c, id);
2038 if (!entry)
2039 return NULL;
2040 strlcpy(buf, entry->name, bufsize);
2041 return buf;
2042}
2043
2044
1912char *tagtree_get_title(struct tree_context* c) 2045char *tagtree_get_title(struct tree_context* c)
1913{ 2046{
1914 switch (c->currtable) 2047 switch (c->currtable)
diff --git a/apps/tagtree.h b/apps/tagtree.h
index aaf5158e5b..26952b40b7 100644
--- a/apps/tagtree.h
+++ b/apps/tagtree.h
@@ -30,19 +30,14 @@
30#define TAGMENU_MAX_MENUS 32 30#define TAGMENU_MAX_MENUS 32
31#define TAGMENU_MAX_FMTS 32 31#define TAGMENU_MAX_FMTS 32
32 32
33struct tagentry {
34 char *name;
35 int newtable;
36 int extraseek;
37};
38
39bool tagtree_export(void); 33bool tagtree_export(void);
40bool tagtree_import(void); 34bool tagtree_import(void);
41void tagtree_init(void) INIT_ATTR; 35void tagtree_init(void) INIT_ATTR;
42int tagtree_enter(struct tree_context* c); 36int tagtree_enter(struct tree_context* c);
43void tagtree_exit(struct tree_context* c); 37void tagtree_exit(struct tree_context* c);
44int tagtree_load(struct tree_context* c); 38int tagtree_load(struct tree_context* c);
45struct tagentry* tagtree_get_entry(struct tree_context *c, int id); 39char* tagtree_get_entry_name(struct tree_context *c, int id,
40 char* buf, size_t bufsize);
46bool tagtree_insert_selection_playlist(int position, bool queue); 41bool tagtree_insert_selection_playlist(int position, bool queue);
47char *tagtree_get_title(struct tree_context* c); 42char *tagtree_get_title(struct tree_context* c);
48int tagtree_get_attr(struct tree_context* c); 43int tagtree_get_attr(struct tree_context* c);
diff --git a/apps/tdspeed.c b/apps/tdspeed.c
index 476995a271..69699e5bb4 100644
--- a/apps/tdspeed.c
+++ b/apps/tdspeed.c
@@ -38,6 +38,46 @@
38 38
39#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */ 39#define FIXED_BUFSIZE 3072 /* 48KHz factor 3.0 */
40 40
41static int32_t** dsp_src;
42static int handles[4];
43static int32_t *overlap_buffer[2] = { NULL, NULL };
44static int32_t *outbuf[2] = { NULL, NULL };
45
46static int move_callback(int handle, void* current, void* new)
47{
48 /* TODO */
49 (void)handle;
50 if (dsp_src)
51 {
52 int ch = (current == outbuf[0]) ? 0 : 1;
53 dsp_src[ch] = outbuf[ch] = new;
54 }
55 return BUFLIB_CB_OK;
56}
57
58static struct buflib_callbacks ops = {
59 .move_callback = move_callback,
60 .shrink_callback = NULL,
61};
62
63static int ovl_move_callback(int handle, void* current, void* new)
64{
65 /* TODO */
66 (void)handle;
67 if (dsp_src)
68 {
69 int ch = (current == overlap_buffer[0]) ? 0 : 1;
70 overlap_buffer[ch] = new;
71 }
72 return BUFLIB_CB_OK;
73}
74
75static struct buflib_callbacks ovl_ops = {
76 .move_callback = ovl_move_callback,
77 .shrink_callback = NULL,
78};
79
80
41static struct tdspeed_state_s 81static struct tdspeed_state_s
42{ 82{
43 bool stereo; 83 bool stereo;
@@ -51,39 +91,47 @@ static struct tdspeed_state_s
51 int32_t *ovl_buff[2]; /* overlap buffer */ 91 int32_t *ovl_buff[2]; /* overlap buffer */
52} tdspeed_state; 92} tdspeed_state;
53 93
54static int32_t *overlap_buffer[2] = { NULL, NULL };
55static int32_t *outbuf[2] = { NULL, NULL };
56
57void tdspeed_init(void) 94void tdspeed_init(void)
58{ 95{
59 int handle;
60
61 if (!global_settings.timestretch_enabled) 96 if (!global_settings.timestretch_enabled)
62 return; 97 return;
63 98
64 /* Allocate buffers */ 99 /* Allocate buffers */
65 if (overlap_buffer[0] == NULL) 100 if (overlap_buffer[0] == NULL)
66 { 101 {
67 handle = core_alloc("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t)); 102 handles[0] = core_alloc_ex("tdspeed ovl left", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
68 overlap_buffer[0] = core_get_data(handle); 103 overlap_buffer[0] = core_get_data(handles[0]);
69 } 104 }
70 if (overlap_buffer[1] == NULL) 105 if (overlap_buffer[1] == NULL)
71 { 106 {
72 handle = core_alloc("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t)); 107 handles[1] = core_alloc_ex("tdspeed ovl right", FIXED_BUFSIZE * sizeof(int32_t), &ovl_ops);
73 overlap_buffer[1] = core_get_data(handle); 108 overlap_buffer[1] = core_get_data(handles[1]);
74 } 109 }
75 if (outbuf[0] == NULL) 110 if (outbuf[0] == NULL)
76 { 111 {
77 handle = core_alloc("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 112 handles[2] = core_alloc_ex("tdspeed left", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
78 outbuf[0] = core_get_data(handle); 113 outbuf[0] = core_get_data(handles[2]);
79 } 114 }
80 if (outbuf[1] == NULL) 115 if (outbuf[1] == NULL)
81 { 116 {
82 handle = core_alloc("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t)); 117 handles[3] = core_alloc_ex("tdspeed right", TDSPEED_OUTBUFSIZE * sizeof(int32_t), &ops);
83 outbuf[1] = core_get_data(handle); 118 outbuf[1] = core_get_data(handles[3]);
84 } 119 }
85} 120}
86 121
122void tdspeed_finish(void)
123{
124 for(unsigned i = 0; i < ARRAYLEN(handles); i++)
125 {
126 if (handles[i] > 0)
127 {
128 core_free(handles[i]);
129 handles[i] = 0;
130 }
131 }
132 overlap_buffer[0] = overlap_buffer[1] = NULL;
133 outbuf[0] = outbuf[1] = NULL;
134}
87 135
88bool tdspeed_config(int samplerate, bool stereo, int32_t factor) 136bool tdspeed_config(int samplerate, bool stereo, int32_t factor)
89{ 137{
@@ -390,6 +438,7 @@ long tdspeed_est_input_size(long size)
390 438
391int tdspeed_doit(int32_t *src[], int count) 439int tdspeed_doit(int32_t *src[], int count)
392{ 440{
441 dsp_src = src;
393 count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] }, 442 count = tdspeed_apply( (int32_t *[2]) { outbuf[0], outbuf[1] },
394 src, count, 0, TDSPEED_OUTBUFSIZE); 443 src, count, 0, TDSPEED_OUTBUFSIZE);
395 444
diff --git a/apps/tdspeed.h b/apps/tdspeed.h
index c3b7fc4635..e91eeb1701 100644
--- a/apps/tdspeed.h
+++ b/apps/tdspeed.h
@@ -36,7 +36,8 @@
36#define GET_STRETCH(pitch, speed) \ 36#define GET_STRETCH(pitch, speed) \
37 ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch) 37 ((speed * PITCH_SPEED_100 + pitch / 2L) / pitch)
38 38
39void tdspeed_init(void) INIT_ATTR; 39void tdspeed_init(void);
40void tdspeed_finish(void);
40bool tdspeed_config(int samplerate, bool stereo, int32_t factor); 41bool tdspeed_config(int samplerate, bool stereo, int32_t factor);
41long tdspeed_est_output_size(void); 42long tdspeed_est_output_size(void);
42long tdspeed_est_input_size(long size); 43long tdspeed_est_input_size(long size);
diff --git a/apps/tree.c b/apps/tree.c
index 211ddb2f9b..c7484ff420 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -104,12 +104,18 @@ static int ft_play_dirname(char* name);
104static void ft_play_filename(char *dir, char *file); 104static void ft_play_filename(char *dir, char *file);
105static void say_filetype(int attr); 105static void say_filetype(int attr);
106 106
107static struct entry* get_entry_at(struct tree_context *t, int index) 107struct entry* tree_get_entries(struct tree_context *t)
108{ 108{
109 struct entry* entries = t->cache.entries; 109 return core_get_data(t->cache.entries_handle);
110}
111
112struct entry* tree_get_entry_at(struct tree_context *t, int index)
113{
114 struct entry* entries = tree_get_entries(t);
110 return &entries[index]; 115 return &entries[index];
111} 116}
112 117
118
113static const char* tree_get_filename(int selected_item, void *data, 119static const char* tree_get_filename(int selected_item, void *data,
114 char *buffer, size_t buffer_len) 120 char *buffer, size_t buffer_len)
115{ 121{
@@ -122,12 +128,12 @@ static const char* tree_get_filename(int selected_item, void *data,
122 128
123 if (id3db) 129 if (id3db)
124 { 130 {
125 return tagtree_get_entry(&tc, selected_item)->name; 131 return tagtree_get_entry_name(&tc, selected_item, buffer, buffer_len);
126 } 132 }
127 else 133 else
128#endif 134#endif
129 { 135 {
130 struct entry* e = get_entry_at(local_tc, selected_item); 136 struct entry* e = tree_get_entry_at(local_tc, selected_item);
131 name = e->name; 137 name = e->name;
132 attr = e->attr; 138 attr = e->attr;
133 } 139 }
@@ -169,7 +175,7 @@ static int tree_get_filecolor(int selected_item, void * data)
169 if (*tc.dirfilter == SHOW_ID3DB) 175 if (*tc.dirfilter == SHOW_ID3DB)
170 return -1; 176 return -1;
171 struct tree_context * local_tc=(struct tree_context *)data; 177 struct tree_context * local_tc=(struct tree_context *)data;
172 struct entry* e = get_entry_at(local_tc, selected_item); 178 struct entry* e = tree_get_entry_at(local_tc, selected_item);
173 return filetype_get_color(e->name, e->attr); 179 return filetype_get_color(e->name, e->attr);
174} 180}
175#endif 181#endif
@@ -185,7 +191,7 @@ static enum themable_icons tree_get_fileicon(int selected_item, void * data)
185 else 191 else
186#endif 192#endif
187 { 193 {
188 struct entry* e = get_entry_at(local_tc, selected_item); 194 struct entry* e = tree_get_entry_at(local_tc, selected_item);
189 return filetype_get_icon(e->attr); 195 return filetype_get_icon(e->attr);
190 } 196 }
191} 197}
@@ -197,16 +203,17 @@ static int tree_voice_cb(int selected_item, void * data)
197 int attr=0; 203 int attr=0;
198#ifdef HAVE_TAGCACHE 204#ifdef HAVE_TAGCACHE
199 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB; 205 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
206 char buf[AVERAGE_FILENAME_LENGTH*2];
200 207
201 if (id3db) 208 if (id3db)
202 { 209 {
203 attr = tagtree_get_attr(local_tc); 210 attr = tagtree_get_attr(local_tc);
204 name = tagtree_get_entry(local_tc, selected_item)->name; 211 name = tagtree_get_entry_name(local_tc, selected_item, buf, sizeof(buf));
205 } 212 }
206 else 213 else
207#endif 214#endif
208 { 215 {
209 struct entry* e = get_entry_at(local_tc, selected_item); 216 struct entry* e = tree_get_entry_at(local_tc, selected_item);
210 name = e->name; 217 name = e->name;
211 attr = e->attr; 218 attr = e->attr;
212 } 219 }
@@ -329,7 +336,7 @@ static int tree_get_file_position(char * filename)
329 /* use lastfile to determine the selected item (default=0) */ 336 /* use lastfile to determine the selected item (default=0) */
330 for (i=0; i < tc.filesindir; i++) 337 for (i=0; i < tc.filesindir; i++)
331 { 338 {
332 e = get_entry_at(&tc, i); 339 e = tree_get_entry_at(&tc, i);
333 if (!strcasecmp(e->name, filename)) 340 if (!strcasecmp(e->name, filename))
334 return(i); 341 return(i);
335 } 342 }
@@ -531,7 +538,7 @@ char* get_current_file(char* buffer, size_t buffer_len)
531 return NULL; 538 return NULL;
532#endif 539#endif
533 540
534 struct entry* e = get_entry_at(&tc, tc.selected_item); 541 struct entry* e = tree_get_entry_at(&tc, tc.selected_item);
535 if (getcwd(buffer, buffer_len)) 542 if (getcwd(buffer, buffer_len))
536 { 543 {
537 if (tc.dirlength) 544 if (tc.dirlength)
@@ -650,7 +657,6 @@ static int dirbrowse(void)
650 657
651 gui_synclist_draw(&tree_lists); 658 gui_synclist_draw(&tree_lists);
652 while(1) { 659 while(1) {
653 struct entry *entries = tc.cache.entries;
654 bool restore = false; 660 bool restore = false;
655 if (tc.dirlevel < 0) 661 if (tc.dirlevel < 0)
656 tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */ 662 tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */
@@ -666,8 +672,9 @@ static int dirbrowse(void)
666 if ( numentries == 0 ) 672 if ( numentries == 0 )
667 break; 673 break;
668 674
675 short attr = tree_get_entry_at(&tc, tc.selected_item)->attr;
669 if ((tc.browse->flags & BROWSE_SELECTONLY) && 676 if ((tc.browse->flags & BROWSE_SELECTONLY) &&
670 !(entries[tc.selected_item].attr & ATTR_DIRECTORY)) 677 !(attr & ATTR_DIRECTORY))
671 { 678 {
672 tc.browse->flags |= BROWSE_SELECTED; 679 tc.browse->flags |= BROWSE_SELECTED;
673 get_current_file(tc.browse->buf, tc.browse->bufsize); 680 get_current_file(tc.browse->buf, tc.browse->bufsize);
@@ -792,15 +799,14 @@ static int dirbrowse(void)
792 else 799 else
793#endif 800#endif
794 { 801 {
795 attr = entries[tc.selected_item].attr; 802 struct entry *entry = tree_get_entry_at(&tc, tc.selected_item);
803 attr = entry->attr;
796 804
797 if (currdir[1]) /* Not in / */ 805 if (currdir[1]) /* Not in / */
798 snprintf(buf, sizeof buf, "%s/%s", 806 snprintf(buf, sizeof buf, "%s/%s",
799 currdir, 807 currdir, entry->name);
800 entries[tc.selected_item].name);
801 else /* In / */ 808 else /* In / */
802 snprintf(buf, sizeof buf, "/%s", 809 snprintf(buf, sizeof buf, "/%s", entry->name);
803 entries[tc.selected_item].name);
804 } 810 }
805 onplay_result = onplay(buf, attr, curr_context, hotkey); 811 onplay_result = onplay(buf, attr, curr_context, hotkey);
806 } 812 }
@@ -999,10 +1005,36 @@ int rockbox_browse(struct browse_context *browse)
999 return ret_val; 1005 return ret_val;
1000} 1006}
1001 1007
1008static int move_callback(int handle, void* current, void* new)
1009{
1010 struct tree_cache* cache = &tc.cache;
1011 if (cache->lock_count > 0)
1012 return BUFLIB_CB_CANNOT_MOVE;
1013
1014 size_t diff = new - current;
1015 /* FIX_PTR makes sure to not accidentally update static allocations */
1016#define FIX_PTR(x) \
1017 { if ((void*)x > current && (void*)x < (current+cache->name_buffer_size)) x+= diff; }
1018
1019 if (handle == cache->name_buffer_handle)
1020 { /* update entry structs, *even if they are struct tagentry */
1021 struct entry *this = core_get_data(cache->entries_handle);
1022 struct entry *last = this + cache->max_entries;
1023 for(; this < last; this++)
1024 FIX_PTR(this->name);
1025 }
1026 /* nothing to do if entries moved */
1027 return BUFLIB_CB_OK;
1028}
1029
1030static struct buflib_callbacks ops = {
1031 .move_callback = move_callback,
1032 .shrink_callback = NULL,
1033};
1034
1002void tree_mem_init(void) 1035void tree_mem_init(void)
1003{ 1036{
1004 /* initialize tree context struct */ 1037 /* initialize tree context struct */
1005 int handle;
1006 struct tree_cache* cache = &tc.cache; 1038 struct tree_cache* cache = &tc.cache;
1007 memset(&tc, 0, sizeof(tc)); 1039 memset(&tc, 0, sizeof(tc));
1008 tc.dirfilter = &global_settings.dirfilter; 1040 tc.dirfilter = &global_settings.dirfilter;
@@ -1010,12 +1042,14 @@ void tree_mem_init(void)
1010 1042
1011 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH * 1043 cache->name_buffer_size = AVERAGE_FILENAME_LENGTH *
1012 global_settings.max_files_in_dir; 1044 global_settings.max_files_in_dir;
1013 handle = core_alloc("tree names", cache->name_buffer_size); 1045 cache->name_buffer_handle = core_alloc_ex("tree names",
1014 cache->name_buffer = core_get_data(handle); 1046 cache->name_buffer_size,
1047 &ops);
1015 1048
1016 cache->max_entries = global_settings.max_files_in_dir; 1049 cache->max_entries = global_settings.max_files_in_dir;
1017 handle = core_alloc("tree entries", cache->max_entries*(sizeof(struct entry))); 1050 cache->entries_handle = core_alloc_ex("tree entries",
1018 cache->entries = core_get_data(handle); 1051 cache->max_entries*(sizeof(struct entry)),
1052 &ops);
1019 tree_get_filetypes(&filetypes, &filetypes_count); 1053 tree_get_filetypes(&filetypes, &filetypes_count);
1020} 1054}
1021 1055
diff --git a/apps/tree.h b/apps/tree.h
index c07b92f298..2b296050d3 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -26,26 +26,30 @@
26#include <file.h> 26#include <file.h>
27#include "icon.h" 27#include "icon.h"
28 28
29/* keep this struct compatible (total size and name member)
30 * with struct tagtree_entry (tagtree.h) */
29struct entry { 31struct entry {
30 short attr; /* FAT attributes + file type flags */
31 unsigned long time_write; /* Last write time */
32 char *name; 32 char *name;
33 int attr; /* FAT attributes + file type flags */
34 unsigned time_write; /* Last write time */
33}; 35};
34 36
35
36#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ 37#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
37#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ 38#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */
38#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ 39#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */
39 40
40struct tree_context; 41struct tree_context;
42
41struct tree_cache { 43struct tree_cache {
42 /* A big buffer with plenty of entry structs, 44 /* A big buffer with plenty of entry structs, contains all files and dirs
43 * contains all files and dirs in the current 45 * in the current dir (with filters applied)
44 * dir (with filters applied) */ 46 * Note that they're buflib-allocated and can therefore possibly move
45 void* entries; 47 * They need to be locked if used around yielding functions */
46 char* name_buffer; 48 int entries_handle; /* handle to the entry cache */
47 int max_entries; /* Max entries in the cache */ 49 int name_buffer_handle; /* handle to the name cache */
48 int name_buffer_size; /* in bytes */ 50 int max_entries; /* Max entries in the cache */
51 int name_buffer_size; /* in bytes */
52 volatile int lock_count; /* non-0 if buffers may not move */
49}; 53};
50 54
51struct browse_context { 55struct browse_context {
@@ -95,6 +99,10 @@ struct tree_context {
95 struct browse_context *browse; 99 struct browse_context *browse;
96}; 100};
97 101
102/*
103 * Call one of the two below after yields since the entrys may move inbetween */
104struct entry* tree_get_entries(struct tree_context *t);
105struct entry* tree_get_entry_at(struct tree_context *t, int index);
98void tree_drawlists(void); 106void tree_drawlists(void);
99void tree_mem_init(void) INIT_ATTR; 107void tree_mem_init(void) INIT_ATTR;
100void tree_gui_init(void) INIT_ATTR; 108void tree_gui_init(void) INIT_ATTR;
@@ -108,6 +116,14 @@ void browse_context_init(struct browse_context *browse,
108int rockbox_browse(struct browse_context *browse); 116int rockbox_browse(struct browse_context *browse);
109bool create_playlist(void); 117bool create_playlist(void);
110void resume_directory(const char *dir); 118void resume_directory(const char *dir);
119static inline void tree_lock_cache(struct tree_context *t)
120{
121 t->cache.lock_count++;
122}
123static inline void tree_unlock_cache(struct tree_context *t)
124{
125 t->cache.lock_count--;
126}
111#ifdef WIN32 127#ifdef WIN32
112/* it takes an int on windows */ 128/* it takes an int on windows */
113#define getcwd_size_t int 129#define getcwd_size_t int
diff --git a/firmware/buflib.c b/firmware/buflib.c
index 51cf86bf5b..880357ccf4 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -192,10 +192,6 @@ handle_table_shrink(struct buflib_context *ctx)
192static bool 192static bool
193move_block(struct buflib_context* ctx, union buflib_data* block, int shift) 193move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
194{ 194{
195#if 1 /* moving temporarily disabled */
196 (void)ctx;(void)block;(void)shift;
197 return false;
198#else
199 char* new_start; 195 char* new_start;
200 union buflib_data *new_block, *tmp = block[1].handle; 196 union buflib_data *new_block, *tmp = block[1].handle;
201 struct buflib_callbacks *ops = block[2].ops; 197 struct buflib_callbacks *ops = block[2].ops;
@@ -218,7 +214,6 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
218 memmove(new_block, block, block->val * sizeof(union buflib_data)); 214 memmove(new_block, block, block->val * sizeof(union buflib_data));
219 215
220 return true; 216 return true;
221#endif
222} 217}
223 218
224/* Compact allocations and handle table, adjusting handle pointers as needed. 219/* Compact allocations and handle table, adjusting handle pointers as needed.
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c
index 334801ce57..6b2260def3 100644
--- a/firmware/common/dircache.c
+++ b/firmware/common/dircache.c
@@ -66,12 +66,21 @@ struct fdbind_queue {
66 int fd; 66 int fd;
67}; 67};
68 68
69/* Exported structures. */ 69/* Unions with char to make pointer arithmetic simpler and avoid casting */
70struct dircache_entry { 70struct dircache_entry {
71 struct dirinfo info; 71 struct dirinfo info;
72 struct dircache_entry *next; 72 union {
73 struct dircache_entry *up; 73 struct dircache_entry *next;
74 struct dircache_entry *down; 74 char* next_char;
75 };
76 union {
77 struct dircache_entry *up;
78 char* up_char;
79 };
80 union {
81 struct dircache_entry *down;
82 char* down_char;
83 };
75 long startcluster; 84 long startcluster;
76 char *d_name; 85 char *d_name;
77}; 86};
@@ -130,6 +139,44 @@ static inline struct dircache_entry* get_entry(int id)
130 return &dircache_root[id]; 139 return &dircache_root[id];
131} 140}
132 141
142/* flag to make sure buffer doesn't move due to other allocs.
143 * this is set to true completely during dircache build */
144static bool dont_move = false;
145static int dircache_handle;
146static int move_callback(int handle, void* current, void* new)
147{
148 (void)handle;
149 if (dont_move)
150 return BUFLIB_CB_CANNOT_MOVE;
151
152 /* relocate the cache */
153 ptrdiff_t diff = new - current;
154 for(unsigned i = 0; i < entry_count; i++)
155 {
156 if (dircache_root[i].d_name)
157 dircache_root[i].d_name += diff;
158 if (dircache_root[i].next_char)
159 dircache_root[i].next_char += diff;
160 if (dircache_root[i].up_char)
161 dircache_root[i].up_char += diff;
162 if (dircache_root[i].down_char)
163 dircache_root[i].down_char += diff;
164 }
165 dircache_root = new;
166
167 d_names_start -= diff;
168 d_names_end -= diff;
169 dot -= diff;
170 dotdot -= diff;
171
172 return BUFLIB_CB_OK;
173}
174
175static struct buflib_callbacks ops = {
176 .move_callback = move_callback,
177 .shrink_callback = NULL,
178};
179
133#ifdef HAVE_EEPROM_SETTINGS 180#ifdef HAVE_EEPROM_SETTINGS
134/** 181/**
135 * Open the dircache file to save a snapshot on disk 182 * Open the dircache file to save a snapshot on disk
@@ -573,10 +620,11 @@ int dircache_load(void)
573 } 620 }
574 621
575 allocated_size = maindata.size + DIRCACHE_RESERVE; 622 allocated_size = maindata.size + DIRCACHE_RESERVE;
576 int handle = core_alloc("dircache", allocated_size); 623 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
577 dircache_root = core_get_data(handle); 624 /* block movement during upcoming I/O */
578 /* needs to be struct-size aligned so that the pointer arithmetic below works */ 625 dont_move = true;
579 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 626 dircache_root = core_get_data(dircache_handle);
627 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
580 entry_count = maindata.entry_count; 628 entry_count = maindata.entry_count;
581 appflags = maindata.appflags; 629 appflags = maindata.appflags;
582 630
@@ -608,8 +656,9 @@ int dircache_load(void)
608 dotdot = dot - sizeof(".."); 656 dotdot = dot - sizeof("..");
609 657
610 /* d_names are in reverse order, so the last entry points to the first string */ 658 /* d_names are in reverse order, so the last entry points to the first string */
611 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start, 659 ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start;
612 offset_entries = maindata.root_entry - dircache_root; 660 ptrdiff_t offset_entries = maindata.root_entry - dircache_root;
661 offset_entries *= sizeof(struct dircache_entry); /* make it bytes */
613 662
614 /* offset_entries is less likely to differ, so check if it's 0 in the loop 663 /* offset_entries is less likely to differ, so check if it's 0 in the loop
615 * offset_d_names however is almost always non-zero, since dircache_save() 664 * offset_d_names however is almost always non-zero, since dircache_save()
@@ -625,12 +674,12 @@ int dircache_load(void)
625 674
626 if (offset_entries == 0) 675 if (offset_entries == 0)
627 continue; 676 continue;
628 if (dircache_root[i].next) 677 if (dircache_root[i].next_char)
629 dircache_root[i].next -= offset_entries; 678 dircache_root[i].next_char -= offset_entries;
630 if (dircache_root[i].up) 679 if (dircache_root[i].up_char)
631 dircache_root[i].up -= offset_entries; 680 dircache_root[i].up_char -= offset_entries;
632 if (dircache_root[i].down) 681 if (dircache_root[i].down_char)
633 dircache_root[i].down -= offset_entries; 682 dircache_root[i].down_char -= offset_entries;
634 } 683 }
635 } 684 }
636 685
@@ -640,6 +689,7 @@ int dircache_load(void)
640 logf("Done, %ld KiB used", dircache_size / 1024); 689 logf("Done, %ld KiB used", dircache_size / 1024);
641 dircache_initialized = true; 690 dircache_initialized = true;
642 memset(fd_bindings, 0, sizeof(fd_bindings)); 691 memset(fd_bindings, 0, sizeof(fd_bindings));
692 dont_move = false;
643 693
644 return 0; 694 return 0;
645} 695}
@@ -660,6 +710,7 @@ int dircache_save(void)
660 return -1; 710 return -1;
661 711
662 logf("Saving directory cache"); 712 logf("Saving directory cache");
713 dont_move = true;
663 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); 714 fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666);
664 715
665 maindata.magic = DIRCACHE_MAGIC; 716 maindata.magic = DIRCACHE_MAGIC;
@@ -698,7 +749,7 @@ int dircache_save(void)
698 return -4; 749 return -4;
699 } 750 }
700 751
701 752 dont_move = false;
702 return 0; 753 return 0;
703} 754}
704#endif /* HAVE_EEPROM_SETTINGS */ 755#endif /* HAVE_EEPROM_SETTINGS */
@@ -720,6 +771,7 @@ static int dircache_do_rebuild(void)
720 /* reset dircache and alloc root entry */ 771 /* reset dircache and alloc root entry */
721 entry_count = 0; 772 entry_count = 0;
722 root_entry = allocate_entry(); 773 root_entry = allocate_entry();
774 dont_move = true;
723 775
724#ifdef HAVE_MULTIVOLUME 776#ifdef HAVE_MULTIVOLUME
725 append_position = root_entry; 777 append_position = root_entry;
@@ -740,6 +792,7 @@ static int dircache_do_rebuild(void)
740 cpu_boost(false); 792 cpu_boost(false);
741 dircache_size = 0; 793 dircache_size = 0;
742 dircache_initializing = false; 794 dircache_initializing = false;
795 dont_move = false;
743 return -2; 796 return -2;
744 } 797 }
745 cpu_boost(false); 798 cpu_boost(false);
@@ -765,7 +818,8 @@ static int dircache_do_rebuild(void)
765 if (allocated_size - dircache_size < DIRCACHE_RESERVE) 818 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
766 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); 819 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
767 } 820 }
768 821
822 dont_move = false;
769 return 1; 823 return 1;
770} 824}
771 825
@@ -790,7 +844,8 @@ static void dircache_thread(void)
790#endif 844#endif
791 case DIRCACHE_BUILD: 845 case DIRCACHE_BUILD:
792 thread_enabled = true; 846 thread_enabled = true;
793 dircache_do_rebuild(); 847 if (dircache_do_rebuild() < 0)
848 core_free(dircache_handle);
794 thread_enabled = false; 849 thread_enabled = false;
795 break ; 850 break ;
796 851
@@ -848,11 +903,10 @@ int dircache_build(int last_size)
848 903
849 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) 904 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
850 { 905 {
851 int handle;
852 allocated_size = last_size + DIRCACHE_RESERVE; 906 allocated_size = last_size + DIRCACHE_RESERVE;
853 handle = core_alloc("dircache", allocated_size); 907 dircache_handle = core_alloc_ex("dircache", allocated_size, &ops);
854 dircache_root = core_get_data(handle); 908 dircache_root = core_get_data(dircache_handle);
855 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry)); 909 ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*));
856 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; 910 d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1;
857 dircache_size = 0; 911 dircache_size = 0;
858 thread_enabled = true; 912 thread_enabled = true;
@@ -869,10 +923,10 @@ int dircache_build(int last_size)
869 * after generation the buffer will be compacted with DIRCACHE_RESERVE 923 * after generation the buffer will be compacted with DIRCACHE_RESERVE
870 * free bytes inbetween */ 924 * free bytes inbetween */
871 size_t got_size; 925 size_t got_size;
872 int handle = core_alloc_maximum("dircache", &got_size, NULL); 926 dircache_handle = core_alloc_maximum("dircache", &got_size, &ops);
873 char* buf = core_get_data(handle); 927 char* buf = core_get_data(dircache_handle);
874 dircache_root = (struct dircache_entry*)ALIGN_UP(buf, 928 dircache_root = (struct dircache_entry*)ALIGN_UP(buf,
875 sizeof(struct dircache_entry)); 929 sizeof(struct dircache_entry*));
876 d_names_start = d_names_end = buf + got_size - 1; 930 d_names_start = d_names_end = buf + got_size - 1;
877 dircache_size = 0; 931 dircache_size = 0;
878 generate_dot_d_names(); 932 generate_dot_d_names();
@@ -909,11 +963,11 @@ int dircache_build(int last_size)
909 allocated_size = (d_names_end - buf); 963 allocated_size = (d_names_end - buf);
910 reserve_used = 0; 964 reserve_used = 0;
911 965
912 core_shrink(handle, dircache_root, allocated_size); 966 core_shrink(dircache_handle, dircache_root, allocated_size);
913 return res; 967 return res;
914fail: 968fail:
915 dircache_disable(); 969 dircache_disable();
916 core_free(handle); 970 core_free(dircache_handle);
917 return res; 971 return res;
918} 972}
919 973
@@ -928,7 +982,9 @@ void* dircache_steal_buffer(size_t *size)
928 *size = 0; 982 *size = 0;
929 return NULL; 983 return NULL;
930 } 984 }
931 985
986 /* since we give up the buffer (without freeing), it must not move anymore */
987 dont_move = true;
932 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); 988 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used);
933 989
934 return dircache_root; 990 return dircache_root;
diff --git a/firmware/core_alloc.c b/firmware/core_alloc.c
index 75dfc75b86..2250f5c664 100644
--- a/firmware/core_alloc.c
+++ b/firmware/core_alloc.c
@@ -6,7 +6,6 @@
6 6
7/* not static so it can be discovered by core_get_data() */ 7/* not static so it can be discovered by core_get_data() */
8struct buflib_context core_ctx; 8struct buflib_context core_ctx;
9
10void core_allocator_init(void) 9void core_allocator_init(void)
11{ 10{
12 buffer_init(); 11 buffer_init();