Пример #1
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);
        }
Пример #2
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);
        }
Пример #3
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);
        }