By default the left button is Y, which opens the dialogue log in a dialogue box. While MessageDialog_00 is open the
left button now presses B, which advances the text and shows the rest of a scrolling line, and the right one A.
Dialogue choices keep the menu pointer, where a left click picks the choice under it, so clicking through a
conversation does not pick the choice the focus starts on. The setting is live and independent of menu_cursor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod file whose name carries Product.<n> and which the RomFS lacks was counted as made for another game version and
never served. The game does ask for such names: on 1.4.3 with mods on, the file trace (trace = fs) shows it opening
Sound/Resource/Pack_MasterSword.Product.110.bars.zs, while its RomFS holds only the Product.140 and the unversioned
file. A sound mod that ships its pack under all those names was silent here and works on a console, where LayeredFS
shows the game every file of a mod. Such files are now added overlays like any other new file. Another version's
resource size table is still not served and still lends its entries; the files are still counted, and the Mods tab
and docs/mods.md now say they are read only when asked for by name.
tests/mods_romfs_check.py gets a case for it. Not run here: the test and --probe-fs need a build and the game's files.
runtime/romfs.c compiles with llvm-mingw.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgQM2zwuVagwScByEJhT8V
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>
- Enable VK_NV_low_latency2 and VK_KHR_present_id with present ID features
- Add VkSwapchainLatencyCreateInfoNV to swapchain creation
- Link present and submission IDs with standard Vulkan latency types
- Insert latency markers: simulation start/end, input sample, render submit, and present
- Gate all extensions and latency chains on reflex != off (RESTART setting)
- Log graceful notice when Reflex is requested on unsupported hardware
- Add UI menu setting and translations for en, ru, pt-BR, zh-Hans
- Document reflex setting in totk.ini.example and docs/settings.md
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>
replay_workers = N (0, the default, keeps the sequential replay) records a submission of enough draws in chunks that start between passes, on N threads and in twice as many chunks, and submits them in order. In the town at 4K on an RTX 5070, 4 threads take the replay from 8.2 to 4.5 ms a frame and the frame rate from 71 to 79.
A sequential scan on the submitting thread applies only what is queue state (fixed-function and bind ops, render targets, render enable and conditions, samples-passed counting) and keeps a copy of it where each chunk starts, with how far its lists got: inline uniform updates, RENDER_COND results, contact shadow marches and the ops that drop uniform copies. A chunk takes those from the lists instead of from the chunks before it, starts with every image in GENERAL and ends with a full barrier, so its recording depends only on the copy and the lists.
Each thread records with a context of its own: the replay's memos, descriptor state, occlusion query segments and per-command-buffer state moved from file statics into VkExec and VkExecDraw, and the tables the contexts share (stand-ins, depth twins, views, mirrors, timestamps, completion callbacks, crumbs) take locks or atomics. The scan makes render targets live in replay order and notes the op (vk_texture_revive), since a chunk can sample a storage before the chunk that renders it got there or after the scan went past a later target there: a chunk's texture slots resolve as of its own op, and the chunks after a texture with no entry yet wait for the chunk that makes it. Uniform copies are dropped at the op that writes them (vk_usnap_drop_at).
Contact shadows plan their march in replay order (vk_contact_plan) and the chunk that holds its op records it; the HDR source keeps the copy that comes last in replay order. DLSS and fxaa = 0 act on the frame draw by draw and keep the replay sequential. TOTK_REPLAY_STATE_CHECK=1 compares the state after the last chunk with the scan's.
A draw or dispatch whose shaders write storage buffers drops the uniform copies they cover from its own op on, as the other GPU writes do (scan_writes). The scan still translates no shader: it reads the shaders the recording contexts translated from a table shared with them (vk_exec_draw_writes), and an op whose shader no context translated yet holds the chunks after it until it is recorded. Before, the chunk that recorded such a write dropped the copies for every position at that moment, so a later chunk recorded first bound the copy taken at the submit call: on an RX 7800 XT on RADV the grass near the camera came out dark or unshaded in some runs with 4 threads.
TOTK_TRACE=perf counts the chunks held per submission by reason and logs when each chunk of the last submission began. The settings menu has the thread count under Advanced, Scheduling, with the count this run uses under it.
The recorder thread begins, ends and submits the submission's open part (vk_rec.c rec_end_cb), so a chunk's mirror prologue stays off it: vk_sync_part waits until the recorder is idle, as the part pool allows one thread at a time, then records, ends and submits the prologue itself. Through the recorder the prologue could reach the queue before it was ended, and the GPU lost the device at the title screen.
(cherry picked from commit b94e2e9642)
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>
A host thread adds each second in which the window has the focus and the game presents frames, keeps the total in
<profile>/playtime.txt (every 30 s and at exit) and the Game tab of the settings menu shows it with this run's share.
Texts in English, Russian, Brazilian Portuguese and Simplified Chinese. Closes#720.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Button prompts row gets a third choice, PC: every button glyph of the game's button font and every image of the
button guide is drawn as the key or mouse button the keyboard defaults press it with (E, Shift, Space, left click, Q,
R, right click, F, Tab, M, 1 to 4, W A S D, Ctrl, C) and the right stick as the mouse, whatever the pad layout. The
setup writes romfs-patches-pc next to the Xbox folders (make_romfs_patches.py --prompts pc, button_icons.py pc_icons);
--binds <totk.ini> draws the keys of its bind_ lines instead. At a game resolution of another shape the game takes
romfs-patches-pc-<w>x<h> (--prompts pc --aspect) in place of romfs-patches-<w>x<h> when it is there.
(cherry picked from commit 3f81cf02be664100bd9c569c53588d620d90b527)
#483 draws the stick glyphs on the wrong code points: U+E056 and U+E057 (the stick presses) get WASD and the mouse,
U+E05E and U+E05F (the sticks) get Ctrl and C, and both left-stick HUD images show Ctrl. #585 maps them by the game's
font and keeps each glyph's advance.
This reverts commit cd35ffa.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
G (bind_SHAKE, keys only) swings the right stick left and right for 0.8 s, longer while held, so a part comes off
without shaking the arrow keys by hand. The pad keeps shaking its own stick. Remappable in the Controls tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mouse_mode now defaults to direct: on 1.4.3 the camera turns by the raw mouse motion; 1.1.0 and 1.2.1 have no hooks
and keep stick emulation, and no setting changes the gamepad. The Controls tab gets a "Mouse in the game's menus" row
for menu_cursor, and the mouse rows are in all four catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add keyboard and mouse button icons to tools/button_icons.py and
tools/make_romfs_patches.py --prompts keyboard, with a third 'Keyboard
and mouse' choice under Controls > Button prompts (prompts = keyboard).
Keycaps cut their labels out of the game's own text fonts, mouse clicks
and right-stick camera look draw clean mouse silhouettes, and
make_romfs_patches.py --ini reads any bind_ overrides from totk.ini.
The game stretches each decoded movie frame over the whole canvas, so the 16:9 movies were stretched on 32:9, 21:9 and
16:10 outputs. The host decoder now shrinks the picture inside the frame with a box filter and fills the rest with
black, so that the game's stretch gives the picture its own shape back. The new setting movie_aspect (TOTK_MOVIE_ASPECT,
keep or stretch, read when a movie starts) turns it off, and the menu has a row for it next to the picture shape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nvnWindowAcquireTexture holds the game until the image is free, which was one period after its present's display time, when the next frame reaches the display. The present is a copy queued by then, so with frame_queue = 1 the image is free from that time: the game draws the next frame while the previous one waits for its turn, and a frame that takes up to one period more still makes its slot. In the town at 4K at 60 fps on an RTX 5070, frames over 17.5 ms fell from 25-33 to 1-8 per 30 s, for up to one frame more input delay. Off by default; the menu switches it at once.
Conflicts in cfg.c (the key list) and main.c (the usage line) resolved by keeping dev's lines and adding the
Discord keys and --probe-discord.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The panel opens with the port version and the game update, so a screenshot in a bug report carries both. Under RAM,
after a gap, two rows on the same grid: FPS with the 1% and 0.1% lows, and the frame times. perfmon.c keeps a
histogram of frame times in 0.25 ms bins, one slice per second over the last ten seconds, and reads the lows from it
twice a second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game language row listed eleven languages. 1.4.3 also ships Brazilian Portuguese (Mals USpt), and every build
ships British English, Canadian French and Latin American Spanish (EUen, USfr, USes), which ov_language already
accepted. ui_text_game_langs lists them per game build: pt-BR from 1.4.3 on, since 1.2.1 has no USpt. A row selects
only its own code, so en-GB and es-419 no longer show as English and Spanish.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged on top of #566 and #594, by hand in vk_dlaa.c:
- One dlss_preset key: default keeps #594's model per mode (K for DLAA, Quality and Balanced, M for Performance, L
for Ultra Performance); k, j or any other letter a..m takes that model in every mode, live, recreating the features
as #534 does. The menu texts say so, with pt-BR and zh-Hans added.
- The mask pass stays on #594's camera motion (the identity and the CPU-made reprojection in dlaa_mv.comp's two
blocks) and adds #534's outputs; it runs before the per-object pass. Where the reactive mask test
(TOTK_DLSS_REACTIVE) ran, DLSS gets that mask instead of the characters'.
- #566 dropped the scaler_skipped/scaler_missed report, so #534's reset of it goes too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged on top of #566. The camera motion is #594's: its blocks in camb also feed the per-object pass, and
dlaa_mv.comp stays as on main, fed the identity and the reprojection made on the CPU in double. #566's
vk_usnap_host goes, vk_usnap_bytes serves the same. #566's scaling-draw swap, camera rune guard and output-size
scaling pass stay in vk_dlaa.c. Translator patch 0012 becomes 0015 after dev's 0012-0014.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Solid scene draws whose position reads an object's own uniform blocks (bones c5, place c7, c6) are drawn a second time
with a vertex shader variant (translator patch 0012) that computes last frame's position from last frame's blocks; the
game writes those into two copies that take turns, and the uniform snapshot keeps both (vk_mvprev.c). The draws are
listed during the replay and recorded through the recorder thread with repeated state skipped. Blocks whose last-frame
copies measured wrong (c9 and c11 of walls and cliffs) keep this frame's; positions without an object block (terrain,
grass) keep the camera's motion, which measured as good.
The camera motion is reprojected in double precision on the CPU: the float inverse made noise on far surfaces and a
still camera that DLSS followed into a wobble. DLSS gets Ultra Performance (a third of the output, also in the F1 menu)
and NVIDIA's recommended preset per mode (K, M for Performance, L for Ultra Performance; TOTK_DLSS_PRESET overrides).
Test switches: TOTK_MV_CHECK measures the motion vectors and DLSS's output against the picture, TOTK_DLAA_TIMES times
DLSS and the motion passes on the GPU. A reactive mask pass for transparents is included but off (TOTK_DLSS_REACTIVE):
its mask covers sky and fog too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The motion vectors follow the camera only, so a character, animal or held item that moves on its own drags a faint
copy of itself through DLSS's history. TotK's G-buffer target 0 carries an object class in green: above 64/255 for
characters, creatures and held items (Link 253, others 125, held items 160), 0 for terrain and 32 for foliage, checked
in the field, in Goron City and on horseback. The motion-vector pass now also writes DLSS's bias mask
(DLSS.Input.Bias.Current.Color.Mask) where such an object is now or was the frame before, read through the motion
vector from the previous frame's class mask, so the history that holds the moving object is weighed down there.
dlss_char_mask (0, 35, 65 or 100 percent, default 65; F1 > Screen > "DLSS character trails", live) sets the strength.
A 640x360 pass of the G-buffer's shape, seen while loading, no longer replaces the scene's class buffer in a frame.
The status line also prints after 300 frames, with the frames that had the mask.
dlss_preset (default, k or j; F1 > Screen > "DLSS model", live) sets the render preset hints when a DLSS feature is
created. default leaves the choice to NGX (preset K for DLAA, Quality and Balanced in the 310 releases); j is the
transformer model NVIDIA describes as slightly less ghosting at the cost of more flicker. A change recreates the
features at the next DLAA run. FrameTimeDeltaInMsec now carries the time since the previous evaluation instead of a
fixed 16.67 ms. The menu and totk.ini.example name the DLSS model file of the platform in use.
The device sink loop now writes every output channel; upstream's TOTK_AUDIO_DUMP gets the front pair before clipping.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The replay marks the start and the end of every render pass and dispatch
(vk_exec.c vk_exec_pass_begin and vk_exec_pass_end, vk_exec_draw.c
op_dispatch and op_dispatch_ind), outside the pass so no pass is broken.
With VK_AMD_buffer_marker the GPU writes the last started and finished mark
into words 2 and 3 of the breadcrumb buffer; with
VK_NV_device_diagnostic_checkpoints the same points are checkpoints, read
with vkGetQueueCheckpointData2NV after the loss. A ring of 4096 records maps
a mark to its submission, index, target formats and size, draw count and the
input ids of the first and last draw's shaders. vk_device_fault_report logs
both marks with their records. The calls go through the recorder thread.
TOTK_PASS_CRUMBS (menu: GPU pass marks): unset, AMD buffer marks only; 1,
also NVIDIA checkpoints, whose extension is enabled only then; 0, none.
The design follows Savage Wind Recomp 3fae40c; the code is ours.
--probe-vkexec gets a pass marks part: pass, dispatch, pass in one
submission; the marks read back after the fence name the last pass and the
report text says so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host thread speaks Discord's documented IPC protocol to a running client
through platform.h's new plat_ipc_* (a named pipe on Windows, a Unix socket
in $DISCORD_IPC_PATH, $XDG_RUNTIME_DIR, $TMPDIR or /tmp on Linux) and shows
the game update and the elapsed play time as the status of a configurable
application. It never holds a virtual core, picks up settings changes within
half a second of the menu writing them, re-sends the activity once a minute
as its keep-alive, answers the client's Ping frames and reconnects 15 s
after a break; Discord clears the status when the process ends.
The Menu tab of the settings menu switches discord_rpc, and discord_app_id
and discord_image in totk.ini name the application and its art asset (a
player can register their own). The default app id is the one this was
developed against; the maintainer may want to register a project-owned one
and swap it. --probe-discord runs the exchange without the game: it caught
the opcode numbering (Handshake is 0, not 1) against a live client.
Tested on 1.4.3, Linux: probe passed end to end against Vesktop's arRPC
bridge, an invalid app id falls back to the default, selftest clean.
The game's SDR clamp turns bright saturated colours towards white: a torch flame clips red, then green, and reads
yellow-white, the colour its emitted light was tuned to. The extended HDR curve keeps the channel ratios, so the same
flame stays deep orange next to the yellow light it casts. hdr_hue (0 to 100, default 80, live) moves colours from the
picture's white upwards towards the game's own SDR result at the HDR luminance, as RenoDX's TotK mod does. It rides in
the support buffer's HDR vector (z), unused until now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixer folded the game's 5.1 device sink down to stereo and IAudioDevice told the game it had two channels.
audio_channels (auto, stereo, 5.1; Sound tab, Speakers) now opens the SDL stream with six channels in the device sink's
order (FL FR C LFE RL RR) and reports six to the game, which then mixes for 5.1. auto picks 5.1 when the default
playback device has six or more channels (a 5.1 or 7.1 speaker setup in Windows) and stereo otherwise; stereo keeps
the existing downmix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mod that patched the game code (.ips, .pchtxt) stayed off as a whole.
tools/exefs_patches.py now reads IPS, IPS32 and IPSwitch files from each
mod's exefs folder and from atmosphere/exefs_patches/<name>/. It keeps the
ones whose build id is main's and turns their writes into main's code into
one patch set per file. The writes go through the same rules as the code
cheats (tools/cheats.py convert). A file that writes main's data, or that the
rules cannot express, is unsupported as a whole. The setup's lift runs it
after cheats.py and writes build-cache/exefs.status.
runtime/mods.c checks each patch file against that status:
- a built file's set is switched on with its mod;
- a file the build lacks keeps the mod off and starts the rebuild at the next
start, like a code cheat does;
- an unsupported file keeps the mod off, and the Mods tab shows the reason;
- a file for another version is counted and left out.
A mod patch on a site of one of the port's own sets turns that set off while
the mod is on, and the Mods tab names it. The Mods tab also gets the "Rebuild
the game now" row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code cheats need the game lifted and compiled with them. Running the setup
again did that, but it lifted and compiled everything and prepared the
shaders again.
- The setup keeps the generated C and the objects in its temp folder (about
1.3 GB). gen.py rewrites only the chunks whose C changed, so Ninja
recompiles only those. One new code cheat on 1.4.3 took a 47 s lift and
19 s of compile and link on 24 cores.
- Prepare's key no longer follows the compile key, only the runtime, the
translator and the game version, so a rebuild keeps the prepared shaders.
- The cleanup writes build-cache/rebuild.txt, the command that opens the
setup window in a new --rebuild mode on the install folder. That mode runs
at once and starts the game again when it succeeds.
- At start, runtime/cheats.c runs that command and exits when an enabled
mod has code cheats the build lacks (plat_spawn, new for Windows and
Linux). build-cache/rebuild.skip keeps a failed or cancelled rebuild from
starting again for the same cheats. "Rebuild the game now" on the Cheats
tab tries again, and [mods] mods_rebuild = off turns the rebuild off.
Scripted runs never start it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cheat file is now part of a mod: mods/<mod>/cheats/<build id>.txt, or the
Atmosphere places inside the mod folder, the file name in any case. The port
ships no cheat files any more (the MyNXCheats files and their notice row are
gone).
- tools/cheats.py --mods builds the code cheats of every mod folder; a
cheat's key hashes "<mod folder>/<name>", so two mods' cheats of one name
keep their own switches.
- The setup reads the cheats from <install>/mods and writes the status to
build-cache/cheats.status.
- runtime/cheats.c reads the files of the enabled mods the port can use, in
the Mods tab order, and the status from build-cache.
- A mod with only cheats is usable. The Cheats tab groups the cheats by mod,
and the Mods tab says how many cheats a mod brought.
- The cheat texts are in all four menu languages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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>
Each mod is a row with its switch, the files it serves and why it is unused when it is; the top and left face buttons,
Ctrl+Up and Ctrl+Down or the drawn arrows move it. The real-time step, cloth and high-fps fix switches move here from
the Game tab under the port's own patches. The text is in the catalog in English, Russian, Brazilian Portuguese and
Simplified Chinese. The tester guide says where mods go and how TKMM's merged output fits in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runtime/cfg.c: the known names take both sides, the per-stick dead zones beside the shadow and
DLSS switches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>