Verified by runs in the audit: the Market's periodic stall (a pool slot was listed once per refresh pass instead of once; the list fell from 160,385 entries to at most 3,192 and no frame over 20 ms remained while standing), orderly exits delete the pipeline prebuild's running marker (left behind, the next start wrote a skip file and every later start skipped the prebuild), the log and crash report name an area a save asks for that the game does not have, and the menu atlas hash no longer overflows a signed multiply. The other fixes compile and pass the regression route but their faults were never reproduced: see AUDIT.md section 13 for each.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A save that names such an area (a child save with HyruleCastle_Child on 1.1.1) makes the game read through a null area object right after the missing files; the report now says which area it was and that the save cannot be loaded.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The action prompt at the top right of the HUD draws the four face buttons as a diamond in the console's places: A right, B bottom, X top, Y left. Under pad_layout = xbox, playstation or auto the A action is the bottom button, yet the diamond kept A on the right, so the prompt pointed at the wrong button. At start the runtime now edits the prompt's layout in the shared interface archive for such a layout (the buttons, the dots and the lit crosses move, and the bubbles of X and Y flip) and serves the result from the profile folder; with nintendo, the default, nothing is changed. Every field is found and checked before anything is written, so a layout a mod changed is left as it is, and the archive is packed again with the game's own dictionary at the same unpacked size. One SARC reader and dictionary lookup now serve this, the camera pack and the menu's assets. When the layout changes, the menu's Restart counts the diamond.
tests/action_prompt_check.py passes without game files and on the game's own archive (4 copies, 208 fields, 13 ms at start).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Four "mem: sizes" lines give the entries and bytes of what no other line counted: the guest heap, physical maps, kernel region, lazily written ranges and the fixed kernel, setjmp, save-file, RomFS and zstd tables; pool records, mirrors, deferred destructions, upload and staging buffers, the device address table and the texture, sampler and slot caches; image views, descriptor spares, the completion queue, uniform snapshot arenas, recording copies and pins; and pipelines, layouts, stage libraries, job queues, the shader memory cache and the translator processes. Each subsystem reads its own counters under its lock; settings now count the replaced values they keep. Segment and scene lines say "-" for the world clock before it starts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An input script line "<tick> MARK <name>" (or "@<ms> MARK <name>") ends the running segment with one line of its frame count, mean, 1% low and worst frame, the work marks on any thread, process memory and VRAM, then the 600-frame memory lines, and starts a segment of that name. RomFS opens of an area's static placement file give a "scene:" line, and once no area has loaded for 10 s the memory snapshot is taken with a "settled" line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slow frame's line and the 60-frame window's line now say how long each kind took on any thread, how often, how many bytes and the longest single one, so a hitch outside the render thread's own waits has a named cause. While NX2NAVI_TRACE=perf is off each site pays one branch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Controls tab's Button prompts row and nx2navi.ini take steamdeck: the Deck's own labels in the game's
text, action prompt and ocarina notes, which are A B X Y where an Xbox pad has them, L1 R1 L2 R2 and L3 R3,
Menu and View. The style is drawn by tools/pad_prompts.py like the others, into romfs-patches-steamdeck
and romfs-patches-steamdeck-label, and every build with button art writes them.
Pull request #107 by loteran asked for PlayStation and Steam Deck prompts; it was closed, and the tree
took the PlayStation half in another form, drawn from button art instead of redrawn font glyphs. This is
the Steam Deck half in that form, written here: the art has no Deck style, so its entry in
tools/prompt_art.py names the Xbox pictures for the face buttons, Menu, View, the d-pad and the sticks
and the PlayStation pictures for the shoulders and stick clicks. It needs four pictures of the Xbox set
that no other style used (the white letters on a light button, for the ocarina's notes); a release made
with --prompt-art copies them with the rest.
The Windows files compile and tests/launcher_test.py passes, with the prompts step's test following the
number of folders (seven now); every picture the style names is in the art used for releases. The style
was not drawn here (that needs the game's font), and --probe-cfg, which has a new check, was not run.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
NX2NAVI_STICK_SENS was served to the game as a multiplied camera pack at start only. A change during the run is now written into the game's memory: the StickSensitivity array as the game holds it, at the factor in effect, is looked for in the guest's heap and every copy is overwritten at the new factor. The game reads the array each time it steps the camera, so the speed follows at once. The copies found are remembered, so a later step of the menu's slider takes no search, and a search runs again a moment after the game opens the pack anew. On Ocarina 1.1.1 the first change of a run found 4 copies in about 2 s on its own thread, and the same stick push for a third of a second turned the camera about 30 degrees at 1, 90 at 3, 10 at 0.3 and 30 again at 1, all in one run.
NX2NAVI_AUDIO_CHANNELS was decided once. A change that gives another channel count now signals the device change events the game holds, as a console does when its output moves: the game asks GetActiveChannelCount again and mixes for the new count, and the mixer makes its SDL stream again with that count at its next render. On Ocarina 1.1.1 the centre input of the device sink went from 0.0006 rms to 0.013 within the 5 s after stereo became 5.1 and back to 0.0006 after stereo was set again, with no underrun. NX2NAVI_TRACE=audio logs the six inputs' levels with its level line.
Both rows lose their "after restart" note. The graphics card, widescreen, the resolution, the game's language, the button prompts and the game's native HDR still take a restart: the game or the Vulkan device reads them once while starting.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A file its loader cannot use is opened again and again until the game does without it, and what needs it stays out of the world. The log now names such a file after 24 opens in a row, which tells a player's report of missing geometry apart from a rendering bug. It is one count and one timestamp per file, touched only when a file is opened.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Button prompts row gains Keyboard and mouse (prompts = pc). tools/pad_prompts.py --style pc writes
romfs-patches-pc: every button glyph shows the first key its bindings give it (Ocarina's defaults, with the bind_
lines of the install's nx2navi.ini over them), the mouse buttons as a mouse, the d-pad and the left stick as their
keys laid out as on the keyboard (1 to 4, W A S D), and the camera as the mouse moving. The action prompt shows the
key's label, the ocarina's notes the light keys. A key the art has no picture of is written on a blank key. The pad
styles draw the same bytes as before.
The prompts description says so in each menu language, and the Chinese one no longer splits words across its lines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Controls tab has a Button prompts row again, with Nintendo, Xbox and PlayStation. prompts = playstation is a new
value next to xbox; nintendo, xbox and pc read as before, and pc stays out of the row as nothing makes its files for
this game. The game reads its prompts at start, so the row says when a restart would change them.
tools/pad_prompts.py writes the overlay folders romfs-patches-xbox, -xbox-label, -playstation and -playstation-label
from the player's RomFS and a folder of button art that stays outside the repository. The button font's glyphs become
outlines traced from the art, the action prompt's letters and the ocarina's note buttons are painted again in their
own BC4 and ASTC formats, and the font's entry in the resource size table grows with it. The by-position folders show
Switch A as B or circle, the by-label ones as A or cross, following the pad layout. The settings probe checks the new
value and folder names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A folder in a mod's RomFS that could not be listed (no permission, or a path longer than 260 characters, as a mod
deep in a long game folder makes) lost its files without a word in the log; the walk now names it. The manifest
asks for long path support, so where Windows has it switched on (LongPathsEnabled) those files are served.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Crashes: the render-target heap (HeapDef DynamicTextureAllocatorMem and its parent) grows with the widescreen
picture, so the pause menu no longer reads a null block at 5120x2160; texture uploads keep the game's memory mapped
while they copy it, so UnmapPhysicalMemory no longer pulls it away mid-copy; DLSS Frame Generation no longer crashes
in Streamline's set-up (physical devices and the window surface go through its proxies, its swapchain images are
counted first, resources are tagged per frame); a stale core-wait target is cleared before its thread is freed.
Freezes and races: a resize during a swapchain rebuild is kept, a failed frame generation swapchain is retried
instead of replaced by a plain one, three acquire timeouts in a row rebuild the swapchain, device waits take the
queue lock, a translator child that stops answering times out, an in-flight compute pipeline builds at normal
priority, a save Commit started before the window closes finishes and none starts after, a settings change just
before closing is saved, and a movie sample taken before a flush is dropped.
HDR: Enhanced HDR extends the game's own tonemap so highlights go above the picture's white (the hue slider,
which no longer applies, is gone from the menu); frame generation gets a HUD-less picture, the HDR flag and the HDR10
metadata under HDR too.
Memory: ASTC textures are encoded to BC3 unless the PC has 20 GiB of VRAM and 48 GiB of RAM, as Windows charges video
memory to the commit charge (a long session held 17 GiB of RGBA8 textures and 45.7 GiB of commit on a 32 GiB PC).
Shaders: the translator lowers spill-mode programs with more than eight uniform register groups in a wider spill
layout, and local loads and stores with a uniform-register address (36029 of the game's 36031 variants translate,
20 failed before).
The launcher release ships the shader tools and AMD's FidelityFX library (make_launcher.py --ffx); windres there gets
the toolchain on PATH.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game used to start after at most 3 s of prewarm, so after a translator, GPU or driver change it met the rest of
its shaders in play and stalled on each one. It now waits for all of it: a warm start is ready in about half a second
and shows nothing, while a cold one shows a "Preparing shaders" screen in the menu's colours and the game's fonts, with
the shader and then the pipeline count and a bar. The shader and pipeline prewarm report their numbers every 100 ms.
The present path takes a frame without a game image for the screen. The RomFS opens on the first host-side read, so
the screen's fonts load before the guest's first file access. NX2NAVI_PREWARM_WAIT_MS now defaults to 0 (no cap). With
no progress for 60 s the game starts anyway and the prewarm finishes in the background.
Measured with an emptied cache: 2,988 shaders translated and 2,399 pipelines built in 9.4 s behind the screen, then
0 ms shader and pipeline waits in Kokiri Forest. Warm: the game starts 0.47 s after launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening and closing the menu, moving the cursor, changing a value, pressing an item, cancelling a dialog and changing tabs play the game's system sounds (Plus menu open and close, cursor, decide, cancel, tab move), read from the user's RomFS at the first use and decoded from BWAV (DSP-ADPCM or PCM16, 48 kHz) in runtime/menu_sound.c. They are mixed over the game's output in the audio callback with up to eight voices started without locks. The Sound tab gets a Menu sounds volume (menu_sound_volume, 0 to 100, default 80, under the output volume). A RomFS without the files stays silent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each file that contains copied code names its copyright holder and license and points at THIRD_PARTY_NOTICES.md; zlib code keeps its full notice and the mark that it was altered. Comments and probe text only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The names are NX2NAVI, NX2navi or nx2navi: the settings prefix (NX2NAVI_*), the ini file
(nx2navi.ini), the shader translator (nx2navi_xlate), the Windows resources, the installer and the docs. The 8-byte
file magics keep their size (NX2NSANS, NX2NSPRG, NX2NSPRP, NX2NPKEY, NX2NRFS1), so caches and the RomFS index from
older builds are not read. cfg.c and the settings pages took the prefix length as 5; it now follows the prefix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NVN2 front end and the SM86 to Maxwell shader lowering (tools/xlate/AmpereLowering.cs, mesh and task stages,
bindless texture pools, BRX jump tables, the NVN2 compute c0 layout) with four translator patches (0017-0020) bring the
opening of the game to 58-60 fps on the Vulkan renderer.
This round fixes the haze: SM86 IPA is lowered as a plain Maxwell IPA.PASS (texture coordinates were corrected for
perspective twice); LDSM, TXD, both F2IP byte forms, HMMA, MOVM and HFMA2.RELU are lowered and spill mode has eight
uniform register groups; conditional rendering keeps the newest counter report per address and treats a report as
pending until its value is in guest memory, so occlusion tests and the depth clears around the depth copy behave; and
the upscaler stand-in decodes the post pass's sRGB picture before writing the linear target. Tensor-core programs keep
the stand-in unless NX2NAVI_NATIVE_TENSOR=1, because their lowering does not match the hardware yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>