private static UndertaleInstruction.Reference <UndertaleVariable> ParseVariableReference(string line, IList <UndertaleVariable> vars, Dictionary <string, UndertaleVariable> localvars, ref UndertaleInstruction.InstanceType instance, UndertaleInstruction instr, Func <int, UndertaleInstruction.InstanceType?> lookOnStack = null, UndertaleData data = null) { string str = line; string inst = null; int instdot = str.IndexOf('.'); if (instdot >= 0) { inst = str.Substring(0, instdot); str = str.Substring(instdot + 1); if (inst == "") { throw new Exception("Whoops?"); } } if (inst != null) { short instnum; if (Int16.TryParse(inst, out instnum)) { instance = (UndertaleInstruction.InstanceType)instnum; } else { instance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), inst, true); } } else { instance = UndertaleInstruction.InstanceType.Undefined; } UndertaleInstruction.VariableType type = UndertaleInstruction.VariableType.Normal; if (str[0] == '[') { int typeend = str.IndexOf(']'); if (typeend >= 0) { string typestr = str.Substring(1, typeend - 1); str = str.Substring(typeend + 1); type = (UndertaleInstruction.VariableType)Enum.Parse(typeof(UndertaleInstruction.VariableType), typestr, true); } } UndertaleInstruction.InstanceType realinstance = instance; // for arrays, the type is on the stack which totally breaks things // This is an ugly hack to handle that // see https://github.com/krzys-h/UndertaleModTool/issues/27#issuecomment-426637438 if (type == UndertaleInstruction.VariableType.Array && lookOnStack != null) { var instTypeOnStack = lookOnStack(instr.Kind == UndertaleInstruction.Opcode.Pop && instr.Type1 == UndertaleInstruction.DataType.Int32 ? 3 : 2); if (instTypeOnStack.HasValue) { realinstance = instTypeOnStack.Value; } } if (realinstance >= 0) { realinstance = UndertaleInstruction.InstanceType.Self; } else if (realinstance == UndertaleInstruction.InstanceType.Other) { realinstance = UndertaleInstruction.InstanceType.Self; } if (data?.GeneralInfo?.BytecodeVersion <= 14) { realinstance = UndertaleInstruction.InstanceType.Undefined; } UndertaleVariable varobj; if (realinstance == UndertaleInstruction.InstanceType.Local) { varobj = localvars.ContainsKey(str) ? localvars[str] : null; } else { varobj = vars.Where((x) => x.Name.Content == str && x.InstanceType == realinstance).FirstOrDefault(); } if (varobj == null) { throw new Exception("Bad variable: " + realinstance.ToString().ToLower() + "." + str); } return(new UndertaleInstruction.Reference <UndertaleVariable>(varobj, type)); }
public static UndertaleInstruction AssembleOne(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg, Dictionary <string, UndertaleVariable> localvars, out string label, UndertaleData data = null, Func <int, UndertaleInstruction.InstanceType?> lookOnStack = null) { label = null; string line = source; UndertaleInstruction instr = new UndertaleInstruction(); string opcode = line; int space = opcode.IndexOf(' '); if (space >= 0) { opcode = line.Substring(0, space); line = line.Substring(space + 1).Trim(); } else { line = ""; } string[] types = opcode.Split('.'); if (types.Length > 3) { throw new Exception("Too many type parameters"); } instr.Kind = (UndertaleInstruction.Opcode)Enum.Parse(typeof(UndertaleInstruction.Opcode), types[0], true); if (types.Length >= 2) { instr.Type1 = UndertaleInstructionUtil.FromOpcodeParam(types[1]); } if (types.Length >= 3) { instr.Type2 = UndertaleInstructionUtil.FromOpcodeParam(types[2]); } switch (UndertaleInstruction.GetInstructionType(instr.Kind)) { case UndertaleInstruction.InstructionType.SingleTypeInstruction: if (instr.Kind == UndertaleInstruction.Opcode.Dup) { instr.DupExtra = Byte.Parse(line); line = ""; } break; case UndertaleInstruction.InstructionType.DoubleTypeInstruction: break; case UndertaleInstruction.InstructionType.ComparisonInstruction: instr.ComparisonKind = (UndertaleInstruction.ComparisonType)Enum.Parse(typeof(UndertaleInstruction.ComparisonType), line, true); line = ""; break; case UndertaleInstruction.InstructionType.GotoInstruction: if (line[0] == '$') { instr.JumpOffset = Int32.Parse(line.Substring(1)); } else { if (line == "[drop]") { instr.JumpOffsetPopenvExitMagic = true; if (data?.GeneralInfo?.BytecodeVersion <= 14) { instr.JumpOffset = -1048576; // I really don't know at this point. Magic for little endian 00 00 F0 } } else { label = line; } } line = ""; break; case UndertaleInstruction.InstructionType.PopInstruction: if (instr.Type1 == UndertaleInstruction.DataType.Int16) { // Special scenario - the swap instruction // TODO: Figure out the proper syntax, see #129 instr.SwapExtra = Byte.Parse(line); } else { UndertaleInstruction.InstanceType inst = instr.TypeInst; instr.Destination = ParseVariableReference(line, vars, localvars, ref inst, instr, lookOnStack, data); instr.TypeInst = inst; } line = ""; break; case UndertaleInstruction.InstructionType.PushInstruction: switch (instr.Type1) { case UndertaleInstruction.DataType.Double: instr.Value = Double.Parse(line, CultureInfo.InvariantCulture); break; case UndertaleInstruction.DataType.Float: instr.Value = Single.Parse(line, CultureInfo.InvariantCulture); break; case UndertaleInstruction.DataType.Int32: int ival; if (Int32.TryParse(line, out ival)) { instr.Value = ival; } else { instr.Value = (int)ParseResourceName(line, data); } break; case UndertaleInstruction.DataType.Int64: long lval; if (Int64.TryParse(line, out lval)) { instr.Value = lval; } else { instr.Value = (long)ParseResourceName(line, data); } break; case UndertaleInstruction.DataType.Boolean: instr.Value = bool.Parse(line); break; case UndertaleInstruction.DataType.Variable: UndertaleInstruction.InstanceType inst2 = instr.TypeInst; instr.Value = ParseVariableReference(line, vars, localvars, ref inst2, instr, lookOnStack, data); instr.TypeInst = inst2; break; case UndertaleInstruction.DataType.String: instr.Value = ParseStringReference(line, strg); break; case UndertaleInstruction.DataType.Int16: short sval; if (Int16.TryParse(line, out sval)) { instr.Value = sval; } else { instr.Value = (short)ParseResourceName(line, data); } break; } line = ""; break; case UndertaleInstruction.InstructionType.CallInstruction: Match match = Regex.Match(line, @"^(.*)\(argc=(.*)\)$"); if (!match.Success) { throw new Exception("Call instruction format error"); } UndertaleFunction func = funcs.ByName(match.Groups[1].Value); if (func == null) { throw new Exception("Function not found: " + match.Groups[1].Value); } instr.Function = new UndertaleInstruction.Reference <UndertaleFunction>() { Target = func }; instr.ArgumentsCount = UInt16.Parse(match.Groups[2].Value); line = ""; break; case UndertaleInstruction.InstructionType.BreakInstruction: instr.Value = Int16.Parse(line); line = ""; break; } if (line != "") { throw new Exception("Excess parameters"); } return(instr); }
private static UndertaleInstruction.Reference <UndertaleVariable> ParseVariableReference(string line, IList <UndertaleVariable> vars, Dictionary <string, UndertaleVariable> localvars, ref UndertaleInstruction.InstanceType instance, UndertaleInstruction instr, UndertaleData data = null) { string str = line; UndertaleInstruction.VariableType type = UndertaleInstruction.VariableType.Normal; UndertaleInstruction.InstanceType realinstance = instance; if (str[0] != '[') { string inst = null; int instdot = str.IndexOf('.'); if (instdot >= 0) { inst = str.Substring(0, instdot); str = str.Substring(instdot + 1); if (inst == "") { throw new Exception("Whoops?"); } } if (inst != null) { short instnum; if (Int16.TryParse(inst, out instnum)) { instance = (UndertaleInstruction.InstanceType)instnum; } else { instance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), inst, true); } } else { 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@@ } } else { int typeend = str.IndexOf(']'); if (typeend >= 0) { string typestr = str.Substring(1, typeend - 1); str = str.Substring(typeend + 1); type = (UndertaleInstruction.VariableType)Enum.Parse(typeof(UndertaleInstruction.VariableType), typestr, true); int instanceEnd = str.IndexOf('.'); if (instanceEnd >= 0) { string instancestr = str.Substring(0, instanceEnd); str = str.Substring(instanceEnd + 1); realinstance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), instancestr, true); } else { 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; } } } else { throw new Exception("Missing ']' character in variable reference"); } } if (data?.GeneralInfo?.BytecodeVersion <= 14) { realinstance = UndertaleInstruction.InstanceType.Undefined; } UndertaleVariable varobj; if (realinstance == UndertaleInstruction.InstanceType.Local) { varobj = localvars.ContainsKey(str) ? localvars[str] : null; } else { varobj = vars.Where((x) => x.Name.Content == str && x.InstanceType == realinstance).FirstOrDefault(); } if (varobj == null) { throw new Exception("Bad variable: " + realinstance.ToString().ToLower() + "." + str); } return(new UndertaleInstruction.Reference <UndertaleVariable>(varobj, type)); }
public static UndertaleInstruction AssembleOne(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg, Dictionary <string, UndertaleVariable> localvars, out string label, UndertaleInstruction.InstanceType?instTypeOnStack) { label = null; string line = source; UndertaleInstruction instr = new UndertaleInstruction(); string opcode = line; int space = opcode.IndexOf(' '); if (space >= 0) { opcode = line.Substring(0, space); line = line.Substring(space + 1).Trim(); } else { line = ""; } string[] types = opcode.Split('.'); if (types.Length > 3) { throw new Exception("Too many type parameters"); } instr.Kind = (UndertaleInstruction.Opcode)Enum.Parse(typeof(UndertaleInstruction.Opcode), types[0], true); if (types.Length >= 2) { instr.Type1 = UndertaleInstructionUtil.FromOpcodeParam(types[1]); } if (types.Length >= 3) { instr.Type2 = UndertaleInstructionUtil.FromOpcodeParam(types[2]); } switch (UndertaleInstruction.GetInstructionType(instr.Kind)) { case UndertaleInstruction.InstructionType.SingleTypeInstruction: if (instr.Kind == UndertaleInstruction.Opcode.Dup) { instr.DupExtra = Byte.Parse(line); line = ""; } break; case UndertaleInstruction.InstructionType.DoubleTypeInstruction: break; case UndertaleInstruction.InstructionType.ComparisonInstruction: instr.ComparisonKind = (UndertaleInstruction.ComparisonType)Enum.Parse(typeof(UndertaleInstruction.ComparisonType), line, true); line = ""; break; case UndertaleInstruction.InstructionType.GotoInstruction: if (line[0] == '$') { instr.JumpOffset = Int32.Parse(line.Substring(1)); } else { label = line; } line = ""; break; case UndertaleInstruction.InstructionType.PopInstruction: UndertaleInstruction.InstanceType inst = instr.TypeInst; instr.Destination = ParseVariableReference(line, vars, localvars, ref inst, instTypeOnStack); instr.TypeInst = inst; line = ""; break; case UndertaleInstruction.InstructionType.PushInstruction: switch (instr.Type1) { case UndertaleInstruction.DataType.Double: instr.Value = Double.Parse(line, CultureInfo.InvariantCulture); break; case UndertaleInstruction.DataType.Float: instr.Value = Single.Parse(line, CultureInfo.InvariantCulture); break; case UndertaleInstruction.DataType.Int32: instr.Value = Int32.Parse(line); break; case UndertaleInstruction.DataType.Int64: instr.Value = Int64.Parse(line); break; case UndertaleInstruction.DataType.Boolean: instr.Value = Boolean.Parse(line); break; case UndertaleInstruction.DataType.Variable: UndertaleInstruction.InstanceType inst2 = instr.TypeInst; instr.Value = ParseVariableReference(line, vars, localvars, ref inst2, instTypeOnStack); instr.TypeInst = inst2; break; case UndertaleInstruction.DataType.String: instr.Value = ParseStringReference(line, strg); break; case UndertaleInstruction.DataType.Int16: instr.Value = Int16.Parse(line); break; } line = ""; break; case UndertaleInstruction.InstructionType.CallInstruction: Match match = Regex.Match(line, @"^(.*)\(argc=(.*)\)$"); if (!match.Success) { throw new Exception("Call instruction format error"); } UndertaleFunction func = funcs.ByName(match.Groups[1].Value); if (func == null) { throw new Exception("Function not found: " + match.Groups[1].Value); } instr.Function = new UndertaleInstruction.Reference <UndertaleFunction>() { Target = func }; instr.ArgumentsCount = UInt16.Parse(match.Groups[2].Value); line = ""; break; case UndertaleInstruction.InstructionType.BreakInstruction: instr.Value = Int16.Parse(line); line = ""; break; } if (line != "") { throw new Exception("Excess parameters"); } return(instr); }
public static UndertaleVariable EnsureDefined(this IList <UndertaleVariable> list, string name, UndertaleInstruction.InstanceType inst, bool isBuiltin, IList <UndertaleString> strg, UndertaleData data, bool fast = false) { if (inst == UndertaleInstruction.InstanceType.Local) { throw new InvalidOperationException("Use DefineLocal instead"); } bool bytecode14 = (data?.GeneralInfo?.BytecodeVersion <= 14); if (bytecode14) { inst = UndertaleInstruction.InstanceType.Undefined; } UndertaleVariable vari = fast ? null : list.Where((x) => x.Name?.Content == name && x.InstanceType == inst).FirstOrDefault(); if (vari == null) { var str = strg.MakeString(name, out int id); var oldId = data.VarCount1; if (!bytecode14) { if (data.GMS2_3) { // GMS 2.3+ if (!isBuiltin) { data.VarCount1++; data.VarCount2 = data.VarCount1; } oldId = (uint)id; } else if (!data.DifferentVarCounts) { // Bytecode 16+ data.VarCount1++; data.VarCount2++; } else { // Bytecode 15 if (inst == UndertaleInstruction.InstanceType.Self && !isBuiltin) { oldId = data.VarCount2; data.VarCount2++; } else if (inst == UndertaleInstruction.InstanceType.Global) { data.VarCount1++; } } } vari = new UndertaleVariable() { Name = str, InstanceType = inst, VarID = bytecode14 ? 0 : (isBuiltin ? (int)UndertaleInstruction.InstanceType.Builtin : (int)oldId), NameStringID = id }; list.Add(vari); } return(vari); }
private static UndertaleInstruction.Reference <UndertaleVariable> ParseVariableReference(string line, IList <UndertaleVariable> vars, Dictionary <string, UndertaleVariable> localvars, ref UndertaleInstruction.InstanceType instance, UndertaleInstruction.InstanceType?prevInstType) { string str = line; string inst = null; int instdot = str.IndexOf('.'); if (instdot >= 0) { inst = str.Substring(0, instdot); str = str.Substring(instdot + 1); if (inst == "") { throw new Exception("Whoops?"); } } if (inst != null) { short instnum; if (Int16.TryParse(inst, out instnum)) { instance = (UndertaleInstruction.InstanceType)instnum; } else { instance = (UndertaleInstruction.InstanceType)Enum.Parse(typeof(UndertaleInstruction.InstanceType), inst, true); } } else { instance = UndertaleInstruction.InstanceType.Undefined; } UndertaleInstruction.VariableType type = UndertaleInstruction.VariableType.Normal; if (str[0] == '[') { int typeend = str.IndexOf(']'); if (typeend >= 0) { string typestr = str.Substring(1, typeend - 1); str = str.Substring(typeend + 1); type = (UndertaleInstruction.VariableType)Enum.Parse(typeof(UndertaleInstruction.VariableType), typestr, true); } } UndertaleInstruction.InstanceType realinstance = instance; // for arrays, the type is on the stack which totally breaks things // This is an ugly hack to handle that if (type == UndertaleInstruction.VariableType.Array && prevInstType.HasValue) { realinstance = prevInstType.Value; } if (realinstance >= 0) { realinstance = UndertaleInstruction.InstanceType.Self; } UndertaleVariable varobj; if (realinstance == UndertaleInstruction.InstanceType.Local) { varobj = localvars.ContainsKey(str) ? localvars[str] : null; } else { varobj = vars.Where((x) => x.Name.Content == str && x.InstanceType == realinstance).FirstOrDefault(); } if (varobj == null) { throw new Exception("Bad variable!"); } return(new UndertaleInstruction.Reference <UndertaleVariable>(varobj, type)); }