Merge branch 'master' of https://github.com/tahg/isletahg into extra

This commit is contained in:
Nathan 2024-03-29 22:40:50 -04:00
commit 8fc00ea7dd
38 changed files with 722 additions and 158 deletions

View File

@ -156,6 +156,13 @@ jobs:
python3 tools/vtable/vtable.py legobin/ISLE.EXE build/ISLE.EXE build/ISLE.PDB .
python3 tools/vtable/vtable.py legobin/LEGO1.DLL build/LEGO1.DLL build/LEGO1.PDB .
- name: Check Variables
shell: bash
run: |
python3 tools/datacmp.py legobin/CONFIG.EXE build/CONFIG.EXE build/CONFIG.PDB .
python3 tools/datacmp.py legobin/ISLE.EXE build/ISLE.EXE build/ISLE.PDB .
python3 tools/datacmp.py legobin/LEGO1.DLL build/LEGO1.DLL build/LEGO1.PDB .
- name: Upload Artifact
uses: actions/upload-artifact@master
with:

View File

@ -297,7 +297,7 @@
// GLOBAL: ISLE 0x4105b0
// __shi_TaskRecord
// GLOBAL: ISLE 0x4125f8
// ~GLOBAL: ISLE 0x4125f8
// ?_pnhHeap@@3P6AHI@ZA
// GLOBAL: ISLE 0x412830

View File

@ -35,8 +35,8 @@ class Act3 : public LegoWorld {
MxBool VTable0x64() override; // vtable+0x64
void Enable(MxBool p_enable) override; // vtable+0x68
inline void SetUnkown420c(MxEntity* p_entity) { m_unk0x420c = p_entity; }
inline void SetUnkown4270(MxU32 p_unk0x4270) { m_unk0x4270 = p_unk0x4270; }
inline void SetUnknown420c(MxEntity* p_entity) { m_unk0x420c = p_entity; }
inline void SetUnknown4270(MxU32 p_unk0x4270) { m_unk0x4270 = p_unk0x4270; }
// SYNTHETIC: LEGO1 0x10072630
// Act3::`scalar deleting destructor'

View File

@ -20,8 +20,7 @@ class BeachHouseEntity : public BuildingEntity {
return !strcmp(p_name, BeachHouseEntity::ClassName()) || BuildingEntity::IsA(p_name);
}
// STUB: LEGO1 0x100153b0
MxLong VTable0x50(MxParam& p_param) override { return 0; }
MxLong VTable0x50(MxParam& p_param) override;
// SYNTHETIC: LEGO1 0x1000f970
// BeachHouseEntity::`scalar deleting destructor'

View File

@ -20,8 +20,7 @@ class GasStationEntity : public BuildingEntity {
return !strcmp(p_name, GasStationEntity::ClassName()) || BuildingEntity::IsA(p_name);
}
// STUB: LEGO1 0x100151d0
MxLong VTable0x50(MxParam& p_param) override { return 0; }
MxLong VTable0x50(MxParam& p_param) override;
// SYNTHETIC: LEGO1 0x1000f890
// GasStationEntity::`scalar deleting destructor'

View File

@ -20,8 +20,7 @@ class InfoCenterEntity : public BuildingEntity {
return !strcmp(p_name, InfoCenterEntity::ClassName()) || BuildingEntity::IsA(p_name);
}
// STUB: LEGO1 0x100150c0
MxLong VTable0x50(MxParam& p_param) override { return 0; }
MxLong VTable0x50(MxParam& p_param) override; // vtable+0x50
// SYNTHETIC: LEGO1 0x1000f7b0
// InfoCenterEntity::`scalar deleting destructor'

View File

@ -69,6 +69,7 @@ class Isle : public LegoWorld {
void FUN_10031590();
void FUN_10032620();
void FUN_100330e0();
void FUN_10033350();
void FUN_10032d30(
IsleScript::Script p_script,
JukeboxScript::Script p_music,

View File

@ -19,6 +19,8 @@ class LegoAct2 : public LegoWorld {
MxBool VTable0x64() override; // vtable+0x64
void Enable(MxBool p_enable) override; // vtable+0x68
inline void SetUnknown0x1150(undefined4 p_unk0x1150) { m_unk0x1150 = p_unk0x1150; }
// SYNTHETIC: LEGO1 0x1004fe20
// LegoAct2::`scalar deleting destructor'

View File

@ -28,10 +28,11 @@ class LegoAct2State : public LegoState {
// LegoAct2State::`scalar deleting destructor'
inline undefined4 GetUnknown0x08() { return m_unk0x08; }
inline void SetUnknown0x0c(undefined p_unk0x0c) { m_unk0x0c = p_unk0x0c; }
private:
undefined4 m_unk0x08; // 0x08
undefined4 m_unk0x0c; // 0x0c
undefined m_unk0x0c; // 0x0c
};
#endif // LEGOACT2STATE_H

View File

@ -10,13 +10,18 @@
// SIZE 0x18
struct Character {
char* m_name; // 0x00
undefined m_unk0x04; // 0x04
MxS8 m_vehicleId; // 0x05
undefined m_unk0x06; // 0x06
MxBool m_unk0x07; // 0x07
undefined m_unk0x08[12]; // 0x08
MxBool m_active; // 0x14
char* m_name; // 0x00
MxBool m_unk0x04; // 0x04
MxS8 m_vehicleId; // 0x05
undefined m_unk0x06; // 0x06 (unused?)
MxBool m_unk0x07; // 0x07
MxBool m_unk0x08; // 0x08
MxBool m_unk0x09; // 0x09
MxU32 m_unk0x0c; // 0x0c
MxU32 m_unk0x10; // 0x10
MxBool m_active; // 0x14
MxU8 m_unk0x15; // 0x15
MxU8 m_unk0x16; // 0x16
};
// SIZE 0x08
@ -62,10 +67,10 @@ class LegoAnimationManager : public MxCore {
void FUN_1005f6d0(MxBool);
void FUN_1005f700(MxBool);
MxResult LoadScriptInfo(MxS32 p_scriptIndex);
MxBool FUN_10060140(char* p_name, MxU32& p_index);
MxBool FindVehicle(const char* p_name, MxU32& p_index);
MxResult ReadAnimInfo(LegoFile* p_file, AnimInfo* p_info);
MxResult ReadModelInfo(LegoFile* p_file, ModelInfo* p_info);
void FUN_100603c0();
void DeleteAnimations();
MxResult StartEntityAction(MxDSAction& p_dsAction, LegoEntity* p_entity);
void FUN_10060570(MxBool);
undefined4 FUN_10060dc0(

View File

@ -10,8 +10,8 @@ class LegoBackgroundColor : public MxVariable {
LegoBackgroundColor(const char* p_key, const char* p_value);
void SetValue(const char* p_colorString) override;
void SetLights(float p_r, float p_g, float p_b);
void SetLights();
void SetLightColor(float p_r, float p_g, float p_b);
void SetLightColor();
void ToggleDayNight(MxBool);
void ToggleSkyColor();

View File

@ -47,17 +47,19 @@ class LegoCharacterManager {
void Init();
static void SetCustomizeAnimFile(const char* p_value);
static MxBool FUN_10084c00(const LegoChar*);
static MxBool Exists(const char* p_key);
void FUN_100832a0();
MxU32 GetRefCount(LegoROI* p_roi);
void FUN_10083db0(LegoROI* p_roi);
void FUN_10083f10(LegoROI* p_roi);
LegoExtraActor* FUN_10084c40(const LegoChar*);
LegoCharacterData* Find(const char* p_key);
LegoExtraActor* GetActor(const char* p_key);
LegoCharacterData* GetData(const char* p_key);
LegoCharacterData* GetData(LegoROI* p_roi);
MxBool FUN_10084ec0(LegoROI* p_roi);
MxU32 FUN_10085140(LegoROI*, MxBool);
LegoROI* FUN_10085210(const LegoChar*, LegoChar*, undefined);
LegoROI* FUN_10085a80(LegoChar* p_und1, LegoChar* p_und2, undefined p_und3);
MxU32 FUN_10085140(LegoROI* p_roi, MxBool p_und);
LegoROI* FUN_10085210(const char*, char*, undefined);
LegoROI* FUN_10085a80(char* p_und1, char* p_und2, undefined p_und3);
static const char* GetCustomizeAnimFile() { return g_customizeAnimFile; }

View File

@ -25,8 +25,8 @@ void FUN_1003ef00(MxBool);
void SetAppCursor(WPARAM p_wparam);
MxBool FUN_1003ef60();
MxBool RemoveFromWorld(MxAtomId& p_entityAtom, MxS32 p_entityId, MxAtomId& p_worldAtom, MxS32 p_worldEntityId);
MxS32 FUN_1003f050(MxS32);
void SetLightPosition(MxU32);
MxS32 UpdateLightPosition(MxS32 p_increase);
void SetLightPosition(MxS32 p_index);
LegoNamedTexture* ReadNamedTexture(LegoFile* p_file);
void FUN_1003f540(LegoFile* p_file, const char* p_filename);
void WriteNamedTexture(LegoFile* p_file, LegoNamedTexture* p_texture);

View File

@ -20,7 +20,7 @@ class PoliceEntity : public BuildingEntity {
return !strcmp(p_name, PoliceEntity::ClassName()) || BuildingEntity::IsA(p_name);
}
MxLong VTable0x50(MxParam& p_param) override;
MxLong VTable0x50(MxParam& p_param) override; // vtable+0x50
// SYNTHETIC: LEGO1 0x1000f900
// PoliceEntity::`scalar deleting destructor'

View File

@ -19,8 +19,7 @@ class RaceStandsEntity : public BuildingEntity {
return !strcmp(p_name, RaceStandsEntity::ClassName()) || BuildingEntity::IsA(p_name);
}
// STUB: LEGO1 0x10015450
MxLong VTable0x50(MxParam& p_param) override { return 0; }
MxLong VTable0x50(MxParam& p_param) override;
// SYNTHETIC: LEGO1 0x1000f9e0
// RaceStandsEntity::`scalar deleting destructor'

View File

@ -41,7 +41,7 @@ MxResult Helicopter::Create(MxDSAction& p_dsAction)
LegoWorld* world = CurrentWorld();
SetWorld(world);
if (world->IsA("Act3")) {
((Act3*) GetWorld())->SetUnkown420c(this);
((Act3*) GetWorld())->SetUnknown420c(this);
}
world = GetWorld();
if (world) {
@ -160,7 +160,7 @@ MxU32 Helicopter::VTable0xd4(LegoControlManagerEvent& p_param)
switch (p_param.GetClickedObjectId()) {
case IsleScript::c_HelicopterArms_Ctl:
if (*g_act3Script == script) {
((Act3*) CurrentWorld())->SetUnkown4270(2);
((Act3*) CurrentWorld())->SetUnknown4270(2);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
else if (m_state->GetUnkown8() != 0) {

View File

@ -15,11 +15,66 @@ DECOMP_SIZE_ASSERT(Vehicle, 0x8)
DECOMP_SIZE_ASSERT(Unknown0x3c, 0x18)
// GLOBAL: LEGO1 0x100f6d20
Vehicle g_vehicles[] = {"bikebd", 0, FALSE, "bikepg", 0, FALSE, "bikerd", 0, FALSE, "bikesy", 0,
FALSE, "motoni", 0, FALSE, "motola", 0, FALSE, "board", 0, FALSE};
Vehicle g_vehicles[] = {
{"bikebd", 0, FALSE},
{"bikepg", 0, FALSE},
{"bikerd", 0, FALSE},
{"bikesy", 0, FALSE},
{"motoni", 0, FALSE},
{"motola", 0, FALSE},
{"board", 0, FALSE}
};
// GLOBAL: LEGO1 0x100f7048
Character g_characters[47]; // TODO: Initialize this
Character g_characters[47] = {
{"pepper", FALSE, 6, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 50, 1},
{"mama", FALSE, -1, 0, FALSE, FALSE, FALSE, 1500, 20000, FALSE, 0, 2},
{"papa", FALSE, -1, 0, FALSE, FALSE, FALSE, 1500, 20000, FALSE, 0, 3},
{"nick", FALSE, 4, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 20, 4},
{"laura", FALSE, 5, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 20, 5},
{"brickstr", FALSE, -1, 0, FALSE, FALSE, FALSE, 1000, 20000, FALSE, 0, 6},
{"studs", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"rhoda", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"valerie", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"snap", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"pt", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"mg", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"bu", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"ml", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"nu", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"na", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"cl", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"en", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"re", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"ro", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"d1", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"d2", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"d3", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"d4", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l1", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l2", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l3", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l4", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l5", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"l6", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"b1", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"b2", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"b3", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"b4", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"cm", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"gd", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"rd", FALSE, 2, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 50, 9},
{"pg", FALSE, 1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 50, 8},
{"bd", FALSE, 0, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 100, 7},
{"sy", FALSE, 3, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 100, 10},
{"gn", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"df", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"bs", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"lt", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"st", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"bm", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0},
{"jk", FALSE, -1, 0, FALSE, FALSE, TRUE, 1500, 20000, FALSE, 0, 0}
};
// GLOBAL: LEGO1 0x100f74f8
MxS32 g_legoAnimationManagerConfig = 1;
@ -118,7 +173,7 @@ MxResult LegoAnimationManager::LoadScriptInfo(MxS32 p_scriptIndex)
m_animState->FUN_10065240(m_animCount, m_anims, m_unk0x3fc);
}
FUN_100603c0();
DeleteAnimations();
LegoFile file;
@ -199,7 +254,7 @@ MxResult LegoAnimationManager::LoadScriptInfo(MxS32 p_scriptIndex)
for (MxS32 m = 0; m < m_anims[j].m_modelCount; m++) {
MxU32 n;
if (FUN_10060140(m_anims[j].m_models[m].m_modelName, n) && m_anims[j].m_models[m].m_unk0x2c) {
if (FindVehicle(m_anims[j].m_models[m].m_modelName, n) && m_anims[j].m_models[m].m_unk0x2c) {
m_anims[j].m_unk0x2a[count++] = n;
if (count > 3) {
break;
@ -233,16 +288,22 @@ MxResult LegoAnimationManager::LoadScriptInfo(MxS32 p_scriptIndex)
done:
if (result == FAILURE) {
FUN_100603c0();
DeleteAnimations();
}
return result;
}
// STUB: LEGO1 0x10060140
MxBool LegoAnimationManager::FUN_10060140(char* p_name, MxU32& p_index)
// FUNCTION: LEGO1 0x10060140
MxBool LegoAnimationManager::FindVehicle(const char* p_name, MxU32& p_index)
{
// TODO
for (MxS32 i = 0; i < _countof(g_vehicles); i++) {
if (!strcmpi(p_name, g_vehicles[i].m_name)) {
p_index = i;
return TRUE;
}
}
return FALSE;
}
@ -347,10 +408,29 @@ MxResult LegoAnimationManager::ReadModelInfo(LegoFile* p_file, ModelInfo* p_info
return result;
}
// STUB: LEGO1 0x100603c0
void LegoAnimationManager::FUN_100603c0()
// FUNCTION: LEGO1 0x100603c0
void LegoAnimationManager::DeleteAnimations()
{
// TODO
undefined unk0x42b = m_unk0x42b;
if (m_anims != NULL) {
for (MxS32 i = 0; i < m_animCount; i++) {
delete m_anims[i].m_animName;
if (m_anims[i].m_models != NULL) {
for (MxS32 j = 0; j < m_anims[i].m_modelCount; j++) {
delete m_anims[i].m_models[j].m_modelName;
}
delete m_anims[i].m_models;
}
}
delete m_anims;
}
Init();
m_unk0x42b = unk0x42b;
}
// STUB: LEGO1 0x10060570
@ -366,7 +446,7 @@ MxResult LegoAnimationManager::StartEntityAction(MxDSAction& p_dsAction, LegoEnt
LegoROI* roi = p_entity->GetROI();
if (p_entity->GetUnknown0x59() == 0) {
LegoPathActor* actor = CharacterManager()->FUN_10084c40(roi->GetName());
LegoPathActor* actor = CharacterManager()->GetActor(roi->GetName());
if (actor) {
LegoPathController* controller = actor->GetController();

View File

@ -91,7 +91,7 @@ void LegoBackgroundColor::ToggleDayNight(MxBool p_sun)
float convertedR, convertedG, convertedB;
ConvertHSVToRGB(m_h, m_s, m_v, &convertedR, &convertedG, &convertedB);
VideoManager()->SetSkyColor(convertedR, convertedG, convertedB);
SetLights(convertedR, convertedG, convertedB);
SetLightColor(convertedR, convertedG, convertedB);
}
// FUNCTION: LEGO1 0x1003c330
@ -110,18 +110,39 @@ void LegoBackgroundColor::ToggleSkyColor()
float convertedR, convertedG, convertedB;
ConvertHSVToRGB(m_h, m_s, m_v, &convertedR, &convertedG, &convertedB);
VideoManager()->SetSkyColor(convertedR, convertedG, convertedB);
SetLights(convertedR, convertedG, convertedB);
SetLightColor(convertedR, convertedG, convertedB);
}
// STUB: LEGO1 0x1003c400
void LegoBackgroundColor::SetLights(float p_r, float p_g, float p_b)
// FUNCTION: LEGO1 0x1003c400
void LegoBackgroundColor::SetLightColor(float p_r, float p_g, float p_b)
{
if (!VideoManager()->GetVideoParam().Flags().GetF2bit0()) {
// TODO: Computed constants based on what?
p_r *= 4.3478260869565215;
p_g *= 1.5873015873015872;
p_b *= 1.1764705882352942;
if (p_r > 1.0) {
p_r = 1.0;
}
if (p_g > 1.0) {
p_g = 1.0;
}
if (p_b > 1.0) {
p_b = 1.0;
}
VideoManager()->Get3DManager()->GetLego3DView()->SetLightColor(FALSE, p_r, p_g, p_b);
VideoManager()->Get3DManager()->GetLego3DView()->SetLightColor(TRUE, p_r, p_g, p_b);
}
}
// FUNCTION: LEGO1 0x1003c4b0
void LegoBackgroundColor::SetLights()
void LegoBackgroundColor::SetLightColor()
{
float convertedR, convertedG, convertedB;
ConvertHSVToRGB(m_h, m_s, m_v, &convertedR, &convertedG, &convertedB);
SetLights(convertedR, convertedG, convertedB);
SetLightColor(convertedR, convertedG, convertedB);
}

View File

@ -17,6 +17,12 @@ DECOMP_SIZE_ASSERT(LegoCharacterManager, 0x08)
// GLOBAL: LEGO1 0x100fc4e4
char* LegoCharacterManager::g_customizeAnimFile = NULL;
// GLOBAL: LEGO1 0x100fc4d8
MxU32 g_unk0x100fc4d8 = 50;
// GLOBAL: LEGO1 0x100fc4dc
MxU32 g_unk0x100fc4dc = 66;
// GLOBAL: LEGO1 0x10104f20
LegoCharacterData g_characterData[66];
@ -38,10 +44,26 @@ void LegoCharacterManager::Init()
}
}
// STUB: LEGO1 0x100832a0
// FUNCTION: LEGO1 0x100832a0
void LegoCharacterManager::FUN_100832a0()
{
// TODO
for (MxS32 i = 0; i < _countof(g_characterData); i++) {
LegoCharacterData* data = GetData(g_characterData[i].m_name);
if (data != NULL) {
LegoExtraActor* actor = data->m_actor;
if (actor != NULL && actor->IsA("LegoExtraActor")) {
LegoROI* roi = g_characterData[i].m_roi;
MxU32 refCount = GetRefCount(roi);
while (refCount != 0) {
FUN_10083db0(roi);
refCount = GetRefCount(roi);
}
}
}
}
}
// FUNCTION: LEGO1 0x10083310
@ -49,7 +71,7 @@ MxResult LegoCharacterManager::Write(LegoStorage* p_storage)
{
MxResult result = FAILURE;
for (MxS32 i = 0; i < _countof(g_characterData) - 1; i++) {
for (MxS32 i = 0; i < _countof(g_characterData); i++) {
LegoCharacterData* data = &g_characterData[i];
if (p_storage->Write(&data->m_unk0x0c, sizeof(data->m_unk0x0c)) != SUCCESS) {
@ -95,7 +117,7 @@ MxResult LegoCharacterManager::Read(LegoStorage* p_storage)
{
MxResult result = FAILURE;
for (MxS32 i = 0; i < _countof(g_characterData) - 1; i++) {
for (MxS32 i = 0; i < _countof(g_characterData); i++) {
LegoCharacterData* data = &g_characterData[i];
if (p_storage->Read(&data->m_unk0x0c, sizeof(data->m_unk0x0c)) != SUCCESS) {
@ -180,7 +202,7 @@ LegoROI* LegoCharacterManager::GetROI(const char* p_key, MxBool p_createEntity)
actor->SetROI(character->m_roi, FALSE, FALSE);
actor->FUN_100114e0(0);
actor->SetFlag(LegoActor::c_bit2);
Find(p_key)->m_actor = actor;
GetData(p_key)->m_actor = actor;
}
return character->m_roi;
@ -189,6 +211,23 @@ LegoROI* LegoCharacterManager::GetROI(const char* p_key, MxBool p_createEntity)
return NULL;
}
// FUNCTION: LEGO1 0x10083bc0
MxU32 LegoCharacterManager::GetRefCount(LegoROI* p_roi)
{
LegoCharacterMap::iterator it;
for (it = m_characters->begin(); it != m_characters->end(); it++) {
LegoCharacter* character = (*it).second;
LegoROI* roi = character->m_roi;
if (roi == p_roi) {
return character->m_refCount;
}
}
return 0;
}
// STUB: LEGO1 0x10083db0
void LegoCharacterManager::FUN_10083db0(LegoROI* p_roi)
{
@ -215,21 +254,21 @@ LegoROI* LegoCharacterManager::CreateROI(const char* p_key)
Tgl::Renderer* renderer = VideoManager()->GetRenderer();
ViewLODListManager* lodManager = GetViewLODListManager();
LegoTextureContainer* textureContainer = TextureContainer();
LegoCharacterData* characterData = Find(p_key);
LegoCharacterData* data = GetData(p_key);
if (characterData == NULL) {
if (data == NULL) {
goto done;
}
if (!strcmpi(p_key, "pep")) {
LegoCharacterData* pepper = Find("pepper");
LegoCharacterData* pepper = GetData("pepper");
characterData->m_unk0x0c = pepper->m_unk0x0c;
characterData->m_unk0x10 = pepper->m_unk0x10;
characterData->m_unk0x14 = pepper->m_unk0x14;
data->m_unk0x0c = pepper->m_unk0x0c;
data->m_unk0x10 = pepper->m_unk0x10;
data->m_unk0x14 = pepper->m_unk0x14;
for (i = 0; i < _countof(characterData->m_parts); i++) {
characterData->m_parts[i] = pepper->m_parts[i];
for (i = 0; i < _countof(data->m_parts); i++) {
data->m_parts[i] = pepper->m_parts[i];
}
}
@ -260,7 +299,7 @@ LegoROI* LegoCharacterManager::CreateROI(const char* p_key)
const char* parentName;
char lodName[64];
LegoCharacterData::Part& part = characterData->m_parts[i];
LegoCharacterData::Part& part = data->m_parts[i];
if (i == 0 || i == 1) {
parentName = part.m_unk0x04[part.m_unk0x00[part.m_unk0x08]];
@ -338,7 +377,7 @@ LegoROI* LegoCharacterManager::CreateROI(const char* p_key)
);
roi->WrappedSetLocalTransform(mat);
characterData->m_roi = roi;
data->m_roi = roi;
success = TRUE;
done:
@ -350,25 +389,36 @@ LegoROI* LegoCharacterManager::CreateROI(const char* p_key)
return roi;
}
// STUB: LEGO1 0x10084c00
MxBool LegoCharacterManager::FUN_10084c00(const LegoChar*)
// FUNCTION: LEGO1 0x10084c00
MxBool LegoCharacterManager::Exists(const char* p_key)
{
// TODO
for (MxU32 i = 0; i < _countof(g_characterData); i++) {
if (!strcmpi(g_characterData[i].m_name, p_key)) {
return TRUE;
}
}
return FALSE;
}
// STUB: LEGO1 0x10084c40
LegoExtraActor* LegoCharacterManager::FUN_10084c40(const LegoChar*)
// FUNCTION: LEGO1 0x10084c40
LegoExtraActor* LegoCharacterManager::GetActor(const char* p_key)
{
LegoCharacterData* data = GetData(p_key);
if (data != NULL) {
return data->m_actor;
}
return NULL;
}
// FUNCTION: LEGO1 0x10084c60
LegoCharacterData* LegoCharacterManager::Find(const char* p_key)
LegoCharacterData* LegoCharacterManager::GetData(const char* p_key)
{
MxU32 i;
for (i = 0; i < _countof(g_characterData) - 1; i++) {
for (i = 0; i < _countof(g_characterData); i++) {
if (!strcmpi(g_characterData[i].m_name, p_key)) {
break;
}
@ -381,6 +431,24 @@ LegoCharacterData* LegoCharacterManager::Find(const char* p_key)
return NULL;
}
// FUNCTION: LEGO1 0x10084cb0
LegoCharacterData* LegoCharacterManager::GetData(LegoROI* p_roi)
{
MxU32 i;
for (i = 0; i < _countof(g_characterData); i++) {
if (g_characterData[i].m_roi == p_roi) {
break;
}
}
if (i < _countof(g_characterData)) {
return &g_characterData[i];
}
return NULL;
}
// STUB: LEGO1 0x10084ec0
MxBool LegoCharacterManager::FUN_10084ec0(LegoROI* p_roi)
{
@ -388,10 +456,19 @@ MxBool LegoCharacterManager::FUN_10084ec0(LegoROI* p_roi)
return FALSE;
}
// STUB: LEGO1 0x10085140
MxU32 LegoCharacterManager::FUN_10085140(LegoROI*, MxBool)
// FUNCTION: LEGO1 0x10085140
MxU32 LegoCharacterManager::FUN_10085140(LegoROI* p_roi, MxBool p_und)
{
// TODO
LegoCharacterData* data = GetData(p_roi);
if (p_und) {
return data->m_unk0x14 + g_unk0x100fc4dc;
}
if (data != NULL) {
return data->m_unk0x0c + g_unk0x100fc4d8;
}
return 0;
}
@ -415,13 +492,13 @@ void LegoCharacterManager::SetCustomizeAnimFile(const char* p_value)
}
// STUB: LEGO1 0x10085210
LegoROI* LegoCharacterManager::FUN_10085210(const LegoChar*, LegoChar*, undefined)
LegoROI* LegoCharacterManager::FUN_10085210(const char*, char*, undefined)
{
return NULL;
}
// FUNCTION: LEGO1 0x10085a80
LegoROI* LegoCharacterManager::FUN_10085a80(LegoChar* p_und1, LegoChar* p_und2, undefined p_und3)
LegoROI* LegoCharacterManager::FUN_10085a80(char* p_und1, char* p_und2, undefined p_und3)
{
return FUN_10085210(p_und1, p_und2, p_und3);
}

View File

@ -350,7 +350,7 @@ MxResult LegoGameState::Load(MxULong p_slot)
}
} while (status != 2);
m_backgroundColor->SetLights();
m_backgroundColor->SetLightColor();
lightPosition = VariableTable()->GetVariable("lightposition");
if (lightPosition) {
@ -1072,7 +1072,7 @@ void LegoGameState::RegisterState(LegoState* p_state)
void LegoGameState::Init()
{
m_backgroundColor->SetValue("set 56 54 68");
m_backgroundColor->SetLights();
m_backgroundColor->SetLightColor();
m_tempBackgroundColor->SetValue("set 56 54 68");
VariableTable()->SetVariable("lightposition", "2");
SetLightPosition(2);

View File

@ -14,9 +14,11 @@
#include "mxnotificationmanager.h"
#include "mxstreamer.h"
#include "mxtypes.h"
#include "realtime/realtime.h"
#include <process.h>
#include <string.h>
#include <vec.h>
// STUB: LEGO1 0x1003e050
void FUN_1003e050(LegoAnimPresenter* p_presenter)
@ -339,22 +341,95 @@ MxBool FUN_1003ef60()
return FALSE;
}
// STUB: LEGO1 0x1003f050
MxS32 FUN_1003f050(MxS32)
// FUNCTION: LEGO1 0x1003f050
MxS32 UpdateLightPosition(MxS32 p_increase)
{
// TODO
return 0;
MxS32 lightPosition = atoi(VariableTable()->GetVariable("lightposition"));
// Only ever increases by 1 irrespective of p_increase
if (p_increase > 0) {
lightPosition += 1;
if (lightPosition > 5) {
lightPosition = 5;
}
}
else {
lightPosition -= 1;
if (lightPosition < 0) {
lightPosition = 0;
}
}
SetLightPosition(lightPosition);
char lightPositionBuffer[32];
sprintf(lightPositionBuffer, "%d", lightPosition);
VariableTable()->SetVariable("lightposition", lightPositionBuffer);
return lightPosition;
}
// STUB: LEGO1 0x1003f0d0
void SetLightPosition(MxU32)
// FUNCTION: LEGO1 0x1003f0d0
void SetLightPosition(MxS32 p_index)
{
float lights[6][6] = {
{1.0, 0.0, 0.0, -150.0, 50.0, -50.0},
{0.809, -0.588, 0.0, -75.0, 50.0, -50.0},
{0.0, -1.0, 0.0, 0.0, 150.0, -150.0},
{-0.309, -0.951, 0.0, 25.0, 50.0, -50.0},
{-0.809, -0.588, 0.0, 75.0, 50.0, -50.0},
{-1.0, 0.0, 0.0, 150.0, 50.0, -50.0}
};
Mx3DPointFloat up(1.0, 0.0, 0.0);
Mx3DPointFloat direction;
Mx3DPointFloat position;
Tgl::FloatMatrix4 matrix;
Matrix4 in(matrix);
MxMatrix transform;
if (p_index < 0) {
p_index = 0;
}
else if (p_index > 5) {
p_index = 5;
}
direction = lights[p_index];
position = &lights[p_index][3];
CalcLocalTransform(position, direction, up, transform);
SETMAT4(in, transform);
VideoManager()->Get3DManager()->GetLego3DView()->SetLightTransform(FALSE, matrix);
VideoManager()->Get3DManager()->GetLego3DView()->SetLightTransform(TRUE, matrix);
}
// STUB: LEGO1 0x1003f3b0
// FUNCTION: LEGO1 0x1003f3b0
LegoNamedTexture* ReadNamedTexture(LegoFile* p_file)
{
return NULL;
LegoTexture* texture = NULL;
LegoNamedTexture* namedTexture = NULL;
MxString string;
p_file->ReadString(string);
texture = new LegoTexture();
if (texture != NULL) {
if (texture->Read(p_file, 0) != SUCCESS) {
delete texture;
return namedTexture;
}
namedTexture = new LegoNamedTexture(string.GetData(), texture);
if (namedTexture == NULL) {
delete texture;
}
}
return namedTexture;
}
// STUB: LEGO1 0x1003f540

View File

@ -1,3 +1,39 @@
#include "gasstationentity.h"
#include "act1state.h"
#include "isle.h"
#include "isle_actions.h"
#include "islepathactor.h"
#include "legoanimationmanager.h"
#include "legogamestate.h"
#include "legoomni.h"
#include "legoutils.h"
#include "legoworld.h"
#include "misc.h"
#include "mxtransitionmanager.h"
DECOMP_SIZE_ASSERT(GasStationEntity, 0x68)
// FUNCTION: LEGO1 0x100151d0
MxLong GasStationEntity::VTable0x50(MxParam& p_param)
{
if (FUN_1003ef60()) {
Act1State* state = (Act1State*) GameState()->GetState("Act1State");
if (state->GetUnknown18() != 8) {
state->SetUnknown18(0);
if (CurrentActor()->GetActorId() != GameState()->GetActorId()) {
CurrentActor()->VTable0xe4();
}
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->SetDestLocation(LegoGameState::Area::e_garage);
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
}
return 1;
}

View File

@ -30,7 +30,7 @@ MxLong HospitalEntity::VTable0x50(MxParam& p_param)
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->SetDestLocation(LegoGameState::Area::e_hospital);
AnimationManager()->FUN_10061010(NULL);
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
}

View File

@ -1,3 +1,60 @@
#include "infocenterentity.h"
#include "act1state.h"
#include "act2main_actions.h"
#include "act3.h"
#include "act3_actions.h"
#include "act3state.h"
#include "isle.h"
#include "isle_actions.h"
#include "islepathactor.h"
#include "legoact2.h"
#include "legoact2state.h"
#include "legoanimationmanager.h"
#include "legogamestate.h"
#include "legoomni.h"
#include "legoutils.h"
#include "legoworld.h"
#include "misc.h"
#include "mxtransitionmanager.h"
DECOMP_SIZE_ASSERT(InfoCenterEntity, 0x68)
// FUNCTION: LEGO1 0x100150c0
MxLong InfoCenterEntity::VTable0x50(MxParam& p_param)
{
switch (GameState()->GetCurrentAct()) {
case LegoGameState::Act::e_act1: {
if (CurrentActor()->GetActorId() != GameState()->GetActorId()) {
CurrentActor()->VTable0xe4();
}
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->FUN_10033350();
isle->SetDestLocation(LegoGameState::Area::e_infomain);
Act1State* act1state = (Act1State*) GameState()->GetState("Act1State");
act1state->SetUnknown18(0);
break;
}
case LegoGameState::Act::e_act2: {
LegoAct2* act2 = (LegoAct2*) FindWorld(*g_act2mainScript, Act2mainScript::c__Act2Main);
act2->SetUnknown0x1150(2);
LegoAct2State* act2state = (LegoAct2State*) GameState()->GetState("LegoAct2State");
if (act2state) {
act2state->SetUnknown0x0c(0);
}
break;
}
case LegoGameState::Act::e_act3:
Act3* act3 = (Act3*) FindWorld(*g_act3Script, Act3Script::c__Act3);
act3->SetUnknown4270(2);
break;
}
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
return 1;
}

View File

@ -1,3 +1,36 @@
#include "beachhouseentity.h"
#include "act1state.h"
#include "isle.h"
#include "isle_actions.h"
#include "islepathactor.h"
#include "legoanimationmanager.h"
#include "legogamestate.h"
#include "legoomni.h"
#include "legoutils.h"
#include "legoworld.h"
#include "misc.h"
#include "mxtransitionmanager.h"
DECOMP_SIZE_ASSERT(BeachHouseEntity, 0x68)
// FUNCTION: LEGO1 0x100153b0
MxLong BeachHouseEntity::VTable0x50(MxParam& p_param)
{
if (FUN_1003ef60()) {
Act1State* state = (Act1State*) GameState()->GetState("Act1State");
state->SetUnknown18(0);
if (CurrentActor()->GetActorId() != GameState()->GetActorId()) {
CurrentActor()->VTable0xe4();
}
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->SetDestLocation(LegoGameState::Area::e_jetskibuild);
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
return 1;
}

View File

@ -30,7 +30,7 @@ MxLong PoliceEntity::VTable0x50(MxParam& p_param)
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->SetDestLocation(LegoGameState::Area::e_police);
AnimationManager()->FUN_10061010(NULL);
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
}

View File

@ -1,3 +1,36 @@
#include "racestandsentity.h"
#include "act1state.h"
#include "isle.h"
#include "isle_actions.h"
#include "islepathactor.h"
#include "legoanimationmanager.h"
#include "legogamestate.h"
#include "legoomni.h"
#include "legoutils.h"
#include "legoworld.h"
#include "misc.h"
#include "mxtransitionmanager.h"
DECOMP_SIZE_ASSERT(RaceStandsEntity, 0x68)
// FUNCTION: LEGO1 0x10015450
MxLong RaceStandsEntity::VTable0x50(MxParam& p_param)
{
if (FUN_1003ef60()) {
Act1State* state = (Act1State*) GameState()->GetState("Act1State");
state->SetUnknown18(0);
if (CurrentActor()->GetActorId() != GameState()->GetActorId()) {
CurrentActor()->VTable0xe4();
}
Isle* isle = (Isle*) FindWorld(*g_isleScript, IsleScript::c__Isle);
isle->SetDestLocation(LegoGameState::Area::e_racecarbuild);
AnimationManager()->FUN_10061010(0);
TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE);
}
return 1;
}

View File

@ -115,7 +115,7 @@ LegoChar* LegoAnimPresenter::FUN_10069150(const LegoChar* p_und1)
{
LegoChar* str;
if (LegoCharacterManager::FUN_10084c00(p_und1 + 1)) {
if (LegoCharacterManager::Exists(p_und1 + 1)) {
str = new LegoChar[strlen(p_und1)];
if (str != NULL) {

View File

@ -399,11 +399,11 @@ MxLong Isle::HandleClick(LegoControlManagerEvent& p_param)
FUN_10031590();
break;
case IsleScript::c_Observe_GlobeLArrow_Ctl:
FUN_1003f050(-1);
UpdateLightPosition(-1);
FUN_10031590();
break;
case IsleScript::c_Observe_GlobeRArrow_Ctl:
FUN_1003f050(1);
UpdateLightPosition(1);
FUN_10031590();
break;
case IsleScript::c_Observe_Draw1_Ctl:
@ -1115,3 +1115,9 @@ MxBool Isle::VTable0x64()
// TODO
return FALSE;
}
// STUB: LEGO1 0x10033350
void Isle::FUN_10033350()
{
// TODO
}

View File

@ -182,3 +182,45 @@ void LegoView1::Destroy()
LegoView::Destroy();
}
// FUNCTION: LEGO1 0x100abb60
void LegoView1::SetLightTransform(BOOL bDirectionalLight, Tgl::FloatMatrix4& rMatrix)
{
Tgl::Light* pLight;
if (bDirectionalLight == FALSE) {
pLight = m_pSunLight;
}
else {
pLight = m_pDirectionalLight;
}
SetLightTransform(pLight, rMatrix);
}
// FUNCTION: LEGO1 0x100abb80
void LegoView1::SetLightTransform(Tgl::Light* pLight, Tgl::FloatMatrix4& rMatrix)
{
pLight->SetTransformation(rMatrix);
}
// FUNCTION: LEGO1 0x100abba0
void LegoView1::SetLightColor(BOOL bDirectionalLight, float red, float green, float blue)
{
Tgl::Light* pLight;
if (bDirectionalLight == FALSE) {
pLight = m_pSunLight;
}
else {
pLight = m_pDirectionalLight;
}
SetLightColor(pLight, red, green, blue);
}
// FUNCTION: LEGO1 0x100abbd0
void LegoView1::SetLightColor(Tgl::Light* pLight, float red, float green, float blue)
{
pLight->SetColor(red, green, blue);
}

View File

@ -8,7 +8,8 @@
namespace Tgl
{
class Camera;
}
class Light;
} // namespace Tgl
/////////////////////////////////////////////////////////////////////////////
// LegoView
@ -64,7 +65,13 @@ class LegoView1 : public LegoView {
BOOL Create(const TglSurface::CreateStruct&, Tgl::Renderer*);
void Destroy() override; // vtable+0x08
void SetLightTransform(BOOL bDirectionalLight, Tgl::FloatMatrix4& rMatrix);
void SetLightColor(BOOL bDirectionalLight, float red, float green, float blue);
private:
void SetLightTransform(Tgl::Light* pLight, Tgl::FloatMatrix4& rMatrix);
void SetLightColor(Tgl::Light* pLight, float red, float green, float blue);
Tgl::Light* m_pSunLight; // 0x78
Tgl::Light* m_pDirectionalLight; // 0x7c
Tgl::Light* m_pAmbientLight; // 0x80

View File

@ -23,9 +23,6 @@ typedef struct {
int m_alpha;
} ROIColorAlias;
// GLOBAL: LEGO1 0x100dbe28
const double g_normalizeByteToFloat = 1.0 / 255;
// GLOBAL: LEGO1 0x101011b0
ROIColorAlias g_roiColorAliases[22] = {
{"lego black", 0x21, 0x21, 0x21, 0}, {"lego black f", 0x21, 0x21, 0x21, 0},
@ -529,19 +526,15 @@ LegoBool LegoROI::FUN_100a9bf0(const LegoChar* p_param, float& p_red, float& p_g
// FUNCTION: LEGO1 0x100a9c50
LegoBool LegoROI::ColorAliasLookup(const LegoChar* p_param, float& p_red, float& p_green, float& p_blue, float& p_alpha)
{
// TODO: this seems awfully hacky for these devs. is there a dynamic way
// to represent `the end of this array` that would improve this?
unsigned int i = 0;
do {
for (MxU32 i = 0; i < _countof(g_roiColorAliases); i++) {
if (strcmpi(g_roiColorAliases[i].m_name, p_param) == 0) {
p_red = g_roiColorAliases[i].m_red * g_normalizeByteToFloat;
p_green = g_roiColorAliases[i].m_green * g_normalizeByteToFloat;
p_blue = g_roiColorAliases[i].m_blue * g_normalizeByteToFloat;
p_alpha = g_roiColorAliases[i].m_alpha * g_normalizeByteToFloat;
p_red = g_roiColorAliases[i].m_red / 255.0;
p_green = g_roiColorAliases[i].m_green / 255.0;
p_blue = g_roiColorAliases[i].m_blue / 255.0;
p_alpha = g_roiColorAliases[i].m_alpha / 255.0;
return TRUE;
}
i++;
} while ((int*) &g_roiColorAliases[i] < &g_roiConfig);
}
return FALSE;
}

View File

@ -47,6 +47,13 @@ def parse_args() -> argparse.Namespace:
parser.add_argument(
"--no-color", "-n", action="store_true", help="Do not color the output"
)
parser.add_argument(
"--all",
"-a",
dest="show_all",
action="store_true",
help="Only show variables with a problem",
)
parser.add_argument(
"--print-rec-addr",
action="store_true",
@ -236,7 +243,7 @@ def do_the_comparison(args: argparse.Namespace) -> Iterable[ComparisonItem]:
# If we are here, we can do the type-aware comparison.
compared = []
compare_items = mini_cvdump.types.get_scalars(type_name)
compare_items = mini_cvdump.types.get_scalars_gapless(type_name)
format_str = mini_cvdump.types.get_format_string(type_name)
orig_data = unpack(format_str, orig_raw)
@ -308,8 +315,15 @@ def display_match(result: CompareResult) -> str:
)
return f"{match_color}{result.name}{colorama.Style.RESET_ALL}"
var_count = 0
problems = 0
for item in do_the_comparison(args):
if not args.verbose and item.result == CompareResult.MATCH:
var_count += 1
if item.result in (CompareResult.DIFF, CompareResult.ERROR):
problems += 1
if not args.show_all and item.result == CompareResult.MATCH:
continue
address_display = (
@ -334,8 +348,14 @@ def display_match(result: CompareResult) -> str:
f" {c.offset:5} {value_get(c.name, '(value)'):30} {value_a} : {value_b}"
)
print()
if args.verbose:
print()
print(
f"{os.path.basename(args.original)} - Variables: {var_count}. Issues: {problems}"
)
return 0 if problems == 0 else 1
if __name__ == "__main__":
main()
raise SystemExit(main())

View File

@ -69,14 +69,16 @@ def float_replace(self, addr: int, data_size: int) -> Optional[str]:
return None
def lookup(self, addr: int) -> Optional[str]:
def lookup(self, addr: int, use_cache: bool = True) -> Optional[str]:
"""Return a replacement name for this address if we find one."""
if (cached := self.replacements.get(addr, None)) is not None:
if use_cache and (cached := self.replacements.get(addr, None)) is not None:
return cached
if callable(self.name_lookup):
if (name := self.name_lookup(addr)) is not None:
self.replacements[addr] = name
if use_cache:
self.replacements[addr] = name
return name
return None
@ -110,6 +112,16 @@ def hex_replace_relocated(self, match: re.Match) -> str:
return match.group(0)
def hex_replace_annotated(self, match: re.Match) -> str:
"""For replacing immediate value operands. Here we replace the value
only if the name lookup returns something. Do not use a placeholder."""
value = int(match.group(1), 16)
placeholder = self.lookup(value, use_cache=False)
if placeholder is not None:
return match.group(0).replace(match.group(1), placeholder)
return match.group(0)
def hex_replace_float(self, match: re.Match) -> str:
"""Special case for replacements on float instructions.
If the pointer is a float constant, read it from the binary."""
@ -178,7 +190,13 @@ def sanitize(self, inst: DisasmLiteInst) -> Tuple[str, str]:
# vtable call, or this->member access.
op_str = displace_replace_regex.sub(self.hex_replace_relocated, op_str)
op_str = immediate_replace_regex.sub(self.hex_replace_relocated, op_str)
# In the event of pointer comparison, only replace the immediate value
# if it is a known address.
if inst.mnemonic == "cmp":
op_str = immediate_replace_regex.sub(self.hex_replace_annotated, op_str)
else:
op_str = immediate_replace_regex.sub(self.hex_replace_relocated, op_str)
return (inst.mnemonic, op_str)
def parse_asm(self, data: bytes, start_addr: Optional[int] = 0) -> List[str]:

View File

@ -1,5 +1,5 @@
import re
from typing import Dict, Iterator, List, NamedTuple, Optional
from typing import Dict, List, NamedTuple, Optional
class CvdumpTypeError(Exception):
@ -109,38 +109,10 @@ def scalar_type_format_char(type_name: str) -> str:
return char if scalar_type_signed(type_name) else char.upper()
def member_string_iter(
members: List[ScalarType], size: Optional[int] = None
) -> Iterator[str]:
if len(members) == 0:
yield "x" * (size or 0)
def member_list_to_struct_string(members: List[ScalarType]) -> str:
"""Create a string for use with struct.unpack"""
last_offset = 0
last_size = 0
for m in members:
padding = m.offset - last_offset - last_size
if padding > 0:
yield "x" * padding
yield m.format_char
last_offset = m.offset
last_size = m.size
if size is not None:
padding = size - (last_offset + last_size)
if padding > 0:
yield "x" * padding
def member_list_to_struct_string(
members: List[ScalarType], size: Optional[int] = None
) -> str:
"""Create a string for use with struct.unpack
Will pad to `size` bytes if present."""
if len(members) == 0:
return "x" * (size or 0)
format_string = "".join(list(member_string_iter(members, size)))
format_string = "".join(m.format_char for m in members)
if len(format_string) > 0:
return "<" + format_string
@ -372,11 +344,43 @@ def get_scalars(self, type_key: str) -> List[ScalarType]:
for cm in self.get_scalars(m.type)
]
def get_format_string(self, type_key: str) -> str:
def get_scalars_gapless(self, type_key: str) -> List[ScalarType]:
"""Reduce the given type to a list of scalars so we can
compare each component value."""
obj = self.get(type_key)
members = self.get_scalars(type_key)
# We need both to pad the data to size
return member_list_to_struct_string(members, obj.size)
total_size = obj.size
scalars = self.get_scalars(type_key)
output = []
last_extent = total_size
# Walk the scalar list in reverse; we assume a gap could not
# come at the start of the struct.
for scalar in scalars[::-1]:
this_extent = scalar.offset + scalar_type_size(scalar.type)
size_diff = last_extent - this_extent
# We need to add the gap fillers in reverse here
for i in range(size_diff - 1, -1, -1):
# Push to front
output.insert(
0,
ScalarType(
offset=this_extent + i,
name="(padding)",
type="T_UCHAR",
),
)
output.insert(0, scalar)
last_extent = scalar.offset
return output
def get_format_string(self, type_key: str) -> str:
members = self.get_scalars_gapless(type_key)
return member_list_to_struct_string(members)
def read_line(self, line: str):
if (match := self.INDEX_RE.match(line)) is not None:

View File

@ -313,14 +313,27 @@ def test_struct(parser):
def test_struct_padding(parser):
"""Struct format string should insert padding characters 'x'
where a value is padded to alignment size (probably 4 bytes)"""
"""For data comparison purposes, make sure we have no gaps in the
list of scalar types. Any gap is filled by an unsigned char."""
# MxString, padded to 16 bytes. 4 actual members. 2 bytes of padding.
assert len(parser.get_scalars("0x4db6")) == 4
assert len(parser.get_scalars_gapless("0x4db6")) == 6
# MxVariable, with two MxStrings (and a vtable)
# Fill in the middle gap and the outer gap.
assert len(parser.get_scalars("0x22d5")) == 9
assert len(parser.get_scalars_gapless("0x22d5")) == 13
def test_struct_format_string(parser):
"""Generate the struct.unpack format string using the
list of scalars with padding filled in."""
# MxString, padded to 16 bytes.
assert parser.get_format_string("0x4db6") == "<LLLHxx"
assert parser.get_format_string("0x4db6") == "<LLLHBB"
# MxVariable, with two MxString members.
assert parser.get_format_string("0x22d5") == "<LLLLHxxLLLHxx"
assert parser.get_format_string("0x22d5") == "<LLLLHBBLLLHBB"
def test_array(parser):

View File

@ -221,3 +221,38 @@ def substitute_float(_: int, __: int) -> str:
inst = DisasmLiteInst(0x1000, 6, "fld", "dword ptr [0x1234]")
(_, op_str) = p.sanitize(inst)
assert op_str == "dword ptr [g_myFloatVariable]"
def test_pointer_compare():
"""A loop on an array could get optimized into comparing on the address
that immediately follows the array. This may or may not be a valid address
and it may or may not be annotated. To avoid a situation where an
erroneous address value would get replaced with a placeholder and silently
pass the comparison check, we will only replace an immediate value on the
CMP instruction if it is a known address."""
# 0x1234 and 0x5555 are relocated and so are considered to be addresses.
def relocate_lookup(addr: int) -> bool:
return addr in (0x1234, 0x5555)
# Only 0x5555 is a "known" address
def name_lookup(addr: int) -> Optional[str]:
return "hello" if addr == 0x5555 else None
p = ParseAsm(relocate_lookup=relocate_lookup, name_lookup=name_lookup)
# Will always replace on MOV instruction
(_, op_str) = p.sanitize(mock_inst("mov", "eax, 0x1234"))
assert op_str == "eax, <OFFSET1>"
(_, op_str) = p.sanitize(mock_inst("mov", "eax, 0x5555"))
assert op_str == "eax, hello"
# n.b. We have already cached the replacement for 0x1234, but the
# special handling for CMP should skip the cache and not use it.
# Do not replace here
(_, op_str) = p.sanitize(mock_inst("cmp", "eax, 0x1234"))
assert op_str == "eax, 0x1234"
# Should replace here
(_, op_str) = p.sanitize(mock_inst("cmp", "eax, 0x5555"))
assert op_str == "eax, hello"

View File

@ -107,7 +107,7 @@ def print_match_verbose(match, show_both_addrs: bool = False, is_plain: bool = F
print(f"{addrs}: {match.name} 100% match.\n\n{ok_text}\n\n")
else:
print(
f"{addrs}: {match.name} Effective 100%% match. (Differs in register allocation only)\n\n{ok_text} (still differs in register allocation)\n\n"
f"{addrs}: {match.name} Effective 100% match. (Differs in register allocation only)\n\n{ok_text} (still differs in register allocation)\n\n"
)
else:
print_combined_diff(match.udiff, is_plain, show_both_addrs)