P8Plus RPCS3 patch -> native hook mapping (thp8_mod_p8plus.dll)
===============================================================
Source of truth: the retired RPCS3 patch file dist/P8Plus_patch.yml (generated by tools/gen_patches.py
+ tools/gfx/gfx_patches.py + tools/hud/gen_hud_patch.py + tools/vram/gen_mainram_patch.py). The first two
were removed with the RPCS3 version: `git show be3c641:dist/P8Plus_patch.yml` / `be3c641:tools/gen_patches.py`. PPU hash
PPU-de89165d4428c218f9ade10050ea093f7bde9e8d. The table that drives registration is
s_hooks[] in src/p8plus_main.c. This file mirrors it; the per-hook comments are in
src/p8plus_*.c.

Conversion rules (thp8-native/work/p8plus/design.txt 1.5(3)): a lifted `bl` sets
ctx->lr = site+4, so "patch the nop after bl X at S" == WRAP X with entry_lr == S+4
(post-step); "bl X -> bl cave at T" == WRAP X with entry_lr == T+4 (pre and/or post);
`li r3,0; blr` at an entry == REPLACE. Cave words (0x0042CEBC.. in dead sub_42CEBC)
are dropped; their logic is C in the plugin. The LR filter is passed to
hook_register, so the dispatcher skips the hook on every other call; the hooks
check entry_lr again as a safeguard.

Checked for every row:
  - call site, argument registers and return value against work/eboot.asm;
  - the function is in the current lift's function_table[] (src/recomp, 34,402 entries);
  - each filtered call site is lifted exactly once as `ctx->lr = 0x<S+4>; func_X(ctx);`;
  - none of the filter LRs is a branch target in eboot.asm (design.txt 1.3), and
    neither are the patched words 0x0068F5B4, 0x00305580, 0x00690800, so "after the
    bl" is the only way to reach them. 0x0068F4F8 IS a branch target (from 0x0068F618
    and 0x0068F650); all three of its paths are hooked (rows 21-22).

k = Hor+ factor = window aspect / render aspect (1280x720 = 16:9), live.
m = draw_distance, s = shadow_distance, b = bloom/100. f32(x) = round to single.

#  | P8Plus patch (yml)                 | RPCS3 patch addresses                                   | native hook: function, mode, entry_lr filter       | native logic
---+------------------------------------+---------------------------------------------------------+-----------------------------------------------------+---------------------------------------------
1  | 60 FPS                             | 0x0069A70C li r5,2 -> li r5,1                            | sub_0044CB14 WRAP  lr 0x0069A718                   | pre: if r4 == 0x7C22B645 (render_lock_framerate) r5 = 1
2  | Unlocked FPS                       | 0x0069A70C li r5,2 -> li r5,0                            | (same hook, fps = unlocked / > 60)                 | pre: r5 = 0
3  | 60 FPS Menus                       | 0x005683F4 nop -> bl cave 0x0042CED8 (..0x0042CEE7)      | sub_00443A78 WRAP  lr 0x005683F4                   | post: if (s32)r3 == 2 then r3 = 1 (0 with fps > 60)
4  | 60 FPS Cutscenes (experimental)    | 0x005C1964 nop -> bl cave 0x0042CEE8 (..0x0042CEF7)      | sub_004492C0 WRAP  lr 0x005C1964                   | post: if (s32)r3 == 2 then r3 = 0
5-8| Ultrawide 21:9 (2560x1080),        | 0x00500E14..0x00500E24 (sub_500E14 rewrite, b MUL_K);    | sub_00500E14 WRAP  (all callers)                   | post: f1 = f32(f1 * k)
   | Ultrawide 21:9 (3440x1440),        | MUL_K cave 0x0042CEBC..0x0042CED3                        |                                                     |
   | Super Ultrawide 32:9,              | 0x005EA9E4, 0x005EB908, 0x005EE294 nop -> bl MUL_K       | sub_00A03C60 (tanf) WRAP  lr 0x005EA9E4,           | post: f1 = f32(f1 * k)
   | Triple Monitor 48:9                | (NOTES also 0x0048700C)                                  |   0x005EB908, 0x005EE294, 0x0048700C               |
   |   -> one option: hor_plus = auto | off | <aspect>; k = 1.33333 / 1.34375 / 2 / 3 become a live k
9-12| 16:9 HUD for <each aspect>        | 0x005AADD4 bl sub_5CA1B0 -> bl HUD_TEXT 0x0042D000       | sub_005CA1B0 WRAP  lr 0x005AADD8                   | pre: x' = (x - W/2)/k + W/2, r9 verts at r6, stride r8
   |                                    | 0x0060E8EC, 0x005F6404 bl sub_5CA1B0 -> bl HUD_SPRITE    | sub_005CA1B0 WRAP  lr 0x0060E8F0, 0x005F6408       | pre: same unless min x <= W/64 && max x >= W - W/64
   |                                    |   0x0042D058                                            |                                                     |
   |                                    | 0x005170CC nop -> bl POS3D 0x0042D0F8; pool 0x0042DF00   | sub_00500E2C WRAP  lr 0x005170CC                   | post: f32 *(r5@entry = r1+0x74) = 320 + (x-320)*k
   |   -> one option: hud169 (uses the same live k; W = *(s32*)0x1038F7F4)
13 | No Depth of Field                  | 0x004DB8F8/FC, 0x004DBA00/04 -> li r3,0; blr             | sub_004DB8F8, sub_004DBA00 REPLACE                 | r3 = 0 (DOF / poisson_dof IsActive)
14 | No Screen Blur                     | 0x004DBAB8/BC -> li r3,0; blr                            | sub_004DBAB8 REPLACE                               | r3 = 0 (Blur IsActive)
15 | No Radial Blur (keep tints)        | 0x0058A020 ld r8,0x20(r31) -> li r8,0                    | sub_00589FAC WRAP  (all callers)                   | post: u64 at r3 (Blur clone) + 0x20 = 0
16 | No Bloom                           | 0x004DB844/48 -> li r3,0; blr                            | sub_004DB844 REPLACE   (bloom = 0)                 | r3 = 0 (bloom IsActive)
17 | Bloom 50%                          | 0x0058A0F8 stvx v1,r3,r8 -> bl cave 0x0042E000           | sub_0058A088 WRAP  (all callers)                   | post: f32 at r3 (bloom clone) + 0x20/0x24/0x28 *= b
18 | Bloom 25%                          | (same, k = 0.25)                                         | (same hook, bloom = 25; any 1..200 works)          |
19 | Neutral Colour Grading             | 0x00589F3C / 0x00589F40 lfs f13/f12 -> lfs 1.0f (TOC)    | sub_00589ECC WRAP  (all callers)                   | post: f32 at r3 (Bright_Sat clone) + 0x18 = +0x1C = 1.0
20 | No Film Grain                      | 0x004DB9A8/AC -> li r3,0; blr                            | sub_004DB9A8 REPLACE                               | r3 = 0 (Noise IsActive)
21 | Ped/Object Draw Distance x2        | 0x0068F5B4 lfs -> bl cave 0x0042E080 (lod_dist1/2/3)     | sub_00442E6C WRAP  lr 0x0068F5B0                   | post: this = r5@entry - 0x3C; +0x34/+0x38/+0x3C *= m
22 | Ped/Object Draw Distance x4        | 0x0068F4F8 fmuls -> bl cave 0x0042E0E0 (SuspendDistance) | sub_00442E6C WRAP  lr 0x0068F46C, 0x0068F608       | post: if r3 != 0: f32 *(r5@entry = this+0x1C) *= m
   |   -> one option: draw_distance = m | (path 3 of the same site)                              | sub_00446148 WRAP  lr 0x0068F648, 0x006907F8       | post: f1 = f32(f1 * m)
   |                                    | 0x00690800 fmuls -> bl cave 0x0042E120 (suspend ctor)    |   (same hook as path 3)                            |
23 | Shadow Distance x2 (experimental)  | 0x00305580 lis r4,0x7e77 -> bl cave 0x0042E160           | sub_00442E6C WRAP  lr 0x0030557C                   | post: this = r5@entry - 0x48; +0x44/+0x48 *= s
24 | No LOD (debug nolod)               | 0x0068F4B0 beq cr7,0x68f534 -> nop                       | sub_0044909C WRAP  lr 0x0068F4A8                   | post: r3 = 1 (nolod path taken)
25 | Main RAM 255 MB (Debug Console)    | 0x0063FE0C, 0x0063FE4C                                   | NOT PORTED                                          | see "Skipped"
26 | Texture location from address      | 0x005BA6C8, 0x005BA73C                                   | NOT PORTED                                          |
27 | Zone VRAM slots in main RAM        | 0x00245FD8, 0x00246004                                   | NOT PORTED                                          |
28 | Zone VRAM slots split (3+1)        | 0x00245FD8, 0x00246004 + cave 0x0042E200..0x0042E237     | NOT PORTED                                          |
29 | Zone VRAM slots split (2+2)        | (same, mask 0xC)                                         | NOT PORTED                                          |

Totals: 24 of 29 yml patches covered. 5 REPLACE hooks, 13 wrapped functions, 21 hook
registrations, 18 call-site (LR) filters, 0 data patches, 0 mid-function site hooks.
Variants collapse into config values: fps (2), hor_plus (4), hud169 (4), bloom (3),
draw_distance (2), screen_blur (2).

Combinations the yml forbids are made impossible or enforced in load_config():
  60 FPS + Unlocked     -> one key (fps)
  one aspect only       -> one key (hor_plus); the HUD always uses the same k
  bloom variants        -> one key (bloom)
  DD x2 / x4 / No LOD   -> one key (draw_distance); nolod forces draw_distance = 1
  Menus needs 60 FPS    -> menus60 is ignored with fps = 30. Alone, the 2 -> 1 clamp
                           would make the whole game 60: the engine-init lock is the
                           same script global.
  Cutscenes needs both  -> cutscenes60 is ignored unless fps != 30 and menus60 is on
  No Screen Blur vs No Radial Blur -> one key (screen_blur = off | tints)


Generalisations (beyond the yml, same mechanism)
------------------------------------------------
  - hor_plus = auto takes k from the live window (DISPLAY_READY / RESIZE events, with
    PRESENT as a per-frame safety net); hor_plus = <w>:<h> forces an aspect, also below
    16:9 (3D only; the HUD squeeze needs k > 1).
  - bloom is any percentage 0..200; 0 still means the IsActive replace ("No Bloom").
  - draw_distance is 1..8 and shadow_distance 1..4 (yml: 2/4 and 2).
  - 0x0048700C (tanf in sub_486EA0, which has no callers) is included, as in NOTES.md.
    The yml leaves it out. It is harmless, and that projection also calls sub_500E14.
  - hor_plus_pip (native only, ini key, default off): render-to-texture viewports (+0x10/+0x14
    smaller than the 1280x720 canvas in both axes, not depth-only flag 0x40: the goal viewport
    640x360, photo capture 960x540) keep the stock projection. sub_500E14 post leaves f1 alone for
    them; the tanf post compares the builder's aspect FPR (sub_5EA504 f27, sub_5EB14C f30,
    sub_5EDAA0 f29) with the last stock PiP aspect and then leaves tan alone too; sub_486EA0
    (no callers) checks the viewport in r29. The RPCS3 yml widened every viewport (thp8-mod
    work/hud/REPORT.md "Risks": PiP content looks thin).
  - pillarbox (native only, ini key, default on; needs hor_plus + hud169 and k > 1):
      sub_0038C300 WRAP (all callers; the Bink quad, HUD REPORT "Bink fullscreen quad"):
        pre: f1 = (f1 - W/2)/k + W/2, f3 = f3/k   (f1 = x, f3 = x scale; right edge =
        x + w*sx at 0x0038C364; W = *(s32*)0x10391CD4). Movies become a centred 16:9
        picture with the cleared target at the sides.
      sub_0056A0F4 WRAP (all callers; DisplayLoadingScreen's draw, used by every boot /
        legal / loading screen): marks the thread while it runs.
      sub_005CA61C WRAP, entry_lr 0x00573C30 (DrawPrimitive-from-array in the sprite-mesh
        renderer sub_573B24), only inside sub_0056A0F4: pre: x' = (x - W/2)/k + W/2 for the
        vertices of the mesh in r31 (+0x24 array, +0x3C count, +0x48 stride; x = first f32),
        W = *(s32*)0x10391CD4. The loading screen is three such meshes (background
        [0x1029CD88], wheel [0x1029CD7C], tip [0x1029CD80]) after a GCM clear to black, NOT
        SDraw2D: the 2026-10-05 version squeezed HUD_SPRITE batches inside sub_56A0F4 and
        never fired (p8verify census: 0 DrawPrimitiveUP calls inside it), so the loading
        screens stayed stretched. The arrays stay squeezed after the draw (the GPU may read
        them later); a per-array cache maps values we wrote back to the originals, so k
        changes (resize, F11) apply on the next frame without compounding, and a 16:9
        window restores them. Mode 1 ([0x1029CD90] == 1, `DisplayLoadingScreen blank`, a
        black full-screen quad) is left full width.
      sub_00567810 WRAP, entry_lr 0x0056A240 (the wheel spin; only caller sub_0056A0F4, right
        before the clear): pre: the squeezed vertex arrays of the wheel list [0x1029CD7C]
        (next element at +4) get their original x back. sub_567810 rotates the wheel's
        vertices in place (reads them back), so without this the squeeze compounded every
        frame and the wheel collapsed into a sliver near the centre (seen in game,
        2026-10-08 n32b); the draw then squeezes the rotated vertices again.
      HUD_SPRITE still squeezes full-width SDraw2D batches drawn inside sub_0056A0F4 (none
        seen; safety net).
    count_calls also logs a census of new DrawPrimitiveUP and DrawPrimitive(array) call
    sites, flagged when they come from inside the loading-screen draw.
  - host renderer keys (native only, [renderer] section; no hook, no guest change, no yml
    row): the ps3recomp D3D12 renderer (thp8-gfx, libs/video/rsx_live_gfx.h) does render
    scale, FXAA, anisotropic filtering, host shadow-map scale / PCF / PCSS and the GPU
    submit batching on the host side. ps3mod_init (apply_host_gfx, after load_config)
    sets the matching Win32 environment variables with SetEnvironmentVariableA before
    the renderer reads them (once, at its start; after the plugins have loaded):
      graphics_preset   PS3_GFX_PRESET         vanilla | high | ultra
      render_scale      PS3_GFX_SCALE          1 | 2 | 3 | factor 1..6 | auto | WxH
      aa                PS3_GFX_AA             off | fxaa | fxaa_all
      aniso             PS3_GFX_ANISO          1 | 2 | 4 | 8 | 16
      kick              PS3_GFX_KICK           0 | 16..100000
      shadow_resolution PS3_GFX_SHADOW_SCALE   1 | 2 | 4
      shadow_filter     PS3_GFX_SHADOW_FILTER  hw | pcf | pcss
    Precedence per option: a PS3_GFX_<KEY> already in the environment (kept) > the ini key
    (overrides the port's graphics.ini) > graphics_preset (the renderer applies the preset
    first; every single option overrides it; graphics.ini keys also win over a preset).
    Passing rules: render_scale / aa / aniso / kick / graphics_preset are passed on when
    set to anything usable (absent, empty or "preset" = not set; stock values like aa = off
    are passed on so they can override a preset); the shadow pair keeps its older rule
    (stock 1 / hw = not set, left to the preset / graphics.ini). Out-of-range values are
    clamped with a WARN (render_scale 1..6, aniso to a power of two 1..16, kick 16..100000),
    unparsable ones are WARNed and treated as not set. The shipped ini sets
    graphics_preset = high and render_scale = auto; aa, aniso and kick are "preset" and the
    shadow pair 1 / hw, so the preset supplies them (high: fxaa, 16x, kick 128, 2x pcf
    shadows). Back to stock = graphics_preset = vanilla + render_scale = 1. Host renderer
    values are applied even when no game hook is
    enabled (the plugin then unloads, the environment stays). Not covered by the plugin:
    PS3_GFX_SHADOW_RADIUS, PS3_GFX_PCSS_K, PS3_GFX_LIVE, PS3_GFX_INI (set them in the
    environment).


Frame pacing beyond the yml (native only, plugin ABI 1.1, host->set_pacing)
-------------------------------------------------------------------------
  fps = 60        lock 1; asks the port for a 60.000 Hz guest vblank (the legacy ticker is 62.5 Hz)
  fps = N (>60)   lock 0; guest vblank 60 Hz, flip-completion clock N Hz: the render thread then
                  waits only for its previous flip (sub_566484), so N is the frame cap
  fps = monitor   lock 0; flips complete on the monitor's vblank (IDXGIOutput::WaitForVBlank)
  fps = unlocked  lock 0; flips complete as soon as the port presented the frame
  vsync / frame_latency -> the port's present mode (Present(0) + ALLOW_TEARING / waitable object)
  fps_method = vblank (experimental): lock 1 at a guest vblank of N (or the monitor's rate); the
                  display-rate constant 0x1005D5E8 (s32, 60) is rewritten to N on each PRESENT
                  event so the game's vblank -> seconds conversions stay right.
  The guest vblank stays 60 Hz by default because the game counts vblanks for timers
  (sub_61380C vblank->ms clock, goal/session timer 0x3DB7A0.., sub_456170 vblank-change waits; see
  thp8-native work/refresh/notes.txt). Game logic uses the measured frame time (Tmr sub_613BC8 /
  sub_613A5C, clamped only above 8 nominal frames). Bink decodes on its own thread (sub_0039178C).
  A 1.0 host (no set_pacing): fps 120/144/monitor fall back to unlocked; vsync/latency ignored.


Skipped and why
---------------
Main RAM 255 MB / Texture location from address / Zone VRAM slots (3 variants), yml
"Patch Version 0.1" test patches. They exist only to fit the HD "full" texture pack
into the PS3 memory budget under RPCS3 Debug Console Mode.
  - The lead's brief: the native port does not need them.
  - The native runtime reports 256 MiB RSX local memory and enforces no retail
    user-memory budget (design.txt Part 2).
  - Main RAM and Texture location are mid-function edits (sub_63FD98, sub_5BA6xx).
    They would need lifter site hooks (--hook-sites), which this ABI version does not
    offer.
  - Main RAM 255 would also overlap the HLE inject window at 0x20000000 until that
    window moves (design.txt R2).
  - If ever wanted, the zone-slot items map onto WRAP sub_0063AE54 lr 0x00245FE0 and
    sub_0063B064 lr 0x0024600C: skip next when CR4.EQ == 0 (design.txt 1.8).
Not part of the yml: the boot/loading screens and HD world textures are data. They go
through the port's mods overlay (mods\<pack>\PS3_GAME\...), not this plugin.


Verify in the first run (open questions)
----------------------------------------
 1. Frame pacing:
      - The port's vblank_ticker ticks every 16 ms (~62.5 Hz) instead of 60 Hz, so
        "60 FPS" is ~62 fps and stock is ~31 fps. Check the log for
        "render_lock_framerate 2 -> 1" and the measured present rate.
      - The swap chain presents with Present(1, 0) (host vsync). On a 120 Hz display
        that should not cap below the guest's pacing; check unlocked fps there.
 2. Hor+:
      - Confirm the port really stretches 1280x720 to a non-16:9 window. The swap chain
        uses the DXGI_SCALING_STRETCH default and no ResizeBuffers was found in
        rsx_live_draw.c. Round wheels / round HUD icons at 32:9 = correct.
      - Confirm the loader fills ps3mod_display.window_w/h and raises DISPLAY_READY
        (and RESIZE). The log line "Hor+ k = ..." must appear after the window opens.
        Before it, k = 1.
      - Movie / Bink frames and the loading screen stay stretched, as under RPCS3.
 3. 16:9 HUD:
      - The vertex buffers passed to sub_5CA1B0 must be reachable through
        guest_to_host(r6, ...). Otherwise the per-vertex read/write fallback runs
        (slower, same result).
      - The full-width rule: quads wider than ~98.4% of W stay stretched, so tiled
        backgrounds get pillarboxed (known RPCS3 behaviour).
      - PiP viewports (goal intro / photo / 2P) look thin, because sub_500E14 widens
        every viewport (as in the yml).
 4. Graphics:
      - Bloom / neutral grading / radial blur scale the Clone's return value r3. Verified
        statically: r3 is the clone at blr in sub_58A088 / sub_589FAC (never
        reassigned), and sub_589ECC restores it with `mr r3,r10` at 0x00589F78.
        verbose = on logs the first clone address and values.
      - Draw distance: verbose logs a WARN if r5@entry - 0x3C != r31 (sub_68F404) or
        r5@entry - 0x48 != r30 (sub_3054CC). Expected: no such warning.
 5. Performance:
      - Every call of a hooked function goes through ppu_hook_enter + the filter test,
        even when the filter does not match.
      - Hot ones: sub_443A78 (script global lookup, menus60), sub_442E6C (CStruct float
        getter, draw distance / shadow; mostly at load time), sub_4492C0
        (Script::GetInteger, only with cutscenes60), sub_5CA1B0 (each 2D draw, hud169).
      - Measure with PS3_SAMPLE as in design.txt 1.9 step 4.
 6. Loader details still to confirm once ppu_mod_loader.cpp lands:
      - the env override spelling (PS3MOD_<PLUGIN>_<KEY>, assumed
        PS3MOD_THP8_MOD_P8PLUS_<KEY>);
      - that plain keys are found inside [sections] ("first match in any section");
      - that REPLACE hooks without a filter on sub_4DB8F8 / sub_4DBA00 / sub_4DBAB8 /
        sub_4DB844 / sub_4DB9A8 are accepted (no other plugin replaces them);
      - that several filtered WRAP hooks on one function (sub_442E6C: 3, sub_5CA1B0: 2)
        chain correctly.
 7. No LOD: sub_68F404 then returns r3 = 1 instead of 0. No caller is known to use it.
    Debug option only.
 8. Shadow distance: the RPCS3 result was never verified visually (stencil shadow
    volumes). It stays experimental.
 9. Host renderer keys (graphics_preset, render_scale, aa, aniso, kick): the parsing and the
    environment hand-over were checked with the DLL against a stub host (every value form,
    clamping, warnings, a user variable winning, all hooks at stock). Still to check once
    with a port exe that has the gfx options: the renderer's own "[gfx] options (ini+env ...)"
    line shows the values, and render_scale = auto picks the monitor of the game window.


Verified in gameplay (2026-10-05, p8verify; thp8-native work\p8verify\notes.txt)
--------------------------------------------------------------------------------
build-hooks\thp8.exe (lift LIFT-34402-fdf5135540217811), window 4981x1401 (PS3_WINDOW=5120x1440
fitted to the work area), free skate in the garage/pool level, Tony Hawk.
 1. Frame pacing: "render_lock_framerate 2 -> 1" applies, menus clamp 2 -> 1. Menus/movies run
    60+ (lead smoke 01:14: 60-78 vs vanilla 43-50). In the level the port itself manages only
    ~15-19 presents/s at lock 2 (vanilla too), so 60 cannot hold there yet. With lock 1 the level
    renders in the port's "garbage mode" (flat untextured polys) straight after loading; vanilla
    hits the same mode after a pause/unpause. Base-port bug (render agent), not a plugin bug.
 2. Hor+: k = 1.99987, viewport aspect 1.7778 -> 3.5553, tan(hfov/2) 0.72654 -> 1.45299 (lr
    0x005EB908). In the window the skater/garage keep their proportions and the view is twice
    as wide at the same vertical view (docs\shots\20261005_p8verify_strip_horplus_level.png).
 3. 16:9 HUD: main menu bar, skater select panel, pause menu and button bar centred at 16:9;
    full-width bars/ticker stay full width. Bink backdrops and the loading screen stay
    stretched (as under RPCS3); the skater-select backdrop sides show the pillarbox seam.
    (2026-10-07: pillarbox = on now pillarboxes Bink movies and the loading screens; see
    the pillarbox entry above.)
    POS3D was not hit (no 3D-anchored marker on screen in these runs).
 4. Bloom 50% + neutral grading: visible (outdoor no longer blown out). Vanilla spawn luma
    151.6 / 20.7% clipped vs P8Plus 103.4 / 4.2% (centre 16:9 of the Hor+ frame). Open: 1 of 6
    consecutive P8Plus frames came out bright with the bloom halo back (luma 167); a same-run
    A/B (bloom=100 vs 50, fps=30) is still to do to tell plugin from port.
 5. Draw distance: SuspendDistance 24 -> 48 (default path), getter path (value 0, unchanged),
    lod_dist 400/401/300001 -> 800/802/600002; no "this" WARN. Far-ped visual test not done.
 6. No WARN lines from the plugin. The port ends with ExitProcess on window close (before
    thp8 cfbcaa4), so ps3mod_shutdown never printed the hit counts: verbose now also logs them
    every ~30 s; count_calls = on counts every call of the hooked functions (overhead).
 7. Loader (item 6 of the first-run list): env overrides PS3MOD_THP8_MOD_P8PLUS_<KEY> work
    (FPS, HOR_PLUS, VERBOSE used), ini keys are found inside [sections], the chained filtered
    WRAPs on sub_442E6C (3) and sub_5CA1B0 (2) all fire. The REPLACE hooks (dof/blur/grain/
    bloom = 0) were not exercised: they are off in the default set.
