prompts = nintendo or xbox (the Button prompts row of the Controls tab, read at start) takes over from
pad_layout = xbox_prompts, which is read as pad_layout = nintendo with prompts = xbox. overlay_init loads
romfs-patches-xbox for the Nintendo layout and romfs-patches-xbox-label for xbox and auto (input_prompts_folder), loads
romfs-patches instead of stopping when the RomFS mods folder replaces one of their files, and the menu says why the
prompts in use differ from the setting. The settings probe checks the folder against the layout's button table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TOTK_VK_DYN_A2C from pull request #188 gets the settings row, the Advanced tab choice and the totk.ini.example
line that TOTK_VK_DYN_VERTEX has. The IME comment in gfx_window.c window_main named a walk key the runtime does not
have, and ov_system.c still said TOTK_ASSUME_FOCUS opens the window without focus, which TOTK_BACKGROUND_WINDOW does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AMD's RDNA2 driver places the depth compression metadata after the depth data. Above 1080p that tail of a depth
target bound into the game's texture heap lay over the next texture, and every depth write scrambled it: a garbage
band at the top of the screen, a pink glow, blocks in the grass and a blue checkered scene in the menu (RX 6600 to
6800, issue #185). Reproduced at 2560x1440 on an RDNA2 iGPU with driver 26.10.1; all 63 oversize images there were
D16 or D32 depth targets. Colour images keep their place in the pool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight places sized a pool (pipeline workers, library prebuild, translator prewarm and seeding, the multithreaded ASTC decoder) by GetActiveProcessorCount, every logical processor of the machine. A process limited with start /affinity, Task Manager or a launcher then started a pool for 16 CPUs on 4. plat_cpu_usable counts the affinity mask (all active processors for a process that spans processor groups); plat_cpu_count, the machine's count, stays for the system info. Idea from MMadmer's UsableCpuCount in a fork of the Shadow of the Colossus PC port.
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 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.
AMD's Windows driver branch for RDNA 2 and older (vk_caps.amd_sc, driverInfo
"(AMD proprietary shader compiler)") applies alpha to coverage even while
vkCmdSetAlphaToCoverageEnableEXT sets it to false. TotK never turns alpha to
coverage on (a TOTK_DUMP frame showed it off in all 1170 pipelines), yet on an
RX 590 with driver 24.9.1 the character draws kept their depth and lost their
colour in the G-buffer, so Link and the NPCs were black silhouettes, and other
surfaces were dithered into checkerboards (issue #67, RX 470/570/580).
On that driver branch vk_pipeline.c ensure_init now leaves alpha to coverage out
of the dynamic state, so it goes into the pipeline key as on GPUs without EDS3.
TOTK_VK_DYN_A2C=0/1 (ini vk_dyn_a2c) forces either behaviour. NVIDIA and RDNA 3/4
are unchanged. Tested on the RX 590 with 1.4.3: characters, the checkerboards and
the black blocks all render correctly.
Fixes#67.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DcYzeu7eb2txzmu4brvcgh
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).
The pane copied each description into 512 bytes. The Russian button layout description is 840 bytes of UTF-8, so it stopped mid-sentence, and the English one (507 bytes) lost the note appended after it. The pane now holds UI_DESC_BYTES (2048), and probe_menu_text checks every English and Russian menu text leaves room for a note.
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>
The Xbox prompts folder is optional, so a manifest that no longer matches the RomFS logs the reason and drops that folder instead of stopping the game. The main romfs-patches and the mods folder keep their fatal checks.
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>
1.2.1 has no camera aspect patch, so the installer no longer builds romfs-patches-<w>x<h> for it (the layout tool is checked against 1.4.3 and could fail the whole install), and romfs.c overlay_init skips that overlay on 1.2.1 through res_wants_wide_overlay. overlay_init and res_init now both call res_is_16x9 (1% tolerance), so 1366x768 counts as 16:9 and logs no missing overlay.
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.
A BC3 image takes whole 4x4 blocks or a part ending on the mip's edge; a WriteTexels region of a 5x5 or 6x6 footprint may end inside one. --probe-astc encodes every footprint to BC3 and holds the blocks to alpha, palette-place, flat-colour and PSNR bounds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A GPU that cannot sample ASTC gets the textures decoded to RGBA8, four times the video memory of the ASTC blocks. With astc_bc = 1 (default 0, GPU decoder only) a second compute pass, bc3_encode.comp, encodes the decoded texels to BC3 and the image is BC3_UNORM or BC3_SRGB: 8 bits per texel instead of 32. The encoder is the usual bounding-box method with the box diagonal turned to the sign of the colour covariance, and the eight-value alpha mode.
A region of a WriteTexels that does not start on a 4x4 block corner is left as it was and logged. The setting is in totk.ini and the Advanced tab. The SPIR-V header was built with glslang 16.6 (build_shaders.py uses glslc) with the same source hash.
On a GTX 1650 (4 GiB) in the Fire Temple the GPU time per frame went from 10-23 ms to 3-12 ms and the frame rate held at 59-60 fps.
Co-Authored-By: Claude Sonnet 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.
#94 ended on a realloc of 1 MiB that failed with 64 GB of commit allowed, and the log could not tell whether the
process had grown towards the limit or the system ran short. rt_fatal now adds one line with the process's private
bytes and the system's commit in use and limit (plat_commit_info).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The render thread ran on its own 5 ms timer while the device consumed on its own clock, so the queue crept: on
DirectSound it grew 2.7 ms every 5 s, which fills the 340 ms ring in about ten minutes of play. The render thread now
renders the next frame when the device has taken the ring below max(prefill, two device periods), as the audio
hardware paces the Switch's DSP and as LASRecomp and SavageWindRecomp pace their renderers by the SDL stream. Without a
device, while the renderer is stopped and while the device takes nothing for 50 ms (at least four periods) it keeps the
timer, so a stalled device does not stall the game; the ring trim then drops what piled up, measured from the target.
60 s on the monsters save: WASAPI 15 ms queued, DirectSound 27-40 ms without growth, no underruns. With the SDL audio
thread suspended for 1.5 s the game held 60 fps and the queue was back at 15 ms after one trim.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The game resolution row started at 1920x1080 although resolution.c accepts sizes from 1280x720, so weak GPUs and
integrated graphics had to edit totk.ini to draw the game smaller. A 1280x720 run on 1.4.3 renders the field with a
1067x600 scene and an intact interface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
motion_restart only flagged the next sample to start a fresh frame, so a read in the frame of the Start returned the
previous session's 16 states as live, and for the next 60 ms 15 of the 16 were from before it, with the angle jumping
back to zero between two reads. Motion aim was dead when the bow was first drawn and lagged after that. A restart now
hides the older samples and zeroes the angle; sampling numbers keep counting.
--probe-services checks a restart in the middle of a turn: 16 old states in the Start frame before, none after.
Reported with the cause and a harness in #104.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stalled audio device (a wireless headset waking up, a change of the default device) filled the 340 ms ring, the
render thread then dropped new frames and the queue stayed full for the rest of the session: about a third of a second
of delay on top of the device's own. ring_pull now skips what is queued beyond the prefill, the device's request and
40 ms. The 5 s level line (trace = audio) reports the queue and the skipped frames.
With the SDL audio thread suspended for 1.5 s: before, 325-340 ms queued for the rest of the run; after, 10 ms.
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 square 2D array of 6 or more layers was created cube compatible, though the replay makes cube views only of
images whose own target is a cubemap or cubemap array (vk_exec_draw.c get_view). On AMD's Windows driver the flag
changes the image layout, and Savage Wind Recomp traced rainbow blocks on terrain that moved with the mip level to
it (their issue #59, BotW on an RX 580 and a 7800 XT); our AMD and Intel reports show the same symptom (#42, #67,
#81). The flag now goes to cube targets only, on every driver.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TotK's compute passes sample a 4x4 R32_SFLOAT texture through a shadow sampler. Vulkan allows depth comparison only
on formats with SAMPLED_IMAGE_DEPTH_COMPARISON, which AMD's drivers do not give R32_SFLOAT, so the result there was
undefined (20 validation errors in 40 s of field play). Such an image now gets a D32_SFLOAT (or D16_UNORM) twin
copied through a buffer right before each dispatch that samples it, and the dispatch samples the twin. Formats the
driver compares as they are keep the colour image. TOTK_DEPTH_TWIN=0 turns it off. Design after Savage Wind Recomp's
depth twins.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AMD drivers lost the device at boot or at menu opens with the recorder thread; vk_recorder = 0 ended it for three
RX 9070 XT users (issue #5), and RADV saw an illegal opcode at the end of a command buffer (issue #40). It now
defaults to off on vendor 0x1002; TOTK_VK_RECORDER=1 turns it back on.
On AMD's separate driver branch for RDNA 1/2 ("AMD proprietary shader compiler") every device loss came with a cold
pipeline cache and draws waiting on fast-linked pipelines, and the recorder setting did not help there. TOTK_GPL now
has no fixed default: off on that branch, on elsewhere.
VK_AMD_buffer_marker breadcrumbs (vk_crumbs.c) mark the start and end of each submission part, upload batch, ASTC
decode chunk and present; on device lost the log names the last point the GPU reached and the last one it finished.
vk_fsr.c ensure_images held no queue lock around vkDeviceWaitIdle, which needs every queue externally synchronized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows turned the monitor off after its idle timeout during play, since gamepad input does not count as activity (#7).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>