diff --git a/common/symbols.h b/common/symbols.h index 2e9f5fda82..8ab1ef323a 100644 --- a/common/symbols.h +++ b/common/symbols.h @@ -1,10 +1,13 @@ #pragma once +#include "common/versions.h" + /*! * @file symbols.h * The location of fixed symbols in the GOAL symbol table. */ +namespace jak1_symbols { constexpr int FIX_SYM_EMPTY_CAR = -0xc; constexpr int FIX_SYM_EMPTY_PAIR = -0xa; constexpr int FIX_SYM_EMPTY_CDR = -0x8; @@ -76,3 +79,34 @@ constexpr int FIX_SYM_SOUND = 0x1b8; // ?? constexpr int FIX_SYM_DGO = 0x1c0; // ?? constexpr int FIX_SYM_TOP_LEVEL = 0x1c8; // ?? constexpr int FIX_FIXED_SYM_END_OFFSET = 0x1d0; +} // namespace jak1_symbols + +namespace jak2_symbols { +constexpr int FIX_SYM_EMPTY_CAR = -0x8; +constexpr int FIX_SYM_EMPTY_PAIR = -0x7; +constexpr int FIX_SYM_EMPTY_CDR = -0x4; +constexpr int FIX_SYM_FALSE = 0x0; // GOAL boolean #f (note that this is equal to the $s7 register) +constexpr int FIX_SYM_TRUE = 0x4; // GOAL boolean #t +} // namespace jak2_symbols + +constexpr int false_symbol_offset() { + return jak1_symbols::FIX_SYM_FALSE; +} + +constexpr int true_symbol_offset(GameVersion version) { + switch (version) { + case GameVersion::Jak1: + return jak1_symbols::FIX_SYM_TRUE; + case GameVersion::Jak2: + return jak2_symbols::FIX_SYM_TRUE; + } +} + +constexpr int empty_pair_offset(GameVersion version) { + switch (version) { + case GameVersion::Jak1: + return jak1_symbols::FIX_SYM_EMPTY_PAIR; + case GameVersion::Jak2: + return jak2_symbols::FIX_SYM_EMPTY_PAIR; + } +} \ No newline at end of file diff --git a/decompiler/Function/CfgVtx.cpp b/decompiler/Function/CfgVtx.cpp index b00961a10b..7abe855cc6 100644 --- a/decompiler/Function/CfgVtx.cpp +++ b/decompiler/Function/CfgVtx.cpp @@ -2612,7 +2612,7 @@ std::shared_ptr build_cfg( Function& func, const CondWithElseLengthHack& cond_with_else_hack, const std::unordered_set& blocks_ending_in_asm_br) { - // fmt::print("START {}\n", func.guessed_name.to_string()); + // fmt::print("START {}\n", func.guessed_name.to_string()); auto cfg = std::make_shared(); const auto& blocks = cfg->create_blocks(func.basic_blocks.size()); diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index bb38fb9ec1..c0de248957 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -508,27 +508,47 @@ void LinkedObjectFile::process_fp_relative_links() { case InstructionKind::DADDU: case InstructionKind::ADDU: { ASSERT(prev_instr); - if (prev_instr->kind != InstructionKind::ORI) { + if (prev_instr->kind == InstructionKind::ORI) { + ASSERT(prev_instr->kind == InstructionKind::ORI); + int offset_reg_src_id = instr.kind == InstructionKind::DADDU ? 0 : 1; + auto offset_reg = instr.get_src(offset_reg_src_id).get_reg(); + ASSERT(offset_reg == prev_instr->get_dst(0).get_reg()); + ASSERT(offset_reg == prev_instr->get_src(0).get_reg()); + auto& atom = prev_instr->get_imm_src(); + int additional_offset = 0; + if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) { + ASSERT(pprev_instr->get_dst(0).get_reg() == offset_reg); + additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm(); + pprev_instr->get_imm_src().set_label( + get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset)); + } + atom.set_label( + get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset)); + stats.n_fp_reg_use_resolved++; + } else if (prev_instr->kind == InstructionKind::DADDIU) { + /* + * Jak 2 has a new use of fp to access elements of a static array that looks like + * this: + * (set! v1 (* idx stride)) + * daddiu v1, v1, 8128 + * daddu v1, v1, fp + */ + auto val_plus_off_reg = prev_instr->get_dst(0).get_reg(); + + // it's possible that this isn't always the case, but works for all of jak 2 + ASSERT(val_plus_off_reg == prev_instr->get_src(0).get_reg()); + ASSERT(val_plus_off_reg == instr.get_src(0).get_reg()); + ASSERT(val_plus_off_reg == instr.get_dst(0).get_reg()); + auto& atom = prev_instr->get_imm_src(); + atom.set_label(get_label_id_for(seg, current_fp + atom.get_imm())); + + stats.n_fp_reg_use_resolved++; + } else { lg::error("Failed to process fp relative links for (d)addu preceded by: {}", prev_instr->to_string(labels)); return; } - ASSERT(prev_instr->kind == InstructionKind::ORI); - int offset_reg_src_id = instr.kind == InstructionKind::DADDU ? 0 : 1; - auto offset_reg = instr.get_src(offset_reg_src_id).get_reg(); - ASSERT(offset_reg == prev_instr->get_dst(0).get_reg()); - ASSERT(offset_reg == prev_instr->get_src(0).get_reg()); - auto& atom = prev_instr->get_imm_src(); - int additional_offset = 0; - if (pprev_instr && pprev_instr->kind == InstructionKind::LUI) { - ASSERT(pprev_instr->get_dst(0).get_reg() == offset_reg); - additional_offset = (1 << 16) * pprev_instr->get_imm_src().get_imm(); - pprev_instr->get_imm_src().set_label( - get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset)); - } - atom.set_label( - get_label_id_for(seg, current_fp + atom.get_imm() + additional_offset)); - stats.n_fp_reg_use_resolved++; + } break; default: diff --git a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp index 4af2f8028b..968f64ca82 100644 --- a/decompiler/ObjectFile/ObjectFileDB_IR2.cpp +++ b/decompiler/ObjectFile/ObjectFileDB_IR2.cpp @@ -39,12 +39,6 @@ void ObjectFileDB::analyze_functions_ir2( const Config& config, const std::unordered_set& skip_functions, const std::unordered_map>& skip_states) { - // First, do basic analysis on the top level: - lg::info("Using IR2 analysis..."); - - lg::info("Processing top-level functions..."); - ir2_top_level_pass(config); - int total_file_count = 0; for (auto& f : obj_files_by_name) { total_file_count += f.second.size(); @@ -178,12 +172,12 @@ void ObjectFileDB::ir2_run_mips2c(const Config& config, ObjectFileData& data) { for_each_function_def_order_in_obj(data, [&](Function& func, int) { if (config.hacks.mips2c_functions_by_name.count(func.name())) { lg::info("MIPS2C on {}", func.name()); - run_mips2c(&func); + run_mips2c(&func, config.game_version); } auto it = config.hacks.mips2c_jump_table_functions.find(func.name()); if (it != config.hacks.mips2c_jump_table_functions.end()) { - run_mips2c_jump_table(&func, it->second); + run_mips2c_jump_table(&func, it->second, config.game_version); } }); } @@ -390,8 +384,9 @@ void ObjectFileDB::ir2_atomic_op_pass(int seg, const Config& config, ObjectFileD blocks_ending_in_asm_branch = asm_branch_it->second; } - auto ops = convert_function_to_atomic_ops(func, data.linked_data.labels, func.warnings, - inline_asm, blocks_ending_in_asm_branch); + auto ops = + convert_function_to_atomic_ops(func, data.linked_data.labels, func.warnings, inline_asm, + blocks_ending_in_asm_branch, config.game_version); func.ir2.atomic_ops = std::make_shared(std::move(ops)); func.ir2.atomic_ops_succeeded = true; func.ir2.env.set_end_var(func.ir2.atomic_ops->end_op().return_var()); @@ -877,7 +872,12 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function& init_types = &func.ir2.env.get_types_at_block_entry(block_id); } - for (int instr_id = block.start_word; instr_id < block.end_word; instr_id++) { + int start_word = block.start_word; + // if we have no prologue, skip the type tag. + if (start_word == 0) { + start_word = 1; + } + for (int instr_id = start_word; instr_id < block.end_word; instr_id++) { print_instr_start(instr_id); bool printed_comment = false; diff --git a/decompiler/analysis/atomic_op_builder.cpp b/decompiler/analysis/atomic_op_builder.cpp index f5d8001419..9a67cdbbb7 100644 --- a/decompiler/analysis/atomic_op_builder.cpp +++ b/decompiler/analysis/atomic_op_builder.cpp @@ -15,7 +15,8 @@ namespace { std::unique_ptr convert_1(const Instruction& i0, int idx, bool hint_inline_asm, - bool force_asm_branch); + bool force_asm_branch, + GameVersion version); ////////////////////// // Register Helpers @@ -368,10 +369,10 @@ std::unique_ptr make_asm_op(const Instruction& i0, int idx) { } } -std::unique_ptr convert_1_allow_asm(const Instruction& i0, int idx) { +std::unique_ptr convert_1_allow_asm(const Instruction& i0, int idx, GameVersion version) { // only used for delay slots, so fine to assume that this can never be an asm branch itself // as there are no branches in delay slots anywhere. - auto as_normal = convert_1(i0, idx, false, false); + auto as_normal = convert_1(i0, idx, false, false, version); if (as_normal) { return as_normal; } @@ -382,7 +383,7 @@ std::unique_ptr convert_1_allow_asm(const Instruction& i0, int idx) { // Branch Helpers //////////////////////// -IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx) { +IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx, GameVersion version) { if (is_nop(i0)) { return IR2_BranchDelay(IR2_BranchDelay::Kind::NOP); } else if (is_gpr_3(i0, InstructionKind::OR, {}, rs7(), rr0())) { @@ -391,7 +392,7 @@ IR2_BranchDelay get_branch_delay(const Instruction& i0, int idx) { return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_REG, make_dst_var(i0, idx), make_src_var(i0.get_src(0).get_reg(), idx)); } else if (i0.kind == InstructionKind::DADDIU && i0.get_src(0).is_reg(rs7()) && - i0.get_src(1).is_imm(FIX_SYM_TRUE)) { + i0.get_src(1).is_imm(true_symbol_offset(version))) { return IR2_BranchDelay(IR2_BranchDelay::Kind::SET_REG_TRUE, make_dst_var(i0, idx)); } else if (i0.kind == InstructionKind::LW && i0.get_src(1).is_reg(rs7()) && i0.get_src(0).is_sym()) { @@ -419,13 +420,14 @@ std::unique_ptr make_branch(const IR2_Condition& condition, bool likely, int dest_label, int my_idx, - bool force_asm) { + bool force_asm, + GameVersion version) { ASSERT(!likely); - auto branch_delay = get_branch_delay(delay, my_idx); + auto branch_delay = get_branch_delay(delay, my_idx, version); if (!force_asm && branch_delay.is_known()) { return std::make_unique(likely, condition, dest_label, branch_delay, my_idx); } else { - auto delay_op = std::shared_ptr(convert_1_allow_asm(delay, my_idx)); + auto delay_op = std::shared_ptr(convert_1_allow_asm(delay, my_idx, version)); if (!delay_op) { throw std::runtime_error( fmt::format("Failed to convert branch delay slot instruction for branch at {}", my_idx)); @@ -459,9 +461,10 @@ std::unique_ptr make_asm_branch(const IR2_Condition& condition, const Instruction& delay, bool likely, int dest_label, - int my_idx) { + int my_idx, + GameVersion version) { ASSERT(!likely); - auto delay_op = std::shared_ptr(convert_1_allow_asm(delay, my_idx)); + auto delay_op = std::shared_ptr(convert_1_allow_asm(delay, my_idx, version)); if (!delay_op) { throw std::runtime_error( fmt::format("Failed to convert branch delay slot instruction for branch at {}", my_idx)); @@ -561,12 +564,12 @@ std::unique_ptr convert_lw_1(const Instruction& i0, int idx) { } } -std::unique_ptr convert_daddiu_1(const Instruction& i0, int idx) { +std::unique_ptr convert_daddiu_1(const Instruction& i0, int idx, GameVersion version) { if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) { // get symbol pointer return std::make_unique( make_dst_var(i0, idx), SimpleAtom::make_sym_ptr(i0.get_src(1).get_sym()).as_expr(), idx); - } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(FIX_SYM_EMPTY_PAIR)) { + } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(empty_pair_offset(version))) { // get empty pair return std::make_unique(make_dst_var(i0, idx), SimpleAtom::make_empty_list().as_expr(), idx); @@ -574,8 +577,7 @@ std::unique_ptr convert_daddiu_1(const Instruction& i0, int idx) { // get pointer to beginning of symbol table (this is a bit of a hack) return std::make_unique( make_dst_var(i0, idx), SimpleAtom::make_sym_val("__START-OF-TABLE__").as_expr(), idx); - } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(FIX_SYM_TRUE)) { - // get pointer to beginning of symbol table (this is a bit of a hack) + } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_imm(true_symbol_offset(version))) { return std::make_unique(make_dst_var(i0, idx), SimpleAtom::make_sym_ptr("#t").as_expr(), idx); } else if (i0.get_src(0).is_reg(rfp()) && i0.get_src(1).is_label()) { @@ -805,7 +807,8 @@ std::unique_ptr convert_subu_1(const Instruction& i0, int idx) { std::unique_ptr convert_1(const Instruction& i0, int idx, bool hint_inline_asm, - bool force_asm_branch) { + bool force_asm_branch, + GameVersion version) { switch (i0.kind) { case InstructionKind::OR: return convert_or_1(i0, idx); @@ -870,7 +873,7 @@ std::unique_ptr convert_1(const Instruction& i0, case InstructionKind::MAXS: return make_3reg_op(i0, SimpleExpression::Kind::MAX_S, idx); case InstructionKind::DADDIU: - return convert_daddiu_1(i0, idx); + return convert_daddiu_1(i0, idx, version); case InstructionKind::DADDU: return convert_daddu_1(i0, idx); case InstructionKind::DSUBU: @@ -1003,7 +1006,8 @@ std::unique_ptr convert_bne_2(const Instruction& i0, const Instruction& i1, int idx, bool likely, - bool force_asm) { + bool force_asm, + GameVersion version) { auto s0 = i0.get_src(0).get_reg(); auto s1 = i0.get_src(1).get_reg(); auto dest = i0.get_src(2).get_label(); @@ -1021,14 +1025,15 @@ std::unique_ptr convert_bne_2(const Instruction& i0, make_src_atom(s1, idx)); condition.make_flipped(); } - return make_branch(condition, i1, likely, dest, idx, force_asm); + return make_branch(condition, i1, likely, dest, idx, force_asm, version); } std::unique_ptr convert_beq_2(const Instruction& i0, const Instruction& i1, int idx, bool likely, - bool force_asm) { + bool force_asm, + GameVersion version) { auto s0 = i0.get_src(0).get_reg(); auto s1 = i0.get_src(1).get_reg(); auto dest = i0.get_src(2).get_label(); @@ -1053,10 +1058,13 @@ std::unique_ptr convert_beq_2(const Instruction& i0, IR2_Condition(IR2_Condition::Kind::EQUAL, make_src_atom(s0, idx), make_src_atom(s1, idx)); condition.make_flipped(); } - return make_branch(condition, i1, likely, dest, idx, force_asm); + return make_branch(condition, i1, likely, dest, idx, force_asm, version); } -std::unique_ptr convert_daddiu_2(const Instruction& i0, const Instruction& i1, int idx) { +std::unique_ptr convert_daddiu_2(const Instruction& i0, + const Instruction& i1, + int idx, + GameVersion version) { // daddiu dest, s7, 8 // mov{n,z} dest, s7, src if (i1.kind == InstructionKind::MOVN || i1.kind == InstructionKind::MOVZ) { @@ -1066,7 +1074,7 @@ std::unique_ptr convert_daddiu_2(const Instruction& i0, const Instruct return nullptr; } ASSERT(i0.get_src(0).is_reg(rs7())); - ASSERT(i0.get_src(1).is_imm(8)); + ASSERT(i0.get_src(1).is_imm(true_symbol_offset(version))); ASSERT(i1.get_dst(0).is_reg(dest)); ASSERT(i1.get_src(0).is_reg(rs7())); auto kind = @@ -1143,42 +1151,55 @@ std::unique_ptr convert_slt_2(const Instruction& i0, return result; } -std::unique_ptr convert_bltz_2(const Instruction& i0, const Instruction& i1, int idx) { +std::unique_ptr convert_bltz_2(const Instruction& i0, + const Instruction& i1, + int idx, + GameVersion version) { // bltz is never emitted outside of inline asm. auto dest = i0.get_src(1).get_label(); return make_asm_branch(IR2_Condition(IR2_Condition::Kind::LESS_THAN_ZERO_SIGNED, make_src_atom(i0.get_src(0).get_reg(), idx)), - i1, false, dest, idx); + i1, false, dest, idx, version); } -std::unique_ptr convert_bgez_2(const Instruction& i0, const Instruction& i1, int idx) { +std::unique_ptr convert_bgez_2(const Instruction& i0, + const Instruction& i1, + int idx, + GameVersion version) { // bgez is never emitted outside of inline asm. auto dest = i0.get_src(1).get_label(); return make_asm_branch(IR2_Condition(IR2_Condition::Kind::GEQ_ZERO_SIGNED, make_src_atom(i0.get_src(0).get_reg(), idx)), - i1, false, dest, idx); + i1, false, dest, idx, version); } -std::unique_ptr convert_blez_2(const Instruction& i0, const Instruction& i1, int idx) { +std::unique_ptr convert_blez_2(const Instruction& i0, + const Instruction& i1, + int idx, + GameVersion version) { // blez is never emitted outside of inline asm. auto dest = i0.get_src(1).get_label(); return make_asm_branch(IR2_Condition(IR2_Condition::Kind::LEQ_ZERO_SIGNED, make_src_atom(i0.get_src(0).get_reg(), idx)), - i1, false, dest, idx); + i1, false, dest, idx, version); } -std::unique_ptr convert_bgtz_2(const Instruction& i0, const Instruction& i1, int idx) { +std::unique_ptr convert_bgtz_2(const Instruction& i0, + const Instruction& i1, + int idx, + GameVersion version) { // bgtz is never emitted outside of inline asm. auto dest = i0.get_src(1).get_label(); return make_asm_branch(IR2_Condition(IR2_Condition::Kind::GREATER_THAN_ZERO_SIGNED, make_src_atom(i0.get_src(0).get_reg(), idx)), - i1, false, dest, idx); + i1, false, dest, idx, version); } std::unique_ptr convert_2(const Instruction& i0, const Instruction& i1, int idx, - bool force_asm_branch) { + bool force_asm_branch, + GameVersion version) { switch (i0.kind) { case InstructionKind::DIV: return convert_division_2(i0, i1, idx, true); @@ -1187,11 +1208,11 @@ std::unique_ptr convert_2(const Instruction& i0, case InstructionKind::JALR: return convert_jalr_2(i0, i1, idx); case InstructionKind::BNE: - return convert_bne_2(i0, i1, idx, false, force_asm_branch); + return convert_bne_2(i0, i1, idx, false, force_asm_branch, version); case InstructionKind::BEQ: - return convert_beq_2(i0, i1, idx, false, force_asm_branch); + return convert_beq_2(i0, i1, idx, false, force_asm_branch, version); case InstructionKind::DADDIU: - return convert_daddiu_2(i0, i1, idx); + return convert_daddiu_2(i0, i1, idx, version); case InstructionKind::LUI: return convert_lui_2(i0, i1, idx); case InstructionKind::SLT: @@ -1201,13 +1222,13 @@ std::unique_ptr convert_2(const Instruction& i0, case InstructionKind::CLTS: return convert_fp_branch_asm(i0, i1, IR2_Condition::Kind::FLOAT_LESS_THAN, idx); case InstructionKind::BLTZ: - return convert_bltz_2(i0, i1, idx); + return convert_bltz_2(i0, i1, idx, version); case InstructionKind::BGEZ: - return convert_bgez_2(i0, i1, idx); + return convert_bgez_2(i0, i1, idx, version); case InstructionKind::BGTZ: - return convert_bgtz_2(i0, i1, idx); + return convert_bgtz_2(i0, i1, idx, version); case InstructionKind::BLEZ: - return convert_blez_2(i0, i1, idx); + return convert_blez_2(i0, i1, idx, version); default: return nullptr; } @@ -1264,7 +1285,8 @@ std::unique_ptr convert_lui_3(const Instruction& i0, std::unique_ptr convert_dsubu_3(const Instruction& i0, const Instruction& i1, const Instruction& i2, - int idx) { + int idx, + GameVersion version) { if (i1.kind == InstructionKind::DADDIU && (i2.kind == InstructionKind::MOVN || i2.kind == InstructionKind::MOVZ)) { // dsubu temp, a, b @@ -1275,7 +1297,7 @@ std::unique_ptr convert_dsubu_3(const Instruction& i0, auto b = i0.get_src(1).get_reg(); auto dest = i1.get_dst(0).get_reg(); ASSERT(i1.get_src(0).is_reg(rs7())); - ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE)); + ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version))); ASSERT(i2.get_dst(0).get_reg() == dest); ASSERT(i2.get_src(0).is_reg(rs7())); ASSERT(i2.get_src(1).get_reg() == temp); @@ -1321,7 +1343,8 @@ std::unique_ptr convert_slt_3(const Instruction& i0, const Instruction& i1, const Instruction& i2, bool is_signed, - int idx) { + int idx, + GameVersion version) { auto s0 = i0.get_src(0).get_reg(); auto s1 = i0.get_src(1).get_reg(); std::unique_ptr result; @@ -1354,7 +1377,7 @@ std::unique_ptr convert_slt_3(const Instruction& i0, if (i1.kind == InstructionKind::BEQ) { condition.invert(); } - result = make_branch(condition, i2, false, dest, idx, false); + result = make_branch(condition, i2, false, dest, idx, false, version); add_clobber_if_unwritten(*result, temp); return result; } else if (i1.kind == InstructionKind::DADDIU && @@ -1366,7 +1389,7 @@ std::unique_ptr convert_slt_3(const Instruction& i0, auto temp = i0.get_dst(0).get_reg(); auto dest = i1.get_dst(0).get_reg(); ASSERT(i1.get_src(0).is_reg(rs7())); - ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE)); + ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version))); ASSERT(i2.get_dst(0).get_reg() == dest); ASSERT(i2.get_src(0).is_reg(rs7())); ASSERT(i2.get_src(1).get_reg() == temp); @@ -1400,7 +1423,8 @@ std::unique_ptr convert_slti_3(const Instruction& i0, const Instruction& i1, const Instruction& i2, bool is_signed, - int idx) { + int idx, + GameVersion version) { auto s0 = i0.get_src(0).get_reg(); auto s1 = SimpleAtom::make_int_constant(i0.get_src(1).get_imm()); std::unique_ptr result; @@ -1419,7 +1443,7 @@ std::unique_ptr convert_slti_3(const Instruction& i0, if (i1.kind == InstructionKind::BEQ) { condition.invert(); } - result = make_branch(condition, i2, false, dest, idx, false); + result = make_branch(condition, i2, false, dest, idx, false, version); add_clobber_if_unwritten(*result, temp); return result; } else if (i1.kind == InstructionKind::DADDIU && @@ -1431,7 +1455,7 @@ std::unique_ptr convert_slti_3(const Instruction& i0, auto temp = i0.get_dst(0).get_reg(); auto dest = i1.get_dst(0).get_reg(); ASSERT(i1.get_src(0).is_reg(rs7())); - ASSERT(i1.get_src(1).is_imm(FIX_SYM_TRUE)); + ASSERT(i1.get_src(1).is_imm(true_symbol_offset(version))); ASSERT(i2.get_dst(0).get_reg() == dest); ASSERT(i2.get_src(0).is_reg(rs7())); ASSERT(i2.get_src(1).get_reg() == temp); @@ -1455,14 +1479,15 @@ std::unique_ptr convert_fp_branch(const Instruction& i0, const Instruction& i1, const Instruction& i2, IR2_Condition::Kind kind, - int idx) { + int idx, + GameVersion version) { if (i1.kind == InstructionKind::BC1T || i1.kind == InstructionKind::BC1F) { IR2_Condition condition(kind, make_src_atom(i0.get_src(0).get_reg(), idx), make_src_atom(i0.get_src(1).get_reg(), idx)); if (i1.kind == InstructionKind::BC1F) { condition.invert(); } - return make_branch(condition, i2, false, i1.get_src(0).get_label(), idx, false); + return make_branch(condition, i2, false, i1.get_src(0).get_label(), idx, false, version); } return nullptr; } @@ -1470,26 +1495,27 @@ std::unique_ptr convert_fp_branch(const Instruction& i0, std::unique_ptr convert_3(const Instruction& i0, const Instruction& i1, const Instruction& i2, - int idx) { + int idx, + GameVersion version) { switch (i0.kind) { case InstructionKind::LUI: return convert_lui_3(i0, i1, i2, idx); case InstructionKind::DSUBU: - return convert_dsubu_3(i0, i1, i2, idx); + return convert_dsubu_3(i0, i1, i2, idx, version); case InstructionKind::SLT: - return convert_slt_3(i0, i1, i2, true, idx); + return convert_slt_3(i0, i1, i2, true, idx, version); case InstructionKind::SLTU: - return convert_slt_3(i0, i1, i2, false, idx); + return convert_slt_3(i0, i1, i2, false, idx, version); case InstructionKind::SLTI: - return convert_slti_3(i0, i1, i2, true, idx); + return convert_slti_3(i0, i1, i2, true, idx, version); case InstructionKind::SLTIU: - return convert_slti_3(i0, i1, i2, false, idx); + return convert_slti_3(i0, i1, i2, false, idx, version); case InstructionKind::CEQS: - return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_EQUAL, idx); + return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_EQUAL, idx, version); case InstructionKind::CLTS: - return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LESS_THAN, idx); + return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LESS_THAN, idx, version); case InstructionKind::CLES: - return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LEQ, idx); + return convert_fp_branch(i0, i1, i2, IR2_Condition::Kind::FLOAT_LEQ, idx, version); default: return nullptr; } @@ -1503,7 +1529,8 @@ std::unique_ptr convert_dsll32_4(const Instruction& i0, const Instruction& i1, const Instruction& i2, const Instruction& i3, - int idx) { + int idx, + GameVersion version) { if (i1.kind == InstructionKind::SLT && i2.kind == InstructionKind::BEQ) { // dsll32 temp, a0, 30 // slt temp, temp, r0 @@ -1524,7 +1551,7 @@ std::unique_ptr convert_dsll32_4(const Instruction& i0, ASSERT(i2.get_src(1).is_reg(rr0())); IR2_Condition condition(IR2_Condition::Kind::IS_NOT_PAIR, make_src_atom(arg, idx)); - auto result = make_branch(condition, i3, false, i2.get_src(2).get_label(), idx, false); + auto result = make_branch(condition, i3, false, i2.get_src(2).get_label(), idx, false, version); result->add_clobber_reg(temp); return result; } @@ -1536,7 +1563,8 @@ std::unique_ptr convert_fp_branch_with_nop(const Instruction& i0, const Instruction& i2, const Instruction& i3, IR2_Condition::Kind kind, - int idx) { + int idx, + GameVersion version) { if (i1.kind != InstructionKind::VNOP) { return nullptr; } @@ -1546,7 +1574,7 @@ std::unique_ptr convert_fp_branch_with_nop(const Instruction& i0, if (i2.kind == InstructionKind::BC1F) { condition.invert(); } - return make_branch(condition, i3, false, i2.get_src(0).get_label(), idx, false); + return make_branch(condition, i3, false, i2.get_src(0).get_label(), idx, false, version); } return nullptr; } @@ -1555,12 +1583,14 @@ std::unique_ptr convert_4(const Instruction& i0, const Instruction& i1, const Instruction& i2, const Instruction& i3, - int idx) { + int idx, + GameVersion version) { switch (i0.kind) { case InstructionKind::DSLL32: - return convert_dsll32_4(i0, i1, i2, i3, idx); + return convert_dsll32_4(i0, i1, i2, i3, idx, version); case InstructionKind::CEQS: - return convert_fp_branch_with_nop(i0, i1, i2, i3, IR2_Condition::Kind::FLOAT_EQUAL, idx); + return convert_fp_branch_with_nop(i0, i1, i2, i3, IR2_Condition::Kind::FLOAT_EQUAL, idx, + version); default: return nullptr; } @@ -2035,6 +2065,7 @@ int convert_block_to_atomic_ops(int begin_idx, const std::vector& labels, FunctionAtomicOps* container, DecompWarnings& warnings, + GameVersion version, bool hint_inline_asm, bool block_ends_in_asm_branch) { container->block_id_to_first_atomic_op.push_back(container->ops.size()); @@ -2089,7 +2120,7 @@ int convert_block_to_atomic_ops(int begin_idx, if (!converted && n_instr >= 4) { // try 4 instructions - op = convert_4(instr[0], instr[1], instr[2], instr[3], op_idx); + op = convert_4(instr[0], instr[1], instr[2], instr[3], op_idx, version); if (op) { converted = true; length = 4; @@ -2098,7 +2129,7 @@ int convert_block_to_atomic_ops(int begin_idx, if (!converted && n_instr >= 3) { // try 3 instructions - op = convert_3(instr[0], instr[1], instr[2], op_idx); + op = convert_3(instr[0], instr[1], instr[2], op_idx, version); if (op) { converted = true; length = 3; @@ -2107,7 +2138,7 @@ int convert_block_to_atomic_ops(int begin_idx, if (!converted && n_instr >= 2) { // try 2 instructions - op = convert_2(instr[0], instr[1], op_idx, block_ends_in_asm_branch); + op = convert_2(instr[0], instr[1], op_idx, block_ends_in_asm_branch, version); if (op) { converted = true; length = 2; @@ -2117,7 +2148,7 @@ int convert_block_to_atomic_ops(int begin_idx, if (!converted) { // try 1 instruction bool force_asm_branch = n_instr == 1 && block_ends_in_asm_branch; - op = convert_1(*instr, op_idx, hint_inline_asm, force_asm_branch); + op = convert_1(*instr, op_idx, hint_inline_asm, force_asm_branch, version); if (op) { converted = true; length = 1; @@ -2162,7 +2193,8 @@ FunctionAtomicOps convert_function_to_atomic_ops( const std::vector& labels, DecompWarnings& warnings, bool hint_inline_asm, - const std::unordered_set& blocks_ending_in_asm_branches) { + const std::unordered_set& blocks_ending_in_asm_branches, + GameVersion version) { FunctionAtomicOps result; int last_op = 0; @@ -2173,7 +2205,7 @@ FunctionAtomicOps convert_function_to_atomic_ops( auto begin = func.instructions.begin() + block.start_word; auto end = func.instructions.begin() + block.end_word; last_op = convert_block_to_atomic_ops( - block.start_word, begin, end, labels, &result, warnings, hint_inline_asm, + block.start_word, begin, end, labels, &result, warnings, version, hint_inline_asm, blocks_ending_in_asm_branches.find(i) != blocks_ending_in_asm_branches.end()); if (i == int(func.basic_blocks.size()) - 1) { // we're the last block. insert the function end op. diff --git a/decompiler/analysis/atomic_op_builder.h b/decompiler/analysis/atomic_op_builder.h index c70bbe0908..d27af404e8 100644 --- a/decompiler/analysis/atomic_op_builder.h +++ b/decompiler/analysis/atomic_op_builder.h @@ -49,6 +49,7 @@ int convert_block_to_atomic_ops(int begin_idx, const std::vector& labels, FunctionAtomicOps* container, DecompWarnings& warnings, + GameVersion version, bool inline_asm_hint = false, bool block_ends_in_asm_branch = false); @@ -60,5 +61,6 @@ FunctionAtomicOps convert_function_to_atomic_ops( const std::vector& labels, DecompWarnings& warnings, bool hint_inline_asm, - const std::unordered_set& blocks_ending_in_asm_branches); + const std::unordered_set& blocks_ending_in_asm_branches, + GameVersion version); } // namespace decompiler \ No newline at end of file diff --git a/decompiler/analysis/cfg_builder.cpp b/decompiler/analysis/cfg_builder.cpp index 8dda80df55..5f28a87947 100644 --- a/decompiler/analysis/cfg_builder.cpp +++ b/decompiler/analysis/cfg_builder.cpp @@ -6,6 +6,7 @@ #include "cfg_builder.h" #include "decompiler/Function/Function.h" #include "decompiler/IR2/Form.h" +#include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/util/MatchParam.h" namespace decompiler { @@ -107,6 +108,13 @@ void clean_up_cond_with_else(FormPool& pool, FormElement* ir, const Env& env) { void clean_up_until_loop(FormPool& pool, UntilElement* ir, const Env& env) { auto condition_branch = get_condition_branch(ir->condition); ASSERT(condition_branch.first); + if (condition_branch.first->op()->branch_delay().kind() != IR2_BranchDelay::Kind::NOP) { + ASSERT_MSG( + false, + fmt::format( + "bad delay slot in until loop: {} in {}\n", env.func->name(), + condition_branch.first->op()->branch_delay().to_form(env.file->labels, env).print())); + } ASSERT(condition_branch.first->op()->branch_delay().kind() == IR2_BranchDelay::Kind::NOP); auto replacement = condition_branch.first->op()->get_condition_as_form(pool, env); replacement->invert(); @@ -708,6 +716,7 @@ void clean_up_cond_no_else_final(Function& func, CondNoElseElement* cne) { ASSERT(fr.has_value()); cne->final_destination = *fr; } else { + fmt::print("failed to clean up cond_no_else_final: {}\n", func.name()); ASSERT(false); } } @@ -1634,6 +1643,10 @@ Form* cfg_to_ir_helper(FormPool& pool, Function& f, const CfgVtx* vtx) { ASSERT(delay_end - delay_start == 1); auto& op = f.ir2.atomic_ops->ops.at(delay_start); auto op_as_expr = dynamic_cast(op.get()); + if (!op_as_expr) { + fmt::print("bad in {}\n", f.name()); + fmt::print("{}\n", op->to_string(f.ir2.env)); + } ASSERT(op_as_expr); e.branch_delay = *op_as_expr; } diff --git a/decompiler/analysis/mips2c.cpp b/decompiler/analysis/mips2c.cpp index afa1014a73..d2418a1d0a 100644 --- a/decompiler/analysis/mips2c.cpp +++ b/decompiler/analysis/mips2c.cpp @@ -591,12 +591,15 @@ Mips2C_Line handle_generic_op2_u16(const Instruction& i0, const std::string& ins instr_str}; } -Mips2C_Line handle_daddiu(Mips2C_Output& out, const Instruction& i0, const std::string& instr_str) { +Mips2C_Line handle_daddiu(Mips2C_Output& out, + const Instruction& i0, + const std::string& instr_str, + GameVersion version) { if (i0.get_src(1).is_label()) { return {instr_str, instr_str}; } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym("#t")) { return {fmt::format("c->{}({}, {}, {});", i0.op_name_to_string(), reg_to_name(i0.get_dst(0)), - reg_to_name(i0.get_src(0)), FIX_SYM_TRUE), + reg_to_name(i0.get_src(0)), true_symbol_offset(version)), instr_str}; } else if (i0.get_src(0).is_reg(rs7()) && i0.get_src(1).is_sym()) { out.require_symbol(i0.get_src(1).get_sym()); @@ -931,7 +934,8 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output, const Instruction& i0, const std::string& instr_str, int& unknown_count, - const LinkedObjectFile* file) { + const LinkedObjectFile* file, + GameVersion version) { switch (i0.kind) { case InstructionKind::CTC2: return handle_ctc2(i0, instr_str); @@ -1073,7 +1077,7 @@ Mips2C_Line handle_normal_instr(Mips2C_Output& output, case InstructionKind::AND: return handle_generic_op3(i0, instr_str, "and_"); // and isn't allowed in C++ case InstructionKind::DADDIU: - return handle_daddiu(output, i0, instr_str); + return handle_daddiu(output, i0, instr_str, version); case InstructionKind::ADDIU: return handle_generic_op2_u16(i0, instr_str); case InstructionKind::QMTC2: @@ -1172,7 +1176,9 @@ struct JumpTableBlock { bool branch_always = false; // that branch is always taken? }; -void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locations) { +void run_mips2c_jump_table(Function* f, + const std::vector& jump_table_locations, + GameVersion version) { fmt::print("mips2c-jump on {}\n", f->name()); u32 magic_code = std::hash()(f->name()); std::unordered_map loc_to_block; @@ -1212,8 +1218,8 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat ASSERT(delay_block.end_instr - delay_block.start_instr == 1); // only 1 instr. auto& delay_instr = f->instructions.at(delay_block.start_instr); auto delay_instr_str = delay_instr.to_string(file->labels); - auto delay_instr_line = - handle_normal_instr(output, delay_instr, delay_instr_str, unknown_count, file); + auto delay_instr_line = handle_normal_instr(output, delay_instr, delay_instr_str, + unknown_count, file, version); output.lines.emplace_back(fmt::format(" {}", delay_instr_line.code), delay_instr_line.comment); ASSERT(delay_block.succ_ft == -1); @@ -1230,7 +1236,7 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); ASSERT(i + 1 == block.end_instr); // then the goto output.lines.emplace_back(fmt::format("next_block = {};", block.succ_branch), @@ -1246,7 +1252,7 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); ASSERT(i + 1 == block.end_instr); // then the goto output.lines.emplace_back( @@ -1264,7 +1270,7 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); // then the goto output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch), @@ -1279,7 +1285,7 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); output.lines.emplace_back("c->jalr(call_addr);", instr_str); } else if (instr.kind == InstructionKind::JR) { // special case for jr's to handle the jump tableing. @@ -1290,7 +1296,8 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat output.lines.emplace_back("break;"); } else { - output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count, file)); + output.lines.push_back( + handle_normal_instr(output, instr, instr_str, unknown_count, file, version)); } ASSERT(output.lines.size() > old_line_count); @@ -1312,7 +1319,7 @@ void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locat } } -void run_mips2c(Function* f) { +void run_mips2c(Function* f, GameVersion version) { g_unknown = 0; auto* file = f->ir2.env.file; std::unordered_set likely_delay_blocks; @@ -1347,8 +1354,8 @@ void run_mips2c(Function* f) { ASSERT(delay_block.end_instr - delay_block.start_instr == 1); // only 1 instr. auto& delay_instr = f->instructions.at(delay_block.start_instr); auto delay_instr_str = delay_instr.to_string(file->labels); - auto delay_instr_line = - handle_normal_instr(output, delay_instr, delay_instr_str, unknown_count, file); + auto delay_instr_line = handle_normal_instr(output, delay_instr, delay_instr_str, + unknown_count, file, version); output.lines.emplace_back(fmt::format(" {}", delay_instr_line.code), delay_instr_line.comment); ASSERT(delay_block.succ_ft == -1); @@ -1364,7 +1371,7 @@ void run_mips2c(Function* f) { auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); ASSERT(i + 1 == block.end_instr); // then the goto output.lines.emplace_back(fmt::format("goto block_{};", block.succ_branch), @@ -1382,7 +1389,7 @@ void run_mips2c(Function* f) { auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); // ASSERT(i + 1 == block.end_instr); // then the goto output.lines.emplace_back(fmt::format("if (bc) {{goto block_{};}}", block.succ_branch), @@ -1398,7 +1405,7 @@ void run_mips2c(Function* f) { auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); // then the goto output.lines.emplace_back(fmt::format("goto end_of_function;", block.succ_branch), @@ -1413,10 +1420,11 @@ void run_mips2c(Function* f) { auto& delay_i = f->instructions.at(i); auto delay_i_str = delay_i.to_string(file->labels); output.lines.push_back( - handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file)); + handle_normal_instr(output, delay_i, delay_i_str, unknown_count, file, version)); output.lines.emplace_back("c->jalr(call_addr);", instr_str); } else { - output.lines.push_back(handle_normal_instr(output, instr, instr_str, unknown_count, file)); + output.lines.push_back( + handle_normal_instr(output, instr, instr_str, unknown_count, file, version)); } ASSERT(output.lines.size() > old_line_count); diff --git a/decompiler/analysis/mips2c.h b/decompiler/analysis/mips2c.h index a13dfcbd6e..ee69b1b73d 100644 --- a/decompiler/analysis/mips2c.h +++ b/decompiler/analysis/mips2c.h @@ -2,9 +2,13 @@ #include +#include "common/versions.h" + namespace decompiler { class Function; -void run_mips2c(Function* f); -void run_mips2c_jump_table(Function* f, const std::vector& jump_table_locations); +void run_mips2c(Function* f, GameVersion version); +void run_mips2c_jump_table(Function* f, + const std::vector& jump_table_locations, + GameVersion version); } // namespace decompiler diff --git a/decompiler/config.cpp b/decompiler/config.cpp index 00b15eb60b..f9031422d0 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -66,6 +66,7 @@ Config read_config_file(const std::string& path_to_config_file, config.process_art_groups = cfg.at("process_art_groups").get(); config.hexdump_code = cfg.at("hexdump_code").get(); config.hexdump_data = cfg.at("hexdump_data").get(); + config.find_functions = cfg.at("find_functions").get(); config.dump_objs = cfg.at("dump_objs").get(); config.print_cfgs = cfg.at("print_cfgs").get(); config.generate_symbol_definition_map = cfg.at("generate_symbol_definition_map").get(); diff --git a/decompiler/config.h b/decompiler/config.h index 74f41c3561..1f3c9b0c71 100644 --- a/decompiler/config.h +++ b/decompiler/config.h @@ -104,6 +104,7 @@ struct Config { bool process_art_groups = false; bool rip_levels = false; bool extract_collision = false; + bool find_functions = false; bool write_hex_near_instructions = false; bool hexdump_code = false; diff --git a/decompiler/config/all-types2.gc b/decompiler/config/all-types2.gc index e69de29bb2..8031702032 100644 --- a/decompiler/config/all-types2.gc +++ b/decompiler/config/all-types2.gc @@ -0,0 +1,16 @@ +;; type system setup +(define-extern object type) +(define-extern type type) +(define-extern structure type) +(define-extern uint128 type) +(define-extern basic type) +(define-extern pair type) +(define-extern array type) + + + +;; some types we need. +(declare-type sparticle-launch-group basic) +(declare-type lightning-spec basic) +(declare-type sparticle-launcher basic) +(declare-type state basic) \ No newline at end of file diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index 56801b8fef..285fa3d14e 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -20,6 +20,9 @@ // Run the decompiler "decompile_code": false, + // run the first pass of the decompiler + "find_functions": true, + //////////////////////////// // DATA ANALYSIS OPTIONS //////////////////////////// diff --git a/decompiler/config/jak2/hacks.jsonc b/decompiler/config/jak2/hacks.jsonc index 178d08b27e..c7c97c3ef7 100644 --- a/decompiler/config/jak2/hacks.jsonc +++ b/decompiler/config/jak2/hacks.jsonc @@ -27,7 +27,35 @@ "hint_inline_assembly_functions": [], "asm_functions_by_name": [ + // checking boxed type is different now - these make the cfg stuff sad + "type?", "print", "printl", "inspect", "valid?", "name=", "(method 8 res-lump)", "joint-control-remap!", "(method 21 game-info)", "(method 12 level)", + "bg", "(anon-function 2 cam-combiner)", "cspace-inspect-tree", "command-get-process", "command-get-trans", "(method 10 script-context)", + "(method 11 script-context)", "(method 9 script-context)", "(anon-function 64 script)", "(anon-function 61 script)", "(anon-function 60 script)", + "(anon-function 54 script)", "(anon-function 52 script)", "(anon-function 49 script)", "(anon-function 33 script)", "debug-menu-func-decode", + "scene-player-init", "(method 77 spyder)", "(method 77 flamer)", "(method 77 grenadier)", "(method 224 bot)", "(method 77 rapid-gunner)", + // until loop without nop: + "rand-vu-int-count-excluding", "rand-vu-int-range-exclude", "(method 9 history)", "history-print", "history-draw", + "(method 9 sparticle-launcher)", "(method 18 tracking-spline)", "cam-string-find-position-rel!", "cam-layout-entity-volume-info-create", + "process-drawable-shock-skel-effect", "target-history-print", "display-list-control", "anim-test-anim-list-handler", + "anim-test-sequence-list-handler", "anim-tester-get-playing-item", "(method 9 mysql-nav-graph)", "(method 58 nav-graph-editor)", + "(method 120 enemy)", "start-pilot-recorder", "(anon-function 10 pilot-recorder)", "(method 0 hover-nav-control)", + "(method 24 nav-network)", "(method 11 predator-manager)", "(method 9 bot-speech-list)", "(method 9 bot-speech-list-shuffle)", + "(anon-function 10 sig-recorder)", "(method 14 trail-graph)", "(method 12 trail-graph)", "(method 11 trail-graph)", + // actual asm + "symlink2", "blerc-a-fragment", "blerc-execute", "foreground-check-longest-edge-asm", "generic-light-proc", + "shadow-add-single-edges","shadow-add-facing-single-tris", "shadow-add-double-tris", "shadow-add-double-edges", + "(method 12 collide-mesh)", "(method 17 collide-edge-work)", "(method 42 collide-shape)", "(method 12 collide-shape-prim-sphere)", + "(method 12 collide-shape-prim-mesh)", "(method 18 collide-shape-prim-mesh)", "(method 10 collide-cache-prim)", + "(method 17 collide-cache)", "(method 16 ocean)", + // unknown bug + "joint-mod-polar-look-at-guts", "(method 13 external-art-control)", "update-mood-copy-ctywide", "update-mood-copy-stadium", + "update-mood-drillb", "reset-target-tracking", "(anon-function 1 target)", "find-nearest-entity", "target-land-effect", + "(method 12 effect-control)", "(method 11 effect-control)", "(method 10 effect-control)", + "(anon-function 2 scene)", "progress-trans", "(method 10 bigmap)", "(method 9 editable-region)", "(method 57 enemy)", + "(anon-function 10 meet-brutter)", "(method 154 vehicle-racer)", "(method 188 predator)", "(anon-function 13 sig0-course)", + "(method 228 hal-sewer)", "(method 154 vehicle-city-racer)", "(method 53 squid)", "(anon-function 11 fort-floor-spike)", + "(method 29 gun-dummy)", "vehicle-explode-post", "(method 158 vehicle-guard)", "(method 207 metalhead-predator)" ], // these functions use pairs and the decompiler diff --git a/decompiler/config/jak2_ntsc_v1.jsonc b/decompiler/config/jak2_ntsc_v1.jsonc index 4f2da6246d..b560ef7aea 100644 --- a/decompiler/config/jak2_ntsc_v1.jsonc +++ b/decompiler/config/jak2_ntsc_v1.jsonc @@ -8,7 +8,7 @@ // if you want to filter to only some object names. // it will make the decompiler much faster. "allowed_objects": [], - "banned_objects": [], + "banned_objects": ["effect-control"], //////////////////////////// // CODE ANALYSIS OPTIONS @@ -19,7 +19,9 @@ "disassemble_code": true, // Run the decompiler - "decompile_code": false, + "decompile_code": true, + + "find_functions": true, //////////////////////////// // DATA ANALYSIS OPTIONS diff --git a/decompiler/main.cpp b/decompiler/main.cpp index 9f5f40d49e..2556b0ab8f 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -151,6 +151,11 @@ int main(int argc, char** argv) { db.process_labels(); fmt::print("[Mem] After code: {} MB\n", get_peak_rss() / (1024 * 1024)); + // top level decompile (do this before printing asm so we get function names) + if (config.find_functions) { + db.ir2_top_level_pass(config); + } + // print disassembly if (config.disassemble_code || config.disassemble_data) { db.write_disassembly(out_folder, config.disassemble_data, config.disassemble_code, diff --git a/game/graphics/gfx.cpp b/game/graphics/gfx.cpp index 6c7495c3bf..f7da0b88fd 100644 --- a/game/graphics/gfx.cpp +++ b/game/graphics/gfx.cpp @@ -241,7 +241,7 @@ void set_fullscreen(DisplayMode mode, int screen) { } void input_mode_set(u32 enable) { - if (enable == s7.offset + FIX_SYM_TRUE) { // #t + if (enable == s7.offset + jak1_symbols::FIX_SYM_TRUE) { // #t Pad::g_input_mode_mapping = g_settings.pad_mapping_info; Pad::EnterInputMode(); } else { diff --git a/game/kernel/klink.cpp b/game/kernel/klink.cpp index e787f380e7..ade87b62e1 100644 --- a/game/kernel/klink.cpp +++ b/game/kernel/klink.cpp @@ -21,6 +21,8 @@ #include "common/util/Assert.h" #include "third-party/fmt/core.h" +using namespace jak1_symbols; + namespace { // turn on printf's for debugging linking issues. constexpr bool link_debug_printfs = false; diff --git a/game/kernel/klisten.cpp b/game/kernel/klisten.cpp index ce28328af6..41db0f1626 100644 --- a/game/kernel/klisten.cpp +++ b/game/kernel/klisten.cpp @@ -17,6 +17,8 @@ #include "kscheme.h" #include "common/symbols.h" +using namespace jak1_symbols; + Ptr ListenerLinkBlock; Ptr ListenerFunction; Ptr kernel_dispatcher; diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index ba526008b8..6db2ae4b60 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -44,6 +44,7 @@ #include "svnrev.h" +using namespace jak1_symbols; using namespace ee; /*! diff --git a/game/kernel/kprint.cpp b/game/kernel/kprint.cpp index 6f98418d1d..8eeaa28e22 100644 --- a/game/kernel/kprint.cpp +++ b/game/kernel/kprint.cpp @@ -24,6 +24,8 @@ #include "common/symbols.h" #include "common/util/Assert.h" +using namespace jak1_symbols; + /////////// // SDATA /////////// diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index daecc47fb4..ab7a65630a 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -25,6 +25,8 @@ #include "game/mips2c/mips2c_table.h" #include "common/util/Assert.h" +using namespace jak1_symbols; + //! Controls link mode when EnableMethodSet = 0, MasterDebug = 1, DiskBoot = 0. Will enable a //! warning message if EnableMethodSet = 1 u32 FastLink; diff --git a/game/mips2c/mips2c_table.cpp b/game/mips2c/mips2c_table.cpp index e0b4c7f154..0cb9432730 100644 --- a/game/mips2c/mips2c_table.cpp +++ b/game/mips2c/mips2c_table.cpp @@ -5,6 +5,8 @@ #include "game/kernel/kscheme.h" #include "common/symbols.h" +using namespace jak1_symbols; + extern "C" { void _mips2c_call_linux(); void _mips2c_call_windows(); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 2dfc109737..635f0650d4 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -5,7 +5,7 @@ #include "common/symbols.h" using namespace emitter; - +using namespace jak1_symbols; // TODO jak 1 symbols namespace { Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_Record irec) { if (rv->rlet_constraint().has_value()) { diff --git a/goalc/debugger/Debugger.cpp b/goalc/debugger/Debugger.cpp index 4d462f0766..d3d3e3b970 100644 --- a/goalc/debugger/Debugger.cpp +++ b/goalc/debugger/Debugger.cpp @@ -513,6 +513,8 @@ bool Debugger::write_memory(const u8* src_buffer, int size, u32 goal_addr) { * Read the GOAL Symbol table from an attached and halted target. */ void Debugger::read_symbol_table() { + // todo: this assumes many things specific to jak 1. + using namespace jak1_symbols; ASSERT(is_valid() && is_attached() && is_halted()); u32 bytes_read = 0; u32 reads = 0; diff --git a/test/decompiler/FormRegressionTest.cpp b/test/decompiler/FormRegressionTest.cpp index 246a833b39..56638b8a8f 100644 --- a/test/decompiler/FormRegressionTest.cpp +++ b/test/decompiler/FormRegressionTest.cpp @@ -173,7 +173,8 @@ std::unique_ptr FormRegressionTest::make_function( // convert instruction to atomic ops DecompWarnings warnings; - auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings, false, {}); + auto ops = convert_function_to_atomic_ops(test->func, program.labels, warnings, false, {}, + GameVersion::Jak1); test->func.ir2.atomic_ops = std::make_shared(std::move(ops)); test->func.ir2.atomic_ops_succeeded = true; test->func.ir2.env.set_end_var(test->func.ir2.atomic_ops->end_op().return_var()); diff --git a/test/decompiler/test_AtomicOpBuilder.cpp b/test/decompiler/test_AtomicOpBuilder.cpp index d31ebc2787..fcad915c7a 100644 --- a/test/decompiler/test_AtomicOpBuilder.cpp +++ b/test/decompiler/test_AtomicOpBuilder.cpp @@ -24,6 +24,7 @@ std::string assembly_from_list(std::vector assemblyLines) { return str; } +// jak 1 specific void test_case(std::string assembly_lines, std::vector output_lines, std::vector> write_regs, @@ -45,7 +46,7 @@ void test_case(std::string assembly_lines, // treat the entire program as a single basic block, and convert! DecompWarnings warnings; convert_block_to_atomic_ops(0, prg.instructions.begin(), prg.instructions.end(), prg.labels, - &container, warnings); + &container, warnings, GameVersion::Jak1); // count operations EXPECT_EQ(container.ops.size(), output_lines.size()); @@ -130,7 +131,7 @@ TEST(DecompilerAtomicOpBuilder, RegUseDuplication) { FunctionAtomicOps container; DecompWarnings warnings; convert_block_to_atomic_ops(0, prg.instructions.begin(), prg.instructions.end(), prg.labels, - &container, warnings); + &container, warnings, GameVersion::Jak1); ASSERT_EQ(1, container.ops.size()); auto& op = container.ops.at(0); for (const auto& reg_group : {op->read_regs(), op->write_regs(), op->clobber_regs()}) { diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index d352482d62..33f49b0202 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -307,6 +307,7 @@ void disassemble(Decompiler& dc) { void decompile(Decompiler& dc, const OfflineTestConfig& config) { dc.db->extract_art_info(); + dc.db->ir2_top_level_pass(*dc.config); dc.db->analyze_functions_ir2({}, *dc.config, config.skip_compile_functions, config.skip_compile_states); } diff --git a/test/test_kernel.cpp b/test/test_kernel.cpp index 2d5342b810..687a9b54ca 100644 --- a/test/test_kernel.cpp +++ b/test/test_kernel.cpp @@ -11,6 +11,8 @@ #include "game/kernel/kscheme.h" #include "all_jak1_symbols.h" +using namespace jak1_symbols; + TEST(Kernel, strend) { char test[] = "test"; char* end = strend(test); diff --git a/tools/MemoryDumpTool/main.cpp b/tools/MemoryDumpTool/main.cpp index 65a0179c86..bca9b3e7cc 100644 --- a/tools/MemoryDumpTool/main.cpp +++ b/tools/MemoryDumpTool/main.cpp @@ -114,6 +114,7 @@ struct SymbolMap { }; SymbolMap build_symbol_map(const Ram& ram, u32 s7) { + // TODO jak 1 specific fmt::print("finding symbols...\n"); SymbolMap map; /* @@ -150,9 +151,10 @@ SymbolMap build_symbol_map(const Ram& ram, u32 s7) { std::unordered_map build_type_map(const Ram& ram, const SymbolMap& symbols, u32 s7) { + // TODO jak 1 specific std::unordered_map result; fmt::print("finding types...\n"); - u32 type_of_type = ram.word(s7 + FIX_SYM_TYPE_TYPE); + u32 type_of_type = ram.word(s7 + jak1_symbols::FIX_SYM_TYPE_TYPE); ASSERT(type_of_type == ram.word(symbols.name_to_addr.at("type"))); for (const auto& [name, addr] : symbols.name_to_addr) {