diff --git a/decompiler/IR/CfgBuilder.cpp b/decompiler/IR/CfgBuilder.cpp index 5239620f68..da389513cd 100644 --- a/decompiler/IR/CfgBuilder.cpp +++ b/decompiler/IR/CfgBuilder.cpp @@ -64,6 +64,58 @@ void insert_cfg_into_list(Function& f, } } +std::pair>*> get_condition_branch_as_vector(IR* in) { + auto as_seq = dynamic_cast(in); + if (as_seq) { + auto irb = dynamic_cast(as_seq->forms.back().get()); + auto loc = &as_seq->forms; + assert(irb); + return std::make_pair(irb, loc); + } + return std::make_pair(nullptr, nullptr); +} + +std::pair*> get_condition_branch(std::shared_ptr* in) { + IR_Branch* condition_branch = dynamic_cast(in->get()); + std::shared_ptr* condition_branch_location = in; + if (!condition_branch) { + // not 100% sure this will always work + auto as_seq = dynamic_cast(in->get()); + if (as_seq) { + condition_branch = dynamic_cast(as_seq->forms.back().get()); + condition_branch_location = &as_seq->forms.back(); + } + } + return std::make_pair(condition_branch, condition_branch_location); +} + +void clean_up_cond_with_else(IR_CondWithElse* cwe, LinkedObjectFile& file) { + for (auto& e : cwe->entries) { + auto jump_to_next = get_condition_branch(&e.condition); + assert(jump_to_next.first); + assert(jump_to_next.first->branch_delay.kind == BranchDelay::NOP); + printf("got cond condition %s\n", jump_to_next.first->print(file).c_str()); + auto replacement = std::make_shared(jump_to_next.first->condition); + *(jump_to_next.second) = replacement; + + auto jump_to_end = get_condition_branch(&e.body); + assert(jump_to_end.first); + assert(jump_to_end.first->branch_delay.kind == BranchDelay::NOP); + assert(jump_to_end.first->condition.kind == Condition::ALWAYS); + auto as_end_of_sequence = get_condition_branch_as_vector(e.body.get()); + if (as_end_of_sequence.first) { + assert(as_end_of_sequence.second->size() > 1); + as_end_of_sequence.second->pop_back(); + } else { + // this means the case is empty, which is a little bit weird but does actually appear to + // happen in a few places. so we just replace the jump with a nop. In the future we could + // consider having a more explicit "this case is empty" operator so this doesn't get confused + // with an actual MIPS nop. + *(jump_to_end.second) = std::make_shared(); + } + } +} + std::shared_ptr cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx) { if (dynamic_cast(vtx)) { auto* bv = dynamic_cast(vtx); @@ -97,6 +149,19 @@ std::shared_ptr cfg_to_ir(Function& f, LinkedObjectFile& file, CfgVtx* vtx) auto result = std::make_shared(cfg_to_ir(f, file, wvtx->condition), cfg_to_ir(f, file, wvtx->body)); return result; + } else if (dynamic_cast(vtx)) { + auto* cvtx = dynamic_cast(vtx); + std::vector entries; + for (auto& x : cvtx->entries) { + IR_CondWithElse::Entry e; + e.condition = cfg_to_ir(f, file, x.condition); + e.body = cfg_to_ir(f, file, x.body); + entries.push_back(std::move(e)); + } + auto else_ir = cfg_to_ir(f, file, cvtx->else_vtx); + auto result = std::make_shared(entries, else_ir); + clean_up_cond_with_else(result.get(), file); + return result; } else { @@ -121,22 +186,14 @@ void clean_up_while_loops(IR_Begin* sequence, LinkedObjectFile& file) { to_remove.push_back(i - 1); // now we should try to find the condition branch: - IR_Branch* condition_branch = dynamic_cast(form_as_while->condition.get()); - std::shared_ptr* condition_branch_location = &form_as_while->condition; - if (!condition_branch) { - // not 100% sure this will always work - auto as_seq = dynamic_cast(form_as_while->condition.get()); - if (as_seq) { - condition_branch = dynamic_cast(as_seq->forms.back().get()); - condition_branch_location = &as_seq->forms.back(); - } - } - assert(condition_branch); - assert(condition_branch->branch_delay.kind == BranchDelay::NOP); - printf("got while condition branch %s\n", condition_branch->print(file).c_str()); - auto replacement = std::make_shared(condition_branch->condition); - *condition_branch_location = replacement; + auto condition_branch = get_condition_branch(&form_as_while->condition); + + assert(condition_branch.first); + assert(condition_branch.first->branch_delay.kind == BranchDelay::NOP); + printf("got while condition branch %s\n", condition_branch.first->print(file).c_str()); + auto replacement = std::make_shared(condition_branch.first->condition); + *(condition_branch.second) = replacement; } } diff --git a/decompiler/IR/IR.cpp b/decompiler/IR/IR.cpp index 195bd72c00..b3a78dfb8b 100644 --- a/decompiler/IR/IR.cpp +++ b/decompiler/IR/IR.cpp @@ -493,6 +493,7 @@ std::shared_ptr
IR_Begin::to_form(const LinkedObjectFile& file) const { return buildList(list); } + void IR_Begin::get_children(std::vector>* output) const { for (auto& x : forms) { output->push_back(x); @@ -525,4 +526,29 @@ std::shared_ptr IR_WhileLoop::to_form(const LinkedObjectFile& file) const void IR_WhileLoop::get_children(std::vector>* output) const { output->push_back(condition); output->push_back(body); +} + +std::shared_ptr IR_CondWithElse::to_form(const LinkedObjectFile& file) const { + // todo - special case to print as if with else + std::vector> list; + list.push_back(toForm("cond")); + for(auto& e : entries) { + std::vector> entry; + entry.push_back(e.condition->to_form(file)); + print_inlining_begin(&entry, e.body.get(), file); + list.push_back(buildList(entry)); + } + std::vector> else_form; + else_form.push_back(toForm("else")); + print_inlining_begin(&else_form, else_ir.get(), file); + list.push_back(buildList(else_form)); + return buildList(list); +} + +void IR_CondWithElse::get_children(std::vector>* output) const { + for(auto& e : entries) { + output->push_back(e.condition); + output->push_back(e.body); + } + output->push_back(else_ir); } \ No newline at end of file diff --git a/decompiler/IR/IR.h b/decompiler/IR/IR.h index 1285a34857..0f7a4ea797 100644 --- a/decompiler/IR/IR.h +++ b/decompiler/IR/IR.h @@ -279,4 +279,18 @@ class IR_WhileLoop : public IR { std::shared_ptr condition, body; }; +class IR_CondWithElse : public IR { + public: + struct Entry { + std::shared_ptr condition = nullptr; + std::shared_ptr body = nullptr; + }; + std::vector entries; + std::shared_ptr else_ir; + IR_CondWithElse(std::vector _entries, std::shared_ptr _else_ir) + : entries(std::move(_entries)), else_ir(std::move(_else_ir)) {} + std::shared_ptr to_form(const LinkedObjectFile& file) const override; + void get_children(std::vector>* output) const override; +}; + #endif // JAK_IR_H diff --git a/decompiler/config/jak1_ntsc_black_label.jsonc b/decompiler/config/jak1_ntsc_black_label.jsonc index e5f8d1e038..0305ed5311 100644 --- a/decompiler/config/jak1_ntsc_black_label.jsonc +++ b/decompiler/config/jak1_ntsc_black_label.jsonc @@ -40,7 +40,9 @@ "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", + "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!", "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)",