Files
Going-Commando/Cargo.toml
T
ReaperOfVeriod 69a8398f6e Verify the tfrag lighting magnitudes against the running game
The harness that compares the port against the game's own memory existed but
had never been compiled: tools/trace did not build (27 errors, all in the
argument plumbing), and the tfrag_light.rs light-bank correction it motivated
was unverified. This finishes it and runs it, and the run finds two real bugs.

Result, on level 0 against three inputs -- the live emulator over PINE and two
PCSX2 savestates from the same level, agreeing byte for byte:

  headers matching the disc on invariant bytes: 833/833
  data pointers relocated as table + disc offset: 833/833
  light bank in RAM at 0x50ab20, byte-identical to the disc's
  vertices compared: 40600 (162400 colour bytes)
  equal: 162400 (100.00%)

The locator finds the level's tfrags block in RAM by tfrag 0's bounding
sphere and accepts a candidate only when every header matches the disc on the
bytes the game leaves alone and every data pointer is the relocated form, so
"the block was found" is itself a verification rather than an assumption. The
bank is searched in RAM by its exact section bytes, which is what settles its
framing: offset at +0x04, an i32 set count, twelve bytes of pad, then the sets.
The previous reading (offset at +0x00, count at +0x04) was wrong.

Two bugs the comparison found, both of which produced plausible output:

1. vertex_normal_bits negated x and y and left z positive, while
   NormalTable::normal negates all three. dA came out -0.707 where the game
   had +0.707, so max(dA, dA*wA) took the wrong branch and every vertex lit
   near its base colour: dark, but not wrong-dark. 95.88% agreement with the
   bug, 100.00% without.
2. A record whose select names a set the bank lacks drew transparent black.
   The game writes the record's base colour; 3,130 of level 0's 40,600
   vertices do this (select 0x000f, set 15 -- a runtime slot the level's
   directional bank does not have). The vertex is unlit, not invisible.

Both are now regression tests in the engine. The comparison prints the pass's
per-vertex terms (light_vertex_terms) for the first mismatches, because a
colour mismatch alone cannot be attributed and the comparison is worthless
without attribution -- that is how the second bug was identified from a
three-line read rather than guessed at.

Docs: docs/plan/pcsx2-verification.md records the harness, the result, the
two bugs and what to compare next; roadmap and level-assets.md stop carrying
lighting magnitudes as an open item. Findings reach tests only as numbers --
no test reads a savestate, a dump or a live emulator.
2026-10-11 19:51:25 +02:00

91 lines
2.4 KiB
TOML

[workspace]
resolver = "2"
members = [
"crates/gc-formats",
"crates/gc-extract",
"crates/gc-engine",
"crates/gc-game",
"crates/gc-video",
"tools/trace",
]
[workspace.package]
version = "0.1.0"
edition = "2021"
rust-version = "1.85"
license = "MIT OR ISC"
publish = false
# The PS2's Emotion Engine is a MIPS R5900 with a 128-bit vector unit and a
# float model that differs from x86 in ways that matter for this game (no
# denormals, specific rounding). Every ported routine must reproduce that model
# rather than Rust's native f32, or the game will subtly not feel right.
# See docs/plan/float-model.md.
[workspace.dependencies]
gc-formats = { path = "crates/gc-formats" }
gc-extract = { path = "crates/gc-extract" }
gc-engine = { path = "crates/gc-engine" }
gc-game = { path = "crates/gc-game" }
gc-video = { path = "crates/gc-video" }
gc-trace = { path = "tools/trace" }
# Dev tools. PCSX2's savestates are zip archives whose entries are
# zstd- or deflate-compressed; both backends are pure Rust and are already
# in the lockfile through Bevy, so the trace tool adds no new sources.
ruzstd = "0.8"
flate2 = { version = "1", default-features = false, features = ["rust_backend"] }
anyhow = "1"
thiserror = "2"
bytemuck = { version = "1", features = ["derive", "min_const_generics"] }
sha1 = "0.10"
# Bevy 0.19.1 is the engine ReRAC uses; matching it keeps the GS/VU work portable.
#
# default-features = false avoids the cpal audio backend, which needs system
# ALSA development headers on Fedora (alsa-lib-devel). Audio decode lives in
# gc-video and is the last phase of the port, so the host audio backend is not
# needed to build. Re-enable "audio" and install alsa-lib-devel when it is.
bevy = { version = "0.19.1", default-features = false, features = [
"std",
"bevy_asset",
"bevy_color",
"bevy_core_pipeline",
"bevy_gizmos",
"bevy_log",
"bevy_mesh",
"bevy_pbr",
"bevy_render",
"bevy_shader",
"bevy_sprite",
"bevy_state",
"bevy_text",
"bevy_ui",
"bevy_window",
"bevy_winit",
"bevy_animation",
"bevy_gltf",
"png",
"ktx2",
"zlib",
"zstd_rust",
"tonemapping_luts",
"multi_threaded",
"x11",
"wayland",
"keyboard",
"mouse",
"gamepad",
] }
[profile.dev]
# Iterate fast on our own code; do not rebuild dependencies unoptimised.
opt-level = 1
[profile.dev.package."*"]
opt-level = 3
debug = false
[profile.release]
lto = "thin"
codegen-units = 1