The game pauses whenever its window loses focus, as it does on the Switch when Home is pressed, and ASSUME_FOCUS was the
only way to change that: a known key with no row in the settings table. The runtime reads it at each focus change rather
than once at start, so the row takes effect the next time the window is left, and the build guide says what an unfocused
window now does.
A lift rewrites every generated chunk with the same contents, so a local sccache hits all of their objects and the
compile step costs seconds instead of minutes. build_game.py writes it into the ninja file ahead of cc when the
environment names one; the build guide describes the setting.
tools/ultrawide_layout.py computes every edit from the canvas aspect, so 32:9 needs no new layout data: the installer now builds romfs-patches-3840x1080 and -5120x1440 beside the four 21:9 sizes, and the menu lists both. Before, a 32:9 output_res got the wide camera with the 16:9 HUD stretched over it (issue #181).
SDL does not open the Switch 2 Pro Controller on Windows by itself, so it needs a bridge program. Tested with the
game: hbashton's DS4Windows fork emulating a DualSense gives buttons, rumble, gyro, accelerometer and calibrated
sticks, read directly by the game. Bridges that output a ViGEm DualShock 4 (ProCon2Windows, connectyourpro) give rumble
but frozen motion values, so motion controls do nothing with them. docs/controls.md gets a short section with the log
lines that show a working setup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The swapped letters are baked into the game's textures, fonts and messages, so a button remapped with a bind_ line or on the Controls tab keeps the old letter on screen. The menu description (English and Russian) and docs/controls.md now say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
romfs-patches-xbox is optional: when make_romfs_patches.py --xbox-prompts fails (a game update whose prompts it does
not recognise), the setup removes the partial folder, warns (warn.xbox_prompts_failed) and finishes, where it stopped
the whole install before. The game then keeps the Switch prompts and the menu says the files are missing. Its log is
prepare-xbox-make_romfs_patches.log, so it no longer replaces the 60 fps run's log, and Prepare no longer counts the
folder as part of a finished step.
The Xbox button prompts section of docs/controls.md sat between the layout list and the controls table it introduces;
it now follows the table. The building.md note follows the paragraph it interrupted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With pad_layout = nintendo on an Xbox pad the right button does Switch A but is labelled B, the bottom one does B but is
labelled A, and the game's prompts still name the Switch buttons. A fourth Button layout choice, Xbox prompts (A right)
(pad_layout = xbox_prompts), maps the buttons as nintendo and swaps the prompts to the labels, so each prompt names the
button to press. Automatic, Nintendo and Xbox are unchanged and keep the game's own prompts. The buttons change at once;
the prompts at the next start, as the game reads its UI archive and fonts at boot (the menu says so).
At start, xbox_prompts loads romfs-patches-xbox in place of romfs-patches (runtime/romfs.c overlay_init).
tools/make_romfs_patches.py --xbox-prompts writes it from the player's own RomFS, with the 60 fps patches; nothing from
the game or a mod is added to the repository. It swaps A with B and X with Y in:
- the letter images Nt_KeyTexA/B/X/Y_00 of the action prompt (DoCommand_00), in the Common archive's __Combined.bntx
(same format, size and tiling, so the image data trades places);
- the action prompt's diamond of the other face buttons: one-letter text panes the game fills from
LayoutMsg/DoCommand_00.msbt in each language's Mals archive (both the messages and the layout's own text);
- the button glyphs U+E040 to U+E043 of scft/External.bfotf in each Font/Font*.bfarc, which every other prompt uses
(CFF outlines and metrics in the deobfuscated OpenType font, then its checksums).
The diamond's shape shows the button's position and stays. The setup writes both folders; the menu notes when the Xbox
prompt files are missing and when the prompts in use differ from the choice until a restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wide sizes other than the four the installer prepares get the wide camera with a 16:9 HUD stretched over it. aspect = fit letterboxes a 16:9 game in a wider window where it used to stretch. Ultrawide is 1.4.3 only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SDL reports the back buttons of an Xbox Elite, DualSense Edge or Steam
Controller as four paddle buttons, but the pad table stopped at the
triggers, so they could not be mapped. They are now pad inputs lpaddle1,
rpaddle1, lpaddle2 and rpaddle2, with no default action, bindable from
totk.ini or the remapping rows.
TOTK_TEST_PAD=<lx>,<ly>,<rx>,<ry> attaches an SDL virtual gamepad at start with its sticks at those raw values, so the
dead zone and the gamepad path can be tested in a run. On the field save: an 11% drift reached the game as 3.3% at the
default 8% zone (the old per-axis cutoff passed 9% on x and dropped y) and not at all at 15% (Link stood still); full
deflection reached it as full at 15%.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
stick_deadzone (0 to 50 percent, default 8) replaces the fixed per-axis cutoff of 2500. The zone is round and the
travel past it is scaled to start at 0, so a drifting stick rests centred and full deflection still reaches full.
The Controls tab shows it as a slider; --probe-cfg checks the math (input_pad_stick).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AMD driver 26.10.1 on RDNA2 cards wrote past the end of a 3D BC4 volume made
BLOCK_TEXEL_VIEW_COMPATIBLE and 2D_ARRAY_COMPATIBLE, which lost the device at the
logo (issue #5). 3D BC textures no longer get block views: draws into a level go
to an uncompressed 2D array stand-in that is copied back at the pass end
(TOTK_BLOCK_VIEWS_3D=1 restores the old path).
Parts the recorder thread recorded and the submitting thread ended turned the
scene green, then white, on the same driver once a submission was split. The
recorder now writes the closing breadcrumb and ends the part itself
(TOTK_REC_END=0 restores the old split). The recorder stays off on AMD by default.
Based on the patch Pelipooper posted in issue #5; stand-ins extended to every
mip level.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Switch button and the eight stick directions the keys drive get two key cells and a controller cell under the
button layout. A cell takes the next key, mouse button or controller button pressed; a key in use moves over, a
controller button swaps. The tab writes bind_ lines (new: bind_LSTICK_* / bind_RSTICK_*, key:default, pad:default) and
input_win.c builds the tables again when one changes, so remapping applies while the game runs. More keys can be
bound: Enter, Backspace, right Shift and Ctrl, Alt, the numpad, punctuation, F2-F7, F9, F12, mouse buttons 4 and 5.
Alt alone no longer enters the window menu loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gamepad map was by position only, so on an Xbox pad the button labelled A pressed Switch B. pad_layout = auto
(default) keeps the Nintendo layout for a pad whose east button SDL reports labelled A and uses the Xbox layout (south
A, east B, west X, north Y; cross A on a PlayStation pad) for the others; nintendo and xbox force one. It applies at
once; bind_ lines name physical buttons and apply on top of either layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bug form asks for the version on the settings menu's title line instead of the window title, which has none,
points to Copy system info and to logs\totk.log. CONTRIBUTING lists the lifter's differential tests and the commit
message style for pull requests. docs/maxwell.md cites nxnative's notes by name, since nxnative rewrote them and the
line numbers pointed nowhere, and pins the MacNativeGames shorthand to the commit its line numbers were read at.
totk.ini.example gains menu_lang and input_record and points to the menu's Advanced tab for the other keys. AGENTS.md
asks comments to cite upstream code by project, file and function. README: Linux and the Steam Deck are the platforms
planned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README is a front page now: a general status, a short feature list, install steps from a release, credits as a
list of links that points to THIRD_PARTY_NOTICES.md, the build steps last. The licence table moved into the notices.
The build steps take the game version as <ver>; 1.4.3 is the primary version in the architecture doc, which also
gains the zstd host decoder, the settings table and menu, spin gates, the uniform snapshot and the resolution patches.
Controls list F1, F10, F11 and the menu keys; the tester guide and CONTRIBUTING point to logs\totk.log.
THIRD_PARTY_NOTICES.md: the four code rows that sat in the installer table move to the main table, the zstd rows
cover ov_zstd.c and the release package, the MacNativeGames resolution commit is given in full, the NVN proc list and
the compiler runtime of a source build are listed.
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 MacNativeGames BKTR and title-key changes and our UTF-8 manifest lived only in the maintainer's toolchain folder, so
nobody else could build the hactool that extraction and the installer need. The script's exe matches the one in use
byte for byte apart from its build time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/building.md merges the two toolchain docs and the README build steps, and lists the developer checks. The
Maxwell reference moves to docs/maxwell.md. architecture.md loses the milestone table, controls.md describes timed
script lines and input recording, and the tester guide includes shader preparation.
The agent skills, hooks, playbook, backlog and the study notes on upstream projects are the maintainer's local setup:
.claude/, notes/ and CLAUDE.local.md are ignored now, and tools/worktree.py links them into task worktrees. AGENTS.md
keeps the project rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release built libtotkrt.a against generated/v121, so patches.c baked in 1.2.1's patch page (0x700472b000, the page
after 1.2.1's main). A 1.4.3 install lifts its code with the 1.4.3 patch table, whose patched sites read the page after
1.4.3's main (0x7003adf000): the runtime never committed it and the game faulted there soon after mounting the save, while
frame_rate.c wrote its frame step into sdk memory at the 1.2.1 address. patches.c now joins romfs.c in INSTALL_SIDE and
the release ships its source, so it compiles against the player's patches.h.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MacNativeGames now translates each shader's default bind state when it builds and keeps only other variants in its
shared list, so the list at origin/HEAD has 134 rows of 1.4.3 shaders. prepare_shaders.py seeds the default variant of
every shader it finds and reads the list through tools/shader_list.py, which adds the V rows older revisions held. On
1.4.3 that prepares 29,587 variants in 7 min with 0 translation failures, and every one of the 1,095 variants the field
and intro runs met in play is among them. The installer's dev path and make_release.py read the list the same way.
Also record that 1.2.1 loads saves 1.4.3 wrote.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check identifies the update by hash among layout.SUPPORTED and records it in totk-install.json; an install that
records none is 1.2.1. Extraction, lifting, compiling and preparing take the version from there: the patch table and
expected hashes per version, build_game.py --version (rt_game_version), make_romfs_patches.py and prepare_shaders.py
--version. Step keys of a 1.2.1 install stay as they were, so updating such an install does not extract again. Another
update file than the recorded one plans the extraction again (a copy of the same file is recognised by its hash), and
an identified version change re-plans every step after the check. The disk space estimate takes the larger RomFS.
Catalog strings name both versions and take the version as a parameter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README now names the three test machines, the installer, remapping and the mouse camera, and lists the open bugs. The backlog drops the dated diary, finished steps and local paths and keeps every open item with its evidence. Citations into our code name functions instead of line numbers that had drifted, build/ notes are no longer cited, and the toolchain doc lists opus and libvpx.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>