I have been working on a Cameratool for League of Legends, and I wanted to share some of my explorations and maybe ask for some help.
To begin with the camera stuff, I searched for the x-axis-position of the camera by moving left and right and searching for increased and decreased float values in Cheat Engine.
I found the x-Axis and continued by trial and error, and I got this:
float xpos;
float ypos;
float zpos;
float readonly; //?
float Farclip; //standard 5000
float Rotation1; // 00 00 00 00
float Rotation2; /-0,8/0,8
float Rotation3; // 0,55/6
float Roll; //
float DoRender // 0,00 = dont draw
float readonly2;
float FOV; //standard 50
But Soon I realized that something was wrong with the Rotations, Yaw Pitch and Roll were not pi-values or degrees.
Also, important about this struct:
You cant just change the values since they are being set by a function that gets the "real" values from another place in the memory. So, you can either nop the relevant parts of the function that sets these values or you can find the "source" of the values.
So, I went looking for the source, here is the IDA-decompiled camera-setter-function:
Code:
int __thiscall setCamera7E8E20(void *this, int a2)
{
void *v2; // edi@1
int v3; // ecx@1
char v4; // cl@8
int v5; // eax@9
double v6; // st7@11
float v7; // ST10_4@11
double v8; // st7@11
float v9; // ST0C_4@11
float v10; // xmm5_4@11
float v11; // xmm1_4@11
float xCamPos; // xmm4_4@11
__m128 v13; // xmm0@11
float zCamPos; // xmm2_4@11
float yCamPos; // xmm3_4@11
bool v16; // cf@11
bool v17; // zf@11
float v18; // xmm0_4@11
double v19; // st7@13
int v20; // esi@15
float v21; // xmm1_4@15
double v22; // st7@15
float v23; // xmm2_4@15
float v24; // xmm0_4@15
double v25; // st7@16
char v27; // [sp+8h] [bp-4Ch]@0
int v28; // [sp+14h] [bp-40h]@11
int v29; // [sp+18h] [bp-3Ch]@11
int v30; // [sp+1Ch] [bp-38h]@11
float v31; // [sp+20h] [bp-34h]@11
float v32; // [sp+24h] [bp-30h]@11
float v33; // [sp+28h] [bp-2Ch]@11
float v34; // [sp+2Ch] [bp-28h]@11
float v35; // [sp+30h] [bp-24h]@11
float v36; // [sp+34h] [bp-20h]@11
float v37; // [sp+38h] [bp-1Ch]@11
float v38; // [sp+3Ch] [bp-18h]@11
float v39; // [sp+40h] [bp-14h]@11
float v40; // [sp+44h] [bp-10h]@12
float v41; // [sp+48h] [bp-Ch]@11
float v42; // [sp+4Ch] [bp-8h]@8
float camPosPointer; // [sp+5Ch] [bp+8h]@15
v2 = this;
v3 = dword_2E279F8;
if ( !dword_2E279F8 )
{
sub_9942D0(
(int)"(spMultiplayerClient != NULL)",
(int)"C:/jenkins/workspace/game-code-Releases-4-11-public-win32/code/Game/LoL/Main/Client/MainClient.cpp",
2073,
"NetAPI::GetClient",
(int)"NetAPI::GetClient: Client pointer not created yet!",
v27);
v3 = dword_2E279F8;
}
if ( *((float *)v2 + 66) < -999.0 && !dword_2E27BEC )
{
if ( (unsigned __int8)(*(int (**)(void))(*(_DWORD *)v3 + 176))() )
{
if ( dword_2E27B94 )
{
if ( *(_DWORD *)(dword_2E27B94 + 288) )
{
sub_59ECB0(&v42);
if ( v42 != 0.0 )
{
v5 = sub_5B94A0(v4, SLOBYTE(v42));
if ( v5 )
sub_7EA0C0(v5 + 100);
}
}
}
}
}
*((_DWORD *)v2 + 66) = 0;
sub_7EAE70(v2);
v31 = *((float *)v2 + 65);
v32 = *((float *)v2 + 66);
v33 = *((float *)v2 + 67);
v34 = *((float *)v2 + 65);
v35 = *((float *)v2 + 66);
v36 = *((float *)v2 + 67);
sub_7EC3A0((char *)v2 + 424);
v6 = (*((float *)v2 + 110) + *((float *)v2 + 110)) * 0.00066666666 * *((float *)v2 + 108);
v42 = v6;
v7 = v6;
sub_7EC510(LODWORD(v7));
v8 = *((float *)v2 + 72);
v37 = 0.0;
v9 = v8;
v38 = 0.0;
v39 = 1.0;
v28 = 0;
v29 = 0;
v30 = 0;
sub_881400(LODWORD(v9), &v28);
v10 = v36;
v11 = v35;
xCamPos = (float)(v42 * 0.0) + v31;
v13 = (__m128)*((_DWORD *)v2 + 73);
v13.m128_f32[0] = v13.m128_f32[0] - *((float *)v2 + 74);
zCamPos = (float)((float)(COERCE_FLOAT(_mm_xor_ps((__m128)LODWORD(v38), (__m128)xmmword_1153A70)) * 600.0) * v42)
+ v32;
yCamPos = (float)((float)(COERCE_FLOAT(_mm_xor_ps((__m128)LODWORD(v39), (__m128)xmmword_1153A70)) * 600.0) * v42)
+ v33;
v41 = v13.m128_f32[0];
v13.m128_i32[0] = (unsigned __int128)_mm_and_ps(v13, (__m128)xmmword_1153A60);
v16 = v13.m128_f32[0] < 0.0000099999997;
v17 = v13.m128_f32[0] == 0.0000099999997;
v18 = v34;
if ( !v16 && !v17 )
{
v38 = zCamPos - v35;
v37 = xCamPos - v34;
v39 = yCamPos - v36;
v42 = (float)((float)(v38 * v38) + (float)(v37 * v37)) + (float)(v39 * v39);
v40 = sub_5F5130(LODWORD(v42));
if ( v42 > 9.9999997e-10 )
{
v19 = sub_66C000(LODWORD(v42));
v37 = v37 * v19;
v38 = v38 * v19;
v39 = v19 * v39;
}
sub_5F0D30(LODWORD(v41));
v11 = v35;
v10 = v36;
v18 = v34;
zCamPos = (float)(v38 * v40) + v35;
xCamPos = (float)(v37 * v40) + v34;
yCamPos = (float)(v39 * v40) + v36;
}
v20 = a2;
v21 = v11 - zCamPos;
*(_DWORD *)(a2 + 48) = 0;
v22 = *((float *)v2 + 76);
*(float *)(a2 + 4) = zCamPos;
v23 = v18 - xCamPos;
*(float *)(a2 + 44) = v22;
*(float *)a2 = xCamPos;
*(float *)(a2 + 8) = yCamPos;
v40 = v18 - xCamPos;
*(float *)(a2 + 24) = v21;
v41 = v21;
*(float *)(a2 + 20) = v18 - xCamPos;
v42 = v10 - yCamPos;
v24 = v42 * v42;
*(float *)(a2 + 28) = v10 - yCamPos;
camPosPointer = (float)((float)(v23 * v23) + (float)(v21 * v21)) + v24;
if ( camPosPointer > 9.9999997e-10 )
{
v25 = sub_66C000(LODWORD(camPosPointer));
*(float *)(v20 + 20) = v40 * v25;
*(float *)(v20 + 24) = v41 * v25;
*(float *)(v20 + 28) = v25 * v42;
}
return sub_7EC0B0(v20);
}
The relevant thing here is
Code:
*(float *)a2 = xCamPos;
a2 is the pointer that is a parameter to the function. Changing it would only change the position in the memory where we write.
But what is "xCamPos"?
Code:
xCamPos = (float)(v42 * 0.0) + v31;
Code:
v31 = *((float *)v2 + 65);
Code:
v2 = this;
So I went looking in ECX+65, but there wasnt a pointer to the x-axis-value of the camera, just a zero. I looked into the memory, moved the cam around to see some changing values and yes!, I found some of the values I found earlier also in this "struct".
this + 104/260 = xpos
this + 10C/268 = ypos
this + 120/288 = Pitch
this + 124/292 = Roll
this + 128/296 = Yaw
this + 12C/300 = ?
this + 130/304 = FOV
this + 1B8/440 = zpos atm
this + 1BC/444 = zpos go to pls
So, we got the Xpos, Ypos. Those and all other values in THIS particular struct can be directly set and will also instantly represent in the game. Thats definitely better than nopping parts of functions. Pitch Roll and Yaw are given in degree measure, standard is 180, alot easier than the struct I found before.
Also an interesting question: Why do these values appear so far from the beginning of the struct? There is not much inbetween.
FOV is 50 as standard value, looks like this when changed btw:
Then there is problem Number 1: We got 2 zpos values, "zpos atm" is the real value that our z-value has RIGHT AT THE MOMENT. "zpos go to pls" however is another value - If you change this one, "zpos atm" will smoothly jump to the value of "zpos go to pls". But it will not go over 2250, since thats the official maximum for zooming out. You can obviously change that by finding the place in the code where this value is being read in, but does anyone here have a better idea?
And problem number 2 is: I need the farclip, but I cannot find it in this new struct. It works if I combine the old one and the new one, but id like to avoid that. If anyone here has some information about this second structure I'd appreciate some insight.
With this new struct I can already create some nice scenes, and I will continue using that one.
If you got any questions regarding this topic, please ask, Id also like to help.
- Alisamix






