Implement LegoRaceCar::HandleSkeletonKicks and dependents (#1065)

* Implement `LegoRaceCar::HandleSkeletonKicks` and dependents

* Fix typo

* Spike to fix array comparisons (needs refactor)

* Refactor: Dedicated method for array element matching

* Address review comments

* Reformat with new version of black

* Apply more review comments

* Address more review comments

---------

Co-authored-by: jonschz <jonschz@users.noreply.github.com>
This commit is contained in:
jonschz
2024-07-17 16:03:02 +02:00
committed by GitHub
parent 0760e4e7d7
commit 210376f272
8 changed files with 179 additions and 17 deletions

View File

@@ -8,6 +8,7 @@ from typing import Any, Callable, Iterable, List, Optional
from isledecomp.bin import Bin as IsleBin, InvalidVirtualAddressError
from isledecomp.cvdump.demangler import demangle_string_const
from isledecomp.cvdump import Cvdump, CvdumpAnalysis
from isledecomp.cvdump.types import scalar_type_pointer
from isledecomp.parser import DecompCodebase
from isledecomp.dir import walk_source_dir
from isledecomp.types import SymbolType
@@ -220,7 +221,8 @@ class Compare:
var.offset, var.name, var.parent_function
)
else:
self._db.match_variable(var.offset, var.name)
if self._db.match_variable(var.offset, var.name):
self._check_if_array_and_match_elements(var.offset, var.name)
for tbl in codebase.iter_vtables():
self._db.match_vtable(tbl.offset, tbl.name, tbl.base_class)
@@ -245,6 +247,69 @@ class Compare:
self._db.match_string(string.offset, string.name)
def _check_if_array_and_match_elements(self, orig_addr: int, name: str):
"""
Checks if the global variable at `orig_addr` is an array.
If yes, adds a match for all its elements. If it is an array of structs, all fields in that struct are also matched.
Note that there is no recursion, so an array of arrays would not be handled entirely.
This step is necessary e.g. for `0x100f0a20` (LegoRacers.cpp).
"""
def _add_match_in_array(
name: str, type_id: str, orig_addr: int, recomp_addr: int
):
self._db.set_recomp_symbol(
recomp_addr,
SymbolType.POINTER if scalar_type_pointer(type_id) else SymbolType.DATA,
name,
name,
# we only need the matches when they are referenced elsewhere, hence we don't need the size
size=None,
)
self._db.set_pair(orig_addr, recomp_addr)
matchinfo = self._db.get_by_orig(orig_addr)
if matchinfo is None or matchinfo.recomp_addr is None:
return
recomp_addr = matchinfo.recomp_addr
node = next(
(x for x in self.cvdump_analysis.nodes if x.addr == recomp_addr),
None,
)
if node is None or node.data_type is None:
return
if not node.data_type.key.startswith("0x"):
# scalar type, so clearly not an array
return
data_type = self.cv.types.keys[node.data_type.key.lower()]
if data_type["type"] == "LF_ARRAY":
array_element_type = self.cv.types.get(data_type["array_type"])
assert node.data_type.members is not None
for array_element in node.data_type.members:
orig_element_base_addr = orig_addr + array_element.offset
recomp_element_base_addr = recomp_addr + array_element.offset
if array_element_type.members is None:
_add_match_in_array(
f"{name}{array_element.name}",
array_element_type.key,
orig_element_base_addr,
recomp_element_base_addr,
)
else:
for member in array_element_type.members:
_add_match_in_array(
f"{name}{array_element.name}.{member.name}",
array_element_type.key,
orig_element_base_addr + member.offset,
recomp_element_base_addr + member.offset,
)
def _find_original_strings(self):
"""Go to the original binary and look for the specified string constants
to find a match. This is a (relatively) expensive operation so we only

View File

@@ -5,7 +5,7 @@ from isledecomp.cvdump import SymbolsEntry
from isledecomp.types import SymbolType
from .parser import CvdumpParser
from .demangler import demangle_string_const, demangle_vtable
from .types import CvdumpKeyError, CvdumpIntegrityError
from .types import CvdumpKeyError, CvdumpIntegrityError, TypeInfo
class CvdumpNode:
@@ -35,6 +35,8 @@ class CvdumpNode:
section_contribution: Optional[int] = None
addr: Optional[int] = None
symbol_entry: Optional[SymbolsEntry] = None
# Preliminary - only used for non-static variables at the moment
data_type: Optional[TypeInfo] = None
def __init__(self, section: int, offset: int) -> None:
self.section = section
@@ -127,6 +129,7 @@ class CvdumpAnalysis:
# get information for built-in "T_" types.
g_info = parser.types.get(glo.type)
node_dict[key].confirmed_size = g_info.size
node_dict[key].data_type = g_info
# Previously we set the symbol type to POINTER here if
# the variable was known to be a pointer. We can derive this
# information later when it's time to compare the variable,