<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Emulation | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/emulation/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/emulation/index.xml" rel="self" type="application/rss+xml"/><item><title>AI Challenge: Converting NES ROMs to the Master System/SMS</title><link>https://akitaonrails.github.io/en/2026/09/07/ai-challenge-converting-nes-roms-to-master-system-sms/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/09/07/ai-challenge-converting-nes-roms-to-master-system-sms/</guid><pubDate>Mon, 07 Sep 2026 14:00:00 -0300</pubDate><description>&lt;p&gt;For many years I had a fixed idea in my head, and I always thought it wasn&amp;rsquo;t practical: converting NES games to run on the Master System.&lt;/p&gt;
&lt;p&gt;Let me be precise. I always knew it could be done. Two Turing-complete machines can, in theory, run each other&amp;rsquo;s code. You can always translate a program from one architecture to another. The problem was never possibility, it was cost. The NES runs a 6502, the Master System runs a Z80, and over the years I read in several places that a direct 6502-to-Z80 translation always ended up running much slower, because of the architecture and hardware differences. Slow enough to never pay off in practice.&lt;/p&gt;</description><content:encoded><![CDATA[<p>For many years I had a fixed idea in my head, and I always thought it wasn&rsquo;t practical: converting NES games to run on the Master System.</p>
<p>Let me be precise. I always knew it could be done. Two Turing-complete machines can, in theory, run each other&rsquo;s code. You can always translate a program from one architecture to another. The problem was never possibility, it was cost. The NES runs a 6502, the Master System runs a Z80, and over the years I read in several places that a direct 6502-to-Z80 translation always ended up running much slower, because of the architecture and hardware differences. Slow enough to never pay off in practice.</p>
<p>And that memory has a basis. If you dig into retro-dev forums, like <a href="https://forums.nesdev.org/viewtopic.php?t=17339"target="_blank" rel="noopener">NESdev</a>, the consensus is that translating instruction by instruction is exactly the worst path: &ldquo;each instruction in the source code becomes multiple instructions of object code, and the resulting program runs much slower and takes much more memory.&rdquo; It&rsquo;s not just the CPU. The 6502 has the zero page, a dirt-cheap addressing mode the Z80 has no equivalent for, so you trade a cheap access for an expensive sequence with <code>IX</code>/<code>IY</code>. And the graphics part is even worse: the NES PPU and the Master System VDP are different beasts, with scroll, sprites, and screen mirroring that don&rsquo;t fit into each other without expensive workarounds.</p>
<p>In other words, everyone who ever thought about this reached the same conclusion: it can be done, but it runs too badly to be worth it.</p>
<p>Except now we have frontier LLMs. And I got curious: could a brute-force approach, with a good LLM in the middle of the process, get a better result than the usual naive translation? I started the <a href="https://github.com/akitaonrails/nes-to-sms"target="_blank" rel="noopener">nes-to-sms</a> project in late May, about three months ago, to find out.</p>
<p>The first result was exactly what the theory predicted: bad. A more or less direct translation generated code that runs, but way too slow. I managed to convert the entire Super Mario Bros, with sprites, levels, everything, but to make it minimally playable I had to run the Mednafen emulator at 500% overclock. After banging my head against Claude, GPT, and other models for a while, I got stuck. I worked hard through June and July, and in August I paused the project to let the dust settle.</p>
<h2>Why the Master System, and not the NES?<span class="hx:absolute hx:-mt-20" id="why-the-master-system-and-not-the-nes"></span>
    <a href="#why-the-master-system-and-not-the-nes" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before explaining the technical part, I need to explain the obsession, because it is the reason for everything.</p>
<p>I always thought the Master System was a superior console to the NES. Better CPU, better video chip, a much richer color palette. The NES works with 4 colors per background tile and 3 per sprite. The Master System works with 16-color palettes. That is a huge difference in practice.</p>
<p>The problem is that Nintendo had a brutal monopoly back then and would not let third parties release for other consoles. Anyone who wanted to make a game for the NES signed an exclusivity contract. The result is that the Master System, technically better, ended up starved of games. Basically only Sega itself released for it, and Sega&rsquo;s library never had the weight of a Castlevania, a Mega Man, a Final Fantasy.</p>
<p>Imagine what we lost. We could have had those same NES classics, but with the Master System&rsquo;s superior capability underneath. A Castlevania with more color, better sprites, better sound. We never had that, and it is exactly this historical frustration that made me want to convert NES to SMS in the first place.</p>
<h2>The port that proved the thesis<span class="hx:absolute hx:-mt-20" id="the-port-that-proved-the-thesis"></span>
    <a href="#the-port-that-proved-the-thesis" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>While my project was paused, another developer, <a href="https://github.com/lackoftrack27/Super-Mario-Bros.-SMS"target="_blank" rel="noopener">lackoftrack27</a>, published a Super Mario Bros port for the Master System that is on another level. It runs at full speed, 60fps, and on top of that with improved sprite art that takes advantage of the console&rsquo;s superior palette. It ended up looking more like the Super Mario All-Stars remaster from the Super Nintendo than the somewhat crude original NES version. The retro community was in awe.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/igOu1NQL5Ww"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>That proved my thesis in practice: the Master System is capable of running superior versions of NES games. It always was.</p>
<p>Now, two honest points about this port, because they matter for the rest of the story.</p>
<p>First: lackoftrack27&rsquo;s work is a hand reimplementation, built from the game&rsquo;s disassembly, optimized specifically for Super Mario. It&rsquo;s not a general-purpose automatic translation like the one I&rsquo;m trying to do. He solved one game, beautifully, by hand. Curiously, that reinforces what the forums already said: the path that works is reimplementing by hand, not machine-translating.</p>
<p>Second: as impressive as it is to see Super Mario on the Master System, it&rsquo;s worth remembering it is still a first-generation game, around 40kb. Meaning it is one of the simpler games on the NES. Later-generation stuff, like Super Mario Bros 3 or Kirby, nobody has tried to convert yet, and for good reasons I explain further down.</p>
<p>But the most important thing was the side effect: with the map his port gave me, I finally got my automatic conversion running properly. I came back to the project in early September, loaded lackoftrack27&rsquo;s code into Claude Fable and then the new GPT Astra, and put both of them to study what he did that I wasn&rsquo;t doing.</p>
<h2>The architecture of the solution<span class="hx:absolute hx:-mt-20" id="the-architecture-of-the-solution"></span>
    <a href="#the-architecture-of-the-solution" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Since this article is for programmers, let me open the black box.</p>
<p>The first important decision is that <code>nes-to-sms</code> is not an emulator, nor a text-to-text translator of 6502 to Z80. It is a <strong>static recompiler</strong>. It reads the NES ROM, lifts the 6502 code into an intermediate representation with explicit semantics, and from that IR it generates Z80 code. The output is a WLA-DX assembly project that compiles into a real <code>.sms</code> ROM. The core is a Rust workspace with thirteen crates, plus a hand-written Z80 runtime shared across games.</p>
<p>The flow, end to end, goes roughly like this:</p>
<ol>
<li>Parse the ROM header, split the code banks (PRG) from the tile banks (CHR), and read the reset and interrupt vectors.</li>
<li>Load a per-game profile (a TOML with labels, data regions, jump tables, mapper type, and replacements).</li>
<li>Discover the functions, build the control-flow graph, and classify every byte as code or data. A key detail enters here: every memory access is tagged with the region it belongs to (zero page, stack, RAM, PPU registers, sprite DMA, sound, mapper).</li>
<li>Lift each routine into IR, with the 6502 flags explicit. A write to the PPU register <code>$2006</code> doesn&rsquo;t become just some <code>mem[]=</code>, it becomes a first-class &ldquo;PPU write&rdquo; primitive.</li>
<li>Lower the IR to Z80. The 6502 flags are kept in a shadow status byte in RAM, and every hardware access becomes a call into the runtime.</li>
<li>Emit the Z80 bytes and also the readable WLA-DX assembly.</li>
<li>Convert the assets: the NES 2bpp tiles become SMS mode-4 4bpp tiles, the NES palette becomes SMS CRAM, and so on.</li>
<li>Write the whole SMS project, with a Makefile, runtime, and data, ready to compile.</li>
</ol>
<p>The CPU mapping is conservative on purpose. The 6502 accumulator <code>A</code> becomes the Z80 <code>A</code>, the <code>X</code> and <code>Y</code> registers live in RAM or registers, the zero page becomes a fixed block in SMS RAM, and every operation that touches a flag calls a helper. It&rsquo;s safe and correct, but this is exactly where the cost lives.</p>
<h2>Where NES and SMS really diverge<span class="hx:absolute hx:-mt-20" id="where-nes-and-sms-really-diverge"></span>
    <a href="#where-nes-and-sms-really-diverge" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The project&rsquo;s central finding is about speed, and it&rsquo;s counterintuitive.</p>
<p>The NES 6502 runs at 1.79 MHz. The Master System Z80 runs at 3.58 MHz, double the clock. You look at that and think &ldquo;so there&rsquo;s plenty of headroom.&rdquo; Except no. The Z80 spends on average about 13 cycles per instruction, while the 6502 spends about 4. In real numbers, a 3.5 MHz Z80 is roughly equivalent to a 1 MHz 6502 for general work. Meaning the SMS Z80 is actually <strong>slower</strong> than the NES 6502 for the same work. The headroom is negative.</p>
<p>Worse: the cheapest 6502 idiom, an <code>LDA table,X</code>, is one of the most expensive to emulate on the Z80. So a faithful translation of a game that already squeezed the NES to the limit simply cannot hit 60fps without overclock. The blame is on the physics of the problem, not on my translator.</p>
<p>And the video is another story. The NES sees video memory in a mapped way, with nametables, hardware scroll, and a sprite table accessed via DMA. The SMS Z80 does not address VRAM as memory: it sets an address latch and streams bytes through VDP ports. So every NES PPU write has to be captured, queued in a buffer, and flushed to the VDP during vblank. The NES flips a sprite for free, just by setting an attribute bit. The Master System has no hardware sprite flipping, so the program has to mirror the tile data in VRAM by hand, burning memory. The mid-frame screen split that NES games do, changing the scroll each scanline, also has no direct equivalent, because the VDP latches the vertical scroll at the top of the frame.</p>
<p>And there&rsquo;s still the VRAM ceiling. Super Mario&rsquo;s 8 KB of tiles become 16 KB in the SMS 4bpp format, which is the console&rsquo;s entire VRAM. Background and sprites can&rsquo;t both stay resident at once. And the code balloons 3 to 6 times in size, which is why the SMS output is already banked from the very first game, even the simplest ones.</p>
<h2>Leveraging what the SMS does best<span class="hx:absolute hx:-mt-20" id="leveraging-what-the-sms-does-best"></span>
    <a href="#leveraging-what-the-sms-does-best" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The trick to recover speed is to stop emulating NES behavior and start spending the Master System&rsquo;s strengths. The project&rsquo;s motto became &ldquo;spend ROM to buy CPU.&rdquo;</p>
<p>The SMS accepts cartridges of several megabytes. So instead of mirroring a sprite by hand at runtime, the build already generates pre-flipped variants of each sprite tile, and the flip becomes a table lookup. Instead of emulating the NES attribute table, the assembler already writes the final SMS nametable word, with the flip, palette, and priority bits baked in. The NES attribute table simply stops existing in the program. The copies to the VDP use unrolled <code>OUTI</code> blocks, faster than the generic loop. And the status-bar split, which on the NES depends on sprite-zero, becomes an SMS line interrupt, which is the native way to do it.</p>
<p>None of this is magic. The negative CPU headroom is still there, so these optimizations reduce the cost but don&rsquo;t work miracles on their own. But it&rsquo;s the difference between unplayable and playable.</p>
<h2>The real trick: making the AI run the emulator<span class="hx:absolute hx:-mt-20" id="the-real-trick-making-the-ai-run-the-emulator"></span>
    <a href="#the-real-trick-making-the-ai-run-the-emulator" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Here&rsquo;s the part I consider the most important of the whole project, and it&rsquo;s what separated the failed attempt from May from the version that finally moves.</p>
<p>Blind translation doesn&rsquo;t work. The model needs real feedback to know what&rsquo;s broken and what&rsquo;s slow. So the heart of the project is the <strong>differential oracle</strong>, the infrastructure that runs the emulators and measures reality. The translator is just the easy part.</p>
<p>It works in two layers. The first compares instruction by instruction: for each routine, the system generates random initial states, runs the original 6502 in a reference interpreter and runs the generated Z80, and compares the result (accumulator, registers, flags, touched RAM). Any divergence raises a flag. The second layer compares frame by frame: it runs the NES ROM as absolute ground truth and the SMS build side by side, with the same button script, and compares RAM byte by byte on every frame. That was later extended with an oracle that checks the SMS VRAM and palette, and with a visual oracle that compares the output against the real NES frame.</p>
<p>This measurement loop is what unlocked the recent fixes. It wasn&rsquo;t guesswork. Two concrete examples:</p>
<p>In Super Mario, every optimization step was measured in cycles per frame, with the emulator actually running. A frame&rsquo;s budget is 59,736 cycles. The baseline sat at around 370 thousand cycles per frame, something like 6 times over budget, which gave about 10fps and explained the need for that brutal 500% overclock. Measuring step by step, each change validated by the oracle, it dropped to 118,296 cycles per frame, or 1.98 times the budget. It went from 6 times to under 2 times. It now runs at full speed already in the emulator&rsquo;s standard overclock, and it&rsquo;s comfortable in the 200 to 300% range, versus the 500 to 700% from the start.</p>
<p>In Castlevania, the biggest speed win came from a profile only the emulator could reveal: a sprite-zero wait loop, at address <code>$F8C7</code>, was doing 255 status reads and exiting by timeout every single time, consuming almost 23% of the entire CPU. Without running the emulator and measuring, nobody would find this by eye. With the measurement, the fix was surgical.</p>
<p>This is the bigger argument I&rsquo;ve been hammering for a while about agents: an LLM that only generates code in the dark makes big mistakes. An LLM that can actually run the target, collect real data, and react to what it measured plays in another league.</p>
<h2>What I learned studying lackoftrack27&rsquo;s port<span class="hx:absolute hx:-mt-20" id="what-i-learned-studying-lackoftrack27s-port"></span>
    <a href="#what-i-learned-studying-lackoftrack27s-port" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>With that oracle in place, I put Claude Fable and GPT Astra to dissect lackoftrack27&rsquo;s port, which is a reimplementation of the same game from the same disassembly I translate. That settled once and for all a doubt that was blocking me: Super Mario fits on the Master System. My 6-times overrun on the budget was 100% translation overhead, not a limitation of the game or the hardware.</p>
<p>His wins, in order of impact, were roughly these:</p>
<ul>
<li><strong>The biggest of all: the data representation.</strong> He reorganized the game&rsquo;s object arrays into RAM pages, one per slot, with the same field offsets. Then the 6502&rsquo;s <code>X</code> becomes the register <code>H</code> and the field name becomes <code>L</code>, and an access that cost about 70 time units in my translator now costs 14. The cruel detail is that this only works because the programmer knows, from memory, that this particular <code>X</code> is always an object index between 0 and 6. A static translator has no way to safely discover that invariant. This is the most powerful optimization, and it&rsquo;s precisely the one automatic translation can&rsquo;t prove on its own. It&rsquo;s the residual that holds back the last 2 times in speed.</li>
<li><strong>Flags audited, not emulated.</strong> He checked the code and found only about 10 places that genuinely depend on some 6502 carry quirk. The rest uses the Z80&rsquo;s native flags. That confirmed a measurement of mine: spending energy emulating flags yields almost nothing.</li>
<li><strong>Native calls.</strong> <code>JSR</code> becoming a real <code>CALL</code>, without the expensive emulated-stack bookkeeping. That was the biggest generic win I ported over to my pipeline.</li>
<li><strong>PPU deleted at build time</strong>, not emulated at runtime, exactly as I described in the section on leveraging the SMS.</li>
<li><strong>Frame architected to overrun gracefully</strong>, turning an overrun into a clean lag frame instead of corrupting the screen.</li>
</ul>
<p>I adopted what could be generalized, and Super Mario dropped from 6 times to under 2 times the budget. This is the result running today:</p>
<div class="embed-container">
  <video controls preload="metadata" playsinline style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000;">
    <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260907152243_smb-sms-conversion.mp4" type="video/mp4">
  </video>
</div>
<p>The most honest lesson from this study is twofold. The real wins are in the data representation and the calling convention, not in what I had attacked first. And the deepest win of all is exactly what the automatic translator can&rsquo;t infer. That draws the ceiling of what can be automated pretty clearly.</p>
<h2>Castlevania and the mapper problem<span class="hx:absolute hx:-mt-20" id="castlevania-and-the-mapper-problem"></span>
    <a href="#castlevania-and-the-mapper-problem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>With Super Mario moving, I went back to my old target: the first Castlevania. And here we hit the NES&rsquo;s most serious limitation.</p>
<p>The NES is a simple console, with a bunch of limits. One of the main ones is that it doesn&rsquo;t support games bigger than about 40kb of ROM, because the 6502 only sees a 32 KB window of program in its address space. The &ldquo;solution&rdquo; from back then was brilliant and a bit crazy: parts of the console were being upgraded by the cartridge itself.</p>
<p>Many people think a cartridge is just a ROM chip with the game code. In the NES case, it&rsquo;s not. The cartridges came with a variety of extra chips that increased the console&rsquo;s capability, be it a better sound chip or, much more commonly, the <strong>mappers</strong>. There are several different mappers, from Nintendo itself and from third parties like Capcom and Konami. They use a technique called <strong>bank switching</strong>: since the 6502 can&rsquo;t see enough address space, the mapper swaps which pieces of ROM are visible in that 32 KB window, as the game asks. That&rsquo;s how big games fit into a console that, on paper, couldn&rsquo;t handle them.</p>
<p>I already explained this in detail in an old video from my channel, <a href="/2020/06/18/akitando-81-aprendendo-sobre-computadores-com-super-mario-do-jeito-hardcore/">Akitando #81, about learning computing with Super Mario the hardcore way</a>:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/hYJ3dvHjeOE"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Castlevania uses one of the earlier mappers, the UxROM. And here is the scale problem of my project: if I want to be able to translate the majority of NES games, I have to map every mapper, one by one. Today the project implements only two: the NROM, which is Super Mario&rsquo;s bare cartridge, and Castlevania&rsquo;s UxROM. All the others, the MMC1, MMC3, and company, are only planned. Each mapper is quite a bit of work to implement, because it&rsquo;s not enough to understand the NES bank switching, you have to translate that behavior into the Master System&rsquo;s own mapper.</p>
<p>The good news is that the Master System is also a banked system, so the mapping is &ldquo;the same shape one level up.&rdquo; A NES PRG bank becomes a set of SMS banks, a NES bank-switch write becomes a write to the SMS mapper through a shim, and a call that crosses banks uses the &ldquo;gate&rdquo; machinery that already exists. A routine&rsquo;s identity becomes the pair (bank, address), and a dispatch table resolves that at runtime. When it doesn&rsquo;t find it, it fails closed, with a trap, instead of running garbage.</p>
<p>This is where bigger games will hit the wall. The tile bank switching of the more advanced mappers, the scanline interrupt timing of the MMC3 and MMC5, and the SMS&rsquo;s own bank ceiling for a really big NES game, all of that is still unsolved. That&rsquo;s why I still can&rsquo;t say whether a Super Mario Bros 3 is viable or not. It&rsquo;s quite possible the bigger games die exactly at this step.</p>
<p>Either way, I worked a good bit more on Castlevania in this comeback. I fixed a pile of things using the oracle: the sprite-zero loop that ate CPU, the weapon and projectile logic that lost call returns, a scroll glitch in the status bar, the VRAM collision between 8x16 and 8x8 sprites, and the coherence between background, palette, HUD, and sprites. Today Castlevania boots, accepts start, draws a recognizable first stage, responds to the controller, and runs for a good while without crashing. But it&rsquo;s honest to say it still runs slowly, and the path I use to play it is Mednafen at 500% overclock, and even then it&rsquo;s still 2 to 3 times below its own speed target. It&rsquo;s far from pixel-perfect and real-time.</p>
<div class="embed-container">
  <video controls preload="metadata" playsinline style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;background:#000;">
    <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20260907152243_castlevania-sms-conversion.mp4" type="video/mp4">
  </video>
</div>
<h2>Conclusion<span class="hx:absolute hx:-mt-20" id="conclusion"></span>
    <a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>After so many weeks and so many attempts, I still don&rsquo;t know how much more can be squeezed out of an automatic translation. The ceiling might be close, because the deepest optimizations are precisely the ones a machine can&rsquo;t infer on its own.</p>
<p>But there&rsquo;s a path that excites me. The automatic translation can become a baseline. An SMS conversion that already runs, correct, even if slow, and that can later be optimized by hand, game by game, exactly like lackoftrack27 did with Super Mario and like so many people have been doing with decompilations and ports out there. The oracle guarantees the base is correct, and the human hand steps in to do what the machine can&rsquo;t.</p>
<p>Porting a game to much better hardware, like a PC port, is relatively easy, because there&rsquo;s capacity to spare. The truly hard part is fitting the game into a same-generation console, where nothing is to spare. And it&rsquo;s exactly that difficulty that makes the idea of making better versions of NES games for the Master System so appealing. It&rsquo;s the console that deserved to have had those games, and never did.</p>
<p>I hope more people get excited about this possibility and contribute to the project. It&rsquo;s all open at <a href="https://github.com/akitaonrails/nes-to-sms"target="_blank" rel="noopener">nes-to-sms</a>. If you enjoy 6502, Z80, VDP, and the challenge of squeezing cycles out of 40-year-old hardware, come by. There&rsquo;s plenty of room.</p>
]]></content:encoded><category>retrocomputing</category><category>coding-agents</category><category>gaming</category><category>emulation</category></item><item><title>How DriveClub and shadPS4 Almost Defeated AI and Me: How to Learn</title><link>https://akitaonrails.github.io/en/2026/04/23/driveclub-shadps4-e-ia-como-aprender/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/23/driveclub-shadps4-e-ia-como-aprender/</guid><pubDate>Thu, 23 Apr 2026 12:00:00 -0300</pubDate><description>&lt;h2&gt;Where we were&lt;span class="hx:absolute hx:-mt-20" id="where-we-were"&gt;&lt;/span&gt;
&lt;a href="#where-we-were" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Last week I &lt;a href="https://akitaonrails.github.io/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/"&gt;posted about my distrobox-gaming setup&lt;/a&gt;, with more than ten retro racing games running on Linux, from Gran Turismo 1 on DuckStation to Forza Motorsport 4 on Xenia. At the end of that piece, I described Driveclub on shadPS4 as the &amp;ldquo;final boss&amp;rdquo;: the game boots, the menu loads, the race starts, the controller responds.&lt;/p&gt;
&lt;p&gt;Except I mentioned two problems in passing and treated them as &amp;ldquo;accepted limitations&amp;rdquo;:&lt;/p&gt;</description><content:encoded><![CDATA[<h2>Where we were<span class="hx:absolute hx:-mt-20" id="where-we-were"></span>
    <a href="#where-we-were" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Last week I <a href="/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/">posted about my distrobox-gaming setup</a>, with more than ten retro racing games running on Linux, from Gran Turismo 1 on DuckStation to Forza Motorsport 4 on Xenia. At the end of that piece, I described Driveclub on shadPS4 as the &ldquo;final boss&rdquo;: the game boots, the menu loads, the race starts, the controller responds.</p>
<p>Except I mentioned two problems in passing and treated them as &ldquo;accepted limitations&rdquo;:</p>
<ol>
<li><strong>During daytime, the image is a bit darker than on a reference PS4.</strong> Not alarming, but noticeable.</li>
<li><strong>At night, the race start in Canada or Munnar went pitch black for 10 to 30 seconds</strong> before the game &ldquo;recovered on its own&rdquo; around the 1:30 mark. During those seconds, only the HUD was visible. Track, car, headlights — all absolute black.</li>
</ol>
<p>In the previous article I pushed the explanation to &ldquo;it&rsquo;s just not fixed in the emulator yet, I&rsquo;ll live with it&rdquo;. But inside I couldn&rsquo;t make myself play the game in a visually unplayable state. I wanted to play DriveClub at its best, and I didn&rsquo;t want to sit around waiting for the project to fix it for me. I decided to take matters into my own hands. Thirty seconds of absolute black isn&rsquo;t &ldquo;a bit darker&rdquo;. It&rsquo;s a serious bug, the kind that makes a game unplayable in practice.</p>
<p>This article is what happened between Sunday night and Thursday morning.</p>
<h2>The blackout, live<span class="hx:absolute hx:-mt-20" id="the-blackout-live"></span>
    <a href="#the-blackout-live" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/original-blackout.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>This video is the behavior I wanted to fix. Race start in India (Munnar), night race. You see the track, headlights turn on, the first few seconds run. Then it darkens progressively. Within about 15 seconds everything is black. Stays that way for another 60-90 seconds. Then the game &ldquo;recalibrates&rdquo; on its own and the track reappears.</p>
<p>If you&rsquo;re a player, you don&rsquo;t care why. You just want to drive.</p>
<h2>Why shadPS4 is hard to debug<span class="hx:absolute hx:-mt-20" id="why-shadps4-is-hard-to-debug"></span>
    <a href="#why-shadps4-is-hard-to-debug" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://shadps4.net/"target="_blank" rel="noopener">shadPS4</a> <strong>still has no stable release</strong>. The project is under active development. The main branch changes several times a week, configs migrate from TOML to JSON without notice, settings hide behind &ldquo;Advanced&rdquo; so users with incompatible hardware don&rsquo;t file issues, open PRs compete over different approaches to the same problem.</p>
<p>Anyone trying to configure DriveClub today will find:</p>
<ul>
<li>September/2025 Reddit guides using <code>readbacks = true</code> (it was a boolean back then) saying &ldquo;for DriveClub always enable readbacks&rdquo;.</li>
<li>November/2025 guides saying &ldquo;disable readbacks, it kills performance&rdquo; (because in 2025-07 readbacks became an enum <code>Disabled | Relaxed | Precise</code> with different performance profiles, and Bloodborne started hanging with it on).</li>
<li>January/2026 YouTube videos using some custom fork nobody documented.</li>
<li>A thread on shadps4.net with three different, mutually exclusive explanations for the blackout.</li>
<li>My own notes from last week in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>distrobox-gaming/docs/driveclub-shadps4.md</code></a> saying <code>readbacks_mode: 0</code>. The exact opposite of what I found this week.</li>
</ul>
<p>So the information exists, but it&rsquo;s scattered, dated, contradictory, and most of it depends on version details that changed between the original post and today. Reproducing a setup from a YouTube video is like trying to solve a Rubik&rsquo;s cube blindfolded.</p>
<h2>Spoiler: the solution is one integer<span class="hx:absolute hx:-mt-20" id="spoiler-the-solution-is-one-integer"></span>
    <a href="#spoiler-the-solution-is-one-integer" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>After 31 phases of investigation, 44 commits on my shadPS4 fork, 15,668 lines of instrumental code added, and three days of nearly uninterrupted debugging, the answer is <strong>one line in the per-game config</strong>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;GPU&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;readbacks_mode&#34;</span><span class="p">:</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>readbacks_mode: 2</code> is the <code>Precise</code> mode. Explaining what it does requires understanding how DriveClub implements auto-exposure, and that&rsquo;s where the journey starts. To know WHY this toggle fixes things, you have to understand the whole chain.</p>
<p>The short version: <strong>DriveClub implements auto-exposure as a GPU→CPU feedback loop</strong>.</p>
<ol>
<li>The scene renders to an HDR target.</li>
<li>A compute shader calculates a luminance histogram into an SSBO.</li>
<li>The CPU <strong>reads</strong> that SSBO on the next frame and derives a target exposure.</li>
<li>The CPU writes that exposure back into a 1936-byte lighting UBO (at slots <code>[38] [48] [50]</code>).</li>
<li>Fragment shaders read that UBO and multiply scene luminance by it.</li>
</ol>
<p>Without <code>readbacks_mode: Precise</code>, step 3 reads stale zeros. The memory page where the GPU wrote the histogram is never synchronized to the CPU side. The CPU exposure integrator concludes &ldquo;scene is dark, open the aperture all the way&rdquo; and ramps its output value monotonically: <code>2.59 → 7.84 → 24 → 90 → 179 → 255</code>. Within 60-90 seconds the scale saturates so high that everything clips to zero. That&rsquo;s the pitch black.</p>
<p>With <code>readbacks_mode: Precise</code> on, the emulator marks the pages the GPU just wrote as kernel-protected (via <code>mprotect</code> on Linux). When the CPU tries to read one of those pages for the first time, a page fault fires. The emulator intercepts it, issues a <code>vkCmdCopyBuffer</code> download from the GPU buffer to the host staging area, waits on the scheduler, and the CPU finally sees fresh data. The real histogram enters the integrator, exposure converges normally, and the race start opens bright as it should.</p>
<p>Why isn&rsquo;t it the default? Three documented reasons. First, per-page <code>mprotect</code> + per-fault GPU stall + compute dispatch + scheduler wait has a real cost: on NVIDIA&rsquo;s warm path it&rsquo;s tolerable, on AMD it tanks below 12 FPS, with an <a href="https://github.com/shadps4-emu/shadPS4/issues/3322"target="_blank" rel="noopener">open issue</a> about it. Second, <a href="https://github.com/shadps4-emu/shadPS4/issues/3826"target="_blank" rel="noopener">Bloodborne hangs</a> on loading screens with Precise enabled. Third, the maintainer tucked the option behind &ldquo;Advanced&rdquo; in the UI precisely to keep users with sensitive hardware from enabling it by mistake. The mode has existed since <code>v0.15.0</code> (September/2025), but <strong>nobody had publicly connected <code>Precise</code> + DriveClub + auto-exposure feedback loop</strong>. The <a href="https://github.com/shadps4-emu/shadPS4/issues/3346"target="_blank" rel="noopener">compat tracker already admits</a> that DriveClub &ldquo;requires readbacks enabled to function properly&rdquo;, but doesn&rsquo;t specify the mode.</p>
<p>In my setup the performance cost is irrelevant. I have an RTX 5090 on my desktop, plenty of hardware to swallow the mprotect, the page fault, the copy and the stall per frame. The call is trivial: I flip Precise on, pay the price, the game runs. On more modest hardware or on AMD the conversation is different, which is exactly why this setting isn&rsquo;t default and hides behind &ldquo;Advanced&rdquo;.</p>
<p>One thing I want to make clear: this whole explanation above (<code>Precise</code> vs <code>Relaxed</code> vs <code>Disabled</code>, GPU→CPU feedback loop, per-page mprotect syscall) is not knowledge I had on Sunday night. I didn&rsquo;t know what a buffer readback was, didn&rsquo;t know DriveClub implemented auto-exposure as a GPU→CPU feedback loop, didn&rsquo;t know <code>readbacks_mode</code> had become an enum in 2025 with three values. To arrive at the right answer, I had to learn every piece from scratch. I couldn&rsquo;t guess. Had I tried to &ldquo;enable readbacks&rdquo; last week without understanding the chain, I&rsquo;d have picked the wrong value (Relaxed isn&rsquo;t enough for this feedback loop, as I&rsquo;ll explain below) and given up thinking &ldquo;it doesn&rsquo;t work&rdquo;.</p>
<p>Two more things need to be in place for this to work, both covered in the <a href="/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/#driveclub-the-impossible-finally-possible">previous article</a>:</p>
<ul>
<li><strong>v1.28 patch applied</strong> on top of the v1.00 base install (without it, content stays locked with &ldquo;not yet released&rdquo;).</li>
<li><strong>60fps XML patch disabled</strong> (it raises the render rate but the logic tickrate is fixed; with it on, the game runs in slow motion).</li>
</ul>
<h2>The result<span class="hx:absolute hx:-mt-20" id="the-result"></span>
    <a href="#the-result" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/canada-fixed.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Same race in Canada, night, with <code>readbacks_mode: 2</code> on. Race start opens bright, auto-exposure converges immediately, the night transition unfolds naturally as the game&rsquo;s TOD (time of day) advances. No blackout, no &ldquo;recalibration&rdquo; at 1:30, no 30-second window of absolute black. This is DriveClub running like it does on PS4.</p>
<p>Now the interesting question is not &ldquo;which setting&rdquo;. It&rsquo;s: <strong>how did we get here?</strong></p>
<h2>How does a junior learn today?<span class="hx:absolute hx:-mt-20" id="how-does-a-junior-learn-today"></span>
    <a href="#how-does-a-junior-learn-today" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>There&rsquo;s a question I hear often: &ldquo;with AI doing everything, how does a junior learn?&rdquo;</p>
<p>The premise is wrong. <strong>AI doesn&rsquo;t do everything.</strong> AI does what you ask it to. It doesn&rsquo;t discover on its own. It has no domain initiative. It&rsquo;s not capable of telling you &ldquo;hey, this looks like a broken GPU→CPU readback feedback loop&rdquo; unless you already know there&rsquo;s a thing called a readback and that broken feedback loops are a class of bug.</p>
<p>Worse: if you go to an AI agent today and ask &ldquo;fix this black screen in DriveClub on shadPS4&rdquo;, it will give you a list of possible answers based on old forum posts, with a high probability of steering you wrong. It will suggest the 60fps XML patch (wrong — causes slow motion). It will suggest a tonemap override (wrong — the game already writes correct SDR). It will suggest a vblank_frequency tweak (useless for the real problem). It will suggest manual gamma (treats the symptom, not the cause).</p>
<p>The way to discover is to <strong>dive into the chain</strong>, phase by phase, ruling out wrong hypotheses until the terrain is familiar enough that you recognize the right answer when it appears.</p>
<p>That&rsquo;s where a lot of people misread me. &ldquo;Akita is already a senior, he has 25+ years of experience, of course he knows.&rdquo; <strong>False.</strong> A senior is someone who has been a junior in every topic they master. And they keep becoming a junior every time they pick up a new domain. I have 25 years of web programming, distributed systems, Ruby, Erlang, Go. But <strong>I&rsquo;ve never programmed for PS4</strong>. I&rsquo;d never looked at the shadPS4 codebase. I didn&rsquo;t know PS4&rsquo;s graphics architecture. On Sunday I was an absolute junior in this domain.</p>
<p>But junior doesn&rsquo;t mean totally green. A while back I had explored on my YouTube channel the low-level architecture of older consoles (from the NES&rsquo;s 6502 onward) and how emulators work internally in general. The two videos below helped build the basic vocabulary for how a game console works under the hood, and made sure I wouldn&rsquo;t trip on concepts like fetch-decode-execute, opcode, interrupt, or HLE vs LLE.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/hYJ3dvHjeOE"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>



<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/vUqLLpUJ47s"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>I had also made an older video on the evolution of CPUs, GPUs, DirectX, and Vulkan, so at least the vocabulary of &ldquo;modern graphics pipeline&rdquo; and &ldquo;shading language&rdquo; wasn&rsquo;t alien.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/JEp7ozWqIps"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Knowing that <code>VkCommandBuffer</code> exists is very different from knowing where in shadPS4 a readback <code>vkCmdCopyBuffer</code> fires. But it gives you a base. That kind of old curiosity paid interest now.</p>
<h2>The rotation: Claude Code and Codex<span class="hx:absolute hx:-mt-20" id="the-rotation-claude-code-and-codex"></span>
    <a href="#the-rotation-claude-code-and-codex" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>AI (in my case <a href="https://claude.com/claude-code"target="_blank" rel="noopener">Claude Code</a>) didn&rsquo;t &ldquo;know&rdquo; more than I did. It has the same dated forum text you&rsquo;d find on Reddit. What it did well was <strong>aggregate</strong>: read shadPS4 source code in parallel with me, index comments, cross-reference between phase docs, compile, run probes, parse 61 MB logs, compare decompiled binary against UBO diffs, shine light on corners of the system faster than I could alone.</p>
<p>But the decision to &ldquo;keep investigating&rdquo; was mine. The perseverance of &ldquo;don&rsquo;t accept a mitigation, want the root cause&rdquo; was mine. And when the AI suggested &ldquo;honestly, we should just accept the current result and document it&rdquo; (and it suggested this <strong>several times</strong> over the four days), I was the one who had to say &ldquo;no, we keep going&rdquo;.</p>
<p>The central point about the rotation: <strong>no single LLM managed to close this problem alone.</strong></p>
<p>Over the four days, Claude Code repeatedly got stuck looping on the same hypotheses with no new direction. When that happened, I&rsquo;d switch to Codex for a few hours to bring in outside ideas, different probes, a re-read of the codebase from a fresh angle. Then back to Claude Code to integrate what Codex had surfaced. And so on, alternating.</p>
<p>The final resolution happened on Claude Code, but the journey switched models several times. Each LLM had its biases and blind spots. Left alone, each one would have given up sooner.</p>
<p>What broke the impasse was me continuing to bring in new ideas to try, forcing the model to reassess, switching models whenever one started to repeat itself.</p>
<p>At two points things went past &ldquo;just repeating hypotheses&rdquo;. Codex literally had an agentic-loop glitch: it started repeating the exact same &ldquo;solution&rdquo; it had just tried, over and over, in sequence, without noticing what it was doing. It was the first time I&rsquo;d ever seen that happen in any AI agent. I had to kill the session and start a fresh one from zero to break the cycle.</p>
<p>That says something about context size and session duration. The complexity and pace of this investigation were enough to bring both LLMs to their knees, each in their own way.</p>
<p>The whole journey below is documented in 33 phase docs on the <a href="https://github.com/akitaonrails/shadPS4/tree/gamma-debug/docs/driveclub-investigation"target="_blank" rel="noopener"><code>gamma-debug</code> branch of my shadPS4 fork</a>. Each section here links to the original doc for anyone who wants the complete detail. Here I summarize the essentials: the hypothesis we had, what we tried, what new concept we had to learn, and why the phase didn&rsquo;t close the case.</p>
<h2>Session zero: baseline and reproducibility<span class="hx:absolute hx:-mt-20" id="session-zero-baseline-and-reproducibility"></span>
    <a href="#session-zero-baseline-and-reproducibility" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Every serious investigation starts before Phase 1. The first thing, for any project, is to make sure the <strong>baseline works as expected</strong> and that the situation you want to research is <strong>reproducible every time</strong>. If you can&rsquo;t reproduce the bug on demand, you&rsquo;re chasing ghosts. Every probe after that will be inconclusive because the bug &ldquo;appeared and vanished&rdquo; without control. And if the baseline is broken in some way you haven&rsquo;t noticed, every experiment measures the wrong problem. Without those two guarantees, you can&rsquo;t make progress.</p>
<p>In my case, on Sunday night I confirmed:</p>
<ul>
<li>The game boots all the way to the race screen without crashing, using the <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>distrobox-gaming</code></a> config.</li>
<li><a href="https://github.com/Nenkai/DriveClubFS"target="_blank" rel="noopener">DriveClubFS</a> extracts v1.28 cleanly, 8018 files, ~47 GB.</li>
<li>MSAA depth resolve applied on the fork; night tracks render shapes instead of uniform absolute black (covered in the <a href="/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/">previous article</a>).</li>
<li>8BitDo controller detected.</li>
<li>Night race in India 19:30 <strong>reproduces the blackout on 100% of attempts</strong>, with the same temporal pattern: progressive fade in the first ~15s, pitch black between ~20s and ~90s, &ldquo;recalibration&rdquo; at 1:30.</li>
</ul>
<p>That baseline is the only solid ground that lets me compare each experiment reliably. Without it, a probe that &ldquo;worked&rdquo; might just be a different cache state, not the real fix. With it, every change has a before and an after that are measurable.</p>
<p>Beyond that, zero specific knowledge of:</p>
<ul>
<li>PS4 PKG format (PFS, param.sfo, disc_info, keystone).</li>
<li>shadPS4 codebase (src/core, src/video_core, shader_recompiler, buffer_cache).</li>
<li>PS4 graphics pipeline (GCN ISA, forward+ lighting, MSAA depth resolve).</li>
<li>Vulkan beyond the surface (descriptor sets, UBO binding, push constants, pipeline cache).</li>
<li>SPIR-V disassembly and patching.</li>
<li>PS4 OELF loader, Itanium RTTI, SCE dynamic relocations.</li>
<li>mprotect-based page-fault tracking for CPU-GPU synchronization.</li>
</ul>
<h2>The journey, phase by phase<span class="hx:absolute hx:-mt-20" id="the-journey-phase-by-phase"></span>
    <a href="#the-journey-phase-by-phase" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ll group the 30+ phases into buckets that make sense as a narrative. Each phase links to the full doc for anyone who wants technical detail.</p>
<blockquote>
  <p><strong>Note for anyone who doesn&rsquo;t want to read all 30+ phases:</strong> after the phase sequence there are reflection and methodology sections you can read on their own. <a href="#the-shotgun-technique">The shotgun technique</a> explains the debugging methodology I used throughout the investigation. <a href="#what-i-knew-on-sunday-vs-thursday">What I knew on Sunday vs Thursday</a> sums up what I actually learned. <a href="#the-ai-suggested-accepting-the-perseverance-was-mine">The AI suggested accepting. The perseverance was mine</a> discusses AI&rsquo;s real role in this process. <a href="#the-real-cost-of-this-journey">The real cost of this journey</a> catalogs what got produced. If you want to jump straight to the argument, those are the shortcuts.</p>

</blockquote>
<h3>Phase 01: pipeline cache, the warm-up<span class="hx:absolute hx:-mt-20" id="phase-01-pipeline-cache-the-warm-up"></span>
    <a href="#phase-01-pipeline-cache-the-warm-up" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-01-shader-compile-stalls.md"target="_blank" rel="noopener"><code>phase-01-shader-compile-stalls.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> menus run at 2-3 fps on first launch. Must be Vulkan pipeline compilation happening in real time.</li>
<li><strong>Action:</strong> enable <code>pipeline_cache_enabled: true</code> in the global config.</li>
<li><strong>New concept:</strong> Vulkan pipeline caching. When the emulator translates a PS4 GCN shader into SPIR-V and then into driver-specific Vulkan binary, it can cache that binary. Without the cache, it recompiles every cold launch (~864 shaders + ~590 pipelines).</li>
<li><strong>Outcome:</strong> solved. First session pays the cost; subsequent sessions read from disk and launch immediately. Easy. Also my first lesson about the shadPS4 codebase.</li>
</ul>
<h3>Phase 02: gamma dim, starting in the wrong direction<span class="hx:absolute hx:-mt-20" id="phase-02-gamma-dim-starting-in-the-wrong-direction"></span>
    <a href="#phase-02-gamma-dim-starting-in-the-wrong-direction" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-02-gamma-dim-image.md"target="_blank" rel="noopener"><code>phase-02-gamma-dim-image.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> DriveClub&rsquo;s image is dim compared to reference. Must be wrong sRGB encoding or an HDR tonemap applied badly by the emulator.</li>
<li><strong>Action:</strong> instrumented the swapchain to log format, added <code>SHADPS4_PP_*</code> env vars to experiment, built a post-processing pipeline with three knobs (exposure, ACES tonemap, gamma curve).</li>
<li><strong>New concept:</strong> sRGB OETF, HDR tonemap (ACES vs Reinhard), Bayer dither as anti-banding.</li>
<li><strong>Outcome:</strong> via bypass path (<code>SHADPS4_PP_BYPASS=1</code>, writes raw game output with no post-processing) I discovered the <strong>game writes correct SDR already</strong> into the framebuffer. The emulator&rsquo;s post-processing pipeline was mangling the signal. Final shader became simply sRGB encode + Bayer dither. Dim persists, but it&rsquo;s not a gamma problem.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/wrong-gamma.png" alt="Gamma adjustments going wrong everywhere"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/wrong-gamma-blown.png" alt="Blown out: gamma too high"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/wrong-gamma-sky-dark.png" alt="Dark sky: gamma too low"  loading="lazy" /></p>
<p>Three hours on this phase. The most important lesson: <strong>when &ldquo;the image looks wrong&rdquo;, the first diagnostic should be a bypass path that shows raw game output</strong>. If bypass looks right, the emulator is the one mangling the signal. Stop touching the tonemap.</p>
<h3>Phase 03: v1.28 content access<span class="hx:absolute hx:-mt-20" id="phase-03-v128-content-access"></span>
    <a href="#phase-03-v128-content-access" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-03-v128-content-access.md"target="_blank" rel="noopener"><code>phase-03-v128-content-access.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> v1.28 extracts, but the game says &ldquo;content not released yet, download required&rdquo;.</li>
<li><strong>Action:</strong> merge v1.28 on top of v1.00, restore <code>param.sfo</code>, <code>disc_info.dat</code>, <code>keystone</code>.</li>
<li><strong>New concept:</strong> PS4 cumulative patches, package metadata.</li>
<li><strong>Outcome:</strong> solved. Detailed in the <a href="/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/">previous article</a>.</li>
</ul>
<h3>Phase 04: MSAA depth + slow motion<span class="hx:absolute hx:-mt-20" id="phase-04-msaa-depth--slow-motion"></span>
    <a href="#phase-04-msaa-depth--slow-motion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-04-slowness.md"target="_blank" rel="noopener"><code>phase-04-slowness.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> night tracks are pitch black and the game &ldquo;feels slow&rdquo;.</li>
<li><strong>Action:</strong> two separate things. Disable the 60fps XML patch (which was decoupling render rate from logic tickrate). Implement <code>ReinterpretMsDepthAsColor</code> on my fork (to let the emulator resolve 4x MSAA depth to 1-sample color, which DriveClub needs for forward+ lighting on night tracks).</li>
<li><strong>New concept:</strong> forward+ lighting (a renderer that writes the scene to an MSAA depth target and then reads it as color for SSAO and volumetrics), MSAA depth resolve.</li>
<li><strong>Outcome:</strong> both fixed. Detailed in the <a href="/en/2026/04/19/retrogames-de-corrida-favoritos-no-distrobox/">previous article</a>.</li>
</ul>
<h3>Phase 05: second pass on brightness<span class="hx:absolute hx:-mt-20" id="phase-05-second-pass-on-brightness"></span>
    <a href="#phase-05-second-pass-on-brightness" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-05-brightness-followup.md"target="_blank" rel="noopener"><code>phase-05-brightness-followup.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> since the game writes correct SDR, maybe a static boost in post-processing helps without touching the UBO feedback loop.</li>
<li><strong>Action:</strong> tried linear boost (1.5x, 2.0x), gamma pre-encode (pow curve), scene-aware auto-exposure with peak+mean sampling.</li>
<li><strong>Outcome:</strong> all rolled back. Linear boost clips highlights. Gamma introduces banding. My auto-exposure layer cancels the user&rsquo;s manual brightness slider. There&rsquo;s no free lunch in post-processing. Dim stays dim without real HDR or shader patches.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/wrong-gamma-sky-dark.png" alt="Sky too dark"  loading="lazy" /></p>
<h3>Narrowing the blackout: the shared gate<span class="hx:absolute hx:-mt-20" id="narrowing-the-blackout-the-shared-gate"></span>
    <a href="#narrowing-the-blackout-the-shared-gate" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/race-start-blackout-040420.md"target="_blank" rel="noopener"><code>race-start-blackout-040420.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> the blackout has a curious signature. Main view goes dim/black, rearview mirror goes absolute black, HUD stays bright. Three layers with different behaviors suggest a shared &ldquo;gate&rdquo;, not a common fade.</li>
<li><strong>Action:</strong> instrumented texture-cache aliasing, probed with live fragment shader substitution on suspected inputs.</li>
<li><strong>New concept:</strong> texture-cache aliasing (multiple images sharing a memory address, which the emulator cache reuses), render-target lifetime.</li>
<li><strong>Outcome:</strong> points to a shared &ldquo;presentation permission&rdquo; between the main scene and the mirror, not a scripted fade.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/screenshot-2026-04-20_21-22-45.png" alt="Blackout baseline"  loading="lazy" /></p>
<h3>Phases 07-09: texture torture, UI surgery, binary patch plan<span class="hx:absolute hx:-mt-20" id="phases-07-09-texture-torture-ui-surgery-binary-patch-plan"></span>
    <a href="#phases-07-09-texture-torture-ui-surgery-binary-patch-plan" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-07-aggressive-torture-probe.md"target="_blank" rel="noopener"><code>phase-07-aggressive-torture-probe.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-08-ui-asset-surgery.md"target="_blank" rel="noopener"><code>phase-08-ui-asset-surgery.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-09-eboot-binary-patch-plan.md"target="_blank" rel="noopener"><code>phase-09-eboot-binary-patch-plan.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> the race-start dim overlay is an asset. Small texture, UI panel, or a method of the <code>FreeplayGetInCar</code> controller in the eboot.</li>
<li><strong>Action:</strong> null-substituted BC-compressed materials, small texture candidates, four-vector weight masks during critical draws. Disabled UI panels (<code>loading_freeplay.txt</code>, <code>get_in_car_animation.txt</code>, <code>vehicle_select_background</code>). Extracted the OELF, located <code>FreeplayGetInCar</code> ASCII strings, identified the constructor at VA 0xf89b00 and the vtable at 0x15dc3e0.</li>
<li><strong>New concept:</strong> PS4 OELF loader (Sony&rsquo;s variant of ELF), Itanium C++ RTTI (how C++ encodes type info in vtables), SCE dynamic relocations (placeholders that only the dynamic loader resolves at runtime).</li>
<li><strong>Outcome:</strong> nulls work but the fade persists. The overlay renders directly into HDR scene targets, not through the panel system. Binary patching has relocation placeholders that aren&rsquo;t statically resolvable without a Ghidra + PS4 plugin. <strong>Asset-side surface exhausted.</strong> Six hours of debugging, final conclusion: the bug lives in state, not in an asset.</li>
</ul>
<h3>Phase 10: rethink + animlib<span class="hx:absolute hx:-mt-20" id="phase-10-rethink--animlib"></span>
    <a href="#phase-10-rethink--animlib" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-10a-rethink-040422.md"target="_blank" rel="noopener"><code>phase-10a-rethink-040422.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-10b-video-diagnostic-animlib.md"target="_blank" rel="noopener"><code>phase-10b-video-diagnostic-animlib.md</code></a></p>

</blockquote>
<p>Tuesday morning. Sat down, rebooted mentally. New model: the blackout is a <strong>state-machine gate</strong>. It&rsquo;s not &ldquo;scene not ready&rdquo;, it&rsquo;s &ldquo;permission transition&rdquo;. The gate closes after the starting grid appears. Next probe: runtime dispatch trace, enough of the blind asset surgery.</p>
<p>In parallel: frame extraction from the recording at 10 Hz, cross-referenced against keyframes of the animation library (<code>animlib</code>) in <code>india_posteffects.lvl</code>. Found a smoking gun. The <code>MasterBrightness</code> track has keyframes 0.003 and 0.0007 (0.63% brightness) where the default would be 1.0. Animlib beats inline defaults every frame.</p>
<ul>
<li><strong>New concept:</strong> animation library, keyframe encoding, binary float-scanning.</li>
</ul>
<video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/debugging-blackout-fade.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Video above: the process of probing the fade in real time.</p>
<h3>Phase 11: multi-scalar patch<span class="hx:absolute hx:-mt-20" id="phase-11-multi-scalar-patch"></span>
    <a href="#phase-11-multi-scalar-patch" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-11-multi-scalar-patch.md"target="_blank" rel="noopener"><code>phase-11-multi-scalar-patch.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> patching <code>MasterBrightness</code> + <code>AutoTargetLuminance</code> + <code>ManualAutoMix</code> simultaneously will lift the blackout.</li>
<li><strong>Action:</strong> extended <code>PatchAnimlibFloatValues</code> to rewrite three scalars. Instrumented with <code>SHADPS4_DC_DRAWLOG</code> + <code>SHADPS4_DC_UBOLOG</code>.</li>
<li><strong>Outcome:</strong> the patch produces a real <strong>numeric</strong> lift (1.2 → 6.5 in 255, a 5×), but still sub-perceptual black. And the crucial finding: the mirror works normally after the patch. It&rsquo;s not gated, just reflecting a dark scene. <strong>At least 285× of additional attenuation lives downstream of the scalars we can patch.</strong></li>
</ul>
<h3>Phase 12: bisect closes the asset side<span class="hx:absolute hx:-mt-20" id="phase-12-bisect-closes-the-asset-side"></span>
    <a href="#phase-12-bisect-closes-the-asset-side" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-12-bisect-closes-asset-side.md"target="_blank" rel="noopener"><code>phase-12-bisect-closes-asset-side.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> one of the 60 accumulated patches broke the mirror independently from the blackout. Bisect.</li>
<li><strong>Action:</strong> binary bisect across 60 overlay files over 5 rounds.</li>
<li><strong>Outcome:</strong> <code>x_live_lobby_pre_race.txt</code> (rerouting loading-spinner transition from <code>ZoomInAndFade</code> to <code>NoFade</code>) was the mirror killer. Rolled back. <strong>Asset side completely closed.</strong> The driver lives in compiled code or post-process state, not in a game asset.</li>
</ul>
<h3>Asset sweeps 01-09 (consolidated)<span class="hx:absolute hx:-mt-20" id="asset-sweeps-01-09-consolidated"></span>
    <a href="#asset-sweeps-01-09-consolidated" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/asset-sweep-09-conclusions.md"target="_blank" rel="noopener"><code>asset-sweep-09-conclusions.md</code></a></p>

</blockquote>
<p>In parallel with the phases above, I swept 12 RPK substitutions: <code>FreeplayGetInCar</code> page swaps, postfx edits in <code>india_landscape_gui.rpk</code>, <code>globaldata</code> RTT-only edits, prerace state-string rewrites. <strong>All clean misses.</strong> No asset family touches the blackout. Lesson: <strong>stop chasing by names; chase by state transitions.</strong></p>
<h3>Phase 13: UBO dump breakthrough<span class="hx:absolute hx:-mt-20" id="phase-13-ubo-dump-breakthrough"></span>
    <a href="#phase-13-ubo-dump-breakthrough" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-13-ubo-dump-breakthrough.md"target="_blank" rel="noopener"><code>phase-13-ubo-dump-breakthrough.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> eye-adaptation feedback loop with stale input is the culprit. A luminance scalar growing without converging.</li>
<li><strong>Action:</strong> dump 128 bytes of UBO in hex during the race window across six hand-picked pipelines. Look for monotonically changing values.</li>
<li><strong>New concept:</strong> <strong>UBO (Uniform Buffer Object)</strong>, a shared memory region the CPU writes and shaders read. <strong>Auto-exposure integrator</strong>, a classic eye-adaptation component that takes time to converge toward a target.</li>
<li><strong>Outcome:</strong> found it. <strong>UBO offset 3 climbs monotonically from 2.59 → 7.84 → 24 → 90 → 179 → 255</strong> over 2 seconds. Textbook feedback loop with broken input. First real hit in 12 phases.</li>
</ul>
<h3>Phase 14: scene-pipeline UBOs are the read side<span class="hx:absolute hx:-mt-20" id="phase-14-scene-pipeline-ubos-are-the-read-side"></span>
    <a href="#phase-14-scene-pipeline-ubos-are-the-read-side" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-14-scene-pipeline-ubos-wrong.md"target="_blank" rel="noopener"><code>phase-14-scene-pipeline-ubos-wrong.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> clamping the climbing scalar will stop the blackout.</li>
<li><strong>Action:</strong> <code>SHADPS4_DC_LUM_CLAMP=2.0</code> forces offset 3 to a fixed value during the race window.</li>
<li><strong>Outcome:</strong> the clamp fires 1093 times, blackout unchanged. Scene-pipeline UBOs <strong>consume</strong> the dim, they don&rsquo;t produce it. The dim is upstream, in a fullscreen tonemap or post-fx compute.</li>
</ul>
<h3>Phases 15-16: runtime texture dump + mutation<span class="hx:absolute hx:-mt-20" id="phases-15-16-runtime-texture-dump--mutation"></span>
    <a href="#phases-15-16-runtime-texture-dump--mutation" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-15-runtime-texture-dump.md"target="_blank" rel="noopener"><code>phase-15-runtime-texture-dump.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-16-runtime-texture-mutation.md"target="_blank" rel="noopener"><code>phase-16-runtime-texture-mutation.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> the blackout is a fullscreen texture composited over the scene.</li>
<li><strong>Action:</strong> <code>SHADPS4_DC_TEX_DUMP=1</code> dumps every bound texture during the race window (1079 textures captured). Convert tiled format to PNG for visual triage. Then, deterministically mutate each texture via hash-based tint + <code>InvalidateMemory</code> to force re-upload.</li>
<li><strong>New concept:</strong> <strong>GCN tile format</strong> (Sony interleaves bytes a specific way for cache locality), texture de-swizzle, buffer cache invalidation.</li>
<li><strong>Outcome:</strong> 14 rounds of mutation (BC textures, float data, render targets, UI atlases, post-fx buffers, LUTs). Blackout <strong>unchanged across all of them</strong>. Texture content 100% ruled out as the source.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/blue-ish-overlay.png" alt="Blue overlay during mutation"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/green-ish-overlay.png" alt="Green overlay during mutation"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/reflections-glitching-blown.png" alt="Reflections glitched"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/reflections-glitched-blown-white.png" alt="Reflections blown white"  loading="lazy" /></p>
<h3>Phases 17-19: UBO smashing, push-constant smashing, compute dispatch probe<span class="hx:absolute hx:-mt-20" id="phases-17-19-ubo-smashing-push-constant-smashing-compute-dispatch-probe"></span>
    <a href="#phases-17-19-ubo-smashing-push-constant-smashing-compute-dispatch-probe" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-17-ubo-batch-smashing.md"target="_blank" rel="noopener"><code>phase-17-ubo-batch-smashing.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-18-push-constant-smashing.md"target="_blank" rel="noopener"><code>phase-18-push-constant-smashing.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-19-compute-dispatch-probe.md"target="_blank" rel="noopener"><code>phase-19-compute-dispatch-probe.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> random-fill on UBOs, push constants, and compute input buffers can bracket and isolate the dim field without crashing.</li>
<li><strong>Action:</strong> <code>SHADPS4_DC_UBO_SMASH=1</code>, <code>SHADPS4_DC_PC_SMASH=1</code>, <code>SHADPS4_DC_DISPATCH_SMASH=1</code> with cb-index and size filters.</li>
<li><strong>New concept:</strong> <strong>push constants</strong> (128 bytes of fast params the driver pushes straight into the pipeline, no descriptor), <strong>compute dispatches</strong> (fires compute shaders with a grid of threads), <strong>ud_regs</strong> (GCN user data registers — how shadPS4 maps user data to push constants).</li>
<li><strong>Outcome:</strong> safe brackets produce overlay bokeh + blinks, but the dim stays put. Wide brackets that reach the dim also hit matrices/transforms and crash. <strong>Random-fill is the wrong instrument.</strong> Needs diff methodology.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/all-bege.png" alt="Everything beige after smash"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/all-green.png" alt="Everything green"  loading="lazy" /></p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/all-cyan.png" alt="Everything cyan"  loading="lazy" /></p>
<h3>Phase 19b-c: dim-vs-lift dispatch diff<span class="hx:absolute hx:-mt-20" id="phase-19b-c-dim-vs-lift-dispatch-diff"></span>
    <a href="#phase-19b-c-dim-vs-lift-dispatch-diff" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-19b-dim-vs-lift-diff.md"target="_blank" rel="noopener"><code>phase-19b-dim-vs-lift-diff.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-19c-dispatch-draw-skip-null.md"target="_blank" rel="noopener"><code>phase-19c-dispatch-draw-skip-null.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> compute dispatches that only fire during dim (and stop once it lifts) are part of the chain.</li>
<li><strong>Action:</strong> split the clean log into pre / dim / bright windows (via user wall-clock), count dispatch frequency per pipeline.</li>
<li><strong>Outcome:</strong> identified <strong>8 compute pipelines</strong> (histogram 256→downsamples, tile-grid reductions, auto-exposure 1x1 scalars) that stop exactly when dim lifts. Shapes strongly suggest the eye-adaptation chain. <strong>First genuine diagnostic hit.</strong> Skip test on those 8 + 101 correlated draws: blackout <strong>unchanged</strong>. They&rsquo;re <strong>downstream effects</strong>, not drivers.</li>
</ul>
<h3>Phase 20-21: tonemap inline, fragment → flip buffer<span class="hx:absolute hx:-mt-20" id="phase-20-21-tonemap-inline-fragment--flip-buffer"></span>
    <a href="#phase-20-21-tonemap-inline-fragment--flip-buffer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-20-shader-dump-tonemap-inline.md"target="_blank" rel="noopener"><code>phase-20-shader-dump-tonemap-inline.md</code></a> · <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-21-fragment-to-flip-buffer.md"target="_blank" rel="noopener"><code>phase-21-fragment-to-flip-buffer.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> the auto-exposure compute shader is broken, or the tonemap compute has the wrong math.</li>
<li><strong>Action:</strong> recompile all shaders, dump SPIR-V, disassemble the histogram auto-exposure. Patch its output to constant 1.0 and test.</li>
<li><strong>New concept:</strong> <strong>SPIR-V disassembly</strong> (Khronos intermediate format with readable assembly). <strong>Compute shader verification.</strong> Later, <strong>pipeline-to-shader mapping</strong>.</li>
<li><strong>Outcome:</strong> patch <strong>has no effect</strong>. Discovery: <strong>DriveClub has no separate tonemap pass</strong>. Scene geometry writes directly to the flip buffer, with exposure inline in material shaders. Each material shader bakes the dim scalar from a shared UBO. Pipeline cache wipe + <code>SHADPS4_DC_PIPEMAP=1</code> dumps every pipeline→shader mapping. Filtering drawlog for pipelines writing to <code>0x5000900000</code> / <code>0x5000108000</code> (the actual swapchain buffers) yields <strong>14 unique pipelines</strong>. None with Exp2/Log2 tonemap ops. Exposure is applied upstream.</li>
</ul>
<h3>Phase 22: tonemap compute identified<span class="hx:absolute hx:-mt-20" id="phase-22-tonemap-compute-identified"></span>
    <a href="#phase-22-tonemap-compute-identified" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-22-tonemap-compute-identified.md"target="_blank" rel="noopener"><code>phase-22-tonemap-compute-identified.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> among the game&rsquo;s 510 compute shaders, one has a tonemap signature (many Exp2/Log2).</li>
<li><strong>Action:</strong> automated scan, cross-reference with dispatchlog.</li>
<li><strong>Outcome:</strong> <strong><code>cs_0x2c918c06</code></strong> is the strong candidate, with 8 Exp2 + 8 Log2, 5 SSBOs (scene params, TAA, bloom, camera, exposure), 11 images. Skip test on that specific pipeline: <strong>stops the blackout cycle</strong>, removes color grading, introduces temporal ghosting. <strong>That&rsquo;s the final scene→swapchain tonemap path.</strong></li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/temporal-glitching.png" alt="Temporal ghosting after skip"  loading="lazy" /></p>
<h3>Phase 22+: tonemap patches → playable<span class="hx:absolute hx:-mt-20" id="phase-22-tonemap-patches--playable"></span>
    <a href="#phase-22-tonemap-patches--playable" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-22plus-tonemap-compute-patches.md"target="_blank" rel="noopener"><code>phase-22plus-tonemap-compute-patches.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> two surgical SPIR-V edits (histogram no-op + tonemap exposure clamp/boost) make the game playable.</li>
<li><strong>Action:</strong> histogram patch to never update; tonemap patch to clamp exposure floor at 0.5 and boost blend by ×10. Both via SPIR-V patching + <code>shader/patch/</code> drop-in.</li>
<li><strong>Outcome:</strong> <strong>playable!</strong> Dim shrinks from pitch black to mild. A <strong>real mitigation, not a root-cause fix.</strong> The fade curve still drags exposure, and each track needs per-threshold tuning.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/blown-just-track-lights.png" alt="Track lights blowing out after ×10 boost"  loading="lazy" /></p>
<video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/india-gamma-correction-attempt.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Here the AI suggested stopping for the first time. <strong>&ldquo;The game is playable, this is a decent mitigation, we should document and accept.&rdquo;</strong> I pushed back: &ldquo;it&rsquo;s playable but it&rsquo;s wrong. Don&rsquo;t accept. We keep going.&rdquo;</p>
<h3>Phase 23: CPU-side exposure intercept<span class="hx:absolute hx:-mt-20" id="phase-23-cpu-side-exposure-intercept"></span>
    <a href="#phase-23-cpu-side-exposure-intercept" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-23-cpu-exposure-intercept.md"target="_blank" rel="noopener"><code>phase-23-cpu-exposure-intercept.md</code></a></p>

</blockquote>
<ul>
<li><strong>Hypothesis:</strong> clamping the exposure scalar at the tonemap compute&rsquo;s <code>BindBuffers</code> point prevents over-dimming.</li>
<li><strong>Action:</strong> <code>SHADPS4_DC_EXPOSURE_PIN=1</code> intercepts <code>BindBuffers</code>, overwrites exposure offsets with a clamp-min threshold, invalidates buffer_cache.</li>
<li><strong>New concept:</strong> <strong>CPU-GPU buffer binding</strong> (the moment when the CPU hands a descriptor to the GPU). <strong>Buffer cache invalidation</strong> (how the emulator keeps consistency between a buffer&rsquo;s CPU-side and GPU-side state).</li>
<li><strong>Outcome:</strong> the scalar oscillates at race start, then climbs monotonically. Per-track tuning is needed. India wants 3.0, Canada blows out at the same threshold. <strong>A single-scalar solution doesn&rsquo;t converge cross-track.</strong></li>
</ul>
<video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/debugging-cli-gamma-correction.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<h3>Phase 24: dim upstream of tonemap<span class="hx:absolute hx:-mt-20" id="phase-24-dim-upstream-of-tonemap"></span>
    <a href="#phase-24-dim-upstream-of-tonemap" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-24-dim-upstream-of-tonemap.md"target="_blank" rel="noopener"><code>phase-24-dim-upstream-of-tonemap.md</code></a></p>

</blockquote>
<ul>
<li><strong>Conclusion:</strong> after the whole probe chain (identity tonemap, exposure pin, compute skip), the dim <strong>is in the HDR target before</strong> the tonemap reads it. Three upstream suspects: (1) atmospheric scattering / volumetric fog, (2) reflection-probe / envmap update, (3) pre-tonemap exposure apply.</li>
<li><strong>New concept:</strong> <strong>atmospheric scattering</strong> (simulating how light travels through atmosphere, Rayleigh/Mie-style), <strong>volumetric fog</strong> (fog rendered as a froxel volume).</li>
<li><strong>Outcome:</strong> plan set. Frame-order dump, walk backwards from tonemap through the whole chain.</li>
</ul>
<h3>Phase 25: frame-order trace<span class="hx:absolute hx:-mt-20" id="phase-25-frame-order-trace"></span>
    <a href="#phase-25-frame-order-trace" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-25-frameorder-trace.md"target="_blank" rel="noopener"><code>phase-25-frameorder-trace.md</code></a></p>

</blockquote>
<ul>
<li><strong>Action:</strong> <code>SHADPS4_DC_FRAMEORDER=&lt;N&gt;</code> logs strict per-submit pipeline/shader chain over N submits during race window.</li>
<li><strong>New concept:</strong> <strong>frame-order reconstruction</strong> (listing every GPU submission in exact order), <strong>froxel volumetrics</strong> (frustum volume voxels — a 3D grid aligned to the camera&rsquo;s frustum that stores light info).</li>
<li><strong>Outcome:</strong> three suspects: (A) half-res compute <code>(60,34,1)</code>, probably SSAO; (B) progressive dispatches <code>(256,1,1)→(2560,1,1)→(10240,1,1)</code>, froxel volumetric; (C) fullscreen apply-fog-to-HDR draw. <strong>Suspect B</strong> is the strongest fit (sunset best-fit, per-pixel non-uniform dim).</li>
</ul>
<h3>Phase 26: The UBO fade pin, the &ldquo;aha moment&rdquo;<span class="hx:absolute hx:-mt-20" id="phase-26-the-ubo-fade-pin-the-aha-moment"></span>
    <a href="#phase-26-the-ubo-fade-pin-the-aha-moment" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-26-ubo-fade-pin.md"target="_blank" rel="noopener"><code>phase-26-ubo-fade-pin.md</code></a></p>

</blockquote>
<p>This is where everything changed.</p>
<ul>
<li><strong>Hypothesis:</strong> a direct memory diff (bright vs dim vs recovered) identifies the exact bytes the game writes to control the blackout.</li>
<li><strong>Action:</strong> capture periodic UBO snapshots across visual state transitions. Diff three snapshots byte by byte. Look for fade signatures.</li>
<li><strong>New concept:</strong> <strong>state diff methodology</strong>. Instead of &ldquo;which pass looks suspicious?&rdquo;, ask &ldquo;which bytes actually change with the visual state?&rdquo;</li>
<li><strong>Outcome:</strong> <strong>breakthrough.</strong> Two UBOs control the fade: a 1936-byte one with slots <code>[38/48/50]</code> (light intensity) being multiplied by 0.094 (fade factor), and a 224-byte sun UBO with slot <code>[50]</code> dropping 97%. Pinning both: race start opens bright. TOD animation intact. Content-aware three-state lifecycle (idle → engaged → expired).</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/touching-sun-light.png" alt="First touch on the sun-light UBO"  loading="lazy" /></p>
<p>Wednesday night. Claude suggests stopping again. <strong>&ldquo;We have the pin working, three-state lifecycle is robust, Canada + Munnar are running. This is upstream quality, we should PR and close it.&rdquo;</strong> I insist: &ldquo;there&rsquo;s still residual dim on other tracks, it&rsquo;s not closed. We keep going.&rdquo;</p>
<h3>Phase 28: pivot to emulator code<span class="hx:absolute hx:-mt-20" id="phase-28-pivot-to-emulator-code"></span>
    <a href="#phase-28-pivot-to-emulator-code" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-28-pivot-to-emulator-code.md"target="_blank" rel="noopener"><code>phase-28-pivot-to-emulator-code.md</code></a></p>

</blockquote>
<ul>
<li><strong>Realization:</strong> the residual dim in Canada dusk/night isn&rsquo;t a wrong UBO value. It&rsquo;s an <strong>emulator translation gap</strong>. The game runs correctly on a real PS4, so shadPS4 has some path that translates or synchronizes incorrectly.</li>
<li><strong>Action:</strong> ranked 5 candidate emulator bugs: HLE shader misidentification, image storage classification, layout transitions, IMAGE_STORE_MIP fallback, OpImageFetch LOD restriction.</li>
<li><strong>New concept:</strong> <strong>HLE (High-Level Emulation)</strong>. Replacing complex PS4 OS functions with host-side implementations. Different from LLE (low-level) which emulates bit by bit. HLE is faster but can diverge from real behavior whenever the host implementation doesn&rsquo;t cover every case.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/canada-dim.png" alt="Canada dusk/night still dim"  loading="lazy" /></p>
<h3>Phase 29: calibrated state + recording harness<span class="hx:absolute hx:-mt-20" id="phase-29-calibrated-state--recording-harness"></span>
    <a href="#phase-29-calibrated-state--recording-harness" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-29-calibrated-state.md"target="_blank" rel="noopener"><code>phase-29-calibrated-state.md</code></a></p>

</blockquote>
<ul>
<li><strong>Action:</strong> <code>SHADPS4_DC_RECORD=1</code>. Recording harness that dumps lighting UBOs periodically + cross-correlates with wall-clock screenshots.</li>
<li><strong>Outcome:</strong> important discovery. <strong>Slots <code>[144..295]</code> of the 1936-byte UBO</strong> (30+ fields) stay <strong>denormal/uninitialized</strong> for the first ~90 seconds of a race. At the 1:30 &ldquo;auto-recalibration&rdquo;, the game writes the real values. <strong>It&rsquo;s not magic recovery, it&rsquo;s the CPU finally writing values that should have been there since frame 1.</strong> On PS4 they&rsquo;re ready before the first frame; on shadPS4 they&rsquo;re delayed by 90s. <strong>Root cause lives in an emulator init gap.</strong></li>
</ul>
<h3>Phase 30: UBO writer audit<span class="hx:absolute hx:-mt-20" id="phase-30-ubo-writer-audit"></span>
    <a href="#phase-30-ubo-writer-audit" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-30-ubo-writer-audit.md"target="_blank" rel="noopener"><code>phase-30-ubo-writer-audit.md</code></a></p>

</blockquote>
<ul>
<li><strong>Action:</strong> exhaustive audit of every path that writes into slot <code>[38]</code> of the 1936-byte UBO. 7 suspects ranked: (1) ObtainBuffer stream-copy race, (2) lazy RegionManager in memory_tracker, (3) histogram compute skip, (4) page-fault delayed invalidation, (5) readback gating, (6) buffer-coherency race, (7) push-constant.</li>
<li><strong>Status:</strong> audit incomplete. Before I could verify each suspect, Phase 31 short-circuited the whole thing.</li>
</ul>
<h3>Phase 31: readbacks_mode = Precise, the turning point<span class="hx:absolute hx:-mt-20" id="phase-31-readbacks_mode--precise-the-turning-point"></span>
    <a href="#phase-31-readbacks_mode--precise-the-turning-point" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-31-readbacks-mode-fix.md"target="_blank" rel="noopener"><code>phase-31-readbacks-mode-fix.md</code></a></p>

</blockquote>
<p>Thursday morning. Tired, thinking about suspect <strong>#5, readback gating</strong>. That one itched at me because it was the only one of the 7 writers that touched the <em>timing</em> of the read rather than the <em>correctness</em> of the write.</p>
<ul>
<li><strong>Hypothesis:</strong> flipping <code>readbacks_mode: 2 (Precise)</code> in per-game config makes the feedback loop work by synchronizing the CPU&rsquo;s read to fresh GPU output.</li>
<li><strong>Action:</strong> one edit in <code>custom_configs/CUSA00003.json</code>. Boot. Canada night.</li>
<li><strong>Outcome:</strong> <strong>race start opens bright. Auto-exposure converges normally. Zero blackout. Zero 1:30 recalibration. Night TOD natural.</strong></li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/23/driveclub-learning/almost-fixed.png" alt="Almost there, the test before the final one"  loading="lazy" /></p>
<p>Tested on Munnar. Same result. One integer. <strong>31 phases. 44 commits. 15,668 lines. Answer: one integer in the config.</strong></p>
<p>Why did this slip past 30 phases? The Phase 31 doc has the honest analysis:</p>
<ol>
<li><strong>Symptom disguise.</strong> Progressive darkening looks exactly like a broken tonemap, bad shader, or miscompiled exposure scalar. Every dump-driven probe found <strong>wrong values in the UBO</strong>, which is true but describes the downstream read, not the upstream gap.</li>
<li><strong><code>readback_linear_images</code> red herring.</strong> I tested that flag in Phase 29, saw no effect, and concluded &ldquo;the readback surface is audited&rdquo;. I forgot that flag is for <em>image</em> readbacks (linear images via <code>TextureCache::DownloadImageMemory</code>). The histogram SSBO is a <strong>buffer</strong>, a separate path.</li>
<li><strong>Pinning making it worse, not better.</strong> Overwriting slots [38/48/50] looked promising but never converged. The game rewrote on top of the pin every frame. I interpreted that as &ldquo;the pin mechanism lands too late in the pipeline&rdquo;. I spent phases 27-29 moving the pin earlier. The real fix was to <strong>stop clobbering the GPU-side input the integrator reads</strong>.</li>
<li><strong>Never audited the buffer readback path.</strong> The Phase 30 audit listed 7 writer candidates, but all of them were about the correctness of the <em>write</em>. None asked &ldquo;what is the CPU reading, expecting the GPU to have filled?&rdquo;</li>
</ol>
<h2>The shotgun technique<span class="hx:absolute hx:-mt-20" id="the-shotgun-technique"></span>
    <a href="#the-shotgun-technique" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>One thing I want to put on record: <strong>torture probes aren&rsquo;t waste, they&rsquo;re learning infrastructure.</strong> And the methodology behind them has a name: shotgun.</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/HkxVhFg81fs?start=40"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>If you have a suspicion (say, &ldquo;the dim comes from some texture&rdquo;), the worst thing you can do is start investigating one texture at a time, in depth. DriveClub has 1079 bound textures during the race window. If a detailed triage on one texture takes 20 minutes, you spend three days on a single branch of that tree and still don&rsquo;t know if the dim even comes from a texture. It might not be in the whole tree.</p>
<p>Shotgun fixes that. Instead of investigating one at a time, you fire at all of them simultaneously. Mutate every bound texture with a hash-based tint, force re-upload, run the game. In 30 seconds you know whether any of them affects the dim. If it does, the dim is texture-side and only then is it worth starting to narrow down. If nothing changes, congratulations: you just ruled out the entire &ldquo;texture&rdquo; category in 30 seconds. Switch categories, shotgun UBOs. No hit? Shotgun push constants. No hit? Shotgun compute dispatches. Keep going until something reacts.</p>
<p><strong>Don&rsquo;t start by narrowing down a tree that might be huge.</strong> You&rsquo;ll spend weeks in irrelevant leaves. Shotgun the trunk first. See which branch reacts. Then go deep inside that branch.</p>
<p>That&rsquo;s what I did, without knowing I was doing it, for the first 30 phases. 32 probe env vars (<code>SHADPS4_DC_UBO_SMASH</code>, <code>SHADPS4_DC_PC_SMASH</code>, <code>SHADPS4_DC_DISPATCH_SMASH</code>, <code>SHADPS4_DC_TEX_NUKE_*</code>, <code>SHADPS4_DC_UBO_NUKE</code>, and more). Random batches, hash-based tints, null substitutions. Most of them never pointed at the real fix. But every shotgun round ruled out an entire category of mental model. &ldquo;Not texture.&rdquo; &ldquo;Not push-constant.&rdquo; &ldquo;Not those 8 compute pipelines.&rdquo; &ldquo;Not the UBO write side anywhere in that region.&rdquo; Every ruling-out narrows the problem.</p>
<p><strong>And while I was doing it, I was learning the system.</strong> Every smash that crashed told me &ldquo;there&rsquo;s a critical descriptor pointer here&rdquo;. Every color tint that appeared told me &ldquo;this UBO feeds that composition pass&rdquo;. Every dispatch that fired differently between dim/bright showed me where the interesting boundary lives.</p>
<p><strong>When you don&rsquo;t know the system, shotgun probes are a map.</strong> You don&rsquo;t use them to find the answer. You use them to learn the terrain.</p>
<h2>What I knew on Sunday vs Thursday<span class="hx:absolute hx:-mt-20" id="what-i-knew-on-sunday-vs-thursday"></span>
    <a href="#what-i-knew-on-sunday-vs-thursday" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><strong>Sunday:</strong> zero everything. All the areas below were black boxes to me.</p>
<p><strong>Thursday:</strong></p>
<ul>
<li>PS4 package format end-to-end (PKG → PFS → param.sfo → disc_info → keystone → npbind). How v1.28 cumulative patches layer on top of a v1.00 base install.</li>
<li>shadPS4 architecture in outline: <code>src/common</code>, <code>src/core</code> (OS emulation, HLE libraries), <code>src/shader_recompiler</code> (GCN→SPIR-V), <code>src/video_core</code> (Vulkan renderer, buffer cache, texture cache, page manager).</li>
<li>Full graphics pipeline: G-buffer → forward+ lighting (writes MSAA depth) → read depth as color for SSAO / volumetric froxel → post-fx compute → tonemap → composite → swapchain.</li>
<li>UBO vs push constant vs descriptor set. When to use each.</li>
<li>SPIR-V disassembly and re-assembly for surgical patching. How to identify a tonemap compute by its Exp2/Log2 signature.</li>
<li>Forward+ lighting + MSAA depth resolve + how to implement ReinterpretMsDepthAsColor.</li>
<li>shadPS4 buffer cache: <code>MemoryTracker</code>, <code>RegionManager</code>, <code>FaultManager</code>. How page faults turn into GPU→CPU sync.</li>
<li><code>readbacks_mode</code>: Disabled / Relaxed / Precise. What each tradeoff looks like. Why Relaxed isn&rsquo;t enough for feedback loops (it only write-protects, not read-protects).</li>
<li>PS4 OELF → Itanium RTTI → SCE relocation → dynamic linker reasoning, even without actually doing the binary patch (the plan is documented).</li>
</ul>
<p>I didn&rsquo;t become an expert in any of it. But I went from <strong>total outsider</strong> to <strong>reasonably oriented operator</strong>. And that jump, as I&rsquo;ll argue below, was the single most valuable thing of the four days.</p>
<h2>The AI suggested accepting. The perseverance was mine<span class="hx:absolute hx:-mt-20" id="the-ai-suggested-accepting-the-perseverance-was-mine"></span>
    <a href="#the-ai-suggested-accepting-the-perseverance-was-mine" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve said a few times in this article that &ldquo;Claude suggested stopping&rdquo;. I want to be fair and specific: it wasn&rsquo;t an AI failure. It was exactly the correct behavior of an intelligent tool: <strong>when you have something that works reasonably well, stopping and documenting is frequently the right call.</strong> The Phase 22+ tonemap patch made the game playable. The Phase 26 UBO pin made the image pretty. The Phase 29 recording harness covered the critical window. All were legitimate stopping points.</p>
<p>If you read <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/codex-conclusion.md"target="_blank" rel="noopener"><code>codex-conclusion.md</code></a>, the document the AI produced after Phase 26 (still not knowing about Phase 31 yet), it describes the then-current solution as &ldquo;<strong>a mitigation, not a root-cause fix</strong>&rdquo;. It honestly admits what we knew: the UBO pin was a countermeasure, not an explanation. That&rsquo;s good behavior.</p>
<p>The difference is that <strong>I had a stubborn hunch that something was still wrong</strong>. It worked in the playable sense. But the cost (DriveClub-specific pin + tonemap SPIR-V patch + per-track threshold tuning + doesn&rsquo;t generalize) was too high for a supposedly engineering-quality fix. A senior professional recognizes when the asymmetry between cost and understanding points to something deeper. You don&rsquo;t always get it right. But that hunch deserves to be heard.</p>
<p>Four days without interruption, 8 a.m. to midnight, Sunday, Monday, Tuesday, Wednesday, resolved Thursday morning. How many times did the AI suggest &ldquo;honestly, we should just accept the current result and document&rdquo;? Four or five. Each technically defensible. <strong>Human perseverance is the difference between &ldquo;the game works well enough&rdquo; and &ldquo;the game works, and I know why&rdquo;.</strong></p>
<h2>The real cost of this journey<span class="hx:absolute hx:-mt-20" id="the-real-cost-of-this-journey"></span>
    <a href="#the-real-cost-of-this-journey" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/investigation-effort-accounting.md"target="_blank" rel="noopener"><code>investigation-effort-accounting.md</code></a> catalogs everything before cleanup. Some highlights:</p>
<ul>
<li><strong>3 days</strong> of active work (Apr 21-23, 2026).</li>
<li><strong>33 numbered phases</strong> + sweeps + side threads = 54 docs, 412 KB, <strong>6,867 lines of prose</strong>.</li>
<li><strong>44 commits</strong> on the gamma-debug fork. <strong>15,668 lines added</strong>, 1,204 removed.</li>
<li><code>src/video_core/renderer_vulkan/vk_rasterizer.cpp</code> grew from 1,364 to <strong>4,680 lines</strong> (3.4×) with instrumentation, pin lifecycles, recording harness.</li>
<li><strong>32 probe env vars</strong> defined. <strong>1 effectively used to find the bug</strong> (<code>SHADPS4_DC_LOG_STREAMCOPY</code>, in Phase 30).</li>
<li><strong>61 MB</strong> of shadPS4 log accumulated. <strong>57 MB</strong> of shader dumps (5,803 files). ~<strong>470 MB</strong> of total reclaimable artifacts once closed.</li>
</ul>
<p>Resolution: <strong>1 integer in the config.</strong></p>
<p>The ratio of <strong>6,867 lines of prose : 1 integer</strong> amuses me. But it isn&rsquo;t a joke about inefficiency. It&rsquo;s the real cost of learning a system you didn&rsquo;t know. Every line of prose ruled out a hypothesis. Every commit taught a layer of the stack. The integer was the end; the path was the product.</p>
<p>An honest note: despite the 15,668 lines added on the <code>gamma-debug</code> fork, <strong>almost none of it is useful to contribute upstream to shadPS4</strong>. What&rsquo;s there is instrumentation: torture probes, recording harness, pin lifecycles, calibrate-at-arm, UBO snapshots, dozens of debug env vars. Diagnostic code, not production code. Diagnostics that only make sense for DriveClub under those specific conditions.</p>
<p>The fork is going to stay public, but <strong>not as a contribution branch to the shadPS4 project</strong>. Unfortunately. It stays as a study document: 54 md files covering the full process, 44 commits showing the sequence of hypotheses, rule-outs and failures. It&rsquo;s material for anyone who wants to understand how the debugging process unfolded, or to reuse some of the techniques in similar investigations. No upstream-ready PR is coming out of that tree.</p>
<h2>Closing<span class="hx:absolute hx:-mt-20" id="closing"></span>
    <a href="#closing" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Yes, the final solution was a config toggle that already existed. It&rsquo;s been in shadPS4&rsquo;s code for seven months. But on Sunday night, with zero PS4 knowledge, zero emulator knowledge, zero notion that auto-exposure was a GPU→CPU feedback loop, <strong>I had no technical authority to say &ldquo;flip readbacks Precise, done&rdquo;</strong>.</p>
<p>I had to see the whole chain to understand <em>why</em> it works. Why Relaxed isn&rsquo;t enough (it only write-protects, and DriveClub reads, it doesn&rsquo;t write into the histogram SSBO). Why Precise is expensive (per-page mprotect + per-fault GPU stall). Why the maintainer hid it behind &ldquo;Advanced&rdquo; (Bloodborne hangs, AMD tanks to 12 FPS). Why nobody in the community had connected Precise to DriveClub before: the compat tracker admits &ldquo;readbacks enabled&rdquo; but doesn&rsquo;t cite the mode, and nobody had seen the monotonic-drift signature through the GPU→CPU lens.</p>
<p>That&rsquo;s the difference between <strong>knowing the answer</strong> and <strong>knowing the terrain well enough to recognize the answer</strong>. To the junior asking me &ldquo;how do I learn today, in the AI era&rdquo;: that&rsquo;s my answer. Don&rsquo;t ask. Dive in. Use AI to speed up the search, to read code in parallel, to index old forums, to compile, to parse logs. But <strong>insist on understanding the why</strong>. When the AI suggests &ldquo;let&rsquo;s accept and document&rdquo; (and it will, because that&rsquo;s often the right behavior), ask yourself whether you actually understood the chain. If the itch &ldquo;this is wrong but I don&rsquo;t know why&rdquo; is still there, keep going.</p>
<p>You&rsquo;re probably going to spend four days on something that has a one-integer solution. You&rsquo;re probably going to discover five wrong paths before the right one. Probably half your probes will be torture shotguns that never pointed at the fix.</p>
<p>And you&rsquo;ll probably, when you finish, know a domain you didn&rsquo;t know before.</p>
<ul>
<li><strong>Fork gamma-debug:</strong> <a href="https://github.com/akitaonrails/shadPS4/tree/gamma-debug"target="_blank" rel="noopener">github.com/akitaonrails/shadPS4/tree/gamma-debug</a></li>
<li><strong>54 investigation docs:</strong> <a href="https://github.com/akitaonrails/shadPS4/tree/gamma-debug/docs/driveclub-investigation"target="_blank" rel="noopener">docs/driveclub-investigation/</a></li>
<li><strong>Operational runbook (recipe):</strong> <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener">distrobox-gaming/docs/driveclub-shadps4.md</a></li>
<li><strong>Phase 31 resolution:</strong> <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-investigation/phase-31-readbacks-mode-fix.md"target="_blank" rel="noopener"><code>readbacks_mode: 2</code></a></li>
</ul>
<p>Now I&rsquo;m going to go play DriveClub. Night race in Canada, natural brightness, no blackout. Good Thursday.</p>
]]></content:encoded><category>emulation</category><category>gaming</category><category>learning</category></item><item><title>My Favorite Retro Racing Games Running on My Distrobox</title><link>https://akitaonrails.github.io/en/2026/04/19/my-favorite-retro-racing-games-on-distrobox/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/19/my-favorite-retro-racing-games-on-distrobox/</guid><pubDate>Sun, 19 Apr 2026 20:00:00 -0300</pubDate><description>&lt;p&gt;I spent the whole Sunday on this, and it was one of the most productive Sundays I&amp;rsquo;ve had in a while. The mission was specific: close the loop on the simcade racing games that are hardest to emulate, the ones I&amp;rsquo;ve wanted to run properly for years, and only now got to a reliable state.&lt;/p&gt;
&lt;p&gt;Two monsters sit at the top of that list. First, &lt;strong&gt;&lt;a href="#driveclub-the-impossible-finally-possible"&gt;Driveclub&lt;/a&gt;&lt;/strong&gt; on shadPS4. It&amp;rsquo;s a PS4 exclusive, never got a port, Evolution Studios was shut down, no remaster. To play it outside the original PS4, emulation is the only option, and PS4 emulation is still the most immature part of the ecosystem. This is &lt;strong&gt;by far the hardest on the list&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I spent the whole Sunday on this, and it was one of the most productive Sundays I&rsquo;ve had in a while. The mission was specific: close the loop on the simcade racing games that are hardest to emulate, the ones I&rsquo;ve wanted to run properly for years, and only now got to a reliable state.</p>
<p>Two monsters sit at the top of that list. First, <strong><a href="#driveclub-the-impossible-finally-possible">Driveclub</a></strong> on shadPS4. It&rsquo;s a PS4 exclusive, never got a port, Evolution Studios was shut down, no remaster. To play it outside the original PS4, emulation is the only option, and PS4 emulation is still the most immature part of the ecosystem. This is <strong>by far the hardest on the list</strong>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/19/distrobox-gaming/shadps4-launcher.png" alt="shadPS4 Qt Launcher showing DRIVECLUB CUSA00003 ready to run"  loading="lazy" /></p>
<p>Second, <strong><a href="#forza-motorsport-4-the-goat">Forza Motorsport 4 with Project Forza Plus</a></strong> on Xenia Canary. FM4 is the GOAT of the Xbox 360 era, Project Forza Plus is the community mod that consolidates patches and content, and getting the two to run together on Xenia with decent graphics, no audio crashes and no shadow bugs took serious hours of trial and error.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/19/distrobox-gaming/xenia-manager.png" alt="Xenia Manager with the Xbox 360 Forza collection installed"  loading="lazy" /></p>
<p>Around those two I organized the rest of what I already knew worked at some level, just needed consolidating into a reproducible setup: <strong><a href="#gran-turismo-4-spec-ii-mod">Gran Turismo 4 with Retexture 3.0 and HD HUD</a></strong>, <strong><a href="#gran-turismo-3-a-spec">Gran Turismo 3 with HD textures and widescreen</a></strong>, <strong><a href="#gran-turismo-2-spec-ii-mod">Gran Turismo 2 with the Spec II mod</a></strong>, the PS3 Gran Turismos (<a href="#gran-turismo-5">GT5</a> and <a href="#gran-turismo-6">GT6</a>), Forza Horizon <a href="#forza-horizon-1-with-xe-mod">1</a> and <a href="#forza-horizon-2">2</a>, the <a href="#project-gotham-racing-3-and-4">PGRs</a>, <a href="#ridge-racer-v">Ridge Racer</a>, <a href="#enthusia-professional-racing">Enthusia</a>, <a href="#colin-mcrae-rally-1-and-2">Colin McRae</a>. I had an idea how to make these work, I just needed a setup that doesn&rsquo;t break the next time I reinstall the system.</p>
<p>Before the details, two things to clarify.</p>
<p>First, on infra: I&rsquo;m not going to repeat here how the setup was built. I wrote that in detail in <a href="/en/2026/04/11/emulation-distrobox-with-claude-code/">An Emulation Distrobox with Claude Code</a>. Arch Linux in a distrobox with <code>--nvidia</code>, 17 Ansible roles, every emulator from the AUR, ES-DE as frontend, automated per-game configs, Python scripts for PS3 update checking and Xbox 360 title updates, Xenia via Wine. Go read that one. The repo is at <a href="https://github.com/akitaonrails/distrobox-gaming"target="_blank" rel="noopener">akitaonrails/distrobox-gaming</a> if you want to reproduce. Here I&rsquo;m going to talk about the games.</p>
<p>Second, on taste: I like simcade racing. Gran Turismo has been my declared addiction since PS1, and I wrote about the rest in the <a href="/en/2026/04/01/my-sim-racing-cockpit-formula-fx1/">Formula FX1 cockpit article</a>. Direct-drive wheel, load-cell pedal, triple monitor when I&rsquo;m feeling masochistic. That&rsquo;s the kind of racing I like.</p>
<h2>The warning for the &ldquo;well, actually&rdquo; crowd<span class="hx:absolute hx:-mt-20" id="the-warning-for-the-well-actually-crowd"></span>
    <a href="#the-warning-for-the-well-actually-crowd" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Yes, I know iRacing and the rest of the current sims and simcades. I have 12 TB of ISOs and ROMs on the NAS, I probably have one you&rsquo;d remember to mention. It&rsquo;s not for lack of options that I&rsquo;m focusing on emulation today.</p>
<p>On the new side, the games I&rsquo;m genuinely anticipating: <strong><a href="https://forza.net/"target="_blank" rel="noopener">Forza Horizon 6</a> in Tokyo</strong>, <strong><a href="https://www.assettocorsa.it/"target="_blank" rel="noopener">Assetto Corsa EVO</a></strong>, and <strong><a href="https://www.assettocorsa.it/"target="_blank" rel="noopener">Assetto Corsa Rally</a></strong>. I&rsquo;ve been playing the demos of both Assetto Corsa titles and enjoying their single-player campaigns a lot. When those ship, they&rsquo;ll become my main sim rotation. This article covers a different side of the collection, pulling classics out of oblivion that still don&rsquo;t have a remaster.</p>
<p>Today, here, I&rsquo;m interested in these specific games I listed above. Period. You do what you want, I do what I want. If you want iRacing, fire up Steam and go in peace. This article is about messing with emulators, preserving old games, and pulling these gems out of oblivion on a Linux setup. If you&rsquo;re here for current-sim reviews, you won&rsquo;t find them.</p>
<p>Now to what matters.</p>
<p><strong>A note on the videos:</strong> most of them have no audio because I forgot to turn on sound capture in OBS before recording and I was too lazy to re-record. Audio works perfectly on every emulator here; this is my OBS mistake, not a setup issue.</p>
<h2>Global settings per emulator<span class="hx:absolute hx:-mt-20" id="global-settings-per-emulator"></span>
    <a href="#global-settings-per-emulator" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before the games, a quick consolidation of the global configs that apply across each emulator. This is automated in the repo, but if you&rsquo;re setting up by hand, here&rsquo;s the summary.</p>
<h3>DuckStation (PS1)<span class="hx:absolute hx:-mt-20" id="duckstation-ps1"></span>
    <a href="#duckstation-ps1" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Renderer:</strong> Vulkan</li>
<li><strong>Internal Resolution Scale:</strong> 8x (8K internal, downscaled to the monitor)</li>
<li><strong>Texture Filtering:</strong> JINC2 (preserves pixel art but smooths)</li>
<li><strong>PGXP:</strong> on (fixes the typical PS1 vertex wobble)</li>
<li><strong>Widescreen Hack:</strong> on as fallback, but I prefer per-game widescreen cheats</li>
<li><strong>Aspect Ratio:</strong> 16:9 with widescreen cheat, 4:3 without</li>
</ul>
<p>PS1 runs smoothly on DuckStation these days. It&rsquo;s the most mature emulator on the list. The attention goes to per-game choices (some games need a specific widescreen cheat).</p>
<h3>PCSX2 (PS2)<span class="hx:absolute hx:-mt-20" id="pcsx2-ps2"></span>
    <a href="#pcsx2-ps2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Renderer:</strong> Vulkan</li>
<li><strong>Upscale:</strong> 4x SSAA (1440p native → 4K internal)</li>
<li><strong>Anisotropic Filtering:</strong> 16x</li>
<li><strong>Post Processing:</strong> FXAA on, PCRTC antiblur on</li>
<li><strong>Deinterlacing:</strong> Automatic (some games need override)</li>
<li><strong>Controller:</strong> Xbox-style bindings (I use an 8BitDo)</li>
</ul>
<p>PCSX2 2.7.x is much better than the 2.6.3 still in Arch&rsquo;s official repo. The AUR&rsquo;s <code>pcsx2-latest-bin</code> tracks 2.7.x and that&rsquo;s where texture replacement, modern FXAA and PCRTC antiblur live. If you&rsquo;re on a different distro stuck on 2.6.3, get the official AppImage.</p>
<h3>RPCS3 (PS3)<span class="hx:absolute hx:-mt-20" id="rpcs3-ps3"></span>
    <a href="#rpcs3-ps3" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Renderer:</strong> Vulkan</li>
<li><strong>Resolution Scale:</strong> 300% (generally) or native (for games with upscale issues)</li>
<li><strong>Shader Precision:</strong> Ultra</li>
<li><strong>Force High Precision Z:</strong> on</li>
<li><strong>SPU XFloat:</strong> Accurate</li>
<li><strong>Multithreaded RSX:</strong> on</li>
<li><strong>Write Color Buffers (WCB) / Read Color Buffers (RCB):</strong> off by default (GT-series safety)</li>
</ul>
<p>RPCS3 is where the most traps are. Each game has a recommended preset in the <a href="https://rpcs3.net/compatibility"target="_blank" rel="noopener">official compatibility DB</a>. And a heads-up: per-game configs live in <code>~/.config/rpcs3/custom_configs/config_&lt;SERIAL&gt;.yml</code>. The <code>config_</code> prefix is mandatory. It cost me time to figure out. RPCS3 silently accepts the YAML and then ignores it if the name is wrong.</p>
<h3>Xenia Canary (Xbox 360)<span class="hx:absolute hx:-mt-20" id="xenia-canary-xbox-360"></span>
    <a href="#xenia-canary-xbox-360" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Build:</strong> Canary (<code>master</code> has been stagnant for months, Canary is where development lives)</li>
<li><strong>Renderer:</strong> Vulkan</li>
<li><strong>Render Target Path:</strong> <code>rtv</code> by default, <code>fsi</code> for specific games (PGR4)</li>
<li><strong>User Profile:</strong> created once, persists</li>
<li><strong>Wine Prefix:</strong> dedicated, managed by Xenia Manager</li>
</ul>
<p>Xenia on Linux runs via Wine, managed by <a href="https://github.com/xenia-manager/xenia-manager"target="_blank" rel="noopener">Xenia Manager</a>. There&rsquo;s no decent native build. It works well, but each game needs specific tuning, and Title Updates have to be pulled manually from archive.org (I wrote a script for that, details in the previous article).</p>
<h3>shadPS4 (PS4)<span class="hx:absolute hx:-mt-20" id="shadps4-ps4"></span>
    <a href="#shadps4-ps4" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Gets its own section at the end. TL;DR: works for some simple games, Driveclub is still a moving target. Still a shot in the dark.</p>
<h2>PS1 on DuckStation<span class="hx:absolute hx:-mt-20" id="ps1-on-duckstation"></span>
    <a href="#ps1-on-duckstation" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Gran Turismo (the first, from 1997)<span class="hx:absolute hx:-mt-20" id="gran-turismo-the-first-from-1997"></span>
    <a href="#gran-turismo-the-first-from-1997" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%201.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>This one is pure sentiment. GT1 was one of the first games I bought for the PS1 and I played it until the CD cracked. At the time, it was on another level. Real simulation on a machine with 128K of video RAM, decent physics, 140 cars when everybody else had 15, memorable soundtrack.</p>
<p>But let&rsquo;s be honest: you can&rsquo;t go back today. GT1&rsquo;s physics has exaggerated drift, the car slides more than it should on any corner above 80 km/h. It&rsquo;s part of the 1997 charm but gets annoying in 2026. The <strong>handling model was refined starting in GT2</strong>, and everything after only got better. Between GT1 and GT2, GT2 wins on content (650+ cars vs 140) and especially on drivability.</p>
<p>I keep GT1 in ES-DE to open in nostalgic moments, drive 10 minutes at Trial Mountain, and close it. For serious PS1-era gameplay, it&rsquo;s GT2.</p>
<h3>Gran Turismo 2 (Spec II Mod)<span class="hx:absolute hx:-mt-20" id="gran-turismo-2-spec-ii-mod"></span>
    <a href="#gran-turismo-2-spec-ii-mod" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%202.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>The original PS1 Gran Turismo is a historical milestone. I played a lot of it back then. To replay today? No. GT2 exists, and GT2 is superior in everything. More cars (650+), more tracks, more modes, and most importantly, <strong>a much better handling model</strong>. GT1 has that exaggerated drift where the car slides more than it should, GT2 tightened that up to something closer to what the series would become starting on PS2. Between playing GT1 or GT2, always GT2.</p>
<p>And the GT2 I run today is the <strong>GT2 Spec II Mod</strong> by <a href="https://x.com/projectaspec?lang=en"target="_blank" rel="noopener">Project A-Spec</a>, a community project that merges the two regional variants (Arcade + Simulation) into a single game, brings back events that were cut from the final release, fixes physics bugs, adds native widescreen support, and ships quality-of-life menu updates. It&rsquo;s the definitive way to play GT2 in 2026. The project doesn&rsquo;t maintain its own website anymore, updates come through the author&rsquo;s Twitter.</p>
<p>To run it:</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Disc</td>
          <td>GT2 Spec II Mod (patch applied over Simulation USA ISO)</td>
      </tr>
      <tr>
          <td>Serial</td>
          <td>SCUS-94455</td>
      </tr>
      <tr>
          <td>Widescreen cheat</td>
          <td>On</td>
      </tr>
      <tr>
          <td>8 MB RAM cheat</td>
          <td>On (fixes audio on crowded tracks)</td>
      </tr>
      <tr>
          <td>Texture Filter</td>
          <td>JINC2</td>
      </tr>
      <tr>
          <td>Resolution Scale</td>
          <td>8x</td>
      </tr>
  </tbody>
</table>
<p>The 8 MB RAM cheat matters for crowded tracks (Seattle in populated races for example) where audio starts clipping. It simulates the memory expansion of the Japanese version that never made it West.</p>
<h3>Colin McRae Rally 1 and 2<span class="hx:absolute hx:-mt-20" id="colin-mcrae-rally-1-and-2"></span>
    <a href="#colin-mcrae-rally-1-and-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Iconic PS1 games. CMR1 has that rough first-gen rally feel that has its charm. CMR2 is technically superior, better physics, more rallies, better graphics. But honestly: <strong>to play CMR2 today, it&rsquo;s worth looking for the original PC version</strong>. Higher resolution, native 60fps (the PS1 version is locked to 30), direct keyboard/wheel support. Codemasters repacks exist on the internet, a 40-second search will find them. I&rsquo;m not going to link them, you know where to look.</p>
<p>On PS1 via DuckStation they run smoothly, nostalgia guaranteed, but it&rsquo;s not where I spend my time.</p>
<h2>PS2 on PCSX2<span class="hx:absolute hx:-mt-20" id="ps2-on-pcsx2"></span>
    <a href="#ps2-on-pcsx2" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Gran Turismo 3 A-Spec<span class="hx:absolute hx:-mt-20" id="gran-turismo-3-a-spec"></span>
    <a href="#gran-turismo-3-a-spec" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%203.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>GT3 was a generational leap. Out of the blocky PS1 world into shaders, decent environment mapping, serious polygon car models, physics that finally started to feel like actual physics. The soundtrack with Feeder&rsquo;s &ldquo;Just a Day&rdquo; opening the game still gives me goosebumps.</p>
<p>The problem is that GT3 has LESS content than GT2. Polyphony was more cautious, cut modes to focus on polish. The A/B/S license tree is smaller, fewer cars, fewer tracks. But what&rsquo;s there is polished.</p>
<p>In 2026 on PCSX2 2.7.x, GT3 looks great. Community HD textures, widescreen pnach, anti-aliasing via SSAA, 16x AF. Feels like a re-release.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Disc</td>
          <td>GT3 A-spec (SCUS-97102) or Bundle (PBPX-95503)</td>
      </tr>
      <tr>
          <td>Widescreen pnach</td>
          <td>On</td>
      </tr>
      <tr>
          <td>Retexture pack</td>
          <td>Optional (off by default, enable if you want full HD)</td>
      </tr>
      <tr>
          <td>Upscale</td>
          <td>4x SSAA</td>
      </tr>
      <tr>
          <td>Deinterlacing</td>
          <td>Automatic</td>
      </tr>
  </tbody>
</table>
<p>Caveat on the retexture pack: when enabled, some car-showcase cutscenes in the garage have momentary flicker due to NFS streaming. Doesn&rsquo;t bother in-race. If it bothers you in menus, turn it off.</p>
<p><strong>Reminder:</strong> for the widescreen pnach to take effect, you still need to go into the <strong>in-game</strong> options and switch the aspect ratio to 16:9. The pnach unlocks the option in the menu, it doesn&rsquo;t apply automatically.</p>
<h3>Gran Turismo 4 (Spec II Mod)<span class="hx:absolute hx:-mt-20" id="gran-turismo-4-spec-ii-mod"></span>
    <a href="#gran-turismo-4-spec-ii-mod" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%204.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>GT4 is my GOAT in the series. 700+ cars, 50+ tracks, physics still respectable by 2026 standards, and that soundtrack mixing alternative rock, Japanese lounge, and Moby&rsquo;s infamous &ldquo;Sail Away&rdquo; on the main menu. It&rsquo;s the game I poured the most hours into back in 2004, and it&rsquo;s what I play the most today.</p>
<p>Like GT2, the GT4 I run today <strong>is not the original disc</strong>. It&rsquo;s the <a href="https://www.theadmiester.co.uk/specii/"target="_blank" rel="noopener"><strong>Gran Turismo 4: Spec II</strong></a> maintained by TheAdmiester — a massive community mod that&rsquo;s objectively the best way to play GT4 in 2026. It combines the final USA disc, the 2003 Beta prototype, and the original Japanese release into one game, restoring cut cars (including several that never left Japan), cancelled events, dozens of missing soundtrack tracks, adding 16:9 widescreen support, over 30 built-in cheats exposed as menu options (pro tuning, top speed, fuel economy), the option to switch between English and Japanese, and original bug fixes. The project is active, still gets patches, and ships its own installer that applies over a clean USA ISO.</p>
<p>In practice, Spec II is what GT4 should have been if Polyphony had six more months to polish. Playing it today feels like GT4 with all the content that got left out, plus modern quality-of-life. For anyone who enjoyed the original, it&rsquo;s mandatory.</p>
<p>On top of that goes the community&rsquo;s <strong>Retexture 3.0</strong> (HD texture pack) and the <strong>HD HUD</strong> by <a href="https://github.com/Silentwarior112/GT4-HD-HUD-Pack"target="_blank" rel="noopener">Silentwarior112</a>. With those three pieces together (Spec II Mod + Retexture + HD HUD), GT4 runs at 1440p SSAA on PCSX2 2.7.x with 16x AF, FXAA, and a look that&rsquo;s honestly indistinguishable from a new low-poly-style release. It&rsquo;s my current favorite to sit down and play long.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Disc</td>
          <td>GT4 USA (SCUS-97328) or Spec II (SCUS-97436, CRC <code>4CE521F2</code>)</td>
      </tr>
      <tr>
          <td>HD HUD pack</td>
          <td>Installed via symlink (Silentwarior112)</td>
      </tr>
      <tr>
          <td>Retexture 3.0</td>
          <td>Installed</td>
      </tr>
      <tr>
          <td>Widescreen pnach</td>
          <td>On (renamed to Spec II CRC if using Spec II)</td>
      </tr>
      <tr>
          <td>Silent&rsquo;s trigger/camera patches</td>
          <td>On</td>
      </tr>
      <tr>
          <td>Deinterlace (Spec II)</td>
          <td>Mode 8 Adaptive TFF</td>
      </tr>
      <tr>
          <td>ShadeBoost (Spec II)</td>
          <td>Saturation +10, Brightness +3, Contrast +2</td>
      </tr>
      <tr>
          <td>FXAA + PCRTC antiblur</td>
          <td>Global</td>
      </tr>
      <tr>
          <td>Upscale</td>
          <td>4x SSAA</td>
      </tr>
  </tbody>
</table>
<p>Spec II users need to pay attention to the CRC: the vanilla GT4 HD packs were built for <code>77E61C8A</code> (USA), so on Spec II with CRC <code>4CE521F2</code> I symlink the assets into the CRC-suffixed folder that PCSX2 looks for. The widescreen pnach was also renamed to match the Spec II CRC. Without that, the game runs but the mods don&rsquo;t load.</p>
<p><strong>In-game reminders:</strong> same as GT3, the widescreen pnach only unlocks the option, you still need to enter Options in-game and switch to 16:9. And important: <strong>change the video mode to progressive 480p</strong> (also in Options). The default is interlaced 480i, which looks terrible on a modern screen. Spec II Mod has native progressive support, just flip it on.</p>
<p>If I could recommend a single GT in the series for someone starting today, it would be this one. If you like racing and never played GT4 in full, you&rsquo;re missing out.</p>
<h3>Enthusia Professional Racing<span class="hx:absolute hx:-mt-20" id="enthusia-professional-racing"></span>
    <a href="#enthusia-professional-racing" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/enthusia.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Enthusia is the forgotten Konami game from 2005. Launched in GT4&rsquo;s shadow and died on the shelves. It&rsquo;s a shame, because <strong>Enthusia&rsquo;s physics was more realistic than GT4&rsquo;s</strong>. Car weight, mass transfer, limit behavior, all closer to real-world behavior. The sim-racing community of the 2000s knew this and the game has a cult following today.</p>
<p>Enthusia&rsquo;s problem was the Enthusia Points system: you earned points for driving clean, lost them for crashing, and the game&rsquo;s progression depended on those points. A lot of people found it punitive. I found it perfect. It forced you to drive with your head.</p>
<p>On PCSX2 with retexture and widescreen, the game looks great. Strong recommendation for anyone who wants something different from Gran Turismo.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Disc</td>
          <td>Enthusia Professional Racing (SLUS-20967)</td>
      </tr>
      <tr>
          <td>Widescreen pnach</td>
          <td>On</td>
      </tr>
      <tr>
          <td>Retexture</td>
          <td>On</td>
      </tr>
      <tr>
          <td>Upscale</td>
          <td>4x SSAA</td>
      </tr>
  </tbody>
</table>
<h3>Ridge Racer V<span class="hx:absolute hx:-mt-20" id="ridge-racer-v"></span>
    <a href="#ridge-racer-v" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/ridge%20racer%20v.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Ridge Racer is its own thing. Not simcade, pure arcade. Drift everything, cartoony grip, electronic soundtrack, saturated colors. Ridge Racer V was the Japanese PS2 launch title and it carries that &ldquo;show what the console can do&rdquo; vibe, with massive scenery and a sense of speed GT never attempted.</p>
<p>On PCSX2 it runs well, with a catch: car textures sometimes flicker on the hardware renderer (PCSX2 issues <a href="https://github.com/PCSX2/pcsx2/issues/3639"target="_blank" rel="noopener">#3639</a> and <a href="https://github.com/PCSX2/pcsx2/issues/13729"target="_blank" rel="noopener">#13729</a>). Software renderer fixes it but kills performance. I stay on hardware and accept the occasional flicker.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Disc</td>
          <td>Ridge Racer V (SLUS-20002)</td>
      </tr>
      <tr>
          <td>Widescreen pnach</td>
          <td>On</td>
      </tr>
      <tr>
          <td>No-interlace pnach</td>
          <td>On</td>
      </tr>
      <tr>
          <td>Upscale</td>
          <td>4x SSAA</td>
      </tr>
  </tbody>
</table>
<h3>Colin McRae Rally 3, 4, 5 (PS2)<span class="hx:absolute hx:-mt-20" id="colin-mcrae-rally-3-4-5-ps2"></span>
    <a href="#colin-mcrae-rally-3-4-5-ps2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>PS2 Colin McRae versions run well on PCSX2 with default configs. CMR3 and CMR04 are good. CMR05 (the last before the rebrand to Dirt) is the best of the three technically. But again: <strong>if you&rsquo;re on PC, look for the native version</strong>. The PS2 ports were relatively inferior to PC of the era, ran at lower resolution, and a modern PC with a repack will hit 144Hz easily.</p>
<p>I keep them on PCSX2 just for collection completeness.</p>
<h2>PS3 on RPCS3<span class="hx:absolute hx:-mt-20" id="ps3-on-rpcs3"></span>
    <a href="#ps3-on-rpcs3" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Gran Turismo 5<span class="hx:absolute hx:-mt-20" id="gran-turismo-5"></span>
    <a href="#gran-turismo-5" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%205.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>GT5 is controversial. Polyphony promised the world and delivered a somewhat fragmented game: gorgeous Premium models next to Standards that were basically HD GT4 cars, a level system some loved and others hated, online that went to mud when PSN service was cut, a Top Gear mode that was weird. The contemporary critical reception was mixed, and I remember entire communities fighting over whether the game was a disappointment or a masterpiece.</p>
<p>For me, it&rsquo;s a masterpiece. GT5 has the Course Maker (design your own track from real sections), real 24h Nurburgring with dynamic weather, the Moon Rover (yes, a drive-on-the-moon mode), Red Bull X-Challenge with Vettel, and the single-player endurance experience is ridiculously big. Anyone who went deep into the game knows there&rsquo;s content for a whole year.</p>
<p>On RPCS3 in 2026, GT5 runs <strong>really well</strong>. Stable, without the serious bugs from years past. It&rsquo;s the most stable GT I have emulated today.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Serial</td>
          <td>BCUS98114 (US) or BCES00569 (EU)</td>
      </tr>
      <tr>
          <td>Resolution Scale</td>
          <td>300%</td>
      </tr>
      <tr>
          <td>Shader Precision</td>
          <td>Ultra</td>
      </tr>
      <tr>
          <td>Force High Precision Z</td>
          <td>On</td>
      </tr>
      <tr>
          <td>SPU XFloat</td>
          <td>Accurate</td>
      </tr>
      <tr>
          <td>Multithreaded RSX</td>
          <td>On</td>
      </tr>
      <tr>
          <td>WCB / RCB</td>
          <td>Off</td>
      </tr>
  </tbody>
</table>
<p>The Shader Precision Ultra + Force High Precision Z combo kills the typical RSX dithering that made GT5 look grainy on RPCS3 in years past. Without those two, asphalt looks permanently noisy. With them, the game looks clean.</p>
<h3>Gran Turismo 6<span class="hx:absolute hx:-mt-20" id="gran-turismo-6"></span>
    <a href="#gran-turismo-6" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/gran%20turismo%206.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>GT6 is one of my favorites in the series. It came out in 2013 for an end-of-life PS3 while the competition was already looking at PS4. Sold poorly. A lot of people didn&rsquo;t play it. That&rsquo;s a shame, because GT6 took the GT5 base, cut the fat (Standards were reduced), added more Premiums, better Moon circuit sessions, dynamic weather on more tracks, physics refinement. It&rsquo;s basically GT5 polished.</p>
<p>On content, GT6 is comparable to GT4. A lot of old-guard players put GT4 above it because of the PS2-era charm. I stay between the two and go GT6 on a normal day, GT4 on a nostalgic day.</p>
<p>On RPCS3, GT6 has a serious catch: <strong>patches 1.06 and beyond regress visually</strong>. Black surfaces on cars in the garage menu, flicker in cockpit view, full-screen flashing on certain tracks. The version that works well in 2026 is <strong>pinned at v1.05</strong>. The repo&rsquo;s <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/scripts/extract_ps3_dlc.py"target="_blank" rel="noopener"><code>extract_ps3_dlc.py</code></a> script has a per-title version ceiling specifically to pin GT6 at 1.05 even when PSN serves 1.22 as &ldquo;most recent&rdquo;.</p>
<p>Also, Force CPU Blit is mandatory. Without it, full-screen flicker in the menu. The trade-off is the rear-view mirror stays permanently black. I prefer losing the mirror to having the flicker. Anyone who wants the mirror can enable Write Color Buffers, but then resolution drops to 720p native.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Serial</td>
          <td>BCES01893 + regional variants</td>
      </tr>
      <tr>
          <td>Version pinned</td>
          <td>v1.05 (patches 1.06+ cause black surfaces)</td>
      </tr>
      <tr>
          <td>Resolution Scale</td>
          <td>300% (menus) or 200% (heavy gameplay)</td>
      </tr>
      <tr>
          <td>Force CPU Blit</td>
          <td>On (mandatory, kills flicker)</td>
      </tr>
      <tr>
          <td>WCB</td>
          <td>Off (trade-off: black rear mirror)</td>
      </tr>
      <tr>
          <td>Shader Precision</td>
          <td>Ultra</td>
      </tr>
  </tbody>
</table>
<p>In 2026, with these settings, GT6 is in my rotation. Not perfect, but playable enough that I can spend an afternoon doing endurance.</p>
<h3>Ridge Racer 7<span class="hx:absolute hx:-mt-20" id="ridge-racer-7"></span>
    <a href="#ridge-racer-7" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ridge Racer 7 was a PS3 launch title in 2006 and the last mainline in the franchise. Arcade like Ridge Racer V, drift-first, electronic soundtrack, cinematic camera. For a PS3 of that era it was a pretty tech demo.</p>
<p>On RPCS3 it runs, but with caveats: requires Write Color Buffers on to avoid lighting issues, and the recommended preset in the compat DB is different from my GT preset. I use a dedicated per-game config. Not my favorite, but it&rsquo;s there for completeness.</p>
<p>No video for this one, I&rsquo;ll record later.</p>
<h2>Xbox 360 on Xenia Canary<span class="hx:absolute hx:-mt-20" id="xbox-360-on-xenia-canary"></span>
    <a href="#xbox-360-on-xenia-canary" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is the part that caused me the most headaches this Sunday. Xbox 360 on Linux is hostile territory. Xenia runs via Wine, each game needs specific tweaks, title updates have to come from archive.org, and Forza Motorsport 4 specifically had serious problems until recently.</p>
<h3>Forza Motorsport 4 (the GOAT)<span class="hx:absolute hx:-mt-20" id="forza-motorsport-4-the-goat"></span>
    <a href="#forza-motorsport-4-the-goat" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20motorsport%204.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>FM4 is, for a lot of people, the best Forza Motorsport ever released. Polyphony made Gran Turismo, Turn 10 made Forza, and FM4 is the peak of the 360 era. Beautiful car models for their time, well-designed tracks, solid physics without being a hard sim, Autovista mode with Jeremy Clarkson narration, epic soundtrack, and that feeling of the Xbox 360 being pushed to its limit.</p>
<p>FM4 on Xenia <strong>was a nightmare until recently</strong>. Severe shadow artifacts, constant texture pop-in on the car, grainy pixelated track textures, sky brightness completely wrong (it came out white-blown-out instead of the deep blue of the original), and worst: frequent audio crashes where XMA (the Xbox 360&rsquo;s proprietary codec) would freeze and the audio would turn into a high-pitched noise until the game hung. I spent hours this Sunday messing with configs, testing builds, searching the xenia-canary issue tracker, testing different title updates.</p>
<p>What unlocked it:</p>
<ul>
<li><strong>Latest Xenia Canary build</strong> (<code>master</code> is stagnant, Canary tracks active development)</li>
<li><strong>Render Target Path:</strong> <code>rtv</code> (the default works fine for FM4)</li>
<li><strong>Title Update 1.0.17.0</strong> installed via Xenia Manager (version matters, older ones regress shadows)</li>
<li><strong>Vulkan GPU with NVIDIA ICD forced</strong> (on my hybrid setup with AMD iGPU, I had to hardforce the 5090)</li>
</ul>
<p>After that, FM4 ran. Not perfectly. Still gets the occasional audio crash (<a href="https://github.com/xenia-canary/xenia-canary/issues/161"target="_blank" rel="noopener">xenia-canary issue #161</a> open). But it&rsquo;s playable, and I spent two hours of the Sunday at Top Gear Test Track just admiring the Koenigsegg CCX. Worth every hour of debugging.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>Latest Xenia Canary</td>
      </tr>
      <tr>
          <td>Render Target Path</td>
          <td><code>rtv</code> (default)</td>
      </tr>
      <tr>
          <td>Title Update</td>
          <td>1.0.17.0</td>
      </tr>
      <tr>
          <td>Wine Prefix</td>
          <td>dedicated via Xenia Manager</td>
      </tr>
      <tr>
          <td>GPU</td>
          <td>NVIDIA via <code>VK_ICD_FILENAMES</code> hardforce</td>
      </tr>
  </tbody>
</table>
<h3>Forza Motorsport 3<span class="hx:absolute hx:-mt-20" id="forza-motorsport-3"></span>
    <a href="#forza-motorsport-3" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20motorsport%203.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>FM3 is one of the solid entries in the series, sitting between FM2&rsquo;s rougher charm and FM4&rsquo;s technical peak. Getting it to emulate was surprisingly annoying, because I was using the wrong ISO.</p>
<p>There are two releases of FM3: the <strong>retail</strong> (original disc sold in stores) and the <strong>Ultimate Edition</strong> (all DLCs packaged on a second disc). I was trying to run the Ultimate, which combines both discs into a hybrid ISO that Xenia can&rsquo;t parse correctly. I swapped to the standard retail + DLCs installed separately via Title Update, and bang, the game ran first try.</p>
<p>Moral: if your FM3 isn&rsquo;t running on Xenia, check that it&rsquo;s retail. Ultimate breaks.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Version</td>
          <td>Retail (NOT Ultimate Edition)</td>
      </tr>
      <tr>
          <td>Build</td>
          <td>Latest Xenia Canary</td>
      </tr>
      <tr>
          <td>Render Target Path</td>
          <td><code>rtv</code></td>
      </tr>
      <tr>
          <td>DLCs</td>
          <td>Installed via Xenia Manager separately</td>
      </tr>
  </tbody>
</table>
<h3>Forza Motorsport 2<span class="hx:absolute hx:-mt-20" id="forza-motorsport-2"></span>
    <a href="#forza-motorsport-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20motorsport%202.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>FM2 is from 2007, first-gen 360. Less refined systems, more basic modeling, but that original Forza charm some prefer to FM4&rsquo;s sophistication. On Xenia it ran practically out of the box. No special tweak.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>Xenia Canary</td>
      </tr>
      <tr>
          <td>Render Target Path</td>
          <td>default</td>
      </tr>
  </tbody>
</table>
<h3>Forza Horizon 2<span class="hx:absolute hx:-mt-20" id="forza-horizon-2"></span>
    <a href="#forza-horizon-2" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20horizon%202.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>FH2 was the most polished Forza Horizon of the 360 generation (there&rsquo;s also the Xbox One version, which is technically better, but the 360 has its charm because of the smaller map and more focused progression). Open world in southern Europe, iconic soundtrack, Horizon festival at its peak.</p>
<p>On Xenia it ran without asking for anything extra. Same as FM2.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>Xenia Canary</td>
      </tr>
      <tr>
          <td>Render Target Path</td>
          <td>default</td>
      </tr>
  </tbody>
</table>
<h3>Forza Horizon 1 (with XE Mod)<span class="hx:absolute hx:-mt-20" id="forza-horizon-1-with-xe-mod"></span>
    <a href="#forza-horizon-1-with-xe-mod" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20horizon%201%20xe%20mod.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Original FH1 runs OK on Xenia, but there&rsquo;s a community maintaining the <strong>Forza Horizon 1 XE Mod</strong>, which improves traffic AI, rebalances progression, adds cars that were cut, fixes known bugs, and brings quality-of-life tweaks to the UI. I tested the XE Mod this Sunday and it&rsquo;s worth it. The game feels more alive and progression feels fairer.</p>
<p>Install details for the XE Mod are on the project site. Basically it&rsquo;s a patch over the original ISO + a few custom title updates.</p>
<p>For comparison, here&rsquo;s FH1 <strong>without</strong> the mod, running vanilla:</p>
<video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/forza%20horizon%201.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>The most visible difference is traffic and the variety of situations that come up, but the XE Mod has a lot of stuff you only notice after a few hours.</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>Xenia Canary</td>
      </tr>
      <tr>
          <td>XE Mod</td>
          <td>Applied over original ISO</td>
      </tr>
      <tr>
          <td>Title Updates</td>
          <td>Version pinned by XE Mod</td>
      </tr>
  </tbody>
</table>
<h3>Project Gotham Racing 3 and 4<span class="hx:absolute hx:-mt-20" id="project-gotham-racing-3-and-4"></span>
    <a href="#project-gotham-racing-3-and-4" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>PGR3 was a 360 launch title in 2005 and still looks gorgeous today. Urban tracks (New York, Tokyo, Nurburgring, Las Vegas), kudos system, cinematic camera. PGR4 added dynamic weather (rain and snow) and motorcycles (some people love them, I prefer to pretend they don&rsquo;t exist).</p>
<p>PGR3 on Xenia Canary runs reasonably well with default configs. PGR4 needs a specific tweak: <strong><code>render_target_path_vulkan = &quot;fsi&quot;</code></strong> in the config. Without it, some tracks break with severe visual artifacts (green bars across the screen in snow races).</p>
<p>And PGR4 on NVIDIA has a known audio bug: XMA decoding produces intermittent garbage (open xenia-canary issue, no resolution). Not game-breaking, but annoying.</p>
<table>
  <thead>
      <tr>
          <th>Game</th>
          <th>Relevant setting</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PGR3</td>
          <td>Default</td>
      </tr>
      <tr>
          <td>PGR4</td>
          <td><code>render_target_path_vulkan = &quot;fsi&quot;</code>, accept the XMA audio bug</td>
      </tr>
  </tbody>
</table>
<h3>Ridge Racer 6<span class="hx:absolute hx:-mt-20" id="ridge-racer-6"></span>
    <a href="#ridge-racer-6" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ridge Racer 6 was the Ridge Racer of the 360, skipping the 7 that stayed on PS3. Pure arcade, like Ridge Racer V of the PS2. On Xenia Canary it runs with default config, no special tweak. Fun for short sessions. No video for this one in the article, but it&rsquo;s in the catalog running.</p>
<h2>PS4 on shadPS4: the state of the emulator<span class="hx:absolute hx:-mt-20" id="ps4-on-shadps4-the-state-of-the-emulator"></span>
    <a href="#ps4-on-shadps4-the-state-of-the-emulator" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p><a href="https://shadps4.net/"target="_blank" rel="noopener">shadPS4</a> <strong>still doesn&rsquo;t have a stable release</strong>. The project is under active development, the main branch changes several times a week, and &ldquo;what works today&rdquo; can regress on a rebase tomorrow. That said, <strong>many games already boot and run well enough</strong>. The community list is on the <a href="https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues"target="_blank" rel="noopener">compatibility tracker</a> and grows every week.</p>
<p>The difference between shadPS4 and the other emulators in this article is the kind of effort. PCSX2, RPCS3, Xenia are mature projects where each game has a reasonably up-to-date wiki page. shadPS4 is the opposite: you read a Reddit guide, try to reproduce, discover the guide uses a specific fork (not the official main), or a two-month-old commit, or a firmware with <code>sys_modules</code> symlinked in a particular way, or XML patches applied in a specific order that the author didn&rsquo;t document. Reproducing a shadPS4 setup from a YouTube video is like trying to solve a Rubik&rsquo;s Cube blindfolded.</p>
<p>But that doesn&rsquo;t mean you shouldn&rsquo;t try. It means you need to treat shadPS4 as a hobby, not a workflow. And if you have the patience, you can pull off impressive results.</p>
<h2>Driveclub: the impossible, finally possible<span class="hx:absolute hx:-mt-20" id="driveclub-the-impossible-finally-possible"></span>
    <a href="#driveclub-the-impossible-finally-possible" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><video
  controls
  preload="metadata"
  playsinline
  class="html5-video"
  style="width:100%; max-width:960px; display:block; margin:1.25em auto; border-radius:8px; background:#000;"
  >
  <source src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/04/videos/driveclub.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

<p>Driveclub is my Moby Dick of emulation. It&rsquo;s the only PS4 game I really want to run. Released in 2014 by Evolution Studios, had a catastrophic launch (online servers collapsed on day one), got patched over two years until it became one of the best racing games of the generation, and was discontinued by Sony when Evolution was closed in 2016. No PC port. No remaster. No sequel. Only exists on PS4.</p>
<p>And today, after many hours of debugging, <strong>it&rsquo;s running here</strong>. Main menu loads, intro plays, races start, controller responds, audio works, daytime tracks visible, night tracks with headlights lighting the road. The video above is real gameplay, stable 30 FPS, v1.28 with DLC content accessible.</p>
<p>Getting here required dismantling several traps. Worth documenting, because most online guides don&rsquo;t mention any of them.</p>
<h3>Trap 1: the FMOD loop error that wasn&rsquo;t FMOD<span class="hx:absolute hx:-mt-20" id="trap-1-the-fmod-loop-error-that-wasnt-fmod"></span>
    <a href="#trap-1-the-fmod-loop-error-that-wasnt-fmod" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>When you try to boot Driveclub on shadPS4 straight from the retail PKG, the log hits an infinite loop of:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>/app0/audio/fmodstudio/masterbank.bank failed, file does not exist</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The first reaction is to investigate FMOD. Some people online spent hours messing with sceFont, libtbb, sceNgs2, convinced it was an audio or HLE lib problem. <strong>None of that is the cause</strong>. The <code>masterbank.bank</code> file literally doesn&rsquo;t exist on disk, because it&rsquo;s packed inside the <code>gameNNN.dat</code> files in a custom Evolution Studios archive format that shadPS4&rsquo;s VFS can&rsquo;t read. Same story for fonts, models, shaders. All the game&rsquo;s content lives inside the <code>.dat</code> files.</p>
<p>The only public tool that parses that format is <strong><a href="https://github.com/Nenkai/DriveClubFS"target="_blank" rel="noopener">Nenkai&rsquo;s DriveClubFS</a></strong>. It&rsquo;s a .NET app that reads <code>game.ndx</code> (the index) and decompresses the <code>.dat</code> files into loose files. Without that unpack, shadPS4 can&rsquo;t open anything.</p>
<p>Build detail: the DriveClubFS project targets <code>net9.0</code>, and Arch has <code>net8</code> and <code>net10</code>. You need to retarget the csproj to <code>net10.0</code> before building. The exact command is documented in the repo&rsquo;s <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>docs/driveclub-shadps4.md</code></a>.</p>
<h3>Trap 2: v1.28 is a patch, not a base installer<span class="hx:absolute hx:-mt-20" id="trap-2-v128-is-a-patch-not-a-base-installer"></span>
    <a href="#trap-2-v128-is-a-patch-not-a-base-installer" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Like every PS4 update, v1.28 is a patch that layers on top of a base install. The v1.28 PKG ships the updated eboot, new-content <code>.dat</code> files, and patch content (<code>sce_sys/about/right.sprx</code>), but <strong>no <code>param.sfo</code>, no <code>disc_info.dat</code>, no <code>keystone</code></strong>. Those metadata files live in the v1.00 base — on a real PS4 they&rsquo;d already be on disk from the original install.</p>
<p>And there&rsquo;s more: v1.28&rsquo;s <code>game.ndx</code> references both the base <code>.dat</code> files and the new ones. Running DriveClubFS against v1.28 alone makes the index point at files that aren&rsquo;t there.</p>
<p>The cruelest symptom hits later: the game boots, the Qt Launcher shows the tile, you click, the loading screen comes up. Then the game decides no content &ldquo;has been released yet&rdquo; and asks you to download. Download obviously isn&rsquo;t happening because PSN for PS4 isn&rsquo;t accessible. You get stuck thinking it&rsquo;s a network bug, when actually it&rsquo;s missing metadata.</p>
<p>The correct flow is to treat v1.28 as an overlay on top of the base. First, extract the v1.00 PKG into a dedicated dir (<code>CUSA00003.v100-working-backup/</code> works well, and doubles as a rollback snapshot if some experiment breaks the live install). Then extract v1.28 into staging and symlink the base&rsquo;s low-index <code>.dat</code> files into the staging dir so DriveClubFS sees the full set. Run the unpack. You get 8018 loose files, ~47 GB.</p>
<p>Move that into the live <code>CUSA00003/</code> dir, copy v1.28&rsquo;s updated eboot and new patch content on top, then pull <code>param.sfo</code>, <code>disc_info.dat</code>, and <code>keystone</code> from the v1.00 base. The base&rsquo;s <code>param.sfo</code> already has <code>APP_VER = 01.28</code> recorded (because it was the version that received the patch originally), so there&rsquo;s no version conflict. The v1.28 eboot reads those files and unlocks the content.</p>
<h3>Trap 3: the 60fps XML patch produces slow-motion<span class="hx:absolute hx:-mt-20" id="trap-3-the-60fps-xml-patch-produces-slow-motion"></span>
    <a href="#trap-3-the-60fps-xml-patch-produces-slow-motion" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>There&rsquo;s a community <code>Driveclub.xml</code> patch that makes the game render at 60fps. Intuitively, it looks like pure upside. In practice, the patch rewrites the render rate but doesn&rsquo;t touch the game&rsquo;s fixed logic tickrate, which is locked at 30fps. On a real PS4 Pro, the hardware reconciles the mismatch. On shadPS4, it doesn&rsquo;t.</p>
<p>Result: the game renders smoothly at 60fps, but the physics, AI, audio, race timing all run in slow motion. You press the throttle and the car takes a perceptible two seconds to launch. Looks like a random bug until you understand the mismatch.</p>
<p>Disabling the XML patch is the move (just rename the file to something like <code>.disabled</code>). Without the patch, the game runs at native 30 FPS, same speed as a stock PS4 base. Which is what we want. If someone in the future produces a patch that moves both rates (render + logic), we&rsquo;ll flip it back on.</p>
<h3>Trap 4: pipeline cache has to be on<span class="hx:absolute hx:-mt-20" id="trap-4-pipeline-cache-has-to-be-on"></span>
    <a href="#trap-4-pipeline-cache-has-to-be-on" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>shadPS4&rsquo;s default ships with <code>pipeline_cache_enabled = false</code>. On a large game like Driveclub, which has hundreds of Vulkan shaders and pipelines, that means every cold launch recompiles everything. My first launch counted ~864 shaders and ~590 pipelines being compiled in real time. The menu animated at 2-3 fps. A minute later, with everything compiled, it snapped to normal fluidity. Next launch, same from-scratch compilation cycle.</p>
<p>Just enable <code>&quot;pipeline_cache_enabled&quot;: true</code> in the global <code>config.json</code>. The first session pays the one-time compilation cost, subsequent sessions read from cache and start up instantly.</p>
<h3>Trap 5: TOML silently ignored<span class="hx:absolute hx:-mt-20" id="trap-5-toml-silently-ignored"></span>
    <a href="#trap-5-toml-silently-ignored" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>shadPS4 migrated from TOML to JSON for per-game configs in late 2025. If you have an old <code>CUSA00003.toml</code> in the <code>custom_configs/</code> folder, it&rsquo;s silently ignored, no warning in the log. You think your config is active, in reality you&rsquo;re running all defaults.</p>
<p>Delete any TOML and use <code>CUSA00003.json</code>. The per-game configs that work for Driveclub are specific and detailed, all documented in the <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>docs/driveclub-shadps4.md</code></a> of the repo.</p>
<h3>Trap 6: controller not detected without hidapi hints<span class="hx:absolute hx:-mt-20" id="trap-6-controller-not-detected-without-hidapi-hints"></span>
    <a href="#trap-6-controller-not-detected-without-hidapi-hints" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>shadPS4&rsquo;s Qt Launcher calls an internal <code>Shadps4-sdl.AppImage</code>. Without specific SDL hidapi env vars, the controller (8BitDo Ultimate 2 in my case) isn&rsquo;t detected, even though it works in every other emulator. The fix is to wrap the AppImage in a shell that exports <code>SDL_JOYSTICK_HIDAPI=1</code> and platform-specific variants (PS4, PS5, Xbox), before executing the original.</p>
<p>Plugging the controller in <strong>before</strong> opening the Qt Launcher also helps. Hot-plug works during the game, not before.</p>
<h3>Trap 7: night tracks are pitch black on upstream<span class="hx:absolute hx:-mt-20" id="trap-7-night-tracks-are-pitch-black-on-upstream"></span>
    <a href="#trap-7-night-tracks-are-pitch-black-on-upstream" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>This one needed a code patch, not just config. On upstream shadPS4, Driveclub night tracks came out pitch black. The HUD rendered on top, but the track, the car, everything hidden in absolute black. Not even the car&rsquo;s own headlights lit anything up.</p>
<p>Investigation pointed at the log repeating 1325 times the message <code>ResolveDepthOverlap: Unimplemented depth overlap copy</code>. The cause: Driveclub uses a forward+ renderer, which writes scene depth into a 4x MSAA D32Sfloat buffer, then reads that same buffer as <code>R32G32B32A32Sfloat</code> 1x for effects like SSAO, volumetrics, and dynamic lighting (headlights). shadPS4 upstream has a path for MSAA → MSAA and for color → color as MSAA depth, but <strong>it didn&rsquo;t have a path for &ldquo;MSAA depth 4x becomes color 1x&rdquo;</strong> in that specific format. It falls into the final <code>else</code>, frees the buffer, and the shader reads uninitialized memory — i.e., black.</p>
<p>I implemented in my local shadPS4 fork a <code>ReinterpretMsDepthAsColor</code> symmetric to the existing <code>ReinterpretColorAsMsDepth</code>. It&rsquo;s a tiny fragment shader (<code>ms_depth_to_color.frag</code>) that does <code>texelFetch</code> of sample 0&rsquo;s depth and writes it as <code>vec4(depth, 0, 0, 1)</code> to the color attachment. Plus a <code>BlitHelper</code> helper and a new <code>else if</code> branch in <code>TextureCache::ResolveDepthOverlap</code>. With that, night tracks light up properly, opponent headlights appear, AO and volumetrics on day scenes get more precise too.</p>
<p>The fix lives on a <code>gamma-debug</code> branch of my fork, ready to become an upstream PR. Until it merges, anyone who wants to race at night on Driveclub on shadPS4 needs to build from that branch. Anyone sticking to daytime can use upstream nightly directly.</p>
<h3>What finally worked<span class="hx:absolute hx:-mt-20" id="what-finally-worked"></span>
    <a href="#what-finally-worked" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><table>
  <thead>
      <tr>
          <th>Component</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>shadPS4</td>
          <td>Upstream Pre-release (nightly) for daytime; <code>gamma-debug</code> fork for night (until MSAA depth fix lands upstream)</td>
      </tr>
      <tr>
          <td>Driveclub</td>
          <td><strong>v1.28 base</strong> (cumulative patch, ~47 GB, 8018 files)</td>
      </tr>
      <tr>
          <td>Extraction</td>
          <td><a href="https://github.com/shadps4-emu/ShadPKG"target="_blank" rel="noopener">ShadPKG</a> → <a href="https://github.com/Nenkai/DriveClubFS"target="_blank" rel="noopener">DriveClubFS</a> (retargeted to net10.0)</td>
      </tr>
      <tr>
          <td>Metadata</td>
          <td>Restore <code>param.sfo</code>, <code>disc_info.dat</code>, <code>keystone</code> from the v1.00 PKG</td>
      </tr>
      <tr>
          <td>Global config</td>
          <td><code>pipeline_cache_enabled: true</code>, <code>readbacks_mode: 0</code></td>
      </tr>
      <tr>
          <td>Per-game config</td>
          <td>JSON at <code>custom_configs/CUSA00003.json</code></td>
      </tr>
      <tr>
          <td><code>vblank_frequency</code></td>
          <td>60</td>
      </tr>
      <tr>
          <td><code>gpu_id</code></td>
          <td>0 (NVIDIA dGPU hardforced)</td>
      </tr>
      <tr>
          <td><code>Driveclub.xml</code> patch</td>
          <td><strong>Disabled</strong> (60fps produces slow-motion, root cause: fixed logic tickrate)</td>
      </tr>
      <tr>
          <td>Controller</td>
          <td>AppImage wrapper with <code>SDL_JOYSTICK_HIDAPI=1</code> + variants</td>
      </tr>
      <tr>
          <td>Qt Launcher</td>
          <td><code>checkForUpdates=false</code> to avoid GitHub rate-limit dialog</td>
      </tr>
  </tbody>
</table>
<p>All of this is encapsulated in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>docs/driveclub-shadps4.md</code></a> in the repo, with the exact step-by-step for extraction, config, and wrapper. The full technical investigation (5 phases, detailed engineering log) lives in the fork&rsquo;s <a href="https://github.com/akitaonrails/shadPS4/blob/gamma-debug/docs/driveclub-v128-investigation.md"target="_blank" rel="noopener"><code>driveclub-v128-investigation.md</code></a>.</p>
<h3>How playable it is today<span class="hx:absolute hx:-mt-20" id="how-playable-it-is-today"></span>
    <a href="#how-playable-it-is-today" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Good. The video above is real gameplay, stable 30 FPS, physics responding, audio in sync, HUD correct, v1.28 with DLC accessible. Day races and night races both work (with the MSAA depth fork for night). What&rsquo;s left as limitation:</p>
<ul>
<li><strong>Daytime brightness sits a bit below what the PS4 delivered.</strong> Not an emulator bug. I spent hours testing tonemap, auto-exposure, gamma curves, ACES, various post-processing shaders, and in the end discovered the game <strong>writes already-correct SDR</strong> to the framebuffer. What was missing was the display-calibration pipeline that PS4 firmware 4.0+ applied on the outside, and that Linux doesn&rsquo;t have. The in-game brightness slider (which on shadPS4 maps to <code>sceVideoOutAdjustColor</code>) works, but only up to the point where the game expected the firmware to pick up the rest. The 5-second dim at the start of a race is also the game&rsquo;s own cinematic fade-in VFX, not an emulator fault. Accepted.</li>
<li><strong>Native 30 FPS.</strong> No viable 60fps patch (due to the fixed logic tickrate explained above). Stock PS4 speed.</li>
<li><strong>MSAA depth fix still on the fork.</strong> PR candidate ready, awaiting upstream merge.</li>
</ul>
<p><strong>&ldquo;Achievement Unlocked&rdquo;.</strong> The game runs, with sound, controller, gameplay, day and night, and I complete a whole race without crashing. That&rsquo;s a personal milestone. I spent years trying to get this game running on Linux and would always hit a point where I just gave up. Now it&rsquo;s part of my ES-DE, shortcut on the desktop, sitting there waiting for me to play.</p>
<p>If you want to replicate, the repo is public. Good luck. You&rsquo;re going to need patience.</p>
<h2>&ldquo;Achievement Unlocked&rdquo;<span class="hx:absolute hx:-mt-20" id="achievement-unlocked"></span>
    <a href="#achievement-unlocked" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve been trying to make these games work, at some level, since each respective emulator came out of alpha. I&rsquo;ve been running GT2 since ePSXe. I&rsquo;ve been messing with GT4 since PCSX2 0.9.6. I tried to get FM4 running on Xenia twice in past years and gave up both times. And I looked at shadPS4&rsquo;s face three times before this Sunday and swallowed hard.</p>
<p>Today it closed. The two monsters that opened this article (Driveclub on shadPS4 and FM4 with Project Forza Plus on Xenia) are running, along with the rest of the Gran Turismo, Forza, Ridge Racer, Colin McRae, and Enthusia collection. All in a single setup, reproducible, automated. I&rsquo;m now part of the <strong>very small group of people who brute-forced their way into emulating Driveclub on Linux</strong>, and that alone made the Sunday unforgettable.</p>
<p>The frustration of years wasn&rsquo;t lack of information. It was the OPPOSITE. Too much information, poorly indexed, scattered across dead forums, YouTube videos that went stale, Reddit threads with solutions for two-year-old emulator versions that don&rsquo;t apply today, wikis that are out of date on half the pages, and mods with VERY specific requirements of CRC, version, patch, path, setting. Every time I sat down to get one of these games running right, I spent three hours reading conflicting material before writing the first line of config. Worse still when the good material was buried under wrong diagnoses, like the Driveclub FMOD loop everyone investigated as if it was an audio problem when in reality it was Evolution Studios&rsquo; proprietary archive format.</p>
<p>What changed in the last year is that Claude Code can be my aggregator. I tell Claude to hit the Xenia issue tracker, the PCSX2 wiki, the RPCS3 subreddit, shadPS4 PRs, cross-reference all that info with the emulator version I have installed, cross-reference with the logs I&rsquo;m looking at, and give me back the combination that works today. It reads source code when needed, understands why <code>Force CPU Blit</code> changes behavior on GT6, finds which FM4 title update was the one that fixed the shadow bug, discovers that DriveClubFS needs a retarget to .NET 10 before compiling. What used to take three weekends of research now takes an afternoon of assisted execution.</p>
<p>This Sunday is the consolidated result of all that work. <a href="https://github.com/akitaonrails/distrobox-gaming"target="_blank" rel="noopener">distrobox-gaming</a> is that knowledge turned into code, reproducible, automated. Starting on a fresh machine, one <code>ansible-playbook site.yml</code> puts me back in this state in under two hours. Fewer than 10 commands separate &ldquo;vanilla Arch Linux&rdquo; from &ldquo;Driveclub running&rdquo;.</p>
<p>If anyone wants to contribute, test on different hardware, report a bug, or send a config that&rsquo;s missing, the repo is open. The more people test on different setups, the more resilient the project gets. If you improve the Driveclub situation (get the MSAA depth fix merged upstream, a 60fps patch that also fixes the logic tickrate, font dumps that don&rsquo;t need jailbroken hardware), send a PR and I&rsquo;ll embrace it.</p>
<p>For now, I&rsquo;m going to do a race in Iceland on Driveclub. Good Sunday night.</p>
]]></content:encoded><category>gaming</category><category>emulation</category><category>linux</category></item><item><title>An Emulation Distrobox with Claude Code</title><link>https://akitaonrails.github.io/en/2026/04/11/emulation-distrobox-with-claude-code/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/04/11/emulation-distrobox-with-claude-code/</guid><pubDate>Sat, 11 Apr 2026 18:00:00 -0300</pubDate><description>&lt;p&gt;I&amp;rsquo;ve loved old videogames since before many of you were born. My first contact was back in the Atari era, in the early 80s. Then came the 8-bit micros, the 90s arcades, the 16, 32 and 64-bit consoles, and I kept following all of it. For me, nostalgia is not a cute Instagram folder. It&amp;rsquo;s an archive. ROMs, BIOS files, dumps, patches, DLC, firmware, saves. Over the years I kept everything on my NAS. At this point I have terabytes of it under &lt;code&gt;/mnt/terachad/Emulators&lt;/code&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve loved old videogames since before many of you were born. My first contact was back in the Atari era, in the early 80s. Then came the 8-bit micros, the 90s arcades, the 16, 32 and 64-bit consoles, and I kept following all of it. For me, nostalgia is not a cute Instagram folder. It&rsquo;s an archive. ROMs, BIOS files, dumps, patches, DLC, firmware, saves. Over the years I kept everything on my NAS. At this point I have terabytes of it under <code>/mnt/terachad/Emulators</code>.</p>
<p>On my YouTube channel I even used old games to teach computer science fundamentals. In <a href="https://www.youtube.com/watch?v=hYJ3dvHjeOE&amp;pp=ygUUYWtpdGFuZG8gc3VwZXIgbWFyaW8%3D"target="_blank" rel="noopener">this Akitando episode about Super Mario and old computers</a>, I explain the 6502, memory maps, the PPU, hardware constraints, and why those games were programmed the way they were. If you never watched it, start there:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/hYJ3dvHjeOE"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>The problem is always the same: every few years I decide to set up a new emulation machine, download the main emulators again, and there I go repeating the same masochistic ritual. <code>PCSX2</code>, <code>RPCS3</code>, <code>Eden</code>, <code>Azahar</code>, <code>Dolphin</code>, <code>RetroArch</code>, <code>Flycast</code>, <code>shadPS4</code>, and so on. Each one with its quirks. Each one with its own particular way of wasting hours of your life.</p>
<p>In theory, setting all this up should be fun. In practice, it takes days. And that&rsquo;s not an exaggeration. <code>Dolphin</code> still manages to be one of the worst because GameCube and Wii controller setups have no standard. <code>RPCS3</code> requires per-game tuning. <code>Eden</code> needs DLC, updates and cheats in the right place. <code>shadPS4</code> is a festival of trial and error. When I&rsquo;d finally finish configuring everything, I didn&rsquo;t feel like playing anything anymore. I just wanted to close the menus and go do something else.</p>
<p>I&rsquo;ve been known to say that maybe the fun was the tuning itself. I don&rsquo;t think that anymore. After repeating this process too many times, I&rsquo;m done. I want to play the games, not fill out forms hidden inside emulator GUIs.</p>
<h2>The problem was never Linux<span class="hx:absolute hx:-mt-20" id="the-problem-was-never-linux"></span>
    <a href="#the-problem-was-never-linux" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve solved this kind of thing in different ways in the past. Years ago I ran Linux on the host and a virtualized Windows with GPU passthrough to play inside the VM. At the time that made sense. I made a whole video about it:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/IDnabc3DjYY"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>But that was before Valve, Proton, modern Wine and the whole open source community pushed the Linux desktop to a much better place. Today you can avoid Windows in most cases. In the other video below I explain the evolution of CPU, GPU, OpenGL, Vulkan, and why we ended up here:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/JEp7ozWqIps"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>My gaming mini-PC with the RTX 4090 is still my main Steam machine, especially now that I&rsquo;ve built a <a href="/en/2026/04/01/my-sim-racing-cockpit-formula-fx1/">proper sim racing cockpit</a>. On it I use <a href="https://www.emudeck.com/"target="_blank" rel="noopener">EmuDeck</a>, which was born to automate installing and configuring emulators on the Steam Deck, then grew Windows support, and helps cut the complexity of setting up this kind of environment. But my main desktop is also way too powerful to leave underused. It has an RTX 5090, and today I use that GPU mostly to test LLMs and for benchmarking, as I wrote in <a href="/en/2026/04/05/testing-llms-open-source-and-commercial-can-anyone-beat-claude-opus/">Testing Open Source and Commercial LLMs</a>. Leaving it idle for gaming would be a waste.</p>
<p>Only on Linux I wanted something else. I didn&rsquo;t want a black box doing everything for me without me knowing exactly what was being changed, especially on my main PC, which is my work machine. I wanted my own setup, handmade in the right sense: not clicking GUI by GUI, but understanding what&rsquo;s being configured, keeping the files under my control, and being able to rebuild everything the way I like. I know the NixOS folks will jump in here to say &ldquo;just use Nix&rdquo; — I&rsquo;ll explain why I ruled that out below. I also didn&rsquo;t want to turn my work machine into a carnival of emulator packages, GTK themes, wrappers, launchers, weird runtimes and configs buried in <code>~/.config</code>. Nor did I want dual boot or a VM. In 2026, the best answer for this, in my opinion, is <a href="https://distrobox.it/"target="_blank" rel="noopener">Distrobox</a>.</p>
<p>From what the project itself explains, Distrobox is a wrapper on top of <code>podman</code>, <code>docker</code> or <code>lilipod</code> to create containers tightly integrated with the host, with access to <code>HOME</code>, Wayland/X11, audio, USB devices, external storage and GPU. In other words: exactly the kind of pragmatic isolation I wanted. It&rsquo;s not a security boundary, and the project site makes that clear. Don&rsquo;t use it thinking about high-security sandboxing. Use it thinking about separating environments without paying the price of a VM.</p>
<p>And before anyone repeats the usual confusion: a container is not a virtual machine. I explain this calmly in this other episode:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/85k8se4Zo70"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>The setup<span class="hx:absolute hx:-mt-20" id="the-setup"></span>
    <a href="#the-setup" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The idea is simple: a vanilla Arch Linux inside a Distrobox called <code>gaming</code>, with NAS paths mounted read-only, Steam library accessible where it makes sense, and all emulators installed inside. Since the container has access to the NVIDIA GPU, audio, USB and other peripherals, I can separate work and gaming on the same machine without practical performance penalty.</p>
<p>The initial bootstrap looks roughly like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">distrobox create --name gaming --image archlinux:latest --nvidia <span class="se">\
</span></span></span><span class="line"><span class="cl">  --home /mnt/data/distrobox/gaming <span class="se">\
</span></span></span><span class="line"><span class="cl">  --volume /mnt/data/steam:/mnt/data/steam <span class="se">\
</span></span></span><span class="line"><span class="cl">  --volume /mnt/terachad/Emulators:/mnt/terachad/Emulators:ro</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Even that first step has a catch. <code>archlinux:latest</code> inside Distrobox comes without <code>[multilib]</code> in <code>pacman.conf</code>, with a misconfigured <code>sudoers</code>, and with the old <code>--nvidia</code> problem: the host driver libs get bind-mounted read-only, so any install that depends on <code>nvidia-utils</code> breaks with file conflicts. Just that alone is the kind of headache that, a few years ago, would make me open half a dozen tabs, a bunch of terminals, and spend a whole night in trial and error.</p>
<p>This time I did it differently.</p>
<h2>Claude Code as an infrastructure assistant<span class="hx:absolute hx:-mt-20" id="claude-code-as-an-infrastructure-assistant"></span>
    <a href="#claude-code-as-an-infrastructure-assistant" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>I&rsquo;ve been getting more and more comfortable using Claude Code as my infrastructure assistant on my personal machines. I wouldn&rsquo;t do this blindly on a client server. But on my own machine, in an environment I can destroy and rebuild however many times I want, it makes all the sense in the world. I wrote about this in <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">Migrating my Home Server with Claude Code</a>, when I used Claude to install and configure openSUSE MicroOS, Docker, NFS, firewall, hardening, and all the rest without me having to remember annoying shell commands.</p>
<p>So I thought: why not do the same thing here?</p>
<p>That&rsquo;s exactly what I did. I started with a sequence of objective prompts, always asking for two things at once: do the work, and document enough for me to rebuild everything later. The most important history ended up in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/distrobox-gaming-prompts.md"target="_blank" rel="noopener"><code>docs/distrobox-gaming-prompts.md</code></a>.</p>
<p>A clarification worth making: those prompts on GitHub aren&rsquo;t my raw messages, the way they came out in real time. After everything worked, I asked Claude itself to rewrite the prompts in a much more organized and detailed way, purely for documentation. The original prompts were much simpler and much less specific. I described the goal and let Claude figure out paths, config files, formats and commands on its own.</p>
<p>The first prompt created the box with <code>--nvidia</code>, a separate <code>--home</code>, and correct mounts. The second solved the three classic problems of Arch inside Distrobox: <code>sudo</code>, <code>multilib</code>, and the <code>nvidia-utils</code> dummy package. The third installed the gaming base, including a detail I would have easily forgotten if I was doing it by hand: <code>pipewire-pulse</code>, needed so several emulators don&rsquo;t end up mute or out of sync.</p>
<p>The point isn&rsquo;t the text itself. The point is the method. I don&rsquo;t need to remember the exact order, the precise options, or where the docs for each detail are. I describe the goal and let the agent carry the piano. I stay in the tech-lead role: watching, reviewing, course-correcting when needed, and telling it to keep going.</p>
<h2>The GitHub project<span class="hx:absolute hx:-mt-20" id="the-github-project"></span>
    <a href="#the-github-project" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The setup lives in <a href="https://github.com/akitaonrails/distrobox-gaming"target="_blank" rel="noopener">akitaonrails/distrobox-gaming</a>, structured as an Ansible project. 17 roles cover everything from box creation, package bootstrap, config seeding, DLC installation, controller setup, to post-setup verification. The main playbooks are:</p>
<ul>
<li><code>site.yml</code>: full setup from scratch</li>
<li><code>reset-configs.yml</code>: reset configs only (useful when you want to wipe tuning and redeploy)</li>
<li><code>backup.yml</code> / <code>restore.yml</code>: container snapshot and restore via <code>podman commit</code></li>
<li><code>refresh-shadps4.yml</code>: standalone shadPS4 update</li>
<li><code>install-xenia.yml</code>: optional Xenia Manager install (Wine prefix)</li>
</ul>
<p>Full walkthrough is in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/rebuild-runbook.md"target="_blank" rel="noopener"><code>docs/rebuild-runbook.md</code></a>. Package decisions (why AUR instead of Flatpak, for example) are in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/distrobox-gaming-packages.md"target="_blank" rel="noopener"><code>docs/distrobox-gaming-packages.md</code></a>.</p>
<h3>Why not Nix?<span class="hx:absolute hx:-mt-20" id="why-not-nix"></span>
    <a href="#why-not-nix" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Someone will ask. &ldquo;You&rsquo;re recreating a reproducible environment. Why not use NixOS, or at least <code>nix-shell</code>/flakes?&rdquo;</p>
<p>I considered and ruled it out. The emulator ecosystem I need lives on the AUR. AUR <code>-bin</code> packages are wrappers around AppImages that ship the official upstream binary without compiling anything. <code>rpcs3-bin</code>, <code>pcsx2-latest-bin</code>, <code>eden-bin</code>, <code>duckstation-qt-bin</code>, <code>shadps4-bin</code> — these binaries track upstream practically on release day. On Nixpkgs, many of these emulators are weeks or months behind release, and some don&rsquo;t exist. Creating a Nix overlay for every emulator I need, maintaining it, and dealing with compatibility patches when upstream changes is work I don&rsquo;t want to have.</p>
<p>There&rsquo;s also the mental model. I don&rsquo;t want to learn the Nix language, the derivation system and the evaluation model just to set up a personal gaming environment. I already know Ansible, roles are folders with YAML tasks, and debugging is <code>ansible-playbook -vvv</code>. When something goes wrong, I read the error, open the task that failed, and know exactly where to fix it. If I needed the same level of reproducibility at scale, across a cluster, with atomic rollback, then Nix would have an argument. For a distrobox with emulators on my personal machine, Ansible solves it with much less friction.</p>
<p>Flatpak was also tested and ruled out. Flatpak&rsquo;s <code>bwrap</code> (bubblewrap) doesn&rsquo;t nest properly inside Docker&rsquo;s mount namespaces, so Flatpak apps simply don&rsquo;t run inside distrobox. The alternative would be installing Flatpak on the host, but then you&rsquo;re back to polluting the work machine with gaming packages. Plus, Flatpak versions of emulators lag well behind AUR — the AUR&rsquo;s <code>pcsx2-latest-bin</code> tracks the 2.7.x AppImage, while Flathub still ships v2.6.3.</p>
<h2>Platforms covered<span class="hx:absolute hx:-mt-20" id="platforms-covered"></span>
    <a href="#platforms-covered" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The setup covers 12 platforms today:</p>
<table>
  <thead>
      <tr>
          <th>Platform</th>
          <th>Emulator</th>
          <th>Renderer</th>
          <th>Highlight</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PS1</td>
          <td>DuckStation</td>
          <td>Vulkan, 8x upscale</td>
          <td>JINC2 filter, PGXP, widescreen hack</td>
      </tr>
      <tr>
          <td>PS2</td>
          <td>PCSX2 2.7.x</td>
          <td>Vulkan, 4x SSAA</td>
          <td>FXAA + PCRTC antiblur, Xbox bindings</td>
      </tr>
      <tr>
          <td>PS3</td>
          <td>RPCS3</td>
          <td>Vulkan</td>
          <td>Curated per-game configs via API</td>
      </tr>
      <tr>
          <td>PS4</td>
          <td>shadPS4</td>
          <td>Vulkan</td>
          <td>Driveclub-focused (experimental)</td>
      </tr>
      <tr>
          <td>PS Vita</td>
          <td>Vita3K</td>
          <td>—</td>
          <td>Default seeding</td>
      </tr>
      <tr>
          <td>GameCube</td>
          <td>Dolphin</td>
          <td>Vulkan</td>
          <td>8BitDo Ultimate 2 profiles</td>
      </tr>
      <tr>
          <td>Wii</td>
          <td>Dolphin</td>
          <td>Vulkan</td>
          <td>Classic Controller + Nunchuk</td>
      </tr>
      <tr>
          <td>Wii U</td>
          <td>Cemu</td>
          <td>Vulkan</td>
          <td>Baseline seed only-if-missing</td>
      </tr>
      <tr>
          <td>Switch</td>
          <td>Eden</td>
          <td>Vulkan</td>
          <td>Atmosphere DLC + cheats</td>
      </tr>
      <tr>
          <td>Dreamcast</td>
          <td>Flycast</td>
          <td>Vulkan, 2880p</td>
          <td>Hi-res wrapper, widescreen</td>
      </tr>
      <tr>
          <td>OG Xbox</td>
          <td>xemu</td>
          <td>—</td>
          <td>BIOS/HDD via symlink</td>
      </tr>
      <tr>
          <td>Xbox 360</td>
          <td>Xenia (Wine)</td>
          <td>Vulkan</td>
          <td>Project Forza Plus, PGR3/4</td>
      </tr>
  </tbody>
</table>
<p>RetroArch comes on top to cover the rest (NES, SNES, Genesis, N64, arcade, etc.), with 21 buildbot cores and 8 asset packs (databases, shaders, cheats, overlays, autoconfig).</p>
<p>Frontend is <a href="https://es-de.org/"target="_blank" rel="noopener">ES-DE</a>. Custom system definitions live in <code>ansible/group_vars/all/esde.yml</code>, and each emulator comes with a wrapper that sets the right GPU environment variables (more on that in a sec).</p>
<h2>GPU hardforce for NVIDIA<span class="hx:absolute hx:-mt-20" id="gpu-hardforce-for-nvidia"></span>
    <a href="#gpu-hardforce-for-nvidia" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>On a hybrid system with NVIDIA dGPU + AMD iGPU (my case on the desktop), Vulkan sometimes picks the iGPU on its own and you end up with an emulator running at 15 fps while the 5090 sits idle. The fix is to force NVIDIA&rsquo;s Vulkan ICD in every launcher.</p>
<p>Each desktop entry rendered by Ansible exports <code>VK_ICD_FILENAMES</code> pointing to NVIDIA&rsquo;s ICD, and the ES-DE wrapper does the same before launching any core. Combined with the <code>--nvidia</code> flag at box creation, which bind-mounts host drivers read-only, the result is predictable: Vulkan always on the NVIDIA dGPU, regardless of system state at runtime.</p>
<p>This mostly matters for hybrid setups. On a desktop with a single GPU, the ICD filtering is placebo, but it doesn&rsquo;t hurt.</p>
<h2>PS2: PCSX2 with per-game tuning<span class="hx:absolute hx:-mt-20" id="ps2-pcsx2-with-per-game-tuning"></span>
    <a href="#ps2-pcsx2-with-per-game-tuning" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The Gran Turismo line is my declared addiction. Each version needs its own specific tweak. The PCSX2 setup today has sane global configs (Vulkan, 4x SSAA, widescreen 16:9, Xbox-style bindings, FXAA + PCRTC antiblur, 16x anisotropic, Automatic deinterlace) and per-game INIs for the titles I actually play:</p>
<ul>
<li><strong>Gran Turismo 3 A-spec</strong> (SCUS-97102 + bundle PBPX-95503): widescreen pnach, optional retexture pack (disabled by default because NFS streaming causes flicker in car-showcase cutscenes)</li>
<li><strong>Gran Turismo 4 USA</strong> (SCUS-97328): Silentwarior112 HD HUD, full retexture pack, Silent&rsquo;s trigger/cam patches, widescreen pnach</li>
<li><strong>Gran Turismo 4 Spec II</strong> (SCUS-97436, CRC <code>4CE521F2</code>): special case — the vanilla GT4 HD packs are linked via symlink because Spec II has the same structure but a different CRC. Widescreen pnach renamed to match the CRC. Deinterlace mode 8 (Adaptive TFF), ShadeBoost tuning (+10 saturation, +3 brightness, +2 contrast) to fix pause-interlace ghosting and the anemic upscale saturation</li>
<li><strong>Enthusia Professional Racing</strong> (SLUS-20967): HD textures + widescreen</li>
<li><strong>Ridge Racer V</strong> (SLUS-20002): widescreen + no-interlace pnach</li>
</ul>
<p>Silent&rsquo;s and community pnach files are downloaded automatically by the <code>pcsx2_textures</code> role, which also deploys the cheats pack from the NAS and installs the Spec II HD packs via symlink. Widescreen is enabled per-CRC via <code>gamesettings</code> (PCSX2 identifies games by CRC before applying pnach).</p>
<p>On PCSX2 versioning: the AUR&rsquo;s <code>pcsx2-latest-bin</code> tracks the 2.7.x line, which is where all the modern features live (texture replacement, FXAA, PCRTC antiblur). The default <code>pcsx2</code> in Arch repos is still on 2.6.3. The gap is over 250 commits of fixes and new features.</p>
<h2>PS3: RPCS3 with curated per-game configs<span class="hx:absolute hx:-mt-20" id="ps3-rpcs3-with-curated-per-game-configs"></span>
    <a href="#ps3-rpcs3-with-curated-per-game-configs" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>RPCS3 has an annoying detail: per-game configs live in <code>~/.config/rpcs3/custom_configs/config_&lt;SERIAL&gt;.yml</code>. The file format has a version, and if you hand-write YAML with the wrong version, RPCS3 silently rejects it and falls back to defaults. That cost me time to figure out.</p>
<p>The <code>rpcs3_per_game_configs</code> role handles it like this:</p>
<ol>
<li>Queries the <a href="https://rpcs3.net/compatibility"target="_blank" rel="noopener">RPCS3 community compatibility DB</a> for recommended presets per title</li>
<li>Applies curated overrides on top for titles I know need specific tuning (typically GT5 and GT6)</li>
<li>Saves the YAML with the exact filename convention (<code>config_&lt;SERIAL&gt;.yml</code>, <code>config_</code> prefix mandatory)</li>
</ol>
<p>For the Gran Turismo line:</p>
<ul>
<li><strong>GT5</strong> (BCUS98114, BCES00569): Resolution Scale 300%, Shader Precision Ultra, Force High Precision Z, SPU XFloat Accurate, Multithreaded RSX. The combo kills the typical RSX dithering without breaking the GT-series safety config (WCB/RCB off).</li>
<li><strong>GT6</strong> (BCES01893 + regional variants): special case, <strong>version pinned to v1.05</strong>. Patches 1.06+ regress with black surfaces on cars in the garage menu. The <code>extract_ps3_dlc.py</code> script has a per-title PATCH ceiling specifically to pin GT6 at 1.05 even when PSN serves newer patches. Also, Force CPU Blit is mandatory (without it, full-screen flicker). The trade-off is that the rear-view mirror stays permanently black — turning WCB on would restore the mirror but downgrade to native 720p. I&rsquo;ll take no mirror.</li>
</ul>
<p>More details on those two in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/gt5-rpcs3.md"target="_blank" rel="noopener"><code>docs/gt5-rpcs3.md</code></a> and <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/gt6-rpcs3.md"target="_blank" rel="noopener"><code>docs/gt6-rpcs3.md</code></a>.</p>
<h3>PS3 update checker<span class="hx:absolute hx:-mt-20" id="ps3-update-checker"></span>
    <a href="#ps3-update-checker" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>To avoid opening RPCS3&rsquo;s GUI and clicking <code>Download Game Updates</code> game by game, I wrote a Python script: <code>check_ps3_updates.py</code>. It walks the games installed in <code>dev_hdd0/game</code>, parses <code>PARAM.SFO</code> to get the current version per title, queries the PSN update server (<code>a0.ww.np.dl.playstation.net</code>), and compares against the local patch cache. <code>--list</code> shows the diff; <code>--download</code> fetches what&rsquo;s missing. <code>--max-version</code> respects per-title ceilings (e.g., 1.05 for GT6).</p>
<p>The first scan of my library found 51 of 72 games with updates available, 69 patches missing locally, ~24 GB in total. GT6 alone had 16 missing patches from 1.07 to 1.22, which I skipped for the reasons above.</p>
<p>Patches arrive as PSN packages type 0x0001 without encryption, so <code>extract_ps3_dlc.py</code> handles the install. Standard call:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">distrobox enter gaming -- python3 scripts/extract_ps3_dlc.py <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;/mnt/terachad/Emulators/EmuDeck/roms_heavy/ps3-DLC&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dest <span class="s2">&#34;/mnt/data/distrobox/gaming/.config/rpcs3/dev_hdd0/game&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tracking PS3 DLC versions manually, game by game through RPCS3, is impractical. It becomes an infinite to-do list, and you only find out there was a 1.22 patch when the game crashes.</p>
<h2>PS1: DuckStation per-game<span class="hx:absolute hx:-mt-20" id="ps1-duckstation-per-game"></span>
    <a href="#ps1-duckstation-per-game" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The Gran Turismo trilogy starts on PS1, and quality there only goes up if you enable the right things:</p>
<ul>
<li><strong>Gran Turismo</strong> (SCUS-94949): DuckStation&rsquo;s built-in WidescreenHack (no cheat exists for this game), PGXP enabled</li>
<li><strong>Gran Turismo 2 Simulation</strong> (SCUS-94455): widescreen cheat + 8 MB RAM cheat (fixes audio issues on crowded tracks), JINC2 filter</li>
<li><strong>Gran Turismo 2 Arcade</strong> (SCUS-94488): same treatment as Simulation</li>
</ul>
<p>Global defaults: Vulkan, 8x upscale, JINC2 as the default filter.</p>
<p>GT2 cheats come from a widescreen-fixes repo and are linked automatically into DuckStation&rsquo;s cheats folder.</p>
<h2>Switch: Eden with DLC, update and cheats<span class="hx:absolute hx:-mt-20" id="switch-eden-with-dlc-update-and-cheats"></span>
    <a href="#switch-eden-with-dlc-update-and-cheats" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>On Switch, <code>eden-bin</code> is the emulator. The integration automates three things I used to do by hand:</p>
<ol>
<li>
<p><strong>DLC and update install</strong>: the <code>install_dlcs</code> role points to the dumps directory on the NAS, identifies <code>.nsp</code>/<code>.nsz</code>/<code>.xci</code>/<code>.xcz</code> files, and installs everything into <code>~/.local/share/eden/nand/user/Contents/registered/</code>. If the dump is messy (patches and DLC mixed in a flat folder), the <code>reorganize_switch_nsps.py</code> script sorts everything by Title ID first.</p>
</li>
<li>
<p><strong>Atmosphere cheats</strong>: Atmosphere-format cheats get symlinked into Eden&rsquo;s load path (<code>~/.local/share/eden/load/&lt;TITLE_ID&gt;/cheats/</code>). The <code>switch_cheats</code> role handles this for every title covered by the NAS pack.</p>
</li>
<li>
<p><strong>Update checker</strong>: <code>check_switch_updates.py</code> lists titles with updates available against what&rsquo;s installed locally. Useful so I don&rsquo;t miss important patches for games still getting updates.</p>
</li>
</ol>
<p>A detail that burned me: <code>QT_STYLE_OVERRIDE</code> needs to be unset before launching <code>eden-bin</code>, otherwise it conflicts with Kvantum and breaks the UI. The wrapper does the <code>unset</code> before <code>exec</code>.</p>
<h2>Xbox 360: Xenia Manager via Wine<span class="hx:absolute hx:-mt-20" id="xbox-360-xenia-manager-via-wine"></span>
    <a href="#xbox-360-xenia-manager-via-wine" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Xbox 360 is still rough territory on Linux. There&rsquo;s no decent native emulator. The best route today is <a href="https://xenia.jp/"target="_blank" rel="noopener">Xenia</a> running through Wine, managed by <a href="https://github.com/xenia-manager/xenia-manager"target="_blank" rel="noopener">Xenia Manager</a>.</p>
<p>This is opt-in in my setup. The <code>install-xenia.yml</code> playbook creates a dedicated Wine prefix, downloads Xenia Manager, and registers launchers. It&rsquo;s not part of the main <code>site.yml</code> because not everyone wants Wine in the mix.</p>
<h3>Project Forza Plus (Forza Motorsport 2/3/4)<span class="hx:absolute hx:-mt-20" id="project-forza-plus-forza-motorsport-234"></span>
    <a href="#project-forza-plus-forza-motorsport-234" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Old Forza is still the king of previous-gen simcade. Running FM2/3/4 on Xenia requires the Project Forza Plus community mods: compatibility patches, performance mods, specific title-update installs. I documented the process in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/project-forza.md"target="_blank" rel="noopener"><code>docs/project-forza.md</code></a>.</p>
<h3>Project Gotham Racing 3 and 4<span class="hx:absolute hx:-mt-20" id="project-gotham-racing-3-and-4"></span>
    <a href="#project-gotham-racing-3-and-4" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>PGR3 runs reasonably well on Xenia Canary. PGR4 needs a specific tweak: <code>render_target_path_vulkan = &quot;fsi&quot;</code> in the config, otherwise some races break with visual artifacts. Setup documented in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/xbox360-pgr.md"target="_blank" rel="noopener"><code>docs/xbox360-pgr.md</code></a>. PGR4 audio on NVIDIA still has an XMA decoding issue that produces intermittent garbage. There&rsquo;s an open xenia-canary issue, no resolution so far.</p>
<h3>Batch Title Updates<span class="hx:absolute hx:-mt-20" id="batch-title-updates"></span>
    <a href="#batch-title-updates" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Title Updates for Xbox 360 games were distributed via Xbox Live, which has been shut down. The alternative is archive.org, which has the complete catalog preserved. I wrote <code>scripts/download-xbox360-tus.py</code> to automate:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">distrobox enter gaming -- python3 scripts/download-xbox360-tus.py <span class="se">\
</span></span></span><span class="line"><span class="cl">  --src /mnt/terachad/Emulators/EmuDeck/roms_heavy/xbox360 <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dest /mnt/terachad/Emulators/EmuDeck/roms_heavy/xbox360-updates <span class="se">\
</span></span></span><span class="line"><span class="cl">  --dry-run</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The script needs the <code>internetarchive</code> CLI authenticated (<code>ia configure</code> once, stores the token). It scans the games folder, matches against the archive.org manifest (cached locally), prioritizes by region (USA &gt; World &gt; Europe &gt; Japan), and downloads via <code>ia download --checksum</code> with automatic retry. The resulting .zip files go to Xenia Manager via <code>Manage → Install Content</code>, which extracts into the correct directory (<code>000B0000/</code>).</p>
<p>Full setup in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/xbox360-title-updates.md"target="_blank" rel="noopener"><code>docs/xbox360-title-updates.md</code></a>.</p>
<h2>Steam in the distrobox<span class="hx:absolute hx:-mt-20" id="steam-in-the-distrobox"></span>
    <a href="#steam-in-the-distrobox" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Since Proton became a first-class citizen on Linux, it makes sense to have Steam in the gaming box too. I added the <code>steam</code> package via Ansible (which pulls in multilib and 32-bit deps), mounted <code>/mnt/data/steam</code> read-write so the library persists outside the container, and created a host-side launcher.</p>
<p>In practice, this lets me run Steam games via Proton side-by-side with emulation, without switching environments. The mini-PC with the RTX 4090 is still the main Steam machine in the sim racing cockpit. The desktop is fallback and a test environment.</p>
<h2>Shell and controllers<span class="hx:absolute hx:-mt-20" id="shell-and-controllers"></span>
    <a href="#shell-and-controllers" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Minimal in-box shell<span class="hx:absolute hx:-mt-20" id="minimal-in-box-shell"></span>
    <a href="#minimal-in-box-shell" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>When I <code>distrobox enter gaming</code>, I want a decent prompt even for quick troubleshooting. I installed <code>zsh</code> + <code>starship</code> with a minimal config, zero exotic plugins. It&rsquo;s not my full dev setup, it&rsquo;s just &ldquo;a prompt that shows path and git branch without embarrassing me&rdquo;.</p>
<h3>Dolphin and the modern-controller hell<span class="hx:absolute hx:-mt-20" id="dolphin-and-the-modern-controller-hell"></span>
    <a href="#dolphin-and-the-modern-controller-hell" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><code>Dolphin</code> has always been the king of manual friction. If you use a modern controller, like my 8BitDo Ultimate 2, you have to remember how I like the GameCube mapping, how I adapt the Wii Remote, when to use a Nunchuk profile, when to switch to Classic Controller. I don&rsquo;t have the patience to rebuild that by hand every time. Profiles are pre-built in <code>config/emulator-overrides/dolphin/Profiles/</code> and copied by the <code>seed_configs</code> role. Details in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/controller-hotkeys.md"target="_blank" rel="noopener"><code>docs/controller-hotkeys.md</code></a>.</p>
<h2>A few stumbles that stuck around<span class="hx:absolute hx:-mt-20" id="a-few-stumbles-that-stuck-around"></span>
    <a href="#a-few-stumbles-that-stuck-around" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Not everything became magic. Emulation always has banana peels.</p>
<p><code>Flycast</code>, for example, has an irritating trap. The <code>emu.cfg</code> file uses <code>rend.*</code> keys inside the <code>[config]</code> section. If you create a separate <code>[rend]</code> section, it looks right, but the emulator just ignores it and later rewrites everything with mediocre defaults. The fix became a dedicated wrapper at <code>$DG_BOX_HOME/bin/flycast-hires</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">$DG_BOX_HOME</span>/bin/flycast-hires <span class="se">\
</span></span></span><span class="line"><span class="cl">  -config config:pvr.rend<span class="o">=</span><span class="m">4</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -config config:rend.Resolution<span class="o">=</span><span class="m">2880</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -config config:rend.EmulateFramebuffer<span class="o">=</span>no <span class="se">\
</span></span></span><span class="line"><span class="cl">  -config config:rend.WideScreen<span class="o">=</span>yes</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Details in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/flycast-resolution.md"target="_blank" rel="noopener"><code>docs/flycast-resolution.md</code></a>.</p>
<p><code>shadPS4</code> is still far from a closed case. The current setup is focused on <code>Driveclub</code>, with a mirrored config, XML patch and sys_modules links from firmware 11.00. And that wasn&rsquo;t random: I really like <code>Driveclub</code>. It&rsquo;s practically the only game still stuck on PS4 that I really wanted to get running properly. I&rsquo;ve seen several YouTube videos showing the game working, but so far I haven&rsquo;t been able to get it running satisfactorily on Linux myself. Documented in <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/driveclub-shadps4.md"target="_blank" rel="noopener"><code>docs/driveclub-shadps4.md</code></a>. The good part is that I no longer depend on muscle memory to remember how to launch it, with which patch, in which folder, with which modules. The bad part is that PS4 emulation is still PS4 emulation. No script makes upstream mature faster. If anyone knows how to close this setup on Linux, check the project on GitHub and send a PR.</p>
<p>Ridge Racer V has car-texture flickering documented in PCSX2 issues (#3639, #13729). The Hardware renderer is acceptable for a racing game where you&rsquo;re not standing still admiring the model. Software renderer fixes it but kills performance. I chose to live with the flicker.</p>
<h2>The real gain isn&rsquo;t just automation<span class="hx:absolute hx:-mt-20" id="the-real-gain-isnt-just-automation"></span>
    <a href="#the-real-gain-isnt-just-automation" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>It&rsquo;d be easy to sum this up as &ldquo;look how cool, I used AI to automate shell scripts&rdquo;. That&rsquo;s not it.</p>
<p>The real gain is more mundane and more important: I stopped spending mental energy on manual work. I didn&rsquo;t have to remember rare commands. I didn&rsquo;t have to keep dozens of tabs of wiki, issue, forum, README. I didn&rsquo;t have to leave half a dozen terminals scattered tailing logs while I try to remember which hidden GUI option the emulator insists on overwriting on the first boot. I started working in conversation.</p>
<p>I tell the agent the goal. It looks up the files, reads the logs, finds the right config format, compares defaults, proposes fixes, writes scripts, validates paths, checks UID/GID, verifies broken symlinks, generates wrappers, exports launchers. I&rsquo;m still responsible for decisions, of course. But I stopped being a typist of rare commands.</p>
<p>That&rsquo;s the point that interests me most about using coding agents on Linux. They dramatically reduce the entry friction. A lot of people bounce off the Linux desktop not because the system is incapable, but because the tuning curve has historically been too irritating. Having an assistant capable of reading docs, cross-referencing configs, proposing automation and executing under supervision changes that game.</p>
<h2>If you want to reproduce<span class="hx:absolute hx:-mt-20" id="if-you-want-to-reproduce"></span>
    <a href="#if-you-want-to-reproduce" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The repo was published for this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/akitaonrails/distrobox-gaming.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> distrobox-gaming/ansible
</span></span><span class="line"><span class="cl">ansible-galaxy collection install -r collections/requirements.yml
</span></span><span class="line"><span class="cl">cp host_vars/localhost.yml.example host_vars/localhost.yml
</span></span><span class="line"><span class="cl"><span class="nv">$EDITOR</span> host_vars/localhost.yml  <span class="c1"># adjust paths for your machine</span>
</span></span><span class="line"><span class="cl">ansible-playbook site.yml</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Xenia is opt-in:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ansible-playbook install-xenia.yml</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Read the <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/README.md"target="_blank" rel="noopener"><code>README.md</code></a> and <a href="https://github.com/akitaonrails/distrobox-gaming/blob/master/docs/rebuild-runbook.md"target="_blank" rel="noopener"><code>docs/rebuild-runbook.md</code></a> first. I don&rsquo;t distribute ROMs, BIOS, firmware or keys. The repo only detects, links and configures what you already have on your own machine.</p>
<h2>If you prefer the Claude Code route<span class="hx:absolute hx:-mt-20" id="if-you-prefer-the-claude-code-route"></span>
    <a href="#if-you-prefer-the-claude-code-route" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>My recommendation is simple:</p>
<ol>
<li>Start with the goal, not the command. &ldquo;I want an Arch distrobox with NVIDIA GPU, separate home, ROMs read-only and Steam rw&rdquo; beats dumping a half shell command without context.</li>
<li>Always ask it to document what it&rsquo;s doing. If it works, promote the result to a script or role. If you don&rsquo;t document, you just manufactured a throwaway hack.</li>
<li>Work in phases. Box creation. Bootstrap. Emulator config. Launcher export. Verification. That&rsquo;s exactly how I broke down the problem.</li>
<li>Ask for objective checks. <code>command -v</code>, file existence, broken symlinks, UID/GID, ROM paths, audio, GPU. The best automation is the one that fails early.</li>
<li>Don&rsquo;t use the agent as a command parrot. Use it as a technical assistant. You still review the decisions and tell it to adjust course.</li>
</ol>
<p>For me, that was the gain. I went from zero to a much more complete emulation machine without having to customize everything by hand, GUI by GUI, one window at a time. And this time, I finished with energy left to do what I wanted from the start.</p>
<p>Play.</p>
]]></content:encoded><category>emulation</category><category>containers</category><category>automation</category></item><item><title>Computing History and Retro Dev on YouTube</title><link>https://akitaonrails.github.io/en/2025/05/19/computing-history-and-retro-dev-on-youtube/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2025/05/19/computing-history-and-retro-dev-on-youtube/</guid><pubDate>Mon, 19 May 2025 15:19:00 -0300</pubDate><description>&lt;p&gt;I watch a LOT of YouTube, and I&amp;rsquo;m always sharing cool stuff I watch on my X and Instagram (follow me there). There are several videos I keep coming back to.&lt;/p&gt;
&lt;p&gt;I finally decided to compile a list with the channels I watch the most, along with a list of the main playlists and example videos from each channel that I recommend watching.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll probably keep coming back to this page to update it from time to time. If anyone remembers one I forgot, send it in the comments. There are channels like GameSack, Sega Lord X, Retro Game Corp, Wullfden, Macho Nacho Productions, and several others I didn&amp;rsquo;t list, because they lean more toward &amp;ldquo;reviews&amp;rdquo; and don&amp;rsquo;t overlap as much with &amp;ldquo;dev&amp;rdquo;. I tried to focus on things I think a &amp;ldquo;dev&amp;rdquo; should know.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I watch a LOT of YouTube, and I&rsquo;m always sharing cool stuff I watch on my X and Instagram (follow me there). There are several videos I keep coming back to.</p>
<p>I finally decided to compile a list with the channels I watch the most, along with a list of the main playlists and example videos from each channel that I recommend watching.</p>
<p>I&rsquo;ll probably keep coming back to this page to update it from time to time. If anyone remembers one I forgot, send it in the comments. There are channels like GameSack, Sega Lord X, Retro Game Corp, Wullfden, Macho Nacho Productions, and several others I didn&rsquo;t list, because they lean more toward &ldquo;reviews&rdquo; and don&rsquo;t overlap as much with &ldquo;dev&rdquo;. I tried to focus on things I think a &ldquo;dev&rdquo; should know.</p>
<h2>List (2025-05-19)<span class="hx:absolute hx:-mt-20" id="list-2025-05-19"></span>
    <a href="#list-2025-05-19" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li><a href="https://www.youtube.com/@UsagiElectric"target="_blank" rel="noopener">Usagi Electric</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=-RGp-bsYf34&amp;list=PLnw98JPyObn3QSIwUz5Iv6NA1x-uGFqAa&amp;pp=0gcJCV8EOCosWNin"target="_blank" rel="noopener">Bendix G15 Vacuum Tube</a></li>
<li><a href="https://www.youtube.com/watch?v=EbWcgepRmY4&amp;list=PLnw98JPyObn2PqgY1mfzXOtnUXlsXSMch"target="_blank" rel="noopener">Data General Nova</a></li>
<li><a href="https://www.youtube.com/watch?v=SB95dlrS5kU&amp;list=PLnw98JPyObn2Kj-rqrKqEB5xmS_uOV40f"target="_blank" rel="noopener">DEC/PDP-11</a></li>
<li><a href="https://www.youtube.com/watch?v=oPA8dHtf_6M&amp;list=PLnw98JPyObn1GUapiXLlGm8RrpQF-J_c1"target="_blank" rel="noopener">1-Bit Computer</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@TechTangents"target="_blank" rel="noopener">Tech Tangents</a>:
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=mBssIIRGkOw&amp;list=PLJVwF78cppBgwAHYKMffuhs0jblPHCyvL"target="_blank" rel="noopener">Data Generals</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=apFLq3-fpL4"target="_blank" rel="noopener">Tandy 1000 RL HD</a></li>
<li><a href="https://www.youtube.com/watch?v=Oa6QPHD6qzw&amp;t=278s"target="_blank" rel="noopener">CP/M on Model 100</a></li>
<li><a href="https://www.youtube.com/watch?v=dqPSVG7zbs4"target="_blank" rel="noopener">KayPro IV</a></li>
<li><a href="https://www.youtube.com/watch?v=vCU4xbHFb58"target="_blank" rel="noopener">HP Series 200 - CAD Monster</a></li>
<li><a href="https://www.youtube.com/watch?v=5WB9d1JM8oo"target="_blank" rel="noopener">8088 PC Networking</a></li>
<li><a href="https://www.youtube.com/watch?v=EOVLlMQs9f8"target="_blank" rel="noopener">FreeDOS on IBM 5150</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@The8BitGuy"target="_blank" rel="noopener">The 8-Bit Guy</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=ayh0qebfD2g&amp;list=PLfABUWdDse7bKGFshxR0itdHBhjUj86SX"target="_blank" rel="noopener">Commander X16</a></li>
<li><a href="https://www.youtube.com/watch?v=NB_VBl7ut9Y&amp;list=PLfABUWdDse7Z8wmD0bbL0P5upEE1VjMJM"target="_blank" rel="noopener">Game Development - Making of Planet X2</a></li>
<li><a href="https://www.youtube.com/watch?v=mXSdikcUuo0&amp;list=PLfABUWdDse7aKLE00YObaVvP1jSH0YqIE"target="_blank" rel="noopener">Texas Tech History</a></li>
<li><a href="https://www.youtube.com/watch?v=eP9y_7it3ZM&amp;list=PLfABUWdDse7Y6LLPlfsHKcvBCgqaudzVY"target="_blank" rel="noopener">Commodore History</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@adriansdigitalbasement"target="_blank" rel="noopener">Adrien&rsquo;s Digital Basement</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=x8DfSg7oMWg&amp;list=PL4tFqFVNf5LSckPX988MBvVzKv96eP0es"target="_blank" rel="noopener">TRS-80 Model II Repair Series</a></li>
<li><a href="https://www.youtube.com/watch?v=gkCDdSdSNu0&amp;list=PL4tFqFVNf5LRvKP94x7VOfdXXQHRt_8BR"target="_blank" rel="noopener">Amiga 4000 Repair Series</a></li>
<li><a href="https://www.youtube.com/watch?v=RMG_StQ67kA&amp;list=PL4tFqFVNf5LQeof2oTPFGxZx0SpvICpSv"target="_blank" rel="noopener">ZX Spectrum</a></li>
<li><a href="https://www.youtube.com/watch?v=_CNruhSYC2E&amp;list=PL4tFqFVNf5LRf-gcl5Lyhl2UxbmrGmZCT"target="_blank" rel="noopener">IBM 16-bit machines</a></li>
<li><a href="https://www.youtube.com/watch?v=5qMP6A_U75E&amp;list=PL4tFqFVNf5LT6X3sMRfbRWuTVE5NuEmKj&amp;pp=0gcJCV8EOCosWNin"target="_blank" rel="noopener">Apple Computers - 8-bit</a></li>
<li><a href="https://www.youtube.com/watch?v=H9dxJ7uNXOA&amp;list=PL4tFqFVNf5LRxSdlzuH2MWOZ_5PltbpLv"target="_blank" rel="noopener">Macintosh Machines - 68000</a></li>
<li><a href="https://www.youtube.com/watch?v=0MP93YbINyI&amp;list=PL4tFqFVNf5LRZKtduWSpZjZpJS6mxZx7I"target="_blank" rel="noopener">Commodore - 8 bit</a></li>
<li><a href="https://www.youtube.com/watch?v=0MP93YbINyI&amp;list=PL4tFqFVNf5LR9SvTg1P8r3EPL657qJFsx"target="_blank" rel="noopener">Commodore 64 Repairs</a></li>
<li><a href="https://www.youtube.com/watch?v=QJRXMYmie7I&amp;list=PL4tFqFVNf5LTPlF7iF_pMl5WiiOsSFMf4"target="_blank" rel="noopener">Tandy 1000 Series</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@clabretro"target="_blank" rel="noopener">clabretro</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=77rVvJBLRhQ&amp;list=PLjeznAUfx0Odrk8qOQJ-_a3xry4PhIxOh"target="_blank" rel="noopener">Home Lab</a></li>
<li><a href="https://www.youtube.com/watch?v=pXuqASXGvgM&amp;list=PLjeznAUfx0OfCseO3gKOvEjw3oHgHLRlK"target="_blank" rel="noopener">Sun Microsystem Stuff</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=PBvDRJ_rU5w"target="_blank" rel="noopener">Ressurrecting an IBM PS/2 Model 50Z</a></li>
<li><a href="https://www.youtube.com/watch?v=7-ISDVFIVgw"target="_blank" rel="noopener">Windows NT 4.0 on a Compaq Proliant Gen 1</a></li>
<li><a href="https://www.youtube.com/watch?v=4UWibKEjyY8&amp;t=10s"target="_blank" rel="noopener">Token Ring Networking</a></li>
<li><a href="https://www.youtube.com/watch?v=LvLPz72IvAM"target="_blank" rel="noopener">Early 2000s Linksys Wi-Fi Gear</a></li>
<li><a href="https://www.youtube.com/watch?v=gyyhqHBmKg4"target="_blank" rel="noopener">Compaq Proliant Gen 1</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@LowSpecGamer"target="_blank" rel="noopener">LowSpecGamer</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=lP2ZBp9O0mk&amp;list=PLp-6MMWURCz3x92-dpEsRLEGL3uHToVDR"target="_blank" rel="noopener">All Lore - Chronological</a></li>
<li><a href="https://www.youtube.com/watch?v=lP2ZBp9O0mk&amp;list=PLp-6MMWURCz1bo3T4vmyNUVl3FefnF7CC"target="_blank" rel="noopener">Chip Wars</a><a href="https://www.youtube.com/watch?v=OBlu2mdpiJ0&amp;list=PLp-6MMWURCz0OdcgoSiQOv8C9hn79XdOt"target="_blank" rel="noopener">Nintendo Lore</a></li>
<li><a href="https://www.youtube.com/watch?v=9Ki-kH751_8&amp;list=PLp-6MMWURCz0OB_iioCUqc2Sq21UXE-08"target="_blank" rel="noopener">The Yokoi Saga</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=nIwdhPOVOUk"target="_blank" rel="noopener">How Amateurs created the world&rsquo;s most popular processor - History of ARM Part 1</a></li>
<li><a href="https://www.youtube.com/watch?v=vx1-6yRAGk0"target="_blank" rel="noopener">ARM&rsquo;s Secret Weapon - History of ARM Part 2</a></li>
<li><a href="https://www.youtube.com/watch?v=PLzTVnQ3gqc"target="_blank" rel="noopener">How IBM Invented the PC, then everyone stole it</a></li>
<li><a href="https://www.youtube.com/watch?v=ztYfJDTjpjk&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">The Great Calculator Wars of the 70s</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@BenEater"target="_blank" rel="noopener">Ben Eater</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=LnzuMJLZRdU&amp;list=PLowKtXNTBypFbtuVMUVXNR0z1mu7dp7eH"target="_blank" rel="noopener">Build a 65c02-based computer from scratch</a></li>
<li><a href="https://www.youtube.com/watch?v=HyznrdDSSGM&amp;list=PLowKtXNTBypGqImE405J2565dvjafglHU&amp;pp=0gcJCV8EOCosWNin"target="_blank" rel="noopener">Building an 8-bit breadboard computer</a></li>
<li><a href="https://www.youtube.com/watch?v=XaGXPObx2Gs&amp;list=PLowKtXNTBypH19whXTVoG3oKSuOcw_XeW"target="_blank" rel="noopener">Networking Tutorial</a></li>
<li><a href="https://www.youtube.com/watch?v=eq5YpKHXJDM&amp;list=PLowKtXNTBypFWff2QjXCWuSfJDWcvE0Vm"target="_blank" rel="noopener">Error detection</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@javidx9"target="_blank" rel="noopener">javidx9</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=nViZg02IMQo&amp;list=PLrOv9FMX8xJHqMvSGB_9G9nZZ_4IgteYf"target="_blank" rel="noopener">NES Emulator from Scratch</a></li>
<li><a href="https://www.youtube.com/watch?v=8OK8_tHeCIA&amp;list=PLrOv9FMX8xJE8NgepZR1etrsU63fDDGxO"target="_blank" rel="noopener">Code-it-Yourself - TETRIS from scratch</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@GamingHistorian"target="_blank" rel="noopener">Gaming Historian</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=G_6PO3N8fig&amp;list=PLScCWE7AP_PUIOOSwRF_e5jR6e2rLwLoF"target="_blank" rel="noopener">Video Game Hardware</a></li>
<li><a href="https://www.youtube.com/watch?v=2EUYSN5aFcE&amp;list=PLScCWE7AP_PVMZgv83NLwp6wQ93I6o4rr"target="_blank" rel="noopener">The Story of Super Mario - Documentary Series</a></li>
<li><a href="https://www.youtube.com/watch?v=S1qD9JRk5vM&amp;list=PLScCWE7AP_PU4_4b4juK8ZugrmptW4g1v&amp;pp=0gcJCV8EOCosWNin"target="_blank" rel="noopener">History of Wolfenstein</a></li>
<li><a href="https://www.youtube.com/watch?v=M55Bpm38qqE&amp;list=PLScCWE7AP_PVO9xcAsZa7Dv5sgdZMyUbb"target="_blank" rel="noopener">History of Sonic the Hedgehog</a></li>
<li><a href="https://www.youtube.com/watch?v=myhBphil-Uw&amp;list=PLScCWE7AP_PVVYlegzt3REr2n5bBmmQJ2"target="_blank" rel="noopener">History of Super Smash Bros</a></li>
<li><a href="https://www.youtube.com/watch?v=BjRuV52Jk78&amp;list=PLScCWE7AP_PXfCIUjxMjoWPUbNYXz8P6w"target="_blank" rel="noopener">Games</a></li>
<li><a href="https://www.youtube.com/watch?v=8QbjlHeoLdc&amp;list=PL397C52406E483BAB&amp;pp=0gcJCV8EOCosWNin"target="_blank" rel="noopener">Gaming Historian</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@ModernVintageGamer"target="_blank" rel="noopener">Modern Vintage Gamer</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=SZbC0Hh4lc8&amp;list=PLUbw0IXAmW2rMk5g19MrqcyebecSrrSeJ"target="_blank" rel="noopener">Mistakes Were Made - Game Console Security</a></li>
<li><a href="https://www.youtube.com/watch?v=siOXFGZj_z0&amp;list=PLUbw0IXAmW2p35mZ_Fis31n2L6HPiFoEs"target="_blank" rel="noopener">The Sony Playstation</a></li>
<li><a href="https://www.youtube.com/watch?v=esl6ctg_9JA&amp;list=PLUbw0IXAmW2qJ8e0XtQgQo8ki0mIeIbHP"target="_blank" rel="noopener">The Original XBox</a></li>
<li><a href="https://www.youtube.com/watch?v=F4Dv32-PoU4&amp;list=PLUbw0IXAmW2pDWN4kZ-vNDNAPycFVR_ux"target="_blank" rel="noopener">Doom Switch Port</a></li>
<li><a href="https://www.youtube.com/watch?v=ZOzWS7MQ_qs&amp;list=PLUbw0IXAmW2ofK7Es3w7q5kVMdzK_IfKY"target="_blank" rel="noopener">Retro Reviews</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=lpOEhtoc3DY"target="_blank" rel="noopener">N64: Decomp vs Recomp vs Ports explained</a></li>
<li><a href="https://www.youtube.com/watch?v=hqpw-QPsdCg"target="_blank" rel="noopener">XenonRecomp</a></li>
<li><a href="https://www.youtube.com/watch?v=nJ9zJ_PDatU"target="_blank" rel="noopener">Game Genie Explained</a></li>
<li><a href="https://www.youtube.com/watch?v=_RVQsqC3A6g"target="_blank" rel="noopener">Super NES Graphics</a></li>
<li><a href="https://www.youtube.com/watch?v=OmKjVpVdHDQ"target="_blank" rel="noopener">N64 Emulation is a Mess</a></li>
<li><a href="https://www.youtube.com/watch?v=8ibEDSfthS0"target="_blank" rel="noopener">The First Amiga Virus</a></li>
<li><a href="https://www.youtube.com/watch?v=ZeaZWBrG__s"target="_blank" rel="noopener">35 Years of Game Boy</a></li>
<li><a href="https://www.youtube.com/watch?v=ockNRSt3Nsk"target="_blank" rel="noopener">Video game piracy in the 80s</a></li>
<li><a href="https://www.youtube.com/watch?v=JqP3ZzWiul0"target="_blank" rel="noopener">Doom SNES Sourcecode</a></li>
<li><a href="https://www.youtube.com/watch?v=zQE1K074v3s"target="_blank" rel="noopener">How Graphics worked on the Game Boy</a></li>
<li><a href="https://www.youtube.com/watch?v=2NF5sU_n0uk"target="_blank" rel="noopener">How UltraHLE changed N64 emulation forever</a></li>
<li><a href="https://www.youtube.com/watch?v=5tADL_fmsHQ"target="_blank" rel="noopener">How Diablo was completely Reverse Engineered without source code</a></li>
<li><a href="https://www.youtube.com/watch?v=Ap3gJT_TMOc&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">Duke3D on the Amiga 600</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@strafefox"target="_blank" rel="noopener">Strafefox</a>
<ul>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=6u37r-Z_Nfo"target="_blank" rel="noopener">Video Game Development Through the 80s</a></li>
<li><a href="https://www.youtube.com/watch?v=5jxYvLYcpZg&amp;t=1441s"target="_blank" rel="noopener">The 3D Revolution in 90s game development</a></li>
<li><a href="https://www.youtube.com/watch?v=69AOVtOUatY"target="_blank" rel="noopener">The Making of Sonic the Hedgehog</a></li>
<li><a href="https://www.youtube.com/watch?v=226Nwq2CN5c"target="_blank" rel="noopener">The Making of Mortal Kombat</a></li>
<li><a href="https://www.youtube.com/watch?v=TYE9GMfr5ug"target="_blank" rel="noopener">The Making of Castlevania Symphony of the Night</a></li>
<li><a href="https://www.youtube.com/watch?v=_LPu7gNzWUE"target="_blank" rel="noopener">The Making of Donkey Kong Country &amp; Killer Instinct</a></li>
<li><a href="https://www.youtube.com/watch?v=kOnUITJqRQQ"target="_blank" rel="noopener">The Making of Aladdin</a></li>
<li><a href="https://www.youtube.com/watch?v=5Z7liHTgnw4"target="_blank" rel="noopener">The Making of Streets of Rage</a></li>
<li><a href="https://www.youtube.com/watch?v=USC6c9Dtak8"target="_blank" rel="noopener">The Making of Street Fighter II</a></li>
<li><a href="https://www.youtube.com/watch?v=KL4l0nMsrMM"target="_blank" rel="noopener">The Making of Super Mario World</a></li>
<li><a href="https://www.youtube.com/watch?v=GDhNT2Qv-Mo"target="_blank" rel="noopener">The Making of Star Fox</a></li>
<li><a href="https://www.youtube.com/watch?v=MJG-OT6S194"target="_blank" rel="noopener">How Video Games were Made - Part 3</a></li>
<li><a href="https://www.youtube.com/watch?v=Yo7UkkGC1AY"target="_blank" rel="noopener">How Video Games were Made - Part 2</a></li>
<li><a href="https://www.youtube.com/watch?v=VDz1IvuWyJw"target="_blank" rel="noopener">MSX Konami Games</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@GTV-Japan"target="_blank" rel="noopener">GTV Japan</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=_s9fmbcds40&amp;list=PLu4Z50DKq3na663p7gGKgxHOZFGv2eqWO"target="_blank" rel="noopener">The GTV Binge Watch</a></li>
<li><a href="https://www.youtube.com/watch?v=zgtYV41yXbU&amp;list=PLu4Z50DKq3nYnRu-3XhDncP8MRzOsusRm"target="_blank" rel="noopener">GTV&rsquo;s Greatest Hits</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=KdS34tc37hk"target="_blank" rel="noopener">The Story of Chrono Trigger</a></li>
<li><a href="https://www.youtube.com/watch?v=gNps2YP2e30&amp;t=860s"target="_blank" rel="noopener">The Final 16-bit Fantasy</a></li>
<li><a href="https://www.youtube.com/watch?v=DV3K3Coz1RA"target="_blank" rel="noopener">Sonic Mania Day</a></li>
<li><a href="https://www.youtube.com/watch?v=I2MYvgNf9lU"target="_blank" rel="noopener">The History of Sonic - 30th Anniversary</a></li>
<li><a href="https://www.youtube.com/watch?v=zgtYV41yXbU"target="_blank" rel="noopener">The Super Story of Mario World</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@DisplacedGamers"target="_blank" rel="noopener">Displaced Gamers</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=55X1KOw_Rhg&amp;list=PLn5sTGew1WGB0-omvT5UtPanoCzRh7A0V"target="_blank" rel="noopener">Game Genie: Uncorked</a></li>
<li><a href="https://www.youtube.com/watch?v=EhO_iHkUkE0&amp;list=PLn5sTGew1WGD5NUstpfwYdLfrmLJOHCSw"target="_blank" rel="noopener">Talkin&rsquo; Code</a></li>
<li><a href="https://www.youtube.com/watch?v=gTfPLl_AJvs&amp;list=PLn5sTGew1WGD2Tnspn9vhpLGf-kqDRbYy"target="_blank" rel="noopener">Behind the Code</a></li>
<li><a href="https://www.youtube.com/watch?v=ssluTgfkdlg&amp;list=PLn5sTGew1WGCMO7Bi-MD7VgFWAoeXbU93"target="_blank" rel="noopener">Gaming Tech Talk</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@RGMechEx"target="_blank" rel="noopener">Retro Game Mechanics Explained</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=UdD26eFVzHQ&amp;list=PLHQ0utQyFw5IrM_zCFY8fhtByNxVGRUMr"target="_blank" rel="noopener">Super Mario 2</a></li>
<li><a href="https://www.youtube.com/watch?v=1whju31WAPA&amp;list=PLHQ0utQyFw5KxchzQnRcc_uk-NU7FbHNM"target="_blank" rel="noopener">Super Mario Bros.</a></li>
<li><a href="https://www.youtube.com/watch?v=q15yNrJHOak&amp;list=PLHQ0utQyFw5I8qqpOXKXKySBsphqdStdq"target="_blank" rel="noopener">Super Mario World</a></li>
<li><a href="https://www.youtube.com/watch?v=fxZuzos7Auk&amp;list=PLHQ0utQyFw5KoUSkHGq5VtMY0v2e5G_u3"target="_blank" rel="noopener">Super Mario Bros 3</a></li>
<li><a href="https://www.youtube.com/watch?v=FPzuYWbnln4&amp;list=PLHQ0utQyFw5IrotCLCfxKGkUL-WjPDrxh"target="_blank" rel="noopener">Super Mario Land 2</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@TheCherno"target="_blank" rel="noopener">The Cherno</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=gfW1Fhd9u9Q&amp;list=PLlrATfBNZ98edc5GshdBtREv5asFW3yXl"target="_blank" rel="noopener">Ray Tracing Series</a></li>
<li><a href="https://www.youtube.com/watch?v=W3gAzLwfIP0&amp;list=PLlrATfBNZ98foTJPJ_Ev03o2oq3-GGOS2"target="_blank" rel="noopener">OpenGL</a></li>
<li><a href="https://www.youtube.com/watch?v=9HCboZHTyrw&amp;list=PLlrATfBNZ98f6Z1cDNeMLL3eXaRk1WCxK"target="_blank" rel="noopener">Code Review</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@Kosmicd12"target="_blank" rel="noopener">Kosmic</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=1wR8x5b_ExM&amp;list=PLbxkd_Hs6FDK2FNWiOAK_LPBMXauJMrlC"target="_blank" rel="noopener">World Record SpeedRuns</a></li>
<li><a href="https://www.youtube.com/watch?v=kyZ4yF3ynsA&amp;list=PLbxkd_Hs6FDKH8YVR82mS2zFDWN_pwkQx"target="_blank" rel="noopener">Super Mario Bros</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@KazeN64"target="_blank" rel="noopener">Kaze Emanuar</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=dfK4Iuz3mME&amp;list=PLY9TGb7t9kuHx1vP2qCascFrhfnzLn-ME"target="_blank" rel="noopener">SM64: Last Impact - Developer Commentary</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=DdXLpoNLywg"target="_blank" rel="noopener">Mario 64 breaks after 7 days</a></li>
<li><a href="https://www.youtube.com/watch?v=OHU2onxKm5o&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">Perfecting Random Number Generation on N64</a></li>
<li><a href="https://www.youtube.com/watch?v=r-2iIBmXULI&amp;t=129s"target="_blank" rel="noopener">Planetary Gravity is Cheap</a></li>
<li><a href="https://www.youtube.com/watch?v=g5u3zW4SLow&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">The Genius of N64 Cache Instruction</a></li>
<li><a href="https://www.youtube.com/watch?v=DotEVFFv-tk"target="_blank" rel="noopener">The N64 has more RAM than you think</a></li>
<li><a href="https://www.youtube.com/watch?v=f05PwswO7qc&amp;t=344s"target="_blank" rel="noopener">Why Devs need to know about Render Matrices</a></li>
<li><a href="https://www.youtube.com/watch?v=-nhhfYkluT0"target="_blank" rel="noopener">Better engine, Better graphics</a></li>
<li><a href="https://www.youtube.com/watch?v=hffgNRfL1XY"target="_blank" rel="noopener">The Folded Polynomial</a></li>
<li><a href="https://www.youtube.com/watch?v=xFKFoGiGlXQ"target="_blank" rel="noopener">Fastest Sine on N64</a></li>
<li><a href="https://www.youtube.com/watch?v=t_rzYnXEQlE"target="_blank" rel="noopener">Fixing the Entire SM64 source code</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@james.lambert"target="_blank" rel="noopener">James Lambert</a>
<ul>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=WDNU9k8uTc4"target="_blank" rel="noopener">Can Portal64 be saved?</a></li>
<li><a href="https://www.youtube.com/watch?v=sb3nHlsBBpg&amp;t=256s"target="_blank" rel="noopener">Portal 64: First Slice, is out of beta</a></li>
<li><a href="https://www.youtube.com/watch?v=r4KGm7ixcqI"target="_blank" rel="noopener">How I optimized Portal to run on the N64</a></li>
<li><a href="https://www.youtube.com/watch?v=Sf036fO-ZUk"target="_blank" rel="noopener">How I implemented MegaTextures on real N64 hardware</a></li>
<li><a href="https://www.youtube.com/watch?v=ao1qVi5Qp3Y&amp;t=58s&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">I rewrote Portal from scratch and solved the Portal Paradox</a></li>
<li><a href="https://www.youtube.com/watch?v=VqDAxcWnq3g"target="_blank" rel="noopener">How are toon shading and dynamic shadows possible on the N64?</a></li>
<li><a href="https://www.youtube.com/watch?v=yXzoZ2AfWwg&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">How can Nintendo 64 run Portal?</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@veritasium"target="_blank" rel="noopener">Veritasium</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=AaZ_RSt0KP8&amp;list=PLkahZjV5wKe8w9GC_n2yyhx6Wj-NKzzeE"target="_blank" rel="noopener">Computing</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=w5ebcowAJD8&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">I build a QR code with my bare hands to see how it workds</a></li>
<li><a href="https://www.youtube.com/watch?v=wVyu7NB7W6Y"target="_blank" rel="noopener">Exposing the flaw in out phone system</a></li>
<li><a href="https://www.youtube.com/watch?v=A5w-dEgIU1M"target="_blank" rel="noopener">The Trillion Dollar equation</a></li>
<li><a href="https://www.youtube.com/watch?v=nmgFG7PUHfo&amp;t=40s"target="_blank" rel="noopener">The Most Important Algorithm of All Time</a></li>
<li><a href="https://www.youtube.com/watch?v=HeQX2HjkcNo"target="_blank" rel="noopener">Math&rsquo;s Fundamental Flaw</a></li>
<li><a href="https://www.youtube.com/watch?v=R13BD8qKeTg"target="_blank" rel="noopener">The Bayesian Trap</a></li>
<li><a href="https://www.youtube.com/watch?v=IcrBqCFLHIY&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">How Does a Transistor Work</a></li>
<li><a href="https://www.youtube.com/watch?v=zNzzGgr2mhk"target="_blank" rel="noopener">How to Make a Quantum Bit</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.youtube.com/@3blue1brown"target="_blank" rel="noopener">3Blue1Brown</a>
<ul>
<li>Playlists:
<ul>
<li><a href="https://www.youtube.com/watch?v=8rrHTtUzyZA&amp;list=PLZHQObOWTQDMp_VZelDYjka8tnXNpXhzJ"target="_blank" rel="noopener">Convolutions</a></li>
<li><a href="https://www.youtube.com/watch?v=8idr1WZ1A7Q&amp;list=PLZHQObOWTQDOjmo3Y6ADm0ScWAlEXf-fp"target="_blank" rel="noopener">Probabilities of Probabilities</a></li>
<li><a href="https://www.youtube.com/watch?v=p_di4Zn4wz4&amp;list=PLZHQObOWTQDNPOjrT6KVlfJuKtYTftqH6"target="_blank" rel="noopener">Differential equations</a></li>
<li><a href="https://www.youtube.com/watch?v=WUvTyaaNkzM&amp;list=PLZHQObOWTQDMsr9K-rj53DwVRMYO3t5Yr"target="_blank" rel="noopener">Essence of Calculus</a></li>
<li><a href="https://www.youtube.com/watch?v=fNk_zzaMoSs&amp;list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"target="_blank" rel="noopener">Essence of Linear Algebra</a></li>
<li><a href="https://www.youtube.com/watch?v=aircAruvnKk&amp;list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi"target="_blank" rel="noopener">Neural Networks</a></li>
</ul>
</li>
<li>Videos:
<ul>
<li><a href="https://www.youtube.com/watch?v=RQWpF2Gb-gU"target="_blank" rel="noopener">But what is a quantum computing - Grover&rsquo;s Algorithm</a></li>
<li><a href="https://www.youtube.com/watch?v=Dlsa9EBKDGI"target="_blank" rel="noopener">Where my explanation of Grover&rsquo;s algorithm failed</a></li>
<li><a href="https://www.youtube.com/watch?v=LPZh9BOjkQs&amp;pp=0gcJCY0JAYcqIYzv"target="_blank" rel="noopener">Large Language Models explained briefly</a></li>
</ul>
</li>
</ul>
</li>
</ul>
]]></content:encoded><category>computing-history</category><category>retrocomputing</category><category>emulation</category><category>off-topic</category></item></channel></rss>