Skip to content

Commit

Permalink
decomp3: more engine stuff, fix ja macro detection for jak 2/3, unm…
Browse files Browse the repository at this point in the history
…erged `let` matcher, `part-tracker-spawn` macro (#3436)

- `aligner`
- `effect-control`
- `pov-camera`
- `powerups`
- `los-control-h`
- `airlock`
- `water-anim`
- `blocking-plane`
- `proc-focusable-spawner`
- `idle-control`
- `enemy-h`
- `nav-enemy-h`
- `enemy`
- `enemy-states`
- `particle-curves`
- `base-plat`
- `plat`
- `bouncer`
- `elevator`
- `rigid-body`
- `rigid-body-queue`
- `process-taskable`
- `scene-actor`
- `warp-gate`
- `guard-projectile`
- `metalhead-projectile`
- `los-control`
- `joint-exploder`
- `ragdoll-test`
- `debris`
- `shield-sphere`
- `text`
- `target-launch`
  • Loading branch information
Hat-Kid authored Mar 30, 2024
1 parent ee015e3 commit dacb704
Show file tree
Hide file tree
Showing 217 changed files with 51,951 additions and 5,946 deletions.
1 change: 1 addition & 0 deletions decompiler/IR2/Form.h
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,7 @@ class StringConstantElement : public FormElement {
FormStack& stack,
std::vector<FormElement*>* result,
bool allow_side_effects) override;
const std::string& value() const { return m_value; }

private:
std::string m_value;
Expand Down
67 changes: 67 additions & 0 deletions decompiler/IR2/GenericElementMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,15 @@ Matcher Matcher::let(bool is_star,
return m;
}

Matcher Matcher::unmerged_let(const std::vector<LetEntryMatcher>& entries,
const std::vector<Matcher>& elts) {
Matcher m;
m.m_kind = Kind::UNMERGED_LET;
m.m_entry_matchers = entries;
m.m_sub_matchers = elts;
return m;
}

bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* const env) const {
switch (m_kind) {
case Kind::ANY:
Expand Down Expand Up @@ -722,6 +731,64 @@ bool Matcher::do_match(Form* input, MatchResult::Maps* maps_out, const Env* cons
return false;
}

case Kind::UNMERGED_LET: {
auto as_let = dynamic_cast<LetElement*>(input->try_as_single_active_element());
if (as_let) {
size_t entries_matched = 0;
Form* innermost_let_body = nullptr;
// first try to find the innermost let, matching all let entries with the entry matchers
// throughout
as_let->apply_form([&](Form* form) {
for (int idx = 0; idx < form->size(); idx++) {
auto* f = form->at(idx);
// if this is the entry of the outermost let, try to do the first match
if (f->parent_form->parent_element == as_let) {
if (m_entry_matchers.at(entries_matched)
.do_match(as_let->entries().at(0), maps_out, env)) {
entries_matched++;
} else {
return;
}
}
auto let = dynamic_cast<LetElement*>(f);
if (!let) {
continue;
}

auto let_body = dynamic_cast<LetElement*>(let->body()->at(0));
if (!let_body) {
break;
}

if (m_entry_matchers.at(entries_matched)
.do_match(let_body->entries().at(0), maps_out, env)) {
entries_matched++;
} else {
return;
}

if (entries_matched == m_entry_matchers.size()) {
innermost_let_body = let_body->body();
return;
}
}
});

if (entries_matched == m_entry_matchers.size() && innermost_let_body) {
// now match body of innermost let
for (int i = 0; i < (int)m_sub_matchers.size(); ++i) {
Form fake;
fake.elts().push_back(innermost_let_body->elts().at(i));
if (!m_sub_matchers.at(i).do_match(&fake, maps_out, env)) {
return false;
}
}
return true;
}
}
return false;
}

default:
ASSERT(false);
return false;
Expand Down
3 changes: 3 additions & 0 deletions decompiler/IR2/GenericElementMatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class Matcher {
static Matcher let(bool is_star,
const std::vector<LetEntryMatcher>& entries,
const std::vector<Matcher>& elts);
static Matcher unmerged_let(const std::vector<LetEntryMatcher>& entries,
const std::vector<Matcher>& elts);

enum class Kind {
ANY_REG, // matching any register
Expand Down Expand Up @@ -106,6 +108,7 @@ class Matcher {
QUOTED_SYMBOL,
SAME_VAR,
LET,
UNMERGED_LET,
VAR_NAME,
INVALID
};
Expand Down
139 changes: 138 additions & 1 deletion decompiler/analysis/find_defstates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,110 @@ FormElement* rewrite_virtual_defstate(
state_override);
}

FormElement* rewrite_virtual_defstate_with_nonvirtual_inherit(
LetElement* elt,
const Env& env,
const std::string& expected_state_name,
FormPool& pool,
const std::unordered_map<std::string, std::unordered_set<std::string>>& skip_states = {}) {
// (let ((gp-6 (new 'static 'state
// :name 'spinning
// :next #f
// :exit #f
// :parent #f
// :code #f
// :trans #f
// :post #f
// :enter #f
// :event #f
// )
// )
// )
// (inherit-state gp-6 gun-yellow-3-saucer-base-state)
// (set! (-> gp-6 parent) gun-yellow-3-saucer-base-state)
// (method-set! gun-yellow-3-saucer 46 gp-6)
// (set! (-> gp-6 enter) L255)
// (set! (-> gp-6 exit) (the-as (function object) L251))
// (set! (-> gp-6 trans) (the-as (function object) L253))
// )

// ASSERT(elt->body()->size() > 1);
env.func->warnings.warning("Encountered virtual defstate {} with non-virtual inherit.",
expected_state_name);
// variable at the top of let, contains the static state with name exptected_state_name
auto state_var_from_let_def = elt->entries().at(0).dest;
// our index into the let body
int body_idx = 0;

// the setup
auto first_in_body = elt->body()->at(body_idx);
auto inherit = dynamic_cast<GenericElement*>(first_in_body);
std::string parent_state;
if (inherit) {
parent_state = inherit->elts().at(1)->to_string(env);
}
body_idx += 2;

// checks to check: method type is a state
// if inherit matches expected.

// next, find (method-set! sunken-elevator 22 (the-as function gp-0))
auto method_set_form = elt->body()->at(body_idx);
Form temp = Form();
temp.elts().push_back(method_set_form);
auto mset_matcher =
Matcher::op(GenericOpMatcher::func(Matcher::symbol("method-set!")),
{Matcher::any_symbol(0), Matcher::any_integer(1), Matcher::any(2)});
auto mset_mr = match(mset_matcher, &temp);
if (!mset_mr.matched) {
env.func->warnings.error_and_throw(
"Failed to recognize virtual defstate. Got a {} as the third thing, but was "
"expecting method-set! call",
temp.to_string(env));
}

// the actual type that gets this as a state
auto type_name = mset_mr.maps.strings.at(0);
auto method_id = mset_mr.maps.ints.at(1);

// should be the state again.
auto val = strip_cast(mset_mr.maps.forms.at(2)->try_as_single_element());
if (val->to_string(env) != env.get_variable_name(state_var_from_let_def)) {
env.func->warnings.error_and_throw(
"Variable name disagreement in virtual defstate: began with {}, but did method "
"set using {}",
val->to_string(env), env.get_variable_name(state_var_from_let_def));
}

// we should double check that the type in the defstate is correct
auto method_info = env.dts->ts.lookup_method(type_name, method_id);
if (method_info.type.base_type() != "state" ||
method_info.type.last_arg().base_type() != "_type_") {
env.func->warnings.error_and_throw(
"Virtual defstate is defining a virtual state \"{}\" in method {} of {}, but the type "
"of this method is {}, which is not a valid virtual state type (must be "
"\"(state ... _type_)\")",
expected_state_name, method_info.name, type_name, method_info.type.print());
}

bool state_override = false;

// name matches
if (expected_state_name != method_info.name) {
env.func->warnings.error_and_throw(
"Disagreement between state name and type system name. The state is named {}, "
"but the slot is named {}, defined in type {}",
expected_state_name, method_info.name, method_info.defined_in_type);
}

auto entries = get_defstate_entries(
elt->body(), body_idx + 1, env, expected_state_name, elt->entries().at(0).dest,
method_info.type.substitute_for_method_call(type_name), pool, type_name, skip_states);

return pool.alloc_element<DefstateElement>(type_name, expected_state_name, parent_state, entries,
true, state_override);
}

FormElement* rewrite_nonvirtual_defstate_with_inherit(
LetElement* elt,
const Env& env,
Expand Down Expand Up @@ -482,7 +586,7 @@ FormElement* rewrite_nonvirtual_defstate_with_inherit(
// (set! (-> gp-1 trans) (the-as (function object) L107))
// (set! (-> gp-1 code) L95)
// )
env.func->warnings.warning("Encountered non-virtual defstate {} with inherit.",
env.func->warnings.warning("Encountered non-virtual defstate {} with non-virtual inherit.",
expected_state_name);
ASSERT(elt->body()->size() > 0);
int body_index = 0;
Expand Down Expand Up @@ -532,6 +636,33 @@ bool is_nonvirtual_state_with_inherit(LetElement* elt) {
return false;
}

bool is_virtual_state_with_nonvirtual_inherit(LetElement* elt) {
if (elt->body()->size() >= 3) {
auto inherit = dynamic_cast<GenericElement*>(elt->body()->at(0));
auto parent = dynamic_cast<SetFormFormElement*>(elt->body()->at(1));
auto method_set = dynamic_cast<GenericElement*>(elt->body()->at(2));
if (!inherit || !parent || !method_set) {
return false;
}
std::vector<FormElement*> forms = {inherit, parent, method_set};
std::vector matchers = {
Matcher::op(GenericOpMatcher::func(Matcher::symbol("inherit-state")),
{Matcher::any_reg(0), Matcher::any_symbol(1)}),
Matcher::set(Matcher::deref(Matcher::any(), false, {DerefTokenMatcher::string("parent")}),
Matcher::any_symbol()),
Matcher::op(GenericOpMatcher::func(Matcher::symbol("method-set!")),
{Matcher::any_symbol(0), Matcher::any_integer(1), Matcher::any(2)})};
for (size_t i = 0; i < matchers.size(); i++) {
auto mr = match(matchers.at(i), forms.at(i));
if (!mr.matched) {
return false;
}
}
return true;
}
return false;
}

} // namespace

void run_defstate(
Expand Down Expand Up @@ -570,6 +701,12 @@ void run_defstate(
if (rewritten) {
fe = rewritten;
}
} else if (is_virtual_state_with_nonvirtual_inherit(as_let)) {
auto rewritten = rewrite_virtual_defstate_with_nonvirtual_inherit(
as_let, env, expected_state_name, pool, skip_states);
if (rewritten) {
fe = rewritten;
}
} else if (is_nonvirtual_state_with_inherit(as_let)) {
auto rewritten = rewrite_nonvirtual_defstate_with_inherit(
as_let, env, expected_state_name, pool, skip_states);
Expand Down
Loading

0 comments on commit dacb704

Please sign in to comment.