From d2c38794494c3f979533876b9504006f15a59cf9 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:09:13 +0100 Subject: [PATCH 01/21] docs: say that a build deletes its build files unless HOOKSHOT_KEEP_BUILD_FILES=1 Since 55abe59 make_game.py and the builder delete the translated code and the compiled objects once the game is ready, so an update translates and compiles the whole game again. The readme still said the builder keeps them and an update compiles only what changed. Co-Authored-By: Claude Opus 5.5 --- readme.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 896d57c9..9c6f5b01 100644 --- a/readme.md +++ b/readme.md @@ -125,7 +125,8 @@ update. When a new release is out, the launcher says "Update available". Press **Update**: in `hookshot.exe` on Windows, or in `hookshot` (or the AppImage) on Linux. It downloads only the parts that changed and builds the game again in the same -folder, compiling only what changed. Your saves and settings are kept. What changed is on the +folder. The build files of the last build are gone by then (see [Where the files go](#where-the-files-go)), so an +update takes about as long as the first build. Your saves and settings are kept. What changed is on the [Releases page](https://github.com/NotRexed/Hookshot/releases).
@@ -153,9 +154,10 @@ with `--update `. - The build reads your game and never changes it. It writes to the folder you choose and to the builder's own folder: `%LOCALAPPDATA%\HookshotBuilder` on Windows, `~/.cache/hookshot-builder` on Linux. - The romfs is hard-linked instead of copied where the disk allows it. -- The builder keeps its build files (about 4 GB) so that updates are quick. None of them are needed to play: on - Windows, **Free up space** on the "Your game is ready" page deletes them, and the build files folder can be moved - before a build (**Change**, **Default**). +- The build files (about 4 GB: the translated code and the compiled objects) are deleted once the game is ready. None + of them are needed to play. To keep them, so that an update compiles only what changed, set the environment variable + `HOOKSHOT_KEEP_BUILD_FILES=1` for the builder; on Windows, **Free up space** on the "Your game is ready" page then + deletes them when you want. The build files folder can be moved before a build (**Change**, **Default**). - Saves: the `user` folder in the game folder on Windows, `~/.local/share/hookshot` on Linux (a `user` folder from an earlier Linux build is moved there once). `--user ` puts them somewhere else ([command line arguments](#running-the-game-from-the-command-line)). From bbf849de3012aecc35ff5af76acb5ecbadc4b474 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:09:22 +0100 Subject: [PATCH 02/21] docs: say that frame pacing works only with frame generation on opts.pace is read only by the frame generation display thread (gpu/vk/vk_framegen.cpp display_main); without frame generation do_present shows each frame as it comes. Co-Authored-By: Claude Opus 5.5 --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 9c6f5b01..5e7f948f 100644 --- a/readme.md +++ b/readme.md @@ -258,7 +258,8 @@ choices and starts the game again, so progress since your last save is lost. `hookshot_log.txt` lists every card as `vk: device `; `RX_VK_DEVICE=` chooses one for a single run and wins over the menu. - **Frame generation** makes extra frames between the game's own. It adds a little input delay, and fast motion can - show artifacts. **Frame pacing** shows frames at an even interval: less stutter, up to one frame of delay. + show artifacts. **Frame pacing** shows frames at an even interval: less stutter, up to one frame of delay. It paces + the frames that frame generation shows, so it has no effect while Frame generation is Off. - **The scaling rows**, in the picture's order: **Output resolution** is the size of the game's whole frame, HUD included. The **Upscaler** fills it from the 3D: the game's own network, or NVIDIA DLSS or DLAA (the 3D at full size, edges smoothed). Both look softer than the game's own: set **Sharpness** to around 80% with them. **DLSS model** From dbc519dc62f4fc9106d27e82b531b5cd9bbf981e Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:09:33 +0100 Subject: [PATCH 03/21] docs: document the mouse and the triggers in the in-game menu menu_tick (gpu/vk/vk_menu.cpp) takes clicks, slider drags, the wheel and a right click as Esc while the menu is open, and the triggers switch tabs as LB and RB do. The readme listed only the keys and the controller buttons. Co-Authored-By: Claude Opus 5.5 --- readme.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 5e7f948f..cc43a9d2 100644 --- a/readme.md +++ b/readme.md @@ -203,10 +203,12 @@ use and cause stalls when the card runs out of headroom. To undo it, remove that Press Insert (or F1) in game, or on a controller the Home button (or Select and Start together). The same keys close it, and so does Esc. The menu has three tabs, Graphics, Gameplay and Controls: Q and E (or Tab and Shift+Tab) switch between them, LB and RB on -a controller. Up and Down choose a row, Left and Right change it -(Enter changes it too); under the rows, the help says what the row is and, on a line of its own, what the -value chosen does. On a controller the D-pad chooses and changes, the bottom face button changes too, and the right -one closes the menu. Your choices are saved in `hookshot_options.ini` in the game folder. +a controller (or the triggers). Up and Down choose a row, Left and Right +change it (Enter changes it too); under the rows, the help says what the row is and, on a line of its own, +what the value chosen does. On a controller the D-pad chooses and changes, the bottom face button changes too, and the +right one closes the menu. The mouse is free while the menu is open: click a tab, click the left or right half of a +value to lower or raise it, drag a slider, turn the wheel over a value to change it, and right-click to go back. Your +choices are saved in `hookshot_options.ini` in the game folder. The rows marked "after a restart" below are read when the game starts. While one is changed, its value says "(after restart)" and the foot of the Graphics tab lists it; closing the menu then asks whether to restart now. **Later** keeps From 30f6b6b06d2116b587efe2379c193bc055b92314 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:09:42 +0100 Subject: [PATCH 04/21] docs: say that mouse_sens.txt is written only with HOS_MOUSE_SENS set host/nn_hid.c keeps the mouse sensitivity in the Insert menu's row (hookshot_options.ini) and moves an old mouse_sens.txt into it; the file in the user folder is written only when HOS_MOUSE_SENS overrides the menu. Co-Authored-By: Claude Opus 5.5 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index cc43a9d2..3c9379ee 100644 --- a/readme.md +++ b/readme.md @@ -436,7 +436,7 @@ use them. |---|---| | `image.bin` | The game image to run. Without it the game uses the `image.bin`, `romfs` and `user` in its folder | | `--romfs ` | The extracted RomFS folder | -| `--user ` | Where the game keeps per-user data: saves, caches, temp files and `mouse_sens.txt`. Default: `user` in the game folder on Windows, `~/.local/share/hookshot` on Linux; with an explicit image path, `user` in the current folder | +| `--user ` | Where the game keeps per-user data: saves, caches and temp files (and `mouse_sens.txt` when `HOS_MOUSE_SENS` is set; the menu keeps the sensitivity otherwise). Default: `user` in the game folder on Windows, `~/.local/share/hookshot` on Linux; with an explicit image path, `user` in the current folder | | `--trace` | Log every Horizon OS call (the same as `HOS_TRACE=1`) | | `--prepare-shaders` | Prepare the shaders and exit without starting the game (the build's last step) | From b05a136a27b6080b868f437913922fb9902bfdfc Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:09:55 +0100 Subject: [PATCH 05/21] docs: say that only the Linux AppImage needs an AVX2 CPU nx2/build.py builds the game and the Vulkan back end for the CPU it runs on (nx2/host_cpu.py: x86-64-v3, else v2 or the baseline, issue 363). Only the AppImage's prebuilt back end is built for x86-64-v3, and make_game.py check_cpu stops there on a CPU without AVX2. Co-Authored-By: Claude Opus 5.5 --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3c9379ee..78e9258d 100644 --- a/readme.md +++ b/readme.md @@ -75,7 +75,8 @@ you start the game from its launcher, which also updates it. - **Your copy of the game at the supported version:** program `0400486024DEC000`, update v131072, build `e0f3ccfa`. Other versions do not work. A `.nup` file, or the `romfs` and `exefs` folders. - 64-bit Windows 10 or 11, or 64-bit Linux with glibc 2.35 or newer (Ubuntu 22.04, Debian 12, Fedora, Arch, - SteamOS ...) and a CPU with AVX2. + SteamOS ...) and a CPU with AVX2. The Linux AppImage needs AVX2; on Windows, and in a build from source, a CPU + without it gets a game built for what it has, a little slower. - A graphics card with Vulkan 1.3 and mesh shaders, with an up-to-date driver: NVIDIA GeForce RTX 20 series or newer, AMD Radeon RX 6000 series or newer, or Intel Arc. On Linux, Mesa or NVIDIA's driver. Cards without mesh shaders are not supported. From 1cb97a2d0dfa974aeba8bbeb3f2051da795d2c13 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:10:06 +0100 Subject: [PATCH 06/21] docs: a new test needs no workflow edit, tests/run_all.py finds it .github/workflows/source-regressions.yml has no paths lists and no step per test any more: one step runs tests/run_all.py --strict, which collects every tests/test_*.py and nx2/test_*.py (its ARGS and SKIP tables take a test's tools and the reasons it cannot run in CI). The job's limit is 20 minutes, not 10. CONTRIBUTING.md contradicted itself on this. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eef9a591..58e2ca7b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -187,10 +187,12 @@ gigabytes and are derived from the game. - Add a game-free test where the behavior can be tested without the dump. The existing tests are small Python files under `tests/` or `nx2/` that read source files and run them, or compile a tiny C++ fixture. -- Wire the test into `.github/workflows/source-regressions.yml`: add a `run:` step, and add the test file and every - source file it reads to both the `push` and `pull_request` `paths` lists. Without the paths, the check does not run - when the code changes. -- Keep the test fast enough for the 10-minute CI job, and make it pass on both Ubuntu and Windows where it can. +- Name it `tests/test_*.py` or `nx2/test_*.py`: `tests/run_all.py` picks it up by itself and CI runs it on every pull + request, so there is no workflow to edit. A test that takes a compiler, headers or `spirv-val` as arguments gets an + entry in `ARGS` at the top of `tests/run_all.py`; one that cannot run in CI (a GPU, the game, one OS only) gets an + entry in `SKIP`, with the reason. +- Keep the test fast: every check shares one CI job with a 20-minute limit. Make it pass on both Ubuntu and Windows + where it can. - If the fix is in the renderer or the runtime, say what you saw before and after, on which GPU and driver, and in which part of the game. From dbb260cade3b1d8c1e64572ecf461ffa92a15d0a Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:10:26 +0100 Subject: [PATCH 07/21] docs: take the development tools from the installed builder, not HookshotBuilder.zip Releases carry HookshotBuilder.exe (the setup) and hookshot-setup.txt; the setup unpacks the builder and its tools into %LOCALAPPDATA%\HookshotBuilder\versions\\HookshotBuilder, as the readme already says. The third_party row now names every prebuilt file the readme lists, and the wgpu-native row says what is lost without it (build.py builds without the WebGPU fallback, host/rx_nowgpu.c). Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58e2ca7b..57cd1792 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,13 +64,15 @@ about 10 GB of RAM. folder. Without it the build falls back to emsdk's clang at `C:\emsdk` with Visual Studio's C++ tools and the Windows SDK (`HOOKSHOT_MSVC=1` forces that route). -3. Copy the pieces a clone does not carry from the `tools` folder of the latest `HookshotBuilder.zip`: +3. Copy the pieces a clone does not carry from the `tools` folder Hookshot Builder installs. Run `HookshotBuilder.exe` + from the latest release once (releases no longer carry a `HookshotBuilder.zip`): the folder is + `%LOCALAPPDATA%\HookshotBuilder\versions\\HookshotBuilder\tools`. - | From the zip | To | Variable | + | From `tools` | To | Variable | |---|---|---| | `tools\cpm` | anywhere | `RX_CPM` points at it (Vulkan headers and FFmpeg; without it the Vulkan renderer is left out) | - | `tools\wgpu-native` | anywhere | `WGPU_NATIVE` points at it | - | `tools\third_party\sdl2\SDL2.dll` | `third_party\sdl2\` | | + | `tools\wgpu-native` | anywhere | `WGPU_NATIVE` points at it (the WebGPU fallback renderer; without it the game is built without one) | + | `tools\third_party` | `third_party\` | (its `.dll`, `.exe`, `.lib` and `.sha1` files, to the same places: `a64emit`, `lld-link`, `zbic` and SDL2) | | `tools\work` | `work\` | (hot-function list, link order, profile and icon; without it the game builds but runs slower) | 4. Run everything with `python -I` from the repository root. From 26ffffb74333610b180ea1a8e1ad5dfab2459ea3 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:10:35 +0100 Subject: [PATCH 08/21] docs: point the hardware testing row at the readme's status table CONTRIBUTING.md still said the project had been tested on one Windows PC and one Linux PC; the readme's status table has reports from many PCs, NVIDIA the most tested. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 57cd1792..4a34cfa6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ game data, leave it out. | | What | |---|---| -| **Test on other hardware** | The project has been tested on one Windows PC (Ryzen 5 7600X, RTX 4060 Ti) and one Linux PC so far. Reports from AMD and Intel cards, other NVIDIA drivers, other distributions and SteamOS are useful even when everything works. | +| **Test on other hardware** | NVIDIA cards are the most tested; the README's [status table](readme.md#project-status) says what works where. Reports from AMD and Intel cards, other NVIDIA drivers, other distributions and SteamOS are useful even when everything works. | | **Play further** | The tested area is the start of the game. Reaching new areas, cutscenes, menus and items finds problems the developer has not seen. | | **Report what is broken** | Use the [issue forms](https://github.com/NotRexed/Hookshot/issues/new/choose): game problem, build problem or idea. Include the version, your hardware, where you were and `hookshot_log.txt`. | | **Send a fix** | Fork, fix, test, open a pull request. Small, focused changes are easiest to review and merge. | From f8c527dbcd6381410a68fef62e8bf601357ff1ea Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:10:57 +0100 Subject: [PATCH 09/21] docs: the stack sampler runs only with HOS_PROFILE set hos_prof_start (host/mkw_glue.c, host/posix/prof.c) starts the HOS_SAMPLE thread only when HOS_PROFILE= is set too; with HOS_SAMPLE and HOS_SAMPLE_FILE alone, as the docs said, nothing is sampled. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 2 +- docs/development.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a34cfa6..4aeadee9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -143,7 +143,7 @@ window, including one started to update a game, uses Automatic again. | `python -I nx2/lift.py --standalone --write` | Lift the game to C in `work/sa/gen`. | | `python -I nx2/build.py --standalone --jobs 4` | Compile `work/sa/build/hookshot.exe` and `hookshot_vk.dll`. | | `powershell -File scripts/run-vk.ps1 -Window -Seconds 120` | Run the standalone build with the Vulkan back end and take screenshots in `work/runs//shots`. | -| `python -I scripts/prof.py ` | Read a profile sampled with `HOS_SAMPLE` and `HOS_SAMPLE_FILE`. | +| `python -I scripts/prof.py ` | Read a profile sampled with `HOS_PROFILE`, `HOS_SAMPLE` and `HOS_SAMPLE_FILE`. | Use a separate `--work` folder or `OOT_WORK` if you keep more than one dump around. The lifted C and every build are local artifacts; they are never committed. diff --git a/docs/development.md b/docs/development.md index 4bf6fe61..2b1b33e3 100644 --- a/docs/development.md +++ b/docs/development.md @@ -31,7 +31,7 @@ Run everything with `python -I` from this folder. | `python -I nx2/test_leaf.py [--count 3000 --states 4] [--calls]` | random whole functions (or call trees), Unicorn vs recompiled native code on the real image | | `python -I nx2/lift.py --standalone --write` then `python -I nx2/build.py --standalone --jobs 4` | the standalone game: `work/sa/build/hookshot.exe` + `hookshot_vk.dll` | | `powershell -File scripts/run-vk.ps1 [-Window] [-Seconds 120] [-Name x]` | a muted test run on Vulkan, with screenshots in `work/runs//shots` | -| `python -I scripts/prof.py ` | a profile sampled with `HOS_SAMPLE=` + `HOS_SAMPLE_FILE=`, named from the linker maps | +| `python -I scripts/prof.py ` | a profile sampled with `HOS_PROFILE=` + `HOS_SAMPLE=` + `HOS_SAMPLE_FILE=` (the sampler runs only with `HOS_PROFILE` set), named from the linker maps | The dump can also come from `OOT_DUMP` (its folder) or `OOT_EXEFS`; `OOT_WORK` moves the work folder. From 6cc2e6fa89cc39d2bb0eeb9d12b5aef8e5d2deef Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:11:38 +0100 Subject: [PATCH 10/21] docs: bring the development guide's introduction and layout up to date - Windows and Linux, not Windows only; the Linux file names. - The status line said the standalone build was next; the game plays. - The link to DESIGN.md is broken on a case-sensitive host: the file is docs/design.md. - The compiler is llvm-mingw first (HOOKSHOT_MINGW or llvm-mingw in the clone), and the CPM cache is RX_CPM, then cpm in the clone, before the old mkw-recomp and Eglantine caches (nx2/build.py cpm_dirs, nx2/make_game.py bundled_mingw). - The layout table had no builder/, tests/ or host/posix, called a64-kit unmodified (four of its files carry (hookshot) changes) and left out third_party/sdl2 and stb. Co-Authored-By: Claude Opus 5.5 --- docs/development.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/docs/development.md b/docs/development.md index 2b1b33e3..3a7bc117 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,9 +1,9 @@ # Hookshot (oot-recomp) -**Hookshot** is the port's name (as Skybound is TOTKPort's and BlueShell is mkw-recomp's); the repository folder -stays `oot-recomp`. The game builds as `hookshot.exe` + `hookshot_vk.dll`. +**Hookshot** is the port's name (as Skybound is TOTKPort's and BlueShell is mkw-recomp's); the maintainer's repository +folder stays `oot-recomp`. The game builds as `hookshot.exe` + `hookshot_vk.dll` (`hookshot` + `hookshot_vk.so` on Linux). -A static recompilation of the Switch 2 *The Legend of Zelda: Ocarina of Time* remake to native x86-64 Windows: +A static recompilation of the Switch 2 *The Legend of Zelda: Ocarina of Time* remake to native x86-64 Windows and Linux: the game's code lifted to C (NotRexed's a64-kit, Rexcomp's AArch64 kit), the Horizon OS and NVN2 implemented natively, the GPU work replayed on Vulkan. It is a fork of mkw-recomp (`..\mkw-recomp`, Mario Kart World), whose paths it keeps so MKW's fixes apply here. @@ -11,13 +11,16 @@ paths it keeps so MKW's fixes apply here. **No game files are in this repository.** The dump is read where it lies (`work/dump.txt`); everything made from it (modules, image, lifted C, builds, runs) goes to `work/`, which git ignores. -Status: the dump is read and lifted (100 % translated, the instruction test passes); the standalone build is next. Plan, stages and the lessons carried over from MKW: [DESIGN.md](DESIGN.md). +Status: the game plays; the README's [status table](../readme.md#project-status) says how far. The design, its stages and +the lessons carried over from MKW: [design.md](design.md). ## Use -Requirements: Python 3 with `lz4`, `numpy`, `capstone`, `unicorn` (2.1), `zstandard`; emsdk's clang at `C:\emsdk` (or `EMSDK`); -for the Vulkan back end, Eden's CPM package cache (found in mkw-recomp's `work/eg` or `..\Eglantine\.cache\cpm`, or -`RX_CPM`). +Requirements: Python 3 with `lz4`, `numpy`, `capstone`, `unicorn` (2.1), `zstandard`; a compiler: on Windows llvm-mingw +(`HOOKSHOT_MINGW`, or an `llvm-mingw` folder in the clone), else emsdk's clang at `C:\emsdk` (or `EMSDK`) with Visual +Studio's C++ tools; on Linux clang and lld. For the Vulkan back end, a CPM package cache with the Vulkan and SPIR-V +headers, VMA and FFmpeg: `RX_CPM`, else `cpm` in the clone, else the old caches in mkw-recomp's `work/eg` or +`..\Eglantine\.cache\cpm`. [CONTRIBUTING.md](../CONTRIBUTING.md#development-setup) says where each comes from. Run everything with `python -I` from this folder. @@ -52,13 +55,15 @@ the script's existing exclusive interval is active; it is not a focus override. | Path | What | |---|---| -| `DESIGN.md` | why mkw-recomp's pipeline, the architecture, MKW's lessons as rules, stages, testing | -| `nx2/` | the lift pipeline and its tests (mkw-recomp's, made game-neutral; `kit.py` has the paths) | -| `host/` | the standalone Horizon runtime (TOTKPort's `hos/` as adapted by mkw-recomp), with the native NVN2 layer `nvn_native.c` | -| `gpu/vk/` | the Vulkan back end (`hookshot_vk.dll`), with Eden's shader recompiler and sirit | -| `gpu/render/`, `gpu/third_party/` | the WebGPU renderer the exe falls back to, its op stream (`rx_nvn.h`), suyu's decoders | -| `scripts/` | test runs and the profile reader | -| `third_party/` | a64-kit (unmodified), libopus, zbic (Atmosphere's zstd with ZBIC, for unpacking only) | +| `docs/design.md` | why mkw-recomp's pipeline, the architecture, MKW's lessons as rules, stages, testing | +| `nx2/` | the lift pipeline and its tests (mkw-recomp's, made game-neutral; `kit.py` has the paths), and `make_game.py`, which the builders run | +| `host/` | the standalone Horizon runtime (TOTKPort's `hos/` as adapted by mkw-recomp), with the native NVN2 layer `nvn_native.c`; `host/posix` has the Win32 calls on Linux | +| `gpu/vk/` | the Vulkan back end (`hookshot_vk.dll`, `hookshot_vk.so`), with Eden's shader recompiler and sirit | +| `gpu/render/`, `gpu/third_party/` | the WebGPU renderer the Windows exe falls back to when it was built with wgpu-native, its op stream (`rx_nvn.h`), suyu's decoders | +| `builder/` | Hookshot Builder: the Windows app (.NET 8 WPF), its setup and MSI, and the Linux builder and launcher scripts | +| `scripts/` | test runs, the profile and log readers, the save copier (`oot_saves.py`), release tooling, `scripts/linux` (dependencies, AppImage) | +| `tests/` | the game-free checks, all run by `tests/run_all.py` | +| `third_party/` | a64-kit (Hookshot's few changes marked `(hookshot)`), libopus, zbic (Atmosphere's zstd with ZBIC, for unpacking only), SDL2's licence, stb_truetype | License: GPL-3.0-or-later, as mkw-recomp: a64-kit's translator is suyu's (GPL-2.0-or-later) and Eden's code is GPL-3.0. From 7eeddfdb921d78574482969005df2458583af94e Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:14:54 +0100 Subject: [PATCH 11/21] docs: a reference of the switches for testing and debugging The development guide named a handful of switches in passing. It now has tables of the ones a contributor needs, checked against where each is read: running a test (headless, window, audio, the options a run can force), scripted input and its press format (host/nn_hid.c parse_presses), screenshots, frame logs and frame dumps, the CPU and GPU profilers and the scripts that read them, the renderer's debugging switches, and the build's. The paragraph on HOS_INPUT_EXCLUSIVE now says what it does: the script alone until 5 s after its last press, on for any value. Co-Authored-By: Claude Opus 5.5 --- docs/development.md | 108 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/docs/development.md b/docs/development.md index 3a7bc117..a58fbc20 100644 --- a/docs/development.md +++ b/docs/development.md @@ -48,8 +48,112 @@ and responds to the supplied controller input. Physical keyboard and mouse input stays with the foreground window. Physical controllers can also play in the background when the existing "Controller in background" option or `HOS_INPUT_BACKGROUND=1` enables them; -`HOS_INPUT_BACKGROUND=0` overrides that option. `HOS_INPUT_EXCLUSIVE=1` controls merging the player's devices while -the script's existing exclusive interval is active; it is not a focus override. +`HOS_INPUT_BACKGROUND=0` overrides that option. With `HOS_INPUT_EXCLUSIVE` set (to any value, `0` too) the script +alone drives the game until 5 s after its last press; without it the player's devices add to the script's presses. It +is not a focus override. + +## Switches for testing and debugging + +The running game reads its switches from the environment or from `hookshot_env.txt` beside `image.bin` (one +`NAME=VALUE` a line, `#` for notes; `HOS_ENV_FILE=` reads that file instead). The build reads its own from the +environment. The README has the ones players are asked to try (`RX_VK_ASTC_RGBA8`, `RX_VK_LOW_SPEC`, +`RX_VK_MOVIE_UPSCALE`, `RX_VK_SKIP_CODE`, `HOS_OOT_CHEATS`). There are several hundred more, most of them one-off +experiments: `grep -rn 'getenv("' host gpu/vk` lists them, and the comment where each is read says what it does. +"Set" below means any value, `0` included. + +### Running a test + +| Switch | What it does | +|---|---| +| `HOS_HEADLESS` (set) | No window: the renderer draws offscreen, for screenshots and frame dumps | +| `HOS_WINDOW_SIZE=x` | The window's size in pixels (1280x720 by default, wider for an ultrawide picture) | +| `HOS_WINDOW_NOACTIVATE` (set) | The window opens behind the others and does not take the focus (Windows) | +| `HOS_FULLSCREEN=1` | Starts in fullscreen; the Insert menu's Fullscreen row is then left alone | +| `HOS_AUDIO=0` | No sound device is opened (the mixer still runs); `scripts/run-vk.ps1` sets it unless `-Sound` | +| `HOS_LANGUAGE=` | The game's language for this run, a code from `language.txt` | +| `HOS_OUTPUT_RES=<0..3>`, `HOS_HANDHELD=1`, `HOS_ASPECT=` | Over the options file: the output resolution (0 2048x1152, 1 2560x1440, 2 3840x2160, 3 1920x1080), the handheld profile, a picture wider than 16:9 (2.3889 for 3440x1440) | +| `HOS_DYNRES=0`, `HOS_REALTIME_STEP=0`, `HOS_TEXT_SPEED=<0..3>` | Over the options file: Resolution 100% (non-zero: Auto), Game speed Per frame (1: Real time), Text speed | +| `RX_VK_FRAMEGEN=0`, `RX_VK_FRAMEGEN_MULT=<2..8>`, `RX_VK_PACE=0`, `RX_VK_UPSCALE=<0..2>` | No frame generation whatever the menu says; over the options file: its multiplier, frame pacing, the upscaling filter | +| `RX_VK_DEVICE=` | The graphics card listed as `vk: device ` in the log, over the menu's GPU device | +| `HOOKSHOT_NO_UPDATE_CHECK=1` | No look for a newer release at the start | +| `HOS_TRACE=1` | Every Horizon call logged, as `--trace` does | +| `RX_BACKEND=` | Unset or `vulkan`: `hookshot_vk.dll`; any other value but `eden`: the WebGPU renderer, when the exe was built with one | + +### Input for scripted runs + +| Switch | What it does | +|---|---| +| `HOS_INPUT_SCRIPT=` | Presses timed from the game's first controller read | +| `HOS_INPUT_LIVE=` | A file the test appends lines of presses to, read every 30 ms; each line's times count from when it is read. A line `!cheat ` sends a Cheats page action, `!synclog` logs the NVN syncs (`!dump`, `!shots`, `!rdoc` and `!flasharm` are the WebGPU renderer's) | +| `HOS_INPUT_FRAME_CLOCK=1` | The script's time runs at 60 game frames a second instead of the wall clock | +| `HOS_INPUT_EXCLUSIVE`, `HOS_INPUT_BACKGROUND`, `HOS_INPUT_SCRIPT_BACKGROUND` (set) | As above; the last lets every device count while the window is behind another one | +| `RX_VK_MENU_SCRIPT=:,...` | Timed Insert menu keys: `INS`, `UP`, `DOWN`, `LEFT`, `RIGHT`, `ESC`, `ENTER`, `TABL`, `TABR` | + +The presses are `:
## How it works From bb4e410eb0134bac0eea55ffee6f1a7df5d17b25 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:17:39 +0100 Subject: [PATCH 18/21] docs: the mesh shader fallback is back since v2.0.6 The status table said the fallback for cards without mesh shaders had been taken out again. 07773ab relanded it (first in v2.0.6): gpu/vk/vk_main.cpp turns it on for a GPU with no VK_EXT_mesh_shader and with buffer device addresses (RX_VK_NO_MESH_FALLBACK=1: off), and vk_draw.cpp fallback_record leaves out the mesh draws with a count buffer. Whether such cards count as supported is left as the table had it. Co-Authored-By: Claude Opus 5.5 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4e04c687..ecf51982 100644 --- a/readme.md +++ b/readme.md @@ -62,7 +62,7 @@ the table says what works and what does not yet. | NVIDIA cards | ✅ Works. The most tested | | AMD cards | ⚠️ Some tested: reports from the RX 6600, RX 7800 XT and RX 7900 XTX. Known issue: the game loses the GPU in the Deku Tree on the RX 6600 ([#708](https://github.com/NotRexed/Hookshot/issues/708), [#614](https://github.com/NotRexed/Hookshot/issues/614)) | | Intel cards | ❓ Few reports, many of them black screens or crashes ([#17](https://github.com/NotRexed/Hookshot/issues/17)) | -| Cards without mesh shaders (such as AMD RX 5000 and older) | ❌ Not supported: parts of the picture are missing. A fallback was tried and taken out again for now ([#640](https://github.com/NotRexed/Hookshot/issues/640)) | +| Cards without mesh shaders (such as AMD RX 5000 and older) | ❌ Not supported: parts of the picture are missing. Since v2.0.6 a fallback runs the mesh programs as compute shaders on cards with buffer device addresses ([#640](https://github.com/NotRexed/Hookshot/issues/640)); it leaves out the mesh draws that take their count from a buffer | | Linux | ✅ Works with the AppImage. Tested on several distributions (CachyOS, Fedora, Bazzite, SteamOS) and cards, with open problems on some AMD cards ([#604](https://github.com/NotRexed/Hookshot/issues/604), [#528](https://github.com/NotRexed/Hookshot/issues/528)) | ## Install and play From 76d407662589ecb9fc9a8108ac2b0a0ffa3bc8bc Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:18:18 +0100 Subject: [PATCH 19/21] docs: --prepare-shaders is not a build step The argument table called --prepare-shaders the build's last step. Neither nx2/make_game.py nor either builder runs it, and none did in this repository's history: a build ends with the shader list (nx2/shader_census.py) and the shipped pipeline lists, and the game prepares its shaders at its first start (gpu/vk/vk_main.cpp started_up). Co-Authored-By: Claude Opus 5.5 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ecf51982..51f37d22 100644 --- a/readme.md +++ b/readme.md @@ -439,7 +439,7 @@ use them. | `--romfs ` | The extracted RomFS folder | | `--user ` | Where the game keeps per-user data: saves, caches and temp files (and `mouse_sens.txt` when `HOS_MOUSE_SENS` is set; the menu keeps the sensitivity otherwise). Default: `user` in the game folder on Windows, `~/.local/share/hookshot` on Linux; with an explicit image path, `user` in the current folder | | `--trace` | Log every Horizon OS call (the same as `HOS_TRACE=1`) | -| `--prepare-shaders` | Prepare the shaders and exit without starting the game (the build's last step) | +| `--prepare-shaders` | Prepare the shaders as the first start does, then exit without starting the game (no build step runs it) | For example, keep saves somewhere else: From aa96077f0baf833164496b584d5f7ad3bf01efff Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:24:19 +0100 Subject: [PATCH 20/21] docs: the movies keep their neural upscale only on NVIDIA cards gpu/vk/vk_shader.cpp leaves a cooperative-matrix program out of translation when its key is not in RX_VK_NETWORK_KEEP (the gameplay TAAU and one more by default) and the card is not NVIDIA's or multiplies matrices in software; the movie's last pass then becomes the linear upscale (vk_draw.cpp upscale_stub) whatever RX_VK_MOVIE_UPSCALE says. The readme described the automatic fallback as if it applied everywhere. Co-Authored-By: Claude Opus 5.5 --- readme.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 51f37d22..1c4f942e 100644 --- a/readme.md +++ b/readme.md @@ -520,8 +520,10 @@ frame generation and upscaling off. It applies only when there is no `hookshot_o the settings in the in-game menu. `RX_VK_MOVIE_UPSCALE=linear` replaces the movies' neural upscale with the plain linear one. The default, `auto`, keeps -the networks and falls back only when a movie frame takes the GPU more than 40 ms. It does not change the gameplay -upscaler, and movies can look softer. `RX_VK_NETWORK_KEEP=all` takes precedence and keeps the movie networks. +the networks and falls back only when a movie frame takes the GPU more than 40 ms. That is on NVIDIA cards: on other +cards, or where the GPU cannot multiply the networks' matrices itself, the movies always get the linear upscale. It does +not change the gameplay upscaler, and movies can look softer. `RX_VK_NETWORK_KEEP=all` takes precedence and keeps the +movie networks. `RX_VK_SKIP_CODE=` leaves out the draws of every program with a stage whose native code key starts with one of the prefixes (the keys the log prints after "its code:"). On a Steam Deck, `RX_VK_SKIP_CODE=2c56dc1006942f74` From a99610cb9cbd1324484f24c6391455bf311712b5 Mon Sep 17 00:00:00 2001 From: itaskaev2 <165683571+itaskaev2@users.noreply.github.com> Date: Mon, 9 Nov 2026 20:28:19 +0100 Subject: [PATCH 21/21] docs: the write-back diff is on again, and the DLSS and Reflex switches v2.0.11 turned the write-back diff back on, so the reference names RX_VK_WB_DIFF=0 as the switch. The NVIDIA DLSS and Reflex switches that arrived with #841 point at the readme's NVIDIA DLSS section, with the two diagnostics it does not list (RX_VK_DLSS_TRACE, RX_VK_DLSS_DEBUG). Co-Authored-By: Claude Opus 5.5 --- docs/development.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/development.md b/docs/development.md index 36b3e691..18a06dc5 100644 --- a/docs/development.md +++ b/docs/development.md @@ -135,7 +135,8 @@ case-sensitive, and an unknown one is ignored. | `RX_VK_MIRROR=0`, `RX_VK_MIRROR_MB=` | No VRAM mirrors of the game's memory pools (on for discrete cards); their budget | | `RX_VK_VRAM_MB=` | The texture cache's budget (default 70% of what the driver allows, at most 6 GB) | | `RX_VK_NO_WRITEBACK` (set) | GPU writes to mirrored pools are not copied back to guest memory (an experiment, for A/B) | -| `RX_VK_WB_DIFF=1` | Write-backs copy only what changed, against a VRAM shadow (off since v2.0.9, issue 1161) | +| `RX_VK_WB_DIFF=0` | Write-backs copy every byte again instead of only what changed against a VRAM shadow (the diff is on by default; v2.0.9 and v2.0.10 had it off, issue 1161) | +| `RX_VK_DLSS=0/1/2`, `RX_VK_DLSS_PRESET=`, `RX_VK_DLSS_LOG=1`, `RX_VK_REFLEX`, `RX_VK_REFLEX_LOG=1` | NVIDIA DLSS and Reflex for one run: the readme's NVIDIA DLSS section. `RX_VK_DLSS_TRACE=` logs one frame's large draws every that many frames, `RX_VK_DLSS_DEBUG=motion` shows the motion vectors, `=alternate` the game's own upscale every other frame | | `RX_VK_ASYNC=1` | The replay on a thread of its own instead of the game's submitting thread | | `HOS_VCORES=0`, `HOS_CORES=<1..8>`, `HOS_RESERVE_CORE=0` | No virtual cores; the cores the game sees (6); no core kept for the replay (one is, with 8 or more logical processors) |