Files
NX2navi/runtime/patches.c
chrg6 750034b7fd Run the water and floor surface simulations at their own 60 steps a second above 60 fps on 1.1.1.
The physics' two water managers and the game's interactive floor simulation (the Deku Tree's ground webs) step their
surfaces in fixed steps of 1/60 s, as many whole steps as the frame's time holds, and drop the fraction, so every
frame shorter than 1/60 s ran none and the surfaces stood still above 60 fps. The new set owater (water_steps, on by
default) calls the page between the division and the game's cut, which keeps the fraction per object; at 60 fps the
count is the game's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-13 03:42:40 -05:00

100 lines
3.8 KiB
C

// Code patch sets (recomp/patches.py, gen.py --patches). The generator lifts both the original and the replacement
// instructions of every patched site, and rt_patch_on[set] picks one when the site runs, so a set is switched at start
// without touching code, and also while the game runs (rt_patch_switch: the frame-limit setting of the menu). The sets,
// their environment switches and defaults, and the patch page come from the generated patches.h; without it (C
// generated without --patches) there are no sets and every site runs the original.
//
// The patch page is the first 4 KiB page after main's image, inside the module window memory.c reserved: replacement
// code reaches it with one adrp. Its
// layout belongs to the sets that use it (frame_rate.c: +0 the 30 Hz ratio, +4 the frame step in seconds, +8 the
// step in 60 Hz ticks).
// Probe modes never call rt_patches_init: every set stays off.
#include "cfg.h"
#include "platform/platform.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "host.h"
#if __has_include("patches.h")
#include "patches.h"
#endif
#ifndef RT_PATCH_COUNT
#define RT_PATCH_COUNT 0
#endif
uint8_t rt_patch_on[RT_PATCH_COUNT > 0 ? RT_PATCH_COUNT : 1]; // read by the generated C at every patched site
// The debug menu's free camera (debug_tools.c dbg_freecam_host): the site of the set freecam calls the page's +0xf70.
// Registered here, in the file that has the game version's page address.
#ifdef RT_PATCH_PAGE
#include "debug_tools.h"
__attribute__((constructor)) static void freecam_register(void) { rt_register_host(RT_PATCH_PAGE + 0xf70, dbg_freecam_host); }
// The time of day's site of the set ostep (patches-v111.txt) calls the page's +0xf60, the water renderer's site its
// +0xf50, and the surfaces' fixed-step sites of the set owater its +0xf20 (frame_rate.c).
__attribute__((constructor)) static void day_register(void) {
if (!RT_V111) return;
rt_register_host(RT_PATCH_PAGE + 0xf60, frame_rate_day_host);
rt_register_host(RT_PATCH_PAGE + 0xf50, frame_rate_water_host);
rt_register_host(RT_PATCH_PAGE + 0xf20, frame_rate_sim_host);
}
#endif
#if RT_PATCH_COUNT
static const struct { const char *name, *env; int on; } sets[] = {
#define X(i, n, e, d) {#n, e, d},
RT_PATCH_SETS(X)
#undef X
};
static void *page;
#endif
void rt_patches_init(void) {
#if RT_PATCH_COUNT
if (!plat_mem_commit(RT_PATCH_PAGE, 0x1000))
rt_fatal("patches: cannot commit the patch page at %#llx", RT_PATCH_PAGE);
page = (void *)(uintptr_t)RT_PATCH_PAGE;
char list[512];
size_t n = 0;
list[0] = 0;
for (int i = 0; i < RT_PATCH_COUNT; i++) {
const char *e = cfg_get(sets[i].env);
// a set with a row in the settings table takes its default from there; the generated default serves the rest
rt_patch_on[i] = (uint8_t)(cfg_meta(sets[i].env) ? cfg_bool(sets[i].env) : e && *e ? strcmp(e, "0") != 0 : sets[i].on);
n += (size_t)snprintf(list + n, sizeof list - n, "%s%s %s", i ? ", " : "", sets[i].name,
rt_patch_on[i] ? "on" : "off");
if (n >= sizeof list) n = sizeof list - 1;
}
rt_log("patches: %s (page %#llx)", list, RT_PATCH_PAGE);
#endif
}
int rt_patch_set(const char *name) {
#if RT_PATCH_COUNT
for (int i = 0; i < RT_PATCH_COUNT; i++)
if (!strcmp(sets[i].name, name)) return i;
#endif
(void)name;
return -1;
}
int rt_patch_enabled(int set) { return set >= 0 && set < RT_PATCH_COUNT && rt_patch_on[set]; }
void rt_patch_disable(int set) {
if (set >= 0 && set < RT_PATCH_COUNT) rt_patch_on[set] = 0;
}
void rt_patch_switch(int set, int on) {
if (set >= 0 && set < RT_PATCH_COUNT) __atomic_store_n(&rt_patch_on[set], (uint8_t)(on != 0), __ATOMIC_RELAXED);
}
void *rt_patch_page(void) {
#if RT_PATCH_COUNT
return page;
#else
return NULL;
#endif
}