The setup runs make_romfs_patches.py --prompts xbox and --prompts xbox-label and warns once when either fails, the
release bundles tools/button_icons.py and fontTools, and build_game.py and package_build.py copy both folders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tools/button_icons.py, adapted from MacNativeGames' make_button_icons.py, draws LB, RB, LT, RT, Menu, View and a d-pad
cross into the button font of every font archive and into the button guide's L and ZL images, with the letters cut out
in the game's own text fonts, and raises the resource size table for the archives that grow. --prompts xbox swaps the
face buttons as --xbox-prompts did (now its older name); --prompts xbox-label keeps their letters for the xbox and auto
pad layouts. The plain overlay is unchanged. tests/romfs_prompts_test.py runs both on a RomFS it builds itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
A Chinese IME switched on over the game attached to the window, then showed its candidate window and took the keys it
composes although there is nothing to type into here; Shift, which a player may have bound as the walk key, is one of
the keys such an IME takes. The software keyboard's edit box is a window of its own and keeps the IME it needs, so only
the game's window is detached.
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>
The root pane's shipped scale_x must be 1.0 before the tool writes the narrowed scale. A layout or animation member missing from the archive stops with a SystemExit naming it instead of a bare KeyError. The docstring no longer describes nvn_mtl.m's UI squeeze or scripts/ paths from upstream, and says where the pane list and distances come from (the GPL TOTK-Ultrawide mod, values only).
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.
llvm-mingw's headers default to Windows 7 and hide AdjustWindowRectExForDpi, GetDpiForWindow and the other per-monitor
DPI functions, so the release toolchain failed on gfx_window.c and dpi_probe.c. MSYS2's headers already default to
Windows 10, the port's minimum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since MacNativeGames f024ef04 the R and B rows keep the key as "<vs variant>:<fs variant>:<rest>" instead of plain hex,
and parse_list stopped with a ValueError on a source checkout. The pinned release list parses as before (identical
variants and priorities); the current one gives 58053 variants.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The installer names its temp folder after the game title (Legend of Zelda, The - ...), and clang splits -Wl, values at every comma, so lld received a truncated --Map path and a stray input file. Fixes issue 47.
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>
Comments cited codes from local notes (port-lessons P1, backlog), task labels (T6c1-T6c3, M4), bare upstream commit
hashes and issue numbers, a file that no longer exists (nvn_api.c, now nvn_api_*.c), a generated file (main_0103.c)
and line numbers into upstream files that refs/ has moved past, so they pointed at unrelated code. Those now name the
file, function or project and say what the cited code does. Attribution of copied code keeps its pinned commits.
Corrected facts: TOTK_REPLAY_ASYNC defaults to 0, and the shader cache, pipeline cache and pipeline keys live next to
totk.exe, not in build/v121.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
With TOTK_TRACE=sched, TOTK_CRIT=<lo>-<hi> records every state change and wake of the guest threads and every
present over those frames (runtime/kernel.c crit_add from acct() and wake(), crit_frame at the present) and writes
them to TOTK_CRIT_OUT at the last frame. tools/crit_path.py walks back from each present through the waits to the
thread whose signal ended them and splits the frame interval by thread and state; the sums equal the interval.
A 1.4.3 fight: with the replay on the submitting thread the path is 7.3 ms of replay on the render thread and 4.3 ms
of guest code; with TOTK_REPLAY_ASYNC=1 it is 10.1 ms of guest code through the fork-join of the main thread and five
workers, at the same frame interval.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.2.1 renders the scene size it reads, while 1.4.3 scales it by output width / 1920: on 1.2.1 the scene set is on
whenever the output grows (2560x1440 gave a 1600x900 scene before, 2133x1199 now). An output at or below the shipped
size needs no grown HeapDef. The release deps carry the static libzstd and its header, totk.ini.example the four new
keys, THIRD_PARTY_NOTICES.md the MacNativeGames sites, FSR 1 and zstd.
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>
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>
Comments named the agent task (T1-T7, M2-M5, Task 2.x) that once owned each file or block, and pointed at local
notes the repository does not carry (backlog items, milestone plans, research notes). They now say what the code
does. gfx_stubs.c held weak stand-ins for functions that every build has defined for a long time.
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>
1.4.3's GPU heap allocator creates a physical pool with flags 0x18c (CPU_CACHED, GPU_NO_ACCESS, COMPRESSIBLE): it
keeps the game's storage and gets device memory like the 0x189 pools of 1.2.1, which virtual pools alias. The CPU
never writes that storage (its hash was unchanged over 3600 frames of the intro), so nothing is synced.
nn::aoc::ListAddOnContent, which 1.4.3's main calls at start, returns no add-on content instead of running the SDK
on an uninitialized aoc client.
probe-ipc runs the 1.4.3 SDK's renderer semaphore constructor (sdk+0x221fb0) instead of the 1.2.1 sf heaps, and
expects SDK 20.5 to keep its audren:u manager session open. probe-services checks ListAddOnContent.
package_build.py takes --version.
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>
The point form of a sampler slot is built once (vk_sampler_point_by_id_locked). Translator patch 0009 rounds the 2DArrayShadow reference in Ryujinx's SPIR-V backend.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The backlog gets one "Open work" section: freezes, bugs (the Yunobo fade with its repro), machine items and paused
branches. prepare_shaders.py no longer stops on the "*" and "*_depth" texture kinds of the newer list revisions.
The playbook records the GTX 1050 Ti, RTX 3090 Ti and 8th-gen i7 numbers and how to get a perf log from those
machines; port-lessons gains the MacNativeGames virtual-core preemption row; totk.ini.example lists mouse_sens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>