d/jak2: finish cty-guard-turret-button | race-h | height-map-h and a lot of rigid-body (#1957)

Also cleaned up `data_decompiler.cpp` to make it a lot less verbose to
add a special case for an array field.
This commit is contained in:
Tyler Wilding
2022-10-11 23:20:36 -04:00
committed by GitHub
parent f7e1e73809
commit e3473c1902
197 changed files with 24065 additions and 614 deletions
+86 -198
View File
@@ -456,6 +456,17 @@ goos::Object decomp_ref_to_inline_array_guess_size(
// we expect that to be a label:
ASSERT((field_location % 4) == 0);
auto pointer_to_data = words.at(field_location / 4);
// inline-arrays can also be initialized as #f
if (pointer_to_data.kind() == LinkedWord::SYM_PTR) {
ASSERT_MSG(
pointer_to_data.symbol_name() == "#f",
fmt::format(
"attempted to decompile an inline-array of '{}', but encounted a non `#f` symbol",
array_elt_type.base_type()));
return pretty_print::to_symbol("#f");
}
ASSERT(pointer_to_data.kind() == LinkedWord::PTR);
// the data shouldn't have any labels in the middle of it, so we can find the end of the array
@@ -522,146 +533,6 @@ goos::Object decomp_ref_to_inline_array_guess_size(
return pretty_print::build_list(array_def);
}
/*!
* Decompile the data field of ocean-near-indices, which is an (inline-array ocean-near-index).
* This is like a C++ ocean_near_index*, meaning we don't know how long the array is.
* We know all the data in a ocean_near_index is just integers, so we can guess that the end
* of the array is just the location of the next label.
* There's a chance that this will include some padding in the array and make it too long,
* but there is no harm in that.
*/
goos::Object ocean_near_indices_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("ocean-near-index"), 32, version);
}
goos::Object ocean_mid_masks_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("ocean-mid-mask"), 8, version);
}
goos::Object sp_field_init_spec_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("sp-field-init-spec"), 16, version);
}
goos::Object nav_mesh_vertex_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-vertex"), 16, version);
}
goos::Object nav_mesh_poly_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-poly"), 8, version);
}
goos::Object nav_mesh_poly_arr_jak2_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-poly"), 64, version);
}
goos::Object nav_mesh_nav_control_arr_decompile(
const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("nav-control"), 288, version);
}
goos::Object xz_height_map_data_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("vector4b"), 4, version);
}
goos::Object nav_mesh_route_arr_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("vector4ub"), 4, version);
}
goos::Object sp_launch_grp_launcher_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("sparticle-group-item"), 32, version);
}
goos::Object probe_dir_decompile(const std::vector<LinkedWord>& words,
const std::vector<DecompilerLabel>& labels,
int my_seg,
int field_location,
const TypeSystem& ts,
const std::vector<std::vector<LinkedWord>>& all_words,
const LinkedObjectFile* file,
GameVersion version) {
return decomp_ref_to_inline_array_guess_size(words, labels, my_seg, field_location, ts, all_words,
file, TypeSpec("vector"), 16, version);
}
goos::Object decompile_sound_spec(const TypeSpec& type,
const DecompilerLabel& label,
const std::vector<DecompilerLabel>& labels,
@@ -811,6 +682,58 @@ goos::Object decompile_sound_spec(const TypeSpec& type,
} // namespace
// TODO - add a common game version
const std::unordered_map<
GameVersion,
std::unordered_map<std::string, std::unordered_map<std::string, ArrayFieldDecompMeta>>>
array_field_decomp_special_cases = {
{GameVersion::Jak1,
/*!
* Decompile the data field of ocean-near-indices, which is an (inline-array
* ocean-near-index). This is like a C++ ocean_near_index*, meaning we don't know how long
* the array is. We know all the data in a ocean_near_index is just integers, so we can
* guess that the end of the array is just the location of the next label. There's a chance
* that this will include some padding in the array and make it too long, but there is no
* harm in that.
*/
{{"ocean-near-indices",
{{"data", ArrayFieldDecompMeta(TypeSpec("ocean-near-index"), 32)}}},
{"ocean-mid-masks", {{"data", ArrayFieldDecompMeta(TypeSpec("ocean-mid-mask"), 8)}}},
{"sparticle-launcher",
{{"init-specs", ArrayFieldDecompMeta(TypeSpec("sp-field-init-spec"), 16)}}},
{"sparticle-launch-group",
{{"launcher", ArrayFieldDecompMeta(TypeSpec("sparticle-group-item"), 32)}}},
{"nav-mesh",
{{"vertex", ArrayFieldDecompMeta(TypeSpec("nav-vertex"), 16)},
{"poly", ArrayFieldDecompMeta(TypeSpec("nav-poly"), 8)},
{"route", ArrayFieldDecompMeta(TypeSpec("vector4ub"), 4)}}},
{"lightning-probe-vars", {{"probe-dirs", ArrayFieldDecompMeta(TypeSpec("vector"), 16)}}},
{"ropebridge-tuning",
{{"col-mesh-indexes",
ArrayFieldDecompMeta(TypeSpec("uint8"),
1,
ArrayFieldDecompMeta::Kind::REF_TO_INTEGER_ARR)}}}}},
{GameVersion::Jak2,
{{"ocean-near-indices",
{{"data", ArrayFieldDecompMeta(TypeSpec("ocean-near-index"), 32)}}},
{"ocean-mid-masks", {{"data", ArrayFieldDecompMeta(TypeSpec("ocean-mid-mask"), 8)}}},
{"sparticle-launcher",
{{"init-specs", ArrayFieldDecompMeta(TypeSpec("sp-field-init-spec"), 16)}}},
{"sparticle-launch-group",
{{"launcher", ArrayFieldDecompMeta(TypeSpec("sparticle-group-item"), 32)}}},
{"race-info",
{{"turbo-pad-array", ArrayFieldDecompMeta(TypeSpec("race-turbo-pad"), 32)},
{"racer-array", ArrayFieldDecompMeta(TypeSpec("race-racer-info"), 16)},
{"decision-point-array", ArrayFieldDecompMeta(TypeSpec("race-decision-point"), 16)}}},
{"xz-height-map",
{{"data", ArrayFieldDecompMeta(TypeSpec("int8"),
1,
ArrayFieldDecompMeta::Kind::REF_TO_INTEGER_ARR)}}},
{"lightning-probe-vars", {{"probe-dirs", ArrayFieldDecompMeta(TypeSpec("vector"), 16)}}},
{"nav-mesh",
{{"poly-array", ArrayFieldDecompMeta(TypeSpec("nav-poly"), 64)},
{"nav-control-array", ArrayFieldDecompMeta(TypeSpec("nav-control"), 288)}}}}}};
goos::Object decompile_structure(const TypeSpec& type,
const DecompilerLabel& label,
const std::vector<DecompilerLabel>& labels,
@@ -1029,64 +952,29 @@ goos::Object decompile_structure(const TypeSpec& type,
fmt::format("Dynamic value field {} in static data type {} not yet implemented",
field.name(), actual_type.print()));
} else {
// TODO - this is getting a little unwieldly -- refactor this at some point
if (field.name() == "data" && type.print() == "ocean-near-indices") {
// first, get the label:
field_defs_out.emplace_back(
field.name(), ocean_near_indices_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "data" && type.print() == "ocean-mid-masks") {
field_defs_out.emplace_back(
field.name(), ocean_mid_masks_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "init-specs" && type.print() == "sparticle-launcher") {
field_defs_out.emplace_back(
field.name(), sp_field_init_spec_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "vertex" && type.print() == "nav-mesh" &&
file->version == GameVersion::Jak1) {
field_defs_out.emplace_back(
field.name(), nav_mesh_vertex_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "poly" && type.print() == "nav-mesh" &&
file->version == GameVersion::Jak1) {
field_defs_out.emplace_back(
field.name(), nav_mesh_poly_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "poly-array" && type.print() == "nav-mesh" &&
file->version == GameVersion::Jak2) {
field_defs_out.emplace_back(field.name(), nav_mesh_poly_arr_jak2_decompile(
obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "nav-control-array" && type.print() == "nav-mesh" &&
file->version == GameVersion::Jak2) {
field_defs_out.emplace_back(field.name(), nav_mesh_nav_control_arr_decompile(
obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "data" && type.print() == "xz-height-map" &&
file->version == GameVersion::Jak2) {
field_defs_out.emplace_back(field.name(), xz_height_map_data_arr_decompile(
obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "route" && type.print() == "nav-mesh" &&
file->version == GameVersion::Jak1) {
field_defs_out.emplace_back(
field.name(), nav_mesh_route_arr_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "launcher" && type.print() == "sparticle-launch-group") {
field_defs_out.emplace_back(field.name(), sp_launch_grp_launcher_decompile(
obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else if (field.name() == "col-mesh-indexes" && type.print() == "ropebridge-tuning") {
field_defs_out.emplace_back(
field.name(), decomp_ref_to_integer_array_guess_size(
obj_words, labels, label.target_segment, field_start, ts, words,
file, TypeSpec("uint8"), 1));
} else if (field.name() == "probe-dirs" && type.print() == "lightning-probe-vars") {
field_defs_out.emplace_back(field.name(),
probe_dir_decompile(obj_words, labels, label.target_segment,
field_start, ts, words, file, version));
} else {
// array field special cases, uses the map initialized above!
// check if there is a special case for this type+field+version combination
if (file && array_field_decomp_special_cases.count(file->version) > 0 &&
array_field_decomp_special_cases.at(file->version).count(type.print()) > 0 &&
array_field_decomp_special_cases.at(file->version)
.at(type.print())
.count(field.name()) > 0) {
// We have a special case, do the things
const auto& metadata =
array_field_decomp_special_cases.at(file->version).at(type.print()).at(field.name());
if (metadata.kind == ArrayFieldDecompMeta::Kind::REF_TO_INLINE_ARR) {
field_defs_out.emplace_back(
field.name(),
decomp_ref_to_inline_array_guess_size(
obj_words, labels, label.target_segment, field_start, ts, words, file,
metadata.element_type, metadata.bytes_per_element, file->version));
} else if (metadata.kind == ArrayFieldDecompMeta::Kind::REF_TO_INTEGER_ARR) {
field_defs_out.emplace_back(
field.name(), decomp_ref_to_integer_array_guess_size(
obj_words, labels, label.target_segment, field_start, ts, words,
file, metadata.element_type, metadata.bytes_per_element));
}
} else { // otherwise, it's a pointer array or plain data
if (field.type().base_type() == "pointer") {
if (obj_words.at(field_start / 4).kind() != LinkedWord::SYM_PTR) {
continue;