mirror of
https://github.com/open-goal/jak-project
synced 2026-06-22 09:05:44 -04:00
decompiler: detect light-trail-tracker-spawn macro (#4317)
This commit is contained in:
@@ -1810,6 +1810,308 @@ FormElement* rewrite_part_tracker_new_jak2(const std::string& type,
|
||||
->try_as_single_element();
|
||||
}
|
||||
|
||||
FormElement* rewrite_light_trail_tracker_spawn(LetElement* in, const Env& env, FormPool& pool) {
|
||||
// (let ((s5-0 (new 'stack-no-clear 'light-trail-tracker-spawn-params)))
|
||||
// (set! (-> s5-0 tracked-obj) (process->handle this))
|
||||
// (set! (-> s5-0 appearance) *blue-shot-trail*)
|
||||
// (set! (-> s5-0 max-num-crumbs) (the int (* 0.2 (the float (-> s5-0 appearance max-age)))))
|
||||
// (set! (-> s5-0 track-immediately?) #t)
|
||||
// (let* ((v1-30 (estimate-light-trail-mem-usage
|
||||
// (the-as uint (-> s5-0 max-num-crumbs))
|
||||
// (the-as uint (= (-> s5-0 appearance lie-mode) (lie-mode use-two-strips)))))
|
||||
// (s4-0 (get-process *default-dead-pool* light-trail-tracker-blue-3 (+ v1-30 8192) 1)))
|
||||
// (when s4-0
|
||||
// (let ((t9-4 (method-of-type process activate)))
|
||||
// (t9-4 s4-0 this "light-trail" (the-as pointer #x70004000)))
|
||||
// (run-now-in-process s4-0 light-trail-tracker-init-by-other s5-0)
|
||||
// (-> s4-0 ppointer))))
|
||||
// ->
|
||||
// (light-trail-tracker-spawn light-trail-tracker-blue-3 :to this
|
||||
// :params ((tracked-obj (process->handle this))
|
||||
// (appearance *blue-shot-trail*)
|
||||
// (max-num-crumbs (the int (* 0.2 (the float (-> *blue-shot-trail* max-age)))))
|
||||
// (track-immediately? #t)))
|
||||
if (env.version < GameVersion::Jak3) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (in->entries().size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto params_var = in->entries().at(0).dest;
|
||||
const auto& params_reg = params_var.reg();
|
||||
auto params_src = in->entries().at(0).src->try_as_element<StackStructureDefElement>();
|
||||
if (!params_src) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& params_type = params_src->entry().ref_type;
|
||||
if (!env.dts->ts.tc(TypeSpec("light-trail-tracker-spawn-params"), params_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (params_src->entry().hint.container_type != StackStructureHint::ContainerType::NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto body = in->body();
|
||||
if (body->size() < 2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// grab all the field setters
|
||||
std::vector<std::pair<std::string, Form*>> fields;
|
||||
std::unordered_map<std::string, Form*> field_values;
|
||||
for (size_t idx = 0; idx + 1 < body->size(); ++idx) {
|
||||
auto set_mr = match(Matcher::set(Matcher::deref(Matcher::reg(params_reg), false,
|
||||
{DerefTokenMatcher::any_string(0)}),
|
||||
Matcher::any(1)),
|
||||
body->at(idx));
|
||||
if (!set_mr.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& field_name = set_mr.maps.strings.at(0);
|
||||
auto field_val = set_mr.maps.forms.at(1);
|
||||
fields.emplace_back(field_name, field_val);
|
||||
field_values[field_name] = field_val;
|
||||
}
|
||||
|
||||
// (let ((v1-30 (estimate...))) (let ((s4-0 (get-process...))) (when s4-0 ...)))
|
||||
auto mem_let = dynamic_cast<LetElement*>(body->at(body->size() - 1));
|
||||
if (!mem_let || mem_let->entries().size() != 1 || mem_let->body()->size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// (v1-30 (estimate-light-trail-mem-usage ...))
|
||||
auto mem_var = mem_let->entries().at(0).dest;
|
||||
auto est = mem_let->entries().at(0).src->try_as_element<GenericElement>();
|
||||
if (!est || est->op().kind() != GenericOperator::Kind::FUNCTION_EXPR || !est->op().func() ||
|
||||
!est->op().func()->to_form(env).is_symbol("estimate-light-trail-mem-usage")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto proc_let = dynamic_cast<LetElement*>(mem_let->body()->at(0));
|
||||
if (!proc_let || proc_let->entries().size() != 1 || proc_let->body()->size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// (s4-0 (get-process *default-dead-pool* light-trail-tracker-blue-3 (+ v1-30 8192) 1))
|
||||
auto proc_var = proc_let->entries().at(0).dest;
|
||||
const auto& proc_reg = proc_var.reg();
|
||||
auto gp_mr = match(Matcher::func("get-process", {Matcher::any(0), Matcher::any_symbol(1),
|
||||
Matcher::any(2), Matcher::any_integer(3)}),
|
||||
proc_let->entries().at(0).src);
|
||||
if (!gp_mr.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& proc_type = gp_mr.maps.strings.at(1);
|
||||
if (!env.dts->ts.tc(TypeSpec("light-trail-tracker"), TypeSpec(proc_type))) {
|
||||
return nullptr;
|
||||
}
|
||||
auto from_form = gp_mr.maps.forms.at(0);
|
||||
auto unk = gp_mr.maps.ints.at(3);
|
||||
// stack size is always (+ <estimated-mem> 8192)
|
||||
if (!match(Matcher::op(GenericOpMatcher::or_match(
|
||||
{GenericOpMatcher::fixed(FixedOperatorKind::ADDITION),
|
||||
GenericOpMatcher::fixed(FixedOperatorKind::ADDITION_PTR)}),
|
||||
{Matcher::reg(mem_var.reg()), Matcher::integer(8192)}),
|
||||
gp_mr.maps.forms.at(2))
|
||||
.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// check the when
|
||||
auto cast_type = TypeSpec("pointer", {TypeSpec(proc_type)});
|
||||
auto when_matcher = Matcher::if_no_else(
|
||||
Matcher::op(GenericOpMatcher::condition(IR2_Condition::Kind::TRUTHY),
|
||||
{Matcher::reg(proc_reg)}),
|
||||
Matcher::begin({Matcher::any(0),
|
||||
Matcher::func_with_rest(Matcher::constant_token("run-now-in-process"),
|
||||
{Matcher::reg(proc_reg), Matcher::any(1)}),
|
||||
Matcher::any(2)}));
|
||||
|
||||
auto mr_with_shell = rewrite_shelled_return_form(
|
||||
when_matcher, proc_let->body()->at(0), env, pool,
|
||||
[&](FormElement* s_in, const MatchResult& /*mr*/, const Env& env_, FormPool& pool_) -> Form* {
|
||||
auto as_when = dynamic_cast<CondNoElseElement*>(s_in);
|
||||
if (!as_when) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& when_body = as_when->entries.front().body->elts();
|
||||
if (when_body.size() != 3) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// (let ((t9-4 (method-of-type process activate)))
|
||||
// (t9-4 s4-0 this "light-trail" (the-as pointer #x70004000)))
|
||||
auto activate_let = dynamic_cast<LetElement*>(when_body.at(0));
|
||||
if (!activate_let || activate_let->entries().size() != 1 ||
|
||||
activate_let->body()->size() != 1) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!match(Matcher::op_fixed(
|
||||
FixedOperatorKind::METHOD_OF_TYPE,
|
||||
{Matcher::symbol("process"), Matcher::constant_token("activate")}),
|
||||
activate_let->entries().at(0).src)
|
||||
.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
auto act_fn_reg = activate_let->entries().at(0).dest;
|
||||
auto act_call_mr = match(
|
||||
Matcher::func(Matcher::reg(act_fn_reg.reg()), {Matcher::reg(proc_reg), Matcher::any(0),
|
||||
Matcher::any(1), Matcher::any(2)}),
|
||||
activate_let->body()->at(0));
|
||||
if (!act_call_mr.matched) {
|
||||
return nullptr;
|
||||
}
|
||||
auto to_form = act_call_mr.maps.forms.at(0);
|
||||
auto name_form = act_call_mr.maps.forms.at(1);
|
||||
auto stack_form = act_call_mr.maps.forms.at(2);
|
||||
|
||||
// (run-now-in-process s4-0 light-trail-tracker-init-by-other s5-0)
|
||||
auto run_gen = dynamic_cast<GenericElement*>(when_body.at(1));
|
||||
if (!run_gen || !run_gen->op().func() ||
|
||||
!run_gen->op().func()->to_form(env_).is_symbol("run-now-in-process")) {
|
||||
return nullptr;
|
||||
}
|
||||
if (run_gen->elts().size() != 3) {
|
||||
return nullptr;
|
||||
}
|
||||
auto init_form = run_gen->elts().at(1);
|
||||
if (!match(Matcher::reg(params_reg), run_gen->elts().at(2)).matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// (-> s4-0 ppointer)
|
||||
auto pptr_matcher =
|
||||
Matcher::deref(Matcher::reg(proc_reg), false, {DerefTokenMatcher::string("ppointer")});
|
||||
auto pptr_let = dynamic_cast<LetElement*>(when_body.at(2));
|
||||
if (pptr_let) {
|
||||
auto new_pptr = rewrite_empty_let(pptr_let, env, pool);
|
||||
if (!new_pptr || !match(pptr_matcher, new_pptr).matched) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (!match(pptr_matcher, when_body.at(2)).matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto is_simple_base = [](Form* f) {
|
||||
auto single = f->try_as_single_element();
|
||||
if (!single) {
|
||||
return false;
|
||||
}
|
||||
return dynamic_cast<DerefElement*>(single) || dynamic_cast<SimpleAtomElement*>(single) ||
|
||||
dynamic_cast<SimpleExpressionElement*>(single) ||
|
||||
dynamic_cast<ConstantTokenElement*>(single);
|
||||
};
|
||||
|
||||
// dry run to check we can substitute all the derefs
|
||||
for (auto& field : fields) {
|
||||
Form* fval = field.second;
|
||||
RegAccessSet uses;
|
||||
fval->collect_vars(uses, true);
|
||||
int var_uses = 0;
|
||||
for (const auto& u : uses) {
|
||||
if (u.reg() == params_reg) {
|
||||
var_uses++;
|
||||
}
|
||||
}
|
||||
if (var_uses == 0) {
|
||||
continue;
|
||||
}
|
||||
int subst_derefs = 0;
|
||||
bool bad = false;
|
||||
fval->apply_form([&](Form* f) {
|
||||
for (auto* e : f->elts()) {
|
||||
auto deref = dynamic_cast<DerefElement*>(e);
|
||||
if (!deref || !match(Matcher::reg(params_reg), deref->base()).matched) {
|
||||
continue;
|
||||
}
|
||||
subst_derefs++;
|
||||
if (deref->tokens().empty() ||
|
||||
deref->tokens().at(0).kind() != DerefToken::Kind::FIELD_NAME) {
|
||||
bad = true;
|
||||
continue;
|
||||
}
|
||||
auto it = field_values.find(deref->tokens().at(0).field_name());
|
||||
if (it == field_values.end() || !is_simple_base(it->second)) {
|
||||
bad = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (bad || subst_derefs != var_uses) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// do the inlining
|
||||
for (auto& field : fields) {
|
||||
Form* fval = field.second;
|
||||
fval->apply_form([&](Form* f) {
|
||||
for (auto*& e : f->elts()) {
|
||||
auto deref = dynamic_cast<DerefElement*>(e);
|
||||
if (!deref || !match(Matcher::reg(params_reg), deref->base()).matched) {
|
||||
continue;
|
||||
}
|
||||
auto repl_base = field_values.at(deref->tokens().at(0).field_name());
|
||||
std::vector rest(deref->tokens().begin() + 1, deref->tokens().end());
|
||||
if (rest.empty()) {
|
||||
e = repl_base->try_as_single_element();
|
||||
} else {
|
||||
e = pool_.alloc_element<DerefElement>(repl_base, deref->is_addr_of(), rest);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<Form*> args;
|
||||
args.push_back(pool_.form<ConstantTokenElement>(proc_type));
|
||||
if (!init_form->to_form(env_).is_symbol("light-trail-tracker-init-by-other")) {
|
||||
ja_push_form_to_args(pool_, args, init_form, "init");
|
||||
}
|
||||
if (params_type != TypeSpec("light-trail-tracker-spawn-params")) {
|
||||
args.push_back(pool_.form<ConstantTokenElement>(":params-type"));
|
||||
args.push_back(pool_.form<ConstantTokenElement>(params_type.base_type()));
|
||||
}
|
||||
if (!from_form->to_form(env_).is_symbol("*default-dead-pool*")) {
|
||||
ja_push_form_to_args(pool_, args, from_form, "from");
|
||||
}
|
||||
ja_push_form_to_args(pool_, args, to_form, "to");
|
||||
auto name_str = dynamic_cast<StringConstantElement*>(name_form->try_as_single_element());
|
||||
if (!name_str || name_str->value() != "light-trail") {
|
||||
ja_push_form_to_args(pool_, args, name_form, "name");
|
||||
}
|
||||
if (stack_form->to_string(env_) != "(the-as pointer #x70004000)") {
|
||||
ja_push_form_to_args(pool_, args, stack_form, "stack");
|
||||
}
|
||||
if (unk != 1) {
|
||||
args.push_back(pool_.form<ConstantTokenElement>(":unk"));
|
||||
args.push_back(pool_.form<ConstantTokenElement>(fmt::format("{}", unk)));
|
||||
}
|
||||
|
||||
std::vector<Form*> param_pairs;
|
||||
for (auto& [fname, fval] : fields) {
|
||||
param_pairs.push_back(pool_.form<GenericElement>(
|
||||
GenericOperator::make_function(pool_.form<ConstantTokenElement>(fname)), fval));
|
||||
}
|
||||
args.push_back(pool_.form<ConstantTokenElement>(":param-list"));
|
||||
auto params_head = GenericOperator::make_function(param_pairs.at(0));
|
||||
std::vector params_rest(param_pairs.begin() + 1, param_pairs.end());
|
||||
args.push_back(pool_.form<GenericElement>(params_head, params_rest));
|
||||
|
||||
return pool_.form<GenericElement>(
|
||||
GenericOperator::make_function(
|
||||
pool_.form<ConstantTokenElement>("light-trail-tracker-spawn")),
|
||||
args);
|
||||
},
|
||||
&cast_type);
|
||||
|
||||
if (!std::get<0>(mr_with_shell).matched) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::get<1>(mr_with_shell)->try_as_single_element();
|
||||
}
|
||||
|
||||
FormElement* rewrite_call_parent_state_handler(LetElement* in, const Env& env, FormPool& pool) {
|
||||
// (let ((t9-3 (-> (find-parent-state) code)))
|
||||
// (if t9-3
|
||||
@@ -2501,6 +2803,12 @@ FormElement* rewrite_let(LetElement* in, const Env& env, FormPool& pool, LetRewr
|
||||
return as_abs;
|
||||
}
|
||||
|
||||
auto as_light_trail_tracker_spawn = rewrite_light_trail_tracker_spawn(in, env, pool);
|
||||
if (as_light_trail_tracker_spawn) {
|
||||
stats.light_trail_tracker_spawn++;
|
||||
return as_light_trail_tracker_spawn;
|
||||
}
|
||||
|
||||
auto as_proc_new = rewrite_proc_new(in, env, pool);
|
||||
if (as_proc_new) {
|
||||
stats.proc_new++;
|
||||
|
||||
Reference in New Issue
Block a user