private List <Instruction> DisassembleFunction(byte[] functionByteCode) { var instructions = new List <Instruction>(); var funcStream = new BinaryReader(new MemoryStream(functionByteCode)); while (funcStream.PeekChar() != -1) { var instruction = new Instruction(); var instructionData = new InstructionData(); instruction.Data = instructionData; instruction.Index = (int)(_bytecode.BaseStream.Position - functionByteCode.Length + funcStream.BaseStream.Position); var opcode = _resolver.ResolveOpcodeById(funcStream.ReadByte()); instruction.Opcode = opcode; var currentIndex = instruction.Index + 1; switch (opcode) { case Opcode.OpCallBuiltin0: case Opcode.OpCallBuiltin1: case Opcode.OpCallBuiltin2: case Opcode.OpCallBuiltin3: case Opcode.OpCallBuiltin4: case Opcode.OpCallBuiltin5: case Opcode.OpGetBuiltinFunction: { var funcId = funcStream.ReadUInt16(); instructionData.AddData(funcId); instructionData.DataString = $"function name : {_resolver.ResolveFunctionNameById(funcId)}"; } break; case Opcode.OpJump: { var offset = funcStream.ReadInt32(); instructionData.AddData(offset); instructionData.DataString = $"offset = 0x{offset:X}, jump to index 0x{currentIndex + 4 + offset:X}"; } break; case Opcode.OpJumpOnTrueExpr: case Opcode.OpJumpOnTrue: case Opcode.OpJumpOnFalseExpr: case Opcode.OpJumpOnFalse: { var offset = funcStream.ReadInt16(); instructionData.AddData(offset); instructionData.DataString = $"offset = 0x{offset:X}, jump to index 0x{currentIndex + 2 + offset:X}"; } break; case Opcode.OpJumpback: { var offset = funcStream.ReadUInt16(); instructionData.AddData(offset); instructionData.DataString = $"offset = 0x{offset:X}, jump back to index 0x{currentIndex + 2 - offset:X}"; } break; case Opcode.OpCallBuiltinMethod0: case Opcode.OpCallBuiltinMethod1: case Opcode.OpCallBuiltinMethod2: case Opcode.OpCallBuiltinMethod3: case Opcode.OpCallBuiltinMethod4: case Opcode.OpCallBuiltinMethod5: case Opcode.OpGetBuiltinMethod: { var funcId = funcStream.ReadUInt16(); instructionData.AddData(funcId); instructionData.DataString = $"method name : {_resolver.ResolveMethodNameById(funcId)}"; } break; case Opcode.OpWaittillmatch: funcStream.ReadUInt16(); break; case Opcode.OpGetUnsignedShort: { var value = funcStream.ReadUInt16(); instructionData.AddData(value); instructionData.DataString = $"value : {value}"; } break; case Opcode.OpGetNegUnsignedShort: { var value = funcStream.ReadUInt16(); instructionData.AddData(value); instructionData.DataString = $"value : {-value}"; } break; case Opcode.OpGetFloat: var f = funcStream.ReadSingle(); instructionData.AddData(f); instructionData.DataString = $"value = {f}"; break; case Opcode.OpSwitch: funcStream.ReadInt32(); break; case Opcode.OpGetInteger: var i = funcStream.ReadInt32(); instructionData.AddData(i); instructionData.DataString = $"value = {i}"; break; case Opcode.OpScriptLocalThreadCall: case Opcode.OpScriptLocalMethodThreadCall: case Opcode.OpScriptLocalMethodChildThreadCall: case Opcode.OpScriptLocalChildThreadCall: DisassembleLocalCall(instructionData, funcStream, currentIndex, true); break; case Opcode.OpScriptFarMethodThreadCall: case Opcode.OpScriptFarChildThreadCall: case Opcode.OpScriptFarMethodChildThreadCall: case Opcode.OpScriptFarThreadCall: DisassembleFarCall(instructionData, funcStream, true); break; //TODO case Opcode.OpGetVector: funcStream.ReadBytes(12); break; case Opcode.OpScriptLocalMethodCall: case Opcode.OpScriptLocalFunctionCall: case Opcode.OpScriptLocalFunctionCall2: case Opcode.OpGetLocalFunction: DisassembleLocalCall(instructionData, funcStream, currentIndex, false); break; case Opcode.OpCallBuiltinMethod: case Opcode.OpCallBuiltin: funcStream.ReadBytes(3); break; case Opcode.OpScriptFarFunctionCall2: case Opcode.OpScriptFarFunctionCall: case Opcode.OpGetFarFunction: case Opcode.OpScriptFarMethodCall: DisassembleFarCall(instructionData, funcStream, false); break; case Opcode.OpEndswitch: DisassembleEndSwitch(instructionData, funcStream, currentIndex); break; //TODO case Opcode.OpGetAnimation: funcStream.ReadBytes(8); throw new InvalidOperationException("OpGetAnimation is not supported yet"); //TODO case Opcode.OpGetAnimTree: _buffer.ReadTerminatedString(); funcStream.ReadByte(); break; case Opcode.OpGetString: case Opcode.OpGetIString: funcStream.ReadBytes(4); var s = _buffer.ReadTerminatedString(); instructionData.AddData(s); instructionData.DataString = $"value = {s}"; break; case Opcode.OpEvalSelfFieldVariable: case Opcode.OpSetLevelFieldVariableField: case Opcode.OpClearFieldVariable: case Opcode.OpEvalFieldVariable: case Opcode.OpEvalFieldVariableRef: case Opcode.OpEvalLevelFieldVariable: case Opcode.OpSetAnimFieldVariableField: case Opcode.OpSetSelfFieldVariableField: case Opcode.OpEvalAnimFieldVariableRef: case Opcode.OpEvalLevelFieldVariableRef: case Opcode.OpEvalAnimFieldVariable: case Opcode.OpEvalSelfFieldVariableRef: var fieldId = funcStream.ReadUInt16(); string fieldName; if (fieldId <= 0x95A1) { fieldName = _resolver.ResolveFieldNameById(fieldId); } else { fieldId = _buffer.ReadUInt16(); fieldName = fieldId == 0 ? _buffer.ReadTerminatedString() : _resolver.ResolveFieldNameById(fieldId); } instructionData.AddData(fieldName); instructionData.DataString = $"field name : {fieldName}"; break; case Opcode.OpSetNewLocalVariableFieldCached0: case Opcode.OpEvalNewLocalArrayRefCached0: case Opcode.OpSafeCreateVariableFieldCached: case Opcode.OpClearLocalVariableFieldCached: case Opcode.OpSetLocalVariableFieldCached: case Opcode.OpRemoveLocalVariables: case Opcode.OpEvalLocalVariableRefCached: case Opcode.OpEvalLocalArrayRefCached: case Opcode.OpSafeSetVariableFieldCached: case Opcode.OpEvalLocalVariableCached: case Opcode.OpSafeSetWaittillVariableFieldCached: case Opcode.OpCreateLocalVariable: case Opcode.OpEvalLocalVariableObjectCached: case Opcode.OpEvalLocalArrayCached: { int index = funcStream.ReadByte(); instructionData.AddData(index); instructionData.DataString = $"index = {index}"; } break; case Opcode.OpScriptChildThreadCallPointer: case Opcode.OpCallBuiltinMethodPointer: case Opcode.OpCallBuiltinPointer: case Opcode.OpScriptMethodThreadCallPointer: case Opcode.OpScriptMethodChildThreadCallPointer: case Opcode.OpScriptThreadCallPointer: { int numOfParams = funcStream.ReadByte(); instructionData.AddData(numOfParams); instructionData.DataString = $"parameters count = {numOfParams}"; } break; case Opcode.OpGetByte: { var b = funcStream.ReadByte(); instructionData.AddData(b); instructionData.DataString = $"value = {b}"; } break; case Opcode.OpGetNegByte: { var b = funcStream.ReadByte(); instructionData.AddData(b); instructionData.DataString = $"value = {-b}"; } break; } instructions.Add(instruction); } return(instructions); }