Exemplo n.º 1
0
        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));
        }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        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));
        }
Exemplo n.º 4
0
        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);
    }
Exemplo n.º 6
0
        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));
        }