diff --git a/runtime/include/mod_plugins.h b/runtime/include/mod_plugins.h index 96d78bf1..b380e94d 100644 --- a/runtime/include/mod_plugins.h +++ b/runtime/include/mod_plugins.h @@ -62,9 +62,6 @@ int psx_mod_finish_function(struct CPUState* cpu); /* Active function-entry hook count (0 = none). Hot callers test it before the * call, so a run without an active hook pays one load per interpreted entry. */ extern uint32_t g_psx_mod_function_entry_hooks; -/* Entry callbacks can make nested guest calls while retaining host registers. - * Save/load and rewind must wait until that host context has returned. */ -int psx_mod_function_entry_active(void); /* Always-on named event counters for trusted plugins (observability, not * logging): e.g. how often each guard in a hook rejected. `name` should be a @@ -72,6 +69,9 @@ int psx_mod_function_entry_active(void); * kept, further names are counted in an overflow bucket. Emulation-thread only. * TCP: {"cmd":"mod_counters"} lists every counter with its last frame. */ void psx_mod_counter_add(const char* name, uint32_t delta); +/* Entry callbacks can make nested guest calls while retaining host registers. + * Save/load and rewind must wait until that host context has returned. */ +int psx_mod_function_entry_active(void); /* Narrow guest services available to trusted plugin callbacks. */ int psx_mod_game_started(void); diff --git a/runtime/include/render_pass_motion.h b/runtime/include/render_pass_motion.h index 9bb1ea7b..c28ddc3b 100644 --- a/runtime/include/render_pass_motion.h +++ b/runtime/include/render_pass_motion.h @@ -45,7 +45,11 @@ enum { PSX_MOTION_SVECTOR = 4, /* s16 x, y, z */ /* s16 x, y, z angles, 4096 per turn: each blends along the shorter arc. */ PSX_MOTION_ANGLES = 5, - PSX_MOTION_SCALAR = 6 /* s32 */ + PSX_MOTION_SCALAR = 6, /* s32 */ + /* A bare 3x3 s16 rotation in 4.12 (18 bytes), blended like a MATRIX's; + * pair it with a VECTOR / SVECTOR kind wherever its translation lives + * (skeleton pose records, matrices stored apart from their position). */ + PSX_MOTION_ROTATION = 7 }; typedef struct PSXMotionLimits { @@ -92,7 +96,7 @@ uint32_t psx_motion_prepare(PSXMotionSet* set, const PSXMotionLimits* limits, /* Write every prepared value at t (0 = previous frame, 1 = this frame). */ void psx_motion_apply(const PSXMotionSet* set, double t); /* Blend one prepared value at t into `out` without writing guest RAM: - * a PSXMotionMatrix for the MATRIX kinds, int32_t[3] for VECTOR / SVECTOR / + * a PSXMotionMatrix for the MATRIX kinds and ROTATION (translation 0), int32_t[3] for VECTOR / SVECTOR / * ANGLES, int32_t for SCALAR. Returns 0 when that value does not blend this * frame (out then holds its current value) or was not tracked (out * untouched, returns -1). */ diff --git a/runtime/src/debug_server.c b/runtime/src/debug_server.c index 5ee1d7c7..fbefca5c 100644 --- a/runtime/src/debug_server.c +++ b/runtime/src/debug_server.c @@ -7802,14 +7802,6 @@ static void handle_input_route_status(int id, const char *json) * {"cmd":"ws_hud_mode","tag_rects":0|1} * tag_rects=1 lets TAGGED rect-family prims re-anchor too (Tomba's AP * counter renders through the tagged sprite funnel). */ -static void handle_ws_hud_mode(int id, const char *json) -{ - int v = json_get_int(json, "tag_rects", -1); - if (v < 0) { send_err(id, "missing tag_rects (0|1)"); return; } - gpu_ws_set_nw_hud_tag_rects(v); - send_fmt("{\"id\":%d,\"ok\":true,\"tag_rects\":%d}", id, v ? 1 : 0); -} - /* Named plugin counters (psx_mod_counter_add), always on: * {"cmd":"mod_counters"} * -> counters: [{name, count, last_frame}], overflow. */ @@ -7849,6 +7841,14 @@ static void handle_mod_counters(int id, const char *json) free(buf); } +static void handle_ws_hud_mode(int id, const char *json) +{ + int v = json_get_int(json, "tag_rects", -1); + if (v < 0) { send_err(id, "missing tag_rects (0|1)"); return; } + gpu_ws_set_nw_hud_tag_rects(v); + send_fmt("{\"id\":%d,\"ok\":true,\"tag_rects\":%d}", id, v ? 1 : 0); +} + /* Explicit HUD-anchor / background tag pipeline counters (always on): * {"cmd":"ws_tag_stats"} * -> hud / background: tag_calls, tag_rejected, hit, stale, guard_mismatch, diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 9ec7ba8a..d43caf13 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -62,7 +62,9 @@ extern "C" void psx_event_step_conservative_env_init(void); #include "psx_netplay_rb.h" #include "psx_selfcheck.h" #include "psx_lobby_client.h" +#if defined(PSX_HAS_RECOMP_NET) #include "recomp_net/host_relay.h" /* RNetHostRelayStatus for the launcher relay line */ +#endif #include "netplay_bios_settle.h" #include "netplay_exit_reason.h" #include "host_time.h" @@ -11024,6 +11026,7 @@ namespace { if (!out || !out_cap) return 0; out[0] = '\0'; if (g_lnch_hosting_lan || g_lnch_joined_lan || !psx_lobby_in_lobby()) return 0; +#if defined(PSX_HAS_RECOMP_NET) RNetHostRelayStatus st; if (!psx_lobby_host_relay_status(&st)) return 0; if (st.role == 1) { @@ -11066,6 +11069,9 @@ namespace { return 1; } return 0; +#else + return 0; /* offline build: recomp-net (and its host relay) is not linked */ +#endif } int ae_np_force_turn_set(void*, int force) { if (g_lnch_hosting_lan || g_lnch_joined_lan) diff --git a/runtime/src/render_pass_motion.c b/runtime/src/render_pass_motion.c index 89d9eeb1..98810907 100644 --- a/runtime/src/render_pass_motion.c +++ b/runtime/src/render_pass_motion.c @@ -108,6 +108,9 @@ static void read_entry(MotionEntry* e) { for (int i = 0; i < 3; i++) e->v[9 + i] = m.t[i]; break; } + case PSX_MOTION_ROTATION: + for (int i = 0; i < 9; i++) e->v[i] = (int16_t)psx_mod_read_half(e->addr + 2u * (uint32_t)i); + break; case PSX_MOTION_VECTOR: for (int i = 0; i < 3; i++) e->v[i] = (int32_t)psx_mod_read_word(e->addr + 4u * (uint32_t)i); break; @@ -123,7 +126,7 @@ static void read_entry(MotionEntry* e) { int psx_motion_track(PSXMotionSet* s, uint32_t kind, uint32_t addr, uint32_t identity) { MotionEntry* e; - if (!s || kind < PSX_MOTION_MATRIX || kind > PSX_MOTION_SCALAR) return 0; + if (!s || kind < PSX_MOTION_MATRIX || kind > PSX_MOTION_ROTATION) return 0; if (!s->began[s->cur] || s->n[s->cur] >= s->cap) return 0; e = &s->cap_buf[s->cur][s->n[s->cur]]; memset(e, 0, sizeof *e); @@ -270,10 +273,11 @@ static int within_limits(const MotionEntry* a, const MotionEntry* b, const PSXMotionLimits* lim, uint32_t ticks) { const double move = lim->move_per_tick > 0 ? lim->move_per_tick * ticks : HUGE_VAL; switch (a->kind) { - case PSX_MOTION_MATRIX: { + case PSX_MOTION_MATRIX: + case PSX_MOTION_ROTATION: { double ang; int16_t ra[9], rb[9]; - if (dist3(&a->v[9], &b->v[9]) > move) return 0; + if (a->kind == PSX_MOTION_MATRIX && dist3(&a->v[9], &b->v[9]) > move) return 0; for (int i = 0; i < 9; i++) { ra[i] = (int16_t)a->v[i]; rb[i] = (int16_t)b->v[i]; } ang = rotation_angle(ra, rb); if (ang >= 0) return ang <= turn_limit(lim->turn_radians); @@ -344,7 +348,8 @@ uint32_t psx_motion_prepare(PSXMotionSet* s, const PSXMotionLimits* lim, /* Blend one pair into `v` (entry layout). */ static void blend_entry(const MotionEntry* a, const MotionEntry* b, double t, int32_t* v) { switch (a->kind) { - case PSX_MOTION_MATRIX: { + case PSX_MOTION_MATRIX: + case PSX_MOTION_ROTATION: { int16_t ra[9], rb[9], ro[9]; for (int i = 0; i < 9; i++) { ra[i] = (int16_t)a->v[i]; rb[i] = (int16_t)b->v[i]; } blend_rotation(ra, rb, t, MOTION_PI * 2, ro); /* limits were applied at prepare */ @@ -388,6 +393,9 @@ static void write_entry(uint8_t kind, uint32_t addr, const int32_t* v) { case PSX_MOTION_SCALAR: psx_mod_write_word(addr, (uint32_t)v[0]); break; + case PSX_MOTION_ROTATION: + for (int i = 0; i < 9; i++) psx_mod_write_half(addr + 2u * (uint32_t)i, (uint16_t)v[i]); + break; } } @@ -405,7 +413,8 @@ void psx_motion_apply(const PSXMotionSet* s, double t) { } static void export_value(uint8_t kind, const int32_t* v, void* out) { - if (kind == PSX_MOTION_MATRIX || kind == PSX_MOTION_MATRIX_TRANSLATION) { + if (kind == PSX_MOTION_MATRIX || kind == PSX_MOTION_MATRIX_TRANSLATION || + kind == PSX_MOTION_ROTATION) { PSXMotionMatrix* m = (PSXMotionMatrix*)out; for (int i = 0; i < 9; i++) m->r[i] = (int16_t)v[i]; for (int i = 0; i < 3; i++) m->t[i] = v[9 + i]; diff --git a/runtime/tests/test_render_pass_motion.c b/runtime/tests/test_render_pass_motion.c index 6098ec78..52f61a50 100644 --- a/runtime/tests/test_render_pass_motion.c +++ b/runtime/tests/test_render_pass_motion.c @@ -164,6 +164,27 @@ static void test_angles_vectors_scalars(void) { psx_motion_set_destroy(s); } +static void test_rotation_only(void) { + PSXMotionSet* s = psx_motion_set_create(4); + PSXMotionLimits lim = {100.0, 0}; + PSXMotionStats st; + /* A bare rotation (as in a pose record) with other data right after it. */ + put_matrix(0xA00, 0.0, 0, 0, 0); + psx_mod_write_word(0xA00 + 0x12, 0xDEADBEEFu); + psx_motion_begin(s, 1); + psx_motion_track(s, PSX_MOTION_ROTATION, 0xA00, 3); + psx_motion_prepare(s, &lim, &st); + put_matrix(0xA00, 1.0, 0, 0, 0); + psx_mod_write_word(0xA00 + 0x12, 0xDEADBEEFu); + psx_motion_begin(s, 2); + psx_motion_track(s, PSX_MOTION_ROTATION, 0xA00, 3); + CHECK(psx_motion_prepare(s, &lim, &st) == 1, "a bare rotation blends"); + psx_motion_apply(s, 0.5); + CHECK(fabs(yaw_of(0xA00) - 0.5) < 0.002, "rotation-only slerps"); + CHECK(psx_mod_read_word(0xA00 + 0x12) == 0xDEADBEEFu, "nothing past the 18 bytes is written"); + psx_motion_set_destroy(s); +} + static int s_fn_calls; static double s_fn_last; static int frame_fn(struct CPUState* cpu, void* user, uint32_t alpha) { @@ -209,6 +230,7 @@ int main(void) { test_matrix_slerp(); test_identity_and_limits(); test_angles_vectors_scalars(); + test_rotation_only(); test_frame_driver(); printf(failures ? "FAILED (%d)\n" : "ALL PASS\n", failures); return failures ? 1 : 0;