mirror of
https://github.com/suchmememanyskill/TegraExplorer.git
synced 2024-11-22 11:56:42 +00:00
commit
6e2a5f0523
67 changed files with 4418 additions and 1778 deletions
2
.github/workflows/builder.yml
vendored
2
.github/workflows/builder.yml
vendored
|
@ -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
|
||||
|
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -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
|
||||
|
|
20
Makefile
20
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
|
||||
|
|
|
@ -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;
|
||||
|
|
54
scripts/FirmwareDump.te
Normal file
54
scripts/FirmwareDump.te
Normal file
|
@ -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()
|
30
scripts/SystemWipe.te
Normal file
30
scripts/SystemWipe.te
Normal file
|
@ -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()
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#pragma once
|
||||
#include "../../utils/vector.h"
|
||||
#include "../../gfx/menu.h"
|
||||
#include "../fstypes.h"
|
||||
|
||||
void FileExplorer(char *path);
|
||||
void FileExplorer(char *path);
|
||||
MenuEntry_t MakeMenuOutFSEntry(FSEntry_t entry);
|
|
@ -11,11 +11,12 @@
|
|||
#include <libs/fatfs/ff.h>
|
||||
#include "../../utils/utils.h"
|
||||
#include "../../keys/nca.h"
|
||||
#include "../../script/lexer.h"
|
||||
#include "../../script/parser.h"
|
||||
#include "../../script/variables.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#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){
|
||||
|
|
|
@ -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);
|
||||
void RunScript(char *path, FSEntry_t entry);
|
||||
void RunScriptString(char *str, u32 size);
|
|
@ -11,9 +11,6 @@
|
|||
#include <libs/fatfs/ff.h>
|
||||
#include "../../utils/utils.h"
|
||||
#include "../../keys/nca.h"
|
||||
#include "../../script/lexer.h"
|
||||
#include "../../script/parser.h"
|
||||
#include "../../script/variables.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../fscopy.h"
|
||||
|
||||
|
|
91
source/script/ABadIdeaVersion3.c
Normal file
91
source/script/ABadIdeaVersion3.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
#ifdef WIN32
|
||||
|
||||
#include <stdio.h>
|
||||
#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
|
188
source/script/ABadIdeaVersion3.vcxproj
Normal file
188
source/script/ABadIdeaVersion3.vcxproj
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{b4ea4793-03fc-4c25-b69b-a5fbad2d89d5}</ProjectGuid>
|
||||
<RootNamespace>ABadIdeaVersion3</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<EnableASAN>false</EnableASAN>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABadIdeaVersion3.c" />
|
||||
<ClCompile Include="arrayClass.c" />
|
||||
<ClCompile Include="arrayReferenceClass.c" />
|
||||
<ClCompile Include="dictionaryClass.c" />
|
||||
<ClCompile Include="else.c" />
|
||||
<ClCompile Include="eval.c" />
|
||||
<ClCompile Include="functionClass.c" />
|
||||
<ClCompile Include="garbageCollector.c" />
|
||||
<ClCompile Include="genericClass.c" />
|
||||
<ClCompile Include="intClass.c" />
|
||||
<ClCompile Include="model.c" />
|
||||
<ClCompile Include="parser.c" />
|
||||
<ClCompile Include="scriptError.c" />
|
||||
<ClCompile Include="standardLibrary.c" />
|
||||
<ClCompile Include="StringClass.c" />
|
||||
<ClCompile Include="unsolvedArrayClass.c" />
|
||||
<ClCompile Include="vector.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="arrayClass.h" />
|
||||
<ClInclude Include="arrayReferenceClass.h" />
|
||||
<ClInclude Include="compat.h" />
|
||||
<ClInclude Include="dictionaryClass.h" />
|
||||
<ClInclude Include="else.h" />
|
||||
<ClInclude Include="eval.h" />
|
||||
<ClInclude Include="functionClass.h" />
|
||||
<ClInclude Include="garbageCollector.h" />
|
||||
<ClInclude Include="genericClass.h" />
|
||||
<ClInclude Include="intClass.h" />
|
||||
<ClInclude Include="model.h" />
|
||||
<ClInclude Include="parser.h" />
|
||||
<ClInclude Include="scriptError.h" />
|
||||
<ClInclude Include="standardLibrary.h" />
|
||||
<ClInclude Include="StringClass.h" />
|
||||
<ClInclude Include="unsolvedArrayClass.h" />
|
||||
<ClInclude Include="vector.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="input.te" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
152
source/script/ABadIdeaVersion3.vcxproj.filters
Normal file
152
source/script/ABadIdeaVersion3.vcxproj.filters
Normal file
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes">
|
||||
<UniqueIdentifier>{c245ac36-a649-432b-a663-98ff9d02d3f4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\String">
|
||||
<UniqueIdentifier>{e4d90dab-a1e0-4a27-82a1-dc2c6b81f0bd}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Generic">
|
||||
<UniqueIdentifier>{1aacfe02-949c-4394-b408-28d73d702918}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Array">
|
||||
<UniqueIdentifier>{5878f4a8-cac0-40aa-8866-6109bf6688ab}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Dictionary">
|
||||
<UniqueIdentifier>{ec7391f8-d60e-46eb-965e-c2d84d48d784}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Function">
|
||||
<UniqueIdentifier>{015ca379-a813-4d13-a929-3b0efe72bb02}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Int">
|
||||
<UniqueIdentifier>{64738969-63b9-4d0e-85b9-d9a63a7e36b3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Classes\Elseable">
|
||||
<UniqueIdentifier>{ba83a808-23f1-44c6-85e4-d0c6c77200fa}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ABadIdeaVersion3.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="vector.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="parser.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="model.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="eval.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="genericClass.c">
|
||||
<Filter>Source Files\Classes\Generic</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="garbageCollector.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="functionClass.c">
|
||||
<Filter>Source Files\Classes\Function</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringClass.c">
|
||||
<Filter>Source Files\Classes\String</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="unsolvedArrayClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="arrayClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="intClass.c">
|
||||
<Filter>Source Files\Classes\Int</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="arrayReferenceClass.c">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="standardLibrary.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scriptError.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="else.c">
|
||||
<Filter>Source Files\Classes\Elseable</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dictionaryClass.c">
|
||||
<Filter>Source Files\Classes\Dictionary</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="model.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="vector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="eval.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="garbageCollector.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dictionaryClass.h">
|
||||
<Filter>Source Files\Classes\Dictionary</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="arrayClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="intClass.h">
|
||||
<Filter>Source Files\Classes\Int</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringClass.h">
|
||||
<Filter>Source Files\Classes\String</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unsolvedArrayClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="functionClass.h">
|
||||
<Filter>Source Files\Classes\Function</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="genericClass.h">
|
||||
<Filter>Source Files\Classes\Generic</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="arrayReferenceClass.h">
|
||||
<Filter>Source Files\Classes\Array</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="standardLibrary.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="scriptError.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="else.h">
|
||||
<Filter>Source Files\Classes\Elseable</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="input.te">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
4
source/script/ABadIdeaVersion3.vcxproj.user
Normal file
4
source/script/ABadIdeaVersion3.vcxproj.user
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
132
source/script/StringClass.c
Normal file
132
source/script/StringClass.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include "StringClass.h"
|
||||
#include "compat.h"
|
||||
#include "intClass.h"
|
||||
#include "scriptError.h"
|
||||
#include "parser.h"
|
||||
#include <string.h>
|
||||
|
||||
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));
|
||||
}
|
10
source/script/StringClass.h
Normal file
10
source/script/StringClass.h
Normal file
|
@ -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);
|
|
@ -1,502 +0,0 @@
|
|||
#include "args.h"
|
||||
#include "types.h"
|
||||
#include "functions.h"
|
||||
#include "variables.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
#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;
|
||||
}
|
|
@ -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);
|
272
source/script/arrayClass.c
Normal file
272
source/script/arrayClass.c
Normal file
|
@ -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 <string.h>
|
||||
|
||||
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));
|
||||
}
|
5
source/script/arrayClass.h
Normal file
5
source/script/arrayClass.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getArrayMember(Variable_t* var, char* memberName);
|
24
source/script/arrayReferenceClass.c
Normal file
24
source/script/arrayReferenceClass.c
Normal file
|
@ -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));
|
||||
}
|
5
source/script/arrayReferenceClass.h
Normal file
5
source/script/arrayReferenceClass.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getArrayReferenceMember(Variable_t* var, char* memberName);
|
23
source/script/compat.h
Normal file
23
source/script/compat.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#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 <mem/heap.h>
|
||||
#include "../utils/vector.h"
|
||||
#include "../utils/utils.h"
|
||||
#endif
|
61
source/script/dictionaryClass.c
Normal file
61
source/script/dictionaryClass.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include "dictionaryClass.h"
|
||||
#include <string.h>
|
||||
#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 };
|
||||
}
|
8
source/script/dictionaryClass.h
Normal file
8
source/script/dictionaryClass.h
Normal file
|
@ -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);
|
22
source/script/else.c
Normal file
22
source/script/else.c
Normal file
|
@ -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));
|
||||
}
|
7
source/script/else.h
Normal file
7
source/script/else.h
Normal file
|
@ -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);
|
276
source/script/eval.c
Normal file
276
source/script/eval.c
Normal file
|
@ -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 <string.h>
|
||||
|
||||
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;
|
||||
}
|
19
source/script/eval.h
Normal file
19
source/script/eval.h
Normal file
|
@ -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);
|
67
source/script/functionClass.c
Normal file
67
source/script/functionClass.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "functionClass.h"
|
||||
#include "compat.h"
|
||||
#include "model.h"
|
||||
#include <malloc.h>
|
||||
|
||||
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;
|
||||
}
|
8
source/script/functionClass.h
Normal file
8
source/script/functionClass.h
Normal file
|
@ -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);
|
|
@ -1,529 +0,0 @@
|
|||
#include "types.h"
|
||||
#include "args.h"
|
||||
#include "variables.h"
|
||||
#include <string.h>
|
||||
#include "../gfx/gfx.h"
|
||||
#include <mem/heap.h>
|
||||
#include "lexer.h"
|
||||
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include "../gfx/gfxutils.h"
|
||||
#include "../hid/hid.h"
|
||||
#include <utils/util.h>
|
||||
#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("<Wait %d seconds> \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);
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#pragma once
|
||||
#include "variables.h"
|
||||
|
||||
Variable_t executeFunction(scriptCtx_t* ctx, char* func_name, lexarToken_t* start, u32 len);
|
26
source/script/garbageCollector.c
Normal file
26
source/script/garbageCollector.c
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
8
source/script/garbageCollector.h
Normal file
8
source/script/garbageCollector.h
Normal file
|
@ -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)
|
297
source/script/genericClass.c
Normal file
297
source/script/genericClass.c
Normal file
|
@ -0,0 +1,297 @@
|
|||
#include "genericClass.h"
|
||||
#include "model.h"
|
||||
#include "intClass.h"
|
||||
#include "compat.h"
|
||||
#include "eval.h"
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
25
source/script/genericClass.h
Normal file
25
source/script/genericClass.h
Normal file
|
@ -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);
|
91
source/script/intClass.c
Normal file
91
source/script/intClass.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include "intClass.h"
|
||||
#include "StringClass.h"
|
||||
#include "compat.h"
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <utils/sprintf.h>
|
||||
#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));
|
||||
}
|
13
source/script/intClass.h
Normal file
13
source/script/intClass.h
Normal file
|
@ -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);
|
|
@ -1,308 +0,0 @@
|
|||
#include "lexer.h"
|
||||
#include "types.h"
|
||||
#include "args.h"
|
||||
#include <mem/heap.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -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);
|
43
source/script/model.c
Normal file
43
source/script/model.c
Normal file
|
@ -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 };
|
276
source/script/model.h
Normal file
276
source/script/model.h
Normal file
|
@ -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 <libs/nx_savedata/save.h>
|
||||
#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()
|
|
@ -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 <mem/heap.h>
|
||||
#include "../hid/hid.h"
|
||||
#include "model.h"
|
||||
#include "compat.h"
|
||||
#include "compat.h"
|
||||
#include "parser.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 <storage/nx_sd.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
#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);
|
108
source/script/saveClass.c
Normal file
108
source/script/saveClass.c
Normal file
|
@ -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));
|
||||
}
|
4
source/script/saveClass.h
Normal file
4
source/script/saveClass.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "model.h"
|
||||
#include "genericClass.h"
|
||||
|
||||
Variable_t getSaveMember(Variable_t* var, char* memberName);
|
20
source/script/scriptError.c
Normal file
20
source/script/scriptError.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "scriptError.h"
|
||||
#include "compat.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
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
|
||||
}
|
19
source/script/scriptError.h
Normal file
19
source/script/scriptError.h
Normal file
|
@ -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__)
|
||||
|
601
source/script/standardLibrary.c
Normal file
601
source/script/standardLibrary.c
Normal file
|
@ -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 <string.h>
|
||||
#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 <mem/heap.h>
|
||||
#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 <utils/util.h>
|
||||
#include "../fs/fsutils.h"
|
||||
#include <storage/nx_sd.h>
|
||||
#include "../storage/emmcfile.h"
|
||||
#include <soc/fuse.h>
|
||||
#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;
|
||||
}
|
4
source/script/standardLibrary.h
Normal file
4
source/script/standardLibrary.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
ClassFunctionTableEntry_t* searchStdLib(char* funcName, u8 *len);
|
|
@ -1,145 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <utils/types.h>
|
||||
#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)
|
187
source/script/unsolvedArrayClass.c
Normal file
187
source/script/unsolvedArrayClass.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
#include "unsolvedArrayClass.h"
|
||||
#include "eval.h"
|
||||
#include "compat.h"
|
||||
#include "intClass.h"
|
||||
#include "scriptError.h"
|
||||
#include "garbageCollector.h"
|
||||
#include <string.h>
|
||||
|
||||
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));
|
||||
}
|
8
source/script/unsolvedArrayClass.h
Normal file
8
source/script/unsolvedArrayClass.h
Normal file
|
@ -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);
|
|
@ -1,105 +0,0 @@
|
|||
#include "variables.h"
|
||||
#include "types.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
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];
|
||||
}
|
|
@ -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);
|
||||
}
|
108
source/script/vector.c
Normal file
108
source/script/vector.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
#ifdef WIN32
|
||||
#include "vector.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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
|
24
source/script/vector.h
Normal file
24
source/script/vector.h
Normal file
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -17,10 +17,22 @@
|
|||
#include <soc/fuse.h>
|
||||
#include "../utils/utils.h"
|
||||
#include "../config.h"
|
||||
#include "../fs/readers/folderReader.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ typedef struct {
|
|||
};
|
||||
const char *pkg1ID;
|
||||
u8 pkg1ver;
|
||||
char *scriptCWD;
|
||||
} TConf_t;
|
||||
|
||||
extern TConf_t TConf;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "vector.h"
|
||||
#include "../gfx/gfx.h"
|
||||
#include <string.h>
|
||||
#include <mem/heap.h>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include <utils/types.h>
|
||||
|
||||
#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))
|
||||
|
|
68
te2c.py
Normal file
68
te2c.py
Normal file
|
@ -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!")
|
185
ts-minifier.py
Normal file
185
ts-minifier.py
Normal file
|
@ -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("(?<!\\.)%s\\(" % s, line[part]))
|
||||
mcode += line[part].replace('\t', '') + ' '
|
||||
else:
|
||||
if line[part] not in str_reuse:
|
||||
str_reuse[line[part]] = 0
|
||||
else:
|
||||
str_reuse[line[part]] += 1
|
||||
mcode += f'"{line[part]}"'
|
||||
|
||||
part += 1
|
||||
|
||||
|
||||
# tsv3 is still an absolute nightmare
|
||||
# so spaces have a couple edge cases
|
||||
# 1. the - operator which requires space between the right operand
|
||||
# yeah that's right only the right one
|
||||
# thanks meme
|
||||
# 2. between 2 letters
|
||||
inquote = False
|
||||
mmcode = ""
|
||||
index = 0
|
||||
newline = list(mcode)
|
||||
while index < (len(mcode) - 3):
|
||||
sec = mcode[index:index + 3]
|
||||
if not inquote and sec[1] == '"':
|
||||
inquote = True
|
||||
elif inquote and sec[1] == '"':
|
||||
inquote = False
|
||||
if (sec[1] == ' ') and not inquote:
|
||||
if wantsumspace(sec[0]) and wantsumspace(sec[2]):
|
||||
pass
|
||||
elif sec[0] == '-' and sec[2].isnumeric():
|
||||
pass
|
||||
else:
|
||||
newline[index + 1] = ''
|
||||
index += 1
|
||||
mmcode += ''.join(newline).strip()
|
||||
|
||||
for func in sub_funcs:
|
||||
# space saved here is given by n * (len(func) - len(min_func)) - (len(min_func)+1 + len(func))
|
||||
# as such with one usage space is always lost (len(func)-2 is never > 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"(?<!\.)%s\(" % func, mmcode):
|
||||
sections.append(m.span()[0])
|
||||
sections.append(m.span()[1])
|
||||
sections.append(len(mmcode))
|
||||
i = 2 # change rather to very
|
||||
while i < len(sections):
|
||||
ucode += mmcode[sections[i-2]:sections[i-1]] + func_min + '('
|
||||
i += 2
|
||||
ucode += mmcode[sections[i-2]:]
|
||||
|
||||
ucode = f"{func_min}={func}\n" + ucode
|
||||
mmcode = ucode
|
||||
# a space isn't any shorter than \n so why not use \n
|
||||
|
||||
for string, count in str_reuse.items():
|
||||
if count >= 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)
|
Loading…
Reference in a new issue