Esempio n. 1
0
        public static string DecompileCode(GMFileContent content, RefData rdata, CodeInfo code, bool absolute = false)
        {
            if (code.Instructions.Length == 0)
            {
                return(String.Empty);
            }

            var sb = new StringBuilder();

            var stack = new Stack <Expression>();
            var dupts = new List <Expression>();

            var firstI = (long)code.Instructions[0];

            var graph = BuildCFGraph(content, code);

            //TODO: CFG kind recognition stuff (if, if/else, for, while, etc)
            var i = 0;

            foreach (var g in graph)
            {
                var stmts = ParseStatements(content, rdata, code, g.Instructions, stack, absolute: absolute);

                sb.Append(SR.HEX_PRE)
                .Append(((long)g.Instructions[0] - firstI).ToString(SR.HEX_FM6))
                .AppendLine(SR.COLON);

                foreach (var s in stmts)
                {
                    sb.Append(SR.INDENT4).AppendLine(s.ToString());
                }

                i++;
            }

            sb.Append(SR.HEX_PRE).Append(code.Size.ToString(SR.HEX_FM6)).AppendLine(SR.COLON);
            sb.Append(SR.INDENT4).AppendLine(FinalRet.ToString());

            return(sb.ToString());
        }
Esempio n. 2
0
        public static string DisplayInstructions(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction *[] instructions = null, bool absolute = false)
        {
            var bcv = content.General->BytecodeVersion;

            var instrs = instructions ?? code.Instructions;

            if (instrs.Length == 0)
            {
                return(String.Empty);
            }

            var sb = new StringBuilder();

            var firstI = code.Instructions[0];

            for (int i = 0; i < instrs.Length; i++)
            {
                var iptr     = instrs[i];
                var relInstr = (long)iptr - (absolute ? (long)content.RawData.BPtr : (long)firstI);

                sb.Append(HEX_PRE).Append(relInstr.ToString(HEX_FM6))
                .Append(COLON_S).Append(iptr->OpCode.ToPrettyString(bcv)).Append(' ');

                switch (iptr->Kind(content.General->BytecodeVersion))
                {
                case InstructionKind.SingleType:
                    var st = iptr->SingleType;

                    sb.Append(st.Type.ToPrettyString());

                    if (bcv > 0xE && st.OpCode.VersionF == FOpCode.Dup)
                    {
                        sb.Append(' ').Append(st.DupExtra);
                    }

                    break;

                case InstructionKind.DoubleType:
                    var dt = iptr->DoubleType;

                    if (bcv > 0xE && iptr->OpCode.VersionF == FOpCode.Cmp)
                    {
                        sb.Append(dt.ComparisonType.ToPrettyString()).Append(' ');
                    }

                    sb.Append(dt.Types);
                    break;

                case InstructionKind.Goto:
                    var g = iptr->Goto;

                    var a = g.Offset.UValue * 4;
                    if ((a & 0xFF000000) != 0)
                    {
                        a &= 0x00FFFFFF;
                        a -= 0x01000000;
                    }

                    sb.Append(HEX_PRE).Append(Utils.ToHexSignString(relInstr + unchecked ((int)a), HEX_FM6));
                    break;

                    #region set
                case InstructionKind.Set:
                    var s = iptr->Set;

                    sb.Append(s.Types).Append(' ');

                    if (s.IsMagic)
                    {
                        sb.Append(MAGIC);
                        break;
                    }

                    if (s.Instance <= InstanceType.StackTopOrGlobal)
                    {
                        sb.Append(s.Instance.ToPrettyString());
                    }
                    else
                    {
                        var o = SectionReader.GetObjectInfo(content, (uint)s.Instance);

                        sb.Append('[').Append(o.Name).Append(']');
                    }

                    sb.Append(':');

                    sb.Append(rdata.Variables[rdata.VarAccessors[(IntPtr)iptr]].Name);
                    sb.Append(s.DestVar.Type.ToPrettyString());
                    break;

                    #endregion
                    #region push
                case InstructionKind.Push:
                    var pp = (PushInstruction *)iptr;
                    var p  = iptr->Push;

                    sb.Append(p.Type.ToPrettyString()).Append(' ');

                    var r = p.ValueRest;

                    switch (p.Type)
                    {
                    case DataType.Int16:
                        sb.Append(p.Value.ToString(CultureInfo.InvariantCulture));
                        break;

                    case DataType.Variable:
                        var rv = *(Reference *)&r;

                        var inst = (InstanceType)p.Value;

                        if (inst <= InstanceType.StackTopOrGlobal)
                        {
                            sb.Append(inst.ToPrettyString());
                        }
                        else
                        {
                            var o = SectionReader.GetObjectInfo(content, (uint)inst);

                            sb.Append('[').Append(o.Name).Append(']');
                        }
                        sb.Append(':');

                        sb.Append(rdata.Variables[rdata.VarAccessors[(IntPtr)iptr]].Name);
                        sb.Append(rv.Type.ToPrettyString());
                        break;

                    case DataType.Boolean:
                        sb.Append(((DwordBool *)&r)->ToPrettyString());
                        break;

                    case DataType.Double:
                        sb.Append(((double *)&r)->ToString(CultureInfo.InvariantCulture));
                        break;

                    case DataType.Single:
                        sb.Append(((float *)&r)->ToString(CultureInfo.InvariantCulture));
                        break;

                    case DataType.Int32:
                        sb.Append(unchecked ((int)r).ToString(CultureInfo.InvariantCulture));
                        break;

                    case DataType.Int64:
                        sb.Append(((long *)&pp->ValueRest)->ToString(CultureInfo.InvariantCulture));
                        break;

                    case DataType.String:
                        sb.Append(SectionReader.GetStringInfo(content, p.ValueRest).Escape());
                        break;
                    }
                    break;

                    #endregion
                    #region call
                case InstructionKind.Call:
                    var c = iptr->Call;

                    sb.Append(c.ReturnType.ToPrettyString()).Append(':')
                    .Append(c.Arguments).Append(' ');

                    sb.Append(rdata.Functions[rdata.FuncAccessors[(IntPtr)iptr]].Name);
                    sb.Append(c.Function.Type.ToPrettyString());
                    break;
                    #endregion

                case InstructionKind.Break:
                    var b = iptr->Break;

                    sb.Append(b.Type.ToPrettyString()).Append(' ').Append(b.Signal);
                    break;
                }

                sb.AppendLine();
            }

            return(sb.ToString());
        }
Esempio n. 3
0
        public static Statement[] ParseStatements(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction *[] instr = null, Stack <Expression> stack = null, List <Expression> dupTars = null, bool absolute = false)
        {
            var bcv = content.General->BytecodeVersion;

            //! here be dragons

            stack   = stack ?? new Stack <Expression>();
            dupTars = dupTars ?? new List <Expression>();
            instr   = instr ?? code.Instructions;

            if (instr.Length == 0)
            {
                return(EmptyStmtArray);
            }

            var stmts = new List <Statement>();

            var firstI  = code.Instructions[0];
            var baseOff = absolute ? (long)content.RawData.BPtr : (long)firstI;

            //TODO: use locals

            Func <Expression> Pop = () => stack.Count == 0 ? PopExpr : stack.Pop();
            //Func<Expression> Peek = () => stack.Count == 0 ? PopExpr : stack.Peek();
            Func <int, IEnumerable <Expression> > PopMany = i =>
            {
                var ret = new List <Expression>();

                for (int j = 0; j < i; j++)
                {
                    ret.Add(Pop());
                }

                return(ret);
            };
            Action FlushStack = () =>
            {
                var readd = new Stack <Expression>();

                //? not sure if this is a good idea (random 'push'es in the wild) (see TODO)
                stmts.AddRange(stack.PopAll().Where(e =>
                {
                    if (dupTars.Contains(e))
                    {
                        readd.Push(e);
                        return(false);
                    }

                    return(!(e is PopExpression)); // 'push pop' is obviously stupid to emit
                }).Reverse().Select(e =>
                                    e is UnaryOperatorExpression &&
                                    ((UnaryOperatorExpression)e).Operator == UnaryOperator.Duplicate
                        ? (Statement) new DupStatement() : new PushStatement {
                    Expr = e
                }));

                stack.PushRange(readd);
            };
            Action <Statement> AddStmt = s =>
            {
                FlushStack();

                stmts.Add(s);
            };
            Func <VariableType, Expression[]> TryGetIndices = vt =>
            {
                Expression index = null;

                var dimentions = 0;
                if (vt == VariableType.Array)
                {
                    index = Pop();

                    var arrInd = Pop();

                    if ((arrInd is LiteralExpression) && ((LiteralExpression)arrInd).Value is short)
                    {
                        var s = (short)((LiteralExpression)arrInd).Value;

                        switch (s)
                        {
                        case -1:
                            dimentions = 2;
                            break;

                        case -5:
                            dimentions = 1;
                            break;
                        }
                    }

                    if (dimentions == 0)
                    {
                        stack.Push(arrInd);
                        stack.Push(index);

                        index = null;
                    }
                }

                if (index == null)
                {
                    return(null);
                }

                // analyse index for specified dimention
                switch (dimentions)
                {
                case 2:
                    if (index is BinaryOperatorExpression && ((BinaryOperatorExpression)index).Operator == BinaryOperator.Addition)
                    {
                        var boe = (BinaryOperatorExpression)index;

                        var a = boe.Arg1;
                        var b = boe.Arg2;

                        if (a is BinaryOperatorExpression && ((BinaryOperatorExpression)a).Operator == BinaryOperator.Multiplication)
                        {
                            var a_ = (BinaryOperatorExpression)a;
                            var c  = a_.Arg2;

                            if (c is LiteralExpression && ((LiteralExpression)c).ReturnType == DataType.Int32 &&
                                (int /* should be */)((LiteralExpression)c).Value == 32000)
                            {
                                return new[] { a_.Arg1, b }
                            }
                            ;
                        }
                    }
                    break;
                }

                return(new[] { index });
            };

            for (int i = 0; i < instr.Length; i++)
            {
                var ins = instr[i];

                #region stuff
                var pst = (SingleTypeInstruction *)ins;
                var pdt = (DoubleTypeInstruction *)ins;
                var pcl = (CallInstruction *)ins;
                var pps = (PushInstruction *)ins;
                var pse = (SetInstruction *)ins;
                var pbr = (BranchInstruction *)ins;
                var pbk = (BreakInstruction *)ins;

                var st = ins->SingleType;
                var dt = ins->DoubleType;
                var cl = ins->Call;
                var ps = ins->Push;
                var se = ins->Set;

                var t1 = ins->Kind(bcv) == InstructionKind.SingleType ? st.Type
                      : (ins->Kind(bcv) == InstructionKind.DoubleType ? dt.Types.Type1 : 0);
                var t2 = ins->Kind(bcv) == InstructionKind.DoubleType ? dt.Types.Type2
                      : (ins->Kind(bcv) == InstructionKind.SingleType ? st.Type        : 0);
                #endregion

                GeneralOpCode opc;
                switch (opc = ins->OpCode.General(bcv))
                {
                    #region dup, pop
                case GeneralOpCode.Dup:
                    var normal = true;
                    if (i < instr.Length - 1 && instr[i + 1]->OpCode.General(bcv) == GeneralOpCode.Push)
                    {
                        var n = &instr[i + 1]->Push;
                        var t = ((Reference *)&n->ValueRest)->Type;

                        if (t == VariableType.Array && stack.Count > 1)
                        {
                            normal = false;

                            stack.Push(stack.Skip(1).First());     // second item
                            stack.Push(stack.Skip(1).First());     // first  item (original stack top)
                        }
                    }

                    if (!normal)
                    {
                        break;
                    }

                    if (!dupTars.Contains(stack.Peek()))
                    {
                        dupTars.Add(stack.Peek());
                    }

                    //var stackArr = stack.ToArray();
                    //for (i = 0; i <= ins->SingleType.DupExtra; i++)
                    //{
                    //    if (i >= stackArr.Length)
                    //    {
                    //        // .__.
                    //        break;
                    //    }

                    var elem = stack.Peek();        //stackArr[stackArr.Length - i - 1];

                    if (elem.WalkExprTree(e => e is CallExpression).Any(Utils.Identity))
                    {
                        stack.Push(new UnaryOperatorExpression
                        {
                            Input        = elem,
                            Operator     = UnaryOperator.Duplicate,
                            OriginalType = elem.ReturnType,
                            ReturnType   = st.Type
                        });
                    }
                    else
                    {
                        stack.Push(elem);
                    }
                    //}
                    break;

                case GeneralOpCode.Pop:
                    if (stack.Count > 0 && stack.Peek() is CallExpression)
                    {
                        AddStmt(new CallStatement {
                            Call = (CallExpression)stack.Pop()
                        });
                    }
                    else
                    {
                        AddStmt(new PopStatement());
                    }
                    break;

                    #endregion
                    #region br etc
                //TODO: use actual '(with obj ...)' syntax
                //! it might mess with the CFG structure
                case GeneralOpCode.PushEnv:
                case GeneralOpCode.PopEnv:
                case GeneralOpCode.Brt:
                case GeneralOpCode.Brf:
                case GeneralOpCode.Br:
                {
                    var a = pbr->Offset.UValue * 4;
                    if ((a & 0xFF000000) != 0)
                    {
                        a &= 0x00FFFFFF;
                        a -= 0x01000000;
                    }

                    var tar = (AnyInstruction *)((long)ins + unchecked ((int)a));
                    var off = (long)ins - baseOff + unchecked ((int)a);

                    switch (opc)
                    {
                    case GeneralOpCode.PushEnv:
                        AddStmt(new PushEnvStatement
                            {
                                Target       = tar,
                                TargetOffset = off,
                                Parent       = stack.Pop()
                            });
                        break;

                    case GeneralOpCode.PopEnv:
                        AddStmt(new PopEnvStatement
                            {
                                Target       = tar,
                                TargetOffset = off
                            });
                        break;

                    case GeneralOpCode.Brt:
                    case GeneralOpCode.Brf:
                    case GeneralOpCode.Br:
                        AddStmt(new BranchStatement
                            {
                                Type         = pbr->Type(bcv),
                                Conditional  = pbr->Type(bcv) == BranchType.Unconditional ? null : Pop(),
                                Target       = tar,
                                TargetOffset = off
                            });
                        break;
                    }
                }
                break;

                    #endregion
                    #region break, ret, exit
                case GeneralOpCode.Break:
                    stack.Push(new AssertExpression
                    {
                        ControlValue = pbk->Signal,
                        ReturnType   = pbk->Type,
                        Expr         = Pop()
                    });
                    break;

                case GeneralOpCode.Ret:
                    AddStmt(new ReturnStatement
                    {
                        ReturnType = pst->Type,
                        RetValue   = Pop()
                    });
                    break;

                case GeneralOpCode.Exit:
                    AddStmt(new ExitStatement());
                    break;

                    #endregion
                    #region set
                case GeneralOpCode.Set:
                    if (se.IsMagic)
                    {
                        AddStmt(new MagicSetStatement
                        {
                            OriginalType = se.Types.Type1,
                            ReturnType   = se.Types.Type2
                        });

                        break;
                    }

                    var ind = TryGetIndices(se.DestVar.Type);     // call before Value's pop
                    AddStmt(new SetStatement
                    {
                        OriginalType = se.Types.Type1,
                        ReturnType   = se.Types.Type2,
                        Type         = se.DestVar.Type,
                        OwnerType    = se.Instance,
                        OwnerName    = se.Instance > InstanceType.StackTopOrGlobal ? SectionReader.GetObjectInfo(content, (uint)se.Instance, true).Name : null,
                        Target       = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]],
                        Value        = Pop(),
                        ArrayIndices = ind ?? TryGetIndices(se.DestVar.Type)
                    });
                    break;

                    #endregion
                default:
                    switch (ins->ExprType(bcv))
                    {
                        #region variable
                    case ExpressionType.Variable:
                        var vt = ((Reference *)&pps->ValueRest)->Type;

                        stack.Push(/*vt == VariableType.StackTop &&*/ (InstanceType)ps.Value == InstanceType.StackTopOrGlobal
                                    ? new MemberExpression
                        {
                            Owner        = Pop(),
                            ReturnType   = ps.Type,
                            Type         = vt,
                            OwnerType    = (InstanceType)ps.Value,
                            OwnerName    = se.Instance > InstanceType.StackTopOrGlobal ? SectionReader.GetObjectInfo(content, (uint)se.Instance, true).Name : null,
                            Variable     = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]],
                            ArrayIndices = TryGetIndices(vt)
                        }
                                    : new VariableExpression
                        {
                            ReturnType   = ps.Type,
                            Type         = vt,
                            OwnerType    = (InstanceType)ps.Value,
                            Variable     = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]],
                            ArrayIndices = TryGetIndices(vt)
                        });
                        break;

                        #endregion
                        #region literal
                    case ExpressionType.Literal:
                        object v    = null;
                        var    rest = &pps->ValueRest;

                        #region get value
                        switch (ps.Type)
                        {
                        case DataType.Int16:
                            v = ps.Value;
                            break;

                        case DataType.Boolean:
                            v = ((DwordBool *)rest)->IsTrue();
                            break;

                        case DataType.Double:
                            v = *(double *)rest;
                            break;

                        case DataType.Single:
                            v = *(float *)rest;
                            break;

                        case DataType.Int32:
                            v = *(int *)rest;
                            break;

                        case DataType.Int64:
                            v = *(long *)rest;
                            break;

                        case DataType.String:
                            v = SectionReader.GetStringInfo(content, (uint)ps.ValueRest);
                            break;
                        }
                        #endregion

                        stack.Push(new LiteralExpression
                        {
                            ReturnType = ps.Type,
                            Value      = v
                        });
                        break;

                        #endregion
                        #region call
                    case ExpressionType.Call:
                        stack.Push(new CallExpression
                        {
                            ReturnType = cl.ReturnType,
                            Type       = cl.Function.Type,
                            Function   = rdata.Functions[rdata.FuncAccessors[(IntPtr)ins]],
                            Arguments  = PopMany(cl.Arguments).Reverse().ToArray()
                        });
                        break;

                        #endregion
                        #region binaryop
                    case ExpressionType.BinaryOp:
                        var a1 = Pop();
                        var a2 = Pop();

                        stack.Push(new BinaryOperatorExpression
                        {
                            OriginalType = t1,
                            ReturnType   = t2,
                            Arg1         = a2,
                            Arg2         = a1,
                            Operator     = ins->BinaryOp(bcv)
                        });
                        break;

                        #endregion
                        #region unaryop
                    case ExpressionType.UnaryOp:
                        stack.Push(new UnaryOperatorExpression
                        {
                            OriginalType = t1,
                            ReturnType   = t2,
                            Input        = Pop(),
                            Operator     = ins->UnaryOp(bcv)
                        });
                        break;
                        #endregion
                    }
                    break;
                }
            }

            FlushStack();

            return(stmts.ToArray());
        }