public bool VisitNode(ExpressionOnlyStatement node) { // expression; Write(""); node.Value.AcceptVisitor(this); return(true); }
public CodeBody Decompile() { // Skip native funcs var Func = DataContainer as ME3Function; if (Func != null && Func.FunctionFlags.HasFlag(FunctionFlags.Native)) { var comment = new ExpressionOnlyStatement(null, null, new SymbolReference(null, null, null, "// Native function")); return(new CodeBody(new List <Statement>() { comment }, null, null)); } Position = 0; _totalPadding = 0; CurrentScope = new Stack <int>(); var statements = new List <Statement>(); StatementLocations = new Dictionary <UInt16, Statement>(); StartPositions = new Stack <UInt16>(); Scopes = new List <List <Statement> >(); LabelTable = new List <LabelTableEntry>(); ForEachScopes = new Stack <UInt16>(); DecompileDefaultParameterValues(statements); Scopes.Add(statements); CurrentScope.Push(Scopes.Count - 1); while (Position < Size && !CurrentIs(StandardByteCodes.EndOfScript)) { var current = DecompileStatement(); if (current == null && CurrentByte == (byte)StandardByteCodes.EndOfScript) { break; // Natural end after label table, no error } if (current == null) { break; // TODO: ERROR! } statements.Add(current); } CurrentScope.Pop();; AddStateLabels(); return(new CodeBody(statements, null, null)); }
private void DecompileDefaultParameterValues(List <Statement> statements) { OptionalParams = new Stack <FunctionParameter>(); var func = DataContainer as ME3Function; if (func != null) // Gets all optional params for default value parsing { for (int n = 0; n < Parameters.Count; n++) { if (func.Parameters[n].PropertyFlags.HasFlag(PropertyFlags.OptionalParm)) { OptionalParams.Push(Parameters[n]); } } } while (CurrentByte == (byte)StandardByteCodes.DefaultParmValue || CurrentByte == (byte)StandardByteCodes.Nothing) { StartPositions.Push((UInt16)Position); var token = PopByte(); if (token == (byte)StandardByteCodes.DefaultParmValue) // default value assigned { ReadInt16(); //MemSize of value var value = DecompileExpression(); PopByte(); // end of value var builder = new CodeBuilderVisitor(); // what a wonderful hack, TODO. value.AcceptVisitor(builder); if (OptionalParams.Count != 0) { var parm = OptionalParams.Pop(); parm.Variables.First().Name += " = " + builder.GetCodeString(); StartPositions.Pop(); } else { // TODO: weird, research how to deal with this var comment = new SymbolReference(null, null, null, "// Orphaned Default Parm: " + builder.GetCodeString()); var statement = new ExpressionOnlyStatement(null, null, comment); StatementLocations.Add(StartPositions.Pop(), statement); statements.Add(statement); } } } }
public Statement DecompileStatement() { StartPositions.Push((UInt16)Position); var token = CurrentByte; switch (token) { // return [expression]; case (byte)StandardByteCodes.Return: return(DecompileReturn()); // switch (expression) case (byte)StandardByteCodes.Switch: return(DecompileSwitch()); // case expression : case (byte)StandardByteCodes.Case: return(DecompileCase()); // if (expression) // while / for / do until case (byte)StandardByteCodes.JumpIfNot: return(DecompileConditionalJump()); // continue case (byte)StandardByteCodes.Jump: // TODO: UDK seems to compile this from break when inside ForEach, handle? return(DecompileJump()); // continue (iterator) case (byte)StandardByteCodes.IteratorNext: PopByte(); // pop iteratornext token return(DecompileJump()); // break; case (byte)StandardByteCodes.IteratorPop: return(DecompileIteratorPop()); // stop; case (byte)StandardByteCodes.Stop: PopByte(); var stopStatement = new StopStatement(null, null); StatementLocations.Add(StartPositions.Pop(), stopStatement); return(stopStatement); // Goto label case (byte)StandardByteCodes.GotoLabel: PopByte(); var labelExpr = DecompileExpression(); var func = new SymbolReference(null, null, null, "goto"); var call = new FunctionCall(func, new List <Expression>() { labelExpr }, null, null); var gotoLabel = new ExpressionOnlyStatement(null, null, call); StatementLocations.Add(StartPositions.Pop(), gotoLabel); return(gotoLabel); // assignable expression = expression; case (byte)StandardByteCodes.Let: case (byte)StandardByteCodes.LetBool: case (byte)StandardByteCodes.LetDelegate: return(DecompileAssign()); // [skip x bytes] case (byte)StandardByteCodes.Skip: // TODO: this should never occur as statement, possibly remove? PopByte(); ReadUInt16(); StartPositions.Pop(); return(DecompileStatement()); case (byte)StandardByteCodes.Nothing: PopByte(); StartPositions.Pop(); return(DecompileStatement()); // TODO, should probably have a nothing expression or statement, this is ugly // foreach IteratorFunction(...) case (byte)StandardByteCodes.Iterator: return(DecompileForEach()); // foreach arrayName(valuevariable[, indexvariable]) case (byte)StandardByteCodes.DynArrayIterator: return(DecompileForEach(isDynArray: true)); case (byte)StandardByteCodes.LabelTable: DecompileLabelTable(); StartPositions.Pop(); return(DecompileStatement()); #region unsupported case (byte)StandardByteCodes.OptIfLocal: // TODO: verify, handle syntax return(DecompileConditionalJump(isOpt: true)); case (byte)StandardByteCodes.OptIfInstance: // TODO: verify, handle syntax return(DecompileConditionalJump(isOpt: true)); #endregion default: var expr = DecompileExpression(); if (expr != null) { var statement = new ExpressionOnlyStatement(null, null, expr); StatementLocations.Add(StartPositions.Pop(), statement); return(statement); } // ERROR! break; } return(null); }
public CodeBody Decompile() { // Skip native funcs if (DataContainer is UFunction Func && Func.FunctionFlags.HasFlag(FunctionFlags.Native)) { var comment = new ExpressionOnlyStatement(new SymbolReference(null, "// Native function")); return(new CodeBody(new List <Statement> { comment })); } if (ContainingClass.Export.ObjectName == "SFXGalaxyMapObject") { //these functions are broken and cannot be decompiled. instead of trying, we construct a simpler, functionally identical version if (DataContainer.Export.ObjectName == "GetEditorLabel") { return(new CodeBody(new List <Statement> { new ReturnStatement(new SymbolReference(null, "sLabel")) })); } if (DataContainer.Export.ObjectName == "InitializeAppearance") { return(new CodeBody(new List <Statement> { new ReturnStatement() })); } } Position = 0; _totalPadding = 0; CurrentScope = new Stack <int>(); var statements = new List <Statement>(); StatementLocations = new Dictionary <ushort, Statement>(); StartPositions = new Stack <ushort>(); Scopes = new List <List <Statement> >(); LabelTable = new List <LabelTableEntry>(); ForEachScopes = new Stack <ushort>(); DecompileDefaultParameterValues(statements); Scopes.Add(statements); CurrentScope.Push(Scopes.Count - 1); while (Position < Size && !CurrentIs(OpCodes.EndOfScript)) { var current = DecompileStatement(); if (current == null && PeekByte == (byte)OpCodes.EndOfScript) { break; // Natural end after label table, no error } if (current == null) { //as well as being eye-catching in generated code, this is totally invalid unrealscript and will cause compilation errors! statements.Clear(); statements.Add(new ExpressionOnlyStatement(new SymbolReference(null, "**************************"))); statements.Add(new ExpressionOnlyStatement(new SymbolReference(null, "* *"))); statements.Add(new ExpressionOnlyStatement(new SymbolReference(null, "* DECOMPILATION ERROR! *"))); statements.Add(new ExpressionOnlyStatement(new SymbolReference(null, "* *"))); statements.Add(new ExpressionOnlyStatement(new SymbolReference(null, "**************************"))); return(new CodeBody(statements)); } statements.Add(current); } CurrentScope.Pop(); AddStateLabels(); Dictionary <Statement, ushort> LocationStatements = StatementLocations.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); DecompileLoopsAndIfs(statements, LocationStatements); //a void return at the end of a function is a bytecode implementation detail, get rid of it. //This will also get rid of returnnothings, so loop to make sure we get both while (statements.Count > 0 && statements.Last() is ReturnStatement ret && ret.Value is null) { statements.RemoveAt(statements.Count - 1); } return(new CodeBody(statements)); }
public bool VisitNode(ExpressionOnlyStatement node) { throw new NotImplementedException(); }
public Statement DecompileStatement(ushort?startPosition = null) { StartPositions.Push(startPosition ?? (ushort)Position); var token = PeekByte; switch (token) { // return [expression]; case (byte)OpCodes.Return: return(DecompileReturn()); // switch (expression) case (byte)OpCodes.Switch: return(DecompileSwitch()); // case expression : case (byte)OpCodes.Case: return(DecompileCase()); // if (expression) // while / for / do until case (byte)OpCodes.JumpIfNot: { PopByte(); var jump = new IfNotJump(ReadUInt16(), DecompileExpression(), Position - StartPositions.Peek()); StatementLocations.Add(StartPositions.Pop(), jump); return(jump); } case (byte)OpCodes.Jump: { PopByte(); ushort jumpLoc = ReadUInt16(); if (ForEachScopes.Count > 0 && jumpLoc == ForEachScopes.Peek()) { var brk = new BreakStatement(); StatementLocations.Add(StartPositions.Pop(), brk); return(brk); } var jump = new UnconditionalJump(jumpLoc); StatementLocations.Add(StartPositions.Pop(), jump); return(jump); } // continue (iterator) case (byte)OpCodes.IteratorNext: { PopByte(); var itNext = new ContinueStatement(); StatementLocations.Add(StartPositions.Pop(), itNext); if (PopByte() != (byte)OpCodes.Jump) { return(null); //there should always be a jump after an iteratornext that's not at the end of the loop } //skip the jump address PopByte(); PopByte(); return(itNext); } // break; case (byte)OpCodes.IteratorPop: { PopByte(); return(DecompileStatement(StartPositions.Pop())); } // stop; case (byte)OpCodes.Stop: PopByte(); var stopStatement = new StopStatement(null, null); StatementLocations.Add(StartPositions.Pop(), stopStatement); return(stopStatement); // Goto label case (byte)OpCodes.GotoLabel: PopByte(); var gotoLabel = new StateGoto(DecompileExpression()); StatementLocations.Add(StartPositions.Pop(), gotoLabel); return(gotoLabel); // assignable expression = expression; case (byte)OpCodes.Let: case (byte)OpCodes.LetBool: case (byte)OpCodes.LetDelegate: return(DecompileAssign()); // [skip x bytes] case (byte)OpCodes.Skip: // TODO: this should never occur as statement, possibly remove? PopByte(); ReadUInt16(); StartPositions.Pop(); return(DecompileStatement()); case (byte)OpCodes.Nothing: PopByte(); StartPositions.Pop(); return(DecompileStatement()); // TODO, should probably have a nothing expression or statement, this is ugly // foreach IteratorFunction(...) case (byte)OpCodes.Iterator: return(DecompileForEach()); // foreach arrayName(valuevariable[, indexvariable]) case (byte)OpCodes.DynArrayIterator: return(DecompileForEach(true)); case (byte)OpCodes.LabelTable: DecompileLabelTable(); StartPositions.Pop(); return(DecompileStatement()); case (byte)OpCodes.OptIfLocal: case (byte)OpCodes.OptIfInstance: { PopByte(); IEntry obj = ReadObject(); var condition = new SymbolReference(null, obj.ObjectName.Instanced); bool not = Convert.ToBoolean(ReadByte()); if (obj.ClassName == "BoolProperty") { var ifJump = new IfNotJump( ReadUInt16(), not ? (Expression)condition : new PreOpReference(new PreOpDeclaration("!", SymbolTable.BoolType, 0, null), condition), Position - StartPositions.Peek()); StatementLocations.Add(StartPositions.Pop(), ifJump); return(ifJump); } var nullJump = new NullJump(ReadUInt16(), condition, not) { SizeOfExpression = Position - StartPositions.Peek() }; StatementLocations.Add(StartPositions.Pop(), nullJump); return(nullJump); } case (byte)OpCodes.FilterEditorOnly: { PopByte(); var edFilter = new InEditorJump(ReadUInt16()); StatementLocations.Add(StartPositions.Pop(), edFilter); return(edFilter); } case (byte)OpCodes.EatReturnValue: PopByte(); ReadObject(); return(DecompileStatement(StartPositions.Pop())); case (byte)OpCodes.Assert: return(DecompileAssert()); default: var expr = DecompileExpression(); if (expr != null) { var statement = new ExpressionOnlyStatement(expr, null, null); StatementLocations.Add(StartPositions.Pop(), statement); return(statement); } // ERROR! return(null); } }