From b561cdfade5c7df21f6f6f615159293dc5721267 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 24 Oct 2020 14:27:50 -0400 Subject: [PATCH] Make decompiler naming consistent (#94) * use a fixed object file naming by default, option to allow new map file creation * fix prints * fixing up edge cases * update json config --- decompiler/Function/Function.cpp | 5 +- decompiler/Function/Function.h | 9 +- decompiler/Function/TypeAnalysis.cpp | 2 +- decompiler/IR/BasicOpBuilder.cpp | 3 + decompiler/ObjectFile/LinkedObjectFile.cpp | 327 +++++----- decompiler/ObjectFile/LinkedObjectFile.h | 7 +- decompiler/ObjectFile/ObjectFileDB.cpp | 213 +++++-- decompiler/ObjectFile/ObjectFileDB.h | 10 +- decompiler/config.cpp | 4 +- decompiler/config.h | 1 + decompiler/config/jak1_ntsc_black_label.jsonc | 591 ++++++++++++------ decompiler/gui/decompiler_gui.py | 5 +- decompiler/main.cpp | 2 +- goal_src/build/all_objs.txt | 4 +- 14 files changed, 757 insertions(+), 426 deletions(-) diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index 65e769830b..e93a618408 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -1,6 +1,7 @@ #include #include #include "Function.h" +#include "third-party/spdlog/include/spdlog/spdlog.h" #include "decompiler/Disasm/InstructionMatching.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "decompiler/util/DecompilerTypeSystem.h" @@ -89,8 +90,8 @@ void Function::analyze_prologue(const LinkedObjectFile& file) { // storing s7 on the stack is done by interrupt handlers, which we probably don't want to // support if (instr.kind == InstructionKind::SD && instr.get_src(0).get_reg() == make_gpr(Reg::S7)) { - printf("[Warning] %s Suspected ASM function based on this instruction in prologue: %s\n", - guessed_name.to_string().c_str(), instr.to_string(file).c_str()); + spdlog::warn("{} Suspected ASM function based on this instruction in prologue: {}\n", + guessed_name.to_string(), instr.to_string(file)); warnings += "Flagged as ASM function because of " + instr.to_string(file) + "\n"; suspected_asm = true; return; diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index 7bb7d4c2e6..223e56b8a7 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -26,6 +26,9 @@ struct FunctionName { int method_id = -1; // only applicable for METHOD int unique_id = -1; + int id_in_object = -1; + std::string object_name; + std::string to_string() const { switch (kind) { case FunctionKind::GLOBAL: @@ -35,7 +38,7 @@ struct FunctionName { case FunctionKind::TOP_LEVEL_INIT: return "(top-level-login)"; case FunctionKind::UNIDENTIFIED: - return "(anon-function " + std::to_string(unique_id) + ")"; + return "(anon-function " + std::to_string(id_in_object) + " " + object_name + ")"; default: throw std::runtime_error("Unsupported FunctionKind"); } @@ -55,10 +58,6 @@ struct FunctionName { type_name = std::move(tn); method_id = id; } - - bool expected_unique() const { - return kind == FunctionKind::GLOBAL || kind == FunctionKind::METHOD; - } }; class BasicOpTypeInfo { diff --git a/decompiler/Function/TypeAnalysis.cpp b/decompiler/Function/TypeAnalysis.cpp index 9046a633e0..38b7e43008 100644 --- a/decompiler/Function/TypeAnalysis.cpp +++ b/decompiler/Function/TypeAnalysis.cpp @@ -80,7 +80,7 @@ void Function::run_type_analysis(const TypeSpec& my_type, assert(my_type.arg_count() > 0); int n_args = int(my_type.arg_count()) - 1; - auto& return_type = my_type.get_arg(int(my_type.arg_count()) - 1); + // auto& return_type = my_type.get_arg(int(my_type.arg_count()) - 1); // all types at the entrance of each basic block. std::vector bb_entry_types; diff --git a/decompiler/IR/BasicOpBuilder.cpp b/decompiler/IR/BasicOpBuilder.cpp index f70bfad09d..9a5a15d872 100644 --- a/decompiler/IR/BasicOpBuilder.cpp +++ b/decompiler/IR/BasicOpBuilder.cpp @@ -1879,6 +1879,9 @@ void add_basic_ops_to_block(Function* func, const BasicBlock& block, LinkedObjec case InstructionKind::PCPYH: case InstructionKind::PINTEH: + case InstructionKind::MTDAB: + case InstructionKind::MTDABM: + // 128 bit integer // case InstructionKind::LQ: // case InstructionKind::SQ: diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index 5307cb6d87..836d0fe7e8 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -12,6 +12,7 @@ #include "decompiler/Disasm/InstructionDecode.h" #include "decompiler/config.h" #include "third-party/json.hpp" +#include "third-party/spdlog/include/spdlog/spdlog.h" /*! * Set the number of segments in this object file. @@ -505,7 +506,7 @@ void LinkedObjectFile::process_fp_relative_links() { } } -std::string LinkedObjectFile::to_asm_json() { +std::string LinkedObjectFile::to_asm_json(const std::string& obj_file_name) { nlohmann::json data; std::vector functions; @@ -515,7 +516,10 @@ std::string LinkedObjectFile::to_asm_json() { auto& func = functions_by_seg.at(seg).at(fi); auto fname = func.guessed_name.to_string(); if (functions_seen.find(fname) != functions_seen.end()) { - printf("duplicated %s\n", fname.c_str()); // todo - this needs fixing + spdlog::warn( + "Function {} appears multiple times in the same object file {} - it cannot be uniquely " + "referenced from config", + func.guessed_name.to_string(), obj_file_name); functions_seen[fname]++; fname += "-v" + std::to_string(functions_seen[fname]); } else { @@ -527,6 +531,7 @@ std::string LinkedObjectFile::to_asm_json() { f["type"] = func.type.print(); f["segment"] = seg; f["warnings"] = func.warnings; + f["parent_object"] = obj_file_name; std::vector ops; for (int i = 1; i < func.end_word - func.start_word; i++) { @@ -569,6 +574,179 @@ std::string LinkedObjectFile::to_asm_json() { return data.dump(); } +std::string LinkedObjectFile::print_function_disassembly(Function& func, + int seg, + bool write_hex, + const std::string& extra_name) { + std::string result; + result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; + result += "; .function " + func.guessed_name.to_string() + " " + extra_name + "\n"; + result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; + result += func.prologue.to_string(2) + "\n"; + if (!func.warnings.empty()) { + result += "Warnings: " + func.warnings + "\n"; + } + + // print each instruction in the function. + bool in_delay_slot = false; + + for (int i = 1; i < func.end_word - func.start_word; i++) { + auto label_id = get_label_at(seg, (func.start_word + i) * 4); + if (label_id != -1) { + result += labels.at(label_id).name + ":\n"; + } + + for (int j = 1; j < 4; j++) { + // assert(get_label_at(seg, (func.start_word + i)*4 + j) == -1); + if (get_label_at(seg, (func.start_word + i) * 4 + j) != -1) { + result += "BAD OFFSET LABEL: "; + result += labels.at(get_label_at(seg, (func.start_word + i) * 4 + j)).name + "\n"; + assert(false); + } + } + + auto& instr = func.instructions.at(i); + std::string line = " " + instr.to_string(*this); + + if (write_hex) { + if (line.length() < 60) { + line.append(60 - line.length(), ' '); + } + result += line; + result += " ;;"; + auto& word = words_by_seg[seg].at(func.start_word + i); + append_word_to_string(result, word); + } else { + // print basic op stuff + if (func.has_basic_ops() && func.instr_starts_basic_op(i)) { + if (line.length() < 30) { + line.append(30 - line.length(), ' '); + } + line += ";; " + func.get_basic_op_at_instr(i)->print(*this); + for (int iidx = 0; iidx < instr.n_src; iidx++) { + if (instr.get_src(iidx).is_label()) { + auto lab = labels.at(instr.get_src(iidx).get_label()); + if (is_string(lab.target_segment, lab.offset)) { + line += " " + get_goal_string(lab.target_segment, lab.offset / 4 - 1); + } + } + } + + // print type map + if (func.has_typemaps()) { + if (line.length() < 60) { + line.append(60 - line.length(), ' '); + } + line += " tm: "; + auto& tm = func.get_typemap_by_instr_idx(i); + bool added = false; + for (auto reg_kind : {Reg::RegisterKind::GPR, Reg::RegisterKind::FPR}) { + for (int reg_idx = 0; reg_idx < 32; reg_idx++) { + auto gpr = Register(reg_kind, reg_idx); + auto kv = tm.find(gpr); + if (kv != tm.end()) { + added = true; + line += fmt::format("{}: {}, ", gpr.to_charp(), kv->second.print()); + } + } + } + + if (added) { + line.pop_back(); + line.pop_back(); + } + } + } + result += line + "\n"; + } + + if (in_delay_slot) { + result += "\n"; + in_delay_slot = false; + } + + if (gOpcodeInfo[(int)instr.kind].has_delay_slot) { + in_delay_slot = true; + } + } + result += "\n"; + // + // int bid = 0; + // for(auto& bblock : func.basic_blocks) { + // result += "BLOCK " + std::to_string(bid++)+ "\n"; + // for(int i = bblock.start_word; i < bblock.end_word; i++) { + // if(i >= 0 && i < func.instructions.size()) { + // result += func.instructions.at(i).to_string(*this) + "\n"; + // } else { + // result += "BAD BBLOCK INSTR ID " + std::to_string(i); + // } + // } + // } + + // hack + if (func.cfg && !func.cfg->is_fully_resolved()) { + result += func.cfg->to_dot(); + result += "\n"; + } + if (func.cfg) { + result += func.cfg->to_form_string() + "\n"; + + // To debug block stuff. + /* + int bid = 0; + for(auto& block : func.basic_blocks) { + in_delay_slot = false; + result += "B" + std::to_string(bid++) + "\n"; + for(auto i = block.start_word; i < block.end_word; i++) { + auto label_id = get_label_at(seg, (func.start_word + i) * 4); + if (label_id != -1) { + result += labels.at(label_id).name + ":\n"; + } + auto& instr = func.instructions.at(i); + result += " " + instr.to_string(*this) + "\n"; + if (in_delay_slot) { + result += "\n"; + in_delay_slot = false; + } + + if (gOpcodeInfo[(int)instr.kind].has_delay_slot) { + in_delay_slot = true; + } + } + } + */ + } + + if (func.ir) { + result += ";; ir\n"; + result += func.ir->print(*this); + } + + result += "\n\n\n"; + return result; +} + +std::string LinkedObjectFile::print_asm_function_disassembly(const std::string& my_name) { + std::string result; + for (int seg = segments; seg-- > 0;) { + bool got_in_seg = false; + for (auto& func : functions_by_seg.at(seg)) { + if (func.suspected_asm) { + if (!got_in_seg) { + result += ";------------------------------------------\n; "; + result += segment_names[seg]; + result += " of " + my_name; + result += "\n;------------------------------------------\n\n"; + got_in_seg = true; + } + result += print_function_disassembly(func, seg, false, my_name); + } + } + } + + return result; +} + /*! * Print disassembled functions and data segments. */ @@ -585,150 +763,7 @@ std::string LinkedObjectFile::print_disassembly() { // functions for (auto& func : functions_by_seg.at(seg)) { - result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; - result += "; .function " + func.guessed_name.to_string() + "\n"; - result += ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"; - result += func.prologue.to_string(2) + "\n"; - if (!func.warnings.empty()) { - result += "Warnings: " + func.warnings + "\n"; - } - - // print each instruction in the function. - bool in_delay_slot = false; - - for (int i = 1; i < func.end_word - func.start_word; i++) { - auto label_id = get_label_at(seg, (func.start_word + i) * 4); - if (label_id != -1) { - result += labels.at(label_id).name + ":\n"; - } - - for (int j = 1; j < 4; j++) { - // assert(get_label_at(seg, (func.start_word + i)*4 + j) == -1); - if (get_label_at(seg, (func.start_word + i) * 4 + j) != -1) { - result += "BAD OFFSET LABEL: "; - result += labels.at(get_label_at(seg, (func.start_word + i) * 4 + j)).name + "\n"; - assert(false); - } - } - - auto& instr = func.instructions.at(i); - std::string line = " " + instr.to_string(*this); - - if (write_hex) { - if (line.length() < 60) { - line.append(60 - line.length(), ' '); - } - result += line; - result += " ;;"; - auto& word = words_by_seg[seg].at(func.start_word + i); - append_word_to_string(result, word); - } else { - // print basic op stuff - if (func.has_basic_ops() && func.instr_starts_basic_op(i)) { - if (line.length() < 30) { - line.append(30 - line.length(), ' '); - } - line += ";; " + func.get_basic_op_at_instr(i)->print(*this); - for (int iidx = 0; iidx < instr.n_src; iidx++) { - if (instr.get_src(iidx).is_label()) { - auto lab = labels.at(instr.get_src(iidx).get_label()); - if (is_string(lab.target_segment, lab.offset)) { - line += " " + get_goal_string(lab.target_segment, lab.offset / 4 - 1); - } - } - } - - // print type map - if (func.has_typemaps()) { - if (line.length() < 60) { - line.append(60 - line.length(), ' '); - } - line += " tm: "; - auto& tm = func.get_typemap_by_instr_idx(i); - bool added = false; - for (auto reg_kind : {Reg::RegisterKind::GPR, Reg::RegisterKind::FPR}) { - for (int reg_idx = 0; reg_idx < 32; reg_idx++) { - auto gpr = Register(reg_kind, reg_idx); - auto kv = tm.find(gpr); - if (kv != tm.end()) { - added = true; - line += fmt::format("{}: {}, ", gpr.to_charp(), kv->second.print()); - } - } - } - - if (added) { - line.pop_back(); - line.pop_back(); - } - } - } - result += line + "\n"; - } - - if (in_delay_slot) { - result += "\n"; - in_delay_slot = false; - } - - if (gOpcodeInfo[(int)instr.kind].has_delay_slot) { - in_delay_slot = true; - } - } - result += "\n"; - // - // int bid = 0; - // for(auto& bblock : func.basic_blocks) { - // result += "BLOCK " + std::to_string(bid++)+ "\n"; - // for(int i = bblock.start_word; i < bblock.end_word; i++) { - // if(i >= 0 && i < func.instructions.size()) { - // result += func.instructions.at(i).to_string(*this) + "\n"; - // } else { - // result += "BAD BBLOCK INSTR ID " + std::to_string(i); - // } - // } - // } - - // hack - if (func.cfg && !func.cfg->is_fully_resolved()) { - result += func.cfg->to_dot(); - result += "\n"; - } - if (func.cfg) { - result += func.cfg->to_form_string() + "\n"; - - // To debug block stuff. - /* - int bid = 0; - for(auto& block : func.basic_blocks) { - in_delay_slot = false; - result += "B" + std::to_string(bid++) + "\n"; - for(auto i = block.start_word; i < block.end_word; i++) { - auto label_id = get_label_at(seg, (func.start_word + i) * 4); - if (label_id != -1) { - result += labels.at(label_id).name + ":\n"; - } - auto& instr = func.instructions.at(i); - result += " " + instr.to_string(*this) + "\n"; - if (in_delay_slot) { - result += "\n"; - in_delay_slot = false; - } - - if (gOpcodeInfo[(int)instr.kind].has_delay_slot) { - in_delay_slot = true; - } - } - } - */ - } - - if (func.ir) { - result += ";; ir\n"; - result += func.ir->print(*this); - } - - result += "\n\n\n"; + result += print_function_disassembly(func, seg, write_hex, ""); } // print data diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index aca90b8bca..ea6b1f352e 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -61,7 +61,12 @@ class LinkedObjectFile { std::string print_disassembly(); bool has_any_functions(); void append_word_to_string(std::string& dest, const LinkedWord& word) const; - std::string to_asm_json(); + std::string to_asm_json(const std::string& obj_file_name); + std::string print_function_disassembly(Function& func, + int seg, + bool write_hex, + const std::string& extra_name); + std::string print_asm_function_disassembly(const std::string& my_name); struct Stats { uint32_t total_code_bytes = 0; diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 30364658b9..4bf7a95aa6 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -21,24 +21,29 @@ #include "decompiler/IR/BasicOpBuilder.h" #include "decompiler/IR/CfgBuilder.h" #include "third-party/spdlog/include/spdlog/spdlog.h" +#include "third-party/json.hpp" -/*! - * Get a unique name for this object file. - */ -std::string ObjectFileRecord::to_unique_name() const { - return name + "-v" + std::to_string(version); +namespace { +std::string strip_dgo_extension(const std::string& x) { + auto ext = x.substr(x.length() - 4, 4); + if (ext == ".CGO" || ext == ".cgo" || ext == ".DGO" || ext == ".dgo") { + return x.substr(0, x.length() - 4); + } + return x; } +} // namespace std::string ObjectFileData::to_unique_name() const { + if (!name_from_map.empty()) { + return name_from_map; + } + if (has_multiple_versions) { std::string result = record.name + "-"; auto dgo_names_sorted = dgo_names; std::sort(dgo_names_sorted.begin(), dgo_names_sorted.end()); for (auto x : dgo_names_sorted) { - auto ext = x.substr(x.length() - 4, 4); - if (ext == ".CGO" || ext == ".cgo" || ext == ".DGO" || ext == ".dgo") { - x = x.substr(0, x.length() - 4); - } + x = strip_dgo_extension(x); result += x + "-"; } result.pop_back(); @@ -47,7 +52,7 @@ std::string ObjectFileData::to_unique_name() const { return record.name; } } -ObjectFileData& ObjectFileDB::lookup_record(ObjectFileRecord rec) { +ObjectFileData& ObjectFileDB::lookup_record(const ObjectFileRecord& rec) { ObjectFileData* result = nullptr; for (auto& x : obj_files_by_name[rec.name]) { @@ -65,12 +70,23 @@ ObjectFileData& ObjectFileDB::lookup_record(ObjectFileRecord rec) { /*! * Build an object file DB for the given list of DGOs. */ -ObjectFileDB::ObjectFileDB(const std::vector& _dgos) { +ObjectFileDB::ObjectFileDB(const std::vector& _dgos, + const std::string& obj_file_name_map_file) { Timer timer; spdlog::info("-Loading types..."); dts.parse_type_defs({"decompiler", "config", "all-types.gc"}); + if (!obj_file_name_map_file.empty()) { + spdlog::info("-Loading obj name map file..."); + load_map_file(file_util::read_text_file(file_util::get_file_path({obj_file_name_map_file}))); + } else { + spdlog::warn( + "Not using an obj name map file! The decompiler will automatically generate object file " + "names and write them to out/objs.txt. It is recommended to reuse this map file to get " + "consistent naming when doing a partial decompilation."); + } + spdlog::info("-Initializing ObjectFileDB..."); for (auto& dgo : _dgos) { get_objs_from_dgo(dgo); @@ -82,11 +98,36 @@ ObjectFileDB::ObjectFileDB(const std::vector& _dgos) { spdlog::info("Total objs: {}", stats.total_obj_files); spdlog::info("Unique objs: {}", stats.unique_obj_files); spdlog::info("Unique data: {} bytes", stats.unique_obj_bytes); - spdlog::info("Total {} ms ({:3f} MB/sec, {} obj/sec", timer.getMs(), + spdlog::info("Total {:.2f} ms ({:.3f} MB/sec, {:.2f} obj/sec)", timer.getMs(), stats.total_dgo_bytes / ((1u << 20u) * timer.getSeconds()), stats.total_obj_files / timer.getSeconds()); } +void ObjectFileDB::load_map_file(const std::string& map_data) { + auto j = nlohmann::json::parse(map_data, nullptr, true, true); + + for (auto& x : j) { + auto mapped_name = x[0].get(); + auto game_name = x[1].get(); + auto dgo_names = x[3].get>(); + bool is_ag = mapped_name.find("-ag") != std::string::npos; + auto game_name_with_ag = game_name; + if (is_ag) { + game_name_with_ag += "-ag"; + } + + // add dgo + for (auto& dgo : dgo_names) { + auto kv = dgo_obj_name_map[dgo].find(game_name_with_ag); + if (kv != dgo_obj_name_map[dgo].end()) { + spdlog::error("Object {} in dgo {} occurs more than one time.", game_name_with_ag, dgo); + assert(false); + } + dgo_obj_name_map[dgo][game_name_with_ag] = mapped_name; + } + } +} + // Header for a DGO file struct DgoHeader { uint32_t size; @@ -215,6 +256,14 @@ void ObjectFileDB::get_objs_from_dgo(const std::string& filename) { assert(reader.bytes_left() >= obj_header.size); assert_string_empty_after(obj_header.name, 60); + if (std::string(obj_header.name).find("-ag") != std::string::npos) { + spdlog::error( + "Object file {} has \"-ag\" in its name. This will break any tools which use this to " + "detect an art group", + obj_header.name); + assert(false); + } + auto name = get_object_file_name(obj_header.name, reader.here(), obj_header.size); add_obj_from_dgo(name, obj_header.name, reader.here(), obj_header.size, dgo_base_name); @@ -276,6 +325,21 @@ void ObjectFileDB::add_obj_from_dgo(const std::string& obj_name, data.record.version = obj_files_by_name[obj_name].size(); data.name_in_dgo = name_in_dgo; data.obj_version = version; + if (!dgo_obj_name_map.empty()) { + auto dgo_kv = dgo_obj_name_map.find(strip_dgo_extension(dgo_name)); + if (dgo_kv == dgo_obj_name_map.end()) { + spdlog::error("Object {} is from DGO {}, but this DGO wasn't in the map.", obj_name, + dgo_name); + assert(false); + } + + auto name_kv = dgo_kv->second.find(obj_name); + if (name_kv == dgo_kv->second.end()) { + spdlog::error("Object {} from DGO {} wasn't found in the name map.", obj_name, dgo_name); + assert(false); + } + data.name_from_map = name_kv->second; + } obj_files_by_dgo[dgo_name].push_back(data.record); obj_files_by_name[obj_name].emplace_back(std::move(data)); stats.unique_obj_files++; @@ -421,7 +485,7 @@ void ObjectFileDB::write_object_file_words(const std::string& output_dir, bool d for_each_obj([&](ObjectFileData& obj) { if (obj.linked_data.segments == 3 || !dump_v3_only) { auto file_text = obj.linked_data.print_words(); - auto file_name = combine_path(output_dir, obj.record.to_unique_name() + ".txt"); + auto file_name = combine_path(output_dir, obj.to_unique_name() + ".txt"); total_bytes += file_text.size(); file_util::write_text_file(file_name, file_text); total_files++; @@ -430,8 +494,8 @@ void ObjectFileDB::write_object_file_words(const std::string& output_dir, bool d spdlog::info("Wrote object file dumps:"); spdlog::info(" Total {} files", total_files); - spdlog::info(" Total {:3f} MB", total_bytes / ((float)(1u << 20u))); - spdlog::info(" Total {} ms ({:3f} MB/sec)", timer.getMs(), + spdlog::info(" Total {:.3f} MB", total_bytes / ((float)(1u << 20u))); + spdlog::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(), total_bytes / ((1u << 20u) * timer.getSeconds())); // printf("\n"); } @@ -445,12 +509,15 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir, Timer timer; uint32_t total_bytes = 0, total_files = 0; + std::string asm_functions; + for_each_obj([&](ObjectFileData& obj) { if (obj.linked_data.has_any_functions() || disassemble_objects_without_functions) { auto file_text = obj.linked_data.print_disassembly(); - auto file_name = combine_path(output_dir, obj.record.to_unique_name() + ".func"); + asm_functions += obj.linked_data.print_asm_function_disassembly(obj.to_unique_name()); + auto file_name = combine_path(output_dir, obj.to_unique_name() + ".func"); - auto json_asm_text = obj.linked_data.to_asm_json(); + auto json_asm_text = obj.linked_data.to_asm_json(obj.to_unique_name()); auto json_asm_file_name = combine_path(output_dir, obj.to_unique_name() + "_asm.json"); file_util::write_text_file(json_asm_file_name, json_asm_text); @@ -460,12 +527,15 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir, } }); + total_bytes += asm_functions.size(); + total_files++; + file_util::write_text_file(combine_path(output_dir, "asm_functions.func"), asm_functions); + spdlog::info("Wrote functions dumps:"); spdlog::info(" Total {} files", total_files); spdlog::info(" Total {} MB", total_bytes / ((float)(1u << 20u))); - spdlog::info(" Total {} ms ({:3f} MB/sec)", timer.getMs(), + spdlog::info(" Total {} ms ({:.3f} MB/sec)", timer.getMs(), total_bytes / ((1u << 20u) * timer.getSeconds())); - // printf("\n"); } /*! @@ -482,31 +552,31 @@ void ObjectFileDB::find_code() { obj.linked_data.find_functions(); obj.linked_data.disassemble_functions(); - if (get_config().game_version == 1 || obj.record.to_unique_name() != "effect-control-v0") { + if (get_config().game_version == 1 || obj.to_unique_name() != "effect-control-v0") { obj.linked_data.process_fp_relative_links(); } else { - spdlog::warn("Skipping process_fp_relative_links in {}", obj.record.to_unique_name().c_str()); + spdlog::warn("Skipping process_fp_relative_links in {}", obj.to_unique_name().c_str()); } auto& obj_stats = obj.linked_data.stats; if (obj_stats.code_bytes / 4 > obj_stats.decoded_ops) { - spdlog::warn("Failed to decode all in {} ({} / {})", obj.record.to_unique_name().c_str(), + spdlog::warn("Failed to decode all in {} ({} / {})", obj.to_unique_name().c_str(), obj_stats.decoded_ops, obj_stats.code_bytes / 4); } combined_stats.add(obj.linked_data.stats); }); spdlog::info("Found code:"); - spdlog::info(" Code {} MB", combined_stats.code_bytes / (float)(1 << 20)); - spdlog::info(" Data {} MB", combined_stats.data_bytes / (float)(1 << 20)); + spdlog::info(" Code {:.3f} MB", combined_stats.code_bytes / (float)(1 << 20)); + spdlog::info(" Data {:.3f} MB", combined_stats.data_bytes / (float)(1 << 20)); spdlog::info(" Functions: {}", combined_stats.function_count); - spdlog::info(" fp uses resolved: {} / {} ({} %)", combined_stats.n_fp_reg_use_resolved, + spdlog::info(" fp uses resolved: {} / {} ({:.3f} %)", combined_stats.n_fp_reg_use_resolved, combined_stats.n_fp_reg_use, 100.f * (float)combined_stats.n_fp_reg_use_resolved / combined_stats.n_fp_reg_use); auto total_ops = combined_stats.code_bytes / 4; - spdlog::info(" Decoded {} / {} ({} %)", combined_stats.decoded_ops, total_ops, + spdlog::info(" Decoded {} / {} ({:.3f} %)", combined_stats.decoded_ops, total_ops, 100.f * (float)combined_stats.decoded_ops / total_ops); - spdlog::info(" Total {} ms", timer.getMs()); + spdlog::info(" Total {:.3f} ms", timer.getMs()); // printf("\n"); } @@ -523,7 +593,7 @@ void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) { auto scripts = obj.linked_data.print_scripts(); if (!scripts.empty()) { all_scripts += ";--------------------------------------\n"; - all_scripts += "; " + obj.record.to_unique_name() + "\n"; + all_scripts += "; " + obj.to_unique_name() + "\n"; all_scripts += ";---------------------------------------\n"; all_scripts += scripts; } @@ -533,7 +603,7 @@ void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) { file_util::write_text_file(file_name, all_scripts); spdlog::info("Found scripts:"); - spdlog::info(" Total {} ms\n", timer.getMs()); + spdlog::info(" Total {:.3f} ms\n", timer.getMs()); } void ObjectFileDB::analyze_functions() { @@ -564,41 +634,46 @@ void ObjectFileDB::analyze_functions() { std::unordered_map> duplicated_functions; int uid = 1; - for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { - (void)segment_id; - func.guessed_name.unique_id = uid++; - auto name = func.guessed_name.to_string(); - if (func.guessed_name.expected_unique()) { - if (unique_names.find(name) != unique_names.end()) { - duplicated_functions[name].insert(data.record.to_unique_name()); + for_each_obj([&](ObjectFileData& data) { + int func_in_obj = 0; + for (int segment_id = 0; segment_id < int(data.linked_data.segments); segment_id++) { + for (auto& func : data.linked_data.functions_by_seg.at(segment_id)) { + func.guessed_name.unique_id = uid++; + func.guessed_name.id_in_object = func_in_obj++; + func.guessed_name.object_name = data.to_unique_name(); + auto name = func.guessed_name.to_string(); + + if (unique_names.find(name) != unique_names.end()) { + duplicated_functions[name].insert(data.to_unique_name()); + } + + unique_names.insert(name); + + if (config.asm_functions_by_name.find(name) != config.asm_functions_by_name.end()) { + func.warnings += "flagged as asm by config\n"; + func.suspected_asm = true; + } } - - unique_names.insert(name); - } - - if (config.asm_functions_by_name.find(name) != config.asm_functions_by_name.end()) { - func.warnings += "flagged as asm by config\n"; - func.suspected_asm = true; } }); for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { (void)segment_id; auto name = func.guessed_name.to_string(); - if (func.guessed_name.expected_unique()) { - if (duplicated_functions.find(name) != duplicated_functions.end()) { - duplicated_functions[name].insert(data.record.to_unique_name()); - func.warnings += "this function exists in multiple non-identical object files"; - } + + if (duplicated_functions.find(name) != duplicated_functions.end()) { + duplicated_functions[name].insert(data.to_unique_name()); + func.warnings += "this function exists in multiple non-identical object files"; } }); - - // for(const auto& kv : duplicated_functions) { - // printf("Function %s is found in non-identical object files:\n", kv.first.c_str()); - // for(const auto& obj : kv.second) { - // printf(" %s\n", obj.c_str()); - // } - // } + /* + for (const auto& kv : duplicated_functions) { + printf("Function %s is found in non-identical object files:\n", kv.first.c_str()); + for (const auto& obj : kv.second) { + printf(" %s\n", obj.c_str()); + } + } + */ } int total_trivial_cfg_functions = 0; @@ -616,17 +691,20 @@ void ObjectFileDB::analyze_functions() { timer.start(); int total_basic_blocks = 0; for_each_function([&](Function& func, int segment_id, ObjectFileData& data) { - // printf("in %s\n", func.guessed_name.to_string().c_str()); + // printf("in %s from %s\n", func.guessed_name.to_string().c_str(), + // data.to_unique_name().c_str()); auto blocks = find_blocks_in_function(data.linked_data, segment_id, func); total_basic_blocks += blocks.size(); func.basic_blocks = blocks; total_functions++; if (!func.suspected_asm) { - // run analysis - // first, find the prologue/epilogue func.analyze_prologue(data.linked_data); + } + + if (!func.suspected_asm) { + // run analysis // build a control flow graph func.cfg = build_cfg(data.linked_data, segment_id, func); @@ -649,6 +727,9 @@ void ObjectFileDB::analyze_functions() { if (func.cfg->is_fully_resolved()) { resolved_cfg_functions++; + } else { + spdlog::warn("Function {} from {} failed cfg ir", func.guessed_name.to_string(), + data.to_unique_name()); } // type analysis @@ -663,9 +744,11 @@ void ObjectFileDB::analyze_functions() { } // GOOD! func.type = kv->second; + /* spdlog::info("Type Analysis on {} {}", func.guessed_name.to_string(), kv->second.print()); func.run_type_analysis(kv->second, dts, data.linked_data); + */ if (func.has_typemaps()) { successful_type_analysis++; } @@ -690,24 +773,20 @@ void ObjectFileDB::analyze_functions() { if (!func.guessed_name.empty()) { total_named_functions++; } - - // if (func.guessed_name.to_string() == "inspect") { - // assert(false); - // } }); spdlog::info("Found {} functions ({} with no control flow)", total_functions, total_trivial_cfg_functions); - spdlog::info("Named {}/{} functions ({}%)", total_named_functions, total_functions, + spdlog::info("Named {}/{} functions ({:.3f}%)", total_named_functions, total_functions, 100.f * float(total_named_functions) / float(total_functions)); spdlog::info("Excluding {} asm functions", asm_funcs); - spdlog::info("Found {} basic blocks in {} ms", total_basic_blocks, timer.getMs()); - spdlog::info(" {}/{} functions passed cfg analysis stage ({}%)", resolved_cfg_functions, + spdlog::info("Found {} basic blocks in {:.3f} ms", total_basic_blocks, timer.getMs()); + spdlog::info(" {}/{} functions passed cfg analysis stage ({:.3f}%)", resolved_cfg_functions, non_asm_funcs, 100.f * float(resolved_cfg_functions) / float(non_asm_funcs)); int successful_basic_ops = total_basic_ops - total_failed_basic_ops; - spdlog::info(" {}/{} basic ops converted successfully ({}%)", successful_basic_ops, + spdlog::info(" {}/{} basic ops converted successfully ({:.3f}%)", successful_basic_ops, total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops)); - spdlog::info(" {}/{} cfgs converted to ir ({}%)", successful_cfg_irs, non_asm_funcs, + spdlog::info(" {}/{} cfgs converted to ir ({:.3f}%)", successful_cfg_irs, non_asm_funcs, 100.f * float(successful_cfg_irs) / float(non_asm_funcs)); spdlog::info(" {}/{} functions passed type analysis ({:.2f}%)\n", successful_type_analysis, non_asm_funcs, 100.f * float(successful_type_analysis) / float(non_asm_funcs)); diff --git a/decompiler/ObjectFile/ObjectFileDB.h b/decompiler/ObjectFile/ObjectFileDB.h index b9d8915f6f..2f18d4c4d6 100644 --- a/decompiler/ObjectFile/ObjectFileDB.h +++ b/decompiler/ObjectFile/ObjectFileDB.h @@ -21,10 +21,9 @@ * A "record" which can be used to identify an object file. */ struct ObjectFileRecord { - std::string name; + std::string name; // including -ag, not including dgo suffix int version = -1; uint32_t hash = 0; - std::string to_unique_name() const; }; /*! @@ -38,13 +37,14 @@ struct ObjectFileData { int obj_version = -1; bool has_multiple_versions = false; std::string name_in_dgo; + std::string name_from_map; std::string to_unique_name() const; uint32_t reference_count = 0; // number of times its used. }; class ObjectFileDB { public: - ObjectFileDB(const std::vector& _dgos); + ObjectFileDB(const std::vector& _dgos, const std::string& obj_file_name_map_file); std::string generate_dgo_listing(); std::string generate_obj_listing(); void process_link_data(); @@ -55,10 +55,11 @@ class ObjectFileDB { void write_object_file_words(const std::string& output_dir, bool dump_v3_only); void write_disassembly(const std::string& output_dir, bool disassemble_objects_without_functions); void analyze_functions(); - ObjectFileData& lookup_record(ObjectFileRecord rec); + ObjectFileData& lookup_record(const ObjectFileRecord& rec); DecompilerTypeSystem dts; private: + void load_map_file(const std::string& map_data); void get_objs_from_dgo(const std::string& filename); void add_obj_from_dgo(const std::string& obj_name, const std::string& name_in_dgo, @@ -105,6 +106,7 @@ class ObjectFileDB { std::unordered_map> obj_files_by_dgo; std::vector obj_file_order; + std::unordered_map> dgo_obj_name_map; struct { uint32_t total_dgo_bytes = 0; diff --git a/decompiler/config.cpp b/decompiler/config.cpp index 6d6a57e8ea..f270d0762e 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -2,7 +2,6 @@ #include "third-party/json.hpp" #include "util/FileIO.h" #include "common/util/FileUtil.h" -#include "third-party/spdlog/include/spdlog/spdlog.h" Config gConfig; @@ -17,6 +16,9 @@ void set_config(const std::string& path_to_config_file) { gConfig.game_version = cfg.at("game_version").get(); gConfig.dgo_names = cfg.at("dgo_names").get>(); + if (cfg.contains("obj_file_name_map_file")) { + gConfig.obj_file_name_map_file = cfg.at("obj_file_name_map_file").get(); + } gConfig.write_disassembly = cfg.at("write_disassembly").get(); gConfig.write_hexdump = cfg.at("write_hexdump").get(); gConfig.write_scripts = cfg.at("write_scripts").get(); diff --git a/decompiler/config.h b/decompiler/config.h index c2c455741a..5310266cfa 100644 --- a/decompiler/config.h +++ b/decompiler/config.h @@ -10,6 +10,7 @@ struct Config { int game_version = -1; std::vector dgo_names; + std::string obj_file_name_map_file; bool write_disassembly = false; bool write_hexdump = false; bool write_scripts = false; diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index 13852f0465..9df4b4e5fe 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -3,14 +3,14 @@ { "game_version":1, // the order here matters. KERNEL and GAME should go first - "dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO"], - /*, "CGO/ENGINE.CGO" + "dgo_names":["CGO/KERNEL.CGO", "CGO/GAME.CGO" + , "CGO/ENGINE.CGO" , "CGO/ART.CGO", "DGO/BEA.DGO", "DGO/CIT.DGO", "CGO/COMMON.CGO", "DGO/DAR.DGO", "DGO/DEM.DGO", "DGO/FIN.DGO", "DGO/INT.DGO", "DGO/JUB.DGO", "DGO/JUN.DGO", "CGO/JUNGLE.CGO", "CGO/L1.CGO", "DGO/FIC.DGO", "DGO/LAV.DGO", "DGO/MAI.DGO", "CGO/MAINCAVE.CGO", "DGO/MIS.DGO", "DGO/OGR.DGO", "CGO/RACERP.CGO", "DGO/ROB.DGO", "DGO/ROL.DGO", "DGO/SNO.DGO", "DGO/SUB.DGO", "DGO/SUN.DGO", "CGO/SUNKEN.CGO", "DGO/SWA.DGO", "DGO/TIT.DGO", "DGO/TRA.DGO", "DGO/VI1.DGO", "DGO/VI2.DGO", "DGO/VI3.DGO", "CGO/VILLAGEP.CGO", "CGO/WATER-AN.CGO" - ],*/ + ], "write_disassembly":true, "write_hex_near_instructions":false, @@ -22,211 +22,412 @@ // to write out hexdump on the v3 only, to avoid the huge level data files "write_hexdump_on_v3_only":true, - // to write out "scripts", which are currently just all the linked lists found - "write_scripts":true, + // to write out "scripts", which are currently just all the linked lists found. mostly a jak 2/3 thing + "write_scripts":false, + + // optional: a predetermined object file name map from a file. Useful if you want to run only on some DGOs but have consistent names + "obj_file_name_map_file":"goal_src/build/all_objs.txt", // Experimental Stuff "find_basic_blocks":true, - "asm_functions_by_name":[ - // functions which have inline assembly - "unpack-comp-huf", "(method 29 collide-shape-prim-group)","find-knot-span","dma-send-no-scratch", - "raw-ray-sphere-intersect","(method 9 bounding-box)","(method 9 matrix)","shadow-find-single-edges", - "generic-tie-dma-to-spad-sync","ray-cylinder-intersect","shadow-scissor-edges","(method 42 collide-shape)", - "(method 9 texture-page-dir)", - "(method 24 collide-shape-prim)", - "(method 23 collide-shape-prim-group)", - "shadow-find-double-edges", - "(method 16 collide-shape-prim)", - "(method 15 collide-shape-prim-group)", - "generic-tie-upload-next", - "(method 17 collide-edge-work)", - "(method 16 drawable-tree)", - "(method 10 collide-mesh)", - "particle-adgif", - "(method 14 collide-shape-prim-group)", - "(method 12 collide-shape-prim-group)", - "(method 14 bounding-box)", - "process-drawable-birth-fuel-cell", - "(method 13 collide-shape-prim-group)", - "ray-triangle-intersect", - "(method 20 collide-shape-prim-group)", - "(method 10 collide-puss-work)", - "setup-blerc-chains-for-one-fragment", - "curve-evaluate!", - "(method 16 collide-edge-work)", - "(method 9 collide-cache-prim)", - "(method 11 collide-mesh)", - "stats-tfrag-asm", - "(method 10 collide-cache-prim)", - "high-speed-reject", - "(method 12 collide-mesh)", - "(method 19 collide-cache)", - "(method 9 collide-puss-work)", - "(method 29 collide-cache)", - "time-of-day-interp-colors-scratch", - "(method 30 collide-cache)", - "calc-animation-from-spr", - "(method 14 collide-shape-prim-mesh)", - "(method 12 collide-shape-prim-mesh)", - "(method 19 process-drawable)", - "(method 13 collide-shape-prim-mesh)", - "moving-sphere-triangle-intersect", - "(method 14 collide-mesh)", - "circle-circle-xz-intersect", - "get-string-length", - "draw-node-cull", - "collide-probe-node", - "(method 28 collide-cache)", - "(method 26 collide-cache)", - "load-game-text-info", - "(method 27 collide-cache)", - "clip-polygon-against-positive-hyperplane", - "sp-process-block-2d", - "sp-init-fields!", - "clip-polygon-against-negative-hyperplane", - "(method 9 edge-grab-info)", - "(method 18 collide-edge-work)", - "sp-process-block-3d", - "time-of-day-interp-colors", - "(method 23 collide-shape-prim-sphere)", - "(method 15 collide-edge-work)", - "(method 15 collide-mesh)", - "(method 21 collide-cache)", - "mercneric-shader-asm", - "shadow-execute", - "(method 16 level)", - "(method 40 collide-shape)", - "(method 32 collide-cache)", - "bones-mtx-calc", - "draw-inline-array-prototype-tie-near-asm", - "decompress-fixed-data-to-accumulator", - "draw-inline-array-prototype-tie-asm", - "(method 10 external-art-buffer)", - "level-update-after-load", - "draw-inline-array-prototype-tie-generic-asm", - "draw-inline-array-tfrag-near", - "collide-probe-instance-tie", - "draw-inline-array-tfrag", - "mercneric-matrix-asm", - "(method 32 nav-control)", - "(method 11 fact-info-target)", - "mercneric-convert", - "generic-envmap-only-proc", - "draw-inline-array-instance-tie", - "generic-tie-convert-proc", - "draw-string", - "draw-inline-array-instance-shrub", - "generic-tie-convert", - "(anon-function 2503)", - "(anon-function 5635)", - "(anon-function 2504)", - "(anon-function 2502)", - "(anon-function 2501)", - "(anon-function 2505)", - "(anon-function 2500)", - "(anon-function 3717)", - "rand-uint31-gen", - "dma-sync-with-count", - "ripple-create-wave-table", - "generic-debug-light-proc", - "draw-bones-hud", - "(method 27 ropebridge)", - "ocean-interp-wave", - "ocean-generate-verts", - "draw-large-polygon-ocean", - "(method 23 collide-shape-prim-mesh)", - "(method 13 collide-mesh)", - "draw-boundary-polygon", - "draw-large-polygon", - "update-mood-lava", - "update-mood-lightning", - "memcpy", - "background-upload-vu0", - "upload-vis-bits", - "generic-envmap-dproc", - "generic-prepare-dma-double", - "generic-envmap-proc", - "generic-light-proc", - "generic-merc-execute-asm", - "generic-merc-init-asm", - "shadow-calc-dual-verts", - "shadow-scissor-top", - "shadow-find-facing-single-tris", - "shadow-find-facing-double-tris", - "shadow-add-verts", - "shadow-add-facing-single-tris", - "shadow-add-single-edges", - "shadow-add-double-tris", - "shadow-add-double-edges", - "test-func", - "(method 14 collide-cache)", - "symlink2", - "draw-bones-merc", - "dma-count-until-done", - "symlink3", - "blerc-execute", - "merc-dma-chain-to-spr", - "ripple-matrix-scale", - "ripple-apply-wave-table", - "ripple-execute-init", - "bones-mtx-calc-execute", - "texscroll-execute", - "generic-light", - "generic-no-light", - "generic-no-light+envmap", - "generic-dma-from-spr", - "upload-vu0-program", - "generic-merc-execute-all", - "closest-pt-in-triangle", - "(method 11 sparticle-launch-control)", - "(anon-function 3751)", - "look-for-points-of-interest", + "asm_functions_by_name":[ + // gcommon + "quad-copy!", - // gcommon - "(method 2 vec4s)", "quad-copy!", "(method 3 vec4s)", "breakpoint-range-set!", + // gkernel + "(method 11 cpu-thread)", - // pskernel - "resend-exception", "kernel-set-interrupt-vector", "kernel-set-exception-vector", "return-from-exception", - "kernel-read", "kernel-read-function", "kernel-write", "kernel-write-function", "kernel-copy-to-kernel-ram", + // pskernel + "return-from-exception", // F: eret + "kernel-read-function", // F: delay slot tricks + "kernel-write-function", // F: delay slot tricks + "kernel-copy-function", - // this one needs more investigation. nothing looks weird about it but it fails... - "camera-change-to", + // math + "rand-uint31-gen", - // two back to back arithmetic shifts... - "texture-relocate", + // bounding box + "(method 9 bounding-box)", // F: asm branching + "(method 14 bounding-box)", - // this one fails due to false compaction where an else case has only a not expression in it. - "master-is-hopeful-better?", + // matrix + "(method 9 matrix)", // F: asm branching + "matrix-axis-sin-cos!", // F: asm branching + "matrix-axis-sin-cos-vu!", - // fails for unknown reason - "target-falling-anim-trans", "change-brother", + // geometry + "curve-evaluate!", // BUG: cfg fails, suspected weird gotos + "circle-circle-xz-intersect", // F: asm branching + "closest-pt-in-triangle", // F: asm branching + "find-knot-span", // ?? + "vector-segment-distance-point!", - // merged right typecase... can probably handle this - "cspace-inspect-tree", + // trigonometry + "exp", // BUG: cfg is wrong. + "atan0", // P: manual use of stack + "sincos!", // P: manual use of stack + "sincos-rad!", - // these are all valid, but use short circuiting branches in strange ways. There's probably a few compiler uses that we're not - "(method 21 actor-link-info)","(method 20 actor-link-info)","(method 28 collide-shape-prim-mesh)", "(method 35 collide-shape)", - "debug-menu-item-var-render", "(method 14 level)","add-blue-motion","anim-tester-add-newobj","(method 27 orb-cache-top)", + // dma-h + "dma-count-until-done", // F: asm branching + "dma-sync-with-count", // F: asm branching + "dma-send-no-scratch", // F: asm branching + "dma-sync-fast", - // real asm - "cspace<-parented-transformq-joint!", "blerc-a-fragment", "render-boundary-tri", "render-boundary-quad", - "(method 19 collide-shape-prim-sphere)","vector-segment-distance-point!", "exp", "(method 11 collide-mesh-cache)", - "(method 13 collide-edge-work)", "ambient-inspect", + // dma + "symlink3", // F: asm branching + "symlink2", // F: asm branching + "dma-sync-hang", - "(method 11 cpu-thread)", "atan0", "sincos!", "sincos-rad!", "disasm-dma-list", "vblank-handler", "vif1-handler", - "vif1-handler-debug", "entity-actor-count", "decompress-frame-data-pair-to-accumulator", - "decompress-frame-data-to-accumulator", "normalize-frame-quaternions", "clear-frame-accumulator", - "generic-copy-vtx-dclr-dtex", "generic-no-light-dproc-only", "generic-no-light-proc", "mercneric-bittable-asm", - "generic-tie-decompress", "matrix-axis-sin-cos!", "matrix-axis-sin-cos-vu!", "generic-prepare-dma-single", - "(method 13 collide-shape-prim-sphere)", "(method 14 collide-shape-prim-sphere)", "(method 12 collide-shape-prim-sphere)", - "adgif-shader<-texture-with-update!", "generic-interp-dproc", "sprite-draw-distorters", "draw-bones", "(method 9 collide-mesh-cache)", - "(method 18 collide-shape-prim-sphere)","birth-pickup-at-point", + // dma-disasm (BUG) + "disasm-dma-list", - "collide-do-primitives", "draw-bones-check-longest-edge-asm", - "sp-launch-particles-var", "(method 15 collide-shape-prim-mesh)", "(method 15 collide-shape-prim-sphere)", - "(method 45 collide-shape)", "cam-layout-save-cam-trans", "kernel-copy-function", "dma-sync-hang", "generic-no-light-dproc", - "dma-sync-fast", "bsp-camera-asm", - "generic-none-dma-wait", "unpack-comp-rle", "level-remap-texture", "(method 10 collide-edge-hold-list)" - ] + // display + "vblank-handler", // F: weird asm for interrupt handler + "vif1-handler", // F: weird asm for interrupt handler + "vif1-handler-debug", + + // texture + "adgif-shader<-texture-with-update!", // F: asm branching + "(method 9 texture-page-dir)", + + // collide-mesh-h + "(method 11 collide-mesh-cache)", + + // actor-link-h (BUG) + "(method 21 actor-link-info)", // BUG: sc cfg / cfg-ir bug + "(method 20 actor-link-info)", + + // collide-func + "moving-sphere-triangle-intersect", // P: weird branching + "collide-do-primitives", // P: asm branching + "ray-triangle-intersect", // F: asm branching + "ray-cylinder-intersect", // F: asm branching + "raw-ray-sphere-intersect", + + // joint + "calc-animation-from-spr", // F: asm branching + "decompress-frame-data-pair-to-accumulator", // P: asm calling + "decompress-frame-data-to-accumulator", // P: asm calling + "decompress-fixed-data-to-accumulator", // P: asm calling + "normalize-frame-quaternions", // F: asm branching, return + "clear-frame-accumulator", // F: asm branching + "cspace<-parented-transformq-joint!", + + // bsp + "level-remap-texture", // BUG: probably missing branch case? + "bsp-camera-asm", // F: asm branching + "sprite-draw-distorters", + + // merc-blend-shape + "setup-blerc-chains-for-one-fragment", // F: asm branching + "blerc-execute", // F: asm branching + "merc-dma-chain-to-spr", // F: asm branching + "blerc-a-fragment", + + // ripple + "ripple-matrix-scale", + "ripple-apply-wave-table", + "ripple-create-wave-table", + "ripple-execute-init", + + // bones + "draw-bones-hud", + "draw-bones", + "draw-bones-check-longest-edge-asm", + "draw-bones-merc", + "bones-mtx-calc-execute", + "bones-mtx-calc", + "texscroll-execute", + + // generic-effect + "generic-debug-light-proc", + "generic-none-dma-wait", + "generic-copy-vtx-dclr-dtex", + "generic-light", + "generic-envmap-only-proc", + "generic-no-light", + "generic-no-light+envmap", + "generic-no-light-dproc", + "generic-no-light-dproc-only", + "generic-no-light-proc", + "generic-interp-dproc", + "generic-envmap-dproc", + "generic-prepare-dma-single", + "generic-prepare-dma-double", + "generic-envmap-proc", + "generic-light-proc", + "generic-dma-from-spr", + "upload-vu0-program", + + // generic-merc + "generic-merc-execute-all", + "generic-merc-execute-asm", + "high-speed-reject", + "mercneric-convert", + "mercneric-bittable-asm", + "mercneric-shader-asm", + "mercneric-matrix-asm", + "generic-merc-init-asm", + + // generic-tie + "generic-tie-convert", + "generic-tie-convert-proc", + "generic-tie-upload-next", + "generic-tie-decompress", + "generic-tie-dma-to-spad-sync", + + // shadow-cpu + "shadow-execute", + "shadow-add-double-edges", + "shadow-add-double-tris", + "shadow-add-single-edges", + "shadow-add-facing-single-tris", + "shadow-add-verts", + "shadow-find-double-edges", + "shadow-find-facing-double-tris", + "shadow-find-single-edges", + "shadow-find-facing-single-tris", + "shadow-scissor-top", + "shadow-scissor-edges", + "shadow-calc-dual-verts", + + // font + "get-string-length", + "draw-string", + + // decomp + "(method 16 level)", // BUG: cfg fails + "unpack-comp-huf", + "unpack-comp-rle", + + // background + "upload-vis-bits", + "background-upload-vu0", + + // draw-node + "draw-node-cull", + + // shrubbery + "test-func", + "draw-inline-array-instance-shrub", + + // tfrag + "stats-tfrag-asm", + "draw-inline-array-tfrag-near", + "draw-inline-array-tfrag", + + // tie-methods + "draw-inline-array-prototype-tie-near-asm", + "draw-inline-array-prototype-tie-asm", + "draw-inline-array-prototype-tie-generic-asm", + "draw-inline-array-instance-tie", + + // sparticle-launcher + "(method 11 sparticle-launch-control)", // BUG: cfg ir + "sp-launch-particles-var", + "particle-adgif", + "sp-init-fields!", + + // sparticle + "memcpy", + "sp-process-block-3d", + "sp-process-block-2d", + + // loader BUG + "(method 10 external-art-buffer)", + + // game-info BUG + "(method 11 fact-info-target)", + + // game-save BUG + "(anon-function 5 game-save)", // BUG: + "(anon-function 6 game-save)", // BUG: + "(anon-function 7 game-save)", // BUG: + "(anon-function 8 game-save)", // BUG: + "(anon-function 9 game-save)", // BUG: + "(anon-function 10 game-save)", + + // mood BUG + "update-mood-lava", // BUG: + "update-mood-lightning", + + // time-of-day + "time-of-day-interp-colors-scratch", + "time-of-day-interp-colors", + + // sky-tng + "clip-polygon-against-negative-hyperplane", + "clip-polygon-against-positive-hyperplane", + "draw-large-polygon", + + // load-boundary + "render-boundary-tri", + "render-boundary-quad", + "draw-boundary-polygon", + + // level BUG + "level-update-after-load", + + // text BUG + "load-game-text-info", + + // collide-probe + "collide-probe-instance-tie", + "collide-probe-node", + + // collide-mesh + "(method 10 collide-mesh)", + "(method 13 collide-mesh)", + "(method 9 collide-mesh-cache)", + "(method 15 collide-mesh)", + "(method 14 collide-mesh)", + "(method 11 collide-mesh)", + "(method 12 collide-mesh)", + + // collide-edge-grab + "(method 13 collide-edge-work)", + "(method 17 collide-edge-work)", + "(method 15 collide-edge-work)", + "(method 16 collide-edge-work)", + "(method 9 edge-grab-info)", // maybe bug + "(method 18 collide-edge-work)", + "(method 10 collide-edge-hold-list)", + + // collide-shape + "(method 15 collide-shape-prim-mesh)", // BUG: + "(method 15 collide-shape-prim-sphere)", // BUG: + "(method 16 collide-shape-prim)", + "(method 15 collide-shape-prim-group)", + "(method 40 collide-shape)", + "(method 45 collide-shape)", + "(method 28 collide-shape-prim-mesh)", // BUG: + "(method 29 collide-shape-prim-group)", + "(method 20 collide-shape-prim-group)", + "(method 19 collide-shape-prim-sphere)", + "(method 18 collide-shape-prim-sphere)", + "(method 23 collide-shape-prim-sphere)", + "(method 23 collide-shape-prim-mesh)", // BUG: maybe + "(method 24 collide-shape-prim)", + "(method 23 collide-shape-prim-group)", + "(method 42 collide-shape)", + + // collide-shape-rider + "(method 35 collide-shape)", + + // cam-master BUG + "master-is-hopeful-better?", + + // cam-layout BUG + "cam-layout-save-cam-trans", + + // process-drawable BUG + "cspace-inspect-tree", // BUG: + "process-drawable-birth-fuel-cell", // BUG: + "(method 19 process-drawable)", + + // ambient + "ambient-inspect", + + // generic-obs BUG + "camera-change-to", + + // target BUG + "target-falling-anim-trans", + + // target2 BUG + "(anon-function 33 target2)", // BUG: + "(anon-function 67 target2)", // BUG: + "look-for-points-of-interest", + + // menu BUG + "debug-menu-item-var-render", + + // drawable-tree + "(method 16 drawable-tree)", + + // collide-cache + "(method 10 collide-puss-work)", + "(method 9 collide-puss-work)", + "(method 19 collide-cache)", + "(method 10 collide-cache-prim)", + "(method 9 collide-cache-prim)", + "(method 30 collide-cache)", + "(method 13 collide-shape-prim-group)", + "(method 13 collide-shape-prim-sphere)", + "(method 13 collide-shape-prim-mesh)", + "(method 14 collide-shape-prim-group)", + "(method 14 collide-shape-prim-sphere)", + "(method 14 collide-shape-prim-mesh)", + "(method 12 collide-shape-prim-group)", // BUG: maybe + "(method 12 collide-shape-prim-sphere)", + "(method 12 collide-shape-prim-mesh)", + "(method 29 collide-cache)", + "(method 27 collide-cache)", + "(method 14 collide-cache)", + "(method 28 collide-cache)", + "(method 26 collide-cache)", + "(method 21 collide-cache)", + "(method 32 collide-cache)", + + // memory-usage BUG + "(method 14 level)", + + // navigate BUG + "(method 32 nav-control)", + + // collectables BUG + "birth-pickup-at-point", + "add-blue-motion", + + // ocean + "draw-large-polygon-ocean", + + // ocean-vu0 + "ocean-generate-verts", + "ocean-interp-wave", + + // anim-tester BUG + "anim-tester-add-newobj", + + // nav-enemy BUG + "(anon-function 28 nav-enemy)", + + // orb-cache BUG + "(method 27 orb-cache-top)", + + // ropebridge BUG + "(method 27 ropebridge)", + + // all unchecked.and in level DGO code + "(anon-function 11 robotboss)", + "(anon-function 18 robotboss)", + "(anon-function 49 robotboss)", + "(anon-function 21 plant-boss)", + "(anon-function 10 ice-cube)", + "(anon-function 15 ice-cube)", + "(anon-function 45 lavatube-energy)", + "(anon-function 5 game-save)", + "(anon-function 6 game-save)", + "(anon-function 7 game-save)", + "(anon-function 8 game-save)", + "(anon-function 9 game-save)", + "(anon-function 10 game-save)", + "(anon-function 28 nav-enemy)", + "mistycannon-find-best-solution", + "target-flut-falling-anim-trans", + "kermit-check-to-hit-player?", + "(anon-function 6 title-obs)", + "(anon-function 36 mistycannon)", + "(anon-function 5 battlecontroller)", + "(anon-function 43 maincave-obs)", + "(anon-function 2 target-tube)", + "(anon-function 5 orbit-plat)", + "(anon-function 2 ogreboss)" + + + + + ] } \ No newline at end of file diff --git a/decompiler/gui/decompiler_gui.py b/decompiler/gui/decompiler_gui.py index b779d860b1..eb443c5596 100644 --- a/decompiler/gui/decompiler_gui.py +++ b/decompiler/gui/decompiler_gui.py @@ -208,7 +208,7 @@ class ObjectFileView(QDialog): self.asm_pane.setModel(asm_model) self.warnings_label.setText(func["warnings"]) self.asm_display.setPlainText("") - self.function_header_label.setText("{}, type: {}".format(name, func["type"])) + self.function_header_label.setText("{}, type: {}\nfunc: {} obj: {}".format(name, func["type"], func["name"], func["parent_object"])) def basic_op_clicked(self, item): text = "" @@ -227,6 +227,9 @@ class ObjectFileView(QDialog): for i in range(asm_idx, self.basic_id_to_asm[item.row() + 1]): text += self.functions_by_name[self.current_function]["asm"][i]["asm_op"] + "\n" + op = self.functions_by_name[self.current_function]["asm"][asm_idx] + if "referenced_string" in op: + text += op["referenced_string"] self.asm_display.setPlainText(text) self.asm_display.setFont(get_monospaced_font()) self.asm_pane.setCurrentIndex(self.asm_pane.model().index(asm_idx, 0)) diff --git a/decompiler/main.cpp b/decompiler/main.cpp index cbbe154bf4..58dadadc40 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -33,7 +33,7 @@ int main(int argc, char** argv) { dgos.push_back(combine_path(in_folder, dgo_name)); } - ObjectFileDB db(dgos); + ObjectFileDB db(dgos, get_config().obj_file_name_map_file); file_util::write_text_file(combine_path(out_folder, "dgo.txt"), db.generate_dgo_listing()); file_util::write_text_file(combine_path(out_folder, "obj.txt"), db.generate_obj_listing()); diff --git a/goal_src/build/all_objs.txt b/goal_src/build/all_objs.txt index 83e0b6d611..2d30c3fa68 100644 --- a/goal_src/build/all_objs.txt +++ b/goal_src/build/all_objs.txt @@ -1,4 +1,4 @@ -["gcommon", "gcommon", 3, ["KERNEL"], "kernel"], +[["gcommon", "gcommon", 3, ["KERNEL"], "kernel"], ["gstring-h", "gstring-h", 3, ["KERNEL"], "kernel"], ["gkernel-h", "gkernel-h", 3, ["KERNEL"], "kernel"], ["gkernel", "gkernel", 3, ["KERNEL"], "kernel"], @@ -1084,4 +1084,4 @@ ["vil3-bridge-36-ag", "vil3-bridge-36", 4, ["VI3"], "levels/village3"], ["water-anim-village3-ag", "water-anim-village3", 4, ["VI3"], "levels/village3"], ["village3-vis", "village3-vis", 4, ["VI3"], "levels/village3"], -["lava", "lava", 3, ["WATER-AN"], "old/lava"] \ No newline at end of file +["lava", "lava", 3, ["WATER-AN"], "old/lava"]] \ No newline at end of file