/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2024 William Wilgus * * 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. * ****************************************************************************/ /*File Picker Plugin FPP accepts several arguments to help in your file picking adventure * NOTE: anything with spaces should be quoted, -f and -l can't be used together * -r "" doesn't need to be a full path demos/file.rock works the file (if choosen, cancel exits) will be passed as a parameter -t "" -s "<START DIR>" -f "<.EXT (max 64 chars) >" the extension of the files you are looking for must have the '.' and ".*" accepts any file -l "<.EXT.EXT,.EXT .EXT (max 64 chars) >" list of extensions for files you are looking for each must have the '.' spaces and commas are ignored -a attrib flags eg FILE_ATTR_AUDIO -d disallow changing directories (hide directories doesn't allow changing from start dir) */ #include "plugin.h" #include "lang_enum.h" #include "lib/arg_helper.h" #if defined(DEBUG) || defined(SIMULATOR) #define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n") #elif defined(ROCKBOX_HAS_LOGF) #define logf rb->logf #else #define logf(...) do { } while(0) #endif #define FIND_NODIRS 0x01 #define FIND_ATTRIB 0x02 #define FIND_WILDCARD 0x04 #define FIND_EXT 0x08 #define FIND_EXT_IN_LIST 0x10 static struct fpp { char return_plugin[MAX_PATH]; char start_dir[MAX_PATH]; char file_ext[64]; char title[64]; int tree_attr; int flags; }fpp; static int arg_callback(char argchar, const char **parameter, void *userdata) { struct fpp *pfp = userdata; int ret; long num; const char* start = *parameter; while (*parameter[0] > '/' && ispunct(*parameter[0])) (*parameter)++; switch (tolower(argchar)) { case 'd' : pfp->flags |= FIND_NODIRS; logf ("Find no dirs"); break; case 'r' : /*return_plugin*/ logf ("trying PLUGIN_DIR..."); size_t l = rb->strlcpy(pfp->return_plugin, PLUGIN_DIR, sizeof(pfp->return_plugin)); ret = string_parse(parameter, pfp->return_plugin + l, sizeof(pfp->return_plugin) - l); if (ret && !rb->file_exists(pfp->return_plugin)) { logf("Failed"); *parameter = start; string_parse(parameter, pfp->return_plugin, sizeof(pfp->return_plugin)); } if (ret) { logf ("Ret plugin: Val: %s\n", pfp->return_plugin); logf("ate %d chars\n", ret); } break; case 't' : /* title */ ret = string_parse(parameter, pfp->title, sizeof(pfp->title)); if (ret) { logf ("Title: Val: %s\n", pfp->title); logf("ate %d chars\n", ret); } break; case 's' : /* start directory */ ret = string_parse(parameter, pfp->start_dir, sizeof(pfp->start_dir)); if (ret) { if (!rb->dir_exists(pfp->start_dir)) { rb->strlcpy(pfp->start_dir, PATH_ROOTSTR, sizeof(pfp->start_dir)); } logf ("Start dir: Val: %s\n", pfp->start_dir); logf("ate %d chars\n", ret); } break; case 'f' : /* file extension */ if (pfp->flags & FIND_EXT_IN_LIST) { rb->splash(HZ*5, "list extensions already active -f ignored"); break; } ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext)); if (ret) { if (pfp->file_ext[1] == '*') pfp->flags |= FIND_WILDCARD; else pfp->flags |= FIND_EXT; logf ("Extension: Val: %s\n", pfp->file_ext); logf("ate %d chars\n", ret); } break; case 'l' : /* file extension list */ if (pfp->flags & FIND_EXT) { rb->splash(HZ*5, "extension already active -l ignored"); break; } ret = string_parse(parameter, pfp->file_ext, sizeof(pfp->file_ext)); if (ret) { char *wr = pfp->file_ext; char *rd = pfp->file_ext; while (*rd != '\0') /* copy the extensions */ { if (*rd == ' ' || *rd == ',' || *rd == ';') { /* ignore spaces, commas, and semicolons */ rd++; continue; } *wr++ = *rd++; } *wr = '\0'; pfp->flags |= FIND_EXT_IN_LIST; logf ("Extension List: Val: %s\n", pfp->file_ext); logf("ate %d chars\n", ret); } break; case 'a' : /* tree attribute */ ret = longnum_parse(parameter, &num, NULL); if (ret) { pfp->tree_attr = (num)&FILE_ATTR_MASK; pfp->flags |= FIND_ATTRIB; logf("Attrib: Val: 0x%lx\n", (unsigned long)num); logf("ate %d chars\n", ret); } break; default : rb->splashf(HZ, "Unknown switch '%c'",argchar); logf("Unknown switch '%c'",argchar); //return 0; } return 1; } static bool cb_show_item(char *name, int attr, struct tree_context *tc) { static int dirlevel = -1; if(attr & ATTR_DIRECTORY) { if (fpp.flags & FIND_NODIRS) { if (tc->dirlevel == dirlevel) return false; dirlevel = tc->dirlevel; if (rb->strcasestr(tc->currdir, fpp.start_dir) == NULL) { tc->is_browsing = false; /* exit immediately */ logf("exiting %d", tc->dirlevel); } } return true; } if (fpp.flags & FIND_WILDCARD) { return true; } if ((fpp.flags & FIND_ATTRIB) && (fpp.tree_attr & attr) != 0) { return true; } if (fpp.flags & FIND_EXT) { const char *p = rb->strrchr(name, '.' ); if (p != NULL && !rb->strcasecmp( p, fpp.file_ext)) return true; } if (fpp.flags & FIND_EXT_IN_LIST) { const char *p = rb->strrchr(name, '.' ); if (p != NULL && rb->strcasestr(fpp.file_ext, p) != NULL) return true; } logf("Excluded: %s", name); return false; } static int browse_file_dir(struct fpp *pfp) { char buf[MAX_PATH]; struct browse_context browse = { .dirfilter = SHOW_ALL, .flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER, .title = pfp->title, .icon = Icon_Playlist, .buf = buf, .bufsize = sizeof(buf), .root = pfp->start_dir, .callback_show_item = &cb_show_item, }; if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS) { if (rb->file_exists(buf)) { logf("Loading %s", buf); return rb->plugin_open(pfp->return_plugin, buf); } else { logf("Error opening %s", buf); rb->splashf(HZ *2, "Error Opening %s", buf); return PLUGIN_ERROR; } } return PLUGIN_OK; } enum plugin_status plugin_start(const void* parameter) { if (!parameter) { rb->splash(HZ *2, "No Args"); return PLUGIN_ERROR; } argparse((const char*) parameter, -1, &fpp, &arg_callback); if (fpp.title[0] == '\0') { if (rb->global_settings->talk_menu) rb->talk_id(LANG_CHOOSE_FILE, true); if ((fpp.flags & FIND_EXT) || (fpp.flags & FIND_EXT_IN_LIST)) { rb->snprintf(fpp.title, sizeof(fpp.title), "%s (%s)", rb->str(LANG_CHOOSE_FILE), fpp.file_ext); if (rb->global_settings->talk_menu) rb->talk_spell(fpp.file_ext, true); } else { rb->snprintf(fpp.title, sizeof(fpp.title), "%s", rb->str(LANG_CHOOSE_FILE)); } rb->talk_force_enqueue_next(); } if (fpp.start_dir[0] == '\0' || !rb->dir_exists(fpp.start_dir)) rb->strcpy(fpp.start_dir, PATH_ROOTSTR); return browse_file_dir(&fpp); }