The setup wrote romfs-patches-xbox and romfs-patches-xbox-label for 16:9 only, so prompts = xbox on a 21:9, 32:9,
16:10 or 3:2 screen (the Steam Deck's 1280x800 among them) kept the game resolution at 1920x1080 (cfg.c) or, with
output_res set to the screen's shape, fell back to the Switch prompts. #979 fixed this for prompts = pc; this extends
the same mechanism to both Xbox folders, the shared-files option of #378 by way of #979's --reuse.
- The setup builds romfs-patches-xbox-<w>x<h> and romfs-patches-xbox-label-<w>x<h> for each of ASPECT_SIZES, from
their 16:9 folder with --reuse, as it does for romfs-patches-pc-<w>x<h>.
- romfs.c overlay_init: at a game resolution of another shape any prompts' folder loads from <folder>-<w>x<h> in place
of romfs-patches-<w>x<h>, not only the PC one; without it the Switch prompts stay, and the log names the missing
folder and the command that makes it. romfs_shaped_pc_ready becomes romfs_shaped_prompts_ready(folder, w, h).
- cfg.c: the display-shaped output_res default applies with prompts = xbox when the folder of the pad layout
(romfs-patches-xbox with the Nintendo layout, romfs-patches-xbox-label with any other) is there for that size.
- The menu's note for a missing shaped folder reads "off at this game resolution (no layout made for it)" in every
menu language, since 16:9 is no longer the only shape with prompts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Crash logs ended in fatal errors when the system's commit ran out for a moment: a staging buffer (-2), a guest commit
with the system at 24442 of 24447 MB, image creation (-2). Windows grows a system-managed page file and some drivers
charge device memory to the process commit, so the same request often succeeds a moment later.
oom.c holds the wait nvn_api_cmd.c realloc_wait already used: it requests a release, retries every 10 ms for up to
30 s, logs the first 8 with the commit numbers. rt_oom_request sets a flag that vk_texture_new_submit takes (one atomic
load per submit): it drops up to 256 released textures of the two kinds sweep_pressure drops (overwritten, clean
mirrored), never GPU-written content, and frees the headroom blocks. vk_oom_release does at once what needs no submit:
headroom blocks, finished upload batches and deferred objects.
- memory.c commit: waits and retries without frz_lock between tries; every caller waits, boot ones too.
- vk_staging and the draw-data chunks (vk_exec_draw.c scratch): release, then wait; the open upload batch is not
submitted, its caller still records into it.
- create_tex: release and one retry; no wait under the texture lock, the texture is asked for again at its next use.
- the texture write-back buffer: release, then wait.
- the headroom thread keeps no spare block for 30 s after a failure.
TOTK_TEST_OOM=<site>:<n> (commit, staging, image, chunk) fails the first n attempts; probe-kernel forces 3 refused
commits, probe-vkexec 2 staging buffers, 1 image and 1 chunk, and both pass through them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup wrote romfs-patches-pc for 16:9 only, so prompts = pc on a 21:9, 32:9, 16:10 or 3:2 screen kept the game
resolution at 1920x1080 (cfg.c) or, with output_res set to the screen's shape, fell back to the Switch prompts.
- make_romfs_patches.py --reuse <folder>: a file of an earlier overlay whose contents match is hard-linked (copied
where links fail) instead of compressed again; the output is byte for byte the same.
- The setup builds romfs-patches-pc-<w>x<h> for each of ASPECT_SIZES from romfs-patches-pc with --reuse: only the
Common archive is compressed, so the fifteen folders take about two minutes and 6 MiB each instead of 22 minutes
and 73 MiB each.
- cfg.c: the display-shaped output_res default applies with prompts = pc when romfs-patches-pc-<w>x<h> is there
(romfs_shaped_pc_ready); Xbox prompts keep 1920x1080 as before.
On an Intel CPU with P-cores and E-cores a thread on an E-core runs at about 60% of a P-core's speed, and the frame
waits for its slowest critical thread. The game's main thread, the thread that presents, and the vk replay, recorder
and present threads now select the CPU sets of the P-core logical processors the process affinity allows. Nothing is
selected on a CPU with one efficiency class or when the affinity leaves only one kind of core. TOTK_PCORE=0 turns it
off; "power:" log lines say what was selected and which threads took it. LAS Recomp does the same for its game,
render and vsync threads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
df9ba65 keeps a content mod's heap sizes when its table is the one served, that
is when the mod is above the texture pack. With the pack on top (HDPacks.com's
4kTextures sorts first), the pack's table was still swapped for the game's own,
and The Second Quest's larger RSDB and UI heaps were lost: the game crashed at
start writing to address 0 in main+0x1d4200. heapdef_overlay now grows the
table of the highest switched-on mod that changes other heaps only, and falls
back to the game's own table when no mod has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
heapdef_overlay replaced the served mod table whenever any switched-on mod grew the texture heap. With an HD pack and
another mod that ships a HeapDef for other heaps on top of it, the other mod's sizes were lost. It now checks the
served table itself (resolution.c res_heapdef_tex_grown): only a table with TextureHeap over the shipped size is
swapped, any other stays the base and gets the growth on top.
A mod HeapDef the port cannot read no longer logs "TextureHeap 0 KiB": the failure is logged with the mod's name.
read_original sat between read_served and its comment; each function has its own comment again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HD texture packs made for the emulators ship their own HeapDef with TextureHeap and its parent grown for the
emulators' 8 GB memory option (HDPacks.com's 4K pack: by 3,419,000 KiB). Served as it is, the game asked for room the
application pool does not have and crashed at start (#673); without the file it looped the panic Blood Moon.
Before mem_init sizes the pool, resolution.c reads the HeapDef of every switched-on mod (mods.c mods_peek_romfs, which
lists the mods' RomFS roots without the rest of the scan: mods_scan needs paths main sets later) and counts the largest
TextureHeap growth over the shipped 506,880 KiB like TOTK_TEX_HEAP_EXTRA_MB (the larger of the two, at most 4 GiB). The
pool and the heap cap grow by it, and romfs.c heapdef_overlay serves the game's own table grown by that amount in the
mod's place, so the other heaps keep the sizes of the game version that runs.
Tested with the HDPacks.com 4K Texture Pack v1.0 as players install it, on 1.4.3 and 1.2.1 (RTX 4080 SUPER): the game
loads a save and runs at 60 fps with 0 validation errors, and holds 4.5 GB of game memory pools against 1.2 GB without
the pack. docs/mods.md, README.md and totk.ini.example say so; other packs come with no guarantee.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HD texture packs made for emulators with 8 GB of DRAM, such as the HDPacks 4K pack, grow "TextureHeap" in their own
HeapDef table. Served as a mod, that table replaces the one the port grows for the resolution and the shadow map, and
the game crashes at boot; without it the pack's textures do not fit, and the game loads and then starts its panic
Blood Moon over and over.
tex_heap_extra_mb ([mods], 0 to 4096 MiB, read at start) grows TextureHeap and its parent ゲームグラフィックス in the
port's HeapDef, and the application pool and the heap cap by the same amount. LOW_SIZE goes from 6 to 10 GiB so the
stack and heap block holds the largest heap. At 0 the pool, the heap cap and the HeapDef are what they were.
The selftest's "beyond the pool" MapPhysicalMemory asked for 4 GiB, which a grown pool holds; it now asks for just
past the pool.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A --seconds run (benches, menu shots, input scripts) reads the total so the Game tab shows it, but starts no counting
thread. The file is now read once at start instead of on the thread. A playtime.txt with a UTF-8 byte order mark reads
as valid, and a damaged one is kept whole as playtime.txt.bad instead of its first 63 bytes. --probe-playtime checks the
parser, a missing, valid and damaged file, the 30 s save and the 2 s cap of one tick.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host thread adds each second in which the window has the focus and the game presents frames, keeps the total in
<profile>/playtime.txt (every 30 s and at exit) and the Game tab of the settings menu shows it with this run's share.
Texts in English, Russian, Brazilian Portuguese and Simplified Chinese. Closes#720.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A game resolution that is not 16:9 widened the camera while the HUD stayed at 16:9 stretched across the picture whenever romfs-patches-<w>x<h> was not next to romfs-index.bin, which is always so for a build from source. resolution.c now falls back to the largest 16:9 size inside the asked one and keeps the camera aspect patch off, logging the command that makes the layout. build_game.py copies every romfs-patches* folder with a manifest.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The map detection reads every open screen of eui::ScreenMgr every 100 ms, and each pointer it follows was checked
with plat_mem_readable, a VirtualQuery. That query costs tens of microseconds in this process, so each scan stalled
the game thread for milliseconds: on a Ryzen 5 4500 the mean frame rate fell from 59 to 50-54 fps and the 1% low from
44 to 23 fps. mem_guest_readable answers from the layout for the committed heap and the loaded modules, where the
game's objects and globals live, and asks the OS only for anything else. With it the same scene runs at 58-60 fps,
1% low 45-48 fps, as without the map detection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts in cfg.c (the key list) and main.c (the usage line) resolved by keeping dev's lines and adding the
Discord keys and --probe-discord.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.4.3 sets its render size once, at start (main+0x7000e67028 calls
main+0x585bd0(renderer, force)). The function-entry hook catches that call's
renderer, and a TOTK_DLSS change writes the new scene size to the patch page and
has the next nvnQueuePresentTexture call the setup again with force set, on the
game's thread between frames, with the guest registers kept. The heap is sized
for the largest scene a mode asks for. The DLSS texture bias is part of the
sampler cache key and follows the mode and the dlaa switch. The game's scaling
pass is told from the in-place FXAA by its missing depth attachment, not by the
sizes, which change a frame ahead of the drawing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host thread speaks Discord's documented IPC protocol to a running client
through platform.h's new plat_ipc_* (a named pipe on Windows, a Unix socket
in $DISCORD_IPC_PATH, $XDG_RUNTIME_DIR, $TMPDIR or /tmp on Linux) and shows
the game update and the elapsed play time as the status of a configurable
application. It never holds a virtual core, picks up settings changes within
half a second of the menu writing them, re-sends the activity once a minute
as its keep-alive, answers the client's Ping frames and reconnects 15 s
after a break; Discord clears the status when the process ends.
The Menu tab of the settings menu switches discord_rpc, and discord_app_id
and discord_image in totk.ini name the application and its art asset (a
player can register their own). The default app id is the one this was
developed against; the maintainer may want to register a project-owned one
and swap it. --probe-discord runs the exchange without the game: it caught
the opcode numbering (Handshake is 0, not 1) against a live client.
Tested on 1.4.3, Linux: probe passed end to end against Vesktop's arRPC
bridge, an invalid app id falls back to the default, selftest clean.
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>
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>
One folder per mod as emulators take them: romfs, exefs and cheats folders, the Atmosphere layout and TKMM's output
(romfslite counts as romfs). A mod with its own code (subsdk or a replaced main) stays off as a whole, so does one with
ExeFS patches until a rebuild applies them; cheat files are listed, not loaded yet. The scan goes through the platform
layer, so Linux builds it. probe-mods covers the layouts and the order keys; probe-cfg fails on a key listed twice in
the defaults table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sampler keeps both the DLSS mip bias and pull request #321's anisotropic level; DLAA's pass hook runs after the
contact shadows'; the extension arrays grow for NGX's extensions beside HDR's and the calibrated timestamps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reverts dafc35f: free_cores = 1 in totk.ini lifts the virtual core gate again, as in 0.2.8. The mode stays as an
experiment, and every place a player meets it says the game crashes: totk.ini.example, a WARNING line in the start log,
and a line under every crash report and FATAL while the gate is off (kern_free_cores_note), so a log from #212 names
the cause at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
frame_rate.c finds the player through a global pointer in main's data segment: 1.4.3 keeps it at
main+0x3add238, 1.2.1 at main+0x4728538, and 1.1.1 was borrowing 1.2.1's address, which points at
something else in that build.
The 1.1.1 slot is main+0x4630a68. It was measured from the build itself: one function there walks
the whole chain this code walks (module+0x1e8 scene, the 14-component check at +0x50, component 13
at +0x58, the 0x70 link stride, the name compare) and 181 scene accesses read the same slot, which
makes it the scene module and not one of the single-instance pointers next to it.
Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
The build knows the player's update as v111: DISPLAY_VERSIONS maps it for the version line of the settings menu, and
boot.c carries its two checkpoint return addresses (0xeb8c14 for SetFocusHandlingMode, 0xebcdec for the first
nvnBootstrapLoader call) beside the 1.2.1 and 1.4.3 ones, with init_array counts of 10668, 45 and 116 for main, subsdk0
and sdk. The build guide lists v111 among the versions every step takes.
Co-authored-by: deepseek-v4.1-flash-1017 <noreply@deepseek.com>
Conflicts: the known-key and section lists in cfg.c keep both sides; vk_backend.c, vk_device.c and vk_exec_draw.c include platform.h and vk_dlaa.h; create_instance takes the window's instance extensions from gfx_window_vk_extensions as on main, in a 12-entry array for NGX's.
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.
The perf line's "bound" said cpu at 640x360 on a Ryzen 5 4600G (Vega 7, RADV) and also at 1600x900, where lowering the
resolution showed the GPU held the frame back; the GPU busy time alone cannot tell the two apart. With
VK_KHR/EXT_calibrated_timestamps the GPU timestamps of each part go on the CPU clock (QPC on Windows, CLOCK_MONOTONIC
elsewhere), each part records when it was handed to vkQueueSubmit2, and a third perf line splits the gap before each
timed part: submitted after the previous part ended (the GPU waited for the replay or the game) or already queued
(untimed uploads and presents, or a wait). It also gives the gaps over 0.5 ms, the longest one and the mean time from
submit to start.
On the 4600G at 640x360 (44 fps) it reads 11.3 ms idle a frame, 10.3 ms of it waiting for the next submission, in 3-4
gaps of up to 8 ms, with the replay at 16 ms a frame: the GPU starves behind the replay. Without the extension, or when
the driver refuses the domain pair, the line is left out. Only TOTK_TRACE=perf and the full overlay time the GPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
TOTK_DLSS (dlaa|quality|balanced|performance, read at start) sets the scene to
67, 58 or 50 % of the output. DLSS then runs at the begin of the game's scaling
pass (the FXAA shader drawing the light target into the output-sized target,
before the HUD) and its result replaces that draw; the HUD is drawn afterwards
and never jittered or passed through DLSS. The jitter now ends where the scene
ends (the bloom), separately from where DLSS runs. Scaled modes add a mip bias
of log2(scale) to repeating, mipmapped material samplers.
With a scene smaller than the output, fxaa = 0 no longer skips the game's
scaling draw (it left the 3D picture out).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.2.1 has no camera aspect patch, so the installer no longer builds romfs-patches-<w>x<h> for it (the layout tool is checked against 1.4.3 and could fail the whole install), and romfs.c overlay_init skips that overlay on 1.2.1 through res_wants_wide_overlay. overlay_init and res_init now both call res_is_16x9 (1% tolerance), so 1366x768 counts as 16:9 and logs no missing overlay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
The game's five camera sensitivities (Options, Very Low to Very High) turn the camera at the rates
in the StickSensitivity array of PlayerCamera.game__component__PlayerCameraParam.bgyml, 0.8 to 2.4.
stick_sens (0.1 to 3, read at start) multiplies that array: camera_speed.c decompresses
Pack/Actor/PlayerCamera.pack.zs with the RomFS's pack.zsdic, scales the floats in place and
compresses it again with the same dictionary, and romfs.c serves the result from
<profile>/camera/. Scaling the stick's deflection instead could not raise the top speed, since a
full tilt cannot go further.
The mouse camera reaches the game as a right-stick deflection, so input_win.c mouse_camera divides
it by the same factor and mouse_sens alone keeps setting the mouse's speed.
romfs.c heapdef_overlay is split into read_served and serve_generated so both generated files
share the reading, writing and overlay bookkeeping.
The game lays its interface out in a 1920x1080 space and draws it over the whole output, so at 1280x800 every menu and
the HUD were 11 % taller than drawn, and at 21:9 34 % wider. tools/aspect_layouts.py rewrites the layout archive for
each shape: every layout's root pane scaled back, full-screen backgrounds kept full, the HUD moved to the screen's
edges, and the minimap, place name and dragon's tears map icon animations adjusted. It follows the behaviour of
Fruithapje21's TOTK-Ultrawide (GPL-3.0, read for behaviour only) and checks every original value before it writes.
make_romfs_patches.py writes the results to romfs-patches/16x10 and 21x9 at install and build time, and romfs.c serves
the one of the output's shape. resolution.c accepts 16:9, 16:10 and 21:9; another shape draws 16:9 with bars. The
menu lists the game resolutions by shape. Checked on 1.2.1 and 1.4.3 at 1280x800 and 3440x1440: HUD, minimap,
inventory, map, system tab and loading screen.
gfx_fit_rect rounded the picture up to an even size, so at 1167x656 a 16:9 picture was 1166 wide and the last column
stayed black, which failed --probe-vkexec; it now takes the window's parity, which keeps the bars equal at any size.
The present check of --probe-vkexec expects the picture's rectangle and black bars, as --probe-window does, so it
passes on a 16:10 screen too. --probe-shader, the draw part of --probe-vkexec and the pipeline self-test looked only
for build/v121/shaders; probe_census_dir takes the running version's census first, then the other one's. The Linux
notes say what the probes need over SSH.
The manifest declared no DPI awareness, so Windows ran the process DPI-unaware: on a 4K monitor at 150% scale the
fullscreen window and its swapchain were 2560x1440, the 4K game image was shrunk to it and Windows stretched the
result back up, blurred (issues #25, #56, #31).
The manifest now declares PerMonitorV2 (true/pm for older Windows 10). The window frame is measured at the window's
DPI, a framed window keeps its client size when it moves to a monitor at another scale, WM_DPICHANGED takes the
suggested rect or keeps fullscreen over the monitor, and window_size means screen pixels. The menu's minimum text
size follows the window's DPI, and the text entry dialog is measured at the game window's DPI. --probe-dpi prints
the process awareness and fails unless it is per-monitor v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shared runtime code now goes through runtime/platform/platform.h, implemented by platform/win32.c and a new
platform/linux.c: futex locks, a userfaultfd and PAGEMAP_SCAN write watch, signal-based async preemption, our own
fiber switch, and a SIGSEGV handler for the thaw and crash hooks. The window, input, menu and software keyboard get
SDL3 back ends beside the Win32 ones, with the swapchain (gfx_present.c) and input merging (input.c) shared. SHA-256
replaces BCrypt, ELF builds get their own override sections, and the build tools gain Linux branches.
On Linux the selftest and the gfx-abi, astc, nvn and window probes pass. probe-vkmem still fails: imported guest
memory cannot be write-watched on Linux, which the shadow buffers in vk_memory.c will address.
A player who starts totk.exe from Explorer or a shortcut had no log to attach to a report: the log went only to stderr.
A game run whose stderr is not a file (a console or a pipe) now also writes logs\totk.log, keeps the four earlier
runs as totk.1.log to totk.4.log, and stops the file at 64 MiB. Runs whose stderr is a file (run_game.py, 2> file) and
the tool modes write no extra file. Lines from before the file opens (mem_init comes first so the CRT heap stays out of
the guest address space) wait in a static buffer.
Crash reports, fatal errors, IPC and SVC diagnostics, guest debug output and the boot: lines now go through
rt_logf / rt_log_raw, so they reach the file too. Open log folder in the menu opens the folder of the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>