improve macro detection

This commit is contained in:
water111
2026-07-28 12:00:35 -07:00
parent 1e4935a164
commit dfb75fc594
226 changed files with 5021 additions and 5049 deletions
+132 -33
View File
@@ -3150,7 +3150,8 @@ bool try_to_rewrite_vector_inline_ctor(const Env& env,
}
bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack& stack) {
if (env.func->name() == "(method 63 collide-shape-moving)") {
if (env.func->name() == "matrix-copy!" ||
env.func->name() == "(method 63 collide-shape-moving)") {
return false;
}
auto matrix_entries =
@@ -3158,13 +3159,21 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
if (matrix_entries) {
// first, check the loads. they should be something like source = (-> MAT vec quad)
// MAT should always be a variable
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
std::vector<RegisterAccess> load_src_ras, store_dest_ras, store_src_ras;
for (int i = 0; i < 4; i++) {
auto deref_matcher =
Matcher::deref(Matcher::any_reg(0), false,
{DerefTokenMatcher::string(names[i]), DerefTokenMatcher::string("quad")});
Matcher deref_matcher;
if (env.version == GameVersion::Jak1) {
deref_matcher = Matcher::deref(
Matcher::any_reg(0), false,
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i),
DerefTokenMatcher::string("quad")});
} else {
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
deref_matcher =
Matcher::deref(Matcher::any_reg(0), false,
{DerefTokenMatcher::string(names[i]),
DerefTokenMatcher::string("quad")});
}
auto mr = match(deref_matcher, matrix_entries->at(i).source, &env);
if (!mr.matched) {
return false;
@@ -3174,10 +3183,20 @@ bool try_to_rewrite_matrix_inline_copy(const Env& env, FormPool& pool, FormStack
// check the stores
for (int i = 4; i < 8; i++) {
Matcher matcher = Matcher::set(Matcher::deref(Matcher::any_reg(0), false,
{DerefTokenMatcher::string(names[i - 4]),
DerefTokenMatcher::string("quad")}),
Matcher::any_reg(1));
Matcher dst_matcher;
if (env.version == GameVersion::Jak1) {
dst_matcher = Matcher::deref(
Matcher::any_reg(0), false,
{DerefTokenMatcher::string("vector"), DerefTokenMatcher::integer(i - 4),
DerefTokenMatcher::string("quad")});
} else {
const char* names[] = {"rvec", "uvec", "fvec", "trans"};
dst_matcher =
Matcher::deref(Matcher::any_reg(0), false,
{DerefTokenMatcher::string(names[i - 4]),
DerefTokenMatcher::string("quad")});
}
Matcher matcher = Matcher::set(dst_matcher, Matcher::any_reg(1));
auto mr = match(matcher, matrix_entries->at(i).elt, &env);
if (!mr.matched) {
return false;
@@ -3356,6 +3375,8 @@ bool try_to_rewrite_matrix_inline_ctor(const Env& env, FormPool& pool, FormStack
}
}
} break;
case GameVersion::JakX:
return false;
}
// success!
@@ -3380,6 +3401,50 @@ bool is_deref_to_quad(DerefElement* deref) {
deref->tokens().back().is_field_name("quad");
}
struct DerefContainerInfo {
TypeSpec type;
bool is_matrix_row = false;
};
std::optional<DerefContainerInfo> deref_container_info(DerefElement* deref, const Env& env) {
if (!is_deref_to_quad(deref)) {
return {};
}
auto base = deref->base()->try_as_element<SimpleExpressionElement>();
if (!base || base->expr().kind() != SimpleExpression::Kind::IDENTITY ||
!base->expr().get_arg(0).is_var()) {
return {};
}
TypeSpec current = env.get_variable_type(base->expr().get_arg(0).var(), true);
bool is_matrix_row = current == TypeSpec("matrix") || current == TypeSpec("matrix3");
for (size_t i = 0; i + 1 < deref->tokens().size(); ++i) {
const auto& token = deref->tokens().at(i);
if (token.kind() == DerefToken::Kind::FIELD_NAME) {
try {
current = env.dts->ts.lookup_field_info(current.base_type(), token.field_name()).type;
} catch (const std::exception&) {
return {};
}
} else if (token.kind() == DerefToken::Kind::INTEGER_CONSTANT ||
token.kind() == DerefToken::Kind::INTEGER_EXPRESSION) {
if (!current.has_single_arg()) {
return {};
}
// get_single_arg() refers into current, so copy it before assignment destroys the argument
// storage.
const auto element_type = current.get_single_arg();
current = element_type;
} else {
return {};
}
is_matrix_row =
is_matrix_row || current == TypeSpec("matrix") || current == TypeSpec("matrix3");
}
return DerefContainerInfo{current, is_matrix_row};
}
Form* pop_last_deref_token(Form* form) {
auto deref = form->try_as_element<DerefElement>();
ASSERT(deref);
@@ -3392,9 +3457,7 @@ Form* pop_last_deref_token(Form* form) {
}
FormElement* try_to_rewrite_vector_copy(Form* dst,
const TypeSpec& dst_type,
Form* src,
const TypeSpec& src_type,
FormPool& pool,
const Env& env) {
if (env.func->name() == "vector-copy!") {
@@ -3402,16 +3465,6 @@ FormElement* try_to_rewrite_vector_copy(Form* dst,
}
if (dst && src) {
// check types
if (dst_type != TypeSpec("vector")) {
return nullptr;
}
// kinda sus - we really want to check the place where this was loaded...
if (src_type != TypeSpec("uint128")) {
return nullptr;
}
auto* dst_deref = dst->try_as_element<DerefElement>();
auto* src_deref = src->try_as_element<DerefElement>();
if (!dst_deref) {
@@ -3422,11 +3475,17 @@ FormElement* try_to_rewrite_vector_copy(Form* dst,
return nullptr;
}
if (!is_deref_to_quad(dst_deref)) {
const auto dst_info = deref_container_info(dst_deref, env);
const auto src_info = deref_container_info(src_deref, env);
if (!dst_info || !src_info ||
!env.dts->ts.tc(TypeSpec("vector"), dst_info->type) ||
!env.dts->ts.tc(TypeSpec("vector"), src_info->type)) {
return nullptr;
}
if (!is_deref_to_quad(src_deref)) {
// A matrix row is represented by an inline vector, but folding its individual quadword copy
// would prevent the four-row matrix-copy! recognizer from seeing the complete operation.
if (dst_info->is_matrix_row || src_info->is_matrix_row) {
return nullptr;
}
@@ -3439,6 +3498,39 @@ FormElement* try_to_rewrite_vector_copy(Form* dst,
return nullptr;
}
FormElement* try_to_rewrite_vector_zero(Form* dst,
Form* value,
FormPool& pool,
const Env& env) {
if (env.func->name() == "vector-zero!") {
return nullptr;
}
auto dst_deref = dst ? dst->try_as_element<DerefElement>() : nullptr;
const auto dst_info = dst_deref ? deref_container_info(dst_deref, env) : std::nullopt;
if (!dst_info || !env.dts->ts.tc(TypeSpec("vector"), dst_info->type) ||
dst_info->is_matrix_row) {
return nullptr;
}
if (!match(Matcher::cast("uint128", Matcher::integer(0)), value, &env).matched) {
return nullptr;
}
return pool.alloc_element<GenericElement>(
GenericOperator::make_function(pool.form<ConstantTokenElement>("vector-zero!")),
std::vector<Form*>{pop_last_deref_token(dst)});
}
bool is_pending_stack_vector_ctor(const FormStack& stack, RegisterAccess destination) {
auto entries = stack.try_getting_active_stack_entries({true});
if (!entries || !entries->front().destination ||
entries->front().destination->reg() != destination.reg()) {
return false;
}
auto stack_value = entries->front().source->try_as_element<StackStructureDefElement>();
return stack_value && stack_value->type() == TypeSpec("vector");
}
} // namespace
void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& stack) {
@@ -3462,11 +3554,9 @@ void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& s
FormElement* fr = nullptr;
// hack: for now only do this on Jak 3
if (size() == 16 && env.version == GameVersion::Jak3) {
fr = try_to_rewrite_vector_copy(
m_dst, env.get_variable_type(m_base_var, true), popped.at(0),
m_src_cast_type.value_or(env.get_variable_type(m_expr.var(), true)), pool, env);
if (size() == 16 &&
(env.version == GameVersion::Jak1 || env.version == GameVersion::Jak3)) {
fr = try_to_rewrite_vector_copy(m_dst, popped.at(0), pool, env);
}
if (!fr) {
@@ -3486,10 +3576,19 @@ void StorePlainDeref::push_to_stack(const Env& env, FormPool& pool, FormStack& s
m_dst->try_as_element<DerefElement>()->inline_nested();
auto val = pool.form<SimpleExpressionElement>(m_expr, m_my_idx);
val->mark_popped();
auto fr = pool.alloc_element<SetFormFormElement>(
m_dst, make_optional_cast(m_src_cast_type, val, pool, env));
fr->mark_popped();
stack.push_form_element(fr, true);
auto typed_value = make_optional_cast(m_src_cast_type, val, pool, env);
FormElement* fr = nullptr;
if (size() == 16 && env.version == GameVersion::Jak1 &&
!is_pending_stack_vector_ctor(stack, m_base_var)) {
fr = try_to_rewrite_vector_zero(m_dst, typed_value, pool, env);
}
if (!fr) {
fr = pool.alloc_element<SetFormFormElement>(m_dst, typed_value);
fr->mark_popped();
stack.push_form_element(fr, true);
} else {
fr->push_to_stack(env, pool, stack);
}
}
if (!try_to_rewrite_matrix_inline_ctor(env, pool, stack)) {
@@ -5,6 +5,7 @@
#include "ObjectFileDB.h"
#include "common/demacro/demacro.h"
#include "common/formatter/formatter.h"
#include "common/goos/PrettyPrinter.h"
#include "common/link_types.h"
@@ -880,6 +881,10 @@ void ObjectFileDB::ir2_write_results(const fs::path& output_dir,
file_util::write_text_file(file_name, file_text);
auto unformatted_code = ir2_final_out(obj, imports, {});
if (!config.demacro_file.empty()) {
unformatted_code =
demacro::rewrite(unformatted_code, file_util::get_file_path({config.demacro_file})).source;
}
auto final_name = output_dir / (obj.to_unique_name() + "_disasm.gc");
if (config.format_code) {
const auto formatted_code = formatter::format_code(unformatted_code);
+3
View File
@@ -46,6 +46,9 @@ Config make_config_via_json(nlohmann::json& json) {
config.expected_elf_name = json.at("expected_elf_name").get<std::string>();
}
config.all_types_file = json.at("all_types_file").get<std::string>();
if (json.contains("demacro_file")) {
config.demacro_file = json.at("demacro_file").get<std::string>();
}
auto inputs_json = read_json_file_from_config(json, "inputs_file");
config.dgo_names = json.contains("dgo_names")
+1
View File
@@ -106,6 +106,7 @@ struct Config {
std::string obj_file_name_map_file;
std::string all_types_file;
std::string demacro_file;
bool disassemble_code = false;
bool dump_function_metadata = false;
+9 -4
View File
@@ -19576,10 +19576,15 @@ the original scratchpad decompressor remains available through the development s
(define-extern mem-size
"Collect value's memory categories using flags, optionally print the complete category table,
and return the sum of aligned total bytes."
(function basic symbol int int))
(function basic symbol mem-usage-flags int))
(define-extern jacc-mem-usage
"Account for a compressed animation control block, its fixed data, and each compressed frame."
(function joint-anim-compressed-control memory-usage-block int joint-anim-compressed-control))
(function
joint-anim-compressed-control
memory-usage-block
mem-usage-flags
joint-anim-compressed-control
))
(define-extern joint-anim-inspect-elt
"Inspect the payload element selected by rounding index to an integer. Matrix and transformq
animations have specialized displays; other animation payload types are left unchanged."
@@ -19722,7 +19727,7 @@ under label, then add those totals into the aggregate collision statistics."
(define-extern mem-usage-bsp-tree
"Recursively count the 32-byte internal nodes reachable from node in the BSP-node memory
category. Nonpositive terminal children are not followed; header and flags are unused."
(function bsp-header bsp-node memory-usage-block int none))
(function bsp-header bsp-node memory-usage-block mem-usage-flags none))
(define-extern bsp-camera-asm
"Traverse the BSP for camera-position. At each split, select front when
dot(camera-position, plane.xyz) - plane.w is nonnegative. Store the terminal leaf index and that
@@ -21384,7 +21389,7 @@ instance distance to the level."
(define-extern mem-usage-shrub-walk
"Recursively account for node storage and leaf instance-shrubbery records across node-count
contiguous shrub BVH roots. flags is forwarded for the memory-usage traversal."
(function draw-node int memory-usage-block int draw-node))
(function draw-node int memory-usage-block mem-usage-flags draw-node))
(define-extern shrub-make-perspective-matrix
"Copy the current camera transform to out, divide its homogeneous coefficients by pfog0, and fold
the horizontal, vertical, depth, and fog offsets into the first three components."
+407
View File
@@ -0,0 +1,407 @@
{
// Patterns are matched against LISP forms after type analysis, expression construction, and
// let insertion have finished. A `$name` atom captures one form; repeated captures must match
// structurally. `$*name` captures zero or more forms within a list, which lets a rule tolerate
// unrelated bindings merged into a compiler-generated let.
//
// `match` and `rewrite` may be either one form string or an array of adjacent form strings.
// Rules are tried in file order. Keep rules conservative: this pass intentionally has no type
// information with which to disambiguate structurally identical operations.
"tables": {
// Mirrors memory-usage-h.gc. `next` is the one-past category index stored in
// memory-usage-block.length.
"mem-usage-id": [
{"value": "0", "next": "1", "symbol": "drawable-group"},
{"value": "1", "next": "2", "symbol": "tfragment"},
{"value": "2", "next": "3", "symbol": "tfragment-base"},
{"value": "3", "next": "4", "symbol": "tfragment-common"},
{"value": "4", "next": "5", "symbol": "tfragment-level0"},
{"value": "5", "next": "6", "symbol": "tfragment-level1"},
{"value": "6", "next": "7", "symbol": "tfragment-color"},
{"value": "7", "next": "8", "symbol": "tfragment-debug"},
{"value": "8", "next": "9", "symbol": "tfragment-pal"},
{"value": "9", "next": "10", "symbol": "tie-fragment"},
{"value": "10", "next": "11", "symbol": "tie-gif"},
{"value": "11", "next": "12", "symbol": "tie-points"},
{"value": "12", "next": "13", "symbol": "tie-colors"},
{"value": "13", "next": "14", "symbol": "tie-draw-points"},
{"value": "14", "next": "15", "symbol": "tie-debug"},
{"value": "15", "next": "16", "symbol": "tie-near"},
{"value": "16", "next": "17", "symbol": "tie-pal"},
{"value": "17", "next": "18", "symbol": "tie-generic"},
{"value": "18", "next": "19", "symbol": "instance-tie"},
{"value": "19", "next": "20", "symbol": "instance-tie-colors0"},
{"value": "20", "next": "21", "symbol": "instance-tie-colors1"},
{"value": "21", "next": "22", "symbol": "instance-tie-colors2"},
{"value": "22", "next": "23", "symbol": "instance-tie-colors3"},
{"value": "23", "next": "24", "symbol": "instance-tie-colors*"},
{"value": "24", "next": "25", "symbol": "prototype-bucket-shrub"},
{"value": "25", "next": "26", "symbol": "generic-shrub"},
{"value": "26", "next": "27", "symbol": "generic-shrub-data"},
{"value": "27", "next": "28", "symbol": "shrubbery"},
{"value": "28", "next": "29", "symbol": "shrubbery-object"},
{"value": "29", "next": "30", "symbol": "shrubbery-vertex"},
{"value": "30", "next": "31", "symbol": "shrubbery-color"},
{"value": "31", "next": "32", "symbol": "shrubbery-stq"},
{"value": "32", "next": "33", "symbol": "shrubbery-pal"},
{"value": "33", "next": "34", "symbol": "billboard"},
{"value": "34", "next": "35", "symbol": "instance-shrubbery"},
{"value": "35", "next": "36", "symbol": "pris-fragment"},
{"value": "43", "next": "44", "symbol": "entity"},
{"value": "44", "next": "45", "symbol": "camera"},
{"value": "45", "next": "46", "symbol": "nav-mesh"},
{"value": "48", "next": "49", "symbol": "res"},
{"value": "49", "next": "50", "symbol": "ambient"},
{"value": "50", "next": "51", "symbol": "collide-fragment-0"},
{"value": "51", "next": "52", "symbol": "collision-poly-0"},
{"value": "52", "next": "53", "symbol": "collision-vertex-0"},
{"value": "53", "next": "54", "symbol": "collide-fragment-1"},
{"value": "54", "next": "55", "symbol": "collision-poly-1"},
{"value": "55", "next": "56", "symbol": "collision-vertex-1"},
{"value": "56", "next": "57", "symbol": "bsp-main"},
{"value": "57", "next": "58", "symbol": "bsp-misc"},
{"value": "58", "next": "59", "symbol": "bsp-node"},
{"value": "59", "next": "60", "symbol": "bsp-leaf-vis-self"},
{"value": "60", "next": "61", "symbol": "bsp-leaf-vis-adj"},
{"value": "61", "next": "62", "symbol": "draw-node"},
{"value": "62", "next": "63", "symbol": "pat"},
{"value": "63", "next": "64", "symbol": "level-code"},
{"value": "64", "next": "65", "symbol": "entity-links"},
{"value": "65", "next": "66", "symbol": "joint"},
{"value": "66", "next": "67", "symbol": "joint-anim-compressed"},
{"value": "67", "next": "68", "symbol": "joint-anim-compressed-control"},
{"value": "68", "next": "69", "symbol": "joint-anim-fixed"},
{"value": "69", "next": "70", "symbol": "joint-anim-frame"},
{"value": "70", "next": "71", "symbol": "art-group"},
{"value": "71", "next": "72", "symbol": "art-mesh-anim"},
{"value": "72", "next": "73", "symbol": "art-mesh-geo"},
{"value": "73", "next": "74", "symbol": "art-joint-geo"},
{"value": "74", "next": "75", "symbol": "art-joint-anim"},
{"value": "75", "next": "76", "symbol": "merc-ctrl"},
{"value": "76", "next": "77", "symbol": "joint-anim-drawable"},
{"value": "77", "next": "78", "symbol": "blend-shape"},
{"value": "78", "next": "79", "symbol": "collide-mesh"},
{"value": "79", "next": "80", "symbol": "texture"},
{"value": "80", "next": "81", "symbol": "string"},
{"value": "81", "next": "82", "symbol": "array"},
{"value": "82", "next": "83", "symbol": "sprite"},
{"value": "83", "next": "84", "symbol": "depth-cue"},
{"value": "84", "next": "85", "symbol": "debug"},
{"value": "85", "next": "86", "symbol": "sky"},
{"value": "86", "next": "87", "symbol": "pris-generic"},
{"value": "87", "next": "88", "symbol": "4k-dead-pool"},
{"value": "88", "next": "89", "symbol": "8k-dead-pool"},
{"value": "89", "next": "90", "symbol": "16k-dead-pool"},
{"value": "90", "next": "91", "symbol": "nk-dead-pool"},
{"value": "91", "next": "92", "symbol": "target-dead-pool"},
{"value": "92", "next": "93", "symbol": "camera-dead-pool"},
{"value": "93", "next": "94", "symbol": "debug-dead-pool"},
{"value": "94", "next": "95", "symbol": "process-active"},
{"value": "95", "next": "96", "symbol": "heap-total"},
{"value": "96", "next": "97", "symbol": "heap-process"},
{"value": "97", "next": "98", "symbol": "heap-header"},
{"value": "98", "next": "99", "symbol": "heap-thread"},
{"value": "99", "next": "100", "symbol": "heap-root"},
{"value": "100", "next": "101", "symbol": "heap-draw-control"},
{"value": "101", "next": "102", "symbol": "heap-joint-control"},
{"value": "102", "next": "103", "symbol": "heap-cspace"},
{"value": "103", "next": "104", "symbol": "heap-bone"},
{"value": "104", "next": "105", "symbol": "heap-part"},
{"value": "105", "next": "106", "symbol": "heap-collide-prim"},
{"value": "106", "next": "107", "symbol": "heap-misc"},
{"value": "107", "next": "108", "symbol": "shadow-geo"},
{"value": "108", "next": "109", "symbol": "eye-anim"}
],
"scratchpad-object-type": [
{"type": "terrain-context"},
{"type": "cam-dbg-scratch"},
{"type": "terrain-bsp"},
{"type": "collide-puss-work"},
{"type": "vector"},
{"type": "vector4w"},
{"type": "bone-memory"},
{"type": "joint"},
{"type": "bone"},
{"type": "vu-lights"},
{"type": "bone-regs"},
{"type": "matrix"},
{"type": "generic-tie-shadow"},
{"type": "generic-tie-calls"},
{"type": "collide-probe-stack"},
{"type": "ambient-list"},
{"type": "adgif-shader"},
{"type": "(inline-array ocean-vertex)"}
],
"scratchpad-pointer-type": [
{"type": "rgba"},
{"type": "uint128"},
{"type": "process-drawable"}
]
},
"rules": [
{
"name": "current-frame",
"match": "(-> *display* frames (-> *display* on-screen) frame)",
"rewrite": "(current-frame)"
},
{
// Let insertion can merge consecutive traversals which reuse the same node and cached-next
// locals. Peel off the first traversal and rebuild the remaining let; repeated rule passes
// then recover every traversal in the chain.
"name": "iterate-engine-connections-merged-let",
"match":
"(let (($node (-> $engine alive-list next0))) $engine (let (($next (-> $node next0))) (while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) $engine (set! $next (-> $next next0))) (set! $node (-> $next-engine alive-list next0)) $next-engine (set! $next (-> $node next0)) $*remaining))",
"rewrite": [
"(iterate-engine-connections ($node $engine) $*body)",
"(let (($node (-> $next-engine alive-list next0))) $next-engine (let (($next (-> $node next0))) $*remaining))"
]
},
{
// Function-scoped locals produce assignments instead of let bindings around the traversal.
"name": "iterate-engine-connections-set-locals",
"match": [
"(set! $node (-> $engine alive-list next0))",
"$engine",
"(set! $next (-> $node next0))",
"(while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) $engine (set! $next (-> $next next0)))"
],
"rewrite": "(iterate-engine-connections ($node $engine) $*body)"
},
{
// Constant engine globals sometimes survive as no-op forms around the cached-next traversal.
"name": "iterate-engine-connections-with-engine-forms",
"match":
"(let (($node (-> $engine alive-list next0))) $engine (let (($next (-> $node next0))) (while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) $engine (set! $next (-> $next next0)))))",
"rewrite": "(iterate-engine-connections ($node $engine) $*body)"
},
{
"name": "iterate-engine-connections-nested-let",
"match":
"(let (($node (-> $engine alive-list next0))) (let (($next (-> $node next0))) (while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) (set! $next (-> $next next0)))))",
"rewrite": "(iterate-engine-connections ($node $engine) $*body)"
},
{
"name": "iterate-engine-connections-let-star",
"match":
"(let* (($node (-> $engine alive-list next0)) ($next (-> $node next0))) (while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) (set! $next (-> $next next0))))",
"rewrite": "(iterate-engine-connections ($node $engine) $*body)"
},
{
// Preserve unrelated bindings which let insertion combined with the traversal's node local.
// Exact single-binding lets are handled above so this fallback never emits an empty let.
"name": "iterate-engine-connections-merged-bindings",
"match":
"(let ($*before ($node (-> $engine alive-list next0)) $*after) $engine (let (($next (-> $node next0))) (while (!= $node (-> $engine alive-list-end)) $*body (set! $node $next) $engine (set! $next (-> $next next0)))))",
"rewrite": "(let ($*before $*after) (iterate-engine-connections ($node $engine) $*body))"
},
{
"name": "with-dma-buffer-add-bucket",
"match":
"(let* (($buf $buffer) ($start (-> $buf base))) $*body (let (($edge (-> $buf base))) (let (($packet (the-as dma-packet (-> $buf base)))) (set! (-> $packet dma) (new 'static 'dma-tag :id (dma-tag-id next))) (set! (-> $packet vif0) (new 'static 'vif-tag)) (set! (-> $packet vif1) (new 'static 'vif-tag)) (set! (-> $buf base) (&+ (the-as pointer $packet) 16))) (dma-bucket-insert-tag $bucket-group $bucket-id $start (the-as (pointer dma-tag) $edge))))",
"rewrite":
"(with-dma-buffer-add-bucket (($buf $buffer) $bucket-id) :bucket-group $bucket-group $*body)"
},
{
"name": "with-dma-buffer-add-bucket-pointer-add-cast",
"match":
"(let* (($buf $buffer) ($start (-> $buf base))) $*body (let (($edge (-> $buf base))) (let (($packet (the-as dma-packet (-> $buf base)))) (set! (-> $packet dma) (new 'static 'dma-tag :id (dma-tag-id next))) (set! (-> $packet vif0) (new 'static 'vif-tag)) (set! (-> $packet vif1) (new 'static 'vif-tag)) (set! (-> $buf base) (the-as pointer (&+ $packet 16)))) (dma-bucket-insert-tag $bucket-group $bucket-id $start (the-as (pointer dma-tag) $edge))))",
"rewrite":
"(with-dma-buffer-add-bucket (($buf $buffer) $bucket-id) :bucket-group $bucket-group $*body)"
},
{
"name": "with-dma-buffer-add-bucket-object-packet",
"match":
"(let* (($buf $buffer) ($start (-> $buf base))) $*body (let (($edge (-> $buf base))) (let (($packet (the-as object (-> $buf base)))) (set! (-> (the-as dma-packet $packet) dma) (new 'static 'dma-tag :id (dma-tag-id next))) (set! (-> (the-as dma-packet $packet) vif0) (new 'static 'vif-tag)) (set! (-> (the-as dma-packet $packet) vif1) (new 'static 'vif-tag)) (set! (-> $buf base) (&+ (the-as pointer $packet) 16))) (dma-bucket-insert-tag $bucket-group $bucket-id $start (the-as (pointer dma-tag) $edge))))",
"rewrite":
"(with-dma-buffer-add-bucket (($buf $buffer) $bucket-id) :bucket-group $bucket-group $*body)"
},
{
// The font setters are methods rather than macros, but Jak 1 inlines them. The enum
// constructors make these two field stores structurally unambiguous without type data.
"name": "font-set-color-conditional-aliased",
"match":
"(let (($alias $font)) (set! (-> $alias color) (if $condition (font-color $*true-colors) (font-color $*false-colors))))",
"rewrite":
"(set-color! $font (if $condition (font-color $*true-colors) (font-color $*false-colors)))"
},
{
"name": "font-set-color-aliased",
"match":
"(let (($alias $font)) (set! (-> $alias color) (font-color $*colors)))",
"rewrite": "(set-color! $font (font-color $*colors))"
},
{
"name": "font-set-color",
"match": "(set! (-> $font color) (font-color $*colors))",
"rewrite": "(set-color! $font (font-color $*colors))"
},
{
"name": "font-set-flags-aliased",
"match":
"(let (($alias $font)) (set! (-> $alias flags) (font-flags $*flags)))",
"rewrite": "(set-flags! $font (font-flags $*flags))"
},
{
"name": "font-set-flags",
"match": "(set! (-> $font flags) (font-flags $*flags))",
"rewrite": "(set-flags! $font (font-flags $*flags))"
},
{
"name": "mem-usage-add-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data {{value}} name) \"{{symbol}}\")",
"(+! (-> $usage data {{value}} count) $count)",
"(let (($temp $bytes)) (+! (-> $usage data {{value}} used) $temp) (+! (-> $usage data {{value}} total) (logand -16 (+ $temp 15))))"
],
"rewrite": "(mem-usage-add! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-enum-index-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data (mem-usage-id {{symbol}}) name) \"{{symbol}}\")",
"(+! (-> $usage data (mem-usage-id {{symbol}}) count) $count)",
"(let (($temp $bytes)) (+! (-> $usage data (mem-usage-id {{symbol}}) used) $temp) (+! (-> $usage data (mem-usage-id {{symbol}}) total) (logand -16 (+ $temp 15))))"
],
"rewrite": "(mem-usage-add! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-runtime-name-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data {{value}} name) (symbol->string '{{symbol}}))",
"(+! (-> $usage data {{value}} count) $count)",
"(let (($temp $bytes)) (+! (-> $usage data {{value}} used) $temp) (+! (-> $usage data {{value}} total) (logand -16 (+ $temp 15))))"
],
"rewrite": "(mem-usage-add-symbol! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-enum-index-runtime-name-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data (mem-usage-id {{symbol}}) name) (symbol->string '{{symbol}}))",
"(+! (-> $usage data (mem-usage-id {{symbol}}) count) $count)",
"(let (($temp $bytes)) (+! (-> $usage data (mem-usage-id {{symbol}}) used) $temp) (+! (-> $usage data (mem-usage-id {{symbol}}) total) (logand -16 (+ $temp 15))))"
],
"rewrite": "(mem-usage-add-symbol! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-flattened-let-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data {{value}} name) \"{{symbol}}\")",
"(+! (-> $usage data {{value}} count) $count)",
"(+! (-> $usage data {{value}} used) $bytes)",
"(+! (-> $usage data {{value}} total) (logand -16 (+ $bytes 15)))"
],
"rewrite": "(mem-usage-add! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-enum-index-flattened-let-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data (mem-usage-id {{symbol}}) name) \"{{symbol}}\")",
"(+! (-> $usage data (mem-usage-id {{symbol}}) count) $count)",
"(+! (-> $usage data (mem-usage-id {{symbol}}) used) $bytes)",
"(+! (-> $usage data (mem-usage-id {{symbol}}) total) (logand -16 (+ $bytes 15)))"
],
"rewrite": "(mem-usage-add! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-runtime-name-flattened-let-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data {{value}} name) (symbol->string '{{symbol}}))",
"(+! (-> $usage data {{value}} count) $count)",
"(+! (-> $usage data {{value}} used) $bytes)",
"(+! (-> $usage data {{value}} total) (logand -16 (+ $bytes 15)))"
],
"rewrite": "(mem-usage-add-symbol! $usage {{symbol}} $count $bytes)"
},
{
"name": "mem-usage-add-enum-index-runtime-name-flattened-let-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data (mem-usage-id {{symbol}}) name) (symbol->string '{{symbol}}))",
"(+! (-> $usage data (mem-usage-id {{symbol}}) count) $count)",
"(+! (-> $usage data (mem-usage-id {{symbol}}) used) $bytes)",
"(+! (-> $usage data (mem-usage-id {{symbol}}) total) (logand -16 (+ $bytes 15)))"
],
"rewrite": "(mem-usage-add-symbol! $usage {{symbol}} $count $bytes)"
},
{
// A zero increment is sometimes decompiled as a self-assignment instead of `(+! ... 0)`.
"name": "mem-usage-add-zero-count-{{symbol}}",
"for_each": "mem-usage-id",
"match": [
"(set! (-> $usage length) (max {{next}} (-> $usage length)))",
"(set! (-> $usage data {{value}} name) \"{{symbol}}\")",
"(set! (-> $usage data {{value}} count) (-> $usage data {{value}} count))",
"(let (($temp $bytes)) (+! (-> $usage data {{value}} used) $temp) (+! (-> $usage data {{value}} total) (logand -16 (+ $temp 15))))"
],
"rewrite": "(mem-usage-add! $usage {{symbol}} 0 $bytes)"
},
{
// These adjacent calls share the larger one-past length after decompilation.
"name": "mem-usage-add-generic-shrub-pair",
"match": [
"(set! (-> $usage length) (max 27 (-> $usage length)))",
"(set! (-> $usage data 25 name) \"generic-shrub\")",
"(+! (-> $usage data 25 count) $header-count)",
"(let (($header-temp $header-bytes)) (+! (-> $usage data 25 used) $header-temp) (+! (-> $usage data 25 total) (logand -16 (+ $header-temp 15))))",
"(set! (-> $usage data 26 name) \"generic-shrub-data\")",
"(+! (-> $usage data 26 count) $stream-count)",
"(let (($stream-temp $stream-bytes)) (+! (-> $usage data 26 used) $stream-temp) (+! (-> $usage data 26 total) (logand -16 (+ $stream-temp 15))))"
],
"rewrite": [
"(mem-usage-add! $usage generic-shrub $header-count $header-bytes)",
"(mem-usage-add! $usage generic-shrub-data $stream-count $stream-bytes)"
]
},
{
"name": "scratchpad-object-direct-{{type}}",
"for_each": "scratchpad-object-type",
"match": "(the-as {{type}} #x70000000)",
"rewrite": "(scratchpad-object {{type}})"
},
{
"name": "scratchpad-object-offset-{{type}}",
"for_each": "scratchpad-object-type",
"match": "(the-as {{type}} (+ $offset #x70000000))",
"rewrite": "(scratchpad-object {{type}} :offset $offset)"
},
{
"name": "scratchpad-object-reversed-offset-{{type}}",
"for_each": "scratchpad-object-type",
"match": "(the-as {{type}} (+ #x70000000 $offset))",
"rewrite": "(scratchpad-object {{type}} :offset $offset)"
},
{
"name": "scratchpad-pointer-direct-{{type}}",
"for_each": "scratchpad-pointer-type",
"match": "(the-as (pointer {{type}}) #x70000000)",
"rewrite": "(scratchpad-ptr {{type}})"
},
{
"name": "scratchpad-pointer-offset-{{type}}",
"for_each": "scratchpad-pointer-type",
"match": "(the-as (pointer {{type}}) (+ $offset #x70000000))",
"rewrite": "(scratchpad-ptr {{type}} :offset $offset)"
},
{
"name": "scratchpad-pointer-reversed-offset-{{type}}",
"for_each": "scratchpad-pointer-type",
"match": "(the-as (pointer {{type}}) (+ #x70000000 $offset))",
"rewrite": "(scratchpad-ptr {{type}} :offset $offset)"
}
]
}
+1
View File
@@ -101,6 +101,7 @@
"art_info_file": "decompiler/config/jak1/ntsc_v1/art_info.jsonc",
"import_deps_file": "decompiler/config/jak1/ntsc_v1/import_deps.jsonc",
"all_types_file": "decompiler/config/jak1/all-types.gc",
"demacro_file": "decompiler/config/jak1/demacro.jsonc",
"art_group_dump_file": "decompiler/config/jak1/ntsc_v1/art-group-info.min.json",
"joint_node_dump_file": "decompiler/config/jak1/ntsc_v1/joint-node-info.min.json",
"tex_dump_file": "decompiler/config/jak1/ntsc_v1/tex-info.min.json",