Show the middle 16:9 in the save picture on ultrawide #1183
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!1183
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/ultrawide-save-picture"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #952.
What
The pause menu draws the whole picture into the save's 543×306 JPEG through the game's viewport helper. When the window is wider than 16:9, the whole wide picture was squeezed into that texture.
nx2/oot_wide.pygets one more site, main+0x284824. That is the helper'sSetViewportcall, right after its address is loaded and before theblr. At that site the viewport is widened by the picture's ratio over 16:9 and centred, so the middle 16:9 fills the texture, the same view a 16:9 window would save. The scissor still clips to the texture.0, 0, 543, 306viewport, and only past 16:9.hos_wide_squeezeis 0, so the game runs as shipped.How it was tested
8b2e1d3.HOS_ASPECT=2.3889), saving from the pause menu now gives a picture that matches a 16:9 save from the same spot. Before, it was the whole 21:9 picture squeezed into 16:9.tests/test_oot_wide_save_picture.pycompiles the site's C exactly as the lifter places it. It checks 21:9, 32:9 and 2560×1080 against the expected width (within 1 px) and centring. It also checks that a 16:9 window and every other viewport stay as the game set them.1f6fbdb),python -I -B tests/run_all.pygives 132 passed, 0 failed, 17 skipped on Linux.🤖 Generated with Claude Code
Thanks, this is a tidy fix for #952. What I checked:
hos_wide_squeezeis set in host/hos_main.c as(16/9) / (ow/oh)from the real picture size, and stays 0 for 16:9 or anything within 1% of it, so the site is a no-op there, as described.nvnCommandBufferSetViewportreads(int)c->x[1]into a float. The Vulkan replay passes the viewport through unclamped (negative x is valid withinviewportBoundsRange), and the scissor is still clamped to the 543x306 target. The rx/WebGPU path does the viewport in its rescale uniform, so it's fine there too.mov w1, ...would leave. x8 isn't touched, and the instruction word check keeps other game versions out.THUMBstring from the module, not a copy, so it really exercises what the lifter places. No overlap with other open PRs that I could find.One thing to fix:
tests/run_all.py'sARGS, so it runs with--cc cc. CI runsrun_all.py --stricton windows-2022 as well, and there's normally noccthere, so this would likely fail with FileNotFoundError on the Windows job. The other C fixture tests are listed as"tests/test_...": ["--cc", "gcc"]. With that entry the runner also skips the test cleanly (or fails it under --strict) when the compiler is missing. Adding the entry, and/or defaulting togcclike test_text_speed.py and test_input_background.py, should be enough.Nit: the reflowed docstring line in nx2/oot_wide.py (line 6) is about 170 chars now. It might be worth rewrapping it like the lines around it.
@ -0,0 +1,101 @@"""Compile nx2/oot_wide.py's save-picture patch (issue 952) around authored registers.This needs an
ARGSentry in tests/run_all.py (e.g.["--cc", "gcc"]). Otherwiserun_all.py --stricton the windows-2022 job callscc, which normally isn't on PATH there.Thanks for the review. Pushed in
ef362de:tests/run_all.pynow liststests/test_oot_wide_save_picture.pywith["--cc", "gcc"], and the test's own default isgcc, liketest_text_speed.py. Without gcc on PATH the runner now skips it ("not found here: gcc"), or fails it under--strict.nx2/oot_wide.py.Thanks,
ef362decovers it: the test is listed in run_all.py with["--cc", "gcc"]like the other C fixture tests and defaults to gcc itself, and the docstring is rewrapped. LGTM.Moved the
run_all.pyentry in5a03e8b: upstream's newtest_motion.pyline landed where it was, so this PR had a conflict. It merges cleanly with main again.Thanks for sorting the conflict. Just the
run_all.pyentry moved, still--cc gcc. LGTM.