private string decompileOperation(ref BodyParseState state, Instruction instr, out bool processed)
        {
            var decompilers = new DOperationDecompiler[] {
                decompileOperation_BaseTypes,
                decompileOperation_Call,
                decompileOperation_Constants,
                decompileOperation_References,
                decompileOperation_StringTypes,
                decompileOperation_Unknown
            };


            if (processed = (instr.Op == Opcode.NoOperation))
            {
                return("");
            }

            foreach (var decom in decompilers)
            {
                if (processed = decom(ref state, instr, out string operation))
                {
                    Console.ForegroundColor = ConsoleColor.DarkGreen;
                    Console.WriteLine("decompileOperation: " + $"${state.currentIdx:X4}, {(byte)instr.Op:X2} - {instr.Op.ToString()}");
                    Console.ForegroundColor = ConsoleColor.Gray;

                    return(operation);
                }
            }

            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.WriteLine("decompileOperation: " + $"${state.currentIdx:X4}, {(byte)instr.Op:X2} - {instr.Op.ToString()}");
            Console.ForegroundColor = ConsoleColor.Gray;
            return($"<${state.currentIdx:X4}, UNPARSED, {(byte)instr.Op:X2} - {instr.Op.ToString()}>");
        }
Beispiel #2
0
        private bool decompileOperation_Constants(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.LoadConstantOne:
                operation = "1";
                state.currentIdx++;
                return(true);

            case Opcode.LoadConstantZero:
                operation = "0";
                state.currentIdx++;
                return(true);

            case Opcode.LoadConstantTrue:
                operation = "true";
                state.currentIdx++;
                return(true);

            case Opcode.LoadConstantFalse:
                operation = "false";
                state.currentIdx++;
                return(true);
            }
            return(false);
        }
        private List <string> decompileBody(FunctionDefinition definition)
        {
            BodyParseState state = new BodyParseState()
            {
                instructions = definition.Body,
                fnDefinition = definition
            };

            List <string>      Lines        = new List <string>();
            List <Instruction> instructions = definition.Body;

            bool parse = true;

            while (state.currentIdx < instructions.Count && parse)
            {
                var instr = instructions[state.currentIdx];

                if (instr.Op == Opcode.NoOperation)
                {
                    state.currentIdx++;
                    continue;
                }

                Lines.Add(decompileOperation(ref state, instr, out bool wasProcessed) + ";");

                if (!wasProcessed)
                {
                    state.currentIdx++;
                }
            }

            return(Lines); // string.Join("\r\n", Lines);
        }
Beispiel #4
0
        /// <summary>
        /// This function does not get called, its just here to ensure I dont miss any op codes.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="instr"></param>
        /// <param name="operation"></param>
        /// <returns></returns>
        private bool decompileOperation_Unknown_TODO(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.LoadTweakDBId:
                // TODO: this - 1998-01-05
                break;

            case Opcode.LoadResource:
                // TODO: this - 1998-01-05
                break;

            case Opcode.LoadConstantTrue:
                // TODO: this - 1998-01-05
                break;

            case Opcode.LoadConstantFalse:
                // TODO: this - 1998-01-05
                break;


            case Opcode.Switch:
                // TODO: this - 1998-01-05
                break;

            case Opcode.SwitchCase:
                // TODO: this - 1998-01-05
                break;

            case Opcode.SwitchDefault:
                // TODO: this - 1998-01-05
                break;

            case Opcode.Construct: // new CLASS(Arguments)
                // TODO: this - 1998-01-05
                break;



            case Opcode.CompareEqual:
                // TODO: this - 1998-01-05
                break;

            case Opcode.CompareNotEqual:
                // TODO: this - 1998-01-05
                break;
            }

            return(false);
        }
Beispiel #5
0
        private bool decompileOperation_Unknown(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch ((byte)instr.Op)
            {
            case 0x51: {
                int    currentIdx    = state.currentIdx;
                string nextInstrDecl = decompileOperation(ref state, state.instructions[++state.currentIdx], out bool processedOpCode);

                operation = $"<$UNK_OP_x51u ({currentIdx})>(${nextInstrDecl})";
                return(true);
            }
            }

            return(false);
        }
Beispiel #6
0
        private bool decompileOperation_StringTypes(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.LoadName:
                break;

            case Opcode.LoadEnumeral:
                break;

            case Opcode.LoadString:
                break;
            }

            return(false);
        }
Beispiel #7
0
        private bool decompileOperation_BaseTypes(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            // Note I've only tested LoadFloat, I assume all the base type primitives are the same.
            case Opcode.LoadInt8:
            case Opcode.LoadInt16:
            case Opcode.LoadInt32:
            case Opcode.LoadInt64:
            case Opcode.LoadUint8:
            case Opcode.LoadUint16:
            case Opcode.LoadUint32:
            case Opcode.LoadDouble:
            case Opcode.LoadUint64:
            case Opcode.LoadString:
            case Opcode.LoadFloat: {
                operation = instr.Argument.ToString();
                state.currentIdx++;
                return(true);
            }

            // I assume this will be a string, I'll get around to implementing it when I get a function using it.
            case Opcode.LoadName:
                // TODO: this - 1998-01-05
                // Do something like this:
                //   &{ObjectHere}
                // I believe that LoadName creates a reference to a class.
                break;

            case Opcode.LoadEnumeral:
                // TODO: this - 1998-01-05
                break;
            }

            return(false);
        }
Beispiel #8
0
        private bool decompileOperation_References(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.StoreRef: {
                state.currentIdx++;

                bool processedOpCode;
                var  value = decompileOperation(ref state, state.instructions[state.currentIdx], out processedOpCode);
                if (!processedOpCode)
                {
                    state.currentIdx++;                       // if the opcode is not implemented make sure we still skip it.
                }
                var assignment = decompileOperation(ref state, state.instructions[state.currentIdx], out processedOpCode);
                if (!processedOpCode)
                {
                    state.currentIdx++;                       // if the opcode is not implemented make sure we still skip it.
                }
                operation = $"{value} = {assignment}";
                return(true);
            }

            case Opcode.RefLocal: {
                // Get the var name through the arguments
                var arguments = ((ValueTuple <Gibbed.RED4.ScriptFormats.Definitions.LocalDefinition>)instr.Argument).Item1;
                operation = arguments.Name;
                state.currentIdx++;
                return(true);
            }

            case Opcode.RefProperty: {
                // TODO: this - 1998-01-05
                break;
            }

            case Opcode.LoadParameter: {
                // Get the var name through the arguments
                var arguments = ((ValueTuple <Gibbed.RED4.ScriptFormats.Definitions.ParameterDefinition>)instr.Argument).Item1;
                operation = $"{arguments.Name}";
                state.currentIdx++;
                return(true);
            }

            case Opcode.LoadProperty: {
                var    arguments     = ((ValueTuple <Gibbed.RED4.ScriptFormats.Definitions.PropertyDefinition>)instr.Argument).Item1;
                string nextInstrDecl = decompileOperation(ref state, state.instructions[++state.currentIdx], out bool processedOpCode);

                if (!processedOpCode)     // if the opcode is not implemented make sure we still skip it.
                {
                    state.currentIdx++;
                }

                operation = nextInstrDecl + "." + arguments.Name;
                return(true);
            }
            }

            return(false);
        }
Beispiel #9
0
        /// <summary>
        /// A function to let intellisense autofill the misisng opcodes (when ever they are added or modified)
        /// this function is also used to keep track of what is implemented.
        ///   Later this will be moved into its own file, for now its here.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="instr"></param>
        /// <param name="operation"></param>
        /// <returns></returns>
        private bool decompileOperation_Template(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.NoOperation: break;                 //  -

            case Opcode.LoadConstantOne: break;             //  -

            case Opcode.LoadConstantZero: break;            //  -

            case Opcode.LoadInt8: break;                    //  -

            case Opcode.LoadInt16: break;                   //  -

            case Opcode.LoadInt32: break;                   //  -

            case Opcode.LoadInt64: break;                   //  -

            case Opcode.LoadUint8: break;                   //  -

            case Opcode.LoadUint16: break;                  //  -

            case Opcode.LoadUint32: break;                  //  -

            case Opcode.LoadUint64: break;                  //  -

            case Opcode.LoadFloat: break;                   //  -

            case Opcode.LoadDouble: break;                  //  -

            case Opcode.LoadName: break;                    //

            case Opcode.LoadEnumeral: break;                //

            case Opcode.LoadString: break;                  //

            case Opcode.LoadTweakDBId: break;               //

            case Opcode.LoadResource: break;                //

            case Opcode.LoadConstantTrue: break;            //  -

            case Opcode.LoadConstantFalse: break;           //  -

            case Opcode.StoreRef: break;                    //

            case Opcode.RefLocal: break;                    //

            case Opcode.LoadParameter: break;               //

            case Opcode.RefProperty: break;                 //

            case Opcode.Switch: break;                      //  NOT

            case Opcode.SwitchCase: break;                  //  NOT

            case Opcode.SwitchDefault: break;               //  NOT

            case Opcode.Jump: break;                        //  NOT

            case Opcode.JumpFalse: break;                   //  NOT

            case Opcode.Construct: break;                   //  NOT

            case Opcode.Call: break;                        //  PARTIALLY

            case Opcode.CallName: break;                    //  NOT

            case Opcode.EndCall: break;                     //  NOT

            case Opcode.ReturnWithValue: break;             //

            case Opcode.LoadProperty: break;                //

            case Opcode.AsObject: break;                    //

            case Opcode.CompareEqual: break;                //  NOT

            case Opcode.CompareNotEqual: break;             //  NOT
            }

            return(false);
        }
Beispiel #10
0
        private bool decompileOperation_Call(ref BodyParseState state, Instruction instr, out string operation)
        {
            operation = "";

            switch (instr.Op)
            {
            case Opcode.Call: {
                (_, _, var fn) = ((short, ushort, FunctionDefinition))instr.Argument;

                if (fn.Parameters == null || fn.Parameters.Count == 0)
                {
                    operation = $"{fn.Name}()";
                    state.currentIdx++;

                    if (state.instructions[state.currentIdx].Op == Opcode.EndCall)
                    {
                        state.currentIdx++;
                    }

                    return(true);
                }

                // TODO: this - 1998-01-05
                break;
            }

            case Opcode.AsObject: {
                // I am expecting the following pattern
                //    AsObject, LoadParamter, Call, ..., EndCall.
                // This is a basic implementation and is expected to be overriden with a better solution.
                //  this is a hard coded solution for the signature above.
                var idx = state.currentIdx;
                if (state.instructions[idx + 1].Op == Opcode.LoadParameter &&
                    state.instructions[idx + 2].Op == Opcode.Call)
                {
                    bool processedOpCode = false;
                    state.currentIdx = idx + 1;     // Get the parameter

                    var parameter = decompileOperation(ref state, state.instructions[idx + 1], out processedOpCode);
                    if (!processedOpCode)
                    {
                        state.currentIdx++;                       // if the opcode is not implemented make sure we still skip it.
                    }
                    var callFn = decompileOperation(ref state, state.instructions[state.currentIdx], out processedOpCode);
                    if (!processedOpCode)
                    {
                        state.currentIdx++;                       // if the opcode is not implemented make sure we still skip it.
                    }
                    operation = $"{parameter}.{callFn}";
                    return(true);
                }

                break;
            }

            case Opcode.CallName:
                // TODO: this - 1998-01-05
                break;

            case Opcode.EndCall:
                // TODO: this - 1998-01-05
                break;

            case Opcode.Jump:
                // TODO: this - 1998-01-05
                break;

            case Opcode.JumpFalse:
                // TODO: this - 1998-01-05
                break;

            case Opcode.ReturnWithValue: {
                string nextInstrDecl = decompileOperation(ref state, state.instructions[++state.currentIdx], out bool processedOpCode);

                if (!processedOpCode)     // if the opcode is not implemented make sure we still skip it.
                {
                    state.currentIdx++;
                }

                operation = $"return {nextInstrDecl}";
                state.currentIdx++;
                return(true);
            }
            }
            return(false);
        }