<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://zero-irp.github.io/Proj-Blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://zero-irp.github.io/Proj-Blog/" rel="alternate" type="text/html" /><updated>2026-05-19T17:45:21+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/feed.xml</id><title type="html">Math at Scale: Reversing the AAA Projection Matrix</title><subtitle>Reversing The Construction Of The Perspective-Projection Matrix</subtitle><author><name>z1rp</name></author><entry><title type="html">Part 1: Introduction</title><link href="https://zero-irp.github.io/Proj-Blog/part-1-intro/" rel="alternate" type="text/html" title="Part 1: Introduction" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-1-intro/"><![CDATA[]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 1: Introduction</title><link href="https://zero-irp.github.io/Proj-Blog/part-1-intro/" rel="alternate" type="text/html" title="Part 1: Introduction" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-1-intro</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-1-intro/"><![CDATA[<style>
.post-nav {
  display: flex;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
/* This specific class pushes the button to the right */
.next-only {
  margin-left: auto;
}
</style>

<h3 id="️-disclaimer"><strong>⚠️ Disclaimer:</strong></h3>

<p>The content on this blog is provided for educational and informational purposes only. I do not encourage, endorse, or support any illegal activity. Any techniques, tools, or concepts 
discussed are intended solely for learning, research, defensive security, or experimentation in safe, legal environments. Use this information responsibly and at your own risk. The author 
is not responsible for any misuse or legal consequences that may result from applying what’s written here.</p>

<h3 id="prerequisites"><strong>Prerequisites:</strong></h3>

<h5 id="theoretical-knowledge"><strong>Theoretical Knowledge:</strong></h5>

<ul>
  <li>Linear Algebra:
    <ul>
      <li>Comfort with matrix-vector multiplication, dot products, and coordinate spaces<br />
  (Model → World → View → Projection)</li>
    </ul>
  </li>
  <li>Trigonometry</li>
  <li>x86-64 Assembly &amp; SIMD:
    <ul>
      <li>Ability to read assembly with a basic understanding of SSE/AVX instruction sets</li>
    </ul>
  </li>
</ul>

<h5 id="technical-skills"><strong>Technical Skills:</strong></h5>

<ul>
  <li>Static Analysis: Experience navigating large binaries in IDA Pro or Ghidra.</li>
  <li>Dynamic Analysis &amp; Memory Research: Cheat Engine for finding addresses and tracing functions.</li>
  <li>Importantly: Comfort reasoning through ambiguity.</li>
</ul>

<h5 id="tools-used"><strong>Tools Used:</strong></h5>

<ul>
  <li>Disassembler: IDA Pro (Ghidra/Binary Ninja are fine alternatives).</li>
  <li>Memory Scanner: Cheat Engine (For finding the “live” variables like FOV or Aspect Ratio).</li>
</ul>

<p>I will not be covering the “how-to” for basic tool usage.</p>

<h3 id="whats-the-point-of-doing-this"><strong>What’s the point of doing this?</strong></h3>

<p>Mainly for:</p>

<h5 id="1-modding--engine-fixes">1. <strong>Modding &amp; Engine Fixes:</strong></h5>

<p>If you can understand how its built, it opens up the door for modifying its construction to do basically whatever we desire, like:</p>
<ul>
  <li>Aspect Ratio Correction / Ultra Wide support:
    <ul>
      <li>if a game doesn’t natively support ultra wide or hardcodes their aspect ratio we can simply modify the values (related to 1/tan(fov/2) or Aspect Ratio multiplier/divisor 
  (e.g., changing 1.777 to 2.333) that gets applied to either the X or Y scale). Also corrects games where they use “Vert-“ scaling (where the top/bottom are cut off) and 
  force “Hor+” scaling.</li>
    </ul>
  </li>
  <li>Frustum Manipulation:
    <ul>
      <li>Extending the Far plane, modifying the Near plane, maybe even switch from standard depth to Reversed-Z buffer (shader logic must also be changed for this)</li>
    </ul>
  </li>
  <li>Wider FOV:
    <ul>
      <li>Bypassing hardcoded FOV sliders and go beyond the limits imposed by developers</li>
    </ul>
  </li>
  <li>Temporal Anti-Aliasing (TAA) &amp; DLSS Jitter injection:
    <ul>
      <li>I’ve been working with devs from Luma who add DLSS to games which never had TAA / had only a few jitters. Modern upscalers need sub-pixel offsets to function properly. 
  If you reverse the construction, you can inject a Halton Sequence or other jitter distributions directly into the projection values to ensure the engine renders slightly different 
  pixel positions every frame.</li>
    </ul>
  </li>
</ul>

<h5 id="2-performance-optimization">2. <strong>Performance Optimization:</strong></h5>
<ul>
  <li>Reversing the construction of the Projection matrix (or other paths in the rendering pipeline) gives you instruction-level insight into how the engine handles its most critical per-frame 
math and can help tell you if the engine was wasting cycles or if you can write a more efficient inline assembly, though most engines are pretty robust and won’t have fundamental flaws that 
tank CPU efficiency you can never be too sure. The Dunia Engine used for Far Cry New Dawn seems to have various optimization issues, but that’s a blog post for another day.</li>
</ul>

<h3 id="write-up-outline"><strong>Write-Up Outline:</strong></h3>

<ul>
  <li><strong><a href="../part-1-intro/">Part 1: Introduction</a></strong>
    <ul>
      <li><a href="../part-1-intro/#prerequisites">Prerequisites:</a></li>
      <li><a href="../part-1-intro/#whats-the-point-of-doing-this">What’s the point of doing this?</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-2-projection-matrix/">Part 2: What is the Projection Matrix?</a></strong>
    <ul>
      <li><a href="../part-2-projection-matrix/#the-bare-minimum-what-is-it">What is  the Projection Matrix?</a></li>
      <li><a href="../part-2-projection-matrix/#the-memory-layout-what-we-actually-care-about">The Memory Layout:</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-3-methods-to-find-proj-mat/">Part 3: Methods to Find The Projection Matrix</a></strong>
    <ul>
      <li><a href="../part-3-methods-to-find-proj-mat/#1-the-1-1--up-down-trick">1. The 1,-1 / Up-Down Trick:</a></li>
      <li><a href="../part-3-methods-to-find-proj-mat/#2-focal-length-scanning">2. Focal Length Scanning:</a></li>
      <li><a href="../part-3-methods-to-find-proj-mat/#3-brute-force">3. Brute Force:</a></li>
      <li><a href="../part-3-methods-to-find-proj-mat/#4-tracing-flow">4. Tracing Flow:</a></li>
      <li><a href="../part-3-methods-to-find-proj-mat/#5-pattern-recognition-jitter-tables">5. Pattern Recognition (Jitter Tables):</a></li>
      <li><a href="../part-3-methods-to-find-proj-mat/#6-the-graphics-debugger-route-renderdoc-to-memory">6. The Graphics Debugger Route (RenderDoc to Memory):</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-4-finding-projection-matrix/">Part 4: Finding and Tracing Projection Construction</a></strong>
    <ul>
      <li><a href="../part-4-finding-projection-matrix/#why">Spotting Perspective Projection Construction:</a></li>
      <li><a href="../part-4-finding-projection-matrix/#visual-verification">Visual Confirmation of Projection Construction</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-5.1-reversing-projection-matrix/">Part 5.1: Reversing Construction of the Projection Matrix (X &amp; Y Scales)</a></strong>
    <ul>
      <li><a href="../part-5.1-reversing-projection-matrix/#but-what-the-hell-is-an-_mm_unpacklo_ps">what the hell is an _mm_unpacklo_ps?</a></li>
      <li><a href="../part-5.1-reversing-projection-matrix/#reversing-construction-of-the-perspective-projection-matrix">Reversing Construction of the Perspective-Projection Matrix</a></li>
      <li><a href="../part-5.1-reversing-projection-matrix/#xscale-construction">xScale Construction:</a></li>
      <li><a href="../part-5.1-reversing-projection-matrix/#yscale-construction">yScale Construction:</a></li>
      <li><a href="../part-5.1-reversing-projection-matrix/#putting-it-all-together-the-cleaned-code">Putting It All Together: The Cleaned Code</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-5.2-reversing-projection-matrix/">Part 5.2: Reversing Construction of the Projection Matrix (Depth Mapping)</a></strong>
    <ul>
      <li><a href="../part-5.2-reversing-projection-matrix/#row-2">Depth Calculation: Row 2</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#the-anomaly--asymmetric-offset">The Anomaly / Asymmetric offset</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#row3">Depth Calculation: Row 3</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#putting-it-all-into-the-projection-matrix">Putting the values into the Projection Matrix:</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#putting-the-code-together">Putting the code together:</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#final-layout">Final Layout:</a></li>
      <li><a href="../part-5.2-reversing-projection-matrix/#the-payoff-owning-the-camera">The Payoff: Owning the Camera</a></li>
    </ul>
  </li>
  <li><strong><a href="../part-6-reversing-inverse-projection-matrix/">Part 6: Reversing Construction of the Inverse Projection Matrix</a></strong>
    <ul>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#row-0">Inverse Projection: Row 0</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#row-1">Inverse Projection: Row 1</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#row-2">Inverse Projection: Row 2</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#row-3">Inverse Projection: Row 3</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#final-layout">Final Layout:</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#reversing-insight">Reversing Insight</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#the-full-picture">The Full Picture:</a></li>
      <li><a href="../part-6-reversing-inverse-projection-matrix/#conclusion-owning-the-pipeline">Conclusion: Owning the Pipeline</a></li>
    </ul>
  </li>
</ul>

<p>Snippets of what we will be working with:</p>

<p><strong>IDA C pseudo code:</strong>
<img src="/Proj-Blog/assets/images/part-1/image1.png" alt="ESP-Image1" /></p>

<p><strong>SIMD Assembly:</strong>
<img src="/Proj-Blog/assets/images/part-1/image2.png" alt="ESP-Image1" /></p>

<p><strong>Cheat Engine:</strong>
<img src="/Proj-Blog/assets/images/part-4/CE-scan-60.png" alt="ESP-Image1" /></p>

<p>The game engine that will be used as an example is Far Cry New Dawn’s Dunia Engine.</p>

<p><em>These Reverse engineering insights from this engine can be applied to others as well</em></p>

<div class="post-nav">
  <a class="next-only" href="/Proj-Blog/part-2-projection-matrix/">Part 2: Projection Matrix &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 2: What is the Projection Matrix?</title><link href="https://zero-irp.github.io/Proj-Blog/part-2-projection-matrix/" rel="alternate" type="text/html" title="Part 2: What is the Projection Matrix?" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-2-projection-matrix</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-2-projection-matrix/"><![CDATA[<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<p>I previously touched on this here: <a href="https://zero-irp.github.io/ViewProj-Blog/part-2.2-projection-matrix/">Reversing The ViewProjection Matrix (Part 2.2)</a>, but we need to establish a 
baseline before we start tearing apart engine binaries.</p>

<p>I won’t be giving a deep-dive computer graphics lecture here (Frustum plane derivations, complex perspective math, etc.). Instead, we will learn how textbooks do the math vs. how real Game Engines do the math.</p>

<h3 id="the-bare-minimum-what-is-it"><strong>The Bare Minimum: What is it?</strong></h3>

<p>If the <strong>View Matrix</strong> acts as the camera’s position and rotation in the world, the <strong>Projection Matrix</strong> is the camera’s lens. It compresses the 3D world flat onto your 2D monitor.</p>

<p>It does this by defining a <strong>View Frustum</strong> (a 3D truncated pyramid of space) using four variables:</p>
<ul>
  <li><strong>Field of View (FOV):</strong> How wide the lens is.</li>
  <li><strong>Aspect Ratio:</strong> Your monitor’s dimensions (16:9, 21:9).</li>
  <li><strong>Near &amp; Far Planes:</strong> The minimum and maximum depth the camera can see.</li>
</ul>

<p>Anything inside this pyramid gets rendered to your screen. Anything outside gets clipped. That is all the theory you need for now.</p>

<p><img src="/Proj-Blog/assets/images/part-2/frustum.png" alt="ESP-Image1" /></p>

<h3 id="the-memory-layout-what-we-actually-care-about"><strong>The Memory Layout (What We Actually Care About)</strong></h3>

<p>As reverse engineers, understanding the theory is secondary to understanding the <em>memory layout</em>. When you are scanning memory in Cheat Engine, a projection matrix just looks like a 
contiguous block of 16 floating-point numbers.</p>

<p>To spot it in the sea of data, you have to know what those 16 floats represent. Here is the standard perspective projection matrix layout (DirectX):</p>

\[P =
\begin{bmatrix}
x_{scale} &amp; 0 &amp; 0 &amp; 0 \\
0 &amp; y_{scale} &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; \dfrac{z_{far}}{z_{far} - z_{near}} &amp; 1 \\
0 &amp; 0 &amp; -\dfrac{z_{near} \cdot z_{far}}{z_{far} - z_{near}} &amp; 0
\end{bmatrix}\]

<p>The formulas used for the <code class="language-plaintext highlighter-rouge">zNear</code> and <code class="language-plaintext highlighter-rouge">zFar</code> depth mapping (Rows 2 and 3) are not universal and can differ engine to engine, convention to convention. What actually matters is that 
after projection and perspective divide (in DirectX convention), the near plane maps to 0 and the far plane maps to 1 (or the opposite in Reversed-Z.)</p>

<p>However, the <strong>X and Y scales</strong> (the floats at <code class="language-plaintext highlighter-rouge">[0][0]</code> and <code class="language-plaintext highlighter-rouge">[1][1]</code>) are almost always derived directly from the Field of View using tangent math:</p>

\[x_{scale} = \frac{1}{\tan{\left(\frac{Fov_X}{2}\right)}}\]

\[y_{scale} = \frac{1}{\tan{\left(\frac{Fov_Y}{2}\right)}}\]

<p><strong>This is our golden ticket.</strong> If we know the game’s FOV, we can calculate the exact floating-point value for <code class="language-plaintext highlighter-rouge">xScale</code>, and use Cheat Engine to hunt down that exact float in memory.</p>

<blockquote>
  <p>A Projection Matrix standing out in a sea of floats.</p>

  <p><img src="/Proj-Blog/assets/images/part-2/proj-find.png" alt="ESP-Image1" /></p>
</blockquote>

<p>Let’s look at the exact methods we use to hunt this matrix down in Part 3.</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-1-intro/">&laquo; Part 1: Intro</a>
  <a href="/Proj-Blog/part-3-methods-to-find-proj-mat/">Part 3: Methods to find Projection Matrix &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 3: Methods to Find The Projection Matrix</title><link href="https://zero-irp.github.io/Proj-Blog/part-3-methods-to-find-proj-mat/" rel="alternate" type="text/html" title="Part 3: Methods to Find The Projection Matrix" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-3-methods-to-find-proj-mat</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-3-methods-to-find-proj-mat/"><![CDATA[<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<p>I will be going over methods that I use to find the projection matrix. Keep in mind these are just the methods I personally use, easier methods probably exist.</p>

<p>Our work flow will go like so:<br />
<strong>Find Projection Matrix -&gt; Trace Construction -&gt; Reverse Construction</strong> <br />
10 times harder than it seems…</p>

<h3 id="methods-to-find-projection-matrix"><strong>Methods to find Projection Matrix</strong></h3>

<h5 id="1-the-1-1--up-down-trick"><strong>1. The 1,-1 / Up-Down Trick:</strong></h5>

<p>First we look max <strong>up</strong> in game and search for the value “1” in cheat engine, then look <strong>down</strong> and search for the value “-1”. Repeat this process until you narrow down the results.<br />
When you finish narrowing it down you will get the View or Camera Matrix. Now the trick here is that all these matrices are probably in some kind of camera structure and they store 
multiple matrices close in memory and there is a high chance one of those matrices is a projection matrix.</p>

<blockquote>
  <p>If you do not find a Projection Matrix but instead a View-Projection Matrix and a View Matrix then you can simply derive the Projection Matrix using Matrix Algebra:</p>

  <p>If the game uses the row-vector convention (common in DirectX), where the multiplication order is:<br />
\(VP = V \times P\)<br />
You can solve for P by left-multiplying both sides by the inverse of the View matrix ($V^{-1}$):<br />
\(P = V^{-1} \times VP\)<br />
If the game uses the column-vector convention (common in OpenGL), where the multiplication order is:  <br />
\(VP = P \times V\)<br />
Then, you can solve for P by right-multiplying both sides by the inverse of the View matrix ($V^{-1}$):<br />
\(P = VP \times V^{-1}\)</p>

  <p>Then simply search for the Projection Values!</p>
</blockquote>

<h5 id="2-focal-length-scanning"><strong>2. Focal Length Scanning:</strong></h5>

<p>For this method to work the game <strong>must</strong> expose its FOV. This will usually be exposed in settings using degrees (Usually FovX is exposed). For this to work you need to understand that the 
first term (xScale) of the projection matrix is <code class="language-plaintext highlighter-rouge">1/tan(fovX/2)</code> so in Cheat engine we are searching for the result of <code class="language-plaintext highlighter-rouge">1/tan(fovX/2)</code>. First step is converting the degrees seen on the FOV 
slider to radians and simply compute the result of <code class="language-plaintext highlighter-rouge">1/tan(fovX/2)</code>. Way easier to narrow down results than method 1.</p>

<h5 id="3-brute-force"><strong>3. Brute Force:</strong></h5>

<p>This is the most time-consuming method. If the game does not give any hints about its FOV (Fov Slider is not available or Values in the slider are not real degrees etc) then we can use this method 
without being a full-blown reverse engineer:</p>

<ul>
  <li>If Fov slider is not exposed:<br />
  Before we get into the method let me teach some theory.
  This is the relation between xScale and yScale:
  \(\tan\left(\frac{\mathrm{FOV}_x}{2}\right) = AR \cdot \tan\left(\frac{\mathrm{FOV}_y}{2}\right)\)<br />
  Now we can’t change FOV but we can change Aspect Ratio! (Hopefully)<br />
  The trick is in the “Increased Value” and “Decreased Value” scan methods of Cheat engine. If you increase the Aspect Ratio (e.g., moving from 16:9 to 21:9) the engine must change 
  the xScale​ value in the Projection Matrix to prevent the image from stretching.
  So: Increasing the aspect ratio causes xScale to decrease and vice-versa.</li>
</ul>

<blockquote>
  <p>Engine might be Vert- scaling instead of Hor+ Scaling, just keep in mind</p>
</blockquote>

<ul>
  <li>If Fov slider has no real values:<br />
  Well its the same principle, “Increased Value” and “Decreased Value” scan methods of Cheat engine is your friend!<br />
  Increasing FOVx → xScale decreases and vice versa</li>
</ul>

<h5 id="4-tracing-flow"><strong>4. Tracing Flow:</strong></h5>

<p>This is kinda difficult for most people to do, you need to have a keen eye for recognizing 4x4 Matrix Multiplies and Projection Matrix Construction code in IDA.</p>

<p>The key idea is that you did method 1 and couldn’t find the projection matrix anywhere close in memory, So you trace the flow of matrices. Got Camera or View Matrix? Great!<br />
Use the “What instruction accesses this memory address” function in cheat engine and it will show you the exact function which uses said matrices. Then simply decompile the functions in IDA. 
Now there are 3 different things I usually look for when decompiling these functions:</p>

<p><strong>a) Large Matrix Construction Function</strong></p>

<p>You would see a very large Matrix Construction function responsible for constructing many different matrices per frame eg Proj, Cam, View, InvProj, InvView, VP, VP^-1 etc…
Now you need a keen eye to isolate just the Projection Construction, ask yourself is it using values relating to an aspect ratio? 1.7777, 1.3333? is it calling tanf/atanf 
functions? is it then doing 1/the tanf call? are zNear and zFar its parameters? etc…</p>

<p>Keep these derivations in mind as well :&gt; (very helpful while trying to pin point it)</p>

<p>Let A denote the Aspect Ratio, defined as</p>

\[A = \text{AspectRatio} = \frac{\text{Width}}{\text{Height}}\]

<p>The relationship between \(FOV_x\) and \(FOV_y\) is:</p>

\[\tan\left(\frac{FOV_X}{2}\right) = A \cdot \tan\left(\frac{FOV_Y}{2}\right)\]

<p>From this, we can express each one in terms of the other:</p>

\[FOV_X = 2 \cdot \tan^{-1}\!\left(A \cdot \tan\!\left(\frac{FOV_Y}{2}\right)\right)\]

\[FOV_Y = 2 \cdot \tan^{-1}\!\left(\frac{\tan\!\left(\frac{FOV_X}{2}\right)}{A}\right)\]

<p>Example Image:</p>

<p><img src="/Proj-Blog/assets/images/part-5.1/yScale-isolate.png" alt="ESP-Image1" /></p>

<p><strong>b) 4x4 Matrix Multiply:</strong></p>

<p>If you see Heavy SIMD usage specifically addps, shufps, mulps where shufps uses these constants to broadcast a single variable (0x00, 0x55, 0xAA, 0xFF) you can be pretty sure its doing a 
Matrix 4x4 Multiply, Now just look at the parameters and you would likely see its doing View * Projection (or Projection * View depending on row vs column major) and boom, it led us to the 
projection matrix!</p>

<p>Example Image:</p>

<p><img src="/Proj-Blog/assets/images/part-3/matrix4x4-multiply.png" alt="ESP-Image1" /></p>

<p><strong>c) Large Matrix copy function</strong></p>

<p>If you landed in a Large Matrix Copy Function, you probably tracked down the final GPU-bound floats. A routine that is just copying the finished matrices into a staging buffer to send to 
the GPU or something else.</p>

<p>You will have to trace further back, Look at the Stack, call locations, trace back etc…</p>

<p>Example Image:</p>

<p><img src="/Proj-Blog/assets/images/part-3/ida-copy-matrix.png" alt="ESP-Image1" /></p>

<h5 id="5-pattern-recognition-jitter-tables"><strong>5. Pattern Recognition (Jitter Tables):</strong></h5>

<p>To understand this concept you need to understand what Temporal Anti-Aliasing is:<br />
The basic principle on why this method works is that TAA relies on different subpixel values for anti aliasing, different subpixel values are achieved by jittering the projection matrix 
every frame. If we know the exact pattern in the Jitter Table we could scan that exact same pattern as a “Grouped Scan” in Cheat engine. Once you find the jitter table use the 
“Find out what instruction access this memory address” and it will lead you to the code that jitters the projection matrix leading us to the projection matrix.
You will likely see them accessing the jitter table and scaling it down with current Resolution X and Y and then it multiplying the projection Matrix with an identity Matrix in which 
the subpixel jitter values were placed inside it.</p>

<p>eg: In almost all SMAA_T2X games the pattern is {0.25, -0.25} and {-0.25, 0.25} (Watch Dogs2, Just cause 3, Dying Light all had this exact pattern for SMAA_T2X).</p>

<p>So in Cheat Engine select “Value Type” as “Grouped” and put “ f:0.25 f:-0.25 f:-0.25 f:0.25 “ in the Scan Box and you should get the Jitter array (Usually a static memory address)</p>

<p>same method used in Watch Dogs 2</p>

<p><img src="/Proj-Blog/assets/images/part-3/jitPat.png" alt="ESP-Image1" /></p>

<h5 id="6-the-graphics-debugger-route-renderdoc-to-memory"><strong>6. The Graphics Debugger Route (RenderDoc to Memory):</strong></h5>

<p>I will be honest, this is a method I haven’t personally had to rely on yet, but the theory is rock solid!</p>

<p>Instead of hunting blindly in memory, you start at the GPU level using a graphics debugger like RenderDoc. The workflow looks something like this:</p>

<ul>
  <li>
    <p><strong>a)</strong> Capture a frame of the game in RenderDoc.</p>
  </li>
  <li>
    <p><strong>b)</strong> Look through the Event Browser and select a substantial geometry draw call, usually something like DrawIndexed() with a high index count.</p>
  </li>
  <li>
    <p><strong>c)</strong> Inspect the Constant Buffers (CBuffer) bound to the Vertex Shader for that draw call.</p>
  </li>
  <li>
    <p><strong>d)</strong> The vertex shader mathematically requires the View and Projection matrices to transform local geometry into clip space. By looking through the bound buffers, you can physically see 
the 4x4 matrix floats sitting right there in the GPU state.</p>
  </li>
  <li>
    <p><strong>e)</strong> Once you spot the Projection Matrix, note it down.</p>
  </li>
</ul>

<p>Armed with those exact highly specific floats, you switch back to Cheat Engine and do a “Grouped” scan. Because you are searching for 4 or 5 exact float values in a specific order, 
you will usually find the CPU-side memory address instantly.</p>

<p>Once you have that address you can do the “Find out what instruction accesses this memory address” in Cheat Engine. From there, you take the instruction pointer straight into IDA Pro.</p>

<p>You will probably reach the Projection Construction function.</p>

<blockquote>
  <p>Sometimes the constant buffers wont even have the Projection Matrix lol. Would Probably be Multiplied with other matrices.</p>
</blockquote>

<p>Next we will be using these methods to find the projection matrix in far cry new dawn.</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-2-projection-matrix/">&laquo; Part 2: Projection Matrix</a>
  <a href="/Proj-Blog/part-4-finding-projection-matrix/">Part 4: Tracing Projection Construction &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 4: Finding and Tracing Projection Construction</title><link href="https://zero-irp.github.io/Proj-Blog/part-4-finding-projection-matrix/" rel="alternate" type="text/html" title="Part 4: Finding and Tracing Projection Construction" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-4-finding-projection-matrix</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-4-finding-projection-matrix/"><![CDATA[<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<p>Let’s try and find the Projection Matrix in Memory:</p>

<p>In Far cry New dawn the Fov Slider exposes the actual degrees used by the projection Matrix thus making it very easy to find it.</p>

<p><img src="/Proj-Blog/assets/images/part-4/FOV-slider.png" alt="ESP-Image1" /></p>

<p>Reminder that the xScale is:</p>

\[\frac{1}{\tan\left(\frac{FOV_X}{2}\right)}\]

<p>Now we have access to FovX. I will set the in-game slider to 105 degrees. Next, we convert it to radians and calculate the result:</p>

\[\frac{1}{\tan{\left(\frac{1.8326}{2}\right)}} = 0.7673\]

<p>And then we simply search for this value:</p>

<p><img src="/Proj-Blog/assets/images/part-4/CE-scan-105.png" alt="ESP-Image1" /></p>

<p>Now I change the Fov Slider value to “60 degrees”, put this through the formula and we get “1.732” and search for it again. Do it a few more times to narrow it down and we get:</p>

<p><img src="/Proj-Blog/assets/images/part-4/CE-scan-60.png" alt="ESP-Image1" /></p>

<p>Now verify all matrices manually till we find a Projection Matrix that is close to the textbook layout.</p>

<p>This seems to be a Projection Matrix that comes close but still not “Textbook layout”:</p>

<p><img src="/Proj-Blog/assets/images/part-4/CE-Proj.png" alt="ESP-Image1" /></p>

<blockquote>
  <p>As I’ve said before Game engines need not follow convention</p>
</blockquote>

<p>In the first Matrix we can see the expected values in [0][0] (xScale) and [1][1] (yScale) as well as Depth Mapping in [2][2] and [3][2], the unexpected value is 0.13 at [2][1] 
(which is supposed to be 0 in a standard layout). We will figure out what this anomaly is later. The second Matrix is simply the Inverse Projection.</p>

<p>Let’s try to find out “what writes to this address” using CE</p>

<p><img src="/Proj-Blog/assets/images/part-4/CE-what-writes.png" alt="ESP-Image1" /></p>

<p>We can see two instructions. Both seem to be from the same function, let’s decompile the function in IDA pro:</p>

<blockquote>
  <p>Far Cry New Dawn uses Denuvo Anti-Tamper, IDA pro will endlessly loop and will never finish decompiling fully due to the obfuscated nature of the binary. Make sure to turn off 
auto-analysis and do manual analysis!</p>
</blockquote>

<p><img src="/Proj-Blog/assets/images/part-4/IDA-decomp.png" alt="ESP-Image1" /></p>

<p>Looks like a mess but to a trained eye we can tell it is doing a Projection Matrix Construction!</p>

<h3 id="why"><strong>Why?</strong></h3>

<p>Look at these 1/x’s and x/2’s and various calls to Tanf and aTanf. A clear indicator for Perspective Projection Matrix Construction</p>

<p><img src="/Proj-Blog/assets/images/part-4/Proj-pin.png" alt="ESP-Image1" /></p>

<h3 id="visual-verification"><strong>Visual Verification</strong></h3>
<p>Before reversing the function, we need to test if this is the real projection matrix used for rendering. I will be changing the FovX value accessed by the function</p>

<p><img src="/Proj-Blog/assets/images/part-4/fcnd-fovX.gif" alt="ESP-Image1" /></p>

<p>Next we will be Reversing this function using IDA pro.</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-3-methods-to-find-proj-mat/">&laquo; Part 3: Methods to find Projection Matrix</a>
  <a href="/Proj-Blog/part-5.1-reversing-projection-matrix/">Part 5.1: Reversing Construction (X &amp; Y) &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 5.1: Reversing Construction of the Projection Matrix (X &amp;amp; Y Scales)</title><link href="https://zero-irp.github.io/Proj-Blog/part-5.1-reversing-projection-matrix/" rel="alternate" type="text/html" title="Part 5.1: Reversing Construction of the Projection Matrix (X &amp;amp; Y Scales)" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-5-reversing-projection-matrix</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-5.1-reversing-projection-matrix/"><![CDATA[<style>
.ida-code{
  background:#1e1e1e;
  color:#dcdcdc;
  padding:12px;
  border-radius:8px;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size:14px;
  line-height:1.4;
  overflow-x:auto;
  white-space: pre; /* preserve spaces and linebreaks */
}

/* token classes you can use inside the div */
.ida-code .kw    { color:#569cd6; } /* keywords */
.ida-code .type  { color:#4ec9b0; } /* types */
.ida-code .fn    { color:#dcdcaa; } /* functions / intrinsics */
.ida-code .num   { color:#b5cea8; } /* numbers / hex */
.ida-code .var   { color:#9cdcfe; } /* variables */
.ida-code .const { color:#ce9178; } /* globals / constants */
.ida-code .comment{ color:#6a9955; font-style:italic; }
</style>

<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<p>Now that we have found the function responsible for constructing the Perspective Projection Matrix Let’s begin reversing it to have a clear understanding on how the game engine constructs 
this matrix every frame!</p>

<p>Now we wont be reversing the entire function which we have stumbled into because the function we found by “finding out what writes to this address” feature in CE seems to be a very 
large function responsible for constructing various Matrices such as Camera, View, Projection, Inverse Projection, Identity Scaler, ViewProjection, InvProjCamera and has multiple 
function calls to Matrix4x4Multiply(), Matrix4x4Inverse() and even Calculate View Frustum function. We will only be looking into Projection and Inverse Projection Construction.</p>

<p>Let’s look at where we were initially:</p>

<p><img src="/Proj-Blog/assets/images/part-4/IDA-decomp.png" alt="ESP-Image1" /></p>

<p>The highlighted block is the <strong>First Row</strong> of the Projection Matrix which is the instruction responsible for updating our Projection Matrix every frame. let’s zoom out a bit and see what’s 
really going on.</p>

<p>It seems to be a part of an if-else block where our instruction is being executed inside the else block.</p>

<p><strong>else block:</strong></p>

<p><img src="/Proj-Blog/assets/images/part-5.1/IDA-screenshot-elseblock.png" alt="ESP-Image1" /></p>

<p><strong>if block:</strong></p>

<p><img src="/Proj-Blog/assets/images/part-5.1/IDA-screenshot-ifblock.png" alt="ESP-Image1" /></p>

<p>Now we clearly see the big picture happening here, The else block is clearly constructing a Perspective Projection Matrix as per my reasoning in <strong>part-4</strong>. The if block seems to be 
constructing an Orthographic Projection Matrix, my reasoning being these lines:</p>

<div class="ida-code"><span class="var">v51</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">2.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C0</span>);  <span class="comment">// 2/width</span>
</div>

<p>and</p>

<div class="ida-code"><span class="var">v55</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = (<span class="type">float</span>)(<span class="num">1.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C4</span>)) + 
						(<span class="type">float</span>)(<span class="num">1.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C4</span>));  <span class="comment">// 2/height</span>
</div>

<blockquote>
  <p>The values inside <code class="language-plaintext highlighter-rouge">a1 + 0x3C0</code> and <code class="language-plaintext highlighter-rouge">a1 + 0x3C4</code> have been checked to be Width and Height using CE during runtime.</p>
</blockquote>

<p>These are the expected xScale and yScale values for an Orthographic Projection matrix. And look where it is stored:</p>

<div class="ida-code"><span class="var">v54</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v51</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="var">v54</span>;  <span class="comment">// v54 = [2/width, 0, 0, 0]</span>
</div>
<p>and</p>
<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v55</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
					<span class="comment">// Final Unpack = [0, 2/height, 0, 0]</span>
</div>

<blockquote>
  <h4 id="but-what-the-hell-is-an-_mm_unpacklo_ps"><strong>But what the hell is an _mm_unpacklo_ps??</strong></h4>

  <p>The basic theory is simple:</p>

  <p>the “lo” in “unpacklo” means we are only targeting the lowest 64-bits inside a 128-bit register and “ps” stands for “Packed Single” which tells the cpu to treat the 128-bit register as 
four 32-bit floats.<br />
This is basically what it does:</p>

  <p>Suppose:</p>

  <p>Register A: [ A3, A2, A1, A0 ] (where A0 is the lowest float) (arg1)<br />
Register B: [ B3, B2, B1, B0 ] (arg2)</p>

  <p>Result:</p>

  <p>[ A0, B0, A1, B1 ]</p>

  <p>that’s all.</p>
</blockquote>

<p>As a refresher, a standard Orthographic Projection Matrix looks like this:</p>

\[\begin{bmatrix}
\frac{2}{w} &amp; 0 &amp; 0 &amp; 0 \\
0 &amp; \frac{2}{h} &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; \frac{1}{z_{far} - z_{near}} &amp; 0 \\
0 &amp; 0 &amp; -\frac{z_{near}}{z_{far} - z_{near}} &amp; 1
\end{bmatrix}\]

<p>Thus validating our belief that the if block is constructing an Orthographic Projection Matrix so the if-else logic would be:</p>

<div class="ida-code"><span class="kw">if</span>(<span class="var">shouldConstructOrthoProj</span>)
{
    <span class="fn">ConstructOrthoProj</span>();
}
<span class="kw">else</span>
{
    <span class="fn">ConstructPerspectiveProj</span>();
}
</div>
<p>Now for the hard part, Reversing the complete logic inside the else block…</p>

<h3 id="reversing-construction-of-the-perspective-projection-matrix"><strong>Reversing Construction of the Perspective-Projection Matrix</strong></h3>

<p>Let’s begin Reversing the else block:</p>

<p><img src="/Proj-Blog/assets/images/part-5.1/IDA-screenshot-elseblock.png" alt="ESP-Image1" /></p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 1</span>  <span class="kw">else</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 2</span>  {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">v33</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">v34</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 7</span>    <span class="var">tanFovXby2_dup</span> = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">v35</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">v36</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">v35</span> = <span class="var">v34</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">21</span>    <span class="var">v37</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">v38</span> = <span class="var">v36</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">v37</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span>    <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span>    <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span>    <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">v42</span> = <span class="var">v35</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span>    <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span>    <span class="var">v44</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">v44</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span>    <span class="var">v45</span> = <span class="var">v32</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span>    <span class="var">v46</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v44</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span>    <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v37</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span>    <span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span>    <span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">v47</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span>    <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span>    <span class="var">v48</span> = <span class="var">v31</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span>    <span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span>    <span class="var">v49</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">47</span>    <span class="var">v50</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">v49</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">49</span>    <span class="var">v42</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">50</span>    <span class="var">v38</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">51</span>    <span class="var">v50</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">52</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x130</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v36</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">53</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x140</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v35</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">54</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x150</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v45</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">55</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x160</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v38</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v42</span>, <span class="var">v50</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">56</span>    *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x1C</span>) = <span class="num">1.0</span> / <span class="fn">fminf</span>(<span class="var">tanFovXby2</span>, <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">57</span>  }
</div>

<h3 id="xscale-construction"><strong>xScale Construction:</strong></h3>

<p>Because the compiler interleaved the instructions for optimization, the calculation for the X and Y scales are tangled together. Let’s isolate just the xScale logic:</p>

<p><img src="/Proj-Blog/assets/images/part-5.1/xScale-isolate.png" alt="ESP-Image1" /></p>

<blockquote>
  <p>Lines highlighted in red are responsible for xScale calculation.</p>
</blockquote>

<p>Let’s start with this block</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">v33</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>();
</div>

<p>Using CE for dynamic analysis we can see that <code class="language-plaintext highlighter-rouge">(a1 + 0x234)</code> is FovX in radians or “1.8326” matching our Fov slider we set to “105 degrees” before.</p>

<p>It loads it as an (__m128)*(unsigned int *) which treats it as an integer but we know it is a float, it doesn’t matter what type it is as long as it’s 4 bytes. At the CPU level, 
bits are just bits…<br />
So basically load a FovX in radians into “v33” then immediately divide it by 2 so now “v33” holds the value <strong>fovX/2</strong>.</p>

<p>Next we see a call <code class="language-plaintext highlighter-rouge">ucrtBase_Tanf();</code> which IDA has failed to assign arguments to. But no worries we will look at the assembly for it’s arguments.</p>

<p>Following Windows ABI convention xmm0 will have the arg and result will be stored inside xmm0 as well. Tracing the assembly we see:</p>

<p><img src="/Proj-Blog/assets/images/part-5.1/tanf-call.png" alt="ESP-Image1" /></p>

<p><code class="language-plaintext highlighter-rouge">[rbx+234h]</code> is our fovX which gets stored into xmm0, gets multiplied by 0.5 and used as an arg for tanf call. After the call the lowest 32-bits of xmm0 register will hold <strong>tan(fovX/2)</strong>.</p>

<blockquote>
  <p>After this block there are lines which use FovX which i have not highlighted. This is because its either trying to derive FovY with FovX or is saving the current value of v33 for 
later use.</p>
</blockquote>

<p>Next block is:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">v36</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
</div>

<p>Moving onto the next line we see redundancy or a quirk, it is an ucrtBase_aTanf(); call and looking at the assembly the argument is v33 again so now “v33 = <strong>atanf(tanf(fovX/2))</strong>” which 
will equal “fovX/2”.</p>

<p><strong>Why?</strong></p>

<p>FovX can only have a value from 60 to 120 degrees in-game. Since we divided it by 2, the angle is between 30 and 60 degrees (well within the -π/2 to π/2 principal bounds of arctan), 
meaning arctan(tan(x)) = x. Calling a atanf function just to get back fovX/2 is a waste of CPU cycles but still negligible.</p>

<blockquote>
  <p>The identity arctan(tan(x)) = x holds if and only if x lies strictly inside (−90°, 90°), the identity holds without exception.</p>
</blockquote>

<p>then:</p>

<p>v36 is initialized with the value of v33 (fovX / 2) and immediately multiplied by 2.0, bringing it back to the original fovX</p>

<p>Next block is:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">v38</span> = <span class="var">v36</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">v37</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>v36 was fovX, now its fovX/2 after “v36 * 0.5”.</p>

<p>Then it calls a ucrtBase_Tanf() with arg as v36 so the result in v36 is <strong>tan(fovX/2)</strong>.</p>

<p>Next it saves tan(fovX/2) into v38 for later calculations and finally does “v37.m128_f32[0] = 1.0 / v36.m128_f32[0];” Completing our calculation for xScale and saving it inside v37.</p>

\[x_{scale} = \frac{1}{\tan{\left(\frac{Fov_X}{2}\right)}}\]

<h3 id="yscale-construction"><strong>yScale Construction:</strong></h3>

<p><img src="/Proj-Blog/assets/images/part-5.1/yScale-isolate.png" alt="ESP-Image1" /></p>

<p>Let’s start with this block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">v34</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">v35</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
</div>

<p>v34 is first assigned the value of tan(fovX/2) (as an m128 so only lowest 32 bits are fov values) then later is divided by a value at <code class="language-plaintext highlighter-rouge">*(float *)(a1 + 0x18)</code>. With dynamic analysis we 
can see it is a constant of “1.777” which is our aspect ratio of 16:9 but the interesting part is that it’s constant and won’t change even when aspect ratio is 4:3 or 16:10.</p>

<p>So now v34 holds the value <strong>“tan(fovX/2) / Aspect Ratio”</strong>, Hmm this formula looks familiar…</p>

\[\tan\left(\frac{FOV_X}{2}\right) \,/\, A = \tan\left(\frac{FOV_Y}{2}\right)\]

<p>In the next line a value from <code class="language-plaintext highlighter-rouge">(__m128)*(unsigned int *)(a1 + 0x430)</code> is loaded into v35. With dynamic analysis we can see most of the time this is zero.</p>

<p>Next block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">v35</span> = <span class="var">v34</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
</div>

<p>if <code class="language-plaintext highlighter-rouge">(__m128)*(unsigned int *)(a1 + 0x430) / v35</code> is zero then it will calculate the value of <strong>FovY</strong> using <strong>FovX</strong> with this formula:</p>

\[FOV_Y = 2 \cdot \tan^{-1}\!\left(\frac{\tan\!\left(\frac{FOV_X}{2}\right)}{A}\right)\]

<p>and save it inside “v35”</p>

<p>v34 previously held the value of <strong>“tan(fovX/2) / Aspect Ratio”</strong> so now after the <code class="language-plaintext highlighter-rouge">ucrtBase_aTanf()</code> call with its arg being “v34”, “v34” will have the value 
<strong>atan(tan(fovX/2) / AspectRatio))</strong>. This gets saved into v35 and immediately after multiples v34 with 2 and saves it inside v35. So now v35 has the value:</p>

<p><strong>“2 * atan(tan(fovX/2) / AspectRatio))”</strong> matching our formula exactly!</p>

<p>Next block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">v42</span> = <span class="var">v35</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">v44</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>Next it will calculate FovY/2 and save it into “v35” then save the value of “v35” into “v42” for later calculations.</p>

<p>Then it finally does a call to <code class="language-plaintext highlighter-rouge">ucrtBase_aTanf()</code> with “v35” as arg so the value inside “v35” is <strong>tan(fovY/2)</strong>, Next it will complete the calculation for yScale by doing:</p>

<p>v44.m128_f32[0] = 1.0 / v35.m128_f32[0]; so final value inside “v44” is <strong>“1/tan(fovY/2)”</strong></p>

<blockquote>
  <p>So now <strong>v44 = 1/tan(fovY/2)</strong> and <strong>v37 = 1/tan(fovX/2)</strong> (Only the lowest 32-bits are used, rest are 0’s).</p>
</blockquote>

<h3 id="putting-it-all-together-the-cleaned-code"><strong>Putting It All Together: The Cleaned Code</strong></h3>

<p>Now that we understand the math behind both the X and Y scales, we can go back into IDA, rename our variables, and comment the IDA pseudo code.</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 1</span>  <span class="kw">else</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 2</span>  {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- Initial Setup &amp; FovX ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Load FovX (radians) from a1 + 0x234</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">fovX_calc</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_calc. Result: fovX_calc = tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">fovY_calc</span> = <span class="var">fovX_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 7</span>    <span class="var">tanFovXby2_dup</span> = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Divide by Aspect Ratio at a1 + 0x18 (hardcoded at 1.777)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovY_calc = tan(FovX / 2) / AspectRatio</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- The Redundant Call ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_calc. Result: atan(tan(FovX / 2)). Reverts back to FovX / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- FovY Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Check if FovY is pre-calculated (usually 0)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">fovY_val</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">fovX_val</span> = <span class="var">fovX_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovX_val = (FovX / 2) * 2.0 -&gt; FovX</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      <span class="comment">// Arg: fovY_calc. Result: atan(tan(FovX / 2) / AspectRatio)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">fovY_val</span> = <span class="var">fovY_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      <span class="comment">// fovY_val = 2 * atan(tan(FovX / 2) / AspectRatio)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovX_val = FovX / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_val. Result: fovX_val = tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">21</span>    <span class="var">xScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovY_val = FovY / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Save tan(FovX / 2) for later calculations</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">tan_fovX_half_saved</span> = <span class="var">fovX_val</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- Final xScale Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// xScale = 1.0 / tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">xScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovY_val. Result: fovY_val = tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span>    <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span>    <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span>    <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Save tan(FovY / 2) for later calculations</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">tan_fovY_half_saved</span> = <span class="var">fovY_val</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span>    <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span>    <span class="var">yScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- Final yScale Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// yScale = 1.0 / tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">yScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>Since this part is getting too long the reversal for depth mapping calculations will be done on the next part!</p>

<p>In Part 5.2, we will look at how the engine uses zNear and zFar for depth mapping, and how it uses SIMD instructions like _mm_unpacklo_ps to pack all of these isolated variables 
into the final 4x4 projection matrix in memory.</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-4-finding-projection-matrix/">&laquo; Part 4: Tracing Projection Construction</a>
  <a href="/Proj-Blog/part-5.2-reversing-projection-matrix/">Part 5.2: Reversing Construction (Depth) &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 5: Reversing Construction of the Projection Matrix</title><link href="https://zero-irp.github.io/Proj-Blog/part-5-reversing-projection-matrix/" rel="alternate" type="text/html" title="Part 5: Reversing Construction of the Projection Matrix" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-5.1-reversing-projection-matrix</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-5-reversing-projection-matrix/"><![CDATA[<style>
.ida-code{
  background:#1e1e1e;
  color:#dcdcdc;
  padding:12px;
  border-radius:8px;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size:14px;
  line-height:1.4;
  overflow-x:auto;
  white-space: pre; /* preserve spaces and linebreaks */
}

/* token classes you can use inside the div */
.ida-code .kw    { color:#569cd6; } /* keywords */
.ida-code .type  { color:#4ec9b0; } /* types */
.ida-code .fn    { color:#dcdcaa; } /* functions / intrinsics */
.ida-code .num   { color:#b5cea8; } /* numbers / hex */
.ida-code .var   { color:#9cdcfe; } /* variables */
.ida-code .const { color:#ce9178; } /* globals / constants */
.ida-code .comment{ color:#6a9955; font-style:italic; }
</style>

<p>Now that we have found the function responsible for constructing the Perspective Projection Matrix let’s begin reversing it to have a clear understanding on how the game engine constructs 
this matrix every frame!</p>

<p>Now we wont be reversing the entire function which we have stumbled into because the function by “finding out what writes to this address” feature in CE, but in short the function we have 
stumbled into is a very large function responsible for constructing various Matrices such as Camera, View, Projection, Inverse Projection, Identity Scaler, ViewProjection, InvProjCamera 
and has multiple function calls to Matrix4x4Multiply(), Matrix4x4Inverse() and even a View Frustum Culling function. We will only be looking into Projection and Inverse Projection.</p>

<p>Let’s look at where we were initially:</p>

<p><img src="/Proj-Blog/assets/images/part-4/IDA-decomp.png" alt="ESP-Image1" /></p>

<p>The highlighted block is the <strong>First Row</strong> of the Projection Matrix which is the instruction responsible for updating our Projection Matrix every frame. let’s zoom out a bit and see what 
is really going on.</p>

<p>It seems to be a part of an if-else branch where our instruction is being executed inside the else block.</p>

<p><strong>else block:</strong></p>

<p><img src="/Proj-Blog/assets/images/part-5/IDA-screenshot-elseblock.png" alt="ESP-Image1" /></p>

<p><strong>if block:</strong></p>

<p><img src="/Proj-Blog/assets/images/part-5/IDA-screenshot-ifblock.png" alt="ESP-Image1" /></p>

<p>Now we clearly see the big picture happening here, The else block is clearly constructing a Perspective Projection Matrix as per my reasoning in <strong>part-4</strong>. The if block seems to be 
constructing a Orthographic Projection Matrix, my reasoning being these lines:</p>

<div class="ida-code"><span class="var">v51</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">2.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C0</span>);  <span class="comment">// 2/width</span>
</div>

<p>and</p>

<div class="ida-code"><span class="var">v55</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = (<span class="type">float</span>)(<span class="num">1.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C4</span>)) + 
						(<span class="type">float</span>)(<span class="num">1.0</span> / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x3C4</span>));  <span class="comment">// 2/height</span>
</div>

<blockquote>
  <p>The values inside a1 + 0x3C0 and a1 + 0x3C4 have been checked to be Width and Height using CE during runtime.</p>
</blockquote>

<p>These are the expected xScale and yScale values for an Orthographic Projection matrix. And look where it is stored:</p>

<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="var">v54</span>;  <span class="comment">// v54 = [2/width, 0, 0, 0]</span>
</div>
<p>and</p>
<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v55</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
					<span class="comment">// Final Unpack = [0, 2/height, 0, 0]</span>
</div>

<h4 id="but-what-the-hell-is-an-_mm_unpacklo_ps"><strong>But what the hell is an _mm_unpacklo_ps??</strong></h4>

<p>The basic theory is simple:</p>

<p>the “lo” in “unpacklo” means we are only targeting the lowest 64-bits inside a 128-bit register and “ps” stands for “Packed Single” which tells the cpu to treat the 128-bit register as 
four 32-bit floats.<br />
This is basically what it does:</p>

<p>Suppose:</p>

<p>Register A: [ A3, A2, A1, A0 ] (where A0 is the lowest/right-most float) (arg1)<br />
Register B: [ B3, B2, B1, B0 ] (arg2)</p>

<p>Result:</p>

<p>[ A0, B0, A1, B1 ]</p>

<p>that’s all.</p>

<p>As a refresher, a standard Orthographic Projection Matrix looks like this:</p>

\[\begin{bmatrix}
\frac{2}{w} &amp; 0 &amp; 0 &amp; 0 \\
0 &amp; \frac{2}{h} &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; \frac{1}{z_{far} - z_{near}} &amp; 0 \\
0 &amp; 0 &amp; -\frac{z_{near}}{z_{far} - z_{near}} &amp; 1
\end{bmatrix}\]

<p>Thus validating our belief that the if block is constructing an Orthographic Projection Matrix so the if-else logic would be:</p>

<div class="ida-code"><span class="kw">if</span>(<span class="var">shouldConstructOrthoProj</span>)
{
    <span class="fn">ConstructOrthoProj</span>();
}
<span class="kw">else</span>
{
    <span class="fn">ConstructPerspectiveProj</span>();
}
</div>
<p>Now for the hard part, Reversing the complete logic inside the else block…</p>

<h3 id="reversing-construction-of-the-perspective-projection-matrix"><strong>Reversing Construction of the Perspective-Projection Matrix</strong></h3>

<p>Let’s begin Reversing the else block:</p>

<p><img src="/Proj-Blog/assets/images/part-5/IDA-screenshot-elseblock.png" alt="ESP-Image1" /></p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 1</span>  <span class="kw">else</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 2</span>  {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">v33</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">v34</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 7</span>    <span class="var">tanFovXby2_dup</span> = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">v35</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">v36</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">v35</span> = <span class="var">v34</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">21</span>    <span class="var">v37</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">v38</span> = <span class="var">v36</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">v37</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span>    <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span>    <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span>    <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">v42</span> = <span class="var">v35</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span>    <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span>    <span class="var">v44</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">v44</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span>    <span class="var">v45</span> = <span class="var">v32</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span>    <span class="var">v46</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v44</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span>    <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v37</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span>    <span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span>    <span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">v47</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span>    <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span>    <span class="var">v48</span> = <span class="var">v31</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span>    <span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span>    <span class="var">v49</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">47</span>    <span class="var">v50</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">v49</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">49</span>    <span class="var">v42</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">50</span>    <span class="var">v38</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">51</span>    <span class="var">v50</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">52</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x130</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v36</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">53</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x140</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v35</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">54</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x150</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v45</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">55</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x160</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v38</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v42</span>, <span class="var">v50</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">56</span>    *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x1C</span>) = <span class="num">1.0</span> / <span class="fn">fminf</span>(<span class="var">tanFovXby2</span>, <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">57</span>  }
</div>

<h3 id="xscale-construction"><strong>xScale Construction:</strong></h3>

<p>Because the compiler interleaved the instructions for optimization, the calculation for the X and Y scales are tangled together. Let’s isolate just the xScale logic:</p>

<p><img src="/Proj-Blog/assets/images/part-5/xScale-isolate.png" alt="ESP-Image1" /></p>

<blockquote>
  <p>Lines highlighted in red are responsible for xScale calculation.</p>
</blockquote>

<p>Let’s start with this block</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">v33</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>();
</div>

<p>Using CE for dynamic analysis we can see that “(a1 + 0x234)” is FovX in radians or “0.7673” matching our Fov slider we set to “105 degrees” before.</p>

<p>It loads it as an (__m128)*(unsigned int *) which treats it as an integer but we know it is a float, it doesn’t matter what type it is as long as it’s 4 bytes. At the CPU level, 
bits are just bits..<br />
So basically load a FovX in radians into v33 then immediately divide it by 2 so now v33 holds the value <strong>fovX/2</strong>.</p>

<p>Next we see a call ucrtBase_Tanf(); which ida has failed to assign arguments to. But no worries we will look at the assembly for its arguments.</p>

<p>Following Windows ABI convention xmm0 will have the arg and result will be stored inside xmm0 as well. Tracing the assembly we see:</p>

<p><img src="/Proj-Blog/assets/images/part-5/tanf-call.png" alt="ESP-Image1" /></p>

<p>[rbx+234h] is our fovX which gets stored into xmm0, gets multiplied by 0.5 and used as an arg for tanf call. After the call the lowest 32-bits of xmm0 register will hold <strong>tan(fovX/2)</strong>.</p>

<blockquote>
  <p>After this block there are lines which use FovX which i have not highlighted, this is becuase its either trying to derive FovY with FovX or is saving the current value inside v33 for 
later use.</p>
</blockquote>

<p>Next block is:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">v36</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
</div>

<p>Moving onto the next line we see redundancy or a quirk, it is an ucrtBase_aTanf(); call and looking at the assembly the argument is v33 again so now v33 = <strong>atanf(tanf(fovX/2))</strong> which 
will equal “fovX/2”.</p>

<p><strong>why?</strong></p>

<p>FovX can only have a value from 60 to 120 degrees in game which is between -π/2 and π/2 so arctan(tan(x)) = x. calling a atanf function just to get back fovX/2 is a waste of 
CPU cycles but still negligible.</p>

<p>then:</p>

<p>v36 = v33 where v33 was fovX/2. Then multiply v33[0] by 2 and store it into v36 effectively being just FovX.</p>

<p>Next block is:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">v38</span> = <span class="var">v36</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">v37</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v36</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>v36 was fovX, now its fovX/2 after “v36 * 0.5”.</p>

<p>Then it calls a ucrtBase_Tanf() with arg as v36 so the result in v36 is <strong>tan(fovX/2)</strong>.</p>

<p>Next it saves tan(fovX/2) into v38 for later calculations and finally does “v37.m128_f32[0] = 1.0 / v36.m128_f32[0];” Completing our calculation for xScale and saving it inside v37.</p>

\[x_{scale} = \frac{1}{\tan{\left(\frac{Fov_X}{2}\right)}}\]

<h3 id="yscale-construction"><strong>yScale Construction:</strong></h3>

<p><img src="/Proj-Blog/assets/images/part-5/yScale-isolate.png" alt="ESP-Image1" /></p>

<p>Let’s start with this block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">v34</span> = <span class="var">v33</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v33</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">v35</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
</div>

<p>v34 is first assigned the value of tan(fovX/2) (as an m128 so only lowest 32 bits are fov values) then later is divided by a value at <strong>“*(float *)(a1 + 0x18)”</strong>;. With dynamic analysis we 
can see it is a constant of “1.777” which is our aspect ratio of 16:9 but the interesting part is that it is constant and wont change even when aspect ratio is 4:3 or 16:10.</p>

<p>So now v34 holds the value <strong>“tan(fovX/2) / Aspect Ratio”</strong>, Hmm this formula looks familiar…</p>

\[\tan\left(\frac{FOV_X}{2}\right) \,/\, A = \tan\left(\frac{FOV_Y}{2}\right)\]

<p>In the next line a value from “(__m128)*(unsigned int *)(a1 + 0x430)” is loaded into v35 but with dynamic analysis we can see most of the time this is zero.</p>

<p>Next block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">v35</span> = <span class="var">v34</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
</div>

<p>if “(__m128)*(unsigned int *)(a1 + 0x430)” / “v35” is zero then it will calculate the value of <strong>FovY</strong> using <strong>FovX</strong> with this formula:</p>

\[FOV_Y = 2 \cdot \tan^{-1}\!\left(\frac{\tan\!\left(\frac{FOV_X}{2}\right)}{A}\right)\]

<p>and save it inside “v35”</p>

<p>v34 previously held the value of <strong>“tan(fovX/2) / Aspect Ratio”</strong> so now after the ucrtBase_aTanf(); call with its arg being “v34”, “v34” will have the value 
<strong>atan(tan(fovX/2) / AspectRatio))</strong>. This gets saved into v35 and and immeditly after multiples v34 with 2 and saves it inside v35. So now v35 has the value:</p>

<p><strong>“2 * atan(tan(fovX/2) / AspectRatio))”</strong> matching our formula exactly!</p>

<p>Next block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>();
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">v42</span> = <span class="var">v35</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">v44</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>Next it will calculate FovY/2 and save it into “v35” then save the value of current “v35” into “v42” for later calculations.</p>

<p>Then it finally does a call to ucrtBase_aTanf(); with “v35” as arg so the value inside “v35” is <strong>tan(fovY/2)</strong>, Next it will complete the calculation for yScale by doing:</p>

<p>v44.m128_f32[0] = 1.0 / v35.m128_f32[0]; so final value inside “v44” is <strong>“1/tan(fovY/2)”</strong></p>

<blockquote>
  <p>So now <strong>v44 = 1/tan(fovY/2)</strong> and <strong>v37 = 1/tan(fovX/2)</strong> (Only the lowest 32-bits are used, rest are 0’s).</p>
</blockquote>

<h3 id="putting-it-all-together-the-cleaned-code"><strong>Putting It All Together: The Cleaned Code</strong></h3>

<p>Now that we understand the math behind both the X and Y scales, we can go back into IDA, rename our variables, and comment the IDA pseudo code.</p>

<div class="ida-code">
  <span class="kw">else</span>
  {
    <span class="comment">// --- 1. Initial Setup &amp; FovX ---</span>
    <span class="comment">// Load FovX (radians) from a1 + 0x234</span>
    <span class="var">fovX_calc</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
    <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
    
    <span class="comment">// Arg: fovX_calc. Result: fovX_calc = tan(FovX / 2)</span>
    <span class="fn">ucrtBase_Tanf</span>(); 
    
    <span class="var">fovY_calc</span> = <span class="var">fovX_calc</span>;
    <span class="var">tanFovXby2_dup</span> = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
    
    <span class="comment">// Divide by Aspect Ratio at a1 + 0x18 (hardcoded at 1.777)</span>
    <span class="comment">// fovY_calc = tan(FovX / 2) / AspectRatio</span>
    <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
    
    <span class="comment">// --- 2. The Redundant Call ---</span>
    <span class="comment">// Arg: fovX_calc. Result: atan(tan(FovX / 2)). Reverts back to FovX / 2</span>
    <span class="fn">ucrtBase_aTanf</span>(); 
    
    <span class="comment">// --- 3. FovY Calculation ---</span>
    <span class="comment">// Check if FovY is pre-calculated (usually 0)</span>
    <span class="var">fovY_val</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
    
    <span class="var">fovX_val</span> = <span class="var">fovX_calc</span>;
    <span class="comment">// fovX_val = (FovX / 2) * 2.0 -&gt; FovX</span>
    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>; 
    
    <span class="kw">if</span> ( <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
    {
      <span class="comment">// Arg: fovY_calc. Result: atan(tan(FovX / 2) / AspectRatio)</span>
      <span class="fn">ucrtBase_aTanf</span>(); 
      <span class="var">fovY_val</span> = <span class="var">fovY_calc</span>;
      
      <span class="comment">// fovY_val = 2 * atan(tan(FovX / 2) / AspectRatio)</span>
      <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
    }
    
    <span class="comment">// --- 4. Final xScale Calculation ---</span>
    <span class="comment">// fovX_val = FovX / 2</span>
    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
    
    <span class="comment">// Arg: fovX_val. Result: fovX_val = tan(FovX / 2)</span>
    <span class="fn">ucrtBase_Tanf</span>(); 
    
    <span class="var">xScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
    
    <span class="comment">// fovY_val = FovY / 2</span>
    <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
    
    <span class="comment">// Save tan(FovX / 2) for later calculations</span>
    <span class="var">tan_fovX_half_saved</span> = <span class="var">fovX_val</span>; 
    
    <span class="comment">// Final xScale = 1.0 / tan(FovX / 2)</span>
    <span class="var">xScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
    
    <span class="comment">// --- 5. Final yScale Calculation ---</span>
    <span class="comment">// Arg: fovY_val. Result: fovY_val = tan(FovY / 2)</span>
    <span class="fn">ucrtBase_Tanf</span>(); 
    
    <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
    <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>);
    <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
    
    <span class="comment">// Save tan(FovY / 2) for later calculations</span>
    <span class="var">tan_fovY_half_saved</span> = <span class="var">fovY_val</span>; 
    
    <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
    <span class="var">yScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
    
    <span class="comment">// Final yScale = 1.0 / tan(FovY / 2)</span>
    <span class="var">yScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
  }
</div>

<h3 id="depth-calculation"><strong>Depth Calculation:</strong></h3>

<p>We’ve got the xScale and yScale calculations sorted now its time to see how they calculate Depth Mapping, the values at [2][2] and [3][2].</p>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 5.2: Reversing Construction of the Projection Matrix (Depth Mapping)</title><link href="https://zero-irp.github.io/Proj-Blog/part-5.2-reversing-projection-matrix/" rel="alternate" type="text/html" title="Part 5.2: Reversing Construction of the Projection Matrix (Depth Mapping)" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-5.2-reversing-projection-matrix.md</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-5.2-reversing-projection-matrix/"><![CDATA[<style>
.ida-code{
  background:#1e1e1e;
  color:#dcdcdc;
  padding:12px;
  border-radius:8px;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size:14px;
  line-height:1.4;
  overflow-x:auto;
  white-space: pre; /* preserve spaces and linebreaks */
}

/* token classes you can use inside the div */
.ida-code .kw    { color:#569cd6; } /* keywords */
.ida-code .type  { color:#4ec9b0; } /* types */
.ida-code .fn    { color:#dcdcaa; } /* functions / intrinsics */
.ida-code .num   { color:#b5cea8; } /* numbers / hex */
.ida-code .var   { color:#9cdcfe; } /* variables */
.ida-code .const { color:#ce9178; } /* globals / constants */
.ida-code .comment{ color:#6a9955; font-style:italic; }
</style>

<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<h3 id="depth-calculation"><strong>Depth Calculation:</strong></h3>

<p>We’ve got the xScale and yScale calculations sorted, now it’s time to see how they calculate Depth Mapping, the values at [2][2] and [3][2].</p>

<h3 id="row-2"><strong>Row 2:</strong></h3>

<p><img src="/Proj-Blog/assets/images/part-5.2/22-ida-isolate.png" alt="ESP-Image1" /></p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span><span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span><span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span><span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span><span class="var">v42</span> = <span class="var">v35</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span><span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span><span class="var">v44</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span><span class="var">v44</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">v35</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span><span class="var">v45</span> = <span class="var">v32</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span><span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span><span class="var">v46</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v44</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span><span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">v32</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v37</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span><span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span><span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v31</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">v47</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
</div>

<p>We will isolate all things related to row 2 (yes, 2… we are counting rows from 0).</p>

<p>Let’s start with these lines:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span><span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span><span class="var">v45</span> = <span class="var">v32</span>;
</div>

<p>The first line seems to just load a zero from memory but the second line is interesting…<br />
Since we’ve never seen what “v35” holds let’s trace back to where it gets it value from.</p>

<p><img src="/Proj-Blog/assets/images/part-5.2/zNear-zFar.png" alt="ESP-Image1" /></p>

<p>Notice the <code class="language-plaintext highlighter-rouge">if (a2)</code> block: it conditionally swaps zNear and zFar. This seems to be a case of  Standard Depth projection VS Reverse-Z projection. 
Since my testing shows the if block does not execute, the engine is defaulting to a standard depth mapping. For the sake of this walkthrough, we will proceed with the unswapped 
variables: v31 as zFar and v32 as zNear</p>

<p>Now I will rename the variables “v31” and “v32” as zFar and zNear respectively.</p>

<p>So now:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span><span class="var">v45</span> = <span class="var">zNear</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span><span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span><span class="var">v46</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);<span class="comment">// 0.1299999952</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">yScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span><span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span><span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span><span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">v47</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
</div>

<h3 id="the-anomaly--asymmetric-offset"><strong>The Anomaly / Asymmetric offset</strong></h3>

<p>Let’s just get the anomaly out of the room now itself:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span><span class="var">v46</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);<span class="comment">// 0.1299999952</span>
</div>

<p>Through dynamic analysis we see it’s loading a constant value of “0.1299999952” and further down the line it gets placed at “[2][1]” slot of the projection matrix. This seems to be 
responsible for some kind of Asymmetric / Off-Center Frustum construction.</p>

<p>How about we just test this theory right now? Let’s try to remove the “0.13” constant from the Projection matrix and see how it would affect the render in-game.</p>

<p>Let’s do so in cheat engine by doing a trampoline hook.</p>

<p>original instruction:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>movss xmm3,[rbx+0000042C]
</code></pre></div></div>

<p>our jmp:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jmp codeCave
nop
</code></pre></div></div>

<p>Code Cave:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xorps xmm3, xmm3
jmp back
</code></pre></div></div>

<p>Here is a comparison in game with the anomaly factor enabled versus nullified</p>

<p><img src="/Proj-Blog/assets/images/part-5.2/013-convert.gif" alt="ESP-Image1" /></p>

<p>This pretty much confirms that this is an intentional asymmetric offset.</p>

<p>Let’s now continue with depth mapping calculations.</p>

<p>Starting with this block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span><span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span><span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span><span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>This is really self-explanatory…</p>

<p><code class="language-plaintext highlighter-rouge">v43 = 1 / zNear - zFar</code></p>

<p>Then:</p>

<p><code class="language-plaintext highlighter-rouge">v47 = v43 * zFar</code></p>

<p>Putting it all together we get:</p>

\[\large v_{47} = \frac{z_{far}}{z_{near} - z_{far}}\]

<p>I will now rename “v47” to “zFarByzNearNegzFar”.</p>

<p>Next is Row3!</p>

<h3 id="row3"><strong>Row3:</strong></h3>

<p><img src="/Proj-Blog/assets/images/part-5.2/32-ida-isolate.png" alt="ESP-Image1" /></p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span><span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span><span class="var">v47</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span><span class="var">v47</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">v47</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span><span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span><span class="var">v48</span> = <span class="var">zFar</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span><span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span><span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span><span class="var">v49</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">47</span><span class="var">v50</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">v49</span>;
</div>

<p>Taking this block:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span><span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span><span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span><span class="var">v48</span> = <span class="var">zFar</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span><span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>The first line for v43 is <code class="language-plaintext highlighter-rouge">1 / zNear - zFar</code></p>

<p>But in the next line the value inside zFar Changes to <code class="language-plaintext highlighter-rouge">zFar * zNear</code></p>

<p>Then it’s simply:  <code class="language-plaintext highlighter-rouge">v48 = zFar(zFar*zNear) * v43</code></p>

<p>putting it all together its:</p>

\[\large v_{48} = \frac{z_{far} \cdot z_{near}}{z_{near} - z_{far}}\]

<h3 id="putting-it-all-into-the-projection-matrix"><strong>Putting it all into the Projection Matrix:</strong></h3>

<p>Let’s isolate all lines which store the Projection Matrix into the Camera Structure:</p>

<p><img src="/Proj-Blog/assets/images/part-5.2/proj-con.png" alt="ESP-Image1" /></p>

<h5 id="row-0"><strong>Row 0:</strong></h5>

<p>Let’s see what’s happening here line by line:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
</div>

<p>This is what mm_unpacklo_ps does: <a href="../part-5.1-reversing-projection-matrix/#but-what-the-hell-is-an-_mm_unpacklo_ps">But what the hell is an _mm_unpacklo_ps??</a></p>

<p>Let’s go through what mm_unpacklo_ps does in this line:</p>

<p>going through the inner unpack first</p>

<div class="ida-code"><span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>)
</div>

<p>the xScale variable has 4 floats and only the lowest float is the “xScale” rest is zero, so:</p>

<p><code class="language-plaintext highlighter-rouge">xScale: x = 1/tan(fovX/2), y = 0, z = 0, w = 0</code></p>

<p>then (__m128)0LL just means a m128 variable with all floats set to 0.</p>

<p>unpacking this we get:</p>

<p>Result = [1/tan(fovX/2), 0, 0, 0]</p>

<p>After this it does one more unpacklo</p>

<div class="ida-code"><span class="fn">_mm_unpacklo_ps</span>(<span class="var">Result</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>)
</div>

<p>Doing this we again get:</p>

<p>Result = [1/tan(fovX/2), 0, 0, 0]</p>

<p>so you really only needed 1 mm_unpacklo_ps…</p>

<p>Then it simply stores it as “ROW 0” of the Projection matrix inside the camera structure “(a1+0xF0)”.</p>

<p>Since mm_unpacklo_ps is a pretty straightforward instruction I will not be babysitting by showing each unpack every single row, here are the lines and the result:</p>

<h5 id="row-1"><strong>Row 1:</strong></h5>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">yScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
</div>

<p>Result: [0, tan(fovY/2), 0, 0]</p>

<h5 id="row-2-1"><strong>Row 2</strong></h5>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, 
			<span class="var">zFarByzNearNegzFar</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">v46</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
</div>

<p>Alright let’s slow down here a bit:</p>

<p>here “v39” comes from:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span><span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>);
</div>

<p>which seems to just be zero.</p>

<p>v46 is the “0.1299999952” constant we’ve seen before and “0xBF800000” is hex for “-1.f”</p>

<p>unpacking all this we get:</p>

<p>Result: [0, 0.13, Far / (Near-Far), -1]</p>

<h5 id="row-3"><strong>Row 3:</strong></h5>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span><span class="var">v49</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">v49</span>;
</div>

<p>v48 is <code class="language-plaintext highlighter-rouge">far*near/Near-far</code></p>

<p>Result: [0, 0, (far*near) / (Near-far), 0]</p>

<h3 id="putting-the-code-together"><strong>Putting the code together:</strong></h3>

<div class="ida-code">
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Load 0.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span>  <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span>  <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>); <span class="comment">// Pointer to Row 0</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span>  <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>  <span class="var">tan_fovY_half_save</span> = <span class="var">fovY_val</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span>  <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span>  <span class="var">yScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// --- Final yScale Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// yScale = 1.0 / tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>  <span class="var">yScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Calculate zNear - zFar</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span>  <span class="var">v45</span> = <span class="var">zNear</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span>  <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Load the asymmetric frustum offset (0.13)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span>  <span class="var">anomaly_offset</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// --- Constructing Row 1 ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Pack and store Row 1: [0, yScale, 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>  *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">yScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// v43 = 1.0 / (zNear - zFar)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span>  <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// --- Constructing Row 0 ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Pack and store Row 0: [xScale, 0, 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>  *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// --- Constructing Row 2 ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span>  <span class="var">zFarByzNearNegzFar</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// zFarByzNearNegzFar = (1.0 / (zNear - zFar) * zFar)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span>  <span class="var">zFarByzNearNegzFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Pack and store Row 2: [0, 0.13, zFar / (zNear - zFar), -1.0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Note: 0xBF800000 is -1.0f, v39 is always 0</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>  *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">zFarByzNearNegzFar</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">anomaly_offset</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// --- Constructing Row 3 ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Modifying zFar in place: zFar = zFar * zNear</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span>  <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span>  <span class="var">v48</span> = <span class="var">zFar</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// v48 = (zFar * zNear) * (1.0 / (zNear - zFar))</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span>  <span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Used for inverse Projection</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span>  <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Pack Row 3: [0, 0, (zFar * zNear) / (zNear - zFar), 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span>  <span class="var">row3_packed</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">47</span>  <span class="var">v50</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f </span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Store Row 3</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>  *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">row3_packed</span>;
</div>

<h3 id="final-layout"><strong>Final Layout:</strong></h3>

<p>Now we have finally reverse engineered how the game constructs the Perspective-Projection Matrix per frame with it’s layout being:</p>

\[\begin{bmatrix}
\frac{1}{\tan(fovX/2)} &amp; 0 &amp; 0 &amp; 0 \\
0 &amp; \frac{1}{\tan(fovY/2)} &amp; 0 &amp; 0 \\
0 &amp; 0.13 &amp; \frac{Far}{Near - Far} &amp; -1 \\
0 &amp; 0 &amp; \frac{far \cdot near}{Near - far} &amp; 0
\end{bmatrix}\]

<p>Stored at: a1 + 0xF0 to a1 + 0x130 with a1 being the Camera Structure</p>

<h3 id="the-payoff-owning-the-camera"><strong>The Payoff: Owning the Camera</strong></h3>

<p>Because we don’t just know what the projection matrix is, we know exactly how it gets built, instruction by instruction. And in reverse engineering, understanding construction means you 
have the power of interception. We are no longer limited to what the game developers expose in the settings menu; we can force the engine to render however we want.</p>

<p>By hooking this function and manipulating the registers before the final _mm_unpacklo_ps calls, we open the door to massive engine modifications!</p>

<p>I already talked about what we could do with accesses to the projection matrix construction: <a href="../part-1-intro/#whats-the-point-of-doing-this">What’s the point of doing this?</a></p>

<p>Next up, we tackle the Inverse Projection Matrix.</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-5.1-reversing-projection-matrix/">&laquo; Part 5.1: Reversing Construction (X &amp; Y)</a>
  <a href="/Proj-Blog/part-6-reversing-inverse-projection-matrix/"> Part 6: Inverse Projection &raquo;</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Part 6: Reversing Construction of the Inverse Projection Matrix</title><link href="https://zero-irp.github.io/Proj-Blog/part-6-reversing-inverse-projection-matrix/" rel="alternate" type="text/html" title="Part 6: Reversing Construction of the Inverse Projection Matrix" /><published>2026-04-03T18:30:00+00:00</published><updated>2026-04-03T18:30:00+00:00</updated><id>https://zero-irp.github.io/Proj-Blog/part-6-reversing-inverse-projection-matrix</id><content type="html" xml:base="https://zero-irp.github.io/Proj-Blog/part-6-reversing-inverse-projection-matrix/"><![CDATA[<style>
.ida-code{
  background:#1e1e1e;
  color:#dcdcdc;
  padding:12px;
  border-radius:8px;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size:14px;
  line-height:1.4;
  overflow-x:auto;
  white-space: pre; /* preserve spaces and linebreaks */
}

/* token classes you can use inside the div */
.ida-code .kw    { color:#569cd6; } /* keywords */
.ida-code .type  { color:#4ec9b0; } /* types */
.ida-code .fn    { color:#dcdcaa; } /* functions / intrinsics */
.ida-code .num   { color:#b5cea8; } /* numbers / hex */
.ida-code .var   { color:#9cdcfe; } /* variables */
.ida-code .const { color:#ce9178; } /* globals / constants */
.ida-code .comment{ color:#6a9955; font-style:italic; }
</style>

<style>
.post-nav {
  display: flex;
  justify-content: space-between;
  margin-top: 40px;
  padding-top: 20px;
  border-top: 1px solid #444;
  font-family: Consolas, "Liberation Mono", Menlo, monospace;
  font-size: 15px;
}
.post-nav a {
  color: #569cd6;
  text-decoration: none;
  padding: 10px 16px;
  background: #1e1e1e;
  border-radius: 6px;
  transition: background 0.2s ease;
}
.post-nav a:hover {
  background: #2d2d2d;
}
</style>

<p>Now for the final matrix getting constructed by the else block!</p>

<p>I have isolated all lines relating to the construction of the Inverse Projection Matrix:</p>

<p><img src="/Proj-Blog/assets/images/part-6/ida-inv-proj.png" alt="ESP-Image1" /></p>

<p>Let’s start with row 0.</p>

<h3 id="row-0"><strong>Row 0:</strong></h3>

<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x130</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">fovX_val</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
</div>

<p>Where the last value for fovX_val was:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovX_val = FovX / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_val. Result: fovX_val = tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>(); 
</div>

<p>so Row 0 = [tan(fovX/2), 0, 0, 0]</p>

<h3 id="row-1"><strong>Row 1:</strong></h3>

<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x140</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">fovY_val</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
</div>

<p>Where the last value for fovY_val was:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovY_val = FovY / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovY_val. Result: fovY_val = tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>(); 
</div>

<p>so Row 1 = [0, tan(fovY/2), 0, 0]</p>

<h3 id="row-2"><strong>Row 2:</strong></h3>

<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x150</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v45</span>));
</div>

<p>“v45” comes from:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Calculate zNear - zFar</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span>  <span class="var">v45</span> = <span class="var">zNear</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span>  <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Modifying zFar in place: zFar = zFar * zNear</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span>  <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Used for inverse Projection</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span>  <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]; 
</div>

<p>I don’t feel like this needs explaining, all very self explanatory:</p>

\[\large v_{45} = \frac{z_{near} - z_{far}}{z_{far} \cdot z_{near}}\]

<p>Final Unpack: [0, 0, 0, (near - far) / (far * near)]</p>

<h3 id="row-3"><strong>Row 3:</strong></h3>

<div class="ida-code">*(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x160</span>) = <span class="fn">_mm_unpacklo_ps</span>(
                            <span class="fn">_mm_unpacklo_ps</span>(<span class="var">tan_fovX_half_save</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>),
                            <span class="fn">_mm_unpacklo_ps</span>(<span class="var">tan_fovY_half_save</span>, <span class="var">v50</span>));
</div>

<p>We already know “tan_fovX_half_save” is tan(fovX/2) which just before get’s multiplied with “0” while “tan_fovY_half_save” which previously had the value of “tan(fovY/2)” is multiplied 
with our anomaly “0.1299999952”.</p>

<p>These calculations are done in these lines:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Apply asymmetric offset (0.13) to tan(FovY/2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">49</span>  <span class="var">tan_fovY_half_save</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>); <span class="comment">// 0.1299999952</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Apply X-offset (0.0) to tan(FovX/2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">50</span>  <span class="var">tan_fovX_half_save</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>); <span class="comment">// 0.0</span>
</div>

<p>Next, “v50” is simply:</p>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>  <span class="comment">// Calculate 1.0 / zNear</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">51</span>  <span class="var">v50</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
</div>

<p>and “0xBF800000” is “-1”</p>

<p>Final Unpack: [0, tan(fovY/2)*0.13, -1, 1/near]</p>

<h3 id="final-layout"><strong>Final Layout:</strong></h3>

\[\begin{bmatrix}
\tan(fovX/2) &amp; 0 &amp; 0 &amp; 0 \\
0 &amp; \tan(fovY/2) &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; 0 &amp; \frac{near - far}{far \cdot near} \\
0 &amp; \tan(fovY/2) \cdot 0.13 &amp; -1 &amp; \frac{1}{near}
\end{bmatrix}\]

<p>This matches what we have seen previously in cheat engine’s memory viewer!</p>

<p><img src="/Proj-Blog/assets/images/part-4/CE-Proj.png" alt="ESP-Image1" /></p>

<h3 id="reversing-insight"><strong>Reversing Insight</strong></h3>

<p>The engine is doing something incredibly smart here. Instead of relying on a generic 4x4 matrix inversion algorithm (like Cramer’s rule) which requires a heavy, separate function call 
and eats up valuable CPU cycles. It performs a heavily optimized algebraic “fast inverse” inline. Because a projection matrix has so many known zeroes, the developers hardcoded the exact 
algebraic inverse right into the function.</p>

<blockquote>
  <p>Bit of a side track - Initially, when staring at this block, I didn’t even realize I was looking at an Inverse Projection Matrix. In hindsight it’s very obviously an inverse projection. 
We unconsciously look for standard math library calls (like a <code class="language-plaintext highlighter-rouge">MatrixInverse</code>function), so when it’s just raw, inline floating-point math, it’s easy to miss.</p>

  <p>So how do you prove a hunch when the code is ambiguous?</p>

  <p>The Scientific Method of Hypothesis ➔ Observation ➔ Conclusion!</p>

  <ul>
    <li><strong>Hypothesis:</strong> This weird block of inline math is manually constructing the Inverse Projection Matrix.</li>
    <li><strong>Observation:</strong> I dumped the originally constructed Projection Matrix directly from memory and ran it through a standard matrix inversion script on my own. I then compared my calculated output against the values the engine was generating in this second matrix.</li>
    <li><strong>Conclusion:</strong> The floats lined up exactly. Hypothesis confirmed! we have found the Inverse Projection.</li>
  </ul>
</blockquote>

<h3 id="the-full-picture"><strong>The Full Picture:</strong></h3>

<div class="ida-code"><span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 1</span>  <span class="kw">else</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 2</span>  {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 1. Initial Setup &amp; FovX ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Load FovX (radians) from a1 + 0x234</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 3</span>    <span class="var">fovX_calc</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x234</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 4</span>    <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_calc. Result: fovX_calc = tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 5</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 6</span>    <span class="var">fovY_calc</span> = <span class="var">fovX_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 7</span>    <span class="var">tanFovXby2_dup</span> = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Divide by Aspect Ratio at a1 + 0x18 (hardcoded at 1.777)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovY_calc = tan(FovX / 2) / AspectRatio</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 8</span>    <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x18</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 2. The Redundant Call ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_calc. Result: atan(tan(FovX / 2)). Reverts back to FovX / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;"> 9</span>    <span class="fn">ucrtBase_aTanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 3. FovY Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Check if FovY is pre-calculated (usually 0)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">10</span>    <span class="var">fovY_val</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x430</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">11</span>    <span class="var">fovX_val</span> = <span class="var">fovX_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovX_val = (FovX / 2) * 2.0 -&gt; FovX</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">12</span>    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">13</span>    <span class="kw">if</span> ( <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] == <span class="num">0.0</span> )
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">14</span>    {
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      <span class="comment">// Arg: fovY_calc. Result: atan(tan(FovX / 2) / AspectRatio)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">15</span>      <span class="fn">ucrtBase_aTanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">16</span>      <span class="var">fovY_val</span> = <span class="var">fovY_calc</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>      <span class="comment">// fovY_val = 2 * atan(tan(FovX / 2) / AspectRatio)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">17</span>      <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_calc</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">2.0</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">18</span>    }
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovX_val = FovX / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">19</span>    <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovX_val. Result: fovX_val = tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">20</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">21</span>    <span class="var">xScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// fovY_val = FovY / 2</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">22</span>    <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="num">0.5</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Save tan(FovX / 2) for later calculations</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">23</span>    <span class="var">tan_fovX_half_saved</span> = <span class="var">fovX_val</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 4. Final xScale Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// xScale = 1.0 / tan(FovX / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">24</span>    <span class="var">xScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Arg: fovY_val. Result: fovY_val = tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">25</span>    <span class="fn">ucrtBase_Tanf</span>(); 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">26</span>    <span class="var">v39</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>); <span class="comment">// Load 0.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">27</span>    <span class="var">v40</span> = (<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>); <span class="comment">// Pointer to Row 0</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">28</span>    <span class="var">v41</span> = <span class="var">a1</span> + <span class="num">0x130</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Save tan(FovY / 2) for later calculations</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">29</span>    <span class="var">tan_fovY_half_save</span> = <span class="var">fovY_val</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">30</span>    <span class="var">v43</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">31</span>    <span class="var">yScale</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 5. Final yScale Calculation ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// yScale = 1.0 / tan(FovY / 2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">32</span>    <span class="var">yScale</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 6. Depth Mapping Construction (Projection Matrix) ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Calculate zNear - zFar</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">33</span>    <span class="var">v45</span> = <span class="var">zNear</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">34</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Load the asymmetric frustum offset (0.13)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">35</span>    <span class="var">anomaly_offset</span> = (<span class="type">__m128</span>)*(<span class="type">unsigned int</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Pack and store Row 1: [0, yScale, 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">36</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x100</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">yScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// v43 = 1.0 / (zNear - zFar)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">37</span>    <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / (<span class="type">float</span>)(<span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] - <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Pack and store Row 0: [xScale, 0, 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">38</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0xF0</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">xScale</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">39</span>    <span class="var">zFarByzNearNegzFar</span> = <span class="var">v43</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// zFarByzNearNegzFar = (1.0 / (zNear - zFar) * zFar)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">40</span>    <span class="var">zFarByzNearNegzFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Pack and store Row 2: [0, 0.13, zFar / (zNear - zFar), -1.0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Note: 0xBF800000 is -1.0f, v39 is always 0</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">41</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x110</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">v39</span>, <span class="var">zFarByzNearNegzFar</span>), <span class="fn">_mm_unpacklo_ps</span>(<span class="var">anomaly_offset</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Modifying zFar in place: zFar = zFar * zNear</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">42</span>    <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">43</span>    <span class="var">v48</span> = <span class="var">zFar</span>; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// v48 = (zFar * zNear) * (1.0 / (zNear - zFar))</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">44</span>    <span class="var">v48</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * <span class="var">v43</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Used later for inverse Projection calculation</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">45</span>    <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">v45</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] / <span class="var">zFar</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]; 
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Pack Row 3: [0, 0, (zFar * zNear) / (zNear - zFar), 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">46</span>    <span class="var">row3_packed</span> = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v48</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">47</span>    <span class="var">v50</span> = (<span class="type">__m128</span>)<span class="num">0x3F800000u</span>; <span class="comment">// 1.0f </span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Store Row 3</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">48</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x120</span>) = <span class="var">row3_packed</span>;
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// --- 7. Inverse Projection Matrix Construction ---</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Apply asymmetric offset (0.13) to tan(FovY/2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">49</span>    <span class="var">tan_fovY_half_save</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovY_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x42C</span>); <span class="comment">// 0.1299999952</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Apply X-offset (0.0) to tan(FovX/2)</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">50</span>    <span class="var">tan_fovX_half_save</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="var">fovX_val</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] * *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x428</span>); <span class="comment">// 0.0</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Calculate 1.0 / zNear</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">51</span>    <span class="var">v50</span>.<span class="var">m128_f32</span>[<span class="num">0</span>] = <span class="num">1.0</span> / <span class="var">zNear</span>.<span class="var">m128_f32</span>[<span class="num">0</span>];
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Store Inverse Matrix Row 0: [tan(FovX/2), 0, 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">52</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x130</span>) = <span class="fn">_mm_unpacklo_ps</span>(<span class="fn">_mm_unpacklo_ps</span>(<span class="var">fovX_val</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>), (<span class="type">__m128</span>)<span class="num">0LL</span>);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Store Inverse Matrix Row 1: [0, tan(FovY/2), 0, 0]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">53</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x140</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>(<span class="var">fovY_val</span>, (<span class="type">__m128</span>)<span class="num">0LL</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Store Inverse Matrix Row 2: [0, 0, 0, near-far/far*near]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">54</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x150</span>) = <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="fn">_mm_unpacklo_ps</span>((<span class="type">__m128</span>)<span class="num">0LL</span>, <span class="var">v45</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    <span class="comment">// Store Inverse Matrix Row 3: [0, tan(fovY/2)*0.13, -1, 1/near]</span>
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">55</span>    *(<span class="type">__m128</span> *)(<span class="var">a1</span> + <span class="num">0x160</span>) = <span class="fn">_mm_unpacklo_ps</span>(
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>                                <span class="fn">_mm_unpacklo_ps</span>(<span class="var">tan_fovX_half_save</span>, (<span class="type">__m128</span>)<span class="num">0xBF800000</span>),
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>                                <span class="fn">_mm_unpacklo_ps</span>(<span class="var">tan_fovY_half_save</span>, <span class="var">v50</span>));
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">  </span>    
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">56</span>    *(<span class="type">float</span> *)(<span class="var">a1</span> + <span class="num">0x1C</span>) = <span class="num">1.0</span> / <span class="fn">fminf</span>(<span class="var">tanFovXby2_dup</span>, <span class="var">v34</span>.<span class="var">m128_f32</span>[<span class="num">0</span>]);
<span style="color:#555;user-select:none;display:inline-block;width:24px;text-align:right;margin-right:12px;border-right:1px solid #444;padding-right:8px;">57</span>  }
</div>

<h3 id="conclusion-owning-the-pipeline"><strong>Conclusion: Owning the Pipeline</strong></h3>

<p>And there we have it. We have successfully reverse-engineered the complete perspective and inverse projection matrix construction inside the Dunia Engine.</p>

<p>We are now free to Intercept, Modify and Read the matrices, We now control what the game can see.</p>

<p>A Trampoline hook here and we can control how the game engine goes from View -&gt; Clip space.</p>

<p>Hopefully, this series has demystified the process and given you the tools to tackle your own reverse engineering targets. The math might look intimidating at first, but at the CPU 
level, it all breaks down to logic, patterns, and proving your hypotheses.</p>

<p>Happy reversing!</p>

<div class="post-nav">
  <a href="/Proj-Blog/part-5.2-reversing-projection-matrix/">&laquo; Part 5.2: Reversing Construction (Depth)</a>
</div>]]></content><author><name>z1rp</name></author><summary type="html"><![CDATA[]]></summary></entry></feed>