// Copyright 2023 xensik. All rights reserved. // // Use of this source code is governed by a GNU GPLv3 license // that can be found in the LICENSE file. #include "stdinc.hpp" #include "decompiler.hpp" #include "context.hpp" #include "utils/string.hpp" namespace xsk::gsc { decompiler::decompiler(context const* ctx) : ctx_{ ctx } { } auto decompiler::decompile(assembly const& data) -> program::ptr { program_ = make_program(); for (auto const& func : data.functions) { decompile_function(*func); } return std::move(program_); } auto decompiler::decompile_function(function const& func) -> void { expr_labels_.clear(); tern_labels_.clear(); in_waittill_ = false; labels_ = func.labels; locs_ = {}; stack_ = {}; auto loc = location{ nullptr, static_cast(func.index) }; auto name = make_expr_identifier(loc, func.name); auto prms = make_expr_parameters(loc); auto body = make_stmt_comp(loc, make_stmt_list(loc)); func_ = make_decl_function(loc, std::move(name), std::move(prms), std::move(body)); for (auto const& inst : func.instructions) { decompile_instruction(*inst); } if (!stack_.empty()) { throw decomp_error("stack isn't empty at function end"); } locs_.last = true; locs_.end = func_->body->block->list.back().label(); func_->body->block->list.pop_back(); decompile_statements(*func_->body->block); process_function(*func_); program_->declarations.push_back(decl{ std::move(func_) }); } auto decompiler::decompile_instruction(instruction const& inst) -> void { decompile_expressions(inst); auto loc = location{ nullptr, static_cast(inst.index) }; switch (inst.opcode) { case opcode::OP_End: { func_->body->block->list.push_back(stmt{ make_stmt_return(loc, expr{ make_node(loc) }) }); break; } case opcode::OP_Return: { auto value = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_stmt_return(value.loc(), std::move(value)) }); break; } case opcode::OP_GetZero: { stack_.push(make_expr_integer(loc, "0")); break; } case opcode::OP_GetByte: case opcode::OP_GetUnsignedShort: case opcode::OP_GetUnsignedInt: case opcode::OP_GetInteger: case opcode::OP_GetInteger64: { stack_.push(make_expr_integer(loc, inst.data[0])); break; } case opcode::OP_GetNegByte: case opcode::OP_GetNegUnsignedShort: case opcode::OP_GetNegUnsignedInt: { stack_.push(make_expr_integer(loc, "-" + inst.data[0])); break; } case opcode::OP_GetFloat: { stack_.push(make_expr_float(loc, inst.data[0])); break; } case opcode::OP_GetVector: { auto x = expr{ make_expr_float(loc, inst.data[0]) }; auto y = expr{ make_expr_float(loc, inst.data[1]) }; auto z = expr{ make_expr_float(loc, inst.data[2]) }; stack_.push(make_expr_vector(loc, std::move(x), std::move(y), std::move(z))); break; } case opcode::OP_GetString: { stack_.push(make_expr_string(loc, inst.data[0])); break; } case opcode::OP_GetIString: { stack_.push(make_expr_istring(loc, inst.data[0])); break; } case opcode::OP_GetUndefined: { stack_.push(make_expr_undefined(loc)); break; } case opcode::OP_EmptyArray: { stack_.push(make_expr_empty_array(loc)); break; } case opcode::OP_GetLevel: case opcode::OP_GetLevelObject: { stack_.push(make_expr_level(loc)); break; } case opcode::OP_GetAnim: case opcode::OP_GetAnimObject: { stack_.push(make_expr_anim(loc)); break; } case opcode::OP_GetSelf: case opcode::OP_GetSelfObject: { stack_.push(make_expr_self(loc)); break; } case opcode::OP_GetGame: case opcode::OP_GetGameRef: { stack_.push(make_expr_game(loc)); break; } case opcode::OP_GetAnimation: { if (!inst.data[0].empty()) { auto dec = make_decl_usingtree(loc, make_expr_string(loc, inst.data[0])); program_->declarations.push_back(decl{ std::move(dec) }); } stack_.push(make_expr_animation(loc, inst.data[1])); break; } case opcode::OP_GetAnimTree: { if (!inst.data[0].empty()) { auto dec = make_decl_usingtree(loc, make_expr_string(loc, inst.data[0])); program_->declarations.push_back(decl{ std::move(dec) }); } stack_.push(make_expr_animtree(loc)); break; } case opcode::OP_GetThisthread: { stack_.push(make_expr_thisthread(loc)); break; } case opcode::OP_GetBuiltinFunction: case opcode::OP_GetBuiltinMethod: { auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_reference(loc, std::move(path), std::move(name))); break; } case opcode::OP_GetLocalFunction: { auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_reference(loc, std::move(path), std::move(name))); break; } case opcode::OP_GetFarFunction: { auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); stack_.push(make_expr_reference(loc, std::move(path), std::move(name))); break; } case opcode::OP_CreateLocalVariable: { if (in_waittill_) { stack_.push(make_asm_create(loc, inst.data[0])); } else { func_->body->block->list.push_back(stmt{ make_asm_create(loc, inst.data[0]) }); } break; } case opcode::OP_RemoveLocalVariables: { func_->body->block->list.push_back(stmt{ make_asm_remove(loc, inst.data[0]) }); break; } case opcode::OP_EvalLocalVariableCached0: { stack_.push(make_asm_access(loc, "0")); break; } case opcode::OP_EvalLocalVariableCached1: { stack_.push(make_asm_access(loc, "1")); break; } case opcode::OP_EvalLocalVariableCached2: { stack_.push(make_asm_access(loc, "2")); break; } case opcode::OP_EvalLocalVariableCached3: { stack_.push(make_asm_access(loc, "3")); break; } case opcode::OP_EvalLocalVariableCached4: { stack_.push(make_asm_access(loc, "4")); break; } case opcode::OP_EvalLocalVariableCached5: { stack_.push(make_asm_access(loc, "5")); break; } case opcode::OP_EvalLocalVariableCached: { stack_.push(make_asm_access(loc, inst.data[0])); break; } case opcode::OP_EvalLocalArrayCached: { auto key = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ make_asm_access(loc, inst.data[0]) }; stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalArray: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto key = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalNewLocalArrayRefCached0: { auto key = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ make_asm_create(loc, inst.data[0]) }; stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalLocalArrayRefCached0: { auto key = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ make_asm_access(loc, "0") }; stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalLocalArrayRefCached: { auto key = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ make_asm_access(loc, inst.data[0]) }; stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalArrayRef: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto key = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_array(key.loc(), std::move(obj), std::move(key))); break; } case opcode::OP_ClearArray: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto key = expr{ std::move(stack_.top()) }; stack_.pop(); loc = key.loc(); auto lvalue = expr{ make_expr_array(loc, std::move(obj), std::move(key)) }; auto rvalue = expr{ make_expr_undefined(loc) }; auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_AddArray: { auto value = expr{ std::move(stack_.top()) }; stack_.pop(); auto array = std::move(stack_.top()); stack_.pop(); if (array->kind() == node::expr_empty_array) { auto args = make_expr_arguments(loc); args->list.push_back(std::move(value)); stack_.push(make_expr_add_array(array->loc(), std::move(args))); } else if (array->kind() == node::expr_add_array) { reinterpret_cast(array.get())->args->list.push_back(std::move(value)); stack_.push(std::move(array)); } else { throw decomp_error("unknown add array type (could be an array variable name?)"); } break; } case opcode::OP_PreScriptCall: { stack_.push(make_asm_prescriptcall(loc)); break; } case opcode::OP_ScriptLocalFunctionCall2: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptLocalFunctionCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptLocalMethodCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptLocalThreadCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptLocalChildThreadCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) })); break; } case opcode::OP_ScriptLocalMethodThreadCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptLocalMethodChildThreadCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) })); break; } case opcode::OP_ScriptFarFunctionCall2: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptFarFunctionCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptFarMethodCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptFarThreadCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); for (auto i = std::stoul(inst.data[2]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptFarChildThreadCall: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); for (auto i = std::stoul(inst.data[2]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) })); break; } case opcode::OP_ScriptFarMethodThreadCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); for (auto i = std::stoul(inst.data[2]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptFarMethodChildThreadCall: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc, inst.data[0]); auto name = make_expr_identifier(loc, inst.data[1]); for (auto i = std::stoul(inst.data[2]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::childthread) })); break; } case opcode::OP_ScriptFunctionCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptMethodCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_prescriptcall) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::normal) })); break; } case opcode::OP_ScriptThreadCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); loc = func.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptChildThreadCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); loc = func.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::childthread) })); break; } case opcode::OP_ScriptMethodThreadCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::thread) })); break; } case opcode::OP_ScriptMethodChildThreadCallPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::childthread) })); break; } case opcode::OP_CallBuiltinPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); loc = func.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethodPointer: { auto args = make_expr_arguments(loc); auto func = expr{ std::move(stack_.top()) }; stack_.pop(); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_pointer(loc, std::move(func), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin0: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin1: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 1u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin2: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 2u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin3: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 3u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin4: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 4u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin5: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 5u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltin: { auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_call(loc, call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod0: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod1: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 1u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod2: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 2u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod3: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 3u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod4: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 4u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod5: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = 5u; i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_CallBuiltinMethod: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto args = make_expr_arguments(loc); auto path = make_expr_path(loc); auto name = make_expr_identifier(loc, inst.data[0]); for (auto i = std::stoul(inst.data[1]); i > 0; i--) { auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); } stack_.push(make_expr_method(loc, std::move(obj), call{ make_expr_function(loc, std::move(path), std::move(name), std::move(args), call::mode::builtin) })); break; } case opcode::OP_DecTop: { auto exp = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_stmt_call(exp.loc(), std::move(exp)) }); break; } case opcode::OP_inc: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_increment(lvalue.loc(), std::move(lvalue), false)); break; } case opcode::OP_dec: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_decrement(lvalue.loc(), std::move(lvalue), false)); break; } case opcode::OP_bit_or: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_bitwise_or(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_bit_ex_or: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_bitwise_exor(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_bit_and: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_bitwise_and(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_equality: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_equality(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_inequality: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_inequality(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_less: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_less(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_greater: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_greater(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_less_equal: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_less_equal(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_greater_equal: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_greater_equal(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_shift_left: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_shift_left(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_shift_right: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_shift_right(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_plus: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_add(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_minus: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_sub(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_multiply: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_mul(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_divide: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_div(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_mod: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_mod(lvalue.loc(), std::move(lvalue), std::move(rvalue))); break; } case opcode::OP_wait: { auto exp = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_stmt_wait(exp.loc(), std::move(exp)) }); break; } case opcode::OP_waittillFrameEnd: { func_->body->block->list.push_back(stmt{ make_stmt_waittillframeend(loc) }); break; } case opcode::OP_waitframe: { func_->body->block->list.push_back(stmt{ make_stmt_waitframe(loc) }); break; } case opcode::OP_waittill: { auto args = make_expr_arguments(loc); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto event = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_stmt_waittill(event.loc(), std::move(obj), std::move(event), std::move(args))); in_waittill_ = true; break; } case opcode::OP_waittillmatch: { auto args = make_expr_arguments(loc); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto event = expr{ std::move(stack_.top()) }; stack_.pop(); loc = event.loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto arg = std::move(stack_.top()); stack_.pop(); loc = arg->loc(); args->list.push_back(std::move(arg)); } func_->body->block->list.push_back(stmt{ make_stmt_waittillmatch(loc, std::move(obj), std::move(event), std::move(args)) }); break; } case opcode::OP_clearparams: { if (in_waittill_) { auto args = make_expr_arguments(loc); auto arg = std::move(stack_.top()); stack_.pop(); while (arg->kind() != node::stmt_waittill) { args->list.push_back(std::move(arg)); arg = std::move(stack_.top()); stack_.pop(); } if (arg->kind() == node::stmt_waittill) { std::reverse(args->list.begin(), args->list.end()); reinterpret_cast(arg.get())->args = std::move(args); in_waittill_ = false; } func_->body->block->list.push_back(stmt{ std::move(arg) }); } break; } case opcode::OP_notify: { auto args = make_expr_arguments(loc); auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto event = expr{ std::move(stack_.top()) }; stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::asm_voidcodepos) { args->list.push_back(std::move(var)); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } func_->body->block->list.push_back(stmt{ make_stmt_notify(loc, std::move(obj), std::move(event), std::move(args)) }); break; } case opcode::OP_endon: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto event = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_stmt_endon(event.loc(), std::move(obj), std::move(event)) }); break; } case opcode::OP_voidCodepos: { stack_.push(make_asm_voidcodepos(loc)); break; } case opcode::OP_vector: { auto x = expr{ std::move(stack_.top()) }; stack_.pop(); auto y = expr{ std::move(stack_.top()) }; stack_.pop(); auto z = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_vector(z.loc(), std::move(x), std::move(y), std::move(z))); break; } case opcode::OP_size: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_size(lvalue.loc(), std::move(lvalue))); break; } case opcode::OP_EvalLevelFieldVariable: { auto obj = expr{ make_expr_level(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalAnimFieldVariable: { auto obj = expr{ make_expr_anim(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalSelfFieldVariable: { auto obj = expr{ make_expr_self(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalFieldVariable: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(obj.loc(), std::move(obj), std::move(field))); break; } case opcode::OP_EvalLevelFieldVariableRef: { auto obj = expr{ make_expr_level(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalAnimFieldVariableRef: { auto obj = expr{ make_expr_anim(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalSelfFieldVariableRef: { auto obj = expr{ make_expr_self(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(loc, std::move(obj), std::move(field))); break; } case opcode::OP_EvalFieldVariableRef: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); auto field = make_expr_identifier(loc, inst.data[0]); stack_.push(make_expr_field(obj.loc(), std::move(obj), std::move(field))); break; } case opcode::OP_ClearFieldVariable: { auto obj = expr{ std::move(stack_.top()) }; stack_.pop(); loc = obj.loc(); auto name = make_expr_identifier(loc, inst.data[0]); auto field = expr{ make_expr_field(loc, std::move(obj), std::move(name)) }; auto undef = expr{ make_expr_undefined(loc) }; auto exp = expr{ make_expr_assign_equal(loc, std::move(field), std::move(undef)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SafeCreateVariableFieldCached: { auto name = (ctx_->props() & props::hash) ? inst.data[0] : fmt::format("var_{}", inst.data[0]); func_->params->list.push_back(make_expr_identifier(loc, name)); break; } case opcode::OP_SafeSetWaittillVariableFieldCached: { if (stack_.top()->kind() != node::asm_create) { stack_.push(make_asm_access(loc, inst.data[0])); } break; } case opcode::OP_SafeSetVariableFieldCached0: { func_->params->list.push_back(make_expr_identifier(loc, "var_0")); break; } case opcode::OP_SafeSetVariableFieldCached: { func_->params->list.push_back(make_expr_identifier(loc, "var_" + inst.data[0])); break; } case opcode::OP_EvalLocalVariableRefCached0: { stack_.push(make_asm_access(loc, "0")); break; } case opcode::OP_EvalLocalVariableRefCached: { stack_.push(make_asm_access(loc, inst.data[0])); break; } case opcode::OP_SetLevelFieldVariableField: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto obj = expr{ make_expr_level(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) }; auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SetVariableField: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = lvalue.loc(); if (lvalue == node::expr_increment) { func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(lvalue)) }); } else if (lvalue == node::expr_decrement) { func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(lvalue)) }); } else { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); } break; } case opcode::OP_SetAnimFieldVariableField: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto obj = expr{ make_expr_anim(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) }; auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SetSelfFieldVariableField: { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto obj = expr{ make_expr_self(loc) }; auto field = make_expr_identifier(loc, inst.data[0]); auto lvalue = expr{ make_expr_field(loc, std::move(obj), std::move(field)) }; auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SetLocalVariableFieldCached0: { auto lvalue = expr{ make_asm_access(loc, "0") }; auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SetNewLocalVariableFieldCached0: { auto lvalue = expr{ make_asm_create(loc, inst.data[0]) }; auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); if (func_->body->block->list.size() > 0) { std::vector vars; while (func_->body->block->list.back() == node::asm_create) { auto& entry = func_->body->block->list.back(); if (loc.begin.line < entry.loc().begin.line) { vars.push_back(entry.as_asm_create->index); func_->body->block->list.pop_back(); continue; } break; } std::reverse(vars.begin(), vars.end()); lvalue.as_asm_create->vars = vars; } auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_SetLocalVariableFieldCached: { auto lvalue = expr{ make_asm_access(loc, inst.data[0]) }; auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = rvalue.loc(); auto exp = expr{ make_expr_assign_equal(loc, std::move(lvalue), std::move(rvalue)) }; func_->body->block->list.push_back(stmt{ make_stmt_assign(loc, std::move(exp)) }); break; } case opcode::OP_ClearLocalVariableFieldCached: { func_->body->block->list.push_back(stmt{ make_asm_clear(loc, inst.data[0]) }); break; } case opcode::OP_ClearLocalVariableFieldCached0: { func_->body->block->list.push_back(stmt{ make_asm_clear(loc, "0") }); break; } case opcode::OP_EvalLocalVariableObjectCached: { stack_.push(make_asm_access(loc, inst.data[0])); break; } case opcode::OP_BoolNot: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_not(lvalue.loc(), std::move(lvalue))); break; } case opcode::OP_BoolComplement: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_complement(lvalue.loc(), std::move(lvalue))); break; } case opcode::OP_switch: { auto test = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_asm_switch(test.loc(), std::move(test), inst.data[0]) }); break; } case opcode::OP_endswitch: { func_->body->block->list.push_back(stmt{ make_asm_endswitch(loc, inst.data) }); break; } case opcode::OP_jump: { func_->body->block->list.push_back(stmt{ make_asm_jmp(loc, inst.data[0]) }); if (stack_.size() != 0) tern_labels_.push_back(inst.data[0]); break; } case opcode::OP_jumpback: { func_->body->block->list.push_back(stmt{ make_asm_jmp_back(loc, inst.data[0]) }); break; } case opcode::OP_JumpOnTrue: { auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); loc = lvalue.loc(); auto test = expr{ make_expr_not(loc, std::move(lvalue)) }; func_->body->block->list.push_back(stmt{ make_asm_jmp_cond(loc, std::move(test), inst.data[0]) }); break; } case opcode::OP_JumpOnFalse: { auto test = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.push_back(stmt{ make_asm_jmp_cond(test.loc(), std::move(test), inst.data[0]) }); break; } case opcode::OP_JumpOnTrueExpr: { auto test = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_asm_jmp_true(test.loc(), std::move(test), inst.data[0])); expr_labels_.push_back(inst.data[0]); break; } case opcode::OP_JumpOnFalseExpr: { auto test = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_asm_jmp_false(test.loc(), std::move(test), inst.data[0])); expr_labels_.push_back(inst.data[0]); break; } case opcode::OP_FormalParams: { auto count = std::stoi(inst.data[0]); for (auto i = 1; i <= count; i++) { auto name = (ctx_->props() & props::hash) ? inst.data[i] : fmt::format("var_{}", inst.data[i]); func_->params->list.push_back(make_expr_identifier(loc, name)); } break; } case opcode::OP_IsDefined: { auto value = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_isdefined(value.loc(), std::move(value))); break; } case opcode::OP_IsTrue: { auto value = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_istrue(value.loc(), std::move(value))); break; } case opcode::OP_BoolNotAfterAnd: { auto value = expr{ std::move(stack_.top()) }; stack_.pop(); stack_.push(make_expr_not(value.loc(), std::move(value))); break; } case opcode::OP_GetStatHash: { stack_.push(make_expr_string(loc, fmt::format("stat_{}", inst.data[0]))); break; } case opcode::OP_GetUnkxHash: { stack_.push(make_expr_string(loc, fmt::format("hunk_{}", inst.data[0]))); break; } case opcode::OP_GetEnumHash: { stack_.push(make_expr_string(loc, fmt::format("enum_{}", inst.data[0]))); break; } case opcode::OP_GetDvarHash: { stack_.push(make_expr_string(loc, fmt::format("dvar_{}", inst.data[0]))); break; } case opcode::OP_waittillmatch2: case opcode::OP_checkclearparams: case opcode::OP_CastFieldObject: case opcode::OP_CastBool: break; default: throw decomp_error(fmt::format("unhandled opcode {}", ctx_->opcode_name(inst.opcode))); } } auto decompiler::decompile_expressions(instruction const& inst) -> void { auto const itr = labels_.find(inst.index); if (itr == labels_.end()) return; for (auto const& exp : expr_labels_) { if (exp == itr->second) { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto jump = std::move(stack_.top()); stack_.pop(); auto loc = jump->loc(); if (jump->kind() == node::asm_jmp_true) { auto lvalue = std::move(reinterpret_cast(jump.get())->test); stack_.push(make_expr_or(loc, std::move(lvalue), std::move(rvalue))); } else if (jump->kind() == node::asm_jmp_false) { auto lvalue = std::move(reinterpret_cast(jump.get())->test); stack_.push(make_expr_and(loc, std::move(lvalue), std::move(rvalue))); } else { throw decomp_error("invalid conditional expression"); } } } for (auto const& tern : tern_labels_) { if (tern == itr->second) { auto rvalue = expr{ std::move(stack_.top()) }; stack_.pop(); auto lvalue = expr{ std::move(stack_.top()) }; stack_.pop(); func_->body->block->list.pop_back(); auto stm = std::move(func_->body->block->list.back()); func_->body->block->list.pop_back(); if (stm == node::asm_jmp_cond) { auto loc = stm.as_cond->loc(); stack_.push(make_expr_ternary(loc, std::move(stm.as_cond->test), std::move(lvalue), std::move(rvalue))); } else { throw decomp_error("invalid ternary expression"); } } } } auto decompiler::decompile_statements(stmt_list& stm) -> void { decompile_loops(stm); decompile_switches(stm); decompile_ifelses(stm); decompile_aborts(stm); decompile_tuples(stm); } auto decompiler::decompile_loops(stmt_list& stm) -> void { if (stm.list.size() == 0) return; for (auto i = static_cast(stm.list.size() - 1); i >= 0; i--) { if (stm.list.at(i) == node::asm_jmp_back) { auto break_loc = last_location_index(stm, i) ? locs_.end : stm.list.at(i + 1).label(); auto start = find_location_index(stm, stm.list.at(i).as_jump_back->value); if (i > 0 && stm.list.at(i - 1) == node::asm_jmp_cond) { if (i - 1 == static_cast(start)) // condition belongs to empty loop { decompile_while(stm, start, i); i = static_cast(stm.list.size()); continue; } else if (static_cast(i) < find_location_index(stm, stm.list.at(i - 1).as_cond->value)) { decompile_dowhile(stm, i - 1, i); i = static_cast(stm.list.size()); continue; } } if (i == static_cast(start)) // empty inf loop { decompile_inf(stm, start, i); } else if (stm.list.at(start) != node::asm_jmp_cond) // no condition { decompile_inf(stm, start, i); } else if (stm.list.at(start).as_cond->value != break_loc) // condition belong to other stmt { decompile_inf(stm, start, i); } else // condition belong to loop { decompile_loop(stm, start, i); } i = static_cast(stm.list.size()); } } } auto decompiler::decompile_switches(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { if (stm.list[i] == node::asm_switch) { auto end = find_location_index(stm, stm.list[i].as_asm_switch->value); decompile_switch(stm, i, end); } } } auto decompiler::decompile_ifelses(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { auto const& entry = stm.list.at(i); if (entry == node::asm_jmp_cond) { auto j = find_location_index(stm, entry.as_cond->value) - 1; auto last_loc = locs_.end; if (stm.list.at(j) == node::asm_jmp) { // if scope is a loop check break, continue if (stm.list.at(j).as_jump->value == locs_.cnt) { // check for if/else or if/continue if (j - i > 1 && stm.list.at(j - 1) == node::stmt_return) { // scope ends with a return, so jump belows to if/else decompile_ifelse(stm, i, j); } else if (j - i > 1 && stm.list.at(j - 1) == node::asm_jmp) { if (stm.list.at(j - 1).as_jump->value == locs_.brk) { // scope ends with a break, so jump belows to if/else decompile_ifelse(stm, i, j); } else if (stm.list.at(j - 1).as_jump->value == locs_.cnt) { // if { break/return } else { continue } at loop scope end if (j - i > 2 && (stm.list.at(j - 2) == node::asm_jmp || stm.list.at(j - 2) == node::stmt_return)) { decompile_if(stm, i, j); } else { // scope ends with a continue, so jump belows to if/else decompile_ifelse(stm, i, j); } } else { // jump belows to if/continue decompile_if(stm, i, j); } } else { // last if/else inside a loop still trigger this :( decompile_if(stm, i, j); } } else if (stm.list.at(j).as_jump->value == locs_.brk) { decompile_if(stm, i, j); } else if (stm.list.at(j).as_jump->value == entry.as_cond->value) { if (find_location_reference(stm, i + 1, j, entry.as_cond->value)) { // if scope, have a empty else inside at end decompile_if(stm, i, j); } else { decompile_ifelse(stm, i, j); // if scope with empty else } } else { decompile_ifelse(stm, i, j); } } else if (stm.list.at(j) == node::stmt_return && stm.list.at(j).as_return->value == node::null) { if(entry.as_cond->value != locs_.end) { auto ref = stm.list.at(j + 1).label(); if (find_location_reference(stm, i + 1, j, ref)) { // after return is referenced inside the scope decompile_if(stm, i, j); continue; } } if (locs_.brk != "" || locs_.cnt != "") { decompile_if(stm, i, j); // inside a loop cant be last } else if (j - i == 1) { decompile_if(stm, i, j); // only one explicit return } else if (stm.list.back() != node::stmt_return) { decompile_if(stm, i, j); // scope end is not a last return } else if (locs_.last && stm.list.back() != node::stmt_return) { decompile_if(stm, i, j); // inside a last scope but is not and inner last } else if (find_location_reference(stm, j, stm.list.size(), last_loc)) { decompile_if(stm, i, j); // reference to func end after the if } else if (!locs_.last) { decompile_if(stm, i, j); // fake last ifelse } else { decompile_ifelse_end(stm, i, j); // special case } } else { decompile_if(stm, i, j); } } } } auto decompiler::decompile_aborts(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { if (stm.list[i] == node::asm_jmp) { auto const loc = stm.list[i].loc(); auto const jmp = stm.list[i].as_jump->value; if (jmp == locs_.brk) { stm.list.erase(stm.list.begin() + i); stm.list.insert(stm.list.begin() + i, stmt{ make_stmt_break(loc) }); } else if (jmp == locs_.cnt) { stm.list.erase(stm.list.begin() + i); stm.list.insert(stm.list.begin() + i, stmt{ make_stmt_continue(loc) }); } else { std::cout << fmt::format("WARNING: unresolved jump to '{}', maybe incomplete for loop\n", jmp); } } } } auto decompiler::decompile_tuples(stmt_list& stm) -> void { for (auto i = 1u; i < stm.list.size(); i++) { if (stm.list.at(i) == node::asm_clear) { auto j = i - 1; auto found = false, done = false; while (j >= 0 && stm.list.at(j) == node::stmt_assign) { auto const& exp = stm.list.at(j).as_assign->value; if (exp != node::expr_assign_equal) break; if (!done) { if (exp.as_assign_equal->rvalue != node::expr_array) break; if (exp.as_assign_equal->rvalue.as_array->key != node::expr_integer) break; if (exp.as_assign_equal->rvalue.as_array->key.as_integer->value == "0") done = true; j--; } else { if (exp.as_assign_equal->lvalue == node::asm_create || exp.as_assign_equal->lvalue == node::asm_access) found = true; break; } } if (found) { auto& entry = stm.list.at(j); // temp = expr; auto tuple = make_expr_tuple(entry.loc()); tuple->temp = std::move(entry.as_assign->value.as_assign_equal->lvalue); j++; while (j < i) { tuple->list.push_back(std::move(stm.list.at(j).as_assign->value.as_assign_equal->lvalue)); stm.list.erase(stm.list.begin() + j); i--; } stm.list.erase(stm.list.begin() + j); // clear temp array i--; entry.as_assign->value.as_assign_equal->lvalue = expr{ std::move(tuple) }; } } } } auto decompiler::decompile_if(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[begin].as_cond->value; auto loc = stm.list[begin].loc(); auto test = std::move(stm.list[begin].as_cond->test); stm.list.erase(stm.list.begin() + begin); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_if(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) }); } auto decompiler::decompile_ifelse(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[end].label(); auto loc = stm.list[begin].loc(); auto test = std::move(stm.list[begin].as_cond->test); stm.list.erase(stm.list.begin() + begin); end--; auto body_if = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body_if->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body_if); locs_ = save; auto end_loc = stm.list[begin].as_jump->value; stm.list.erase(stm.list.begin() + begin); end = find_location_index(stm, end_loc); save = locs_; locs_.last = false; locs_.end = end_loc; auto body_else = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body_else->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body_else); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_ifelse(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }, stmt{ make_stmt_comp(loc, std::move(body_else)) }) }); } auto decompiler::decompile_ifelse_end(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = true; locs_.end = stm.list[find_location_index(stm, stm.list[begin].as_cond->value) - 1].label(); auto loc = stm.list[begin].loc(); auto test = std::move(stm.list[begin].as_cond->test); stm.list.erase(stm.list.begin() + begin); end--; auto body_if = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body_if->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } stm.list.erase(stm.list.begin() + begin); decompile_statements(*body_if); locs_ = save; if (begin == stm.list.size()) { stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_if(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }) }); } else { end = stm.list.size() - 1; save = locs_; locs_.last = true; locs_.end = stm.list[end].label(); auto body_else = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body_else->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } stm.list.erase(stm.list.begin() + begin); decompile_statements(*body_else); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_ifelse(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body_if)) }, stmt{ make_stmt_comp(loc, std::move(body_else)) }) }); } } auto decompiler::decompile_inf(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.brk = last_location_index(stm, end) ? locs_.end : stm.list[end + 1].label(); locs_.end = stm.list[end].label(); locs_.cnt = stm.list[end].label(); auto loc = stm.list[begin].loc(); stm.list.erase(stm.list.begin() + end); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_for(loc, stmt{ make_node(loc) }, expr{ make_node(loc) }, stmt{ make_node(loc) }, stmt{ make_stmt_comp(loc, std::move(body)) }) }); } auto decompiler::decompile_loop(stmt_list& stm, usize start, usize end) -> void { auto const& last = stm.list.at(end - 1); if (last == node::stmt_assign) { if (last.as_assign->value == node::expr_assign_equal) { auto& val = last.as_assign->value.as_assign_equal->rvalue; if (val == node::expr_call && val.as_call->value == node::expr_function) { if (utils::string::to_lower(val.as_call->value.as_function->name->value) == "getnextarraykey") { auto ref = stm.list.at(start).label(); if (!find_location_reference(stm, 0, start, ref)) { decompile_foreach(stm, start, end); return; } } } } if (start > 0) // while at func start { auto index = 1; while (stm.list.at(start - index) == node::asm_create) { if (start - index > 0) index++; else break; } if (stm.list.at(start - index) == node::stmt_assign) { auto ref = stm.list.at(end).label(); auto ref2 = stm.list.at(start - index + 1).label(); if (find_location_reference(stm, start, end, ref)) { // jump is referenced, not post-expr decompile_while(stm, start, end); return; } else if (find_location_reference(stm, 0, start, ref2)) { // begin is at condition or localVarCreate, not pre-expr decompile_while(stm, start, end); return; } else { decompile_for(stm, start, end); return; } } } } decompile_while(stm, start, end); } auto decompiler::decompile_while(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[end].label(); locs_.cnt = stm.list[end].label(); locs_.brk = stm.list[begin].as_cond->value; auto loc = stm.list[begin].loc(); auto test = std::move(stm.list[begin].as_cond->test); end--; stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + end); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_while(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) }); } auto decompiler::decompile_dowhile(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[begin].label(); locs_.cnt = stm.list[begin].label(); locs_.brk = stm.list[begin].as_cond->value; auto test = std::move(stm.list[begin].as_cond->test); begin = find_location_index(stm, stm.list[end].as_jump_back->value); auto loc = stm.list[begin].loc(); end--; stm.list.erase(stm.list.begin() + end); stm.list.erase(stm.list.begin() + end); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_dowhile(loc, std::move(test), stmt{ make_stmt_comp(loc, std::move(body)) }) }); } auto decompiler::decompile_for(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[end - 1].label(); locs_.cnt = stm.list[end - 1].label(); locs_.brk = stm.list[begin].as_cond->value; for (begin -= 1; stm.list[begin] == node::asm_create; begin--); auto loc = stm.list[begin].loc(); auto init = make_stmt_list(loc); while (stm.list[begin] != node::asm_jmp_cond) { init->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } auto test = std::move(stm.list[begin].as_cond->test); stm.list.erase(stm.list.begin() + begin); end -= 2 + init->list.size(); auto iter = make_stmt_list(loc); iter->list.push_back(std::move(stm.list[end])); stm.list.erase(stm.list.begin() + end); stm.list.erase(stm.list.begin() + end); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_for(loc, stmt { std::move(init) }, std::move(test), stmt { std::move(iter) }, stmt{ make_stmt_comp(loc, std::move(body)) }) }); } auto decompiler::decompile_foreach(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.last = false; locs_.end = stm.list[end - 1].label(); locs_.cnt = stm.list[end - 1].label(); locs_.brk = stm.list[begin].as_cond->value; for (begin -= 1; stm.list[begin] == node::asm_create; begin--); auto use_index = false; if ((ctx_->props() & props::foreach) && stm.list[begin] == node::stmt_assign && stm.list[begin].as_assign->value.as_assign->rvalue == node::expr_undefined) { use_index = true; begin--; } begin--; auto loc = stm.list[begin].loc(); auto container = std::move(stm.list[begin].as_assign->value.as_assign->rvalue); auto array = std::move(stm.list[begin].as_assign->value.as_assign->lvalue); stm.list.erase(stm.list.begin() + begin); auto key = std::move(stm.list[begin].as_assign->value.as_assign->lvalue); stm.list.erase(stm.list.begin() + begin); auto index = (use_index) ? std::move(stm.list[begin].as_assign->value.as_assign->lvalue): expr{ make_node() }; if (use_index) { stm.list.erase(stm.list.begin() + begin); } auto init = make_stmt_list(loc); while (stm.list[begin] != node::asm_jmp_cond) { init->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } stm.list.erase(stm.list.begin() + begin); auto value = std::move(stm.list[begin].as_assign->value.as_assign->lvalue); stm.list.erase(stm.list.begin() + begin); if (use_index) { stm.list.erase(stm.list.begin() + begin); } end -= (use_index ? 7 : 5) + init->list.size(); stm.list.erase(stm.list.begin() + end); stm.list.erase(stm.list.begin() + end); auto use_key = true; if (stm.list.size() > end && stm.list[end] == node::asm_clear) { stm.list.erase(stm.list.begin() + end); } if (stm.list.size() > end && stm.list[end] == node::asm_clear) { stm.list.erase(stm.list.begin() + end); use_key = false; } auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; body->list.insert(body->list.begin(), stmt{ std::move(init) }); stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_foreach(loc, std::move(container), std::move(value), std::move(index), std::move(array), std::move(key), stmt{ make_stmt_comp(loc, std::move(body)) }, (ctx_->props() & props::foreach) ? use_index : use_key) }); } auto decompiler::decompile_switch(stmt_list& stm, usize begin, usize end) -> void { auto const& data = stm.list[end].as_asm_endswitch->data; auto const count = std::stoul(data[0]); if (count) { auto type = static_cast(std::stoul(data.back())); auto index = 1u; for (auto i = 0u; i < count; i++) { if (data[index] == "case") { if (ctx_->engine() == engine::iw9) { type = static_cast(std::stoul(data[index + 1])); auto j = find_location_index(stm, data[index + 3]); auto loc = stm.list[j].loc(); auto exp = (type == switch_type::integer) ? expr{ make_expr_integer(loc, data[index + 2]) } : expr{ make_expr_string(loc, data[index + 2]) }; while (stm.list[j] == node::stmt_case) j++; stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_case(loc, std::move(exp), make_stmt_list(loc)) }); index += 4; } else { auto j = find_location_index(stm, data[index + 2]); auto loc = stm.list[j].loc(); auto exp = (type == switch_type::integer) ? expr{ make_expr_integer(loc, data[index + 1]) } : expr{ make_expr_string(loc, data[index + 1]) }; while (stm.list[j] == node::stmt_case) j++; stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_case(loc, std::move(exp), make_stmt_list(loc)) }); index += 3; } } else if (data[index] == "default") { auto j = find_location_index(stm, data[index + 1]); auto loc = stm.list[j].loc(); while (stm.list[j] == node::stmt_case) j++; stm.list.insert(stm.list.begin() + j, stmt{ make_stmt_default(loc, make_stmt_list(loc)) }); index += 2; } else { decomp_error("malformed endswitch statement"); } } end += count; } auto save = locs_; locs_.last = false; locs_.brk = last_location_index(stm, end) ? locs_.end : stm.list[end + 1].label(); locs_.end = stm.list[begin].as_asm_switch->value; auto loc = stm.list[begin].loc(); auto test = std::move(stm.list[begin].as_asm_switch->test); end--; stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + end); auto body = make_stmt_list(loc); for (auto i = begin; i < end; i++) { body->list.push_back(std::move(stm.list[begin])); stm.list.erase(stm.list.begin() + begin); } decompile_statements(*body); locs_ = save; auto temp = stmt{ make_node() }; for (auto i = 0u; i < body->list.size(); ) { if (body->list[i] == node::stmt_case || body->list[i] == node::stmt_default) { if (temp != node::null) { body->list.insert(body->list.begin() + i, std::move(temp)); i++; } temp = std::move(body->list[i]); body->list.erase(body->list.begin() + i); } else { if (temp == node::stmt_case) { temp.as_case->body->list.push_back(std::move(body->list[i])); body->list.erase(body->list.begin() + i); } else if (temp == node::stmt_default) { temp.as_default->body->list.push_back(std::move(body->list[i])); body->list.erase(body->list.begin() + i); } else { decomp_error("missing case or default before stmt inside a switch"); } } } if (temp != node::null) { body->list.push_back(std::move(temp)); } stm.list.insert(stm.list.begin() + begin, stmt{ make_stmt_switch(loc, std::move(test), make_stmt_comp(loc, std::move(body))) }); } auto decompiler::find_location_reference(stmt_list const& stm, usize begin, usize end, std::string const& loc) -> bool { for (auto i = begin; i < end; i++) { auto const& entry = stm.list.at(i); if (entry == node::asm_jmp_cond && entry.as_cond->value == loc) { return true; } else if (entry == node::asm_jmp && entry.as_jump->value == loc) { return true; } } return false; } auto decompiler::find_location_index(stmt_list const& stm, std::string const& loc) -> usize { auto index = 0u; if (loc == locs_.end) return stm.list.size(); for (auto const& entry : stm.list) { if (entry.label() == loc) return index; index++; } throw decomp_error(fmt::format("location '{}' not found", loc)); } auto decompiler::last_location_index(stmt_list const& stm, usize index) -> bool { return (index == stm.list.size() - 1) ? true : false; } auto decompiler::process_function(decl_function& func) -> void { auto scp_body = make_scope(); for (auto const& entry : func.params->list) { scp_body->vars.push_back({ entry->value, static_cast(scp_body->create_count), true }); scp_body->create_count++; } process_stmt_comp(*func.body, *scp_body); } auto decompiler::process_stmt(stmt& stm, scope& scp) -> void { switch (stm.kind()) { case node::stmt_list: process_stmt_list(*stm.as_list, scp); break; case node::stmt_comp: process_stmt_comp(*stm.as_comp, scp); break; case node::stmt_dev: process_stmt_dev(*stm.as_dev, scp); break; case node::stmt_expr: process_stmt_expr(*stm.as_expr, scp); break; case node::stmt_call: process_stmt_call(*stm.as_call, scp); break; case node::stmt_assign: process_stmt_assign(*stm.as_assign, scp); break; case node::stmt_endon: process_stmt_endon(*stm.as_endon, scp); break; case node::stmt_notify: process_stmt_notify(*stm.as_notify, scp); break; case node::stmt_wait: process_stmt_wait(*stm.as_wait, scp); break; case node::stmt_waittill: process_stmt_waittill(*stm.as_waittill, scp); break; case node::stmt_waittillmatch: process_stmt_waittillmatch(*stm.as_waittillmatch, scp); break; case node::stmt_if: process_stmt_if(*stm.as_if, scp); break; case node::stmt_ifelse: process_stmt_ifelse(*stm.as_ifelse, scp); break; case node::stmt_while: process_stmt_while(*stm.as_while, scp); break; case node::stmt_dowhile: process_stmt_dowhile(*stm.as_dowhile, scp); break; case node::stmt_for: process_stmt_for(*stm.as_for, scp); break; case node::stmt_foreach: process_stmt_foreach(*stm.as_foreach, scp); break; case node::stmt_switch: process_stmt_switch(*stm.as_switch, scp); break; case node::stmt_break: process_stmt_break(*stm.as_break, scp); break; case node::stmt_continue: process_stmt_continue(*stm.as_continue, scp); break; case node::stmt_return: process_stmt_return(*stm.as_return, scp); break; case node::asm_create: process_stmt_asm_create(*stm.as_asm_create, scp); break; case node::asm_remove: process_stmt_asm_remove(*stm.as_asm_remove, scp); break; default: break; } } auto decompiler::process_stmt_list(stmt_list& stm, scope& scp) -> void { for (auto& entry : stm.list) { process_stmt(entry, scp); } for (auto i = 0u; i < stm.list.size(); ) { if (stm.list[i] == node::asm_create || stm.list[i] == node::asm_remove) stm.list.erase(stm.list.begin() + i); else i++; } } auto decompiler::process_stmt_comp(stmt_comp& stm, scope& scp) -> void { process_stmt_list(*stm.block, scp); } auto decompiler::process_stmt_dev(stmt_dev& stm, scope& scp) -> void { process_stmt_list(*stm.block, scp); } auto decompiler::process_stmt_expr(stmt_expr& stm, scope& scp) -> void { switch (stm.value.kind()) { case node::expr_increment: process_expr_increment(*stm.value.as_increment, scp); break; case node::expr_decrement: process_expr_decrement(*stm.value.as_decrement, scp); break; case node::expr_assign_equal: case node::expr_assign_add: case node::expr_assign_sub: case node::expr_assign_mul: case node::expr_assign_div: case node::expr_assign_mod: case node::expr_assign_shift_left: case node::expr_assign_shift_right: case node::expr_assign_bitwise_or: case node::expr_assign_bitwise_and: case node::expr_assign_bitwise_exor: process_expr_assign(stm.value.as_assign, scp); break; default: break; } } auto decompiler::process_stmt_call(stmt_call& stm, scope& scp) -> void { switch (stm.value.kind()) { case node::expr_call: process_expr_call(*stm.value.as_call, scp); break; case node::expr_method: process_expr_method(*stm.value.as_method, scp); break; default: break; } } auto decompiler::process_stmt_assign(stmt_assign& stm, scope& scp) -> void { switch (stm.value.kind()) { case node::expr_increment: process_expr_increment(*stm.value.as_increment, scp); break; case node::expr_decrement: process_expr_decrement(*stm.value.as_decrement, scp); break; case node::expr_assign_equal: case node::expr_assign_add: case node::expr_assign_sub: case node::expr_assign_mul: case node::expr_assign_div: case node::expr_assign_mod: case node::expr_assign_shift_left: case node::expr_assign_shift_right: case node::expr_assign_bitwise_or: case node::expr_assign_bitwise_and: case node::expr_assign_bitwise_exor: process_expr_assign(stm.value.as_assign, scp); break; default: break; } } auto decompiler::process_stmt_endon(stmt_endon& stm, scope& scp) -> void { process_expr(stm.event, scp); process_expr(stm.obj, scp); } void decompiler::process_stmt_notify(stmt_notify& stm, scope& scp) { process_expr_arguments(*stm.args, scp); process_expr(stm.event, scp); process_expr(stm.obj, scp); } auto decompiler::process_stmt_wait(stmt_wait& stm, scope& scp) -> void { process_expr(stm.time, scp); } auto decompiler::process_stmt_waittill(stmt_waittill& stm, scope& scp) -> void { process_expr(stm.event, scp); process_expr(stm.obj, scp); for (auto& entry : stm.args->list) { process_expr(entry, scp); } } auto decompiler::process_stmt_waittillmatch(stmt_waittillmatch& stm, scope& scp) -> void { process_expr_arguments(*stm.args, scp); process_expr(stm.event, scp); process_expr(stm.obj, scp); } auto decompiler::process_stmt_if(stmt_if& stm, scope& scp) -> void { auto scp_then = make_scope(); process_expr(stm.test, scp); scp.transfer_dec(scp_then); process_stmt(stm.body, *scp_then); if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt()) { stm.body = std::move(stm.body.as_comp->block->list.back()); } } auto decompiler::process_stmt_ifelse(stmt_ifelse& stm, scope& scp) -> void { auto childs = std::vector{}; auto abort = scope::abort_return; auto scp_then = make_scope(); auto scp_else = make_scope(); process_expr(stm.test, scp); scp.transfer_dec(scp_then); process_stmt(stm.stmt_if, *scp_then); if (scp_then->abort <= scope::abort_return) { abort = scp_then->abort; if (abort == scope::abort_none) childs.push_back(scp_then.get()); } scp.transfer_dec(scp_else); process_stmt(stm.stmt_else, *scp_else); if (scp_else->abort <= abort) { abort = scp_else->abort; if (abort == scope::abort_none) childs.push_back(scp_else.get()); } if (scp.abort == scope::abort_none) scp.abort = abort; scp.append(childs); if (stm.stmt_if.as_comp->block->list.size() == 1 && !stm.stmt_if.as_comp->block->list[0].as_node->is_special_stmt()) { stm.stmt_if = std::move(stm.stmt_if.as_comp->block->list.back()); } if (stm.stmt_else.as_comp->block->list.size() == 1 && !stm.stmt_else.as_comp->block->list[0].as_node->is_special_stmt_noif()) { stm.stmt_else = std::move(stm.stmt_else.as_comp->block->list.back()); } } auto decompiler::process_stmt_while(stmt_while& stm, scope& scp) -> void { auto scp_body = make_scope(); process_expr(stm.test, scp); scp.transfer_dec(scp_body); process_stmt(stm.body, *scp_body); if (stm.test == node::null) scp.append_dec(scp_body); if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt()) { stm.body = std::move(stm.body.as_comp->block->list.back()); } } auto decompiler::process_stmt_dowhile(stmt_dowhile& stm, scope& scp) -> void { auto scp_body = make_scope(); process_expr(stm.test, scp); scp.transfer_dec(scp_body); process_stmt(stm.body, *scp_body); if (stm.test == node::null) scp.append_dec(scp_body); if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt()) { stm.body = std::move(stm.body.as_comp->block->list.back()); } } auto decompiler::process_stmt_for(stmt_for& stm, scope& scp) -> void { auto scp_body = make_scope(); process_stmt(stm.init, scp); if (stm.init == node::stmt_list && stm.init.as_list->list[0] == node::stmt_assign) { stm.init = stmt{ make_stmt_expr(stm.init.loc(), std::move(stm.init.as_list->list[0].as_assign->value)) }; } scp.transfer_dec(scp_body); process_expr(stm.test, scp); process_stmt(stm.body, *scp_body); process_stmt(stm.iter, scp); if (stm.iter == node::stmt_list && stm.iter.as_list->list[0] == node::stmt_assign) { stm.iter = stmt{ make_stmt_expr(stm.iter.loc(), std::move(stm.iter.as_list->list[0].as_assign->value)) }; } if (stm.test == node::null) scp.append_dec(scp_body); if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt()) { stm.body = std::move(stm.body.as_comp->block->list.back()); } } auto decompiler::process_stmt_foreach(stmt_foreach& stm, scope& scp) -> void { auto scp_body = make_scope(); process_expr(stm.container, scp); process_expr(stm.array, scp); process_expr(stm.key, scp); if ((ctx_->props() & props::foreach) && stm.use_key) { process_expr(stm.index, scp); } process_stmt(stm.body.as_comp->block->list[0], scp); stm.body.as_comp->block->list.erase(stm.body.as_comp->block->list.begin()); scp.transfer_dec(scp_body); process_expr(stm.value, *scp_body); process_stmt(stm.body, *scp_body); if (stm.body.as_comp->block->list.size() == 1 && !stm.body.as_comp->block->list[0].as_node->is_special_stmt()) { stm.body = std::move(stm.body.as_comp->block->list.back()); } } auto decompiler::process_stmt_switch(stmt_switch& stm, scope& scp) -> void { auto scopes = std::vector{}; auto childs = std::vector{}; auto has_default = false; process_expr(stm.test, scp); for (auto& entry : stm.body->block->list) { if (entry == node::stmt_case) { auto scp_case = make_scope(); scp.transfer_dec(scp_case); process_stmt_list(*entry.as_case->body, *scp_case); if (scp_case->abort == scope::abort_break) { childs.push_back(scp_case.get()); scopes.push_back(std::move(scp_case)); } } else if (entry == node::stmt_default) { has_default = true; auto scp_case = make_scope(); scp.transfer_dec(scp_case); process_stmt_list(*entry.as_default->body, *scp_case); if (scp_case->abort == scope::abort_break) { childs.push_back(scp_case.get()); scopes.push_back(std::move(scp_case)); } } } if (has_default) { scp.append(childs); } } auto decompiler::process_stmt_break(stmt_break&, scope& scp) -> void { if (scp.abort == scope::abort_none) { scp.abort = scope::abort_break; } } auto decompiler::process_stmt_continue(stmt_continue&, scope& scp) -> void { if (scp.abort == scope::abort_none) { scp.abort = scope::abort_continue; } } auto decompiler::process_stmt_return(stmt_return& stm, scope& scp) -> void { if (scp.abort == scope::abort_none) { scp.abort = scope::abort_return; } if (stm.value != node::null) { process_expr(stm.value, scp); } } auto decompiler::process_stmt_asm_create(asm_create& stm, scope& scp) -> void { auto var = (ctx_->props() & props::hash) ? stm.index : fmt::format("var_{}", stm.index); scp.vars.push_back({ var, static_cast(scp.create_count), true }); scp.create_count++; } auto decompiler::process_stmt_asm_remove(asm_remove& stm, scope& scp) -> void { scp.public_count = static_cast(scp.vars.size() - std::stoi(stm.index)); } auto decompiler::process_expr(expr& exp, scope& scp) -> void { switch (exp.kind()) { case node::expr_ternary: process_expr_ternary(*exp.as_ternary, scp); break; case node::expr_and: process_expr_and(*exp.as_and, scp); break; case node::expr_or: process_expr_or(*exp.as_or, scp); break; case node::expr_equality: case node::expr_inequality: case node::expr_less: case node::expr_greater: case node::expr_less_equal: case node::expr_greater_equal: case node::expr_bitwise_or: case node::expr_bitwise_and: case node::expr_bitwise_exor: case node::expr_shift_left: case node::expr_shift_right: case node::expr_add: case node::expr_sub: case node::expr_mul: case node::expr_div: case node::expr_mod: process_expr_binary(*exp.as_binary, scp); break; case node::expr_complement: process_expr_complement(*exp.as_complement, scp); break; case node::expr_not: process_expr_not(*exp.as_not, scp); break; case node::expr_call: process_expr_call(*exp.as_call, scp); break; case node::expr_method: process_expr_method(*exp.as_method, scp); break; case node::expr_isdefined: process_expr(exp.as_isdefined->value, scp); break; case node::expr_istrue: process_expr(exp.as_istrue->value, scp); break; case node::expr_add_array: process_expr_add_array(*exp.as_add_array, scp); break; case node::expr_size: process_expr_size(*exp.as_size, scp); break; case node::expr_tuple: process_expr_tuple(*exp.as_tuple, scp); break; case node::expr_array: process_expr_array(*exp.as_array, scp); break; case node::expr_field: process_expr_field(*exp.as_field, scp); break; case node::expr_vector: process_expr_vector(*exp.as_vector, scp); break; case node::asm_create: process_expr_asm_create(exp, scp); break; case node::asm_access: process_expr_asm_access(exp, scp); break; default: break; } } auto decompiler::process_expr_assign(expr_assign::ptr& exp, scope& scp) -> void { process_expr(exp->rvalue, scp); process_expr(exp->lvalue, scp); if (exp->kind() == node::expr_assign_equal) { switch (exp->rvalue.kind()) { case node::expr_bitwise_or: if (exp->lvalue == exp->rvalue.as_bitwise_or->lvalue) exp = make_expr_assign_bitwise_or(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_or->rvalue)); break; case node::expr_bitwise_and: if (exp->lvalue == exp->rvalue.as_bitwise_and->lvalue) exp = make_expr_assign_bitwise_and(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_and->rvalue)); break; case node::expr_bitwise_exor: if (exp->lvalue == exp->rvalue.as_bitwise_exor->lvalue) exp = make_expr_assign_bitwise_exor(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_bitwise_exor->rvalue)); break; case node::expr_shift_left: if (exp->lvalue == exp->rvalue.as_shift_left->lvalue) exp = make_expr_assign_shift_left(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_shift_left->rvalue)); break; case node::expr_shift_right: if (exp->lvalue == exp->rvalue.as_shift_right->lvalue) exp = make_expr_assign_shift_right(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_shift_right->rvalue)); break; case node::expr_add: if (exp->lvalue == exp->rvalue.as_add->lvalue) exp = make_expr_assign_add(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_add->rvalue)); break; case node::expr_sub: if (exp->lvalue == exp->rvalue.as_sub->lvalue) exp = make_expr_assign_sub(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_sub->rvalue)); break; case node::expr_mul: if (exp->lvalue == exp->rvalue.as_mul->lvalue) exp = make_expr_assign_mul(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_mul->rvalue)); break; case node::expr_div: if (exp->lvalue == exp->rvalue.as_div->lvalue) exp = make_expr_assign_div(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_div->rvalue)); break; case node::expr_mod: if (exp->lvalue == exp->rvalue.as_mod->lvalue) exp = make_expr_assign_mod(exp->loc(), std::move(exp->lvalue), std::move(exp->rvalue.as_mod->rvalue)); break; default: break; } } } auto decompiler::process_expr_increment(expr_increment& exp, scope& scp) -> void { process_expr(exp.lvalue, scp); } auto decompiler::process_expr_decrement(expr_decrement& exp, scope& scp) -> void { process_expr(exp.lvalue, scp); } auto decompiler::process_expr_ternary(expr_ternary& exp, scope& scp) -> void { process_expr(exp.test, scp); process_expr(exp.true_expr, scp); process_expr(exp.false_expr, scp); } auto decompiler::process_expr_binary(expr_binary& exp, scope& scp) -> void { process_expr(exp.lvalue, scp); process_expr(exp.rvalue, scp); auto prec = exp.lvalue.as_node->precedence(); if (prec && prec < exp.precedence()) { exp.lvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.lvalue)) }; } prec = exp.rvalue.as_node->precedence(); if ((prec && prec < exp.precedence()) || (prec == exp.precedence() && exp.kind() == exp.rvalue.as_node->kind())) { exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) }; } } auto decompiler::process_expr_and(expr_and& exp, scope& scp) -> void { process_expr(exp.lvalue, scp); process_expr(exp.rvalue, scp); auto prec = exp.lvalue.as_node->precedence(); if (prec && prec < exp.precedence()) { exp.lvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.lvalue)) }; } prec = exp.rvalue.as_node->precedence(); if ((prec && prec < exp.precedence()) || (prec == exp.precedence() && exp.kind() == exp.rvalue.kind())) { exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) }; } } auto decompiler::process_expr_or(expr_or& exp, scope& scp) -> void { process_expr(exp.lvalue, scp); process_expr(exp.rvalue, scp); } auto decompiler::process_expr_complement(expr_complement& exp, scope& scp) -> void { process_expr(exp.rvalue, scp); if (exp.rvalue.as_node->is_binary()) { exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) }; } } auto decompiler::process_expr_not(expr_not& exp, scope& scp) -> void { process_expr(exp.rvalue, scp); if (exp.rvalue.as_node->is_binary()) { exp.rvalue = expr{ make_expr_paren(exp.loc(), std::move(exp.rvalue)) }; } } auto decompiler::process_expr_call(expr_call& exp, scope& scp) -> void { switch (exp.value.kind()) { case node::expr_pointer: process_expr_call_pointer(*exp.value.as_pointer, scp); break; case node::expr_function: process_expr_call_function(*exp.value.as_function, scp); break; default: break; } } auto decompiler::process_expr_method(expr_method& exp, scope& scp) -> void { switch (exp.value.kind()) { case node::expr_pointer: process_expr_method_pointer(*exp.value.as_pointer, exp.obj, scp); break; case node::expr_function: process_expr_method_function(*exp.value.as_function, exp.obj, scp); break; default: break; } } auto decompiler::process_expr_call_pointer(expr_pointer& exp, scope& scp) -> void { process_expr_arguments(*exp.args, scp); process_expr(exp.func, scp); } auto decompiler::process_expr_call_function(expr_function& exp, scope& scp) -> void { process_expr_arguments(*exp.args, scp); } auto decompiler::process_expr_method_pointer(expr_pointer& exp, expr& obj, scope& scp) -> void { process_expr_arguments(*exp.args, scp); process_expr(obj, scp); process_expr(exp.func, scp); } auto decompiler::process_expr_method_function(expr_function& exp, expr& obj, scope& scp) -> void { process_expr_arguments(*exp.args, scp); process_expr(obj, scp); } auto decompiler::process_expr_arguments(expr_arguments& exp, scope& scp) -> void { for (auto i = exp.list.size(); i > 0; i--) { process_expr(exp.list[i - 1], scp); } } auto decompiler::process_expr_add_array(expr_add_array& exp, scope& scp) -> void { for (auto& entry : exp.args->list) { process_expr(entry, scp); } } auto decompiler::process_expr_size(expr_size& exp, scope& scp) -> void { process_expr(exp.obj, scp); } auto decompiler::process_expr_tuple(expr_tuple& exp, scope& scp) -> void { process_expr(exp.temp, scp); for (auto& entry : exp.list) { process_expr(entry, scp); } } auto decompiler::process_expr_array(expr_array& exp, scope& scp) -> void { process_expr(exp.key, scp); process_expr(exp.obj, scp); } auto decompiler::process_expr_field(expr_field& exp, scope& scp) -> void { process_expr(exp.obj, scp); } auto decompiler::process_expr_vector(expr_vector& exp, scope& scp) -> void { process_expr(exp.z, scp); process_expr(exp.y, scp); process_expr(exp.x, scp); } auto decompiler::process_expr_asm_create(expr& exp, scope& scp) -> void { for (auto const& entry : exp.as_asm_create->vars) { auto var = (ctx_->props() & props::hash) ? entry : fmt::format("var_{}", entry); scp.vars.push_back({ var, static_cast(scp.create_count), true }); scp.create_count++; } auto var = (ctx_->props() & props::hash) ? exp.as_asm_create->index : fmt::format("var_{}", exp.as_asm_create->index); scp.vars.push_back({ var, static_cast(scp.create_count), true }); scp.create_count++; exp = expr{ make_expr_identifier(exp.loc(), var) }; } auto decompiler::process_expr_asm_access(expr& exp, scope& scp) -> void { auto const index = std::stoul(exp.as_asm_access->index); if (scp.vars.size() <= index) { std::cout << fmt::format("WARNING: bad local var access\n"); } else { exp = expr{ make_expr_identifier(exp.loc(), scp.vars[scp.vars.size() - 1 - index].name) }; } } } // namespace xsk::gsc