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
This commit is contained in:
water111 2020-10-24 14:27:50 -04:00 committed by GitHub
parent 0bc2466f86
commit b561cdfade
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 757 additions and 426 deletions

View file

@ -1,6 +1,7 @@
#include <cassert>
#include <vector>
#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;

View file

@ -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 {

View file

@ -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<TypeMap> bb_entry_types;

View file

@ -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:

View file

@ -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<nlohmann::json::object_t> 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<nlohmann::json::object_t> 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

View file

@ -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;

View file

@ -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<std::string>& _dgos) {
ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _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<std::string>& _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<std::string>();
auto game_name = x[1].get<std::string>();
auto dgo_names = x[3].get<std::vector<std::string>>();
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<std::string, std::unordered_set<std::string>> 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));

View file

@ -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<std::string>& _dgos);
ObjectFileDB(const std::vector<std::string>& _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<std::string, std::vector<ObjectFileRecord>> obj_files_by_dgo;
std::vector<std::string> obj_file_order;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> dgo_obj_name_map;
struct {
uint32_t total_dgo_bytes = 0;

View file

@ -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<int>();
gConfig.dgo_names = cfg.at("dgo_names").get<std::vector<std::string>>();
if (cfg.contains("obj_file_name_map_file")) {
gConfig.obj_file_name_map_file = cfg.at("obj_file_name_map_file").get<std::string>();
}
gConfig.write_disassembly = cfg.at("write_disassembly").get<bool>();
gConfig.write_hexdump = cfg.at("write_hexdump").get<bool>();
gConfig.write_scripts = cfg.at("write_scripts").get<bool>();

View file

@ -10,6 +10,7 @@
struct Config {
int game_version = -1;
std::vector<std::string> dgo_names;
std::string obj_file_name_map_file;
bool write_disassembly = false;
bool write_hexdump = false;
bool write_scripts = false;

View file

@ -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)"
]
}

View file

@ -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))

View file

@ -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());

View file

@ -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"]
["lava", "lava", 3, ["WATER-AN"], "old/lava"]]