Поскольку я начал писать свой экстернал под эту увлекательную игру, мне потребовался так называемый "World To Screen", который оказалось не так просто сделать в этой уникальной игре. Было решено ребилднуть W2S функу игры.
Собственно почему бы не поделиться с другими, поскольку ничего особенного в этой базовой функции нет.
Ребилд произведен с помощью так называемой "IDA Pro 8.3" и имеющимися данными в виде дампа ласт версии и PDB 2019.
Непосредственно ProjectToScreenInternal был ребилднут с ласт дампа, mathVec3Project же я украл с PDB 2019, поскольку ребилд с ласт версии не захотел работать (ну и ладно).
Ко всем функциям и оффсетам прилагается сигнатура по которой будет очень просто обновиться в будущем.
Потребуется класс CRenderer, а точнее некоторые переменные из него - матрица, и вьюпорт.
Также структура матрицы и PTS.
Псевдо-коды функций.
Ребилд функций.
Ну и сам World To Screen.
Отмечу, что это просто ребилд, и код можно(и я бы сказал нужно) привести в более презентабельный вид.
Этот ребилд также можно использовать для интернал софтов, если есть такая необходимость, но я делал под свой экстернал.
Собственно почему бы не поделиться с другими, поскольку ничего особенного в этой базовой функции нет.
Ребилд произведен с помощью так называемой "IDA Pro 8.3" и имеющимися данными в виде дампа ласт версии и PDB 2019.
Непосредственно ProjectToScreenInternal был ребилднут с ласт дампа, mathVec3Project же я украл с PDB 2019, поскольку ребилд с ласт версии не захотел работать (ну и ладно).
Ко всем функциям и оффсетам прилагается сигнатура по которой будет очень просто обновиться в будущем.
Потребуется класс CRenderer, а точнее некоторые переменные из него - матрица, и вьюпорт.
C++:
struct viewport_t {
int x, y, z, w;
};
class c_renderer {
public:
OFFSET(viewport_t, m_pViewPort(), 0x14B718); // BB 18 B7 14 00
public:
/*
* this->m_RP.m_TI[m_nCurThreadProcess].m_matView/m_matProj
* @note: m_nCurThreadProcess is always 0, looks like anti-paste idk
* 48 69 C8 28 03 00 00 0F 29 4C 24 70
*/
cryengine_matrix4x4_t m_matViewTop() {
auto m_matView =
core->proc_read<std::uintptr_t>(core->proc_read<std::uintptr_t>(
reinterpret_cast<std::uintptr_t>(this) + 0x328 + 0xF38));
return core->proc_read<cryengine_matrix4x4_t>(m_matView); // m_matView->m_pTop
}
cryengine_matrix4x4_t m_matProjTop() {
auto m_matProj =
core->proc_read<std::uintptr_t>(core->proc_read<std::uintptr_t>(
reinterpret_cast<std::uintptr_t>(this) + 0x328 + 0xF40));
return core->proc_read<cryengine_matrix4x4_t>(m_matProj); // m_matProj->m_pTop
}
};
C++:
struct cryengine_matrix4x4_t {
float m00, m01, m02, m03;
float m10, m11, m12, m13;
float m20, m21, m22, m23;
float m30, m31, m32, m33;
cryengine_matrix4x4_t() = default;
void set_identity() {
m00 = 1;
m01 = 0;
m02 = 0;
m03 = 0;
m10 = 0;
m11 = 1;
m12 = 0;
m13 = 0;
m20 = 0;
m21 = 0;
m22 = 1;
m23 = 0;
m30 = 0;
m31 = 0;
m32 = 0;
m33 = 1;
}
void sanitaze() {
if (!std::isfinite(m00))
m00 = 0.f;
if (!std::isfinite(m01))
m01 = 0.f;
if (!std::isfinite(m02))
m02 = 0.f;
if (!std::isfinite(m03))
m03 = 0.f;
if (!std::isfinite(m10))
m10 = 0.f;
if (!std::isfinite(m11))
m11 = 0.f;
if (!std::isfinite(m12))
m12 = 0.f;
if (!std::isfinite(m13))
m13 = 0.f;
if (!std::isfinite(m20))
m20 = 0.f;
if (!std::isfinite(m21))
m21 = 0.f;
if (!std::isfinite(m22))
m22 = 0.f;
if (!std::isfinite(m23))
m23 = 0.f;
if (!std::isfinite(m30))
m30 = 0.f;
if (!std::isfinite(m31))
m31 = 0.f;
if (!std::isfinite(m32))
m32 = 0.f;
if (!std::isfinite(m33))
m33 = 0.f;
}
};
struct project_to_screen_t {
float ptx, pty, ptz; // project_to_N
float sx, sy, sz; // screen_N
};
Псевдо-коды функций.
C++:
char __fastcall ProjectToScreenInternal(__int64 a1, __int64 a2)
{
_DWORD *v2; // rbx
int v5; // ebp
__int64 v6; // rbx
DWORD CurrentThreadId; // eax
bool v8; // al
int v9; // xmm1_4
int v10; // xmm0_4
__int64 v11; // rbx
int *v12; // rbx
int v13; // r14d
float *v14; // rax
int v16[4]; // [rsp+30h] [rbp-78h] BYREF
int v17[4]; // [rsp+40h] [rbp-68h] BYREF
int v18[4]; // [rsp+50h] [rbp-58h] BYREF
__int128 v19[4]; // [rsp+60h] [rbp-48h] BYREF
v2 = *(_DWORD **)(a1 + 0x14B048);
if ( GetCurrentThreadId() == v2[0x36] )
v5 = v2[0xC];
else
v5 = v2[0xD];
v6 = *(_QWORD *)(a1 + 0x14B048);
CurrentThreadId = GetCurrentThreadId();
v8 = CurrentThreadId == *(_DWORD *)(v6 + 0xDC) || CurrentThreadId == *(_DWORD *)(v6 + 0xD8);
v9 = *(_DWORD *)(a2 + 4);
v16[0] = *(_DWORD *)a2;
v10 = *(_DWORD *)(a2 + 8);
v11 = 0x14B718i64;
if ( !v8 )
v11 = 0x14B6D0i64;
v16[1] = v9;
v12 = (int *)(a1 + v11);
v16[2] = v10;
v19[0] = xmmword_141E74080;
v13 = v12[2];
v18[0] = *v12;
v18[1] = v12[1];
v18[3] = v12[3];
v19[1] = xmmword_141E7EA80;
v19[2] = xmmword_141E7EA90;
v19[3] = xmmword_141E74100;
v18[2] = v13;
v14 = mathVec3Project(
(float *)v17,
(float *)v16,
v18,
**(float ***)(0x328i64 * v5 + a1 + 0xF40),
**(float ***)(0x328i64 * v5 + a1 + 0xF38),
(float *)v19);
if ( v14 )
{
**(float **)(a2 + 0x10) = (float)(*(float *)v17 * *(float *)&dword_141E7404C) / (float)v13;
**(float **)(a2 + 0x18) = (float)(*(float *)&v17[1] * *(float *)&dword_141E7404C) / (float)v12[3];
**(_DWORD **)(a2 + 0x20) = v17[2];
LOBYTE(v14) = 1;
}
return (char)v14;
}
C++:
Vec3_tpl<float> *__fastcall mathVec3Project(
Vec3_tpl<float> *pvWin,
const Vec3_tpl<float> *pvObj,
const int *pViewport,
const Matrix44_tpl<float,XMVec4A> *pProjection,
const Matrix44_tpl<float,XMVec4A> *pView,
const Matrix44_tpl<float,XMVec4A> *pWorld)
{
float v6; // xmm7_4
float v7; // xmm8_4
float v8; // xmm3_4
float v9; // xmm2_4
float v10; // xmm7_4
float v11; // xmm4_4
float v12; // xmm5_4
float v13; // xmm6_4
float v14; // xmm8_4
Vec3_tpl<float> *result; // rax
float v16; // xmm0_4
float v17; // xmm2_4
v6 = (float)((float)((float)(pvObj->y * pWorld->m11) + (float)(pvObj->x * pWorld->m01))
+ (float)(pvObj->z * pWorld->m21))
+ pWorld->m31;
v7 = (float)((float)((float)(v6 * pView->m10)
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
+ (float)(pvObj->z * pWorld->m20))
+ pWorld->m30)
* pView->m00))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
+ (float)(pvObj->z * pWorld->m22))
+ pWorld->m32)
* pView->m20))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
+ (float)(pvObj->z * pWorld->m23))
+ pWorld->m33)
* pView->m30);
v8 = (float)((float)((float)(v6 * pView->m11)
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
+ (float)(pvObj->z * pWorld->m20))
+ pWorld->m30)
* pView->m01))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
+ (float)(pvObj->z * pWorld->m22))
+ pWorld->m32)
* pView->m21))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
+ (float)(pvObj->z * pWorld->m23))
+ pWorld->m33)
* pView->m31);
v9 = (float)((float)((float)(v6 * pView->m12)
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
+ (float)(pvObj->z * pWorld->m20))
+ pWorld->m30)
* pView->m02))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
+ (float)(pvObj->z * pWorld->m22))
+ pWorld->m32)
* pView->m22))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
+ (float)(pvObj->z * pWorld->m23))
+ pWorld->m33)
* pView->m32);
v10 = (float)((float)((float)(v6 * pView->m13)
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m10) + (float)(pvObj->x * pWorld->m00))
+ (float)(pvObj->z * pWorld->m20))
+ pWorld->m30)
* pView->m03))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m12) + (float)(pvObj->x * pWorld->m02))
+ (float)(pvObj->z * pWorld->m22))
+ pWorld->m32)
* pView->m23))
+ (float)((float)((float)((float)((float)(pvObj->y * pWorld->m13) + (float)(pvObj->x * pWorld->m03))
+ (float)(pvObj->z * pWorld->m23))
+ pWorld->m33)
* pView->m33);
v11 = (float)((float)((float)(v8 * pProjection->m10) + (float)(v7 * pProjection->m00)) + (float)(v9 * pProjection->m20))
+ (float)(v10 * pProjection->m30);
v12 = (float)((float)((float)(v8 * pProjection->m11) + (float)(v7 * pProjection->m01)) + (float)(v9 * pProjection->m21))
+ (float)(v10 * pProjection->m31);
v13 = (float)((float)((float)(v7 * pProjection->m02) + (float)(v8 * pProjection->m12)) + (float)(v9 * pProjection->m22))
+ (float)(v10 * pProjection->m32);
v14 = (float)((float)((float)(v7 * pProjection->m03) + (float)(v8 * pProjection->m13)) + (float)(v9 * pProjection->m23))
+ (float)(v10 * pProjection->m33);
if ( v14 == 0.0 )
return 0i64;
result = pvWin;
pvWin->x = (float)((float)((float)((float)(v11 / v14) + 1.0) * (float)pViewport[2]) * 0.5) + (float)*pViewport;
v16 = (float)pViewport[1];
v17 = (float)((float)(1.0 - (float)(v12 / v14)) * (float)pViewport[3]) * 0.5;
pvWin->z = v13 / v14;
pvWin->y = v17 + v16;
return result;
}
Ребилд функций.
C++:
bool ProjectToScreenInternal_Rebuild(i_renderer *_this,
project_to_screen_t &proj) {
// @xref: 48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 56 48
// 81 EC A0 00 00 00 48 8B 99
int pViewport[4]; // [rsp+50h] [rbp-58h] BYREF
fw::vec3_t pvWin{};
fw::vec3_t pvObj = {proj.ptx, proj.pty, proj.ptz};
auto m_pViewPort = _this->m_pViewPort();
pViewport[0] = m_pViewPort.x;
pViewport[1] = m_pViewPort.y;
pViewport[2] = m_pViewPort.z;
pViewport[3] = m_pViewPort.w;
auto m_matView = _this->m_matViewTop();
auto m_matProj = _this->m_matProjTop();
cryengine_matrix4x4_t pWorld{};
m_matView.sanitaze();
m_matProj.sanitaze();
pWorld.set_identity();
fw::vec3_t result = mathVec3Project_Rebuild(pvWin, pvObj, pViewport, m_matProj,
m_matView, pWorld);
if (!result.empty()) {
proj.sx = pvWin.x * 100.f / pViewport[2];
proj.sy = pvWin.y * 100.f / pViewport[3];
proj.sz = pvWin.z;
}
return !result.empty();
}
C++:
fw::vec3_t mathVec3Project_Rebuild(fw::vec3_t &pvWin, const fw::vec3_t &pvObj,
const int pViewport[4],
const cryengine_matrix4x4_t &pProjection,
const cryengine_matrix4x4_t &pView,
const cryengine_matrix4x4_t &pWorld) {
// @xref: 48 83 EC 38 48 8B 44 24 68
float v6; // xmm7_4
float v7; // xmm8_4
float v8; // xmm3_4
float v9; // xmm2_4
float v10; // xmm7_4
float v11; // xmm4_4
float v12; // xmm5_4
float v13; // xmm6_4
float v14; // xmm8_4
/*
* matrix calculation
* i don't understand how to normal reverse this shit, so i just pasted this
* magic pseudocode :/
*/
{
v6 = pvObj.y * pWorld.m11 + pvObj.x * pWorld.m01 + pvObj.z * pWorld.m21 +
pWorld.m31;
v7 = v6 * pView.m10 +
(pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
pWorld.m30) *
pView.m00 +
(pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
pWorld.m32) *
pView.m20 +
(pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
pWorld.m33) *
pView.m30;
v8 = v6 * pView.m11 +
(pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
pWorld.m30) *
pView.m01 +
(pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
pWorld.m32) *
pView.m21 +
(pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
pWorld.m33) *
pView.m31;
v9 = v6 * pView.m12 +
(pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
pWorld.m30) *
pView.m02 +
(pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
pWorld.m32) *
pView.m22 +
(pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
pWorld.m33) *
pView.m32;
v10 = v6 * pView.m13 +
(pvObj.y * pWorld.m10 + pvObj.x * pWorld.m00 + pvObj.z * pWorld.m20 +
pWorld.m30) *
pView.m03 +
(pvObj.y * pWorld.m12 + pvObj.x * pWorld.m02 + pvObj.z * pWorld.m22 +
pWorld.m32) *
pView.m23 +
(pvObj.y * pWorld.m13 + pvObj.x * pWorld.m03 + pvObj.z * pWorld.m23 +
pWorld.m33) *
pView.m33;
v11 = v8 * pProjection.m10 + v7 * pProjection.m00 + v9 * pProjection.m20 +
v10 * pProjection.m30;
v12 = v8 * pProjection.m11 + v7 * pProjection.m01 + v9 * pProjection.m21 +
v10 * pProjection.m31;
v13 = v7 * pProjection.m02 + v8 * pProjection.m12 + v9 * pProjection.m22 +
v10 * pProjection.m32;
v14 = v7 * pProjection.m03 + v8 * pProjection.m13 + v9 * pProjection.m23 +
v10 * pProjection.m33;
}
if (v14 == 0.f)
return {};
pvWin.x = ((v11 / v14) + 1.f) * (static_cast<float>(pViewport[2]) * .5f) +
static_cast<float>(pViewport[0]);
pvWin.y = ((1.f - (v12 / v14)) * static_cast<float>(pViewport[3]) * .5f) +
static_cast<float>(pViewport[1]);
pvWin.z = v13 / v14;
return pvWin;
}
Ну и сам World To Screen.
C++:
bool world_to_screen(const fw::vec3_t &in, fw::vec3_t &out) {
/*
* here you need to get the address of SSystemGlobalEnvironment structure and screen_size
*/
{
/* removed */
}
project_to_screen_t pts{in.x, in.y, in.z, out.x, out.y, out.z};
if (!ProjectToScreenInternal_Rebuild(sge->pRenderer(), pts))
return false;
if (pts.sz > 1.f) // behind camera
return false;
out.x = pts.sx * (screen_size.x / 100.f);
out.y = pts.sy * (screen_size.y / 100.f);
return true;
}
Отмечу, что это просто ребилд, и код можно(и я бы сказал нужно) привести в более презентабельный вид.
Этот ребилд также можно использовать для интернал софтов, если есть такая необходимость, но я делал под свой экстернал.
Последнее редактирование: