-
Notifications
You must be signed in to change notification settings - Fork 32
/
ModExpansion.cpp
177 lines (149 loc) · 7.38 KB
/
ModExpansion.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <iostream>
#include "dllmain.h"
#include "Patches.h"
#include "Game.h"
#include "Settings.h"
void SetEmListParamExtensions(cEm* em, EM_LIST* emData)
{
// Game sometimes uses hardcoded stack-based EM_LIST to spawn certain enemies
// In those cases, 0xA & 0x1C usually are left untouched, resulting in random stack data being used
// To protect against that, we'll only allow param extensions from emData that came from pG->emList_5410
GLOBAL_WK* pG = GlobalPtr();
if (emData < pG->Em_list_5410 || emData >= &pG->Em_list_5410[256])
return;
if ((emData->be_flag_0 & EM_BE_FLAG_MODEXP_SPEEDSCALE) != EM_BE_FLAG_MODEXP_SPEEDSCALE)
return;
if (!emData->percentageMotionSpeed_1C && !emData->percentageScale_1E)
return;
// let it run the cEm's EmXX_R0_init function before we apply changes
// (since that has a good chance of overwriting scale value)
if (em->r_no_0_FC == uint8_t(cEm::Routine0::Init))
em->move();
if (emData->percentageMotionSpeed_1C)
em->Motion_1D8.Seq_speed_C0 = float(double(emData->percentageMotionSpeed_1C) / 100.0f);
if (emData->percentageScale_1E)
{
auto scalePercent = float(double(emData->percentageScale_1E) / 100.0f);
em->scale_AC.x *= scalePercent;
em->scale_AC.y *= scalePercent;
em->scale_AC.z *= scalePercent;
if (IsGanado(em->id_100))
{
// cEm10 holds a seperate scale value that it seems to grow/shrink the actual scale towards each frame
// make sure we update that as well
cEm10* em10 = (cEm10*)em;
em10->Scale_498 = em->scale_AC;
}
}
}
cEm* (__cdecl* EmSetEvent)(EM_LIST* emData);
cEm* __cdecl EmSetEvent_Hook(EM_LIST* emData)
{
cEm* result = EmSetEvent(emData);
// TODO: check if result == errEm
SetEmListParamExtensions(result, emData);
return result;
}
cEm* (__cdecl* EmSetFromList2)(unsigned int em_list_no, int a2);
cEm* __cdecl EmSetFromList2_Hook(unsigned int em_list_no, int a2)
{
cEm* result = EmSetFromList2(em_list_no, a2);
SetEmListParamExtensions(result, &GlobalPtr()->Em_list_5410[em_list_no]);
return result;
}
void __fastcall EmSetFromList_Hook(cEm* em, uint32_t unused)
{
SetEmListParamExtensions(em, &GlobalPtr()->Em_list_5410[em->emListIndex_3A0]);
em->move(); // code we patched to jump here
}
void __declspec(naked) EmSetFromList_Hook_Trampoline()
{
__asm
{
mov ecx, eax
jmp EmSetFromList_Hook
}
}
BOOL (__fastcall* cEtcTbl__RegistData)(void* thisptr, void* unused, ETS_DATA* pEts, cEm* pPtr);
BOOL __fastcall cEtcTbl__RegistData_Hook(void* thisptr, void* unused, ETS_DATA* pEts, cEm* pPtr)
{
BOOL ret = cEtcTbl__RegistData(thisptr, unused, pEts, pPtr);
// update cEm scale value from the ETS data
// (why doesn't game do this already? ETS data already has a field specifically for scale...)
if (ret && pPtr)
{
Vec scale_model = pEts->scale_4;
Vec scale_collision = scale_model;
// Unfortunately some vanilla ETS entries have bad scale values set for some reason, even though game doesn't actually use them
// Those values usually don't make sense, if our code applies them to the model then it usually won't have a good result
// So we'll check for flag 0x40 at offset 0x3 into the entry first before applying it
if ((pEts->expansion_Flag_3 & 0x40) == 0x40 || re4t::cfg->bForceETSApplyScale)
{
// Check if "short mode" flag is set
// If it is, the 12 bytes for scale are treated as two 6-byte "short vectors"
// First vector controls the scale of model, second one controls collision scale
// Both "short vector" values are treated as percentages, eg. setting to 200 will scale by 2x
// If the flag isn't set then both model & collision are scaled by the normal 12-byte scale vector
// (short mode requires both flag 0x40 and 0x80 to be set at offset 0x3, so 0x3 should be set to 0xC0)
if ((pEts->expansion_Flag_3 & 0x80) == 0x80)
{
scale_model.x = float(double(pEts->expansion_PercentScaleModel_4.x) / 100.0f);
scale_model.y = float(double(pEts->expansion_PercentScaleModel_4.y) / 100.0f);
scale_model.z = float(double(pEts->expansion_PercentScaleModel_4.z) / 100.0f);
scale_collision.x = float(double(pEts->expansion_PercentScaleCollision_A.x) / 100.0f);
scale_collision.y = float(double(pEts->expansion_PercentScaleCollision_A.y) / 100.0f);
scale_collision.z = float(double(pEts->expansion_PercentScaleCollision_A.z) / 100.0f);
}
// Only apply the scales if they aren't all 0, or the expansion flag is set (showing that the values are intended)
// (otherwise ForceETSApplyScale could end up applying scale of 0 to things, since some ETS entry scales are 0 already)
if ((scale_model.x != 0 || scale_model.y != 0 || scale_model.z != 0) || (pEts->expansion_Flag_3 & 0x40) == 0x40)
pPtr->scale_AC = scale_model;
if ((scale_collision.x != 0 || scale_collision.y != 0 || scale_collision.z != 0) || (pEts->expansion_Flag_3 & 0x40) == 0x40)
{
pPtr->atari_2B4.m_height_14 *= scale_collision.z;
// Update "rs" values - radius [for] square?
pPtr->atari_2B4.m_radius_C *= scale_collision.x;
pPtr->atari_2B4.m_radius_n_1C *= scale_collision.x;
// Update "ro" values
pPtr->atari_2B4.m_radius2_10 *= scale_collision.x;
pPtr->atari_2B4.m_radius2_n_20 *= scale_collision.x;
// Update "ra" values
pPtr->atari_2B4.m_radius3_2C *= scale_collision.x;
// Some models collision seems to use an offset for some reason
// Multiplying against our scale mostly seems to fix it though
pPtr->atari_2B4.m_offset_0.x *= scale_collision.x;
pPtr->atari_2B4.m_offset_0.y *= scale_collision.y;
pPtr->atari_2B4.m_offset_0.z *= scale_collision.z;
}
// Remove flag in case game reads EtcModelId somewhere else...
pEts->expansion_Flag_3 = 0;
}
}
return ret;
}
void re4t::init::ModExpansion()
{
// Hook ESL-loading functions so we can add extended scale/speed parameters
auto pattern = hook::pattern("52 66 89 45 E4 66 89 4D F6 E8");
auto EmSetEvent_thunk = (uint8_t*)injector::GetBranchDestination(pattern.count(1).get(0).get<uint32_t>(9)).as_int();
ReadCall(EmSetEvent_thunk, EmSetEvent);
InjectHook(EmSetEvent_thunk, EmSetEvent_Hook, PATCH_JUMP);
pattern = hook::pattern("6A 01 6A 32 E8 ? ? ? ? 83 C4 20");
auto EmSetFromList2_thunk = (uint8_t*)injector::GetBranchDestination(pattern.count(1).get(0).get<uint32_t>(4)).as_int();
ReadCall(EmSetFromList2_thunk, EmSetFromList2);
InjectHook(EmSetFromList2_thunk, EmSetFromList2_Hook, PATCH_JUMP);
pattern = hook::pattern("D9 98 7C 03 00 00 8B 42 10");
auto EmSetFromList_mov = pattern.count(1).get(0).get<uint8_t>(6);
InjectHook(EmSetFromList_mov, EmSetFromList_Hook_Trampoline, PATCH_CALL);
// Hook ETS set function so we can update cEm scale value from the ETS data
// (why doesn't game do this already? ETS data already has a field specifically for scale...)
pattern = hook::pattern("0F B7 46 02 8D 0C 40 56 8D 0C 8D ? ? ? ? E8 ? ? ? ?");
auto cEtcTbl__RegistData_thunk = (uint8_t*)injector::GetBranchDestination(pattern.count(1).get(0).get<uint32_t>(0xF)).as_int();
ReadCall(cEtcTbl__RegistData_thunk, cEtcTbl__RegistData);
InjectHook(cEtcTbl__RegistData_thunk, cEtcTbl__RegistData_Hook, PATCH_JUMP);
// Patch ETS-loading func to read EtcModelNo_2 as byte
// which gives us a spare byte to use as a flag to change how scale values are read
// (game only allows EtcModelNo_2 values 0x40 and below anyway, so the byte was pretty much wasted...)
pattern = hook::pattern("51 0F B7 46 02");
Patch(pattern.count(1).get(0).get<uint8_t>(2), uint8_t(0xB6)); // change "movzx eax, word ptr" -> movzx eax, byte ptr"
}