2

I've posited a similar question over on the Game Development StackExchange, but I'm going to refine it, elaborate, and piecemeal a more R.E.-specific context with the hope that someone out there has experience with this!

To start, I'm looking to create a free-fly cam hack where I can move anywhere with forward orientation always being where my mouse is pointing.

The game I'm hacking (single-player-only) auto-moves the player after tasks are completed; there are no instructions bound to player movement with anything but rotation via mouse. This means I'm building in WASD functionality myself, in a sense. I've found an instruction to inject on which allows me to obtain the XYZ coordinates (which change when the game auto-moves the player) of the camera, as well as its rotation data. Thus, I have the following:

  • camX (Plane) -- Value is a float ranging from negative to positive
  • camY (Plane) -- Value is a float ranging from negative to positive
  • camZ (Up/Down) -- Value is a float ranging from negative to positive
  • camH (Horizontal/Yaw) -- Value is a float in degrees from +180 to -180
  • camV (Vertical/Pitch) -- Value is a float in degrees clamped to +89 to -89

With that information, I've written a script in Lua (for use in Cheat Engine) which allows me to bind directions (forward, backward, left, right, up, and down) to keys (W, A, S, D, Q, and E); however, those simply add/subtract to/from XYZ values along with a speed modifier, meaning if I'm facing forward and moving forward, everything is fine. Turning 180 degress with the mouse, though, effectively means back is forward, left is right, etc.

What I'm looking to achieve is having those directional keys apply to wherever I'm aiming the mouse pointer. So, forward is always where I'm facing, etc.

What I've done so far is some combination of the following:

  • Download flycam/freecam scripts from other games and replace relevant instructions/data/etc. with my own.
  • Attempt to create my own based on various information I've gathered (example here) and general research of trigonometry and vector calculus as related to game development.

It seems that no matter how much I think I understand what's going on, I've yet to make this work.

Something I've gathered is that it would really behoove me to find where the game stores/references sin and cos information, such that I don't have to calculate those values on my own via something like math.cos()/math.sin() and possibly converting degrees/radians, etc. And even those manual calculations I've tried haven't worked out, so I'm not sure I fully grasp what I need to there.

Bearing all of the aforementioned in mind, here are my questions:

Question 1: What do sin/cos values tend to look like, and at what point through a camera/coord-related subroutine might I expect to see them (whether on the stack, in FPU/XMM registers, etc.)? I've gathered that these should be close to where I'm dabbling either in memory or within a subroutine, but I'm just not quite sure what I'm looking for.

Question 2: Are there general approaches through reversing to identify the coordinate system of a game? In the case of the game I'm hacking, it's developed in Unreal Engine 4, so I can simply look up the engine's coordinate system; however, if I wanted to validate such findings (possibly fleshing out if a game is using a custom coordinate system instead of the in-built one) or discover anew when dealing with a custom engine, how I might I do that? Are they values that tend to reside in nearby memory addresses of avatar/camera data, or possibly in registers/on the stack in camera-related subroutines?

Question 3: If I know the coordinate system used in the game, as well as the XYZ and Pitch/Yaw camera values, is there a formulaic way I can calculate sin/cos therewith to then use with a speed modifier to successfully implement a freecam? This, in the event that I cannot find sin/cos within the game to use within my script.

Question 4: I only have a cursory understanding of the trig/calc I've looked up in trying to understand calculations as related to sin/cos. Given a left-hand and right-hand coordinate system with Z being up, along with XYZ, Pitch/Yaw data, and a speed modifier, could someone possibly walk me through the necessary calculations for making forward work in both coordinate systems no matter where the user points the mouse? For example, what exactly the sin/cos values are comprised of at any given time, how/why they matter, and why you would add/sub/mul to appropriately modify XYZ, etc?

Apologies for the verbosity and if I'm unknowingly asking questions that require FAR more understanding to adequately answer, but I just wanted to be thorough in explaining my research and efforts up to this point.

Thank you for any guidance you can provide!

2
  • Q4 could better be asked at Game Development (but it sounds very broad – it's a wide topic, and there are tons of tutorials available on just that single topic). By the way, are you sure an engine as modern as UnReal would not use quaternions instead?
    – Jongware
    May 1, 2018 at 22:07
  • Yeah, I just figured I would throw Q4 in the mix if someone here happened to be able to roll off an answer or personal recommendations of resources. And yes, I'm certain that--at least with Unreal Engine 4 (which this game is coded in)--quaternions aren't being used...well, at least based on my experience with modifying XYZ values in quaternion-based games I've futzed with in Unity, which tend to warp/distort if modifying just one. The great thing about Unity is, sans obfuscation, it's easy to hook Unity's quaternion method to discover XYZW values. But this whole flycam thing is new to me.
    – dsasmblr
    May 1, 2018 at 22:31

2 Answers 2

1

Question 3 & 4 Answer:

To move the camera/player in the direction you're facing you can use this function that works on eular angles. The only addresses you need to find are your current view angles and 3d coordinates.

You use it to overwrite the player/camera 3d coordinates. I do it inside of a GetaSyncKeyState call, so it acts when pressing NUMPAD_1.

This function assumes:

  • X = pitch, Y = Yaw, Z = Roll
  • the Z of your 3D coordinates are the vertical plane
  • Angles are in degrees
#define PI 3.1415927f

vec3 { float x,y,z; };

vec3 Add(vec3 src, vec3 dst)
{
    vec3 sum;
    sum.x = src.x + dst.x;
    sum.y = src.y + dst.y;
    sum.z = src.z + dst.z;
    return sum;
}

float DegreeToRadian(float degree)
{
    return degree * (PI / 180);
}

vec3 DegreeToRadian(vec3 degrees)
{
    vec3 radians;
    radians.x = degrees.x * (PI / 180);
    radians.y = degrees.y * (PI / 180);
    radians.z = degrees.z * (PI / 180);
    return radians;
}

void MoveInCameraDirection(vec3 currAngle, vec3& src, float dist)
{
    vec3 d;

    d.x = cosf(DegreeToRadian(currAngle.x - 90)) * dist;
    d.y = sinf(DegreeToRadian(currAngle.x - 90)) * dist;
    d.z = sinf(DegreeToRadian(currAngle.y)) * dist;

    src = Add(src, d);
}

Example usage in GetAsyncKetState loop:

while (1)
{
    if (GetAsyncKeyState(VK_NUMPAD1) & 1)
    {
        MoveInCameraDirection(localPlayer->angle, localPlayer->pos, 3);
        Sleep(20);
    }
}
  • currAngle = your current view angles
  • src = your current position, passed as reference so the function can modify it
  • dist = distance you want to travel per execution, between 1 and 5 is good

Call the function when a key is pressed to fly around, move your mouse to aim your dude as usual. I have a working project with this in it that I inject and it works just fine. Gravity takes control and you drop slightly per tick but you can also disable gravity client side and then you have full freedom.

1
  • Thanks for posting your solution! It'll be great to keep for future reference.
    – dsasmblr
    May 6, 2018 at 22:43
1

After a lot of trial and error, and bits and pieces of the puzzle coming together from a lot of different sources and peoples' help, I'd like to answer my own questions to the extent I feel I'm able to. I hope this helps someone else down the road who runs into this issue.

Question 1: What do sin/cos values tend to look like, and at what point through a camera/coord-related subroutine might I expect to see them (whether on the stack, in FPU/XMM registers, etc.)? I've gathered that these should be close to where I'm dabbling either in memory or within a subroutine, but I'm just not quite sure what I'm looking for.

These values will be the sin and cos of the camera's pitch (vertical movement) and yaw (horizontal movement). They will likely reside somewhere in nearby memory of your camera's pitch/yaw addresses, or within registers while pitch/yaw are being calculated within a subroutine. A game can represent pitch and yaw in either degrees or radians.

Examples of pitch:

Range in degrees: -89 to 89 (Example of a clamped range, to avoid flipping)

Same range in radians: -1.55334 to 1.55334

Examples of yaw:

Range in degrees: -180 to 180 (Equaling 360 degrees total)

Same range in radians: -3.14159 to 3.14159

If the game you're reversing has its values stored as degrees, depending on how you plan to calculate sin/cos, you may need to convert degrees to radians. Since Lua is what I used, its degrees-to-radians function is math.rad(). So, math.rad(180) would give us a conversion from 180 degrees to 3.14159 radians.

With this value, we can now utilize Lua's math.cos() and math.sin() functions. Suppose we are looking for the sin and cos of the camera's yaw while it's at 63 degrees. The values we would be looking for in memory, on the stack, or in registers are 0.4539 (cosine, or math.cos(63)) and 0.891007 (sine, or math.sin(63)), respectively.

From a formulaic perspective, here's how we could set variables to give us sin and cos of a camera's yaw in Lua, provided we know the address the value is stored in:

--Read and store value from camera yaw memory address local camYaw = readFloat("[cameraBase+1D0]")

--Convert yaw to radians, then calculate cosine local camYawCos = math.cos(math.rad(camYaw))

--Convert yaw to radians, then calculate sine local camYawSin = math.sin(math.rad(camYaw))

Remember, if the game you're reversing already has its values stored as radians, you don't have to worry about the step of converting degrees to radians!

The formulas above can help you figure out where certain values are residing by allowing you to calculate the values yourself, then simply look for them in memory, on the stack, or in registers.

Question 2: Are there general approaches through reversing to identify the coordinate system of a game?

I'm still uncertain about this at the moment. You can figure it out by seeing how your calculations act once you've discovered XYZ Pitch/Yaw, but that's not quite the answer I'd like to know with this. I'll update this answer if/when I figure this out.

Question 3: If I know the coordinate system used in the game, as well as the XYZ and Pitch/Yaw camera values, is there a formulaic way I can calculate sin/cos therewith to then use with a speed modifier to successfully implement a freecam? This, in the event that I cannot find sin/cos within the game to use within my script.

Question 4: I only have a cursory understanding of the trig/calc I've looked up in trying to understand calculations as related to sin/cos. Given a left-hand and right-hand coordinate system with Z being up, along with XYZ, Pitch/Yaw data, and a speed modifier, could someone possibly walk me through the necessary calculations for making forward work in both coordinate systems no matter where the user points the mouse? For example, what exactly the sin/cos values are comprised of at any given time, how/why they matter, and why you would add/sub/mul to appropriately modify XYZ, etc?

I'm going to tie both of these together with the script I ended up creating.

First, credit where credit is due. I used SunBeam's Assassin's Creed: Origins script as an initial template. Also, I found examples from the fine individuals contributing to this post to be very illuminating along the way--as well as the answer here by GuidedHacking, and the answer by DMGregory where I initially posted my inquiry. Lastly, this colossal work by Frans Bouma on game camera reversing is a wonderful reference for anyone looking to broach this topic.

Now, in a left-hand coordinate system, all you should need to plug into this script is the camera's XYZ coordinates, as well as the camera's pitch and yaw. You can change the speed modifier to whatever you would like. I have keys bound to make the camera move faster and slower, and I've also implemented strafing, so you can move diagonally as you rotate.

This is a Lua script, created to run in Cheat Engine. The top of the script uses a Cheat Engine function to allocate memory to store the speed modifier value within. I've commented everything copiously, so you should find it relatively easy to convert this to your high-level language of choice if need be!

globalalloc(speedModifier,8) --Allocate 8 bytes memory
speedModifier:
dd (float)2 --Store a float of 2

{$lua} --Tells Cheat Engine to treat everything beneath as Lua

[ENABLE] --Everything beneath this happens when this script is enabled

function checkKeys(timer) --Check keys for input (check on a timer)

--Speed modifier value, read from memory location we allocated earlier
local speed = readFloat("speedModifier")

--Camera coordinates stored in variables
local camx = readFloat("[camBase]+1A0") -- Camera X
local camy = readFloat("[camBase]+1A8") -- Camera Y
local camz = readFloat("[camBase]+1A4") -- Camera Z

--Mouse rotation in radians
--Use math.rad() to convert from degrees if necessary
local rotv = math.rad(readFloat("[camBase]+1D0")) -- Vertical (Pitch)
local roth = math.rad(readFloat("[camBase]+1D4")) -- Horizontal (Yaw)

--Sine and Cosine of Rotation Values
local sinh = math.sin(roth) -- Sine of Horizontal (Yaw)
local cosh = math.cos(roth) -- Cosine of Horizontal (Yaw)
local sinv = math.sin(rotv) -- Sine of Vertical (Pitch)
local cosv = math.cos(rotv) -- Cosine of Vertical (Pitch)

  --Hotkey Setup
  --[[
        Button Mappings:

        Y - Forward
        G - Left
        H - Backward
        J - Right
        T - Down
        U - Up
        C - Speed Up
        Alt - Slow Down

      --Key combinations for strafing are also defined
      --Keys are oriented to where mouse is pointing
  ]]

  --Forward
  if isKeyPressed(VK_Y) then
    writeFloat("[camBase]+1A0", camx + (cosh * speed))
    writeFloat("[camBase]+1A8", camy + (sinv * speed))
    writeFloat("[camBase]+1A4", camz + (sinh * speed))
  end
  --Left
  if isKeyPressed(VK_G) then
    writeFloat("[camBase]+1A0", camx + (math.cos(roth - math.rad(90)) * speed))
    writeFloat("[camBase]+1A4", camz + (math.sin(roth - math.rad(90)) * speed))
  end
  --Back
  if isKeyPressed(VK_H) then
    writeFloat("[camBase]+1A0", camx - (cosh * speed))
    writeFloat("[camBase]+1A8", camy - (sinv * speed))
    writeFloat("[camBase]+1A4", camz - (sinh * speed))
  end
  --Right
  if isKeyPressed(VK_J) then
    writeFloat("[camBase]+1A0", camx - (math.cos(roth - math.rad(90)) * speed))
    writeFloat("[camBase]+1A4", camz - (math.sin(roth - math.rad(90)) * speed))
  end
  --Forward/Right
  if isKeyPressed(VK_Y) and isKeyPressed(VK_J) then
    writeFloat("[camBase]+1A0", camx + (math.cos(roth + math.rad(45)) * speed))
    writeFloat("[camBase]+1A8", camy + (sinv * speed))
    writeFloat("[camBase]+1A4", camz + (math.sin(roth + math.rad(45)) * speed))
  end
  --Forward/Left
  if isKeyPressed(VK_Y) and isKeyPressed(VK_G) then
    writeFloat("[camBase]+1A0", camx + (math.cos(roth - math.rad(45)) * speed))
    writeFloat("[camBase]+1A8", camy + (sinv * speed))
    writeFloat("[camBase]+1A4", camz + (math.sin(roth - math.rad(45)) * speed))
  end
  --Back/Left
  if isKeyPressed(VK_H) and isKeyPressed(VK_J) then
    writeFloat("[camBase]+1A0", camx - (math.cos(roth - math.rad(45)) * speed))
    writeFloat("[camBase]+1A8", camy - (sinv * speed))
    writeFloat("[camBase]+1A4", camz - (math.sin(roth - math.rad(45)) * speed))
  end
  --Back/Right
  if isKeyPressed(VK_H) and isKeyPressed(VK_G) then
    writeFloat("[camBase]+1A0", camx - (math.cos(roth + math.rad(45)) * speed))
    writeFloat("[camBase]+1A8", camy - (sinv * speed))
    writeFloat("[camBase]+1A4", camz - (math.sin(roth + math.rad(45)) * speed))
  end
  --Up
  if isKeyPressed(VK_U) then
   writeFloat("[camBase]+1A8", camy + (speed * 0.5))
  end
  --Down
  if isKeyPressed(VK_T) then
   writeFloat("[camBase]+1A8", camy - (speed * 0.5))
  end

  --Speed Modifiers
  if isKeyPressed(VK_C) then --If C is pressed, increase speed; max of 100
    if (speed < 100) then
      writeFloat("speedModifier", speed + 1)
    end
  elseif isKeyPressed(VK_MENU) then --If Alt is pressed, slow way down
    writeFloat("speedModifier", .3)
  else --If nothing is pressed, normal speed
    writeFloat("speedModifier", 2)
  end    
end

t=createTimer(nil)
timer_setInterval(t, 10)
timer_onTimer(t, checkKeys)
timer_setEnabled(t, true)

[DISABLE] --Everything beneath this happens when the script is disabled

timer_setEnabled(t, false)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.