51 Commits
Author SHA1 Message Date
Valiantsin DzerakhandClaude Opus 5.5 1a033a4ec8 Apply the optref guard to 1.2.1 too, the same game code at main+0x1622bec (#1005)
1.2.1 has the identical cbz-then-load sequence, so set optref now patches both versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 15:43:03 +03:00
Valiantsin DzerakhandClaude Opus 5.5 507e27da91 Treat an invalid optional reference at object +0x6b0 as absent on 1.4.3, so the random crash in main+0x1bd0138 stops (#1005)
The game checks the reference only for null, and players' crash dumps show it holding 0xffffffffffffffff after minutes to hours of play. MacNativeGames met the same field holding 0xffff0000ffffffff during a save load. Set optref (TOTK_OPTREF_GUARD, on by default) turns the cbz into a test for null or negative; valid holders are heap addresses below 2^63.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 15:26:26 +03:00
itaskaev2andClaude Opus 5.5 9680b8e6ec Add grass_range: how far around the camera grass grows
The game grows grass blades only inside its near grass map, a 62.5 m square around the camera drawn into 256x256 and
128x128 textures, so on open meadows the grass appears a short way in front of the player. grass_range (percent, 50
to 400, default 100) scales the map's extent, step and both textures together through a new grass patch set (1.2.1
and 1.4.3) that loads them from patch page +128..+144, and grows the HeapDef grass heap and the application pool for
the larger textures. At 100 the set stays off and nothing changes. A row in the Display menu, text in all four menu
languages, and totk.ini.example describe it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 20:13:59 +01:00
Valiantsin DzerakhandClaude Opus 5.5 06811330e7 Extend the climb controller's frame-rate factor above 60 fps, so Link climbs walls and ladders at full speed (#35, #276, #1001)
The climb velocity step (1.4.3 main+0x1f49754, 1.2.1 main+0x1918220) multiplies the requested velocity by Nintendo's
factor for a movement loss that grows with the frame rate. 1.4.3 fits it at 30 and 60 fps and caps it at 2 from about
77 fps (63% climb speed at 120, 34% at 240); 1.2.1 has no 60 fps branch (78% at 60). Below a 1/60 s step the set climb
continues the factor with a curve fitted to a replayed ladder climb, taking the patch page's frame ratio without the
time scale, so 30 and 60 fps keep the shipping factor bit for bit, slow motion included. Only the player's climb
modules reach this code. Its own setting, climb_fix under [frame rate], with a menu toggle; off without the real-time
step. Ladder replay, 1.4.3: 1.665 / 1.663 / 1.669 / 1.681 m/s at 30 / 60 / 120 / 240 (shipping 120 1.046, 240 0.563).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 18:44:18 +03:00
Valiantsin DzerakhandClaude Opus 5.5 b6738cf41b Merge pull request #916: dynamic resolution as a setting, on by default, and a scene past 4096 wide (#573 by ProSep, carried by itaskaev2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 18:20:31 +03:00
Valiantsin DzerakhandClaude Opus 5.5 6433550e3d Scale the 1.2.1 gear rotation threshold by the AI update's frame delta, as 1.4.3 does (#330, #984)
ActorCheckRotateModule's update compared the angle a gear turned since the last update with one 30 Hz frame's worth,
so above 30 fps gears and water wheels read as stopped and the Riogok, Turakamik and Wind Temple doors stayed shut.
The new set gears keeps the update context's +8 (frames elapsed in 30 Hz units, the value 1.4.3 divides by) and
multiplies the threshold by it. Measured: the context is 0.25 at 120 fps with the real-time step and 0.5 at 60 fps
without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 15:35:16 +03:00
ProSepandClaude Opus 5.5 a6c33b072c Hold the game's dynamic resolution at 100 % and let the scene grow past 4096 wide
The game picks a scene tier of 100, 90 or 80 % mostly from a fixed cost model made for the Switch that grows with the
scene width squared, so on a wide output it drops to 80 % on any GPU: at 5120x1440 every frame drew 80 % of the scene.
Patch set fixres replaces the one instruction that moves the tier's scale into the controller's result (1.2.1
main+0xcc2c00, 1.4.3 main+0x5848a8 as MacNativeGames patches it) with 1.0. The new setting dynamic_res (menu toggle,
live) brings the game's stepping back and is on by default in the Steam Deck profile.

The scene cap goes from 4096 to 8192 a side, so render_scale above 80 works on a 32:9 output, and the scene also stays
within a DynamicTextureAllocatorMem under 2 GiB; the log says when it was made smaller. The memory selftest now maps
past the real pool size instead of a fixed 4 GiB, which a larger scene grows past.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 10:55:54 +01:00
null15564 2a937319b4 Restore native camera distance after aiming with direct mouse input 2026-11-07 05:55:00 +07:00
fa94e36549 Store the data writes of mods' ExeFS patch files into main before the game runs
An IPS, IPS32 or pchtxt file for main that writes main's .rodata, .data or .bss was refused as a whole. Such writes
need no lifting: tools/exefs_patches.py now puts them in build-cache/exefs.data, and runtime/mods.c stores them into
the loaded main before any game code runs, as emulators and Atmosphere apply a patch file to the module before its
dynamic linker runs. A DT_RELR word gets main's base added; bytes over words an explicit-addend relocation fills change
nothing and are dropped; a write into the dynamic linker's tables, a lazily bound function pointer or outside main's
image keeps the file unsupported. A mod whose data lines do not all fit the build keeps its code patches off too.

The mods are applied from the bottom one up (mods_exefs_apply), so where two mods write the same bytes the higher one
wins, as it does for files. --probe-mods checks it with two mods writing one byte.

The lifted code reads some of main's bytes at lift time (the jump tables). recomp/gen.py lists those ranges in
<gen>/main/folded.txt, and the setup checks the data writes against them after the lift: a file that writes them
becomes unsupported, since the change would not reach the generated code. Generated C without mods is unchanged.

This is the data part of #790 (ericdallo's ExeFS work), split out on its own; the plugin part follows separately.

Co-Authored-By: itaskaev2 <165683571+itaskaev2@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 15:51:42 +01:00
Valiantsin DzerakhandClaude Opus 5.5 61d0075039 Give game 1.2.1 the direct mouse camera of 1.4.3 (issue #751)
The 72 mouse_direct sites of patches-v143.txt mapped to the 1.2.1 main by enclosing function, each original word
checked: the camera input update, free orbit, Ultrahand and the 16 player camera modes. 1.2.1 keeps some values in
other registers and stack slots (camera input x in s11, orbit angles at sp+0x3c/0x40, Ultrahand x in s9), so the
hooks read a per-version MouseLayout; the 1.4.3 values and behaviour are unchanged.

Where 1.2.1 adds a turn in a shared helper, the degrees go to the store of the helper's result (Aiming's pitch
target, new ROLE_BASE_STORE) or to the turn it is given (Horse yaw). Wakeboard, ShieldSurfing and Tail read the stick
twice (REPEAT on the first read), the dialogue cameras' angle loads are pre-indexed (mov -> add, as ClimbObj).
The player state (ability, bow in hand) is not known on 1.2.1 and reads as not found.

tests/mouse_direct_check.py runs the hooks with both layouts and checks each load-site table against its patch file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:47:26 +03:00
Tempus3214andClaude Opus 5.5 8235ea8d34 Fix the crash when the camera turns vertically while climbing a creature
The ClimbObj pitch hook replaced the pre-indexed load ldr s1, [x22, #0x88]! and wrote the write-back to x22 from the
host call. The lifter keeps X19-X29 in locals across a call and does not reload them, so the write-back was lost and
the following str s0, [x22] wrote the pitch over the camera object's vtable pointer; the next virtual call crashed
(seen climbing a Stone Talus). The mov x22, x19 before the load is now patched to add x22, x19, #0x88, the hook
performs a plain ldr s1, [x22], and load hooks no longer support write-back loads at all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 05:43:25 -03:00
Tempus3214andClaude Opus 5.5 a1cd122625 Give the mouse the same degrees per count in every player camera, and fix the soothe button and the mouse shake
Only the free camera took the mouse as degrees. The other camera modes took it as stick input, which their own speeds,
frame time, slow motion and curves reshaped, with the on-foot camera's square-root pitch shaping on top: bow aiming was
far too sensitive vertically on slow movements, abilities and the Purah Pad camera eased their pitch, lock-on turned
27% slower, and climbing, shield surfing and the rest each turned at their own rate.

Each camera mode's update now loads the gamepad's part of the stick where it computes its turn and adds the mouse's
degrees where it adds that turn to its angle (runtime/mouse_direct.c camera_load, the load sites of
recomp/patches-v143.txt): aiming, scope, abilities (and Recall's cursor), horse, Purah Pad camera, lock-on, bow
lock-on, wall and object climbing, the waterfall's turn, shield surfing, wakeboard and the dialogue cameras. Recall's
cursor moves so that its aim turns as many degrees a count. The waterfall's height and the horse's empty lock-on are
not angles and stay stick input, now linear. A step whose mouse motion no hook took is logged with the camera's name.

Holding L to soothe the horse gave the mouse to the ability wheel's virtual cursor, a held stick for the camera: the
mouse goes to the wheel and the D-pad strips only while their screen has the focus (menu_cursor.c
menu_cursor_shortcut). A mouse shake no longer plays a 0.8 s swing after it: the right stick follows the mouse's own
swings and lets go when it rests, and the camera does not turn by that swing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 03:36:51 -03:00
Valiantsin DzerakhandClaude Opus 5.5 0e1ddb41e0 Scale the 1.2.1 map cursor and vertical aim steps by the frame ratio
The map cursor, the vertical Ultrahand aim and the vertical gyro aim moved a fixed step per frame, so above 30 fps they ran faster with the frame rate (#44, #394). The new set cursor multiplies each step by the game's own 30 Hz ratio, ported from MacNativeGames 363a6eb and switched by TOTK_HIGHFPS_FIX.

Map pan with the same stick input on 1.2.1: 398 px/s at 30 fps, 1150 px/s at about 105 fps before, 402 px/s after. The Ultrahand and gyro sites were not exercised in game. 1.1.0 is not covered: its module is not available here to check the sites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:48:48 +03:00
walterwhite69420 050563687b Add experimental direct mouse camera input for game 1.4.3 2026-11-04 07:55:49 +03:00
Valiantsin DzerakhandClaude Opus 5.5 53d3f8ea2e The 1.2.1 texture loader widens a texture's pool offset unsigned
The loader keeps a texture's place in its pool as a 32-bit word (main+0xad4294) and widens it with a sign extend for
nvnTextureBuilderSetStorage's offset (main+0xad43f0) and for SetPackagedTextureData's source (main+0xad440c). Past
2 GiB the offset turns negative and the storage lands 4 GiB below the pool (MacNativeGames ca311e2d, seen with a 4K
texture pack's heap table in a 3.58 GiB pool). The shipped heap table keeps every pool far below 2 GiB, but
TOTK_APP_POOL raised with such a pack's heap table (RomFS mods replace it) can reach it. The new patch set pooloff
(TOTK_POOL_OFFSET_PATCH, on by default) widens unsigned: the same value below 2 GiB, right up to 4 GiB.

1.4.3 needs no patch: both of its loaders (main+0x583c34, main+0x27ed214) keep the offset in 64 bits (str x25 / mov
x2, x25 / add x1, x25, x8). gen.py accepts both original words, and the lifted C at the sites reads the offset as
uint32_t with the set on and as int32_t with it off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:20:32 +03:00
Valiantsin DzerakhandClaude Sonnet 5.5 526e9f32bd Retry the lift's emit pool with half the workers after a MemoryError, and read the worker count from TOTK_LIFT_JOBS
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 23:27:46 +03:00
Valiantsin DzerakhandClaude Opus 5.5 a68106de5d Merge pull request #371: Call the 1.1.0 update 1.1.0, not 1.1.1 (shenjackyuanjie)
The Brazilian Portuguese and Simplified Chinese setup texts now name the 1.1.0 update beside 1.2.1 and 1.4.3, as the
English and Russian ones do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 17:51:58 +03:00
shenjackanddeepseek-v4.1-flash-1017 a7b988b417 Name the 1.1.0 update 1.1.0 instead of 1.1.1
The update this port builds for has version value 0x20000, which is Tears of the Kingdom 1.1.0, not 1.1.1: the upstream expected-v1.1.2.json carries 0x40000 and the other expected files step 0x10000 per release, so 0x20000 is 1.1.0 and 0x30000 is 1.1.1. Pull request #220 introduced the wrong label in expected-v1.1.1.json.

Expected hashes, patch sites, boot constants and the RomFS layout are unchanged; only names change:
- tools/data/expected-v1.1.1.json -> expected-v1.1.0.json, display_version 1.1.0
- recomp/patches-v111.txt -> patches-v110.txt
- installer/layout.py SUPPORTED and tools/build_game.py DISPLAY_VERSIONS
- make_romfs_patches.py --version choices and its COMMON table key (the RomFS archive name stays Common.Product.110)
- runtime RT_V111 -> RT_V110, the inits_111 and expect_111 tables, the probe's census folder v110
- README, docs and the installer texts

Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
2026-11-01 21:08:13 +08:00
Valiantsin DzerakhandClaude Opus 5.5 5e584ea966 Merge pull request #51: Load Atmosphere cheat files and switch their cheats from a Cheats tab in the menu (conongnang)
Merged onto the mods folder (task/mods1); the Cheats tab sits after Mods. The cheats' menu text and the cheat file's
place are reworked for the mods folder in the commits that follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 13:31:17 +03:00
Valiantsin DzerakhandClaude Opus 5.5 4ab1dc80f5 Merge task/shadows: pull request #89, shadow options (itaskaev2), with the follow-ups
Keeps pull request #321's anisotropic filtering, which #89 added a second time, and #267's shadow_res beside #89's
shadow_scale. The HDR parameters move from byte 1408 of the support buffer to 1440: translator patches 0010 and 0011
read pcss at 1408 and shadow_spread at 1424.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 11:39:38 +03:00
conongnangandClaude Opus 5.5 0bf9a09eb3 Merge upstream main into cheats
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 08:07:29 +07:00
Wellington Dias 1a07a8d46f Fix the sun shadow map at one size, so foliage shadows stop turning blocky and jumping.
Each frame the engine sizes its shadow cascades to the area they cover (main+0xcebdf4 in 1.2.1): from 960 down to about
400 near the ground, so leaf shadows turn coarse, and every change of size moves the shadow texels, so they jump instead
of sliding. The Switch does the same; the community mods force a fixed size. The shadow patch set loads the size from
patch page +92 just before the engine rounds it (main+0xcec22c in 1.2.1, 0x683604 in 1.4.3), and the menu's Shadow map size
(shadow_res in totk.ini) chooses it: game, 512, 768, 1024 (default), 1536 or 2048, with the texture heap grown above 960. A write
watch on the size field found the one writer; with the setting, every shadow map in both versions is the fixed size.
recomp/patches.py lists the patch page words in use.
2026-10-31 15:59:53 -03:00
Valiantsin DzerakhandClaude Opus 5.5 b768567f8d Merge pull request #248: Add a field-of-view setting for 1.4.3 (Lucas-blip-png)
TOTK_FOV (70..140 percent, Screen tab, live) multiplies the camera's vertical view angle through the new patch set fov
at the two camera routines the aspect set patches; page +60 holds the factor. At the default 100 the set is off and the
camera runs the game's own instructions. Both fov sites and the rest of the 1.4.3 table pass patches.check against the
1.4.3 main. 1.2.1 and 1.1.1 have no fov sites and log that the setting does nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 21:14:50 +03:00
Valiantsin DzerakhandClaude Opus 5.5 407c6915b5 Merge pull request #220: Support the 1.1.1 update as a third game version (shenjackyuanjie)
1.1.1 (update 131072) gets its boot constants, expected hashes, the 60 fps
RomFS overlay, its own code patch table, the scene module slot, loader probe
values and shader census lookup, and the setup accepts it. 1.2.1 and 1.4.3
keep every value: the runtime branches on RT_V111, and an install without a
recorded version still falls back to 1.2.1.

1.1.1 has no aspect patch set; outputs other than 16:9 are scaled to the
window on that version.

Fixes #173

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 21:03:32 +03:00
shenjackanddeepseek-v4.1-flash-1017 6d27cf8202 Add the 1.1 code patch table with its spin waits, hair and cloth sites
The 60 fps frame step and ratio, the render resolution, the slow-motion check and the actor animation clock, at the
addresses this build has them: each site was located again by instruction-window matching and its words checked against
the 1.1.1 main. The spin-wait back-edges are the ones that only another thread can end, and the last five sites are the
two inlined clocks of the animation update with the shared call they hand their seconds to.

Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
2026-11-01 01:49:38 +08:00
Lucas-blip-png 459d414c29 Add a field-of-view setting for 1.4.3
The camera update and setup routines that the aspect set patches load the camera's vertical view angle a few words
before the ratio and pass it to sead's perspective projection. The new set fov loads the angle as before and multiplies
it by a factor the runtime writes to patch page +60, so TOTK_FOV (70..140 percent, menu Screen tab, live) widens or
narrows the view. The game's own zooms (bow, scope) scale with it; 100 switches the set off.

Checked on 1.4.3 at 1920x1080 at 70, 100, 130 and 140: the view narrows and widens around Link, the HUD is unchanged,
no objects appear or vanish at the edges, no validation errors. 1.2.1 has no fov sites yet (its original words are
unverified here); the setting logs that and does nothing there.
2026-10-31 11:08:06 -04:00
f482217350 Step the 1.2.1 equipment-switch cooldown until it is no longer positive (#18)
1.2.1 has the same mismatch as 1.4.3 at the same player state field: the
update steps +0x109c only while it is above FLT_EPSILON and the equipment
gate blocks changes while it is above 0. The realtime set now compares with
0 in the update. Ported from the 1.4.3 patch by JieML1100 in issue #18.

Co-Authored-By: JieML1100 <183249798+JieML1100@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 18:00:48 +03:00
a80c78f08d Step the 1.4.3 equipment-switch cooldown until it is no longer positive (#18)
The player update steps the countdown at player state +0x109c only while it
is above FLT_EPSILON, while the equipment gate blocks weapon, shield and bow
changes while it is above 0. With the real-time step a rounding remainder in
(0, FLT_EPSILON] stays there and locks equipment after a Fuse until a load.
The realtime set now steps the countdown while it is above 0. Root cause and
patch by JieML1100 in issue #18.

Co-Authored-By: JieML1100 <183249798+JieML1100@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:59:40 +03:00
Valiantsin DzerakhandClaude Opus 5.5 6a04fe9222 Merge pull request #89: Shadow resolution, cascade range, soft and contact shadows, anisotropic filtering (itaskaev2)
Merged with applezra's follow-ups on the same branch: the sun shadow filter narrowed by the shadow map scale
(translator patch 0011), anisotropic filtering forced only on tiling samplers, and TOTK_DUMP_UBO with a vec4 count.

Conflicts with dev: cfg.c (names, display section, defaults next to aspect and device_profile), menu.cpp (the new rows
after the aspect row), vk_texture.c (the pool alias check takes scale == 1 and dev's bind_pool_memory size argument),
build_xlate.py (patches 0005-0011 are ours; the Linux note kept), totk.ini.example (both sections). The contact pass
release went into gfx_present.c, the shared half of the old gfx_window.c.

aniso defaults to game: the picture stays as before until a player raises it in the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:44:48 +03:00
Valiantsin DzerakhandClaude Opus 5.5 98a657dfd1 Give 1.2.1 the camera aspect ratio and the HUD layouts of other shapes
patches-v121.txt takes the aspect set at upstream's 1.2.1 sites (game_patches.c aspect_install): the same two camera
loads and two viewport divisions as 1.4.3, with the same original words and the same replacement from page +56. The
installer now builds the romfs-patches-<w>x<h> overlays for 1.2.1 too; ultrawide_layout.py passes on its
Common.Product.110 at all ten sizes. Checked in game at 3440x1440 (camera 2.3889) and 1920x1200 (1.6000): wider or
taller view, HUD at the corners or edges, no validation errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 14:23:58 +03:00
Valiantsin DzerakhandClaude Opus 5.5 43a18a9bb1 Merge pull request #60: Linux port (AppImage) and Steam Deck support (wdiasjunior)
The branch forked 79 commits before main and 98 before dev. Resolutions:

1. Aspect ratio: dev's implementation stays (camera patch at page +56 on 1.4.3, tools/ultrawide_layout.py layouts per
   size in romfs-patches-<w>x<h>, the aspect = fit|stretch setting). The PR's second implementation is dropped: the
   aspect sets of patches-v121.txt and patches-v143.txt (the 1.4.3 one patched the same viewport sites as dev's),
   tools/aspect_layouts.py, the resolution.c shapes that limited game resolutions to 16:9, 16:10 and 21:9, and the
   picture_fit setting. Its letterbox code (gfx_fit_rect) is kept and reads TOTK_ASPECT.
2. Code dev changed where the PR moved it: the stick dead zone, the test gamepad and the paddles go to input.c; the
   DPI awareness, keep-awake and IME detach of gfx_window.c go to gfx_window_win.c; device fault reports and GPU
   breadcrumbs to gfx_present.c. gfx_window_dpi gets an SDL version.
3. dev's Win32 calls in shared code go through platform.h: the shader cache pack reader (PlatFile), the romfs
   overlay checks, the audio pacing waits, the motion, ASTC and device fault locks. plat_cpu_usable and
   plat_commit_info get Linux versions; plat_cpu_count counts the machine's processors on both. dpi_probe.c becomes
   dpi_probe_win.c.
4. assume_focus defaults to 1 as the PR has it (a load interrupted by a focus loss can stop for good) and stays live,
   with dev's Game tab row.
5. The frame limits 40 and 45 join dev's list. The BC 3D storage fallback for Vega only runs with
   TOTK_BLOCK_VIEWS_3D=1, since dev makes 3D BC images without block views by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 13:26:42 +03:00
conongnang 44105a95ff Merge branch 'valentderah:main' into cheats 2026-10-31 13:41:59 +07:00
Seth 582f675e88 Render a game resolution wider than 16:9 at its own shape, with black bars as the fallback
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.
2026-10-31 07:43:44 +03:00
Wellington Dias 08df794360 Move the aspect words of the patch page to +80..+91 and list the words in use, so other patch sets can take +56..+79.
The shadow cascade range of a pending change loads its four distances from +64..+79 on 1.4.3, where the aspect and the
canvas factors were; with both on, the camera would have read a cascade distance as its aspect. recomp/patches.py now
lists the words each set uses.
2026-10-30 14:35:23 -03:00
itaskaev2andClaude Opus 5.5 386e70453b Add a shadow cascade range setting for 1.4.3
The sun's shadow cascades end at 20, 40, 80 and 160 m: the depth shadow parameter object (main+0x6fa3c0) registers
depth_shadow_near_0_1..3 and depth_shadow_far_0 as immediates, and the field's .bgsdw files do not override them. The
new cascade patch set reads the four distances from patch page +64..+76 instead, and shadow_range (percent, 50 to 400,
default 100) writes them there at start, so the visible steps where shadows turn softer move away from the player. It
spreads the shadow map over more ground, which shadow_scale pays back. Only patches-v143.txt has the set; on 1.2.1 the
setting logs that and does nothing.

The setting is in totk.ini.example and on the Screen tab of the menu (English and Russian).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:06:58 +01:00
Valiantsin Dzerakh f930e56825 Merge task/fade: floor the 1.4.3 LOD and pre-actor fade increments at 1 2026-10-30 15:30:46 +03:00
Valiantsin DzerakhandClaude Opus 5.5 bb1b675381 Floor the 1.4.3 LOD and pre-actor fade increments at 1
Above about 63.5 fps the fade step trunc(2.1 * ratio) is 0, so objects that start a fade stay translucent or
invisible until a slower frame moves them on (issues #10, #37). Switching to 60 fps and back cleared it because the
60 fps step is 1. Port of MacNativeGames' high-FPS fade fix as a fade set under TOTK_HIGHFPS_FIX.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 15:27:44 +03:00
Valiantsin DzerakhandClaude Opus 5.5 a5711539e1 Size the code generator's worker pool by free memory so it stops running out of RAM
gen.py started one emit worker per CPU, about 1.3 GiB each on 1.4.3, so many-core machines with modest RAM died with a MemoryError during "Translating code to C" (issues 17, 28, probably 66). The default is now min(CPU count, 80% of the free physical memory / 1.5 GiB), at least 1, capped by the memory limit of the enclosing job object if there is one. --jobs still overrides it, and the chosen count and its reason are logged. The memory helper is tools/hostmem.py, shipped with the release tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:43:50 +03:00
conongnangandClaude Opus 5.5 3df82b8842 Load Atmosphere cheat files and switch their cheats from a Cheats tab in the menu
Players bring their own cheats\<build id>.txt next to totk.exe; none ship with the port. The setup turns cheats that
write main's code into patch sets (tools/cheats.py), cheats that write data run in a cheat VM after dmnt's every
presented frame (runtime/cheats.c), and the menu's Cheats tab switches each one, saved under [cheats] in totk.ini.

The patch table now takes several sets per site, first set in file order winning, and replacements that end in a
branch or ret, so cheats that patch the same instruction or end in a jump can be built.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:28:37 +07:00
Wellington Dias aa030e4e28 Give 1.4.3 the aspect patch, so it draws 16:10 and 21:9 like 1.2.1.
The 1.4.3 sites, found by their code: the camera parameter reader's DefaultAspect default (main+0x1aa0ac4, the default
at main+0x2d06910), the two camera-viewport updates (0x1ff04f4, 0x253fbb0, where the division becomes the page load
because x9 is free after the dirty-flag store) and two of the 2D canvas-size loads (0x2136fa8, 0x213641c). Three of
1.2.1's five canvas loads have no match yet; switching them off on 1.2.1 left the gameplay HUD and the system menu
unchanged at 21:9. At 16:10 and 21:9 the 1.4.3 title and gameplay show a round emblem, an unstretched world and the
HUD at the screen's edges.
2026-10-29 23:05:30 -03:00
Wellington Dias f3ab1b0e76 Give the camera and interface the output's shape on 1.2.1, so 16:10 and 21:9 are not stretched.
The aspect patch set (recomp/patches-v121.txt) makes the world camera's DefaultAspect parameter default, the two
camera-viewport updates that divide a 16:9 rectangle, and the five loads of the 2D canvas size read the patch page.
resolution.c writes the output's aspect and the canvas factors there (wider than 16:9 the canvas grows in x, narrower
in y) and switches the set on when the game resolution is not 16:9. The sites are those of Fruithapje21's
TOTK-Ultrawide 1.2.1 patch (GPL-3.0, read for behaviour); a heap scan of the live projections confirmed the four world
cameras take the new aspect. The interface keeps its 16:9 layout in the middle of the canvas: the per-layout edits
that move it to the screen's edges are not done.

A version without the set (1.4.3 for now) draws the largest 16:9 size inside the wanted one, letterboxed. The Steam
Deck profile now draws the screen's own 1280x800, and the menu offers 16:10 and 21:9 game resolutions.
2026-10-29 22:20:59 -03:00
Valiantsin DzerakhandClaude Opus 5.5 c2943970cc Merge the spin wait gates: they also end the fight hangs
The generator emits a guest poll such as `ldr w9, [x19]; cmp w9, #1; b.eq` as a plain C load, and clang hoists it out
of the loop: the lock wait in f_7001fa0e2c compiled to `cmp; jne; jmp .`, so a thread that found the lock held spun
forever after its holder released it. A fight run hung this way in 3 of 13 launches on an RX 9070, with the UBO
snapshot on and off; gdb showed the thread on that `jmp` in every sample. The gate is an opaque call on every
back-edge of the 868 loops tools/spin_waits.py lists, so each pass reloads memory: the exe goes from 182 self-jumps to
148, and 144 of those are the game's own `b .` instructions. 8 of 8 fight runs then passed; tests/diff/func.py with
--patches passed 5470 cases on 878 functions, and the four probes passed on 1.4.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:05:44 +03:00
Valiantsin DzerakhandClaude Opus 5.5 1f4c6fcdb5 Decode the game's zstd frames with the host's libzstd: the patch set zstd (TOTK_ZSTD_NATIVE, on by default) sends the calls of ZSTD_decompressMultiFrame and ZSTD_decompress_usingDDict in main to runtime/ov_zstd.c, which makes host dictionaries from the guest DDicts, gives the virtual core up for inputs of 64 KiB and more, and leaves in-place decodes and every error to the guest decoder
The lifted decoder ran its copy loops as chains of small functions. On the island and fight routes TOTK_ZSTD_CHECK=1 decoded every call both ways with no difference; the host took 0.72 s where the guest decoder took 1.99 s. Load time and fps on an RX 9070 and a 7900X did not change: the gain is CPU time on the loading threads, for machines with fewer cores. The caller at main+0x547a04 stays on the guest decoder: it resets a static DCtx with which the game's zstd rejects some frames libzstd decodes, and the game relies on that result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:27:12 +03:00
Valiantsin DzerakhandClaude Opus 5.5 2fe6a6d4c1 Keep MXCSR in line with FPCR where FPCR changes instead of checking it in every FP op
The FP helpers compared c->fpcr with a cached copy before every operation to resync MXCSR lazily. MXCSR now
follows FPCR.FZ at once: MSR FPCR calls rt_fpcr_sync, and so do rt_call (thread, fiber and host entry into guest
code), fiber resume (FIBER_FLAG_FLOAT_SWITCH fibers keep their own MXCSR) and the differential test harness per
case. op and vop only test FPCR.RMode for the slow path, and their redo check is one ordered-not-equal compare of
|r| with the smallest normal, the same NaN-or-smallest-normal set as before.

Hot guest code shrinks by about a quarter (132 functions holding half the guest cycles, 1.4.3); fight frame time
moves 1-3%, inside the run-to-run spread. lift, simd, func, atomic and flags_check pass on 1.4.3; simd.py fails
without the harness sync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 12:25:25 +03:00
Valiantsin DzerakhandClaude Opus 5.5 0ca11dfba2 Keep the six most used general registers of each generated function in C locals
Every lifted instruction read and wrote c->x[n] and c->sp, so each register passed from one guest instruction to the
next went through a store and a load, and clang could not keep it in a host register across a guest store.
recomp/regs.py now copies the six most used X registers and SP of a function into locals at its entry. A forward pass
over the function's flow tracks which locals changed since the last sync, and only those are stored back before a
call, an SVC, a trap, a return or a tail exit. After an SVC every local reloads; after a call only X0-X18 and X30,
since AAPCS64 has the callee preserve X19-X29 and SP. TotK does not break that: main and subsdk0 import neither
longjmp nor the unwinder, sdk's have no direct callers, and the runtime writes no register above X3 except X30.
gen.py --no-callee-saved-trust reloads them too, --reg-locals sets the count. Functions with an unresolved computed br
keep their registers in the Cpu, as they keep NZCV.

All registers in locals was slower than none: clang spills them to the stack and the syncs stay. Over 300 hot
functions, 4-6 locals gave the fewest instructions and memory operands.

1.4.3 .text 299 -> 266 MiB. Fight bench (bench-fight2-2026-10-27b, frames 5400-7500, uncapped): 11.30 and 11.92 ms vs
13.5-13.9 on main; frames 2400-5300 11.24 and 11.36 vs 13.25. Without the callee-saved trust 12.58 ms.
tests/diff/func.py: 0 failing on 1.4.3 (889 functions, 583 with calls) and 1.2.1 (810); a plan with no spills fails
at once. Probes kernel, vkexec, nvn-tex and shader pass in both versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 22:21:18 +03:00
Valiantsin DzerakhandClaude Opus 5.5 27056b212a Keep NZCV in a local of each generated function instead of c->nzcv
Every flag-setting instruction packed N, Z, C and V into c->nzcv and every reader unpacked them. recomp/flags.py
now runs a reaching-definitions pass per function: a compare stores its operands in a local Flags F, and a reader
whose definitions are all of one kind computes its condition from them, so cmp and b.cond become one host compare.
CCMP, FCMP, MSR NZCV and ADCS write the packed form, which readers reached by several kinds also use. Calls and
returns publish nothing (AAPCS64 neither passes nor preserves NZCV); tail branches, falls into the next function and
br through rt_dispatch publish c->nzcv, since discovery can start a function inside another one. Functions with an
unresolved computed br keep flags in c->nzcv.

1.4.3 main: 492k direct and 89k packed flag reads, .text 342 -> 299 MiB. Fight bench (bench-fight2-2026-10-27b,
frames 5400-7500, uncapped) 13.15 and 13.63 ms vs 13.9 before. tests/flags_check.py checks the new helpers against
the packed ones; tests/diff/func.py no longer compares NZCV, which generated code leaves untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:07:58 +03:00
Valiantsin DzerakhandClaude Opus 5.5 3984344635 Spin wait gates: tools/spin_waits.py (the nxnative search) lists the loops that only another thread can end, the patch tables carry them as spin lines (857 in 1.4.3, 864 in 1.2.1), the generator sends their back-edges through rt_spin_gate, and the gate pauses the core once per pass and hands the virtual core to a waiter after 50 us of polling (TOTK_SPIN, TOTK_SPIN_HANDOFF_US)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 15:51:47 +03:00
Valiantsin DzerakhandClaude Opus 5.5 7abdbc90ac Render the game above 1080p and scale it to the window with FSR 1
The output and scene patch sets make the game read its output, swapchain and scene sizes from the patch page
(MacNativeGames' sites for 1.2.1 and 1.4.3); resolution.c picks them from TOTK_OUTPUT_RES and TOTK_RENDER_SCALE and
grows the HeapDef texture heap, its parent and the application pool to match. vk_fsr.c runs AMD FSR 1 EASU and RCAS
on the present when the window is larger than the game's output (TOTK_UPSCALER, TOTK_SHARPNESS).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:40:34 +03:00
Valiantsin DzerakhandClaude Opus 5.5 cd60d20a7f Boot 1.4.3 to its first NVN call with the 1.4.3 patch table, loader values and SDK 20.5 swkbd layout
recomp/patches-v143.txt ports MacNativeGames' 1.4.3 fps60, real-time step, cloth and bounce sites; the real-time
producer reads the nominal interval from patch page +8, which frame_rate.c now writes as seconds * 60.
make_romfs_patches.py takes --version (Common.Product.140 on 1.4.3).

SDK 20.5 grew KeyboardConfig to 0x518 bytes, so ShowKeyboardArg's work buffer is at +0x518; swkbd_win.c and the
services probe pick the offset by version. 1.4.3's movie::Decoder vtable names the complete-object destructor D1,
which gets the D2 override. The loader probe has the measured 1.4.3 import and relocation counts, and probe-ipc
expects REV15 from the 1.4.3 SDK and skips the 1.2.1 sf heap constructors (1.4.3 creates the audio heap lazily).
A non-1.2.1 build defaults to its own profile, build/profile-<version>: 1.4.3 rewrites the saves it loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:50:40 +03:00
Valiantsin DzerakhandClaude Opus 5.5 776d547169 Load 1.4.3 modules: DT_RELR, symbol count from DT_GNU_HASH, stg as a no-op, version-aware boot anchors and loader probe values
The jump-table check now explains oracle targets for case indices peeled off by cmp #k; b.ne before the table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:27:36 +03:00