29 Commits

Author SHA1 Message Date
chrg6
26d26babe5 Bring public main's bug capture on F8 and its removal of the Steam co-op into the 0.10.0 line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	docs/architecture.md
#	launcher/steamlib.py
#	launcher/ui/app.py
#	runtime/boot.c
#	runtime/cfg.c
#	runtime/flight.c
#	runtime/flight.h
#	runtime/gfx_present.c
#	tests/launcher_test.py
2026-11-13 09:52:43 -05:00
epicmeme546
4fd70e398f Make F8 write a bug capture and add a test run that starts from one.
F8 now always writes a folder under logs/captures: the area, Link's position and rotation, the camera of the frame on screen and the game's own, the presented frame as a PNG, frame times around the press, the machine, the settings that differ from the defaults, the end of the log, the save, and the game's flags and texts. A note on screen names the capture. The flight recorder's F8 dump goes into the capture's folder.

NX2NAVI_CAPTURE_REPLAY starts a run on a capture's settings, teleports to its area, walks Link to where he stood and puts the free camera where the camera was; tools/capture_run.py prepares the profile from the capture's save, runs it and compares the two captures. docs/bug-capture.md has the files and the steps.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-13 06:22:18 -05:00
chrg6
d64a9b689c Move the sprint button's stance host off the water ripples' patch page slot, and fix how presses and timings reach it.
The stance site called page +0xf50, which the 0.10.0 line gives the water ripples above 60 fps: the second
registration ends every 1.1.1 start with "registered twice", the sprint button off included. It calls +0xf30 now, and
the comments say which slots the other sets hold. The set's switch is NX2NAVI_SPRINT_BUTTON_PATCH, not a name another
open request uses for a set of its own.

The stand-still timings count 60 Hz frames (frame_step_frames), so a toggled sprint no longer ends on a short stop at
high frame rates. A press is decided at its start for its whole length: kept from the game and the sprint's when it
begins in gameplay (B: while Link moves), else the game's alone, so a held B is not handed to the game half way and a
B that closes the pause menu starts no sprint. A sprint switched off with the button setting does not come back by
itself, Link's state is read only where the memory is readable, and the row, nx2navi.ini.example and the controls
page say that B is the jump under the game's Swap Jump Controls and name the button without assuming the layout.
tests\sprint_check.py covers the press latch, the menu press, 240 fps and the switch-off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 22:57:56 -05:00
kendo
aab082ff51 Say that the sprint button's B puts the weapon away when Link is not moving, not that it attacks. 2026-11-12 22:55:14 -05:00
kendo
54fa3bc0c7 Offer only L3 and the game's B button as sprint buttons, and name B as the Nintendo layout's bottom button. 2026-11-12 22:55:14 -05:00
kendo
0a4fe144c9 Run the sprint patch set only while a sprint button is chosen, and never switch it on over a start that kept it off. 2026-11-12 22:54:58 -05:00
kendo
6faf4bd7f5 Add a sprint button: a setting that makes Link sprint without rolling first, as a toggle or while held. 2026-11-12 22:54:58 -05:00
chrg6
d9bc002d9a Play the game's own amplitude at the default vibration strength on the HD rumble path.
The default strength is 130 to make up for what two motors lose. A linear actuator that takes the game's value needs none of that, and 1.3 times the amplitude clipped the strongest pulses at the safe maximum. The HD path now divides by the default, so the setting scales from the game's own amplitude.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 22:29:04 -05:00
chrg6
aeb6c0d5c1 Add the HD vibration row to the Controls tab and describe HD rumble in the controls guide.
The row switches rumble_hd and is greyed out, with a note of what it needs, while the gamepad in use is not a Switch
Pro Controller or Joy-Con. Its texts are in all four menu languages.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-12 22:28:36 -05:00
chrg6
ec2e05e3a3 Leave F11 to overlays when Ctrl, Shift or Alt is held with it.
F11 switched fullscreen whatever modifier was held, so an overlay or capture tool's Ctrl+F11 or Shift+F11 shortcut also
switched the game's window. The Win32 window now ignores F11 with Ctrl or Shift held (Alt+F11 already arrives as a
system key it does not handle), and the SDL window with any modifier. docs/controls.md says so.

Checked: the runtime compiles; gfx_window_sdl.c passes a syntax check against the SDL3 headers. Not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 19:38:18 -05:00
chrg6
861357b2a4 Make the direct mouse camera work on game 1.1.1.
mouse_mode = direct is the default, but on 1.1.1 it did nothing: only the 1.4.3 patch table had the sites, so the mouse always went through the stick emulation, which gives no turn at slow mouse speeds and tops out at about 107 degrees a second. The 1.1.1 table now has the set mouse_direct (on by default): at main+0x37c45c, where the camera's input update holds the processed stick in s1 and s0, a host call performs the displaced store and adds the raw mouse counts to both, so the mouse meets no dead zone and no curve; at main+0x37c4f0 a second call clears the idle bit for an update the mouse moved in. The counts are divided by the frames the game steps per update and by the stick camera speed, so the same hand movement turns the same angle at every frame rate. The Controls tab gets the mouse mode and mouse sensitivity rows, and mouse_mode = stick or NX2NAVI_MOUSE_DIRECT_PATCH=0 gives the old behaviour.

Checked here: the generator lifts the 1.1.1 game with the two sites (it compares each site's original word), the lifted game compiles and links with the runtime, and tests/mouse_direct_check.py passes its 1.1.1 harness (register contract, displaced store and load, equal turn at every frame rate and stick speed, the reset pause, capture and mode). Not checked here: the game was not run, so aiming with the mouse was not tried, and the differential tests against Unicorn were not run because no Python on this PC has Unicorn. The contributor measured 0.0800 degrees per count on both axes in the game.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
(cherry picked from commit bb8fecfbe43517006eafd7be9c22e8dbe9601de7)
2026-11-11 02:36:54 -05:00
chrg6
8e19b653f5 Show the walk key's rows on the Controls tab again.
The walk key (walk_key, walk_scale) works and the settings example says the Controls tab sets both, but the tab had no row for either: the row's function was left without a caller, and a player asked for a walk key because he could not find one. Walk key and Walk scale are back under the analog stick rows, with the texts that were already there, and the guide names the setting. The second half of the contributor's change, a toggle mode, is not taken: it is a new feature that nobody has pressed a key against on Windows.

Built; the rows were not looked at in the menu, since no game could be started.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
(cherry picked from commit 02fab940a1bb79d8867f5e764ee597ea8eb662e8)
2026-11-11 00:39:50 -05:00
chrg6
b1d08b06a8 Lay out the action prompt's face buttons as the pad layout has them.
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>
2026-11-10 23:16:17 -05:00
Michael Schienbein
919aa7b2a8 Let the mouse camera in stick mode reach the game's top turn speed at a camera speed above 1.
With the mouse camera in stick mode and a camera speed above 1, the mouse could never turn faster than a full stick does at camera speed 1, however high the mouse sensitivity was set. The mouse's counts were limited to full tilt first and divided by the camera speed afterwards, so at camera speed 1.5 the most the mouse could send was two thirds of full tilt, which the game's own factor brought back to the old top speed. The division now comes before the limit (runtime/input_map.c input_mouse_stick): a slow movement turns as far as before, and a fast flick reaches full tilt, which is the game's top speed with the camera speed in it. Direct mouse mode, the default, does not use this code. A camera speed of 1 or less behaves as before.

From pull request #424. tests/mouse_stick_check.py is new here: it builds input_map.c alone and checks the deflection at each camera speed without the game.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 12:48:50 -05:00
chrg6
75b0dca907 Add Steam Deck button prompts beside the Xbox and PlayStation ones (prompts = steamdeck).
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>
2026-11-10 06:53:30 -05:00
chrg6
19eeb29cda Let tools/run_game.py run a Linux game folder and a save from a crash report.
The scripted-run tool finds the game under each name the build tools give it, counts other copies through
/proc where there is no tasklist, uses the game folder's own profile when it has one, takes a save folder
or zip from elsewhere through the launcher's save search (a single slot replaces only that slot), and
--wall ends a run that never reaches the world clock.

Pull request #310 by mooki000, taken as it is except for its note that game time is not counted on 1.1.1,
which no longer holds. --help runs on Windows; nothing was run on Linux here.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 06:17:42 -05:00
epicmeme546
9ae7764ab5 Change a setting with the mouse only on the two framed arrows beside its value, and switch HDR without a restart.
In the settings menu a click anywhere on a row stepped its setting to the next value, and the left arrow stood left of the value's text, so it moved whenever a value of another width came up: clicking the left arrow through a list landed on the row at the second click and stepped the other way. Each stepper now has its arrows in white frames at fixed places, the left one the width of the row's widest value from the right one, and the mouse changes the value on those frames only; a click on the rest of the row selects it. On and off rows have the same arrows. An arrow at the end of its values is drawn faint and does nothing, and a button held on an arrow repeats as a held key does. A, Enter and left and right work as before.

NX2NAVI_MENU_TEST_CLICK clicks rows by their setting's name during a menu test run. On an RX 7900 XT at 3232x1352 the arrows stepped the performance overlay one value a click and stopped at both ends, and clicks on the body of four rows changed nothing.

HDR between off and the tone mapper now follows the menu. The HDR path is set up at every start (vk_hdr_armed): the swapchain colour space and metadata extensions, the tonemap splice and the settings in the support buffer, with the spliced tonemap returning the game's own SDR result while the swapchain is SDR. A change of the setting makes the swapchain again in the other format. On the same card a run that started with HDR off went to an HDR10 swapchain and back, and one that started with HDR on went to SDR and back, with the picture right in each state. The game's native HDR still waits for a restart, as the game asks about the display once, and the row says so. NX2NAVI_HDR_LIVE = 0 leaves the HDR path out of a run that starts with HDR off.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-09 23:27:02 -05:00
Michael Schienbein
a103673341 Make the SDL window honour monitor, --monitor and --list-monitors.
On Linux the window ignored NX2NAVI_MONITOR (the log said it had no effect) and --list-monitors only printed that monitor selection is Win32-only. The SDL window now picks a monitor with the same rules as the Win32 one: auto (the system places it), primary, a 1-based index in listing order, or part of the monitor's name, ignoring case, the first match in list order winning. The monitors are SDL's displays; --list-monitors prints their index, name (the make and model, with the connector on Wayland and X11), pixel size and position, primary flag and display scale, then what NX2NAVI_MONITOR would pick, and the log says the same when the window opens.

The window is created centred on the chosen monitor and centred again at its final size, and that position also names the display the fullscreen setting covers, which is how a Wayland window, which a client cannot move, opens fullscreen on the chosen output. On Wayland a windowed window opens where the compositor puts it; the log says so once. With a chosen monitor, the size widescreen.c reads for fullscreen before the window exists is that monitor's.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-09 20:51:09 -05:00
FemOrWhat
844940c6fb Add DualShock 3 support on Windows, read straight from the controller when no driver presents it, off by default.
runtime/input_ds3_win.c finds a DualShock 3 through hid.dll and setupapi (loaded at run time), sends it the command
that makes it report, sets its first player light and decodes its reports (runtime/input_ds3.c, tested by
tests/ds3_check) into the pad state, with rumble through its light motor. input.c follows whichever pad was pressed
last when an SDL pad is connected too. ds3 = 1 in nx2navi.ini turns it on: it starts a scan for the controller every
three seconds and opens it read-write, so it stays off unless a player asks for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:21:07 -05:00
Thiago Cabral
d0c8588e3f Open the game window on a chosen monitor: the monitor setting and --monitor pick it by index, primary, or part of its device or monitor name, and --list-monitors prints the monitors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:18:08 -05:00
Thiago Cabral
bb0ac8caeb Play the sound on a chosen output device: the audio_device setting and --audio-device pick it by index or part of its name, --list-audio-devices prints the devices, and a chosen device that goes away hands the output to the default one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:17:35 -05:00
chrg6
8e30ad7927 Carry the button art in releases and draw the button prompts during the game build.
make_launcher.py and make_kit.py take --prompt-art (folders holding XGamepad, P5Gamepad and Keyboard_Mouse, or those
folders) and copy only the pictures pad_prompts.py reads into the release's prompt-art folder; tools/prompt_art.py
holds the art tables and that list, with no imaging packages. make_game.py's new last step, prompts, runs
pad_prompts.py on that art into the game folder's romfs-patches-* folders. Its key names everything it reads (the
RomFS, the art, the tools, the bind_ lines of nx2navi.ini, the folders being there), so it runs again only when one of
those changed, and it makes no other step run. A kit without art skips it. The launcher's Python gets fonttools and
pillow, and its jobs show a Draw the Button Prompts row when the version has art.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:09:26 -05:00
chrg6
9e56d5a316 Add keyboard and mouse button prompts, drawn from the art's keys.
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>
2026-11-09 08:09:26 -05:00
chrg6
276325d9ff Add a PlayStation choice to the button layout.
pad_layout = playstation plays a pad by label as xbox does, named for a PlayStation pad: cross A, circle B, square X,
triangle Y. The prompts follow it like the Xbox layout, by label. Under Automatic the note says PlayStation for a pad
whose bottom button is labelled cross. The layout description in each menu language now says what every choice does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 03:34:46 -05:00
chrg6
612c02311d Add Xbox and PlayStation button prompts, drawn from button art on the player's machine.
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>
2026-11-09 03:34:46 -05:00
chrg6
8702ddb356 Default to the Switch's own button positions on every gamepad, and fix menu edge cases.
pad_layout defaults to nintendo: Switch A on the right face button, B bottom, X top, Y left, on an Xbox pad too, where
auto had picked the Xbox layout. The settings probe checks every button's position.

Menu: the "default" mark follows the Steam Deck profile's values (cfg_default); Left on a value the list does not hold
goes to the last entry; the HDR row says "in use: off" when the swapchain is not HDR10; the open state is read again
after taking the reset, so a menu opened by F1 while the overlay alone was drawn starts with its focus row; the font
atlas is rebuilt only for a scale more than 12% off or one that held for half a second, so dragging the window's edge
no longer rebuilds it every frame.

A shader that fails to translate keeps its request and code in the shader cache. NX2NAVI_GPA_TWIN=0 turns the guest
copy of device address table stores off (diagnosis). The memcpy override no longer prints its first 500 calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 00:34:29 -05:00
chrg6
4943b50ff2 Unlock the frame rate, shorten cold starts, and describe the Ocarina build in the docs.
Ocarina steps its game by the clock (the same frame-numbered walk covers half the distance at 120 fps), so in a build
without the fps60 and realtime patch sets the pacer holds frames to the frame limit alone: any rate from 30 to 1000,
the monitor's rate, or unlimited (164 fps average in Kokiri Forest). The menu always shows the row, with 165, 240 and
Unlimited added. NX2NAVI_PACE_HZ sets the pacer's rate for tests.

A compute pipeline is compiled outside the pipeline lock, and one compile serves the replay and the boot prebuild
alike; the prebuild uses half the CPUs. The guest waits at most NX2NAVI_PREWARM_WAIT_MS (3000) for the boot prewarm,
which then goes on in the background (a cold start waited 9.8 s). NX2NAVI_TEST_PAD_AT attaches the test gamepad late
and presses it, to test a controller switched on in play.

README, docs/architecture.md, docs/controls.md and AGENTS.md describe the Ocarina build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:04:39 -05:00
chrg6
9a5b1163f3 Make every settings menu row work for Ocarina and give the controls Ocarina's actions and keys.
The Screen tab hides Game resolution when the build has no output and scene patch sets, and the resolution code no
longer grows the memory pool for it. FSR sharpness now also runs when the game image is shrunk to the window, which is
always the case for Ocarina's 2560x1440 picture at 1920x1080, and the overlay drops NX2navi's fixed 3D scene size. A colour
profile set in nx2navi.ini now applies without the menu. The Game tab hides the frame limit in builds without frame-rate
patch sets and lists the game languages the RomFS ships, adding Polish and Brazilian Portuguese. 5.1 sound is reported to
the game's virtual output device. The remap page describes each button as Ocarina's controls screen does, gives each
D-pad direction its own description and drops NX2navi's shake row; the keyboard defaults follow Ocarina (Shift dashes on A,
Ctrl puts away on B, O and N for the ocarina and Navi, middle mouse resets the camera). docs/controls.md is updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 05:04:39 -05:00
Valiantsin Dzerakh
0478c13e45 Import the base tree (MIT, see LICENSE) 2026-11-07 05:04:38 -05:00