diff --git a/app/include/config.h b/app/include/config.h new file mode 100644 index 0000000..328fb8d --- /dev/null +++ b/app/include/config.h @@ -0,0 +1,10 @@ +#pragma once + +typedef struct { + int background_number; +} Config; + +extern Config config; + +int configSave(void); +int configLoad(void); diff --git a/app/include/fs.h b/app/include/fs.h index 6e1c034..2d4b317 100644 --- a/app/include/fs.h +++ b/app/include/fs.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define FS_MAX_PATH 256 @@ -41,3 +42,7 @@ const char *fsSetPath(FsFileListEntry *entry, const char *path); int fsCopyPath(const char *src_path, const char *dest_path, FileProcessParam *param); int fsMovePath(const char *src_path, const char *dest_path); int fsDelete(const char *path, FileProcessParam *param); +bool fsFileExists(const char *path); +SceSize fsGetFileSize(const char *path); +int fsReadFile(const char *path, void *buf, SceSize size); +int fsWriteFile(const char *path, void *buf, SceSize size); diff --git a/app/source/config.c b/app/source/config.c new file mode 100644 index 0000000..7a5fc20 --- /dev/null +++ b/app/source/config.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include "config.h" +#include "fs.h" +#include "jsmn.h" +#include "utils.h" + +#define CONFIG_VERSION 1 + +Config config; +static const char *config_file = "{\n\t\"config_version\": %d,\n\t\"background_number\": %d\n}\n"; +static int config_version_holder = 0; + +static bool jsonEquals(const char *json, jsmntok_t *tok, const char *s) { + if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return true; + } + + return false; +} + +int configSave(void) { + int ret = 0; + char *buf = (char *)malloc(64); + int len = snprintf(buf, 64, config_file, CONFIG_VERSION, config.background_number); + + if (R_FAILED(ret = fsWriteFile("config.json", buf, len))) { + utilsLogError("fsWriteFile(config.json) failed: 0x%lx\n", ret); + } + + free(buf); + return ret; +} + +int configLoad(void) { + int ret = 0; + + if (!fsFileExists("config.json")) { + config.background_number = 16; + return configSave(); + } + + SceSize size = fsGetFileSize("config.json"); + char *buf = (char *)malloc(size + 1); + + if (R_FAILED(ret = fsReadFile("config.json", buf, size))) { + utilsLogError("fsReadFile(config.json) failed: 0x%lx\n", ret); + free(buf); + return ret; + } + + buf[size] = '\0'; + jsmn_parser parser; + jsmntok_t token[64]; + + jsmn_init(&parser); + if (R_FAILED(ret = jsmn_parse(&parser, buf, strlen(buf), token, 64))) { + utilsLogError("jsmn_parse failed: %d\n", ret); + free(buf); + return ret; + } + + if (ret < 1 || token[0].type != JSMN_OBJECT) { + utilsLogError("jsmn_parse failed: object expected\n"); + free(buf); + return ret; + } + + for (int i = 1; i < ret; i++) { + if (jsonEquals(buf, &token[i], "config_version")) { + config_version_holder = strtol(buf + token[i + 1].start, NULL, 10); + i++; + } + else if (jsonEquals(buf, &token[i], "background_number")) { + config.background_number = strtol(buf + token[i + 1].start, NULL, 10); + i++; + } + else { + utilsLogError("Unexpected key: %.*s\n", token[i].end - token[i].start, buf + token[i].start); + } + } + + free(buf); + + // Delete config file if config version is updated. + if (config_version_holder < CONFIG_VERSION) { + sceIoRemove("config.json"); + config.background_number = 16; + return configSave(); + } + + return 0; +} diff --git a/app/source/fs.c b/app/source/fs.c index f67dd02..75c0817 100644 --- a/app/source/fs.c +++ b/app/source/fs.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -546,3 +545,46 @@ int fsDelete(const char *path, FileProcessParam *param) { return 0; } + +bool fsFileExists(const char *path) { + SceIoStat stat; + return sceIoGetstat(path, &stat) >= 0; +} + +SceSize fsGetFileSize(const char *path) { + int ret = 0; + + SceIoStat stat; + memset(&stat, 0, sizeof(stat)); + + if (R_FAILED(ret = sceIoGetstat(path, &stat))) { + return ret; + } + + return stat.st_size; +} + +int fsReadFile(const char *path, void *buf, SceSize size) { + SceUID file = 0; + + if (R_FAILED(file = sceIoOpen(path, PSP_O_RDONLY, 0))) { + return file; + } + + int bytesRead = sceIoRead(file, buf, size); + sceIoClose(file); + return bytesRead; +} + + +int fsWriteFile(const char *path, void *buf, SceSize size) { + SceUID file = 0; + + if (R_FAILED(file = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777))) { + return file; + } + + int bytesWritten = sceIoWrite(file, buf, size); + sceIoClose(file); + return bytesWritten; +} diff --git a/app/source/gui.c b/app/source/gui.c index c14f106..f183f00 100644 --- a/app/source/gui.c +++ b/app/source/gui.c @@ -7,12 +7,14 @@ #include #include +#include "config.h" #include "fs.h" #include "gui.h" #include "utils.h" #include "vlf.h" #define NUM_GUI_CONTEXT_MENU_ITEMS 4 +#define NUM_GUI_MAIN_MENU_ITEMS 6 enum GuiContextMenuItems { GUI_CONTEXT_MENU_PROPERTIES, @@ -27,13 +29,13 @@ enum GuiCopyActionFlags { GUI_COPY_ACTION_MOVE_PATH }; -enum GuiDeviceList { +enum GuiMainMenuList { GUI_DEVICE_MS0, GUI_DEVICE_FLASH0, GUI_DEVICE_FLASH1, GUI_DEVICE_FLASH2, GUI_DEVICE_FLASH3, - GUI_DEVICE_DISC0 + GUI_DEVICE_DISC0, }; typedef struct { @@ -56,11 +58,11 @@ typedef struct { extern unsigned char backgrounds_bmp_start[]; -static const int start_y = 50, max_entries = 11, scrollbar_height = 222, colour = 16; +static const int start_y = 50, max_entries = 11, scrollbar_height = 222, max_background_number = 35; static GuiFileList gui = { 0 }; static bool file_op_flag = false; -static void guiDisplayDeviceList(void); +static void guiDisplayMainMenu(void); static void guiClearEventHandlers(void); static void guiSetTitle(char *fmt, ...) { @@ -83,11 +85,32 @@ static void guiSetTitle(char *fmt, ...) { title_text = vlfGuiAddText(0, 0, text); title_pic = vlfGuiAddPictureResource("dd_helper.rco", "tex_folder", 4, -1); - vlfGuiSetTitleBarEx(title_text, NULL, 1, 0, colour); + vlfGuiSetTitleBarEx(title_text, NULL, 1, 0, config.background_number); } static void guiSetBackground(void) { - vlfGuiSetBackgroundFileBuffer(backgrounds_bmp_start + colour * 6176, 6176, 1); + if (config.background_number < 0) { + config.background_number = max_background_number; + } + else if (config.background_number > max_background_number) { + config.background_number = 0; + } + + vlfGuiSetBackgroundFileBuffer(backgrounds_bmp_start + config.background_number * 6176, 6176, 1); +} + +int guiIncrementBackgroundNumber(void *param) { + config.background_number++; + guiSetBackground(); + configSave(); + return VLF_EV_RET_NOTHING; +} + +int guiDecrementBackgroundNumber(void *param) { + config.background_number--; + guiSetBackground(); + configSave(); + return VLF_EV_RET_NOTHING; } static void guiSetScrollbar(void) { @@ -151,6 +174,12 @@ static void guiClearScrollbar(void) { } } +static void guiClearSelection(void) { + guiClearScrollbar(); + gui.start = 0; + gui.selected = 0; +} + static void guiGetEntry(void) { gui.entry = g_file_list.head; @@ -229,11 +258,7 @@ static void guiResetContextMenu(u8 *flag) { guiGetEntry(); fsFreeDirectoryEntries(&g_file_list); fsGetDirectoryEntries(&g_file_list, g_cwd); - guiClearScrollbar(); - - gui.start = 0; - gui.selected = 0; - + guiClearSelection(); guiRefreshFileList(true); } @@ -316,14 +341,14 @@ static int guiControlContextMenuSelection(int selection) { } static void guiDisplayContextMenu(void) { - char *item_labels[NUM_GUI_CONTEXT_MENU_ITEMS] = { + const char *item_labels[NUM_GUI_CONTEXT_MENU_ITEMS] = { "Properties", gui.context_menu.copy_flag == 1? "Paste" : "Copy", gui.context_menu.copy_flag == 2? "Paste" : "Move", "Delete" }; - vlfGuiLateralMenuEx(NUM_GUI_CONTEXT_MENU_ITEMS, item_labels, 0, guiControlContextMenuSelection, 120, colour); + vlfGuiLateralMenuEx(NUM_GUI_CONTEXT_MENU_ITEMS, item_labels, 0, guiControlContextMenuSelection, 120, config.background_number); vlfGuiDrawFrame(); } @@ -426,11 +451,7 @@ static int guiControlFileBrowserEnter(void *param) { } } - guiClearScrollbar(); - - gui.start = 0; - gui.selected = 0; - + guiClearSelection(); guiRefreshFileList(true); return VLF_EV_RET_NOTHING; } @@ -455,16 +476,13 @@ static int guiControlFileBrowserCancel(void *param) { guiDisplayErrorDialog(); } - guiClearScrollbar(); - - gui.start = 0; - gui.selected = 0; + guiClearSelection(); // Go back to device selection if (ret == 1) { guiClearFileList(); guiClearEventHandlers(); - guiDisplayDeviceList(); + guiDisplayMainMenu(); return VLF_EV_RET_NOTHING; } @@ -488,7 +506,7 @@ static void guiControlFileBrowser(void) { vlfGuiAddEventHandler(PSP_KEY_CANCEL, -1, guiControlFileBrowserCancel, NULL); } -static int guiControlDeviceSelection(int selection) { +static int guiControlMainMenu(int selection) { int ret = 0; fsFreeDirectoryEntries(&g_file_list); @@ -498,51 +516,19 @@ static int guiControlDeviceSelection(int selection) { break; case GUI_DEVICE_FLASH0: - if ((R_FAILED(ret = sceIoUnassign("flash0:"))) && (ret != 0x80020321)) { - utilsLogError("sceIoUnassign(flash0) failed: 0x%x\n", ret); - } - - if (R_FAILED(ret = sceIoAssign("flash0:", "lflash0:0,0", "flashfat0:", IOASSIGN_RDWR, NULL, 0))) { - utilsLogError("sceIoAssign(flash0) failed: 0x%x\n", ret); - } - - strncpy(g_cwd, "flash0:/", 9); + ret = utilsSetDevice("flash0:", "lflash0:0,0", "flashfat0:", g_cwd); break; case GUI_DEVICE_FLASH1: - if ((R_FAILED(ret = sceIoUnassign("flash1:"))) && (ret != 0x80020321)) { - utilsLogError("sceIoUnassign(flash1) failed: 0x%x\n", ret); - } - - if (R_FAILED(ret = sceIoAssign("flash1:", "lflash0:0,1", "flashfat1:", IOASSIGN_RDWR, NULL, 0))) { - utilsLogError("sceIoAssign(flash1) failed: 0x%x\n", ret); - } - - strncpy(g_cwd, "flash1:/", 9); + ret = utilsSetDevice("flash1:", "lflash0:0,1", "flashfat1:", g_cwd); break; case GUI_DEVICE_FLASH2: - if ((R_FAILED(ret = sceIoUnassign("flash2:"))) && (ret != 0x80020321)) { - utilsLogError("sceIoUnassign(flash2) failed: 0x%x\n", ret); - } - - if (R_FAILED(ret = sceIoAssign("flash2:", "lflash0:0,2", "flashfat2:", IOASSIGN_RDWR, NULL, 0))) { - utilsLogError("sceIoAssign(flash2) failed: 0x%x\n", ret); - } - - strncpy(g_cwd, "flash2:/", 9); + ret = utilsSetDevice("flash2:", "lflash0:0,2", "flashfat2:", g_cwd); break; case GUI_DEVICE_FLASH3: - if ((R_FAILED(ret = sceIoUnassign("flash3:"))) && (ret != 0x80020321)) { - utilsLogError("sceIoUnassign(flash3) failed: 0x%x\n", ret); - } - - if (R_FAILED(ret = sceIoAssign("flash3:", "lflash0:0,3", "flashfat3:", IOASSIGN_RDWR, NULL, 0))) { - utilsLogError("sceIoAssign(flash3) failed: 0x%x\n", ret); - } - - strncpy(g_cwd, "flash3:/", 9); + ret = utilsSetDevice("flash3:", "lflash0:0,3", "flashfat3:", g_cwd); break; case GUI_DEVICE_DISC0: @@ -564,6 +550,7 @@ static int guiControlDeviceSelection(int selection) { } break; + } if (R_SUCCEEDED(ret)) { @@ -581,16 +568,17 @@ static int guiControlDeviceSelection(int selection) { return 0; } -static void guiDisplayDeviceList(void) { - const int num_menu_items = 6; - char *item_labels[] = { "ms0:/", "flash0:/", "flash1:/", "flash2:/", "flash3:/", "disc0:/" }; - +static void guiDisplayMainMenu(void) { + const char *item_labels[NUM_GUI_MAIN_MENU_ITEMS] = { "ms0:/", "flash0:/", "flash1:/", "flash2:/", "flash3:/", "disc0:/" }; + guiSetSecondaryTitle("Devices"); - vlfGuiCentralMenu(num_menu_items, item_labels, 0, guiControlDeviceSelection, 0, 0); + vlfGuiCentralMenu(NUM_GUI_MAIN_MENU_ITEMS, item_labels, 0, guiControlMainMenu, 0, 0); } void guiInit(void) { guiSetTitle("VLFFM v%d.%d", VERSION_MAJOR, VERSION_MINOR); + vlfGuiAddEventHandler(PSP_CTRL_RTRIGGER, -1, guiIncrementBackgroundNumber, NULL); + vlfGuiAddEventHandler(PSP_CTRL_LTRIGGER, -1, guiDecrementBackgroundNumber, NULL); guiSetBackground(); - guiDisplayDeviceList(); + guiDisplayMainMenu(); } diff --git a/app/source/main.c b/app/source/main.c index 8f11242..c6c13ca 100755 --- a/app/source/main.c +++ b/app/source/main.c @@ -2,6 +2,7 @@ #include #include +#include "config.h" #include "gui.h" #include "utils.h" #include "vlf.h" @@ -15,6 +16,7 @@ int app_main(int argc, char *argv[]) { PSP_KEY_ENTER = utilsGetEnterButton(); PSP_KEY_CANCEL = utilsGetCancelButton(); + configLoad(); vlfGuiSystemSetup(1, 1, 1); guiInit(); diff --git a/libs/include/jsmn.h b/libs/include/jsmn.h new file mode 100644 index 0000000..8ac14c1 --- /dev/null +++ b/libs/include/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/libs/include/vlf.h b/libs/include/vlf.h index 999f28e..86ce80b 100755 --- a/libs/include/vlf.h +++ b/libs/include/vlf.h @@ -1795,7 +1795,7 @@ void vlfGuiCancelBottomDialog(); * * @returns - < 0 on error */ -int vlfGuiCentralMenu(int noptions, char **items, int defaultsel, int (* handler)(int sel), int dispx, int dispy); +int vlfGuiCentralMenu(int noptions, const char **items, int defaultsel, int (* handler)(int sel), int dispx, int dispy); /** * Cancels (remove) the central menu @@ -1840,7 +1840,7 @@ int vlfGuiLateralMenu(int noptions, char **items, int defaultsel, int (* handler * * @returns - < 0 on error */ -int vlfGuiLateralMenuEx(int noptions, char **items, int defaultsel, int (* handler)(int sel), int y, u32 color); +int vlfGuiLateralMenuEx(int noptions, const char **items, int defaultsel, int (* handler)(int sel), int y, u32 color); /** * Cancels (remove) the lateral menu