Commit Graph

1032 Commits

Author SHA1 Message Date
Shokunin eee07fe910 GL: raise the internal-resolution ceiling to the driver's limits
The OpenGL backend capped supersampling at 4, the software mirror's limit,
although its hr surface lives on the GPU. Raise the compile-time ceiling to
32 (gpu_render.h) and decide the real scale at context init:

- Query GL_MAX_TEXTURE_SIZE, GL_MAX_RENDERBUFFER_SIZE and
  GL_MAX_VIEWPORT_DIMS and clamp S so the 1024*S x 512*S surface fits them
  and a 2 GiB memory budget (PSX_GL_VRAM_BUDGET_MB overrides it). Apple's
  GL on Metal reports 16384, so 16x is the most a full-VRAM surface can be.
  The clamp is logged with the limit that applied.
- If the surface still fails to allocate, retry one scale lower inside GL.
  Only a 1x failure drops the backend to software, as before.
- Draw lines above 1x as quads one native pixel thick covering both
  endpoints. glLineWidth(S) is capped at 1 on core profiles, so every line
  was one hr pixel thick (1/9 of native at 9x).
- Present a supersampled source with an area resolve when it is more than
  1.25x the output (1200 lines into a 1080 window, 480i menus), and inset the
  UV rect by half a texel instead of half a native pixel, which cropped and
  zoomed the image by (S-1)/2 texels per edge.
- Size the copy scratch to the largest copy (copies stage at its origin) and
  rebuild the mask stencil in tiles, only over the primitive bbox union, so a
  large S does not cost another full-size surface.
- Refuse a native-wide surface wider than the driver limit (uncapped Fit at
  a high scale) with one log line; the frame presents 4:3.
- main.cpp: the offline and dual-raster clamps no longer cap GL at the
  software 4x. The CPU present path presents GL frames at 1x (its readout is
  native) and its staging buffer stays sized for at most 4x.
- [video] supersampling accepts 1..32 in game.toml and settings.toml; the
  runtime clamps per backend.

Every change is gated on S > 1: at 1x the renderer is byte-identical (same
native VRAM and presented framebuffer digests as the previous source on a
fixed GP0 scene).

Tests: gl_scale_limits_test (clamp arithmetic), gl_internal_scale_guards
(source guards + runner parsing), and gl_scale_invariance_test, a hidden
real-GL run at 1,2,3,5,9x that requires identical guest-visible VRAM at
every scale, one-native-pixel lines at internal resolution, and a 32x
request that stays on GL at the driver's clamp (16x on Apple).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:52:22 -07:00
Shokunin 78bec7b55e Add [widescreen.cull] bgez_sites and clip_edge_x_load_sites
Two generic explicit cull-site kinds for native-wide titles whose screen-X
rejects the existing keys cannot express exactly:

- bgez_sites: `bgez SX, keep` in a signed per-vertex left-edge chain
  (`bgez x0,keep; ...; bltz xN,reject`). Emitted through psx_ws_cull_bgez
  (keep while SX >= -margin). bltz_sites alone still rejects a primitive
  whose first vertices sit in the revealed band; with both kinds the chain
  rejects exactly when every vertex is left of -margin.
- clip_edge_x_load_sites (+ clip_edge_width, default screen_w_imms[0]):
  lh/lhu/lw of a clip-rectangle X bound. psx_ws_clip_edge_x moves a loaded 0
  to -margin and a loaded width to width+margin and leaves interior bounds
  (mirrors, split-screen halves) unchanged. xclip_load_sites is lw-only and
  upper-bound-only, so it cannot express this.

Both are identity at 4:3, emit nothing when empty, and fail main-EXE
generation on an opcode mismatch (overlay variants stay vanilla). They enter
the overlay-cache hash only when non-empty, so every existing title keeps
its hash (pinned in the test against the previous layout).

Interpreter parity: the dirty-RAM interpreter now also honours explicit
bltz_sites, bgez_sites, branch_keep_sites and clip_edge_x_load_sites. Its
explicit site lists are stored sorted (binary-search lookup), the cap rises
from 64 to 256 per key, and an over-long list is logged instead of being
silently truncated.

Shared math lives in runtime/include/ws_cull_edge.h. New ctest
ws_cull_edge_codegen_test covers parsing, hash identity, emitted code,
the opcode guards and the helper math.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:39:26 -07:00
Shokunin 255d05e8e4 debug_server: judge frame_fingerprint on guest-visible facts
frame_fingerprint's wr column is an ordered hash over main-RAM writes.
DMA and device writes (MDEC-out, CD, SPU, GPU->RAM) are recorded in host
device-service order. Native shards flush cycles at every store barrier,
while batched interpreted and static code services devices after the
block. So identical guest writes gave a different wr forever after.

- ws: an order-independent sum of a splitmix64 term per (addr, val) over
  main RAM. Together with wc it is the main-RAM judge column. wr and pc
  stay as ordered locators.
- qc: fingerprint-eligible writes that FMV-quiet kept out. If a straddled
  write falls into a quiet frame in one run only, the cumulative columns
  never agree again, and qc is where that shows.
- The accumulator moves to runtime/include/frame_fingerprint.h so the
  semantics are unit-tested (frame_fingerprint_test).
- The JSON buffer is sized from the real maximum entry length (284 B),
  which the old 224 B per entry could overflow.

docs/TCP_COMMANDS.md documents the column roles, the one-frame snapshot
straddle and the FMV-quiet caveat (set PSX_DEBUG_FMV_QUIET=0 for A/B
identity runs). Existing columns keep their meaning. Nothing outside
debug-tools builds changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:30:24 -07:00
Shokunin 649aceba7b overlay: write the runtime's store-PC breadcrumb from shards (ABI v24)
The emitter writes g_debug_last_store_pc before every guest store. The
overlay preamble gave each DLL a private copy on the theory that only debug
tooling reads it. The runtime actually reads it in every build:

- memory.c psx_write_word_raw keys its RAM 0x0-0xF store filters on it;
- the fingerprint pc/mmio/sp columns use it, and so do wtrace,
  record_frame, the MMIO/GP1 rings, parity provenance and the freeze
  heartbeat.

So after a native overlay store the host still named an older store, and
those consumers saw an instruction the interpreter would not have.

OverlayCallbacks gains last_store_pc, which the loader always points at
the runtime's copy. In PSX_OVERLAY_DLL_BUILD, cpu_state.h maps the
emitted name onto that pointer, the same pattern as the bail-state
pointers. Generated code is unchanged. A NULL host pointer keeps the old
private copy.

The ABI goes to 24, so the loader rejects and regenerates older shards.
The preamble change already moves the codegen-hash cache path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:03:43 -07:00
Shokunin 6b6e2c712e overlay: run shards only for the segment they were compiled for
Overlay shards are compiled at the KSEG0 address of their bytes: captures
record 0x80000000|offset and the manifest parser keys entries there. The
emitter bakes that segment into every PC a shard produces: the $ra a
jal/jalr writes, jump and exception-resume PCs, and I-cache fetch tags.
Dispatch matched candidates by the segment-stripped address, so a KUSEG or
KSEG1 alias of the same bytes ran the KSEG0 body at the wrong
architectural PC.

Found in R4 (SLUS-00797) with OpenBIOS: the exception handler enters its
RAM patch slots at KUSEG (0x0000281C, 0x0000357C). The kernel-page shard
linked $ra=0x8000282C where the interpreter (and hardware) gives
0x0000282C, and fetched 0x8000281C/0x8000357C, missing lines the kernel
had just filled with KUSEG tags: +4 cycles per slot, so the next VBlank
IRQ landed at a different instruction and warm (native) vs cold
(interpreted) overlay-cache runs saved different registers from frame
194 on. A KSEG1 entry would also be charged as cached.

PSX_OVERLAY_CODE_SEGMENT (overlay_loader.h) names the rule for capture,
the manifest parser, overlay_loader_dispatch and the generated static
overlay table: a PC outside it falls to the interpreter, which runs at
the PC it was handed. CPS continuations already failed closed this way
(their entry switch only knows KSEG0 PCs); function entries now match.
overlay_loader_status reports segment_alias_interp.

Static game code has the same shape for KUSEG-linked EXEs (the parser
normalizes their headers to KSEG0); that needs segment-relative emission
and is not changed here. Shard cache tags are unchanged.

Tests: test_overlay_segment_gate.py drives the real loader (pair-dedup
harness, which also gains the RAM-geometry globals it no longer linked
without) with a KSEG0 fixture shard: KUSEG and KSEG1 dispatches stay
interpreted and never call it, KSEG0 still runs native. It fails with the
gate removed. test_compile_overlays_static_split.py covers the generated
static table and rejects a non-KSEG0 entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:56:55 -07:00
Shokunin a87277bdad Reset mod-owned presentation state at every session start
The lobby rematch is the only in-process second session: an offline
session ends the process, and only a netplay match launched from the
lobby soft-returns (netplay_soft_exit -> psx_request_return_to_lobby).
The rematch path re-enters via goto session_reboot, below the first-boot
session block, so it reset no mod-owned state at all. The reachable leak:
a match with [netplay] local_viewport_aspect = "adaptive" leaves Fit
(g_ws_adaptive_view) on, and an offline rematch then runs adaptive
widescreen. The launcher also round-trips the match's fixed aspect
(16:9/21:9 from the local viewport) through ls.aspect_index.

reset_mod_owned_presentation() runs immediately before activation in the
first-boot session block (absorbing the existing blend reset) and in the
rematch path after its commit or netplay clear. Every call clears Fit, the
world and retained scene predicates, the adaptive backdrop preload, bezel
artwork, the blend mode and native VBlank pacing. Later calls also restore
vsync, frame interpolation and its rate, and automatic FMV skipping to
their first-session values, and clear the 8 MiB RAM request (memory_init()
re-latches it at session_reboot) and the texture-bank resolver and
batching flag. Netplay clears the plan, so no plugin has activated before
a rematch; everything past Fit is defensive. The renderer and fixed aspect
are launcher controls and process-lifetime allocations are left alone
(documented in MOD_PACKAGES.md).

The first-call capture lives in mod_session_baseline.h: its first call
records the pre-activation values (settings.toml, PSX_VSYNC, launcher) and
writes nothing back, so the first session, and every run that never
soft-returns, is unchanged. A static_assert keeps the restore honest: it
is only valid while interpolation and Skip FMVs have no launcher control.
Function-entry hooks need nothing here: the commit or netplay clear
empties the hook table and only activation rebuilds it.

The rematch path also re-applies the Settings aspect clamp (4:3 while
widescreen is mod-owned) before apply_netplay_local_viewport_aspect, so a
netplay rematch still gets its local viewport. The first-boot path keeps
the launcher/lobby aspect as before.

Not changed here: the rematch path still skips activation and the
controller, load and disc-speed resets of the first-boot block, so an
offline rematch with mods enabled applies the plan's patches and runs its
VBlank callbacks without activation.

Tests: mod_session_baseline_test (new, CTest) drives the first-call
capture and later restore with vsync on and off; it fails if the first
call writes anything. test_mod_owned_display_controls.py guards the
reset list, both call sites, reset-before-activation on the rematch path
and the rematch 4:3 re-clamp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:26:33 -07:00
Matthew Stanley 7b2962efb5 Preserve background composite lifetime and park visual validation 2026-09-27 23:30:23 -07:00
Matthew Stanley 4dfc7b9c1c Add guarded native-wide background polygon composites 2026-09-27 23:30:23 -07:00
Matthew Stanley ff63ee9bde Windows: embed UTF-8 code-page manifest; wide picker for pre-1903 (#371)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 22:05:14 -07:00
Matthew Stanley 78534deff4 mods: [runtime] hide_hidden_mod_features opts the launcher into never showing hidden features
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 16:41:47 -07:00
Matt Stanley 68d77ed6a9 Merge pull request #404 from RetroPortingToolKit/fix/vblank-follows-video-mode
runtime: VBlank rate follows the GPU video mode, not the disc region
2026-09-26 16:03:47 -07:00
Matthew Stanley f51617bb7d runtime: VBlank rate follows the GPU video mode (GP1(08h) bit 3), not the disc region
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 15:38:21 -07:00
Matthew Stanley c547f3b967 mods: implicit cross-package feature requirements ([[requirement]], format 7)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 13:58:16 -07:00
Matthew Stanley 141eb39002 mods: let titles exclude framework builtins with EXCLUDE_BUILTIN_MODS
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 12:11:39 -07:00
Matt Stanley 0487e0cb81 Merge pull request #398 from RetroPortingToolKit/feat/8mb-ram-opt-in
Opt-in 8 MB main RAM
2026-09-26 10:04:39 -07:00
Matthew Stanley 0238117450 runtime: keep RAM-mirror PCs architectural; fold only bytes, through one live mask
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 08:03:57 -07:00
Matthew Stanley 18e3030440 savestate: validate every section before applying any; header records the live RAM size
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 08:03:39 -07:00
Matthew Stanley 199cfe01bc mods: flat function-entry hook table built at activation; interpreter skips it when empty
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 07:34:20 -07:00
Matthew Stanley c564ccd1f1 runtime: route every RAM consumer through one live 2/8 MiB geometry
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 06:31:33 -07:00
Matthew Stanley fa951645b4 runtime: add opt-in 8 MB main RAM mod
Adapt the 8 MB RAM work from the rbengine branch into a master-targeted change. The builtin package is disabled by default, activation requests expanded RAM before memory init, and runtime/state/digest/dispatch paths respect the live RAM map.
2026-09-26 06:02:23 -07:00
Matthew Stanley 41a76a72d8 mods: resolve function-entry plugins and run them only for the active plan
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 00:27:12 -07:00
Matthew Stanley f9109e21c6 Resolve relative bezel artwork paths against the exe directory
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 22:27:47 -07:00
Alex Vanderveen b5ef7b86d9 netplay: take the scheduler, input history and hash-confirm from recomp-net
recomp-net now carries the peer-facing rollback modules that used to live in
retcomm-rbengine (RetroPortingToolKit/recomp-net#14). Switch the MotK shims and
psx_netplay_sched.c from rbe_sched_* / rbe_ih_* / rbe_hc_* /
rbe_rb_peer_post_tip_ok to their rnet_* names and recomp_net/ headers, and bump
lib/recomp-net to c2338c6.

The netplay_* / np_sched_* surface the rest of the runtime calls is unchanged.
The snap ring and rbe_mono_ms still come from retcomm-rbengine, which stays at
a7b9850 for now; the three moved-module unit tests link recomp_net directly
instead of reaching it through rbengine. RBE_RB_* env knobs keep their names.

Checked with PSX_NETPLAY=ON: psx-runtime links and the netplay tests pass
against both the current rbengine and the split one (rbengine
refactor/launcher-core), and the binary's 33 scheduler / history /
hash-confirm functions now resolve to rnet_* where they were rbe_*.
lobby_chat_ring_test does not compile on master either (chat_push arity) and
is untouched here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 22:06:27 -04:00
Matt Stanley dc7a4c1084 Merge pull request #389 from RetroPortingToolKit/fix/cue-pregap
Honour cue PREGAP/POSTGAP in the BIN/CUE mount (fixes #388)
2026-09-24 15:42:52 -07:00
Matt Stanley 9e9ec7b401 Merge pull request #383 from x6568tank/feat/mid-session-disc-change
Mount a different disc while the game runs
2026-09-24 15:15:43 -07:00
Matthew Stanley eec71b9787 fix: open the game window at a normal size instead of maximised
Default = largest multiple of 240 lines within 2/3 of the usable height;
an explicit window_width is still honoured.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 14:42:12 -07:00
Matthew Stanley 0e75a37f7c debug: record immediate CD responses in the command history ring
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 14:38:03 -07:00
Matthew Stanley e09980e6cd fix: honour cue PREGAP/POSTGAP in the BIN/CUE mount
Gaps the dump does not store were dropped, so every later track (and GetTD)
sat 150 sectors early; games that seek via GetTD landed in the previous track.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 14:38:03 -07:00
Matthew Stanley 39ee7d7940 Support adaptive screen masks and native effect presentation 2026-09-20 12:01:32 -07:00
Matthew Stanley 69570b4760 Add opt-in function filters with safe snapshot boundaries 2026-09-19 22:42:49 -07:00
Matthew Stanley 73fd940294 Retain streamed tile textures with live palettes and stable scene bounds 2026-09-19 20:02:29 -07:00
Matthew Stanley 95a977953b Support adaptive host tile packets and mirrored panorama extensions 2026-09-19 19:06:42 -07:00
Matthew Stanley 41e92d8a32 Merge current framework master preserving accepted MMX6 widescreen and performance work 2026-09-19 17:49:10 -07:00
Matthew Stanley c68bf6e085 Merge current master while preserving Tomba adaptive renderer 2026-09-19 16:23:00 -07:00
Matthew Stanley fa435a4a0c render: add uncapped adaptive viewport and stable world classification 2026-09-19 16:19:23 -07:00
x6568tank 7c99a88565 feat: mount a different disc while the game runs
The runtime mounts one disc image at startup. No code path changes the
mounted image after that. Some multi-disc games prompt for a disc
change during play and then poll the drive. That poll never succeeds.
The CD-reinsert path reopens the same image path, so it cannot mount
the other disc.

cdrom_disc_select() closes the current image and opens another roster
entry, inside the existing lid window. cdrom_lid_media_ready() returns
0 for the whole window, so the guest cannot read the image until the
window ends. The existing timed close then ends the window.

cdrom_init() is not called. It also resets warm-route, trace and timing
state, which would discard a running session. cdrom_disc_select()
resets the sector buffers, the seek position, and the warm-route
arming. Registered warm routes are configuration and are not reset.

begin_tray_open_cycle() now holds the tray-open code, moved out of
debug_force_cd_reinsert(). Both paths call it.

Add a disc_select debug command. It returns the roster and mounts entry
n when n is given. It also returns the sector count and track count of
the mounted image, which confirms that the reader opened another image.

Tested on Ace Combat 3: Electrosphere (SLPS-02020 / SLPS-02021). Boot
disc 2 with no disc-2 save. The game prompts for disc 1. At that prompt
the CD read count is static at 2955 and frames run at 60.5 Hz, so the
drive is idle. disc_select n=1 mounts disc 1. The game resumes and
plays its first cutscene. Reads restart within two seconds and pass
19971. The read addresses change to disc-1 values. The frame rate does
not drop.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-18 11:10:00 -04:00
Pegasus 77a8480363 Read disc region from the actual SYSTEM.CNF directory entry
Prefer the ISO9660 boot file over the bounded raw-image scan. Biohazard stores SYSTEM.CNF outside that scan, leaving fresh builds at the default SCEA response. Add raw and cooked late-file regressions with an early decoy serial. The full-profile prefix again matches the retained native baseline through 5999 returns; the independent-source frontier remains return 464.

(cherry picked from commit 82c3de13ef)
2026-09-14 17:26:33 +03:00
Pegasus 5e23de8393 feat: add PS1 conformance harness and fix GTE fidelity
(cherry picked from commit 98d617b5f3)
2026-09-14 17:26:31 +03:00
Matthew Stanley 8b689140dd Add opt-in world scene classification and immutable terrain batching 2026-09-13 21:06:58 -07:00
Matthew Stanley b4d41b173c Merge current master and preserve both runtime regression test groups 2026-09-13 19:40:33 -07:00
Matthew Stanley f2352e3d32 Keep retained scene aspect until the next displayed buffer flip 2026-09-13 19:27:03 -07:00
Matthew Stanley e3beb365ba Add opt-in native-wide presentation hold for retained loading scenes 2026-09-13 19:11:21 -07:00
Matthew Stanley 9037e7bd23 Add opt-in retained texture banks and enhancement memory snapshots 2026-09-13 16:50:26 -07:00
Matt Stanley d4c45adf62 fix(sio): remove title-specific card IRQ repair and opt-in (#362) 2026-09-13 16:33:46 -07:00
Aliet Exposito Garcia 72b3ead892 feat(OpenGL renderer): add post presentation gamma control
Some titles and enhancement mods/plugins might require display level gamma correction to improve shadow detail and overall visibility without altering guest rendering state. This introduces a public gamma adjustment interface for the OpenGL renderer via `gl_renderer_set_post_gamma`, that enables external plugins to adjust the display curve cleanly at runtime.

This adjustment applies exclusively to game content and temporal interpolation passes while leaving host interface elements, bezels, and previously composed frames untouched. Modifying the gamma setting immediately invalidates the presentation buffer so that changes become visible on screen without requiring new video memory writes from the guest system.
2026-09-13 01:30:10 -04:00
Alex Vanderveen b9ca600778 netplay: block list, account keys, list scope and automatch for the launcher
Brings the PSX runtime level with what recomp-ui's netplay pages now ask a
host for, and with the SNES runner that already answers them
(snes_lobby_client.c / snes_host_lobby.c):

- Chat lines, seat rows and players-online rows carry the server's opaque
  account key, so the launcher's ignore/block list survives a rename.
- psx_lobby_set_blocks pushes that list to the server as `set_blocks`, since
  refusing to pair or seat the two together is the server's job; recomp-ui
  re-sends it on every reconnect.
- list_scope_set: a LAN player no longer sees online rooms they have no
  connection for, and an online player no longer sees their own LAN rows.
- Automatch: rulesets discovery, the relay latency probe (14-byte LE frame,
  type 200 -> 201, nonce-matched), queue with the disc fingerprint, the
  accept gate with a locally counted deadline, requeue/cancel/cooldown, and
  claiming automatch's `error` codes only while a ticket is in flight.
  Leaving an automatch room after the match: it is the server's room, has
  no host to rematch with, and holding the seat blocks the next ticket.
- Account: init the auth client once per URL rather than every pump (the
  login worker reads the host), anchor the device key to the executable
  directory, and publish the signed-in handle as the lobby display name.

mods_enabled is asserted false on PSX: every netplay launch clears the mod
plan or refuses to start, and the caps a match runs are the ruleset's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPXfGGhBBBxQPcEzaMsauF
2026-09-12 19:39:09 -04:00
Matthew Stanley 8c53cb0ba7 Merge validated widescreen anchoring and activation bounds into master 2026-09-12 16:34:13 -07:00
Alex Vanderveen 615d35fe22 Merge remote-tracking branch 'origin/master' into feat/lobby-parity
# Conflicts:
#	tools/new_project_layout/project_studio/readme_metrics.py
2026-09-12 19:30:47 -04:00
Alex Vanderveen fddc8b8516 netplay: report chat lines for moderation, and pin recomp-net for it
PsxLobbyChatMsg gains the server's `mid` for a line, taken off `chat` and
`server_chat`, and psx_lobby_report_chat hands a list of them to
rnet_chat_report_build. The frame carries ids and never the text, so a
report cannot fabricate what somebody said. Same eight lines as the SNES
copy; what a report contains lives in recomp-net.

lib/recomp-net -> 859ba28 (support chat reporting), which is also where
rnet_account_set_secret_path comes from.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FPXfGGhBBBxQPcEzaMsauF
2026-09-12 19:29:57 -04:00
Matthew Stanley 33e3ed78eb Expand lower activation bounds with matching native and runtime semantics 2026-09-12 14:47:47 -07:00