Files
NX2navi/tests/frame_step_check.py
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

73 lines
3.0 KiB
Python

"""Build and run tests/frame_step_check.c: the step runtime/frame_rate.c gives Ocarina, with the rest of the runtime
stubbed. No game files: the module layout the runtime includes is a stand-in written to the test's build folder.
Usage: frame_step_check.py
"""
import os
import shutil
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
sys.path[:0] = [str(ROOT / 'recomp')]
from patches import load # noqa: E402
PAGE = 0x4ea0000 # the patch page of 1.1.1, main+0x4ea0000
# The sites that call frame_rate.c's hosts, as recomp/patches-v111.txt has them: set, module offset, the original word,
# the words before the bl (the original's own, where the host comes after it), the page offset the bl reaches.
FDIV = 0x1e201900 # fdiv s0, s8, s0
HOST_SITES = [('ostep', 0x3cdfc0, 0x1e380008, [], 0xf60), ('ostep', 0x475f8, 0x1e380009, [], 0xf50),
('owater', 0x3876014, FDIV, [FDIV], 0xf20), ('owater', 0x38827f8, FDIV, [FDIV], 0xf20),
('owater', 0x632cfc, FDIV, [FDIV], 0xf20)]
def compiler():
if os.environ.get('CC'):
return os.environ['CC']
if os.name == 'nt' and (MSYS_BIN / 'clang.exe').exists():
return str(MSYS_BIN / 'clang.exe')
return shutil.which('clang') or 'clang'
LAYOUT = '''// A stand-in for the generated layout.h (tests/frame_step_check.py).
#pragma once
#define MOD_MAIN_BASE 0x7000000000ull
#define MOD_MAIN_SIZE 0x4ea0000ull
'''
def check_sites():
"""Each host site is its listed words and one bl to its page slot, over its original word."""
table = load(ROOT / 'recomp/patches-v111.txt', [('main', 0x7000000000, PAGE)])
bad = 0
for name, site, original, before, slot in HOST_SITES:
(key, words, have), = table.sites['main'][site]
bl = words[-1] if isinstance(words[-1], int) else 0
target = site + (((bl & 0x3ffffff) ^ 0x2000000) - 0x2000000) * 4
ok = (table.sets[key][0] == name and have == original and words[:-1] == before and bl >> 26 == 0x25 and
target == PAGE + slot)
if not ok:
print(f'FAIL: the site main+{site:#x} of set {name} is {words} over {have:#x}')
bad += 1
return bad
def main():
work = ROOT / 'build' / 'test' / 'frame_step'
work.mkdir(parents=True, exist_ok=True)
(work / 'layout.h').write_text(LAYOUT)
exe = work / ('frame_step_check.exe' if os.name == 'nt' else 'frame_step_check')
env = dict(os.environ)
if os.name == 'nt' and MSYS_BIN.exists():
env['PATH'] = str(MSYS_BIN) + os.pathsep + env.get('PATH', '')
subprocess.check_call([compiler(), '-O1', '-Wall', '-Wno-unused-function', '-fno-strict-aliasing',
'-I', str(ROOT / 'runtime'), '-I', str(work),
str(ROOT / 'tests' / 'frame_step_check.c'), '-o', str(exe), '-lm'], env=env)
rc = subprocess.run([str(exe)], env=env).returncode
return subprocess.run([str(exe), 'mean'], env=env).returncode or rc or check_sites()
if __name__ == '__main__':
sys.exit(main())