Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

plugins: random folder advance: misc improvements

fixes:
- traverse_dir was called recursively using two
MAX_PATH local buffers => possible stack overflow
- The import function inserted additional, incorrect,
entries when the whole line buffer was filled
- "Dirs not found" message overlapped number
of folders when generating folder list
- Final number of scanned folders wasn't displayed
- Wouldn't stop inserting when maximum number
of files in playlist was already reached
- Prevent buffer overflow when importing
- Don't write to fd after opening failed
- Use whole buffer with read_line, instead
of subtracting 1. Remove hard coded sizes
- CRs don't need to be removed in import function
(already handled by read_line)

features:
- Use insert context for *much* faster insertion
of large # of folders, and nicer progress display
- Use UI viewport
- Add progress indicator when saving or loading
- Display number of folders in edit list title
- Go back to plugin's main menu from edit list
- Only ask to save changes if list is dirty
- Warn before erasing modified playlist
- Flag successfully created playlist as modified
- Make folder scan wait for dircache
- Shorten menu item names
- Put "Play Shuffled" menu item first
- Remember selection when returning from submenus
- Go to WPS after ACTION_TREE_WPS
- Exit menu when pressing Back
- Perform an initial scan, if no data file exists
yet, when "Play Shuffled" or "Edit" is selected

Change-Id: I7df76f8fb8387888ce491d8b74b01b481e9997d3

+514 -360
+1 -1
apps/onplay.c
··· 296 296 } 297 297 298 298 playlist_insert_directory(NULL, selected_file.path, position, queue, 299 - recurse == RECURSE_ON); 299 + recurse == RECURSE_ON, NULL); 300 300 } 301 301 } 302 302
+15 -8
apps/playlist.c
··· 2221 2221 /* user abort */ 2222 2222 if (action_userabort(TIMEOUT_NOBLOCK)) 2223 2223 { 2224 - result = -1; 2224 + result = -2; 2225 2225 break; 2226 2226 } 2227 2227 ··· 2507 2507 */ 2508 2508 int playlist_insert_directory(struct playlist_info* playlist, 2509 2509 const char *dirname, int position, bool queue, 2510 - bool recurse) 2510 + bool recurse, struct playlist_insert_context* context) 2511 2511 { 2512 - int result = -1; 2513 - struct playlist_insert_context context; 2514 - result = playlist_insert_context_create(playlist, &context, 2515 - position, queue, true); 2512 + bool context_provided = context; 2513 + int result = 0; 2514 + struct playlist_insert_context c; 2515 + 2516 + if (!context_provided) 2517 + { 2518 + context = &c; 2519 + result = playlist_insert_context_create(playlist, &c, 2520 + position, queue, true); 2521 + } 2516 2522 if (result >= 0) 2517 2523 { 2518 2524 cpu_boost(true); 2519 2525 2520 2526 result = playlist_directory_tracksearch(dirname, recurse, 2521 - directory_search_callback, &context); 2527 + directory_search_callback, context); 2522 2528 2523 2529 cpu_boost(false); 2524 2530 } 2525 - playlist_insert_context_release(&context); 2531 + if (!context_provided) 2532 + playlist_insert_context_release(&c); 2526 2533 return result; 2527 2534 } 2528 2535
+1 -1
apps/playlist.h
··· 157 157 void playlist_insert_context_release(struct playlist_insert_context *context); 158 158 int playlist_insert_directory(struct playlist_info* playlist, 159 159 const char *dirname, int position, bool queue, 160 - bool recurse); 160 + bool recurse, struct playlist_insert_context *context); 161 161 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename, 162 162 int position, bool queue); 163 163 bool playlist_entries_iterate(const char *filename,
+5 -1
apps/plugin.c
··· 357 357 simplelist_show_list, 358 358 yesno_pop, 359 359 yesno_pop_confirm, 360 + 361 + /* status bar */ 360 362 sb_set_title_text, 363 + sb_set_persistent_title, 361 364 362 365 /* action handling */ 363 366 get_custom_action, ··· 738 741 playlist_insert_track, 739 742 playlist_insert_directory, 740 743 playlist_insert_playlist, 744 + playlist_insert_context_create, 745 + playlist_insert_context_release, 741 746 playlist_shuffle, 742 747 warn_on_pl_erase, 743 748 audio_play, ··· 874 879 875 880 /* new stuff at the end, sort into place next time 876 881 the API gets incompatible */ 877 - sb_set_persistent_title, 878 882 }; 879 883 880 884 static int plugin_buffer_handle;
+10 -4
apps/plugin.h
··· 178 178 * when this happens please take the opportunity to sort in 179 179 * any new functions "waiting" at the end of the list. 180 180 */ 181 - #define PLUGIN_API_VERSION 276 181 + #define PLUGIN_API_VERSION 277 182 182 183 183 /* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ 184 184 ··· 419 419 bool (*simplelist_show_list)(struct simplelist_info *info); 420 420 bool (*yesno_pop)(const char* text); 421 421 bool (*yesno_pop_confirm)(const char* text); 422 + 423 + /* status bar */ 422 424 bool (*sb_set_title_text)(const char* title, enum themable_icons icon, enum screen_type screen); 425 + bool (*sb_set_persistent_title)(const char* title, enum themable_icons icon, 426 + enum screen_type screen); 423 427 424 428 /* action handling */ 425 429 int (*get_custom_action)(int context,int timeout, ··· 863 867 const char *filename, int position, bool queue, bool sync); 864 868 int (*playlist_insert_directory)(struct playlist_info* playlist, 865 869 const char *dirname, int position, bool queue, 866 - bool recurse); 870 + bool recurse, struct playlist_insert_context *context); 867 871 int (*playlist_insert_playlist)(struct playlist_info* playlist, 868 872 const char *filename, int position, bool queue); 873 + int (*playlist_insert_context_create)(struct playlist_info* playlist, 874 + struct playlist_insert_context *context, 875 + int position, bool queue, bool progress); 876 + void (*playlist_insert_context_release)(struct playlist_insert_context *context); 869 877 int (*playlist_shuffle)(int random_seed, int start_index); 870 878 bool (*warn_on_pl_erase)(void); 871 879 void (*audio_play)(unsigned long elapsed, unsigned long offset); ··· 1026 1034 1027 1035 /* new stuff at the end, sort into place next time 1028 1036 the API gets incompatible */ 1029 - bool (*sb_set_persistent_title)(const char* title, enum themable_icons icon, 1030 - enum screen_type screen); 1031 1037 }; 1032 1038 1033 1039 /* plugin header */
+1 -1
apps/plugins/lua/rocklib.c
··· 392 392 pos = luaL_optint(L, 3, PLAYLIST_INSERT); 393 393 queue = lua_toboolean(L, 4); /* default to false */ 394 394 recurse = lua_toboolean(L, 5); /* default to false */ 395 - result = rb->playlist_insert_directory(NULL, dir, pos, queue, recurse); 395 + result = rb->playlist_insert_directory(NULL, dir, pos, queue, recurse, NULL); 396 396 break; 397 397 case PLAYL_INSERTPLAYL: 398 398 filename = luaL_checkstring(L, 2); /* only required parameter */
+474 -338
apps/plugins/random_folder_advance_config.c
··· 22 22 #include "plugin.h" 23 23 #include "file.h" 24 24 25 + #define RFA_LIST_DAT ROCKBOX_DIR "/folder_advance_list.dat" /* Folder data */ 26 + #define RFA_LIST_TXT ROCKBOX_DIR "/folder_advance_list.txt" /* Exported txt */ 27 + #define RFA_CSTM_DIR ROCKBOX_DIR "/folder_advance_dir.txt" /* Custom dirs */ 28 + #define MAX_IGNORED_DIRS 10 /* Maximum number of folders that are skipped */ 25 29 30 + struct rfa_dirs /* File format for folder data */ 31 + { 32 + int count; 33 + char dir[][MAX_PATH]; 34 + }; 26 35 27 - static bool cancel; 28 - static int fd; 29 - static int dirs_count; 30 - static int lasttick; 31 - #define RFA_FILE ROCKBOX_DIR "/folder_advance_list.dat" 32 - #define RFADIR_FILE ROCKBOX_DIR "/folder_advance_dir.txt" 33 - #define RFA_FILE_TEXT ROCKBOX_DIR "/folder_advance_list.txt" 34 - #define MAX_REMOVED_DIRS 10 35 - 36 - char *buffer = NULL; 37 - size_t buffer_size; 38 - int num_replaced_dirs = 0; 39 - char removed_dirs[MAX_REMOVED_DIRS][MAX_PATH]; 40 - struct file_format { 41 - int count; 42 - char folder[][MAX_PATH]; 36 + struct rfa_scan /* Context for single run */ 37 + { 38 + char ignored_dirs[MAX_IGNORED_DIRS][MAX_PATH]; 39 + char path[MAX_PATH]; 40 + struct rfa_dirs *dirs; 41 + char *err_dir; 42 + unsigned long last_update; 43 + int num_ignored_dirs; 44 + int num_dirs; 45 + int num_err; 46 + int fd; /* writes to RFA_LIST_DAT */ 47 + bool cancel; 48 + bool dirty; 43 49 }; 44 - struct file_format *list = NULL; 45 50 46 - static void update_screen(bool clear) 51 + static void update_screen(struct rfa_scan *scan) 47 52 { 48 53 char buf[15]; 54 + struct screen *display; 55 + struct viewport vp, *last_vp; 49 56 50 - rb->snprintf(buf,sizeof(buf),"Folders: %d",dirs_count); 57 + rb->snprintf(buf, sizeof buf, "Folders: %d ", scan->num_dirs); 58 + 51 59 FOR_NB_SCREENS(i) 52 60 { 53 - if(clear) 54 - rb->screens[i]->clear_display(); 55 - rb->screens[i]->putsxy(0,0,buf); 56 - rb->screens[i]->update(); 61 + display = rb->screens[i]; 62 + rb->viewport_set_defaults(&vp, i); 63 + last_vp = display->set_viewport(&vp); 64 + if(!scan->path[0]) 65 + display->clear_viewport(); 66 + 67 + display->puts(0, 0, buf); 68 + if (scan->num_err && scan->err_dir) 69 + { 70 + display->puts(0, 1, "Not found:"); 71 + display->puts(0, 1 + scan->num_err, scan->err_dir); 72 + } 73 + 74 + display->update_viewport(); 75 + display->set_viewport(last_vp); 57 76 } 77 + scan->last_update = *rb->current_tick; 58 78 } 59 79 60 - static void traversedir(char* location, char* name) 80 + static void traverse_path(struct rfa_scan *scan) 61 81 { 82 + DIR* dir; 62 83 struct dirent *entry; 63 - DIR* dir; 64 - char fullpath[MAX_PATH], path[MAX_PATH]; 65 - bool check = false; 66 - int i; 84 + char *p; 85 + int i, dirlen; 86 + bool skip; 87 + 88 + dir = rb->opendir(scan->path); 89 + 90 + /* remove slash from root dir; will be prepended later */ 91 + if (scan->path[0] == '/' && scan->path[1] == '\0') 92 + scan->path[0] = dirlen = 0; 93 + else 94 + dirlen = rb->strlen(scan->path); 67 95 68 - /* behave differently if we're at root to avoid 69 - duplication of the initial slash later on */ 70 - if (location[0] == '\0' && name[0] == '\0') { 71 - rb->strcpy(fullpath, ""); 72 - dir = rb->opendir("/"); 73 - } else { 74 - rb->snprintf(fullpath, sizeof(fullpath), "%s/%s", location, name); 75 - dir = rb->opendir(fullpath); 76 - } 77 - if (dir) { 78 - entry = rb->readdir(dir); 79 - while (entry) { 80 - if (cancel) 81 - break; 82 - /* Skip .. and . */ 83 - if (entry->d_name[0] == '.') 84 - { 85 - if ( !rb->strcmp(entry->d_name,".") 86 - || !rb->strcmp(entry->d_name,"..") 87 - || !rb->strcmp(entry->d_name,".rockbox")) 88 - check = false; 89 - else check = true; 90 - } 91 - else check = true; 96 + if (!dir) 97 + return; 92 98 93 - /* check if path is removed directory, if so dont enter it */ 94 - rb->snprintf(path, MAX_PATH, "%s/%s", fullpath, entry->d_name); 95 - while(path[0] == '/') 96 - rb->strlcpy(path, path + 1, sizeof(path)); 97 - for(i = 0; i < num_replaced_dirs; i++) 99 + while ((entry = rb->readdir(dir))) 100 + { 101 + if (scan->cancel) 102 + break; 103 + /* Don't check special dirs */ 104 + if (entry->d_name[0] == '.') 98 105 { 99 - if(!rb->strcmp(path, removed_dirs[i])) 100 - { 101 - check = false; 102 - break; 103 - } 106 + if (!rb->strcmp(entry->d_name, ".") || 107 + !rb->strcmp(entry->d_name, "..") || 108 + !rb->strcmp(entry->d_name, ".rockbox")) 109 + continue; 104 110 } 111 + struct dirinfo info = rb->dir_get_info(dir, entry); 112 + if (info.attribute & ATTR_DIRECTORY) 113 + { 114 + /* Append name to full path */ 115 + rb->snprintf(scan->path + dirlen, sizeof(scan->path) - dirlen, 116 + "/%s", entry->d_name); 105 117 106 - if (check) 107 - { 108 - struct dirinfo info = rb->dir_get_info(dir, entry); 109 - if (info.attribute & ATTR_DIRECTORY) { 110 - char *start; 111 - dirs_count++; 112 - rb->snprintf(path,MAX_PATH,"%s/%s",fullpath,entry->d_name); 113 - start = &path[rb->strlen(path)]; 114 - rb->memset(start,0,&path[MAX_PATH-1]-start); 115 - rb->write(fd,path,MAX_PATH); 116 - traversedir(fullpath, entry->d_name); 117 - } 118 - } 119 - if (*rb->current_tick - lasttick > (HZ/2)) { 120 - update_screen(false); 121 - lasttick = *rb->current_tick; 122 - if (rb->action_userabort(TIMEOUT_NOBLOCK)) 118 + /* Skip ignored dirs */ 119 + skip = false; 120 + for(i = 0; i < scan->num_ignored_dirs; i++) 121 + if(!rb->strcmp(scan->path, scan->ignored_dirs[i])) 123 122 { 124 - cancel = true; 123 + skip = true; 125 124 break; 126 125 } 127 - } 126 + if (skip) 127 + continue; 128 128 129 - entry = rb->readdir(dir); 130 - } 131 - rb->closedir(dir); 132 - } 133 - } 129 + scan->num_dirs++; 134 130 135 - static bool custom_dir(void) 136 - { 137 - DIR* dir_check; 138 - char *starts, line[MAX_PATH], formatted_line[MAX_PATH]; 139 - static int fd2; 140 - char buf[11]; 141 - int i, errors = 0; 131 + /* Write full path to file (erase end of buffer) */ 132 + p = &scan->path[rb->strlen(scan->path)]; 133 + rb->memset(p, 0, &scan->path[sizeof(scan->path) - 1] - p); 134 + rb->write(scan->fd, scan->path, sizeof scan->path); 142 135 143 - /* populate removed dirs array */ 144 - if((fd2 = rb->open(RFADIR_FILE,O_RDONLY)) >= 0) 145 - { 146 - while ((rb->read_line(fd2, line, MAX_PATH - 1)) > 0) 136 + /* Recursion */ 137 + traverse_path(scan); 138 + } 139 + 140 + if (*rb->current_tick - scan->last_update > (HZ/2)) 147 141 { 148 - if ((line[0] == '-') && (line[1] == '/') && 149 - (num_replaced_dirs < MAX_REMOVED_DIRS)) 142 + update_screen(scan); 143 + if (rb->action_userabort(TIMEOUT_NOBLOCK)) 150 144 { 151 - num_replaced_dirs ++; 152 - rb->strlcpy(removed_dirs[num_replaced_dirs - 1], line + 2, 153 - sizeof(line)); 145 + scan->cancel = true; 146 + break; 154 147 } 155 148 } 156 - rb->close(fd2); 149 + rb->yield(); 157 150 } 158 - 159 - if((fd2 = rb->open(RFADIR_FILE,O_RDONLY)) >= 0) 160 - { 161 - while ((rb->read_line(fd2, line, MAX_PATH - 1)) > 0) 162 - { 163 - /* blank lines and removed dirs ignored */ 164 - if (rb->strlen(line) && ((line[0] != '-') || (line[1] != '/'))) 165 - { 166 - /* remove preceeding '/'s from the line */ 167 - while(line[0] == '/') 168 - rb->strlcpy(line, line + 1, sizeof(line)); 151 + rb->closedir(dir); 152 + } 169 153 170 - rb->snprintf(formatted_line, MAX_PATH, "/%s", line); 154 + static bool custom_paths(struct rfa_scan *scan) 155 + { 156 + char line[MAX_PATH], *p; 157 + int i, fd; 158 + bool write_line; 171 159 172 - dir_check = rb->opendir(formatted_line); 160 + fd = rb->open(RFA_CSTM_DIR, O_RDONLY); 161 + if (fd < 0) 162 + return false; 173 163 174 - if (dir_check) 175 - { 176 - rb->closedir(dir_check); 177 - starts = &formatted_line[rb->strlen(formatted_line)]; 178 - rb->memset(starts, 0, &formatted_line[MAX_PATH-1]-starts); 179 - bool write_line = true; 164 + /* Populate ignored dirs array */ 165 + while ((rb->read_line(fd, line, sizeof line)) > 0) 166 + if ((line[0] == '-') && (line[1] == '/') && 167 + (scan->num_ignored_dirs < MAX_IGNORED_DIRS)) 168 + { 169 + rb->strlcpy(scan->ignored_dirs[scan->num_ignored_dirs], 170 + line + 1, sizeof(*scan->ignored_dirs)); 171 + scan->num_ignored_dirs++; 172 + } 180 173 181 - for(i = 0; i < num_replaced_dirs; i++) 182 - { 183 - if(!rb->strcmp(line, removed_dirs[i])) 184 - { 185 - write_line = false; 186 - break; 187 - } 188 - } 174 + /* Check which paths to traverse */ 175 + rb->lseek(fd, 0, SEEK_SET); 176 + while ((rb->read_line(fd, line, sizeof line)) > 0) 177 + { 178 + /* Skip blank lines or dirs to ignore */ 179 + if (line[0] == '\0' || ((line[0] == '-') && (line[1] == '/'))) 180 + continue; 189 181 190 - if(write_line) 191 - { 192 - dirs_count++; 193 - rb->write(fd, formatted_line, MAX_PATH); 194 - } 182 + /* Set path to traverse */ 183 + p = line; 184 + while(*p == '/') 185 + p++; 186 + rb->snprintf(scan->path, sizeof scan->path, "/%s", p); 187 + if (rb->dir_exists(scan->path)) 188 + { 189 + /* Erase end of path buffer */ 190 + p = &scan->path[rb->strlen(scan->path)]; 191 + rb->memset(p, 0, &scan->path[sizeof(scan->path) - 1] - p); 195 192 196 - traversedir("", line); 197 - } 198 - else 193 + /* Don't write ignored dirs to file */ 194 + write_line = true; 195 + for(i = 0; i < scan->num_ignored_dirs; i++) 196 + if(!rb->strcmp(scan->path, scan->ignored_dirs[i])) 199 197 { 200 - errors ++; 201 - rb->snprintf(buf,sizeof(buf),"Not found:"); 202 - FOR_NB_SCREENS(i) 203 - { 204 - rb->screens[i]->puts(0,0,buf); 205 - rb->screens[i]->puts(0, errors, line); 206 - } 207 - update_screen(false); 198 + write_line = false; 199 + break; 208 200 } 201 + 202 + if(write_line) 203 + { 204 + scan->num_dirs++; 205 + rb->write(scan->fd, scan->path, sizeof scan->path); 209 206 } 207 + 208 + traverse_path(scan); 209 + update_screen(scan); 210 210 } 211 - rb->close(fd2); 212 - if(errors) 213 - /* Press button to continue */ 214 - rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK); 211 + else 212 + { 213 + scan->num_err++; 214 + scan->err_dir = scan->path; 215 + update_screen(scan); 216 + scan->err_dir = NULL; 217 + } 215 218 } 216 - else 217 - return false; 219 + 220 + rb->close(fd); 221 + 222 + if(scan->num_err) 223 + rb->get_action(CONTEXT_STD, TIMEOUT_BLOCK); 224 + 218 225 return true; 219 226 } 220 227 221 - static void generate(void) 228 + static void generate(struct rfa_scan *scan) 222 229 { 223 - dirs_count = 0; 224 - cancel = false; 225 - fd = rb->open(RFA_FILE,O_CREAT|O_WRONLY, 0666); 226 - rb->write(fd,&dirs_count,sizeof(int)); 227 - if (fd < 0) 230 + bool update_title = false; 231 + 232 + /* Reset run context */ 233 + rb->memset(scan, 0, sizeof(struct rfa_scan)); 234 + 235 + FOR_NB_SCREENS(i) 236 + update_title = rb->sb_set_title_text("Scanning...", Icon_NOICON, i) || 237 + update_title; 238 + if (update_title) 239 + rb->send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); 240 + 241 + #ifdef HAVE_DIRCACHE 242 + rb->splash(0, ID2P(LANG_WAIT)); 243 + rb->dircache_wait(); 244 + #endif 245 + 246 + scan->fd = rb->open(RFA_LIST_DAT, O_CREAT|O_WRONLY, 0666); 247 + if (scan->fd < 0) 228 248 { 229 - rb->splashf(HZ, "Couldnt open %s", RFA_FILE); 249 + rb->splashf(HZ, "Couldnt open %s", RFA_LIST_DAT); 230 250 return; 231 251 } 232 - update_screen(true); 233 - lasttick = *rb->current_tick; 234 252 235 - if(!custom_dir()) 236 - traversedir("", ""); 253 + /* Write placeholder for number of folders */ 254 + rb->write(scan->fd, &scan->num_dirs, sizeof(int)); 237 255 238 - rb->lseek(fd,0,SEEK_SET); 239 - rb->write(fd,&dirs_count,sizeof(int)); 240 - rb->close(fd); 256 + update_screen(scan); 257 + if(!custom_paths(scan)) 258 + { 259 + scan->path[0] = '/'; 260 + scan->path[1] = '\0'; 261 + traverse_path(scan); 262 + update_screen(scan); 263 + } 264 + rb->lseek(scan->fd, 0, SEEK_SET); 265 + rb->write(scan->fd, &scan->num_dirs, sizeof(int)); 266 + rb->close(scan->fd); 241 267 rb->splash(HZ, "Done"); 242 268 } 243 269 244 270 static const char* list_get_name_cb(int selected_item, void* data, 245 271 char* buf, size_t buf_len) 246 272 { 247 - (void)data; 248 - rb->strlcpy(buf, list->folder[selected_item], buf_len); 273 + struct rfa_scan *scan = (struct rfa_scan *)data; 274 + rb->strlcpy(buf, scan->dirs->dir[selected_item], buf_len); 249 275 return buf; 250 276 } 251 277 252 - static int load_list(void) 278 + static int load_dirs(struct rfa_scan *scan) 253 279 { 254 - int myfd = rb->open(RFA_FILE,O_RDONLY); 255 - if (myfd < 0) 280 + char *buf; 281 + size_t buf_sz; 282 + 283 + int fd = rb->open(RFA_LIST_DAT , O_RDONLY); 284 + if (fd < 0) 256 285 return -1; 257 - buffer = rb->plugin_get_audio_buffer(&buffer_size); 258 - if (!buffer) 259 - { 286 + 287 + buf = rb->plugin_get_audio_buffer(&buf_sz); 288 + if (!buf) 260 289 return -2; 261 - } 262 290 263 - rb->read(myfd,buffer,buffer_size); 264 - rb->close(myfd); 265 - list = (struct file_format *)buffer; 291 + rb->read(fd, buf, buf_sz); 292 + rb->close(fd); 293 + scan->dirs = (struct rfa_dirs *) buf; 266 294 267 295 return 0; 268 296 } 269 297 270 - static int save_list(void) 298 + static int save_dirs(struct rfa_scan *scan) 271 299 { 272 - int myfd = rb->creat(RFA_FILE, 0666); 273 - if (myfd < 0) 300 + int num_dirs = 0, i = 0; 301 + int fd = rb->creat(RFA_LIST_DAT, 0666); 302 + if (fd < 0) 274 303 { 275 - rb->splash(HZ, "Could Not Open " RFA_FILE); 304 + rb->splash(HZ, "Could Not Open " RFA_LIST_DAT); 276 305 return -1; 277 306 } 278 - int dirs_count = 0, i = 0; 279 - rb->write(myfd,&dirs_count,sizeof(int)); 280 - for ( ;i<list->count;i++) 307 + 308 + rb->splash_progress_set_delay(HZ/2); 309 + rb->write(fd, &num_dirs, sizeof(int)); 310 + for ( ; i < scan->dirs->count; i++) 281 311 { 282 - if (list->folder[i][0] != ' ') 312 + /* (voiced) */ 313 + rb->splash_progress(i, scan->dirs->count, "%s", rb->str(LANG_WAIT)); 314 + if (scan->dirs->dir[i][0] != ' ') 283 315 { 284 - dirs_count++; 285 - rb->write(myfd,list->folder[i],MAX_PATH); 316 + num_dirs++; 317 + rb->write(fd, scan->dirs->dir[i], sizeof(*scan->dirs->dir)); 286 318 } 287 319 } 288 - rb->lseek(myfd,0,SEEK_SET); 289 - rb->write(myfd,&dirs_count,sizeof(int)); 290 - rb->close(myfd); 320 + rb->lseek(fd, 0, SEEK_SET); 321 + rb->write(fd, &num_dirs, sizeof(int)); 322 + rb->close(fd); 291 323 292 324 return 1; 293 325 } 294 326 295 - static int edit_list(void) 327 + static bool disable_dir(int i, struct rfa_scan *scan) 328 + { 329 + if (scan->dirs->dir[i][0] != ' ') 330 + { 331 + scan->dirty = true; 332 + scan->dirs->dir[i][0] = ' '; 333 + scan->dirs->dir[i][1] = '\0'; 334 + return true; 335 + } 336 + return false; 337 + } 338 + 339 + static void set_title(struct gui_synclist *lists, bool update, int num_dirs) 340 + { 341 + static int title_count; 342 + static char title[15]; 343 + 344 + if (update) 345 + title_count += num_dirs; 346 + else if (num_dirs >= 0) 347 + title_count = num_dirs; 348 + 349 + rb->snprintf(title, sizeof title, title_count == 1 ? 350 + "%d Folder" : "%d Folders", title_count); 351 + rb->gui_synclist_set_title(lists, title, Icon_NOICON); 352 + } 353 + 354 + static int edit(struct rfa_scan *scan) 296 355 { 297 356 struct gui_synclist lists; 298 - bool exit = false; 299 - int button,i; 300 - int selection, ret = 0; 357 + int button, i, j, selection; 301 358 302 359 /* load the dat file if not already done */ 303 - if ((list == NULL || list->count == 0) && (i = load_list()) != 0) 360 + if (!scan->dirs || !scan->dirs->count) 304 361 { 305 - rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i); 306 - return -1; 307 - } 308 - 309 - dirs_count = list->count; 362 + if (!rb->file_exists(RFA_LIST_DAT)) 363 + generate(scan); 310 364 311 - rb->gui_synclist_init(&lists,list_get_name_cb,0, false, 1, NULL); 312 - rb->gui_synclist_set_nb_items(&lists,list->count); 365 + if ((i = load_dirs(scan)) != 0) 366 + { 367 + rb->splashf(HZ*2, "Could not load %s, err %d", RFA_LIST_DAT, i); 368 + return -1; 369 + } 370 + } 371 + scan->dirty = false; 372 + rb->gui_synclist_init(&lists, list_get_name_cb, (void *) scan, 373 + false, 1, NULL); 374 + rb->gui_synclist_set_nb_items(&lists, scan->dirs->count); 313 375 rb->gui_synclist_select_item(&lists, 0); 314 - 315 - while (!exit) 376 + set_title(&lists, false, scan->dirs->count); 377 + while (true) 316 378 { 317 379 rb->gui_synclist_draw(&lists); 318 380 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); ··· 322 384 switch (button) 323 385 { 324 386 case ACTION_STD_OK: 325 - list->folder[selection][0] = ' '; 326 - list->folder[selection][1] = '\0'; 387 + if (disable_dir(selection, scan)) 388 + set_title(&lists, true, -1); 327 389 break; 328 390 case ACTION_STD_CONTEXT: 329 391 { ··· 334 396 switch (rb->do_menu(&menu, NULL, NULL, false)) 335 397 { 336 398 case 0: 337 - list->folder[selection][0] = ' '; 338 - list->folder[selection][1] = '\0'; 399 + if (disable_dir(selection, scan)) 400 + set_title(&lists, true, -1); 339 401 break; 340 402 case 1: 341 - { 342 - char temp[MAX_PATH]; 343 - rb->strcpy(temp,list->folder[selection]); 344 - len = rb->strlen(temp); 345 - for (i=0;i<list->count;i++) 346 - { 347 - if (!rb->strncmp(list->folder[i],temp,len)) 348 - { 349 - list->folder[i][0] = ' '; 350 - list->folder[i][1] = '\0'; 351 - } 352 - } 353 - } 354 - break; 403 + rb->strcpy(scan->path, scan->dirs->dir[selection]); 404 + len = rb->strlen(scan->path); 405 + j = 0; 406 + for (i = 0; i < scan->dirs->count; i++) 407 + if (!rb->strncmp(scan->dirs->dir[i], scan->path, len)) 408 + if (disable_dir(i, scan)) 409 + j++; 410 + set_title(&lists, true, -j); 411 + break; 412 + default: 413 + set_title(&lists, false, -1); 414 + break; 355 415 } 356 416 } 357 417 break; 358 418 case ACTION_STD_CANCEL: 359 419 { 360 - MENUITEM_STRINGLIST(menu, "Exit", NULL, 361 - "Save and Exit", "Ignore Changes and Exit"); 420 + MENUITEM_STRINGLIST(menu, "Save Changes?", NULL, 421 + "Save", "Ignore Changes"); 362 422 423 + if (!scan->dirty) 424 + return 0; 363 425 switch (rb->do_menu(&menu, NULL, NULL, false)) 364 426 { 365 427 case 0: 366 - save_list(); 428 + save_dirs(scan); 367 429 /* fallthrough */ 368 430 case 1: 369 - exit = true; 370 - ret = -2; 431 + scan->dirs = NULL; 432 + return 0; 433 + default: 434 + set_title(&lists, false, -1); 435 + break; 371 436 } 372 437 } 373 438 break; 374 439 } 375 440 } 376 - return ret; 441 + return 0; 377 442 } 378 443 379 - static int export_list_to_file_text(void) 444 + static int export(struct rfa_scan *scan) 380 445 { 381 - int i = 0; 446 + int i, fd; 447 + 382 448 /* load the dat file if not already done */ 383 - if ((list == NULL || list->count == 0) && (i = load_list()) != 0) 384 - { 385 - rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i); 386 - return 0; 387 - } 449 + if (!scan->dirs || !scan->dirs->count) 450 + if ((i = load_dirs(scan)) != 0) 451 + { 452 + rb->splashf(HZ*2, "Could not load %s, err %d", RFA_LIST_DAT, i); 453 + return 0; 454 + } 388 455 389 - if (list->count <= 0) 456 + if (scan->dirs->count <= 0) 390 457 { 391 - rb->splashf(HZ*2, "no dirs in list file: %s", RFA_FILE); 458 + rb->splashf(HZ*2, "no dirs in list file: %s", RFA_LIST_DAT); 392 459 return 0; 393 460 } 394 461 395 462 /* create and open the file */ 396 - int myfd = rb->creat(RFA_FILE_TEXT, 0666); 397 - if (myfd < 0) 463 + fd = rb->creat(RFA_LIST_TXT, 0666); 464 + if (fd < 0) 398 465 { 399 - rb->splashf(HZ*4, "failed to open: fd = %d, file = %s", 400 - myfd, RFA_FILE_TEXT); 466 + rb->splashf(HZ*4, "failed to open: fd %d, file %s", 467 + fd, RFA_LIST_TXT); 401 468 return -1; 402 469 } 403 470 404 471 /* write each directory to file */ 405 - for (i = 0; i < list->count; i++) 472 + rb->splash_progress_set_delay(HZ/2); 473 + for (i = 0; i < scan->dirs->count; i++) 406 474 { 407 - if (list->folder[i][0] != ' ') 408 - { 409 - rb->fdprintf(myfd, "%s\n", list->folder[i]); 410 - } 475 + /* (voiced) */ 476 + rb->splash_progress(i, scan->dirs->count, "%s", rb->str(LANG_WAIT)); 477 + 478 + if (scan->dirs->dir[i][0] != ' ') 479 + rb->fdprintf(fd, "%s\n", scan->dirs->dir[i]); 411 480 } 412 481 413 - rb->close(myfd); 482 + rb->close(fd); 414 483 rb->splash(HZ, "Done"); 415 484 return 1; 416 485 } 417 486 418 - static int import_list_from_file_text(void) 487 + static int import(struct rfa_scan *scan) 419 488 { 420 - char line[MAX_PATH]; 489 + char *buf; 490 + size_t buf_sz; 491 + int fd; 421 492 422 - buffer = rb->plugin_get_audio_buffer(&buffer_size); 423 - if (buffer == NULL) 493 + buf = rb->plugin_get_audio_buffer(&buf_sz); 494 + if (!buf || buf_sz < sizeof(int)) 424 495 { 425 496 rb->splash(HZ*2, "failed to get audio buffer"); 426 497 return -1; 427 498 } 428 499 429 - int myfd = rb->open(RFA_FILE_TEXT, O_RDONLY); 430 - if (myfd < 0) 500 + fd = rb->open(RFA_LIST_TXT, O_RDONLY); 501 + if (fd < 0) 431 502 { 432 - rb->splashf(HZ*2, "failed to open: %s", RFA_FILE_TEXT); 503 + rb->splashf(HZ*2, "failed to open: %s", RFA_LIST_TXT); 433 504 return -1; 434 505 } 435 506 507 + rb->splash(0, ID2P(LANG_WAIT)); 508 + 436 509 /* set the list structure, and initialize count */ 437 - list = (struct file_format *)buffer; 438 - list->count = 0; 510 + scan->dirs = (struct rfa_dirs *)buf; 511 + scan->dirs->count = 0; 512 + buf_sz -= sizeof(int); 439 513 440 - while ((rb->read_line(myfd, line, MAX_PATH - 1)) > 0) 514 + while (buf_sz >= sizeof(*scan->dirs->dir)) 441 515 { 442 - /* copy the dir name, and skip the newline */ 443 - int len = rb->strlen(line); 444 - /* remove CRs */ 445 - if (len > 0) 516 + int numread = rb->read_line(fd, scan->dirs->dir[scan->dirs->count], 517 + sizeof(*scan->dirs->dir)); 518 + if ((numread) <= 0) 519 + break; 520 + 521 + /* if the line we read fills up buffer, the next call to read_line 522 + will return an empty string for a remaining newline character. */ 523 + if (scan->dirs->dir[scan->dirs->count][0]) 446 524 { 447 - if (line[len-1] == 0x0A || line[len-1] == 0x0D) 448 - line[len-1] = 0x00; 449 - if (len > 1 && 450 - (line[len-2] == 0x0A || line[len-2] == 0x0D)) 451 - line[len-2] = 0x00; 525 + scan->dirs->count++; 526 + buf_sz -= sizeof(*scan->dirs->dir); 452 527 } 453 - 454 - rb->strcpy(list->folder[list->count++], line); 455 528 } 456 529 457 - rb->close(myfd); 530 + rb->close(fd); 458 531 459 - if (list->count == 0) 460 - { 461 - load_list(); 462 - } 532 + if (!scan->dirs->count) 533 + load_dirs(scan); 463 534 else 464 - { 465 - save_list(); 466 - } 535 + save_dirs(scan); 536 + 467 537 rb->splash(HZ, "Done"); 468 - return list->count; 538 + return scan->dirs->count; 469 539 } 470 540 471 - static int start_shuffled_play(void) 541 + static int play_shuffled(struct rfa_scan *scan) 472 542 { 473 - int *order; 543 + struct playlist_insert_context pl_context; 544 + int *order, i = 0, ret = -1; 474 545 size_t max_shuffle_size; 475 - int i = 0; 476 546 477 - /* get memory for shuffling */ 478 - order=rb->plugin_get_buffer(&max_shuffle_size); 479 - max_shuffle_size/=sizeof(int); 480 - if (order==NULL || max_shuffle_size==0) 547 + /* load the dat file if not already done */ 548 + if (!scan->dirs || !scan->dirs->count) 481 549 { 482 - rb->splashf(HZ*2, "Not enough memory for shuffling"); 483 - return 0; 550 + if (!rb->file_exists(RFA_LIST_DAT)) 551 + generate(scan); 552 + 553 + if ((i = load_dirs(scan)) != 0) 554 + { 555 + rb->splashf(HZ*2, "Could not load %s, err %d", RFA_LIST_DAT, i); 556 + return ret; 557 + } 484 558 } 485 559 486 - /* load the dat file if not already done */ 487 - if ((list == NULL || list->count == 0) && (i = load_list()) != 0) 560 + if (scan->dirs->count <= 0) 488 561 { 489 - rb->splashf(HZ*2, "Could not load %s, rv = %d", RFA_FILE, i); 490 - return 0; 562 + rb->splashf(HZ*2, "No dirs in list file: %s", RFA_LIST_DAT); 563 + return ret; 491 564 } 492 565 493 - if (list->count <= 0) 566 + /* get memory for shuffling */ 567 + order = rb->plugin_get_buffer(&max_shuffle_size); 568 + max_shuffle_size /= sizeof(int); 569 + if (!order || !max_shuffle_size) 494 570 { 495 - rb->splashf(HZ*2, "no dirs in list file: %s", RFA_FILE); 496 - return 0; 571 + rb->splashf(HZ*2, "Not enough memory for shuffling"); 572 + return ret; 497 573 } 498 574 499 575 /* shuffle the thing */ 500 576 rb->srand(*rb->current_tick); 501 - if(list->count>(int)max_shuffle_size) 577 + if(scan->dirs->count > (int) max_shuffle_size) 502 578 { 503 - rb->splashf(HZ*2, "Too many folders: %d (room for %d)", list->count,(int)max_shuffle_size); 504 - return 0; 579 + rb->splashf(HZ*2, "Too many folders: %d (room for %d)", 580 + scan->dirs->count, (int) max_shuffle_size); 581 + return ret; 505 582 } 506 - for(i=0;i<list->count;i++) 507 - order[i]=i; 583 + for(i = 0; i < scan->dirs->count; i++) 584 + order[i] = i; 508 585 509 - for(i = list->count - 1; i >= 0; i--) 586 + for(i = scan->dirs->count - 1; i >= 0; i--) 510 587 { 511 588 /* the rand is from 0 to RAND_MAX, so adjust to our value range */ 512 589 int candidate = rb->rand() % (i + 1); ··· 517 594 order[i] = store; 518 595 } 519 596 520 - /* We don't want whatever is playing */ 521 597 if (!(rb->playlist_remove_all_tracks(NULL) == 0 522 598 && rb->playlist_create(NULL, NULL) == 0)) 523 599 { 524 600 rb->splashf(HZ*2, "Could not clear playlist"); 525 - return 0; 601 + return ret; 602 + } 603 + 604 + rb->splash_progress_set_delay(HZ/2); 605 + 606 + /* Create insert context, disable progress for individual dirs */ 607 + if (rb->playlist_insert_context_create(NULL, &pl_context, PLAYLIST_INSERT_LAST, 608 + false, false) < 0) 609 + { 610 + rb->playlist_insert_context_release(&pl_context); 611 + rb->splashf(HZ*2, "Could not insert directories"); 612 + return ret; 526 613 } 527 614 528 - /* add the lot to the playlist */ 529 - for (i = 0; i < list->count; i++) 615 + for (i = 0; i < scan->dirs->count; i++) 530 616 { 531 - if (list->folder[order[i]][0] != ' ') 617 + /* (voiced) */ 618 + rb->splash_progress(i, scan->dirs->count, "%s (%s)", 619 + rb->str(LANG_WAIT), rb->str(LANG_OFF_ABORT)); 620 + if (rb->action_userabort(TIMEOUT_NOBLOCK)) 532 621 { 533 - rb->playlist_insert_directory(NULL,list->folder[order[i]],PLAYLIST_INSERT_LAST,false,false); 622 + ret = 0; 623 + break; 534 624 } 535 - if (rb->action_userabort(TIMEOUT_NOBLOCK)) 625 + 626 + if (scan->dirs->dir[order[i]][0] != ' ') 536 627 { 537 - break; 628 + ret = rb->playlist_insert_directory(NULL, scan->dirs->dir[order[i]], 629 + PLAYLIST_INSERT_LAST, false, 630 + false, &pl_context); 631 + if (ret < 0) 632 + { 633 + if (ret == -2) /* User canceled */ 634 + break; 635 + else if (rb->playlist_amount() >= 636 + rb->global_settings->max_files_in_playlist) 637 + { 638 + rb->splashf(HZ, "Reached maximum number of files in playlist (%d)", 639 + rb->global_settings->max_files_in_playlist); 640 + ret = 1; 641 + break; 642 + } 643 + else 644 + rb->splashf(HZ, "Error inserting: %s", scan->dirs->dir[order[i]]); 645 + } 646 + ret = 1; 538 647 } 539 648 } 540 - rb->splash(HZ, "Done"); 541 - /* the core needs the audio buffer back in order to start playback. */ 542 - list = NULL; 543 - rb->plugin_release_audio_buffer(); 544 - rb->playlist_start(0, 0, 0); 545 - return 1; 649 + rb->playlist_insert_context_release(&pl_context); 650 + 651 + if (ret > 0) 652 + { 653 + /* the core needs the audio buffer back in order to start playback. */ 654 + scan->dirs = NULL; 655 + rb->plugin_release_audio_buffer(); 656 + rb->playlist_set_modified(NULL, true); 657 + rb->playlist_start(0, 0, 0); 658 + } 659 + return ret; 546 660 } 547 661 548 662 static enum plugin_status main_menu(void) 549 663 { 550 - bool exit = false; 664 + static struct rfa_scan scan; 665 + int selected = 0; 666 + 551 667 MENUITEM_STRINGLIST(menu, "Random Folder Advance", NULL, 552 - "Generate Folder List", 553 - "Edit Folder List", 554 - "Export List To Textfile", 555 - "Import List From Textfile", 556 668 "Play Shuffled", 669 + "Scan Folders", 670 + "Edit", 671 + "Export to Text File", 672 + "Import from Text File", 557 673 "Quit"); 558 674 559 - while (!exit) 675 + while (true) 560 676 { 561 - switch (rb->do_menu(&menu, NULL, NULL, false)) 677 + switch (rb->do_menu(&menu, &selected, NULL, false)) 562 678 { 563 - case 0: /* generate */ 679 + case GO_TO_PREVIOUS_MUSIC: 680 + return PLUGIN_GOTO_WPS; 681 + case GO_TO_ROOT: 682 + return PLUGIN_GOTO_ROOT; 683 + case GO_TO_PREVIOUS: 684 + return PLUGIN_OK; 685 + case 0: /* Play Shuffled */ 686 + if (!rb->warn_on_pl_erase()) 687 + break; 688 + 689 + #ifdef HAVE_ADJUSTABLE_CPU_FREQ 690 + rb->cpu_boost(true); 691 + #endif 692 + int ret = play_shuffled(&scan); 693 + #ifdef HAVE_ADJUSTABLE_CPU_FREQ 694 + rb->cpu_boost(false); 695 + #endif 696 + if (ret > 0) 697 + return PLUGIN_GOTO_WPS; 698 + break; 699 + case 1: /* Scan Folders */ 564 700 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 565 701 rb->cpu_boost(true); 566 702 #endif 567 - generate(); 703 + generate(&scan); 568 704 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 569 705 rb->cpu_boost(false); 570 706 #endif ··· 575 711 rb->backlight_on(); 576 712 #endif 577 713 break; 578 - case 1: 714 + case 2: /* Edit */ 579 715 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 580 716 rb->cpu_boost(true); 581 717 #endif 582 - if (edit_list() < 0) 583 - exit = true; 718 + edit(&scan); 584 719 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 585 720 rb->cpu_boost(false); 586 721 #endif ··· 591 726 rb->backlight_on(); 592 727 #endif 593 728 break; 594 - case 2: /* export to textfile */ 729 + case 3: /* Export to Text File */ 595 730 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 596 731 rb->cpu_boost(true); 597 732 #endif 598 - export_list_to_file_text(); 733 + export(&scan); 599 734 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 600 735 rb->cpu_boost(false); 601 736 #endif ··· 606 741 rb->backlight_on(); 607 742 #endif 608 743 break; 609 - case 3: /* import from textfile */ 744 + case 4: /* Import from Text File */ 610 745 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 611 746 rb->cpu_boost(true); 612 747 #endif 613 - import_list_from_file_text(); 748 + import(&scan); 614 749 #ifdef HAVE_ADJUSTABLE_CPU_FREQ 615 750 rb->cpu_boost(false); 616 751 #endif ··· 621 756 rb->backlight_on(); 622 757 #endif 623 758 break; 624 - case 4: 625 - if (!start_shuffled_play()) 626 - return PLUGIN_ERROR; 627 - else 628 - return PLUGIN_GOTO_WPS; 629 - case 5: 759 + case 5: /* Quit */ 630 760 return PLUGIN_OK; 631 761 } 632 762 } ··· 636 766 enum plugin_status plugin_start(const void* parameter) 637 767 { 638 768 (void)parameter; 769 + enum plugin_status ret; 770 + 639 771 #ifdef HAVE_TOUCHSCREEN 640 772 rb->touchscreen_set_mode(rb->global_settings->touch_mode); 641 773 #endif 642 774 643 - cancel = false; 775 + FOR_NB_SCREENS(i) 776 + rb->viewportmanager_theme_enable(i, true, NULL); 777 + ret = main_menu(); 778 + FOR_NB_SCREENS(i) 779 + rb->viewportmanager_theme_undo(i, false); 644 780 645 - return main_menu(); 781 + return ret; 646 782 }
+2 -1
docs/PLUGIN_API
··· 1874 1874 \return 1875 1875 \description 1876 1876 1877 - int playlist_insert_directory(struct playlist_info* playlist, const char *dirname, int position, bool queue, bool recurse) 1877 + int playlist_insert_directory(struct playlist_info* playlist, const char *dirname, int position, bool queue, bool recurse, struct playlist_insert_context *context) 1878 1878 \group playback control 1879 1879 \param playlist 1880 1880 \param dirname 1881 1881 \param position 1882 1882 \param queue 1883 1883 \param recurse 1884 + \param context 1884 1885 \return 1885 1886 \description 1886 1887
+5 -5
manual/plugins/random_folder_advance_config.tex
··· 5 5 6 6 \subsubsection{Menu} 7 7 \begin{description} 8 - \item[Generate Folder List] Generates a list of all folders found 8 + \item[Play Shuffled] Starts playback with the selected directories in random order. Tracks within a directory will be played in normal order. The plugin will exit after starting playback. 9 + \item[Scan Folders] Generates a list of all folders found 9 10 on the player. You can filter the directories which are scanned by 10 11 creating a file called \\* 11 12 \fname{/.rockbox/folder\_advance\_dir.txt}. ··· 16 17 directory to be ignored.). If you just want \fname{/CDs} 17 18 to be ignored but want to include the folders within 18 19 it you need to have both \fname{-/CDs} and \fname{CDs} as entries. 19 - \item[Edit Folder List] Enter the folder list editor 20 - \item[Export List To Textfile] Exports the list to 20 + \item[Edit] Enter the folder list editor 21 + \item[Export to Text File] Exports the list to 21 22 \fname{/.rockbox/folder\_advance\_list.txt} 22 - \item[Import List From Textfile] Imports the list from 23 + \item[Import from Text File] Imports the list from 23 24 \fname{/.rockbox/folder\_advance\_list.txt} 24 - \item[Play Shuffled] Starts playback with the selected directories in random order. Tracks within a directory will be played in normal order. The plugin will exit after starting playback. 25 25 \item[Quit] 26 26 \end{description} 27 27