示例#1
0
        // TODO: Improve the error messages

        public static UndertaleInstruction AssembleOne(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg, Dictionary <string, UndertaleVariable> localvars = null, UndertaleData data = null)
        {
            string label;
            UndertaleInstruction instr = AssembleOne(source, funcs, vars, strg, localvars, out label, data, null);

            if (label != null)
            {
                throw new Exception("Cannot use labels in this context");
            }
            return(instr);
        }
示例#2
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));
        }
示例#3
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);
        }
示例#4
0
        public static List <UndertaleInstruction> Assemble(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg, UndertaleData data = null)
        {
            var  lines = source.Split('\n');
            uint addr  = 0;
            Dictionary <string, uint> labels = new Dictionary <string, uint>();
            Dictionary <UndertaleInstruction, string> labelTargets = new Dictionary <UndertaleInstruction, string>();
            List <UndertaleInstruction>            instructions    = new List <UndertaleInstruction>();
            Dictionary <string, UndertaleVariable> localvars       = new Dictionary <string, UndertaleVariable>();

            foreach (var fullline in lines)
            {
                string line = fullline;
                if (line.Length > 0 && line[0] == ';')
                {
                    continue;
                }
                string label    = null;
                int    labelEnd = line.IndexOf(':');

                if (labelEnd > 0)
                {
                    bool isLabel = true;
                    for (var i = 0; i < labelEnd; i++)
                    {
                        if (!Char.IsLetterOrDigit(line[i]) && line[i] != '_')
                        {
                            isLabel = false;
                            break;
                        }
                    }

                    if (isLabel)
                    {
                        label = line.Substring(0, labelEnd).Trim();
                        line  = line.Substring(labelEnd + 1);
                    }
                }
                line = line.Trim();
                if (String.IsNullOrEmpty(line))
                {
                    if (!String.IsNullOrEmpty(label))
                    {
                        throw new Exception("Label with no instruction");
                    }
                    else
                    {
                        continue;
                    }
                }

                if (line.StartsWith("."))
                {
                    // Assembler directive
                    // TODO: Does not update the CodeLocals block yet!!
                    string[] aaa = line.Split(' ');
                    if (aaa[0] == ".localvar")
                    {
                        if (aaa.Length >= 4)
                        {
                            var varii = vars[Int32.Parse(aaa[3])];
                            if (data?.GeneralInfo?.BytecodeVersion >= 15 && varii.InstanceType != UndertaleInstruction.InstanceType.Local)
                            {
                                throw new Exception("Not a local var");
                            }
                            if (varii.Name.Content != aaa[2])
                            {
                                throw new Exception("Name mismatch");
                            }
                            localvars.Add(aaa[2], varii);
                        }
                    }
                    else
                    {
                        throw new Exception("Unknown assembler directive: " + aaa[0]);
                    }
                    continue;
                }

                // Really ugly hack for compiling array variable references
                // See https://github.com/krzys-h/UndertaleModTool/issues/27#issuecomment-426637438
                Func <int, UndertaleInstruction.InstanceType?> lookOnStack = (int amt) => {
                    int stackCounter = amt;
                    foreach (var i in instructions.Cast <UndertaleInstruction>().Reverse())
                    {
                        if (stackCounter == 1) // This needs to be here because otherwise sth[aaa].another[bbb] doesn't work (damn this workaround is getting crazy, CHAOS, CHAOS)
                        {
                            if (i.Kind == UndertaleInstruction.Opcode.Push)
                            {
                                return(UndertaleInstruction.InstanceType.Self); // This is probably an instance variable then (e.g. pushi.e 1337; push.v self.someinstance; conv.v.i; pushi.e 0; pop.v.v [array]alarm)
                            }
                            else if (i.Kind == UndertaleInstruction.Opcode.PushLoc)
                            {
                                return(UndertaleInstruction.InstanceType.Local);
                            }
                        }
                        //int old = stackCounter;
                        stackCounter -= UndertaleInstruction.CalculateStackDiff(i);
                        //Debug.WriteLine(i.ToString() + "; " + old + " -> " + stackCounter);
                        if (stackCounter == 0)
                        {
                            if (i.Kind == UndertaleInstruction.Opcode.PushI)
                            {
                                return((UndertaleInstruction.InstanceType?)(short) i.Value);
                            }
                            else if (i.Kind == UndertaleInstruction.Opcode.Dup)
                            {
                                stackCounter += 1 + i.DupExtra; // Keep looking for the value that was duplicated
                            }
                            else
                            {
                                throw new Exception("My workaround still sucks");
                            }
                        }
                    }
                    return(null);
                };

                string labelTgt;
                UndertaleInstruction instr = AssembleOne(line, funcs, vars, strg, localvars, out labelTgt, data, lookOnStack);
                instr.Address = addr;
                if (labelTgt != null)
                {
                    labelTargets.Add(instr, labelTgt);
                }

                if (!String.IsNullOrEmpty(label))
                {
                    if (labels.ContainsKey(label))
                    {
                        throw new Exception("Duplicate label: " + label);
                    }
                    labels.Add(label, instr.Address);
                }
                instructions.Add(instr);

                addr += instr.CalculateInstructionSize();
            }
            if (labels.ContainsKey("func_end"))
            {
                throw new Exception("func_end is a reserved label name");
            }
            labels.Add("func_end", addr);
            foreach (var pair in labelTargets)
            {
                pair.Key.JumpOffset = (int)labels[pair.Value] - (int)pair.Key.Address;
            }
            return(instructions);
        }
示例#5
0
        private void DisassembleCode(UndertaleCode code)
        {
            code.UpdateAddresses();

            FlowDocument document = new FlowDocument();

            document.PagePadding = new Thickness(0);
            document.PageWidth   = 2048; // Speed-up.
            document.FontFamily  = new FontFamily("Lucida Console");
            Paragraph par = new Paragraph();

            par.Margin = new Thickness(0);

            if (code.Instructions.Count > 5000)
            {
                // Disable syntax highlighting. Loading it can take a few MINUTES on large scripts.
                var      data  = (Application.Current.MainWindow as MainWindow).Data;
                string[] split = code.Disassemble(data.Variables, data.CodeLocals.For(code)).Split('\n');

                for (var i = 0; i < split.Length; i++)
                { // Makes it possible to select text.
                    if (i > 0 && (i % 100) == 0)
                    {
                        document.Blocks.Add(par);
                        par        = new Paragraph();
                        par.Margin = new Thickness(0);
                    }

                    par.Inlines.Add(split[i] + (split.Length > i + 1 && ((i + 1) % 100) != 0 ? "\n" : ""));
                }
            }
            else
            {
                Brush addressBrush = new SolidColorBrush(Color.FromRgb(50, 50, 50));
                Brush opcodeBrush  = new SolidColorBrush(Color.FromRgb(0, 100, 0));
                Brush argBrush     = new SolidColorBrush(Color.FromRgb(0, 0, 150));
                Brush typeBrush    = new SolidColorBrush(Color.FromRgb(0, 0, 50));
                var   data         = (Application.Current.MainWindow as MainWindow).Data;
                par.Inlines.Add(new Run(code.GenerateLocalVarDefinitions(data.Variables, data.CodeLocals.For(code)))
                {
                    Foreground = addressBrush
                });
                foreach (var instr in code.Instructions)
                {
                    par.Inlines.Add(new Run(instr.Address.ToString("D5") + ": ")
                    {
                        Foreground = addressBrush
                    });
                    par.Inlines.Add(new Run(instr.Kind.ToString().ToLower())
                    {
                        Foreground = opcodeBrush, FontWeight = FontWeights.Bold
                    });

                    switch (UndertaleInstruction.GetInstructionType(instr.Kind))
                    {
                    case UndertaleInstruction.InstructionType.SingleTypeInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });

                        if (instr.Kind == UndertaleInstruction.Opcode.Dup || instr.Kind == UndertaleInstruction.Opcode.CallV)
                        {
                            par.Inlines.Add(new Run(" "));
                            par.Inlines.Add(new Run(instr.Extra.ToString())
                            {
                                Foreground = argBrush
                            });
                        }
                        break;

                    case UndertaleInstruction.InstructionType.DoubleTypeInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        break;

                    case UndertaleInstruction.InstructionType.ComparisonInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run(" "));
                        par.Inlines.Add(new Run(instr.ComparisonKind.ToString())
                        {
                            Foreground = opcodeBrush
                        });
                        break;

                    case UndertaleInstruction.InstructionType.GotoInstruction:
                        par.Inlines.Add(new Run(" "));
                        string tgt = (instr.Address + instr.JumpOffset).ToString("D5");
                        if (instr.Address + instr.JumpOffset == code.Length / 4)
                        {
                            tgt = "func_end";
                        }
                        if (instr.JumpOffsetPopenvExitMagic)
                        {
                            tgt = "[drop]";
                        }
                        par.Inlines.Add(new Run(tgt)
                        {
                            Foreground = argBrush, ToolTip = "$" + instr.JumpOffset.ToString("+#;-#;0")
                        });
                        break;

                    case UndertaleInstruction.InstructionType.PopInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run("." + instr.Type2.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run(" "));
                        if (instr.Type1 == UndertaleInstruction.DataType.Int16)
                        {
                            // Special scenario - the swap instruction
                            // TODO: Figure out the proper syntax, see #129
                            Run runType = new Run(instr.SwapExtra.ToString().ToLower())
                            {
                                Foreground = argBrush
                            };
                            par.Inlines.Add(runType);
                        }
                        else
                        {
                            if (instr.Type1 == UndertaleInstruction.DataType.Variable && instr.TypeInst != UndertaleInstruction.InstanceType.Undefined)
                            {
                                par.Inlines.Add(new Run(instr.TypeInst.ToString().ToLower())
                                {
                                    Foreground = typeBrush
                                });
                                par.Inlines.Add(new Run("."));
                            }

                            Run runDest = new Run(instr.Destination.ToString())
                            {
                                Foreground = argBrush, Cursor = Cursors.Hand
                            };
                            runDest.MouseDown += (sender, e) =>
                            {
                                (Application.Current.MainWindow as MainWindow).ChangeSelection(instr.Destination);
                            };
                            par.Inlines.Add(runDest);
                        }
                        break;

                    case UndertaleInstruction.InstructionType.PushInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run(" "));
                        if (instr.Type1 == UndertaleInstruction.DataType.Variable && instr.TypeInst != UndertaleInstruction.InstanceType.Undefined)
                        {
                            par.Inlines.Add(new Run(instr.TypeInst.ToString().ToLower())
                            {
                                Foreground = typeBrush
                            });
                            par.Inlines.Add(new Run("."));
                        }
                        Run valueRun = new Run((instr.Value as IFormattable)?.ToString(null, CultureInfo.InvariantCulture) ?? instr.Value.ToString())
                        {
                            Foreground = argBrush, Cursor = (instr.Value is UndertaleObject || instr.Value is UndertaleResourceRef) ? Cursors.Hand : Cursors.Arrow
                        };
                        if (instr.Value is UndertaleResourceRef)
                        {
                            valueRun.MouseDown += (sender, e) =>
                            {
                                (Application.Current.MainWindow as MainWindow).ChangeSelection((instr.Value as UndertaleResourceRef).Resource);
                            };
                        }
                        else if (instr.Value is UndertaleObject)
                        {
                            valueRun.MouseDown += (sender, e) =>
                            {
                                (Application.Current.MainWindow as MainWindow).ChangeSelection(instr.Value);
                            };
                        }
                        par.Inlines.Add(valueRun);
                        break;

                    case UndertaleInstruction.InstructionType.CallInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run(" "));
                        par.Inlines.Add(new Run(instr.Function.ToString())
                        {
                            Foreground = argBrush
                        });
                        par.Inlines.Add(new Run("(argc="));
                        par.Inlines.Add(new Run(instr.ArgumentsCount.ToString())
                        {
                            Foreground = argBrush
                        });
                        par.Inlines.Add(new Run(")"));
                        break;

                    case UndertaleInstruction.InstructionType.BreakInstruction:
                        par.Inlines.Add(new Run("." + instr.Type1.ToOpcodeParam())
                        {
                            Foreground = typeBrush
                        });
                        par.Inlines.Add(new Run(" "));
                        par.Inlines.Add(new Run(instr.Value.ToString())
                        {
                            Foreground = argBrush
                        });
                        break;
                    }

                    if (par.Inlines.Count >= 250)
                    { // Makes selecting text possible.
                        document.Blocks.Add(par);
                        par        = new Paragraph();
                        par.Margin = new Thickness(0);
                    }
                    else
                    {
                        par.Inlines.Add(new Run("\n"));
                    }
                }
            }
            document.Blocks.Add(par);

            DisassemblyView.Document = document;

            CurrentDisassembled = code;
        }
示例#6
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));
        }
示例#7
0
        public static List <UndertaleInstruction> Assemble(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg, UndertaleData data = null)
        {
            var  lines = source.Replace("\r", "").Split('\n');
            uint addr  = 0;
            Dictionary <string, uint> labels = new Dictionary <string, uint>();
            Dictionary <UndertaleInstruction, string> labelTargets = new Dictionary <UndertaleInstruction, string>();
            List <UndertaleInstruction>            instructions    = new List <UndertaleInstruction>();
            Dictionary <string, UndertaleVariable> localvars       = new Dictionary <string, UndertaleVariable>();

            foreach (var fullline in lines)
            {
                string line = fullline;
                if (line.Length == 0)
                {
                    continue;
                }

                if (line[0] == ';')
                {
                    continue;
                }
                if (line[0] == '>')
                {
                    // Code entry inside of this one
                    line = line.Substring(2, line.Length - 2).Trim();
                    int    space    = line.IndexOf(' ');
                    string codeName = line.Substring(0, space);
                    var    code     = data.Code.ByName(codeName);
                    if (code == null)
                    {
                        throw new Exception($"Failed to find code entry with name \"{codeName}\".");
                    }
                    string info = line.Substring(space + 1);

                    Match match = Regex.Match(info, @"^\(locals=(.*)\,\s*argc=(.*)\)$");
                    if (!match.Success)
                    {
                        throw new Exception("Sub-code entry format error");
                    }
                    code.LocalsCount    = ushort.Parse(match.Groups[1].Value);
                    code.ArgumentsCount = ushort.Parse(match.Groups[2].Value);
                    code.Offset         = addr * 4;
                    continue;
                }
                if (line[0] == ':' && line.Length >= 3)
                {
                    if (line[1] == '[')
                    {
                        string label = line.Substring(2, line.IndexOf(']') - 2);

                        if (!string.IsNullOrEmpty(label))
                        {
                            if (labels.ContainsKey(label))
                            {
                                throw new Exception("Duplicate label: " + label);
                            }
                            labels.Add(label, addr);
                        }

                        continue;
                    }
                }

                if (line[0] == '.')
                {
                    // Assembler directive
                    // TODO: Does not update the CodeLocals block yet!!
                    string[] aaa = line.Split(' ');
                    if (aaa[0] == ".localvar")
                    {
                        if (aaa.Length >= 4)
                        {
                            var varii = vars[Int32.Parse(aaa[3])];
                            if (data?.GeneralInfo?.BytecodeVersion >= 15 && varii.InstanceType != UndertaleInstruction.InstanceType.Local)
                            {
                                throw new Exception("Not a local var");
                            }
                            if (varii.Name.Content != aaa[2])
                            {
                                throw new Exception("Name mismatch");
                            }
                            localvars.Add(aaa[2], varii);
                        }
                    }
                    else
                    {
                        throw new Exception("Unknown assembler directive: " + aaa[0]);
                    }
                    continue;
                }

                string labelTgt;
                UndertaleInstruction instr = AssembleOne(line, funcs, vars, strg, localvars, out labelTgt, data);
                instr.Address = addr;
                if (labelTgt != null)
                {
                    labelTargets.Add(instr, labelTgt);
                }

                instructions.Add(instr);

                addr += instr.CalculateInstructionSize();
            }
            foreach (var pair in labelTargets)
            {
                pair.Key.JumpOffset = (int)labels[pair.Value] - (int)pair.Key.Address;
            }
            return(instructions);
        }
示例#8
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);
        }
示例#9
0
        public static List <UndertaleInstruction> Assemble(string source, IList <UndertaleFunction> funcs, IList <UndertaleVariable> vars, IList <UndertaleString> strg)
        {
            var  lines = source.Split('\n');
            uint addr  = 0;
            Dictionary <string, uint> labels = new Dictionary <string, uint>();
            Dictionary <UndertaleInstruction, string> labelTargets = new Dictionary <UndertaleInstruction, string>();
            List <UndertaleInstruction>            instructions    = new List <UndertaleInstruction>();
            Dictionary <string, UndertaleVariable> localvars       = new Dictionary <string, UndertaleVariable>();

            foreach (var fullline in lines)
            {
                string line = fullline;
                if (line.Length > 0 && line[0] == ';')
                {
                    continue;
                }
                string label    = null;
                int    labelEnd = line.IndexOf(':');
                if (labelEnd >= 0)
                {
                    label = line.Substring(0, labelEnd).Trim();
                    line  = line.Substring(labelEnd + 1);
                    if (String.IsNullOrEmpty(label))
                    {
                        throw new Exception("Empty label");
                    }
                }
                line = line.Trim();
                if (String.IsNullOrEmpty(line))
                {
                    if (!String.IsNullOrEmpty(label))
                    {
                        throw new Exception("Label with no instruction");
                    }
                    else
                    {
                        continue;
                    }
                }

                if (line.StartsWith("."))
                {
                    // Assembler directive
                    // TODO: Does not update the CodeLocals block yet!!
                    string[] aaa = line.Split(' ');
                    if (aaa[0] == ".localvar")
                    {
                        if (aaa.Length >= 4)
                        {
                            var varii = vars[Int32.Parse(aaa[3])];
                            if (varii.InstanceType != UndertaleInstruction.InstanceType.Local)
                            {
                                throw new Exception("Not a local var");
                            }
                            if (varii.Name.Content != aaa[2])
                            {
                                throw new Exception("Name mismatch");
                            }
                            localvars.Add(aaa[2], varii);
                        }
                    }
                    else
                    {
                        throw new Exception("Unknown assembler directive: " + aaa[0]);
                    }
                    continue;
                }

                // Really ugly hack for compiling array variable references
                // See https://github.com/krzys-h/UndertaleModTool/issues/27#issuecomment-426637438
                UndertaleInstruction instTypePush = instructions.Cast <UndertaleInstruction>().Reverse().Where((x) => UndertaleInstruction.GetInstructionType(x.Kind) == UndertaleInstruction.InstructionType.PushInstruction).ElementAtOrDefault(1);
                UndertaleInstruction.InstanceType?instTypeOnStack = instTypePush != null && instTypePush.Kind == UndertaleInstruction.Opcode.PushI ? (UndertaleInstruction.InstanceType?)(short) instTypePush.Value : null;

                string labelTgt;
                UndertaleInstruction instr = AssembleOne(line, funcs, vars, strg, localvars, out labelTgt, instTypeOnStack);
                instr.Address = addr;
                if (labelTgt != null)
                {
                    labelTargets.Add(instr, labelTgt);
                }

                if (!String.IsNullOrEmpty(label))
                {
                    if (labels.ContainsKey(label))
                    {
                        throw new Exception("Duplicate label: " + label);
                    }
                    labels.Add(label, instr.Address);
                }
                instructions.Add(instr);

                addr += instr.CalculateInstructionSize();
            }
            if (labels.ContainsKey("func_end"))
            {
                throw new Exception("func_end is a reserved label name");
            }
            labels.Add("func_end", addr);
            foreach (var pair in labelTargets)
            {
                pair.Key.JumpOffset = (int)labels[pair.Value] - (int)pair.Key.Address;
            }
            return(instructions);
        }