Add portable Windows build and update GC runtime

This commit is contained in:
2026-08-15 13:01:11 +02:00
parent cd300cd554
commit 623ef9a733
13 changed files with 580 additions and 60 deletions
+49 -10
View File
@@ -70,20 +70,21 @@ The file is big-endian. The initial words are not all section offsets. Confirmed
| 2 | track points |
| 3 | notes |
| 4 | camera |
| 5 | particles |
| 5 | `TuneBGEffectData` / FlowItem keys |
| 6 | visualizer |
| 7 | unknown section |
| 7 | background texture names and image keys |
| 8 | first color table |
| 9 | objects |
| 10 | scalar/unknown; often `0x30`, not an offset |
| 11 | second color table |
| 11 | points into the post-object extension offset table |
| 12 | scalar/unknown; can accidentally look like an in-file offset |
This matters because treating every plausible header word as an offset can split the notes or camera section at a false boundary.
Older 11-word stage headers instead store the second color-table offset in
slot 10. The scalar/offset/scalar arrangement in slots 10..12 belongs to the
newer 13-word format used by the 4.71-era charts.
The post-object extension base is the reader position after the variable
object stream. Relative offset slot 1 extends background colors and slot 2
stores object parents. Deriving that base from consumed bytes, as Android
`LoadBGData` does, also handles the small mobile chart revision correctly.
## Note array: confirmed wire layout
@@ -199,8 +200,15 @@ BPM active at the note timestamp. With `beat_ms = 60000 / bpm`:
| ---: | --- |
| `+6` | signed marker-effect/UV selector; the note-head draw passes `value - 1` to effect 3 |
| `+39` | appearance lead in beats; runtime `+0xbc = max(time - value * beat_ms, 0)` |
| `+37` | enable positional fly-in |
| `+38` | draw the additive fly-in trail |
| `+43` | fly-in interpolation start, in beats after appearance |
| `+47` | fly-in interpolation end, in beats before the hit time |
| `+51` | duration in beats for types 3/4/5/10/15; runtime `+0x58` and `+0xac = time + value * beat_ms` |
| `+55` | packed authored `RRGGBBAA` colour used by duration-target geometry |
| `+55` | primary packed authored `RRGGBBAA` target colour; also used by duration geometry |
| `+59` | secondary packed `RRGGBBAA` colour |
| `+63` | integer fly-in oscillation count |
| `+67` | fly-in oscillation endpoint/phase scalar |
| `+71` | number of generated MERRY GO ROUND targets |
| `+75` | MERRY GO ROUND spacing in beats |
@@ -333,7 +341,7 @@ and the displaced point with the camera evaluated at the note timestamp, then
uses `RotateHPB::SetVector` (`atan2(screen_dx, screen_dy_down)`) and stores the
resulting screen angle. It is computed before gameplay and remains fixed while
the live camera moves. Effect 39 resolves to a 128x32 animated strip whose
arrows extend to the sides of the 32x32 head; it uses the same `0.025` scale,
arrows extend to the sides of the 32x32 head; it uses the same `0.025` scale
colour and marker alpha. This is independent of the lower-right control
helper, even though that helper also has a direction overlay.
@@ -366,8 +374,13 @@ synthetic pulsing rings are no longer used for decoded GC stages.
Duration bodies are also type-specific in the executable:
- HOLD calls `0x00647cf0` and SLIDE HOLD calls `0x00641fd0`; both emit ribbon
triangles rather than an OpenGL-style line.
- Android HOLD does not submit the prebuilt ribbon array. Its normal gameplay
branch calls `DrawWay(max(current_ms, appear_ms), end_ms, color, color)` with
additive blending and a beat-reactive width from 3 to 5 pixels. This exact
moving timestamp boundary is why dropping whole prebuilt samples produces a
visibly stepped disappearance. Arcade still constructs HOLD helper geometry,
but it is not evidence for the mobile draw path.
- SLIDE HOLD calls its prebuilt triangle-ribbon path.
- SCRATCH is sampled every `0.15` world units. `0x005ebaa0` derives two
opposing paths with radius `0.2` and rotates the offset by 45 degrees per
sample; `0x00641d50` emits six vertices per segment for each path.
@@ -391,6 +404,32 @@ Duration bodies are also type-specific in the executable:
screen direction. Both endpoint layers inherit the long note's colour, fade alpha,
billboard transform, beat-synchronised animation and `0.025` sprite scale.
### Positional fly-in
`GameScene::DrawMark` converts wire `+25/+29/+33` into direction vector `D` and
starts from the authored route position `P`. With:
```text
start = max(0, appear_ms + field_43 * beat_ms)
end = max(0, note_ms - field_47 * beat_ms)
u = 0 before start, (now-start)/(end-start) in between, 1 at/after end
```
`0x005ebaa0` first stores runtime `+0xc0 = ROUND(appear_ms / frame_ms)`.
`0x0064ab80` then computes the start from that stored integer, but computes the
end from the still-fractional `note_ms / frame_ms`; each final boundary passes
through another nearest-integer conversion at `0x0050a4a0`. The live clock is
quantized the same way.
Flag `+37` selects positional interpolation. With no oscillations the displayed
position is `P + D*(1-u)`. When integer `cycles = +63` is positive, let
`c = +67` for values up to 1 and `c = 2-(+67)` otherwise; then position is
`P + D*(c - abs(sin(u*(asin(c) + cycles*2*pi))))`. Integer cycles guarantee
that the marker lands back on `P` at `u=1`. The approach circle follows the
same moved position. Flag `+38` draws an additive line from the initial external
anchor to the moving marker before the hit time. The player preserves that
nearest-frame 60 Hz quantization rather than smoothing the movement on the
audio clock.
## Tap judgment timing
The system-config loader at `0x00635c90` lays out the timing overrides in four