The game checks the reference only for null, and players' crash dumps show it holding 0xffffffffffffffff after minutes to hours of play. MacNativeGames met the same field holding 0xffff0000ffffffff during a save load. Set optref (TOTK_OPTREF_GUARD, on by default) turns the cbz into a test for null or negative; valid holders are heap addresses below 2^63.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game grows grass blades only inside its near grass map, a 62.5 m square around the camera drawn into 256x256 and
128x128 textures, so on open meadows the grass appears a short way in front of the player. grass_range (percent, 50
to 400, default 100) scales the map's extent, step and both textures together through a new grass patch set (1.2.1
and 1.4.3) that loads them from patch page +128..+144, and grows the HeapDef grass heap and the application pool for
the larger textures. At 100 the set stays off and nothing changes. A row in the Display menu, text in all four menu
languages, and totk.ini.example describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The climb velocity step (1.4.3 main+0x1f49754, 1.2.1 main+0x1918220) multiplies the requested velocity by Nintendo's
factor for a movement loss that grows with the frame rate. 1.4.3 fits it at 30 and 60 fps and caps it at 2 from about
77 fps (63% climb speed at 120, 34% at 240); 1.2.1 has no 60 fps branch (78% at 60). Below a 1/60 s step the set climb
continues the factor with a curve fitted to a replayed ladder climb, taking the patch page's frame ratio without the
time scale, so 30 and 60 fps keep the shipping factor bit for bit, slow motion included. Only the player's climb
modules reach this code. Its own setting, climb_fix under [frame rate], with a menu toggle; off without the real-time
step. Ladder replay, 1.4.3: 1.665 / 1.663 / 1.669 / 1.681 m/s at 30 / 60 / 120 / 240 (shipping 120 1.046, 240 0.563).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ActorCheckRotateModule's update compared the angle a gear turned since the last update with one 30 Hz frame's worth,
so above 30 fps gears and water wheels read as stopped and the Riogok, Turakamik and Wind Temple doors stayed shut.
The new set gears keeps the update context's +8 (frames elapsed in 30 Hz units, the value 1.4.3 divides by) and
multiplies the threshold by it. Measured: the context is 0.25 at 120 fps with the real-time step and 0.5 at 60 fps
without it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game picks a scene tier of 100, 90 or 80 % mostly from a fixed cost model made for the Switch that grows with the
scene width squared, so on a wide output it drops to 80 % on any GPU: at 5120x1440 every frame drew 80 % of the scene.
Patch set fixres replaces the one instruction that moves the tier's scale into the controller's result (1.2.1
main+0xcc2c00, 1.4.3 main+0x5848a8 as MacNativeGames patches it) with 1.0. The new setting dynamic_res (menu toggle,
live) brings the game's stepping back and is on by default in the Steam Deck profile.
The scene cap goes from 4096 to 8192 a side, so render_scale above 80 works on a 32:9 output, and the scene also stays
within a DynamicTextureAllocatorMem under 2 GiB; the log says when it was made smaller. The memory selftest now maps
past the real pool size instead of a fixed 4 GiB, which a larger scene grows past.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An IPS, IPS32 or pchtxt file for main that writes main's .rodata, .data or .bss was refused as a whole. Such writes
need no lifting: tools/exefs_patches.py now puts them in build-cache/exefs.data, and runtime/mods.c stores them into
the loaded main before any game code runs, as emulators and Atmosphere apply a patch file to the module before its
dynamic linker runs. A DT_RELR word gets main's base added; bytes over words an explicit-addend relocation fills change
nothing and are dropped; a write into the dynamic linker's tables, a lazily bound function pointer or outside main's
image keeps the file unsupported. A mod whose data lines do not all fit the build keeps its code patches off too.
The mods are applied from the bottom one up (mods_exefs_apply), so where two mods write the same bytes the higher one
wins, as it does for files. --probe-mods checks it with two mods writing one byte.
The lifted code reads some of main's bytes at lift time (the jump tables). recomp/gen.py lists those ranges in
<gen>/main/folded.txt, and the setup checks the data writes against them after the lift: a file that writes them
becomes unsupported, since the change would not reach the generated code. Generated C without mods is unchanged.
This is the data part of #790 (ericdallo's ExeFS work), split out on its own; the plugin part follows separately.
Co-Authored-By: itaskaev2 <165683571+itaskaev2@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 72 mouse_direct sites of patches-v143.txt mapped to the 1.2.1 main by enclosing function, each original word
checked: the camera input update, free orbit, Ultrahand and the 16 player camera modes. 1.2.1 keeps some values in
other registers and stack slots (camera input x in s11, orbit angles at sp+0x3c/0x40, Ultrahand x in s9), so the
hooks read a per-version MouseLayout; the 1.4.3 values and behaviour are unchanged.
Where 1.2.1 adds a turn in a shared helper, the degrees go to the store of the helper's result (Aiming's pitch
target, new ROLE_BASE_STORE) or to the turn it is given (Horse yaw). Wakeboard, ShieldSurfing and Tail read the stick
twice (REPEAT on the first read), the dialogue cameras' angle loads are pre-indexed (mov -> add, as ClimbObj).
The player state (ability, bow in hand) is not known on 1.2.1 and reads as not found.
tests/mouse_direct_check.py runs the hooks with both layouts and checks each load-site table against its patch file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ClimbObj pitch hook replaced the pre-indexed load ldr s1, [x22, #0x88]! and wrote the write-back to x22 from the
host call. The lifter keeps X19-X29 in locals across a call and does not reload them, so the write-back was lost and
the following str s0, [x22] wrote the pitch over the camera object's vtable pointer; the next virtual call crashed
(seen climbing a Stone Talus). The mov x22, x19 before the load is now patched to add x22, x19, #0x88, the hook
performs a plain ldr s1, [x22], and load hooks no longer support write-back loads at all.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the free camera took the mouse as degrees. The other camera modes took it as stick input, which their own speeds,
frame time, slow motion and curves reshaped, with the on-foot camera's square-root pitch shaping on top: bow aiming was
far too sensitive vertically on slow movements, abilities and the Purah Pad camera eased their pitch, lock-on turned
27% slower, and climbing, shield surfing and the rest each turned at their own rate.
Each camera mode's update now loads the gamepad's part of the stick where it computes its turn and adds the mouse's
degrees where it adds that turn to its angle (runtime/mouse_direct.c camera_load, the load sites of
recomp/patches-v143.txt): aiming, scope, abilities (and Recall's cursor), horse, Purah Pad camera, lock-on, bow
lock-on, wall and object climbing, the waterfall's turn, shield surfing, wakeboard and the dialogue cameras. Recall's
cursor moves so that its aim turns as many degrees a count. The waterfall's height and the horse's empty lock-on are
not angles and stay stick input, now linear. A step whose mouse motion no hook took is logged with the camera's name.
Holding L to soothe the horse gave the mouse to the ability wheel's virtual cursor, a held stick for the camera: the
mouse goes to the wheel and the D-pad strips only while their screen has the focus (menu_cursor.c
menu_cursor_shortcut). A mouse shake no longer plays a 0.8 s swing after it: the right stick follows the mouse's own
swings and lets go when it rests, and the camera does not turn by that swing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The map cursor, the vertical Ultrahand aim and the vertical gyro aim moved a fixed step per frame, so above 30 fps they ran faster with the frame rate (#44, #394). The new set cursor multiplies each step by the game's own 30 Hz ratio, ported from MacNativeGames 363a6eb and switched by TOTK_HIGHFPS_FIX.
Map pan with the same stick input on 1.2.1: 398 px/s at 30 fps, 1150 px/s at about 105 fps before, 402 px/s after. The Ultrahand and gyro sites were not exercised in game. 1.1.0 is not covered: its module is not available here to check the sites.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The loader keeps a texture's place in its pool as a 32-bit word (main+0xad4294) and widens it with a sign extend for
nvnTextureBuilderSetStorage's offset (main+0xad43f0) and for SetPackagedTextureData's source (main+0xad440c). Past
2 GiB the offset turns negative and the storage lands 4 GiB below the pool (MacNativeGames ca311e2d, seen with a 4K
texture pack's heap table in a 3.58 GiB pool). The shipped heap table keeps every pool far below 2 GiB, but
TOTK_APP_POOL raised with such a pack's heap table (RomFS mods replace it) can reach it. The new patch set pooloff
(TOTK_POOL_OFFSET_PATCH, on by default) widens unsigned: the same value below 2 GiB, right up to 4 GiB.
1.4.3 needs no patch: both of its loaders (main+0x583c34, main+0x27ed214) keep the offset in 64 bits (str x25 / mov
x2, x25 / add x1, x25, x8). gen.py accepts both original words, and the lifted C at the sites reads the offset as
uint32_t with the set on and as int32_t with it off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Brazilian Portuguese and Simplified Chinese setup texts now name the 1.1.0 update beside 1.2.1 and 1.4.3, as the
English and Russian ones do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The update this port builds for has version value 0x20000, which is Tears of the Kingdom 1.1.0, not 1.1.1: the upstream expected-v1.1.2.json carries 0x40000 and the other expected files step 0x10000 per release, so 0x20000 is 1.1.0 and 0x30000 is 1.1.1. Pull request #220 introduced the wrong label in expected-v1.1.1.json.
Expected hashes, patch sites, boot constants and the RomFS layout are unchanged; only names change:
- tools/data/expected-v1.1.1.json -> expected-v1.1.0.json, display_version 1.1.0
- recomp/patches-v111.txt -> patches-v110.txt
- installer/layout.py SUPPORTED and tools/build_game.py DISPLAY_VERSIONS
- make_romfs_patches.py --version choices and its COMMON table key (the RomFS archive name stays Common.Product.110)
- runtime RT_V111 -> RT_V110, the inits_111 and expect_111 tables, the probe's census folder v110
- README, docs and the installer texts
Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
Merged onto the mods folder (task/mods1); the Cheats tab sits after Mods. The cheats' menu text and the cheat file's
place are reworked for the mods folder in the commits that follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps pull request #321's anisotropic filtering, which #89 added a second time, and #267's shadow_res beside #89's
shadow_scale. The HDR parameters move from byte 1408 of the support buffer to 1440: translator patches 0010 and 0011
read pcss at 1408 and shadow_spread at 1424.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each frame the engine sizes its shadow cascades to the area they cover (main+0xcebdf4 in 1.2.1): from 960 down to about
400 near the ground, so leaf shadows turn coarse, and every change of size moves the shadow texels, so they jump instead
of sliding. The Switch does the same; the community mods force a fixed size. The shadow patch set loads the size from
patch page +92 just before the engine rounds it (main+0xcec22c in 1.2.1, 0x683604 in 1.4.3), and the menu's Shadow map size
(shadow_res in totk.ini) chooses it: game, 512, 768, 1024 (default), 1536 or 2048, with the texture heap grown above 960. A write
watch on the size field found the one writer; with the setting, every shadow map in both versions is the fixed size.
recomp/patches.py lists the patch page words in use.
TOTK_FOV (70..140 percent, Screen tab, live) multiplies the camera's vertical view angle through the new patch set fov
at the two camera routines the aspect set patches; page +60 holds the factor. At the default 100 the set is off and the
camera runs the game's own instructions. Both fov sites and the rest of the 1.4.3 table pass patches.check against the
1.4.3 main. 1.2.1 and 1.1.1 have no fov sites and log that the setting does nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.1.1 (update 131072) gets its boot constants, expected hashes, the 60 fps
RomFS overlay, its own code patch table, the scene module slot, loader probe
values and shader census lookup, and the setup accepts it. 1.2.1 and 1.4.3
keep every value: the runtime branches on RT_V111, and an install without a
recorded version still falls back to 1.2.1.
1.1.1 has no aspect patch set; outputs other than 16:9 are scaled to the
window on that version.
Fixes#173
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 60 fps frame step and ratio, the render resolution, the slow-motion check and the actor animation clock, at the
addresses this build has them: each site was located again by instruction-window matching and its words checked against
the 1.1.1 main. The spin-wait back-edges are the ones that only another thread can end, and the last five sites are the
two inlined clocks of the animation update with the shared call they hand their seconds to.
Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
The camera update and setup routines that the aspect set patches load the camera's vertical view angle a few words
before the ratio and pass it to sead's perspective projection. The new set fov loads the angle as before and multiplies
it by a factor the runtime writes to patch page +60, so TOTK_FOV (70..140 percent, menu Screen tab, live) widens or
narrows the view. The game's own zooms (bow, scope) scale with it; 100 switches the set off.
Checked on 1.4.3 at 1920x1080 at 70, 100, 130 and 140: the view narrows and widens around Link, the HUD is unchanged,
no objects appear or vanish at the edges, no validation errors. 1.2.1 has no fov sites yet (its original words are
unverified here); the setting logs that and does nothing there.
1.2.1 has the same mismatch as 1.4.3 at the same player state field: the
update steps +0x109c only while it is above FLT_EPSILON and the equipment
gate blocks changes while it is above 0. The realtime set now compares with
0 in the update. Ported from the 1.4.3 patch by JieML1100 in issue #18.
Co-Authored-By: JieML1100 <183249798+JieML1100@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The player update steps the countdown at player state +0x109c only while it
is above FLT_EPSILON, while the equipment gate blocks weapon, shield and bow
changes while it is above 0. With the real-time step a rounding remainder in
(0, FLT_EPSILON] stays there and locks equipment after a Fuse until a load.
The realtime set now steps the countdown while it is above 0. Root cause and
patch by JieML1100 in issue #18.
Co-Authored-By: JieML1100 <183249798+JieML1100@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged with applezra's follow-ups on the same branch: the sun shadow filter narrowed by the shadow map scale
(translator patch 0011), anisotropic filtering forced only on tiling samplers, and TOTK_DUMP_UBO with a vec4 count.
Conflicts with dev: cfg.c (names, display section, defaults next to aspect and device_profile), menu.cpp (the new rows
after the aspect row), vk_texture.c (the pool alias check takes scale == 1 and dev's bind_pool_memory size argument),
build_xlate.py (patches 0005-0011 are ours; the Linux note kept), totk.ini.example (both sections). The contact pass
release went into gfx_present.c, the shared half of the old gfx_window.c.
aniso defaults to game: the picture stays as before until a player raises it in the menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
patches-v121.txt takes the aspect set at upstream's 1.2.1 sites (game_patches.c aspect_install): the same two camera
loads and two viewport divisions as 1.4.3, with the same original words and the same replacement from page +56. The
installer now builds the romfs-patches-<w>x<h> overlays for 1.2.1 too; ultrawide_layout.py passes on its
Common.Product.110 at all ten sizes. Checked in game at 3440x1440 (camera 2.3889) and 1920x1200 (1.6000): wider or
taller view, HUD at the corners or edges, no validation errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The branch forked 79 commits before main and 98 before dev. Resolutions:
1. Aspect ratio: dev's implementation stays (camera patch at page +56 on 1.4.3, tools/ultrawide_layout.py layouts per
size in romfs-patches-<w>x<h>, the aspect = fit|stretch setting). The PR's second implementation is dropped: the
aspect sets of patches-v121.txt and patches-v143.txt (the 1.4.3 one patched the same viewport sites as dev's),
tools/aspect_layouts.py, the resolution.c shapes that limited game resolutions to 16:9, 16:10 and 21:9, and the
picture_fit setting. Its letterbox code (gfx_fit_rect) is kept and reads TOTK_ASPECT.
2. Code dev changed where the PR moved it: the stick dead zone, the test gamepad and the paddles go to input.c; the
DPI awareness, keep-awake and IME detach of gfx_window.c go to gfx_window_win.c; device fault reports and GPU
breadcrumbs to gfx_present.c. gfx_window_dpi gets an SDL version.
3. dev's Win32 calls in shared code go through platform.h: the shader cache pack reader (PlatFile), the romfs
overlay checks, the audio pacing waits, the motion, ASTC and device fault locks. plat_cpu_usable and
plat_commit_info get Linux versions; plat_cpu_count counts the machine's processors on both. dpi_probe.c becomes
dpi_probe_win.c.
4. assume_focus defaults to 1 as the PR has it (a load interrupted by a focus loss can stop for good) and stays live,
with dev's Game tab row.
5. The frame limits 40 and 45 join dev's list. The BC 3D storage fallback for Vega only runs with
TOTK_BLOCK_VIEWS_3D=1, since dev makes 3D BC images without block views by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An output_res such as 3440x1440 now widens the camera's view to that shape on 1.4.3: patches-v143.txt
sets the aspect ratio at the camera and viewport sites that hand it to the perspective projection.
The HUD and menus come from a romfs-patches-<w>x<h> overlay that make_romfs_patches.py --aspect
builds through tools/ultrawide_layout.py, and romfs.c overlay_init picks that overlay when the game
resolution is not 16:9. The installer prepares it for 2560x1080, 3440x1440, 3840x1600 and 5120x2160.
When the window's shape differs from the game resolution's, the new aspect setting keeps the
picture's shape with black bars (fit, the default) or stretches it over the window.
The shadow cascade range of a pending change loads its four distances from +64..+79 on 1.4.3, where the aspect and the
canvas factors were; with both on, the camera would have read a cascade distance as its aspect. recomp/patches.py now
lists the words each set uses.
The sun's shadow cascades end at 20, 40, 80 and 160 m: the depth shadow parameter object (main+0x6fa3c0) registers
depth_shadow_near_0_1..3 and depth_shadow_far_0 as immediates, and the field's .bgsdw files do not override them. The
new cascade patch set reads the four distances from patch page +64..+76 instead, and shadow_range (percent, 50 to 400,
default 100) writes them there at start, so the visible steps where shadows turn softer move away from the player. It
spreads the shadow map over more ground, which shadow_scale pays back. Only patches-v143.txt has the set; on 1.2.1 the
setting logs that and does nothing.
The setting is in totk.ini.example and on the Screen tab of the menu (English and Russian).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Above about 63.5 fps the fade step trunc(2.1 * ratio) is 0, so objects that start a fade stay translucent or
invisible until a slower frame moves them on (issues #10, #37). Switching to 60 fps and back cleared it because the
60 fps step is 1. Port of MacNativeGames' high-FPS fade fix as a fade set under TOTK_HIGHFPS_FIX.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gen.py started one emit worker per CPU, about 1.3 GiB each on 1.4.3, so many-core machines with modest RAM died with a MemoryError during "Translating code to C" (issues 17, 28, probably 66). The default is now min(CPU count, 80% of the free physical memory / 1.5 GiB), at least 1, capped by the memory limit of the enclosing job object if there is one. --jobs still overrides it, and the chosen count and its reason are logged. The memory helper is tools/hostmem.py, shipped with the release tools.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Players bring their own cheats\<build id>.txt next to totk.exe; none ship with the port. The setup turns cheats that
write main's code into patch sets (tools/cheats.py), cheats that write data run in a cheat VM after dmnt's every
presented frame (runtime/cheats.c), and the menu's Cheats tab switches each one, saved under [cheats] in totk.ini.
The patch table now takes several sets per site, first set in file order winning, and replacements that end in a
branch or ret, so cheats that patch the same instruction or end in a jump can be built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 1.4.3 sites, found by their code: the camera parameter reader's DefaultAspect default (main+0x1aa0ac4, the default
at main+0x2d06910), the two camera-viewport updates (0x1ff04f4, 0x253fbb0, where the division becomes the page load
because x9 is free after the dirty-flag store) and two of the 2D canvas-size loads (0x2136fa8, 0x213641c). Three of
1.2.1's five canvas loads have no match yet; switching them off on 1.2.1 left the gameplay HUD and the system menu
unchanged at 21:9. At 16:10 and 21:9 the 1.4.3 title and gameplay show a round emblem, an unstretched world and the
HUD at the screen's edges.
The aspect patch set (recomp/patches-v121.txt) makes the world camera's DefaultAspect parameter default, the two
camera-viewport updates that divide a 16:9 rectangle, and the five loads of the 2D canvas size read the patch page.
resolution.c writes the output's aspect and the canvas factors there (wider than 16:9 the canvas grows in x, narrower
in y) and switches the set on when the game resolution is not 16:9. The sites are those of Fruithapje21's
TOTK-Ultrawide 1.2.1 patch (GPL-3.0, read for behaviour); a heap scan of the live projections confirmed the four world
cameras take the new aspect. The interface keeps its 16:9 layout in the middle of the canvas: the per-layout edits
that move it to the screen's edges are not done.
A version without the set (1.4.3 for now) draws the largest 16:9 size inside the wanted one, letterboxed. The Steam
Deck profile now draws the screen's own 1280x800, and the menu offers 16:10 and 21:9 game resolutions.
The generator emits a guest poll such as `ldr w9, [x19]; cmp w9, #1; b.eq` as a plain C load, and clang hoists it out
of the loop: the lock wait in f_7001fa0e2c compiled to `cmp; jne; jmp .`, so a thread that found the lock held spun
forever after its holder released it. A fight run hung this way in 3 of 13 launches on an RX 9070, with the UBO
snapshot on and off; gdb showed the thread on that `jmp` in every sample. The gate is an opaque call on every
back-edge of the 868 loops tools/spin_waits.py lists, so each pass reloads memory: the exe goes from 182 self-jumps to
148, and 144 of those are the game's own `b .` instructions. 8 of 8 fight runs then passed; tests/diff/func.py with
--patches passed 5470 cases on 878 functions, and the four probes passed on 1.4.3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The lifted decoder ran its copy loops as chains of small functions. On the island and fight routes TOTK_ZSTD_CHECK=1 decoded every call both ways with no difference; the host took 0.72 s where the guest decoder took 1.99 s. Load time and fps on an RX 9070 and a 7900X did not change: the gain is CPU time on the loading threads, for machines with fewer cores. The caller at main+0x547a04 stays on the guest decoder: it resets a static DCtx with which the game's zstd rejects some frames libzstd decodes, and the game relies on that result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FP helpers compared c->fpcr with a cached copy before every operation to resync MXCSR lazily. MXCSR now
follows FPCR.FZ at once: MSR FPCR calls rt_fpcr_sync, and so do rt_call (thread, fiber and host entry into guest
code), fiber resume (FIBER_FLAG_FLOAT_SWITCH fibers keep their own MXCSR) and the differential test harness per
case. op and vop only test FPCR.RMode for the slow path, and their redo check is one ordered-not-equal compare of
|r| with the smallest normal, the same NaN-or-smallest-normal set as before.
Hot guest code shrinks by about a quarter (132 functions holding half the guest cycles, 1.4.3); fight frame time
moves 1-3%, inside the run-to-run spread. lift, simd, func, atomic and flags_check pass on 1.4.3; simd.py fails
without the harness sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every lifted instruction read and wrote c->x[n] and c->sp, so each register passed from one guest instruction to the
next went through a store and a load, and clang could not keep it in a host register across a guest store.
recomp/regs.py now copies the six most used X registers and SP of a function into locals at its entry. A forward pass
over the function's flow tracks which locals changed since the last sync, and only those are stored back before a
call, an SVC, a trap, a return or a tail exit. After an SVC every local reloads; after a call only X0-X18 and X30,
since AAPCS64 has the callee preserve X19-X29 and SP. TotK does not break that: main and subsdk0 import neither
longjmp nor the unwinder, sdk's have no direct callers, and the runtime writes no register above X3 except X30.
gen.py --no-callee-saved-trust reloads them too, --reg-locals sets the count. Functions with an unresolved computed br
keep their registers in the Cpu, as they keep NZCV.
All registers in locals was slower than none: clang spills them to the stack and the syncs stay. Over 300 hot
functions, 4-6 locals gave the fewest instructions and memory operands.
1.4.3 .text 299 -> 266 MiB. Fight bench (bench-fight2-2026-10-27b, frames 5400-7500, uncapped): 11.30 and 11.92 ms vs
13.5-13.9 on main; frames 2400-5300 11.24 and 11.36 vs 13.25. Without the callee-saved trust 12.58 ms.
tests/diff/func.py: 0 failing on 1.4.3 (889 functions, 583 with calls) and 1.2.1 (810); a plan with no spills fails
at once. Probes kernel, vkexec, nvn-tex and shader pass in both versions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every flag-setting instruction packed N, Z, C and V into c->nzcv and every reader unpacked them. recomp/flags.py
now runs a reaching-definitions pass per function: a compare stores its operands in a local Flags F, and a reader
whose definitions are all of one kind computes its condition from them, so cmp and b.cond become one host compare.
CCMP, FCMP, MSR NZCV and ADCS write the packed form, which readers reached by several kinds also use. Calls and
returns publish nothing (AAPCS64 neither passes nor preserves NZCV); tail branches, falls into the next function and
br through rt_dispatch publish c->nzcv, since discovery can start a function inside another one. Functions with an
unresolved computed br keep flags in c->nzcv.
1.4.3 main: 492k direct and 89k packed flag reads, .text 342 -> 299 MiB. Fight bench (bench-fight2-2026-10-27b,
frames 5400-7500, uncapped) 13.15 and 13.63 ms vs 13.9 before. tests/flags_check.py checks the new helpers against
the packed ones; tests/diff/func.py no longer compares NZCV, which generated code leaves untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The output and scene patch sets make the game read its output, swapchain and scene sizes from the patch page
(MacNativeGames' sites for 1.2.1 and 1.4.3); resolution.c picks them from TOTK_OUTPUT_RES and TOTK_RENDER_SCALE and
grows the HeapDef texture heap, its parent and the application pool to match. vk_fsr.c runs AMD FSR 1 EASU and RCAS
on the present when the window is larger than the game's output (TOTK_UPSCALER, TOTK_SHARPNESS).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recomp/patches-v143.txt ports MacNativeGames' 1.4.3 fps60, real-time step, cloth and bounce sites; the real-time
producer reads the nominal interval from patch page +8, which frame_rate.c now writes as seconds * 60.
make_romfs_patches.py takes --version (Common.Product.140 on 1.4.3).
SDK 20.5 grew KeyboardConfig to 0x518 bytes, so ShowKeyboardArg's work buffer is at +0x518; swkbd_win.c and the
services probe pick the offset by version. 1.4.3's movie::Decoder vtable names the complete-object destructor D1,
which gets the D2 override. The loader probe has the measured 1.4.3 import and relocation counts, and probe-ipc
expects REV15 from the 1.4.3 SDK and skips the 1.2.1 sf heap constructors (1.4.3 creates the audio heap lazily).
A non-1.2.1 build defaults to its own profile, build/profile-<version>: 1.4.3 rewrites the saves it loads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The jump-table check now explains oracle targets for case indices peeled off by cmp #k; b.ne before the table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>