mirror of
https://github.com/isledecomp/isle-portable.git
synced 2025-03-24 13:39:41 -04:00
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:
parent
0760e4e7d7
commit
210376f272
8 changed files with 179 additions and 17 deletions
LEGO1/lego
legoomni
sources/geom
tools/isledecomp/isledecomp
|
@ -52,6 +52,9 @@ public:
|
|||
MxLong HandleEndAction(MxEndActionNotificationParam&) override; // vtable+0x74
|
||||
MxLong HandleType0Notification(MxNotificationParam&) override; // vtable+0x78
|
||||
|
||||
// FUNCTION: BETA10 0x100cd060
|
||||
undefined4 GetUnk0x150() { return m_unk0x150; }
|
||||
|
||||
// SYNTHETIC: LEGO1 0x10016c70
|
||||
// CarRace::`scalar deleting destructor'
|
||||
|
||||
|
|
|
@ -4,12 +4,24 @@
|
|||
#include "legocarraceactor.h"
|
||||
#include "legoracemap.h"
|
||||
|
||||
#define LEGORACECAR_UNKNOWN_STATE 0
|
||||
#define LEGORACECAR_KICK1 2 // name guessed
|
||||
#define LEGORACECAR_KICK2 4 // name validated by BETA10 0x100cb659
|
||||
|
||||
// SIZE 0x08
|
||||
struct EdgeReference {
|
||||
const char* m_name; // 0x00
|
||||
LegoPathBoundary* m_data; // 0x04
|
||||
};
|
||||
|
||||
// SIZE 0x10
|
||||
struct SkeletonKickPhase {
|
||||
EdgeReference* m_edgeRef; // 0x00
|
||||
float m_lower; // 0x04
|
||||
float m_upper; // 0x08
|
||||
MxU8 m_userState; // 0x0c
|
||||
};
|
||||
|
||||
// VTABLE: LEGO1 0x100d58a0 LegoRaceActor
|
||||
// VTABLE: LEGO1 0x100d58a8 LegoAnimActor
|
||||
// VTABLE: LEGO1 0x100d58b8 LegoPathActor
|
||||
|
@ -41,7 +53,7 @@ public:
|
|||
return !strcmp(p_name, LegoRaceCar::ClassName()) || LegoCarRaceActor::IsA(p_name);
|
||||
}
|
||||
|
||||
void ParseAction(char*) override; // vtable+0x20
|
||||
void ParseAction(char* p_extra) override; // vtable+0x20
|
||||
void SetWorldSpeed(MxFloat p_worldSpeed) override; // vtable+0x30
|
||||
MxU32 VTable0x6c(
|
||||
LegoPathBoundary* p_boundary,
|
||||
|
@ -58,8 +70,8 @@ public:
|
|||
MxResult VTable0x9c() override; // vtable+0x9c
|
||||
|
||||
virtual void SetMaxLinearVelocity(float p_maxLinearVelocity);
|
||||
virtual void FUN_10012ff0(float);
|
||||
virtual MxBool FUN_10013130(float);
|
||||
virtual void FUN_10012ff0(float p_param);
|
||||
virtual MxU32 HandleSkeletonKicks(float p_param1);
|
||||
|
||||
// SYNTHETIC: LEGO1 0x10014240
|
||||
// LegoRaceCar::`scalar deleting destructor'
|
||||
|
@ -74,7 +86,9 @@ private:
|
|||
LegoPathBoundary* m_unk0x7c; // 0x7c
|
||||
|
||||
static EdgeReference g_edgeReferences[];
|
||||
static const EdgeReference* g_pEdgeReferences;
|
||||
static const SkeletonKickPhase g_skeletonKickPhases[]; // TODO: better name
|
||||
|
||||
static const char* g_soundSkel3;
|
||||
};
|
||||
|
||||
#endif // LEGORACERS_H
|
||||
|
|
|
@ -12,8 +12,10 @@ class RaceSkel : public LegoAnimActor {
|
|||
public:
|
||||
RaceSkel();
|
||||
|
||||
void GetCurrentAnimData(float* p_outCurAnimPosition, float* p_outCurAnimDuration);
|
||||
|
||||
private:
|
||||
undefined4 m_unk0x1c; // 0x1c
|
||||
float m_animPosition; // 0x1c
|
||||
};
|
||||
|
||||
#endif // RACESKEL_H
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
#include "legoracers.h"
|
||||
|
||||
#include "anim/legoanim.h"
|
||||
#include "carrace.h"
|
||||
#include "define.h"
|
||||
#include "legocachesoundmanager.h"
|
||||
#include "legocameracontroller.h"
|
||||
#include "legorace.h"
|
||||
#include "legosoundmanager.h"
|
||||
#include "misc.h"
|
||||
#include "mxdebug.h"
|
||||
#include "mxmisc.h"
|
||||
#include "mxnotificationmanager.h"
|
||||
#include "mxutilities.h"
|
||||
#include "raceskel.h"
|
||||
|
||||
DECOMP_SIZE_ASSERT(EdgeReference, 0x08)
|
||||
DECOMP_SIZE_ASSERT(SkeletonKickPhase, 0x10)
|
||||
DECOMP_SIZE_ASSERT(LegoRaceCar, 0x200)
|
||||
|
||||
// GLOBAL: LEGO1 0x100f0a20
|
||||
|
@ -41,7 +47,24 @@ EdgeReference LegoRaceCar::g_edgeReferences[] = {
|
|||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100f0a50
|
||||
const EdgeReference* LegoRaceCar::g_pEdgeReferences = g_edgeReferences;
|
||||
const SkeletonKickPhase LegoRaceCar::g_skeletonKickPhases[] = {
|
||||
{&LegoRaceCar::g_edgeReferences[0], 0.1, 0.2, LEGORACECAR_KICK2},
|
||||
{&LegoRaceCar::g_edgeReferences[1], 0.2, 0.3, LEGORACECAR_KICK2},
|
||||
{&LegoRaceCar::g_edgeReferences[2], 0.3, 0.4, LEGORACECAR_KICK2},
|
||||
{&LegoRaceCar::g_edgeReferences[2], 0.6, 0.7, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[1], 0.7, 0.8, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[0], 0.8, 0.9, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[3], 0.1, 0.2, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[4], 0.2, 0.3, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[5], 0.3, 0.4, LEGORACECAR_KICK1},
|
||||
{&LegoRaceCar::g_edgeReferences[5], 0.6, 0.7, LEGORACECAR_KICK2},
|
||||
{&LegoRaceCar::g_edgeReferences[4], 0.7, 0.8, LEGORACECAR_KICK2},
|
||||
{&LegoRaceCar::g_edgeReferences[3], 0.8, 0.9, LEGORACECAR_KICK2},
|
||||
};
|
||||
|
||||
// GLOBAL: LEGO1 0x100f0b70
|
||||
// STRING: LEGO1 0x100f08bc
|
||||
const char* LegoRaceCar::g_soundSkel3 = "skel3";
|
||||
|
||||
// FUNCTION: LEGO1 0x10012950
|
||||
LegoRaceCar::LegoRaceCar()
|
||||
|
@ -140,13 +163,11 @@ void LegoRaceCar::FUN_10012ff0(float p_param)
|
|||
LegoAnimActorStruct* a; // called `a` in BETA10
|
||||
float deltaTime;
|
||||
|
||||
if (m_userState == 2) {
|
||||
if (m_userState == LEGORACECAR_KICK1) {
|
||||
a = m_unk0x70;
|
||||
}
|
||||
else {
|
||||
// TODO: Possibly an enum?
|
||||
const char legoracecarKick2 = 4; // original name: LEGORACECAR_KICK2
|
||||
assert(m_userState == legoracecarKick2);
|
||||
assert(m_userState == LEGORACECAR_KICK2);
|
||||
a = m_unk0x74;
|
||||
}
|
||||
|
||||
|
@ -156,7 +177,7 @@ void LegoRaceCar::FUN_10012ff0(float p_param)
|
|||
deltaTime = p_param - m_unk0x58;
|
||||
|
||||
if (a->GetDuration() <= deltaTime || deltaTime < 0.0) {
|
||||
if (m_userState == 2) {
|
||||
if (m_userState == LEGORACECAR_KICK1) {
|
||||
LegoEdge** edges = m_unk0x78->GetEdges();
|
||||
m_destEdge = (LegoUnknown100db7f4*) (edges[2]);
|
||||
m_boundary = m_unk0x78;
|
||||
|
@ -167,7 +188,7 @@ void LegoRaceCar::FUN_10012ff0(float p_param)
|
|||
m_boundary = m_unk0x7c;
|
||||
}
|
||||
|
||||
m_userState = 0;
|
||||
m_userState = LEGORACECAR_UNKNOWN_STATE;
|
||||
}
|
||||
else if (a->GetAnimTreePtr()->GetCamAnim()) {
|
||||
MxMatrix transformationMatrix;
|
||||
|
@ -189,10 +210,50 @@ void LegoRaceCar::FUN_10012ff0(float p_param)
|
|||
}
|
||||
}
|
||||
|
||||
// STUB: LEGO1 0x10013130
|
||||
MxBool LegoRaceCar::FUN_10013130(float)
|
||||
// FUNCTION: LEGO1 0x10013130
|
||||
// FUNCTION: BETA10 0x100cce50
|
||||
MxU32 LegoRaceCar::HandleSkeletonKicks(float p_param1)
|
||||
{
|
||||
// TODO
|
||||
const SkeletonKickPhase* current = g_skeletonKickPhases;
|
||||
|
||||
// TODO: Type is guesswork so far
|
||||
CarRace* r = (CarRace*) CurrentWorld(); // called `r` in BETA10
|
||||
assert(r);
|
||||
|
||||
RaceSkel* s = (RaceSkel*) r->GetUnk0x150(); // called `s` in BETA10
|
||||
assert(s);
|
||||
|
||||
float skeletonCurAnimPosition;
|
||||
float skeletonCurAnimDuration;
|
||||
|
||||
s->GetCurrentAnimData(&skeletonCurAnimPosition, &skeletonCurAnimDuration);
|
||||
|
||||
float skeletonCurAnimPhase = skeletonCurAnimPosition / skeletonCurAnimDuration;
|
||||
|
||||
for (MxS32 i = 0; i < sizeOfArray(g_skeletonKickPhases); i++) {
|
||||
if (m_boundary == current->m_edgeRef->m_data && current->m_lower <= skeletonCurAnimPhase &&
|
||||
skeletonCurAnimPhase <= current->m_upper) {
|
||||
m_userState = current->m_userState;
|
||||
}
|
||||
current = ¤t[1];
|
||||
}
|
||||
|
||||
if (m_userState != LEGORACECAR_KICK1 && m_userState != LEGORACECAR_KICK2) {
|
||||
MxTrace(
|
||||
// STRING: BETA10 0x101f64c8
|
||||
"Got kicked in boundary %s %d %g:%g %g\n",
|
||||
// TODO: same as in above comparison
|
||||
m_boundary->GetName(),
|
||||
skeletonCurAnimPosition,
|
||||
skeletonCurAnimDuration,
|
||||
skeletonCurAnimPhase
|
||||
);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_unk0x58 = p_param1;
|
||||
SoundManager()->GetCacheSoundManager()->Play(g_soundSkel3, NULL, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "raceskel.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
DECOMP_SIZE_ASSERT(RaceSkel, 0x178)
|
||||
|
||||
// STUB: LEGO1 0x100719b0
|
||||
|
@ -7,3 +9,13 @@ RaceSkel::RaceSkel()
|
|||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// FUNCTION: LEGO1 0x10071cb0
|
||||
// FUNCTION: BETA10 0x100f158b
|
||||
void RaceSkel::GetCurrentAnimData(float* p_outCurAnimPosition, float* p_outCurAnimDuration)
|
||||
{
|
||||
*p_outCurAnimPosition = m_animPosition;
|
||||
|
||||
assert(m_curAnim >= 0);
|
||||
*p_outCurAnimDuration = m_animMaps[m_curAnim]->GetDuration();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
LegoU32 GetFlag0x10() { return m_flags & c_bit5 ? FALSE : TRUE; }
|
||||
Mx4DPointFloat* GetUnknown0x14() { return &m_unk0x14; }
|
||||
Mx4DPointFloat* GetEdgeNormal(int index) { return &m_edgeNormals[index]; }
|
||||
|
||||
// FUNCTION: BETA10 0x1001c9b0
|
||||
LegoChar* GetName() { return m_name; }
|
||||
|
||||
void SetFlag0x10(LegoU32 p_disable)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue