You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f44a756
Browse filesBrowse the repository at this point in the historyBrowse files
Port: camera, save and config fixes from review; get every CI job passing (#23)
* Port: preserve camera continuity and full-extent outline presentation
Retain mouse look through town falls and landing while cutting authored camera steps, resets and dungeon eye transitions at their owning tick. Preserve retail reset bodies through generated bridges with source mapping, deduplicate zoom geometry and remove unreachable zoom state.
Copy fixed-look recording metadata and retain full-visible outline presentation. Update the shared runtime pin together with composite API removal and GPU suite classification. Cover camera ownership, fixed-look pixels, zoom limits and retail bridge extraction.
* Port: preserve config reads and archive legacy save imports safely
Reject incomplete config reads before replacing a file or publishing a backup. Publish legacy slot copies exclusively, retain recoverable sources, and archive entries with no-replace renames rather than markers or rollback deletion.
Defer automatic copies when source comparisons are unavailable and retain source-backed row replacement. Cover archive collisions, interrupted recovery, write interleaving and comparison faults, and document recovery and preserved retail bridges.
* Port: share item-message dispatch and adjust rod points with the mouse
Route item-name and message dispatch through one native implementation shared by retail callers. Bound rod-point wheel adjustment and use the retail hover sound, with regressions for dispatch parity and input limits.
* Port: honour configured test timeouts and isolate save import cases
Apply the greater of 30 seconds and CT_TEST_TIMEOUT to slow GPU cases, retaining the hosted-CI labels and configured CPU timeout. Split multi-configuration save import regressions into separately discovered parameterized cases so each retains an independent timeout and failure report.
* Port: build the data bridge regression with Windows tests
Include dc2data_bridge_test whenever the Windows test target set is requested so every registered CPU test has an executable. Keep its existing stack size and CMake runtime deployment unchanged.
* Port: create coherent empty data fixtures for screenshot tests
Generate data and port-data manifests plus a publisher marker using the data version from the pinned shared source. Keep each fixture isolated and retain canonical/display image comparisons without relaxing the production first-run data check.
* Port: scale menu pointer texels for high-resolution texture packs
Scale the hand atlas coordinates by the registered image scale while retaining logical pointer geometry. Host pointer overlays record outside the game frame, so use the shared texture scale directly rather than the frame-gated submission wrapper.
Load a real double-resolution PNG replacement and verify physical texel coordinates and the unchanged fingertip position. Retain canonical image and existing pointer ownership regressions.
* Port: use opaque host RGBA in rendering fixtures
Match the PNG renderer by encoding opaque synthetic texture pixels with host alpha 255. Preserve lens placement, backdrop colours, slide poses, resource reuse, material wrap and every sampling assertion previously tested through PS2 alpha conversion.
* Port: keep GPU regression coverage within hosted CI time limits
Reuse one renderer and texture across every options/help slide pose and assertion. Render frame-key metadata checks at the same aspect with a smaller target, and replace the duplicate fourth-influence shader vector with a citation to the retained shared numeric regression.
* Port: load real-asset regressions through the published texture catalog
Index the current PNG catalog and register the archive source before loading real glyph members. Retain all rendered ownership and endpoint comparisons, and skip incompatible extractions explicitly.
Make PC settings replay availability follow the versioned publisher artifacts and pinned data version instead of the retired manifest format.
* Port: isolate the password generator from host libc
* Port: compare indexed datapack files by filesystem identity
* Port: load the ELF reader from its shared script location
* Port: build the data bridge regression with native targets
* Port: retire fixture texture sources and reject invalid data markers
* Kazarov: pin bc3f14b
* Kazarov: pin 930ca9c
* Port: match an unnamed arena's address with a POSIX bracket class
* Port: clear the event flag of local scenes in town camera tests
* Port: end the part chain of local characters in dungeon eye tests
* Port: value-initialise the mesh entry in the display rotation test
* Kazarov: pin 3dc38ce
* Build: report the macOS runner's virtual-GPU rendering failures without failing the job
Fourteen rendering tests fail only on the hosted macOS runner, whose GPU is the
Apple Paravirtual device under MoltenVK. The same test binary passes all of
them on Apple Silicon hardware (M3, macOS 27.0.1, MoltenVK 1.4.2), and Linux CI
still gates them. Run them in a separate continue-on-error step on macOS so
their results stay visible.
- macOS (`macos-26`, Homebrew libraries): builds `macos-arm64`, runs `ctest --preset macos-arm64`, and uploads the packaged binary and a `vulkaninfo` summary. The pinned shared code carries the executable bit for `tools/kazarov/scripts/build/kz-ffmpeg`.
82
82
- Windows (`windows-2025`): fetches the toolchain and the Vulkan runtime with `acquire.ps1 -VulkanLoader`, configures the test build with `configure.ps1 -Tests -VulkanLibrary`, builds it, runs `ctest -LE gpu`, and uploads the zipped build.
83
83
84
-
Cases that need a Vulkan device carry the CTest label `gpu`. The hosted Windows runner has no device, so its job excludes them with `-LE gpu`. `ctest -L gpu -N` lists them. Local runs run every case.
84
+
Cases default to a 5-second timeout. Slow GPU cases use the larger of 30 seconds and `CT_TEST_TIMEOUT`, so the Linux job gives them 60 seconds. Cases that need a Vulkan device carry the CTest label `gpu`. The hosted Windows runner has no device, so its job excludes them with `-LE gpu`. `ctest -L gpu -N` lists them. Local runs run every case.
85
85
86
86
### Game data and first run
87
87
@@ -206,18 +206,36 @@ complete image and checks the retail digits before changing game state. Files
206
206
are never removed automatically; unrelated names and temporary files are not rows.
207
207
208
208
On discovery, old `mc0/BESCES-51190dkcl<N>/BESCES-51190dkcl<N>` payloads are
209
-
copied byte for byte into `saves/`. The equivalent files from `mc1` get a `.card2`
210
-
suffix and an `(import 2)` label, keeping equal slot numbers distinct. Existing
211
-
flat files are never overwritten. A collision is considered already imported
212
-
only when the complete files have identical bytes; a different legacy save is
213
-
imported under the first unused number. Original card folders,
214
-
icons and payloads are retained. If an import cannot be written, the original
215
-
entry remains visible and can still be loaded if readable. Back up both the old
216
-
card directories, `saves/` and `.save-imports/` when moving a save root. Durable
217
-
markers in `.save-imports/` record completed imports and byte-identical card
218
-
copies, so deleting a flat save does not bring its card copy back. A failed marker
219
-
write is logged and retried on the next scan; it does not block saving. Until a
220
-
marker can be stored, deleting an imported flat file may cause it to be imported again.
209
+
copied byte for byte into new flat files in `saves/`, using exclusive creation.
210
+
The equivalent files from `mc1` get a `.card2` suffix and an `(import 2)` label,
211
+
keeping different saves at equal slot numbers distinct. Existing flat files
212
+
are never overwritten during import. If any flat save has identical complete
213
+
bytes, no new copy is needed; a different legacy save at an occupied number is
214
+
imported under the first unused number. If a possible matching flat file cannot
215
+
be compared because its metadata or contents are unavailable, copying that
216
+
legacy entry is deferred until a later scan; existing copies are kept.
217
+
218
+
After copying or finding identical bytes, the whole original card entry,
219
+
including icons and payload, is moved atomically into `imported/mc0/<name>` or
220
+
`imported/mc1/<name>`. An occupied archive name gets a numbered suffix; nothing
221
+
is overwritten or deleted. Archived entries are never scanned for import.
222
+
If copying fails, the original entry stays loadable if readable. If the archive
223
+
move fails, its original row stays listed alongside the flat copy; this identical
224
+
duplicate is temporary. While that copy still has identical bytes, a later
225
+
readable scan finds it and retries the move without creating another copy.
226
+
No import markers are read or written; any existing `.save-imports/` directory is left untouched.
227
+
228
+
Replacing a still-listed legacy row writes a new flat save with exclusive
229
+
creation and then attempts the same archive move. If the move fails, the new
230
+
save is kept and the original row stays listed. Ordinary flat saves retain
231
+
atomic replacement. While archiving keeps failing, replacing or deleting the
232
+
last matching flat copy can cause the unarchived legacy save to be published
233
+
again, including when the legacy payload becomes readable again after failed
234
+
reads. Newer saved progress is preserved; the old save can reappear as another
235
+
row. Once an identical copy is readable, later scans do not publish more copies.
236
+
Card payloads and archived originals are never modified.
237
+
When moving or backing up a save root, include `saves/`, both card directories
238
+
and `imported/`.
221
239
222
240
The payload is the existing port save image and its retail header and checksums;
223
241
no PC metadata is added. As before, it uses the host layout of `CSaveData`,
@@ -763,7 +781,7 @@ The reason for this design: the retail thread loops (`GamePadStep`, `NowLoadingL
763
781
- Frame grabs: `mgPortRenderTarget(..., frame_target = true)` makes a gfx frame target, which a display render grabs again from its own image.
764
782
- The FPS counter: `video.show_fps` turns it on (F3 and the `fps_toggle` host action flip it; headless only with `--show-fps`). It is drawn through `OverlayRecord`, as a display render's overlay, in the overlay's own font (the Dark Cloud Compendium Community Font baked into `overlay_font.inc`), so no canonical image holds it. The game's message font is not used: its texture lives only in the current mode's texture block (`ReLoadFontTexture` per mode) and `FontTex[0]` dangles across mode changes, so a badge in it would come and go with the modes. Its text is set by `video.fps_detail`; with all fields it reads `FPS 143.9 TICK 50.0/50 DRAWS 412`. TICK counts clock ticks per second, so it reads the tick rate whatever `mgFrameRate` is. DRAWS is the newest list's mesh plus 2D draws. `--log-fps` prints the same text on stderr once a second.
765
783
- Interpolation diagnostic: set `KZ_INTERPOLATION_TINT=1` to tint draws on the main presentation image (default off). Green is a matched transformed mesh; cyan is an unmatched or pose-excluded mesh carried by the display camera; red is newest-only geometry; blue is flat 2D or a screen effect; yellow is a depth-tested sprite with reprojected corners. Unchanged matched meshes remain green. This classifies geometry, not all its inputs: texture contents, lighting, fog parameters and sprite motion can still be tick-exact. Alpha tests, coverage, depth and blending are retained, so translucent passes can mix tints. Reusable intermediate render targets keep their original colours; their final composites are tinted on the main image. Canonical renders and ordinary screenshots never receive the tint. Use `--screenshot-display 8` to inspect the diagnostic at eight interpolation phases. Optional `KZ_INTERPOLATION_TRACE=<file>` appends per-draw CSV sections at alpha zero, including matching/cut reasons, texture IDs, camera IDs, target IDs, whether a draw is tinted on the main image, and changed fog/material/projection/local inputs. It writes only while tint is enabled; remove both environment variables after diagnosis.
766
-
- Outlined models render into a shared transparent image. Canonical clears and copies retain the retail draw box; presentation clears and composites cover the visible extent because the interpolated model can move outside the newest tick's box. Transparent texels in a depth-writing presentation composite do not write depth. `tools/input/area-dungeon-chest-turn.input` with `--jump dungeon:0:1 --fast-load --frames 2518 --screenshot chest.png --screenshot-display 8` exercises the unopened chest during a camera turn. For multi-part NPCs, use `--input tools/input/repro-town-npc-outline.input --jump town:10 --fast-load --frames 278 --window 1280x720 --screenshot woman.png --screenshot-display 8`; the woman's face must remain visible throughout the turn. `--input tools/input/repro-clown-outline.input --frames 27230 --window 1280x720 --screenshot clown.png --screenshot-display 8` checks the clown's face during the prologue camera move.
784
+
- Outlined models render into a shared transparent image. Canonical clears and copies retain the retail draw rectangle; presentation scratch clears and replacement vertices cover the full visible extent so the interpolated model/bone pose remains complete under the display camera, including fixed-camera mouse look. Each copy preserves its texture-to-screen offset. Fixed-look current and previous lists preserve presentation vertices and clear rectangles while leaving resource retirement ownership with their source lists. Transparent texels in a depth-writing presentation composite do not write depth. GPU regressions compare a rotating thin part at alpha 0.5 against an independently recorded horizontal pose and complete fixed-look outline pixels at alpha 1 and 0.5 against a direct full-extent reference render; translation, scratch-target reuse and transparent-depth regressions remain covered. `tools/input/area-dungeon-chest-turn.input` with `--jump dungeon:0:1 --fast-load --frames 2518 --screenshot chest.png --screenshot-display 8` exercises the unopened chest during a camera turn. For multi-part NPCs, use `--input tools/input/repro-town-npc-outline.input --jump town:10 --fast-load --frames 278 --window 1280x720 --screenshot woman.png --screenshot-display 8`; the woman's face must remain visible throughout the turn. `--input tools/input/repro-clown-outline.input --frames 27230 --window 1280x720 --screenshot clown.png --screenshot-display 8` checks the clown's face during the prologue camera move.
767
785
-`--screenshot-display N` writes N display frames of the screenshot's frame, at alpha k/N from the previous list to the last, as `<stem>-display-<k>.png`. `--screenshot-fps` writes the screenshot with the FPS counter drawn into it, at window size, from a presented frame. With `--offscreen`, nothing is presented, and the image is read back from the display image instead. The counter reaches no other written image.
768
786
-`video.max_fps=0` leaves presentation uncapped; a positive cap paces display starts without catching up in bursts. A due logic tick takes priority. FIFO follows the display's synchronization, while mailbox and immediate depend on device support. Interpolation off presents canonical frames at their game cadence; a high display cap does not increase the logic tick rate.
`port/CMakeLists.txt` builds the two halves on every platform, with `PORT` and `PAL` defined:
979
997
980
998
1. `tools/kazarov/scripts/build/port_override.py list` reads the tags in `port/src` and writes the name each tagged definition links under to `port/build/pc/port_overrides.txt`.
981
-
2. `scripts/port/unit_copy.py`, run once per unit, writes a copy of each `ps2/src` unit under `<build dir>/ps2_src` (for example `port/build/windows-x64/ps2_src`) without the overridden definitions (`port_override`'s `strip_unit`), then with the unit fixups and long64 applied. Each copy opens with a `#line 1` marker naming its `ps2/src` source, so lines keep their numbers and diagnostics point into `ps2/src`.
999
+
2. `scripts/port/unit_copy.py`, run once per unit, writes a copy of each `ps2/src` unit under `<build dir>/ps2_src` (for example `port/build/windows-x64/ps2_src`) without the overridden definitions (`port_override`'s `strip_unit`), plus the preserved retail bridges described below, then with the unit fixups and long64 applied. Each copy opens with a `#line 1` marker naming its `ps2/src` source, so lines keep their numbers and diagnostics point into `ps2/src`.
982
1000
3. `port_override.py check` stops the build if a tagged name is defined by no unit of `ps2/src`, since its replacement would be unused wherever the signatures differ.
983
1001
4. The copies are compiled with `PORT` defined, and the units in `port/src` are linked with them.
984
1002
1003
+
`unit_copy.py` also preserves the routines listed in `RETAIL_BRIDGES` under new definition names in the generated unit. It reads each body from the unstripped retail source, changes only the definition name, and appends it before the unit fixups and long64 rewrites. Calls in the original unit keep the public name, so they reach the native `PORT_OVERRIDE`; that override can add native behavior and delegate to the preserved routine. Each appended body has its own `#line` marker naming the original retail source and definition line, keeping diagnostics and debugger locations valid.
1004
+
1005
+
Use this mechanism when a native seam needs to wrap retail behavior rather than replace it, as with the camera-cut seams around `ResetViewMode` and `ResetEyeView`. Add the unit, exact signature, original name and bridge name to `RETAIL_BRIDGES`, declare the bridge's asm link name in both `port/include/stubs/<unit>.hpp` and the native caller, and call it from the public override. The bridge body is generated from retail rather than transcribed into native source; `ps2/` stays unchanged. `NativeRetailBridges` runs `scripts/port/test_unit_copy.py` through CTest, alongside the other native unit-copy Python tests.
1006
+
985
1007
Each name then has one definition. A replacement without the tag leaves `ps2/src`'s definition in, and the link fails on the duplicate symbol. A replacement takes the signature `ps2/src` gives the function: parameter types as spelled; names and default arguments may differ. By convention a replacement goes in the file that mirrors its unit.
986
1008
987
1009
A `static` function cannot be replaced, so its non-static callers are replaced instead. A `static` function guarded `#ifndef PORT` in `ps2/src` is the one exception, whether it is written in assembly (`collision.cpp`'s `pre_trance_normal`) or in C (`editmap2.cpp`'s `PlaneNormalXZ`). The port defines a non-static function of the same name, declared to the unit by `port/include/stubs/<unit>.hpp`.
Bone-3 sub-case (bug, 5.2): add weight 1 on slot 3 (offset 12, M3 = identity), with w0 = w1 = 0 and N = (0, 0, 1). Correct normal = M3.3x3 * N = (0, 0, 1). VU: `maddz vf31, vf14, vf28z` uses col1 of M3 = (0, 1, 0), so the normal is (0, 1, 0). Expected 0 in z, observed 1 in y.
1038
+
Bone-3 sub-case (bug, 5.2): add weight 1 on slot 3 (offset 12, M3 = identity), with w0 = w1 = 0 and N = (0, 0, 1). Correct normal = M3.3x3 * N = (0, 0, 1). VU: `maddz vf31, vf14, vf28z` uses col1 of M3 = (0, 1, 0), so the normal is (0, 1, 0). Expected 0 in z, observed 1 in y. The shared `GfxGpuMeshDc2.SkinnedFourthInfluenceNormal` regression covers this sub-case with a y-light output of 100 and a z-light output of 0.
0 commit comments