public static List <int> FindBlockAddresses(GMCode.Bytecode bytecode, bool slow = true)
        {
            HashSet <int> addresses = new HashSet <int>();

            if (bytecode.Instructions.Count != 0)
            {
                addresses.Add(0);
                for (int i = 0; i < bytecode.Instructions.Count; i++)
                {
                    Instruction instr = bytecode.Instructions[i];
                    switch (instr.Kind)
                    {
                    case Instruction.Opcode.B:
                    case Instruction.Opcode.Bf:
                    case Instruction.Opcode.Bt:
                    case Instruction.Opcode.PushEnv:
                        addresses.Add(instr.Address + 4);
                        addresses.Add(instr.Address + (instr.JumpOffset * 4));
                        break;

                    case Instruction.Opcode.PopEnv:
                        if (!instr.PopenvExitMagic)
                        {
                            addresses.Add(instr.Address + (instr.JumpOffset * 4));
                        }
                        break;

                    case Instruction.Opcode.Exit:
                    case Instruction.Opcode.Ret:
                        addresses.Add(instr.Address + 4);
                        break;

                    case Instruction.Opcode.Call:
                        if (slow && i >= 4 && instr.Function.Target.Name?.Content == "@@try_hook@@")
                        {
                            int finallyBlock = (int)bytecode.Instructions[i - 4].Value;
                            addresses.Add(finallyBlock);

                            int catchBlock = (int)bytecode.Instructions[i - 2].Value;
                            if (catchBlock != -1)
                            {
                                addresses.Add(catchBlock);
                            }

                            // Technically not usually a block here (before/after the call), but for our purposes,
                            // this is easier to split into its own section to isolate it now.
                            addresses.Add(instr.Address - 24);
                            addresses.Add(instr.Address + 12);
                        }
                        break;
                    }
                }
            }

            List <int> res = addresses.ToList();

            res.Sort();
            return(res);
        }
Example #2
0
        public static List <int> FindBlockAddresses(GMCode.Bytecode bytecode)
        {
            HashSet <int> addresses = new HashSet <int>();

            if (bytecode.Instructions.Count != 0)
            {
                addresses.Add(0);
            }

            foreach (var i in bytecode.Instructions)
            {
                switch (i.Kind)
                {
                case GMCode.Bytecode.Instruction.Opcode.B:
                case GMCode.Bytecode.Instruction.Opcode.Bf:
                case GMCode.Bytecode.Instruction.Opcode.Bt:
                case GMCode.Bytecode.Instruction.Opcode.PushEnv:
                    addresses.Add(i.Address + 4);
                    addresses.Add(i.Address + (i.JumpOffset * 4));
                    break;

                case GMCode.Bytecode.Instruction.Opcode.PopEnv:
                    if (!i.PopenvExitMagic)
                    {
                        addresses.Add(i.Address + (i.JumpOffset * 4));
                    }
                    break;

                case GMCode.Bytecode.Instruction.Opcode.Exit:
                case GMCode.Bytecode.Instruction.Opcode.Ret:
                    addresses.Add(i.Address + 4);
                    break;
                }
            }

            List <int> res = addresses.ToList();

            res.Sort();
            return(res);
        }
Example #3
0
        public static string Disassemble(GMCode codeEntry, GMData data)
        {
            GMCode.Bytecode  bytecode = codeEntry.BytecodeEntry;
            IList <GMString> strings  = ((GMChunkSTRG)data.Chunks["STRG"]).List;

            StringBuilder sb = new StringBuilder();

            sb.AppendLine($"# Name: {codeEntry.Name.Content}");
            if (codeEntry.BytecodeOffset != 0) // Usually should be 0, but for information sake write this
            {
                sb.AppendLine($"# Offset: {codeEntry.BytecodeOffset}");
            }

            List <int> blocks = FindBlockAddresses(bytecode);

            foreach (var i in bytecode.Instructions)
            {
                int ind = blocks.IndexOf(i.Address);
                if (ind != -1)
                {
                    sb.AppendLine();
                    sb.AppendLine($":[{ind}]");
                }

                if (i.Kind != GMCode.Bytecode.Instruction.Opcode.Break)
                {
                    sb.Append(i.Kind.ToString().ToLower());
                }

                switch (GMCode.Bytecode.Instruction.GetInstructionType(i.Kind))
                {
                case GMCode.Bytecode.Instruction.InstructionType.SingleType:
                    sb.Append($".{DataTypeToChar[i.Type1]}");

                    if (i.Kind == GMCode.Bytecode.Instruction.Opcode.Dup ||
                        i.Kind == GMCode.Bytecode.Instruction.Opcode.CallV)
                    {
                        sb.Append($" {i.Extra}");
                    }
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.DoubleType:
                    sb.Append($".{DataTypeToChar[i.Type1]}.{DataTypeToChar[i.Type2]}");
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Comparison:
                    sb.Append($".{DataTypeToChar[i.Type1]}.{DataTypeToChar[i.Type2]} {i.ComparisonKind}");
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Branch:
                    if (i.Address + (i.JumpOffset * 4) == codeEntry.Length)
                    {
                        sb.Append(" [end]");
                    }
                    else if (i.PopenvExitMagic)
                    {
                        sb.Append(" [magic]");     // magic popenv instruction when returning early inside a with statement
                    }
                    else
                    {
                        sb.Append($" [{blocks.IndexOf(i.Address + (i.JumpOffset * 4))}]");
                    }
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Pop:
                    sb.Append($".{DataTypeToChar[i.Type1]}.{DataTypeToChar[i.Type2]} ");
                    if (i.Type1 == GMCode.Bytecode.Instruction.DataType.Int16)
                    {
                        sb.Append(i.SwapExtra.ToString());     // Special swap instruction
                    }
                    else
                    {
                        if (i.Type1 == GMCode.Bytecode.Instruction.DataType.Variable &&
                            i.TypeInst != GMCode.Bytecode.Instruction.InstanceType.Undefined)
                        {
                            sb.Append($"{i.TypeInst.ToString().ToLower()}.");
                        }

                        sb.Append(StringifyVariableRef(i.Variable));
                    }
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Push:
                    sb.Append($".{DataTypeToChar[i.Type1]} ");
                    if (i.Type1 == GMCode.Bytecode.Instruction.DataType.Variable)
                    {
                        if (i.TypeInst != GMCode.Bytecode.Instruction.InstanceType.Undefined)
                        {
                            sb.Append($"{i.TypeInst.ToString().ToLower()}.");
                        }

                        sb.Append(StringifyVariableRef(i.Variable));
                    }
                    else if (i.Type1 == GMCode.Bytecode.Instruction.DataType.String)
                    {
                        sb.Append($"\"{SanitizeString(strings[(int)i.Value].Content)}\"");
                    }
                    else if (i.Function != null)
                    {
                        sb.Append(i.Function.Target.Name.Content);
                    }
                    else
                    {
                        sb.Append((i.Value as IFormattable)?.ToString(null, CultureInfo.InvariantCulture) ?? i.Value.ToString());
                    }
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Call:
                    sb.Append($".{DataTypeToChar[i.Type1]} {i.Function.Target.Name.Content} {i.ArgumentsCount}");
                    break;

                case GMCode.Bytecode.Instruction.InstructionType.Break:
                    sb.Append($"{BreakIDToName[(short)i.Value]}.{DataTypeToChar[i.Type1]}");
                    break;
                }
                sb.AppendLine();
            }

            sb.AppendLine();
            sb.Append(":[end]");

            return(sb.ToString());
        }