diff --git a/recomp/patches-v111.txt b/recomp/patches-v111.txt index 1fa3800f..bf4f179b 100644 --- a/recomp/patches-v111.txt +++ b/recomp/patches-v111.txt @@ -4,7 +4,8 @@ # matched against, +116 the ratio that replaces it and +120 the tolerance of the match, as floats; +144 the bit # patterns of the UI's full-screen orthographic projection (m00 = 2 / 3840, m11 = 2 / 2160), +160..+175 and +208..+223 # the four screen vtables that keep it, +176 the UI's horizontal squeeze (16:9 over the picture's ratio) in four lanes, +192 the -# movie's margin on each side as a share of the width. +# movie's margin on each side as a share of the width, +224 what the viewport of the save's picture gets added to its x +# and +228 to its width, as 32-bit integers. # # The matrix-update, UI and movie sites: Copyright (c) 2026 Ivan0xFF (MIT; see THIRD_PARTY_NOTICES.md). Each is # written without branches or flags, as the generator's patch sites need. The set names differ from the other patch @@ -163,6 +164,22 @@ site wide_hud main 774368 ad401041 ad401041 @page:x16 fd404a10 6e040431 6e0c2491 # at x29 - 40: min x +8, max x +16); it moves in by the page's margin on each side, the middle 16:9 of the picture. site wide_movie main 3b5c758 a941a269 d100a3b0 bd400a10 bd401211 1e303a32 @page:x17 bd40c233 1e330a52 1e322a10 1e323a31 bd000a10 bd001211 a941a269 # movie layer viewport: ldp x9, x8, [x19, #0x18] +# The save's picture: the viewport helper (main+0x2846e0) makes whole pixels of a viewport object on its target and +# hands x, y, width and height in w1..w4 to nvnCommandBufferSetScissor (main+0x284804) and then to +# nvnCommandBufferSetViewport (main+0x284828), before it leaves through SetDepthRange; the two procedures' addresses +# are the globals behind main+0x4c8da90 and main+0x4c8e7d8, which main+0x12a1738 onwards fills by name. The picture a +# save file shows is drawn through it into a target of 543x306 (not a constant of main: the size comes with the game's +# data, so the site knows the viewport by its numbers), and the whole picture went in: wider than 16:9, it came out +# squeezed. Where the helper loads SetViewport's address, a viewport of exactly 0, 0, 543, 306 gets the page's +224 +# added to its x and +228 to its width (runtime/widescreen.c wide_init: wider by the picture's ratio over 16:9 and +# centred on the target). The scissor was set before and stays the target's, so the save shows the middle 16:9 of the +# picture, the part a 16:9 picture holds. Without branches or flags: the differences from those four numbers are or-ed +# together, clz >> 5 of that is 1 only when there is none, and madd adds the page's two numbers times it. x9..x12 and +# x16 are free: the call takes x0..x4, and the helper reads none of them after it. The set is on only for a picture +# wider than 16:9. +set wide_save NX2NAVI_PATCH_WIDE_SAVE 0 +site wide_save main 284824 f9400108 f9400108 @page:x16 295c2e0a 51087c69 5104c88c 2a0c0129 2a010129 2a020129 5ac01129 53057d29 1b0a0521 1b0b0d23 # ldr x8, [x8]; ldp w10, w11, [x16, #224]; sub w9, w3, #543; sub w12, w4, #306; orr w9, w9, w12; orr w9, w9, w1; orr w9, w9, w2; clz w9, w9; lsr w9, w9, #5; madd w1, w9, w10, w1; madd w3, w9, w11, w3 + # The game's zstd decoder (BSD zstd, statically linked into main) through the host's libzstd (runtime/ov_zstd.c): the # three calls of ZSTD_decompressMultiFrame (main+0xeb380: dctx, dst, capacity, src, size, dict, dict size, ddict) go to # the host function on the patch page (+0xff0). main+0xe910c is the file loader's call for a file that fits one read diff --git a/runtime/cfg.c b/runtime/cfg.c index 8b8dc1da..53bd67e6 100644 --- a/runtime/cfg.c +++ b/runtime/cfg.c @@ -59,7 +59,7 @@ static const char *const known[] = { "MOTION_GUARD_DEGREES", "MOTION_POLICY_TRACE", "SELECTIVE_MOTION", "SELECTIVE_MOTION_RAW", // motion_owner.c, vk_fg_pose.inc "MVOBJ_ALL", "MVOBJ_BANKS", "MVOBJ_EQUAL", "MVOBJ_EXACT", "MVOBJ_EXEC", "MVOBJ_NOC4", "MVOBJ_ONLY", "MVOBJ_TIMES", "MVPREV_CHECK", "MVPREV_GROW", "MVPREV_SKIP", "MVPREV_TRUST", "MV_CHECK", "MV_SURVEY", - "ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_RES_FIXED", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_FREECAM", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPELINE_CACHE_MAX_MB", "PIPELINE_CACHE_PLAY", "PIPELINE_CACHE_PLAY_MB", "PIPE_BINARIES", "PIPE_BINARIES_MAX_MB", "PIPE_FEEDBACK", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SEED", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT", "POWER_FULL", "TEX_SAME", "TEX_SAME_BIG", "TEX_FLAT3D", "TEX_KEEP_TAKEN", "TEX_STALE_KEPT", "PRESENT_HELD", "HELD_SYNC", + "ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_RES_FIXED", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_FREECAM", "PATCH_WIDE_SAVE", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPELINE_CACHE_MAX_MB", "PIPELINE_CACHE_PLAY", "PIPELINE_CACHE_PLAY_MB", "PIPE_BINARIES", "PIPE_BINARIES_MAX_MB", "PIPE_FEEDBACK", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SEED", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT", "POWER_FULL", "TEX_SAME", "TEX_SAME_BIG", "TEX_FLAT3D", "TEX_KEEP_TAKEN", "TEX_STALE_KEPT", "PRESENT_HELD", "HELD_SYNC", "PREPARE_PIPELINES", "PREWARM_FULL", "PREWARM_WAIT_MS", "PREWARM_XLATE", "PROBE_MOVIE", "PROBE_MOVIE_SHOT", "PROFILE", "PROFILE_CHAINS", "PROFILE_DIR", "PROFILE_FRAMES", "PROFILE_REPLAY", "PROFILE_SLOW", "POOL_FAST", "POOL_GROUP", "POOL_GROUP_CHECK", "POOL_PRESNAP", "POOL_PRESNAP_RUNS", "SUBMIT_FINISH_GPU", "SVC_WAITS", "SNAP_WIDEN", "SUBMIT_FINISH_RESULTS", "SUBMIT_FINISH_KINDS", "FOLIAGE", "MIRROR_STAGE", "FLUSH_SKIP_CLEAN", "MIRROR_HOT", "MESH_EMULATION", "MESH_EMU_MB", "MESH_EMU_TASKS", "MESH_EMU_BATCH", "GUEST_OPT", "LOG_TEX_NEW", "DUMP_KEY", "VEC_INDEX_FIX", "PASS_TIMES", diff --git a/runtime/widescreen.c b/runtime/widescreen.c index 48f3d7e0..c583ded6 100644 --- a/runtime/widescreen.c +++ b/runtime/widescreen.c @@ -6,9 +6,10 @@ // them from the patch page instead (wide_out: the picture, wide_scene: the scene), wide_aspect replaces a 16:9 ratio of // sead's perspective projection (every 16:9 camera of the game) with the picture's, wide_hud keeps the HUD and menus // in their 16:9 shape in the middle of the picture (the UI's full-screen projection squeezed horizontally; fades and -// the menu backgrounds that redraw a capture of the whole picture stay full width), and wide_movie shows pre-rendered -// movies in the same middle 16:9. The heights stay the game's (1440 and 1080), so only the width grows, by the shape's -// ratio to 16:9; the present scales the picture to the window keeping its shape (gfx_present.c, NX2NAVI_ASPECT=fit). +// the menu backgrounds that redraw a capture of the whole picture stay full width), wide_movie shows pre-rendered +// movies in the same middle 16:9, and wide_save draws that middle 16:9 into the picture a save file keeps. The heights +// stay the game's (1440 and 1080), so only the width grows, by the shape's ratio to 16:9; the present scales the +// picture to the window keeping its shape (gfx_present.c, NX2NAVI_ASPECT=fit). // // Setting NX2NAVI_GAME_ASPECT, read at start: "16:9" (the game's own, every set off), "window" (the shape of the window, // or of the monitor in fullscreen), or a fixed shape "21:9" (2560x1080 monitors, 64:27), "32:9". A shape within 1% of @@ -32,6 +33,7 @@ #define GAME_H 1440 #define SCENE_H 1080 #define GAME_DTA_KIB 312320.0 // the texture heap made for that picture (wide_heap_extra_kib) +#define SAVE_W 543 // the width of the target a save's picture is drawn into (save_extra) // The window's size, or the monitor's in fullscreen; 0 when unknown. static int screen_size(int *w, int *h) { @@ -164,9 +166,19 @@ uint64_t wide_heap_extra_kib(void) { return grow > 0 ? (uint64_t)ceil(GAME_DTA_KIB * grow / 1024) * 1024 : 0; } +// The save's picture (recomp/patches-v111.txt, set wide_save): the game draws the whole picture into a target 543 +// pixels wide, which squeezes a picture wider than 16:9. The viewport of that draw is made wider than its target by the +// picture's ratio over 16:9, half of it on each side, so the target, which the scissor keeps to, holds the middle 16:9 +// of the picture. Returns the pixels added for a picture of ow x oh: an even number, so the picture's middle stays in +// the target's middle; 0 for a picture that is not wider than 16:9, taller or not. +static int32_t save_extra(unsigned ow, unsigned oh) { + double wider = (double)ow * GAME_H / ((double)oh * GAME_W); + return wider > 1 ? 2 * (int32_t)lround(SAVE_W * (wider - 1) / 2) : 0; +} + void wide_init(void) { int so = rt_patch_set("wide_out"), ss = rt_patch_set("wide_scene"), sa = rt_patch_set("wide_aspect"), - sh = rt_patch_set("wide_hud"), sm = rt_patch_set("wide_movie"); + sh = rt_patch_set("wide_hud"), sm = rt_patch_set("wide_movie"), sv = rt_patch_set("wide_save"); // The scene's share of the picture is not a size in this set: the game's GPU-time dynamic resolution sets it each // frame (3/4 of the picture at most), and writing other sizes into wide_scene's constant or the 1920x1080 render // sizes left the light target where the controller put it. DLSS's modes fix the share through res_fixed instead @@ -201,14 +213,24 @@ void wide_init(void) { memcpy(page + 176, squeeze4, 16); memcpy(page + 192, &margin, 4); memcpy(page + 208, keep2, 16); + // the save's picture: what its viewport gets added to its x and to its width. The set is not one wide_available + // asks for: C generated before it existed keeps the other sets and squeezes the save's picture as before. + int32_t extra = save_extra(ow, oh), save[2] = {-(extra / 2), extra}; + memcpy(page + 224, save, 8); rt_patch_switch(so, 1); rt_patch_switch(ss, 1); rt_patch_switch(sa, 1); rt_patch_switch(sh, 1); rt_patch_switch(sm, 1); + if (extra > 0) rt_patch_switch(sv, 1); scale_k = oh > GAME_H ? (double)oh / GAME_H : 1.0; rt_log("widescreen: picture %ux%u, scene %ux%u (%.0f%% of it), camera aspect ratio %.4f (16:9 is %.4f), UI and movies " "in the middle %.1f%%", ow, oh, sw, sch, f * 100, ratio, game, squeeze * 100); + if (extra > 0 && sv >= 0) + rt_log("widescreen: a save's picture holds the middle 16:9 (its viewport %d pixels wide on the %d of its target)", + SAVE_W + extra, SAVE_W); + else if (extra > 0) + rt_log("widescreen: the generated C has no set wide_save; a save's picture holds the whole picture, squeezed"); } // The picture the game draws: the widescreen one wide_init set up, else the game's own 2560x1440. DLSS's largest mode