diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index b168d8c..b71ccbb 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -14,7 +14,7 @@ jobs: run: | sudo apt update -y sudo apt install build-essential -y - alias py=python3 + make -j$(nproc) - uses: actions/upload-artifact@master diff --git a/.gitignore b/.gitignore index 2a385fc..8b35bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,12 @@ loader/payload_00.h loader/payload_01.h +source/script/Debug +source/script/*.te +source/script/.vs + *.exe + +source/script/builtin.c + +source/script/builtin.h diff --git a/Makefile b/Makefile index f88173a..2ea2712 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ IPL_LOAD_ADDR := 0x40008000 LPVERSION_MAJOR := 3 LPVERSION_MINOR := 0 LPVERSION_BUGFX := 6 +LPVERSION := \"$(LPVERSION_MAJOR).$(LPVERSION_MINOR).$(LPVERSION_BUGFX)\" ################################################################################ @@ -27,7 +28,7 @@ BIN2CDIR := ./tools/bin2c VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/)) VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/)) -OBJS = $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \ +OBJS = $(BUILDDIR)/$(TARGET)/script/builtin.c $(patsubst $(SOURCEDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \ $(patsubst $(SOURCEDIR)/%.c, $(BUILDDIR)/$(TARGET)/%.o, \ $(call rwildcard, $(SOURCEDIR), *.S *.c))) OBJS += $(patsubst $(BDKDIR)/%.S, $(BUILDDIR)/$(TARGET)/%.o, \ @@ -40,7 +41,7 @@ FFCFG_INC := '"../$(SOURCEDIR)/libs/fatfs/ffconf.h"' ################################################################################ CUSTOMDEFINES := -DIPL_LOAD_ADDR=$(IPL_LOAD_ADDR) -CUSTOMDEFINES += -DLP_VER_MJ=$(LPVERSION_MAJOR) -DLP_VER_MN=$(LPVERSION_MINOR) -DLP_VER_BF=$(LPVERSION_BUGFX) +CUSTOMDEFINES += -DLP_VER_MJ=$(LPVERSION_MAJOR) -DLP_VER_MN=$(LPVERSION_MINOR) -DLP_VER_BF=$(LPVERSION_BUGFX) -DLP_VER=$(LPVERSION) CUSTOMDEFINES += -DGFX_INC=$(GFX_INC) -DFFCFG_INC=$(FFCFG_INC) # 0: UART_A, 1: UART_B. @@ -103,3 +104,18 @@ $(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.c $(BUILDDIR)/$(TARGET)/%.o: $(BDKDIR)/%.S @mkdir -p "$(@D)" $(CC) $(CFLAGS) -c $< -o $@ + +$(BUILDDIR)/$(TARGET)/script/builtin.o: $(BUILDDIR)/$(TARGET)/script/builtin.c + @mkdir -p "$(@D)" + $(CC) $(CFLAGS) $(BDKINC) -c $< -o $@ + +$(BUILDDIR)/$(TARGET)/script/builtin.c: scripts/*.te + @mkdir -p "$(@D)" + @mkdir -p "$(BUILDDIR)/$(TARGET)/scripts" +ifeq ($(OS),Windows_NT) + @py ts-minifier.py -d "$(BUILDDIR)/$(TARGET)/scripts" $(wildcard scripts/*.te) + @py te2c.py "$(BUILDDIR)/$(TARGET)/script/builtin" "$(BUILDDIR)/$(TARGET)/scripts" +else + @python3 ts-minifier.py -d "$(BUILDDIR)/$(TARGET)/scripts" $(wildcard scripts/*.te) + @python3 te2c.py "$(BUILDDIR)/$(TARGET)/script/builtin" "$(BUILDDIR)/$(TARGET)/scripts" +endif diff --git a/bdk/libs/nx_savedata/save_data_file.c b/bdk/libs/nx_savedata/save_data_file.c index edf85ba..7f3645d 100644 --- a/bdk/libs/nx_savedata/save_data_file.c +++ b/bdk/libs/nx_savedata/save_data_file.c @@ -99,10 +99,9 @@ bool save_data_file_write(save_data_file_ctx_t *ctx, uint64_t *out_bytes_written if (!save_data_file_validate_write_params(ctx, offset, count, ctx->mode, &is_resize_needed)) return false; - if (is_resize_needed) { - if (!save_data_file_set_size(ctx, offset + count)) - return false; - } + if (!save_data_file_set_size(ctx, offset + count)) + return false; + *out_bytes_written = save_allocation_table_storage_write(&ctx->base_storage, buffer, offset, count); return true; diff --git a/scripts/FirmwareDump.te b/scripts/FirmwareDump.te new file mode 100644 index 0000000..d01d5c8 --- /dev/null +++ b/scripts/FirmwareDump.te @@ -0,0 +1,54 @@ +#REQUIRE KEYS +#REQUIRE SD +p=println +pe={pause() exit()} +fwstr={fw=maj.str()+"."+min.str()+"."+pat.str()} +fv={a=readsave("bis:/save/8000000000000120") + b=a.read("/meta/imkvdb.arc") + c=["BYTE[]",9,8,0,0,0,0,0,1] + d=b.find(c) + if(d>0){ + e=b.slice(d+8,4).project() + ver=(e[3]<<24)|(e[2]<<16)|(e[1]<<8)|(e[0]) + pat=((ver>>16)&((1<<4)- 1)) + min=((ver>>20)&((1<<6)- 1)) + maj=((ver>>26)&((1<<6)- 1)) + }.else(){print("Fw not found")pe()} + a=0 fwstr()} +if(mountsys("SYSTEM")){print("SYSTEM MOUNT FAIL")pe()} +fv() +sysfw=fw +p("Sys' fw is",fw) +emufw="" +if (emu()){if(!mountemu("SYSTEM")){fv()emufw=fw p("Emu's fw is",fw)}} +op=["Exit","Dump sysmmc"].copy() +if (emufw!=""){op+"Dump emummc"} +p()r=menu(op,0)clear() +if(r==0){exit()} +if(r==1){fw=sysfw if(mountsys("SYSTEM")){print("SYSTEM MOUNT FAIL")pe()}} +if(r==2){fw=emufw if(mountemu("SYSTEM")){print("SYSTEM MOUNT FAIL")pe()}} +mkdir("sd:/tegraexplorer") +mkdir("sd:/tegraexplorer/Firmware") +bsp="sd:/tegraexplorer/Firmware/"+fw +if(fsexists(bsp)){p("Dir already exists! Press power to replace, any other key to exit") a=pause() if(!a.power){exit()} deldir(bsp)} +mkdir(bsp) +p("Dumping to",bsp) +con=readdir("bis:/Contents/registered") +if(con.result!=0){println("Fail reading dir")pe()} +c=(con.files.len()+con.folders.len()).str() +i=1 f=0 t=timer() +dump={ + fi.foreach("x"){ + if(f){fp="bis:/Contents/registered/"+x+"/00"} + .else(){fp="bis:/Contents/registered/"+x} + name=x + if(ncatype(fp)==1){name=name- 4+".cnmt.nca"} + color(0x00FF00) + print("\r["+i.str()+"/"+c+"]", x) + if(copyfile(fp,bsp+"/"+name)){println("\nErr during copy")pe()} + i=i+1 + } +} +fi=con.files dump() +f=1 fi=con.folders dump() +p("\nDone! Took",timer()-t/1000,"s")pause() \ No newline at end of file diff --git a/scripts/SystemWipe.te b/scripts/SystemWipe.te new file mode 100644 index 0000000..e33506a --- /dev/null +++ b/scripts/SystemWipe.te @@ -0,0 +1,30 @@ +is=["8000000000000120","80000000000000d1","8000000000000047"] +p=println +pr=print +pe={pause() exit()} +wait={t=timer()while(timer()<(t+tw)){print("Wait for",(t+tw-timer()/1000),"seconds \r")}} +p("System wiper\n") +op=["Exit","Wipe sysmmc"].copy() +if (emu()){op+"Wipe emummc"} +r=menu(op,0)clear() +if(r==0){exit()} +if(r==1){p("Selected sysmmc")mount=mountsys} +if(r==2){p("Selected emummc")mount=mountemu} +if(mount("SYSTEM")){p("Mount failed!")pe()} +color(0xFF0000) +p("Are you sure you want to wipe everything?\nThis includes:\n- Saves\n- Game Data\n- All other data on the system\n\nUse this only as a last resort!") +color(0xFFFF00) +tw=10000 +wait() +color(0x0000FF) +p("Press power to wipe, any other key to exit")a=pause()if(!a.power){exit()}clear() +color(0xFF0000) +pr("Deleting system saves... ") +f=readdir("bis:/save") +if(f.folders.len()!=0){p("Folders in save dir???")pe()} +f.files.foreach("x"){if(!is.contains(x)){if(delfile("bis:/save/"+x)){p("File deletion failed: ", x)pe()}}} +pr("Done!\nDeleting user dirs...")ud=["Album","Contents","save","saveMeta","temp"] +if(mount("USER")){p("Mount failed!")pe()} +ud.foreach("x"){pr("\n"+x,"")if(deldir("bis:/"+x)){p("Dir deletion failed")pe()}mkdir("bis:/"+x)} +mkdir("bis:/Contents/placehld")mkdir("bis:/Contents/registered") +color(0x00FF00)p("\n\nDone!")pause() \ No newline at end of file diff --git a/source/fs/fsutils.c b/source/fs/fsutils.c index 8217e73..6efa3aa 100644 --- a/source/fs/fsutils.c +++ b/source/fs/fsutils.c @@ -47,6 +47,7 @@ char *GetFileAttribs(FSEntry_t entry){ return ret; } +// Returns 1 if a file exists, 0 if it does not bool FileExists(const char* path){ FRESULT fr; FILINFO fno; diff --git a/source/fs/menus/explorer.c b/source/fs/menus/explorer.c index efe4631..f06679f 100644 --- a/source/fs/menus/explorer.c +++ b/source/fs/menus/explorer.c @@ -74,6 +74,7 @@ void FileExplorer(char *path){ res = 0; res = newMenu(&entries, res, 60, 42, ENABLEB | ENABLEPAGECOUNT, (int)fileVec.count); + vecFree(entries); char *oldPath = storedPath; @@ -125,6 +126,7 @@ void FileExplorer(char *path){ else if (res < ARR_LEN(topEntries)) { if (!strcmp(storedPath, path)){ clearFileVector(&fileVec); + free(storedPath); return; } diff --git a/source/fs/menus/explorer.h b/source/fs/menus/explorer.h index 840f4d8..cda9e09 100644 --- a/source/fs/menus/explorer.h +++ b/source/fs/menus/explorer.h @@ -1,4 +1,7 @@ #pragma once #include "../../utils/vector.h" +#include "../../gfx/menu.h" +#include "../fstypes.h" -void FileExplorer(char *path); \ No newline at end of file +void FileExplorer(char *path); +MenuEntry_t MakeMenuOutFSEntry(FSEntry_t entry); \ No newline at end of file diff --git a/source/fs/menus/filemenu.c b/source/fs/menus/filemenu.c index 8cbcbee..046f059 100644 --- a/source/fs/menus/filemenu.c +++ b/source/fs/menus/filemenu.c @@ -11,11 +11,12 @@ #include #include "../../utils/utils.h" #include "../../keys/nca.h" -#include "../../script/lexer.h" -#include "../../script/parser.h" -#include "../../script/variables.h" #include #include "../../storage/emummc.h" +#include "../../script/eval.h" +#include "../../script/parser.h" +#include "../../script/garbageCollector.h" + MenuEntry_t FileMenuEntries[] = { {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- File menu --"}, @@ -71,29 +72,45 @@ void DeleteFile(char *path, FSEntry_t entry){ free(thing); } +void RunScriptString(char *str, u32 size){ + TConf.scriptCWD = "sd:/"; + gfx_clearscreen(); + ParserRet_t ret = parseScript(str, size); + setStaticVars(&ret.staticVarHolder); + initRuntimeVars(); + eval(ret.main.operations.data, ret.main.operations.count, 0); + exitRuntimeVars(); + exitStaticVars(&ret.staticVarHolder); + exitFunction(ret.main.operations.data, ret.main.operations.count); + vecFree(ret.staticVarHolder); + vecFree(ret.main.operations); +} + void RunScript(char *path, FSEntry_t entry){ char *thing = CombinePaths(path, entry.name); u32 size; char *script = sd_file_read(thing, &size); - free(thing); + TConf.scriptCWD = thing; + if (!script) return; - if (((entry.size >= 64 && entry.sizeDef == 1) || entry.sizeDef >= 2) && !TConf.minervaEnabled) + if (((entry.size >= 16 && entry.sizeDef == 1) || entry.sizeDef >= 2) && !TConf.minervaEnabled) return; gfx_clearscreen(); - scriptCtx_t ctx = createScriptCtx(); - ctx.script = runLexer(script, size); + + ParserRet_t ret = parseScript(script, size); free(script); - - dictVectorAdd(&ctx.varDict, newDict(CpyStr("_CWD"), (newVar(StringType, 0, .stringType = path)))); - dictVectorAdd(&ctx.varDict, newDict(CpyStr("_EMU"), (newVar(IntType, 0, emu_cfg.enabled)))); - - printError(mainLoop(&ctx)); - - freeDictVector(&ctx.varDict); - lexarVectorClear(&ctx.script); + setStaticVars(&ret.staticVarHolder); + initRuntimeVars(); + Variable_t* res = eval(ret.main.operations.data, ret.main.operations.count, 1); + exitRuntimeVars(); + exitStaticVars(&ret.staticVarHolder); + exitFunction(ret.main.operations.data, ret.main.operations.count); + vecFree(ret.staticVarHolder); + vecFree(ret.main.operations); + free(thing); } void RenameFile(char *path, FSEntry_t entry){ diff --git a/source/fs/menus/filemenu.h b/source/fs/menus/filemenu.h index debcedd..0699e25 100644 --- a/source/fs/menus/filemenu.h +++ b/source/fs/menus/filemenu.h @@ -4,4 +4,5 @@ typedef void (*fileMenuPath)(char *path, FSEntry_t entry); void FileMenu(char *path, FSEntry_t entry); -void RunScript(char *path, FSEntry_t entry); \ No newline at end of file +void RunScript(char *path, FSEntry_t entry); +void RunScriptString(char *str, u32 size); \ No newline at end of file diff --git a/source/fs/menus/foldermenu.c b/source/fs/menus/foldermenu.c index 1d8b4d3..74325a5 100644 --- a/source/fs/menus/foldermenu.c +++ b/source/fs/menus/foldermenu.c @@ -11,9 +11,6 @@ #include #include "../../utils/utils.h" #include "../../keys/nca.h" -#include "../../script/lexer.h" -#include "../../script/parser.h" -#include "../../script/variables.h" #include #include "../fscopy.h" diff --git a/source/script/ABadIdeaVersion3.c b/source/script/ABadIdeaVersion3.c new file mode 100644 index 0000000..9fdcba5 --- /dev/null +++ b/source/script/ABadIdeaVersion3.c @@ -0,0 +1,91 @@ +#ifdef WIN32 + +#include +#include "compat.h" +#include "parser.h" +#include "intClass.h" +#include "StringClass.h" +#include "eval.h" +#include "garbageCollector.h" + +// TODO: error handling DONE +// TODO: unsolved arrays +// TODO: free-ing vars & script at end +// TODO: implement functions from tsv2 +// TODO: add len to string +// TODO: clear old int values from array DONE +// TODO: int and str should be statically included in OP_t DONE + +char* readFile(char* path) { + FILE* fp = fopen(path, "r"); + if (fp == NULL) + return NULL; + + fseek(fp, 0L, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + char* ret = calloc(size + 1, 1); + fread(ret, size, 1, fp); + + fclose(fp); + return ret; +} + +int main() +{ + //gfx_printf("Hello world!\n"); + + //StartParse("a b c de 0x1 0x10 \"yeet\" + + "); + /* + Variable_t b = newStringVariable("Hello world\n", 1, 0); + callClass("__print__", &b, NULL, NULL); + + Variable_t a = newIntVariable(69, 0); + Variable_t c = newIntVariable(1, 0); + Variable_t e = newStringVariable("snake", 1, 0); + Vector_t fuk = newVec(sizeof(Variable_t), 1); + vecAdd(&fuk, c); + Variable_t *d = callClass("+", &a, NULL, &fuk); + callClass("__print__", d, NULL, NULL); + */ + /* + Vector_t v = newVec(sizeof(int), 4); + int a = 69; + vecAdd(&v, a); + vecAdd(&v, a); + vecAdd(&v, a); + vecAdd(&v, a); + + vecForEach(int*, b, (&v)) + printf("%d\n", *b); + + return; + */ + char* script = readFile("input.te"); + if (script == NULL) + return; + + //parseScript("#REQUIRE VER 3.0.5\nmain = { two = 1 + 1 }"); + //ParserRet_t ret = parseScript("a.b.c(1){ a.b.c() }"); + //while (1) { + ParserRet_t ret = parseScript(script, strlen(script)); + + + setStaticVars(&ret.staticVarHolder); + initRuntimeVars(); + + Variable_t* res = eval(ret.main.operations.data, ret.main.operations.count, 1); + + exitRuntimeVars(); + exitStaticVars(&ret.staticVarHolder); + exitFunction(ret.main.operations.data, ret.main.operations.count); + vecFree(ret.staticVarHolder); + vecFree(ret.main.operations); + //} + + + free(script); + gfx_printf("done"); +} +#endif \ No newline at end of file diff --git a/source/script/ABadIdeaVersion3.vcxproj b/source/script/ABadIdeaVersion3.vcxproj new file mode 100644 index 0000000..bcfb9f4 --- /dev/null +++ b/source/script/ABadIdeaVersion3.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {b4ea4793-03fc-4c25-b69b-a5fbad2d89d5} + ABadIdeaVersion3 + 10.0 + + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + ProgramDatabase + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MinSpace + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/script/ABadIdeaVersion3.vcxproj.filters b/source/script/ABadIdeaVersion3.vcxproj.filters new file mode 100644 index 0000000..fa0362c --- /dev/null +++ b/source/script/ABadIdeaVersion3.vcxproj.filters @@ -0,0 +1,152 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {c245ac36-a649-432b-a663-98ff9d02d3f4} + + + {e4d90dab-a1e0-4a27-82a1-dc2c6b81f0bd} + + + {1aacfe02-949c-4394-b408-28d73d702918} + + + {5878f4a8-cac0-40aa-8866-6109bf6688ab} + + + {ec7391f8-d60e-46eb-965e-c2d84d48d784} + + + {015ca379-a813-4d13-a929-3b0efe72bb02} + + + {64738969-63b9-4d0e-85b9-d9a63a7e36b3} + + + {ba83a808-23f1-44c6-85e4-d0c6c77200fa} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Classes\Generic + + + Source Files + + + Source Files\Classes\Function + + + Source Files\Classes\String + + + Source Files\Classes\Array + + + Source Files\Classes\Array + + + Source Files\Classes\Int + + + Source Files\Classes\Array + + + Source Files + + + Source Files + + + Source Files\Classes\Elseable + + + Source Files\Classes\Dictionary + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Source Files\Classes\Dictionary + + + Source Files\Classes\Array + + + Source Files\Classes\Int + + + Source Files\Classes\String + + + Source Files\Classes\Array + + + Source Files\Classes\Function + + + Source Files\Classes\Generic + + + Source Files\Classes\Array + + + Header Files + + + Header Files + + + Source Files\Classes\Elseable + + + + + Source Files + + + \ No newline at end of file diff --git a/source/script/ABadIdeaVersion3.vcxproj.user b/source/script/ABadIdeaVersion3.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/source/script/ABadIdeaVersion3.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/source/script/StringClass.c b/source/script/StringClass.c new file mode 100644 index 0000000..9530453 --- /dev/null +++ b/source/script/StringClass.c @@ -0,0 +1,132 @@ +#include "StringClass.h" +#include "compat.h" +#include "intClass.h" +#include "scriptError.h" +#include "parser.h" +#include + +char* getStringValue(Variable_t* var) { + if (var->variableType != StringClass) + return NULL; + + return var->string.value; +} + +// Will NOT copy the string, the pointer is taken as-is +StringClass_t createStringClass(char* in, u8 free) { + StringClass_t a = { 0 }; + a.free = free; + a.value = in; + return a; +} + +Variable_t newStringVariable(char *x, u8 readOnly, u8 freeOnExit) { + Variable_t var = { .variableType = StringClass, .readOnly = readOnly, .string = createStringClass(x, freeOnExit) }; + return var; +} + +ClassFunction(printStringVariable) { + if (caller->variableType == StringClass) { + StringClass_t* a = &caller->string; + gfx_printf("%s", a->value); + } + return &emptyClass; +} + +ClassFunction(addStringVariables) { + char* s1 = getStringValue(caller); + char* s2 = getStringValue(*args); + + char* n = malloc(strlen(s1) + strlen(s2) + 1); + strcpy(n, s1); + strcat(n, s2); + + return newStringVariablePtr(n, 0, 1); +} + +ClassFunction(getStringLength) { + char* s1 = getStringValue(caller); + return newIntVariablePtr(strlen(s1)); +} + +ClassFunction(stringBytes) { + Variable_t v = { 0 }; + v.variableType = ByteArrayClass; + u32 len = strlen(caller->string.value); + v.solvedArray.vector = newVec(1, len); + v.solvedArray.vector.count = len; + memcpy(v.solvedArray.vector.data, caller->string.value, len); + return copyVariableToPtr(v); +} + +ClassFunction(stringIndexGet) { + u32 len = strlen(caller->string.value); + u32 idx = args[0]->integer.value; + if (len < idx || idx < 0) { + SCRIPT_FATAL_ERR("Index of string out of range"); + } + + char* a = calloc(1,2); + a[0] = caller->string.value[idx]; + return newStringVariablePtr(a, 0, 0); +} + +ClassFunction(stringMinusInt){ + u32 baseStrLen = strlen(caller->string.value); + if (baseStrLen < args[0]->integer.value){ + SCRIPT_FATAL_ERR("Index of string out of range"); + } + char* newStr = calloc(baseStrLen - args[0]->integer.value + 1, 1); + memcpy(newStr, caller->string.value, baseStrLen - args[0]->integer.value); + return newStringVariablePtr(newStr, 0, 1); +} + +ClassFunction(stringEq){ + return newIntVariablePtr(!strcmp(caller->string.value, args[0]->string.value)); +} + +ClassFunction(stringInEq){ + return newIntVariablePtr(strcmp(caller->string.value, args[0]->string.value)); +} + +ClassFunction(stringSplit) { + int valLen = strlen(args[0]->string.value); + + char* start = caller->string.value; + char* find = NULL; + Vector_t arr = newVec(sizeof(char**), 1); + char* temp; + + while ((find = (strstr(start, args[0]->string.value))) != NULL) { + temp = utils_copyStringSize(start, find - start); + vecAdd(&arr, temp); + + start = find + valLen; + } + + temp = utils_copyStringSize(start, caller->string.value + strlen(caller->string.value) - start); + vecAdd(&arr, temp); + + Variable_t a = { .variableType = StringArrayClass, .solvedArray.vector = arr }; + return copyVariableToPtr(a); +} + +u8 strOneIntArg[] = { IntClass }; +u8 oneStringArg[] = { StringClass }; + +ClassFunctionTableEntry_t stringFunctions[] = { + {"print", printStringVariable, 0, 0}, + {"+", addStringVariables, 1, oneStringArg }, + {"len", getStringLength, 0, 0}, + {"bytes", stringBytes, 0, 0}, + {"get", stringIndexGet, 1, strOneIntArg}, + {"-", stringMinusInt, 1, strOneIntArg}, + {"==", stringEq, 1, oneStringArg}, + {"!=", stringInEq, 1, oneStringArg}, + {"split", stringSplit, 1, oneStringArg}, + {"/", stringSplit, 1, oneStringArg}, +}; + +Variable_t getStringMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, stringFunctions, ARRAY_SIZE(stringFunctions)); +} \ No newline at end of file diff --git a/source/script/StringClass.h b/source/script/StringClass.h new file mode 100644 index 0000000..f712de0 --- /dev/null +++ b/source/script/StringClass.h @@ -0,0 +1,10 @@ +#pragma once +#include "model.h" +#include "genericClass.h" + +StringClass_t createStringClass(char* in, u8 free); +char* getStringValue(Variable_t* var); + +Variable_t newStringVariable(char *x, u8 readOnly, u8 freeOnExit); +#define newStringVariablePtr(x, readOnly, freeOnExit) copyVariableToPtr(newStringVariable(x, readOnly, freeOnExit)) +Variable_t getStringMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/args.c b/source/script/args.c deleted file mode 100644 index ff53131..0000000 --- a/source/script/args.c +++ /dev/null @@ -1,502 +0,0 @@ -#include "args.h" -#include "types.h" -#include "functions.h" -#include "variables.h" -#include -#include -#include "../utils/utils.h" - -char* utils_copyStringSize(const char* in, int size) { - if (size > strlen(in) || size < 0) - size = strlen(in); - - char* out = calloc(size + 1, 1); - //strncpy(out, in, size); - if (size) - memcpy(out, in, size); - return out; -} - -// 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; -} - -Vector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len) { - Vector_t args = newVec(sizeof(Variable_t), 5); - 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 == LBracket){ - int distance = distanceBetweenTokens(&tokens[i + 1], len - i - 1, LBracket, RBracket); - i += distance + 1; - } - if (tokens[i].token == Seperator) { - Variable_t res = solveEquation(ctx, &tokens[lastLoc], i - lastLoc, 0); - lastLoc = i + 1; - - vecAddElement(&args, res); - } - - if (i + 1 >= len) { - Variable_t res = solveEquation(ctx, &tokens[lastLoc], i + 1 - lastLoc, 0); - vecAddElement(&args, res); - } - } - - return args; -} - -#define ErrValue(err) (Variable_t) {.varType = ErrType, .integerType = err} -#define IntValue(i) (Variable_t) {.varType = IntType, .integerType = i} -#define StrValue(s) (Variable_t) {.varType = StringType, .stringType = s} - -#define ELIFTX(x) else if (tokens[i].token == x) - -Variable_t getVarFromToken(scriptCtx_t* ctx, lexarToken_t* tokens, int* index, u32 maxLen) { - Variable_t val = { 0 }; - int i = *index; - - if (tokens[i].token == Variable) { - Variable_t* var = dictVectorFind(&ctx->varDict, tokens[i].text); - if (var != NULL) { - val = *var; - val.free = 0; - } - else { - val = ErrValue(ERRNOVAR); - } - } - ELIFTX(IntLit) { - val = IntValue(tokens[i].val); - } - ELIFTX(StrLit) { - val = StrValue(tokens[i].text); - val.free = 0; - } - ELIFTX(ArrayVariable) { - Variable_t* var = dictVectorFind(&ctx->varDict, tokens[i].text); - i += 2; - - if (var == NULL) - return ErrValue(ERRNOVAR); - - int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LSBracket, RSBracket); - if (argCount < 0) - return ErrValue(ERRSYNTAX); - - Variable_t index = solveEquation(ctx, &tokens[i], argCount, 0); - i += argCount; - - if (index.varType != IntType) - return ErrValue(ERRINVALIDTYPE); - - if (var->vectorType.count <= index.integerType || index.integerType < 0) - return ErrValue(ERRSYNTAX); - - switch (var->varType) { - case StringArrayType: - val = StrValue((vecGetArray(char**, var->vectorType))[index.integerType]); - break; - case IntArrayType: - val = IntValue((vecGetArray(int*, var->vectorType))[index.integerType]); - break; - case ByteArrayType: - val = IntValue((vecGetArray(u8*, var->vectorType))[index.integerType]); - break; - default: - return ErrValue(ERRINVALIDTYPE); - } - } - ELIFTX(Function) { - i += 2; - - int argCount = distanceBetweenTokens(&tokens[i], maxLen - 2, LBracket, RBracket); - if (argCount < 0) - return ErrValue(ERRSYNTAX); - - val = executeFunction(ctx, tokens[i - 2].text, &tokens[i], argCount); - - i += argCount; - } - ELIFTX(LSBracket) { - i++; - - int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LSBracket, RSBracket); - if (argCount < 0) - return ErrValue(ERRSYNTAX); - - val.varType = EmptyArrayType; - - if (argCount > 0){ - Vector_t arrayVars = extractVars(ctx, &tokens[i], argCount); - Variable_t* variables = vecGetArray(Variable_t*, arrayVars); - int type = variables[0].varType; - if (!(type == StringType || type == IntType)) - return ErrValue(ERRINVALIDTYPE); - - val.varType = (type + 2); - val.free = 1; - val.vectorType = newVec((type == IntType) ? sizeof(int) : sizeof(char*), arrayVars.count); - - for (int j = 0; j < arrayVars.count; j++) { - if (variables[j].varType != type) - return ErrValue(ERRINVALIDTYPE); // Free-ing issue!! - - if (type == StringType) { - char* temp = CpyStr(variables[j].stringType); - vecAddElement(&val.vectorType, temp); - } - else { - vecAddElement(&val.vectorType, variables[j].integerType); - } - } - - i += argCount; - freeVariableVector(&arrayVars); - } - } - ELIFTX(LBracket) { - i++; - int argCount = distanceBetweenTokens(&tokens[i], maxLen - 1, LBracket, RBracket); - if (argCount < 0) - return ErrValue(ERRSYNTAX); - - val = solveEquation(ctx, &tokens[i], argCount, 0); - i += argCount; - } - else { - // ERR - return ErrValue(ERRSYNTAX); - } - - *index = i; - return val; -} - -int matchTypes(Variable_t d1, Variable_t d2, int type) { - return (d1.varType == d2.varType && d1.varType == type); -} - -#define ELIFT(token) else if (localOpToken == token) - -Variable_t solveEquation(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len, u8 shouldFree) { - Variable_t res = { 0 }; - u8 lastToken = 0; - u8 invertValue = 0; - lexarToken_t* varToken = NULL; - - for (int i = 0; i < len; i++) { - if (tokens[i].token == Not) - invertValue = !invertValue; - - else if (tokens[i].token == ArrayVariableAssignment || tokens[i].token == VariableAssignment) { - varToken = &tokens[i]; - if (tokens[i].token == ArrayVariableAssignment) { - int distance = distanceBetweenTokens(&tokens[i] + 2, len, LSBracket, RSBracket); - i += distance + 2; - } - } - - else if (tokens[i].token >= Variable && tokens[i].token <= LSBracket) { - Variable_t val = getVarFromToken(ctx, tokens, &i, len - i); - - if (val.varType == ErrType) - return val; - - if (val.varType == IntType && invertValue) { - val.integerType = !val.integerType; - } - invertValue = 0; - - if (lastToken) { - u16 localOpToken = lastToken; // do we need local op token? - lastToken = 0; - - if (matchTypes(res, val, IntType)) { - if (localOpToken == Plus) - res.integerType += val.integerType; - ELIFT(Minus) - res.integerType -= val.integerType; - ELIFT(Multiply) - res.integerType *= val.integerType; - ELIFT(Division) { - if (!val.integerType) - res = ErrValue(ERRDIVBYZERO); - else - res.integerType = res.integerType / val.integerType; - } - ELIFT(Mod) - res.integerType %= val.integerType; - ELIFT(Smaller) - res.integerType = res.integerType < val.integerType; - ELIFT(SmallerEqual) - res.integerType = res.integerType <= val.integerType; - ELIFT(Bigger) - res.integerType = res.integerType > val.integerType; - ELIFT(BiggerEqual) - res.integerType = res.integerType >= val.integerType; - ELIFT(EqualEqual) - res.integerType = res.integerType == val.integerType; - ELIFT(NotEqual) - res.integerType = res.integerType != val.integerType; - ELIFT(LogicAND) { - res.integerType = res.integerType && val.integerType; - if (!res.integerType) - break; - } - ELIFT(LogicOR) { - res.integerType = res.integerType || val.integerType; - if (res.integerType) - break; - } - ELIFT(AND) - res.integerType = res.integerType & val.integerType; - ELIFT(OR) - res.integerType = res.integerType | val.integerType; - ELIFT(BitShiftLeft) - res.integerType = res.integerType << val.integerType; - ELIFT(BitShiftRight) - res.integerType = res.integerType >> val.integerType; - else - return ErrValue(ERRBADOPERATOR); - } - else if (matchTypes(res, val, StringType)) { - if (localOpToken == Plus) { - char* buff = calloc(strlen(res.stringType) + strlen(val.stringType) + 1, 1); - strcpy(buff, res.stringType); - strcat(buff, val.stringType); - - if (res.free) free(res.stringType); // we should replace these with variablefree - if (val.free) free(val.stringType); - res.stringType = buff; - res.free = 1; - } - ELIFT(EqualEqual) { - res.typeUnion = IntType; - int compRes = !strcmp(res.stringType, val.stringType); - if (res.free) free(res.stringType); - if (val.free) free(val.stringType); - res.integerType = compRes; - res.free = 0; - } - ELIFT(Minus) { - u32 lenRes = strlen(res.stringType); - u32 valRes = strlen(val.stringType); - if (!strcmp(res.stringType + lenRes - valRes, val.stringType)) { - char *temp = malloc(lenRes - valRes + 1); - memcpy(temp, res.stringType, lenRes - valRes); - temp[lenRes - valRes] = 0; - freeVariable(res); - res.free = 1; - res.stringType = temp; - } - - freeVariable(val); - } - ELIFT(Division) { - int valLen = strlen(val.stringType); - if (!valLen) { - res = ErrValue(ERRSYNTAX); - continue; - } - - char* start = res.stringType; - char* find = NULL; - Vector_t arr = newVec(sizeof(char**), 10); - char* temp; - - while ((find = (strstr(start, val.stringType))) != NULL) { - temp = utils_copyStringSize(start, find - start); - vecAddElement(&arr, temp); - - start = find + valLen; - } - - temp = utils_copyStringSize(start, res.stringType + strlen(res.stringType) - start); - vecAddElement(&arr, temp); - if (res.free) free(res.stringType); // do we free here? - if (val.free) free(val.stringType); - - res.varType = StringArrayType; - res.free = 1; - res.vectorType = arr; - } - else - return ErrValue(ERRBADOPERATOR); - } - else if ((res.varType == IntArrayType || res.varType == ByteArrayType) && val.varType == IntType) { - if (localOpToken == Plus) { - Vector_t newV = vecCopy(&res.vectorType); - freeVariable(res); - res.vectorType = newV; - res.free = 1; - if (res.varType == IntArrayType) - vecAddElement(&res.vectorType, val.integerType); - else { - u8 in = ((u8)val.integerType & 0xFF); - vecAddElement(&res.vectorType, in); - } - } - ELIFT(Minus){ - if (val.integerType >= res.vectorType.count) - return ErrValue(ERRSYNTAX); - - res.vectorType.count -= val.integerType; - Vector_t newV = vecCopy(&res.vectorType); - freeVariable(res); - res.vectorType = newV; - res.free = 1; - } - ELIFT(Selector){ - if (val.integerType >= res.vectorType.count) - return ErrValue(ERRSYNTAX); - - Vector_t newV = vecCopyOffset(&res.vectorType, val.integerType); - freeVariable(res); - res.vectorType = newV; - res.free = 1; - } - } - else if (res.varType == StringType && val.varType == IntType){ - if (localOpToken == Minus){ - u32 resLen = strlen(res.stringType); - if (resLen < val.integerType) - return ErrValue(ERRSYNTAX); - - char *temp = utils_copyStringSize(res.stringType, resLen - val.integerType); - - freeVariable(res); - res.stringType = temp; - res.free = 1; - } - ELIFT(Selector){ - u32 resLen = strlen(res.stringType); - if (resLen < val.integerType) - return ErrValue(ERRSYNTAX); - - char *temp = CpyStr(res.stringType + val.integerType); - - freeVariable(res); - res.stringType = temp; - res.free = 1; - } - else - return ErrValue(ERRBADOPERATOR); - } - else if (res.varType == EmptyArrayType && localOpToken == Plus){ - res.free = 1; - res.varType = val.varType + 2; - - if (val.varType == IntType){ - res.vectorType = newVec(sizeof(int), 4); - vecAddElem(&res.vectorType, val.integerType); - } - else if (val.varType == StringType) { - res.vectorType = newVec(sizeof(char*), 4); - char *temp = CpyStr(val.stringType); - vecAddElem(&res.vectorType, temp); - } - else - return ErrValue(ERRBADOPERATOR); - - freeVariable(val); - } - else if (res.varType == StringArrayType && val.varType == StringType){ - if (localOpToken == Plus){ - Vector_t new = vecCopy(&res.vectorType); - vecDefArray(char **, strings, new); - for (int j = 0; j < new.count; j++){ - strings[j] = CpyStr(strings[j]); - } - - freeVariable(res); - - char *temp = CpyStr(val.stringType); - vecAddElem(&new, temp); - - res.free = 1; - res.vectorType = new; - } - else - return ErrValue(ERRBADOPERATOR); - } - else - return ErrValue(ERRBADOPERATOR); - } - else { - res = val; - } - } - else if (tokens[i].token >= Plus && tokens[i].token <= BitShiftRight) { - lastToken = tokens[i].token; - } - } - - if (varToken != NULL) { - if (varToken->token == VariableAssignment) { - dict_t newVar = newDict(CpyStr(varToken->text), res); - dictVectorAdd(&ctx->varDict, newVar); - } - else { - Variable_t* var = dictVectorFind(&ctx->varDict, varToken->text); - if (var != NULL) { - if (var->varType - 2 == res.varType || (var->varType == ByteArrayType && res.varType == IntType)) { - int distance = distanceBetweenTokens(varToken + 2, len, LSBracket, RSBracket); - if (distance < 0) { - // ERR - return ErrValue(ERRSYNTAX); - } - Variable_t index = solveEquation(ctx, varToken + 2, distance, 0); - if (index.varType != IntType) { - // ERR - } - else if (index.integerType < 0 || var->vectorType.count <= index.integerType) { - // ERR - } - else { - if (var->varType == IntArrayType) { - int* arr = vecGetArray(int*, var->vectorType); - arr[index.integerType] = res.integerType; - } - else if (var->varType == StringArrayType) { - char** arr = vecGetArray(char**, var->vectorType); - arr[index.integerType] = CpyStr(res.stringType); - } - else if (var->varType == ByteArrayType) { - u8* arr = vecGetArray(u8*, var->vectorType); - arr[index.integerType] = res.integerType & 0xFF; - } - } - } - else { - // ERR - } - } - else { - //ERR - } - } - } - else { - if (shouldFree) // we should get rid of this ugly free. why not set .free to 0 when assigning it then free in parser.c? - freeVariable(res); - } - - return res; -} \ No newline at end of file diff --git a/source/script/args.h b/source/script/args.h deleted file mode 100644 index d900961..0000000 --- a/source/script/args.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "types.h" - -char* utils_copyStringSize(const char* in, int size); -Variable_t solveEquation(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len, u8 shouldFree); -int distanceBetweenTokens(lexarToken_t* tokens, u32 len, int openToken, int closeToken); -Vector_t extractVars(scriptCtx_t* ctx, lexarToken_t* tokens, u32 len); \ No newline at end of file diff --git a/source/script/arrayClass.c b/source/script/arrayClass.c new file mode 100644 index 0000000..ae4c075 --- /dev/null +++ b/source/script/arrayClass.c @@ -0,0 +1,272 @@ +#include "model.h" +#include "compat.h" +#include "genericClass.h" +#include "intClass.h" +#include "arrayClass.h" +#include "garbageCollector.h" +#include "eval.h" +#include "scriptError.h" +#include "StringClass.h" +#include + +u8 anotherOneIntArg[] = { IntClass }; +u8 oneStringoneFunction[] = { StringClass, FunctionClass }; +u8 doubleInt[] = {IntClass, IntClass}; +u8 oneIntOneAny[] = { IntClass, VARARGCOUNT }; +u8 anotherAnotherOneVarArg[] = { VARARGCOUNT }; +u8 oneByteArrayClass[] = {ByteArrayClass}; +u8 oneIntArrayClass[] = {IntArrayClass}; + +Variable_t arrayClassGetIdx(Variable_t *caller, s64 idx) { + if (caller->variableType == IntArrayClass) { + s64* arr = caller->solvedArray.vector.data; + return newIntVariable(arr[idx]); + } + else if (caller->variableType == StringArrayClass) { + char** arr = caller->solvedArray.vector.data; + Variable_t v = newStringVariable(arr[idx], 1, 0); + v.readOnly = 1; + v.reference = 1; + return v; + } + else if (caller->variableType == ByteArrayClass) { + u8* arr = caller->solvedArray.vector.data; + return newIntVariable(arr[idx]); + } + + return (Variable_t) { 0 }; +} + +int arrayClassAdd(Variable_t *caller, Variable_t *add){ + if (caller->variableType == IntArrayClass) { + if (add->variableType != IntClass) { + return 1; + } + + vecAdd(&caller->solvedArray.vector, add->integer.value); + } + else if (caller->variableType == StringArrayClass) { + if (add->variableType != StringClass) { + return 1; + } + + char* str = CpyStr(add->string.value); + vecAdd(&caller->solvedArray.vector, str); + } + else if (caller->variableType == ByteArrayClass) { + if (add->variableType != IntClass) { + return 1; + } + + u8 val = (u8)(add->integer.value & 0xFF); + vecAdd(&caller->solvedArray.vector, val); + } + + return 0; +} + +ClassFunction(getArrayIdx) { + s64 getVal = (*args)->integer.value; + // Out of bounds + if (getVal < 0 || getVal >= caller->solvedArray.vector.count) { + SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)getVal, (int)caller->solvedArray.vector.count); + } + + Variable_t a = arrayClassGetIdx(caller, getVal); + if (a.variableType == None) + return NULL; + return copyVariableToPtr(a); +} + +ClassFunction(getArrayLen) { + return newIntVariablePtr(caller->solvedArray.vector.count); +} + +ClassFunction(arraySlice) { + s64 skipAmount = getIntValue(*args); + s64 takeAmount = getIntValue(args[1]); + + if (caller->solvedArray.vector.count < (skipAmount + takeAmount) || skipAmount < 0 || takeAmount <= 0) { + SCRIPT_FATAL_ERR("Slicing out of range of array with len %d", (int)caller->solvedArray.vector.count); + } + + Variable_t refSkip = { .variableType = SolvedArrayReferenceClass }; + refSkip.solvedArray.arrayClassReference = caller; + refSkip.solvedArray.offset = skipAmount; + refSkip.solvedArray.len = takeAmount; + addPendingReference(caller); + return copyVariableToPtr(refSkip); +} + +// TODO: arrayForEach does not like the new garbage collector +ClassFunction(arrayForEach) { + Vector_t* v = &caller->solvedArray.vector; + + Callback_SetVar_t setVar = { .isTopLevel = 1, .varName = (*args)->string.value }; + Variable_t* iter = NULL; + iter = copyVariableToPtr(newIntVariable(0)); + iter->gcDoNotFree = 1; + runtimeVariableEdit(&setVar, iter); + + for (int i = 0; i < v->count; i++) { + *iter = arrayClassGetIdx(caller, i); + iter->gcDoNotFree = 1; + + Variable_t* res = genericCallDirect(args[1], NULL, 0); + if (res == NULL) { + if (scriptLastError == SCRIPT_BREAK) { + break; + } + else { + return NULL; + } + } + } + + iter->reference = 1; + free(iter); + + return &emptyClass; +} + +ClassFunction(arrayCopy) { + Vector_t* v = &caller->solvedArray.vector; + Vector_t copiedArray = vecCopy(v); + Variable_t var = { .variableType = caller->variableType, .solvedArray.vector = copiedArray }; + return copyVariableToPtr(var); +} + +ClassFunction(arraySet) { + s64 idx = getIntValue(*args); + Vector_t* v = &caller->solvedArray.vector; + if (v->count < idx || idx < 0) { + SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)idx, (int)caller->solvedArray.vector.count); + } + + u32 oldCount = caller->solvedArray.vector.count; + caller->solvedArray.vector.count = idx; + if (arrayClassAdd(caller, args[1])){ + SCRIPT_FATAL_ERR("Adding the wrong type to a typed array"); + } + caller->solvedArray.vector.count = oldCount; + + return &emptyClass; +} + +ClassFunction(arrayAdd) { + Variable_t* arg = *args; + + if (caller->readOnly) { + SCRIPT_FATAL_ERR("Array is read-only"); + } + + if (arrayClassAdd(caller, arg)){ + SCRIPT_FATAL_ERR("Adding the wrong type to a typed array"); + } + + return &emptyClass; +} + +ClassFunction(arrayContains) { + Vector_t* v = &caller->solvedArray.vector; + Variable_t* arg = *args; + + for (int i = 0; i < v->count; i++) { + Variable_t iter = arrayClassGetIdx(caller, i); + + if (iter.variableType != arg->variableType){ + SCRIPT_FATAL_ERR("type of contains does not match"); + } + + if (caller->variableType == StringArrayClass) { + if (!strcmp(arg->string.value, iter.string.value)) + return newIntVariablePtr(1); + } + else { + if (arg->integer.value == iter.integer.value) + return newIntVariablePtr(1); + } + } + + return newIntVariablePtr(0); +} + +ClassFunction(arrayMinus) { + s64 count = getIntValue(*args); + Vector_t* v = &caller->solvedArray.vector; + if (v->count < count || count <= 0) { + SCRIPT_FATAL_ERR("Accessing index %d while array is %d long", (int)count, (int)caller->solvedArray.vector.count); + } + + if (caller->variableType == StringArrayClass) { + char** arr = v->data; + for (int i = v->count - count; i < count; i++) { + FREE(arr[i]); + } + } + + v->count -= count; + return &emptyClass; +} + +ClassFunction(bytesToStr) { + if (caller->variableType != ByteArrayClass) { + SCRIPT_FATAL_ERR("Need a bytearray to convert to str"); + } + + char* buff = malloc(caller->solvedArray.vector.count + 1); + memcpy(buff, caller->solvedArray.vector.data, caller->solvedArray.vector.count); + buff[caller->solvedArray.vector.count] = '\0'; + return newStringVariablePtr(buff, 0, 0); +} + +ClassFunction(eqArray){ + Variable_t *arg = (*args); + if (caller->solvedArray.vector.count != arg->solvedArray.vector.count || arg->variableType != caller->variableType){ + return newIntVariablePtr(0); + } + + s64 res = memcmp(caller->solvedArray.vector.data, arg->solvedArray.vector.data, caller->solvedArray.vector.count * caller->solvedArray.vector.elemSz); + return newIntVariablePtr(!res); +} + +ClassFunction(arrayFind){ + Variable_t *arg = (*args); + if (caller->solvedArray.vector.count <= arg->solvedArray.vector.count || arg->variableType != caller->variableType){ + return newIntVariablePtr(-1); + } + + u8 step = (arg->variableType == ByteArrayClass) ? 1 : 8; + char *haystack = caller->solvedArray.vector.data; + void *needle = arg->solvedArray.vector.data; + + for (int i = 0; i < caller->solvedArray.vector.count - arg->solvedArray.vector.count; i++){ + if (!memcmp(haystack + (i * step), needle, step * arg->solvedArray.vector.count)){ + return newIntVariablePtr(i); + } + } + + return newIntVariablePtr(-1); +} + +ClassFunctionTableEntry_t arrayFunctions[] = { + {"get", getArrayIdx, 1, anotherOneIntArg }, + {"len", getArrayLen, 0, 0}, + {"slice", arraySlice, 2, doubleInt}, + {"foreach", arrayForEach, 2, oneStringoneFunction}, + {"copy", arrayCopy, 0, 0}, + {"set", arraySet, 2, oneIntOneAny}, + {"+", arrayAdd, 1, anotherAnotherOneVarArg}, + {"add", arrayAdd, 1, anotherAnotherOneVarArg}, + {"-", arrayMinus, 1, anotherOneIntArg}, + {"contains", arrayContains, 1, anotherAnotherOneVarArg}, + {"bytestostr", bytesToStr, 0, 0}, + {"==", eqArray, 1, oneByteArrayClass}, + {"==", eqArray, 1, oneIntArrayClass}, + {"find", arrayFind, 1, oneByteArrayClass}, + {"find", arrayFind, 1, oneIntArrayClass}, +}; + +Variable_t getArrayMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, arrayFunctions, ARRAY_SIZE(arrayFunctions)); +} \ No newline at end of file diff --git a/source/script/arrayClass.h b/source/script/arrayClass.h new file mode 100644 index 0000000..733852d --- /dev/null +++ b/source/script/arrayClass.h @@ -0,0 +1,5 @@ +#pragma once +#include "model.h" +#include "genericClass.h" + +Variable_t getArrayMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/arrayReferenceClass.c b/source/script/arrayReferenceClass.c new file mode 100644 index 0000000..2a75e95 --- /dev/null +++ b/source/script/arrayReferenceClass.c @@ -0,0 +1,24 @@ +#include "model.h" +#include "compat.h" +#include "genericClass.h" +#include "intClass.h" +#include "arrayReferenceClass.h" + + +ClassFunction(projectArray) { + Variable_t newArray = { .variableType = caller->solvedArray.arrayClassReference->variableType, .reference = 1, .readOnly = 1 }; + + newArray.solvedArray.vector = caller->solvedArray.arrayClassReference->solvedArray.vector; + newArray.solvedArray.vector.data = (u8*)caller->solvedArray.arrayClassReference->solvedArray.vector.data + (caller->solvedArray.offset * caller->solvedArray.arrayClassReference->solvedArray.vector.elemSz); + newArray.solvedArray.vector.count = caller->solvedArray.len; + + return copyVariableToPtr(newArray); +} + +ClassFunctionTableEntry_t arrayReferenceFunctions[] = { + {"project", projectArray, 0, 0}, +}; + +Variable_t getArrayReferenceMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, arrayReferenceFunctions, ARRAY_SIZE(arrayReferenceFunctions)); +} \ No newline at end of file diff --git a/source/script/arrayReferenceClass.h b/source/script/arrayReferenceClass.h new file mode 100644 index 0000000..c3287d8 --- /dev/null +++ b/source/script/arrayReferenceClass.h @@ -0,0 +1,5 @@ +#pragma once +#include "model.h" +#include "genericClass.h" + +Variable_t getArrayReferenceMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/compat.h b/source/script/compat.h new file mode 100644 index 0000000..c35cf6f --- /dev/null +++ b/source/script/compat.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef WIN32 + #include + #include + #define gfx_printf(str, ...) printf(str, ##__VA_ARGS__) + #define gfx_vprintf(str, va) vprintf(str, va); + #define gfx_putc(x) gfx_printf("%c", x) + #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + #define LP_VER_MJ 3 + #define LP_VER_MN 0 + #define LP_VER_BF 5 + #define LP_VER "3.0.5" + #define FREE(x) if (x) free(x) + #define CpyStr(x) _strdup(x) + #include "vector.h" + #pragma _CRT_SECURE_NO_WARNINGS +#else + #include "../gfx/gfx.h" + #include + #include "../utils/vector.h" + #include "../utils/utils.h" +#endif \ No newline at end of file diff --git a/source/script/dictionaryClass.c b/source/script/dictionaryClass.c new file mode 100644 index 0000000..07e5fd9 --- /dev/null +++ b/source/script/dictionaryClass.c @@ -0,0 +1,61 @@ +#include "dictionaryClass.h" +#include +#include "garbageCollector.h" +#include "intClass.h" + +u8 dictOneStrOneAll[] = { StringClass, VARARGCOUNT }; + +void addVariableToDict(Variable_t *dict, char* name, Variable_t *add){ + Dict_t a = {.name = CpyStr(name), .var = add}; + vecAdd(&dict->dictionary.vector, a); +} + +void addIntToDict(Variable_t *dict, char* name, s64 integer){ + Variable_t *v = newIntVariablePtr(integer); + addVariableToDict(dict, name, v); +} + +Dict_t* getEntry(Vector_t *v, char* name) { + vecForEach(Dict_t*, dict, v) { + if (!strcmp(name, dict->name)) { + return dict; + } + } + return NULL; +} + +ClassFunction(dictSet) { + addPendingReference(args[1]); + char* arg = CpyStr(args[0]->string.value); + Dict_t* possibleEntry = getEntry(&caller->dictionary.vector, arg); + if (possibleEntry == NULL) { + Dict_t a = { .name = arg, .var = args[1] }; + vecAdd(&caller->dictionary.vector, a); + } + else { + possibleEntry->var = args[1]; + free(arg); + } + return &emptyClass; +} + +ClassFunctionTableEntry_t dictFunctions[] = { + {"set", dictSet, 2, dictOneStrOneAll}, +}; + +Variable_t getDictMember(Variable_t* var, char* memberName) { + if (!strcmp(memberName, "set")) + return getGenericFunctionMember(var, memberName, dictFunctions, ARRAY_SIZE(dictFunctions)); + + vecForEach(Dict_t*, dict, (&var->dictionary.vector)) { + if (!strcmp(dict->name, memberName)) { + Variable_t a = { 0 }; + a.variableType = ReferenceType; + a.referenceType = dict->var; + addPendingReference(dict->var); + return a; + } + } + + return (Variable_t) { 0 }; +} \ No newline at end of file diff --git a/source/script/dictionaryClass.h b/source/script/dictionaryClass.h new file mode 100644 index 0000000..a744a7f --- /dev/null +++ b/source/script/dictionaryClass.h @@ -0,0 +1,8 @@ +#pragma once +#include "model.h" +#include "genericClass.h" +#include "compat.h" + +Variable_t getDictMember(Variable_t* var, char* memberName); +void addVariableToDict(Variable_t *dict, char* name, Variable_t *add); +void addIntToDict(Variable_t *dict, char* name, s64 integer); \ No newline at end of file diff --git a/source/script/else.c b/source/script/else.c new file mode 100644 index 0000000..dca9498 --- /dev/null +++ b/source/script/else.c @@ -0,0 +1,22 @@ +#include "else.h" + +ClassFunction(scriptElse) { + if (!caller->integer.value) { + Variable_t* res = genericCallDirect(args[0], NULL, 0); + if (res == NULL) + return NULL; + + removePendingReference(res); + } + return &emptyClass; +} + +u8 elseOneFunction[] = { FunctionClass }; + +ClassFunctionTableEntry_t elseFunctions[] = { + {"else", scriptElse, 1, elseOneFunction}, +}; + +Variable_t getElseMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, elseFunctions, ARRAY_SIZE(elseFunctions)); +} \ No newline at end of file diff --git a/source/script/else.h b/source/script/else.h new file mode 100644 index 0000000..170e7be --- /dev/null +++ b/source/script/else.h @@ -0,0 +1,7 @@ +#pragma once +#include "model.h" +#include "genericClass.h" +#include "compat.h" +#include "garbageCollector.h" + +Variable_t getElseMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/eval.c b/source/script/eval.c new file mode 100644 index 0000000..c80bb3c --- /dev/null +++ b/source/script/eval.c @@ -0,0 +1,276 @@ +#include "model.h" +#include "compat.h" +#include "genericClass.h" +#include "eval.h" +#include "garbageCollector.h" +#include "standardLibrary.h" +#include "scriptError.h" +#include "StringClass.h" +#include "intClass.h" +#include "unsolvedArrayClass.h" +#include "functionClass.h" +#include + +Variable_t* staticVars; + +void setStaticVars(Vector_t* vec) { + staticVars = vec->data; +} + +Vector_t runtimeVars; + +void initRuntimeVars() { + runtimeVars = newVec(sizeof(Dict_t), 8); +} + +void exitRuntimeVars() { + vecForEach(Dict_t*, runtimeVar, (&runtimeVars)) { + FREE(runtimeVar->name); + removePendingReference(runtimeVar->var); + } + + vecFree(runtimeVars); + runtimeVars.count = 0; +} + +Variable_t* opToVar(Operator_t* op, Callback_SetVar_t *setCallback, u8 possibleCallArg) { + Variable_t* var = NULL; + CallArgs_t* args = NULL; + + if ((op + 1)->token == CallArgs && possibleCallArg) + args = &(op + 1)->callArgs; + + if (op->token == BetweenBrackets) { + var = eval(op->variable.betweenBrackets.data, op->variable.betweenBrackets.len, 1); + } + else if (op->token == Variable) { + if (op->variable.staticVariableSet) { + if (op->variable.staticVariableRef) { + op->variable.staticVariable = &staticVars[(int)op->variable.staticVariable]; + op->variable.staticVariableRef = 0; + op->variable.staticVariable->readOnly = 1; + op->variable.staticVariable->reference = 1; + op->variable.staticVariable->gcDoNotFree = 1; + } + + var = op->variable.staticVariable; + + if (var->variableType == UnresolvedArrayClass) { + var = solveArray(var); + } + } + else if (op->variable.staticVariableType > 0) { + if (op->variable.staticVariableType == 1) { + var = copyVariableToPtr(newIntVariable(op->variable.integerType)); + } + else if (op->variable.staticVariableType == 2) { + var = copyVariableToPtr(newStringVariable(op->variable.stringType, 1, 0)); + var->reference = 1; + } + else if (op->variable.staticVariableType == 3) { + var = copyVariableToPtr(newFunctionVariable(createFunctionClass((Function_t) { 0 }, op->variable.staticFunction))); + var->function.len = op->variable.staticFunctionLen; + var->reference = 1; + + + if (!strcmp(var->function.builtInPtr->name,"while")) { + var->function.firstArgAsFunction = 1; + } + } + } + else { + if (args != NULL) { + if (args->action == ActionSet && args->extraAction == ActionExtraNone) { + setCallback->isTopLevel = 1; + setCallback->varName = op->variable.name; + setCallback->hasVarName = 1; + return NULL; + } + } + + vecForEach(Dict_t*, variableArrayEntry, (&runtimeVars)) { + if (!strcmp(variableArrayEntry->name, op->variable.name)) { + var = variableArrayEntry->var; + break; + } + } + + if (var == NULL) { + SCRIPT_FATAL_ERR("Variable '%s' not found", op->variable.name); + } + + addPendingReference(var); + } + } + + while (args) { + Variable_t* varNext = NULL; + if (args->action == ActionGet) { + if (args->extraAction == ActionExtraMemberName && args->next != NULL && args->next->action == ActionCall) { + varNext = callMemberFunction(var, args->extra, args->next); + args = args->next; + } + else { + varNext = genericGet(var, args); + } + } + else if (args->action == ActionSet) { + if (var->readOnly) { + SCRIPT_FATAL_ERR("Variable which set was called on is read-only"); + return NULL; + } + + if (args->extraAction == ActionExtraMemberName || args->extraAction == ActionExtraArrayIndex) { + setCallback->hasVarName = (args->extraAction == ActionExtraMemberName) ? 1 : 0; + setCallback->setVar = var; + if (args->extraAction == ActionExtraMemberName) { + setCallback->varName = args->extra; + } + else { + setCallback->idxVar = args->extra; + } + } + else { + SCRIPT_FATAL_ERR("Unexpected set!"); + } + return NULL; + } + else if (args->action == ActionCall) { + varNext = genericCall(var, args); + } + + if (varNext == NULL) + return NULL; + + removePendingReference(var); + + //if (!var->reference) + // freeVariable(&var); + + var = varNext; + args = args->next; + } + + if (op->not && var) { + Variable_t* newVar = callMemberFunctionDirect(var, "not", NULL, 0); + removePendingReference(var); + var = newVar; + } + + return var; +} + +void runtimeVariableEdit(Callback_SetVar_t* set, Variable_t* curRes) { + if (set->isTopLevel) { + vecForEach(Dict_t*, variableArrayEntry, (&runtimeVars)) { + if (!strcmp(variableArrayEntry->name, set->varName)) { + removePendingReference(variableArrayEntry->var); + //addPendingReference(curRes); + variableArrayEntry->var = curRes; + return; + } + } + + Dict_t newStoredVariable = { 0 }; + newStoredVariable.name = CpyStr(set->varName); + newStoredVariable.var = curRes; + vecAdd(&runtimeVars, newStoredVariable); + return; + } + + if (set->idxVar) { + Variable_t* var = eval(set->idxVar->operations.data, set->idxVar->operations.count, 1); + Variable_t* args[2] = { var, curRes }; + callMemberFunctionDirect(set->setVar, "set", args, 2); + removePendingReference(var); + } + else { + Variable_t varName = { .variableType = StringClass, .reference = 1, .string.value = set->varName }; + Variable_t* args[2] = { &varName, curRes }; + callMemberFunctionDirect(set->setVar, "set", args, 2); + } + + removePendingReference(curRes); + removePendingReference(set->setVar); + + // TODO: add non-top level sets +} + +Variable_t* eval(Operator_t* ops, u32 len, u8 ret) { + Variable_t* curRes = NULL; + Operator_t* curOp = NULL; + Callback_SetVar_t set = { 0 }; + for (u32 i = 0; i < len; i++) { + Operator_t* cur = &ops[i]; + + if (cur->token == CallArgs) + continue; + + if (cur->token == EquationSeperator) { + scriptCurrentLine = cur->lineNumber; + if (set.hasBeenNoticed == 1) + runtimeVariableEdit(&set, curRes); + else + removePendingReference(curRes); + + memset(&set, 0, sizeof(Callback_SetVar_t)); + curRes = NULL; + curOp = NULL; + continue; + } + + if (curRes == NULL) { + if (cur->token != Variable && cur->token != BetweenBrackets) { + SCRIPT_FATAL_ERR("First token is not a variable"); + } + else { + curRes = opToVar(cur, &set, (len - i) > 1); + if (!curRes) { + if ((set.varName != NULL || set.idxVar != NULL) && set.hasBeenNoticed == 0) { + set.hasBeenNoticed = 1; + continue; + } + return NULL; + } + } + continue; + } + + if (curOp == NULL) { + if (cur->token != Variable && cur->token != BetweenBrackets) { + curOp = cur; + } + else { + SCRIPT_FATAL_ERR("Expected operator"); + } + continue; + } + + Variable_t* rightSide = opToVar(cur, &set, (len - i) > 1); + if (!rightSide) + return NULL; + + + // Issue lies here for freeing issues, curRes is corrupted + Variable_t* result = callMemberFunctionDirect(curRes, curOp->tokenStr, &rightSide, 1); + // Free old values + + removePendingReference(curRes); + removePendingReference(rightSide); + rightSide = NULL; + curOp = NULL; + + curRes = result; + } + + if (set.hasBeenNoticed == 1) { + runtimeVariableEdit(&set, curRes); + return &emptyClass; + } + else if (!ret) { + removePendingReference(curRes); + return &emptyClass; + } + + return curRes; +} \ No newline at end of file diff --git a/source/script/eval.h b/source/script/eval.h new file mode 100644 index 0000000..b64ee76 --- /dev/null +++ b/source/script/eval.h @@ -0,0 +1,19 @@ +#pragma once +#include "model.h" + +typedef struct { + struct { + u8 isTopLevel : 1; + u8 hasBeenNoticed : 1; + u8 hasVarName : 1; + }; + Variable_t* setVar; + char* varName; + Function_t* idxVar; +} Callback_SetVar_t; + +Variable_t* eval(Operator_t* ops, u32 len, u8 ret); +void setStaticVars(Vector_t* vec); +void initRuntimeVars(); +void exitRuntimeVars(); +void runtimeVariableEdit(Callback_SetVar_t* set, Variable_t* curRes); \ No newline at end of file diff --git a/source/script/functionClass.c b/source/script/functionClass.c new file mode 100644 index 0000000..489a7fb --- /dev/null +++ b/source/script/functionClass.c @@ -0,0 +1,67 @@ +#include "functionClass.h" +#include "compat.h" +#include "model.h" +#include + +Function_t* getFunctionValue(Variable_t* var) { + if (var->variableType != FunctionClass) + return NULL; + + FunctionClass_t* a = &var->function; + + if (a->builtIn) + return NULL; + + return &a->function; +} + +Function_t createEmptyFunction() { + Function_t a = { 0 }; + a.operations = newVec(sizeof(Operator_t), 0); + return a; +} + +// Will NOT copy the Function, the pointer is taken as-is. Set as NULL to make it builtin +FunctionClass_t createFunctionClass(Function_t in, ClassFunctionTableEntry_t *builtIn) { + FunctionClass_t a = { 0 }; + if (!builtIn) { + a.function = in; + } + else { + a.builtIn = 1; + a.builtInPtr = builtIn; + a.len = 1; + } + + return a; +} + +FunctionClass_t* creteFunctionClassPtr(Function_t in, ClassFunctionTableEntry_t* builtIn) { + FunctionClass_t* a = malloc(sizeof(FunctionClass_t)); + *a = createFunctionClass(in, builtIn); + return a; +} + +Function_t* createFunctionPtrFromFunction(Function_t in) { + Function_t* f = malloc(sizeof(Function_t)); + *f = in; + return f; +} + +// Functions are always readonly +Variable_t newFunctionVariable(FunctionClass_t func) { + Variable_t var = { .variableType = FunctionClass, .readOnly = 1, .function = func }; + return var; +} + +int countTokens(Function_t *func, u8 toCount) { + Operator_t* ops = func->operations.data; + int count = 0; + for (int i = 0; i < func->operations.count; i++) { + if (ops[i].token == toCount) { + count++; + } + } + + return count; +} \ No newline at end of file diff --git a/source/script/functionClass.h b/source/script/functionClass.h new file mode 100644 index 0000000..5b5467f --- /dev/null +++ b/source/script/functionClass.h @@ -0,0 +1,8 @@ +#pragma once +#include "model.h" + +Function_t createEmptyFunction(); +Function_t* createFunctionPtrFromFunction(Function_t in); +Variable_t newFunctionVariable(FunctionClass_t func); +FunctionClass_t createFunctionClass(Function_t in, ClassFunctionTableEntry_t* builtIn); +int countTokens(Function_t* func, u8 toCount); \ No newline at end of file diff --git a/source/script/functions.c b/source/script/functions.c deleted file mode 100644 index c2b7d75..0000000 --- a/source/script/functions.c +++ /dev/null @@ -1,529 +0,0 @@ -#include "types.h" -#include "args.h" -#include "variables.h" -#include -#include "../gfx/gfx.h" -#include -#include "lexer.h" - -#include -#include "../fs/fsutils.h" -#include "../gfx/gfxutils.h" -#include "../hid/hid.h" -#include -#include "../fs/fscopy.h" -#include "../storage/mountmanager.h" -#include "../storage/emummc.h" -#include "../fs/readers/folderReader.h" -#include "../utils/utils.h" -#include "../keys/keys.h" -#include "../storage/emmcfile.h" -#include "../keys/nca.h" -#include "../keys/save.h" -#include "../tegraexplorer/tconf.h" - -#define scriptFunction(name) Variable_t name(scriptCtx_t *ctx, Variable_t *vars, u32 varLen) - -#define varInt(i) newVar(IntType, 0, i) -#define varStr(s) newVar(StringType, 1, .stringType = s) - -scriptFunction(funcIf) { - setCurIndentInstruction(ctx, (vars[0].integerType == 0), 0, -1); - return NullVar; -} - -scriptFunction(funcPrint) { - for (u32 i = 0; i < varLen; i++) { - if (vars[i].varType == IntType) - gfx_printf("%d", vars[i].integerType); - else if (vars[i].varType == StringType) - gfx_printf("%s", vars[i].stringType); - else if (vars[i].varType == IntArrayType) { - gfx_printf("["); - int* v = vecGetArray(int*, vars[i].vectorType); - for (u32 j = 0; j < vars[i].vectorType.count; j++) - gfx_printf((j + 1 == vars[i].vectorType.count) ? "%d" : "%d, ", v[j]); - gfx_printf("]"); - } - } - - return NullVar; -} - -scriptFunction(funcPrintln) { - funcPrint(ctx, vars, varLen); - gfx_printf("\n"); - return NullVar; -} - -scriptFunction(funcWhile) { - setCurIndentInstruction(ctx, (vars[0].integerType == 0), 0, ctx->startEquation); - - return NullVar; -} - -scriptFunction(funcElse) { - indentInstructor_t* curInstruction = getCurIndentInstruction(ctx); - setCurIndentInstruction(ctx, !curInstruction->skip, 0, -1); - return NullVar; -} - -scriptFunction(funcLen) { - if (vars[0].varType >= IntArrayType && vars[0].varType <= ByteArrayType) - return IntVal(vars[0].vectorType.count); - else if (vars[0].varType == StringType) { - if (vars[0].stringType != NULL) - return IntVal(strlen(vars[0].stringType)); - } - - return ErrVar(ERRINVALIDTYPE); -} - -scriptFunction(funcMakeByteArray){ - u8 *buff = malloc(vars[0].vectorType.count); - vecDefArray(int*, entries, vars[0].vectorType); - for (int i = 0; i < vars[0].vectorType.count; i++) - buff[i] = (u8)(entries[i] & 0xFF); - - Vector_t v = vecFromArray(buff, vars[0].vectorType.count, sizeof(u8)); - return newVar(ByteArrayType, 1, .vectorType = v); -} - -scriptFunction(funcSetPixel){ - u32 color = 0xFF000000 | ((vars[2].integerType & 0xFF) << 16) | ((vars[3].integerType & 0xFF) << 8) | (vars[4].integerType & 0xFF); - gfx_set_pixel_horz(vars[0].integerType, vars[1].integerType, color); - return NullVar; -} - -scriptFunction(funcFileExists){ - return newVar(IntType, 0, FileExists(vars[0].stringType)); -} - -// Args: Str (Path) -scriptFunction(funcReadFile){ - u32 fSize = 0; - u8 *buff = sd_file_read(vars[0].stringType, &fSize); - if (buff == NULL) - return ErrVar(ERRFATALFUNCFAIL); - - Vector_t vec = vecFromArray(buff, fSize, sizeof(u8)); - return newVar(ByteArrayType, 1, .vectorType = vec); -} - -// Args: Str (Path), vector(Byte) (toWrite) -scriptFunction(funcWriteFile){ - return newVar(IntType, 0, sd_save_to_file(vars[1].vectorType.data, vars[1].vectorType.count, vars[0].stringType)); -} - -// Args: vector(Byte) -scriptFunction(funcByteToStr){ - char *str = calloc(vars[0].vectorType.count + 1, 1); - memcpy(str, vars[0].vectorType.data, vars[0].vectorType.count); - return newVar(StringType, 1, .stringType = str); -} - -scriptFunction(funcReturn){ - if (ctx->indentIndex > 0){ - vecDefArray(indentInstructor_t*, instructors, ctx->indentInstructors); - for (int i = ctx->indentIndex - 1; i >= 0; i--){ - indentInstructor_t ins = instructors[i]; - if (ins.active && ins.jump && ins.function){ - ctx->curPos = ins.jumpLoc - 1; - ins.active = 0; - ctx->indentIndex = i; - break; - } - } - } - - return NullVar; -} - -scriptFunction(funcExit){ - return ErrVar(ERRESCSCRIPT); -} - -/* -scriptFunction(funcContinue){ - if (ctx->indentIndex > 0){ - vecDefArray(indentInstructor_t*, instructors, ctx->indentInstructors); - for (int i = ctx->indentIndex - 1; i >= 0; i--){ - indentInstructor_t ins = instructors[i]; - if (ins.active && ins.jump && !ins.function){ - ctx->curPos = ins.jumpLoc - 1; - ins.active = 0; - ctx->indentIndex = i; - break; - } - } - } - - return NullVar; -} -*/ - -// Args: Int, Int -scriptFunction(funcSetPrintPos){ - if (vars[0].integerType > 78 || vars[0].integerType < 0 || vars[1].integerType > 42 || vars[1].integerType < 0) - return ErrVar(ERRFATALFUNCFAIL); - - gfx_con_setpos(vars[0].integerType * 16, vars[1].integerType * 16); - return NullVar; -} - -scriptFunction(funcClearScreen){ - gfx_clearscreen(); - return NullVar; -} - -int validRanges[] = { - 1279, - 719, - 1279, - 719 -}; - -// Args: Int, Int, Int, Int, Int -scriptFunction(funcDrawBox){ - for (int i = 0; i < ARR_LEN(validRanges); i++){ - if (vars[i].integerType > validRanges[i] || vars[i].integerType < 0) - return ErrVar(ERRFATALFUNCFAIL); - } - - gfx_box(vars[0].integerType, vars[1].integerType, vars[2].integerType, vars[3].integerType, vars[4].integerType); - return NullVar; -} - -typedef struct { - char *name; - u32 color; -} ColorCombo_t; - -ColorCombo_t combos[] = { - {"RED", COLOR_RED}, - {"ORANGE", COLOR_ORANGE}, - {"YELLOW", COLOR_YELLOW}, - {"GREEN", COLOR_GREEN}, - {"BLUE", COLOR_BLUE}, - {"VIOLET", COLOR_VIOLET}, - {"GREY", COLOR_GREY}, -}; - -u32 GetColor(char *color){ - for (int i = 0; i < ARR_LEN(combos); i++){ - if (!strcmp(combos[i].name, color)){ - return combos[i].color; - } - } - - return COLOR_WHITE; -} - -// Args: Str -scriptFunction(funcSetColor){ - SETCOLOR(GetColor(vars[0].stringType), COLOR_DEFAULT); - return NullVar; -} - -scriptFunction(funcPause){ - return newVar(IntType, 0, hidWait()->buttons); -} - -// Args: Int -scriptFunction(funcWait){ - u32 timer = get_tmr_ms(); - while (timer + vars[0].integerType > get_tmr_ms()){ - gfx_printf(" \r", (vars[0].integerType - (get_tmr_ms() - timer)) / 1000); - hidRead(); - } - return NullVar; -} - -scriptFunction(funcGetVer){ - int *arr = malloc(3 * sizeof(int)); - arr[0] = LP_VER_MJ; - arr[1] = LP_VER_MN; - arr[2] = LP_VER_BF; - Vector_t res = vecFromArray(arr, 3, sizeof(int)); - return newVar(IntArrayType, 1, .vectorType = res); -} - - -// Args: vec(str), int, (optional) vec(str) -scriptFunction(funcMakeMenu){ - if (varLen == 3 && vars[2].vectorType.count != vars[0].vectorType.count) - return ErrVar(ERRSYNTAX); - - Vector_t menuEntries = newVec(sizeof(MenuEntry_t), vars[0].vectorType.count); - vecDefArray(char**, names, vars[0].vectorType); - char **colors; - if (varLen >= 3) - colors = vecGetArray(char**, vars[2].vectorType); - - int *options; - if (varLen == 4) - options = vecGetArray(int*, vars[3].vectorType); - - for (int i = 0; i < vars[0].vectorType.count; i++){ - u32 color = COLORTORGB(((varLen >= 3) ? GetColor(colors[i]) : COLOR_WHITE)); - MenuEntry_t a = {.optionUnion = color, .name = names[i]}; - - if (varLen == 4){ - a.skip = (options[i] & 1) ? 1 : 0; - a.hide = (options[i] & 2) ? 1 : 0; - } - - vecAddElem(&menuEntries, a); - } - - int res = newMenu(&menuEntries, vars[1].integerType, 78, 10, ENABLEB | ALWAYSREDRAW, menuEntries.count); - vecFree(menuEntries); - return varInt(res); -} - -// Args: Str, Str -scriptFunction(funcCombinePath){ - if (varLen <= 1) - return NullVar; - - for (int i = 0; i < varLen; i++){ - if (vars[i].varType != StringType) - return ErrVar(ERRINVALIDTYPE); - } - - char *res = CpyStr(vars[0].stringType); - - for (int i = 1; i < varLen; i++){ - char *temp = CombinePaths(res, vars[i].stringType); - free(res); - res = temp; - } - - return varStr(res); -} - -// Args: Str -scriptFunction(funcEscFolder){ - return newVar(StringType, 1, .stringType = EscapeFolder(vars[0].stringType)); -} - -// Args: Str, Str -scriptFunction(funcFileMove){ - return varInt(f_rename(vars[0].stringType, vars[1].stringType)); -} - -// Args: Str, Str -scriptFunction(funcFileCopy){ - return varInt(FileCopy(vars[0].stringType, vars[1].stringType, COPY_MODE_PRINT).err); -} - -// Args: Str -scriptFunction(funcMmcConnect){ - int res = 0; - if (!strcmp(vars[0].stringType, "SYSMMC")) - res = connectMMC(MMC_CONN_EMMC); - else if (!strcmp(vars[0].stringType, "EMUMMC") && emu_cfg.enabled) - res = connectMMC(MMC_CONN_EMUMMC); - else - return ErrVar(ERRFATALFUNCFAIL); - - return varInt(res); -} - -// Args: Str -scriptFunction(funcMmcMount){ - if (!TConf.keysDumped) - return ErrVar(ERRFATALFUNCFAIL); - - return varInt((mountMMCPart(vars[0].stringType).err)); -} - -// Args: Str -scriptFunction(funcMkdir){ - return varInt((f_mkdir(vars[0].stringType))); -} - -// Args: Str -scriptFunction(funcReadDir){ - int res = 0; - Vector_t files = ReadFolder(vars[0].stringType, &res); - if (res){ - clearFileVector(&files); - return ErrVar(ERRFATALFUNCFAIL); - } - - vecDefArray(FSEntry_t*, fsEntries, files); - - Vector_t fileNames = newVec(sizeof(char*), files.count); - Vector_t fileProperties = newVec(sizeof(int), files.count); - - for (int i = 0; i < files.count; i++){ - vecAddElem(&fileNames, fsEntries[i].name); - int isFolder = fsEntries[i].isDir; - vecAddElem(&fileProperties, isFolder); - } - - vecFree(files); - - dictVectorAdd(&ctx->varDict, newDict(CpyStr("fileProperties"), (newVar(IntArrayType, 1, .vectorType = fileProperties)))); - - return newVar(StringArrayType, 1, .vectorType = fileNames); -} - -// Args: Str, Str -scriptFunction(funcCopyDir){ - return varInt((FolderCopy(vars[0].stringType, vars[1].stringType).err)); -} - -// Args: Str -scriptFunction(funcDelDir){ - return varInt((FolderDelete(vars[0].stringType).err)); -} - -// Args: Str -scriptFunction(funcDelFile){ - return varInt((f_unlink(vars[0].stringType))); -} - -// Args: Str, Str -scriptFunction(funcMmcDump){ - return varInt((DumpOrWriteEmmcPart(vars[0].stringType, vars[1].stringType, 0, 1).err)); -} - -// Args: Str, Str, Int -scriptFunction(funcMmcRestore){ - return varInt((DumpOrWriteEmmcPart(vars[0].stringType, vars[1].stringType, 1, vars[2].integerType).err)); -} - -// Args: Str -scriptFunction(funcGetNcaType){ - if (!TConf.keysDumped) - return ErrVar(ERRFATALFUNCFAIL); - - return varInt((GetNcaType(vars[0].stringType))); -} - -// Args: Str -scriptFunction(funcSignSave){ - if (!TConf.keysDumped) - return ErrVar(ERRFATALFUNCFAIL); - - return varInt((saveCommit(vars[0].stringType).err)); -} - -scriptFunction(funcGetMs){ - return varInt(get_tmr_ms()); -} - -extern int launch_payload(char *path); - -// Args: Str -scriptFunction(funcLaunchPayload){ - return varInt(launch_payload(vars[0].stringType)); -} - -u8 fiveInts[] = {IntType, IntType, IntType, IntType, IntType}; -u8 singleIntArray[] = { IntArrayType }; -u8 singleInt[] = { IntType }; -u8 singleAny[] = { varArgs }; -u8 singleStr[] = { StringType }; -u8 singleByteArr[] = { ByteArrayType }; -u8 StrByteVec[] = { StringType, ByteArrayType}; -u8 MenuArgs[] = { StringArrayType, IntType, StringArrayType, IntArrayType}; -u8 twoStrings[] = { StringType, StringType }; -u8 mmcReadWrite[] = { StringType, StringType, IntType}; - -functionStruct_t scriptFunctions[] = { - {"if", funcIf, 1, singleInt}, - {"print", funcPrint, varArgs, NULL}, - {"println", funcPrintln, varArgs, NULL}, - {"while", funcWhile, 1, singleInt}, - {"else", funcElse, 0, NULL}, - {"len", funcLen, 1, singleAny}, - {"byte", funcMakeByteArray, 1, singleIntArray}, - {"setPixel", funcSetPixel, 5, fiveInts}, - {"fileRead", funcReadFile, 1, singleStr}, - {"fileWrite", funcWriteFile, 2, StrByteVec}, - {"fileExists", funcFileExists, 1, singleStr}, - {"bytesToStr", funcByteToStr, 1, singleByteArr}, - {"return", funcReturn, 0, NULL}, - {"exit", funcExit, 0, NULL}, - //{"continue", funcContinue, 0, NULL}, - {"printPos", funcSetPrintPos, 2, fiveInts}, - {"clearscreen", funcClearScreen, 0, NULL}, - {"drawBox", funcDrawBox, 5, fiveInts}, - {"color", funcSetColor, 1, singleStr}, - {"pause", funcPause, 0, NULL}, - {"wait", funcWait, 1, singleInt}, - {"version", funcGetVer, 0, NULL}, - {"menu", funcMakeMenu, 2, MenuArgs}, // for the optional arg - {"menu", funcMakeMenu, 3, MenuArgs}, - {"menu", funcMakeMenu, 4, MenuArgs}, - {"pathCombine", funcCombinePath, varArgs, NULL}, - {"pathEscFolder", funcEscFolder, 1, singleStr}, - {"fileMove", funcFileMove, 2, twoStrings}, - {"fileCopy", funcFileCopy, 2, twoStrings}, - {"fileDel", funcDelFile, 1, singleStr}, - {"mmcConnect", funcMmcConnect, 1, singleStr}, - {"mmcMount", funcMmcMount, 1, singleStr}, - {"mkdir", funcMkdir, 1, singleStr}, - {"dirRead", funcReadDir, 1, singleStr}, - {"dirCopy", funcCopyDir, 2, twoStrings}, - {"dirDel", funcDelDir, 1, singleStr}, - {"mmcDump", funcMmcDump, 2, mmcReadWrite}, - {"mmcRestore", funcMmcRestore, 3, mmcReadWrite}, - {"ncaGetType", funcGetNcaType, 1, singleStr}, - {"saveSign", funcSignSave, 1, singleStr}, - {"timerMs", funcGetMs, 0, NULL}, - {"launchPayload", funcLaunchPayload, 1, singleStr}, - // Left from old: keyboard(?) -}; - -Variable_t executeFunction(scriptCtx_t* ctx, char* func_name, lexarToken_t *start, u32 len) { - Vector_t args = { 0 }; - - if (len > 0) { - args = extractVars(ctx, start, len); - Variable_t* vars = vecGetArray(Variable_t*, args); - for (int i = 0; i < args.count; i++) { - if (vars[i].varType == ErrType) - return vars[i]; - } - } - - Variable_t* vars = vecGetArray(Variable_t*, args); - - for (u32 i = 0; i < ARRAY_SIZE(scriptFunctions); i++) { - if (scriptFunctions[i].argCount == args.count || scriptFunctions[i].argCount == varArgs) { - if (!strcmp(scriptFunctions[i].key, func_name)) { - if (scriptFunctions[i].argCount != varArgs && scriptFunctions[i].argCount != 0) { - u8 argsMatch = 1; - for (u32 j = 0; j < args.count; j++) { - if (vars[j].varType != scriptFunctions[i].typeArray[j] && scriptFunctions[i].typeArray[j] != varArgs) { - argsMatch = 0; - break; - } - } - - if (!argsMatch) - continue; - } - - Variable_t ret = scriptFunctions[i].value(ctx, vars, args.count); - freeVariableVector(&args); - return ret; - } - } - } - - Variable_t* var = dictVectorFind(&ctx->varDict, func_name); - if (var != NULL) { - if (var->varType == JumpType) { - setCurIndentInstruction(ctx, 0, 1, ctx->curPos); - ctx->curPos = var->integerType - 1; - return NullVar; - } - } - - return ErrVar(ERRNOFUNC); -} \ No newline at end of file diff --git a/source/script/functions.h b/source/script/functions.h deleted file mode 100644 index b5640e8..0000000 --- a/source/script/functions.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include "variables.h" - -Variable_t executeFunction(scriptCtx_t* ctx, char* func_name, lexarToken_t* start, u32 len); \ No newline at end of file diff --git a/source/script/garbageCollector.c b/source/script/garbageCollector.c new file mode 100644 index 0000000..11bd98e --- /dev/null +++ b/source/script/garbageCollector.c @@ -0,0 +1,26 @@ +#include "model.h" +#include "genericClass.h" +#include "compat.h" +#include "garbageCollector.h" + +void modReference(Variable_t* ref, u8 add) { + if (ref == NULL || ref->gcDoNotFree) + return; + + if (add) { + ref->tagCount++; + } + else { + ref->tagCount--; + if (ref->tagCount <= 0) { + // TODO: move to parser.c + if (ref->variableType == FunctionClass && ref->function.builtIn && ref->function.origin != NULL) + modReference(ref->function.origin, 0); + + if (ref->variableType == SolvedArrayReferenceClass) + modReference(ref->solvedArray.arrayClassReference, 0); + + freeVariable(&ref); + } + } +} \ No newline at end of file diff --git a/source/script/garbageCollector.h b/source/script/garbageCollector.h new file mode 100644 index 0000000..f60a706 --- /dev/null +++ b/source/script/garbageCollector.h @@ -0,0 +1,8 @@ +#include "model.h" + +//void removePendingReference(Variable_t* ref); + +void modReference(Variable_t* ref, u8 add); + +#define removePendingReference(ref) modReference(ref, 0) +#define addPendingReference(ref) modReference(ref, 1) \ No newline at end of file diff --git a/source/script/genericClass.c b/source/script/genericClass.c new file mode 100644 index 0000000..6113f8a --- /dev/null +++ b/source/script/genericClass.c @@ -0,0 +1,297 @@ +#include "genericClass.h" +#include "model.h" +#include "intClass.h" +#include "compat.h" +#include "eval.h" +#include +#include "garbageCollector.h" +#include "StringClass.h" +#include "arrayClass.h" +#include "arrayReferenceClass.h" +#include "functionClass.h" +#include "scriptError.h" +#include "saveClass.h" +#include "unsolvedArrayClass.h" +#include "else.h" +#include "dictionaryClass.h" + +Variable_t* copyVariableToPtr(Variable_t var) { + Variable_t* a = malloc(sizeof(Variable_t)); + *a = var; + a->tagCount = 0; + addPendingReference(a); + return a; +} + +MemberGetters_t memberGetters[] = { + {IntClass, getIntegerMember}, + {StringClass, getStringMember}, + {IntArrayClass, getArrayMember}, + {StringArrayClass, getArrayMember}, + {ByteArrayClass, getArrayMember}, + {SolvedArrayReferenceClass, getArrayReferenceMember}, + {UnresolvedArrayClass, getUnsolvedArrayMember}, + {ElseClass, getElseMember}, + {DictionaryClass, getDictMember}, +#ifndef WIN32 + {SaveClass, getSaveMember}, +#endif +}; + +Variable_t* genericGet(Variable_t* var, CallArgs_t* ref) { + if (ref->extraAction == ActionExtraMemberName) { + for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) { + if (var->variableType == memberGetters[i].classType) { + Variable_t member = memberGetters[i].func(var, ref->extra); + if (member.variableType == None) + break; + + if (member.variableType == ReferenceType) { + return member.referenceType; + } + + addPendingReference(var); // So caller doesn't fall out of scope. Don't forget to free! + return copyVariableToPtr(member); + } + } + + SCRIPT_FATAL_ERR("Did not find member '%s'", ref->extra); + } + else if (ref->extraAction == ActionExtraArrayIndex) { + Function_t* idx = ref->extra; + Variable_t *solvedIdx = eval(idx->operations.data, idx->operations.count, 1); + + if (solvedIdx->variableType != IntClass) { + SCRIPT_FATAL_ERR("Index is not an integer"); + } + + Variable_t* res = callMemberFunctionDirect(var, "get", &solvedIdx, 1); + removePendingReference(solvedIdx); + return res; + } + + SCRIPT_FATAL_ERR("???"); +} + +Variable_t* genericCallDirect(Variable_t* var, Variable_t** args, u8 len) { + if (var->variableType != FunctionClass){ + SCRIPT_FATAL_ERR("Call on non function class"); + } + + if (var->function.builtIn) { + for (u32 i = 0; i < var->function.len; i++) { + if (var->function.builtInPtr[i].argCount == len || var->function.builtInPtr[i].argCount == VARARGCOUNT) { + int valid = 1; + if (var->function.builtInPtr[i].argCount != VARARGCOUNT) { + for (u32 j = 0; j < var->function.builtInPtr[i].argCount; j++) { + if (var->function.builtInPtr[i].argTypes[j] != args[j]->variableType && var->function.builtInPtr[i].argTypes[j] != VARARGCOUNT) { + valid = 0; + break; + } + } + } + + if (valid) { + return var->function.builtInPtr[i].func(var->function.origin, args, len); + } + } + } + } + else { + Variable_t *ret = eval(var->function.function.operations.data, var->function.function.operations.count, 1); + if (ret == NULL) + return NULL; + removePendingReference(ret); + return &emptyClass; + } + + SCRIPT_FATAL_ERR("Arguments do not match function defenition(s)"); +} + +Variable_t* genericCall(Variable_t* var, CallArgs_t* ref) { + if (var->variableType != FunctionClass){ + SCRIPT_FATAL_ERR("Call on non function class"); + } + Function_t* f = ref->extra; + if (var->function.builtIn) { + if (f->operations.count == 0) { + return genericCallDirect(var, NULL, 0); + } + else { + //Vector_t argsHolder = newVec(sizeof(Variable_t*), 1); + Variable_t** argsHolder = NULL; + if (var->function.builtInPtr->argCount != 0) + argsHolder = malloc(sizeof(Variable_t*) * var->function.builtInPtr->argCount); + int argCount = 0; + int lasti = 0; + Operator_t* ops = f->operations.data; + + int tooManyArgs = 0; + + // Loops trough the function to get all args out + for (int i = 0; i < f->operations.count; i++) { + if (ops[i].token == EquationSeperator || i + 1 == f->operations.count) { + if (i + 1 == f->operations.count) + i++; + + if (argCount == var->function.builtInPtr->argCount) { + tooManyArgs = 1; + break; + } + + if (var->function.firstArgAsFunction && argCount == 0) { + Function_t f = { .operations = vecFromArray(&ops[lasti], i - lasti, sizeof(Operator_t)) }; + Variable_t var = newFunctionVariable(createFunctionClass(f, NULL)); + var.reference = 1; + Variable_t* varPtr = copyVariableToPtr(var); + //vecAdd(&argsHolder, varPtr); + argsHolder[argCount++] = varPtr; + } + else { + Variable_t* var = eval(&ops[lasti], i - lasti, 1); + if (var == NULL) + return NULL; // maybe free first? + + //vecAdd(&argsHolder, var); + argsHolder[argCount++] = var; + } + + lasti = i + 1; + } + } + + Variable_t* res = NULL; + + if (!tooManyArgs) + res = genericCallDirect(var, argsHolder, argCount); + else { + SCRIPT_FATAL_ERR("Too many args provided (got more than %d)", argCount); + } + + + for (int i = 0; i < argCount; i++) + removePendingReference(argsHolder[i]); + + //vecForEach(Variable_t**, tofree, (&argsHolder)) + // removePendingReference(*tofree); + + FREE(argsHolder); + + return res; + } + } + else { + if (f->operations.count){ + if (eval(f->operations.data, f->operations.count, 0) == NULL) + return NULL; + } + + if (eval(var->function.function.operations.data, var->function.function.operations.count, 0) == NULL) + return NULL; + + return &emptyClass; + } +} + +// TODO: add staticStorage +Variable_t getGenericFunctionMember(Variable_t* var, char* memberName, ClassFunctionTableEntry_t* entries, u8 len) { + Variable_t newVar = {.readOnly = 1, .variableType = FunctionClass}; + newVar.function.origin = var; + newVar.function.builtIn = 1; + for (u32 i = 0; i < len; i++) { + if (!strcmp(entries[i].name, memberName)) { + newVar.function.builtInPtr = &entries[i]; + + u32 j = i; + for (; j < len && !strcmp(entries[j].name, memberName); j++); + newVar.function.len = j - i; + + return newVar; + } + } + + return (Variable_t){ 0 }; +} + +Variable_t* callMemberFunction(Variable_t* var, char* memberName, CallArgs_t* args) { + for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) { + if (var->variableType == memberGetters[i].classType) { + Variable_t funcRef = memberGetters[i].func(var, memberName); + if (funcRef.variableType == None) { + SCRIPT_FATAL_ERR("Did not find member '%s'", memberName); + } + + Variable_t* ptr = &funcRef; + if (funcRef.variableType == ReferenceType) { + ptr = funcRef.referenceType; + } + + return genericCall(ptr, args); + } + } + + SCRIPT_FATAL_ERR("Could not find function table for given type"); +} + +Variable_t* callMemberFunctionDirect(Variable_t* var, char* memberName, Variable_t** args, u8 argsLen) { + for (u32 i = 0; i < ARRAY_SIZE(memberGetters); i++) { + if (var->variableType == memberGetters[i].classType) { + Variable_t funcRef = memberGetters[i].func(var, memberName); + if (funcRef.variableType == None) { + SCRIPT_FATAL_ERR("Did not find member '%s'", memberName); + } + + Variable_t* ptr = &funcRef; + if (funcRef.variableType == ReferenceType) { + ptr = funcRef.referenceType; + } + + return genericCallDirect(ptr, args, argsLen); + } + } + + SCRIPT_FATAL_ERR("Could not find function table for given type"); +} + +void freeVariableInternal(Variable_t* referencedTarget) { + switch (referencedTarget->variableType) { + case StringClass: + FREE(referencedTarget->string.value); + break; + case StringArrayClass: + vecForEach(char**, stringsInArray, (&referencedTarget->solvedArray.vector)) { + FREE(*stringsInArray); + } + case ByteArrayClass: + case IntArrayClass: + vecFree(referencedTarget->solvedArray.vector); + break; + case DictionaryClass:; + vecForEach(Dict_t*, dict, (&referencedTarget->dictionary.vector)) { + modReference(dict->var, 0); + free(dict->name); + } + FREE(referencedTarget->dictionary.vector.data); + break; + case SaveClass:; + #ifndef WIN32 + save_free_contexts(&referencedTarget->save->saveCtx); + f_close(&referencedTarget->save->saveFile); + FREE(referencedTarget->save); + #endif // !WIN32 + break; + } +} + +void freeVariable(Variable_t** target) { + // Add specific freeing logic here + Variable_t* referencedTarget = *target; + + if (!referencedTarget->reference) { + freeVariableInternal(referencedTarget); + } + + + FREE(referencedTarget); + *target = NULL; +} \ No newline at end of file diff --git a/source/script/genericClass.h b/source/script/genericClass.h new file mode 100644 index 0000000..0f9fb4f --- /dev/null +++ b/source/script/genericClass.h @@ -0,0 +1,25 @@ +#pragma once +#include "model.h" + +Variable_t* copyVariableToPtr(Variable_t var); + +#define VARARGCOUNT 255 + +#define ClassFunction(name) Variable_t* name(Variable_t* caller, Variable_t** args, u8 argsLen) + + +typedef Variable_t (*getMemberFunction)(Variable_t*, char*); + +typedef struct { + u8 classType; + getMemberFunction func; +} MemberGetters_t; + +Variable_t getGenericFunctionMember(Variable_t* var, char* memberName, ClassFunctionTableEntry_t* entries, u8 len); +Variable_t* genericGet(Variable_t* var, CallArgs_t* ref); +Variable_t* genericCallDirect(Variable_t* var, Variable_t** args, u8 len); +Variable_t* callMemberFunctionDirect(Variable_t* var, char* memberName, Variable_t** args, u8 argsLen); +Variable_t* genericCall(Variable_t* var, CallArgs_t* ref); +void freeVariable(Variable_t** target); +Variable_t* callMemberFunction(Variable_t* var, char* memberName, CallArgs_t* args); +void freeVariableInternal(Variable_t* referencedTarget); \ No newline at end of file diff --git a/source/script/intClass.c b/source/script/intClass.c new file mode 100644 index 0000000..b569832 --- /dev/null +++ b/source/script/intClass.c @@ -0,0 +1,91 @@ +#include "intClass.h" +#include "StringClass.h" +#include "compat.h" +#include +#include +#ifndef WIN32 +#include +#endif + +IntClass_t createIntClass(s64 in) { + IntClass_t a = { in }; + return a; +} + +Variable_t newIntVariable(s64 x) { + // Integers are always read-only + Variable_t var = { .variableType = IntClass, .readOnly = 1, .integer = createIntClass(x) }; + return var; +} + +ClassFunction(printIntVariable) { + IntClass_t* a = &caller->integer; + gfx_printf("%d", (int)a->value); + return &emptyClass; +} + +ClassFunction(intToStr) { +#ifndef WIN32 + char buff[64] = { 0 }; + s_printf(buff, "%d", getIntValue(caller)); + return newStringVariablePtr(CpyStr(buff), 0, 1); +#else + return newIntVariablePtr(0); +#endif // !WIN32 +} + +#define IntOpFunction(name, op) ClassFunction(name) { s64 i1 = getIntValue(caller); s64 i2 = getIntValue(*args); return newIntVariablePtr((i1 op i2)); } + +IntOpFunction(addInt, +) +IntOpFunction(minusInt, -) +IntOpFunction(multInt, *) +IntOpFunction(divInt, /) +IntOpFunction(modInt, %) +IntOpFunction(smallerInt, <) +IntOpFunction(biggerInt, >) +IntOpFunction(smallerEqInt, <=) +IntOpFunction(biggerEqInt, >=) +IntOpFunction(eqInt, ==) +IntOpFunction(notEqInt, !=) +IntOpFunction(logicAndInt, &&) +IntOpFunction(logicOrInt, ||) +IntOpFunction(andInt, &) +IntOpFunction(orInt, |) +IntOpFunction(bitshiftLeftInt, <<) +IntOpFunction(bitshiftRightInt, >>) + +ClassFunction(notInt) { + return newIntVariablePtr(!(getIntValue(caller))); +} + +u8 oneVarArgInt[] = { VARARGCOUNT }; +u8 oneIntArgInt[] = { IntClass }; + +#define IntOpFunctionEntry(opName, functionName) {opName, functionName, 1, oneIntArgInt} + +ClassFunctionTableEntry_t intFunctions[] = { + {"print", printIntVariable, 0, 0}, + {"not", notInt, 0, 0}, + {"str", intToStr, 0, 0}, + IntOpFunctionEntry("+", addInt), + IntOpFunctionEntry("-", minusInt), + IntOpFunctionEntry("*", multInt), + IntOpFunctionEntry("/", divInt), + IntOpFunctionEntry("%", modInt), + IntOpFunctionEntry("<", smallerInt), + IntOpFunctionEntry(">", biggerInt), + IntOpFunctionEntry("<=", smallerEqInt), + IntOpFunctionEntry(">=", biggerEqInt), + IntOpFunctionEntry("==", eqInt), + IntOpFunctionEntry("!=", notEqInt), + IntOpFunctionEntry("&&", logicAndInt), + IntOpFunctionEntry("||", logicOrInt), + IntOpFunctionEntry("&", andInt), + IntOpFunctionEntry("|", orInt), + IntOpFunctionEntry("<<", bitshiftLeftInt), + IntOpFunctionEntry(">>", bitshiftRightInt), +}; + +Variable_t getIntegerMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, intFunctions, ARRAY_SIZE(intFunctions)); +} \ No newline at end of file diff --git a/source/script/intClass.h b/source/script/intClass.h new file mode 100644 index 0000000..c8138f1 --- /dev/null +++ b/source/script/intClass.h @@ -0,0 +1,13 @@ +#pragma once +#include "model.h" +#include "genericClass.h" + + + +#define getIntValue(var) (var)->integer.value + +IntClass_t createIntClass(s64 in); + +Variable_t newIntVariable(s64 x); +#define newIntVariablePtr(x) copyVariableToPtr(newIntVariable(x)) +Variable_t getIntegerMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/lexer.c b/source/script/lexer.c deleted file mode 100644 index 3bc203a..0000000 --- a/source/script/lexer.c +++ /dev/null @@ -1,308 +0,0 @@ -#include "lexer.h" -#include "types.h" -#include "args.h" -#include - -static inline int isValidWord(char c) { - char r = c | 0x20; - return ((r >= 'a' && r <= 'z') || c == '_'); -} - -static inline int isValidNum(char c) { - return (c >= '0' && c <= '9'); -} - -static inline int isValidVar(char c) { - return (isValidWord(c) || isValidNum(c)); -} - -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}) - -typedef struct { - u8 tokenC; - u8 tokenN; -} lexarTranslation_t; - -lexarTranslation_t lexarTranslations[] = { - {'}', RCBracket}, - {',', Seperator}, - {'+', Plus}, - {'-', Minus}, - {'*', Multiply}, - {'/', Division}, - {'%', Mod}, - {'!', Not}, - {':', Selector}, - {')', RBracket}, - {']', RSBracket}, - {'(', LBracket}, - {'{', LCBracket}, - {'=', Equal}, - {'[', LSBracket}, - {'<', Smaller}, - {'>', Bigger}, - {'\0', 0}, -}; - -/* - Should we make vars with next char being '(' a function and vars with an equals (or [x] wait how are we gonna spot that) after it to be an assignmentVar -*/ - -char lexarDebugGetTokenC(u8 tokenN) { - for (int i = 0; lexarTranslations[i].tokenC; i++) { - if (lexarTranslations[i].tokenN == tokenN) { - return lexarTranslations[i].tokenC; - } - } - - if (tokenN == EquationSeperator) - return ';'; - - return '?'; -} - -/* -* !! we need to remake this -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); -} -*/ - -void lexarVectorClear(Vector_t *v){ - vecPDefArray(lexarToken_t*, entries, v); - - for (int i = 0; i < v->count; i++){ - if (entries[i].token != IntLit && entries[i].text != NULL){ - free(entries[i].text); - } - } - - vecFreePtr(v); -} - -#define ELIFC(c) else if (*in == c) - -Vector_t runLexer(const char* in, u32 len) { - const char *start = in; - Vector_t vec = newVec(sizeof(lexarToken_t), 16); - // store last var for re-assignment - // var -> func if next obj is '(' - // var -> assignment if next obj is '=' - // var -> arrassignment if next obj is '[' and before '=' is ']' - // maybe measure len between ( ) and [ ], so this doesn't have to be done during runtime? - // We also have to support (()). maybe if '(' set indent level, then if ')' minus indent level, set len. indent level contains {u8 level, u16 token, u16 startoffset} - - u32 lastAssignment = 0; - - while ((in - start) < len) { - lexarToken_t* lx = vecGetArray(lexarToken_t*, vec); - - if ((lx[vec.count - 2].token == StrLit || lx[vec.count - 2].token == IntLit || lx[vec.count - 2].token == Variable || lx[vec.count - 2].token == RSBracket || lx[vec.count - 2].token == RBracket) - && (lx[vec.count - 1].token == Variable || lx[vec.count - 1].token == LCBracket || lx[vec.count - 1].token == RCBracket)) { - if (!(lx[lastAssignment].token == ArrayVariableAssignment && lx[vec.count - 1].token == Variable && lx[vec.count - 2].token == RSBracket)) { - lexarToken_t holder = lx[vec.count - 1]; - lx[vec.count - 1] = makeLexarToken(EquationSeperator, 0); - vecAddElement(&vec, holder); - lx = vecGetArray(lexarToken_t*, vec); - } - } - - if (isValidWord(*in)) { - char* startWord = in; - in++; - while (isValidVar(*in)) - in++; - - vecAddElement(&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; - - vecAddElement(&vec, makeLexarToken(IntLit, parse)); - continue; - } - ELIFC('(') { - if (lx[vec.count - 1].token == Variable) - lx[vec.count - 1].token = Function; - - vecAddElement(&vec, makeLexarToken(LBracket, 0)); - } - ELIFC('[') { - if (lx[vec.count - 1].token == Variable) - lx[vec.count - 1].token = ArrayVariable; - - vecAddElement(&vec, makeLexarToken(LSBracket, 0)); - } - ELIFC('=') { // Do we need to keep = if the vars are assignments anyway? - if (in[1] == '='){ - vecAddElement(&vec, makeLexarToken(EqualEqual, 0)); - in++; - continue; - } - - if (lx[vec.count - 1].token == Variable) - lx[vec.count - 1].token = VariableAssignment; - - else if (lx[vec.count - 1].token == RSBracket) { - int back = 1; - while (lx[vec.count - back].token != ArrayVariable) { - back++; - if (vec.count - back < 0) - break; // major error - } - if (lx[vec.count - back].token == ArrayVariable) { - lx[vec.count - back].token = ArrayVariableAssignment; - lastAssignment = vec.count - back; - in++; - continue; - } - } - lastAssignment = 0; - } - ELIFC('{') { - if (lx[vec.count - 1].token == VariableAssignment) { - lx[vec.count - 1].token = FunctionAssignment; - } - vecAddElement(&vec, makeLexarToken(LCBracket, 0)); - } - ELIFC('"') { - char* startStr = ++in; - int len = 0; - while (*in != '"') { - in++; - } - len = in - startStr; - - char* storage = malloc(len + 1); - - int pos = 0; - for (int i = 0; i < len; i++) { - if (startStr[i] == '\\') { - if (startStr[i + 1] == 'n') { - storage[pos++] = '\n'; - i++; - continue; - } - - if (startStr[i + 1] == 'r') { - storage[pos++] = '\r'; - i++; - continue; - } - } - storage[pos++] = startStr[i]; - } - storage[pos] = '\0'; - - vecAddElement(&vec, makeLexarToken(StrLit, storage)); - } - ELIFC('#') { - while (*in != '\n') - in++; - } - ELIFC('&') { - if (in[1] == '&') { - vecAddElement(&vec, makeLexarToken(LogicAND, 0)); - in++; - } - else { - vecAddElement(&vec, makeLexarToken(AND, 0)); - } - } - ELIFC('|') { - if (in[1] == '|') { - vecAddElement(&vec, makeLexarToken(LogicOR, 0)); - in++; - } - else { - vecAddElement(&vec, makeLexarToken(OR, 0)); - } - } - ELIFC('>'){ - if (in[1] == '>'){ - vecAddElement(&vec, makeLexarToken(BitShiftRight, 0)); - in++; - } - else { - int a = (in[1] == '=') ? 1 : 0; - vecAddElement(&vec, makeLexarToken(Bigger, 0)); - in += a; - } - - } - ELIFC('<'){ - if (in[1] == '<'){ - vecAddElement(&vec, makeLexarToken(BitShiftLeft, 0)); - in++; - } - else { - int a = (in[1] == '=') ? 1 : 0; - vecAddElement(&vec, makeLexarToken(Smaller + a, 0)); - in += a; - } - - } - else { - 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) - vecAddElement(&vec, makeLexarToken(val, 0)); - - continue; - } - in++; - } - - lexarToken_t* lx = vecGetArray(lexarToken_t*, vec); - if ((lx[vec.count - 2].token == StrLit || lx[vec.count - 2].token == IntLit || lx[vec.count - 2].token == Variable || lx[vec.count - 2].token == RSBracket || lx[vec.count - 2].token == RBracket) - && (lx[vec.count - 1].token == Variable || lx[vec.count - 1].token == LCBracket || lx[vec.count - 1].token == RCBracket)) { - lexarToken_t holder = lx[vec.count - 1]; - lx[vec.count - 1] = makeLexarToken(EquationSeperator, 0); - vecAddElement(&vec, holder); - } - - vecAddElement(&vec, makeLexarToken(EquationSeperator, 0)); - return vec; -} \ No newline at end of file diff --git a/source/script/lexer.h b/source/script/lexer.h deleted file mode 100644 index a6b5d42..0000000 --- a/source/script/lexer.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "types.h" - -Vector_t runLexer(const char* in, u32 len); -char lexarDebugGetTokenC(u8 tokenN); -void lexarVectorClear(Vector_t *v); \ No newline at end of file diff --git a/source/script/model.c b/source/script/model.c new file mode 100644 index 0000000..bb39fb8 --- /dev/null +++ b/source/script/model.c @@ -0,0 +1,43 @@ +#include "model.h" +#include "compat.h" +#include "StringClass.h" +#include "intClass.h" + +TokenConvertion_t tokenConvertions[] = { + {SmallerEqual, "<="}, + {BiggerEqual, ">="}, + {NotEqual, "!="}, + {LogicAnd, "&&"}, + {LogicOr, "||"}, + {EqualEqual, "=="}, + + {BitShiftLeft, "<<"}, + {BitShiftRight, ">>"}, + + {Not, "!"}, + {Plus, "+"}, + {Equals, "="}, + {Minus, "-"}, + {Multiply, "*"}, + {Division, "/"}, + {Modulo, "%"}, + + {LeftSquareBracket, "["}, + {LeftCurlyBracket, "{"}, + {LeftBracket, "("}, + {RightSquareBracket, "]"}, + {RightCurlyBracket, "}"}, + {RightBracket, ")"}, + + {Smaller, "<"}, + {Bigger, ">"}, + + {And, "&"}, + {Or, "|"}, + {Dot, "."}, + {EquationSeperator, ","}, +}; + +u32 tokenConvertionCount = ARRAY_SIZE(tokenConvertions); + +Variable_t emptyClass = { .variableType = EmptyClass, .readOnly = 1, .reference = 1, .gcDoNotFree = 1 }; \ No newline at end of file diff --git a/source/script/model.h b/source/script/model.h new file mode 100644 index 0000000..e9d2b2e --- /dev/null +++ b/source/script/model.h @@ -0,0 +1,276 @@ +#pragma once + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef long long s64; + + + +#ifdef WIN32 +#pragma pack(1) +typedef struct { + void* data; + u32 capacity; + u32 count; + u8 elemSz; +} Vector_t; +#else +#include "../utils/vector.h" +#include +#pragma pack(1) +#endif + + +typedef enum { + None = 0, + IntClass, + FunctionClass, + StringClass, + ByteArrayClass, + StringArrayClass, + IntArrayClass, + UnresolvedArrayClass, + DictionaryClass, + EmptyClass, + SolvedArrayReferenceClass, + SaveClass, + ElseClass, + ReferenceType, +} VariableType_t; + +typedef enum { + Invalid = 0, + Variable, + BetweenBrackets, + Not, + + Plus, + Equals, + Minus, + Multiply, + Division, + Modulo, + + LeftSquareBracket, + LeftCurlyBracket, + LeftBracket, + RightSquareBracket, + RightCurlyBracket, + RightBracket, + + Smaller, + SmallerEqual, + Bigger, + BiggerEqual, + EqualEqual, + NotEqual, + LogicAnd, + LogicOr, + + BitShiftLeft, + BitShiftRight, + And, + Or, + + EquationSeperator, + Dot, + CallArgs, +} Token_t; + +typedef enum { + ActionGet = 0, + ActionSet, + ActionCall, +} ActionType_t; + +typedef enum { + ActionExtraNone = 0, + ActionExtraArrayIndex, + ActionExtraMemberName, + ActionExtraCallArgs, + ActionExtraCallArgsFunction +} ActionExtraType_t; + + +// Change to a Vector_t with Operator_t's +typedef struct { + Vector_t operations; // Operation_t. Equations seperated by EquationSep +} Function_t; + +struct _ClassFunctionTableEntry_t; +struct _Variable_t; + +typedef struct _FunctionClass_t { + union { + struct { + u8 builtIn : 1; + u8 firstArgAsFunction : 1; + }; + u8 unionFunctionOptions; + }; + union { + Function_t function; + struct { + struct _ClassFunctionTableEntry_t* builtInPtr; + struct _Variable_t* origin; + u8 len; + }; + + }; + +} FunctionClass_t; + +typedef enum { + ArrayType_Int = 0, + ArrayType_String, + ArrayType_Byte +} ArrayType_t; + +typedef struct { + union { + Vector_t vector; // vector of typeof(value) + struct { + struct _Variable_t* arrayClassReference; + u32 offset; + u32 len; + }; + }; + +} ArrayClass_t; + +typedef struct _UnsolvedArrayClass_t { + Vector_t operations; // Operator_t +} UnsolvedArrayClass_t; + +typedef struct _DictionaryClass_t { + Vector_t vector; // vector of typeof(Dict_t) +} DictionaryClass_t; + +typedef struct _IntClass_t { + s64 value; +} IntClass_t; + +typedef struct _StringClass_t { + char* value; + struct { + u8 free : 1; + }; +} StringClass_t; + +#ifndef WIN32 +typedef struct { + save_ctx_t saveCtx; + FIL saveFile; +} SaveClass_t; +#endif + +typedef struct _Variable_t { + //void* variable; + union { + FunctionClass_t function; + UnsolvedArrayClass_t unsolvedArray; + DictionaryClass_t dictionary; + IntClass_t integer; + StringClass_t string; + ArrayClass_t solvedArray; + #ifndef WIN32 + SaveClass_t *save; + #endif + struct _Variable_t* referenceType; + }; + union { + struct { + u8 variableType : 5; + u8 readOnly : 1; + u8 reference : 1; + u8 gcDoNotFree : 1; + }; + }; + u8 tagCount; +} Variable_t; + +typedef struct _CallArgs_t { + struct { + u8 action : 4; + u8 extraAction : 4; + }; + void* extra; // Function_t for arrayIdx, char* for member, Function_t for funcCall, Function_t x2 for funcCallArgs + struct _CallArgs_t* next; +} CallArgs_t; + +typedef struct { + void* data; + u32 len; +} Array_t; + +typedef Variable_t* (*ClassFunction)(Variable_t* caller, Variable_t** args, u8 argsLen); + +typedef struct _ClassFunctionTableEntry_t { + char* name; + ClassFunction func; + u8 argCount; + u8* argTypes; +} ClassFunctionTableEntry_t; + +typedef struct _VariableReference_t { + union { + struct { + u8 staticVariableSet : 1; + u8 staticVariableRef : 1; + u8 staticVariableType : 2; // 0 = ref, 1 = int, 2 = str, 3 = stdlib func + }; + u8 staticVariableOptionsUnion; + }; + + union { + Variable_t* staticVariable; + char* name; + Array_t betweenBrackets; + s64 integerType; + char* stringType; + struct { + ClassFunctionTableEntry_t* staticFunction; + u8 staticFunctionLen; + }; + }; +} VariableReference_t; + +//typedef Variable_t* (*classFunctionTable)(VariableReference_t*); + +typedef Variable_t* (*classFunctionTable)(char*, Variable_t*, VariableReference_t*, Vector_t*); + +typedef struct { + char* name; + Variable_t* var; +} Dict_t; + +typedef struct { + u8 token : 8; + char strToken[3]; +} TokenConvertion_t; + +extern TokenConvertion_t tokenConvertions[]; +extern u32 tokenConvertionCount; + +typedef struct { + Vector_t operations; // Operator_t +} Equation_t; + +typedef struct { + struct { + u8 token : 7; + u8 not : 1; + }; + union { + VariableReference_t variable; + CallArgs_t callArgs; + char* tokenStr; + s64 lineNumber; + }; + // probably should add u16 lineNum here +} Operator_t; + +extern Variable_t emptyClass; + +#pragma pack() \ No newline at end of file diff --git a/source/script/parser.c b/source/script/parser.c index 21104f0..9323ff2 100644 --- a/source/script/parser.c +++ b/source/script/parser.c @@ -1,130 +1,670 @@ -#include "args.h" -#include "types.h" -#include "variables.h" -#include "lexer.h" -#include "../gfx/gfx.h" -#include "../utils/utils.h" -#include -#include "../hid/hid.h" +#include "model.h" +#include "compat.h" +#include "compat.h" +#include "parser.h" +#include +#include -#define scriptResultCreate(resCode, nearToken) (scriptResult_t) {resCode, nearToken, 1} -#define scriptResultCreateLen(resCode, nearToken, len) (scriptResult_t) {resCode, nearToken, len} +#include "intClass.h" +#include "StringClass.h" +#include "unsolvedArrayClass.h" +#include "functionClass.h" -scriptResult_t runFunction(scriptCtx_t* ctx, u32 len) { - lexarToken_t* tokens = vecGetArray(lexarToken_t*, ctx->script); - Variable_t res = solveEquation(ctx, &tokens[ctx->startEquation], len, 1); +#include "scriptError.h" +#include "standardLibrary.h" - if (res.varType == ErrType) { - return scriptResultCreateLen(res.integerType, &tokens[ctx->startEquation], len); +#ifndef WIN32 +#include "../tegraexplorer/tconf.h" +#include +#endif + +static inline int isValidWord(char c) { + char r = c | 0x20; + return ((r >= 'a' && r <= 'z') || c == '_'); +} + +static inline int isValidNum(char c) { + return (c >= '0' && c <= '9'); +} + +static inline int isValidVar(char c) { + return (isValidWord(c) || isValidNum(c)); +} + +static inline int isValidHexNum(char c) { + char r = c | 0x20; + return (isValidNum(r) || (r >= 'a' && r <= 'f')); +} + +char* getTokenText(u8 token) { + for (u32 i = 0; i < tokenConvertionCount; i++) { + if (tokenConvertions[i].token == token) + return tokenConvertions[i].strToken; } - return scriptResultCreate(0, 0); + return NULL; } -#define RUNFUNCWITHPANIC(ctx, len) scriptResult_t res = runFunction(ctx, len); if (res.resCode != 0) return res +char* utils_copyStringSize(const char* in, int size) { + if (size > strlen(in) || size < 0) + size = strlen(in); -static inline int checkIfVar(u8 token) { - return (token == StrLit || token == IntLit || token == Variable || token == RSBracket || token == ArrayVariable); + char* out = calloc(size + 1, 1); + //strncpy(out, in, size); + if (size) + memcpy(out, in, size); + return out; } -scriptResult_t mainLoop(scriptCtx_t* ctx) { - ctx->startEquation = 0; - lexarToken_t* lexArr = vecGetArray(lexarToken_t*, ctx->script); - for (ctx->curPos = 0; ctx->curPos < ctx->script.count; ctx->curPos++) { - u32 i = ctx->curPos; - lexarToken_t curToken = lexArr[i]; +#define ELIFC(c) else if (*in == c) - if (curToken.token == EquationSeperator) { - RUNFUNCWITHPANIC(ctx, ctx->curPos - ctx->startEquation); - ctx->startEquation = ctx->curPos + 1; - } - else if (curToken.token == FunctionAssignment) { - setCurIndentInstruction(ctx, 1, 0, -1); - dict_t x = newDict(CpyStr(curToken.text), newVar(JumpType, 0, .integerType = ctx->curPos + 1)); - vecAddElement(&ctx->varDict, x); - } - else if (curToken.token == LCBracket) { - indentInstructor_t* ins = getCurIndentInstruction(ctx); - if (ins->active) { - if (ins->skip) { - int distance = distanceBetweenTokens(&lexArr[i + 1], ctx->script.count - i - 1, LCBracket, RCBracket); - if (distance < 0) - return scriptResultCreate(ERRSYNTAX, &lexArr[i]); - ctx->curPos += distance + 1; - } - else - ctx->indentIndex++; - } - else - return scriptResultCreate(ERRINACTIVEINDENT, &lexArr[i]); +Vector_t script; +s64 lineNumber; - ctx->startEquation = ctx->curPos + 1; - } - else if (curToken.token == RCBracket) { - ctx->indentIndex--; - indentInstructor_t* ins = getCurIndentInstruction(ctx); - if (ins->active && ins->jump) { - ctx->curPos = ins->jumpLoc - 1; - } - - ins->active = 0; - ctx->startEquation = ctx->curPos + 1; - } - } - - return scriptResultCreate(0, 0); -} - -void printToken(lexarToken_t* token) { - switch (token->token) { - case Variable: - case ArrayVariable: - case Function: - gfx_printf("%s", token->text); - break; - case FunctionAssignment: - case VariableAssignment: - case ArrayVariableAssignment: - gfx_printf("%s=", token->text); - break; - case StrLit: - //printf("%d: '%s'\n", vec.tokens[i].token, vec.tokens[i].text); - gfx_printf("\"%s\"", token->text); - break; - case IntLit: - //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; - } -} - -char *ErrorText[] = { - "Bad operator", - "Double not", - "Syntax err", - "Invalid type", - "No var", - "No func", - "Inactive indent", - "Div by 0", - "Func fail" +enum TokenType { + Token_Variable = 0, + Token_String, + Token_Int, + Token_Token, + Token_Err, + Token_Fatal_Err, }; -void printError(scriptResult_t res) { - if (res.resCode) { - if (res.resCode == ERRESCSCRIPT) - return; +typedef enum { + History_Function = 0, + History_Bracket, + History_Array, +} StackHistory_t; - gfx_printf("Error found! %s\nNear: ", ErrorText[res.resCode - 1]); - for (int i = 0; i < res.len; i++) { - printToken(&res.nearToken[i]); +char* end; + +u8 nextToken(char** inPtr, void** val) { + char* in = *inPtr; + u8 ret = Token_Err; + while (ret == Token_Err) { + if (*in == '#') { + if (!memcmp(in + 1, "REQUIRE ", 8)) { + if (!memcmp(in + 9, "VER ", 4)) { + char* verStart = in + 13; + char* verEnd = verStart; + + while (isValidNum(*verEnd) || *verEnd == '.') + verEnd++; + + u8 outdated = (verEnd - verStart != strlen(LP_VER)); + + if (!outdated){ + outdated = (memcmp(LP_VER, verStart, verEnd - verStart) < 0); + } + + if (outdated) { + printScriptError(SCRIPT_LEXER_FATAL, "Script requires a newer TegraExplorer version!"); + return Token_Fatal_Err; + } + } + else if (!memcmp(in + 9, "MINERVA", 7)) { + #ifdef WIN32 + u8 minervaEnabled = 0; + #else + u8 minervaEnabled = TConf.minervaEnabled; + #endif + if (!minervaEnabled) { + printScriptError(SCRIPT_LEXER_FATAL, "Extended memory required.\nPut the bootloader folder from hekate on your sd!"); + return Token_Fatal_Err; + } + } + else if (!memcmp(in + 9, "KEYS", 4)) { + #ifdef WIN32 + u8 gotKeys = 0; + #else + u8 gotKeys = TConf.keysDumped; + #endif + if (!gotKeys){ + printScriptError(SCRIPT_LEXER_FATAL, "Keys required.\nMake sure you're on the latest version of TegraExplorer!"); + return Token_Fatal_Err; + } + } + else if (!memcmp(in + 9, "SD", 2)) { + #ifdef WIN32 + u8 gotSd = 0; + #else + u8 gotSd = sd_mount(); + #endif + if (!gotSd){ + printScriptError(SCRIPT_LEXER_FATAL, "Sd required."); + return Token_Fatal_Err; + } + } + } + + while (*in && *in != '\n') + in++; + + lineNumber++; + scriptCurrentLine = lineNumber; + } + else if (isValidWord(*in)) { + char* startWord = in; + in++; + while (isValidVar(*in)) + in++; + + char* str = utils_copyStringSize(startWord, in - startWord); + + //gfx_printf("Variable: '%s'\n", str); + ret = Token_Variable; + *val = str; + break; + } + else if (isValidNum(*in) || (*in == '-' && isValidNum(in[1]))) { + s64 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; + + //gfx_printf("Integer: '%d'\n", parse); + ret = Token_Int; + + s64* parsePersistent = malloc(sizeof(s64)); + *parsePersistent = parse; + + *val = parsePersistent; + break; + } + ELIFC('"') { + char* startStr = ++in; + int len = 0; + while (*in != '"') { + in++; + } + len = in - startStr; + + char* storage = malloc(len + 1); + + int pos = 0; + for (int i = 0; i < len; i++) { + if (startStr[i] == '\\') { + if (startStr[i + 1] == 'n') { + storage[pos++] = '\n'; + i++; + continue; + } + + if (startStr[i + 1] == 'r') { + storage[pos++] = '\r'; + i++; + continue; + } + } + storage[pos++] = startStr[i]; + } + storage[pos] = '\0'; + + //gfx_printf("String: '%s'\n", storage); + ret = Token_String; + *val = storage; + } + else if (*in == '\0' || in > end) { + *inPtr = in; + return ret; + } + else if (*in == '\n'){ + lineNumber++; + scriptCurrentLine = lineNumber; + } + else { + for (u32 i = 0; i < tokenConvertionCount; i++) { + TokenConvertion_t t = tokenConvertions[i]; + if (!memcmp(t.strToken, in, (t.strToken[1] == '\0') ? 1 : 2)) { + //gfx_printf("Token: '%s'\n", t.strToken); + ret = Token_Token; + *val = t.token; + + if (t.strToken[1] != '\0') + in++; + + break; + } + } + } + in++; + } + + *inPtr = in; + return ret; +} + +#define CreateVariableReferenceStatic(var) VariableReference_t reference = { .staticVariable = var, .staticVariableSet = 1, .staticVariableRef = 1 } +#define CreateVariableReferenceStr(str) VariableReference_t reference = { .name = str } + +void setNextActionOperator(Vector_t *opHolder, ActionType_t action, ActionExtraType_t actionExtra, void *extra) { + Operator_t* ops = opHolder->data; + Operator_t* lastOp = &ops[opHolder->count - 1]; + + if (lastOp->token == CallArgs) { + CallArgs_t* last = &lastOp->callArgs; + for (; last->next != NULL; last = last->next); + last->next = calloc(sizeof(CallArgs_t), 1); + last->next->action = action; + last->next->extra = extra; + last->next->extraAction = actionExtra; + } + else { + Operator_t newOp = { .token = CallArgs }; + newOp.callArgs.action = action; + newOp.callArgs.extra = extra; + newOp.callArgs.extraAction = actionExtra; + vecAdd(opHolder, newOp); + } +} + +CallArgs_t* getLastRef(CallArgs_t* ref) { + for (; ref->next != NULL; ref = ref->next); + return ref; +} + +int isLastVarSet(Operator_t* opHolder) { + return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionSet); +} + +int isLastVarCall(Operator_t* opHolder) { + return (opHolder->token == CallArgs && getLastRef(&opHolder->callArgs)->action == ActionCall); +} + +ParserRet_t parseScript(char* in, u32 len) { + Vector_t functionStack; // Function_t + Vector_t stackHistoryHolder; // StaticHistory_t + Vector_t staticVariableHolder; // Variable_t + + functionStack = newVec(sizeof(Function_t), 0); + Function_t firstFunction = createEmptyFunction(); + vecAdd(&functionStack, firstFunction); + + staticVariableHolder = newVec(sizeof(Variable_t), 0); + + stackHistoryHolder = newVec(sizeof(StackHistory_t), 1); + StackHistory_t firstHistory = History_Function; + vecAdd(&stackHistoryHolder, firstHistory); + u8 notNext = 0; + lineNumber = 1; + scriptCurrentLine = 1; + end = in + len; + + while (*in && in <= end) { + Function_t* lastFunc = getStackEntry(&functionStack); + StackHistory_t* lastHistory = getStackEntry(&stackHistoryHolder); + + Operator_t* lastOp = NULL; + + if (lastFunc) { + lastOp = getStackEntry(&lastFunc->operations); } - gfx_printf("\nPress any key to exit"); - hidWait(); + void* var = NULL; + u8 tokenType = nextToken(&in, &var); + + if (tokenType == Token_Err) + break; + + if (tokenType == Token_Fatal_Err) + return (ParserRet_t) { 0 }; + + Operator_t op = { .token = Variable }; + + if (tokenType >= Token_Variable && tokenType <= Token_Int && lastOp) { + if (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp))) { + op.token = EquationSeperator; + op.lineNumber = lineNumber; + vecAdd(&lastFunc->operations, op); + op.token = Variable; + } + } + + if (tokenType == Token_Variable) { + u8 stdLen = 0; + ClassFunctionTableEntry_t* cfte = searchStdLib(var, &stdLen); + + if (cfte == NULL) { + CreateVariableReferenceStr(var); + op.variable = reference; + } + else { + VariableReference_t reference = { .staticVariableType = 3, .staticFunction = cfte, .staticFunctionLen = stdLen }; + op.variable = reference; + } + } + else if (tokenType == Token_Int) { + /* + Variable_t a = newIntVariable(*((s64*)var)); + a.gcDoNotFree = 1; + free(var); + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + */ + + VariableReference_t reference = { .staticVariableType = 1, .integerType = *((s64*)var) }; + op.variable = reference; + free(var); + } + else if (tokenType == Token_String) { + /* + Variable_t a = newStringVariable(var, 1, 1); + a.gcDoNotFree = 1; + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + */ + + VariableReference_t reference = { .staticVariableType = 2, .stringType = var }; + op.variable = reference; + } + else if (tokenType == Token_Token) { + u8 token = (u8)var; + + if (token == Equals && lastOp) { + if (lastOp->token == Variable) { + if (lastOp->variable.staticVariableSet) { + SCRIPT_PARSER_ERR("Trying to assign to a static variable"); + } + else { + setNextActionOperator(&lastFunc->operations, ActionSet, ActionExtraNone, NULL); + continue; + } + } + else if (lastOp->token == CallArgs) { + CallArgs_t* last = getLastRef(&lastOp->callArgs); + last->action = ActionSet; + continue; + } + else { + SCRIPT_PARSER_ERR("Trying to assign to non-object"); + } + } + else if (token == LeftCurlyBracket) { + Function_t templateFunction = createEmptyFunction(); + vecAdd(&functionStack, templateFunction); + + StackHistory_t functionHistory = History_Function; + vecAdd(&stackHistoryHolder, functionHistory); + continue; + } + else if (token == RightCurlyBracket) { + if (stackHistoryHolder.count != 1 && *lastHistory == History_Function) { + Function_t *popFunc = popStackEntry(&functionStack); + popStackEntry(&stackHistoryHolder); + + lastFunc = getStackEntry(&functionStack); + + if (lastFunc) { // TODO: Add check for null deref + lastOp = getStackEntry(&lastFunc->operations); + } + + if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) { + if (lastOp->token == Variable) { + SCRIPT_PARSER_ERR("GET variable before {}"); + continue; + } + + // Set last arg to {} + CallArgs_t* lastCall = getLastRef(&lastOp->callArgs); + if (lastCall->extraAction == ActionExtraCallArgs) { + Function_t* funcArgs = lastCall->extra; + if (funcArgs->operations.count != 0) { + op.token = EquationSeperator; + op.lineNumber = lineNumber; + vecAdd(&funcArgs->operations, op); + op.token = Variable; + } + + Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL)); + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + op.variable = reference; + vecAdd(&funcArgs->operations, op); + continue; + } + } + + + + Variable_t a = newFunctionVariable(createFunctionClass(*popFunc, NULL)); + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + op.variable = reference; + vecAdd(&lastFunc->operations, op); + + op.token = EquationSeperator; + op.lineNumber = lineNumber; + } + else { + SCRIPT_PARSER_ERR("Stack count is 1 or state is not a function"); + } + } + else if (token == Dot) { + if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) { + tokenType = nextToken(&in, &var); + if (tokenType != Token_Variable) { + SCRIPT_PARSER_ERR("Acessing member with non-dynamic token"); + } + else { + setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraMemberName, var); + continue; + } + } + else { + SCRIPT_PARSER_ERR("Member access on non-variable"); + } + } + else if (token == LeftBracket) { + Function_t templateFunction = createEmptyFunction(); + vecAdd(&functionStack, templateFunction); + + StackHistory_t functionHistory = History_Bracket; + vecAdd(&stackHistoryHolder, functionHistory); + continue; + } + else if (token == RightBracket) { + if (*lastHistory == History_Bracket) { + Function_t* bstack = popStackEntry(&functionStack); + popStackEntry(&stackHistoryHolder); + lastFunc = getStackEntry(&functionStack); + + if (lastFunc) { // TODO: Add check for null deref + lastOp = getStackEntry(&lastFunc->operations); + } + + if (lastOp && (lastOp->token == Variable || lastOp->token == BetweenBrackets || (lastOp->token == CallArgs && !isLastVarSet(lastOp) && !isLastVarCall(lastOp)))) { + Function_t* newBStack = malloc(sizeof(Function_t)); + *newBStack = *bstack; + setNextActionOperator(&lastFunc->operations, ActionCall, ActionExtraCallArgs, newBStack); // Maybe pass NULL if array is empty? + continue; + } + else { + if (lastOp && isLastVarCall(lastOp)) { + op.token = EquationSeperator; + vecAdd(&lastFunc->operations, op); + } + + if (!countTokens(bstack, EquationSeperator)) { + op.variable.betweenBrackets.data = bstack->operations.data; + op.variable.betweenBrackets.len = bstack->operations.count; + op.token = BetweenBrackets; + } + else { + SCRIPT_PARSER_ERR("Priority brackets can only contain 1 argument"); + } + + } + } + else { + SCRIPT_PARSER_ERR(") without ("); + } + } + else if (token == LeftSquareBracket) { + Function_t templateFunction = createEmptyFunction(); + vecAdd(&functionStack, templateFunction); + + StackHistory_t functionHistory = History_Array; + vecAdd(&stackHistoryHolder, functionHistory); + continue; + } + else if (token == RightSquareBracket) { + if (*lastHistory == History_Array) { + Function_t* astack = popStackEntry(&functionStack); + popStackEntry(&stackHistoryHolder); + lastFunc = getStackEntry(&functionStack); + + if (lastFunc) { // TODO: Add check for null deref + lastOp = getStackEntry(&lastFunc->operations); + } + + if (lastOp && (lastOp->token == Variable || (lastOp->token == CallArgs && !isLastVarSet(lastOp)))) { + if (!countTokens(astack, EquationSeperator)) { + Function_t* newAStack = malloc(sizeof(Function_t)); + *newAStack = *astack; + setNextActionOperator(&lastFunc->operations, ActionGet, ActionExtraArrayIndex, newAStack); + continue; + } + else { + // We're just assuming that it's a new array lol + + op.token = EquationSeperator; + vecAdd(&lastFunc->operations, op); + op.token = Variable; + + + Variable_t a = createUnsolvedArrayVariable(astack); + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + op.variable = reference; + + //gfx_printf("[FATAL] indexes cannot contain mutiple arguments"); + } + } + else { + // TODO: optimize output to a typed array, if possible + Variable_t a = createUnsolvedArrayVariable(astack); + vecAdd(&staticVariableHolder, a); + CreateVariableReferenceStatic((Variable_t*)(staticVariableHolder.count - 1)); + op.variable = reference; + /* + vecAdd(&lastFunc->operations, op); + op.token = EquationSeperator; + op.lineNumber = lineNumber; + */ + } + } + else { + SCRIPT_PARSER_ERR("] without ["); + } + } + else if (token == Not) { + notNext = !notNext; + continue; + } + else { + op.token = token; + + for (u32 i = 0; i < tokenConvertionCount; i++) { + if (token == tokenConvertions[i].token) { + op.tokenStr = tokenConvertions[i].strToken; + break; + } + } + } + } + + if (notNext) { + op.not = 1; + notNext = 0; + } + + vecAdd(&lastFunc->operations, op); + } + + if (functionStack.count != 1 || stackHistoryHolder.count != 1) { + SCRIPT_PARSER_ERR("There seems to be an open bracket somewhere. EOF reached"); + } + + + ParserRet_t parse = { .main = (*(Function_t*)getStackEntry(&functionStack)), .staticVarHolder = staticVariableHolder, .valid = 1 }; + + vecFree(functionStack); + vecFree(stackHistoryHolder); + scriptCurrentLine = 1; + return parse; +} + + +void exitFunction(Operator_t* start, u32 len) { + for (u32 i = 0; i < len; i++) { + if (start[i].token == Variable) { + if (start[i].variable.staticVariableOptionsUnion == 0) { + FREE(start[i].variable.name); + } + + if (start[i].variable.staticVariableType == 2) { + FREE(start[i].variable.stringType); + } + } + else if (start[i].token == BetweenBrackets) { + exitFunction(start[i].variable.betweenBrackets.data, start[i].variable.betweenBrackets.len); + FREE(start[i].variable.betweenBrackets.data); + } + else if (start[i].token == CallArgs) { + CallArgs_t* call = &start[i].callArgs; + + while (call != NULL) { + if (call->extraAction == ActionExtraArrayIndex) { + Function_t* f = call->extra; + exitFunction(f->operations.data, f->operations.count); + vecFree(f->operations); + FREE(f); + } + else if (call->extraAction == ActionExtraMemberName) { + FREE(call->extra); + } + else if (call->extraAction == ActionExtraCallArgs) { + Function_t* f = call->extra; + exitFunction(f->operations.data, f->operations.count); + vecFree(f->operations); + FREE(f); + } + + CallArgs_t* nextCall = call->next; + if (call != &start[i].callArgs) { + FREE(call); + } + + call = nextCall; + } + } + } +} + +void exitStaticVars(Vector_t* v) { + vecForEach(Variable_t*, staticVar, v) { + if (staticVar->variableType == FunctionClass) { + if (!staticVar->function.builtIn) { + exitFunction(staticVar->function.function.operations.data, staticVar->function.function.operations.count); + vecFree(staticVar->function.function.operations); + } + } + else { + freeVariableInternal(staticVar); + } } } \ No newline at end of file diff --git a/source/script/parser.h b/source/script/parser.h index 37c21be..51dc4d4 100644 --- a/source/script/parser.h +++ b/source/script/parser.h @@ -1,6 +1,16 @@ + #pragma once +#include "compat.h" -#include "types.h" +typedef struct { + Function_t main; + Vector_t staticVarHolder; + u8 valid; +} ParserRet_t; -scriptResult_t mainLoop(scriptCtx_t* ctx); -void printError(scriptResult_t res); \ No newline at end of file +#define SCRIPT_PARSER_ERR(message, ...) printScriptError(SCRIPT_PARSER_FATAL, message, ##__VA_ARGS__); return (ParserRet_t){0} + +void exitStaticVars(Vector_t* v); +void exitFunction(Operator_t* start, u32 len); +ParserRet_t parseScript(char* in, u32 len); +char* utils_copyStringSize(const char* in, int size); \ No newline at end of file diff --git a/source/script/saveClass.c b/source/script/saveClass.c new file mode 100644 index 0000000..eb7583a --- /dev/null +++ b/source/script/saveClass.c @@ -0,0 +1,108 @@ +#include "saveClass.h" +#include "compat.h" +#include "intClass.h" +#include "dictionaryClass.h" + +u8 oneStringArgSave[] = {StringClass}; +u8 oneStrOneByteArrayArgSave[] = {StringClass, ByteArrayClass}; + +ClassFunction(readFile){ + Variable_t *arg = (*args); + save_data_file_ctx_t dataArc; + if (!save_open_file(&caller->save->saveCtx, &dataArc, arg->string.value, OPEN_MODE_READ)) + return NULL; + + u64 totalSize; + save_data_file_get_size(&dataArc, &totalSize); + + u8 *buff = malloc(totalSize); + + save_data_file_read(&dataArc, &totalSize, 0, buff, totalSize); + + Variable_t a = {.variableType = ByteArrayClass}; + a.solvedArray.vector = vecFromArray(buff, totalSize, 1); + return copyVariableToPtr(a); +} + +ClassFunction(writeFile){ + Variable_t *arg = (*args); + save_data_file_ctx_t dataArc; + if (!save_open_file(&caller->save->saveCtx, &dataArc, arg->string.value, OPEN_MODE_WRITE)) + return newIntVariablePtr(1); + + u64 outBytes = 0; + if (!save_data_file_write(&dataArc, &outBytes, 0, args[1]->solvedArray.vector.data, args[1]->solvedArray.vector.count)){ + return newIntVariablePtr(3); + }; + + if (outBytes != args[1]->solvedArray.vector.count){ + return newIntVariablePtr(4); + } + + return newIntVariablePtr(0); +} + +ClassFunction(getFiles){ + Variable_t* resPtr = newIntVariablePtr(0); + Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 4)}; + addVariableToDict(&ret, "result", resPtr); + + save_data_directory_ctx_t ctx; + if (!save_open_directory(&caller->save->saveCtx, &ctx, "/", OPEN_DIR_MODE_ALL)){ + resPtr->integer.value = 1; + return copyVariableToPtr(ret); + } + + u64 entryCount = 0; + if (!save_data_directory_get_entry_count(&ctx, &entryCount)){ + resPtr->integer.value = 2; + return copyVariableToPtr(ret); + } + + directory_entry_t* entries = malloc(sizeof(directory_entry_t) * entryCount); + u64 entryCountDirRead = 0; + if (!save_data_directory_read(&ctx, &entryCountDirRead, entries, entryCount)){ + resPtr->integer.value = 3; + + return copyVariableToPtr(ret); + } + + Variable_t fileNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)}; + Variable_t dirNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)}; + Variable_t fileSizeArray = {.variableType = IntArrayClass, .solvedArray.vector = newVec(sizeof(s64), 0)}; + + for (int i = 0; i < entryCountDirRead; i++){ + char *add = CpyStr(entries[i].name); + if (entries[i].type == DIR_ENT_TYPE_FILE){ + vecAdd(&fileNamesArray.solvedArray.vector, add); + s64 fileSize = entries[i].size; + vecAdd(&fileSizeArray.solvedArray.vector, fileSize); + } + else { + vecAdd(&dirNamesArray.solvedArray.vector, add); + } + } + + free(entries); + + addVariableToDict(&ret, "files", copyVariableToPtr(fileNamesArray)); + addVariableToDict(&ret, "folders", copyVariableToPtr(dirNamesArray)); + addVariableToDict(&ret, "fileSizes", copyVariableToPtr(fileSizeArray)); + + return copyVariableToPtr(ret); +} + +ClassFunction(saveClassCommit){ + return newIntVariablePtr(!save_commit(&caller->save->saveCtx)); +} + +ClassFunctionTableEntry_t saveFunctions[] = { + {"read", readFile, 1, oneStringArgSave}, + {"write", writeFile, 2, oneStrOneByteArrayArgSave}, + //{"readdir", getFiles, 1, oneStringArgSave}, // Seems broken? + {"commit", saveClassCommit, 0, 0}, +}; + +Variable_t getSaveMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, saveFunctions, ARRAY_SIZE(saveFunctions)); +} \ No newline at end of file diff --git a/source/script/saveClass.h b/source/script/saveClass.h new file mode 100644 index 0000000..f17f99c --- /dev/null +++ b/source/script/saveClass.h @@ -0,0 +1,4 @@ +#include "model.h" +#include "genericClass.h" + +Variable_t getSaveMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/scriptError.c b/source/script/scriptError.c new file mode 100644 index 0000000..7feef5b --- /dev/null +++ b/source/script/scriptError.c @@ -0,0 +1,20 @@ +#include "scriptError.h" +#include "compat.h" +#include + +s64 scriptCurrentLine; +u8 scriptLastError = 0; + +void printScriptError(u8 errLevel, char* message, ...) { + va_list args; + scriptLastError = errLevel; + va_start(args, message); + gfx_printf("\n\n[%s] ", (errLevel == SCRIPT_FATAL) ? "FATAL" : (errLevel == SCRIPT_PARSER_FATAL) ? "PARSE_FATAL" : "WARN"); + gfx_vprintf(message, args); + if (errLevel < SCRIPT_WARN) + gfx_printf("\nError occured on or near line %d\n", (u32)scriptCurrentLine); + va_end(args); + #ifndef WIN32 + hidWait(); + #endif +} \ No newline at end of file diff --git a/source/script/scriptError.h b/source/script/scriptError.h new file mode 100644 index 0000000..8b4d74f --- /dev/null +++ b/source/script/scriptError.h @@ -0,0 +1,19 @@ +#pragma once +#include "model.h" + +enum { + SCRIPT_FATAL = 0, + SCRIPT_PARSER_FATAL, + SCRIPT_WARN, + SCRIPT_LEXER_FATAL, + SCRIPT_BREAK, +}; + +extern s64 scriptCurrentLine; +extern u8 scriptLastError; + +void printScriptError(u8 errLevel, char* message, ...); + +#define SCRIPT_FATAL_ERR(message, ...) printScriptError(SCRIPT_FATAL, message, ##__VA_ARGS__); return NULL +#define SCRIPT_WARN_ERR(message, ...) printScriptError(SCRIPT_WARN, message, ##__VA_ARGS__) + diff --git a/source/script/standardLibrary.c b/source/script/standardLibrary.c new file mode 100644 index 0000000..48b4f56 --- /dev/null +++ b/source/script/standardLibrary.c @@ -0,0 +1,601 @@ +#include "model.h" +#include "compat.h" +#include "genericClass.h" +#include "eval.h" +#include "garbageCollector.h" +#include "intClass.h" +#include "standardLibrary.h" +#include "scriptError.h" +#include +#include "dictionaryClass.h" +#include "StringClass.h" + +#ifndef WIN32 +#include "../storage/mountmanager.h" +#include "../keys/keys.h" +#include "../fs/readers/folderReader.h" +#include "../fs/fscopy.h" +#include +#include "../keys/nca.h" +#include "../hid/hid.h" +#include "../gfx/menu.h" +#include "../gfx/gfxutils.h" +#include "../tegraexplorer/tconf.h" +#include "../storage/emummc.h" +#include +#include "../fs/fsutils.h" +#include +#include "../storage/emmcfile.h" +#include +#endif +// Takes [int, function]. Returns elseable. +ClassFunction(stdIf) { + s64 value = getIntValue(args[0]); + + if (value) { + Variable_t* res = genericCallDirect(args[1], NULL, 0); + if (res == NULL) + return NULL; + + removePendingReference(res); + } + + Variable_t* ret = newIntVariablePtr(value); + ret->variableType = ElseClass; + + return ret; +} + +// Takes [function, function]. Returns empty. Works by evaling the first function and running the 2nd if true. +ClassFunction(stdWhile) { + Variable_t* result = eval(args[0]->function.function.operations.data, args[0]->function.function.operations.count, 1); + if (result == NULL || result->variableType != IntClass) + return NULL; + + while (result->integer.value) { + removePendingReference(result); + Variable_t* res = genericCallDirect(args[1], NULL, 0); + if (res == NULL) { + if (scriptLastError == SCRIPT_BREAK) { + break; + } + else { + return NULL; + } + } + + removePendingReference(res); + + result = eval(args[0]->function.function.operations.data, args[0]->function.function.operations.count, 1); + if (result == NULL || result->variableType != IntClass) + return NULL; + } + + removePendingReference(result); + + return &emptyClass; +} + +// Takes [???]. Returns empty. Works by calling .print on every argument +ClassFunction(stdPrint) { + for (int i = 0; i < argsLen; i++) { + Variable_t* res = callMemberFunctionDirect(args[i], "print", NULL, 0); + if (res == NULL) + return NULL; + if (i + 1 != argsLen) + gfx_putc(' '); + } + + + return &emptyClass; +} + +// Takes [???]. Returns empty. Calls stdPrint +ClassFunction(stdPrintLn) { + stdPrint(caller, args, argsLen); + gfx_printf("\n"); + return &emptyClass; +} + +// Takes none. Returns none. Returning NULL will cause a cascade of errors and will exit runtime +ClassFunction(stdExit) { + return NULL; +} + +// Takes none. Returns none. See stdExit. stdWhile and array.foreach look for SCRIPT_BREAK and break when seen. +ClassFunction(stdBreak) { + scriptLastError = SCRIPT_BREAK; + return NULL; +} + +// Takes none. Returns empty dictionary. +ClassFunction(stdDict) { + Variable_t a = { 0 }; + a.variableType = DictionaryClass; + a.dictionary.vector = newVec(sizeof(Dict_t), 0); + return copyVariableToPtr(a); +} + +#ifndef WIN32 + + +int mountMmc(u8 mmc, char *part){ + if (connectMMC(mmc)) + return 1; + + if (mountMMCPart(part).err) + return 1; + + return 0; +} + +// Takes [str]. Returns int (0=success). str=partition to mount +ClassFunction(stdMountSysmmc){ + return newIntVariablePtr(mountMmc(MMC_CONN_EMMC, args[0]->string.value)); +} + +ClassFunction(stdMountEmummc){ + if (!emu_cfg.enabled){ + SCRIPT_FATAL_ERR("emummc is not enabled"); + } + + return newIntVariablePtr(mountMmc(MMC_CONN_EMUMMC, args[0]->string.value)); +} + +// Takes [str]. Returns int (0=success) str=path to save +ClassFunction(stdMountSave){ + + Variable_t *arg = (*args); + Variable_t var = {.variableType = SaveClass}; + SaveClass_t* save = calloc(1, sizeof(SaveClass_t)); + if (f_open(&save->saveFile, arg->string.value, FA_READ | FA_WRITE)) + return NULL; + save_init(&save->saveCtx, &save->saveFile, dumpedKeys.save_mac_key, 0); + if (!save_process(&save->saveCtx)) + return NULL; + + var.save = save; + return copyVariableToPtr(var); +} + +// Takes [int, int, int]. Returns empty. 0: posX, 1: posY, 2: hexColor +ClassFunction(stdSetPixel) { + u32 color = getIntValue(args[2]); + gfx_set_pixel_horz(args[0]->integer.value, args[1]->integer.value, color); + return &emptyClass; +} + +ClassFunction(stdSetPixels){ + gfx_box(getIntValue(args[0]), getIntValue(args[1]), getIntValue(args[2]), getIntValue(args[3]), (u32)getIntValue(args[4])); + return &emptyClass; +} + +ClassFunction(stdSetPrintPos){ + if (getIntValue(args[0]) > 0 && getIntValue(args[1]) > 0){ + gfx_con_setpos((getIntValue(args[0]) % 78) * 16, (getIntValue(args[1]) % 42) * 16); + } + return &emptyClass; +} + +// Takes [str]. Returns empty. str: path to dir +ClassFunction(stdReadDir){ + Variable_t* resPtr = newIntVariablePtr(0); + Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 4)}; + addVariableToDict(&ret, "result", resPtr); + + Variable_t fileNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)}; + Variable_t dirNamesArray = {.variableType = StringArrayClass, .solvedArray.vector = newVec(sizeof(char*), 0)}; + Variable_t fileSizeArray = {.variableType = IntArrayClass, .solvedArray.vector = newVec(sizeof(s64), 0)}; + + DIR dir; + if ((resPtr->integer.value = f_opendir(&dir, args[0]->string.value))){ + return copyVariableToPtr(ret); + } + + FILINFO fno; + while (!(resPtr->integer.value = f_readdir(&dir, &fno)) && fno.fname[0]){ + char *name = CpyStr(fno.fname); + if (fno.fattrib & AM_DIR){ + vecAdd(&dirNamesArray.solvedArray.vector, name); + } + else { + vecAdd(&fileNamesArray.solvedArray.vector, name); + s64 size = fno.fsize; + vecAdd(&fileSizeArray.solvedArray.vector, size); + } + } + + f_closedir(&dir); + + addVariableToDict(&ret, "files", copyVariableToPtr(fileNamesArray)); + addVariableToDict(&ret, "folders", copyVariableToPtr(dirNamesArray)); + addVariableToDict(&ret, "fileSizes", copyVariableToPtr(fileSizeArray)); + + return copyVariableToPtr(ret); +} + +char *abxyNames[] = { + "y", + "x", + "b", + "a" +}; + +char *ulrdNames[] = { + "down", + "up", + "right", + "left", +}; + +char *powNames[] = { + "power", + "volplus", + "volminus", +}; + +// Takes [int]. Returns dict[a,b,x,y,down,up,right,left,power,volplus,volminus,raw]. int: mask for hidWaitMask +ClassFunction(stdPauseMask){ + Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 9)}; + Input_t *i = hidWaitMask((u32)getIntValue(*args)); + + u32 raw = i->buttons; + + addIntToDict(&ret, "raw", raw); + + for (int i = 0; i < ARRAY_SIZE(abxyNames); i++){ + addIntToDict(&ret, abxyNames[i], raw & 0x1); + raw >>= 1; + } + + raw >>= 12; + + for (int i = 0; i < ARRAY_SIZE(ulrdNames); i++){ + addIntToDict(&ret, ulrdNames[i], raw & 0x1); + raw >>= 1; + } + + raw >>= 4; + + for (int i = 0; i < ARRAY_SIZE(powNames); i++){ + addIntToDict(&ret, powNames[i], raw & 0x1); + raw >>= 1; + } + + return copyVariableToPtr(ret); +} + +// Takes none. Returns dict (same as stdPauseMask). +ClassFunction(stdPause){ + Variable_t a = {.integer.value = 0xFFFFFFFF}; + Variable_t *b = &a; + return stdPauseMask(caller, &b, 1); +} + +// Takes [str, str]. Returns int (0=success). 0: src path, 1: dst path +ClassFunction(stdFileCopy){ + ErrCode_t e = FileCopy(args[0]->string.value, args[1]->string.value, COPY_MODE_PRINT); + return newIntVariablePtr(e.err); +} + +ClassFunction(stdMkdir){ + return newIntVariablePtr(f_mkdir(args[0]->string.value)); +} + +// Broken???? +ClassFunction(stdGetMemUsage){ + heap_monitor_t mon; + heap_monitor(&mon, false); + Dict_t a = {.name = CpyStr("used"), .var = newIntVariablePtr((s64)mon.used)}; + Dict_t b = {.name = CpyStr("total"), .var = newIntVariablePtr((s64)mon.total)}; + Variable_t ret = {.variableType = DictionaryClass, .dictionary.vector = newVec(sizeof(Dict_t), 2)}; + vecAdd(&ret.dictionary.vector, a); + vecAdd(&ret.dictionary.vector, b); + return copyVariableToPtr(ret); +} + +// Takes [int]. Returns empty. int: hex color +ClassFunction(stdColor){ + gfx_con_setcol((u32)getIntValue(*args) | 0xFF000000, gfx_con.fillbg, gfx_con.bgcol); + return &emptyClass; +} + +// Takes [str]. Returns int. str: path to nca +ClassFunction(stdGetNcaType){ + int type = GetNcaType(args[0]->string.value); + return newIntVariablePtr(type); +} + +// Takes [str[], int, int[]]. Returns int (index). str[]: names of entries, int: starting index, int[]: colors & options. The 3rd argument is optional +ClassFunction(stdMenuFull){ + if (argsLen > 2){ + if (args[2]->solvedArray.vector.count < args[0]->solvedArray.vector.count){ + SCRIPT_FATAL_ERR("invalid menu args"); + } + } + + Vector_t v = newVec(sizeof(MenuEntry_t), args[0]->solvedArray.vector.count); + + vecDefArray(char**, menuEntryNames, args[0]->solvedArray.vector); + vecDefArray(s64*, menuEntryOptions, args[2]->solvedArray.vector); + + for (int i = 0; i < args[0]->solvedArray.vector.count; i++){ + MenuEntry_t a = {.name = menuEntryNames[i]}; + if (argsLen > 2){ + u32 options = (u32)menuEntryOptions[i]; + if (options & BIT(26)){ + a.icon = 128; + } + else if (options & BIT(27)){ + a.icon = 127; + } + + a.optionUnion = options; + } + else { + a.optionUnion = COLORTORGB(COLOR_WHITE); + } + + vecAdd(&v, a); + } + + u32 x=0,y=0; + gfx_con_getpos(&x,&y); + int res = newMenu(&v, getIntValue(args[1]), ScreenDefaultLenX - ((x + 1) / 16), 40 - ((y + 1) / 16) - 1, ENABLEB | ALWAYSREDRAW, 0); + vecFree(v); + return newIntVariablePtr(res); +} + +ClassFunction(stdHasEmu){ + return newIntVariablePtr(emu_cfg.enabled); +} + +ClassFunction(stdClear){ + gfx_clearscreen(); + return &emptyClass; +} + +ClassFunction(stdRmDir){ + return newIntVariablePtr(FolderDelete(args[0]->string.value).err); +} + +ClassFunction(stdGetMs){ + return newIntVariablePtr(get_tmr_ms()); +} + +ClassFunction(stdFileExists){ + return newIntVariablePtr(FileExists(args[0]->string.value)); +} + +ClassFunction(stdFileDel){ + return newIntVariablePtr(f_unlink(args[0]->string.value)); +} + +ClassFunction(stdCopyDir){ + return newIntVariablePtr(FolderCopy(args[0]->string.value, args[1]->string.value).err); +} + +ClassFunction(stdFileMove){ + return newIntVariablePtr(f_rename(args[0]->string.value, args[1]->string.value)); +} + +ClassFunction(stdFileRead){ + u32 fSize = 0; + u8 *buff = sd_file_read(args[0]->string.value, &fSize); + if (buff == NULL){ + SCRIPT_FATAL_ERR("Failed to read file"); + } + + Vector_t vec = vecFromArray(buff, fSize, sizeof(u8)); + Variable_t v = {.variableType = ByteArrayClass, .solvedArray.vector = vec}; + return copyVariableToPtr(v); +} + +ClassFunction(stdFileWrite){ + return newIntVariablePtr(sd_save_to_file(args[1]->solvedArray.vector.data, args[1]->solvedArray.vector.count, args[0]->string.value)); +} + +extern int launch_payload(char *path); + +ClassFunction(stdLaunchPayload){ + return newIntVariablePtr(launch_payload(args[0]->string.value)); +} + +int emmcFile(char *sdFile, char *sysPart, u8 mmc, u8 write){ + + if (!emu_cfg.enabled && mmc == MMC_CONN_EMUMMC){ + return 1; + } + + if (connectMMC(mmc)) + return 1; + + return DumpOrWriteEmmcPart(sdFile, sysPart, write, 1).err; +} + +ClassFunction(stdEmmcFileRead){ + return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMMC, 0)); +} + +ClassFunction(stdEmmcFileWrite){ + return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMMC, 1)); +} + +ClassFunction(stdEmummcFileRead){ + return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMUMMC, 0)); +} + +ClassFunction(stdEmummcFileWrite){ + return newIntVariablePtr(emmcFile(args[0]->string.value, args[1]->string.value, MMC_CONN_EMUMMC, 1)); +} + +ClassFunction(stdCombinePaths){ + if (argsLen < 2 || args[0]->variableType != StringClass){ + SCRIPT_FATAL_ERR("stdCombinePaths needs 2+ args"); + } + + char *res = CpyStr(args[0]->string.value); + + for (int i = 1; i < argsLen; i++){ + if (args[i]->variableType != StringClass){ + SCRIPT_FATAL_ERR("stdCombinePaths needs 2+ args"); + } + + char *temp = CombinePaths(res, args[i]->string.value); + free(res); + res = temp; + } + + return newStringVariablePtr(res, 0, 1); +} + +ClassFunction(stdEscPaths){ + return newStringVariablePtr(EscapeFolder(args[0]->string.value), 0, 1); +} + +ClassFunction(stdGetCwd){ + return newStringVariablePtr(CpyStr(TConf.scriptCWD), 0, 1); +} + +ClassFunction(stdPower){ + power_set_state(MAX(0, (getIntValue(args[0]) % POWER_OFF_REBOOT))); + return &emptyClass; +} + +ClassFunction(stdIsPatched){ + return newIntVariablePtr(fuse_check_patched_rcm()); +} + +ClassFunction(stdHwType){ + return newIntVariablePtr(fuse_read_hw_type()); +} + +#else +#define STUBBED(name) ClassFunction(name) { return newIntVariablePtr(0); } + +STUBBED(stdMountSysmmc) +STUBBED(stdMountSave) +STUBBED(stdSetPixel) +STUBBED(stdReadDir) +STUBBED(stdFileCopy) +STUBBED(stdMkdir) +STUBBED(stdGetMemUsage) +STUBBED(stdGetNcaType) +STUBBED(stdPause) +STUBBED(stdPauseMask) +STUBBED(stdColor) +STUBBED(stdMenuFull) +STUBBED(stdMountEmummc) +STUBBED(stdHasEmu) +STUBBED(stdGetMs) +STUBBED(stdClear) +STUBBED(stdRmDir) +STUBBED(stdFileExists) +STUBBED(stdFileDel) +STUBBED(stdCopyDir) +STUBBED(stdFileMove) +STUBBED(stdLaunchPayload) +STUBBED(stdFileWrite) +STUBBED(stdFileRead) +STUBBED(stdCombinePaths) +STUBBED(stdEmmcFileWrite) +STUBBED(stdEmmcFileRead) +STUBBED(stdEmummcFileRead) +STUBBED(stdEmummcFileWrite) +STUBBED(stdEscPaths) +STUBBED(stdGetCwd) +STUBBED(stdPower) +STUBBED(stdSetPrintPos) +STUBBED(stdSetPixels) +STUBBED(stdIsPatched) +STUBBED(stdHwType) +#endif + +u8 oneIntoneFunction[] = { IntClass, FunctionClass }; +u8 doubleFunctionClass[] = { FunctionClass, FunctionClass }; +u8 threeIntsStd[] = { IntClass, IntClass, IntClass, IntClass, IntClass }; +u8 twoStringArgStd[] = {StringClass, StringClass}; +u8 menuArgsStd[] = {StringArrayClass, IntClass, IntArrayClass}; +u8 oneStringOneByteArrayStd[] = {StringClass, ByteArrayClass}; + +ClassFunctionTableEntry_t standardFunctionDefenitions[] = { + // Flow control + {"if", stdIf, 2, oneIntoneFunction}, + {"while", stdWhile, 2, doubleFunctionClass}, + {"exit", stdExit, 0, 0}, + {"break", stdBreak, 0, 0}, + + // Class creation + {"readsave", stdMountSave, 1, twoStringArgStd}, + {"dict", stdDict, 0, 0}, + + // Utils + {"print", stdPrint, VARARGCOUNT, 0}, + {"println", stdPrintLn, VARARGCOUNT, 0}, + {"printpos", stdSetPrintPos, 2, threeIntsStd}, + {"setpixel", stdSetPixel, 3, threeIntsStd}, + {"setpixels", stdSetPixels, 5, threeIntsStd}, + {"emu", stdHasEmu, 0, 0}, + {"cwd", stdGetCwd, 0, 0}, + {"clear", stdClear, 0, 0}, + {"timer", stdGetMs, 0, 0}, + {"pause", stdPauseMask, 1, threeIntsStd}, + {"pause", stdPause, 0, 0}, + {"color", stdColor, 1, threeIntsStd}, + {"menu", stdMenuFull, 3, menuArgsStd}, + {"menu", stdMenuFull, 2, menuArgsStd}, + {"power", stdPower, 1, threeIntsStd}, + + // System + {"mountsys", stdMountSysmmc, 1, twoStringArgStd}, + {"mountemu", stdMountEmummc, 1, twoStringArgStd}, + {"ncatype", stdGetNcaType, 1, twoStringArgStd}, + {"emmcread", stdEmmcFileRead, 2, twoStringArgStd}, + {"emmcwrite", stdEmmcFileWrite, 2, twoStringArgStd}, + {"emummcread", stdEmummcFileRead, 2, twoStringArgStd}, + {"emummcwrite", stdEmummcFileWrite, 2, twoStringArgStd}, + {"fuse_patched", stdIsPatched, 0, 0}, + {"fuse_hwtype", stdHwType, 0, 0}, + + // FileSystem + // Dir + {"readdir", stdReadDir, 1, twoStringArgStd}, + {"deldir", stdRmDir, 1, twoStringArgStd}, + {"mkdir", stdMkdir, 1, twoStringArgStd}, + {"copydir", stdCopyDir, 2, twoStringArgStd}, + + // File + {"copyfile", stdFileCopy, 2, twoStringArgStd}, + {"movefile", stdFileMove, 2, twoStringArgStd}, + {"delfile", stdFileDel, 1, twoStringArgStd}, + {"readfile", stdFileRead, 1, twoStringArgStd}, + {"writefile", stdFileWrite, 2, oneStringOneByteArrayStd}, + + // Utils + {"fsexists", stdFileExists, 1, twoStringArgStd}, + {"payload", stdLaunchPayload, 1, twoStringArgStd}, + {"combinepath", stdCombinePaths, VARARGCOUNT, 0}, + {"escapepath", stdEscPaths, 1, twoStringArgStd}, +}; + +ClassFunctionTableEntry_t* searchStdLib(char* funcName, u8 *len) { + u8 lenInternal = 0; + *len = 0; + ClassFunctionTableEntry_t *ret = NULL; + + for (int i = 0; i < ARRAY_SIZE(standardFunctionDefenitions); i++) { + if (!strcmp(funcName, standardFunctionDefenitions[i].name)) { + lenInternal++; + if (ret == NULL){ + ret = &standardFunctionDefenitions[i]; + } + } + else if (lenInternal != 0){ + *len = lenInternal; + return ret; + } + } + + *len = lenInternal; + return ret; +} diff --git a/source/script/standardLibrary.h b/source/script/standardLibrary.h new file mode 100644 index 0000000..bab6837 --- /dev/null +++ b/source/script/standardLibrary.h @@ -0,0 +1,4 @@ +#pragma once +#include "model.h" + +ClassFunctionTableEntry_t* searchStdLib(char* funcName, u8 *len); \ No newline at end of file diff --git a/source/script/types.h b/source/script/types.h deleted file mode 100644 index e224c94..0000000 --- a/source/script/types.h +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include -#include "../utils/vector.h" - -enum Tokens { - Invalid = 0, - Variable = 1, - ArrayVariable, - Function, - LBracket, - StrLit, - IntLit, - LSBracket, - VariableAssignment, - ArrayVariableAssignment, - FunctionAssignment, - RBracket, - RCBracket, - LCBracket, - Seperator, - Plus, - Minus, - Multiply, - Division, - Mod, - Smaller, - SmallerEqual, - Bigger, - BiggerEqual, - Equal, - EqualEqual, - Not, - NotEqual, - LogicAND, - LogicOR, - RSBracket, - AND, - OR, - Selector, - BitShiftLeft, - BitShiftRight, - EquationSeperator, -}; - -typedef struct { - u8 token; - union { - char* text; - int val; - }; -} lexarToken_t; - -enum Errors { - ERRBADOPERATOR = 1, - ERRDOUBLENOT, - ERRSYNTAX, - ERRINVALIDTYPE, - ERRNOVAR, - ERRNOFUNC, - ERRINACTIVEINDENT, - ERRDIVBYZERO, - ERRFATALFUNCFAIL, - ERRESCSCRIPT, -}; - -enum Variables { - IntType = 0, - StringType, - IntArrayType, - StringArrayType, - ByteArrayType, - JumpType, - DictType, - NullType, - ErrType, - EmptyArrayType, -}; - -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 function : 1; - }; - u8 container; - }; - int jumpLoc; -} indentInstructor_t; - -typedef struct { - union { - struct { - u8 varType : 7; - u8 free : 1; - }; - u8 typeUnion; - }; - union { - int integerType; - char* stringType; - Vector_t vectorType; - }; -} Variable_t; - -typedef struct { - char* key; - Variable_t value; -} dict_t; - -typedef struct { - Vector_t indentInstructors; // Type indentInstructor_t - Vector_t varDict; // Type dict_t - Vector_t script; // Type lexarToken_t - u32 startEquation; - u32 curPos; - u8 indentIndex; -} scriptCtx_t; - -typedef Variable_t(*func_int_ptr)(scriptCtx_t* ctx, Variable_t *vars, u32 len); - -typedef struct { - char* key; - func_int_ptr value; - u8 argCount; - u8* typeArray; -} functionStruct_t; - -typedef struct { - int resCode; - lexarToken_t* nearToken; - u32 len; -} scriptResult_t; - - -#define newDict(strName, var) (dict_t) {strName, var} -#define newVar(var, frii, value) (Variable_t) {.varType = var, .free = frii, value} - -#define varArgs 255 - -#define NullVar newVar(NullType, 0, 0) -#define ErrVar(err) newVar(ErrType, 0, err) -#define IntVal(val) newVar(IntType, 0, val) \ No newline at end of file diff --git a/source/script/unsolvedArrayClass.c b/source/script/unsolvedArrayClass.c new file mode 100644 index 0000000..a328c03 --- /dev/null +++ b/source/script/unsolvedArrayClass.c @@ -0,0 +1,187 @@ +#include "unsolvedArrayClass.h" +#include "eval.h" +#include "compat.h" +#include "intClass.h" +#include "scriptError.h" +#include "garbageCollector.h" +#include + +Variable_t* solveArray(Variable_t *unsolvedArray) { + if (unsolvedArray->unsolvedArray.operations.count <= 0) { + return copyVariableToPtr(*unsolvedArray); + // Return empty unsolved array that turns into a solved array once something is put into it + } + + int lasti = 0; + Operator_t* ops = unsolvedArray->unsolvedArray.operations.data; + u8 type = None; + Vector_t v = { 0 }; + + + for (int i = 0; i < unsolvedArray->unsolvedArray.operations.count; i++) { + if (ops[i].token == EquationSeperator || i + 1 == unsolvedArray->unsolvedArray.operations.count) { + if (i + 1 == unsolvedArray->unsolvedArray.operations.count) + i++; + + Variable_t* var = eval(&ops[lasti], i - lasti, 1); + if (var == NULL) + return NULL; + + if (v.data == NULL) { + if (var->variableType == IntClass) { + v = newVec(sizeof(s64), 1); + type = IntClass; + } + else if (var->variableType == StringClass) { + v = newVec(sizeof(char*), 1); + type = StringClass; + } + else { + SCRIPT_FATAL_ERR("Unknown array type"); + } + } + + if (var->variableType == type) { + if (var->variableType == IntClass) { + vecAdd(&v, var->integer.value); + } + else { + char* str = CpyStr(var->string.value); + vecAdd(&v, str); + } + } + else { + SCRIPT_FATAL_ERR("Variable type is not the same as array type"); + } + + removePendingReference(var); + + lasti = i + 1; + } + } + + Variable_t arrayVar = { .variableType = (type == IntClass) ? IntArrayClass : StringArrayClass, .solvedArray.vector = v }; + return copyVariableToPtr(arrayVar); +} + +Variable_t createUnsolvedArrayVariable(Function_t* f) { + Variable_t var = { 0 }; + Vector_t holder = { 0 }; + u8 varType = Invalid; + + // Foreach to attempt to create the array. Should fail if calcs are done or types are not equal + if (f->operations.count > 0) { + vecForEach(Operator_t*, curOp, (&f->operations)) { + if (holder.data == NULL) { + if (curOp->variable.staticVariableType == 1) { + varType = IntClass; + holder = newVec(sizeof(s64), 4); + vecAdd(&holder, (curOp->variable.integerType)); + } + else if (curOp->variable.staticVariableType == 2) { + if (!strcmp(curOp->variable.stringType, "BYTE[]")) { + varType = ByteArrayClass; // Repurpose varType + holder = newVec(sizeof(u8), 4); + FREE(curOp->variable.stringType); + } + else { + varType = StringClass; + holder = newVec(sizeof(char*), 4); + vecAdd(&holder, (curOp->variable.stringType)); + } + } + else { + break; + } + } + else { + if ((curOp - 1)->token == EquationSeperator && curOp->token == Variable) { + if (curOp->variable.staticVariableType == 1) { + if (varType == IntClass) { + vecAdd(&holder, curOp->variable.integerType); + } + else if (varType == ByteArrayClass) { + u8 var = (u8)(curOp->variable.integerType & 0xFF); + vecAdd(&holder, var); + } + } + else if (curOp->variable.staticVariableType == 2) { + if (varType == StringClass) { + vecAdd(&holder, curOp->variable.stringType); + } + } + else { + vecFree(holder); + holder.data = NULL; + break; + } + } + else if (curOp->token == EquationSeperator) { + continue; + } + else { + vecFree(holder); + holder.data = NULL; + break; + } + } + } + } + + if (varType != Invalid) { + if (varType == IntClass) { + var.variableType = IntArrayClass; + } + else if (varType == StringClass) { + var.variableType = StringArrayClass; + } + else { + var.variableType = varType; + } + + vecFree(f->operations); + var.solvedArray.vector = holder; + var.readOnly = 1; + } + else { + var.unsolvedArray.operations = f->operations; + var.variableType = UnresolvedArrayClass; + } + + + return var; +} + +u8 anotherOneVarArg[] = { VARARGCOUNT }; + +ClassFunction(createTypedArray) { + Vector_t v = { 0 }; + Variable_t* arg = *args; + + if (arg->variableType == IntClass) { + v = newVec(sizeof(s64), 1); + vecAdd(&v, arg->integer.value); + } + else if (arg->variableType == StringClass) { + v = newVec(sizeof(char*), 1); + char* str = CpyStr(arg->string.value); + vecAdd(&v, str); + } + else { + SCRIPT_FATAL_ERR("Unknown array type"); + } + + Variable_t arrayVar = { .variableType = (arg->variableType == IntClass) ? IntArrayClass : StringArrayClass, .solvedArray.vector = v }; + *caller = arrayVar; + + return &emptyClass; +} + +ClassFunctionTableEntry_t unsolvedArrayFunctions[] = { + {"+", createTypedArray, 1, anotherOneVarArg}, + {"add", createTypedArray, 1, anotherOneVarArg}, +}; + +Variable_t getUnsolvedArrayMember(Variable_t* var, char* memberName) { + return getGenericFunctionMember(var, memberName, unsolvedArrayFunctions, ARRAY_SIZE(unsolvedArrayFunctions)); +} \ No newline at end of file diff --git a/source/script/unsolvedArrayClass.h b/source/script/unsolvedArrayClass.h new file mode 100644 index 0000000..557adf8 --- /dev/null +++ b/source/script/unsolvedArrayClass.h @@ -0,0 +1,8 @@ +#pragma once +#include "model.h" +#include "genericClass.h" +#include "compat.h" + +Variable_t createUnsolvedArrayVariable(Function_t* f); +Variable_t* solveArray(Variable_t* unsolvedArray); +Variable_t getUnsolvedArrayMember(Variable_t* var, char* memberName); \ No newline at end of file diff --git a/source/script/variables.c b/source/script/variables.c deleted file mode 100644 index ed3c2a5..0000000 --- a/source/script/variables.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "variables.h" -#include "types.h" -#include -#include - -void freeVariable(Variable_t dv) { - if (!dv.free) - return; - - switch (dv.varType) { - case StringType: - FREE(dv.stringType); - break; - - case StringArrayType:; - char** strArray = vecGetArray(char**, dv.vectorType); - for (u32 i = 0; i < dv.vectorType.count; i++){ - free(strArray[i]); - } - - case IntArrayType: - case ByteArrayType: - vecFree(dv.vectorType); - break; - } -} - -void freeVariableVector(Vector_t *v) { - Variable_t* vars = vecGetArrayPtr(v, Variable_t*); - for (int i = 0; i < v->count; i++) { - freeVariable(vars[i]); - } - vecFreePtr(v); -} - -void freeDictVector(Vector_t *v) { - dict_t* dic = vecGetArrayPtr(v, dict_t*); - for (int i = 0; i < v->count; i++) { - FREE(dic[i].key); - freeVariable(dic[i].value); - } - vecFreePtr(v); -} - -Variable_t* dictVectorFind(Vector_t* v, const char* key) { - dict_t* dic = vecGetArrayPtr(v, dict_t*); - for (int i = 0; i < v->count; i++) { - if (!strcmp(dic[i].key, key)) - return &dic[i].value; - } - - return NULL; -} - -void dictVectorAdd(Vector_t* v, dict_t add) { - if (add.key == NULL) - return; - - Variable_t* var = dictVectorFind(v, add.key); - - if (var != NULL) { - if ((var->varType >= StringType && var->varType <= ByteArrayType && var->varType == add.value.varType) && (add.value.stringType == var->stringType || add.value.vectorType.data == var->vectorType.data)) - return; - - freeVariable(*var); - *var = add.value; - free(add.key); - return; - } - else { - vecAddElement(v, add); - } -} - -scriptCtx_t createScriptCtx() { - scriptCtx_t s = { - .indentInstructors = newVec(sizeof(indentInstructor_t), 64), - .varDict = newVec(sizeof(dict_t), 8), - .startEquation = 0, - .curPos = 0 - }; - return s; -} - -// We should rewrite this to actually use the vector! -u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, u8 func, int jumpLoc) { - if (level >= 64) - return 1; - - indentInstructor_t* instructors = vecGetArray(indentInstructor_t*, ctx->indentInstructors); - indentInstructor_t* instructor = &instructors[level]; - - instructor->skip = skip; - instructor->active = 1; - instructor->function = func; - instructor->jump = (jumpLoc >= 0) ? 1 : 0; - instructor->jumpLoc = jumpLoc; - - return 0; -} - -indentInstructor_t* getCurIndentInstruction(scriptCtx_t* ctx) { - indentInstructor_t* instructors = vecGetArray(indentInstructor_t*, ctx->indentInstructors); - return &instructors[ctx->indentIndex]; -} \ No newline at end of file diff --git a/source/script/variables.h b/source/script/variables.h deleted file mode 100644 index dac6115..0000000 --- a/source/script/variables.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "types.h" - -void dictVectorAdd(Vector_t* v, dict_t add); -Variable_t* dictVectorFind(Vector_t* v, const char* key); -void freeDictVector(Vector_t* v); -void freeVariableVector(Vector_t* v); -void freeVariable(Variable_t dv); -scriptCtx_t createScriptCtx(); - -u8 setIndentInstruction(scriptCtx_t* ctx, u8 level, u8 skip, u8 func, int jumpLoc); -indentInstructor_t* getCurIndentInstruction(scriptCtx_t* ctx); - -static inline u8 setCurIndentInstruction(scriptCtx_t* ctx, u8 skip, u8 func, int jumpLoc) { - return setIndentInstruction(ctx, ctx->indentIndex, skip, func, jumpLoc); -} \ No newline at end of file diff --git a/source/script/vector.c b/source/script/vector.c new file mode 100644 index 0000000..01e833e --- /dev/null +++ b/source/script/vector.c @@ -0,0 +1,108 @@ +#ifdef WIN32 +#include "vector.h" +#include +#include +#include + +Vector_t newVec(u8 typesz, u32 preallocate) { + if (preallocate) { + Vector_t res = { + .data = calloc(preallocate, typesz), + .capacity = preallocate * typesz, + .count = 0, + .elemSz = typesz + }; + + // check .data != null; + return res; + } + else { + Vector_t res = { + .data = NULL, + .capacity = 1 * typesz, + .count = 0, + .elemSz = typesz + }; + + return res; + } +} + +Vector_t vecFromArray(void* array, u32 count, u32 typesz) +{ + Vector_t res = { + .data = array, + .capacity = count * typesz, + .count = count, + .elemSz = typesz + }; + return res; +} + +int vecAddElem(Vector_t* v, void* elem, u8 sz) { + if (!v || !elem || v->elemSz != sz) + return 0; + + if (v->data == NULL) { + v->data = calloc(1, v->elemSz); + } + + u32 usedbytes = v->count * sz; + if (usedbytes >= v->capacity) + { + v->capacity *= 2; + void* buff = malloc(v->capacity); + if (!buff) + return 0; + memcpy(buff, v->data, v->capacity / 2); + free(v->data); + v->data = buff; + } + + memcpy((char*)v->data + usedbytes, elem, sz); + v->count++; + return 1; +} + +Vector_t vecCopyOffset(Vector_t* orig, u32 offset) { + Vector_t dst = newVec(orig->elemSz, orig->count - offset); + memcpy(dst.data, ((u8*)orig->data + orig->elemSz * offset), (orig->count - offset) * orig->elemSz); + dst.count = orig->count - offset; + return dst; +} + +Vector_t vecCopy(Vector_t* orig) { + return vecCopyOffset(orig, 0); +} + + +void* getStackEntry(Vector_t *stack) { + if (stack->count <= 0) + return NULL; + + return ((u8*)stack->data + (stack->elemSz * (stack->count - 1))); +} + +// This will stay valid until the queue is modified +void* popStackEntry(Vector_t* stack) { + if (stack->count <= 0) + return NULL; + + void* a = getStackEntry(stack); + stack->count--; + return a; +} + +void vecRem(Vector_t *vec, int idx) { + if (vec->count <= 0 || idx >= vec->count) + return; + + if (idx == (vec->count - 1)) { + vec->count--; + return; + } + + memcpy((u8*)vec->data + (vec->elemSz * idx), (u8*)vec->data + (vec->elemSz * (idx + 1)), (vec->count - idx - 1) * vec->elemSz); + vec->count--; +} +#endif \ No newline at end of file diff --git a/source/script/vector.h b/source/script/vector.h new file mode 100644 index 0000000..5e2681d --- /dev/null +++ b/source/script/vector.h @@ -0,0 +1,24 @@ +#ifdef WIN32 +#pragma once + +#include "model.h" + +int vecAddElem(Vector_t* v, void* elem, u8 sz); +Vector_t newVec(u8 typesz, u32 preallocate); +Vector_t vecCopy(Vector_t* orig); +Vector_t vecCopyOffset(Vector_t* orig, u32 offset); +Vector_t vecFromArray(void* array, u32 count, u32 typesz); + +#define vecAdd(vec, element) vecAddElem(vec, &element, sizeof(element)) +#define vecGetArrayPtr(vec, type) (type)(vec)->data +#define vecGetArray(vec, type) (type)(vec).data +#define vecFreePtr(vec) FREE(vec->data) +#define vecFree(vec) FREE(vec.data) +#define vecGetCapacity(vec) (vec.capacity / vec.elemSz) + +#define vecForEach(type, varname, vecPtr) for (type varname = vecPtr->data; ((u8*)varname - (u8*)vecPtr->data) < (vecPtr->count * vecPtr->elemSz); varname++) + +void* getStackEntry(Vector_t* stack); +void* popStackEntry(Vector_t* stack); +void vecRem(Vector_t * vec, int idx); +#endif \ No newline at end of file diff --git a/source/storage/nx_sd.c b/source/storage/nx_sd.c index 8e4eb56..f122380 100644 --- a/source/storage/nx_sd.c +++ b/source/storage/nx_sd.c @@ -194,7 +194,8 @@ void *sd_file_read(const char *path, u32 *fsize) if (fsize) *fsize = size; - void *buf = malloc(size); + char *buf = malloc(size + 1); + buf[size] = '\0'; if (f_read(&fp, buf, size, NULL) != FR_OK) { diff --git a/source/tegraexplorer/mainmenu.c b/source/tegraexplorer/mainmenu.c index feffed2..52337eb 100644 --- a/source/tegraexplorer/mainmenu.c +++ b/source/tegraexplorer/mainmenu.c @@ -17,10 +17,22 @@ #include #include "../utils/utils.h" #include "../config.h" +#include "../fs/readers/folderReader.h" +#include +#include +#include "../fs/menus/filemenu.h" + +#define INCLUDE_BUILTIN_SCRIPTS 1 +//#define SCRIPT_ONLY 1 + +#ifdef INCLUDE_BUILTIN_SCRIPTS +#include "../../build/TegraExplorer/script/builtin.h" +#endif extern hekate_config h_cfg; enum { + #ifndef SCRIPT_ONLY MainExplore = 0, MainBrowseSd, MainMountSd, @@ -28,18 +40,22 @@ enum { MainBrowseEmummc, MainTools, MainPartitionSd, - MainDumpFw, MainViewKeys, MainViewCredits, MainExit, + #else + MainExit = 0, + #endif MainPowerOff, MainRebootRCM, MainRebootNormal, MainRebootHekate, - MainRebootAMS + MainRebootAMS, + MainScripts, }; MenuEntry_t mainMenuEntries[] = { + #ifndef SCRIPT_ONLY [MainExplore] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "-- Explore --"}, [MainBrowseSd] = {.optionUnion = COLORTORGB(COLOR_GREEN), .name = "Browse SD"}, [MainMountSd] = {.optionUnion = COLORTORGB(COLOR_YELLOW)}, // To mount/unmount the SD @@ -47,15 +63,18 @@ MenuEntry_t mainMenuEntries[] = { [MainBrowseEmummc] = {.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Browse EMUMMC"}, [MainTools] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Tools --"}, [MainPartitionSd] = {.optionUnion = COLORTORGB(COLOR_ORANGE), .name = "Partition the sd"}, - [MainDumpFw] = {.optionUnion = COLORTORGB(COLOR_BLUE), .name = "Dump Firmware"}, [MainViewKeys] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "View dumped keys"}, [MainViewCredits] = {.optionUnion = COLORTORGB(COLOR_YELLOW), .name = "Credits"}, [MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Exit --"}, + #else + [MainExit] = {.optionUnion = COLORTORGB(COLOR_WHITE), .name = "\n-- Exit --"}, + #endif [MainPowerOff] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Power off"}, [MainRebootRCM] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to RCM"}, [MainRebootNormal] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot normally"}, [MainRebootHekate] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to bootloader/update.bin"}, - [MainRebootAMS] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to atmosphere/reboot_payload.bin"} + [MainRebootAMS] = {.optionUnion = COLORTORGB(COLOR_VIOLET), .name = "Reboot to atmosphere/reboot_payload.bin"}, + [MainScripts] = {.optionUnion = COLORTORGB(COLOR_WHITE) | SKIPBIT, .name = "\n-- Scripts --"} }; void HandleSD(){ @@ -108,6 +127,7 @@ void ViewCredits(){ if (hidRead()->r) gfx_printf("%k\"I'm not even sure if it works\" - meme", COLOR_ORANGE); + hidWait(); } @@ -132,18 +152,19 @@ void MountOrUnmountSD(){ } menuPaths mainMenuPaths[] = { + #ifndef SCRIPT_ONLY [MainBrowseSd] = HandleSD, [MainMountSd] = MountOrUnmountSD, [MainBrowseEmmc] = HandleEMMC, [MainBrowseEmummc] = HandleEMUMMC, [MainPartitionSd] = FormatSD, - [MainDumpFw] = DumpSysFw, [MainViewKeys] = ViewKeys, + [MainViewCredits] = ViewCredits, + #endif [MainRebootAMS] = RebootToAMS, [MainRebootHekate] = RebootToHekate, [MainRebootRCM] = reboot_rcm, [MainPowerOff] = power_off, - [MainViewCredits] = ViewCredits, [MainRebootNormal] = reboot_normal, }; @@ -153,6 +174,7 @@ void EnterMainMenu(){ if (sd_get_card_removed()) sd_unmount(); + #ifndef SCRIPT_ONLY // -- Explore -- mainMenuEntries[MainBrowseSd].hide = !sd_mounted; mainMenuEntries[MainMountSd].name = (sd_mounted) ? "Unmount SD" : "Mount SD"; @@ -160,21 +182,89 @@ void EnterMainMenu(){ // -- Tools -- mainMenuEntries[MainPartitionSd].hide = (!is_sd_inited || sd_get_card_removed()); - mainMenuEntries[MainDumpFw].hide = (!TConf.keysDumped || !sd_mounted); mainMenuEntries[MainViewKeys].hide = !TConf.keysDumped; // -- Exit -- mainMenuEntries[MainRebootAMS].hide = (!sd_mounted || !FileExists("sd:/atmosphere/reboot_payload.bin")); mainMenuEntries[MainRebootHekate].hide = (!sd_mounted || !FileExists("sd:/bootloader/update.bin")); mainMenuEntries[MainRebootRCM].hide = h_cfg.t210b01; + #endif + // -- Scripts -- + #ifndef INCLUDE_BUILTIN_SCRIPTS + mainMenuEntries[MainScripts].hide = (!sd_mounted || !FileExists("sd:/tegraexplorer/scripts")); + #else + mainMenuEntries[MainScripts].hide = ((!sd_mounted || !FileExists("sd:/tegraexplorer/scripts")) && !EMBEDDED_SCRIPTS_LEN); + #endif + + Vector_t ent = newVec(sizeof(MenuEntry_t), ARRAY_SIZE(mainMenuEntries)); + ent.count = ARRAY_SIZE(mainMenuEntries); + memcpy(ent.data, mainMenuEntries, sizeof(MenuEntry_t) * ARRAY_SIZE(mainMenuEntries)); + Vector_t scriptFiles = {0}; + u8 hasScripts = 0; + + #ifdef INCLUDE_BUILTIN_SCRIPTS + for (int i = 0; i < EMBEDDED_SCRIPTS_LEN; i++){ + MenuEntry_t m = {.name = embedded_scripts_g[i].name, .optionUnion = COLORTORGB(COLOR_BLUE), .icon = 128}; + vecAdd(&ent, m); + } + #endif + + if (sd_mounted && FileExists("sd:/tegraexplorer/scripts")){ + scriptFiles = ReadFolder("sd:/tegraexplorer/scripts", &res); + if (!res){ + if (!scriptFiles.count){ + FREE(scriptFiles.data); + mainMenuEntries[MainScripts].hide = 1; + } + else { + hasScripts = 1; + vecForEach(FSEntry_t*, scriptFile, (&scriptFiles)){ + if (!scriptFile->isDir && StrEndsWith(scriptFile->name, ".te")){ + MenuEntry_t a = MakeMenuOutFSEntry(*scriptFile); + vecAdd(&ent, a); + } + } + + if (ent.count == ARRAY_SIZE(mainMenuEntries)){ + clearFileVector(&scriptFiles); + hasScripts = 0; + mainMenuEntries[MainScripts].hide = 1; + } + } + } + } + gfx_clearscreen(); gfx_putc('\n'); - Vector_t ent = vecFromArray(mainMenuEntries, ARR_LEN(mainMenuEntries), sizeof(MenuEntry_t)); - res = newMenu(&ent, res, 79, 30, ALWAYSREDRAW, 0); - if (mainMenuPaths[res] != NULL) + res = newMenu(&ent, res, 79, 30, (ent.count == ARRAY_SIZE(mainMenuEntries)) ? ALWAYSREDRAW : ALWAYSREDRAW | ENABLEPAGECOUNT, ent.count - ARRAY_SIZE(mainMenuEntries)); + if (res < MainScripts && mainMenuPaths[res] != NULL) mainMenuPaths[res](); + #ifndef INCLUDE_BUILTIN_SCRIPTS + else if (hasScripts){ + #else + else { + if (res - ARRAY_SIZE(mainMenuEntries) < EMBEDDED_SCRIPTS_LEN){ + char *script = embedded_scripts_g[res - ARRAY_SIZE(mainMenuEntries)].script; + RunScriptString(script, strlen(script)); + } + else { + #endif + vecDefArray(MenuEntry_t*, entArray, ent); + MenuEntry_t entry = entArray[res]; + FSEntry_t fsEntry = {.name = entry.name, .sizeUnion = entry.sizeUnion}; + RunScript("sd:/tegraexplorer/scripts", fsEntry); + #ifdef INCLUDE_BUILTIN_SCRIPTS + } + #endif + } + + if (hasScripts){ + clearFileVector(&scriptFiles); + } + + free(ent.data); } } diff --git a/source/tegraexplorer/tconf.h b/source/tegraexplorer/tconf.h index 33f1237..0e0ea3b 100644 --- a/source/tegraexplorer/tconf.h +++ b/source/tegraexplorer/tconf.h @@ -34,6 +34,7 @@ typedef struct { }; const char *pkg1ID; u8 pkg1ver; + char *scriptCWD; } TConf_t; extern TConf_t TConf; diff --git a/source/utils/vector.c b/source/utils/vector.c index 900e1f8..cb8f246 100644 --- a/source/utils/vector.c +++ b/source/utils/vector.c @@ -1,4 +1,5 @@ #include "vector.h" +#include "../gfx/gfx.h" #include #include @@ -44,8 +45,7 @@ int _vecAdd(Vector_t* v, void* elem, u8 sz) { if (v->data == NULL) { v->data = calloc(1, v->elemSz); } - - u32 usedbytes = v->count * sz; + u32 usedbytes = v->count * (u32)v->elemSz; if (usedbytes >= v->capacity) { v->capacity *= 2; @@ -56,8 +56,7 @@ int _vecAdd(Vector_t* v, void* elem, u8 sz) { free(v->data); v->data = buff; } - - memcpy((char*)v->data + usedbytes, elem, sz); + memcpy(((u8*)v->data) + usedbytes, elem, v->elemSz); v->count++; return 1; } diff --git a/source/utils/vector.h b/source/utils/vector.h index cefbfe0..9fcb1d0 100644 --- a/source/utils/vector.h +++ b/source/utils/vector.h @@ -1,6 +1,8 @@ #pragma once #include +#pragma pack(1) + typedef struct { void* data; u32 capacity; @@ -9,6 +11,8 @@ typedef struct { // u32 typeTag; } Vector_t; +#pragma pack() + #define FREE(x) free(x); x = NULL; #define vecAddElem(v, elem) _vecAdd(v, &elem, sizeof(elem)) diff --git a/te2c.py b/te2c.py new file mode 100644 index 0000000..892ef33 --- /dev/null +++ b/te2c.py @@ -0,0 +1,68 @@ +""" +Usage: python te2c.py dest src_folder +Usage for TE: py te2c.py source/script/builtin scripts +Creator: https://github.com/maddiethecafebabe +""" + +import os +import sys +import string +from typing import List, Tuple, Dict + +IDENTCHARS = string.ascii_letters + string.digits + +SOURCE_HEADER = """ /* this file has been autogenerated with te2c.py */ +#include "{}"\n +""" + +HEADER_DEF = """ /* this file has been autogenerated with te2c.py */ +#ifndef TE_EMBEDDED_SCRIPTS_H +#define TE_EMBEDDED_SCRIPTS_H + +typedef struct {{ + const char const * script; + const char const * name; +}} embedded_script_t; + +extern const embedded_script_t embedded_scripts_g[]; + +#define EMBEDDED_SCRIPTS_LEN {} + +#endif /* TE_EMBEDDED_SCRIPTS_H */ +""" + +def gather_scripts(path: str, suffix: str=".te") -> List[Tuple[str, str]]: + files = [] + for dirname, _, filenames in os.walk(path): + files += [(f, os.path.join(dirname, f)) for f in filenames if f.endswith(suffix)] + return files + +def process_script(path: str) -> str: + with open(path, "r") as fp: + raw = fp.readlines() + return str().join(line.__repr__()[1:-1].replace("\"", "\\\"").replace("\\t", "") for line in raw) + +def process_name(name: str) -> str: + return "script_" + str().join(c for c in name.split(".")[0] if c in IDENTCHARS) + "_g" + +def map_scripts_from(path: str, suffix: str=".te") -> Dict[str, Dict[str, str]]: + return {process_name(name): {"script": process_script(path), "og-name": name} for (name, path) in gather_scripts(path, suffix=suffix)} + +def te2c(dest: str, path: str, suffix: str=".te"): + script_map = map_scripts_from(path, suffix=suffix) + with open(dest + ".c", "w") as fp: + fp.write(SOURCE_HEADER.format(os.path.basename(dest) + ".h")) + fp.write( + "const embedded_script_t embedded_scripts_g[] = {{\n{}}};\n".format( + str().join( + f"\t{{ .name = \"{vals['og-name']}\", .script = \"{vals['script']}\"}}, \n" for name, vals in script_map.items() + ) + ) + ) + + with open(dest + ".h", "w") as fp: + fp.write(HEADER_DEF.format(len(script_map))) + +if __name__ == "__main__": + te2c(dest=sys.argv[1], path=sys.argv[2]) + print("converting .te files done!") diff --git a/ts-minifier.py b/ts-minifier.py new file mode 100644 index 0000000..a036e9b --- /dev/null +++ b/ts-minifier.py @@ -0,0 +1,185 @@ +# Copyright (c) 2021 bleck9999 +# https://github.com/bleck9999/ts-minifier +# Version: 91345ce1 + +import argparse +import re + +# if is not included because it's already 2 characters +sub_funcs = {'while': "_h", 'print': "_p", 'println': "_l", 'mountsys': "_s", 'mountemu': "_e", 'readsave': "_r", + 'exit': "_q", 'break': "_b", 'dict': "_d", 'setpixel': "_y", 'readdir': "_i", 'copyfile': "_c", + 'mkdir': "_k", 'ncatype': "_n", 'pause': "_w", 'color': "_a", 'menu': "__", 'emu': "_u", + 'clear': "_x", 'timer': "_t", 'deldir': "_g", 'fsexists': "_f", 'delfile': "_z", "copydir": "c_", + "movefile": "_v", "payload": "_j", "readfile": "_o", "writefile": "_W", "setpixels": "_Y", "printpos": "_P", + "emmcread": "_E", "emmcwrite": "_F", "emummcread": "_R", "emummcwrite": "_S", "escapepath": "_X", + "combinepath": "_A", "cwd": "_D", "power": "_O", "fuse_patched": "_M", "fuse_hwtype": "_N"} +replace_functions = False + + +def wantsumspace(s: str): + for c in s.lower(): + if (ord(c) < 97 or ord(c) > 122) and (ord(c) != 95) and not (c.isnumeric()): + return False + return True + + +def commentstartswhere(s: str): + quoted = False + for c in range(len(s)): + if s[c] == '"': + quoted = not quoted + if s[c] == '#' and not quoted: + return c + return None + + +def minify(script: str): + # currently ts does not seem to allow 's to mark a quote + # (https://github.com/suchmememanyskill/TegraExplorer/blob/tsv3/source/script/parser.c#L173) + # im fine with that, it makes doing this a lot easier + # strings = script.split(sep='"') + str_reuse = {} + requires = "" + mcode = "" + stl_counts = {}.fromkeys(sub_funcs, 0) + # while part < len(strings): + for line in script.split(sep='\n'): + # maybe in future it'll shrink user defined names + # dont hold out hope for that because `a.files.foreach("b") {println(b)}` is valid syntax + # and i dont have the skill or patience to deal with that + + # # in theory all the even numbered indexes should be outside quotes, so we ignore any parts with an odd index + # if part % 2 == 1: + # if strings[part] not in str_reuse: + # str_reuse[strings[part]] = 0 + # else: + # str_reuse[strings[part]] += 1 + # mcode += f'"{strings[part]}"' + start = commentstartswhere(line) + if start is None: + start = -1 + + if "REQUIRE " in line[start:]: + requires += line[start:] + '\n' # leave REQUIREs unmodified + # comments are terminated by a newline so we need to add one back in + + # *deep breath* + # slicing is exclusive on the right side of the colon so the "no comment" value of start=-1 would cut off + # the last character of the line which would lead to several issues + # however this is desirable when there *is* a comment, since it being exclusive means there isn't a trailing # + # and if you're wondering about the above check that uses line[start:] this doesn't matter, + # one character cant contain an 8 character substring + if start != -1: + line = line[:start] + line = line.split(sep='"') + + if len(line) % 2 == 0: + print("You appear to have string literals spanning multiple lines. Please seek professional help") + raise Exception("Too much hatred") + part = 0 + while part < len(line): + # all the odd numbered indexes should be inside quotes + if part % 2 == 0: + if not line[part]: + break + for s in sub_funcs: + stl_counts[s] += len(re.findall("(? len(func)+3) so dont even try + if stl_counts[func] >= 2: + savings = stl_counts[func] * (len(func) - 2) - (len(func) + 3) + print(f"Replacing all {stl_counts[func]} usages of {func} would save {savings}byte{'s' if savings != 1 else ''}") + if (savings < 0) or not replace_functions: + print("Savings negative or automatic replacement disabled, continuing") + continue + func_min = sub_funcs[func] # now here we have to assume nobody is using any of our substitute vars + # should be a pretty safe assumption but knowing for sure would require about the same amount of effort + # as it would to replace all user defined variables + ucode = "" # this is rather hacky + sections = [0] + for m in re.finditer(r"(?= 2: + # we can't auto replace strings without a full parser + # unlike with the stdlib functions we cant make a lookup table ahead of time + # and generating shorter names on the fly sounds like an absolute nightmare no thanks + print(f'Warning: string "{string}" of len {len(string)} reused {count} times') + + return requires + mmcode.strip() + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Minify tsv3 scripts, useful for embedding", + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument("source", type=str, nargs='+', help="source files to minify") + parser.add_argument("-d", type=str, nargs='?', help="destination folder for minified scripts" + "\ndefault: ./", default='./') + parser.add_argument("--replace-functions", action="store_true", default=False, + help="automatically replace reused functions instead of just warning\ndefault: false") + + args = parser.parse_args() + files = args.source + dest = args.d[:-1] if args.d[-1] == '/' else args.d + replace_functions = args.replace_functions if args.replace_functions is not None else False + + for file in files: + print(f"Minifying {file}") + with open(file, 'r') as f: + r = minify(f.read()) + file = file.split(sep='.')[0].split(sep='/')[-1] + if dest != '.': + f = open(f"{dest}/{file}.te", 'w') + else: + f = open(f"{dest}/{file}_min.te", 'w') + f.write(r)