// 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 "xsk/stdinc.hpp" #include "xsk/utils/string.hpp" #include "xsk/arc/decompiler.hpp" #include "xsk/arc/context.hpp" namespace xsk::arc { decompiler::decompiler(context const* ctx) : ctx_{ ctx } { } auto decompiler::decompile(assembly const& data) -> program::ptr { program_ = program::make(); namespace_ = {}; for (auto it = data.includes.rbegin(); it != data.includes.rend(); it++) { program_->includes.push_back(include::make(location{}, expr_path::make(location{}, *it))); } 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(); locals_.clear(); params_.clear(); vars_.clear(); labels_ = func.labels; in_waittill_ = false; retbool_ = true; locs_ = {}; stack_ = {}; auto loc = location{ nullptr, static_cast(func.index) }; auto space = expr_identifier::make(loc, func.space); auto name = expr_identifier::make(loc, func.name); auto prms = expr_parameters::make(loc); auto body = stmt_comp::make(loc, stmt_list::make(loc)); auto flags = static_cast(func.flags); func_ = decl_function::make(loc, std::move(space), std::move(name), std::move(prms), std::move(body), flags); for (auto const& inst : func.instructions) { decompile_instruction(*inst, &inst == &func.instructions.back()); } for (auto i = 0u; i < func.params; i++) { auto const& param = locals_.at(locals_.size() - 1 - i); if (ctx_->props() & props::refvarg) { auto const& type = params_.at(params_.size() - 1 - i); if (type == param_type::reference) func_->params->list.push_back(expr_reference::make(loc, expr_path::make(loc), expr_identifier::make(loc, param))); else if (type == param_type::vararg) func_->params->list.push_back(expr_ellipsis::make(loc)); else func_->params->list.push_back(expr_identifier::make(loc, param)); } else { func_->params->list.push_back(expr_identifier::make(loc, param)); } } if (!stack_.empty()) { throw decomp_error("stack isn't empty at function end"); } auto& list = func_->body->block->list; locs_.end = fmt::format("loc_{:X}", list.back()->loc().begin.line + 1); decompile_statements(*func_->body->block); if (!list.empty() && list.back()->is()) { if (list.back()->as().value->is()) list.pop_back(); } process_function(*func_); program_->declarations.push_back(std::move(func_)); } auto decompiler::decompile_instruction(instruction const& inst, bool last) -> void { decompile_expressions(inst); auto loc = location{ nullptr, static_cast(inst.index) }; switch (inst.opcode) { case opcode::OP_End: { retbool_ = !last ? false : retbool_; func_->body->block->list.push_back(stmt_return::make(loc, expr_empty::make(loc))); break; } case opcode::OP_Return: { auto value = node::as(std::move(stack_.top())); stack_.pop(); retbool_ = !value->is_boolean() ? false : retbool_; func_->body->block->list.push_back(stmt_return::make(value->loc(), std::move(value))); break; } case opcode::OP_GetZero: { stack_.push(expr_integer::make(loc, "0")); break; } case opcode::OP_GetByte: case opcode::OP_GetUnsignedShort: { stack_.push(expr_integer::make(loc, inst.data[0])); break; } case opcode::OP_GetNegByte: case opcode::OP_GetNegUnsignedShort: { stack_.push(expr_integer::make(loc, "-" + inst.data[0])); break; } case opcode::OP_GetInteger: { if (inst.data.size() == 2) { auto found = false; for (auto i = program_->declarations.rbegin(); i != program_->declarations.rend(); i++) { if ((*i)->is()) { found = (*i)->as().name->value == inst.data[0]; break; } } if (!found) { auto dec = decl_usingtree::make(loc, expr_string::make(loc, inst.data[0])); program_->declarations.push_back(std::move(dec)); } stack_.push(expr_animtree::make(loc)); } else { stack_.push(expr_integer::make(loc, inst.data[0])); } break; } case opcode::OP_GetFloat: { stack_.push(expr_float::make(loc, inst.data[0])); break; } case opcode::OP_GetVector: { auto x = expr_float::make(loc, inst.data[0]); auto y = expr_float::make(loc, inst.data[1]); auto z = expr_float::make(loc, inst.data[2]); stack_.push(expr_vector::make(loc, std::move(x), std::move(y), std::move(z))); break; } case opcode::OP_GetString: { stack_.push(expr_string::make(loc, inst.data[0])); break; } case opcode::OP_GetIString: { stack_.push(expr_istring::make(loc, inst.data[0])); break; } case opcode::OP_GetUndefined: case opcode::OP_SafeDecTop: { stack_.push(expr_undefined::make(loc)); break; } case opcode::OP_EmptyArray: { stack_.push(expr_empty_array::make(loc)); break; } case opcode::OP_GetClasses: case opcode::OP_GetClassesObject: { stack_.push(expr_classes::make(loc)); break; } case opcode::OP_GetWorld: case opcode::OP_GetWorldObject: { stack_.push(expr_world::make(loc)); break; } case opcode::OP_GetLevel: case opcode::OP_GetLevelObject: { stack_.push(expr_level::make(loc)); break; } case opcode::OP_GetAnim: case opcode::OP_GetAnimObject: { stack_.push(expr_anim::make(loc)); break; } case opcode::OP_GetSelf: case opcode::OP_GetSelfObject: { stack_.push(expr_self::make(loc)); break; } case opcode::OP_GetGame: case opcode::OP_GetGameRef: { stack_.push(expr_game::make(loc)); break; } case opcode::OP_GetAnimation: { auto found = false; for (auto i = program_->declarations.rbegin(); i != program_->declarations.rend(); i++) { if ((*i)->is()) { found = (*i)->as().name->value == inst.data[0]; break; } } if (!found) { auto dec = decl_usingtree::make(loc, expr_string::make(loc, inst.data[0])); program_->declarations.push_back(std::move(dec)); } stack_.push(expr_animation::make(loc, inst.data[1])); break; } case opcode::OP_GetFunction: { auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); stack_.push(expr_reference::make(loc, std::move(path), std::move(name))); break; } case opcode::OP_SafeCreateLocalVariables: { if (ctx_->props() & props::refvarg) { for (auto i = 0u; i < inst.data.size(); i += 2) { locals_.insert(locals_.begin(), inst.data[i]); params_.insert(params_.begin(), static_cast(std::stoul(inst.data[i + 1]))); } } else { for (const auto& entry : inst.data) locals_.insert(locals_.begin(), entry); } break; } case opcode::OP_EvalLocalVariableCached: { stack_.push(expr_identifier::make(loc, locals_.at(std::stoi(inst.data[0])))); break; } case opcode::OP_EvalArray: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto key = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_array::make(key->loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalLocalArrayRefCached: { auto key = node::as(std::move(stack_.top())); stack_.pop(); auto obj = expr_identifier::make(loc, locals_.at(std::stoi(inst.data[0]))); stack_.push(expr_array::make(key->loc(), std::move(obj), std::move(key))); break; } case opcode::OP_EvalArrayRef: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto key = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_array::make(key->loc(), std::move(obj), std::move(key))); break; } case opcode::OP_ClearArray: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto key = node::as(std::move(stack_.top())); stack_.pop(); loc = key->loc(); auto lvalue = expr_array::make(loc, std::move(obj), std::move(key)); auto rvalue = expr_undefined::make(loc); auto exp = expr_assign::make(loc, std::move(lvalue), std::move(rvalue), expr_assign::op::eq); func_->body->block->list.push_back(stmt_expr::make(loc, std::move(exp))); break; } case opcode::OP_EvalFieldVariable: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto field = expr_identifier::make(loc, inst.data[0]); stack_.push(expr_field::make(obj->loc(), std::move(obj), std::move(field))); break; } case opcode::OP_EvalFieldVariableRef: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto field = expr_identifier::make(loc, inst.data[0]); stack_.push(expr_field::make(obj->loc(), std::move(obj), std::move(field))); break; } case opcode::OP_ClearFieldVariable: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); loc = obj->loc(); auto name = expr_identifier::make(loc, inst.data[0]); auto field = expr_field::make(loc, std::move(obj), std::move(name)); auto undef = expr_undefined::make(loc); auto exp = expr_assign::make(loc, std::move(field), std::move(undef), expr_assign::op::eq); func_->body->block->list.push_back(stmt_expr::make(loc, std::move(exp))); break; } case opcode::OP_SafeSetWaittillVariableFieldCached: { stack_.push(expr_identifier::make(loc, locals_.at(std::stoi(inst.data[0])))); break; } case opcode::OP_ClearParams: { if (in_waittill_) { auto args = expr_arguments::make(loc); auto arg = std::move(stack_.top()); stack_.pop(); while (arg->kind() != node::stmt_waittill) { args->list.push_back(node::as(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(node::as(std::move(arg))); } break; } case opcode::OP_EvalLocalVariableRefCached: { stack_.push(expr_identifier::make(loc, locals_.at(std::stoi(inst.data[0])))); break; } case opcode::OP_SetVariableField: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); loc = rvalue->loc(); auto exp = expr_assign::make(loc, std::move(lvalue), std::move(rvalue), expr_assign::op::eq); func_->body->block->list.push_back(stmt_expr::make(loc, std::move(exp))); break; } case opcode::OP_CallBuiltin: { auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_CallBuiltinMethod: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); loc = obj->loc(); auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_method::make(loc, std::move(obj), expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_Wait: { auto exp = node::as(std::move(stack_.top())); stack_.pop(); func_->body->block->list.push_back(stmt_wait::make(exp->loc(), std::move(exp))); break; } case opcode::OP_WaitTillFrameEnd: { func_->body->block->list.push_back(stmt_waittillframeend::make(loc)); break; } case opcode::OP_PreScriptCall: { stack_.push(node_prescriptcall::make(loc)); break; } case opcode::OP_ScriptFunctionCall: { auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_ScriptFunctionCallPointer: { auto args = expr_arguments::make(loc); auto func = node::as(std::move(stack_.top())); stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_pointer::make(loc, std::move(func), std::move(args), call::mode::normal))); break; } case opcode::OP_ScriptMethodCall: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); loc = obj->loc(); auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_method::make(loc, std::move(obj), expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_ScriptMethodCallPointer: { auto args = expr_arguments::make(loc); auto func = node::as(std::move(stack_.top())); stack_.pop(); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_method::make(loc, std::move(obj), expr_pointer::make(loc, std::move(func), std::move(args), call::mode::normal))); break; } case opcode::OP_ScriptThreadCall: { auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::thread))); break; } case opcode::OP_ScriptThreadCallPointer: { auto args = expr_arguments::make(loc); auto func = node::as(std::move(stack_.top())); stack_.pop(); loc = func->loc(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_pointer::make(loc, std::move(func), std::move(args), call::mode::thread))); break; } case opcode::OP_ScriptMethodThreadCall: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); loc = obj->loc(); auto args = expr_arguments::make(loc); auto path = expr_path::make(loc, inst.data[0]); auto name = expr_identifier::make(loc, inst.data[1]); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_method::make(loc, std::move(obj), expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::thread))); break; } case opcode::OP_ScriptMethodThreadCallPointer: { auto args = expr_arguments::make(loc); auto func = node::as(std::move(stack_.top())); stack_.pop(); auto obj = node::as(std::move(stack_.top())); stack_.pop(); loc = obj->loc(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_method::make(loc, std::move(obj), expr_pointer::make(loc, std::move(func), std::move(args), call::mode::thread))); break; } case opcode::OP_DecTop: { if (stack_.top()->kind() != node::expr_new) { auto exp = node::as(std::move(stack_.top())); stack_.pop(); func_->body->block->list.push_back(stmt_expr::make(exp->loc(), std::move(exp))); } break; } case opcode::OP_BoolNot: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_not::make(lvalue->loc(), std::move(lvalue))); break; } case opcode::OP_BoolComplement: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_complement::make(lvalue->loc(), std::move(lvalue))); break; } case opcode::OP_JumpOnTrue: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); loc = lvalue->loc(); if (inst.index > resolve_label(inst.data[0])) { func_->body->block->list.push_back(stmt_jmp_cond::make(loc, std::move(lvalue), inst.data[0])); } else { auto test = expr_not::make(loc, std::move(lvalue)); func_->body->block->list.push_back(stmt_jmp_cond::make(loc, std::move(test), inst.data[0])); } break; } case opcode::OP_JumpOnFalse: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); loc = lvalue->loc(); if (inst.index > resolve_label(inst.data[0])) { auto test = expr_not::make(loc, std::move(lvalue)); func_->body->block->list.push_back(stmt_jmp_cond::make(loc, std::move(test), inst.data[0])); } else { func_->body->block->list.push_back(stmt_jmp_cond::make(lvalue->loc(), std::move(lvalue), inst.data[0])); } break; } case opcode::OP_JumpOnTrueExpr: { auto test = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(stmt_jmp_true::make(test->loc(), std::move(test), inst.data[0])); expr_labels_.push_back(inst.data[0]); break; } case opcode::OP_JumpOnFalseExpr: { auto test = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(stmt_jmp_false::make(test->loc(), std::move(test), inst.data[0])); expr_labels_.push_back(inst.data[0]); break; } case opcode::OP_Jump: { func_->body->block->list.push_back(stmt_jmp::make(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_jmp_back::make(loc, inst.data[0])); break; } case opcode::OP_Inc: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); loc = lvalue->loc(); auto exp = expr_increment::make(lvalue->loc(), std::move(lvalue), false); func_->body->block->list.push_back(stmt_expr::make(loc, std::move(exp))); break; } case opcode::OP_Dec: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); loc = lvalue->loc(); auto exp = expr_decrement::make(loc, std::move(lvalue), false); func_->body->block->list.push_back(stmt_expr::make(loc, std::move(exp))); break; } case opcode::OP_Bit_Or: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::bwor)); break; } case opcode::OP_Bit_Xor: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::bwexor)); break; } case opcode::OP_Bit_And: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::bwand)); break; } case opcode::OP_SuperEqual: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::seq)); break; } case opcode::OP_SuperNotEqual: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::sne)); break; } case opcode::OP_Equal: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::eq)); break; } case opcode::OP_NotEqual: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::ne)); break; } case opcode::OP_LessThan: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::lt)); break; } case opcode::OP_GreaterThan: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::gt)); break; } case opcode::OP_LessThanOrEqualTo: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::le)); break; } case opcode::OP_GreaterThanOrEqualTo: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::ge)); break; } case opcode::OP_ShiftLeft: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::shl)); break; } case opcode::OP_ShiftRight: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::shr)); break; } case opcode::OP_Plus: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::add)); break; } case opcode::OP_Minus: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::sub)); break; } case opcode::OP_Multiply: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::mul)); break; } case opcode::OP_Divide: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::div)); break; } case opcode::OP_Modulus: { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_binary::make(lvalue->loc(), std::move(lvalue), std::move(rvalue), expr_binary::op::mod)); break; } case opcode::OP_SizeOf: { auto lvalue = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_size::make(lvalue->loc(), std::move(lvalue))); break; } case opcode::OP_WaitTillMatch: { auto args = expr_arguments::make(loc); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto event = node::as(std::move(stack_.top())); stack_.pop(); loc = event->loc(); for (auto i = std::stoul(inst.data[0]); i > 0; i--) { auto arg = node::as(std::move(stack_.top())); stack_.pop(); loc = arg->loc(); args->list.push_back(std::move(arg)); } func_->body->block->list.push_back(stmt_waittillmatch::make(loc, std::move(obj), std::move(event), std::move(args))); break; } case opcode::OP_WaitTill: { auto args = expr_arguments::make(loc); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto event = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(stmt_waittill::make(event->loc(), std::move(obj), std::move(event), std::move(args))); in_waittill_ = true; break; } case opcode::OP_Notify: { auto args = expr_arguments::make(loc); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto event = node::as(std::move(stack_.top())); stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != ((ctx_->props() & props::size64) ? node::node_prescriptcall : node::node_voidcodepos)) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } func_->body->block->list.push_back(stmt_notify::make(loc, std::move(obj), std::move(event), std::move(args))); break; } case opcode::OP_EndOn: { auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto event = node::as(std::move(stack_.top())); stack_.pop(); func_->body->block->list.push_back(stmt_endon::make(event->loc(), std::move(obj), std::move(event))); break; } case opcode::OP_VoidCodePos: { stack_.push(node_voidcodepos::make(loc)); break; } case opcode::OP_Switch: { auto test = node::as(std::move(stack_.top())); stack_.pop(); func_->body->block->list.push_back(stmt_jmp_switch::make(test->loc(), std::move(test), inst.data[0])); break; } case opcode::OP_EndSwitch: { func_->body->block->list.push_back(stmt_jmp_endswitch::make(loc, inst.data)); break; } case opcode::OP_Vector: { auto x = node::as(std::move(stack_.top())); stack_.pop(); auto y = node::as(std::move(stack_.top())); stack_.pop(); auto z = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_vector::make(z->loc(), std::move(x), std::move(y), std::move(z))); break; } case opcode::OP_GetHash: { stack_.push(expr_hash::make(loc, inst.data[0])); break; } case opcode::OP_RealWait: { auto exp = node::as(std::move(stack_.top())); stack_.pop(); func_->body->block->list.push_back(stmt_waitrealtime::make(exp->loc(), std::move(exp))); break; } case opcode::OP_VectorConstant: { auto flags = static_cast(std::stoi(inst.data[0])); auto x = expr_float::make(loc, (flags & 0x20) ? "1" : (flags & 0x10) ? "-1" : "0"); auto y = expr_float::make(loc, (flags & 0x08) ? "1" : (flags & 0x04) ? "-1" : "0"); auto z = expr_float::make(loc, (flags & 0x02) ? "1" : (flags & 0x01) ? "-1" : "0"); stack_.push(expr_vector::make(loc, std::move(x), std::move(y), std::move(z))); break; } case opcode::OP_IsDefined: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_isdefined::make(arg->loc(), std::move(arg))); break; } case opcode::OP_VectorScale: { auto arg1 = node::as(std::move(stack_.top())); stack_.pop(); auto arg2 = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_vectorscale::make(arg2->loc(), std::move(arg1), std::move(arg2))); break; } case opcode::OP_AnglesToUp: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_anglestoup::make(arg->loc(), std::move(arg))); break; } case opcode::OP_AnglesToRight: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_anglestoright::make(arg->loc(), std::move(arg))); break; } case opcode::OP_AnglesToForward: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_anglestoforward::make(arg->loc(), std::move(arg))); break; } case opcode::OP_AngleClamp180: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_angleclamp180::make(arg->loc(), std::move(arg))); break; } case opcode::OP_VectorToAngles: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_vectortoangles::make(arg->loc(), std::move(arg))); break; } case opcode::OP_Abs: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_abs::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetTime: { stack_.push(expr_gettime::make(loc)); break; } case opcode::OP_GetDvar: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvar::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarInt: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarint::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarFloat: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarfloat::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarVector: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarvector::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarColorRed: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarcolorred::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarColorGreen: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarcolorgreen::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarColorBlue: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarcolorblue::make(arg->loc(), std::move(arg))); break; } case opcode::OP_GetDvarColorAlpha: { auto arg = node::as(std::move(stack_.top())); stack_.pop(); stack_.push(expr_getdvarcoloralpha::make(arg->loc(), std::move(arg))); break; } case opcode::OP_FirstArrayKey: { auto args = expr_arguments::make(loc); auto path = expr_path::make(loc); auto name = expr_identifier::make(loc, "getfirstarraykey"); auto var = node::as(std::move(stack_.top())); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); stack_.push(expr_call::make(loc, expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_NextArrayKey: { auto args = expr_arguments::make(loc); auto path = expr_path::make(loc); auto name = expr_identifier::make(loc, "getnextarraykey"); auto var = node::as(std::move(stack_.top())); stack_.pop(); args->list.push_back(std::move(var)); var = node::as(std::move(stack_.top())); stack_.pop(); loc = var->loc(); args->list.push_back(std::move(var)); stack_.push(expr_call::make(loc, expr_function::make(loc, std::move(path), std::move(name), std::move(args), call::mode::normal))); break; } case opcode::OP_LevelEvalFieldVariable: case opcode::OP_LevelEvalFieldVariableRef: { auto obj = expr_level::make(loc); auto field = expr_identifier::make(loc, inst.data[0]); stack_.push(expr_field::make(loc, std::move(obj), std::move(field))); break; } case opcode::OP_SelfEvalFieldVariable: case opcode::OP_SelfEvalFieldVariableRef: { auto obj = expr_self::make(loc); auto field = expr_identifier::make(loc, inst.data[0]); stack_.push(expr_field::make(loc, std::move(obj), std::move(field))); break; } case opcode::OP_DevblockBegin: { func_->body->block->list.push_back(stmt_jmp_dev::make(loc, inst.data[0])); break; } case opcode::OP_New: { stack_.push(expr_new::make(loc, expr_identifier::make(loc, inst.data[0]))); break; } case opcode::OP_ScriptFunctionCallClass: { if (stack_.top()->kind() != node::expr_new || inst.data[0] != "__constructor") { auto args = expr_arguments::make(loc); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_member::make(loc, std::move(obj), expr_identifier::make(loc, inst.data[0]), std::move(args), call::mode::normal))); } break; } case opcode::OP_ScriptThreadCallClass: { if (stack_.top()->kind() != node::expr_new) { auto args = expr_arguments::make(loc); auto obj = node::as(std::move(stack_.top())); stack_.pop(); auto var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); while (var->kind() != node::node_prescriptcall) { args->list.push_back(node::as(std::move(var))); var = std::move(stack_.top()); stack_.pop(); loc = var->loc(); } stack_.push(expr_call::make(loc, expr_member::make(loc, std::move(obj), expr_identifier::make(loc, inst.data[0]), std::move(args), call::mode::thread))); } break; } case opcode::OP_CheckClearParams: case opcode::OP_CastFieldObject: case opcode::OP_CastBool: break; case opcode::OP_CreateLocalVariable: case opcode::OP_RemoveLocalVariables: case opcode::OP_SafeSetVariableFieldCached: case opcode::OP_ProfileStart: case opcode::OP_ProfileStop: case opcode::OP_Nop: case opcode::OP_Abort: case opcode::OP_Object: case opcode::OP_ThreadObject: case opcode::OP_EvalLocalVariable: case opcode::OP_EvalLocalVariableRef: case opcode::OP_DevblockEnd: case opcode::OP_GetUintptr: case opcode::OP_GetAPIFunction: case opcode::OP_EvalLocalVariableCachedDebug: case opcode::OP_EvalLocalVariableRefCachedDebug: 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 = node::as(std::move(stack_.top())); stack_.pop(); auto jump = std::move(stack_.top()); stack_.pop(); auto loc = jump->loc(); if (jump->kind() == node::stmt_jmp_true) { auto lvalue = std::move(reinterpret_cast(jump.get())->test); stack_.push(expr_binary::make(loc, std::move(lvalue), std::move(rvalue), expr_binary::op::bool_or)); } else if (jump->kind() == node::stmt_jmp_false) { auto lvalue = std::move(reinterpret_cast(jump.get())->test); stack_.push(expr_binary::make(loc, std::move(lvalue), std::move(rvalue), expr_binary::op::bool_and)); } else { throw decomp_error("invalid conditional expression"); } } } for (auto const& tern : tern_labels_) { if (tern == itr->second) { auto rvalue = node::as(std::move(stack_.top())); stack_.pop(); auto lvalue = node::as(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->is()) { auto loc = stm->as().loc(); stack_.push(expr_ternary::make(loc, std::move(stm->as().test), std::move(lvalue), std::move(rvalue))); } else { throw decomp_error("invalid ternary expression"); } } } } auto decompiler::decompile_statements(stmt_list& stm) -> void { decompile_infinites(stm); decompile_loops(stm); decompile_switches(stm); decompile_ifelses(stm); decompile_aborts(stm); decompile_devblocks(stm); } auto decompiler::decompile_infinites(stmt_list& stm) -> void { if (stm.list.empty()) return; for (auto i = static_cast(stm.list.size() - 1); i > 0; i--) { if (stm.list.at(i)->is()) { if (stm.list.at(i)->loc().begin.line < std::stol(stm.list.at(i)->as().value.substr(4), 0, 16)) continue; if (stm.list.at(i)->as().value == locs_.cnt) continue; 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().value); if (stm.list.at(i - 1)->is() && stm.list.at(i - 1)->as().value == break_loc) { continue; } else if (!stm.list.at(start)->is()) { decompile_inf(stm, start, i); i = static_cast(stm.list.size()); } else if (stm.list.at(start)->as().value != break_loc) { decompile_inf(stm, start, i); i = static_cast(stm.list.size()); } else if (stm.list.at(start)->as().value == break_loc) { decompile_loop(stm, start, i); i = static_cast(stm.list.size()); } } } } auto decompiler::decompile_loops(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { auto& entry = stm.list.at(i); if (entry->is()) { auto j = find_location_index(stm, entry->as().value); if (j < i) { decompile_dowhile(stm, j, i); i = 0; continue; } j -= 1; if (stm.list.at(j)->is()) { if (stm.list.at(j)->loc().begin.line < std::stol(stm.list.at(j)->as().value.substr(4), 0, 16)) continue; if (stm.list.at(i)->loc().label() == stm.list.at(j)->as().value) { decompile_loop(stm, i, j); i = 0; } } } } } auto decompiler::decompile_switches(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { if (stm.list[i]->is()) { auto end = find_location_index(stm, stm.list[i]->as().value); decompile_switch(stm, i, end); } } } auto decompiler::decompile_ifelses(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { const auto& entry = stm.list.at(i); if (entry->is()) { auto j = find_location_index(stm, entry->as().value) - 1; auto last_loc = locs_.end; if (stm.list.at(j)->is()) { // if block is a loop check break, continue if (stm.list.at(j)->as().value == locs_.cnt) { //if its a while, continue jumps back if (stm.list.at(j)->loc().begin.line > std::stol(stm.list.at(j)->as().value.substr(4), 0, 16)) { decompile_if(stm, i, j); } // a dowhile, for or foreach, check for if/else or if/continue else if (j - i > 1 && stm.list.at(j - 1)->is()) { // block 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)->is()) { if (stm.list.at(j - 1)->as().value == locs_.brk) { // block ends with a break, so jump belows to if/else decompile_ifelse(stm, i, j); } else if (stm.list.at(j - 1)->as().value == locs_.cnt) { // block 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 { decompile_if(stm, i, j); } } else if (stm.list.at(j)->as().value == locs_.brk) { decompile_if(stm, i, j); } else if (stm.list.at(j)->as().value == locs_.end) { if (find_location_reference(stm, i + 1, j, entry->as().value)) decompile_if(stm, i, j); else decompile_ifelse(stm, i, j); } else if (stm.list.at(j)->as().value == entry->as().value) { if (find_location_reference(stm, i + 1, j, entry->as().value)) decompile_if(stm, i, j); else decompile_ifelse(stm, i, j); } else { decompile_ifelse(stm, i, j); } } else { decompile_if(stm, i, j); } } } } auto decompiler::decompile_aborts(stmt_list& stm) -> void { for (auto i = static_cast>(stm.list.size() - 1); i >= 0; i--) { if (stm.list[i]->is()) { auto const loc = stm.list[i]->loc(); auto const jmp = stm.list[i]->as().value; if (jmp == locs_.brk) { stm.list.erase(stm.list.begin() + i); stm.list.insert(stm.list.begin() + i, stmt_break::make(loc)); } else if (jmp == locs_.cnt) { stm.list.erase(stm.list.begin() + i); stm.list.insert(stm.list.begin() + i, stmt_continue::make(loc)); } else { // fix for treyarch compiler bug: nested switch break locs are not preserved if (jmp != locs_.end) { auto j = find_location_index(stm, jmp); if (stm.list.at(j)->is()) { stm.list.erase(stm.list.begin() + i); stm.list.insert(stm.list.begin() + i, stmt_break::make(loc)); continue; } } std::cout << "WARNING: unresolved jump to '" + jmp + "', maybe incomplete for loop\n"; } } } } auto decompiler::decompile_devblocks(stmt_list& stm) -> void { for (auto i = 0u; i < stm.list.size(); i++) { if (stm.list.at(i)->is()) { if (locs_.is_dev) { stm.list.erase(stm.list.begin() + i--); continue; } if (i + 1 < stm.list.size()) { if (stm.list.at(i + 1)->is() && stm.list.at(i + 1)->as().value == stm.list.at(i)->as().value) { stm.list.erase(stm.list.begin() + i + 1); } } auto loc = stm.list.at(i)->loc(); auto end = find_location_index(stm, stm.list.at(i)->as().value); auto save = locs_; locs_.end = stm.list.at(i)->as().value; locs_.is_dev = true; stm.list.erase(stm.list.begin() + i); end -= 1; auto list_stmt = stmt_list::make(loc); for (auto j = i; j < end; j++) { list_stmt->list.push_back(std::move(stm.list[i])); stm.list.erase(stm.list.begin() + i); } decompile_statements(*list_stmt); locs_ = save; if (list_stmt->list.size() == 1 && list_stmt->list.at(0)->is()) { const auto& st = list_stmt->list.at(0); if (st->as().value->is() && st->as().value->as().value->is()) { if (st->as().value->as().value->as().name->value == "assert") { stm.list.insert(stm.list.begin() + i, std::move(list_stmt->list.at(0))); continue; } } } auto new_stmt = stmt_dev::make(loc, std::move(list_stmt)); stm.list.insert(stm.list.begin() + i, std::move(new_stmt)); } } } auto decompiler::decompile_if(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[begin]->as().value; auto loc = stm.list[begin]->loc(); auto test = std::move(stm.list[begin]->as().test); stm.list.erase(stm.list.begin() + begin); auto body = stmt_list::make(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_if::make(loc, std::move(test), stmt_comp::make(loc, std::move(body)))); } auto decompiler::decompile_ifelse(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[end]->label(); auto loc = stm.list[begin]->loc(); auto test = std::move(stm.list[begin]->as().test); stm.list.erase(stm.list.begin() + begin); end--; auto body_if = stmt_list::make(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().value; stm.list.erase(stm.list.begin() + begin); end = find_location_index(stm, end_loc); save = locs_; locs_.end = end_loc; auto body_else = stmt_list::make(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_ifelse::make(loc, std::move(test), stmt_comp::make(loc, std::move(body_if)), stmt_comp::make(loc, std::move(body_else)))); } auto decompiler::decompile_inf(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; 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 = stmt_list::make(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_for::make(loc, stmt_empty::make(loc), expr_empty::make(loc), stmt_empty::make(loc), stmt_comp::make(loc, std::move(body)))); } auto decompiler::decompile_loop(stmt_list& stm, usize begin, usize end) -> void { if (ctx_->props() & props::foreach && (begin + 2 < end)) { auto& last = stm.list.at(begin + 2); if (last->is()) { if (last->as().value->is()) { auto& val = last->as().value->as().rvalue; if (val->is() && val->as().value->is()) { if (utils::string::to_lower(val->as().value->as().name->value) == "getnextarraykey") { auto ref = stm.list.at(begin)->label(); if (!find_location_reference(stm, 0, begin, ref)) { decompile_foreach(stm, begin, end); return; } } } } } } auto& last = stm.list.at(end - 1); if (last->is()) { if (last->as().value->is()) { auto& val = last->as().value->as().rvalue; if (val->is() && val->as().value->is()) { if (utils::string::to_lower(val->as().value->as().name->value) == "getnextarraykey") { auto ref = stm.list.at(begin)->label(); if (!find_location_reference(stm, 0, begin, ref)) { decompile_foreach(stm, begin, end); return; } } } } if (begin > 0 && stm.list.at(begin - 1)->is()) // while at func start { if (stm.list.at(begin - 1)->as().value->is_assign()) { if (!lvalues_match(stm.list.at(begin - 1)->as(), last->as())) { decompile_while(stm, begin, end); return; } auto ref1 = stm.list.at(end)->label(); auto ref2 = stm.list.at(begin)->label(); if (find_location_reference(stm, begin, end, ref1)) { // jump is referenced, not post-expr decompile_while(stm, begin, end); return; } else if (find_location_reference(stm, begin, end, ref2)) { // continue is at begin, not post-expr decompile_while(stm, begin, end); return; } else if (find_location_reference(stm, 0, begin, ref2)) { // begin is at condition, not pre-expr decompile_while(stm, begin, end); return; } else { decompile_for(stm, begin, end); return; } } } } decompile_while(stm, begin, end); } auto decompiler::decompile_while(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[end]->label(); locs_.cnt = stm.list[begin]->label(); locs_.brk = stm.list[begin]->as().value; auto loc = stm.list[begin]->loc(); auto test = std::move(stm.list[begin]->as().test); end--; stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + end); auto body = stmt_list::make(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_while::make(loc, std::move(test), stmt_comp::make(loc, std::move(body)))); } auto decompiler::decompile_dowhile(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[end]->label(); locs_.cnt = stm.list[end]->label(); locs_.brk = last_location_index(stm, end) ? save.end : stm.list[end + 1]->label(); auto test = std::move(stm.list[end]->as().test); auto loc = stm.list[begin]->loc(); stm.list.erase(stm.list.begin() + end); auto body = stmt_list::make(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_dowhile::make(loc, std::move(test), stmt_comp::make(loc, std::move(body)))); } auto decompiler::decompile_for(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[end - 1]->label(); locs_.cnt = stm.list[end - 1]->label(); locs_.brk = stm.list[begin]->as().value; begin--; auto loc = stm.list[begin]->loc(); auto init = std::move(stm.list[begin]); stm.list.erase(stm.list.begin() + begin); auto test = std::move(stm.list[begin]->as().test); stm.list.erase(stm.list.begin() + begin); end -= 3; auto iter = std::move(stm.list[end]); stm.list.erase(stm.list.begin() + end); stm.list.erase(stm.list.begin() + end); auto body = stmt_list::make(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_for::make(loc, std::move(init), std::move(test), std::move(iter), stmt_comp::make(loc, std::move(body)))); } auto decompiler::decompile_foreach(stmt_list& stm, usize begin, usize end) -> void { auto save = locs_; locs_.end = stm.list[end - 1]->label(); locs_.cnt = stm.list[end - 1]->label(); locs_.brk = stm.list[begin]->as().value; begin -= 2; auto loc = stm.list[begin]->loc(); auto container = std::move(stm.list[begin]->as().value->as().rvalue); auto array = std::move(stm.list[begin]->as().value->as().lvalue); stm.list.erase(stm.list.begin() + begin); auto key = std::move(stm.list[begin]->as().value->as().lvalue); stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + begin); auto value = std::move(stm.list[begin]->as().value->as().lvalue); stm.list.erase(stm.list.begin() + begin); end -= (ctx_->props() & props::foreach) ? 6 : 5; if (ctx_->props() & props::foreach) stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + end); stm.list.erase(stm.list.begin() + end); auto use_key = !key->as().value.starts_with("_k"); if ((ctx_->props() & props::foreach) && key->as().value.starts_with("_id_")) use_key = false; auto body = stmt_list::make(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_foreach::make(loc, std::move(container), std::move(value), std::move(array), std::move(key), stmt_comp::make(loc, std::move(body)), use_key)); } auto decompiler::decompile_switch(stmt_list& stm, usize begin, usize end) -> void { auto const& data = stm.list[end]->as().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") { auto j = find_location_index(stm, data[index + 2]); auto loc = stm.list[j]->loc(); auto exp = (type == switch_type::integer) ? expr::ptr{ expr_integer::make(loc, data[index + 1]) } : expr::ptr{ expr_string::make(loc, data[index + 1]) }; while (stm.list[j]->is()) j++; stm.list.insert(stm.list.begin() + j, stmt_case::make(loc, std::move(exp), stmt_list::make(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]->is()) j++; stm.list.insert(stm.list.begin() + j, stmt_default::make(loc, stmt_list::make(loc))); index += 2; } else { decomp_error("malformed endswitch statement"); } } end += count; } auto save = locs_; locs_.brk = last_location_index(stm, end) ? locs_.end : stm.list[end + 1]->label(); locs_.end = stm.list[begin]->as().value; auto loc = stm.list[begin]->loc(); auto test = std::move(stm.list[begin]->as().test); end--; stm.list.erase(stm.list.begin() + begin); stm.list.erase(stm.list.begin() + end); auto body = stmt_list::make(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::ptr{ stmt_empty::make(location{}) }; for (auto i = 0u; i < body->list.size(); ) { if (body->list[i]->is() || body->list[i]->is()) { if (!temp->is()) { 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->is()) { temp->as().body->list.push_back(std::move(body->list[i])); body->list.erase(body->list.begin() + i); } else if (temp->is()) { temp->as().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->is()) { body->list.push_back(std::move(temp)); } stm.list.insert(stm.list.begin() + begin, stmt_switch::make(loc, std::move(test), stmt_comp::make(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->is() && entry->as().value == loc) { return true; } else if (entry->is() && entry->as().value == loc) { return true; } else if (entry->is() && entry->as().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); } auto decompiler::lvalues_match(stmt_expr const& stm1, stmt_expr const& stm2) -> bool { expr* lvalue1 {}; expr* lvalue2 {}; switch (stm1.value->kind()) { case node::expr_increment: lvalue1 = stm1.value->as().lvalue.get(); break; case node::expr_decrement: lvalue1 = stm1.value->as().lvalue.get(); break; case node::expr_assign: lvalue1 = stm1.value->as().lvalue.get(); break; default: break; } switch (stm2.value->kind()) { case node::expr_increment: lvalue2 = stm2.value->as().lvalue.get(); break; case node::expr_decrement: lvalue2 = stm2.value->as().lvalue.get(); break; case node::expr_assign: lvalue2 = stm2.value->as().lvalue.get(); break; default: break; } if (lvalue1 && lvalue2 && *lvalue1 == *lvalue2) return true; return false; } auto decompiler::resolve_label(std::string const& name) -> u32 { for (auto const& entry : labels_) { if (entry.second == name) { return entry.first; } } throw decomp_error(fmt::format("couldn't resolve label address of '{}'", name)); } auto decompiler::process_function(decl_function& func) -> void { process_stmt_comp(*func.body); process_expr_parameters(*func.params); if (ctx_->props() & props::spaces) { if (namespace_ != func.space->value) { namespace_ = func.space->value; program_->declarations.push_back(decl_namespace::make(func.loc(), expr_string::make(func.loc(), func.space->value))); } } func.space->value = {}; } auto decompiler::process_stmt(stmt& stm) -> void { switch (stm.kind()) { case node::stmt_list: process_stmt_list(stm.as()); break; case node::stmt_comp: process_stmt_comp(stm.as()); break; case node::stmt_dev: process_stmt_dev(stm.as()); break; case node::stmt_expr: process_stmt_expr(stm.as()); break; case node::stmt_endon: process_stmt_endon(stm.as()); break; case node::stmt_notify: process_stmt_notify(stm.as()); break; case node::stmt_wait: process_stmt_wait(stm.as()); break; case node::stmt_waitrealtime: process_stmt_waitrealtime(stm.as()); break; case node::stmt_waittill: process_stmt_waittill(stm.as()); break; case node::stmt_waittillmatch: process_stmt_waittillmatch(stm.as()); break; case node::stmt_if: process_stmt_if(stm.as()); break; case node::stmt_ifelse: process_stmt_ifelse(stm.as()); break; case node::stmt_while: process_stmt_while(stm.as()); break; case node::stmt_dowhile: process_stmt_dowhile(stm.as()); break; case node::stmt_for: process_stmt_for(stm.as()); break; case node::stmt_foreach: process_stmt_foreach(stm.as()); break; case node::stmt_switch: process_stmt_switch(stm.as()); break; case node::stmt_return: process_stmt_return(stm.as()); break; default: break; } } auto decompiler::process_stmt_list(stmt_list& stm) -> void { for (auto& entry : stm.list) { process_stmt(*entry); } } auto decompiler::process_stmt_comp(stmt_comp& stm) -> void { process_stmt_list(*stm.block); } auto decompiler::process_stmt_dev(stmt_dev& stm) -> void { process_stmt_list(*stm.block); } auto decompiler::process_stmt_expr(stmt_expr& stm) -> void { switch (stm.value->kind()) { case node::expr_increment: process_expr_increment(stm.value->as()); break; case node::expr_decrement: process_expr_decrement(stm.value->as()); break; case node::expr_assign: process_expr_assign(*reinterpret_cast(&stm.value)); break; case node::expr_call: process_expr_call(stm.value->as()); break; case node::expr_method: process_expr_method(stm.value->as()); break; default: break; } } auto decompiler::process_stmt_endon(stmt_endon& stm) -> void { process_expr(stm.event); process_expr(stm.obj); } void decompiler::process_stmt_notify(stmt_notify& stm) { process_expr_arguments(*stm.args); process_expr(stm.event); process_expr(stm.obj); } auto decompiler::process_stmt_wait(stmt_wait& stm) -> void { process_expr(stm.time); } auto decompiler::process_stmt_waitrealtime(stmt_waitrealtime& stm) -> void { process_expr(stm.time); } auto decompiler::process_stmt_waittill(stmt_waittill& stm) -> void { process_expr(stm.event); process_expr(stm.obj); for (auto& entry : stm.args->list) { process_expr(entry); } } auto decompiler::process_stmt_waittillmatch(stmt_waittillmatch& stm) -> void { process_expr_arguments(*stm.args); process_expr(stm.event); process_expr(stm.obj); } auto decompiler::process_stmt_if(stmt_if& stm) -> void { process_expr(stm.test); process_stmt(*stm.body); if (stm.body->as().block->list.size() == 1 && !stm.body->as().block->list[0]->is_special_stmt_dev()) { stm.body = std::move(stm.body->as().block->list.back()); } } auto decompiler::process_stmt_ifelse(stmt_ifelse& stm) -> void { process_expr(stm.test); process_stmt(*stm.stmt_if); process_stmt(*stm.stmt_else); if (stm.stmt_if->as().block->list.size() == 1 && !stm.stmt_if->as().block->list[0]->is_special_stmt_dev()) { stm.stmt_if = std::move(stm.stmt_if->as().block->list.back()); } if (stm.stmt_else->as().block->list.size() == 1 && !stm.stmt_else->as().block->list[0]->is_special_stmt_dev_noif()) { stm.stmt_else = std::move(stm.stmt_else->as().block->list.back()); } } auto decompiler::process_stmt_while(stmt_while& stm) -> void { if (stm.test->is() && stm.test->as().value == "1") { stm.test = expr_true::make(stm.test->loc()); } process_expr(stm.test); process_stmt(*stm.body); if (stm.body->as().block->list.size() == 1 && !stm.body->as().block->list[0]->is_special_stmt_dev()) { stm.body = std::move(stm.body->as().block->list.back()); } } auto decompiler::process_stmt_dowhile(stmt_dowhile& stm) -> void { if (stm.test->is() && stm.test->as().value == "1") { stm.test = expr_true::make(stm.test->loc()); } process_expr(stm.test); process_stmt(*stm.body); if (stm.body->as().block->list.size() == 1 && !stm.body->as().block->list[0]->is_special_stmt_dev()) { stm.body = std::move(stm.body->as().block->list.back()); } } auto decompiler::process_stmt_for(stmt_for& stm) -> void { process_stmt(*stm.init); process_expr(stm.test); process_stmt(*stm.body); process_stmt(*stm.iter); if (stm.body->as().block->list.size() == 1 && !stm.body->as().block->list[0]->is_special_stmt_dev()) { stm.body = std::move(stm.body->as().block->list.back()); } } auto decompiler::process_stmt_foreach(stmt_foreach& stm) -> void { process_expr(stm.container); process_expr(stm.array); process_expr(stm.key); process_expr(stm.value); if ((ctx_->props() & props::foreach) && !stm.use_key) vars_.insert(stm.key->as().value); process_stmt(*stm.body); if ((ctx_->props() & props::foreach) && !stm.use_key) { auto const it = vars_.find(stm.key->as().value); if (it != vars_.end()) vars_.erase(it); else stm.use_key = true; } if (stm.body->as().block->list.size() == 1 && !stm.body->as().block->list[0]->is_special_stmt_dev()) { stm.body = std::move(stm.body->as().block->list.back()); } } auto decompiler::process_stmt_switch(stmt_switch& stm) -> void { process_expr(stm.test); for (auto& entry : stm.body->block->list) { if (entry->is()) { process_stmt_list(*entry->as().body); } else if (entry->is()) { process_stmt_list(*entry->as().body); } } } auto decompiler::process_stmt_return(stmt_return& stm) -> void { if (!stm.value->is()) { if (retbool_ && stm.value->is()) stm.value = stm.value->as().value == "1" ? expr::ptr{ expr_true::make(stm.loc()) } : expr::ptr{ expr_false::make(stm.loc()) }; else process_expr(stm.value); } } auto decompiler::process_expr(expr::ptr& exp) -> void { switch (exp->kind()) { case node::expr_ternary: process_expr_ternary(exp->as()); break; case node::expr_binary: process_expr_binary(exp->as()); break; case node::expr_complement: process_expr_complement(exp->as()); break; case node::expr_not: process_expr_not(exp->as()); break; case node::expr_call: process_expr_call(exp->as()); break; case node::expr_method: process_expr_method(exp->as()); break; case node::expr_getnextarraykey: process_expr(exp->as().arg2); process_expr(exp->as().arg1); break; case node::expr_getfirstarraykey: process_expr(exp->as().arg); break; case node::expr_getdvarcoloralpha: process_expr(exp->as().arg); break; case node::expr_getdvarcolorblue: process_expr(exp->as().arg); break; case node::expr_getdvarcolorgreen: process_expr(exp->as().arg); break; case node::expr_getdvarcolorred: process_expr(exp->as().arg); break; case node::expr_getdvarvector: process_expr(exp->as().arg); break; case node::expr_getdvarfloat: process_expr(exp->as().arg); break; case node::expr_getdvarint: process_expr(exp->as().arg); break; case node::expr_getdvar: process_expr(exp->as().arg); break; case node::expr_abs: process_expr(exp->as().arg); break; case node::expr_vectortoangles: process_expr(exp->as().arg); break; case node::expr_angleclamp180: process_expr(exp->as().arg); break; case node::expr_anglestoforward: process_expr(exp->as().arg); break; case node::expr_anglestoright: process_expr(exp->as().arg); break; case node::expr_anglestoup: process_expr(exp->as().arg); break; case node::expr_vectorscale: process_expr(exp->as().arg2); process_expr(exp->as().arg1); break; case node::expr_isdefined: process_expr(exp->as().value); break; case node::expr_reference: process_expr_reference(exp->as()); break; case node::expr_array: process_expr_array(exp->as()); break; case node::expr_field: process_expr_field(exp->as()); break; case node::expr_size: process_expr_size(exp->as()); break; case node::expr_vector: process_expr_vector(exp->as()); break; case node::expr_identifier: process_expr_identifier(exp->as()); break; default: break; } } auto decompiler::process_expr_increment(expr_increment& exp) -> void { process_expr(exp.lvalue); } auto decompiler::process_expr_decrement(expr_decrement& exp) -> void { process_expr(exp.lvalue); } auto decompiler::process_expr_assign(expr_assign::ptr& exp) -> void { process_expr(exp->rvalue); process_expr(exp->lvalue); if (exp->oper != expr_assign::op::eq) return; if (exp->rvalue->kind() != node::expr_binary) return; auto& rval = exp->rvalue->as(); if (exp->lvalue != rval.lvalue) return; switch (rval.oper) { case expr_binary::op::add: exp->oper = expr_assign::op::add; break; case expr_binary::op::sub: exp->oper = expr_assign::op::sub; break; case expr_binary::op::mul: exp->oper = expr_assign::op::mul; break; case expr_binary::op::div: exp->oper = expr_assign::op::div; break; case expr_binary::op::mod: exp->oper = expr_assign::op::mod; break; case expr_binary::op::shl: exp->oper = expr_assign::op::shl; break; case expr_binary::op::shr: exp->oper = expr_assign::op::shr; break; case expr_binary::op::bwor: exp->oper = expr_assign::op::bwor; break; case expr_binary::op::bwand: exp->oper = expr_assign::op::bwand; break; case expr_binary::op::bwexor: exp->oper = expr_assign::op::bwexor; break; default: return; } exp->rvalue = std::move(rval.rvalue); } auto decompiler::process_expr_ternary(expr_ternary& exp) -> void { process_expr(exp.test); process_expr(exp.true_expr); process_expr(exp.false_expr); } auto decompiler::process_expr_binary(expr_binary& exp) -> void { process_expr(exp.lvalue); process_expr(exp.rvalue); auto prec = exp.lvalue->precedence(); if (prec && prec < exp.precedence()) { exp.lvalue = expr_paren::make(exp.loc(), std::move(exp.lvalue)); } prec = exp.rvalue->precedence(); if ((prec && prec < exp.precedence()) || (prec == exp.precedence() && exp.kind() == exp.rvalue->kind())) { exp.rvalue = expr_paren::make(exp.loc(), std::move(exp.rvalue)); } } auto decompiler::process_expr_complement(expr_complement& exp) -> void { process_expr(exp.rvalue); if (exp.rvalue->is()) { exp.rvalue = expr_paren::make(exp.loc(), std::move(exp.rvalue)); } } auto decompiler::process_expr_not(expr_not& exp) -> void { process_expr(exp.rvalue); if (exp.rvalue->is()) { exp.rvalue = expr_paren::make(exp.loc(), std::move(exp.rvalue)); } } auto decompiler::process_expr_call(expr_call& exp) -> void { switch (exp.value->kind()) { case node::expr_member: process_expr_call_member(exp.value->as()); break; case node::expr_pointer: process_expr_call_pointer(exp.value->as()); break; case node::expr_function: process_expr_call_function(exp.value->as()); break; default: break; } } auto decompiler::process_expr_method(expr_method& exp) -> void { switch (exp.value->kind()) { case node::expr_pointer: process_expr_method_pointer(exp.value->as(), exp.obj); break; case node::expr_function: process_expr_method_function(exp.value->as(), exp.obj); break; default: break; } } auto decompiler::process_expr_call_member(expr_member& exp) -> void { process_expr_arguments(*exp.args); process_expr(exp.obj); } auto decompiler::process_expr_call_pointer(expr_pointer& exp) -> void { process_expr_arguments(*exp.args); process_expr(exp.func); } auto decompiler::process_expr_call_function(expr_function& exp) -> void { process_expr_arguments(*exp.args); if (ctx_->props() & props::spaces) { if (exp.path->value == namespace_) { exp.path->value = {}; } } } auto decompiler::process_expr_method_pointer(expr_pointer& exp, expr::ptr& obj) -> void { process_expr_arguments(*exp.args); process_expr(obj); process_expr(exp.func); } auto decompiler::process_expr_method_function(expr_function& exp, expr::ptr& obj) -> void { process_expr_arguments(*exp.args); process_expr(obj); if (ctx_->props() & props::spaces) { if (exp.path->value == namespace_) { exp.path->value = {}; } } } auto decompiler::process_expr_parameters(expr_parameters& exp) -> void { if (exp.list.empty()) return; while (!func_->body->block->list.empty()) { auto& stmt = func_->body->block->list.at(0); if (!stmt->is() || !stmt->as().test->is()) return; auto& test = stmt->as().test->as().rvalue; if (!test->is() || !test->as().value->is()) return; if (!stmt->as().body->is() || !stmt->as().body->as().value->is()) return; if (stmt->as().body->as().value->as().oper != expr_assign::op::eq) return; if (test->as().value != stmt->as().body->as().value->as().lvalue) return; auto index = 0u; for (auto& entry : exp.list) { if (entry->is() && entry->as().value == test->as().value->as().value) { exp.list.erase(exp.list.begin() + index); exp.list.insert(exp.list.begin() + index, std::move(stmt->as().body->as().value)); func_->body->block->list.erase(func_->body->block->list.begin()); break; } index++; } if (index == exp.list.size()) return; } } auto decompiler::process_expr_arguments(expr_arguments& exp) -> void { for (auto i = exp.list.size(); i > 0; i--) { process_expr(exp.list[i - 1]); } } auto decompiler::process_expr_reference(expr_reference& exp) -> void { if (ctx_->props() & props::spaces) { if (exp.path->value == namespace_) { exp.path->value = {}; } } } auto decompiler::process_expr_array(expr_array& exp) -> void { process_expr(exp.key); process_expr(exp.obj); } auto decompiler::process_expr_field(expr_field& exp) -> void { process_expr(exp.obj); } auto decompiler::process_expr_size(expr_size& exp) -> void { process_expr(exp.obj); } auto decompiler::process_expr_vector(expr_vector& exp) -> void { process_expr(exp.z); process_expr(exp.y); process_expr(exp.x); } auto decompiler::process_expr_identifier(expr_identifier& exp) -> void { auto const it = vars_.find(exp.value); if (it != vars_.end()) vars_.erase(it); } } // namespace xsk::arc