public SwitchStatement DecompileSwitch() { PopByte(); var objIndex = ReadObject(); var unknByte = ReadByte(); var expr = DecompileExpression(); var scopeStatements = new List <Statement>(); UInt16 endOffset = 0xFFFF; // set it at max to begin with, so we can begin looping Scopes.Add(scopeStatements); CurrentScope.Push(Scopes.Count - 1); while (Position < endOffset && Position < Size) { if (CurrentIs(StandardByteCodes.Jump)) // break detected, save the endOffset { // executes for all occurences, to handle them all. StartPositions.Push((UInt16)Position); PopByte(); endOffset = ReadUInt16(); var breakStatement = new BreakStatement(null, null); StatementLocations.Add(StartPositions.Pop(), breakStatement); scopeStatements.Add(breakStatement); continue; } var current = DecompileStatement(); if (current == null) { return(null); // ERROR ? } scopeStatements.Add(current); if (current is DefaultStatement && endOffset == 0xFFFF) { break; // If no break was detected, we end the switch rather than include the rest of ALL code in the default. } } CurrentScope.Pop(); var statement = new SwitchStatement(expr, new CodeBody(scopeStatements, null, null), null, null); StatementLocations.Add(StartPositions.Pop(), statement); return(statement); }
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); } }
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 Expression DecompileExpression() { StartPositions.Push((ushort)Position); var token = PeekByte; if (token >= 0x80) // native table { return(DecompileNativeFunction(PopByte())); } if (token >= 0x71) // extended native table, 0x70 is unused { var higher = ReadByte() & 0x0F; var lower = ReadByte(); int index = (higher << 8) + lower; return(DecompileNativeFunction((ushort)index)); } switch (token) { // variable lookups case (byte)OpCodes.DefaultVariable: PopByte(); return(DecompileDefaultReference()); case (byte)OpCodes.LocalVariable: case (byte)OpCodes.InstanceVariable: case (byte)OpCodes.LocalOutVariable: case (byte)OpCodes.LocalFloatVariable: case (byte)OpCodes.LocalIntVariable: case (byte)OpCodes.LocalByteVariable: case (byte)OpCodes.LocalObjectVariable: case (byte)OpCodes.InstanceFloatVariable: case (byte)OpCodes.InstanceIntVariable: case (byte)OpCodes.InstanceByteVariable: case (byte)OpCodes.InstanceObjectVariable: PopByte(); return(DecompileObjectLookup()); case (byte)OpCodes.Nothing: PopByte(); StartPositions.Pop(); return(DecompileExpression()); // TODO, solve this better? What about variable assignments etc? // array[index] case (byte)OpCodes.DynArrayElement: //TODO: possibly separate this case (byte)OpCodes.ArrayElement: return(DecompileArrayRef()); // new (...) class (.) case (byte)OpCodes.New: // TODO: support in AST return(DecompileNew()); // (class|object|struct).member case (byte)OpCodes.ClassContext: // TODO: support in AST return(DecompileContext(isClass: true)); case (byte)OpCodes.Context: return(DecompileContext()); case (byte)OpCodes.StructMember: return(DecompileStructMember()); // unknown, interface case (byte)OpCodes.InterfaceContext: PopByte(); StartPositions.Pop(); return(DecompileExpression()); // TODO: research this // class<Name>(Obj) case (byte)OpCodes.Metacast: return(DecompileCast(meta: true)); // TODO: ugly hack to make this qork quickly // Self case (byte)OpCodes.Self: PopByte(); StartPositions.Pop(); return(new SymbolReference(null, SELF, null, null)); // TODO: solve better // Skip(numBytes) case (byte)OpCodes.Skip: // handles skips in operator arguments PopByte(); ReadInt16(); // MemSize StartPositions.Pop(); return(DecompileExpression()); // Function calls case (byte)OpCodes.FinalFunction: return(DecompileFunctionCall()); case (byte)OpCodes.GlobalFunction: return(DecompileFunctionCall(byName: true, global: true)); // TODO: is this correct? case (byte)OpCodes.VirtualFunction: return(DecompileFunctionCall(byName: true)); // int, eg. 5 case (byte)OpCodes.IntConst: return(DecompileIntConst()); // float, eg. 5.5 case (byte)OpCodes.FloatConst: return(DecompileFloatConst()); // "string" case (byte)OpCodes.StringConst: return(DecompileStringConst()); // Object case (byte)OpCodes.ObjectConst: return(DecompileObjectConst()); // 'name' case (byte)OpCodes.NameConst: return(DecompileNameConst()); // rot(1, 2, 3) case (byte)OpCodes.RotationConst: return(DecompileRotationConst()); //TODO: properly // vect(1.0, 2.0, 3.0) case (byte)OpCodes.VectorConst: return(DecompileVectorConst()); // byte, eg. 0B case (byte)OpCodes.ByteConst: return(DecompileByteConst(BYTE)); case (byte)OpCodes.IntConstByte: return(DecompileByteConst(INT)); // 0 case (byte)OpCodes.IntZero: return(DecompileIntConstVal(0)); // 1 case (byte)OpCodes.IntOne: return(DecompileIntConstVal(1)); // true case (byte)OpCodes.True: return(DecompileBoolConstVal(true)); // false case (byte)OpCodes.False: return(DecompileBoolConstVal(false)); // None (object literal) case (byte)OpCodes.NoObject: case (byte)OpCodes.EmptyDelegate: PopByte(); StartPositions.Pop(); return(new NoneLiteral()); // (bool expression) case (byte)OpCodes.BoolVariable: return(DecompileBoolExprValue()); // ClassName(Obj) case (byte)OpCodes.InterfaceCast: case (byte)OpCodes.DynamicCast: return(DecompileCast()); // struct == struct case (byte)OpCodes.StructCmpEq: return(DecompileStructComparison(true)); // struct != struct case (byte)OpCodes.StructCmpNe: return(DecompileStructComparison(false)); // delegate == delegate case (byte)OpCodes.EqualEqual_DelDel: return(DecompileDelegateComparison(true)); // delegate != delegate case (byte)OpCodes.NotEqual_DelDel: return(DecompileDelegateComparison(false)); // delegate == Function case (byte)OpCodes.EqualEqual_DelFunc: return(DecompileDelegateComparison(true)); // delegate != Function case (byte)OpCodes.NotEqual_DelFunc: return(DecompileDelegateComparison(false)); // primitiveType(expr) case (byte)OpCodes.PrimitiveCast: return(DecompilePrimitiveCast()); // (bool expr) ? expr : expr case (byte)OpCodes.Conditional: return(DecompileConditionalExpression()); // end of script case (byte)OpCodes.EndOfScript: return(null); // ERROR? // (empty function param) case (byte)OpCodes.EmptyParmValue: PopByte(); StartPositions.Pop(); return(new SymbolReference(null, "")); // TODO: solve better // arrayName.Length case (byte)OpCodes.DynArrayLength: return(DecompileDynArrLength()); // arrayName.Find(value) case (byte)OpCodes.DynArrayFind: return(DecompileDynArrayFind()); // arrayName.Find(StructProperty, value) case (byte)OpCodes.DynArrayFindStruct: return(DecompileDynArrayFindStructMember()); // arrayName.Insert(Index, Count) case (byte)OpCodes.DynArrayInsert: return(DecompileDynArrayInsert()); // arrayName.Remove(Index, Count) case (byte)OpCodes.DynArrayRemove: return(DecompileDynArrayRemove()); // arrayName.Add(value) case (byte)OpCodes.DynArrayAdd: return(DecompileDynArrayAdd()); // arrayName.AddItem(value) case (byte)OpCodes.DynArrayAddItem: return(DecompileDynArrayAddItem()); // arrayName.RemoveItem(value) case (byte)OpCodes.DynArrayRemoveItem: return(DecompileDynArrayRemoveItem()); // arrayName.InsertItem(StructProperty, value) case (byte)OpCodes.DynArrayInsertItem: return(DecompileDynArrayInsertItem()); // arrayName.Sort(value) case (byte)OpCodes.DynArraySort: return(DecompileDynArraySort()); // TODO: temporary delegate handling, probably wrong: case (byte)OpCodes.DelegateFunction: return(DecompileDelegateFunction()); case (byte)OpCodes.DelegateProperty: return(DecompileDelegateProperty()); case (byte)OpCodes.NamedFunction: return(DecompileFunctionCall(byName: true, withFuncListIdx: true)); case (byte)OpCodes.StringRefConst: return(DecompileStringRefConst()); /***** * TODO: all of these needs changes, see functions below. * */ #region Unsupported case (byte)OpCodes.NativeParm: // is this even present anywhere? return(DecompileNativeParm()); case (byte)OpCodes.GoW_DefaultValue: return(DecompileGoW_DefaultValue()); case (byte)OpCodes.InstanceDelegate: return(DecompileInstanceDelegate()); #endregion // TODO: 41, debugInfo // TODO: 0x3B - 0x3E native calls default: // ERROR! break; } return(null); }