diff --git a/source/gfx/gfx.c b/source/gfx/gfx.c index a05f640..7b7b7b4 100644 --- a/source/gfx/gfx.c +++ b/source/gfx/gfx.c @@ -540,6 +540,11 @@ void gfx_boxGrey(int x0, int y0, int x1, int y1, u8 shade){ memset(gfx_ctxt.fb + y * gfx_ctxt.stride + y0, shade, (y1 - y0 + 1) * 4); } } + +void gfx_setPixel(int x, int y, u32 color) { + *(gfx_ctxt.fb + (YLEFT - x) * gfx_ctxt.stride + y) = color; +} + /* void gfx_boxGrey_old(int x0, int y0, int x1, int y1, u8 shade){ for (int y = y0; y <= y1; y++){ diff --git a/source/gfx/gfx.h b/source/gfx/gfx.h index 28908a0..065c327 100644 --- a/source/gfx/gfx.h +++ b/source/gfx/gfx.h @@ -53,6 +53,7 @@ void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 po void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y); void gfx_box(int x0, int y0, int x1, int y1, u32 color); void gfx_boxGrey(int x0, int y0, int x1, int y1, u8 shade); +void gfx_setPixel(int x, int y, u32 color); /* #define GFX_SETPOSCORRECTED(x, y) gfx_con_setpos(y - YLEFT, x) diff --git a/source/tegraexplorer/common/types.h b/source/tegraexplorer/common/types.h index 7eff426..2671fd5 100644 --- a/source/tegraexplorer/common/types.h +++ b/source/tegraexplorer/common/types.h @@ -58,4 +58,6 @@ typedef struct { } gpt_entry_rule; #define isFS 0x80 -#define isBOOT 0x2 \ No newline at end of file +#define isBOOT 0x2 + +typedef void (*func_void_ptr)(); \ No newline at end of file diff --git a/source/tegraexplorer/fs/filemenu.c b/source/tegraexplorer/fs/filemenu.c index 4e18fd8..31d74d5 100644 --- a/source/tegraexplorer/fs/filemenu.c +++ b/source/tegraexplorer/fs/filemenu.c @@ -204,7 +204,7 @@ int filemenu(menu_entry file){ while (hidRead()->buttons); */ - runScript(fsutil_getnextloc(currentpath, file.name)); + startScript(fsutil_getnextloc(currentpath, file.name)); fsreader_readfolder(currentpath); break; case FILE_HEXVIEW: diff --git a/source/tegraexplorer/script/args.c b/source/tegraexplorer/script/args.c new file mode 100644 index 0000000..157f9ed --- /dev/null +++ b/source/tegraexplorer/script/args.c @@ -0,0 +1,281 @@ +#include "args.h" +#include "parser.h" +#include "scriptCtx.h" +#include "lexer.h" +#include "functions.h" +#include "list.h" +#include +#include "../../mem/heap.h" +#include "../utils/utils.h" + +// do not include first openToken! +int distanceBetweenTokens(lexarToken_t* tokens, u32 len, int openToken, int closeToken) { + int i = 0; + int layer = 0; + for (; i < len; i++) { + if (tokens[i].token == openToken) + layer++; + else if (tokens[i].token == closeToken) { + if (layer == 0) + return i; + layer--; + } + } + + return -1; +} + +dictValue_t getVarFromToken(scriptCtx_t* ctx, lexarToken_t* tokens, int* index, u32 maxLen) { + dictValue_t val = { 0 }; + val.free = 0; + int i = *index; + u8 not = 0; + + if (tokens[i].token == Not) { + not = 1; + i++; + } + + if (tokens[i].token == Not) + return ErrDictValue(ERRDOUBLENOT); + + if (tokens[i].token == Variable) { + char* funcName = tokens[i].text; + + if (tokens[i + 1].token == LBracket) { + i += 2; + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LBracket, RBracket); + if (argCount < 0) + return ErrDictValue(ERRSYNTAX); + + ctx->args_loc.tokens = &tokens[i]; + ctx->args_loc.stored = argCount; + val = executeFunction(ctx, funcName); + + i += argCount; + } + else if (tokens[i + 1].token == LSBracket) { + dictValue_t* var = varVectorFind(&ctx->vars, tokens[i].text); + i += 2; + + if (var == NULL) + return ErrDictValue(ERRNOVAR); + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LSBracket, RSBracket); + if (argCount < 0) + return ErrDictValue(ERRSYNTAX); + + dictValue_t index = solveEquation(ctx, &tokens[i], argCount); + i += argCount; + + if (index.varType != IntType) + return ErrDictValue(ERRINVALIDTYPE); + + if (var->arrayLen <= index.integer || index.integer < 0) + return ErrDictValue(ERRSYNTAX); + + switch (var->varType) { + case StringArrayType: + val = StrDictValue(var->stringArray[index.integer]); + break; + case IntArrayType: + val = IntDictValue(var->integerArray[index.integer]); + break; + case ByteArrayType: + val = IntDictValue(var->byteArray[index.integer]); + break; + default: + return ErrDictValue(ERRINVALIDTYPE); + } + } + else { + dictValue_t* var = varVectorFind(&ctx->vars, tokens[i].text); + + if (var != NULL) { + if (var->type == IntType) { + val = IntDictValue(var->integer); + } + else if (var->type == StringType){ + val = StrDictValue(util_cpyStr(var->string)); + val.free = 1; + } + else { + val = *var; + val.free = 0; + } + } + else + return ErrDictValue(ERRNOVAR); + } + } + else if (tokens[i].token == LSBracket) { + i++; + + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LSBracket, RSBracket); + if (argCount <= 0) + return ErrDictValue(-5); + + varVector_t arrayVars = extractVars(ctx, &tokens[i], argCount); + int type = arrayVars.vars[0].value.varType; + if (!(type == StringType || type == IntType)) + return ErrDictValue(ERRINVALIDTYPE); + + val.type = (type + 2) | FREEINDICT; + val.arrayLen = arrayVars.stored; + val.integerArray = calloc(val.arrayLen, (type == IntType) ? sizeof(int) : sizeof(char*)); + val.free = (type == StringType) ? 1 : 0; + + for (int i = 0; i < arrayVars.stored; i++) { + if (arrayVars.vars[i].value.varType != type) + return ErrDictValue(ERRINVALIDTYPE); // Free-ing issue!! + + if (type == StringType) { + val.stringArray[i] = util_cpyStr(arrayVars.vars[i].value.string); + } + else { + val.integerArray[i] = arrayVars.vars[i].value.integer; + } + } + + i += argCount; + varVectorFree(&arrayVars); + } + else if (tokens[i].token == LBracket) { + i += 1; + int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LBracket, RBracket); + if (argCount < 0) + return ErrDictValue(ERRSYNTAX); + + val = solveEquation(ctx, &tokens[i], argCount); + i += argCount; + } + else if (tokens[i].token == IntLit) { + val = IntDictValue(tokens[i].val); + } + else if (tokens[i].token == StrLit) { + val = StrDictValue(tokens[i].text); // Do we need to copy the string here? if we set var.free = 0, it shouldn't matter + val.free = 0; + } + else { + // ERR + return ErrDictValue(ERRNOVAR); + } + + if (not) { + if (val.type == IntType) + val.integer = !val.integer; + else + return ErrDictValue(ERRINVALIDTYPE); + } + + *index = i; + return val; +} + +int matchTypes(dictValue_t d1, dictValue_t d2, int type) { + return (d1.varType == d2.varType && d1.varType == type); +} + +#define ELIFT(token) else if (localOpToken == token) + +dictValue_t solveEquation(scriptCtx_t *ctx, lexarToken_t* tokens, u32 len) { + dictValue_t res = { 0 }; + u16 lastToken = 0; + + for (int i = 0; i < len; i++) { + if (tokens[i].token == Variable || tokens[i].token == StrLit || tokens[i].token == IntLit || tokens[i].token == Not || tokens[i].token == LBracket || tokens[i].token == LSBracket){ + dictValue_t val = getVarFromToken(ctx, tokens, &i, len - i); + + if (val.type == ErrType) + return val; + + if (lastToken) { + u16 localOpToken = lastToken; // do we need local op token? + lastToken = 0; + + if (matchTypes(res, val, IntType)) { + if (localOpToken == Plus) + res.integer += val.integer; + ELIFT(Minus) + res.integer -= val.integer; + ELIFT(Multiply) + res.integer *= val.integer; + ELIFT(Division) + res.integer /= val.integer; + ELIFT(Mod) + res.integer %= val.integer; + ELIFT(Smaller) + res.integer = res.integer < val.integer; + ELIFT(SmallerEqual) + res.integer = res.integer <= val.integer; + ELIFT(Bigger) + res.integer = res.integer > val.integer; + ELIFT(BiggerEqual) + res.integer = res.integer >= val.integer; + ELIFT(EqualEqual) + res.integer = res.integer == val.integer; + ELIFT(NotEqual) + res.integer = res.integer != val.integer; + ELIFT(LogicAND) { + res.integer = res.integer && val.integer; + if (!res.integer) + break; + } + ELIFT(LogicOR) { + res.integer = res.integer || val.integer; + if (res.integer) + break; + } + ELIFT(AND) + res.integer = res.integer & val.integer; + ELIFT(OR) + res.integer = res.integer | val.integer; + else + return ErrDictValue(ERRBADOPERATOR); + } + else if (matchTypes(res, val, StringType)) { + if (localOpToken == Plus) { + char* buff = calloc(strlen(res.string) + strlen(val.string) + 1, 1); + strcpy(buff, res.string); + strcat(buff, val.string); + if (res.free) + free(res.string); + if (val.free) + free(val.string); + res.string = buff; + } + ELIFT(EqualEqual) { + res.type = IntType; + int compRes = !strcmp(res.string, val.string); + if (res.free) + free(res.string); + if (val.free) + free(val.string); + res.integer = compRes; + res.free = 0; + } + ELIFT(Minus) { + if (!strcmp(res.string + strlen(res.string) - strlen(val.string), val.string)) { + *(res.string + strlen(res.string) - strlen(val.string)) = 0; + } + + free(val.string); + } + else + return ErrDictValue(ERRBADOPERATOR); + } + else + return ErrDictValue(ERRBADOPERATOR); + } + else { + res = val; + } + } + else if (tokens[i].token >= Plus && tokens[i].token <= OR) { + lastToken = tokens[i].token; + } + } + + return res; +} \ No newline at end of file diff --git a/source/tegraexplorer/script/args.h b/source/tegraexplorer/script/args.h new file mode 100644 index 0000000..e11d5fd --- /dev/null +++ b/source/tegraexplorer/script/args.h @@ -0,0 +1,6 @@ +#pragma once +#include "types.h" + +char* createStrOutTextHolder(textHolder_t t); +dictValue_t solveEquation(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len); +int distanceBetweenTokens(lexarToken_t* tokens, u32 len, int openToken, int closeToken); \ No newline at end of file diff --git a/source/tegraexplorer/script/functions.c b/source/tegraexplorer/script/functions.c index 9c20d41..675a867 100644 --- a/source/tegraexplorer/script/functions.c +++ b/source/tegraexplorer/script/functions.c @@ -1,664 +1,177 @@ -#include -#include -#include "../../mem/heap.h" -#include "../gfx/gfxutils.h" -#include "../emmc/emmc.h" -#include "../../utils/types.h" -#include "../../libs/fatfs/ff.h" -#include "../../utils/sprintf.h" -#include "../../hid/hid.h" -#include "../../gfx/gfx.h" -#include "../../utils/util.h" -#include "../../storage/emummc.h" -#include "parser.h" -#include "../common/common.h" -#include "../fs/fsactions.h" -#include "variables.h" -#include "../utils/utils.h" #include "functions.h" -#include "../fs/fsutils.h" -#include "../../utils/sprintf.h" -#include "../fs/fsactions.h" -#include "../emmc/emmcoperations.h" -#include "../emmc/emmcmenu.h" +#include "list.h" +#include "args.h" +#include "parser.h" +#include "lexer.h" +#include "types.h" +#include "scriptCtx.h" +#include "../../gfx/gfx.h" +#include "../../mem/heap.h" +#include +#include "../../hid/hid.h" -extern FIL scriptin; -extern char **argv; -extern u32 argc; -extern int forceExit; -extern short currentlyMounted; +dictValue_t funcPrint(scriptCtx_t* ctx, varVector_t* args) { + for (int i = 0; i < args->stored; i++) { + switch (args->vars[i].value.varType) { + case IntType: + gfx_printf("%d", args->vars[i].value.integer); + break; + case StringType: + gfx_printf("%s", args->vars[i].value.string); + break; + } + } -int parseIntInput(char *in, int *out){ - if (in[0] == '@'){ - if (str_int_find(in, out)) - return -1; - } - else - *out = atoi(in); - - return 0; -} -/* -int parseJmpInput(char *in, u64 *out){ - if (in[0] == '?'){ - if (str_jmp_find(in, out)) - return -1; - else - return 0; - } - else - return -1; -} -*/ - -int parseStringInput(char *in, char **out){ - if (in[0] == '$'){ - if (str_str_find(in, out)) - return -1; - else - return 0; - } - else{ - *out = in; - return 0; - } + return NullDictValue(); } -#pragma GCC push_options -#pragma GCC optimize ("Os") - -u32 currentcolor = COLOR_WHITE; -int part_printf(){ - SWAPCOLOR(currentcolor); - for (int i = 0; i < argc; i++){ - if (argv[i][0] == '@'){ - int toprintint; - if (parseIntInput(argv[i], &toprintint)) - return INFUNC_FAIL; - - gfx_printf("%d", toprintint); - } - else { - char *toprintstring; - if (parseStringInput(argv[i], &toprintstring)) - return INFUNC_FAIL; - - gfx_printf(toprintstring); - } - } - - gfx_printf("\n"); - return 0; +dictValue_t funcPrintln(scriptCtx_t* ctx, varVector_t* args) { + funcPrint(ctx, args); + gfx_printf("\n"); + return NullDictValue(); } -int part_print_int(){ - int toprint; - if (parseIntInput(argv[0], &toprint)) - return INFUNC_FAIL; - - SWAPCOLOR(currentcolor); - gfx_printf("%s: %d\n", argv[0], toprint); - return 0; +dictValue_t funcIf(scriptCtx_t* ctx, varVector_t* args) { + if (args->vars[0].value.varType == IntType) { + setCurIndentInstruction(ctx, (args->vars[0].value.integer == 0), -1); + return NullDictValue(); + } + + return ErrDictValue(ERRINVALIDTYPE); } -int part_Wait(){ - int arg; - u32 begintime; - SWAPCOLOR(currentcolor); - - if (parseIntInput(argv[0], &arg)) - return INFUNC_FAIL; - - begintime = get_tmr_s(); - - while (begintime + arg > get_tmr_s()){ - gfx_printf("\r ", (begintime + arg) - get_tmr_s()); - } - - gfx_printf("\r \r"); - return 0; +dictValue_t scriptElse(scriptCtx_t* ctx, varVector_t* args) { + indentInstructor_t curInstruction = getCurIndentInstruction(ctx); + setCurIndentInstruction(ctx, !curInstruction.skip, -1); + return NullDictValue(); } -int part_Check(){ - int left, right; - if (parseIntInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseIntInput(argv[2], &right)) - return INFUNC_FAIL; +dictValue_t scriptWhile(scriptCtx_t* ctx, varVector_t* args) { + if (args->vars[0].value.varType == IntType) { + setCurIndentInstruction(ctx, (args->vars[0].value.integer == 0), ctx->lastTokenPos); + return NullDictValue(); + } - if (!strcmp(argv[1], "==")) - return (left == right); - else if (!strcmp(argv[1], "!=")) - return (left != right); - else if (!strcmp(argv[1], ">=")) - return (left >= right); - else if (!strcmp(argv[1], "<=")) - return (left <= right); - else if (!strcmp(argv[1], ">")) - return (left > right); - else if (!strcmp(argv[1], "<")) - return (left < right); - else - return INFUNC_FAIL; + return ErrDictValue(ERRINVALIDTYPE); } -int part_if(){ - int condition; - if (parseIntInput(argv[0], &condition)) - return INFUNC_FAIL; +dictValue_t scriptLen(scriptCtx_t* ctx, varVector_t* args) { + if (args->vars[0].value.varType >= IntArrayType && args->vars[0].value.varType <= ByteArrayType) + return IntDictValue(args->vars[0].value.arrayLen); + else if (args->vars[0].value.varType == StringType) { + if (args->vars[0].value.string != NULL) + return IntDictValue(strlen(args->vars[0].value.string)); + } - getfollowingchar('{'); - - if (!condition) - skipbrackets(); - - return 0; - - /* - if (condition) - return 0; - else { - skipbrackets(); - return 0; - } - */ + return ErrDictValue(ERRINVALIDTYPE); } -int part_if_args(){ - int condition; - if ((condition = part_Check()) < 0) - return INFUNC_FAIL; +dictValue_t scriptMakeByteArray(scriptCtx_t* ctx, varVector_t* args) { + if (args->vars[0].value.varType == IntArrayType) { + u8* buff = calloc(args->vars[0].value.arrayLen, 1); + for (int i = 0; i < args->vars[0].value.arrayLen; i++) { + buff[i] = (u8)(args->vars[0].value.integerArray[i] & 0xFF); + } - getfollowingchar('{'); + return DictValueCreate(ByteArrayType | FREEINDICT, args->vars[0].value.arrayLen, buff); + } - if (!condition) - skipbrackets(); - - return 0; + return ErrDictValue(ERRINVALIDTYPE); } -int part_Math(){ - int left, right; - if (parseIntInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseIntInput(argv[2], &right)) - return INFUNC_FAIL; - - switch (argv[1][0]){ - case '+': - return left + right; - case '-': - return left - right; - case '*': - return left * right; - case '/': - return left / right; - } - return INFUNC_FAIL; +dictValue_t scriptWait(scriptCtx_t*ctx, varVector_t* args){ + Inputs* input = hidWait(); + + return IntDictValue(input->buttons); } -int part_SetInt(){ - int out; - parseIntInput(argv[0], &out); - return out; +dictValue_t scriptSetPixel(scriptCtx_t *ctx, varVector_t* args){ + int pixelArgs[5] = {0}; + + for (int i = 0; i < args->stored; i++){ + if (args->vars[i].value.varType != IntType) + return ErrDictValue(ERRINVALIDTYPE); + pixelArgs[i] = args->vars[i].value.integer; + } + + u32 color = 0xFF000000 | ((pixelArgs[2] & 0xFF) << 16) | ((pixelArgs[3] & 0xFF) << 8) | ((pixelArgs[4] & 0xFF)); + + gfx_setPixel(pixelArgs[0], pixelArgs[1], color); + return NullDictValue(); } -int part_SetString(){ - char *arg0; - if (parseStringInput(argv[0], &arg0)) - return INFUNC_FAIL; - if (argv[1][0] != '$') - return INFUNC_FAIL; - - str_str_add(argv[1], arg0); - return 0; -} - -int part_SetStringIndex(){ - int index; - char *out; - if (parseIntInput(argv[0], &index)) - return INFUNC_FAIL; - if (argv[1][0] != '$') - return INFUNC_FAIL; - if (str_str_index(index, &out)) - return INFUNC_FAIL; - - str_str_add(argv[1], out); - return 0; -} - -int part_goto(){ - int target = 0; - if (parseIntInput(argv[0], &target)) - return INFUNC_FAIL; - - str_int_add("@RETURN", (int)f_tell(&scriptin)); - - f_lseek(&scriptin, target); - return 0; -} - -int part_invert(){ - int arg; - if (parseIntInput(argv[0], &arg)) - return INFUNC_FAIL; - return (arg) ? 0 : 1; -} - -int part_fs_exists(){ - char *path; - if (parseStringInput(argv[0], &path)) - return INFUNC_FAIL; - - return fsutil_checkfile(path); -} - -int part_ConnectMMC(){ - char *arg; - parseStringInput(argv[0], &arg); - - if (!strcmp(arg, "SYSMMC")) - connect_mmc(SYSMMC); - else if (!strcmp(arg, "EMUMMC")) - connect_mmc(EMUMMC); - else - return INFUNC_FAIL; - - return 0; -} - -int part_MountMMC(){ - char *arg; - parseStringInput(argv[0], &arg); - return mount_mmc(arg, 2); -} - -int part_Pause(){ - Inputs *input = hidWaitMask(KEY_A | KEY_B | KEY_X | KEY_Y | KEY_POW | KEY_VOLP | KEY_VOLM | KEY_LUP | KEY_LDOWN | KEY_LLEFT | KEY_LRIGHT); - - str_int_add("@BTN_POWER", input->pow); - str_int_add("@BTN_VOL+", input->volp); - str_int_add("@BTN_VOL-", input->volm); - str_int_add("@BTN_A", input->a); - str_int_add("@BTN_B", input->b); - str_int_add("@BTN_X", input->x); - str_int_add("@BTN_Y", input->y); - str_int_add("@BTN_UP", input->Lup); - str_int_add("@BTN_DOWN", input->Ldown); - str_int_add("@BTN_LEFT", input->Lleft); - str_int_add("@BTN_RIGHT", input->Lright); - str_int_add("@JOYCONN", hidConnected()); - - return input->buttons; -} - -int part_addstrings(){ - char *combined, *left, *middle; - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &middle)) - return INFUNC_FAIL; - if (argv[2][0] != '$') - return INFUNC_FAIL; - - combined = calloc(strlen(left) + strlen(middle) + 1, sizeof(char)); - sprintf(combined, "%s%s", left, middle); - - str_str_add(argv[2], combined); - free(combined); - return 0; -} - -int part_setColor(){ - char *arg; - if (parseStringInput(argv[0], &arg)) - return INFUNC_FAIL; - - if (!strcmp(arg, "RED")) - currentcolor = COLOR_RED; - else if (!strcmp(arg, "ORANGE")) - currentcolor = COLOR_ORANGE; - else if (!strcmp(arg, "YELLOW")) - currentcolor = COLOR_YELLOW; - else if (!strcmp(arg, "GREEN")) - currentcolor = COLOR_GREEN; - else if (!strcmp(arg, "BLUE")) - currentcolor = COLOR_BLUE; - else if (!strcmp(arg, "VIOLET")) - currentcolor = COLOR_VIOLET; - else if (!strcmp(arg, "WHITE")) - currentcolor = COLOR_WHITE; - else - return INFUNC_FAIL; - - return 0; -} - -int part_Exit(){ - forceExit = true; - return 0; -} - -int part_fs_Move(){ - char *left, *right; - - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &right)) - return INFUNC_FAIL; - - int res; - res = f_rename(left, right); - if (res) - res = f_rename(left, right); - - return res; -} - -int part_fs_Delete(){ - char *arg; - - if (parseStringInput(argv[0], &arg)) - return INFUNC_FAIL; - - int res; - res = f_unlink(arg); - if (res) - res = f_unlink(arg); - - return res; -} - -int part_fs_DeleteRecursive(){ - char *arg; - - if (parseStringInput(argv[0], &arg)) - return INFUNC_FAIL; - - return fsact_del_recursive(arg); -} - -int part_fs_Copy(){ - char *left, *right; - - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &right)) - return INFUNC_FAIL; - - return fsact_copy(left, right, COPY_MODE_PRINT); -} - -int part_fs_CopyRecursive(){ - char *left, *right; - - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &right)) - return INFUNC_FAIL; - - return fsact_copy_recursive(left, right); -} - -int part_fs_MakeDir(){ - char *arg; - - if (parseStringInput(argv[0], &arg)) - return INFUNC_FAIL; - - int res; - res = f_mkdir(arg); - if (res) - res = f_mkdir(arg); - - return res; -} - -DIR dir; -FILINFO fno; -int isdirvalid = false; -int part_fs_OpenDir(){ - char *path; - - if (parseStringInput(argv[0], &path)) - return INFUNC_FAIL; - - if (f_opendir(&dir, path)) - return INFUNC_FAIL; - - isdirvalid = true; - str_int_add("@ISDIRVALID", isdirvalid); - return 0; -} - -int part_fs_CloseDir(){ - if (!isdirvalid) - return 0; - - f_closedir(&dir); - isdirvalid = false; - str_int_add("@ISDIRVALID", isdirvalid); - return 0; -} - -int part_fs_ReadDir(){ - if (!isdirvalid) - return INFUNC_FAIL; - - if (!f_readdir(&dir, &fno) && fno.fname[0]){ - str_str_add("$FILENAME", fno.fname); - str_int_add("@ISDIR", (fno.fattrib & AM_DIR) ? 1 : 0); - } - else { - part_fs_CloseDir(); - } - - return 0; -} - -int part_setPrintPos(){ - int left, right; - - if (parseIntInput(argv[0], &left)) - return INFUNC_FAIL; - - if (parseIntInput(argv[1], &right)) - return INFUNC_FAIL; - - if (left > 78) - return INFUNC_FAIL; - - if (right > 42) - return INFUNC_FAIL; - - gfx_con_setpos(left * 16, right * 16); - return 0; -} - -int part_stringcompare(){ - char *left, *right; - - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &right)) - return INFUNC_FAIL; - - return (strcmp(left, right)) ? 0 : 1; -} - -int part_fs_combinePath(){ - char *combined, *left, *middle; - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &middle)) - return INFUNC_FAIL; - if (argv[2][0] != '$') - return INFUNC_FAIL; - - combined = fsutil_getnextloc(left, middle); - - str_str_add(argv[2], combined); - free(combined); - return 0; -} - -int part_mmc_dumpPart(){ - char *left, *right; - - if (parseStringInput(argv[0], &left)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &right)) - return INFUNC_FAIL; - - if (!strcmp(left, "BOOT")){ - return emmcDumpBoot(right); - } - else { - return emmcDumpSpecific(left, right); - } -} - -int part_mmc_restorePart(){ - char *path; - - if (parseStringInput(argv[0], &path)) - return INFUNC_FAIL; - - if (currentlyMounted < 0) - return INFUNC_FAIL; - - return mmcFlashFile(path, currentlyMounted, false); -} - -int part_fs_extractBisFile(){ - char *path, *outfolder; - - if (parseStringInput(argv[0], &path)) - return INFUNC_FAIL; - if (parseStringInput(argv[1], &outfolder)) - return INFUNC_FAIL; - - return extract_bis_file(path, outfolder); -} - -int part_clearscreen(){ - gfx_clearscreen(); - return 0; -} - -int part_getPos(){ - return (int)f_tell(&scriptin); -} - -int part_subString(){ - char *str, *sub; - int start, size; - - if (parseStringInput(argv[0], &str)) - return INFUNC_FAIL; - if (parseIntInput(argv[1], &start)) - return INFUNC_FAIL; - if (parseIntInput(argv[2], &size)) - return INFUNC_FAIL; - if (argv[3][0] != '$') - return INFUNC_FAIL; - - if (start >= strlen(str)) - return INFUNC_FAIL; - - sub = utils_copyStringSize(str + start, size); - str_str_add(argv[3], sub); - free(sub); - return 0; -} - -int part_inputString(){ - char *start, *out; - int len; - - if (parseStringInput(argv[0], &start)) - return INFUNC_FAIL; - if (parseIntInput(argv[1], &len)) - return INFUNC_FAIL; - if (argv[2][0] != '$') - return INFUNC_FAIL; - - if (len > 39) - return INFUNC_FAIL; - - out = utils_InputText(start, len); - if (out == NULL) - return 1; - - str_str_add(argv[2], out); - free(out); - return 0; -} - -int part_strLen(){ - char *in; - - if (parseStringInput(argv[0], &in)) - return INFUNC_FAIL; - - return strlen(in); -} - -str_fnc_struct functions[] = { - {"printf", part_printf, 255}, - {"printInt", part_print_int, 1}, - {"setPrintPos", part_setPrintPos, 2}, - {"clearscreen", part_clearscreen, 0}, - {"if", part_if, 1}, - {"if", part_if_args, 3}, // function overloading - {"math", part_Math, 3}, - {"check", part_Check, 3}, - {"setInt", part_SetInt, 1}, - {"goto", part_goto, 1}, - {"setString", part_SetString, 2}, - {"setStringIndex", part_SetStringIndex, 2}, - {"setColor", part_setColor, 1}, - {"combineStrings", part_addstrings, 3}, - {"compareStrings", part_stringcompare, 2}, - {"subString", part_subString, 4}, - {"inputString", part_inputString, 3}, - {"stringLength", part_strLen, 1}, - {"invert", part_invert, 1}, - {"fs_exists", part_fs_exists, 1}, - {"fs_move", part_fs_Move, 2}, - {"fs_mkdir", part_fs_MakeDir, 1}, - {"fs_del", part_fs_Delete, 1}, - {"fs_delRecursive", part_fs_DeleteRecursive, 1}, - {"fs_copy", part_fs_Copy, 2}, - {"fs_copyRecursive", part_fs_CopyRecursive, 2}, - {"fs_openDir", part_fs_OpenDir, 1}, - {"fs_closeDir", part_fs_CloseDir, 0}, - {"fs_readDir", part_fs_ReadDir, 0}, - {"fs_combinePath", part_fs_combinePath, 3}, - {"fs_extractBisFile", part_fs_extractBisFile, 2}, - {"mmc_connect", part_ConnectMMC, 1}, - {"mmc_mount", part_MountMMC, 1}, - {"mmc_dumpPart", part_mmc_dumpPart, 2}, - {"mmc_restorePart", part_mmc_restorePart, 1}, - {"getPosition", part_getPos, 0}, - {"pause", part_Pause, 0}, - {"wait", part_Wait, 1}, - {"exit", part_Exit, 0}, - {NULL, NULL, 0} +const static str_fnc_struct functions[] = { + {"print", funcPrint, {0x40}}, + {"println", funcPrintln, {0x40}}, + {"if", funcIf, {1}}, + {"while", scriptWhile, {1}}, + {"else", scriptElse, {0}}, + {"len", scriptLen, {1}}, + {"byte", scriptMakeByteArray, {1}}, + {"wait", scriptWait, {0}}, + {"setPixel", scriptSetPixel, {5}}, + {NULL, NULL, {0}}, }; -#pragma GCC pop_options +varVector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len) { + varVector_t args = varVectorInit(4); + int lastLoc = 0; + for (int i = 0; i < len; i++) { + if (tokens[i].token == LSBracket) { + int distance = distanceBetweenTokens(&tokens[i + 1], len - i - 1, LSBracket, RSBracket); + i += distance + 1; + } + if (tokens[i].token == Seperator) { + dictValue_t res = solveEquation(ctx, &tokens[lastLoc], i - lastLoc); + lastLoc = i + 1; + varVectorAdd(&args, DictCreate(NULL, res)); + } -int run_function(char *func_name, int *out){ - for (u32 i = 0; functions[i].key != NULL; i++){ - if (!strcmp(functions[i].key, func_name)){ - if (argc != functions[i].arg_count && functions[i].arg_count != 255) - continue; + if (i + 1 >= len) { + dictValue_t res = solveEquation(ctx, &tokens[lastLoc], i + 1 - lastLoc); + varVectorAdd(&args, DictCreate(NULL, res)); + } + } - *out = functions[i].value(); - return (*out == INFUNC_FAIL) ? -1 : 0; - } - } - return -2; + return args; +} + +dictValue_t executeFunction(scriptCtx_t* ctx, char* func_name) { + int argCount = 0; + varVector_t args = { 0 }; + + if (ctx->args_loc.stored > 0) { + args = extractVars(ctx, ctx->args_loc.tokens, ctx->args_loc.stored); + + for (int i = 0; i < args.stored; i++) { + if (args.vars[i].value.varType == ErrType) + return args.vars[i].value; + } + + argCount = args.stored; + } + + for (u32 i = 0; functions[i].key != NULL; i++) { + if (!strcmp(functions[i].key, func_name)) { + if (argCount != functions[i].argCount && !functions[i].varArgs) + continue; + + dictValue_t val = functions[i].value(ctx, &args); + // free args + varVectorFree(&args); + return val; + } + } + + dictValue_t* var = varVectorFind(&ctx->vars, func_name); + if (var != NULL) { + if (var->type == JumpType) { + setCurIndentInstruction(ctx, 0, ctx->curPos); + ctx->curPos = var->integer - 1; + ctx->lastToken.token = Invalid; + return NullDictValue(); + } + } + + return ErrDictValue(ERRNOFUNC); } \ No newline at end of file diff --git a/source/tegraexplorer/script/functions.h b/source/tegraexplorer/script/functions.h index ac9e03d..7013ed3 100644 --- a/source/tegraexplorer/script/functions.h +++ b/source/tegraexplorer/script/functions.h @@ -1,13 +1,10 @@ #pragma once -#include "../../utils/types.h" +#include "types.h" -typedef void (*func_void_ptr)(); -typedef int (*func_int_ptr)(); +#define BIT(n) (1U << n) -typedef struct { - char *key; - func_int_ptr value; - u8 arg_count; -} str_fnc_struct; +#define VARARGS(x) {x, 1, 0} +#define OPERATORARGS(x) {x, 0, 1} -int run_function(char *func_name, int *out); \ No newline at end of file +dictValue_t executeFunction(scriptCtx_t* ctx, char* func_name); +varVector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len); \ No newline at end of file diff --git a/source/tegraexplorer/script/lexer.c b/source/tegraexplorer/script/lexer.c new file mode 100644 index 0000000..196e800 --- /dev/null +++ b/source/tegraexplorer/script/lexer.c @@ -0,0 +1,195 @@ +#include "lexer.h" +#include "parser.h" +#include "args.h" +#include "../utils/utils.h" +#include "../../mem/heap.h" +#include + +static inline int isValidWord(char c) { + char r = c | 0x20; + return (r >= 'a' && r <= 'z'); +} + +static inline int isValidVar(char c) { + char r = c | 0x20; + return ((r >= 'a' && r <= 'z') || (r >= '0' && r <= '9')); +} + +static inline int isValidNum(char c) { + return (c >= '0' && c <= '9'); +} + +static inline int isValidHexNum(char c) { + char r = c | 0x20; + return (isValidNum(r) || (r >= 'a' && r <= 'f')); +} + +#define makeLexarToken(token, var) (lexarToken_t) {token, {var}} +#define makeLexarIntToken(intVar) (lexarToken_t) {IntLit, .val = intVar} + +typedef struct { + u8 tokenC; + u16 tokenN; +} lexarTranslation_t; + +lexarTranslation_t lexarTranslations[] = { + {'(', LBracket}, + {')', RBracket}, + {'{', LCBracket}, + {'}', RCBracket}, + {',', Seperator}, + {'+', Plus}, + {'-', Minus}, + {'*', Multiply}, + {'/', Division}, + {'%', Mod}, + {'<', Smaller}, + {'>', Bigger}, + {'=', Equal}, + {'!', Not}, + {'[', LSBracket}, + {']', RSBracket}, + {'\0', 0}, +}; + +char lexarDebugGetTokenC(u16 tokenN) { + for (int i = 0; lexarTranslations[i].tokenC; i++) { + if (lexarTranslations[i].tokenN == tokenN) { + return lexarTranslations[i].tokenC; + } + } + + return '?'; +} + +lexarVector_t lexarVectorInit(int startSize) { + lexarVector_t l = { 0 }; + l.tokens = calloc(startSize, sizeof(lexarVector_t)); + l.len = startSize; + l.stored = 0; + return l; +} + +void lexarVectorAdd(lexarVector_t* vec, lexarToken_t token) { + if (vec->stored >= vec->len) { + vec->len *= 2; + //vec->tokens = realloc(vec->tokens, vec->len * sizeof(lexarToken_t)); + void *temp = calloc(vec->len, sizeof(lexarToken_t)); + memcpy(temp, vec->tokens, vec->len / 2 * sizeof(lexarToken_t)); + free(vec->tokens); + vec->tokens = temp; + } + + vec->tokens[vec->stored++] = token; +} + +void lexarVectorClear(lexarVector_t* vec) { + for (int i = 0; i < vec->stored; i++) { + if (vec->tokens[i].token == Variable || vec->tokens[i].token == StrLit) + if (vec->tokens[i].text != NULL) + free(vec->tokens[i].text); + } + free(vec->tokens); +} + +#define ELIFC(c) else if (*in == c) + +lexarVector_t lexarGo(const char* in) { + lexarVector_t vec = lexarVectorInit(16); + + while (*in) { + if (isValidWord(*in)) { + char* startWord = in; + in++; + while (isValidVar(*in)) + in++; + + lexarVectorAdd(&vec, makeLexarToken(Variable, utils_copyStringSize(startWord, in - startWord))); + continue; + } + else if (isValidNum(*in) || (*in == '-' && isValidNum(in[1]))) { + int parse = 0; + u8 negative = (*in == '-'); + if (negative) + in++; + + if (*in == '0' && (in[1] | 0x20) == 'x') { + in += 2; + while (isValidHexNum(*in)) { + parse = parse * 16 + (*in & 0x0F) + (*in >= 'A' ? 9 : 0); + in++; + } + } + else while (isValidNum(*in)) { + parse = parse * 10 + *in++ - '0'; + } + + if (negative) + parse *= -1; + + lexarVectorAdd(&vec, makeLexarIntToken(parse)); + continue; + } + ELIFC('"') { + char* startStr = ++in; + while (*in != '"') + in++; + + lexarVectorAdd(&vec, makeLexarToken(StrLit, utils_copyStringSize(startStr, in - startStr))); + in++; + continue; + } + ELIFC('#') { + while (*in != '\n') + in++; + + in++; + continue; + } + ELIFC('&') { + if (in[1] == '&') { + lexarVectorAdd(&vec, makeLexarToken(LogicAND, 0)); + in++; + } + else { + lexarVectorAdd(&vec, makeLexarToken(AND, 0)); + } + + in++; + continue; + } + ELIFC('|') { + if (in[1] == '|') { + lexarVectorAdd(&vec, makeLexarToken(LogicOR, 0)); + in++; + } + else { + lexarVectorAdd(&vec, makeLexarToken(OR, 0)); + } + + in++; + continue; + } + + int val = 0; + + for (int i = 0; lexarTranslations[i].tokenC; i++) { + if (lexarTranslations[i].tokenC == *in) { + val = lexarTranslations[i].tokenN; + break; + } + } + + in++; + + if (*in == '=' && val >= Smaller && val <= Not) { + val++; + in++; + } + + if (val != Invalid) + lexarVectorAdd(&vec, makeLexarToken(val, 0)); + } + + return vec; +} \ No newline at end of file diff --git a/source/tegraexplorer/script/lexer.h b/source/tegraexplorer/script/lexer.h new file mode 100644 index 0000000..741e1dd --- /dev/null +++ b/source/tegraexplorer/script/lexer.h @@ -0,0 +1,6 @@ +#pragma once +#include "types.h" + +lexarVector_t lexarGo(const char* in); +char lexarDebugGetTokenC(u16 tokenN); +void lexarVectorClear(lexarVector_t* vec); \ No newline at end of file diff --git a/source/tegraexplorer/script/list.c b/source/tegraexplorer/script/list.c new file mode 100644 index 0000000..b425a37 --- /dev/null +++ b/source/tegraexplorer/script/list.c @@ -0,0 +1,88 @@ +#include "list.h" +#include +#include "types.h" +#include "../../mem/heap.h" + +void freeDictValue(dictValue_t dv) { + if (dv.string == NULL) + return; + + switch (dv.varType) { + case StringType: + case IntArrayType: + case ByteArrayType: // As it's a union with the biggest object being a pointer, this should be the same + free(dv.string); + break; + case StringArrayType: + for (unsigned int i = 0; i < dv.arrayLen; i++) + free(dv.stringArray[i]); + + free(dv.stringArray); + } +} + +void freeDict(dict_t* dict) { + freeDictValue(dict->value); + free(dict->key); + free(dict); +} + +varVector_t varVectorInit(int startSize) { + varVector_t vec = { 0 }; + + vec.vars = calloc(startSize, sizeof(dict_t)); + vec.len = startSize; + vec.stored = 0; + return vec; +} + +dictValue_t* varVectorFind(varVector_t* vec, const char* key) { + for (int i = 0; i < vec->stored; i++) { + if (!strcmp(vec->vars[i].key, key)) + return &vec->vars[i].value; + } + + return NULL; +} + +void varVectorAdd(varVector_t* vec, dict_t add) { + dictValue_t* var = NULL; + if (add.key != NULL) + var = varVectorFind(vec, add.key); + + if (var != NULL) { + freeDictValue(*var); + *var = add.value; + free(add.key); + return; + } + + if (vec->stored >= vec->len) { + vec->len *= 2; + dict_t* old = vec->vars; + //vec->vars = realloc(old, vec->len * sizeof(dict_t)); + void *temp = calloc(vec->len, sizeof(dict_t)); + memcpy(temp, vec->vars, vec->len / 2 * sizeof(dict_t)); + free(vec->vars); + vec->vars = temp; + if (vec->vars == NULL) { + free(old); + } + } + + vec->vars[vec->stored] = add; + vec->stored++; +} + +void varVectorFree(varVector_t* vec) { + for (int i = 0; i < vec->stored; i++) { + dict_t *var = &vec->vars[i]; + if (var->key != NULL) { + free(var->key); + var->key = NULL; + } + if (var->value.free) + freeDictValue(var->value); + } + free(vec->vars); +} \ No newline at end of file diff --git a/source/tegraexplorer/script/list.h b/source/tegraexplorer/script/list.h new file mode 100644 index 0000000..1cf841b --- /dev/null +++ b/source/tegraexplorer/script/list.h @@ -0,0 +1,25 @@ +#pragma once +#include "types.h" + +#define dictValSetInt(dictVal, i) dictVal.integer = i; dictVal.type = IntType; +#define dictValSetStr(dictVal, str) dictVal.string = strdup(str); dictVal.type = StringType; +#define dictValSetJump(dictVal, i) dictVal.integer = i; dictVal.type = JumpType; + +#define dictValInt(i) (dictValue_t) {IntType, 0, i} + +#define DictCreate(key, value) (dict_t) {key, value} +#define DictValueCreate(type, arrayLen, val) (dictValue_t) {type, arrayLen, val} + +void freeDictValue(dictValue_t dv); +void freeDict(dict_t* dict); + +varVector_t varVectorInit(int startSize); +void varVectorAdd(varVector_t* vec, dict_t add); +dictValue_t* varVectorFind(varVector_t* vec, const char* key); + +static inline void freeDictValueOnVar(dictValue_t dv) { + if (dv.free) + freeDictValue(dv); +} + +void varVectorFree(varVector_t* vec); \ No newline at end of file diff --git a/source/tegraexplorer/script/parser.c b/source/tegraexplorer/script/parser.c index 0cb52e7..36707d4 100644 --- a/source/tegraexplorer/script/parser.c +++ b/source/tegraexplorer/script/parser.c @@ -1,307 +1,263 @@ +#include "args.h" +#include "parser.h" +#include "types.h" +#include "list.h" +#include "functions.h" +#include "scriptCtx.h" +#include "lexer.h" #include #include "../../mem/heap.h" -#include "../gfx/gfxutils.h" -#include "../emmc/emmc.h" -#include "../../utils/types.h" -#include "../../libs/fatfs/ff.h" -#include "../../utils/sprintf.h" -#include "../../utils/btn.h" -#include "../../gfx/gfx.h" -#include "../../utils/util.h" -#include "../../storage/emummc.h" -#include "parser.h" -#include "../common/common.h" -#include "../fs/fsactions.h" -#include "functions.h" -#include "variables.h" -#include "../fs/fsreader.h" #include "../utils/utils.h" +#include "../../storage/nx_sd.h" +#include "../../gfx/gfx.h" #include "../../hid/hid.h" +#include "../gfx/gfxutils.h" -int countchars(const char* in, char target) { - u32 len = strlen(in); - u32 count = 0; - - for (u32 i = 0; i < len; i++) { - if (in[i] == '"'){ - while (in[++i] != '"'){ - if (i >= len) - return -1; - } - } - if (in[i] == target) - count++; - } +//#define DPRINTF(str, ...) printf(str, __VA_ARGS__) +#define DPRINTF - return count; +#define findNextCharsCtx(ctx, targets) findNextChars(&(ctx->curPos), targets) + +int checkIfVar(u16 token) { + return (token == StrLit || token == IntLit || token == Variable || token == RSBracket); } -char **argv = NULL; -u32 argc; -u32 splitargs(const char* in) { - // arg like '5, "6", @arg7' - u32 i = 0, count = 1, len = strlen(in), curcount = 0, begin, end; +void printToken(lexarToken_t* token) { + switch (token->token) { + case 1: + gfx_printf("%s ", token->text); + break; + case 6: + //printf("%d: '%s'\n", vec.tokens[i].token, vec.tokens[i].text); + gfx_printf("\"%s\" ", token->text); + break; + case 7: + //printf("%d: %d\n", vec.tokens[i].token, vec.tokens[i].val); + gfx_printf("%d ", token->val); + break; + default: + //printf("%d: %c\n", vec.tokens[i].token, lexarDebugGetTokenC(vec.tokens[i].token)); + gfx_printf("%c ", lexarDebugGetTokenC(token->token)); + break; + } +} - count += countchars(in, ','); +scriptResult_t runFunction(scriptCtx_t* ctx) { + dictValue_t res = solveEquation(ctx, &ctx->script->tokens[ctx->startEq], ctx->curPos - ctx->startEq); - if (!count) - return 0; - - argv = calloc(count + 1, sizeof(char*)); - - while (i < len && curcount < count) { - if (in[i] == ' ' || in[i] == ','){ - i++; - continue; - } - - begin = i; + if (res.varType == ErrType) + return scriptResultCreate(res.integer, &ctx->script->tokens[ctx->startEq]); - while (strrchr(" ,)", in[i]) == NULL){ - if (in[i] == '"'){ - begin = i + 1; - while (in[++i] != '"'){ - if (in[i] == '\0') - return 0; + ctx->startEq = ctx->curPos; + if (ctx->varToken.token != Invalid) { // we should not assign null's + ctx->varToken.token = Invalid; + if (ctx->varIndexSet) { + ctx->varIndexSet = 0; + dictValue_t* arrayVal = varVectorFind(&ctx->vars, ctx->varToken.text); + if (!(arrayVal == NULL || arrayVal->arrayLen <= ctx->varIndex)) { + if (arrayVal->varType == IntArrayType && res.varType == IntType) { + arrayVal->integerArray[ctx->varIndex] = res.integer; + } + else if (arrayVal->varType == StringArrayType && res.varType == StringType) { + free(arrayVal->stringArray[ctx->varIndex]); + arrayVal->stringArray[ctx->varIndex] = res.string; + } + else if (arrayVal->varType == ByteArrayType && res.varType == IntType) { + arrayVal->byteArray[ctx->varIndex] = (u8)(res.integer & 0xFF); + } + else { + return scriptResultCreate(ERRINVALIDTYPE, &ctx->script->tokens[ctx->startEq]); } } + else { + return scriptResultCreate(ERRNOVAR, &ctx->script->tokens[ctx->startEq]); + } + } + else { + varVectorAdd(&ctx->vars, DictCreate(util_cpyStr(ctx->varToken.text), res)); + } + } + else { + freeDictValueOnVar(res); + } - if (in[i] == '\0') - return 0; + return scriptResultCreate(0, 0); +} +// TODO +// Make sure mem gets free'd properly +// Fix bug that custom functions cannot be run from end of script +// add arrays + +#define RUNFUNCWITHPANIC(ctx) scriptResult_t res = runFunction(ctx); if (res.resCode != 0) return res + +scriptResult_t mainLoop(scriptCtx_t* ctx) { + for (ctx->curPos = 0; ctx->curPos < ctx->script->stored; ctx->curPos++) { + u32 i = ctx->curPos; + lexarToken_t curToken = ctx->script->tokens[i]; + //printToken(&curToken); + + if (checkIfVar(ctx->lastToken.token) && curToken.token == Variable) { + RUNFUNCWITHPANIC(ctx); + } + else if (curToken.token == Equal) { + // Set last var to lastToken, if lastToken is var + + if (ctx->lastToken.token == RSBracket) { + int layer = 0; + i = ctx->lastTokenPos - 1; + for (; i > 0; i--) { + if (ctx->script->tokens[i].token == RSBracket) + layer++; + else if (ctx->script->tokens[i].token == LSBracket) { + if (layer == 0) + break; + layer--; + } + } + + i--; + + if (ctx->script->tokens[i].token == Variable) { + dictValue_t index = solveEquation(ctx, &ctx->script->tokens[i + 2], ctx->lastTokenPos - i - 2); + ctx->lastTokenPos = i; + ctx->lastToken = ctx->script->tokens[i]; + + if (index.varType == IntType) { + ctx->varIndex = index.integer; + ctx->varIndexSet = 1; + + if (ctx->varIndexSet) { + dictValue_t* arrayVal = varVectorFind(&ctx->vars, ctx->varToken.text); + if (arrayVal == NULL) { + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); + } + if (ctx->varIndex < 0 || ctx->varIndex >= arrayVal->arrayLen || arrayVal->varType < IntArrayType) + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); + } + } + else + return scriptResultCreate(ERRINVALIDTYPE, &ctx->script->tokens[ctx->curPos]); + } + else + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); + } + + if (ctx->lastToken.token == Variable) { + ctx->startEq = ctx->curPos + 1; + ctx->varToken = ctx->lastToken; + } + else + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); + + //printf("Setting token %s to next assignment", ctx->lastToken.text); + + // !! check prev for ] for arrays + } + else if (curToken.token == LBracket) { i++; - } + int distance = distanceBetweenTokens(&ctx->script->tokens[i], ctx->script->stored - i, LBracket, RBracket); + if (distance < 0) + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); - end = i; + i += distance; + ctx->curPos = i; - if (in[i - 1] == '"'){ - end--; - } - - argv[curcount++] = utils_copyStringSize(in + begin, (u32)(end - begin)); - } - return curcount; -} - -FIL scriptin; -UINT endByte = 0; -int forceExit = false; -char currentchar = 0; - -char getnextchar(){ - f_read(&scriptin, ¤tchar, sizeof(char), &endByte); - - if (sizeof(char) != endByte) - forceExit = true; - - //gfx_printf("|%c|", currentchar); - return currentchar; -} - -void getfollowingchar(char end){ - while (currentchar != end && !f_eof(&scriptin)){ - if (currentchar == '"'){ - while (getnextchar() != '"' && !f_eof(&scriptin)); - } - getnextchar(); - } -} - -void getnextvalidchar(){ - while ((!((currentchar >= '?' && currentchar <= 'Z') || (currentchar >= 'a' && currentchar <= 'z') || currentchar == '#') && !f_eof(&scriptin)) /*|| currentchar == ';' */) - getnextchar(); -} - -char *makestr(u32 size, char ignore){ - char *str; - u32 count = 0; - - str = calloc(size + 1, sizeof(char)); - for (u32 i = 0; i < size; i++){ - getnextchar(); - if (ignore != 0 && ignore == currentchar) + if (ctx->curPos + 1 >= ctx->script->stored && checkIfVar(ctx->lastToken.token)) { + solveEquation(ctx, &ctx->script->tokens[ctx->startEq], i + 1 - ctx->startEq); + } continue; - - str[count++] = currentchar; - } - - return str; -} + } + else if (curToken.token == LCBracket) { + if (ctx->lastToken.token == Equal && ctx->varToken.token == Variable) { + varVectorAdd(&ctx->vars, DictCreate(util_cpyStr(ctx->varToken.text), DictValueCreate(JumpType, 0, ctx->curPos))); + ctx->varToken.token = Invalid; + setCurIndentInstruction(ctx, 1, -1); + } -char *readtilchar(char end, char ignore){ - FSIZE_t offset, size; + if (checkIfVar(ctx->lastToken.token)) { + RUNFUNCWITHPANIC(ctx); + } - offset = f_tell(&scriptin); - getfollowingchar(end); - size = f_tell(&scriptin) - offset; + ctx->startEq = ctx->curPos + 1; - if (size <= 0) - return NULL; + indentInstructor_t ins = getCurIndentInstruction(ctx); + if (ins.active) { + if (ins.skip) { + int distance = distanceBetweenTokens(&ctx->script->tokens[i + 1], ctx->script->stored - i - 1, LCBracket, RCBracket); + if (distance < 0) + return scriptResultCreate(ERRSYNTAX, &ctx->script->tokens[ctx->curPos]); + ctx->curPos += distance + 1; + ctx->startEq = ctx->curPos + 1; + } + else { + ctx->indentLevel++; + } + } + else + return scriptResultCreate(ERRINACTIVEINDENT, &ctx->script->tokens[ctx->curPos]); + } + else if (curToken.token == RCBracket) { + if (checkIfVar(ctx->lastToken.token)) { + RUNFUNCWITHPANIC(ctx); + if (i != ctx->curPos) + continue; + } - f_lseek(&scriptin, offset - 1); + ctx->indentLevel--; - return makestr((u32)size, ignore); -} + indentInstructor_t ins = getCurIndentInstruction(ctx); + if (ins.active && ins.jump) { + ctx->curPos = ins.jumpLoc - 1; + } - -char *funcbuff = NULL; -void functionparser(){ - char *unsplitargs; - - /* - if (funcbuff != NULL) - free(funcbuff); - */ - - funcbuff = readtilchar('(', ' '); - - getfollowingchar('('); - getnextchar(); - - unsplitargs = readtilchar(')', 0); - - if (unsplitargs != NULL){ - argc = splitargs(unsplitargs); - getnextchar(); - } - else { - argc = 0; - } - getnextchar(); - - free(unsplitargs); -} - -char *gettargetvar(){ - char *variable = NULL; - - variable = readtilchar('=', ' '); - - getfollowingchar('='); - getnextchar(); - - return variable; -} - -void mainparser(){ - char *variable = NULL; - int res, out = 0; - - getnextvalidchar(); - - if (f_eof(&scriptin)) - return; - - if (currentchar == '#'){ - getfollowingchar('\n'); - return; - } - - if (currentchar == '@'){ - variable = gettargetvar(); - getnextvalidchar(); - } - - functionparser(); - - res = run_function(funcbuff, &out); - if (res < 0){ - printerrors = true; - //gfx_printf("%s|%s|%d", funcbuff, argv[0], argc); - //btn_wait(); - int lineNumber = 1; - u64 end = f_tell(&scriptin); - f_lseek(&scriptin, 0); - - while (f_tell(&scriptin) < end && !f_eof(&scriptin)){ - if (getnextchar() == '\n') - lineNumber++; + ctx->startEq = ctx->curPos + 1; + } + else if (ctx->curPos + 1 >= ctx->script->stored && checkIfVar(ctx->lastToken.token)) { + solveEquation(ctx, &ctx->script->tokens[ctx->startEq], i + 1 - ctx->startEq); } - gfx_errDisplay((res == -1) ? funcbuff : "run_function", (res == -1) ? ERR_IN_FUNC : ERR_SCRIPT_LOOKUP_FAIL, lineNumber); - forceExit = true; - //gfx_printf("Func: %s\nArg1: %s\n", funcbuff, argv[0]); - } - else { - str_int_add("@RESULT", out); - - if (variable != NULL) - str_int_add(variable, out); + ctx->lastToken = ctx->script->tokens[ctx->curPos]; + ctx->lastTokenPos = ctx->curPos; } - //gfx_printf("\nGoing to next func %c\n", currentchar); - - if (funcbuff != NULL){ - free(funcbuff); - funcbuff = NULL; - } - - if (argv != NULL) { - for (int i = 0; argv[i] != NULL; i++) - free(argv[i]); - free(argv); - argv = NULL; - } - - if (variable != NULL){ - free(variable); - } + return scriptResultCreate(0, 0); } -void skipbrackets(){ - u32 bracketcounter = 0; +const char* functionFails[] = { + "An invalid operation was performed", + "Double Nots are not allowed", + "A syntax error was found", + "The recieved type was incorrect", + "The variable could not be found", + "The specified function could not be found", + "An inactive indent was found" +}; - getfollowingchar('{'); - getnextchar(); +void startScript(char* path) { + char* file = sd_file_read(path, NULL); - while ((currentchar != '}' || bracketcounter != 0) && !f_eof(&scriptin)){ - if (currentchar == '{') - bracketcounter++; - else if (currentchar == '}') - bracketcounter--; - - getnextchar(); - } -} - -extern u32 currentcolor; -extern char *currentpath; -void runScript(char *path){ - int res; - char *path_local = NULL; - forceExit = false; - currentchar = 0; - currentcolor = COLOR_WHITE; - gfx_clearscreen(); - utils_copystring(path, &path_local); - - res = f_open(&scriptin, path, FA_READ | FA_OPEN_EXISTING); - if (res != FR_OK){ - gfx_errDisplay("ParseScript", res, 1); + if (file == NULL) return; + + gfx_clearscreen(); + + scriptCtx_t ctx = createScriptCtx(); + lexarVector_t vec = lexarGo(file); + free(file); + ctx.script = &vec; + + scriptResult_t res = mainLoop(&ctx); + gfx_printf("end of script\n"); + + if (res.resCode) { + gfx_printf("[ERROR] %d\n%s\nNear:", res.resCode, functionFails[res.resCode - 1]); + printToken(res.nearToken); } - - printerrors = false; - - //add builtin vars - str_int_add("@EMUMMC", emu_cfg.enabled); - str_int_add("@RESULT", 0); - str_int_add("@JOYCONN", hidConnected()); - str_str_add("$CURRENTPATH", currentpath); - - //str_int_printall(); - - while (!f_eof(&scriptin) && !forceExit){ - mainparser(); - } - - printerrors = true; - //str_int_printall(); - - f_close(&scriptin); - str_int_clear(); - //str_jmp_clear(); - str_str_clear(); - free(path_local); - //btn_wait(); + + gfx_printf("\n\n"); + //mainLoop2(&ctx, &vec); + lexarVectorClear(&vec); + destroyScriptCtx(&ctx); + hidWait(); } \ No newline at end of file diff --git a/source/tegraexplorer/script/parser.h b/source/tegraexplorer/script/parser.h index 6917783..16d6508 100644 --- a/source/tegraexplorer/script/parser.h +++ b/source/tegraexplorer/script/parser.h @@ -1,7 +1,9 @@ #pragma once -#define INFUNC_FAIL (int)0xC0000000 +#include "types.h" +//#define DictValue(type, val, len) (dictValue_t) {type, len, val} -void runScript(char *path); -void skipbrackets(); -void getfollowingchar(char end); \ No newline at end of file +#define scriptResultCreate(resCode, funcName) (scriptResult_t) {resCode, funcName} + +void startScript(char* path); +char* readFile(char* path); \ No newline at end of file diff --git a/source/tegraexplorer/script/scriptCtx.c b/source/tegraexplorer/script/scriptCtx.c new file mode 100644 index 0000000..072fc89 --- /dev/null +++ b/source/tegraexplorer/script/scriptCtx.c @@ -0,0 +1,37 @@ +#include "scriptCtx.h" +#include "list.h" +#include "malloc.h" +#include "types.h" + +scriptCtx_t createScriptCtx() { + scriptCtx_t ctx; + + ctx.indentInstructors = calloc(sizeof(indentInstructor_t), 64); + ctx.indentLevel = 0; + ctx.vars = varVectorInit(16); + ctx.script = NULL; + ctx.lastToken.token = 0; + ctx.varToken.token = 0; + ctx.startEq = 0; + ctx.varIndexStruct = 0; + + return ctx; +} + +void destroyScriptCtx(scriptCtx_t* ctx) { + // TODO + varVectorFree(&ctx->vars); + free(ctx->indentInstructors); +} + +u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, int jumpLoc) { + if (level >= 64) + return 1; + + ctx->indentInstructors[level].skip = skip; + ctx->indentInstructors[level].active = 1; + ctx->indentInstructors[level].jump = (jumpLoc >= 0) ? 1 : 0; + ctx->indentInstructors[level].jumpLoc = jumpLoc; + + return 0; +} \ No newline at end of file diff --git a/source/tegraexplorer/script/scriptCtx.h b/source/tegraexplorer/script/scriptCtx.h new file mode 100644 index 0000000..19aa5bd --- /dev/null +++ b/source/tegraexplorer/script/scriptCtx.h @@ -0,0 +1,19 @@ +#pragma once +#include "types.h" + +#define TEXTHOLDEREMPTY() (textHolder_t) {0} +#define TEXTHOLDER(start, len) (textHolder_t) {start, len, 1} + +u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, int jumpLoc); +void destroyScriptCtx(scriptCtx_t* ctx); +scriptCtx_t createScriptCtx(); +static inline indentInstructor_t getCurIndentInstruction(scriptCtx_t* ctx); +static inline u8 setCurIndentInstruction(scriptCtx_t* ctx, u8 skip, int jumpLoc); + +static inline u8 setCurIndentInstruction(scriptCtx_t* ctx, u8 skip, int jumpLoc) { + return setIndentInstruction(ctx, ctx->indentLevel, skip, jumpLoc); +} + +static inline indentInstructor_t getCurIndentInstruction(scriptCtx_t* ctx) { + return ctx->indentInstructors[ctx->indentLevel]; +} \ No newline at end of file diff --git a/source/tegraexplorer/script/types.h b/source/tegraexplorer/script/types.h new file mode 100644 index 0000000..54c78dc --- /dev/null +++ b/source/tegraexplorer/script/types.h @@ -0,0 +1,174 @@ +#pragma once + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; + +typedef struct { + u16 token; + union { + char* text; + int val; + }; +} lexarToken_t; + +typedef struct { + lexarToken_t* tokens; + u32 len; + u32 stored; +} lexarVector_t; + +enum Tokens { + Invalid = 0, + Variable = 1, + LBracket, + RBracket, + RCBracket, + LCBracket, + StrLit, + IntLit, + Seperator, + Plus, + Minus, + Multiply, + Division, + Mod, + Smaller, + SmallerEqual, + Bigger, + BiggerEqual, + Equal, + EqualEqual, + Not, + NotEqual, + LogicAND, + LogicOR, + LSBracket, + RSBracket, + AND, + OR +}; + +enum Errors { + ERRBADOPERATOR = 1, + ERRDOUBLENOT, + ERRSYNTAX, + ERRINVALIDTYPE, + ERRNOVAR, + ERRNOFUNC, + ERRINACTIVEINDENT +}; + +typedef struct { // this is to keep track of how many {} we passed. Keep an internal var with the "indentation level", +1 for {, -1 for }. have an array with the following def on what to do (on func: enter, set indentation & jump back, on while, jump to while, use while as if, on if simply set true or false) + union { + struct { + u8 active : 1; + u8 skip : 1; + u8 jump : 1; + }; + u8 container; + }; + int jumpLoc; +} indentInstructor_t; + +typedef struct { + char* start; + u32 len; + union { + struct { + u8 set : 1; + }; + u8 options; + }; +} textHolder_t; + +typedef struct { + int resCode; + lexarToken_t* nearToken; +} scriptResult_t; + +#define JumpDictValue(jump) (dictValue_t) {{JumpType}, 0, .integer = jump} +#define StrDictValue(str) (dictValue_t) {{StringType}, 0, .string = str} +#define ErrDictValue(err) (dictValue_t) {{ErrType}, 0, .integer = err} +#define NullDictValue() (dictValue_t) {{NullType}, 0, .integer = 0} +#define IntArrDictValue(arr, len) (dictValue_t) {{IntArrayType}, len, .integerArray = arr} +#define StringArrDictValue(arr, len) (dictValue_t) {{StringArrayType}, len, .stringArray = arr} +#define IntDictValue(i) (dictValue_t) {{IntType}, 0, .integer = i} + +enum { + IntType = 0, + StringType, + IntArrayType, + StringArrayType, + ByteArrayType, + JumpType, + DictType, + NullType, + ErrType, +}; + +typedef struct { + union { + u8 type; + struct { + u8 varType : 7; + u8 free : 1; + }; + }; + unsigned int arrayLen; + union { + int integer; + char* string; + unsigned char* byteArray; + int* integerArray; + char** stringArray; + }; +} dictValue_t; + +typedef struct { + char* key; + dictValue_t value; +} dict_t; + +typedef struct { + dict_t* vars; + u32 len; + u32 stored; +} varVector_t; + +typedef struct { + indentInstructor_t* indentInstructors; + u8 indentLevel; // -> max 63 + varVector_t vars; + lexarVector_t* script; + lexarToken_t lastToken; + lexarToken_t varToken; + u32 startEq; + u32 curPos; + u32 lastTokenPos; + lexarVector_t args_loc; + union { + u32 varIndexStruct; + struct { + u32 varIndex : 31; + u32 varIndexSet : 1; + }; + }; +} scriptCtx_t; + +typedef dictValue_t(*func_int_ptr)(scriptCtx_t* ctx, varVector_t* args); + +typedef struct { + char* key; + func_int_ptr value; + union { + u8 args; + struct { + u8 argCount : 6; + u8 varArgs : 1; + u8 operatorParse : 1; + }; + }; +} str_fnc_struct; + +#define FREEINDICT 0x80 \ No newline at end of file diff --git a/source/tegraexplorer/script/variables.c b/source/tegraexplorer/script/variables.c deleted file mode 100644 index 0382dcb..0000000 --- a/source/tegraexplorer/script/variables.c +++ /dev/null @@ -1,250 +0,0 @@ -#include -#include "../../mem/heap.h" -#include "../gfx/gfxutils.h" -#include "../emmc/emmc.h" -#include "../../utils/types.h" -#include "../../libs/fatfs/ff.h" -#include "../../utils/sprintf.h" -#include "../../utils/btn.h" -#include "../../gfx/gfx.h" -#include "../../utils/util.h" -#include "../../storage/emummc.h" -#include "parser.h" -#include "../common/common.h" -#include "../fs/fsactions.h" -#include "variables.h" -#include "../utils/utils.h" - -static dict_str_int *str_int_table = NULL; -static dict_str_str *str_str_table = NULL; -//static dict_str_loc *str_jmp_table = NULL; - -int str_int_add(char *key, int value){ - char *key_local; - dict_str_int *keyvaluepair; - - utils_copystring(key, &key_local); - - keyvaluepair = calloc(1, sizeof(dict_str_int)); - keyvaluepair->key = key_local; - keyvaluepair->value = value; - keyvaluepair->next = NULL; - - if (str_int_table == NULL){ - str_int_table = keyvaluepair; - } - else { - dict_str_int *temp; - temp = str_int_table; - while (temp != NULL){ - if (!strcmp(temp->key, key_local)){ - free(keyvaluepair); - free(key_local); - temp->value = value; - return 0; - } - - if (temp->next == NULL){ - temp->next = keyvaluepair; - return 0; - } - - temp = temp->next; - } - } - - return 0; -} - -int str_int_find(char *key, int *out){ - dict_str_int *temp; - temp = str_int_table; - while (temp != NULL){ - if (!strcmp(temp->key, key)){ - *out = temp->value; - return 0; - } - temp = temp->next; - } - - return -1; -} - -void str_int_clear(){ - dict_str_int *cur, *next; - cur = str_int_table; - - while (cur != NULL){ - next = cur->next; - free(cur->key); - free(cur); - cur = next; - } - str_int_table = NULL; -} - -void str_int_printall(){ - dict_str_int *temp; - temp = str_int_table; - while (temp != NULL){ - gfx_printf("%s -> %d\n", temp->key, temp->value); - temp = temp->next; - } -} -/* -int str_jmp_add(char *key, u64 value){ - char *key_local; - dict_str_loc *keyvaluepair; - - //gfx_printf("Adding |%s|\n", key_local); - - utils_copystring(key, &key_local); - - keyvaluepair = calloc(1, sizeof(dict_str_loc)); - keyvaluepair->key = key_local; - keyvaluepair->value = value; - keyvaluepair->next = NULL; - - if (str_jmp_table == NULL){ - str_jmp_table = keyvaluepair; - } - else { - dict_str_loc *temp; - temp = str_jmp_table; - while (temp != NULL){ - if (!strcmp(temp->key, key_local)){ - free(keyvaluepair); - free(key_local); - - temp->value = value; - return 0; - } - - if (temp->next == NULL){ - temp->next = keyvaluepair; - return 0; - } - - temp = temp->next; - } - } - - return 0; -} - -int str_jmp_find(char *key, u64 *out){ - dict_str_loc *temp; - temp = str_jmp_table; - //gfx_printf("Searching |%s|\n", key); - while (temp != NULL){ - if (!strcmp(temp->key, key)){ - //gfx_printf("Key found!\n", temp->value); - *out = temp->value; - return 0; - } - temp = temp->next; - } - - //gfx_printf("no key!\n"); - return -1; -} - -void str_jmp_clear(){ - dict_str_loc *cur, *next; - cur = str_jmp_table; - - while (cur != NULL){ - next = cur->next; - free(cur->key); - free(cur); - cur = next; - } - str_jmp_table = NULL; -} -*/ - -int str_str_add(char *key, char *value){ - char *key_local, *value_local; - dict_str_str *keyvaluepair; - //gfx_printf("Adding |%s|\n", key_local); - utils_copystring(value, &value_local); - utils_copystring(key, &key_local); - - keyvaluepair = calloc(1, sizeof(dict_str_str)); - keyvaluepair->key = key_local; - keyvaluepair->value = value_local; - keyvaluepair->next = NULL; - - if (str_str_table == NULL){ - str_str_table = keyvaluepair; - } - else { - dict_str_str *temp; - temp = str_str_table; - while (temp != NULL){ - if (!strcmp(temp->key, key_local)){ - free(keyvaluepair); - free(key_local); - - free(temp->value); - temp->value = value_local; - return 0; - } - - if (temp->next == NULL){ - temp->next = keyvaluepair; - return 0; - } - - temp = temp->next; - } - } - - return 0; -} - -int str_str_find(char *key, char **out){ - dict_str_str *temp; - temp = str_str_table; - - while (temp != NULL){ - if (!strcmp(temp->key, key)){ - *out = temp->value; - return 0; - } - temp = temp->next; - } - - return -1; -} - -int str_str_index(int index, char **out){ - dict_str_str *temp; - temp = str_str_table; - - for (int i = 0; i < index; i++){ - if (temp == NULL) - return -1; - temp = temp->next; - } - - if (temp == NULL) - return -1; - - *out = temp->value; - return 0; -} - -void str_str_clear(){ - dict_str_str *cur, *next; - cur = str_str_table; - - while (cur != NULL){ - next = cur->next; - free(cur->key); - free(cur->value); - free(cur); - cur = next; - } - str_str_table = NULL; -} \ No newline at end of file diff --git a/source/tegraexplorer/script/variables.h b/source/tegraexplorer/script/variables.h deleted file mode 100644 index 20ef4ee..0000000 --- a/source/tegraexplorer/script/variables.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "../../utils/types.h" - -typedef struct _dict_str_int { - char *key; - int value; - struct _dict_str_int *next; -} dict_str_int; - -typedef struct _dict_str_str { - char *key; - char *value; - struct _dict_str_str *next; -} dict_str_str; - -typedef struct _dict_str_loc { - char *key; - u64 value; - struct _dict_str_loc *next; -} dict_str_loc; - -int str_int_add(char *key, int value); -int str_int_find(char *key, int *out); -void str_int_clear(); -void str_int_printall(); -/* -int str_jmp_add(char *key, u64 value); -int str_jmp_find(char *key, u64 *out); -void str_jmp_clear(); -*/ -int str_str_add(char *key, char *value); -int str_str_find(char *key, char **out); -int str_str_index(int index, char **out); -void str_str_clear(); \ No newline at end of file diff --git a/source/tegraexplorer/utils/utils.c b/source/tegraexplorer/utils/utils.c index 563a69a..22a70b0 100644 --- a/source/tegraexplorer/utils/utils.c +++ b/source/tegraexplorer/utils/utils.c @@ -202,4 +202,10 @@ char *utils_copyStringSize(const char *in, int size){ //strncpy(out, in, size); memcpy(out, in, size); return out; +} + +char* util_cpyStr(const char* in){ + char *out = malloc(strlen(in) + 1); + strcpy(out, in); + return out; } \ No newline at end of file diff --git a/source/tegraexplorer/utils/utils.h b/source/tegraexplorer/utils/utils.h index b987661..7cf1ee7 100644 --- a/source/tegraexplorer/utils/utils.h +++ b/source/tegraexplorer/utils/utils.h @@ -24,4 +24,5 @@ int utils_mmcMenu(); void utils_copystring(const char *in, char **out); char *utils_InputText(char *start, int maxLen); void utils_takeScreenshot(); -char *utils_copyStringSize(const char *in, int size); \ No newline at end of file +char *utils_copyStringSize(const char *in, int size); +char* util_cpyStr(const char* in); \ No newline at end of file