diff --git a/.github/workflows/publish_cli.yml b/.github/workflows/publish_cli.yml
index 111062784..49c173b40 100644
--- a/.github/workflows/publish_cli.yml
+++ b/.github/workflows/publish_cli.yml
@@ -42,6 +42,7 @@ jobs:
run: |
cp ./README.md ./CLI-${{ matrix.os }}/
cp ./LICENSE.txt ./CLI-${{ matrix.os }}/
+ cp -r ./UndertaleModLib/GameSpecificData/ ./CLI-${{ matrix.os }}/GameSpecificData/
- name: Upload ${{ matrix.os }} CLI
uses: actions/upload-artifact@v4
with:
diff --git a/.github/workflows/publish_gui.yml b/.github/workflows/publish_gui.yml
index d366c23c2..36eed51d2 100644
--- a/.github/workflows/publish_gui.yml
+++ b/.github/workflows/publish_gui.yml
@@ -41,6 +41,7 @@ jobs:
cp ./README.md ./${{ matrix.os }}
cp ./SCRIPTS.md ./${{ matrix.os }}
cp ./LICENSE.txt ./${{ matrix.os }}
+ cp -r ./UndertaleModLib/GameSpecificData/ ./${{ matrix.os }}/GameSpecificData/
- name: Upload ${{ matrix.os }} GUI
uses: actions/upload-artifact@v4
with:
diff --git a/.github/workflows/publish_gui_nightly.yml b/.github/workflows/publish_gui_nightly.yml
index 0e6db792f..c61421c8f 100644
--- a/.github/workflows/publish_gui_nightly.yml
+++ b/.github/workflows/publish_gui_nightly.yml
@@ -45,6 +45,7 @@ jobs:
cp ./README.md ./${{ matrix.os }}
cp ./SCRIPTS.md ./${{ matrix.os }}
cp ./LICENSE.txt ./${{ matrix.os }}
+ cp -r ./UndertaleModLib/GameSpecificData/ ./${{ matrix.os }}/GameSpecificData/
- name: Create zip for nightly release Windows GUI
run: |
7z a -tzip GUI-${{ matrix.os }}-${{ matrix.configuration }}-isBundled-${{ matrix.bundled }}-isSingleFile-${{ matrix.singlefile }}.zip ./${{ matrix.os }}/* -mx0
@@ -90,6 +91,7 @@ jobs:
run: |
cp ./README.md ./CLI-${{ matrix.os }}/
cp ./LICENSE.txt ./CLI-${{ matrix.os }}/
+ cp -r ./UndertaleModLib/GameSpecificData/ ./CLI-${{ matrix.os }}/GameSpecificData/
- name: Create zip for nightly release CLI
run: |
7z a -tzip CLI-${{ matrix.os }}-${{ matrix.configuration }}-isBundled-${{ matrix.bundled }}.zip ./CLI-${{ matrix.os }}/* -mx0
diff --git a/.github/workflows/publish_gui_release.yml b/.github/workflows/publish_gui_release.yml
index cab6845dd..598b3d6ee 100644
--- a/.github/workflows/publish_gui_release.yml
+++ b/.github/workflows/publish_gui_release.yml
@@ -41,6 +41,7 @@ jobs:
cp ./README.md ./${{ matrix.os }}
cp ./SCRIPTS.md ./${{ matrix.os }}
cp ./LICENSE.txt ./${{ matrix.os }}
+ cp -r ./UndertaleModLib/GameSpecificData/ ./${{ matrix.os }}/GameSpecificData/
- name: Create zip for stable release Windows GUI
run: |
7z a -tzip GUI-${{ matrix.os }}-${{ matrix.configuration }}-isBundled-${{ matrix.bundled }}-isSingleFile-${{ matrix.singlefile }}.zip ./${{ matrix.os }}/* -mx0
diff --git a/.github/workflows/publish_pr.yml b/.github/workflows/publish_pr.yml
index ac86e2c47..f34433e33 100644
--- a/.github/workflows/publish_pr.yml
+++ b/.github/workflows/publish_pr.yml
@@ -43,6 +43,7 @@ jobs:
cp ./README.md ./${{ matrix.os }}
cp ./SCRIPTS.md ./${{ matrix.os }}
cp ./LICENSE.txt ./${{ matrix.os }}
+ cp -r ./UndertaleModLib/GameSpecificData/ ./${{ matrix.os }}/GameSpecificData/
- name: Upload ${{ matrix.os }} GUI
uses: actions/upload-artifact@v4
with:
@@ -85,6 +86,7 @@ jobs:
run: |
cp ./README.md ./CLI-${{ matrix.os }}/
cp ./LICENSE.txt ./CLI-${{ matrix.os }}/
+ cp -r ./UndertaleModLib/GameSpecificData/ ./CLI-${{ matrix.os }}/GameSpecificData/
- name: Upload ${{ matrix.os }} CLI
uses: actions/upload-artifact@v4
with:
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..af38bac38
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "Underanalyzer"]
+ path = Underanalyzer
+ url = https://github.com/UnderminersTeam/Underanalyzer.git
diff --git a/Underanalyzer b/Underanalyzer
new file mode 160000
index 000000000..e669966fb
--- /dev/null
+++ b/Underanalyzer
@@ -0,0 +1 @@
+Subproject commit e669966fbe7c5c3c2e154e741801a1d7c43b70a7
diff --git a/UndertaleModCli/Program.UMTLibInherited.cs b/UndertaleModCli/Program.UMTLibInherited.cs
index 4847e967c..8da953b49 100644
--- a/UndertaleModCli/Program.UMTLibInherited.cs
+++ b/UndertaleModCli/Program.UMTLibInherited.cs
@@ -10,6 +10,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
+using Underanalyzer.Decompiler;
using UndertaleModLib;
using UndertaleModLib.Decompiler;
using UndertaleModLib.Models;
@@ -293,7 +294,7 @@ public void ReapplyProfileCode()
}
///
- public async Task GenerateGMLCache(ThreadLocal decompileContext = null, object dialog = null, bool isSaving = false)
+ public async Task GenerateGMLCache(GlobalDecompileContext decompileContext = null, object dialog = null, bool isSaving = false)
{
await Task.Delay(1); //dummy await
@@ -410,21 +411,23 @@ public string PromptLoadFile(string defaultExt, string filter)
}
///
- public string GetDecompiledText(string codeName, GlobalDecompileContext context = null)
+ public string GetDecompiledText(string codeName, GlobalDecompileContext context = null, IDecompileSettings settings = null)
{
- return GetDecompiledText(Data.Code.ByName(codeName), context);
+ return GetDecompiledText(Data.Code.ByName(codeName), context, settings);
}
///
- public string GetDecompiledText(UndertaleCode code, GlobalDecompileContext context = null)
+ public string GetDecompiledText(UndertaleCode code, GlobalDecompileContext context = null, IDecompileSettings settings = null)
{
if (code.ParentEntry is not null)
return $"// This code entry is a reference to an anonymous function within \"{code.ParentEntry.Name.Content}\", decompile that instead.";
- GlobalDecompileContext decompileContext = context is null ? new(Data, false) : context;
+ GlobalDecompileContext decompileContext = context is null ? new(Data) : context;
try
{
- return code != null ? Decompiler.Decompile(code, decompileContext) : "";
+ return code != null
+ ? new Underanalyzer.Decompiler.DecompileContext(decompileContext, code, settings ?? Data.ToolInfo.DecompilerSettings).DecompileToString()
+ : "";
}
catch (Exception e)
{
@@ -446,7 +449,7 @@ public string GetDisassemblyText(UndertaleCode code)
try
{
- return code != null ? code.Disassemble(Data.Variables, Data.CodeLocals.For(code)) : "";
+ return code != null ? code.Disassemble(Data.Variables, Data.CodeLocals?.For(code)) : "";
}
catch (Exception e)
{
@@ -633,17 +636,17 @@ public void ImportASMFile(string fileName, bool doParse = true, bool destroyASM
}
///
- public void ReplaceTextInGML(string codeName, string keyword, string replacement, bool caseSensitive = false, bool isRegex = false, GlobalDecompileContext context = null)
+ public void ReplaceTextInGML(string codeName, string keyword, string replacement, bool caseSensitive = false, bool isRegex = false, GlobalDecompileContext context = null, IDecompileSettings settings = null)
{
UndertaleCode code = Data.Code.ByName(codeName);
if (code is null)
throw new ScriptException($"No code named \"{codeName}\" was found!");
- ReplaceTextInGML(code, keyword, replacement, caseSensitive, isRegex, context);
+ ReplaceTextInGML(code, keyword, replacement, caseSensitive, isRegex, context, settings);
}
///
- public void ReplaceTextInGML(UndertaleCode code, string keyword, string replacement, bool caseSensitive = false, bool isRegex = false, GlobalDecompileContext context = null)
+ public void ReplaceTextInGML(UndertaleCode code, string keyword, string replacement, bool caseSensitive = false, bool isRegex = false, GlobalDecompileContext context = null, IDecompileSettings settings = null)
{
if (code == null) throw new ArgumentNullException(nameof(code));
if (code.ParentEntry is not null)
@@ -652,7 +655,7 @@ public void ReplaceTextInGML(UndertaleCode code, string keyword, string replacem
EnsureDataLoaded();
string passBack = "";
- GlobalDecompileContext decompileContext = context is null ? new(Data, false) : context;
+ GlobalDecompileContext decompileContext = context is null ? new(Data) : context;
if (!Data.ToolInfo.ProfileMode)
{
@@ -661,7 +664,7 @@ public void ReplaceTextInGML(UndertaleCode code, string keyword, string replacem
// It would just be recompiling an empty string and messing with null entries seems bad
if (code is null)
return;
- string originalCode = Decompiler.Decompile(code, decompileContext);
+ string originalCode = new Underanalyzer.Decompiler.DecompileContext(decompileContext, code, settings ?? Data.ToolInfo.DecompilerSettings).DecompileToString();
passBack = GetPassBack(originalCode, keyword, replacement, caseSensitive, isRegex);
// No need to compile something unchanged
if (passBack == originalCode)
@@ -749,7 +752,7 @@ void ImportCode(string codeName, string gmlCode, bool isGML = true, bool doParse
else if (code.ParentEntry is not null)
return;
- if (Data?.GeneralInfo.BytecodeVersion > 14 && Data.CodeLocals.ByName(codeName) == null)
+ if (Data.CodeLocals is not null && Data.CodeLocals.ByName(codeName) is null)
{
UndertaleCodeLocals locals = new UndertaleCodeLocals();
locals.Name = code.Name;
diff --git a/UndertaleModCli/Program.cs b/UndertaleModCli/Program.cs
index 67bd20024..b6dc58b48 100644
--- a/UndertaleModCli/Program.cs
+++ b/UndertaleModCli/Program.cs
@@ -195,7 +195,8 @@ public Program(FileInfo datafile, FileInfo[] scripts, FileInfo output, bool verb
typeof(JsonConvert).GetTypeInfo().Assembly,
typeof(System.Text.RegularExpressions.Regex).GetTypeInfo().Assembly,
typeof(TextureWorker).GetTypeInfo().Assembly,
- typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly)
+ typeof(ImageMagick.MagickImage).GetTypeInfo().Assembly,
+ typeof(Underanalyzer.Decompiler.DecompileContext).Assembly)
// "WithEmitDebugInformation(true)" not only lets us to see a script line number which threw an exception,
// but also provides other useful debug info when we run UMT in "Debug".
.WithEmitDebugInformation(true);
@@ -584,7 +585,8 @@ private void CliQuickInfo()
if (!Data.IsYYC())
{
Console.WriteLine($"{Data.Code.Count} Code Entries, {Data.Variables.Count} Variables, {Data.Functions.Count} Functions");
- Console.WriteLine($"{Data.CodeLocals.Count} Code locals, {Data.Strings.Count} Strings, {Data.EmbeddedTextures.Count} Embedded Textures");
+ var codeLocalsInfo = Data.CodeLocals is not null ? $"{Data.CodeLocals.Count} Code locals, " : "";
+ Console.WriteLine($"{codeLocalsInfo}{Data.Strings.Count} Strings, {Data.EmbeddedTextures.Count} Embedded Textures");
}
else
{
@@ -871,4 +873,4 @@ private void ProgressUpdater()
Thread.Sleep(100); //10 times per second
}
}
-}
\ No newline at end of file
+}
diff --git a/UndertaleModCli/UndertaleModCli.csproj b/UndertaleModCli/UndertaleModCli.csproj
index 67a9fcde8..0ef164e2f 100644
--- a/UndertaleModCli/UndertaleModCli.csproj
+++ b/UndertaleModCli/UndertaleModCli.csproj
@@ -22,6 +22,7 @@
+
diff --git a/UndertaleModLib/Compiler/AssemblyWriter.cs b/UndertaleModLib/Compiler/AssemblyWriter.cs
index 0eaa5fa18..48faecf77 100644
--- a/UndertaleModLib/Compiler/AssemblyWriter.cs
+++ b/UndertaleModLib/Compiler/AssemblyWriter.cs
@@ -21,8 +21,9 @@ public class CodeWriter
public List instructions;
public uint offset = 0;
public Stack typeStack = new Stack();
- public Stack loopContexts = new Stack();
- public Stack otherContexts = new Stack();
+ public Stack controlFlowContexts = new Stack();
+ public Stack loopContexts = new Stack();
+ public Stack funcContexts = new Stack();
public List ErrorMessages = new List();
public List varPatches = new List();
public List funcPatches = new List();
@@ -106,7 +107,7 @@ public List Finish()
bool defineArguments = true;
if (compileContext.OriginalCode != null)
{
- UndertaleCodeLocals locals = compileContext.Data?.CodeLocals.For(compileContext.OriginalCode);
+ UndertaleCodeLocals locals = compileContext.Data?.CodeLocals?.For(compileContext.OriginalCode);
if (locals != null)
{
// Update the code locals of the UndertaleCode
@@ -199,42 +200,74 @@ bool hasLocal(string name)
}
}
- foreach (var patch in varPatches)
+ // Patch variables in the compiled code
+ foreach (VariablePatch patch in varPatches)
{
- if (patch.InstType != InstanceType.Local)
+ // Only process non-local variables (extra check needed for room instance IDs, as InstType can overlap)
+ if (patch.InstType != InstanceType.Local || patch.VarType == VariableType.Instance)
{
- var realInstType = patch.InstType;
- if (realInstType >= 0)
- realInstType = InstanceType.Self;
- else if (realInstType == InstanceType.Other)
- realInstType = InstanceType.Self;
- else if (realInstType == InstanceType.Arg)
- realInstType = InstanceType.Builtin;
- else if (realInstType == InstanceType.Builtin)
- realInstType = InstanceType.Self; // used with @@This@@
- else if (realInstType == InstanceType.Stacktop)
- realInstType = InstanceType.Self; // used with @@GetInstance@@
+ // Change VARI instance type depending on context
+ InstanceType variInstanceType = patch.InstType switch
+ {
+ >= 0 => InstanceType.Self,
+ InstanceType.Other => InstanceType.Self,
+ InstanceType.Arg => InstanceType.Builtin,
+ InstanceType.Builtin => InstanceType.Self, // used with @@This@@
+ InstanceType.Stacktop => InstanceType.Self, // used with @@GetInstance@@
+ _ => patch.InstType
+ };
+
+ // Room instance ID variables should always be type self (even if inst type is negative)
+ if (patch.VarType == VariableType.Instance)
+ {
+ variInstanceType = InstanceType.Self;
+ }
// 2.3 variable fix
// Definitely needs at least some change when ++/-- support is added,
// since that does use instance type global
- if (CompileContext.GMS2_3 &&
- patch.VarType == VariableType.Array &&
- realInstType == InstanceType.Global)
- realInstType = InstanceType.Self;
-
- UndertaleVariable def = variables.EnsureDefined(patch.Name, realInstType,
- compileContext.BuiltInList.GlobalArray.ContainsKey(patch.Name) ||
- compileContext.BuiltInList.GlobalNotArray.ContainsKey(patch.Name) ||
- compileContext.BuiltInList.Instance.ContainsKey(patch.Name) ||
- compileContext.BuiltInList.InstanceLimitedEvent.ContainsKey(patch.Name),
- compileContext.Data.Strings, compileContext.Data);
+ if (CompileContext.GMS2_3 && patch.VarType == VariableType.Array && variInstanceType == InstanceType.Global)
+ {
+ variInstanceType = InstanceType.Self;
+ }
+
+ // Define (or locate) variable
+ UndertaleVariable def = variables.EnsureDefined(patch.Name, variInstanceType,
+ compileContext.BuiltInList.GlobalArray.ContainsKey(patch.Name) ||
+ compileContext.BuiltInList.GlobalNotArray.ContainsKey(patch.Name) ||
+ compileContext.BuiltInList.Instance.ContainsKey(patch.Name) ||
+ compileContext.BuiltInList.InstanceLimitedEvent.ContainsKey(patch.Name),
+ compileContext.Data.Strings, compileContext.Data);
if (patch.Target.Kind == Opcode.Pop)
+ {
+ // Pop instruction, set instruction's destination
patch.Target.Destination = new Reference(def, patch.VarType);
+ }
else
+ {
+ // All other instructions, just set instruction's value
patch.Target.Value = new Reference(def, patch.VarType);
+ }
+
+ // Perform final adjustments to the instance type
if (patch.VarType == VariableType.Normal)
+ {
+ if (patch.InstType == InstanceType.Self && compileContext.Data.IsVersionAtLeast(2024, 2))
+ {
+ // For some reason, 2024 versions seem to use builtin instead of self, for simple variables
+ patch.Target.TypeInst = InstanceType.Builtin;
+ }
+ else
+ {
+ // For all other normal variables, just use the existing instance type like usual
+ patch.Target.TypeInst = patch.InstType;
+ }
+ }
+ else if (patch.VarType == VariableType.Instance)
+ {
+ // In this case, the instance type is the room object instance ID
patch.Target.TypeInst = patch.InstType;
+ }
}
}
}
@@ -244,7 +277,7 @@ bool hasLocal(string name)
// The FUNC chunk contains references to builtin functions, and anonymous function definitions called gml_Script_...
// The anonymous functions are bound to names by code in Data.GlobalInit
// so to get an actual mapping from names to functions, you have to decompile all GlobalInit scripts...
- Decompiler.Decompiler.BuildSubFunctionCache(compileContext.Data);
+ Decompiler.GlobalDecompileContext.BuildGlobalFunctionCache(compileContext.Data);
foreach (var patch in funcPatches)
{
if (patch.isNewFunc)
@@ -260,7 +293,7 @@ bool hasLocal(string name)
ParentEntry = compileContext.OriginalCode,
Offset = patch.Offset,
ArgumentsCount = (ushort)patch.ArgCount,
- LocalsCount = compileContext.OriginalCode.LocalsCount // todo: use just the locals for the individual script
+ LocalsCount = (uint?)patch.FuncContext?.ParseInfo?.LocalVars?.Count ?? compileContext.OriginalCode.LocalsCount
};
compileContext.OriginalCode.ChildEntries.Add(childEntry);
int childEntryIndex = compileContext.Data.Code.IndexOf(compileContext.OriginalCode) + compileContext.OriginalCode.ChildEntries.Count;
@@ -269,7 +302,8 @@ bool hasLocal(string name)
UndertaleScript childScript = new()
{
Name = childName,
- Code = childEntry
+ Code = childEntry,
+ IsConstructor = patch.isNewConstructor
};
// If we don't set IsConstructor, the game will crash when creating the struct
if (patch.Name.StartsWith("___struct___")) childScript.IsConstructor = true;
@@ -284,8 +318,8 @@ bool hasLocal(string name)
compileContext.Data.Functions.Add(childFunction);
- compileContext.Data.KnownSubFunctions.Add(patch.Name, childFunction);
-
+ compileContext.Data.GlobalFunctions.DefineFunction(patch.Name, childFunction);
+
continue;
}
@@ -298,7 +332,10 @@ bool hasLocal(string name)
{
if (def != null && def.Autogenerated)
def = null;
- def ??= compileContext.Data.KnownSubFunctions.GetValueOrDefault(patch.Name);
+ if (def is null && compileContext.Data.GlobalFunctions.TryGetFunction(patch.Name, out Underanalyzer.IGMFunction foundFunction))
+ {
+ def = foundFunction as UndertaleFunction;
+ }
}
if (compileContext.ensureFunctionsDefined)
@@ -317,7 +354,10 @@ bool hasLocal(string name)
{
def = compileContext.Data.Functions.ByName(patch.Name);
// This code is only reachable using a 2.3 function definition. ("push.i gml_Script_scr_stuff")
- def ??= compileContext.Data.KnownSubFunctions.GetValueOrDefault(patch.Name);
+ if (def is null && compileContext.Data.GlobalFunctions.TryGetFunction(patch.Name, out Underanalyzer.IGMFunction foundFunction))
+ {
+ def = foundFunction as UndertaleFunction;
+ }
if (compileContext.ensureFunctionsDefined)
def ??= compileContext.Data.Functions.EnsureDefined(patch.Name, compileContext.Data.Strings, true);
@@ -371,29 +411,6 @@ bool hasLocal(string name)
}
}
- public class VariablePatch
- {
- public UndertaleInstruction Target;
- public string Name;
- public VariableType VarType;
- public InstanceType InstType;
- }
-
- public class FunctionPatch
- {
- public UndertaleInstruction Target;
- public string Name;
- public int ArgCount;
- public uint Offset;
- public bool isNewFunc = false;
- }
-
- public class StringPatch
- {
- public UndertaleInstruction Target;
- public string Content;
- }
-
public class Patch
{
public List Patches;
@@ -457,57 +474,61 @@ public void Add(UndertaleInstruction instr)
}
}
- public class OtherContext
+ public class VariablePatch
{
- public enum ContextKind
- {
- Switch,
- With
- }
+ public UndertaleInstruction Target;
+ public string Name;
+ public VariableType VarType;
+ public InstanceType InstType;
+ }
- public ContextKind Kind;
- public Patch Break;
- public Patch Continue;
- public DataType TypeToPop; // switch statements
- public bool BreakUsed = false;
- public bool ContinueUsed = false;
+ public class FunctionPatch
+ {
+ public UndertaleInstruction Target;
+ public string Name;
+ public int ArgCount;
+ public uint Offset;
+ public bool isNewFunc = false;
+ public bool isNewConstructor = false;
+ public FunctionContext FuncContext;
- public OtherContext(Patch @break, Patch @continue, DataType typeToPop)
+ public FunctionPatch(CodeWriter cw)
{
- Kind = ContextKind.Switch;
- Break = @break;
- Continue = @continue;
- TypeToPop = typeToPop;
+ FuncContext = (cw.funcContexts.Count > 0) ? cw.funcContexts.Peek() : null;
}
+ }
- public OtherContext(Patch @break, Patch @continue)
- {
- Kind = ContextKind.With;
- Break = @break;
- Continue = @continue;
- }
+ public class StringPatch
+ {
+ public UndertaleInstruction Target;
+ public string Content;
+ }
- public Patch UseBreak()
- {
- BreakUsed = true;
- return Break;
- }
+ public class FunctionContext
+ {
+ public Stack ControlFlowContexts { get; }
+ public Stack LoopContexts { get; }
+ public List NamedArguments { get; }
+ public Parser.FunctionParseInfo ParseInfo { get; }
- public Patch UseContinue()
+ public FunctionContext(Stack controlFlowContexts, Stack loopContexts,
+ List namedArguments, Parser.FunctionParseInfo parseInfo)
{
- ContinueUsed = true;
- return Continue;
+ ControlFlowContexts = controlFlowContexts;
+ LoopContexts = loopContexts;
+ NamedArguments = namedArguments;
+ ParseInfo = parseInfo;
}
}
- public struct LoopContext
+ public class ControlFlowContext
{
public Patch Break;
public Patch Continue;
public bool BreakUsed;
public bool ContinueUsed;
- public LoopContext(Patch @break, Patch @continue)
+ public ControlFlowContext(Patch @break, Patch @continue)
{
Break = @break;
Continue = @continue;
@@ -528,30 +549,53 @@ public Patch UseContinue()
}
}
- private static Patch HelpUseBreak(ref Stack s)
+ public class LoopContext : ControlFlowContext
{
- LoopContext c = s.Pop();
- Patch res = c.UseBreak();
- s.Push(c);
- return res;
+ public LoopContext(Patch @break, Patch @continue) : base(@break, @continue)
+ {
+ }
}
- private static Patch HelpUseContinue(ref Stack s)
+
+ public class RepeatLoopContext : LoopContext
{
- LoopContext c = s.Pop();
- Patch res = c.UseContinue();
- s.Push(c);
- return res;
+ public RepeatLoopContext(Patch @break, Patch @continue) : base(@break, @continue)
+ {
+ }
}
- private static Patch HelpUseBreak(ref Stack s)
+
+ public class SwitchWithContext : ControlFlowContext
+ {
+ public enum ContextKind
+ {
+ Switch,
+ With
+ }
+
+ public ContextKind Kind;
+ public DataType TypeToPop; // switch statements
+
+ public SwitchWithContext(Patch @break, Patch @continue, DataType typeToPop) : base(@break, @continue)
+ {
+ Kind = ContextKind.Switch;
+ TypeToPop = typeToPop;
+ }
+
+ public SwitchWithContext(Patch @break, Patch @continue) : base(@break, @continue)
+ {
+ Kind = ContextKind.With;
+ }
+ }
+
+ private static Patch HelpUseBreak(ref Stack s)
{
- OtherContext c = s.Pop();
+ ControlFlowContext c = s.Pop();
Patch res = c.UseBreak();
s.Push(c);
return res;
}
- private static Patch HelpUseContinue(ref Stack s)
+ private static Patch HelpUseContinue(ref Stack s)
{
- OtherContext c = s.Pop();
+ ControlFlowContext c = s.Pop();
Patch res = c.UseContinue();
s.Push(c);
return res;
@@ -564,7 +608,7 @@ public static CodeWriter AssembleStatement(CompileContext compileContext, Parser
return cw;
}
- private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int remaining = -1)
+ private static void AssembleStatement(CodeWriter cw, Parser.Statement s)
{
switch (s.Kind)
{
@@ -574,7 +618,7 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
{
for (int i = 0; i < s.Children.Count; i++)
{
- AssembleStatement(cw, s.Children[i], s.Children.Count - i);
+ AssembleStatement(cw, s.Children[i]);
}
}
break;
@@ -619,6 +663,25 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
}
}
break;
+ case Parser.Statement.StatementKind.FunctionDefAssign:
+ {
+ AssembleExpression(cw, s.Children[1], s.Children[0]);
+
+ bool isStructDef = s.Children[0].Text.StartsWith("___struct___");
+ cw.varPatches.Add(new VariablePatch()
+ {
+ Target = cw.EmitRef(Opcode.Pop, DataType.Variable, DataType.Variable),
+ Name = s.Children[0].Text,
+ InstType = isStructDef ? InstanceType.Static : InstanceType.Self,
+ VarType = VariableType.StackTop
+ });
+ if (!isStructDef)
+ {
+ cw.typeStack.Pop();
+ cw.Emit(Opcode.Popz, DataType.Variable);
+ }
+ break;
+ }
case Parser.Statement.StatementKind.Pre:
AssemblePostOrPre(cw, s, false, false);
break;
@@ -685,8 +748,11 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
endLoopPatch.Add(cw.Emit(Opcode.Bf));
var continuePatch = Patch.Start();
- cw.loopContexts.Push(new LoopContext(endLoopPatch, continuePatch));
+ var context = new LoopContext(endLoopPatch, continuePatch);
+ cw.controlFlowContexts.Push(context);
+ cw.loopContexts.Push(context);
AssembleStatement(cw, s.Children[3]); // body
+ cw.controlFlowContexts.Pop();
cw.loopContexts.Pop();
continuePatch.Finish(cw);
AssembleStatement(cw, s.Children[2]); // code that runs each iteration, usually "i++" or something
@@ -712,8 +778,11 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
}
Patch endLoopPatch = Patch.Start();
endLoopPatch.Add(cw.Emit(Opcode.Bf));
- cw.loopContexts.Push(new LoopContext(endLoopPatch, conditionPatch));
+ var context = new LoopContext(endLoopPatch, conditionPatch);
+ cw.controlFlowContexts.Push(context);
+ cw.loopContexts.Push(context);
AssembleStatement(cw, s.Children[1]); // body
+ cw.controlFlowContexts.Pop();
cw.loopContexts.Pop();
conditionPatch.Add(cw.Emit(Opcode.B));
conditionPatch.Finish(cw);
@@ -746,7 +815,9 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
.ComparisonKind = ComparisonType.LTE;
endPatch.Add(cw.Emit(Opcode.Bt));
- cw.loopContexts.Push(new LoopContext(endPatch, repeatPatch));
+ var context = new RepeatLoopContext(endPatch, repeatPatch);
+ cw.controlFlowContexts.Push(context);
+ cw.loopContexts.Push(context);
Patch startPatch = Patch.StartHere(cw);
AssembleStatement(cw, s.Children[1]); // body
@@ -755,13 +826,15 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
cw.Emit(Opcode.Push, DataType.Int32).Value = 1; // This is also weird- normally it's pushi.e
cw.Emit(Opcode.Sub, DataType.Int32, DataType.Int32);
cw.Emit(Opcode.Dup, DataType.Int32).Extra = 0;
- cw.Emit(Opcode.Conv, DataType.Int32, DataType.Boolean);
+ if (!cw.compileContext.Data.IsVersionAtLeast(2022, 11))
+ cw.Emit(Opcode.Conv, DataType.Int32, DataType.Boolean);
startPatch.Add(cw.Emit(Opcode.Bt));
startPatch.Finish(cw);
endPatch.Finish(cw);
cw.Emit(Opcode.Popz, DataType.Int32); // Cleans up the stack of the decrementing value, which at this point should be <= 0
+ cw.controlFlowContexts.Pop();
cw.loopContexts.Pop();
}
break;
@@ -776,7 +849,9 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
Patch endPatch = Patch.Start();
Patch repeatPatch = Patch.Start();
- cw.loopContexts.Push(new LoopContext(endPatch, repeatPatch));
+ var context = new LoopContext(endPatch, repeatPatch);
+ cw.controlFlowContexts.Push(context);
+ cw.loopContexts.Push(context);
Patch startPatch = Patch.StartHere(cw);
AssembleStatement(cw, s.Children[0]); // body
@@ -792,16 +867,16 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
startPatch.Finish(cw);
endPatch.Finish(cw);
+ cw.controlFlowContexts.Pop();
cw.loopContexts.Pop();
}
break;
case Parser.Statement.StatementKind.Switch:
{
Patch endPatch = Patch.Start();
- bool isEnclosingLoop = (cw.loopContexts.Count > 0);
Patch continueEndPatch = null;
- LoopContext enclosingContext = default(LoopContext);
- if (isEnclosingLoop)
+ ControlFlowContext enclosingContext = null;
+ if (cw.loopContexts.Count > 0)
{
continueEndPatch = Patch.Start();
enclosingContext = cw.loopContexts.Peek();
@@ -811,7 +886,7 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
AssembleExpression(cw, s.Children[0]);
var compareType = cw.typeStack.Pop();
- cw.otherContexts.Push(new OtherContext(endPatch, continueEndPatch, compareType));
+ cw.controlFlowContexts.Push(new SwitchWithContext(endPatch, continueEndPatch, compareType));
List> cases = new List>();
Patch defaultPatch = null;
@@ -895,8 +970,8 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
}
// Write part at end in case a continue statement is used
- OtherContext context = cw.otherContexts.Pop();
- if (isEnclosingLoop && context.ContinueUsed)
+ ControlFlowContext context = cw.controlFlowContexts.Pop();
+ if (enclosingContext is not null && context.ContinueUsed)
{
endPatch.Add(cw.Emit(Opcode.B));
@@ -918,7 +993,7 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
s.Children[0].Kind == Parser.Statement.StatementKind.ExprConstant &&
((InstanceType)s.Children[0].Constant.valueNumber).In(InstanceType.Other, InstanceType.Self))
{
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Call, DataType.Int32),
Name = (InstanceType)s.Children[0].Constant.valueNumber == InstanceType.Other ? "@@Other@@" : "@@This@@",
@@ -937,7 +1012,9 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
cw.Emit(Opcode.Conv, type, DataType.Int32);
}
- cw.otherContexts.Push(new OtherContext(endPatch, popEnvPatch));
+ var context = new SwitchWithContext(endPatch, popEnvPatch);
+ cw.controlFlowContexts.Push(context);
+ cw.loopContexts.Push(context);
popEnvPatch.Add(cw.Emit(Opcode.PushEnv));
Patch startPatch = Patch.StartHere(cw);
@@ -948,7 +1025,9 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
startPatch.Add(cw.Emit(Opcode.PopEnv));
startPatch.Finish(cw);
- if (cw.otherContexts.Pop().BreakUsed)
+ cw.controlFlowContexts.Pop();
+ cw.loopContexts.Pop();
+ if (context.BreakUsed)
{
Patch cleanUpEndPatch = Patch.Start();
cleanUpEndPatch.Add(cw.Emit(Opcode.B));
@@ -968,29 +1047,23 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
}
break;
case Parser.Statement.StatementKind.Continue:
- if (cw.loopContexts.Count == 0 && (cw.otherContexts.Count == 0 || cw.otherContexts.Peek().Continue == null))
+ if (cw.loopContexts.Count == 0 && (cw.controlFlowContexts.Count == 0 || cw.controlFlowContexts.Peek().Continue == null))
{
AssemblyWriterError(cw, "Continue statement placed outside of any loops.", s.Token);
}
else
{
- if (cw.otherContexts.Count > 0 && cw.otherContexts.Peek().Continue != null)
- HelpUseContinue(ref cw.otherContexts).Add(cw.Emit(Opcode.B));
- else
- HelpUseContinue(ref cw.loopContexts).Add(cw.Emit(Opcode.B));
+ HelpUseContinue(ref cw.controlFlowContexts).Add(cw.Emit(Opcode.B));
}
break;
case Parser.Statement.StatementKind.Break:
- if (cw.loopContexts.Count == 0 && cw.otherContexts.Count == 0)
+ if (cw.loopContexts.Count == 0 && cw.controlFlowContexts.Count == 0)
{
AssemblyWriterError(cw, "Break statement placed outside of any loops.", s.Token);
}
else
{
- if (cw.otherContexts.Count > 0 && cw.otherContexts.Peek().Break != null)
- HelpUseBreak(ref cw.otherContexts).Add(cw.Emit(Opcode.B));
- else
- HelpUseBreak(ref cw.loopContexts).Add(cw.Emit(Opcode.B));
+ HelpUseBreak(ref cw.controlFlowContexts).Add(cw.Emit(Opcode.B));
}
break;
case Parser.Statement.StatementKind.FunctionCall:
@@ -1015,7 +1088,11 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
}
// Clean up contexts if necessary
- bool useLocalVar = (cw.otherContexts.Count != 0);
+ bool useLocalVar;
+ if (CompileContext.GMS2_3)
+ useLocalVar = cw.controlFlowContexts.Any(c => c is SwitchWithContext or RepeatLoopContext);
+ else
+ useLocalVar = cw.controlFlowContexts.Any(c => c is SwitchWithContext);
if (useLocalVar)
{
// Put the return value into a local variable (GM does this as well)
@@ -1029,20 +1106,31 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
VarType = VariableType.Normal
});
cw.compileContext.LocalVars["$$$$temp$$$$"] = "$$$$temp$$$$";
+ if (cw.funcContexts.Count > 0)
+ {
+ cw.funcContexts.Peek().ParseInfo.LocalVars.Add("$$$$temp$$$$");
+ }
}
- foreach (OtherContext oc in cw.otherContexts)
+ foreach (ControlFlowContext c in cw.controlFlowContexts)
{
- if (oc.Kind == OtherContext.ContextKind.Switch)
+ if (c is SwitchWithContext sw)
{
- cw.Emit(Opcode.Popz, oc.TypeToPop);
- }
- else
+ if (sw.Kind == SwitchWithContext.ContextKind.Switch)
+ {
+ cw.Emit(Opcode.Popz, sw.TypeToPop);
+ }
+ else
+ {
+ // With
+ var dropPopenv = cw.Emit(Opcode.PopEnv);
+ dropPopenv.JumpOffsetPopenvExitMagic = true;
+ if (cw.compileContext.Data?.GeneralInfo?.BytecodeVersion <= 14)
+ dropPopenv.JumpOffset = -1048576; // magic for older versions
+ }
+ }
+ else if (CompileContext.GMS2_3 && c is RepeatLoopContext)
{
- // With
- var dropPopenv = cw.Emit(Opcode.PopEnv);
- dropPopenv.JumpOffsetPopenvExitMagic = true;
- if (cw.compileContext.Data?.GeneralInfo?.BytecodeVersion <= 14)
- dropPopenv.JumpOffset = -1048576; // magic for older versions
+ cw.Emit(Opcode.Popz, DataType.Int32);
}
}
if (useLocalVar)
@@ -1066,6 +1154,26 @@ private static void AssembleStatement(CodeWriter cw, Parser.Statement s, int rem
case Parser.Statement.StatementKind.Exit:
AssembleExit(cw);
break;
+ case Parser.Statement.StatementKind.Enum:
+ // No assembly logic for this
+ break;
+ case Parser.Statement.StatementKind.Throw:
+ AssembleExpression(cw, s.Children[0]);
+ if (cw.typeStack.Peek() != DataType.Variable)
+ {
+ cw.Emit(Opcode.Conv, cw.typeStack.Pop(), DataType.Variable);
+ cw.typeStack.Push(DataType.Variable);
+ }
+ cw.funcPatches.Add(new FunctionPatch(cw)
+ {
+ Target = cw.EmitRef(Opcode.Call, DataType.Int32),
+ Name = "@@throw@@",
+ ArgCount = 1
+ });
+ break;
+ case Parser.Statement.StatementKind.New:
+ AssembleNew(cw, s, false);
+ break;
default:
AssemblyWriterError(cw, "Expected a statement, none found", s.Token);
break;
@@ -1136,27 +1244,29 @@ private static void AssemblePostPreStackOperation(CodeWriter cw, bool isSingle,
private static void AssembleExit(CodeWriter cw)
{
- // First switch statements
- foreach (OtherContext oc in cw.otherContexts)
+ foreach (ControlFlowContext c in cw.controlFlowContexts)
{
- if (oc.Kind == OtherContext.ContextKind.Switch)
+ if (c is SwitchWithContext sw)
{
- cw.Emit(Opcode.Popz, oc.TypeToPop);
+ if (sw.Kind == SwitchWithContext.ContextKind.Switch)
+ {
+ cw.Emit(Opcode.Popz, sw.TypeToPop);
+ }
+ else
+ {
+ // With
+ var dropPopenv = cw.Emit(Opcode.PopEnv);
+ dropPopenv.JumpOffsetPopenvExitMagic = true;
+ if (cw.compileContext.Data?.GeneralInfo?.BytecodeVersion <= 14)
+ dropPopenv.JumpOffset = -1048576; // magic for older versions
+ }
}
- }
-
- // Then with statements
- foreach (OtherContext oc in cw.otherContexts)
- {
- if (oc.Kind == OtherContext.ContextKind.With)
+ else if (CompileContext.GMS2_3 && c is RepeatLoopContext)
{
- var dropPopenv = cw.Emit(Opcode.PopEnv);
- dropPopenv.JumpOffsetPopenvExitMagic = true;
- if (cw.compileContext.Data?.GeneralInfo?.BytecodeVersion <= 14)
- dropPopenv.JumpOffset = -1048576; // magic for older versions
+ cw.Emit(Opcode.Popz, DataType.Int32);
}
}
-
+
cw.Emit(Opcode.Exit, DataType.Int32);
}
@@ -1175,7 +1285,7 @@ private static void AssembleFunctionCall(CodeWriter cw, Parser.Statement fc)
}
}
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Call, DataType.Int32),
Name = fc.Text,
@@ -1198,7 +1308,7 @@ private static void AssembleStructDef(CodeWriter cw, Parser.Statement str)
AssembleStatement(cw, str.Children[1]);
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Call, DataType.Int32),
Name = "@@NewGMLObject@@",
@@ -1208,6 +1318,50 @@ private static void AssembleStructDef(CodeWriter cw, Parser.Statement str)
cw.typeStack.Push(DataType.Variable);
}
+ private static void AssembleNew(CodeWriter cw, Parser.Statement e, bool expression)
+ {
+ // Needs to push args onto stack backwards
+ Parser.Statement fc = e.Children[0];
+ for (int i = fc.Children.Count - 1; i >= 0; i--)
+ {
+ AssembleExpression(cw, fc.Children[i]);
+
+ // Convert to Variable data type
+ var typeToConvertFrom = cw.typeStack.Pop();
+ if (typeToConvertFrom != DataType.Variable)
+ {
+ cw.Emit(Opcode.Conv, typeToConvertFrom, DataType.Variable);
+ }
+ }
+
+ // Push reference to constructor function
+ cw.funcPatches.Add(new FunctionPatch(cw)
+ {
+ Target = cw.EmitRef(Opcode.Push, DataType.Int32),
+ Name = fc.Text,
+ ArgCount = -1
+ });
+ cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
+
+ // Create new object
+ cw.funcPatches.Add(new FunctionPatch(cw)
+ {
+ Target = cw.EmitRef(Opcode.Call, DataType.Int32),
+ Name = "@@NewGMLObject@@",
+ ArgCount = fc.Children.Count + 1
+ });
+
+ // If in expression, a variable data type is on the stack; otherwise, pop the unused data
+ if (expression)
+ {
+ cw.typeStack.Push(DataType.Variable);
+ }
+ else
+ {
+ cw.Emit(Opcode.Popz, DataType.Variable);
+ }
+ }
+
private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser.Statement funcDefName = null)
{
switch (e.Kind)
@@ -1302,60 +1456,117 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
break;
case Parser.Statement.StatementKind.FunctionDef:
{
- if (e.Children.Count != 2)
+ if (e.Children.Count < 2 || e.Children.Count > 4)
{
AssemblyWriterError(cw, "Malformed function assignment.", e.Token);
break;
}
- bool isStructDef = funcDefName.Text.StartsWith("___struct___");
-
- Patch startPatch = Patch.StartHere(cw);
+ bool isConstructor = e.Children.Count >= 3;
+ Parser.Statement baseCall = null;
+ if (isConstructor)
+ {
+ e.Children.RemoveAt(0);
+ if (e.Children.Count >= 3)
+ {
+ baseCall = e.Children[0];
+ e.Children.RemoveAt(0);
+ }
+ }
+
+ // Construct new function context
+ List namedArgs = new();
+ foreach (Parser.Statement argName in e.Children[0].Children)
+ namedArgs.Add(argName.Text);
+ FunctionContext newFuncContext = new(cw.controlFlowContexts, cw.loopContexts, namedArgs, cw.compileContext.FunctionParseInfo[e]);
+
+ // Branch around function declaration
Patch endPatch = Patch.Start();
endPatch.Add(cw.Emit(Opcode.B));
+
// we're accessing a subfunction here, so build the cache if needed
- Decompiler.Decompiler.BuildSubFunctionCache(cw.compileContext.Data);
+ Decompiler.GlobalDecompileContext.BuildGlobalFunctionCache(cw.compileContext.Data);
- //Attempt to find the function before rushing to create a new one
+ if (funcDefName is null)
+ {
+ AssemblyWriterError(cw, "Anonymous function compilation support does not work yet.", e.Token);
+ break;
+ }
+
+ // Attempt to find the function before rushing to create a new one
var func = cw.compileContext.Data.Functions.FirstOrDefault(f => f.Name.Content == "gml_Script_" + funcDefName.Text);
- if (func != null)
- cw.compileContext.Data.KnownSubFunctions.TryAdd(funcDefName.Text, func);
-
- if (cw.compileContext.Data.KnownSubFunctions.ContainsKey(funcDefName.Text))
+ if (func != null && !cw.compileContext.Data.GlobalFunctions.FunctionNameExists(funcDefName.Text))
+ {
+ cw.compileContext.Data.GlobalFunctions.DefineFunction(funcDefName.Text, func);
+ }
+
+ if (cw.compileContext.Data.GlobalFunctions.TryGetFunction(funcDefName.Text, out Underanalyzer.IGMFunction foundFunction))
{
- string subFunctionName = cw.compileContext.Data.KnownSubFunctions[funcDefName.Text].Name.Content;
+ string subFunctionName = foundFunction.Name.Content;
UndertaleCode childEntry = cw.compileContext.OriginalCode.ChildEntries.ByName(subFunctionName);
childEntry.Offset = cw.offset * 4;
childEntry.ArgumentsCount = (ushort)e.Children[0].Children.Count;
- childEntry.LocalsCount = cw.compileContext.OriginalCode.LocalsCount; // todo: use just the locals for the individual script
+ childEntry.LocalsCount = (uint?)newFuncContext.ParseInfo?.LocalVars?.Count ?? cw.compileContext.OriginalCode.LocalsCount;
+
+ UndertaleScript script = cw.compileContext.Data.Scripts.ByName(childEntry.Name.Content);
+ if (script is not null)
+ {
+ script.IsConstructor = isConstructor;
+ }
}
else // we're making a new function baby
{
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Name = funcDefName.Text,
Offset = cw.offset * 4,
ArgCount = (ushort)e.Children[0].Children.Count,
- isNewFunc = true
+ isNewFunc = true,
+ isNewConstructor = isConstructor
+ });
+ }
+
+ cw.funcContexts.Push(newFuncContext);
+ cw.loopContexts = new();
+ cw.controlFlowContexts = new();
+
+ if (baseCall is not null)
+ {
+ AssembleFunctionCall(cw, baseCall);
+ cw.funcPatches.Add(new FunctionPatch(cw)
+ {
+ Target = cw.EmitRef(Opcode.Push, DataType.Int32),
+ Name = baseCall.Text,
+ ArgCount = -1
+ });
+ cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
+ cw.funcPatches.Add(new FunctionPatch(cw)
+ {
+ Target = cw.EmitRef(Opcode.Call, DataType.Int32),
+ Name = "@@CopyStatic@@",
+ ArgCount = 1
});
}
- cw.loopContexts.Push(new LoopContext(endPatch, startPatch));
AssembleStatement(cw, e.Children[1]); // body
AssembleExit(cw);
- cw.loopContexts.Pop();
endPatch.Finish(cw);
- cw.funcPatches.Add(new FunctionPatch()
+ FunctionContext funcContext = cw.funcContexts.Pop();
+ cw.loopContexts = funcContext.LoopContexts;
+ cw.controlFlowContexts = funcContext.ControlFlowContexts;
+
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Push, DataType.Int32),
Name = funcDefName.Text,
ArgCount = -1
});
cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
- if (isStructDef)
+
+ if (isConstructor)
{
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Call, DataType.Int32),
Name = "@@NullObject@@",
@@ -1367,7 +1578,8 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1;
cw.Emit(Opcode.Conv, DataType.Int32, DataType.Variable);
}
- cw.funcPatches.Add(new FunctionPatch()
+
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Call, DataType.Int32),
Name = "method",
@@ -1375,12 +1587,18 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
});
cw.typeStack.Push(DataType.Variable);
cw.Emit(Opcode.Dup, DataType.Variable).Extra = 0;
- if (isStructDef)
+
+ if (funcDefName.Text.StartsWith("___struct___"))
cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-16;
+ else if (cw.compileContext.Data.IsVersionAtLeast(2024) || cw.compileContext.OriginalCode.Name.Content == $"gml_GlobalScript_{funcDefName.Text}")
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1;
else
- cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-1; // todo: -6 sometimes?
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-6;
}
break;
+ case Parser.Statement.StatementKind.ExprNew:
+ AssembleNew(cw, e, true);
+ break;
case Parser.Statement.StatementKind.ExprBinaryOp:
{
// Push the left value onto the stack
@@ -1637,7 +1855,7 @@ private static void AssembleExpression(CodeWriter cw, Parser.Statement e, Parser
break;
case Parser.Statement.StatementKind.ExprFuncName:
{
- cw.funcPatches.Add(new FunctionPatch()
+ cw.funcPatches.Add(new FunctionPatch(cw)
{
Target = cw.EmitRef(Opcode.Push, DataType.Int32),
Name = e.Text,
@@ -1767,6 +1985,9 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
{
if (e.Children[0].Children.Count != 0)
{
+ // Final processing on variable
+ var processedArray = CheckFor23BuiltinOrArg(cw, e.Children[0].Text, e.Children[0].ID);
+
if (e.Children[0].Kind == Parser.Statement.StatementKind.ExprFunctionCall)
{
// Function call
@@ -1776,10 +1997,8 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
}
// Special array access- instance type needs to be pushed beforehand
- if (CompileContext.GMS2_3 && (cw.compileContext.BuiltInList.GlobalArray.ContainsKey(e.Children[0].Text) || cw.compileContext.BuiltInList.GlobalNotArray.ContainsKey(e.Children[0].Text)))
- cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)(e.Children[0].Text == "argument" ? InstanceType.Arg : InstanceType.Builtin); // hack x2
- else
- cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)e.Children[0].ID;
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)processedArray.NewID;
+
// Pushing array (incl. 2D) but not popping
AssembleArrayPush(cw, e.Children[0], !duplicate);
@@ -1815,8 +2034,8 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
cw.varPatches.Add(new VariablePatch()
{
Target = cw.EmitRef(Opcode.Push, DataType.Variable),
- Name = e.Children[0].Text,
- InstType = GetIDPrefixSpecial(e.Children[0].ID),
+ Name = processedArray.NewVarName,
+ InstType = GetIDPrefixSpecial(processedArray.NewID),
VarType = VariableType.Array
});
}
@@ -1825,43 +2044,58 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
return;
}
isSingle = true;
+
+ // Get variable type (if self) and instance ID
+ VariableType varTypeIfSelf = VariableType.Normal;
int id = e.Children[0].ID;
if (id >= 100000)
+ {
+ // This is a room instance ID, encoded as a 16-bit integer apparently
+ varTypeIfSelf = VariableType.Instance;
id -= 100000;
+ }
+
string name = e.Children[0].Text;
switch (id)
{
case -1:
- if (cw.compileContext.BuiltInList.GlobalArray.ContainsKey(name) || cw.compileContext.BuiltInList.GlobalNotArray.ContainsKey(name))
+ var processedSelf = CheckFor23BuiltinOrArg(cw, name, id);
+ if (processedSelf.NewID != id)
{
- if (CompileContext.GMS2_3 &&
- name.In(
- "argument0", "argument1", "argument2", "argument3",
- "argument4", "argument5", "argument6", "argument7",
- "argument8", "argument9", "argument10", "argument11",
- "argument12", "argument13", "argument14", "argument15"))
+ if (processedSelf.NewID == (int)InstanceType.Builtin)
{
- // 2.3 argument (excuse the condition... the ID seems to be lost, so this is the easiest way to check)
+ // Builtin global
cw.varPatches.Add(new VariablePatch()
{
- Target = cw.EmitRef(Opcode.Push, DataType.Variable),
- Name = name,
- InstType = InstanceType.Arg,
+ Target = cw.EmitRef(useNoSpecificType ? Opcode.Push : Opcode.PushBltn, DataType.Variable),
+ Name = processedSelf.NewVarName,
+ InstType = (CompileContext.GMS2_3 && !useNoSpecificType) ? InstanceType.Builtin : InstanceType.Self,
VarType = VariableType.Normal
});
}
else
{
- // Builtin global
+ // Argument
cw.varPatches.Add(new VariablePatch()
{
- Target = cw.EmitRef(useNoSpecificType ? Opcode.Push : Opcode.PushBltn, DataType.Variable),
- Name = name,
- InstType = (CompileContext.GMS2_3 && !useNoSpecificType) ? InstanceType.Builtin : InstanceType.Self,
+ Target = cw.EmitRef(Opcode.Push, DataType.Variable),
+ Name = processedSelf.NewVarName,
+ InstType = (InstanceType)processedSelf.NewID,
VarType = VariableType.Normal
});
}
}
+ else if (cw.compileContext.BuiltInList.GlobalArray.ContainsKey(name) || cw.compileContext.BuiltInList.GlobalNotArray.ContainsKey(name))
+ {
+ // Builtin global
+ cw.varPatches.Add(new VariablePatch()
+ {
+ Target = cw.EmitRef(useNoSpecificType ? Opcode.Push : Opcode.PushBltn, DataType.Variable),
+ Name = name,
+ InstType = (CompileContext.GMS2_3 && !useNoSpecificType) ? InstanceType.Builtin : InstanceType.Self,
+ VarType = VariableType.Normal
+ });
+ }
else
{
cw.varPatches.Add(new VariablePatch()
@@ -1869,7 +2103,7 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
Target = cw.EmitRef(Opcode.Push, DataType.Variable),
Name = name,
InstType = InstanceType.Self,
- VarType = VariableType.Normal
+ VarType = varTypeIfSelf
});
}
break;
@@ -1939,7 +2173,15 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
cw.typeStack.Push(DataType.Variable);
if (notLast)
{
- cw.Emit(Opcode.Conv, cw.typeStack.Pop(), DataType.Int32);
+ if (CompileContext.GMS2_3)
+ {
+ cw.typeStack.Pop();
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-9; // stacktop conversion
+ }
+ else
+ {
+ cw.Emit(Opcode.Conv, cw.typeStack.Pop(), DataType.Int32);
+ }
}
else
isArray = true;
@@ -1960,7 +2202,15 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
cw.typeStack.Push(DataType.Variable);
if (next + 1 < e.Children.Count)
{
- cw.Emit(Opcode.Conv, cw.typeStack.Pop(), DataType.Int32);
+ if (CompileContext.GMS2_3)
+ {
+ cw.typeStack.Pop();
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-9; // stacktop conversion
+ }
+ else
+ {
+ cw.Emit(Opcode.Conv, cw.typeStack.Pop(), DataType.Int32);
+ }
}
}
}
@@ -1974,13 +2224,17 @@ private static void AssembleVariablePush(CodeWriter cw, Parser.Statement e, out
string variableName = e.Text;
if (!fix2.WasIDSet || fix2.ID >= 100000)
{
- if (cw.compileContext.LocalVars.ContainsKey(variableName))
+ if (cw.funcContexts.Count > 0 && cw.funcContexts.Peek().ParseInfo.LocalVars.Contains(variableName))
+ {
+ fix2.ID = (int)InstanceType.Local;
+ }
+ else if (cw.funcContexts.Count == 0 && cw.compileContext.LocalVars.ContainsKey(variableName))
{
- fix2.ID = -7; // local
+ fix2.ID = (int)InstanceType.Local;
}
else
{
- fix2.ID = -1; // self
+ fix2.ID = (int)InstanceType.Self;
}
}
fix.Children.Add(fix2);
@@ -2024,10 +2278,11 @@ private static void AssembleArrayPush(CodeWriter cw, Parser.Statement a, bool ar
else
{
// Surprise! One small instruction.
+ var processed = CheckFor23BuiltinOrArg(cw, a.Text, a.ID);
cw.varPatches.Add(new VariablePatch()
{
Target = cw.EmitRef(Opcode.Push, DataType.Variable),
- Name = a.Text,
+ Name = processed.NewVarName,
InstType = InstanceType.Self,
VarType = arraypushaf ? VariableType.ArrayPushAF : VariableType.ArrayPopAF
});
@@ -2069,6 +2324,44 @@ private static InstanceType GetIDPrefixSpecial(int ID)
};
}
+ private static (string NewVarName, int NewID) CheckFor23BuiltinOrArg(CodeWriter cw, string varName, int id)
+ {
+ if (CompileContext.GMS2_3)
+ {
+ if (cw.funcContexts.Count > 0)
+ {
+ FunctionContext topFunctionCtx = cw.funcContexts.Peek();
+ int argIndex = topFunctionCtx.NamedArguments.IndexOf(varName);
+ if (argIndex != -1)
+ {
+ // Found a named argument; rename it to builtin variable
+ return ($"argument{argIndex}", (int)InstanceType.Arg);
+ }
+ }
+
+ if (cw.compileContext.BuiltInList.GlobalArray.ContainsKey(varName) || cw.compileContext.BuiltInList.GlobalNotArray.ContainsKey(varName))
+ {
+ if (varName.In(
+ "argument",
+ "argument0", "argument1", "argument2", "argument3",
+ "argument4", "argument5", "argument6", "argument7",
+ "argument8", "argument9", "argument10", "argument11",
+ "argument12", "argument13", "argument14", "argument15"))
+ {
+ // Found normal argument variable
+ return (varName, (int)InstanceType.Arg);
+ }
+ else
+ {
+ // Found builtin variable
+ return (varName, (int)InstanceType.Builtin);
+ }
+ }
+ }
+
+ return (varName, id);
+ }
+
private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, DataType typeToStore, bool skip = false, bool duplicate = false)
{
if (s.Kind == Parser.Statement.StatementKind.ExprVariableRef)
@@ -2081,6 +2374,9 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
if (s.Children[0].Children.Count != 0)
{
+ // Final processing on variable
+ var processedArray = CheckFor23BuiltinOrArg(cw, s.Children[0].Text, s.Children[0].ID);
+
// Special array set- instance type needs to be pushed beforehand
if (!skip)
{
@@ -2090,7 +2386,7 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
cw.Emit(Opcode.Conv, typeToStore, DataType.Variable);
typeToStore = DataType.Variable;
}
- cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)s.Children[0].ID;
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)processedArray.NewID;
AssembleArrayPush(cw, s.Children[0]);
}
@@ -2118,55 +2414,32 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
cw.varPatches.Add(new VariablePatch()
{
Target = cw.EmitRef(Opcode.Pop, popLocation, typeToStore),
- Name = s.Children[0].Text,
- InstType = GetIDPrefixSpecial(s.Children[0].ID),
+ Name = processedArray.NewVarName,
+ InstType = GetIDPrefixSpecial(processedArray.NewID),
VarType = VariableType.Array
});
}
return;
}
- // Simple common assignment
+ // Simple common assignment. Get variable type and instance ID
+ VariableType varTypeIfSelf = VariableType.Normal;
int id = s.Children[0].ID;
if (id >= 100000)
- id -= 100000;
- if (CompileContext.GMS2_3 && (cw.compileContext.BuiltInList.GlobalArray.ContainsKey(s.Children[0].Text) || cw.compileContext.BuiltInList.GlobalNotArray.ContainsKey(s.Children[0].Text)))
{
- if (s.Children[0].Text.In(
- "argument0", "argument1", "argument2", "argument3",
- "argument4", "argument5", "argument6", "argument7",
- "argument8", "argument9", "argument10", "argument11",
- "argument12", "argument13", "argument14", "argument15"))
- {
- cw.varPatches.Add(new VariablePatch()
- {
- Target = cw.EmitRef(Opcode.Pop, popLocation, typeToStore),
- Name = s.Children[0].Text,
- InstType = InstanceType.Arg,
- VarType = VariableType.Normal
- });
- }
- else
- {
- cw.varPatches.Add(new VariablePatch()
- {
- Target = cw.EmitRef(Opcode.Pop, popLocation, typeToStore),
- Name = s.Children[0].Text,
- InstType = InstanceType.Builtin,
- VarType = VariableType.Normal
- });
- }
+ // This is a room instance ID, encoded as a 16-bit integer apparently
+ varTypeIfSelf = VariableType.Instance;
+ id -= 100000;
}
- else
+
+ var processedCommon = CheckFor23BuiltinOrArg(cw, s.Children[0].Text, id);
+ cw.varPatches.Add(new VariablePatch()
{
- cw.varPatches.Add(new VariablePatch()
- {
- Target = cw.EmitRef(Opcode.Pop, popLocation, typeToStore),
- Name = s.Children[0].Text,
- InstType = (InstanceType)id,
- VarType = VariableType.Normal
- });
- }
+ Target = cw.EmitRef(Opcode.Pop, popLocation, typeToStore),
+ Name = processedCommon.NewVarName,
+ InstType = (InstanceType)processedCommon.NewID,
+ VarType = varTypeIfSelf
+ });
}
else
{
@@ -2211,7 +2484,18 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
VarType = s.Children[next].Children.Count != 0 ? VariableType.Array : VariableType.StackTop
});
if (next + 1 < s.Children.Count)
- cw.Emit(Opcode.Conv, DataType.Variable, DataType.Int32);
+ {
+ if (CompileContext.GMS2_3)
+ {
+ cw.typeStack.Pop();
+ cw.Emit(Opcode.PushI, DataType.Int16).Value = (short)-9; // stacktop conversion
+ cw.typeStack.Push(DataType.Int32);
+ }
+ else
+ {
+ cw.Emit(Opcode.Conv, DataType.Variable, DataType.Int32);
+ }
+ }
}
if (!skip)
cw.typeStack.Pop();
@@ -2225,17 +2509,21 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
string variableName = s.Text;
if (!fix2.WasIDSet || fix2.ID >= 100000)
{
- if (cw.compileContext.LocalVars.ContainsKey(variableName))
+ if (cw.funcContexts.Count > 0 && cw.funcContexts.Peek().ParseInfo.LocalVars.Contains(variableName))
{
- fix2.ID = -7; // local
- }
+ fix2.ID = (int)InstanceType.Local;
+ }
+ else if (cw.funcContexts.Count == 0 && cw.compileContext.LocalVars.ContainsKey(variableName))
+ {
+ fix2.ID = (int)InstanceType.Local;
+ }
else if (cw.compileContext.GlobalVars.ContainsKey(variableName))
{
- fix2.ID = -5; // global
+ fix2.ID = (int)InstanceType.Global;
}
else
{
- fix2.ID = -1; // self
+ fix2.ID = (int)InstanceType.Self;
}
}
fix.Children.Add(fix2);
@@ -2243,16 +2531,10 @@ private static void AssembleStoreVariable(CodeWriter cw, Parser.Statement s, Dat
}
else if (s.Kind == Parser.Statement.StatementKind.ExprFuncName)
{
- bool isStructDef = s.Text.StartsWith("___struct___");
- // Until further notice, I'm assuming this only comes up in 2.3 script definition.
- cw.varPatches.Add(new VariablePatch()
- {
- Target = cw.EmitRef(Opcode.Pop, DataType.Variable, DataType.Variable),
- Name = s.Text,
- InstType = isStructDef ? InstanceType.Static : InstanceType.Self,
- VarType = VariableType.StackTop
- });
- if (!isStructDef) cw.Emit(Opcode.Popz, DataType.Variable);
+ // Technically not fully valid syntax, but permit it as a simple variable
+ Parser.Statement fix = new Parser.Statement(s);
+ fix.Kind = Parser.Statement.StatementKind.ExprSingleVariable;
+ AssembleStoreVariable(cw, fix, typeToStore, skip, duplicate);
}
else
{
diff --git a/UndertaleModLib/Compiler/BuiltinList.cs b/UndertaleModLib/Compiler/BuiltinList.cs
index 3aff20824..0ded302e8 100644
--- a/UndertaleModLib/Compiler/BuiltinList.cs
+++ b/UndertaleModLib/Compiler/BuiltinList.cs
@@ -125,7 +125,8 @@ public class BuiltinList
public int Argument0ID = 0;
public int Argument15ID = 0;
- public BuiltinList() {
+ public BuiltinList()
+ {
Initialize(null);
}
@@ -2090,7 +2091,7 @@ public void Initialize(UndertaleData data)
Functions["buffer_async_group_begin"] = new FunctionInfo(this, 1);
Functions["buffer_async_group_end"] = new FunctionInfo(this, 0);
Functions["buffer_async_group_option"] = new FunctionInfo(this, 2);
- Functions["buffer_get_surface"] = new FunctionInfo(this, (data.IsVersionAtLeast(2, 3, 1) ? 3 : 5)); // be more robust here
+ Functions["buffer_get_surface"] = new FunctionInfo(this, ((data?.IsVersionAtLeast(2, 3, 1) ?? false) ? 3 : 5)); // be more robust here
Functions["buffer_set_surface"] = new FunctionInfo(this, 5);
Functions["buffer_set_network_safe"] = new FunctionInfo(this, 2);
Functions["buffer_create_from_vertex_buffer"] = new FunctionInfo(this, 3);
diff --git a/UndertaleModLib/Compiler/Compiler.cs b/UndertaleModLib/Compiler/Compiler.cs
index bc66d386c..064746782 100644
--- a/UndertaleModLib/Compiler/Compiler.cs
+++ b/UndertaleModLib/Compiler/Compiler.cs
@@ -2,10 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using UndertaleModLib.Models;
using static UndertaleModLib.Compiler.Compiler.AssemblyWriter;
-using AssetRefType = UndertaleModLib.Decompiler.Decompiler.ExpressionAssetRef.RefType;
+using static UndertaleModLib.Util.AssetReferenceTypes;
namespace UndertaleModLib.Compiler
{
@@ -22,8 +21,12 @@ public class CompileContext
public bool TypedAssetRefs => Data.IsVersionAtLeast(2023, 8);
public int LastCompiledArgumentCount = 0;
public Dictionary LocalVars = new Dictionary();
- public Dictionary GlobalVars = new Dictionary();
- public Dictionary> Enums = new Dictionary>();
+ public Dictionary GlobalVars = new Dictionary();
+ public Stack FunctionParseStack = new();
+ public Dictionary FunctionParseInfo = new();
+ public List<(string, Compiler.Parser.Statement)> EnumStatements = new List<(string, Compiler.Parser.Statement)>();
+ public Dictionary> Enums = new Dictionary>();
+ public bool FirstPassResolvingEnums = false;
public UndertaleCode OriginalCode;
public IList OriginalReferencedLocalVars;
public BuiltinList BuiltInList => Data.BuiltinList;
@@ -44,9 +47,35 @@ public CompileContext(UndertaleData data, UndertaleCode oldCode)
OriginalReferencedLocalVars = OriginalCode?.FindReferencedLocalVars();
}
+ ///
+ /// Returns the asset index (including encoded reference type, if applicable) of a given identifier,
+ /// or -1 if no asset/reference is found.
+ ///
public int GetAssetIndexByName(string name)
{
- return assetIds.TryGetValue(name, out int val) ? val : -1;
+ // Look up asset names
+ if (assetIds.TryGetValue(name, out int val))
+ {
+ return val;
+ }
+
+ // Handle named instance IDs
+ string instanceIdPrefix = Data.ToolInfo.InstanceIdPrefix();
+ if (name.StartsWith(instanceIdPrefix, StringComparison.InvariantCulture))
+ {
+ if (int.TryParse(name[instanceIdPrefix.Length..], out int id) && id >= 100000)
+ {
+ if (TypedAssetRefs)
+ {
+ // Add type to ID
+ id = (id & 0xffffff) | ((ConvertFromRefType(Data, RefType.RoomInstance) & 0x7f) << 24);
+ }
+ return id;
+ }
+ }
+
+ // Nothing found
+ return -1;
}
public void OnSuccessfulFinish()
@@ -74,7 +103,7 @@ public void OnSuccessfulFinish()
UndertaleFunction functionObj = Data.Functions.ByName(scriptName);
if (functionObj is not null)
Data.Functions.Remove(functionObj);
- Data.KnownSubFunctions.Remove(name);
+ Data.GlobalFunctions.UndefineFunction(name, functionObj);
}
FunctionsToObliterate.Clear();
}
@@ -130,21 +159,21 @@ private void MakeAssetDictionary()
assetIds.EnsureCapacity(maxSize);
scripts.EnsureCapacity(Data.Scripts?.Count ?? 0);
- AddAssetsFromList(Data.GameObjects, AssetRefType.Object);
- AddAssetsFromList(Data.Sprites, AssetRefType.Sprite);
- AddAssetsFromList(Data.Sounds, AssetRefType.Sound);
- AddAssetsFromList(Data.Backgrounds, AssetRefType.Background);
- AddAssetsFromList(Data.Paths, AssetRefType.Path);
- AddAssetsFromList(Data.Fonts, AssetRefType.Font);
- AddAssetsFromList(Data.Timelines, AssetRefType.Timeline);
+ AddAssetsFromList(Data.GameObjects, RefType.Object);
+ AddAssetsFromList(Data.Sprites, RefType.Sprite);
+ AddAssetsFromList(Data.Sounds, RefType.Sound);
+ AddAssetsFromList(Data.Backgrounds, RefType.Background);
+ AddAssetsFromList(Data.Paths, RefType.Path);
+ AddAssetsFromList(Data.Fonts, RefType.Font);
+ AddAssetsFromList(Data.Timelines, RefType.Timeline);
if (!GMS2_3)
- AddAssetsFromList(Data.Scripts, AssetRefType.Object /* not actually used */);
- AddAssetsFromList(Data.Shaders, AssetRefType.Shader);
- AddAssetsFromList(Data.Rooms, AssetRefType.Room);
- AddAssetsFromList(Data.AudioGroups, AssetRefType.Sound /* apparently? */);
- AddAssetsFromList(Data.AnimationCurves, AssetRefType.AnimCurve);
- AddAssetsFromList(Data.Sequences, AssetRefType.Sequence);
- AddAssetsFromList(Data.ParticleSystems, AssetRefType.ParticleSystem);
+ AddAssetsFromList(Data.Scripts, RefType.Script /* not actually used */);
+ AddAssetsFromList(Data.Shaders, RefType.Shader);
+ AddAssetsFromList(Data.Rooms, RefType.Room);
+ AddAssetsFromList(Data.AudioGroups, RefType.Sound /* apparently? */);
+ AddAssetsFromList(Data.AnimationCurves, RefType.AnimCurve);
+ AddAssetsFromList(Data.Sequences, RefType.Sequence);
+ AddAssetsFromList(Data.ParticleSystems, RefType.ParticleSystem);
if (Data.Scripts is not null)
{
@@ -168,7 +197,7 @@ private void MakeAssetDictionary()
}
}
- private void AddAssetsFromList(IList list, AssetRefType type) where T : UndertaleNamedResource
+ private void AddAssetsFromList(IList list, RefType type) where T : UndertaleNamedResource
{
if (list == null)
return;
@@ -180,7 +209,7 @@ private void AddAssetsFromList(IList list, AssetRefType type) where T : Un
if (name != null)
{
// Typed asset refs pack their type into the ID
- assetIds[name] = (i & 0xffffff) | (((int)type & 0x7f) << 24);
+ assetIds[name] = (i & 0xffffff) | ((ConvertFromRefType(Data, type) & 0x7f) << 24);
}
}
}
diff --git a/UndertaleModLib/Compiler/Lexer.cs b/UndertaleModLib/Compiler/Lexer.cs
index 73443783f..74b63bc9e 100644
--- a/UndertaleModLib/Compiler/Lexer.cs
+++ b/UndertaleModLib/Compiler/Lexer.cs
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
-using System.Threading.Tasks;
namespace UndertaleModLib.Compiler
{
@@ -166,15 +165,45 @@ private static Token ReadToken(CodeReader cr, bool gms2)
return new Token(Token.TokenKind.EOF);
}
- if (cr.PeekChar() == '#')
+ while (cr.PeekChar() == '#')
{
- // Skip preprocessor directive/macro/etc. Maybe support could be added later... but not yet.
+ int startHashtagIndex = cr.Position;
+ cr.AdvancePosition();
+
+ // Read first word to see if it's one we want to skip
+ StringBuilder firstWordBuilder = new("#", 8);
while (!cr.EOF)
{
- if (cr.PeekChar() == '\n')
+ char nextChar = cr.PeekChar();
+ if ((nextChar < '0' || nextChar > '9') && (nextChar < 'A' || nextChar > 'Z') &&
+ (nextChar < 'a' || nextChar > 'z') && nextChar != '_')
+ {
break;
+ }
+ firstWordBuilder.Append(nextChar);
cr.AdvancePosition();
}
+
+ string firstWord = firstWordBuilder.ToString();
+ if (firstWord == "#macro" || firstWord == "#region" || firstWord == "#endregion")
+ {
+ // Skip preprocessor directive/macros/etc
+ while (!cr.EOF)
+ {
+ if (cr.PeekChar() == '\n')
+ break;
+ cr.AdvancePosition();
+ }
+ SkipWhitespaceAndComments(cr);
+ if (cr.EOF)
+ {
+ return new Token(Token.TokenKind.EOF);
+ }
+ }
+ else
+ {
+ return new Token(Token.TokenKind.Number, firstWord, cr.GetPositionInfo(startHashtagIndex));
+ }
}
char c = cr.PeekChar();
@@ -492,8 +521,10 @@ private static Token ReadIdentifier(CodeReader cr)
"globalvar" => new Token(Token.TokenKind.KeywordGlobalVar, cr.GetPositionInfo(index)),
"return" => new Token(Token.TokenKind.KeywordReturn, cr.GetPositionInfo(index)),
"default" => new Token(Token.TokenKind.KeywordDefault, cr.GetPositionInfo(index)),
- "struct" => new Token(Token.TokenKind.KeywordStruct, cr.GetPositionInfo(index)),
"function" when CompileContext.GMS2_3 => new Token(Token.TokenKind.KeywordFunction, cr.GetPositionInfo(index)),
+ "throw" when CompileContext.GMS2_3 => new Token(Token.TokenKind.KeywordThrow, cr.GetPositionInfo(index)),
+ "constructor" when CompileContext.GMS2_3 => new Token(Token.TokenKind.KeywordConstructor, cr.GetPositionInfo(index)),
+ "new" when CompileContext.GMS2_3 => new Token(Token.TokenKind.KeywordNew, cr.GetPositionInfo(index)),
"for" => new Token(Token.TokenKind.KeywordFor, cr.GetPositionInfo(index)),
"case" => new Token(Token.TokenKind.KeywordCase, cr.GetPositionInfo(index)),
"switch" => new Token(Token.TokenKind.KeywordSwitch, cr.GetPositionInfo(index)),
@@ -698,13 +729,14 @@ private static Token ReadNumberLiteral(CodeReader cr)
private static Token ReadHexLiteral(CodeReader cr)
{
- StringBuilder sb = new StringBuilder();
+ StringBuilder sb = new(8);
int index = cr.Position;
// Read the prefix ($ or 0x)
- sb.Append(cr.ReadChar());
- if (cr.PeekChar() == 'x')
+ char first = cr.ReadChar();
+ sb.Append(first);
+ if (first == '0' && cr.PeekChar() == 'x')
sb.Append(cr.ReadChar());
// Read the digits
@@ -811,8 +843,10 @@ public enum TokenKind
KeywordExit,
KeywordBreak,
KeywordContinue,
- KeywordStruct, // Apparently this exists
KeywordFunction,
+ KeywordThrow,
+ KeywordConstructor,
+ KeywordNew,
OpenBlock, // {
CloseBlock, // }
OpenArray, // [
diff --git a/UndertaleModLib/Compiler/Parser.cs b/UndertaleModLib/Compiler/Parser.cs
index 6d0d68e93..e441bb1aa 100644
--- a/UndertaleModLib/Compiler/Parser.cs
+++ b/UndertaleModLib/Compiler/Parser.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
-using System.Threading.Tasks;
using UndertaleModLib.Models;
using static UndertaleModLib.Compiler.Compiler.Lexer.Token;
@@ -103,6 +103,9 @@ public enum StatementKind
SwitchDefault,
FunctionCall,
FunctionDef,
+ FunctionDefAssign,
+ Throw,
+ New,
Break,
Continue,
Exit,
@@ -125,6 +128,7 @@ public enum StatementKind
ExprVariableRef,
ExprSingleVariable,
ExprFuncName,
+ ExprNew,
Token,
Discard // optimization stage produces this
@@ -242,6 +246,11 @@ public Statement(TokenKind newKind, Lexer.Token copyFrom, ExpressionConstant con
}
}
+ public class FunctionParseInfo
+ {
+ public HashSet LocalVars { get; } = new();
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Statement EnsureStatementKind(Statement.StatementKind kind)
{
@@ -445,7 +454,10 @@ public static Statement ParseTokens(CompileContext context, List to
context.LocalVars.Clear();
if ((context.Data?.GeneralInfo?.BytecodeVersion ?? 15) >= 15)
context.LocalVars["arguments"] = "arguments";
+ context.FunctionParseStack.Clear();
+ context.FunctionParseInfo.Clear();
context.GlobalVars.Clear();
+ context.EnumStatements.Clear();
context.Enums.Clear();
context.FunctionsToObliterate.Clear();
uuidCounter = 0;
@@ -498,11 +510,13 @@ public static Statement ParseTokens(CompileContext context, List to
ExpressionConstant constant = null;
if (t.Content[0] == '$' || t.Content.StartsWith("0x", StringComparison.InvariantCulture))
{
+ // General hex number literal
long val;
try
{
val = Convert.ToInt64(t.Content.Substring(t.Content[0] == '$' ? 1 : 2), 16);
- } catch (Exception)
+ }
+ catch (Exception)
{
ReportCodeError("Invalid hex literal.", t, false);
constant = new ExpressionConstant(0);
@@ -518,11 +532,23 @@ public static Statement ParseTokens(CompileContext context, List to
constant = new ExpressionConstant((double)val);
}
}
+ else if (t.Content[0] == '#')
+ {
+ // CSS color hex literal - needs to convert from RGB to BGR
+ if (t.Content.Length != 7 ||
+ !int.TryParse(t.Content[1..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int value))
+ {
+ ReportCodeError("Invalid CSS color hex literal. Must have exactly 6 hex characters.", t, false);
+ constant = new ExpressionConstant(0);
+ firstPass.Add(new Statement(TokenKind.ProcConstant, t, constant));
+ continue;
+ }
+ constant = new ExpressionConstant((double)(((value & 0xff) << 16) | (value & 0xff00) | ((value & 0xff0000) >> 16)));
+ }
else
{
- if (!double.TryParse(t.Content, System.Globalization.NumberStyles.Float,
- System.Globalization.CultureInfo.InvariantCulture,
- out double val))
+ // Double-precision floating point literal
+ if (!double.TryParse(t.Content, NumberStyles.Float, CultureInfo.InvariantCulture, out double val))
{
ReportCodeError("Invalid double number format.", t, false);
}
@@ -557,6 +583,9 @@ public static Statement ParseTokens(CompileContext context, List to
// This should be safe since struct functions should only be used in one place each
while (usableStructNames.Count > 0)
context.FunctionsToObliterate.Add(usableStructNames.Dequeue());
+
+ // Resolve all enum values to their constants
+ ResolveEnumDeclarations(context);
return rootBlock;
}
@@ -660,6 +689,12 @@ private static Statement ParseStatement(CompileContext context)
case TokenKind.KeywordFunction:
s = ParseFunction(context);
break;
+ case TokenKind.KeywordNew:
+ s = ParseNew(context, false);
+ break;
+ case TokenKind.KeywordThrow:
+ s = ParseThrow(context);
+ break;
default:
// Assumes it's a variable assignment
if (remainingStageOne.Count > 0)
@@ -686,6 +721,10 @@ private static Statement ParseFunction(CompileContext context)
Statement args = new Statement();
bool expressionMode = true;
Statement destination = null;
+
+ FunctionParseInfo info = new();
+ context.FunctionParseStack.Push(info);
+ context.FunctionParseInfo[result] = info;
if (GetNextTokenKind() == TokenKind.ProcFunction)
{
@@ -701,11 +740,25 @@ private static Statement ParseFunction(CompileContext context)
EnsureTokenKind(TokenKind.OpenParen);
+ int numNamedArguments = 0;
while (remainingStageOne.Count > 0 && !hasError && !IsNextToken(TokenKind.EOF, TokenKind.CloseParen))
{
- Statement expr = ParseExpression(context);
+ numNamedArguments++;
+ Statement expr = EnsureTokenKind(TokenKind.ProcVariable);
if (expr != null)
args.Children.Add(expr);
+
+ if (IsNextTokenDiscard(TokenKind.Assign))
+ {
+ Statement defaultValue = ParseExpression(context);
+ args.Children.Add(defaultValue);
+ }
+ else
+ {
+ // No default value supplied
+ args.Children.Add(null);
+ }
+
if (!IsNextTokenDiscard(TokenKind.Comma))
{
if (!IsNextToken(TokenKind.CloseParen))
@@ -717,21 +770,52 @@ private static Statement ParseFunction(CompileContext context)
}
result.Children.Add(args);
+ if (numNamedArguments > 16)
+ {
+ ReportCodeError("Only 16 named arguments are allowed per function.", result.Token, false);
+ }
+
if (EnsureTokenKind(TokenKind.CloseParen) == null) return null;
+ if (IsNextTokenDiscard(TokenKind.Colon))
+ {
+ result.Children.Insert(0, ParseFunctionCall(context)); // TODO: variable function calls
+ result.Children.Insert(0, EnsureTokenKind(TokenKind.KeywordConstructor));
+ }
+ else if (IsNextToken(TokenKind.KeywordConstructor))
+ {
+ result.Children.Insert(0, EnsureTokenKind(TokenKind.KeywordConstructor));
+ }
- result.Children.Add(ParseStatement(context));
+ result.Children.Add(ParseBlock(context));
+
+ context.FunctionParseStack.Pop();
if (expressionMode)
return result;
else // Whatever you call non-anonymous definitions
{
- Statement trueResult = new Statement(Statement.StatementKind.Assign, new Lexer.Token(TokenKind.Assign));
+ Statement trueResult = new Statement(Statement.StatementKind.FunctionDefAssign);
trueResult.Children.Add(destination);
- trueResult.Children.Add(new Statement(Statement.StatementKind.Token, trueResult.Token));
trueResult.Children.Add(result);
return trueResult;
}
}
+ private static Statement ParseThrow(CompileContext context)
+ {
+ Statement result = new(Statement.StatementKind.Throw, EnsureTokenKind(TokenKind.KeywordThrow).Token);
+ result.Children.Add(ParseExpression(context));
+ return result;
+ }
+
+ private static Statement ParseNew(CompileContext context, bool expression)
+ {
+ Statement result = new(
+ expression ? Statement.StatementKind.ExprNew : Statement.StatementKind.New,
+ EnsureTokenKind(TokenKind.KeywordNew).Token);
+ result.Children.Add(ParseFunctionCall(context, true)); // TODO: variable function calls
+ return result;
+ }
+
private static Statement ParseFor(CompileContext context)
{
Statement result = new Statement(Statement.StatementKind.ForLoop, EnsureTokenKind(TokenKind.KeywordFor).Token);
@@ -849,11 +933,8 @@ private static Statement ParseAssign(CompileContext context)
private static Statement ParseEnum(CompileContext context)
{
- ReportCodeError("Enums not currently supported.", true);
- return null;
- /*
Statement result = new Statement(Statement.StatementKind.Enum, EnsureTokenKind(TokenKind.Enum).Token);
- Dictionary values = new Dictionary();
+ Dictionary values = new Dictionary();
Statement name = EnsureTokenKind(TokenKind.ProcVariable);
if (name == null)
@@ -863,43 +944,36 @@ private static Statement ParseEnum(CompileContext context)
result.Text = name.Text;
result.ID = name.ID;
- if (EnsureTokenKind(TokenKind.OpenBlock) == null) return null;
+ if (EnsureTokenKind(TokenKind.OpenBlock) == null)
+ return null;
- if (Enums.ContainsKey(name.Text))
+ if (context.Enums.ContainsKey(name.Text))
{
ReportCodeError("Enum \"" + name.Text + "\" is defined more than once.", name.Token, true);
- } else
+ }
+ else
{
- Enums[name.Text] = values;
+ context.Enums[name.Text] = values;
+ context.EnumStatements.Add((name.Text, result));
}
- int incrementingValue = 0;
while (!hasError && !IsNextToken(TokenKind.CloseBlock))
{
Statement val = new Statement(Statement.StatementKind.VariableName, remainingStageOne.Dequeue().Token);
result.Children.Add(val);
-
+
if (IsNextTokenDiscard(TokenKind.Assign))
{
- Statement expr = ParseExpression();
+ Statement expr = ParseExpression(context);
val.Children.Add(expr);
- Statement optimized = Optimize(expr);
- if (expr.Token.Kind == TokenKind.Constant && (expr.Kind != Statement.StatementKind.ExprConstant ||
- expr.Constant.kind == ExpressionConstant.Kind.Constant || expr.Constant.kind == ExpressionConstant.Kind.Number))
- {
- incrementingValue = (int)optimized.Constant.valueNumber;
- } else
- {
- ReportCodeError("Enum value must be an integer constant value.", expr.Token, true);
- }
}
if (values.ContainsKey(val.Text))
{
- ReportCodeError("Duplicate enum value found.", val.Token, true);
+ ReportCodeError("Duplicate enum entry found.", val.Token, true);
}
- values[val.Text] = incrementingValue++;
+ values[val.Text] = null;
if (!IsNextTokenDiscard(TokenKind.Comma))
{
@@ -908,7 +982,131 @@ private static Statement ParseEnum(CompileContext context)
}
}
- return result;*/
+ return result;
+ }
+
+ private static void ResolveEnumDeclarations(CompileContext context)
+ {
+ // First pass on all enums
+ context.FirstPassResolvingEnums = true;
+ foreach ((string Name, Statement Statement) enumDecl in context.EnumStatements)
+ {
+ Dictionary values = context.Enums[enumDecl.Name];
+
+ // Populate values (with null, if unknown in this first pass)
+ long? incrementingValue = 0;
+ foreach (Statement entry in enumDecl.Statement.Children)
+ {
+ if (entry.Children.Count == 1)
+ {
+ // Read expression, and if it's constant, assign it
+ Statement value = entry.Children[0] = Optimize(context, entry.Children[0]);
+ if (value.Kind == Statement.StatementKind.ExprConstant)
+ {
+ ExpressionConstant constant = value.Constant;
+ if (constant == null)
+ {
+ ReportCodeError("Invalid constant in enum", value.Token, false);
+ incrementingValue = null;
+ }
+ else
+ {
+ if (constant.kind == ExpressionConstant.Kind.Number)
+ {
+ incrementingValue = (long)constant.valueNumber;
+ }
+ else if (constant.kind == ExpressionConstant.Kind.Int64)
+ {
+ incrementingValue = constant.valueInt64;
+ }
+ else
+ {
+ incrementingValue = null;
+ }
+ }
+ }
+ else
+ {
+ incrementingValue = null;
+ }
+ }
+
+ // Assign value to this entry
+ values[entry.Token.Content] = incrementingValue;
+
+ // Increment to next value (if known)
+ if (incrementingValue is not null)
+ {
+ incrementingValue++;
+ }
+ }
+ }
+ context.FirstPassResolvingEnums = false;
+
+ // Second pass on all enums
+ foreach ((string Name, Statement Statement) enumDecl in context.EnumStatements)
+ {
+ Dictionary values = context.Enums[enumDecl.Name];
+
+ // Populate remaining values not covered in first pass
+ long? incrementingValue = 0;
+ foreach (Statement entry in enumDecl.Statement.Children)
+ {
+ long? fromFirstPass = values[entry.Token.Content];
+ if (fromFirstPass is not null)
+ {
+ incrementingValue = fromFirstPass + 1;
+ continue;
+ }
+
+ if (entry.Children.Count == 1)
+ {
+ // Read expression, and make sure it's constant this time
+ Statement value = entry.Children[0] = Optimize(context, entry.Children[0]);
+ if (value.Kind != Statement.StatementKind.ExprConstant)
+ {
+ ReportCodeError($"Enum entry \"{enumDecl.Name}.{entry.Token.Content}\" failed to resolve to a constant", false);
+ continue;
+ }
+ else
+ {
+ ExpressionConstant constant = value.Constant;
+ if (constant == null)
+ {
+ ReportCodeError("Invalid constant in enum", value.Token, false);
+ incrementingValue = null;
+ }
+ else
+ {
+ if (constant.kind == ExpressionConstant.Kind.Number)
+ {
+ incrementingValue = (long)constant.valueNumber;
+ }
+ else if (constant.kind == ExpressionConstant.Kind.Int64)
+ {
+ incrementingValue = constant.valueInt64;
+ }
+ else
+ {
+ incrementingValue = null;
+ }
+ }
+ }
+ }
+
+ if (incrementingValue is null)
+ {
+ ReportCodeError($"Failed to resolve enum entry value \"{enumDecl.Name}.{entry.Token.Content}\"", false);
+ incrementingValue = 0;
+ }
+
+ // Assign value to this entry
+ values[entry.Token.Content] = incrementingValue;
+
+ // Increment to next value
+ incrementingValue++;
+ }
+ }
}
private static Statement ParseDoUntil(CompileContext context)
@@ -1011,6 +1209,10 @@ private static Statement ParseLocalVarDeclare(CompileContext context)
Statement variable = new Statement(var) { Kind = Statement.StatementKind.ExprSingleVariable };
result.Children.Add(variable);
context.LocalVars[var.Text] = var.Text;
+ if (context.FunctionParseStack.Count > 0)
+ {
+ context.FunctionParseStack.Peek().LocalVars.Add(var.Text);
+ }
// Read assignments if necessary
if (remainingStageOne.Count > 0 && IsNextToken(TokenKind.Assign))
@@ -1507,6 +1709,8 @@ private static Statement ParseLowLevel(CompileContext context)
return ParseFunctionCall(context, true);
case TokenKind.KeywordFunction:
return ParseFunction(context);
+ case TokenKind.KeywordNew:
+ return ParseNew(context, true);
case TokenKind.ProcVariable:
{
Statement variableRef = ParseSingleVar(context);
@@ -1610,7 +1814,8 @@ private static Statement ParseStructLiteral(CompileContext context)
varName = "___struct___" + context.OriginalCode.Name.Content +
"__" + uuidCounter++.ToString();
i++;
- } while (context.Data.KnownSubFunctions.ContainsKey(varName));
+ }
+ while (context.Data.GlobalFunctions.FunctionNameExists(varName));
}
int ID = GetVariableID(context, varName, out _);
@@ -1626,12 +1831,14 @@ private static Statement ParseStructLiteral(CompileContext context)
{ ID = procVar.ID, Text = varName };
Statement body = new Statement();
+ function.Children.Add(new Statement(Statement.StatementKind.Token, new Lexer.Token(TokenKind.KeywordConstructor))); // ensure it's a constructor function
function.Children.Add(args);
function.Children.Add(body);
- Statement functionAssign = new Statement(Statement.StatementKind.Assign, new Lexer.Token(TokenKind.Assign));
+ context.FunctionParseInfo[function] = new();
+
+ Statement functionAssign = new Statement(Statement.StatementKind.FunctionDefAssign);
functionAssign.Children.Add(destination);
- functionAssign.Children.Add(new Statement(Statement.StatementKind.Token, functionAssign.Token));
functionAssign.Children.Add(function);
result.Children.Add(functionAssign);
@@ -1725,6 +1932,18 @@ public static Statement Optimize(CompileContext context, Statement s)
// There's nothing to optimize here, don't waste time checking
return s;
}
+ else if (s.Kind == Statement.StatementKind.FunctionDef)
+ {
+ // Maintain a reference for function declarations
+ result = s;
+ for (int i = 0; i < result.Children.Count; i++)
+ {
+ if (result.Children[i] == null)
+ result.Children[i] = new Statement(Statement.StatementKind.Discard);
+ else
+ result.Children[i] = Optimize(context, result.Children[i]);
+ }
+ }
else
{
result = new Statement(s);
@@ -1736,7 +1955,8 @@ public static Statement Optimize(CompileContext context, Statement s)
result.Children[i] = Optimize(context, result.Children[i]);
}
}
- } else
+ }
+ else
result = new Statement(s);
Statement child0 = result.Children[0];
@@ -2059,6 +2279,15 @@ public static Statement Optimize(CompileContext context, Statement s)
}
break;
case Statement.StatementKind.ExprVariableRef:
+ // Optimize enums, if detected
+ if (result.Children.Count == 2 &&
+ result.Children[0].Kind == Statement.StatementKind.ExprSingleVariable &&
+ result.Children[1].Kind == Statement.StatementKind.ExprSingleVariable)
+ {
+ result = OptimizeEnumConstant(context, result, result.Children[0], result.Children[1]);
+ break;
+ }
+
for (int i = 0; i < result.Children.Count; i++)
{
if (result.Children[i].Children.Count != 2 || result.Children[i].Children[0].Kind != Statement.StatementKind.Token)
@@ -2096,7 +2325,38 @@ public static Statement Optimize(CompileContext context, Statement s)
ReportCodeError("Case argument must be constant.", result.Token, false);
}
break;
- // todo: parse enum references
+ case Statement.StatementKind.FunctionDef:
+ {
+ // Produce default argument assignments, if they exist
+ int childrenOffset = result.Children.Count - 2;
+ Statement args = result.Children[childrenOffset], body = result.Children[childrenOffset + 1];
+ for (int i = args.Children.Count - 2; i >= 0; i -= 2)
+ {
+ Statement defaultValue = args.Children[i + 1];
+ if (defaultValue is not null && defaultValue.Kind != Statement.StatementKind.Discard)
+ {
+ Statement ifStmt = new(Statement.StatementKind.If);
+ Statement compare = new(Statement.StatementKind.ExprBinaryOp, new Lexer.Token(TokenKind.CompareEqual));
+ Statement argVariable = new(Statement.StatementKind.ExprSingleVariable, new Lexer.Token(TokenKind.ProcVariable, $"argument{i / 2}"));
+ Statement undefinedVariable = new(Statement.StatementKind.ExprSingleVariable, new Lexer.Token(TokenKind.ProcVariable, "undefined"));
+ compare.Children.Add(argVariable);
+ compare.Children.Add(undefinedVariable);
+ ifStmt.Children.Add(compare);
+ Statement assign = new(Statement.StatementKind.Assign);
+ assign.Children.Add(argVariable);
+ assign.Children.Add(new Statement(Statement.StatementKind.Assign, new Lexer.Token(TokenKind.Assign)));
+ assign.Children.Add(defaultValue);
+ ifStmt.Children.Add(assign);
+ body.Children.Insert(0, ifStmt);
+ args.Children.RemoveAt(i + 1);
+ }
+ else
+ {
+ args.Children.RemoveAt(i + 1);
+ }
+ }
+ }
+ break;
}
return result;
}
@@ -2121,6 +2381,30 @@ private static AccessorInfo GetAccessorInfoFromStatement(CompileContext context,
return ai;
}
+ private static Statement OptimizeEnumConstant(CompileContext context, Statement original, Statement enumName, Statement enumEntry)
+ {
+ if (context.Enums.TryGetValue(enumName.Token.Content, out Dictionary values))
+ {
+ if (values.TryGetValue(enumEntry.Token.Content, out long? value) && value is not null)
+ {
+ Statement newConstant = new(Statement.StatementKind.ExprConstant)
+ {
+ Constant = new ExpressionConstant((long)value)
+ };
+ return newConstant;
+ }
+ else
+ {
+ if (!context.FirstPassResolvingEnums)
+ {
+ ReportCodeError($"Failed to resolve enum entry \"{enumName.Token.Content}.{enumEntry.Token.Content}\"", false);
+ }
+ }
+ }
+
+ return original;
+ }
+
// This is probably the messiest function. I can't think of any easy ways to clean it right now though.
private static Statement OptimizeBinaryOp(Statement s)
{
@@ -2847,7 +3131,6 @@ private static bool IsKeyword(TokenKind t)
TokenKind.KeywordIf,
TokenKind.KeywordRepeat,
TokenKind.KeywordReturn,
- TokenKind.KeywordStruct,
TokenKind.KeywordSwitch,
TokenKind.KeywordThen,
TokenKind.KeywordUntil,
diff --git a/UndertaleModLib/Decompiler/Assembler.cs b/UndertaleModLib/Decompiler/Assembler.cs
index 9c22dda6d..5f38a3ed0 100644
--- a/UndertaleModLib/Decompiler/Assembler.cs
+++ b/UndertaleModLib/Decompiler/Assembler.cs
@@ -172,11 +172,25 @@ public static UndertaleInstruction AssembleOne(string source, IList(data.Variables.EnsureDefined(line,
+ UndertaleInstruction.InstanceType.Self, false, data.Strings, data));
+ }
+ else if (line.StartsWith("[function]"))
+ {
+ line = line.Substring("[function]".Length);
+ instr.Value = new UndertaleInstruction.Reference(data.Functions.ByName(line));
+ }
else
- instr.Value = new UndertaleInstruction.Reference(f);
+ {
+ var f = data.Functions.ByName(line);
+ if (f == null)
+ instr.Value = (int)ParseResourceName(line, data);
+ else
+ instr.Value = new UndertaleInstruction.Reference(f);
+ }
}
break;
case UndertaleInstruction.DataType.Int64:
@@ -404,97 +418,131 @@ private static UndertaleResourceById ParseS
private static UndertaleInstruction.Reference ParseVariableReference(string line, IList vars, Dictionary localvars, ref UndertaleInstruction.InstanceType instance, UndertaleInstruction instr, UndertaleData data = null)
{
- string str = line;
+ ReadOnlySpan str = line.AsSpan();
+ int strPosition = 0;
+
+ // Variable type, and instance type as stored in VARI chunk, adjusted based on context
UndertaleInstruction.VariableType type = UndertaleInstruction.VariableType.Normal;
- UndertaleInstruction.InstanceType realinstance = instance;
- if (str[0] != '[')
+ UndertaleInstruction.InstanceType variInstanceType = instance;
+
+ // Parse instance type, if at the beginning
+ if (str[strPosition] != '[')
{
- string inst = null;
- int instdot = str.IndexOf('.', StringComparison.InvariantCulture);
- if (instdot >= 0)
- {
- inst = str.Substring(0, instdot);
- str = str.Substring(instdot + 1);
- if (inst == "")
- throw new Exception("Whoops?");
- }
- if (inst != null)
+ // Read up until first dot character
+ int instanceTypeDot = str.IndexOf('.');
+ if (instanceTypeDot >= 0)
{
- short instnum;
- if (Int16.TryParse(inst, out instnum))
+ ReadOnlySpan instanceTypeStr = str[..instanceTypeDot];
+ if (short.TryParse(instanceTypeStr, out short instNum))
{
- instance = (UndertaleInstruction.InstanceType)instnum;
+ // This is a valid 16-bit integer, probably an object or room instance ID
+ instance = (UndertaleInstruction.InstanceType)instNum;
}
else
{
- instance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), inst, true);
+ // Otherwise, this should always be one of the valid instance type enum values
+ instance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), instanceTypeStr, true);
}
}
else
{
+ // Instance type is missing seemingly, so just use undefined
instance = UndertaleInstruction.InstanceType.Undefined;
}
- realinstance = instance;
- if (realinstance >= 0)
- realinstance = UndertaleInstruction.InstanceType.Self;
- else if (realinstance == UndertaleInstruction.InstanceType.Other)
- realinstance = UndertaleInstruction.InstanceType.Self;
- else if (realinstance == UndertaleInstruction.InstanceType.Arg)
- realinstance = UndertaleInstruction.InstanceType.Builtin;
- else if (realinstance == UndertaleInstruction.InstanceType.Builtin)
- realinstance = UndertaleInstruction.InstanceType.Self; // used with @@This@@
- else if (realinstance == UndertaleInstruction.InstanceType.Stacktop)
- realinstance = UndertaleInstruction.InstanceType.Self; // used with @@GetInstance@@
+ // Adjust VARI instance type based on existing type
+ variInstanceType = instance switch
+ {
+ >= 0 => UndertaleInstruction.InstanceType.Self,
+ UndertaleInstruction.InstanceType.Other => UndertaleInstruction.InstanceType.Self,
+ UndertaleInstruction.InstanceType.Arg => UndertaleInstruction.InstanceType.Builtin,
+ UndertaleInstruction.InstanceType.Builtin => UndertaleInstruction.InstanceType.Self, // used with @@This@@
+ UndertaleInstruction.InstanceType.Stacktop => UndertaleInstruction.InstanceType.Self, // used with @@GetInstance@@
+ _ => instance
+ };
+
+ // Set up for parsing after the dot
+ strPosition = instanceTypeDot + 1;
}
- else
+
+ // Parse variable type, if present here, as well as the alternate location of the instance type, if present (directly after it)
+ if (strPosition < str.Length && str[strPosition] == '[')
{
- int typeend = str.IndexOf(']', StringComparison.InvariantCulture);
- if (typeend >= 0)
+ // Read up until closing bracket character
+ int variableTypeEnd = str[(strPosition + 1)..].IndexOf(']') + (strPosition + 1);
+ if (variableTypeEnd >= (strPosition + 1))
{
- string typestr = str.Substring(1, typeend - 1);
- str = str.Substring(typeend + 1);
- type = (UndertaleInstruction.VariableType)Enum.Parse(typeof(UndertaleInstruction.VariableType), typestr, true);
+ // Variable type should always be one of the enum values
+ ReadOnlySpan variableTypeStr = str[(strPosition + 1)..variableTypeEnd];
+ type = (UndertaleInstruction.VariableType)Enum.Parse(typeof(UndertaleInstruction.VariableType), variableTypeStr, true);
- int instanceEnd = str.IndexOf('.', StringComparison.InvariantCulture);
- if (instanceEnd >= 0)
+ // Parse instance type, if present
+ int instanceTypeDot = str[(variableTypeEnd + 1)..].IndexOf('.') + (variableTypeEnd + 1);
+ if (instanceTypeDot >= (variableTypeEnd + 1))
{
- string instancestr = str.Substring(0, instanceEnd);
- str = str.Substring(instanceEnd + 1);
- realinstance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), instancestr, true);
- } else
+ // This instance type should always be one of the enum values
+ ReadOnlySpan instanceTypeStr = str[(variableTypeEnd + 1)..instanceTypeDot];
+ variInstanceType = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), instanceTypeStr, true);
+
+ // Set up parsing after the dot
+ strPosition = instanceTypeDot + 1;
+ }
+ else
{
- if (type == UndertaleInstruction.VariableType.Array ||
+ // Older versions of the assembly syntax did not print out instance types for array/stacktop references, which loses info in GMS 2.3+
+ if (type == UndertaleInstruction.VariableType.Array ||
type == UndertaleInstruction.VariableType.StackTop)
{
throw new Exception("Old instruction format is incompatible (missing instance type in array or stacktop)");
}
- if (realinstance >= 0)
- realinstance = UndertaleInstruction.InstanceType.Self;
- else if (realinstance == UndertaleInstruction.InstanceType.Other)
- realinstance = UndertaleInstruction.InstanceType.Self;
+ // Adjust VARI instance type based on existing type
+ if (variInstanceType >= 0)
+ {
+ variInstanceType = UndertaleInstruction.InstanceType.Self;
+ }
+ else if (variInstanceType == UndertaleInstruction.InstanceType.Other)
+ {
+ variInstanceType = UndertaleInstruction.InstanceType.Self;
+ }
+
+ // Set up parsing after the variable type's closing bracket
+ strPosition = variableTypeEnd + 1;
}
}
else
+ {
+ // Invalid formatting, objectively
throw new Exception("Missing ']' character in variable reference");
+ }
}
+ // In older versions, VARI does not assign instance types properly, so account for that
if (data?.GeneralInfo?.BytecodeVersion <= 14)
- realinstance = UndertaleInstruction.InstanceType.Undefined;
+ {
+ variInstanceType = UndertaleInstruction.InstanceType.Undefined;
+ }
- UndertaleVariable varobj;
- if (realinstance == UndertaleInstruction.InstanceType.Local)
+ // Locate variable from either local variables, or VARI chunk
+ UndertaleVariable locatedVariable;
+ string variableName = str[strPosition..].ToString();
+ if (variInstanceType == UndertaleInstruction.InstanceType.Local && data?.CodeLocals is not null)
{
- varobj = localvars.ContainsKey(str) ? localvars[str] : null;
+ locatedVariable = localvars.ContainsKey(variableName) ? localvars[variableName] : null;
}
else
{
- varobj = vars.Where((x) => x.Name.Content == str && x.InstanceType == realinstance).FirstOrDefault();
+ locatedVariable = vars.Where((x) => x.Name.Content == variableName && x.InstanceType == variInstanceType).FirstOrDefault();
+ }
+
+ // If nothing is found, throw an error, as we cannot properly assemble it
+ if (locatedVariable == null)
+ {
+ throw new Exception($"Failed to find existing variable: {variInstanceType.ToString().ToLower(CultureInfo.InvariantCulture)}.{variableName}");
}
- if (varobj == null)
- throw new Exception("Bad variable: " + realinstance.ToString().ToLower(CultureInfo.InvariantCulture) + "." + str);
- return new UndertaleInstruction.Reference(varobj, type);
+
+ // Return reference to be used in instruction
+ return new UndertaleInstruction.Reference(locatedVariable, type);
}
}
}
diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs
deleted file mode 100644
index 480e0ea6c..000000000
--- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs
+++ /dev/null
@@ -1,2048 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using UndertaleModLib.Models;
-
-namespace UndertaleModLib.Decompiler
-{
- public enum AssetIDType
- {
- Other = 0,
- Color,
- KeyboardKey,
- MouseButton,
- Enum_MouseCursor,
- Enum_HAlign,
- Enum_VAlign,
- Enum_GameSpeed, // GMS2 only
- Enum_OSType,
- Enum_GamepadButton,
- Enum_PathEndAction,
- Enum_BufferKind,
- Enum_BufferType,
- Enum_BufferSeek,
- Enum_Steam_UGC_FileType,
- Enum_Steam_UGC_List,
- Enum_Steam_UGC_QueryType,
- Enum_Steam_UGC_MatchType,
- Enum_Steam_UGC_SortOrder,
- Enum_Steam_LeaderBoard_Sort,
- Enum_Steam_LeaderBoard_Display,
- Enum_Steam_Overlay,
- e__VW, // The constant used in __view_get and __view_set compatibility scripts
- e__BG, // The constant used in __background_get and __background_set compatibility scripts
- Boolean,
-
- Sprite,
- Background,
- Sound,
- Font,
- Path,
- Timeline,
- Room,
- GameObject, // or GameObjectInstance or InstanceType, these are all interchangable
- Script,
- Shader,
- AnimCurve,
- Sequence,
- ParticleSystem,
-
- EventType, // For event_perform
-
- Macro, // Bytecode <= 14 has macros in the Constants field of OPTN, only useful for Undertale 1.00
- ContextDependent, // Can be anything, depends on the function and/or other arguments
-
- TileSet, // Identical to AssetIDType.Background, used internally for GMS2 to prevent tileset functions from resolving incorrectly.
- Layer // GMS2
- }
-
- public enum HAlign
- {
- fa_left = 0,
- fa_center = 1,
- fa_right = 2
- }
-
- public enum VAlign
- {
- fa_top = 0,
- fa_middle = 1,
- fa_bottom = 2
- }
-
- public enum GameSpeed
- {
- gamespeed_fps,
- gamespeed_microseconds
- }
-
- public enum MouseCursor
- {
- cr_size_all = -22,
- cr_handpoint = -21,
- cr_appstart = -19, // I have no idea why they aren't aligned.
- cr_drag = -12,
- cr_hourglass = -11,
- cr_uparrow = -10,
- cr_size_we = -9,
- cr_size_nwse = -8,
- cr_size_ns = -7,
- cr_size_nesw = -6,
- cr_beam = -4,
- cr_cross = -3,
- cr_arrow = -2,
- cr_none = -1,
- cr_default = 0
- }
-
- public enum OSType
- {
- os_windows = 0, // legacy constant os_win32 is equal to os_windows
- os_macosx = 1,
- os_psp = 2,
- os_ios = 3,
- os_android = 4,
- os_symbian = 5,
- os_linux = 6,
- os_winphone = 7,
- os_tizen = 8,
- os_win8native = 9,
- os_wiiu = 10,
- os_3ds = 11,
- os_psvita = 12,
- os_bb10 = 13,
- os_ps4 = 14,
- os_xboxone = 15,
- os_ps3 = 16,
- os_xbox360 = 17,
- os_uwp = 18,
- os_amazon = 19, // the same as android but... different?
- os_switch_beta = 20, // this one was used while switch support was in beta and changed later? In newer runtimes 20 is now os_tvos...
- os_switch = 21,
- os_unknown = -1
- }
-
- public enum GamepadButton
- {
- gp_face1 = 32769,
- gp_face2 = 32770,
- gp_face3 = 32771,
- gp_face4 = 32772,
- gp_shoulderl = 32773,
- gp_shoulderlb = 32775,
- gp_shoulderr = 32774,
- gp_shoulderrb = 32776,
- gp_select = 32777,
- gp_start = 32778,
- gp_stickl = 32779,
- gp_stickr = 32780,
- gp_padu = 32781,
- gp_padd = 32782,
- gp_padl = 32783,
- gp_padr = 32784,
- gp_axislh = 32785,
- gp_axislv = 32786,
- gp_axisrh = 32787,
- gp_axisrv = 32788,
- }
-
- public enum PathEndAction
- {
- path_action_stop = 0,
- path_action_restart = 1,
- path_action_continue = 2,
- path_action_reverse = 3
- }
-
- public enum BufferKind
- {
- buffer_fixed = 0,
- buffer_grow = 1,
- buffer_wrap = 2,
- buffer_fast = 3,
- buffer_vbuffer = 4,
- buffer_network = 5
- }
-
- public enum BufferType
- {
- buffer_u8 = 1,
- buffer_s8 = 2,
- buffer_u16 = 3,
- buffer_s16 = 4,
- buffer_u32 = 5,
- buffer_s32 = 6,
- buffer_f16 = 7,
- buffer_f32 = 8,
- buffer_f64 = 9,
- buffer_bool = 10,
- buffer_string = 11,
- buffer_u64 = 12,
- buffer_text = 13
- }
-
- public enum BufferSeek
- {
- buffer_seek_start = 0,
- buffer_seek_relative = 1,
- buffer_seek_end = 2
- }
-
- public enum MouseButton
- {
- mb_any = -1,
- mb_none,
- mb_left,
- mb_right,
- mb_middle
- }
-
- public enum e__VW
- {
- XView = 0,
- YView = 1,
- WView = 2,
- HView = 3,
- Angle = 4,
- HBorder = 5,
- VBorder = 6,
- HSpeed = 7,
- VSpeed = 8,
- Object = 9,
- Visible = 10,
- XPort = 11,
- YPort = 12,
- WPort = 13,
- HPort = 14,
- Camera = 15,
- SurfaceID = 16,
- }
- public enum e__BG
- {
- Visible = 0,
- Foreground = 1,
- Index = 2,
- X = 3,
- Y = 4,
- Width = 5,
- Height = 6,
- HTiled = 7,
- VTiled = 8,
- XScale = 9,
- YScale = 10,
- HSpeed = 11,
- VSpeed = 12,
- Blend = 13,
- Alpha = 14
- }
-
- public enum Boolean
- {
- @false = 0,
- @true = 1
- }
-
- public enum Steam_UGC_FileType
- {
- ugc_filetype_community,
- ugc_filetype_microtrans
- }
-
- public enum Steam_UGC_MatchType
- {
- ugc_match_Items,
- ugc_match_Items_Mtx,
- ugc_match_Items_ReadyToUse,
- ugc_match_Collections,
- ugc_match_Artwork,
- ugc_match_Videos,
- ugc_match_Screenshots,
- ugc_match_AllGuides,
- ugc_match_WebGuides,
- ugc_match_IntegratedGuides,
- ugc_match_UsableInGame,
- ugc_match_ControllerBindings,
- }
-
- public enum Steam_UGC_QueryType
- {
- ugc_query_RankedByVote,
- ugc_query_RankedByPublicationDate,
- ugc_query_AcceptedForGameRankedByAcceptanceDate,
- ugc_query_RankedByTrend,
- ugc_query_FavoritedByFriendsRankedByPublicationDate,
- ugc_query_CreatedByFriendsRankedByPublicationDate,
- ugc_query_RankedByNumTimesReported,
- ugc_query_CreatedByFollowedUsersRankedByPublicationDate,
- ugc_query_NotYetRated,
- ugc_query_RankedByTotalVotesAsc,
- ugc_query_RankedByVotesUp,
- ugc_query_RankedByTextSearch,
- }
-
- public enum Steam_UGC_List
- {
- ugc_list_Published,
- ugc_list_VotedOn,
- ugc_list_VotedUp,
- ugc_list_VotedDown,
- ugc_list_WillVoteLater,
- ugc_list_Favorited,
- ugc_list_Subscribed,
- ugc_list_UsedOrPlayed,
- ugc_list_Followed
- }
-
- public enum Steam_UGC_SortOrder
- {
- ugc_sortorder_CreationOrderDesc,
- ugc_sortorder_CreationOrderAsc,
- ugc_sortorder_TitleAsc,
- ugc_sortorder_LastUpdatedDesc,
- ugc_sortorder_SubscriptionDateDesc,
- ugc_sortorder_VoteScoreDesc,
- ugc_sortorder_ForModeration
- }
-
- public enum Steam_LeaderBoard_Sort
- {
- lb_sort_none,
- lb_sort_ascending,
- lb_sort_descending,
- }
-
- public enum Steam_LeaderBoard_Display
- {
- lb_disp_none,
- lb_disp_numeric,
- lb_disp_time_sec,
- lb_disp_time_ms,
- }
-
- public enum Steam_Overlay
- {
- ov_friends,
- ov_community,
- ov_players,
- ov_settings,
- ov_gamegroup,
- ov_achievements,
- }
-
- // Subtypes are pulled from the builtin list case frankly I
- // don't care enough to type them all out manually.
- // There's like a hundred subtypes.
- public enum Enum_EventType
- {
- ev_create,
- ev_destroy,
- ev_alarm,
- ev_step,
- ev_collision,
- ev_keyboard,
- ev_mouse,
- ev_other,
- ev_draw,
- ev_keypress,
- ev_keyrelease,
- ev_trigger,
-
- // GMS2
- ev_cleanup,
- ev_gesture,
- ev_pre_create,
- }
-
- public class AssetTypeResolver
- {
- public static Dictionary builtin_funcs; // keys are function names
-
- public static Dictionary> builtin_var_overrides; // keys are code block names or object names. In the resulting dictionary keys are variable names.
- public static Dictionary builtin_vars; // keys are variable names
-
- public static Dictionary return_types; // keys are script names (< GMS2.3) or member function variable names (>= GMS2.3)
-
- internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDType[] arguments, DecompileContext context)
- {
- return AnnotateTypesForFunctionCall(function_name, arguments, context, null);
- }
-
- internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDType[] arguments, DecompileContext context, Decompiler.FunctionCall function)
- {
- Dictionary scriptArgs = context.GlobalContext.ScriptArgsCache;
-
- bool overloaded = false;
- // Scripts overload builtins because in GMS2 some functions are just backwards-compatibility scripts
- if (scriptArgs.ContainsKey(function_name) && scriptArgs[function_name] != null)
- {
- overloaded = true;
- for (int i = 0; i < arguments.Length && i < scriptArgs[function_name].Length; i++)
- arguments[i] = scriptArgs[function_name][i];
- }
-
- function_name = function_name.Replace("color", "colour", StringComparison.InvariantCulture); // Just GameMaker things... both are valid :o
-
- if(context.GlobalContext.Data?.IsGameMaker2() ?? false)
- {
- // Backgrounds don't exist in GMS2
- for (int i = 0; i < arguments.Length; i++)
- {
- if (arguments[i] == AssetIDType.Background)
- arguments[i] = AssetIDType.Sprite;
- }
- }
-
- if (builtin_funcs.ContainsKey(function_name))
- {
- AssetIDType[] func_types = builtin_funcs[function_name];
-
- if (context.GlobalContext.Data?.IsGameMaker2() ?? false)
- {
- // Backgrounds don't exist in GMS2
- for (int i = 0; i < func_types.Length; i++)
- {
- if (func_types[i] == AssetIDType.Background)
- func_types[i] = AssetIDType.Sprite;
- }
- }
-
- if (overloaded)
- {
- // Copy the array to make sure we don't overwrite existing known types
- func_types = (AssetIDType[]) func_types.Clone();
- AssetIDType scriptArgType;
-
- for (int i = 0; i < arguments.Length && i < func_types.Length && i < scriptArgs[function_name].Length; i++)
- {
- scriptArgType = scriptArgs[function_name][i];
-
- // Merge types together
- if (func_types[i] == AssetIDType.Other && scriptArgType != AssetIDType.Other)
- func_types[i] = scriptArgType;
- // Conflicting types - do not resolve
- else if (func_types[i] != AssetIDType.Other && scriptArgType != AssetIDType.Other && func_types[i] != scriptArgType)
- func_types[i] = AssetIDType.Other;
- // func_types[i] is correct, do not replace
- }
- }
- for (int i = 0; i < arguments.Length && i < func_types.Length; i++)
- arguments[i] = func_types[i];
- return true;
- }
- if (function_name == "script_execute")
- {
- // This needs a special case
- if (arguments.Length < 1)
- throw new Exception("Bad call to " + function_name + " with " + arguments.Length + " arguments (instead of at least 1)");
- arguments[0] = AssetIDType.Script;
-
- // Attempt to resolve the arguments of the script being called.
- // This is done by reading the literal values passed to the function and resolving
- // the first argument as a function, then recursively calling the asset resolver on it.
- // There's probably a better way to do this.
-
- if (function != null)
- {
- if (function.Arguments[0] is Decompiler.ExpressionCast)
- {
- var firstArg = (function.Arguments[0] as Decompiler.ExpressionCast).Argument;
- if ((firstArg is Decompiler.ExpressionConstant) && firstArg.Type == UndertaleInstruction.DataType.Int16)
- {
- short script_id = (short) (firstArg as Decompiler.ExpressionConstant).Value;
- if (script_id >= 0 && script_id < context.GlobalContext.Data.Scripts.Count)
- {
- var script = context.GlobalContext.Data.Scripts[script_id];
- AssetIDType[] args = new AssetIDType[arguments.Length-1];
- AnnotateTypesForFunctionCall(script.Name.Content, args, context);
- Array.Copy(args, 0, arguments, 1, args.Length);
- return true;
- }
- }
- }
- }
- if (scriptArgs.ContainsKey(function_name) && scriptArgs[function_name] != null)
- {
- for (int i = 0; i < arguments.Length && i < scriptArgs[function_name].Length; i++)
- arguments[1 + i] = scriptArgs[function_name][i];
- }
- return true;
- }
- return overloaded;
- }
-
- internal static AssetIDType AnnotateTypeForVariable(DecompileContext context, string variable_name)
- {
- var overrides = GetTypeOverridesFor(context.TargetCode.Name.Content);
- if (overrides.ContainsKey(variable_name))
- return overrides[variable_name];
-
- if (context.Object != null)
- {
- overrides = GetTypeOverridesFor(context.Object.Name.Content);
- if (overrides.ContainsKey(variable_name))
- return overrides[variable_name];
- }
-
-
- if (builtin_vars.ContainsKey(variable_name))
- return builtin_vars[variable_name];
- return AssetIDType.Other;
- }
-
- internal static AssetIDType AnnotateTypeForScript(string script_name)
- {
- if (return_types.ContainsKey(script_name))
- return return_types[script_name];
- return AssetIDType.Other;
- }
-
- internal static Dictionary GetTypeOverridesFor(DecompileContext context)
- {
- return GetTypeOverridesFor(context.TargetCode.Name.Content);
- }
-
- internal static Dictionary GetTypeOverridesFor(string code_entry_name)
- {
- lock(builtin_var_overrides)
- {
- if (!builtin_var_overrides.ContainsKey(code_entry_name))
- builtin_var_overrides.Add(code_entry_name, new Dictionary());
-
- return builtin_var_overrides[code_entry_name];
- }
- }
-
- internal static void AddOverrideFor(string code_entry_name, string variable_name, AssetIDType type)
- {
- var overrides = GetTypeOverridesFor(code_entry_name);
- overrides[variable_name] = type;
- }
-
- public static int? FindConstValue(string const_name)
- {
- if (const_name.Length >= 1 && Char.IsDigit(const_name[0]))
- return null; // that is not a constant
- if (const_name.Length >= 1 && const_name[0] == '-')
- return null; // that is not a constant either
-
- // By avoiding Enum.TryParse, we avoid exception spam in the console, and there isn't any speed loss.
- if (Enum.IsDefined(typeof(OSType), const_name))
- return (int)Enum.Parse(typeof(OSType), const_name);
- if (Enum.IsDefined(typeof(GamepadButton), const_name))
- return (int)Enum.Parse(typeof(GamepadButton), const_name);
- if (Enum.IsDefined(typeof(MouseButton), const_name))
- return (int)Enum.Parse(typeof(MouseButton), const_name);
- if (Enum.IsDefined(typeof(MouseCursor), const_name))
- return (int)Enum.Parse(typeof(MouseCursor), const_name);
- if (Enum.IsDefined(typeof(HAlign), const_name))
- return (int)Enum.Parse(typeof(HAlign), const_name);
- if (Enum.IsDefined(typeof(VAlign), const_name))
- return (int)Enum.Parse(typeof(VAlign), const_name);
- if (Enum.IsDefined(typeof(GameSpeed), const_name))
- return (int)Enum.Parse(typeof(GameSpeed), const_name);
- if (Enum.IsDefined(typeof(e__VW), const_name))
- return (int)Enum.Parse(typeof(e__VW), const_name);
- if (Enum.IsDefined(typeof(e__BG), const_name))
- return (int)Enum.Parse(typeof(e__BG), const_name);
- if (Enum.IsDefined(typeof(EventSubtypeKey), const_name))
- return Convert.ToInt32((uint)Enum.Parse(typeof(EventSubtypeKey), const_name));
- if (Enum.IsDefined(typeof(Enum_EventType), const_name))
- return (int)Enum.Parse(typeof(Enum_EventType), const_name);
-
- return null;
- }
-
- // Properly initializes per-project/game
- public static void InitializeTypes(UndertaleData data)
- {
-
- ContextualAssetResolver.Initialize(data);
-
- return_types = new Dictionary();
-
- builtin_funcs = new Dictionary
- {
- { "action_create_object", new[] { AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other } },
- { "instance_activate_object", new[] { AssetIDType.GameObject } },
- { "script_exists", new[] { AssetIDType.Script } },
- { "script_get_name", new[] { AssetIDType.Script } },
- // script_execute handled separately
-
- { "instance_change", new[] { AssetIDType.GameObject, AssetIDType.Boolean } },
- { "instance_copy", new[] { AssetIDType.Boolean } },
- { "instance_create", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "instance_destroy", new[] { AssetIDType.GameObject, AssetIDType.Boolean } },
- { "instance_exists", new[] { AssetIDType.GameObject } },
- { "instance_find", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "instance_furthest", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "instance_nearest", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "instance_number", new[] { AssetIDType.GameObject } },
- { "instance_place", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "instance_position", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "instance_deactivate_all", new[] { AssetIDType.Boolean } },
- { "application_surface_enable", new[] { AssetIDType.Boolean } },
- { "application_surface_draw_enable", new[] { AssetIDType.Boolean } },
- { "instance_deactivate_object", new[] { AssetIDType.GameObject } },
- { "instance_activate_region", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean } },
- { "instance_deactivate_region", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean , AssetIDType.Boolean } },
-
- { "instance_activate_layer", new[] { AssetIDType.Layer } }, // GMS2
- { "instance_deactivate_layer", new[] { AssetIDType.Layer } }, // GMS2
- { "instance_create_depth", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } }, // GMS2
- { "instance_create_layer", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } }, // GMS2
-
- { "sprite_get_name", new[] { AssetIDType.Sprite } },
- { "sprite_get_number", new[] { AssetIDType.Sprite } },
- { "sprite_get_width", new[] { AssetIDType.Sprite } },
- { "sprite_get_height", new[] { AssetIDType.Sprite } },
- { "sprite_get_xoffset", new[] { AssetIDType.Sprite } },
- { "sprite_get_yoffset", new[] { AssetIDType.Sprite } },
- { "sprite_get_bbox_bottom", new[] { AssetIDType.Sprite } },
- { "sprite_get_bbox_left", new[] { AssetIDType.Sprite } },
- { "sprite_get_bbox_right", new[] { AssetIDType.Sprite } },
- { "sprite_get_bbox_top", new[] { AssetIDType.Sprite } },
- { "sprite_get_tpe", new[] { AssetIDType.Sprite, AssetIDType.Other } },
- { "sprite_get_texture", new[] { AssetIDType.Sprite, AssetIDType.Other } },
- { "sprite_get_uvs", new[] { AssetIDType.Sprite, AssetIDType.Other } },
-
- { "sprite_exists", new[] { AssetIDType.Sprite } },
- { "sprite_add", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_replace", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_duplicate", new[] { AssetIDType.Sprite } },
- { "sprite_assign", new[] { AssetIDType.Sprite, AssetIDType.Sprite } },
- { "sprite_merge", new[] { AssetIDType.Sprite, AssetIDType.Sprite } },
- { "sprite_create_from_surface", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_add_from_surface", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean } },
- { "sprite_collision_mask", new[] { AssetIDType.Sprite, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_set_offset", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_delete", new[] { AssetIDType.Sprite } },
- { "sprite_set_alpha_from_sprite", new[] { AssetIDType.Sprite, AssetIDType.Sprite } },
- { "sprite_set_cache_size", new[] { AssetIDType.Sprite, AssetIDType.Other } },
- { "sprite_set_cache_size_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_save", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other } },
- { "sprite_save_strip", new[] { AssetIDType.Sprite, AssetIDType.Other } },
- { "sprite_flush", new[] { AssetIDType.Sprite } },
- { "sprite_flush_multi", new[] { AssetIDType.Sprite } }, // sprite ARRAY
- { "sprite_prefetch", new[] { AssetIDType.Sprite } },
- { "sprite_prefetch_multi", new[] { AssetIDType.Sprite } }, // sprite ARRAY
-
- { "background_get_name", new[] { AssetIDType.Background } },
- { "background_get_width", new[] { AssetIDType.Background } },
- { "background_get_height", new[] { AssetIDType.Background } },
- { "background_get_texture", new[] { AssetIDType.Background } },
- { "background_get_uvs", new[] { AssetIDType.Background } },
- { "background_exists", new[] { AssetIDType.Background } },
- { "background_add", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "background_replace", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "background_duplicate", new[] { AssetIDType.Background } },
- { "background_assign", new[] { AssetIDType.Background, AssetIDType.Background } },
- { "background_create_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color } },
- { "background_create_gradient", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "background_create_from_surface", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "background_set_alpha_from_background", new[] { AssetIDType.Background, AssetIDType.Background } },
- { "background_save", new[] { AssetIDType.Background, AssetIDType.Other } },
- { "background_delete", new[] { AssetIDType.Background } },
- { "background_flush", new[] { AssetIDType.Background } },
- { "background_flush_multi", new[] { AssetIDType.Background } }, // array
- { "background_prefetch", new[] { AssetIDType.Background } },
- { "background_prefetch_multi", new[] { AssetIDType.Background } }, // array
-
- // only a few relevant ones for tiles
- { "tile_add", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "tile_set_background", new[] { AssetIDType.Other, AssetIDType.Background } },
- { "tile_set_blend", new[] { AssetIDType.Other, AssetIDType.Color } },
-
- { "audio_exists", new[] { AssetIDType.Sound } },
- { "audio_get_name", new[] { AssetIDType.Sound } },
- { "audio_get_type", new[] { AssetIDType.Sound } },
- { "audio_play_sound", new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Boolean } },
- { "audio_play_sound_at", new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other } },
- { "audio_pause_sound", new[] { AssetIDType.Sound } },
- { "audio_pause_all", Array.Empty() },
- { "audio_resume_sound", new[] { AssetIDType.Sound } },
- { "audio_resume_all", Array.Empty() },
- { "audio_stop_sound", new[] { AssetIDType.Sound } },
- { "audio_stop_all", Array.Empty() },
- { "audio_is_playing", new[] { AssetIDType.Sound } },
- { "audio_is_paused", new[] { AssetIDType.Sound } },
- { "audio_create_streaam", new[] { AssetIDType.Other } },
- { "audio_destroy_streaam", new[] { AssetIDType.Other } },
-
- { "audio_sound_set_track_position", new[] { AssetIDType.Sound, AssetIDType.Other } },
- { "audio_sound_get_track_position", new[] { AssetIDType.Sound } },
- { "audio_sound_length", new[] { AssetIDType.Sound } },
- { "audio_sound_pitch", new[] { AssetIDType.Sound, AssetIDType.Other } },
- { "audio_sound_get_pitch", new[] { AssetIDType.Sound } },
- { "audio_falloff_set_model", new[] { AssetIDType.Other } },
- { "audio_sound_gain", new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other } },
- { "audio_sound_get_gain", new[] { AssetIDType.Sound } },
- { "audio_master_gain", new[] { AssetIDType.Other } },
- { "audio_play_sound_on", new[] { AssetIDType.Other, AssetIDType.Sound, AssetIDType.Boolean, AssetIDType.Other } },
- { "audio_play_in_sync_group", new[] { AssetIDType.Other, AssetIDType.Sound } },
- // TODO? I don't know if the ones with only asset type Other are worth adding here
-
- // Legacy sound functions
- { "sound_exists", new[] { AssetIDType.Sound } },
- { "sound_get_name", new[] { AssetIDType.Sound } },
- { "sound_play", new[] { AssetIDType.Sound } },
- { "sound_loop", new[] { AssetIDType.Sound } },
- { "sound_stop", new[] { AssetIDType.Sound } },
- { "sound_stop_all", Array.Empty() },
- { "sound_isplaying", new[] { AssetIDType.Sound } },
- { "sound_volume", new[] { AssetIDType.Sound, AssetIDType.Other } },
- { "sound_fade", new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other } },
- { "sound_global_volume", new[] { AssetIDType.Other } },
- // Deprecated legacy functions (wait what)
- { "sound_add", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "sound_replace", new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "sound_delete", new[] { AssetIDType.Sound } },
-
- { "font_get_name", new[] { AssetIDType.Font } },
- { "font_get_fontname", new[] { AssetIDType.Font } },
- { "font_get_first", new[] { AssetIDType.Font } },
- { "font_get_last", new[] { AssetIDType.Font } },
- { "font_get_italic", new[] { AssetIDType.Font } },
- { "font_get_bold", new[] { AssetIDType.Font } },
- { "font_get_size", new[] { AssetIDType.Font } },
- { "font_get_texture", new[] { AssetIDType.Font } },
- { "font_get_uvs", new[] { AssetIDType.Font } },
-
- { "font_set_cache_size", new[] { AssetIDType.Font, AssetIDType.Other } },
- { "font_exists", new[] { AssetIDType.Font } },
- { "font_add", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_add_sprite", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_add_sprite_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_replace", new[] { AssetIDType.Font, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_replace_sprite", new[] { AssetIDType.Font, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_replace_sprite_ext", new[] { AssetIDType.Font, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "font_delete", new[] { AssetIDType.Font } },
-
- { "path_start", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Enum_PathEndAction, AssetIDType.Other } },
- { "path_end", Array.Empty() },
-
- { "path_exists", new[] { AssetIDType.Path } },
- { "path_get_closed", new[] { AssetIDType.Path } },
- { "path_get_kind", new[] { AssetIDType.Path } },
- { "path_get_length", new[] { AssetIDType.Path } },
- { "path_get_name", new[] { AssetIDType.Path } },
- { "path_get_number", new[] { AssetIDType.Path } },
- { "path_get_point_speed", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_point_x", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_point_y", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_precision", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_speed", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_x", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_get_y", new[] { AssetIDType.Path, AssetIDType.Other} },
-
- { "path_add", Array.Empty() },
- { "path_add_point", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "path_change_point", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "path_insert_point", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "path_delete_point", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_clear_points", new[] { AssetIDType.Path } },
- { "path_append", new[] { AssetIDType.Path, AssetIDType.Path } },
- { "path_assign", new[] { AssetIDType.Path, AssetIDType.Path } },
- { "path_delete", new[] { AssetIDType.Path } },
- { "path_duplicate", new[] { AssetIDType.Path } },
- { "path_flip", new[] { AssetIDType.Path } },
- { "path_mirror", new[] { AssetIDType.Path } },
- { "path_reverse", new[] { AssetIDType.Path } },
- { "path_rotate", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_scale", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other } },
- { "path_set_closed", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_set_kind", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_set_precision", new[] { AssetIDType.Path, AssetIDType.Other } },
- { "path_shift", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other } },
-
- { "timeline_exists", new[] { AssetIDType.Timeline } },
- { "timeline_get_name", new[] { AssetIDType.Timeline } },
- { "timeline_delete", new[] { AssetIDType.Timeline } },
- { "timeline_moment_add_script", new[] { AssetIDType.Timeline, AssetIDType.Other, AssetIDType.Other } },
- { "timeline_moment_clear", new[] { AssetIDType.Timeline, AssetIDType.Other } },
- { "timeline_clear", new[] { AssetIDType.Timeline } },
- { "timeline_size", new[] { AssetIDType.Timeline } },
- { "timeline_max_moment", new[] { AssetIDType.Timeline } },
-
- { "room_exists", new[] { AssetIDType.Room } },
- { "room_next", new[] { AssetIDType.Room } },
- { "room_previous", new[] { AssetIDType.Room } },
- { "room_get_name", new[] { AssetIDType.Room } },
-
- { "room_goto", new[] { AssetIDType.Room } },
- { "room_goto_next", Array.Empty() },
- { "room_goto_previous", Array.Empty() },
- { "room_restart", Array.Empty() },
-
- { "room_add", Array.Empty() },
- { "room_duplicate", new[] { AssetIDType.Room } },
- { "room_assign", new[] { AssetIDType.Room, AssetIDType.Room } },
- { "room_instance_add", new[] { AssetIDType.Room, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "room_instance_clear", new[] { AssetIDType.Room } },
- { "room_tile_add", new[] { AssetIDType.Room, AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "room_tile_add_ext", new[] { AssetIDType.Room, AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "room_tile_clear", new[] { AssetIDType.Room } },
- { "room_set_background", new[] { AssetIDType.Room, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "room_set_background_colour", new[] { AssetIDType.Room, AssetIDType.Color, AssetIDType.Other } },
- { "room_set_height", new[] { AssetIDType.Room, AssetIDType.Other } },
- { "room_set_width", new[] { AssetIDType.Room, AssetIDType.Other } },
- { "room_set_persistent", new[] { AssetIDType.Room, AssetIDType.Other } },
- { "room_set_view_enabled", new[] { AssetIDType.Room, AssetIDType.Other } },
-
- { "room_set_viewport", new[] { AssetIDType.Room, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } }, // GMS2 only
- { "room_get_viewport", new[] { AssetIDType.Room, AssetIDType.Other } }, // GMS2 only
- { "room_get_camera", new[] { AssetIDType.Room, AssetIDType.Other } }, // GMS2 only
- { "room_set_camera", new[] { AssetIDType.Room, AssetIDType.Other, AssetIDType.Other } }, // GMS2 only
-
- // GMS2 viewport compatibility scripts
- { "__view_get", new[] { AssetIDType.Other, AssetIDType.Other } }, // Don't ask why this is here I was going somewhere with this
- { "__view_set", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.ContextDependent } },
-
- { "object_exists", new[] { AssetIDType.GameObject } },
- { "object_get_depth", new[] { AssetIDType.GameObject } },
- { "object_get_mask", new[] { AssetIDType.GameObject } },
- { "object_get_name", new[] { AssetIDType.GameObject } },
- { "object_get_parent", new[] { AssetIDType.GameObject } },
- { "object_get_persistent", new[] { AssetIDType.GameObject } },
- { "object_get_solid", new[] { AssetIDType.GameObject } },
- { "object_get_sprite", new[] { AssetIDType.GameObject } },
- { "object_get_visible", new[] { AssetIDType.GameObject } },
- { "object_get_physics", new[] { AssetIDType.GameObject } },
- { "object_is_ancestor", new[] { AssetIDType.GameObject, AssetIDType.GameObject } },
- { "object_set_depth", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "object_set_mask", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "object_set_persistent", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "object_set_solid", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "object_set_sprite", new[] { AssetIDType.GameObject, AssetIDType.Other } },
- { "object_set_visible", new[] { AssetIDType.GameObject, AssetIDType.Other } },
-
- // Event functions
- { "event_perform_object", new[] { AssetIDType.GameObject, AssetIDType.EventType, AssetIDType.ContextDependent } },
- { "event_perform", new[] { AssetIDType.EventType, AssetIDType.ContextDependent } },
-
- { "merge_colour", new[] { AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
-
- // incomplete
- { "draw_clear", new[] { AssetIDType.Color } },
- { "draw_clear_alpha", new[] { AssetIDType.Color, AssetIDType.Other } },
- { "draw_set_colour", new[] { AssetIDType.Color } },
-
- { "draw_circle_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_ellipse_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_line_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color } },
- { "draw_line_width_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color } },
- { "draw_point_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color } },
- { "draw_rectangle", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean } },
- { "draw_rectangle_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_roundrect_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_roundrect_colour_ext", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_healthbar", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_set_alpha", new[] { AssetIDType.Other } },
-
- { "draw_set_blend_mode", new[] { AssetIDType.ContextDependent } },
- { "draw_set_blend_mode_ext", new[] { AssetIDType.ContextDependent, AssetIDType.ContextDependent } },
- { "gpu_set_blendmode", new[] { AssetIDType.ContextDependent } },
- { "gpu_set_blendmode_ext", new[] { AssetIDType.ContextDependent, AssetIDType.ContextDependent } },
-
- { "d3d_set_fog", new[] { AssetIDType.Boolean, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other } },
- { "gpu_set_fog", new[] { AssetIDType.Boolean, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other } },
-
- { "layer_script_begin", new[] { AssetIDType.Other, AssetIDType.Script } },
- { "layer_background_create", new[] { AssetIDType.Other, AssetIDType.Sprite } },
- { "layer_background_blend", new[] { AssetIDType.Other, AssetIDType.Color } },
- { "layer_background_visible", new[] { AssetIDType.Other, AssetIDType.Boolean } },
- { "layer_sprite_change", new[] { AssetIDType.Other, AssetIDType.Sprite } },
- { "gpu_set_blendenable", new[] { AssetIDType.Boolean } },
- { "layer_script_end", new[] { AssetIDType.Other, AssetIDType.Script } },
- { "draw_sprite", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_sprite_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_sprite_general", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_sprite_part", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_sprite_part_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_sprite_stretched", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_sprite_stretched_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_sprite_pos", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_sprite_tiled", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_sprite_tiled_ext", new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
-
- { "draw_background", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other } },
- { "draw_background_ext", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_background_part", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_background_part_ext", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_background_stretched", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "draw_background_stretched_ext", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_background_tiled", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other } },
- { "draw_background_tiled_ext", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_background_general", new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
-
- { "draw_set_font", new[] { AssetIDType.Font } },
- { "draw_set_halign", new[] { AssetIDType.Enum_HAlign } },
- { "draw_set_valign", new[] { AssetIDType.Enum_VAlign } },
- { "draw_text_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_text_ext_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_text_transformed_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
- { "draw_text_transformed_ext_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Color, AssetIDType.Other } },
-
- { "draw_vertex_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
- { "draw_vertex_texture_colour", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
-
- { "distance_to_object", new[] { AssetIDType.GameObject } },
-
- { "place_meeting", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "position_meeting", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "position_change", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other } },
- { "collision_circle", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other } },
- { "collision_ellipse", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other } },
- { "collision_line", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Boolean, AssetIDType.Boolean } },
- { "collision_point", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other } },
- { "collision_rectangle", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other } },
-
- { "mp_linear_step", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "mp_linear_step_object", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "mp_linear_path", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "mp_linear_path_object", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "mp_potential_settings", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "mp_potential_step", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "mp_potential_step_object", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- { "mp_potential_path", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "mp_potential_path_object", new[] { AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject } },
- // mp_grid only relevant ones because I'm lazy
- { "mp_grid_path", new[] { AssetIDType.Other, AssetIDType.Path, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean } },
- { "mp_grid_add_instances", new[] { AssetIDType.Other, AssetIDType.GameObject, AssetIDType.Other } },
-
- // TODO: 3D drawing, didn't bother
-
- // TODO: surface drawing
- { "draw_surface_ext", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other } },
-
- { "shader_is_compiled", new[] { AssetIDType.Shader } },
- { "shader_set", new[] { AssetIDType.Shader } },
- { "shader_get_uniform", new[] { AssetIDType.Shader, AssetIDType.Other } },
- { "shader_get_sampler_index", new[] { AssetIDType.Shader, AssetIDType.Other } },
- { "shader_enable_corner_id", new[] { AssetIDType.Boolean } },
-
- // { "shader_current", new AssetIDType[] { } }, returns shader.
-
- // Interpolation
- { "texture_set_interpolation", new[] { AssetIDType.Boolean } },
- { "texture_set_tiled", new[] { AssetIDType.Boolean } },
- { "gpu_set_texfilter", new[] { AssetIDType.Boolean } }, // GMS2 equivalent of texture_set_interpolation.
-
- // TODO: GMS2 tilemaps
- { "tileset_get_texture", new[] { AssetIDType.TileSet } },
- { "tileset_get_uvs", new[] { AssetIDType.TileSet } },
-
- // TODO: GMS2 layers
-
- // GMS2 only equivalents of room_speed.
- { "game_get_speed", new[] { AssetIDType.Enum_GameSpeed } },
- { "game_set_speed", new[] { AssetIDType.Other, AssetIDType.Enum_GameSpeed } },
-
- // window_ functions
- { "window_set_cursor", new[] { AssetIDType.Enum_MouseCursor } },
- { "window_set_fullscreen", new[] { AssetIDType.Boolean } },
- { "window_set_color", new[] { AssetIDType.Color } },
-
- { "io_clear", Array.Empty() },
- { "keyboard_check", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_check_pressed", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_check_released", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_check_direct", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_clear", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_key_press", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_key_release", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_set_map", new[] { AssetIDType.KeyboardKey, AssetIDType.KeyboardKey } },
- { "keyboard_get_map", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_unset_map", new[] { AssetIDType.KeyboardKey } },
- { "keyboard_set_numlock", new[] { AssetIDType.Boolean } },
- { "keyboard_get_numlock", Array.Empty() },
-
- // Mouse functions
- { "mouse_check_button", new[] { AssetIDType.MouseButton } },
- { "mouse_check_button_pressed", new[] { AssetIDType.MouseButton } },
- { "mouse_check_button_released", new[] { AssetIDType.MouseButton } },
- { "mouse_clear", new[] { AssetIDType.MouseButton } },
-
- // Device Mouse functions
- { "device_mouse_check_button", new[] { AssetIDType.Other, AssetIDType.MouseButton } },
- { "device_mouse_check_button_pressed", new[] { AssetIDType.Other, AssetIDType.MouseButton } },
- { "device_mouse_check_button_released", new[] { AssetIDType.Other, AssetIDType.MouseButton } },
- { "device_mouse_dbclick_enable", new[] { AssetIDType.Boolean } },
-
- { "gamepad_button_value", new[] { AssetIDType.Other, AssetIDType.Enum_GamepadButton } },
- { "gamepad_button_check", new[] { AssetIDType.Other, AssetIDType.Enum_GamepadButton } },
- { "gamepad_button_check_pressed", new[] { AssetIDType.Other, AssetIDType.Enum_GamepadButton } },
- { "gamepad_button_check_released", new[] { AssetIDType.Other, AssetIDType.Enum_GamepadButton } },
- { "gamepad_axis_value", new[] { AssetIDType.Other, AssetIDType.Enum_GamepadButton } },
- { "gamepad_set_color", new[] { AssetIDType.Other, AssetIDType.Color } }, // PS4 only, DualShock pads have an LED panel.
-
- { "buffer_create", new[] { AssetIDType.Other, AssetIDType.Enum_BufferKind, AssetIDType.Other } },
- { "buffer_create_from_vertex_buffer", new[] { AssetIDType.Other, AssetIDType.Enum_BufferKind, AssetIDType.Other } },
- { "buffer_create_from_vertex_buffer_ext", new[] { AssetIDType.Other, AssetIDType.Enum_BufferKind, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "buffer_read", new[] { AssetIDType.Other, AssetIDType.Enum_BufferType } },
- { "buffer_write", new[] { AssetIDType.Other, AssetIDType.Enum_BufferType, AssetIDType.Other } },
- { "buffer_peek", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Enum_BufferType } },
- { "buffer_poke", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Enum_BufferType, AssetIDType.Other } },
- { "buffer_fill", new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Enum_BufferType, AssetIDType.Other, AssetIDType.Other } },
- { "buffer_sizeof", new[] { AssetIDType.Enum_BufferType } },
- { "buffer_seek", new[] { AssetIDType.Other, AssetIDType.Enum_BufferSeek, AssetIDType.Other } },
-
- // Steam functions
- { "steam_ugc_create_item", new[] { AssetIDType.Other, AssetIDType.Enum_Steam_UGC_FileType } },
- { "steam_ugc_create_query_user", new[] { AssetIDType.Enum_Steam_UGC_List, AssetIDType.Enum_Steam_UGC_MatchType, AssetIDType.Enum_Steam_UGC_SortOrder, AssetIDType.Other } },
- { "steam_ugc_create_query_user_ex", new[] { AssetIDType.Enum_Steam_UGC_List, AssetIDType.Enum_Steam_UGC_MatchType, AssetIDType.Enum_Steam_UGC_SortOrder, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
- { "steam_ugc_create_query_all", new[] { AssetIDType.Enum_Steam_UGC_QueryType, AssetIDType.Enum_Steam_UGC_MatchType, AssetIDType.Other } },
- { "steam_ugc_create_query_all_ex", new[] { AssetIDType.Enum_Steam_UGC_QueryType, AssetIDType.Enum_Steam_UGC_MatchType, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other } },
-
- { "steam_ugc_query_set_cloud_filename_filter", new[] { AssetIDType.Other, AssetIDType.Boolean } },
- { "steam_ugc_query_set_match_any_tag", new[] { AssetIDType.Other, AssetIDType.Boolean } },
- { "steam_ugc_query_set_return_long_description", new[] { AssetIDType.Other, AssetIDType.Boolean } },
- { "steam_ugc_query_set_return_total_only", new[] { AssetIDType.Other, AssetIDType.Boolean } },
- { "steam_ugc_query_set_allow_cached_response", new[] { AssetIDType.Other, AssetIDType.Boolean } },
-
- { "steam_create_leaderboard", new[] { AssetIDType.Other, AssetIDType.Enum_Steam_LeaderBoard_Sort, AssetIDType.Enum_Steam_LeaderBoard_Display } },
-
- { "steam_activate_overlay", new[] { AssetIDType.Enum_Steam_Overlay } },
-
- // Also big TODO: Implement Boolean type for all these functions
-
- // Special internal functions
- { "@@GetInstance@@", new[] { AssetIDType.GameObject } },
- };
-
- builtin_var_overrides = new Dictionary>();
-
- if (data?.Code != null)
- {
- foreach (var code in data.Code)
- builtin_var_overrides[code.Name.Content] = new Dictionary();
- }
-
- builtin_vars = new Dictionary
- {
- // only the relevant ones because I'm sick of writing this
- { "background_index", AssetIDType.Background }, // array
- { "background_colour", AssetIDType.Color }, // array
- { "view_object", AssetIDType.GameObject }, // array
- { "path_index", AssetIDType.Path },
- { "room_persistent", AssetIDType.Boolean },
- { "room_first", AssetIDType.Room },
- { "room_last", AssetIDType.Room },
- { "room", AssetIDType.Room },
- { "object_index", AssetIDType.GameObject },
- { "sprite_index", AssetIDType.Sprite },
- { "mask_index", AssetIDType.Sprite },
- { "image_blend", AssetIDType.Color },
- { "event_object", AssetIDType.GameObject },
- { "keyboard_key", AssetIDType.KeyboardKey },
- { "keyboard_lastkey", AssetIDType.KeyboardKey },
- { "mouse_button", AssetIDType.MouseButton },
- { "mouse_last_button", AssetIDType.MouseButton },
- { "os_type", AssetIDType.Enum_OSType },
- { "timeline_index", AssetIDType.Timeline },
- { "path_endaction", AssetIDType.Enum_PathEndAction },
- { "view_enabled", AssetIDType.Boolean },
- { "view_visible", AssetIDType.Boolean },
- { "visible", AssetIDType.Boolean }
-
- };
-
- // TODO: make proper file/manifest for all games to use, not just UT/DR, and also not these specific names
- string lowerName = data?.GeneralInfo?.DisplayName?.Content.ToLower(CultureInfo.InvariantCulture);
-
- // Just Undertale
- if (lowerName != null && lowerName.StartsWith("undertale", StringComparison.InvariantCulture))
- {
-
- //AddOverrideFor("gml_Object_obj_wizardorb_chaser_Alarm_0", "pop", AssetIDType.Script);
-
- AddOverrideFor("gml_Object_obj_fakeborderdraw_Draw_0", "op", AssetIDType.GameObject);
- AddOverrideFor("gml_Object_obj_vertcroissant_Step_0", "op", AssetIDType.GameObject);
-
- return_types["scr_getmusindex"] = AssetIDType.Sound;
- return_types["scr_getsprite"] = AssetIDType.Sprite;
- return_types["caster_load"] = AssetIDType.Sound;
-
- // Sometimes used as a bool, should not matter though and be an improvement overall.
- builtin_vars.Add("king", AssetIDType.GameObject);
- builtin_funcs["SCR_TEXTSETUP"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- // I should confirm adding this causes no adverse effects later.
- builtin_vars.Add("myroom", AssetIDType.Room);
- // gml_Object_obj_dummytrigger_Collision_1576
- builtin_vars.Add("dummy", AssetIDType.GameObject);
- // gml_Object_obj_asriel_swordarm_Create_0
- builtin_vars.Add("sm", AssetIDType.GameObject);
- // This should do something to fix the piano room
- builtin_vars.Add("sprite_id", AssetIDType.Sprite);
- builtin_funcs["scr_getsprite"] = new[] { AssetIDType.Sprite };
- // gml_Object_obj_barabody_Create_0
- builtin_vars.Add("hand1pic", AssetIDType.Sprite);
- builtin_vars.Add("hand2pic", AssetIDType.Sprite);
- builtin_vars.Add("headpic", AssetIDType.Sprite);
- // gml_Object_obj_asgoreb_body_Create_0
- builtin_vars.Add("bodypic", AssetIDType.Sprite);
- // gml_Object_obj_castroll_Draw_0
- builtin_vars.Add("do_room_goto", AssetIDType.Boolean);
- builtin_vars.Add("do_room_goto_target", AssetIDType.Room);
- }
-
- // Just Deltarune
- if (lowerName != null && (lowerName == "survey_program" || lowerName.StartsWith("deltarune", StringComparison.InvariantCulture) || lowerName == "deltarune chapter 1 & 2"))
- {
- builtin_vars.Add("actreadysprite", AssetIDType.Sprite);
- builtin_vars.Add("actsprite", AssetIDType.Sprite);
- builtin_vars.Add("defendsprite", AssetIDType.Sprite);
- builtin_vars.Add("attackreadysprite", AssetIDType.Sprite);
- builtin_vars.Add("attacksprite", AssetIDType.Sprite);
- builtin_vars.Add("itemsprite", AssetIDType.Sprite);
- builtin_vars.Add("itemreadysprite", AssetIDType.Sprite);
- builtin_vars.Add("spellreadysprite", AssetIDType.Sprite);
- builtin_vars.Add("spellsprite", AssetIDType.Sprite);
- builtin_vars.Add("defeatsprite", AssetIDType.Sprite);
- builtin_vars.Add("victorysprite", AssetIDType.Sprite);
- builtin_vars.Add("dsprite_blush", AssetIDType.Sprite);
- builtin_vars.Add("usprite_blush", AssetIDType.Sprite);
- builtin_vars.Add("lsprite_blush", AssetIDType.Sprite);
- builtin_vars.Add("rsprite_blush", AssetIDType.Sprite);
- builtin_vars.Add("heartsprite", AssetIDType.Sprite);
- builtin_vars.Add("msprite", AssetIDType.Sprite);
- builtin_vars.Add("particlesprite", AssetIDType.Sprite);
- builtin_vars.Add("s_sprite", AssetIDType.Sprite);
- builtin_vars.Add("shopkeepsprite", AssetIDType.Sprite);
- builtin_vars.Add("slidesprite", AssetIDType.Sprite);
- builtin_vars.Add("smsprite", AssetIDType.Sprite);
- builtin_vars.Add("sparedsprite", AssetIDType.Sprite);
- builtin_vars.Add("sussprite", AssetIDType.Sprite);
- // "targetsprite" seems to be unused but just in case
- builtin_vars.Add("targetsprite", AssetIDType.Sprite);
- builtin_vars.Add("thissprite", AssetIDType.Sprite);
- builtin_vars.Add("touchsprite", AssetIDType.Sprite);
- builtin_vars.Add("sprite_type", AssetIDType.Sprite);
- builtin_vars.Add("darkzone", AssetIDType.Boolean);
- builtin_vars.Add("darkmode", AssetIDType.Boolean);
- builtin_vars.Add("darkify", AssetIDType.Boolean);
- builtin_vars.Add("noroom", AssetIDType.Boolean);
- builtin_vars.Add("loop", AssetIDType.Boolean);
- builtin_vars.Add("__loadedroom", AssetIDType.Room);
- builtin_vars.Add("roomchoice", AssetIDType.Room);
- builtin_vars.Add("writersnd", AssetIDType.Sound);
- builtin_vars.Add("sndchange", AssetIDType.Boolean);
- builtin_vars.Add("muschange", AssetIDType.Boolean);
- builtin_vars.Add("audchange", AssetIDType.Boolean);
- builtin_vars.Add("sndplay", AssetIDType.Boolean);
- builtin_vars.Add("sound_played", AssetIDType.Boolean);
- builtin_vars.Add("chalksound", AssetIDType.Boolean);
- builtin_vars.Add("grabsounded", AssetIDType.Boolean);
- builtin_vars.Add("hatsounded", AssetIDType.Boolean);
- builtin_vars.Add("soundplayed", AssetIDType.Boolean);
- builtin_vars.Add("windsound", AssetIDType.Boolean);
- builtin_vars.Add("playtextsound", AssetIDType.Boolean);
- builtin_vars.Add("textsound", AssetIDType.Sound);
- builtin_vars.Add("selectnoise", AssetIDType.Boolean);
- builtin_vars.Add("movenoise", AssetIDType.Boolean);
- builtin_vars.Add("grazenoise", AssetIDType.Boolean);
- builtin_vars.Add("selnoise", AssetIDType.Boolean);
- builtin_vars.Add("damagenoise", AssetIDType.Boolean);
- builtin_vars.Add("laznoise", AssetIDType.Boolean);
- builtin_vars.Add("stepnoise", AssetIDType.Boolean);
- builtin_vars.Add("bumpnoise", AssetIDType.Boolean);
- builtin_vars.Add("burstnoise", AssetIDType.Boolean);
- builtin_vars.Add("BACKNOISE", AssetIDType.Boolean);
- builtin_vars.Add("DEATHNOISE", AssetIDType.Boolean);
- builtin_vars.Add("gnoise", AssetIDType.Boolean);
- builtin_vars.Add("firstnoise", AssetIDType.Boolean);
- builtin_vars.Add("dmgnoise", AssetIDType.Boolean);
- builtin_vars.Add("usable", AssetIDType.Boolean);
- builtin_vars.Add("tempkeyitemusable", AssetIDType.Boolean);
- builtin_vars.Add("spellusable", AssetIDType.Boolean);
- builtin_vars.Add("NAMEFADE_COMPLETE", AssetIDType.Boolean);
- builtin_vars.Add("dancekris", AssetIDType.GameObject);
- builtin_vars.Add("noiseskip", AssetIDType.Boolean);
- builtin_vars.Add("attacked", AssetIDType.Boolean);
- builtin_vars.Add("attack_qual", AssetIDType.Boolean);
- builtin_vars.Add("attacking", AssetIDType.Boolean);
- builtin_vars.Add("attackedkris", AssetIDType.Boolean);
- builtin_vars.Add("attacks", AssetIDType.Boolean);
- builtin_vars.Add("battleend", AssetIDType.Boolean);
- builtin_vars.Add("battlemoder", AssetIDType.Boolean);
- builtin_vars.Add("becamebattle", AssetIDType.Boolean);
- builtin_vars.Add("seriousbattle", AssetIDType.Boolean);
- // Deltarune console versions.
- builtin_vars.Add("_border_image", AssetIDType.Sprite);
- // A little bit wrong, but probably fine.
- builtin_vars.Add("cango", AssetIDType.Boolean);
- builtin_vars.Add("canact", AssetIDType.Boolean);
- builtin_vars.Add("CANCEL", AssetIDType.Boolean);
- builtin_vars.Add("cancelwalk", AssetIDType.Boolean);
- builtin_vars.Add("cancelattack", AssetIDType.Boolean);
- builtin_vars.Add("canchoose", AssetIDType.Boolean);
- builtin_vars.Add("canclick", AssetIDType.Boolean);
- builtin_vars.Add("cancollide", AssetIDType.Boolean);
- builtin_vars.Add("candodge", AssetIDType.Boolean);
- builtin_vars.Add("candraw", AssetIDType.Boolean);
- builtin_vars.Add("canequip", AssetIDType.Boolean);
- builtin_vars.Add("canpress", AssetIDType.Boolean);
- builtin_vars.Add("cant", AssetIDType.Boolean);
- builtin_vars.Add("depthcancel", AssetIDType.Boolean);
- builtin_vars.Add("defend_command", AssetIDType.Boolean);
- builtin_vars.Add("automiss", AssetIDType.Boolean);
- builtin_vars.Add("awoke", AssetIDType.Boolean);
- builtin_vars.Add("act_command", AssetIDType.Boolean);
- builtin_vars.Add("acted", AssetIDType.Boolean);
- builtin_vars.Add("activated", AssetIDType.Boolean);
- builtin_vars.Add("activatethrow", AssetIDType.Boolean);
- builtin_vars.Add("addflag", AssetIDType.Boolean);
- builtin_vars.Add("addup", AssetIDType.Boolean);
- builtin_vars.Add("afford", AssetIDType.Boolean);
- builtin_vars.Add("aftercon", AssetIDType.Boolean);
- builtin_vars.Add("ALREADY", AssetIDType.Boolean);
- builtin_vars.Add("ambushed", AssetIDType.Boolean);
- builtin_vars.Add("permashake", AssetIDType.Boolean);
- builtin_vars.Add("aster", AssetIDType.Boolean);
- builtin_vars.Add("autoaster", AssetIDType.Boolean);
- builtin_vars.Add("autoed", AssetIDType.Boolean);
- builtin_vars.Add("betray", AssetIDType.Boolean);
- builtin_vars.Add("abovemaxhp", AssetIDType.Boolean);
- builtin_vars.Add("abletotarget", AssetIDType.Boolean);
- builtin_vars.Add("accept", AssetIDType.Boolean);
- builtin_vars.Add("actual", AssetIDType.Boolean);
- builtin_vars.Add("currentsong", AssetIDType.Sound);
- builtin_vars.Add("batmusic", AssetIDType.Sound);
- builtin_vars.Add("beanie", AssetIDType.Boolean);
- builtin_vars.Add("beaten", AssetIDType.Boolean);
- builtin_vars.Add("becomeflash", AssetIDType.Boolean);
- builtin_vars.Add("becomesleep", AssetIDType.Boolean);
- builtin_vars.Add("sleeping", AssetIDType.Boolean);
- builtin_vars.Add("bellcon", AssetIDType.Boolean);
- builtin_vars.Add("belowzero", AssetIDType.Boolean);
- //builtin_vars.Add("noiseskip", AssetIDType.Boolean);
- // Colors weave into a spire of flame
- builtin_vars.Add("mycolor", AssetIDType.Color);
- builtin_vars.Add("colorchange", AssetIDType.Boolean);
- builtin_vars.Add("xcolor", AssetIDType.Color);
- builtin_vars.Add("skippable", AssetIDType.Boolean);
- builtin_vars.Add("charcolor", AssetIDType.Color);
- builtin_vars.Add("hpcolor", AssetIDType.Color);
- builtin_vars.Add("bcolor", AssetIDType.Color);
- builtin_vars.Add("flashcolor", AssetIDType.Color);
- builtin_vars.Add("smcolor", AssetIDType.Color);
- builtin_vars.Add("dcolor", AssetIDType.Color);
- builtin_vars.Add("basecolor", AssetIDType.Color);
- builtin_vars.Add("_abilitycolor", AssetIDType.Color);
- builtin_vars.Add("mnamecolor1", AssetIDType.Color);
- builtin_vars.Add("mnamecolor2", AssetIDType.Color);
- builtin_vars.Add("scolor", AssetIDType.Color);
- builtin_vars.Add("arrowcolor", AssetIDType.Color);
- builtin_vars.Add("particlecolor", AssetIDType.Color);
- builtin_vars.Add("linecolor", AssetIDType.Color);
- builtin_vars.Add("fadecolor", AssetIDType.Color);
- builtin_vars.Add("color", AssetIDType.Color);
- // Scripts
- builtin_funcs["SCR_TEXTSETUP"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- }
-
- // Both UT and DR
- if (lowerName != null && (lowerName.StartsWith("undertale", StringComparison.InvariantCulture) || lowerName == "survey_program" || lowerName.StartsWith("deltarune", StringComparison.InvariantCulture)))
- {
- AddOverrideFor("gml_Script_scr_getbuttonsprite", "control", AssetIDType.Enum_GamepadButton);
- AddOverrideFor("gml_Script_scr_getbuttonsprite", "button", AssetIDType.Enum_GamepadButton);
-
- // Don't use this. It will not recompile.
- // AddOverrideFor("gml_Object_obj_shop3_Draw_0", "mycolor", AssetIDType.Macro);
-
- return_types["scr_getbuttonsprite"] = AssetIDType.Sprite;
-
- // gml_Object_obj_vulkinbody_UNDERTALE_Create_0
- // Seems to be used a lot as a regular value between the values of around 0-20.
- AddOverrideFor("obj_vulkinbody", "face", AssetIDType.Sprite);
-
- AddOverrideFor("gml_Script_scr_setfont", "newfont", AssetIDType.Font);
-
- //builtin_vars.Add("face", AssetIDType.Sprite);
-
- builtin_vars.Add("myfont", AssetIDType.Font);
-
- // Hope this script works!
- builtin_funcs["scr_bouncer"] = new[] { AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other };
-
- // Deltarune Chapter 2 asset resolutions:
- // Seems to be x, y, measure of distance (maybe)
-
- builtin_funcs["gml_Script_c_soundplay_wait"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_pitch_time"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_soundplay_wait"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_pitch_time"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
-
- builtin_funcs["gml_Script__background_set"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_c_addxy"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["gml_Script_c_autodepth"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_autofacing"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_autowalk"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_fadeout"] = new[] { AssetIDType.Other };
- builtin_funcs["gml_Script_c_fadeout_color"] = new[] { AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["gml_Script_c_instance"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_c_msgzurasu"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_pan"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_pannable"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_panobj"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["gml_Script_c_panspeed"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_script_instance"] = new[] { AssetIDType.GameObject, AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_script_instance_stop"] = new[] { AssetIDType.GameObject, AssetIDType.Script };
- builtin_funcs["gml_Script_c_setxy"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_shakestep_x"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_soundplay"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_c_soundplay_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_sprite"] = new[] { AssetIDType.Sprite };
- builtin_funcs["gml_Script_c_stickto"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["gml_Script_c_visible"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_wait"] = new[] { AssetIDType.Other };
- builtin_funcs["gml_Script_c_walkdirect"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_c_walkdirect_wait"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_d3d_set_fog_ch1"] = new[] { AssetIDType.Boolean, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_background_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_background_part_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_background_tiled_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_enable_alphablend_ch1"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_draw_enable_alphablend"] = new[] { AssetIDType.Boolean };
- builtin_funcs["draw_enable_alphablend"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_draw_monster_body_part"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_monster_body_part_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_sprite_ext_centerscale"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_sprite_ext_flash"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_sprite_skew_ext_cute"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_text_outline"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_i_ex"] = new[] { AssetIDType.GameObject };
- builtin_funcs["gml_Script_instance_create_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_msgsetloc"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_mus_loop"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_mus_loop_ext"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_safe_delete"] = new[] { AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_84_debug"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_scr_act_charsprite"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["gml_Script_scr_anim"] = new[] { AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_anim_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_battle_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_bullet_create"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_bulletspawner"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_caterpillar_facing_ch1"] = new[] { AssetIDType.Other };
- builtin_funcs["gml_Script_scr_custom_afterimage"] = new[] { AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_custom_afterimage_ext"] = new[] { AssetIDType.GameObject, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_dark_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_dark_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_dark_marker_depth"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_debug_keycheck"] = new[] { AssetIDType.KeyboardKey };
- builtin_funcs["gml_Script_scr_draw_background_ps4_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_draw_outline_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_draw_sprite_crop_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_ds_list_write"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_enemyblcon"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_following_afterimage"] = new[] { AssetIDType.GameObject, AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_forcefield"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean };
- builtin_funcs["gml_Script_scr_fx_housesquare"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["gml_Script_scr_guardpeek"] = new[] { AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_mercyadd"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_monster_add"] = new[] { AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_monster_change"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_move_to_point_over_time"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_pan_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_pan_to_obj"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_pan_to_obj_ch1"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_script_delayed"] = new[] { AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_textsetup"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_textsetup_ch1"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_snd_is_playing"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_loop"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_loop_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other };
- builtin_funcs["gml_Script_snd_play"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_play_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_play_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other };
- builtin_funcs["gml_Script_snd_play_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_snd_stop"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_stop_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["gml_Script_snd_volume"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_act_charsprite"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["gml_Script_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Boolean };
- builtin_funcs["gml_Script_c_actorsetsprites"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite, AssetIDType.Sprite, AssetIDType.Sprite };
- builtin_funcs["gml_Script_c_actortoobject"] = new[] { AssetIDType.GameObject };
- builtin_funcs["gml_Script_scr_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["scr_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["gml_Script_c_jump_sprite"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite };
- builtin_funcs["gml_Script_scr_dark_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean };
- builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Boolean };
- builtin_funcs["c_actorsetsprites"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite, AssetIDType.Sprite, AssetIDType.Sprite };
- builtin_funcs["c_actortoobject"] = new[] { AssetIDType.GameObject };
- builtin_funcs["c_jump_sprite"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite };
- builtin_funcs["scr_dark_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean };
- builtin_funcs["_background_set"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["c_addxy"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["c_autodepth"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_autofacing"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_autowalk"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_fadeout"] = new[] { AssetIDType.Other };
- builtin_funcs["c_fadeout_color"] = new[] { AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["c_instance"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["c_msgzurasu"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_pan"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_pannable"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_panobj"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["c_panspeed"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_script_instance"] = new[] { AssetIDType.GameObject, AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_script_instance_stop"] = new[] { AssetIDType.GameObject, AssetIDType.Script };
- builtin_funcs["c_setxy"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_shakestep_x"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["c_soundplay"] = new[] { AssetIDType.Sound };
- builtin_funcs["c_soundplay_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_sprite"] = new[] { AssetIDType.Sprite };
- builtin_funcs["c_stickto"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["c_visible"] = new[] { AssetIDType.Boolean };
- builtin_funcs["c_wait"] = new[] { AssetIDType.Other };
- builtin_funcs["c_walkdirect"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["c_walkdirect_wait"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["d3d_set_fog_ch1"] = new[] { AssetIDType.Boolean, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_background_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_background_part_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_background_tiled_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_enable_alphablend_ch1"] = new[] { AssetIDType.Boolean };
- builtin_funcs["draw_monster_body_part"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_monster_body_part_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_sprite_ext_centerscale"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_sprite_ext_flash"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other };
- builtin_funcs["draw_sprite_skew_ext_cute"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["draw_text_outline"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["i_ex"] = new[] { AssetIDType.GameObject };
- builtin_funcs["instance_create_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["msgsetloc"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["mus_loop"] = new[] { AssetIDType.Sound };
- builtin_funcs["mus_loop_ext"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["gml_Script_scr_bullet_inherit_ch1"] = new[] { AssetIDType.GameObject };
- builtin_funcs["scr_bullet_inherit_ch1"] = new[] { AssetIDType.GameObject };
- builtin_funcs["safe_delete"] = new[] { AssetIDType.GameObject };
- builtin_funcs["scr_84_debug"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_texture_set_interpolation"] = new[] { AssetIDType.Boolean };
- builtin_funcs["texture_set_interpolation"] = new[] { AssetIDType.Boolean };
- builtin_funcs["gml_Script_texture_set_interpolation_ch1"] = new[] { AssetIDType.Boolean };
- builtin_funcs["texture_set_interpolation_ch1"] = new[] { AssetIDType.Boolean };
- builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Boolean };
- builtin_funcs["scr_anim"] = new[] { AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["scr_anim_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other };
- builtin_funcs["scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_battle_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_bullet_create"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["scr_bulletspawner"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["scr_caterpillar_facing_ch1"] = new[] { AssetIDType.Other };
- builtin_funcs["scr_custom_afterimage"] = new[] { AssetIDType.Sprite };
- builtin_funcs["scr_custom_afterimage_ext"] = new[] { AssetIDType.GameObject, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_dark_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_dark_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_dark_marker_depth"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_debug_keycheck"] = new[] { AssetIDType.KeyboardKey };
- builtin_funcs["scr_draw_background_ps4_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_draw_outline_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_draw_sprite_crop_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_ds_list_write"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_enemyblcon"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_following_afterimage"] = new[] { AssetIDType.GameObject, AssetIDType.GameObject };
- builtin_funcs["scr_forcefield"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean };
- builtin_funcs["scr_fx_housesquare"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color };
- builtin_funcs["scr_guardpeek"] = new[] { AssetIDType.GameObject };
- builtin_funcs["scr_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite };
- builtin_funcs["scr_mercyadd"] = new[] { AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_monster_add"] = new[] { AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["scr_monster_change"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject };
- builtin_funcs["scr_move_to_point_over_time"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_pan_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_pan_to_obj"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["scr_pan_to_obj_ch1"] = new[] { AssetIDType.GameObject, AssetIDType.Other };
- builtin_funcs["scr_script_delayed"] = new[] { AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_textsetup"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_textsetup_ch1"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["snd_is_playing"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_loop"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_loop_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other };
- builtin_funcs["snd_play"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_play_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_play_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other };
- builtin_funcs["snd_play_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["snd_stop"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_stop_ch1"] = new[] { AssetIDType.Sound };
- builtin_funcs["snd_volume"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other };
- builtin_funcs["scr_valid_room"] = new[] { AssetIDType.Room, AssetIDType.Other };
-
- builtin_vars.Add("_instruments", AssetIDType.Sound);
- builtin_vars.Add("_instrumentsB", AssetIDType.Sound);
- builtin_vars.Add("_instrumentsAlt", AssetIDType.Sound);
- builtin_vars.Add("doorSound", AssetIDType.Sound);
- builtin_vars.Add("doorSound2", AssetIDType.Sound);
- builtin_vars.Add("pushSound", AssetIDType.Sound);
- builtin_vars.Add("firstsound", AssetIDType.Sound);
- builtin_vars.Add("lastsound", AssetIDType.Sound);
- builtin_vars.Add("voiceclips", AssetIDType.Sound);
-
- builtin_vars.Add("roome", AssetIDType.Room);
- builtin_vars.Add("room_index", AssetIDType.Room);
- builtin_vars.Add("room_offset", AssetIDType.Room);
- builtin_vars.Add("door_destination", AssetIDType.Room);
-
- builtin_vars.Add("source", AssetIDType.GameObject);
- builtin_vars.Add("sourceobject", AssetIDType.GameObject);
- //builtin_vars.Add("target", AssetIDType.GameObject);
- builtin_vars.Add("writergod", AssetIDType.GameObject);
- AddOverrideFor("obj_carcutscene", "k", AssetIDType.GameObject);
- AddOverrideFor("obj_carcutscene_ch1", "k", AssetIDType.GameObject);
- builtin_vars.Add("childBullet", AssetIDType.GameObject);
- builtin_vars.Add("body_obj", AssetIDType.GameObject);
- builtin_vars.Add("sneo", AssetIDType.GameObject);
- builtin_vars.Add("swatchbc", AssetIDType.GameObject);
- builtin_vars.Add("animator", AssetIDType.GameObject);
-
- builtin_vars.Add("new_color", AssetIDType.Color);
- builtin_vars.Add("base_colors", AssetIDType.Color);
- builtin_vars.Add("COL_A", AssetIDType.Color);
- builtin_vars.Add("COL_B", AssetIDType.Color);
- builtin_vars.Add("COL_PLUS", AssetIDType.Color);
- builtin_vars.Add("stats_amount", AssetIDType.Color);
- builtin_vars.Add("housecolor", AssetIDType.Color);
- builtin_vars.Add("partblend", AssetIDType.Color);
- builtin_vars.Add("drawcolor", AssetIDType.Color);
- builtin_vars.Add("shockwave_color", AssetIDType.Color);
- builtin_vars.Add("bird_color", AssetIDType.Color);
- builtin_vars.Add("startcolor", AssetIDType.Color);
- builtin_vars.Add("targetColor", AssetIDType.Color);
- builtin_vars.Add("override_color", AssetIDType.Color);
-
- builtin_vars.Add("control", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("button0", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("button1", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("button2", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("input_g", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("gamepad_controls", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("new_gamepad_key", AssetIDType.Enum_GamepadButton);
- builtin_vars.Add("_control", AssetIDType.Enum_GamepadButton);
-
- builtin_vars.Add("input_k", AssetIDType.KeyboardKey);
-
-/*
- builtin_vars.Add("_pacified", AssetIDType.Boolean);
- builtin_vars.Add("_spared", AssetIDType.Boolean);
- builtin_vars.Add("_violenced", AssetIDType.Boolean);
- builtin_vars.Add("_frozened", AssetIDType.Boolean);
-*/
- builtin_vars.Add("nitro", AssetIDType.Boolean);
- builtin_vars.Add("confirm", AssetIDType.Boolean);
- builtin_vars.Add("attack_succeeded", AssetIDType.Boolean);
- builtin_vars.Add("phase3_hit_check", AssetIDType.Boolean);
- builtin_vars.Add("beat_phase2_no_damage_taken_check", AssetIDType.Boolean);
- builtin_vars.Add("beat_phase1_no_damage_taken_check", AssetIDType.Boolean);
- builtin_vars.Add("has_tutorial_kick_hit_player", AssetIDType.Boolean);
- builtin_vars.Add("freezable", AssetIDType.Boolean);
- builtin_vars.Add("charcan", AssetIDType.Boolean);
- builtin_vars.Add("kktalked", AssetIDType.Boolean);
- builtin_vars.Add("capntalked", AssetIDType.Boolean);
- builtin_vars.Add("moveswapped", AssetIDType.Boolean);
- builtin_vars.Add("cause_explosion", AssetIDType.Boolean);
- builtin_vars.Add("exploded", AssetIDType.Boolean);
- builtin_vars.Add("make_simple_bullet", AssetIDType.Boolean);
- builtin_vars.Add("dance_active", AssetIDType.Boolean);
- builtin_vars.Add("spawnVirus", AssetIDType.Boolean);
- builtin_vars.Add("spawning", AssetIDType.Boolean);
- builtin_vars.Add("caralert", AssetIDType.Boolean);
- builtin_vars.Add("shotready", AssetIDType.Boolean);
- builtin_vars.Add("deleteCars", AssetIDType.Boolean);
- builtin_vars.Add("clear_traffic", AssetIDType.Boolean);
- builtin_vars.Add("onroad", AssetIDType.Boolean);
- builtin_vars.Add("quizloop", AssetIDType.Boolean);
- builtin_vars.Add("quizmode", AssetIDType.Boolean);
- builtin_vars.Add("gif_recording", AssetIDType.Boolean);
- builtin_vars.Add("quicksaved", AssetIDType.Boolean);
- builtin_vars.Add("paused", AssetIDType.Boolean);
- builtin_vars.Add("dbselect", AssetIDType.Boolean);
- builtin_vars.Add("drawfeet", AssetIDType.Boolean);
- builtin_vars.Add("moving", AssetIDType.Boolean);
- builtin_vars.Add("duckmode", AssetIDType.Boolean);
- builtin_vars.Add("dancing", AssetIDType.Boolean);
- builtin_vars.Add("hasCandy", AssetIDType.Boolean);
- builtin_vars.Add("allowAll", AssetIDType.Boolean);
- builtin_vars.Add("partvisible", AssetIDType.Boolean);
- builtin_vars.Add("breaking", AssetIDType.Boolean);
- builtin_vars.Add("destroyable", AssetIDType.Boolean);
- builtin_vars.Add("skipintro", AssetIDType.Boolean);
- builtin_vars.Add("peeking", AssetIDType.Boolean);
- builtin_vars.Add("destroyonhit", AssetIDType.Boolean);
- builtin_vars.Add("spawned", AssetIDType.Boolean);
- builtin_vars.Add("turnSignal", AssetIDType.Boolean);
- builtin_vars.Add("dothis", AssetIDType.Boolean);
- builtin_vars.Add("stuck", AssetIDType.Boolean);
- builtin_vars.Add("save_loaded", AssetIDType.Boolean);
- builtin_vars.Add("save_ready", AssetIDType.Boolean);
- builtin_vars.Add("ignoredepth", AssetIDType.Boolean);
- builtin_vars.Add("drawshadow", AssetIDType.Boolean);
- builtin_vars.Add("tracking", AssetIDType.Boolean);
- builtin_vars.Add("haventusedspell", AssetIDType.Boolean);
- builtin_vars.Add("back", AssetIDType.Boolean);
- builtin_vars.Add("spare_used", AssetIDType.Boolean);
- builtin_vars.Add("touched", AssetIDType.Boolean);
- builtin_vars.Add("interactshower", AssetIDType.Boolean);
- builtin_vars.Add("windowswitcher", AssetIDType.Boolean);
- builtin_vars.Add("cutsceneshow", AssetIDType.Boolean);
- builtin_vars.Add("showdebug", AssetIDType.Boolean);
- builtin_vars.Add("writedisplay", AssetIDType.Boolean);
- builtin_vars.Add("displaymode", AssetIDType.Boolean);
- builtin_vars.Add("oldway", AssetIDType.Boolean);
- builtin_vars.Add("special", AssetIDType.Boolean);
- builtin_vars.Add("sameattacker", AssetIDType.Boolean);
- builtin_vars.Add("sameattack", AssetIDType.Boolean);
- builtin_vars.Add("bosscheck", AssetIDType.Boolean);
- builtin_vars.Add("dheld", AssetIDType.Boolean);
- builtin_vars.Add("uheld", AssetIDType.Boolean);
- builtin_vars.Add("rheld", AssetIDType.Boolean);
- builtin_vars.Add("lheld", AssetIDType.Boolean);
- builtin_vars.Add("edgedebug", AssetIDType.Boolean);
- builtin_vars.Add("solve", AssetIDType.Boolean);
- builtin_vars.Add("noface", AssetIDType.Boolean);
- builtin_vars.Add("drawshine", AssetIDType.Boolean);
- builtin_vars.Add("weird", AssetIDType.Boolean);
- builtin_vars.Add("freeze", AssetIDType.Boolean);
- builtin_vars.Add("fightmode", AssetIDType.Boolean);
- builtin_vars.Add("show_door_open", AssetIDType.Boolean);
- builtin_vars.Add("disable_face", AssetIDType.Boolean);
- builtin_vars.Add("enable_face", AssetIDType.Boolean);
- builtin_vars.Add("end_game", AssetIDType.Boolean);
- builtin_vars.Add("float", AssetIDType.Boolean);
- builtin_vars.Add("checkPress", AssetIDType.Boolean);
- builtin_vars.Add("pressable", AssetIDType.Boolean);
- builtin_vars.Add("init_forcefields", AssetIDType.Boolean);
- builtin_vars.Add("auto_continue", AssetIDType.Boolean);
- builtin_vars.Add("no_silhouette", AssetIDType.Boolean);
- builtin_vars.Add("allow_move", AssetIDType.Boolean);
- builtin_vars.Add("grazed", AssetIDType.Boolean);
- builtin_vars.Add("pressed", AssetIDType.Boolean);
- builtin_vars.Add("border_fade_in", AssetIDType.Boolean);
- builtin_vars.Add("border_fade_out", AssetIDType.Boolean);
- builtin_vars.Add("rideactgo", AssetIDType.Boolean);
- builtin_vars.Add("forgiveLoop", AssetIDType.Boolean);
- builtin_vars.Add("readyToGo", AssetIDType.Boolean);
- builtin_vars.Add("shouldActivate", AssetIDType.Boolean);
- builtin_vars.Add("destroyoffscreen", AssetIDType.Boolean);
- builtin_vars.Add("autocancel", AssetIDType.Boolean);
- builtin_vars.Add("destroyoncomplete", AssetIDType.Boolean);
- builtin_vars.Add("spelluse", AssetIDType.Boolean);
- builtin_vars.Add("animdone", AssetIDType.Boolean);
- builtin_vars.Add("bigcar", AssetIDType.Boolean);
- //builtin_vars.Add("forceypos", AssetIDType.Boolean);
- builtin_vars.Add("bump", AssetIDType.Boolean);
- builtin_vars.Add("drawpremonition", AssetIDType.Boolean);
- builtin_vars.Add("premonition", AssetIDType.Boolean);
- builtin_vars.Add("queenmode", AssetIDType.Boolean);
- builtin_vars.Add("arcade", AssetIDType.Boolean);
- builtin_vars.Add("arcadebaseballused", AssetIDType.Boolean);
- builtin_vars.Add("playerhitbykick", AssetIDType.Boolean);
- builtin_vars.Add("finalbaseballused", AssetIDType.Boolean);
- builtin_vars.Add("has_boss_done_pattern7", AssetIDType.Boolean);
- builtin_vars.Add("dead", AssetIDType.Boolean);
- builtin_vars.Add("filefound", AssetIDType.Boolean);
- builtin_vars.Add("loadcompletion", AssetIDType.Boolean);
- builtin_vars.Add("COMPLETEFILE_PREV", AssetIDType.Boolean);
- builtin_vars.Add("input_pressed", AssetIDType.Boolean);
- builtin_vars.Add("input_held", AssetIDType.Boolean);
- builtin_vars.Add("input_released", AssetIDType.Boolean);
- builtin_vars.Add("return_title", AssetIDType.Boolean);
- builtin_vars.Add("d_cancel", AssetIDType.Boolean);
- builtin_vars.Add("killactive", AssetIDType.Boolean);
- builtin_vars.Add("gameover", AssetIDType.Boolean);
- builtin_vars.Add("font_set", AssetIDType.Boolean);
- builtin_vars.Add("playcheck", AssetIDType.Boolean);
- builtin_vars.Add("play", AssetIDType.Boolean);
- builtin_vars.Add("mouthmove", AssetIDType.Boolean);
- builtin_vars.Add("spellanim", AssetIDType.Boolean);
- builtin_vars.Add("textwait", AssetIDType.Boolean);
- builtin_vars.Add("noneleft", AssetIDType.Boolean);
- builtin_vars.Add("actingsimul", AssetIDType.Boolean);
- builtin_vars.Add("halt", AssetIDType.Boolean);
- builtin_vars.Add("drawaster", AssetIDType.Boolean);
- builtin_vars.Add("formatted", AssetIDType.Boolean);
- builtin_vars.Add("forcebutton1", AssetIDType.Boolean);
- //builtin_vars.Add("inv", AssetIDType.Boolean);
- builtin_vars.Add("actsimulsus", AssetIDType.Boolean);
- builtin_vars.Add("actsimulral", AssetIDType.Boolean);
- builtin_vars.Add("actsimulnoe", AssetIDType.Boolean);
- builtin_vars.Add("actsimul", AssetIDType.Boolean);
- builtin_vars.Add("actingsus", AssetIDType.Boolean);
- builtin_vars.Add("actingral", AssetIDType.Boolean);
- builtin_vars.Add("actingnoe", AssetIDType.Boolean);
- builtin_vars.Add("shakereduct", AssetIDType.Boolean);
- builtin_vars.Add("_playsound", AssetIDType.Boolean);
- builtin_vars.Add("haveit", AssetIDType.Boolean);
- builtin_vars.Add("removed", AssetIDType.Boolean);
- builtin_vars.Add("_noroominventory", AssetIDType.Boolean);
- builtin_vars.Add("_pocketed", AssetIDType.Boolean);
- builtin_vars.Add("replaceable", AssetIDType.Boolean);
- builtin_vars.Add("invert", AssetIDType.Boolean);
- builtin_vars.Add("is_dualshock", AssetIDType.Boolean);
- builtin_vars.Add("isString", AssetIDType.Boolean);
- builtin_vars.Add("charauto", AssetIDType.Boolean);
- builtin_vars.Add("auto_length", AssetIDType.Boolean);
- builtin_vars.Add("simultotal_funny", AssetIDType.Boolean);
- builtin_vars.Add("actingsingle", AssetIDType.Boolean);
- builtin_vars.Add("talked", AssetIDType.Boolean);
- builtin_vars.Add("acting", AssetIDType.Boolean);
- builtin_vars.Add("__noactors", AssetIDType.Boolean);
- builtin_vars.Add("fatal", AssetIDType.Boolean);
- builtin_vars.Add("recruitable", AssetIDType.Boolean);
- builtin_vars.Add("__frozen", AssetIDType.Boolean);
- builtin_vars.Add("debug", AssetIDType.Boolean);
- builtin_vars.Add("oldcalculation", AssetIDType.Boolean);
- builtin_vars.Add("chemg_god_mode", AssetIDType.Boolean);
- builtin_vars.Add("debug_inv", AssetIDType.Boolean);
- builtin_vars.Add("gamepad_shoulderlb_reassign", AssetIDType.Boolean);
- builtin_vars.Add("ladef", AssetIDType.Boolean);
- builtin_vars.Add("armorconverted", AssetIDType.Boolean);
- builtin_vars.Add("armorchar1temp", AssetIDType.Boolean);
- builtin_vars.Add("armorchar2temp", AssetIDType.Boolean);
- builtin_vars.Add("armorchar3temp", AssetIDType.Boolean);
- builtin_vars.Add("armorchar4temp", AssetIDType.Boolean);
- builtin_vars.Add("weaponchar1temp", AssetIDType.Boolean);
- builtin_vars.Add("weaponchar2temp", AssetIDType.Boolean);
- builtin_vars.Add("weaponchar3temp", AssetIDType.Boolean);
- builtin_vars.Add("weaponchar4temp", AssetIDType.Boolean);
- builtin_vars.Add("legacy", AssetIDType.Boolean);
- builtin_vars.Add("jp_data_loaded", AssetIDType.Boolean);
- builtin_vars.Add("ingame", AssetIDType.Boolean);
- builtin_vars.Add("skipped", AssetIDType.Boolean);
- builtin_vars.Add("draw_screen", AssetIDType.Boolean);
- builtin_vars.Add("gamepad_active", AssetIDType.Boolean);
- builtin_vars.Add("screen_border_active", AssetIDType.Boolean);
- builtin_vars.Add("window_center_toggle", AssetIDType.Boolean);
- builtin_vars.Add("fullscreen_toggle", AssetIDType.Boolean);
- builtin_vars.Add("keyboard_active", AssetIDType.Boolean);
- builtin_vars.Add("_isConsole", AssetIDType.Boolean);
- builtin_vars.Add("pausing", AssetIDType.Boolean);
- builtin_vars.Add("store_prompt", AssetIDType.Boolean);
- builtin_vars.Add("visit_shop", AssetIDType.Boolean);
- builtin_vars.Add("loaded", AssetIDType.Boolean);
- builtin_vars.Add("commerce_dialog_open", AssetIDType.Boolean);
- builtin_vars.Add("beenset", AssetIDType.Boolean);
- builtin_vars.Add("menuOpened", AssetIDType.Boolean);
- builtin_vars.Add("game_won", AssetIDType.Boolean);
- builtin_vars.Add("timeruse", AssetIDType.Boolean);
- builtin_vars.Add("leapmode", AssetIDType.Boolean);
- //builtin_vars.Add("chapter_return", AssetIDType.Boolean);
- builtin_vars.Add("m_quit", AssetIDType.Boolean);
- builtin_vars.Add("border_select", AssetIDType.Boolean);
- builtin_vars.Add("check_border", AssetIDType.Boolean);
- builtin_vars.Add("disable_border", AssetIDType.Boolean);
- builtin_vars.Add("cancelnoise", AssetIDType.Boolean);
- builtin_vars.Add("_disable_border", AssetIDType.Boolean);
- builtin_vars.Add("restart", AssetIDType.Boolean);
- //builtin_vars.Add("battlemode", AssetIDType.Boolean);
- builtin_vars.Add("init", AssetIDType.Boolean);
- builtin_vars.Add("autobattle", AssetIDType.Boolean);
- builtin_vars.Add("acttoenemytalktransition", AssetIDType.Boolean); // Probably, haven't checked
- builtin_vars.Add("STARTGAME", AssetIDType.Boolean);
- builtin_vars.Add("SELNOISE", AssetIDType.Boolean);
- builtin_vars.Add("temp_comment_is_interesting", AssetIDType.Boolean);
- builtin_vars.Add("FILECHECK", AssetIDType.Boolean);
- builtin_vars.Add("input_enabled", AssetIDType.Boolean);
- builtin_vars.Add("INCOMPLETE_LOAD", AssetIDType.Boolean);
- builtin_vars.Add("is_console", AssetIDType.Boolean);
- builtin_vars.Add("CANQUIT", AssetIDType.Boolean);
- builtin_vars.Add("BGMADE", AssetIDType.Boolean);
- builtin_vars.Add("finished", AssetIDType.Boolean);
- builtin_vars.Add("is_active", AssetIDType.Boolean);
- builtin_vars.Add("spam_car", AssetIDType.Boolean);
- builtin_vars.Add("show_queen", AssetIDType.Boolean);
- builtin_vars.Add("queen_animate", AssetIDType.Boolean);
- builtin_vars.Add("actor_visible", AssetIDType.Boolean);
- builtin_vars.Add("drawcustom", AssetIDType.Boolean);
- builtin_vars.Add("alwayswalking", AssetIDType.Boolean);
- builtin_vars.Add("prepopulate", AssetIDType.Boolean);
- builtin_vars.Add("walking", AssetIDType.Boolean);
- builtin_vars.Add("speedadjust", AssetIDType.Boolean);
- builtin_vars.Add("fresh", AssetIDType.Boolean);
- builtin_vars.Add("canactnoe", AssetIDType.Boolean);
- builtin_vars.Add("canactral", AssetIDType.Boolean);
- builtin_vars.Add("canactsus", AssetIDType.Boolean);
- builtin_vars.Add("skip", AssetIDType.Boolean);
- builtin_vars.Add("stayVisible", AssetIDType.Boolean);
- builtin_vars.Add("playsound", AssetIDType.Boolean);
- builtin_vars.Add("noAlertSound", AssetIDType.Boolean);
-
- builtin_vars.Add("hitbox", AssetIDType.Sprite);
- builtin_vars.Add("sprite", AssetIDType.Sprite);
- builtin_vars.Add("writerimg", AssetIDType.Sprite);
- builtin_vars.Add("_sprite", AssetIDType.Sprite);
- builtin_vars.Add("specialsprite", AssetIDType.Sprite);
- builtin_vars.Add("o_boxingqueen_janky_sprite_index", AssetIDType.Sprite);
- builtin_vars.Add("character_sprite", AssetIDType.Sprite);
- builtin_vars.Add("victorySprite", AssetIDType.Sprite);
- builtin_vars.Add("contentsprite", AssetIDType.Sprite);
- builtin_vars.Add("sprite_palette", AssetIDType.Sprite);
- builtin_vars.Add("head_sprite", AssetIDType.Sprite);
- builtin_vars.Add("pilot_sprite", AssetIDType.Sprite);
- builtin_vars.Add("default_sprite_front", AssetIDType.Sprite);
- builtin_vars.Add("default_sprite_back", AssetIDType.Sprite);
- builtin_vars.Add("hurt_sprite_front", AssetIDType.Sprite);
- builtin_vars.Add("hurt_sprite_back", AssetIDType.Sprite);
- builtin_vars.Add("hurt_character_sprite", AssetIDType.Sprite);
- builtin_vars.Add("raspr", AssetIDType.Sprite);
- builtin_vars.Add("suspr", AssetIDType.Sprite);
- builtin_vars.Add("car_sprite", AssetIDType.Sprite);
- builtin_vars.Add("hsprite", AssetIDType.Sprite);
- builtin_vars.Add("vsprite", AssetIDType.Sprite);
- builtin_vars.Add("menuSprite", AssetIDType.Sprite);
- builtin_vars.Add("actor_startsprite", AssetIDType.Sprite);
- builtin_vars.Add("actor_endsprite", AssetIDType.Sprite);
- builtin_vars.Add("button_sprite", AssetIDType.Sprite);
- builtin_vars.Add("endanimation", AssetIDType.Sprite);
- builtin_vars.Add("lastSprite", AssetIDType.Sprite);
- builtin_vars.Add("myLastSprite", AssetIDType.Sprite);
- builtin_vars.Add("mySprite", AssetIDType.Sprite);
- builtin_vars.Add("current_sprites", AssetIDType.Sprite);
- builtin_vars.Add("fireworksprite", AssetIDType.Sprite);
- builtin_vars.Add("customSprite", AssetIDType.Sprite);
- builtin_vars.Add("queen_sprite", AssetIDType.Sprite);
- builtin_vars.Add("pic", AssetIDType.Sprite);
- builtin_vars.Add("picb", AssetIDType.Sprite);
- builtin_vars.Add("leftTurnSprite", AssetIDType.Sprite);
- builtin_vars.Add("rightTurnSprite", AssetIDType.Sprite);
- builtin_vars.Add("topsprite", AssetIDType.Sprite);
- builtin_vars.Add("frozensprite", AssetIDType.Sprite);
- builtin_vars.Add("layersprites", AssetIDType.Sprite);
- builtin_vars.Add("cursor_sprite", AssetIDType.Sprite);
- builtin_vars.Add("rimsprite", AssetIDType.Sprite);
- builtin_vars.Add("flashSprite", AssetIDType.Sprite);
- builtin_vars.Add("spriteindex1", AssetIDType.Sprite);
- builtin_vars.Add("spriteindex2", AssetIDType.Sprite);
- builtin_vars.Add("shieldpiece_sprite_index", AssetIDType.Sprite);
- builtin_vars.Add("shieldpiece_alpha", AssetIDType.Sprite);
- builtin_vars.Add("sabersprite", AssetIDType.Sprite);
- builtin_vars.Add("idlesprite", AssetIDType.Sprite);
- builtin_vars.Add("puzzle_icon", AssetIDType.Sprite);
- builtin_vars.Add("item0pic", AssetIDType.Sprite);
- builtin_vars.Add("item1pic", AssetIDType.Sprite);
- builtin_vars.Add("item2pic", AssetIDType.Sprite);
- builtin_vars.Add("item3pic", AssetIDType.Sprite);
- builtin_vars.Add("top", AssetIDType.Sprite);
- builtin_vars.Add("sparesprite", AssetIDType.Sprite);
- builtin_vars.Add("_rollSprites", AssetIDType.Sprite);
- builtin_vars.Add("_sprites", AssetIDType.Sprite);
- builtin_vars.Add("_spritesTea", AssetIDType.Sprite);
- builtin_vars.Add("fruit", AssetIDType.Sprite);
- builtin_vars.Add("chsprite", AssetIDType.Sprite);
- builtin_vars.Add("_headsprite", AssetIDType.Sprite);
- builtin_vars.Add("partsprite", AssetIDType.Sprite);
-
- builtin_vars.Add("fleetsize", AssetIDType.Other);
- builtin_vars.Add("ar", AssetIDType.Other);
- builtin_vars.Add("as", AssetIDType.Other);
- builtin_vars.Add("be", AssetIDType.Other);
- builtin_vars.Add("char", AssetIDType.Other);
- builtin_vars.Add("choice", AssetIDType.Other);
- builtin_vars.Add("direction", AssetIDType.Other);
- builtin_vars.Add("encounterno", AssetIDType.Other);
- builtin_vars.Add("flag", AssetIDType.Other);
- builtin_vars.Add("gi", AssetIDType.Other);
- builtin_vars.Add("gold", AssetIDType.Other);
- builtin_vars.Add("hg", AssetIDType.Other);
- builtin_vars.Add("la", AssetIDType.Other);
- builtin_vars.Add("lhp", AssetIDType.Other);
- builtin_vars.Add("na", AssetIDType.Other);
- builtin_vars.Add("nl", AssetIDType.Other);
- builtin_vars.Add("no", AssetIDType.Other);
- builtin_vars.Add("plot", AssetIDType.Other);
- builtin_vars.Add("qu", AssetIDType.Other);
- builtin_vars.Add("sa", AssetIDType.Other);
- builtin_vars.Add("side", AssetIDType.Other);
- builtin_vars.Add("st", AssetIDType.Other);
- builtin_vars.Add("sw", AssetIDType.Other);
- builtin_vars.Add("to", AssetIDType.Other);
- builtin_vars.Add("un", AssetIDType.Other);
- builtin_vars.Add("walkpoint", AssetIDType.Other);
- builtin_vars.Add("xx", AssetIDType.Other);
- builtin_vars.Add("yy", AssetIDType.Other);
-
- // Undertale 1.05+ and Deltarune console versions.
- builtin_funcs["scr_draw_background_ps4"] = new[] { AssetIDType.Background, AssetIDType.Other, AssetIDType.Other };
- builtin_vars.Add("room_id", AssetIDType.Room);
-
- builtin_vars.Add("currentroom", AssetIDType.Room);
- builtin_vars.Add("dsprite", AssetIDType.Sprite);
- builtin_vars.Add("usprite", AssetIDType.Sprite);
- builtin_vars.Add("lsprite", AssetIDType.Sprite);
- builtin_vars.Add("rsprite", AssetIDType.Sprite);
- builtin_vars.Add("dtsprite", AssetIDType.Sprite);
- builtin_vars.Add("utsprite", AssetIDType.Sprite);
- builtin_vars.Add("ltsprite", AssetIDType.Sprite);
- builtin_vars.Add("rtsprite", AssetIDType.Sprite);
- builtin_vars.Add("normalsprite", AssetIDType.Sprite);
- builtin_vars.Add("hurtsprite", AssetIDType.Sprite);
- builtin_vars.Add("hurtsound", AssetIDType.Sound);
- // New built in vars found by Grossley
- builtin_vars.Add("interact", AssetIDType.Other);
- // Test me!
- builtin_vars.Add("sound0", AssetIDType.Sound);
- // From v1.11 Undertale comparison, not tested unlike v1.001!
- builtin_vars.Add("asprite", AssetIDType.Sprite);
- builtin_vars.Add("bsprite", AssetIDType.Sprite);
- builtin_vars.Add("tailobj", AssetIDType.GameObject);
- builtin_vars.Add("heart", AssetIDType.GameObject);
- builtin_vars.Add("draedmode", AssetIDType.Boolean);
- // Deltarune
- builtin_vars.Add("haveauto", AssetIDType.Boolean);
- builtin_vars.Add("goahead", AssetIDType.Boolean);
- builtin_vars.Add("is_auto_susie", AssetIDType.Boolean);
- builtin_vars.Add("techwon", AssetIDType.Boolean);
- builtin_vars.Add("itemed", AssetIDType.Boolean);
- builtin_vars.Add("critical", AssetIDType.Boolean);
- builtin_vars.Add("tile_fade", AssetIDType.Boolean);
- builtin_vars.Add("boss", AssetIDType.Boolean);
- builtin_vars.Add("skipvictory", AssetIDType.Boolean);
- builtin_vars.Add("victory", AssetIDType.Boolean);
- builtin_vars.Add("fighting", AssetIDType.Boolean);
- builtin_vars.Add("charmove", AssetIDType.Boolean);
- builtin_vars.Add("charcantarget", AssetIDType.Boolean);
- builtin_vars.Add("chardead", AssetIDType.Boolean);
- builtin_vars.Add("targeted", AssetIDType.Boolean);
- builtin_vars.Add("havechar", AssetIDType.Boolean);
- builtin_vars.Add("noreturn", AssetIDType.Boolean);
- builtin_vars.Add("timeron", AssetIDType.Boolean);
- builtin_vars.Add("flash", AssetIDType.Boolean);
- builtin_vars.Add("mercydraw", AssetIDType.Boolean);
- builtin_vars.Add("tireddraw", AssetIDType.Boolean);
- builtin_vars.Add("pacify_glow", AssetIDType.Boolean);
- builtin_vars.Add("drawsus", AssetIDType.Boolean);
- builtin_vars.Add("drawral", AssetIDType.Boolean);
- builtin_vars.Add("susblend", AssetIDType.Color);
- builtin_vars.Add("ralblend", AssetIDType.Color);
- builtin_vars.Add("hurt", AssetIDType.Boolean);
- builtin_vars.Add("skipme", AssetIDType.Boolean);
- builtin_vars.Add("darken", AssetIDType.Boolean);
- builtin_vars.Add("combatdarken", AssetIDType.Boolean);
- builtin_vars.Add("stepped", AssetIDType.Boolean);
- //warned being a bool is probably mostly correct.
- builtin_vars.Add("warned", AssetIDType.Boolean);
- builtin_vars.Add("tired", AssetIDType.Boolean);
- builtin_vars.Add("fixed", AssetIDType.Boolean);
- builtin_vars.Add("nexttry", AssetIDType.Boolean);
- builtin_vars.Add("floating", AssetIDType.Boolean);
- builtin_vars.Add("bodyfade", AssetIDType.Boolean);
- builtin_vars.Add("selected", AssetIDType.Boolean);
- builtin_vars.Add("hurk", AssetIDType.Boolean);
- builtin_vars.Add("persistent", AssetIDType.Boolean);
- builtin_vars.Add("dhaver", AssetIDType.Boolean);
- builtin_vars.Add("walk", AssetIDType.Boolean);
- builtin_vars.Add("fun", AssetIDType.Boolean);
- builtin_vars.Add("runmove", AssetIDType.Boolean);
- builtin_vars.Add("frozen", AssetIDType.Boolean);
- builtin_vars.Add("hadfrozen", AssetIDType.Boolean);
- builtin_vars.Add("offscreen_frozen", AssetIDType.Boolean);
- builtin_vars.Add("ignoresolid", AssetIDType.Boolean);
- builtin_vars.Add("eraser", AssetIDType.Boolean);
- builtin_vars.Add("bikeflip", AssetIDType.Boolean);
- builtin_vars.Add("checked", AssetIDType.Boolean);
- builtin_vars.Add("secondtime", AssetIDType.Boolean);
- builtin_vars.Add("ralsei_lecture", AssetIDType.Boolean);
- builtin_vars.Add("choiced", AssetIDType.Boolean);
- builtin_vars.Add("FINISH", AssetIDType.Boolean);
- builtin_vars.Add("LOCK", AssetIDType.Boolean);
- builtin_vars.Add("locked", AssetIDType.Boolean);
- builtin_vars.Add("ERASE", AssetIDType.Boolean);
- builtin_vars.Add("fastmode", AssetIDType.Boolean);
- builtin_vars.Add("fadeplease", AssetIDType.Boolean);
- builtin_vars.Add("active", AssetIDType.Boolean);
- builtin_vars.Add("alpha_changed", AssetIDType.Boolean);
- builtin_vars.Add("charinstance", AssetIDType.GameObject);
- builtin_vars.Add("reset", AssetIDType.Boolean);
- // globals pertaining to monsters in Deltarune
- builtin_vars.Add("monsterstatus", AssetIDType.Boolean);
- builtin_vars.Add("monster", AssetIDType.Boolean);
- // Cutscene
- builtin_vars.Add("cutscene", AssetIDType.Boolean);
- builtin_vars.Add("black", AssetIDType.Boolean);
- builtin_vars.Add("monsterinstancetype", AssetIDType.GameObject);
- //builtin_vars.Add("itemed", AssetIDType.Boolean);
- //builtin_vars.Add("itemed", AssetIDType.Boolean);
- //builtin_vars.Add("itemed", AssetIDType.Boolean);
- //builtin_vars.Add("itemed", AssetIDType.Boolean);
- //Undertale
- builtin_vars.Add("background_color", AssetIDType.Color);
- builtin_vars.Add("myblend", AssetIDType.Color);
- builtin_vars.Add("object0", AssetIDType.GameObject);
- builtin_vars.Add("part1", AssetIDType.GameObject);
- builtin_vars.Add("pap", AssetIDType.GameObject);
- builtin_vars.Add("fileerased", AssetIDType.Sprite);
- builtin_vars.Add("catty", AssetIDType.GameObject);
- builtin_vars.Add("bratty", AssetIDType.GameObject);
- builtin_vars.Add("creator", AssetIDType.GameObject);
- // It's not 100% accurate to resolve this way but it seems like this variable only gets directly assigned values and is used as a bool, it should be fine.
- builtin_vars.Add("parent", AssetIDType.GameObject);
- // These are not so consistent... ;-;
- // From v1.001 Undertale via comparison
- // A TIER quality
- builtin_vars.Add("onionsprite", AssetIDType.Sprite);
- builtin_vars.Add("headsprite", AssetIDType.Sprite);
- builtin_vars.Add("breaksprite", AssetIDType.Sprite);
- builtin_vars.Add("foodimg", AssetIDType.Sprite);
- builtin_vars.Add("facespr", AssetIDType.Sprite);
- builtin_vars.Add("bombsprite", AssetIDType.Sprite);
- builtin_vars.Add("mysprite", AssetIDType.Sprite);
- builtin_vars.Add("arms", AssetIDType.Sprite);
- builtin_vars.Add("levelpic", AssetIDType.Sprite);
- builtin_vars.Add("image", AssetIDType.Sprite);
- builtin_vars.Add("song_index", AssetIDType.Sound);
- builtin_vars.Add("thischara", AssetIDType.GameObject);
- // B TIER quality
- builtin_vars.Add("tspr5", AssetIDType.Sprite);
- builtin_vars.Add("tspr3", AssetIDType.Sprite);
- builtin_vars.Add("tspr2", AssetIDType.Sprite);
- builtin_vars.Add("tspr1", AssetIDType.Sprite);
- builtin_vars.Add("tspr4", AssetIDType.Sprite);
- builtin_vars.Add("snapper", AssetIDType.GameObject);
- builtin_vars.Add("subject", AssetIDType.GameObject);
- builtin_vars.Add("clip", AssetIDType.GameObject);
- // C TIER quality
- builtin_vars.Add("sound1", AssetIDType.Sound);
- builtin_vars.Add("sound2", AssetIDType.Sound);
- }
-
- // In 2.3.7+, booleans don't need to be typed.
- // Turn any boolean types (other than overrides) into AssetIDType.Other
- // so integers don't turn into booleans when they shouldn't
- if (data.IsVersionAtLeast(2, 3, 7))
- {
- foreach (KeyValuePair kvp in builtin_vars)
- {
- if (kvp.Value == AssetIDType.Boolean)
- builtin_vars.Remove(kvp.Key);
- }
- foreach (KeyValuePair kvp in return_types)
- {
- if (kvp.Value == AssetIDType.Boolean)
- builtin_vars.Remove(kvp.Key);
- }
- foreach (KeyValuePair kvp in builtin_funcs)
- {
- AssetIDType[] arr = kvp.Value;
- for (int i = 0; i < arr.Length; i++)
- {
- if (arr[i] == AssetIDType.Boolean)
- arr[i] = AssetIDType.Other;
- }
- }
- }
- }
- }
-}
diff --git a/UndertaleModLib/Decompiler/ContextualAssetResolver.cs b/UndertaleModLib/Decompiler/ContextualAssetResolver.cs
deleted file mode 100644
index 91954f59d..000000000
--- a/UndertaleModLib/Decompiler/ContextualAssetResolver.cs
+++ /dev/null
@@ -1,261 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using UndertaleModLib.Compiler;
-using UndertaleModLib.Models;
-
-namespace UndertaleModLib.Decompiler
-{
- public class ContextualAssetResolver
- {
- // Is there a better way to do this?
- // Probably
- public static Dictionary> resolvers;
- public static Dictionary> variable_resolvers;
-
- internal static Dictionary> event_subtypes;
- internal static Dictionary blend_modes, gamepad_controls;
- internal static Dictionary macros;
-
- public static void Initialize(UndertaleData data)
- {
- // TODO: make this nicer by not hacking
- // into the builtin list
- event_subtypes = new Dictionary>();
- gamepad_controls = new Dictionary();
- blend_modes = new Dictionary();
- macros = new Dictionary();
-
- // Don't use
- // Error because of loading audiogroup
- if (data.GeneralInfo != null)
- {
- if (data.GeneralInfo.BytecodeVersion <= 14)
- {
- foreach (var constant in data.Options.Constants)
- {
- if (!constant.Name.Content.StartsWith("@@", StringComparison.InvariantCulture))
- macros[constant.Value.Content] = constant.Name.Content;
- }
- }
- }
-
- var builtins = data.BuiltinList;
- var constants = builtins.Constants;
-
- Func GetEventTypeFromSubtype = (string subtype) =>
- {
- if (subtype.Contains("gesture", StringComparison.InvariantCulture))
- return Enum_EventType.ev_gesture;
- if (subtype.Contains("gui", StringComparison.InvariantCulture) || subtype.Contains("draw", StringComparison.InvariantCulture)) // DrawGUI events are apparently just prefixed with ev_gui...
- return Enum_EventType.ev_draw;
- if (subtype.Contains("step", StringComparison.InvariantCulture))
- return Enum_EventType.ev_step;
- // End me
- if (subtype.Contains("user", StringComparison.InvariantCulture) || subtype.Contains("game_", StringComparison.InvariantCulture) ||
- subtype.Contains("room_", StringComparison.InvariantCulture) || subtype.Contains("animation_end", StringComparison.InvariantCulture) ||
- subtype.Contains("lives", StringComparison.InvariantCulture) || subtype.Contains("end_of_path", StringComparison.InvariantCulture) ||
- subtype.Contains("health", StringComparison.InvariantCulture) || subtype.Contains("close_button", StringComparison.InvariantCulture) ||
- subtype.Contains("outside", StringComparison.InvariantCulture) || subtype.Contains("boundary", StringComparison.InvariantCulture))
- return Enum_EventType.ev_other;
-
- // ev_close_button is handled above and the various joystick events are
- // skipped in the loop
- if (subtype.Contains("button", StringComparison.InvariantCulture) || subtype.Contains("mouse", StringComparison.InvariantCulture) ||
- subtype.Contains("global", StringComparison.InvariantCulture) || subtype.Contains("press", StringComparison.InvariantCulture) ||
- subtype.Contains("release", StringComparison.InvariantCulture))
- return Enum_EventType.ev_mouse;
-
-
- // Note: events with arbitrary subtypes (keyboard, create, precreate, destroy, etc)
- // are not handled here.
- // It also appears to be impossible to manually trigger joystick events?
-
- // idk what exception to use
- throw new NotImplementedException("No event type for subtype " + subtype);
- };
-
- Func> GetDictForEventType = (Enum_EventType type) =>
- {
- // These 3 resolve to the same thing
- if (type == Enum_EventType.ev_keypress || type == Enum_EventType.ev_keyrelease)
- type = Enum_EventType.ev_keyboard;
-
- if (!event_subtypes.ContainsKey(type))
- event_subtypes[type] = new Dictionary();
-
- return event_subtypes[type];
- };
-
- // This is going to get bulky really quickly
- foreach (string constant in constants.Keys)
- {
- if (constant.StartsWith("vk_", StringComparison.InvariantCulture))
- GetDictForEventType(Enum_EventType.ev_keyboard)[(int)constants[constant]] = constant;
- else if (constant.StartsWith("bm_") && !constant.Contains("colour", StringComparison.InvariantCulture))
- blend_modes[(int)constants[constant]] = constant;
- else if (constant.StartsWith("gp_", StringComparison.InvariantCulture))
- gamepad_controls[(int)constants[constant]] = constant;
- else if (constant.StartsWith("ev_") && !Enum.IsDefined(typeof(Enum_EventType), constant) && !constant.Contains("joystick", StringComparison.InvariantCulture))
- GetDictForEventType(GetEventTypeFromSubtype(constant))[(int)constants[constant]] = constant;
- }
-
-
- // Uncurse this some time
- Func ConvertToConstExpression = (expr) =>
- {
- if (expr is Decompiler.ExpressionCast)
- expr = (expr as Decompiler.ExpressionCast).Argument;
-
- if (expr is Decompiler.ExpressionConstant)
- return expr as Decompiler.ExpressionConstant;
-
- return null;
- };
-
- Func GetTypeInt = (expr) =>
- {
- var constExpr = ConvertToConstExpression(expr);
-
- if (constExpr == null)
- return null;
-
- return AssetTypeResolver.FindConstValue(Decompiler.ExpressionConstant.ConvertToEnumStr(constExpr.Value));
- };
-
- Func resolve_event_perform = (context, func, index, self) =>
- {
- int? typeInt = GetTypeInt(func.Arguments[index - 1]);
-
- if(typeInt != null)
- {
- Enum_EventType type = (Enum_EventType)typeInt;
- int? initialVal = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
- if (initialVal == null)
- return null;
-
- int val = initialVal.Value;
-
- var subtypes = event_subtypes;
- if (type == Enum_EventType.ev_collision && val >= 0 && val < data.GameObjects.Count)
- return data.GameObjects[val].Name.Content;
- else if (type == Enum_EventType.ev_keyboard || type == Enum_EventType.ev_keypress || type == Enum_EventType.ev_keyrelease)
- {
- string key = self.GetAsKeyboard(context);
- if (key != null)
- return key;
- }
- else if (subtypes.ContainsKey(type))
- {
- var mappings = subtypes[type];
- if (mappings.ContainsKey(val))
- return mappings[val];
- }
- }
-
- return null;
- };
-
- // TODO: Finish context-dependent variable resolution
- variable_resolvers = new Dictionary>()
- {
- { "scr_getbuttonsprite", (context, varname, value) =>
- {
- return null;
- }
- }
- };
-
- resolvers = new Dictionary>()
- {
- // TODO: __background* compatibility scripts
- { "event_perform", resolve_event_perform },
- { "event_perform_object", resolve_event_perform },
- { "draw_set_blend_mode", (context, func, index, self) =>
- {
- int? val = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
- if (val != null)
- {
- switch(val)
- {
- case 0: return "bm_normal";
- case 1: return "bm_add";
- case 2: return "bm_max";
- case 3: return "bm_subtract";
- }
- }
- return null;
- }
- },
- { "gpu_set_blendmode", (context, func, index, self) =>
- {
- int? val = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
- if (val != null)
- {
- switch(val)
- {
- case 0: return "bm_normal";
- case 1: return "bm_add";
- case 2: return "bm_max";
- case 3: return "bm_subtract";
- }
- }
- return null;
- }
- },
- { "draw_set_blend_mode_ext", (context, func, index, self) =>
- {
- int? val = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
- if (val == null)
- return null;
-
- return blend_modes.ContainsKey(val.Value) ? blend_modes[val.Value] : null;
- }
- },
- { "gpu_set_blendmode_ext", (context, func, index, self) =>
- {
- int? val = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
- if (val == null)
- return null;
-
- return blend_modes.ContainsKey(val.Value) ? blend_modes[val.Value] : null;
- }
- },
- { "__view_set", (context, func, index, self) =>
- {
- var first = ConvertToConstExpression(func.Arguments[0]);
- if (first == null)
- return null;
-
- int type = Decompiler.ExpressionConstant.ConvertToInt(first.Value) ?? -1;
- int? val = Decompiler.ExpressionConstant.ConvertToInt(self.Value);
-
- if (val == null)
- return null;
-
- switch(type)
- {
- case 9:
- {
- if (val < 0)
- return ((UndertaleInstruction.InstanceType)self.Value).ToString().ToLower(CultureInfo.InvariantCulture);
- else if (val < data.GameObjects.Count)
- return data.GameObjects[val.Value].Name.Content;
-
- } break;
-
- case 10:
- {
- if (val == 0)
- return "false";
- else if (val == 1)
- return "true";
- } break;
- }
- return null;
- }
- },
- };
- }
- }
-}
diff --git a/UndertaleModLib/Decompiler/DecompileContext.cs b/UndertaleModLib/Decompiler/DecompileContext.cs
deleted file mode 100644
index e23d31678..000000000
--- a/UndertaleModLib/Decompiler/DecompileContext.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using UndertaleModLib.Models;
-
-namespace UndertaleModLib.Decompiler;
-
-///
-/// The DecompileContext is bound to the currently decompiled code block
-///
-public class DecompileContext
-{
- public GlobalDecompileContext GlobalContext;
- public UndertaleCode TargetCode;
- public UndertaleGameObject Object;
- public static bool GMS2_3;
- public bool BooleanTypeEnabled => GlobalContext.Data?.IsVersionAtLeast(2, 3, 7) ?? false;
- public bool AssetResolutionEnabled => !GlobalContext.Data.IsVersionAtLeast(2023, 8);
-
- public DecompileContext(GlobalDecompileContext globalContext, UndertaleCode code, bool computeObject = true)
- {
- GlobalContext = globalContext;
- TargetCode = code;
-
- if (code.ParentEntry != null)
- throw new InvalidOperationException("This code block represents a function nested inside " + code.ParentEntry.Name + " - decompile that instead");
-
- if (computeObject && globalContext.Data is not null)
- {
- // TODO: This is expensive, move it somewhere else as a dictionary
- // and have it update when events/objects are modified.
-
- // Currently using for loops on purpose, as foreach has memory issues due to IEnumerable
- for (int i = 0; i < globalContext.Data.GameObjects.Count; i++)
- {
- UndertaleGameObject obj = globalContext.Data.GameObjects[i];
- for (int j = 0; j < obj.Events.Count; j++)
- {
- var eventList = obj.Events[j];
- for (int k = 0; k < eventList.Count; k++)
- {
- UndertaleGameObject.Event subEvent = eventList[k];
- for (int l = 0; l < subEvent.Actions.Count; l++)
- {
- UndertaleGameObject.EventAction ev = subEvent.Actions[l];
- if (ev.CodeId != code) continue;
- Object = obj;
- return;
- }
- }
- }
- }
- }
- }
-
- #region Struct management
- public List ArgumentReplacements;
- public bool DecompilingStruct;
- #endregion
-
- #region Indentation management
- public const string Indent = " ";
- private int _indentationLevel = 0;
- private string _indentation = "";
-
- public int IndentationLevel
- {
- get
- {
- return _indentationLevel;
- }
- set
- {
- _indentationLevel = value;
-
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < IndentationLevel; i++)
- {
- sb.Append(Indent);
- }
- _indentation = sb.ToString();
- }
- }
- public string Indentation => _indentation;
- #endregion
-
- #region Temp var management
- ///
- /// Maps a temp var to a place where it was created
- ///
- public Dictionary TempVarMap = new Dictionary();
- ///
- /// If used for auto-naming temp vars
- ///
- public int TempVarId { get; private set; }
- public Decompiler.AssignmentStatement CompilerTempVar;
-
- public Decompiler.TempVar NewTempVar()
- {
- return new Decompiler.TempVar(++TempVarId);
- }
- #endregion
-
- #region Local var management
- public HashSet LocalVarDefines = new HashSet();
- #endregion
-
- #region GMS 2.3+ Function management
- ///
- /// Set containing already-decompiled child code entries.
- /// Used to prevent decompiling the same child entry multiple times.
- /// Only applies to function entries, struct and constructors are unaffected.
- ///
- public ISet AlreadyProcessed = new HashSet();
- #endregion
-
- #region Asset type resolution
- ///
- /// Contains the resolved asset type for every variable
- ///
- public Dictionary assetTypes = new Dictionary();
- public Decompiler.DirectFunctionCall currentFunction; // TODO: clean up this hack
- #endregion
-
- #region Decompilation results
- ///
- /// Contains the result of decompiling this code block.
- /// This is a map from an entry point address to a list of statements.
- /// Needs to be here to access it in ToString for inline function definitions.
- ///
- public Dictionary> Statements { get; internal set; }
- #endregion
-
- ///
- /// Allows to disable the anonymous code name resolution to prevent recursion
- ///
- public bool DisableAnonymousFunctionNameResolution = false;
-}
\ No newline at end of file
diff --git a/UndertaleModLib/Decompiler/Decompiler.cs b/UndertaleModLib/Decompiler/Decompiler.cs
deleted file mode 100644
index 0c211f296..000000000
--- a/UndertaleModLib/Decompiler/Decompiler.cs
+++ /dev/null
@@ -1,1664 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using UndertaleModLib.Models;
-using UndertaleModLib.Util;
-
-namespace UndertaleModLib.Decompiler
-{
- public static partial class Decompiler
- {
- // Color dictionary for resolving.
- public static readonly Dictionary ColorDictionary = new Dictionary
- {
- [16776960] = "c_aqua",
- [0] = "c_black",
- [16711680] = "c_blue",
- [4210752] = "c_dkgray",
- [16711935] = "c_fuchsia",
- [8421504] = "c_gray",
- [32768] = "c_green",
- [65280] = "c_lime",
- //[12632256] = "c_ltgray",
- [128] = "c_maroon",
- [8388608] = "c_navy",
- [32896] = "c_olive",
- [8388736] = "c_purple",
- [255] = "c_red",
- [12632256] = "c_silver",
- [8421376] = "c_teal",
- [16777215] = "c_white", // maximum color value
- [65535] = "c_yellow",
- [4235519] = "c_orange"
- };
-
- static int GetTypeSize(UndertaleInstruction.DataType type)
- {
- switch (type)
- {
- case UndertaleInstruction.DataType.Int16:
- case UndertaleInstruction.DataType.Int32:
- return 4;
- case UndertaleInstruction.DataType.Double: // Fallthrough
- case UndertaleInstruction.DataType.Int64:
- return 8;
- case UndertaleInstruction.DataType.Variable:
- return 16;
- default:
- throw new NotImplementedException("Unknown size for data type " + type);
- }
- }
- static int GetTypeSize(Expression e)
- {
- if (e is ExpressionVar || e is ExpressionTempVar)
- return GetTypeSize(UndertaleInstruction.DataType.Variable);
- if (e is FunctionCall) // function call returns an internal variable
- return GetTypeSize(UndertaleInstruction.DataType.Variable);
- if (e is FunctionDefinition)
- return GetTypeSize(UndertaleInstruction.DataType.Variable);
- if (e is ExpressionTwo exprTwo)
- return GetTypeSize(exprTwo.Type2); // for add.i.v, the output is a var
- return GetTypeSize(e.Type);
- }
-
- // The core function to decompile a specific block.
- internal static void DecompileFromBlock(DecompileContext context, Dictionary blocks, Block block, List tempvars, Stack>> workQueue)
- {
- if (block.TempVarsOnEntry != null && (block.nextBlockTrue != null || block.nextBlockFalse != null))
- {
- // Reroute tempvars to alias them to our ones
- if (block.TempVarsOnEntry.Count != tempvars.Count)
- {
- throw new Exception("Reentered block with different amount of vars on stack (Entry: " + block.TempVarsOnEntry.Count + ", Actual Count: " + tempvars.Count + ")");
- }
- else
- {
- for (int i = 0; i < tempvars.Count; i++)
- {
- tempvars[i].Var = block.TempVarsOnEntry[i].Var;
- }
- }
- }
-
- // Don't decompile more than once
- if (block.Statements != null)
- return;
-
- // Recover stack tempvars which may be needed
- block.TempVarsOnEntry = tempvars;
- Stack stack = new Stack();
- foreach (TempVarReference var in tempvars)
- stack.Push(new ExpressionTempVar(var, var.Var.Type));
-
- // Iterate through all of the sta
- List statements = new List();
- bool end = false;
- bool returned = false;
- for (int i = 0; i < block.Instructions.Count; i++)
- {
- if (end)
- throw new Exception("Expected end of block, but still has instructions");
-
- var instr = block.Instructions[i];
- switch (instr.Kind)
- {
- case UndertaleInstruction.Opcode.Neg:
- case UndertaleInstruction.Opcode.Not:
- stack.Push(new ExpressionOne(instr.Kind, instr.Type1, stack.Pop()));
- break;
-
- case UndertaleInstruction.Opcode.Dup:
- if (instr.ComparisonKind != 0)
- {
- // This is the GMS 2.3+ stack move / swap instruction
- if (instr.Type1 == UndertaleInstruction.DataType.Variable)
- {
- // This variant seems to do literally nothing...?
- break;
- }
-
- int bytesToTake = instr.Extra * 4;
- Stack taken = new Stack();
- while (bytesToTake > 0)
- {
- Expression e = stack.Pop();
- taken.Push(e);
- bytesToTake -= GetTypeSize(e);
- if (bytesToTake < 0)
- throw new InvalidOperationException("The stack got misaligned? Error 0");
- }
-
- int b2 = (byte)instr.ComparisonKind & 0x7F;
- if ((b2 & 0b111) != 0)
- throw new InvalidOperationException("Don't know what to do with this");
- int bytesToMove = (b2 >> 3) * 4;
- Stack moved = new Stack();
- while (bytesToMove > 0)
- {
- Expression e = stack.Pop();
- moved.Push(e);
- bytesToMove -= GetTypeSize(e);
- if (bytesToMove < 0)
- throw new InvalidOperationException("The stack got misaligned? Error 1");
- }
-
- while (taken.Count > 0)
- stack.Push(taken.Pop());
- while (moved.Count > 0)
- stack.Push(moved.Pop());
-
- break;
- }
-
- // Normal dup instruction
-
- List topExpressions1 = new List();
- List topExpressions2 = new List();
- int bytesToDuplicate = (instr.Extra + 1) * GetTypeSize(instr.Type1);
- while (bytesToDuplicate > 0)
- {
- var item = stack.Pop();
-
- if (item.IsDuplicationSafe())
- {
- item.WasDuplicated = true;
- topExpressions1.Add(item);
- topExpressions2.Add(item);
- }
- else
- {
- TempVar var = context.NewTempVar();
- var.Type = item.Type;
- TempVarReference varref = new TempVarReference(var);
- statements.Add(new TempVarAssignmentStatement(varref, item));
-
- topExpressions1.Add(new ExpressionTempVar(varref, varref.Var.Type) { WasDuplicated = true });
- topExpressions2.Add(new ExpressionTempVar(varref, instr.Type1) { WasDuplicated = true });
- }
-
- bytesToDuplicate -= GetTypeSize(item);
- if (bytesToDuplicate < 0)
- throw new InvalidOperationException("The stack got misaligned? Error 2: Attempted to duplicate "
- + GetTypeSize(item)
- + " bytes, only found "
- + (bytesToDuplicate + GetTypeSize(item)));
- }
- topExpressions1.Reverse();
- topExpressions2.Reverse();
- for (int j = 0; j < topExpressions1.Count; j++)
- stack.Push(topExpressions1[j]);
- for (int j = 0; j < topExpressions2.Count; j++)
- stack.Push(topExpressions2[j]);
- break;
-
- case UndertaleInstruction.Opcode.Ret:
- case UndertaleInstruction.Opcode.Exit:
- // 2.3 scripts add exits to every script, even those that lack a return
- // This detects that type of exit using the next block.
- Block nextBlock = null;
- if (DecompileContext.GMS2_3 && instr.Kind == UndertaleInstruction.Opcode.Exit)
- {
- uint[] blockAddresses = blocks.Keys.ToArray();
- Array.Sort(blockAddresses);
- int nextBlockIndex = Array.IndexOf(blockAddresses, block.Address ?? 0) + 1;
- if (blockAddresses.Length > nextBlockIndex)
- {
- uint nextBlockAddress = blockAddresses[nextBlockIndex];
- nextBlock = blocks[nextBlockAddress];
- }
- }
-
- if (!(nextBlock is not null
- && nextBlock.Instructions.Count > 0
- && nextBlock.Instructions[0].Kind == UndertaleInstruction.Opcode.Push
- && nextBlock.Instructions[0].Value is UndertaleInstruction.Reference))
- {
- ReturnStatement stmt = new ReturnStatement(instr.Kind == UndertaleInstruction.Opcode.Ret ? stack.Pop() : null);
- /*
- This shouldn't be necessary: all unused things on the stack get converted to tempvars at the end anyway, and this fixes decompilation of repeat()
- See #85
-
- foreach (var expr in stack.Reverse())
- if (!(expr is ExpressionTempVar))
- statements.Add(expr);
- stack.Clear();
- */
- statements.Add(stmt);
- }
- end = true;
- returned = true;
- break;
-
- case UndertaleInstruction.Opcode.Popz:
- Expression popped = stack.Pop();
- if (!popped.IsDuplicationSafe()) // <- not duplication safe = has side effects and needs to be listed in the output
- statements.Add(popped);
- break;
-
- case UndertaleInstruction.Opcode.Conv:
- {
- Expression val = stack.Pop();
- if (context.BooleanTypeEnabled && instr.Type1 == UndertaleInstruction.DataType.Boolean)
- val.CastToBoolean(context);
- stack.Push(new ExpressionCast(instr.Type2, val));
- }
- break;
-
- case UndertaleInstruction.Opcode.Mul:
- case UndertaleInstruction.Opcode.Div:
- case UndertaleInstruction.Opcode.Rem:
- case UndertaleInstruction.Opcode.Mod:
- case UndertaleInstruction.Opcode.Add:
- case UndertaleInstruction.Opcode.Sub:
- case UndertaleInstruction.Opcode.And:
- case UndertaleInstruction.Opcode.Or:
- case UndertaleInstruction.Opcode.Xor:
- case UndertaleInstruction.Opcode.Shl:
- case UndertaleInstruction.Opcode.Shr:
- Expression a2 = stack.Pop();
- Expression a1 = stack.Pop();
- stack.Push(new ExpressionTwo(instr.Kind, instr.Type1, instr.Type2, a1, a2));
- break;
-
- case UndertaleInstruction.Opcode.Cmp:
- Expression aa2 = stack.Pop();
- if (context.BooleanTypeEnabled && instr.Type1 == UndertaleInstruction.DataType.Boolean)
- aa2.CastToBoolean(context);
- Expression aa1 = stack.Pop();
- if (context.BooleanTypeEnabled && instr.Type2 == UndertaleInstruction.DataType.Boolean)
- aa1.CastToBoolean(context);
- stack.Push(new ExpressionCompare(instr.ComparisonKind, aa1, aa2));
- break;
-
- case UndertaleInstruction.Opcode.B:
- end = true;
- break;
-
- case UndertaleInstruction.Opcode.Bt:
- case UndertaleInstruction.Opcode.Bf:
- {
- Expression val = stack.Pop();
- if (context.BooleanTypeEnabled && val.Type == UndertaleInstruction.DataType.Int16)
- val.CastToBoolean(context);
- block.ConditionStatement = val;
- end = true;
- }
- break;
-
- case UndertaleInstruction.Opcode.PushEnv:
- if (DecompileContext.GMS2_3 == true)
- {
- Expression expr = stack.Pop();
-
- // -9 signifies stacktop
- if (expr is ExpressionConstant c &&
- c.Type == UndertaleInstruction.DataType.Int16 && (short)c.Value == -9)
- expr = stack.Pop();
-
- statements.Add(new PushEnvStatement(expr));
- }
- else
- statements.Add(new PushEnvStatement(stack.Pop()));
- end = true;
- break;
-
- case UndertaleInstruction.Opcode.PopEnv:
- if (!instr.JumpOffsetPopenvExitMagic)
- statements.Add(new PopEnvStatement());
- // For JumpOffsetPopenvExitMagic:
- // This is just an instruction to make sure the pushenv/popenv stack is cleared on early function return
- // Works kinda like 'break', but doesn't have a high-level representation as it's immediately followed by a 'return'
- end = true;
- break;
-
- case UndertaleInstruction.Opcode.Pop:
- {
- if (instr.Destination == null)
- {
- // pop.e.v 5/6, strange magic stack operation
- // TODO: this is probably an older version of the GMS2.3+ swap hidden in dup, but I'm not gonna touch it if it works
- Expression e1 = stack.Pop();
- Expression e2 = stack.Pop();
- for (int j = 0; j < instr.SwapExtra - 4; j++)
- stack.Pop();
- stack.Push(e2);
- stack.Push(e1);
- break;
- }
- ExpressionVar target = new ExpressionVar(instr.Destination.Target, new ExpressionConstant(UndertaleInstruction.DataType.Int16, instr.TypeInst), instr.Destination.Type);
- Expression val = null;
- if (instr.Type1 != UndertaleInstruction.DataType.Int32 && instr.Type1 != UndertaleInstruction.DataType.Variable)
- throw new Exception("Unrecognized pop instruction, doesn't conform to pop.i.X, pop.v.X, or pop.e.v");
- if (instr.Type1 == UndertaleInstruction.DataType.Int32)
- val = stack.Pop();
- switch (target.VarType)
- {
- case UndertaleInstruction.VariableType.Normal:
- case UndertaleInstruction.VariableType.Instance:
- break;
- case UndertaleInstruction.VariableType.StackTop:
- target.InstType = stack.Pop();
- break;
- case UndertaleInstruction.VariableType.Array:
- Tuple ind = ExpressionVar.Decompile2DArrayIndex(stack.Pop());
- target.ArrayIndices = new List { ind.Item1 };
- if (ind.Item2 != null)
- target.ArrayIndices.Add(ind.Item2);
- target.InstType = stack.Pop();
- break;
- default:
- throw new NotImplementedException("Don't know how to decompile variable type " + target.VarType);
- }
-
- // Check if instance type is "StackTop"
- ExpressionConstant instanceTypeConstExpr = null;
- if (target.InstType is ExpressionConstant c1) {
- instanceTypeConstExpr = c1;
- } else if (target.InstType is ExpressionTempVar tempVar) {
- TempVarAssignmentStatement assignment = context.TempVarMap[tempVar.Var.Var.Name];
- if (assignment != null && assignment.Value is ExpressionConstant c2) {
- instanceTypeConstExpr = c2;
- }
- }
- if (instanceTypeConstExpr != null &&
- instanceTypeConstExpr.Type == UndertaleInstruction.DataType.Int16 &&
- (short)instanceTypeConstExpr.Value == (short)UndertaleInstruction.InstanceType.Stacktop) {
- target.InstType = stack.Pop();
- }
-
- if (instr.Type1 == UndertaleInstruction.DataType.Variable)
- val = stack.Pop();
- if (val != null)
- {
- if (context.BooleanTypeEnabled && instr.Type2 == UndertaleInstruction.DataType.Boolean)
- val.CastToBoolean(context);
- if ((target.VarType == UndertaleInstruction.VariableType.StackTop || target.VarType == UndertaleInstruction.VariableType.Array) && target.InstType.WasDuplicated)
- {
- // Almost safe to assume that this is a +=, -=, etc.
- // Need to confirm a few things first. It's not certain, could be ++ even.
- if (val is ExpressionTwo)
- {
- var two = (val as ExpressionTwo);
- if (two.Opcode != UndertaleInstruction.Opcode.Rem && // Not possible in GML, but possible in bytecode. Don't deal with these,
- two.Opcode != UndertaleInstruction.Opcode.Shl && // frankly we don't care enough.
- two.Opcode != UndertaleInstruction.Opcode.Shr)
- {
- var arg = two.Argument1;
- if (arg is ExpressionVar)
- {
- var v = arg as ExpressionVar;
- if (v.Var == target.Var && v.InstType == target.InstType &&
- ((v.ArrayIndices == null && target.ArrayIndices == null) ||
- v.ArrayIndices?.SequenceEqual(target.ArrayIndices) == true) && // even if null
- (!(two.Argument2 is ExpressionConstant) || // Also check to make sure it's not a ++ or --
- (!((two.Argument2 as ExpressionConstant).IsPushE && ExpressionConstant.ConvertToInt((two.Argument2 as ExpressionConstant).Value) == 1))))
- {
- if (!(context.GlobalContext.Data?.GeneralInfo?.BytecodeVersion > 14 && v.Opcode != UndertaleInstruction.Opcode.Push && instr.Destination.Target.InstanceType != UndertaleInstruction.InstanceType.Self))
- {
- statements.Add(new OperationEqualsStatement(target, two.Opcode, two.Argument2));
- break;
- }
- }
- }
- }
- }
- }
- }
- else
- Debug.Fail("Pop value is null.");
- statements.Add(new AssignmentStatement(target, val));
- }
- break;
-
- case UndertaleInstruction.Opcode.Push:
- case UndertaleInstruction.Opcode.PushLoc:
- case UndertaleInstruction.Opcode.PushGlb:
- case UndertaleInstruction.Opcode.PushBltn:
- case UndertaleInstruction.Opcode.PushI:
- if (instr.Value is UndertaleInstruction.Reference)
- {
- ExpressionVar pushTarget = new ExpressionVar((instr.Value as UndertaleInstruction.Reference).Target, new ExpressionConstant(UndertaleInstruction.DataType.Int16, instr.TypeInst), (instr.Value as UndertaleInstruction.Reference).Type);
- pushTarget.Opcode = instr.Kind;
- switch(pushTarget.VarType)
- {
- case UndertaleInstruction.VariableType.Normal:
- case UndertaleInstruction.VariableType.Instance:
- break;
- case UndertaleInstruction.VariableType.StackTop:
- pushTarget.InstType = stack.Pop();
- break;
- case UndertaleInstruction.VariableType.Array:
- Tuple ind = ExpressionVar.Decompile2DArrayIndex(stack.Pop());
- pushTarget.ArrayIndices = new List() { ind.Item1 };
- if (ind.Item2 != null)
- pushTarget.ArrayIndices.Add(ind.Item2);
- pushTarget.InstType = stack.Pop();
- break;
- case UndertaleInstruction.VariableType.ArrayPopAF:
- case UndertaleInstruction.VariableType.ArrayPushAF:
- pushTarget.ArrayIndices = new List() { stack.Pop() };
- pushTarget.InstType = stack.Pop();
- break;
- default:
- throw new NotImplementedException("Don't know how to decompile variable type " + pushTarget.VarType);
- }
- if (pushTarget.InstType is ExpressionConstant c &&
- c.Type == UndertaleInstruction.DataType.Int16 && (short)c.Value == -9)
- pushTarget.InstType = stack.Pop();
- stack.Push(pushTarget);
- }
- else
- {
- bool isPushE = (instr.Kind == UndertaleInstruction.Opcode.Push && instr.Type1 == UndertaleInstruction.DataType.Int16);
- Expression pushTarget = new ExpressionConstant(instr.Type1, instr.Value, isPushE);
- if (isPushE && pushTarget.Type == UndertaleInstruction.DataType.Int16 && Convert.ToInt32((pushTarget as ExpressionConstant).Value) == 1)
- {
- // Check for expression ++ or --
- if (((i >= 1 && block.Instructions[i - 1].Kind == UndertaleInstruction.Opcode.Dup && block.Instructions[i - 1].Type1 == UndertaleInstruction.DataType.Variable) ||
- (i >= 2 && block.Instructions[i - 2].Kind == UndertaleInstruction.Opcode.Dup && block.Instructions[i - 2].Type1 == UndertaleInstruction.DataType.Variable &&
- block.Instructions[i - 1].Kind == UndertaleInstruction.Opcode.Pop && block.Instructions[i - 1].Type1 == UndertaleInstruction.DataType.Int16 && block.Instructions[i - 1].Type2 == UndertaleInstruction.DataType.Variable)) &&
- (i + 1 < block.Instructions.Count && (block.Instructions[i + 1].Kind == UndertaleInstruction.Opcode.Add || block.Instructions[i + 1].Kind == UndertaleInstruction.Opcode.Sub)))
- {
- // We've detected a post increment/decrement (i.e., x = y++)
- // Remove duplicate from stack
- stack.Pop();
-
- // Do the magic
- stack.Push(new ExpressionPost(block.Instructions[i + 1].Kind, stack.Pop()));
-
- while (i < block.Instructions.Count && (block.Instructions[i].Kind != UndertaleInstruction.Opcode.Pop || (block.Instructions[i].Type1 == UndertaleInstruction.DataType.Int16 && block.Instructions[i].Type2 == UndertaleInstruction.DataType.Variable)))
- i++;
- }
- else if (i + 2 < block.Instructions.Count && (block.Instructions[i + 1].Kind == UndertaleInstruction.Opcode.Add || block.Instructions[i + 1].Kind == UndertaleInstruction.Opcode.Sub) &&
- block.Instructions[i + 2].Kind == UndertaleInstruction.Opcode.Dup && block.Instructions[i + 2].Type1 == UndertaleInstruction.DataType.Variable)
- {
- // We've detected a pre increment/decrement (i.e., x = ++y)
- // Do the magic
- stack.Push(new ExpressionPre(block.Instructions[i + 1].Kind, stack.Pop()));
-
- while (i < block.Instructions.Count && block.Instructions[i].Kind != UndertaleInstruction.Opcode.Pop)
- i++;
- var _inst = block.Instructions[i];
- if (_inst.Type1 == UndertaleInstruction.DataType.Int16 && _inst.Type2 == UndertaleInstruction.DataType.Variable)
- {
- Expression e = stack.Pop();
- stack.Pop();
- stack.Push(e);
- i++;
- }
- }
- else
- {
- stack.Push(pushTarget);
- }
- }
- else
- {
- stack.Push(pushTarget);
- }
- }
- break;
-
- case UndertaleInstruction.Opcode.Call:
- {
- List args = new List();
- for (int j = 0; j < instr.ArgumentsCount; j++)
- args.Add(stack.Pop());
-
- if (instr.Function.Target.Name.Content == "method" && args.Count == 2)
- {
- // Special case - method creation
- // See if the body should be inlined
-
- Expression arg1 = args[0];
- while (arg1 is ExpressionCast cast)
- arg1 = cast.Argument;
- Expression arg2 = args[1];
- while (arg2 is ExpressionCast cast)
- arg2 = cast.Argument;
-
- if (arg2 is ExpressionConstant argCode && argCode.Type == UndertaleInstruction.DataType.Int32 &&
- argCode.Value is UndertaleInstruction.Reference argCodeFunc)
- {
- UndertaleCode functionBody = context.GlobalContext.Data.Code.First(x => x.Name.Content == argCodeFunc.Target.Name.Content);
-
- FunctionDefinition.FunctionType type = FunctionDefinition.FunctionType.Function;
- bool processChildEntry;
-
- if (arg1 is DirectFunctionCall call && call.Function.Name.Content == "@@NullObject@@")
- {
- type = FunctionDefinition.FunctionType.Constructor;
- processChildEntry = true;
- }
- else
- processChildEntry = context.AlreadyProcessed.Add(functionBody);
-
- if (context.TargetCode.ChildEntries.Contains(functionBody) && processChildEntry)
- {
- // This function is somewhere inside this UndertaleCode block
- // inline the definition
- Block functionBodyEntryBlock = blocks[functionBody.Offset / 4];
- stack.Push(new FunctionDefinition(argCodeFunc.Target, functionBody, functionBodyEntryBlock, type));
- workQueue.Push(new Tuple>(functionBodyEntryBlock, new List()));
- break;
- }
- }
- }
-
- UndertaleCode callTargetBody = context.GlobalContext.Data?.Code.FirstOrDefault(x => x.Name.Content == instr.Function.Target.Name.Content);
- if (callTargetBody != null && callTargetBody.ParentEntry != null && !context.DisableAnonymousFunctionNameResolution)
- {
- // Special case: this is a direct reference to a method variable
- // Figure out what its actual name is
-
- static string FindActualNameForAnonymousCodeObject(DecompileContext context, UndertaleCode anonymousCodeObject)
- {
- // Decompile the parent object, and find the anonymous function assignment
- DecompileContext childContext = new DecompileContext(context.GlobalContext, anonymousCodeObject.ParentEntry);
- childContext.DisableAnonymousFunctionNameResolution = true; // prevent recursion - we don't even need the names in the child block
- try
- {
- Dictionary blocks2 = PrepareDecompileFlow(anonymousCodeObject.ParentEntry, new List() { 0 });
- DecompileFromBlock(childContext, blocks2, blocks2[0]);
- // This hack handles decompilation of code entries getting shorter, but not longer or out of place.
- // Probably is no longer needed since we now update Length mostly-correctly
- Block lastBlock;
- if (!blocks2.TryGetValue(anonymousCodeObject.Length / 4, out lastBlock))
- lastBlock = blocks2[blocks2.Keys.Max()];
- List statements = HLDecompile(childContext, blocks2, blocks2[0], lastBlock);
- foreach (Statement stmt2 in statements)
- {
- if (stmt2 is AssignmentStatement assign &&
- assign.Value is FunctionDefinition funcDef &&
- funcDef.FunctionBodyCodeEntry == anonymousCodeObject)
- {
- if (funcDef.FunctionBodyEntryBlock.Address == anonymousCodeObject.Offset / 4)
- return assign.Destination.Var.Name.Content;
- else
- return string.Empty; //throw new Exception("Non-matching offset: " + funcDef.FunctionBodyEntryBlock.Address.ToString() + " versus " + (anonymousCodeObject.Offset / 4).ToString() + " (got name " + assign.Destination.Var.Name.Content + ")");
- }
- }
- throw new Exception("Unable to find the var name for anonymous code object " + anonymousCodeObject.Name.Content);
- }
- catch (Exception e)
- {
- context.GlobalContext.DecompilerWarnings.Add("/*\nWARNING: Recursive script decompilation (for member variable name resolution) failed for " + anonymousCodeObject.Name.Content + "\n\n" + e.ToString() + "\n*/");
- return string.Empty;
- }
- }
-
- string funcName;
- if (!context.GlobalContext.AnonymousFunctionNameCache.TryGetValue(instr.Function.Target, out funcName))
- {
- funcName = FindActualNameForAnonymousCodeObject(context, callTargetBody);
- context.GlobalContext.AnonymousFunctionNameCache.Add(instr.Function.Target, funcName);
- }
- if (funcName != string.Empty)
- {
- stack.Push(new DirectFunctionCall(funcName, instr.Function.Target, instr.Type1, args));
- break;
- }
- }
-
- stack.Push(new DirectFunctionCall(instr.Function.Target, instr.Type1, args));
- }
- break;
-
- case UndertaleInstruction.Opcode.CallV:
- {
- Expression func = stack.Pop();
- Expression func_this = stack.Pop();
- List args = new List();
- for (int j = 0; j < instr.Extra; j++)
- args.Add(stack.Pop());
- stack.Push(new IndirectFunctionCall(func_this, func, instr.Type1, args));
- }
- break;
-
- case UndertaleInstruction.Opcode.Break:
- // GMS 2.3 sub-opcodes
- if (DecompileContext.GMS2_3 == true)
- {
- switch ((short)instr.Value)
- {
- case -2: // GMS2.3+, pushaf
- {
- // TODO, work out more specifics here, like ++
- Expression ind = stack.Pop();
- Expression target = stack.Pop();
- if (target is ExpressionVar targetVar)
- {
- if (targetVar.VarType != UndertaleInstruction.VariableType.ArrayPushAF && targetVar.VarType != UndertaleInstruction.VariableType.ArrayPopAF) // The popaf arrays support pushaf as well, judging by how they are used with dup
- throw new InvalidOperationException("Tried to pushaf on var of type " + targetVar.VarType);
-
- ExpressionVar newVar = new ExpressionVar(targetVar.Var, targetVar.InstType, targetVar.VarType);
- newVar.Opcode = instr.Kind;
- newVar.ArrayIndices = new List(targetVar.ArrayIndices);
- newVar.ArrayIndices.Add(ind);
- stack.Push(newVar);
- }
- else
- throw new InvalidOperationException("Tried to pushaf on something that is not a var");
- }
- break;
- case -3: // GMS2.3+, popaf
- {
- // TODO, work out more specifics here, like ++
- Expression ind = stack.Pop();
- Expression target = stack.Pop();
- if (target is ExpressionVar targetVar)
- {
- if (targetVar.VarType != UndertaleInstruction.VariableType.ArrayPopAF)
- throw new InvalidOperationException("Tried to popaf on var of type " + targetVar.VarType);
-
- ExpressionVar newVar = new ExpressionVar(targetVar.Var, targetVar.InstType, targetVar.VarType);
- newVar.Opcode = instr.Kind;
- newVar.ArrayIndices = new List(targetVar.ArrayIndices);
- newVar.ArrayIndices.Add(ind);
-
- Expression value = stack.Pop();
- statements.Add(new AssignmentStatement(newVar, value));
- }
- else
- throw new InvalidOperationException("Tried to popaf on something that is not a var");
- }
- break;
- case -4: // GMS2.3+, pushac
- {
- Expression ind = stack.Pop();
- Expression target = stack.Pop();
- if (target is ExpressionVar targetVar)
- {
- if (targetVar.VarType != UndertaleInstruction.VariableType.ArrayPushAF && targetVar.VarType != UndertaleInstruction.VariableType.ArrayPopAF)
- throw new InvalidOperationException("Tried to pushac on var of type " + targetVar.VarType);
-
- ExpressionVar newVar = new ExpressionVar(targetVar.Var, targetVar.InstType, targetVar.VarType);
- newVar.Opcode = instr.Kind;
- newVar.ArrayIndices = new List(targetVar.ArrayIndices);
- newVar.ArrayIndices.Add(ind);
- stack.Push(newVar);
- }
- else
- throw new InvalidOperationException("Tried to pushac on something that is not a var");
- }
- break;
- case -5: // GMS2.3+, setowner
- // Stop 'setowner' values from leaking into the decompiled output as tempvars.
- // Used in the VM to let copy-on-write functionality work, but unnecessary for decompilation
- stack.Pop();
- /*
- var statement = stack.Pop();
- object owner;
- if (statement is ExpressionConstant)
- owner = (statement as ExpressionConstant).Value?.ToString();
- else
- owner = statement.ToString(context);
- statements.Add(new CommentStatement("setowner: " + (owner ?? "")));
- */
- break;
- case -10: // GMS2.3+, chknullish
-
- // TODO: Implement nullish operator in decompiled output.
- // Appearance in assembly is:
-
- /*
- * chknullish
- * bf [block2]
- *
- * :[block1]
- * popz.v
- *
- *
- * :[block2]
- *