diff --git a/apps/SOURCES b/apps/SOURCES
index 53a67fd..10bb3dd 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -87,6 +87,7 @@ gui/pitchscreen.c
 #ifdef HAVE_QUICKSCREEN
 gui/quickscreen.c
 #endif
+gui/tagcache_config.c
 
 gui/wps.c
 gui/scrollbar.c
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 09a66f3..500ed82 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -152,9 +152,11 @@ void list_draw(struct screen *display, struct gui_synclist *list)
     int icon_yoffset = 0; /* to center the icon */
     bool show_title;
     struct viewport *list_text_vp = &list_text[screen];
-
+    int indent = 0;
+    int char_width;
     line_height = parent->line_height;
     display->set_viewport(parent);
+    char_width = display->getstringsize("M", NULL, NULL);
     display->clear_viewport();
     display->scroll_stop(list_text_vp);
     *list_text_vp = *parent;
@@ -247,9 +249,29 @@ void list_draw(struct screen *display, struct gui_synclist *list)
         unsigned char *entry_name;
         int text_pos = 0;
         int line = i - start;
+        indent = 0;
         s = list->callback_get_item_name(i, list->data, entry_buffer,
                                          sizeof(entry_buffer));
         entry_name = P2STR(s);
+        while (*entry_name == '\t')
+        {
+            indent++;
+            entry_name++;
+        }
+        if (indent)
+        {
+            if (icon_width)
+                indent *= icon_width;
+            else
+                indent *= char_width;
+        }
+        if (indent)
+        {
+            list_icons.x += indent;
+            list_text_vp->x += indent;
+            list_text_vp->width -= indent;
+        }
+            
         display->set_viewport(list_text_vp);
         style = STYLE_DEFAULT;
         /* position the string at the correct offset place */
@@ -351,6 +373,12 @@ void list_draw(struct screen *display, struct gui_synclist *list)
                             line*line_height + draw_offset + icon_yoffset,
                             Icon_Cursor);
         }
+        if (indent)
+        {
+            list_icons.x -= indent;
+            list_text_vp->x -= indent;
+            list_text_vp->width += indent;
+        }
     }
     display->set_viewport(parent);
     display->update_viewport();
diff --git a/apps/gui/tagcache_config.c b/apps/gui/tagcache_config.c
new file mode 100644
index 0000000..f232ca6
--- /dev/null
+++ b/apps/gui/tagcache_config.c
@@ -0,0 +1,392 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ *
+ * Copyright (C) 2011 Jonathan Gordon
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "inttypes.h"
+#include "config.h"
+#include "core_alloc.h"
+#include "filetypes.h"
+#include "lang.h"
+#include "language.h"
+#include "list.h"
+#include "plugin.h"
+
+
+/*
+ * Order for changing child states:
+ * 1) expand folder (skip to 2 if empty)
+ * 2) collapse and select
+ * 3) unselect
+ */
+
+enum child_state {
+    EXPANDED,
+    SELECTED,
+    COLLAPSED
+};
+    
+
+struct child {
+    char* name;
+    struct folder *folder;
+    enum child_state state;
+};
+
+struct folder {
+    char *name;
+    struct child *children;
+    int children_count;
+    int depth;
+
+    struct folder* previous;
+};
+
+static char *buffer_front, *buffer_end;
+static char* folder_alloc(size_t size)
+{
+    char* retval;
+    /* 32-bit aligned */
+    size = (size + 3) & ~3;
+    if (buffer_front + size > buffer_end)
+    {
+        return NULL;
+    }
+    retval = buffer_front;
+    buffer_front += size;
+    return retval;
+}
+static char* folder_alloc_from_end(size_t size)
+{
+    if (buffer_end - size < buffer_front)
+    {
+        return NULL;
+    }
+    buffer_end -= size;
+    return buffer_end;
+}
+    
+
+static char* get_full_path(struct folder *start)
+{
+    static char buffer[MAX_PATH];
+    char *buf = &buffer[MAX_PATH-1];
+    int remaining = MAX_PATH-1;
+    struct folder *this = start;
+    if (start == NULL)
+        return "/";
+    buf[0] = '\0';
+    buf--;
+    remaining--;
+    while (this)
+    {
+        int len = strlen(this->name);
+        if (len + 1 > remaining)
+            return NULL;
+        buf -= len + 1;
+        memcpy(buf, this->name, len);
+        buf[len] = '/';
+        this = this->previous;
+    }
+    if (remaining < 1)
+        return NULL;
+    /* remove the trailing / */
+    buf[strlen(buf)-1] = '\0';
+    return buf;
+}
+/* support function for qsort() */
+static int compare(const void* p1, const void* p2)
+{
+    struct child *left = (struct child*)p1;
+    struct child *right = (struct child*)p2;
+    return strcasecmp(left->name, right->name);
+}
+
+static struct folder* load_folder(struct folder* parent, char *folder)
+{
+    DIR *dir;
+    char* path = get_full_path(parent);
+    char fullpath[MAX_PATH];
+    struct dirent *entry;
+    struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
+    int child_count = 0;
+    char *first_child = NULL;
+
+    snprintf(fullpath, MAX_PATH, "%s/%s", parent ? path : "", folder);
+    
+    if (!this)
+        return NULL;
+    dir = opendir(fullpath);
+    if (!dir)
+        return NULL;
+    this->previous = parent;
+    this->name = folder;
+    this->children = NULL;
+    this->children_count = 0;
+    this->depth = parent ? parent->depth + 1 : -1;
+    
+    while ((entry = readdir(dir))) {
+        int len = strlen((char *)entry->d_name);
+        struct dirinfo info;
+
+        info = dir_get_info(dir, entry);
+
+        /* skip anything not a directory */
+        if ((info.attribute & ATTR_DIRECTORY) == 0) {
+            continue;
+        }
+        /* skip directories . and .. */
+        if ((((len == 1) && (!strncmp((char *)entry->d_name, ".", 1))) ||
+             ((len == 2) && (!strncmp((char *)entry->d_name, "..", 2))))) {
+            continue;
+        }
+        char *name = folder_alloc_from_end(len+1);
+        if (!name)
+            return NULL;
+        memcpy(name, (char *)entry->d_name, len+1);
+        child_count++;
+        first_child = name;
+    }
+    closedir(dir);
+    /* now put the names in the array */
+    this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
+    if (!this->children)
+        return NULL;
+    while (child_count)
+    {
+        this->children[this->children_count].name = first_child;
+        this->children[this->children_count].folder = NULL;
+        this->children[this->children_count].state = COLLAPSED;
+        this->children_count++;
+        first_child += strlen(first_child) + 1;
+        child_count--;
+    }
+    qsort(this->children, this->children_count, sizeof(struct child), compare);
+    return this;
+}
+
+static int count_items(struct folder *start)
+{
+    int count = 0;
+    int i;
+
+    for (i=0; i<start->children_count; i++)
+    {
+        struct child *foo = &start->children[i];
+        if (foo->state == EXPANDED)
+            count += count_items(foo->folder);
+        count++;
+    }
+    return count;
+}    
+
+static struct child* find_index(struct folder *start, int index, struct folder **parent)
+{
+    int i = 0;
+    while (i < start->children_count)
+    {
+        struct child *foo = &start->children[i];
+        if (i == index)
+        {
+            *parent = start;
+            return foo;
+        }
+        i++;
+        if (foo->state == EXPANDED)
+        {
+            struct child *bar = find_index(foo->folder, index - i, parent);
+            if (bar)
+            {
+                return bar;
+            }
+            index -= count_items(foo->folder);
+        }
+    }
+    return NULL;
+}
+
+static const char * folder_get_name(int selected_item, void * data,
+                                   char * buffer, size_t buffer_len)
+{
+    (void)buffer_len;
+    struct folder *root = (struct folder*)data;
+    struct folder *parent = NULL;
+    struct child *this = find_index(root, selected_item, &parent);
+    
+    buffer[0] = '\0';
+    if (parent->depth >= 0)
+    {
+        int i = 0;
+        while (i < parent->depth + 1)
+        {
+            strcat(buffer, "\t");
+            i++;
+        }
+    }
+    strcat(buffer, this->name);
+    return buffer;
+}
+
+static enum themable_icons folder_get_icon(int selected_item, void * data)
+{
+    struct folder *root = (struct folder*)data;
+    struct folder *parent = NULL;
+    struct child *this = find_index(root, selected_item, &parent);
+
+    switch (this->state)
+    {
+        case SELECTED:
+            return Icon_Cursor;
+        case COLLAPSED:
+            return Icon_Folder;
+        case EXPANDED:
+            return Icon_Submenu;
+    }
+    return Icon_NOICON;
+}
+
+static int folder_action_callback(int action, struct gui_synclist *list)
+{
+    struct folder *root = (struct folder*)list->data;
+    if (action == ACTION_STD_OK)
+    {
+        struct folder *parent = NULL;
+        struct child *this = find_index(root, list->selected_item, &parent);        
+        switch (this->state)
+        {
+            case EXPANDED:
+                this->state = SELECTED;
+                break;
+            case SELECTED:
+                this->state = COLLAPSED;
+                if (this->folder && this->folder->children_count == 0)
+                    this->state = COLLAPSED;
+                break;
+            case COLLAPSED:
+                if (this->folder == NULL)
+                    this->folder = load_folder(parent, this->name);
+                this->state = this->folder->children_count == 0 ?
+                        SELECTED : EXPANDED;
+        }
+        list->nb_items = count_items(root);
+        return ACTION_REDRAW;
+    }
+    return action;
+}
+
+static struct child* find_from_filename(char* filename, struct folder *root)
+{
+    char *slash = strchr(filename, '/');
+    int i = 0;
+    if (slash)
+        *slash = '\0';
+    if (!root)
+        return NULL;
+
+    while (i < root->children_count)
+    {
+        struct child *this = &root->children[i];
+        if (!strcasecmp(this->name, filename))
+        {
+            if (!slash)
+                return this;
+            if (!this->folder)
+                this->folder = load_folder(root, this->name);
+            this->state = EXPANDED;
+            return find_from_filename(slash+1, this->folder);
+        }
+        i++;
+    }
+    return NULL;
+}
+
+static int readline_callback(int n, char *buf, void *parameters)
+{
+    (void)n;
+    struct folder *root = (struct folder*)parameters;
+    char* slash = strchr(buf, '/');
+    struct child *item = find_from_filename(slash ? slash + 1 : buf, root);
+    if (item)
+        item->state = SELECTED;
+    return 0;
+}
+
+static void save_folders(struct folder *root, int fd)
+{
+    int i = 0;
+
+    while (i < root->children_count)
+    {
+        struct child *this = &root->children[i];
+        if (this->state == SELECTED)
+        {
+            if (this->folder)
+                snprintf(buffer_front, buffer_end - buffer_front,
+                        "%s\n", get_full_path(this->folder));
+            else
+                snprintf(buffer_front, buffer_end - buffer_front,
+                        "%s/%s\n", get_full_path(root), this->name);
+            write(fd, buffer_front, strlen(buffer_front));
+        }
+        else if (this->state == EXPANDED)
+            save_folders(this->folder, fd);
+        i++;
+    }
+}
+        
+
+bool tagcache_do_config(void)
+{
+    struct folder *root;
+    struct simplelist_info info;
+    size_t buf_size;
+    int fd;
+    char buf[512];
+
+    buffer_front = plugin_get_buffer(&buf_size);
+    buffer_end = buffer_front + buf_size;
+    root = load_folder(NULL, "");
+    
+    fd = open_utf8(ROCKBOX_DIR "/database.txt", O_RDONLY);
+    if (fd >= 0)
+    {
+        fast_readline(fd, buf, sizeof buf, root, readline_callback);
+        close(fd);
+    }
+
+   // while (1)
+    {
+        simplelist_info_init(&info, /*ID2P(LANG_SELECT_DATABASE_DIRS)*/"scandirs",
+                count_items(root), root);
+        info.get_name = folder_get_name;
+        info.action_callback = folder_action_callback;
+        info.get_icon = folder_get_icon;
+        simplelist_show_list(&info);
+    }
+    fd = open_utf8(ROCKBOX_DIR "/database.txt", O_CREAT|O_TRUNC|O_RDWR);
+    if (fd >= 0)
+    {
+        save_folders(root, fd);
+        close(fd);
+        return true;
+    }
+    return false;
+    
+}
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 627fda8..083c870 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12993,3 +12993,17 @@
     *: "Restart Sleep Timer On Keypress"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_SELECT_DATABASE_DIRS
+  desc: in settings_menu
+  user: core
+  <source>
+    *: "Select directories to scan"
+  </source>
+  <dest>
+    *: "Select directories to scan"
+  </dest>
+  <voice>
+    *: "Select directories to scan"
+  </voice>
+</phrase>
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c
index 659a7f2..608fb6f 100644
--- a/apps/menus/settings_menu.c
+++ b/apps/menus/settings_menu.c
@@ -65,6 +65,21 @@ static void tagcache_update_with_splash(void)
     splash(HZ*2, ID2P(LANG_TAGCACHE_FORCE_UPDATE_SPLASH));
 }
 
+bool tagcache_do_config(void);
+static int dirs_to_scan(void)
+{
+    if (tagcache_do_config())
+    {
+        static const char *lines[] = {ID2P(LANG_TAGCACHE_BUSY),
+                                      ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
+        static const struct text_message message = {lines, 2};
+        
+        if (gui_syncyesno_run(&message, NULL, NULL) == YESNO_YES)
+            tagcache_rebuild_with_splash();
+    }
+    return 0;
+}
+
 #ifdef HAVE_TC_RAMCACHE
 MENUITEM_SETTING(tagcache_ram, &global_settings.tagcache_ram, NULL);
 #endif
@@ -82,12 +97,17 @@ MENUITEM_FUNCTION(tc_export, 0, ID2P(LANG_TAGCACHE_EXPORT),
 MENUITEM_FUNCTION(tc_import, 0, ID2P(LANG_TAGCACHE_IMPORT),
                     (int(*)(void))tagtree_import, NULL,
                     NULL, Icon_NOICON);
+MENUITEM_FUNCTION(tc_paths, 0, ID2P(LANG_SELECT_DATABASE_DIRS),
+                    dirs_to_scan, NULL, NULL, Icon_NOICON);
+                    
 MAKE_MENU(tagcache_menu, ID2P(LANG_TAGCACHE), 0, Icon_NOICON,
 #ifdef HAVE_TC_RAMCACHE
                 &tagcache_ram,
 #endif
                 &tagcache_autoupdate, &tc_init, &tc_update, &runtimedb,
-                &tc_export, &tc_import);
+                &tc_export, &tc_import
+                ,&tc_paths
+                );
 #endif /* HAVE_TAGCACHE */
 /*    TAGCACHE MENU                */
 /***********************************/
diff --git a/apps/misc.c b/apps/misc.c
index b1def59..ccb3a85 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -1005,6 +1005,37 @@ void format_time(char* buf, int buf_size, long t)
     }
 }
 
+/**
+ * Splits str at each occurence of split_char and puts the substrings into vector,
+ * but at most vector_lenght items. Empty substrings are ignored.
+ *
+ * Modifies str by replacing each split_char following a substring with nul
+ *
+ * Returns the number of substrings found, i.e. the number of valid strings
+ * in vector
+ */
+int split_string(char *str, const char split_char, char *vector[], const int vector_length)
+{
+    int i;
+    char *p = str;
+
+    /* skip leading splitters */
+    while(*p == split_char) p++;
+
+    /* *p in the condition takes care of trailing splitters */
+    for(i = 0; p && *p && i < vector_length; i++)
+    {
+        vector[i] = p;
+        if ((p = strchr(p, split_char)))
+        {
+            *p++ = '\0';
+            while(*p == split_char) p++; /* skip successive splitters */
+        }
+    }
+
+    return i;
+}
+
 
 /** Open a UTF-8 file and set file descriptor to first byte after BOM.
  *  If no BOM is present this behaves like open().
diff --git a/apps/misc.h b/apps/misc.h
index 2206894..509e7af 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -73,6 +73,7 @@ extern int show_logo(void);
 #define BOM_UTF_16_BE "\xfe\xff"
 #define BOM_UTF_16_SIZE 2
 
+int split_string(char *str, const char needle, char *vector[], int vector_length);
 int open_utf8(const char* pathname, int flags);
 
 #ifdef BOOTFILE
diff --git a/apps/plugins/random_folder_advance_config.c b/apps/plugins/random_folder_advance_config.c
index f459aa0..d9147d1 100644
--- a/apps/plugins/random_folder_advance_config.c
+++ b/apps/plugins/random_folder_advance_config.c
@@ -5,9 +5,8 @@
  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
  *                     \/            \/     \/    \/            \/
- * $Id$
  *
- * Copyright (C) 2006 Jonathan Gordon
+ * Copyright (C) 2011 Jonathan Gordon
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -18,360 +17,377 @@
  * KIND, either express or implied.
  *
  ****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "inttypes.h"
 #include "config.h"
+#include "core_alloc.h"
+#include "filetypes.h"
+#include "language.h"
+#include "list.h"
+#include "lang.h"
+#include "settings.h"
 #include "plugin.h"
-#include "file.h"
 
 
+/*
+ * Order for changing child states:
+ * 1) expand folder (skip to 2 if empty)
+ * 2) collapse and select
+ * 3) unselect
+ */
 
-static bool cancel;
-static int fd;
-static int dirs_count;
-static int lasttick;
-#define RFA_FILE ROCKBOX_DIR "/folder_advance_list.dat"
-#define RFADIR_FILE ROCKBOX_DIR "/folder_advance_dir.txt"
-#define RFA_FILE_TEXT ROCKBOX_DIR "/folder_advance_list.txt"
-#define MAX_REMOVED_DIRS 10
+enum child_state {
+    EXPANDED,
+    SELECTED,
+    COLLAPSED
+};
+
+
+struct child {
+    char* name;
+    struct folder *folder;
+    enum child_state state;
+};
 
-char *buffer = NULL;
-size_t buffer_size;
-int num_replaced_dirs = 0;
-char removed_dirs[MAX_REMOVED_DIRS][MAX_PATH];
-struct file_format {
-    int count;
-    char folder[][MAX_PATH];
+struct folder {
+    char *name;
+    struct child *children;
+    int children_count;
+    int depth;
+    
+    struct folder* previous;
 };
-struct file_format *list = NULL;
 
-static void update_screen(bool clear)
+static char *buffer_front, *buffer_end;
+static char* folder_alloc(size_t size)
+{
+    char* retval;
+    /* 32-bit aligned */
+    size = (size + 3) & ~3;
+    if (buffer_front + size > buffer_end)
+    {
+        return NULL;
+    }
+    retval = buffer_front;
+    buffer_front += size;
+    return retval;
+}
+static char* folder_alloc_from_end(size_t size)
 {
-    char buf[15];
+    if (buffer_end - size < buffer_front)
+    {
+        return NULL;
+    }
+    buffer_end -= size;
+    return buffer_end;
+}
 
-    rb->snprintf(buf,sizeof(buf),"Folders: %d",dirs_count);
-    FOR_NB_SCREENS(i)
+
+static char* get_full_path(struct folder *start)
+{
+    static char buffer[MAX_PATH];
+    char *buf = &buffer[MAX_PATH-1];
+    int remaining = MAX_PATH-1;
+    struct folder *this = start;
+    if (start == NULL)
+        return "/";
+    buf[0] = '\0';
+    buf--;
+    remaining--;
+    while (this)
     {
-        if(clear)
-            rb->screens[i]->clear_display();
-        rb->screens[i]->putsxy(0,0,buf);
-        rb->screens[i]->update();
+        int len = rb->strlen(this->name);
+        if (len + 1 > remaining)
+            return NULL;
+        buf -= len + 1;
+        rb->memcpy(buf, this->name, len);
+        buf[len] = '/';
+        this = this->previous;
     }
+    if (remaining < 1)
+        return NULL;
+    /* remove the trailing / */
+    buf[strlen(buf)-1] = '\0';
+    return buf;
+}
+/* support function for qsort() */
+static int compare(const void* p1, const void* p2)
+{
+    struct child *left = (struct child*)p1;
+    struct child *right = (struct child*)p2;
+    return rb->strcasecmp(left->name, right->name);
 }
 
-static void traversedir(char* location, char* name)
+static struct folder* load_folder(struct folder* parent, char *folder)
 {
+    DIR *dir;
+    char* path = get_full_path(parent);
+    char fullpath[MAX_PATH];
     struct dirent *entry;
-    DIR* dir;
-    char fullpath[MAX_PATH], path[MAX_PATH];
-    bool check = false;
-    int i;
-
-    rb->snprintf(fullpath, sizeof(fullpath), "%s/%s", location, name);
+    struct folder* this = (struct folder*)folder_alloc(sizeof(struct folder));
+    int child_count = 0;
+    char *first_child = NULL;
+    
+    rb->snprintf(fullpath, MAX_PATH, "%s/%s", parent ? path : "", folder);
+    
+    if (!this)
+        return NULL;
     dir = rb->opendir(fullpath);
-    if (dir) {
-        entry = rb->readdir(dir);
-        while (entry) {
-            if (cancel)
-                break;
-            /* Skip .. and . */
-            if (entry->d_name[0] == '.')
-            {
-                if (    !rb->strcmp(entry->d_name,".")
-                     || !rb->strcmp(entry->d_name,"..")
-                     || !rb->strcmp(entry->d_name,".rockbox"))
-                    check = false;
-                else check = true;
-            }
-            else check = true;
-
-        /* check if path is removed directory, if so dont enter it */
-        rb->snprintf(path, MAX_PATH, "%s/%s", fullpath, entry->d_name);
-        while(path[0] == '/')
-            rb->strlcpy(path, path + 1, sizeof(path));
-        for(i = 0; i < num_replaced_dirs; i++)
-        {
-            if(!rb->strcmp(path, removed_dirs[i]))
-            {
-                check = false;
-                break;
-            }
+    if (!dir)
+        return NULL;
+    this->previous = parent;
+    this->name = folder;
+    this->children = NULL;
+    this->children_count = 0;
+    this->depth = parent ? parent->depth + 1 : -1;
+    
+    while ((entry = rb->readdir(dir))) {
+        int len = strlen((char *)entry->d_name);
+        struct dirinfo info;
+        
+        info = rb->dir_get_info(dir, entry);
+        
+        /* skip anything not a directory */
+        if ((info.attribute & ATTR_DIRECTORY) == 0) {
+            continue;
         }
-
-            if (check)
-            {
-                struct dirinfo info = rb->dir_get_info(dir, entry);
-                if (info.attribute & ATTR_DIRECTORY) {
-                    char *start;
-                    dirs_count++;
-                    rb->snprintf(path,MAX_PATH,"%s/%s",fullpath,entry->d_name);
-                    start = &path[rb->strlen(path)];
-                    rb->memset(start,0,&path[MAX_PATH-1]-start);
-                    rb->write(fd,path,MAX_PATH);
-                    traversedir(fullpath, entry->d_name);
-                }
-            }
-            if (*rb->current_tick - lasttick > (HZ/2)) {
-                update_screen(false);
-                lasttick = *rb->current_tick;
-                if (rb->action_userabort(TIMEOUT_NOBLOCK))
-                {
-                    cancel = true;
-                    break;
-                }
+        /* skip directories . and .. */
+        if ((((len == 1) && (!strncmp((char *)entry->d_name, ".", 1))) ||
+            ((len == 2) && (!strncmp((char *)entry->d_name, "..", 2))))) {
+            continue;
             }
-
-            entry = rb->readdir(dir);
-        }
-        rb->closedir(dir);
+            char *name = folder_alloc_from_end(len+1);
+        if (!name)
+            return NULL;
+        rb->memcpy(name, (char *)entry->d_name, len+1);
+        child_count++;
+        first_child = name;
     }
+    rb->closedir(dir);
+    /* now put the names in the array */
+    this->children = (struct child*)folder_alloc(sizeof(struct child) * child_count);
+    if (!this->children)
+        return NULL;
+    while (child_count)
+    {
+        this->children[this->children_count].name = first_child;
+        this->children[this->children_count].folder = NULL;
+        this->children[this->children_count].state = COLLAPSED;
+        this->children_count++;
+        first_child += strlen(first_child) + 1;
+        child_count--;
+    }
+    rb->qsort(this->children, this->children_count, sizeof(struct child), compare);
+    return this;
 }
 
-static bool custom_dir(void)
+static int count_items(struct folder *start)
 {
-    DIR* dir_check;
-    char *starts, line[MAX_PATH], formatted_line[MAX_PATH];
-    static int fd2;
-    char buf[11];
-    int i, errors = 0;
+    int count = 0;
+    int i;
+    
+    for (i=0; i<start->children_count; i++)
+    {
+        struct child *foo = &start->children[i];
+        if (foo->state == EXPANDED)
+            count += count_items(foo->folder);
+        count++;
+    }
+    return count;
+}    
 
-    /* populate removed dirs array */
-    if((fd2 = rb->open(RFADIR_FILE,O_RDONLY)) >= 0)
+static struct child* find_index(struct folder *start, int index, struct folder **parent)
+{
+    int i = 0;
+    while (i < start->children_count)
     {
-        while ((rb->read_line(fd2, line, MAX_PATH - 1)) > 0)
+        struct child *foo = &start->children[i];
+        if (i == index)
+        {
+            *parent = start;
+            return foo;
+        }
+        i++;
+        if (foo->state == EXPANDED)
         {
-            if ((line[0] == '-') && (line[1] == '/') &&
-                     (num_replaced_dirs < MAX_REMOVED_DIRS))
+            struct child *bar = find_index(foo->folder, index - i, parent);
+            if (bar)
             {
-                num_replaced_dirs ++;
-                rb->strlcpy(removed_dirs[num_replaced_dirs - 1], line + 2,
-                                sizeof(line));
+                return bar;
             }
+            index -= count_items(foo->folder);
         }
-        rb->close(fd2);
     }
+    return NULL;
+}
 
-    if((fd2 = rb->open(RFADIR_FILE,O_RDONLY)) >= 0)
+static const char * folder_get_name(int selected_item, void * data,
+                                    char * buffer, size_t buffer_len)
+{
+    (void)buffer_len;
+    struct folder *root = (struct folder*)data;
+    struct folder *parent = NULL;
+    struct child *this = find_index(root, selected_item, &parent);
+    
+    buffer[0] = '\0';
+    if (parent->depth >= 0)
     {
-        while ((rb->read_line(fd2, line, MAX_PATH - 1)) > 0)
+        int i = 0;
+        while (i < parent->depth + 1)
         {
-            /* blank lines and removed dirs ignored */
-            if (rb->strlen(line) && ((line[0] != '-') || (line[1] != '/')))
-            {
-                /* remove preceeding '/'s from the line */
-                while(line[0] == '/')
-                    rb->strlcpy(line, line + 1, sizeof(line));
-
-                rb->snprintf(formatted_line, MAX_PATH, "/%s", line);
-
-                dir_check = rb->opendir(formatted_line);
-
-                if (dir_check)
-                {
-                    rb->closedir(dir_check);
-                    starts = &formatted_line[rb->strlen(formatted_line)];
-                    rb->memset(starts, 0, &formatted_line[MAX_PATH-1]-starts);
-                    bool write_line = true;
-
-                    for(i = 0; i < num_replaced_dirs; i++)
-                    {
-                        if(!rb->strcmp(line, removed_dirs[i]))
-                        {
-                             write_line = false;
-                             break;
-                        }
-                    }
-
-                    if(write_line)
-                    {
-                        dirs_count++;
-                        rb->write(fd, formatted_line, MAX_PATH);
-                    }
-
-                    traversedir("", line);
-                }
-                else
-                {
-                     errors ++;
-                     rb->snprintf(buf,sizeof(buf),"Not found:");
-                     FOR_NB_SCREENS(i)
-                     {
-                         rb->screens[i]->puts(0,0,buf);
-                         rb->screens[i]->puts(0, errors, line);
-                     }
-                     update_screen(false);
-                }
-            }
+            rb->strcat(buffer, "\t");
+            i++;
         }
-        rb->close(fd2);
-        if(errors)
-            /* Press button to continue */
-            rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK); 
     }
-    else
-        return false;
-    return true;
+    rb->strcat(buffer, this->name);
+    return buffer;
 }
 
-static void generate(void)
+static enum themable_icons folder_get_icon(int selected_item, void * data)
 {
-    dirs_count = 0;
-    cancel = false;
-    fd = rb->open(RFA_FILE,O_CREAT|O_WRONLY, 0666);
-    rb->write(fd,&dirs_count,sizeof(int));
-    if (fd < 0)
+    struct folder *root = (struct folder*)data;
+    struct folder *parent = NULL;
+    struct child *this = find_index(root, selected_item, &parent);
+    
+    switch (this->state)
     {
-        rb->splashf(HZ, "Couldnt open %s", RFA_FILE);
-        return;
+        case SELECTED:
+            return Icon_Cursor;
+        case COLLAPSED:
+            return Icon_Folder;
+        case EXPANDED:
+            return Icon_Submenu;
     }
-#ifndef HAVE_LCD_CHARCELLS
-    update_screen(true);
-#endif
-    lasttick = *rb->current_tick;
-
-    if(!custom_dir())
-        traversedir("", "");
-
-    rb->lseek(fd,0,SEEK_SET);
-    rb->write(fd,&dirs_count,sizeof(int));
-    rb->close(fd);
-    rb->splash(HZ, "Done");
+    return Icon_NOICON;
 }
-
-static const char* list_get_name_cb(int selected_item, void* data,
-                                    char* buf, size_t buf_len)
+static bool list_dirty;
+static int folder_action_callback(int action, struct gui_synclist *list)
 {
-    (void)data;
-    rb->strlcpy(buf, list->folder[selected_item], buf_len);
-    return buf;
+    struct folder *root = (struct folder*)list->data;
+    if (action == ACTION_STD_OK)
+    {
+        struct folder *parent = NULL;
+        struct child *this = find_index(root, list->selected_item, &parent);        
+        switch (this->state)
+        {
+            case EXPANDED:
+                this->state = SELECTED;
+                break;
+            case SELECTED:
+                this->state = COLLAPSED;
+                if (this->folder && this->folder->children_count == 0)
+                    this->state = COLLAPSED;
+                break;
+            case COLLAPSED:
+                if (this->folder == NULL)
+                    this->folder = load_folder(parent, this->name);
+                this->state = this->folder->children_count == 0 ?
+                SELECTED : EXPANDED;
+        }
+        list->nb_items = count_items(root);
+        list_dirty = true;
+        return ACTION_REDRAW;
+    }
+    return action;
 }
 
-static int load_list(void)
+static struct child* find_from_filename(char* filename, struct folder *root)
 {
-    int myfd = rb->open(RFA_FILE,O_RDONLY);
-    if (myfd < 0)
-        return -1;
-    buffer = rb->plugin_get_audio_buffer(&buffer_size);
-    if (!buffer)
+    char *slash = strchr(filename, '/');
+    int i = 0;
+    if (slash)
+        *slash = '\0';
+    if (!root)
+        return NULL;
+    
+    while (i < root->children_count)
     {
-        return -2;
+        struct child *this = &root->children[i];
+        if (!rb->strcasecmp(this->name, filename))
+        {
+            if (!slash)
+                return this;
+            if (!this->folder)
+                this->folder = load_folder(root, this->name);
+            this->state = EXPANDED;
+            return find_from_filename(slash+1, this->folder);
+        }
+        i++;
     }
-    
-    rb->read(myfd,buffer,buffer_size);
-    rb->close(myfd);
-    list = (struct file_format *)buffer;
-    
+    return NULL;
+}
+
+static int readline_callback(int n, char *buf, void *parameters)
+{
+    (void)n;
+    struct folder *root = (struct folder*)parameters;
+    char* slash = strchr(buf, '/');
+    struct child *item = find_from_filename(slash ? slash + 1 : buf, root);
+    if (item)
+        item->state = SELECTED;
     return 0;
 }
 
-static int save_list(void)
+static void save_folders(struct folder *root, int fd)
 {
-    int myfd = rb->creat(RFA_FILE, 0666);
-    if (myfd < 0)
-    {
-        rb->splash(HZ, "Could Not Open " RFA_FILE);
-        return -1;
-    }
-    int dirs_count = 0, i = 0;
-    rb->write(myfd,&dirs_count,sizeof(int));
-    for ( ;i<list->count;i++)
+    int i = 0;
+    
+    while (i < root->children_count)
     {
-        if (list->folder[i][0] != ' ')
+        struct child *this = &root->children[i];
+        if (this->state == SELECTED)
         {
-            dirs_count++;
-            rb->write(myfd,list->folder[i],MAX_PATH);
+            if (this->folder)
+                rb->snprintf(buffer_front, buffer_end - buffer_front,
+                         "%s\n", get_full_path(this->folder));
+                else
+                    rb->snprintf(buffer_front, buffer_end - buffer_front,
+                             "%s/%s\n", get_full_path(root), this->name);
+                    rb->write(fd, buffer_front, strlen(buffer_front));
         }
+        else if (this->state == EXPANDED)
+            save_folders(this->folder, fd);
+        i++;
     }
-    rb->lseek(myfd,0,SEEK_SET);
-    rb->write(myfd,&dirs_count,sizeof(int));
-    rb->close(myfd);
-    
-    return 1;
 }
 
-static int edit_list(void)
+void tagcache_do_config(void)
 {
-    struct gui_synclist lists;
-    bool exit = false;
-    int button,i;
-    int selection, ret = 0;
+    struct folder *root;
+    struct simplelist_info info;
+    size_t buf_size;
+    int fd;
+    char buf[512];
     
-    /* load the dat file if not already done */
-    if ((list == NULL || list->count == 0) && (i = load_list()) != 0)
+    buffer_front = rb->plugin_get_buffer(&buf_size);
+    buffer_end = buffer_front + buf_size;
+    root = load_folder(NULL, "");
+    
+    fd = rb->open_utf8(ROCKBOX_DIR "/database.txt", O_RDONLY);
+    if (fd >= 0)
     {
-        rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i);
-        return -1;
+        //FIXME rb->fast_readline(fd, buf, sizeof buf, root, readline_callback);
+        rb->close(fd);
     }
+    list_dirty = false;
     
-    dirs_count = list->count;
+    rb->simplelist_info_init(&info, "Choose folders for random advance",
+                         count_items(root), root);
+    info.get_name = folder_get_name;
+    info.action_callback = folder_action_callback;
+    info.get_icon = folder_get_icon;
+    rb->simplelist_show_list(&info);
     
-    rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL);
-    rb->gui_synclist_set_icon_callback(&lists,NULL);
-    rb->gui_synclist_set_nb_items(&lists,list->count);
-    rb->gui_synclist_limit_scroll(&lists,true);
-    rb->gui_synclist_select_item(&lists, 0);
-    
-    while (!exit)
+    if (list_dirty )//FIXME&& rb->yesno_pop(ID2P(LANG_SAVE_CHANGES)))
     {
-        rb->gui_synclist_draw(&lists);
-        button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
-        if (rb->gui_synclist_do_button(&lists,&button,LIST_WRAP_UNLESS_HELD))
-            continue;
-        selection = rb->gui_synclist_get_sel_pos(&lists);
-        switch (button)
-        {
-            case ACTION_STD_OK:
-                list->folder[selection][0] = ' ';
-                list->folder[selection][1] = '\0';
-                break;
-            case ACTION_STD_CONTEXT:
-            {
-                int len;
-                MENUITEM_STRINGLIST(menu, "Remove Menu", NULL,
-                                    "Remove Folder", "Remove Folder Tree");
-
-                switch (rb->do_menu(&menu, NULL, NULL, false))
-                {
-                    case 0:
-                        list->folder[selection][0] = ' ';
-                        list->folder[selection][1] = '\0';
-                        break;
-                    case 1:
-                    {
-                        char temp[MAX_PATH];
-                        rb->strcpy(temp,list->folder[selection]);
-                        len = rb->strlen(temp);
-                        for (i=0;i<list->count;i++)
-                        {
-                            if (!rb->strncmp(list->folder[i],temp,len))
-                            {
-                                list->folder[i][0] = ' ';
-                                list->folder[i][1] = '\0';
-                            }
-                        }
-                    }
-                    break;
-                }
-            }
-            break;
-            case ACTION_STD_CANCEL:
-            {
-                MENUITEM_STRINGLIST(menu, "Exit Menu", NULL,
-                                    "Save and Exit", "Ignore Changes and Exit");
-
-                switch (rb->do_menu(&menu, NULL, NULL, false))
-                {
-                    case 0:
-                        save_list();
-                    case 1:
-                        exit = true;
-                        ret = -2;
-                }
-            }
-            break;
-        }
+        fd = rb->open_utf8(ROCKBOX_DIR "/database.txt", O_CREAT|O_TRUNC|O_RDWR);
+        if (fd >= 0)
+            save_folders(root, fd);
     }
-    return ret;
 }
-
+#if 0
 static int export_list_to_file_text(void)
 {
     int i = 0;
@@ -617,7 +633,7 @@ static enum plugin_status main_menu(void)
     }
     return PLUGIN_OK;
 }
-
+#endif
 enum plugin_status plugin_start(const void* parameter)
 {
     (void)parameter;
@@ -625,7 +641,7 @@ enum plugin_status plugin_start(const void* parameter)
     rb->touchscreen_set_mode(rb->global_settings->touch_mode);
 #endif
 
-    cancel = false;
+    //cancel = false;
     
-    return main_menu();
+    return 0;//main_menu();
 }
diff --git a/apps/tagcache.c b/apps/tagcache.c
index ef642b1..4b21b40 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -4254,28 +4254,87 @@ static bool check_deleted_files(void)
     
     return true;
 }
-
-
+#include "string.h"
+#include "misc.h"
+struct scanfolder {
+    struct scanfolder *next;
+    char name[0];
+} *head = NULL;
+static size_t scanfolder_buf_size;
+static char * scanfolder_buf;
+static int readline_callback(int n, char *buf, void *parameters)
+{
+    (void)n;
+    (void)parameters;
+    struct scanfolder *this = head;
+    size_t size = (strlen(buf) + sizeof(struct scanfolder) + 3) & ~3;
+    if (size >= scanfolder_buf_size)
+        return 1;
+    if (!this)
+    {
+        head = scanfolder_buf;
+        scanfolder_buf += size;
+        scanfolder_buf_size -= size;
+        memcpy(head->name, buf, strlen(buf));
+        head->next = NULL;
+        return 0;
+    }
+    while (this->next)
+        this = this->next;
+    this->next = scanfolder_buf;
+    scanfolder_buf += size;
+    scanfolder_buf_size -= size;
+    memcpy(this->name, buf, strlen(buf));
+    this->next = NULL;
+    return 0;
+}
+    
 /* Note that this function must not be inlined, otherwise the whole point
  * of having the code in a separate function is lost.
  */
 static void __attribute__ ((noinline)) check_ignore(const char *dirname,
     int *ignore, int *unignore)
 {
-    char newpath[MAX_PATH];
-
-    /* check for a database.ignore file */
-    snprintf(newpath, MAX_PATH, "%s/database.ignore", dirname);
-    *ignore = file_exists(newpath);
-    /* check for a database.unignore file */
-    snprintf(newpath, MAX_PATH, "%s/database.unignore", dirname);
-    *unignore = file_exists(newpath);
+    struct scanfolder *this = head;
+    int len;
+    if (!strcmp("/", dirname))
+    {
+        *ignore = 0;
+        return;
+    }    
+    *ignore = 1;
+    *unignore = 0;
+    while (*ignore && this)
+    {
+        len = strlen(this->name);
+        if (!strncmp(dirname, this->name, len) && 
+            (this->name[len] == '/' || this->name[len] == '\0'))
+            *ignore = 0;
+        this = this->next;
+    }
 }
 
+#define MAX_STATIC_ROOTS 8
 static struct search_roots_ll {
     const char *path;
     struct search_roots_ll * next;
-} roots_ll;
+} roots_ll[MAX_STATIC_ROOTS];
+
+/* check if the path is already included in the search roots, by the
+ * means that the path itself or one of its parents folders is in the list */
+static bool search_root_exists(const char *path)
+{
+    struct search_roots_ll *this;
+    for(this = &roots_ll[0]; this; this = this->next)
+    {
+        size_t root_len = strlen(this->path);
+        /* check if the link target is inside of an existing search root
+         * don't add if target is inside, we'll scan it later */
+        if (!strncmp(this->path, path, root_len))
+            return true;
+    }
+    return false;
+}
 
 #ifdef APPLICATION
 /*
@@ -4316,14 +4375,11 @@ static bool add_search_root(const char *name)
     if (realpath(target, abs_target) == NULL)
         return false;
 
-    for(this = &roots_ll; this; prev = this, this = this->next)
-    {
-        size_t root_len = strlen(this->path);
-        /* check if the link target is inside of an existing search root
-         * don't add if target is inside, we'll scan it later */
-        if (!strncmp(this->path, abs_target, root_len))
-            return false;
-    }
+    if (search_root_exists(abs_target))
+        return false;
+
+    /* get the end of the list */
+    for(this = &roots_ll[0]; this; prev = this, this = this->next);
 
     if (prev)
     {
@@ -4347,14 +4403,22 @@ static bool add_search_root(const char *name)
     return false;
 }
 
+static int free_search_root_single(struct search_roots_ll * start)
+{
+    if (start < &roots_ll[0] && start >= &roots_ll[MAX_STATIC_ROOTS])
+    {
+        free(start->next);
+        return sizeof(struct search_roots_ll);
+    }
+    return 0;
+}
+
 static int free_search_roots(struct search_roots_ll * start)
 {
     int ret = 0;
     if (start->next)
     {
-        ret += free_search_roots(start->next);
-        ret += sizeof(struct search_roots_ll);
-        free(start->next);
+        ret += free_search_root_single(start->next);
     }
     return ret;
 }
@@ -4382,6 +4446,11 @@ static bool check_dir(const char *dirname, int add_files)
     /* don't do anything if both ignore and unignore are there */
     if (ignore != unignore)
         add_files = unignore;
+    if (ignore)
+    {
+        closedir(dir);
+        return true;
+    }
 
     /* Recursively scan the dir. */
 #ifdef __PCTOOL__
@@ -4412,7 +4481,7 @@ static bool check_dir(const char *dirname, int add_files)
 
         processed_dir_count++;
         if (info.attribute & ATTR_DIRECTORY)
-#ifndef SIMULATOR
+#if 0 //ifndef SIMULATOR
         {   /* don't follow symlinks to dirs, but try to add it as a search root
              * this makes able to avoid looping in recursive symlinks */
             if (info.attribute & ATTR_LINK)
@@ -4459,7 +4528,8 @@ void tagcache_screensync_enable(bool state)
     tc_stat.syncscreen = state;
 }
 
-void tagcache_build(const char *path)
+
+static void do_tagcache_build(const char *path[])
 {
     struct tagcache_header header;
     bool ret;
@@ -4501,17 +4571,29 @@ void tagcache_build(const char *path)
     write(cachefd, &header, sizeof(struct tagcache_header));
 
     ret = true;
-    roots_ll.path = path;
-    roots_ll.next = NULL;
+
+    roots_ll[0].path = path[0];
+    roots_ll[0].next = NULL;
+    /* i is for the path vector, j for the roots_ll array
+     * path can be skipped , but root_ll entries can't */
+    for(int i = 1, j = 1; path[i] && j < MAX_STATIC_ROOTS; i++)
+    {
+        if (search_root_exists(path[i])) /* skip this path */
+            continue;
+
+        roots_ll[j].path = path[i];
+        roots_ll[j-1].next = &roots_ll[j];
+        j++;
+    }
+
     struct search_roots_ll * this;
     /* check_dir might add new roots */
-    for(this = &roots_ll; this; this = this->next)
+    for(this = &roots_ll[0]; this; this = this->next)
     {
         strcpy(curpath, this->path);
         ret = ret && check_dir(this->path, true);
     }
-    if (roots_ll.next)
-        free_search_roots(roots_ll.next);
+    free_search_roots(&roots_ll[0]);
 
     /* Write the header. */
     header.magic = TAGCACHE_MAGIC;
@@ -4558,6 +4640,31 @@ void tagcache_build(const char *path)
     cpu_boost(false);
 }
 
+
+void tagcache_build(void)
+{
+    int fd = open_utf8(ROCKBOX_DIR "/database.txt", O_RDONLY);
+    char *vect[MAX_STATIC_ROOTS + 1]; /* +1 to ensure NULL sentinel */
+    char str[1024];
+    char *line = str;
+    int readlen, maxlen = 1024, count = 0;
+
+    if (fd >= 0)
+    {
+        while (read_line(fd, line, maxlen) > 0)
+        {
+            vect[count++] = line;
+            line -= strlen(line);
+            line += strlen(line);
+        }
+        close(fd);
+    }
+
+    vect[count] = NULL;
+
+    do_tagcache_build((const char**)vect);
+}
+
 #ifdef HAVE_TC_RAMCACHE
 static void load_ramcache(void)
 {
@@ -4643,11 +4750,11 @@ static void tagcache_thread(void)
             case Q_REBUILD:
                 remove_files();
                 remove(TAGCACHE_FILE_TEMP);
-                tagcache_build("/");
+                tagcache_build();
                 break;
             
             case Q_UPDATE:
-                tagcache_build("/");
+                tagcache_build();
 #ifdef HAVE_TC_RAMCACHE
                 load_ramcache();
 #endif
@@ -4665,13 +4772,13 @@ static void tagcache_thread(void)
                 {
                     load_ramcache();
                     if (tc_stat.ramcache && global_settings.tagcache_autoupdate)
-                        tagcache_build("/");
+                        tagcache_build();
                 }
                 else
 #endif
                 if (global_settings.tagcache_autoupdate)
                 {
-                    tagcache_build("/");
+                    tagcache_build();
                     
                     /* This will be very slow unless dircache is enabled
                        or target is flash based, but do it anyway for
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 44161cf..c1d0df9 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -201,8 +201,6 @@ struct tagcache_search {
     int32_t idx_id;      /* Entry number in the master index. */
 };
 
-void tagcache_build(const char *path);
-
 #ifdef __PCTOOL__
 void tagcache_reverse_scan(void);
 #endif
