2456d28e21
opx shortcuts allow an easy way to add parameters to plugins you should be able to set them as shortcuts now the plugin hd a use after free bug on the dat file file descriptor Change-Id: I5fe3b0628e7da003c03b9b54f5788b0d0dbfea74
924 lines
26 KiB
C
924 lines
26 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// __ \_/ ___\| |/ /| __ \ / __ \ \/ /
|
|
* Jukebox | | ( (__) ) \___| ( | \_\ ( (__) ) (
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2020 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* open_plugins.rock interfaces with the open_plugin core
|
|
*
|
|
* When opened directly it acts as a viewer for the plugin.dat file
|
|
* this allows you to edit the paths and parameters for
|
|
* core shortcuts as well as your added plugins
|
|
*
|
|
* If a plugin is supplied to the viewer it is added to the dat file
|
|
*
|
|
* If instead the plugin has previously been added then it is run
|
|
* with the parameters previously supplied
|
|
*/
|
|
|
|
#include "plugin.h"
|
|
#include "lang_enum.h"
|
|
#include "../open_plugin.h"
|
|
|
|
#define ROCK_EXT "rock"
|
|
#define ROCK_LEN 5
|
|
|
|
#define OP_EXT "opx"
|
|
#define OP_LEN 4
|
|
|
|
#define OP_PLUGIN_RESTART (PLUGIN_GOTO_PLUGIN | 0x8000)
|
|
|
|
#define MENU_ID_MAIN "0"
|
|
#define MENU_ID_EDIT "1"
|
|
|
|
static int fd_dat;
|
|
static struct gui_synclist lists;
|
|
struct open_plugin_entry_t op_entry;
|
|
static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM;
|
|
static const off_t op_entry_sz = sizeof(struct open_plugin_entry_t);
|
|
|
|
/* we only need the names for the first menu so don't bother reading paths yet */
|
|
const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry);
|
|
|
|
static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key);
|
|
|
|
static bool _yesno_pop(const char* text)
|
|
{
|
|
const char *lines[]={text};
|
|
const struct text_message message={lines, 1};
|
|
bool ret = (rb->gui_syncyesno_run(&message,NULL,NULL)== YESNO_YES);
|
|
FOR_NB_SCREENS(i)
|
|
rb->screens[i]->clear_viewport();
|
|
return ret;
|
|
}
|
|
|
|
static size_t pathbasename(const char *name, const char **nameptr)
|
|
{
|
|
const char *p = name;
|
|
const char *q = p;
|
|
const char *r = q;
|
|
|
|
while (*(p = GOBBLE_PATH_SEPCH(p)))
|
|
{
|
|
q = p;
|
|
p = GOBBLE_PATH_COMP(++p);
|
|
r = p;
|
|
}
|
|
|
|
if (r == name && p > name)
|
|
q = p, r = q--; /* root - return last slash */
|
|
/* else path is an empty string */
|
|
|
|
*nameptr = q;
|
|
return r - q;
|
|
}
|
|
static int op_entry_checksum(void)
|
|
{
|
|
if (op_entry.checksum != open_plugin_csum +
|
|
(op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
|
|
{
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static bool op_entry_read(int fd, int selected_item, off_t data_sz)
|
|
{
|
|
rb->memset(&op_entry, 0, op_entry_sz);
|
|
op_entry.lang_id = -1;
|
|
return ((selected_item >= 0) && (fd >= 0) &&
|
|
(rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
|
|
(rb->read(fd, &op_entry, data_sz) == data_sz) && op_entry_checksum() > 0);
|
|
}
|
|
|
|
static bool op_entry_read_name(int fd, int selected_item)
|
|
{
|
|
return op_entry_read(fd, selected_item, op_name_sz);
|
|
}
|
|
|
|
static int op_entry_read_opx(const char *path)
|
|
{
|
|
int ret = -1;
|
|
off_t filesize;
|
|
int fd_opx;
|
|
int len;
|
|
|
|
len = rb->strlen(path);
|
|
if(len > OP_LEN && rb->strcasecmp(&((path)[len-OP_LEN]), "." OP_EXT) == 0)
|
|
{
|
|
fd_opx = rb->open(path, O_RDONLY);
|
|
if (fd_opx >= 0)
|
|
{
|
|
filesize = rb->filesize(fd_opx);
|
|
ret = filesize;
|
|
if (filesize == op_entry_sz && !op_entry_read(fd_opx, 0, op_entry_sz))
|
|
ret = 0;
|
|
else if (op_entry_checksum() <= 0)
|
|
ret = 0;
|
|
rb->close(fd_opx);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void op_entry_export(int selection)
|
|
{
|
|
int len;
|
|
int fd = -1;
|
|
char filename [MAX_PATH + 1];
|
|
|
|
if (!op_entry_read(fd_dat, selection, op_entry_sz) || op_entry_checksum() <= 0)
|
|
goto failure;
|
|
|
|
rb->snprintf(filename, MAX_PATH, "%s/%s", PLUGIN_APPS_DIR, op_entry.name);
|
|
|
|
if( !rb->kbd_input( filename, MAX_PATH, NULL ) )
|
|
{
|
|
len = rb->strlen(filename);
|
|
if(len > OP_LEN && filename[len] != PATH_SEPCH &&
|
|
rb->strcasecmp(&((filename)[len-OP_LEN]), "." OP_EXT) != 0)
|
|
{
|
|
rb->strcat(filename, "." OP_EXT);
|
|
}
|
|
|
|
fd = rb->open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
|
|
if (fd >= 0 && rb->write(fd, &op_entry, op_entry_sz) == op_entry_sz)
|
|
{
|
|
rb->close(fd);
|
|
rb->splashf( 1*HZ, "File Saved (%s)", filename );
|
|
return;
|
|
}
|
|
rb->close(fd);
|
|
}
|
|
|
|
failure:
|
|
rb->splashf( 2*HZ, "Save Failed (%s)", filename );
|
|
|
|
}
|
|
|
|
static void op_entry_set_checksum(void)
|
|
{
|
|
op_entry.checksum = open_plugin_csum +
|
|
(op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
|
|
}
|
|
|
|
static void op_entry_set_name(void)
|
|
{
|
|
char tmp_buf[OPEN_PLUGIN_NAMESZ+1];
|
|
rb->strlcpy(tmp_buf, op_entry.name, OPEN_PLUGIN_NAMESZ);
|
|
if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_NAMESZ, NULL) >= 0)
|
|
rb->strlcpy(op_entry.name, tmp_buf, OPEN_PLUGIN_NAMESZ);
|
|
}
|
|
|
|
static int op_entry_set_path(void)
|
|
{
|
|
int ret = 0;
|
|
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
|
|
|
|
if (op_entry.path[0] == '\0')
|
|
rb->strcpy(op_entry.path, PLUGIN_DIR"/");
|
|
|
|
struct browse_context browse = {
|
|
.dirfilter = SHOW_ALL,
|
|
.flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
|
|
.title = rb->str(LANG_ADD),
|
|
.icon = Icon_Plugin,
|
|
.root = op_entry.path,
|
|
.buf = tmp_buf,
|
|
.bufsize = sizeof(tmp_buf),
|
|
};
|
|
|
|
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
|
|
{
|
|
ret = rb->strlcpy(op_entry.path, tmp_buf, OPEN_PLUGIN_BUFSZ);
|
|
if (ret > OPEN_PLUGIN_BUFSZ)
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int op_entry_set_param_path(void)
|
|
{
|
|
int ret = 0;
|
|
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
|
|
|
|
if (op_entry.param[0] == '\0')
|
|
rb->strcpy(tmp_buf, "/");
|
|
else
|
|
rb->strcpy(tmp_buf, op_entry.param);
|
|
|
|
struct browse_context browse = {
|
|
.dirfilter = SHOW_ALL,
|
|
.flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
|
|
.title = rb->str(LANG_PARAMETER),
|
|
.icon = Icon_Plugin,
|
|
.root = tmp_buf,
|
|
.buf = tmp_buf,
|
|
.bufsize = sizeof(tmp_buf),
|
|
};
|
|
|
|
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
|
|
{
|
|
ret = rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ);
|
|
if (ret > OPEN_PLUGIN_BUFSZ)
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void op_entry_set_param(void)
|
|
{
|
|
if (_yesno_pop(ID2P(LANG_BROWSE)))
|
|
op_entry_set_param_path();
|
|
|
|
char tmp_buf[OPEN_PLUGIN_BUFSZ+1];
|
|
rb->strlcpy(tmp_buf, op_entry.param, OPEN_PLUGIN_BUFSZ);
|
|
if (rb->kbd_input(tmp_buf, OPEN_PLUGIN_BUFSZ, NULL) >= 0)
|
|
rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ);
|
|
}
|
|
|
|
static int op_et_exclude_hash(struct open_plugin_entry_t *op_entry, int item, void *data)
|
|
{
|
|
(void)item;
|
|
|
|
if (op_entry->hash == 0 || op_entry->name[0] == '\0')
|
|
return 0;
|
|
|
|
if (data)
|
|
{
|
|
uint32_t *hash = data;
|
|
if (op_entry->hash != *hash)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int op_et_exclude_builtin(struct open_plugin_entry_t *op_entry, int item, void *data)
|
|
{
|
|
(void)item;
|
|
(void)data;
|
|
|
|
if (op_entry->lang_id >= 0)
|
|
return 0;
|
|
else if(op_entry->hash == 0 || op_entry->name[0] == '\0')
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, void *data)
|
|
{
|
|
(void)item;
|
|
(void)data;
|
|
|
|
if (op_entry->lang_id < 0)
|
|
return 0;
|
|
else if (op_entry->hash == 0 || op_entry->name[0] == '\0')
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int op_entry_transfer(int fd, int fd_tmp,
|
|
int(*compfn)(struct open_plugin_entry_t*, int, void*),
|
|
void *data)
|
|
{
|
|
int entries = -1;
|
|
if (fd_tmp >= 0 && fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0)
|
|
{
|
|
entries = 0;
|
|
while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz)
|
|
{
|
|
if (compfn && compfn(&op_entry, entries, data) > 0 && op_entry_checksum() > 0)
|
|
{
|
|
rb->write(fd_tmp, &op_entry, op_entry_sz);
|
|
entries++;
|
|
}
|
|
}
|
|
}
|
|
return entries + 1;
|
|
}
|
|
|
|
static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key)
|
|
{
|
|
int len;
|
|
uint32_t hash;
|
|
uint32_t newhash;
|
|
char *pos = "";;
|
|
int fd_tmp = -1;
|
|
use_key = (use_key == true && key != NULL);
|
|
|
|
if (key)
|
|
{
|
|
open_plugin_get_hash(key, &hash);
|
|
op_entry.hash = hash;
|
|
}
|
|
else if (op_entry.lang_id < 0 && plugin)
|
|
{
|
|
/* need to keep the old hash so we can remove the old entry */
|
|
hash = op_entry.hash;
|
|
open_plugin_get_hash(plugin, &newhash);
|
|
op_entry.hash = newhash;
|
|
}
|
|
else
|
|
hash = op_entry.hash;
|
|
|
|
if (plugin)
|
|
{
|
|
/* name */
|
|
if (use_key)
|
|
{
|
|
op_entry.lang_id = -1;
|
|
rb->strlcpy(op_entry.name, key, OPEN_PLUGIN_NAMESZ);
|
|
}
|
|
|
|
if (pathbasename(plugin, (const char **)&pos) == 0)
|
|
pos = "\0";
|
|
if (op_entry.name[0] == '\0' || op_entry.lang_id >= 0)
|
|
rb->strlcpy(op_entry.name, pos, OPEN_PLUGIN_NAMESZ);
|
|
|
|
len = rb->strlen(pos);
|
|
if(len > ROCK_LEN && rb->strcasecmp(&(pos[len-ROCK_LEN]), "." ROCK_EXT) == 0)
|
|
{
|
|
fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (fd_tmp < 0)
|
|
return 0;
|
|
|
|
/* path */
|
|
if (plugin != op_entry.path)
|
|
rb->strlcpy(op_entry.path, plugin, OPEN_PLUGIN_BUFSZ);
|
|
|
|
if(parameter)
|
|
{
|
|
if (parameter[0] == '\0' &&
|
|
_yesno_pop(ID2P(LANG_PARAMETER)))
|
|
{
|
|
op_entry_set_param();
|
|
}
|
|
else if (parameter != op_entry.param)
|
|
rb->strlcpy(op_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
|
|
|
|
/* hash on the parameter path if it is a file */
|
|
if (op_entry.lang_id <0 && key == op_entry.path &&
|
|
rb->file_exists(op_entry.param))
|
|
{
|
|
open_plugin_get_hash(op_entry.path, &newhash);
|
|
op_entry.hash = newhash;
|
|
}
|
|
}
|
|
op_entry_set_checksum();
|
|
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
|
|
}
|
|
else if(op_entry_read_opx(plugin) == op_entry_sz)
|
|
{
|
|
fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (fd_tmp < 0)
|
|
return 0;
|
|
|
|
if (op_entry.lang_id <0 && rb->file_exists(op_entry.param))
|
|
open_plugin_get_hash(op_entry.param, &hash);
|
|
else
|
|
open_plugin_get_hash(op_entry.path, &hash);
|
|
|
|
op_entry.hash = hash;
|
|
op_entry_set_checksum();
|
|
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
|
|
}
|
|
else
|
|
{
|
|
if (op_entry.lang_id != LANG_SHORTCUTS)
|
|
rb->splashf(HZ * 2, rb->str(LANG_OPEN_PLUGIN_NOT_A_PLUGIN), pos);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_hash, &hash) > 0)
|
|
{
|
|
rb->close(fd_tmp);
|
|
rb->close(fd_dat);
|
|
fd_dat = -1;
|
|
rb->remove(OPEN_PLUGIN_DAT);
|
|
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
|
|
}
|
|
else
|
|
{
|
|
rb->close(fd_tmp);
|
|
rb->remove(OPEN_PLUGIN_DAT ".tmp");
|
|
hash = 0;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
void op_entry_browse_add(int selection)
|
|
{
|
|
char* key;
|
|
op_entry_read(fd_dat, selection, op_entry_sz);
|
|
if (op_entry_set_path() > 0)
|
|
{
|
|
if (op_entry.lang_id >= 0)
|
|
key = rb->str(op_entry.lang_id);
|
|
else
|
|
key = op_entry.path;
|
|
|
|
op_entry_add_path(key, op_entry.path, NULL, false);
|
|
}
|
|
}
|
|
|
|
static void op_entry_remove(int selection)
|
|
{
|
|
|
|
int entries = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
|
|
int32_t hash = 0;
|
|
int lang_id = -1;
|
|
|
|
if (entries > 0 && _yesno_pop(ID2P(LANG_REMOVE)))
|
|
{
|
|
op_entry_read(fd_dat, selection, op_entry_sz);
|
|
if (rb->lseek(fd_dat, selection * op_entry_sz, SEEK_SET) >= 0)
|
|
{
|
|
if (op_entry.lang_id >= 0)
|
|
{
|
|
lang_id = op_entry.lang_id;
|
|
hash = op_entry.hash;
|
|
}
|
|
rb->memset(&op_entry, 0, op_entry_sz);
|
|
op_entry.lang_id = lang_id;
|
|
op_entry.hash = hash;
|
|
rb->write(fd_dat, &op_entry, op_entry_sz);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void op_entry_remove_empty(void)
|
|
{
|
|
bool resave = false;
|
|
if (fd_dat >= 0 && rb->lseek(fd_dat, 0, SEEK_SET) == 0)
|
|
{
|
|
while (resave == false &&
|
|
rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
|
|
{
|
|
if (op_entry.hash == 0 || !op_entry_checksum())
|
|
resave = true;
|
|
}
|
|
}
|
|
|
|
if (resave)
|
|
{
|
|
int fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
|
if (fd_tmp < 0)
|
|
return;
|
|
|
|
if ((op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_user, NULL)
|
|
+ op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_builtin, NULL)) > 0)
|
|
{
|
|
rb->close(fd_tmp);
|
|
rb->close(fd_dat);
|
|
fd_dat = -1;
|
|
rb->remove(OPEN_PLUGIN_DAT);
|
|
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
|
|
}
|
|
else
|
|
{
|
|
rb->close(fd_tmp);
|
|
rb->remove(OPEN_PLUGIN_DAT ".tmp");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int op_entry_run(void)
|
|
{
|
|
int ret = PLUGIN_ERROR;
|
|
char* path;
|
|
char* param;
|
|
if (op_entry.hash != 0 && op_entry.path[0] != '\0')
|
|
{
|
|
//rb->splash(1, ID2P(LANG_OPEN_PLUGIN));
|
|
path = op_entry.path;
|
|
param = op_entry.param;
|
|
if (param[0] == '\0')
|
|
param = NULL;
|
|
|
|
ret = rb->plugin_open(path, param);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static const char* list_get_name_cb(int selected_item, void* data,
|
|
char* buf, size_t buf_len)
|
|
{
|
|
/*TODO memoize names so we don't keep reading the disk when not necessary */
|
|
if (data == (void*) &MENU_ID_MAIN) /* check address */
|
|
{
|
|
if (op_entry_read_name(fd_dat, selected_item))
|
|
{
|
|
if (op_entry.lang_id >= 0)
|
|
rb->snprintf(buf, buf_len, "%s [%s] ",
|
|
rb->str(op_entry.lang_id), op_entry.name);
|
|
else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len)
|
|
rb->strcpy(&buf[buf_len-10], " ...");
|
|
}
|
|
else
|
|
return "?";
|
|
}
|
|
else /* op_entry should already be loaded */
|
|
{
|
|
switch(selected_item)
|
|
{
|
|
case 0:
|
|
return ID2P(LANG_NAME);
|
|
case 1:
|
|
if (op_entry.lang_id >= 0)
|
|
rb->snprintf(buf, buf_len, "%s [%s] ",
|
|
rb->str(op_entry.lang_id), op_entry.name);
|
|
else if (rb->strlcpy(buf, op_entry.name, buf_len) >= buf_len)
|
|
rb->strcpy(&buf[buf_len-10], " ...");
|
|
break;
|
|
case 2:
|
|
return ID2P(LANG_DISPLAY_FULL_PATH);
|
|
case 3:
|
|
if (rb->strlcpy(buf, op_entry.path, buf_len) >= buf_len)
|
|
rb->strcpy(&buf[buf_len-10], " ...");
|
|
break;
|
|
case 4:
|
|
return ID2P(LANG_PARAMETER);
|
|
case 5:
|
|
if (op_entry.param[0] == '\0')
|
|
return "[NULL]";
|
|
else if (rb->strlcpy(buf, op_entry.param, buf_len) >= buf_len)
|
|
rb->strcpy(&buf[buf_len-10], " ...");
|
|
break;
|
|
case 6:
|
|
return "";
|
|
case 7:
|
|
return ID2P(LANG_BACK);
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int list_voice_cb(int list_index, void* data)
|
|
{
|
|
if (data == (void*) &MENU_ID_MAIN) /* check address */
|
|
{
|
|
if (op_entry_read_name(fd_dat, list_index))
|
|
{
|
|
if (op_entry.lang_id >= 0)
|
|
{
|
|
rb->talk_id(op_entry.lang_id, false);
|
|
rb->talk_id(VOICE_PAUSE, true);
|
|
rb->talk_force_enqueue_next();
|
|
}
|
|
return rb->talk_spell(op_entry.name, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(list_index)
|
|
{
|
|
case 0:
|
|
rb->talk_id(LANG_NAME, false);
|
|
rb->talk_id(VOICE_PAUSE, true);
|
|
|
|
if (op_entry.lang_id >= 0)
|
|
{
|
|
rb->talk_id(op_entry.lang_id, true);
|
|
rb->talk_id(VOICE_PAUSE, true);
|
|
rb->talk_force_enqueue_next();
|
|
}
|
|
return rb->talk_spell(op_entry.name, false);
|
|
case 2:
|
|
return rb->talk_id(LANG_DISPLAY_FULL_PATH, false);
|
|
case 4:
|
|
return rb->talk_id(LANG_PARAMETER, false);
|
|
case 6:
|
|
return rb->talk_id(LANG_BACK, false);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void synclist_set(char* menu_id, int selection, int items, int sel_size)
|
|
{
|
|
if (selection < 0)
|
|
selection = 0;
|
|
|
|
rb->gui_synclist_init(&lists,list_get_name_cb,
|
|
menu_id, false, sel_size, NULL);
|
|
|
|
rb->gui_synclist_set_icon_callback(&lists,NULL);
|
|
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
|
|
rb->gui_synclist_set_nb_items(&lists,items);
|
|
rb->gui_synclist_select_item(&lists, selection);
|
|
list_voice_cb(selection, menu_id);
|
|
}
|
|
|
|
static int context_menu_cb(int action,
|
|
const struct menu_item_ex *this_item,
|
|
struct gui_synclist *this_list)
|
|
{
|
|
(void)this_item;
|
|
|
|
int selection = rb->gui_synclist_get_sel_pos(this_list);
|
|
|
|
if(action == ACTION_ENTER_MENUITEM)
|
|
{
|
|
if (selection == 0 &&
|
|
op_entry.lang_id >= 0 && op_entry.lang_id != LANG_OPEN_PLUGIN)
|
|
{
|
|
rb->gui_synclist_set_title(this_list,
|
|
rb->str(op_entry.lang_id), 0);
|
|
}
|
|
}
|
|
else if ((action == ACTION_STD_OK))
|
|
{
|
|
/*Run, Edit, Remove, Export, Blank, Import, Add, Back*/
|
|
switch(selection)
|
|
{
|
|
case 0:case 1:case 2:case 3:case 5:
|
|
return ACTION_STD_OK;
|
|
case 4: /*blank*/
|
|
break;
|
|
default:
|
|
return ACTION_STD_CANCEL;
|
|
}
|
|
rb->gui_synclist_draw(this_list); /* redraw */
|
|
return 0;
|
|
}
|
|
|
|
return action;
|
|
}
|
|
|
|
static void edit_menu(int selection)
|
|
{
|
|
int selected_item;
|
|
bool exit = false;
|
|
int action = 0;
|
|
|
|
if (!op_entry_read(fd_dat, selection, op_entry_sz))
|
|
return;
|
|
|
|
uint32_t crc = rb->crc_32(&op_entry, op_entry_sz, 0xffffffff);
|
|
|
|
synclist_set(MENU_ID_EDIT, 2, 8, 2);
|
|
rb->gui_synclist_draw(&lists);
|
|
|
|
while (!exit)
|
|
{
|
|
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
|
|
|
|
if (rb->gui_synclist_do_button(&lists, &action))
|
|
continue;
|
|
selected_item = rb->gui_synclist_get_sel_pos(&lists);
|
|
switch (action)
|
|
{
|
|
case ACTION_STD_OK:
|
|
if (selected_item == 0)
|
|
op_entry_set_name();
|
|
else if (selected_item == 2)
|
|
op_entry_set_path();
|
|
else if (selected_item == 4)
|
|
op_entry_set_param();
|
|
else
|
|
exit = true;
|
|
|
|
rb->gui_synclist_draw(&lists);
|
|
break;
|
|
case ACTION_STD_CANCEL:
|
|
exit = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (crc != rb->crc_32(&op_entry, op_entry_sz, 0xffffffff) &&
|
|
_yesno_pop(ID2P(LANG_SAVE)) == true)
|
|
{
|
|
char *param = op_entry.param;
|
|
if (param[0] == '\0')
|
|
param = NULL;
|
|
|
|
op_entry_add_path(NULL, op_entry.path, param, false);
|
|
fd_dat = rb->open(OPEN_PLUGIN_DAT, O_RDWR, 0666);
|
|
}
|
|
}
|
|
|
|
static int context_menu(int selection)
|
|
{
|
|
int selected_item;
|
|
if (op_entry_read(fd_dat, selection, op_entry_sz))
|
|
{
|
|
MENUITEM_STRINGLIST(menu, op_entry.name, context_menu_cb,
|
|
ID2P(LANG_RUN), ID2P(LANG_EDIT), ID2P(LANG_REMOVE), ID2P(LANG_EXPORT),
|
|
ID2P(VOICE_BLANK), ID2P(LANG_ADD), ID2P(LANG_BACK));
|
|
|
|
selected_item = rb->do_menu(&menu, 0, NULL, false);
|
|
switch (selected_item)
|
|
{
|
|
case 0: /*run*/
|
|
return PLUGIN_GOTO_PLUGIN;
|
|
case 1: /*edit*/
|
|
edit_menu(selection);
|
|
break;
|
|
case 2: /*remove*/
|
|
op_entry_remove(selection);
|
|
break;
|
|
case 3: /*export*/
|
|
op_entry_export(selection);
|
|
break;
|
|
case 4: /*blank*/
|
|
break;
|
|
case 5: /*add*/
|
|
op_entry_browse_add(-1);
|
|
rb->plugin_open(rb->plugin_get_current_filename(), "\0");
|
|
return OP_PLUGIN_RESTART;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return PLUGIN_OK;
|
|
}
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
enum plugin_status plugin_start(const void* parameter)
|
|
{
|
|
int ret = PLUGIN_OK;
|
|
uint32_t hash = 0;
|
|
int item = -1;
|
|
int selection = -1;
|
|
int action;
|
|
int items;
|
|
int res;
|
|
char *path;
|
|
bool exit = false;
|
|
|
|
const int creat_flags = O_RDWR | O_CREAT;
|
|
|
|
reopen_datfile:
|
|
fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666);
|
|
if (!fd_dat)
|
|
exit = true;
|
|
|
|
items = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
|
|
if (parameter)
|
|
{
|
|
path = (char*)parameter;
|
|
while (path[0] == ' ')
|
|
path++;
|
|
|
|
if (rb->strncasecmp(path, "-add", 4) == 0)
|
|
{
|
|
parameter = NULL;
|
|
op_entry_browse_add(-1);
|
|
rb->close(fd_dat);
|
|
goto reopen_datfile;
|
|
}
|
|
}
|
|
|
|
if (parameter)
|
|
{
|
|
path = (char*)parameter;
|
|
res = op_entry_read_opx(path);
|
|
if (res >= 0)
|
|
{
|
|
if (res == op_entry_sz)
|
|
{
|
|
exit = true;
|
|
ret = op_entry_run();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
open_plugin_get_hash(parameter, &hash);
|
|
rb->lseek(fd_dat, 0, SEEK_SET);
|
|
while (rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
|
|
{
|
|
item++;
|
|
if (op_entry.hash == hash)
|
|
{
|
|
selection = item;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selection >= 0)
|
|
{
|
|
if (op_entry_read(fd_dat, selection, op_entry_sz))
|
|
{
|
|
ret = op_entry_run();
|
|
if (ret == PLUGIN_GOTO_PLUGIN)
|
|
exit = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
op_entry_read(fd_dat, selection, op_entry_sz);
|
|
if (op_entry_add_path(parameter, parameter, "\0", false) > 0)
|
|
{
|
|
selection = 0;
|
|
items++;
|
|
fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666);
|
|
if (!fd_dat)
|
|
exit = true;
|
|
}
|
|
}
|
|
}/* OP_EXT */
|
|
}
|
|
|
|
for (int i = items - 1; i > 0 && !exit; i--)
|
|
{
|
|
if (!op_entry_read(fd_dat, i, op_entry_sz))
|
|
items--;
|
|
}
|
|
|
|
if (items < 1 && !exit)
|
|
{
|
|
char* cur_filename = rb->plugin_get_current_filename();
|
|
|
|
if (op_entry_add_path(rb->str(LANG_ADD), cur_filename, "-add", true))
|
|
{
|
|
rb->close(fd_dat);
|
|
parameter = NULL;
|
|
goto reopen_datfile;
|
|
}
|
|
rb->close(fd_dat);
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
|
|
|
|
if (!exit)
|
|
{
|
|
synclist_set(MENU_ID_MAIN, selection, items, 1);
|
|
rb->gui_synclist_draw(&lists);
|
|
|
|
while (!exit && fd_dat >= 0)
|
|
{
|
|
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
|
|
|
|
if (rb->gui_synclist_do_button(&lists, &action))
|
|
continue;
|
|
selection = rb->gui_synclist_get_sel_pos(&lists);
|
|
switch (action)
|
|
{
|
|
case ACTION_STD_CONTEXT:
|
|
ret = context_menu(selection);
|
|
if (ret == OP_PLUGIN_RESTART)
|
|
{
|
|
ret = PLUGIN_GOTO_PLUGIN;
|
|
exit = true;
|
|
break;
|
|
}
|
|
else if (ret != PLUGIN_GOTO_PLUGIN)
|
|
{
|
|
synclist_set(MENU_ID_MAIN, selection, items, 1);
|
|
rb->gui_synclist_draw(&lists);
|
|
break;
|
|
}
|
|
case ACTION_STD_OK:
|
|
if (op_entry_read(fd_dat, selection, op_entry_sz))
|
|
{
|
|
ret = op_entry_run();
|
|
exit = true;
|
|
}
|
|
break;
|
|
case ACTION_STD_CANCEL:
|
|
case ACTION_STD_MENU:
|
|
{
|
|
selection = -2;
|
|
exit = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
op_entry_remove_empty();
|
|
}
|
|
rb->close(fd_dat);
|
|
if (ret != PLUGIN_OK)
|
|
return ret;
|
|
else
|
|
return PLUGIN_OK;
|
|
}
|