Files
openroller/docs/re_gc_test_mode.md
tsuki 831d96e562 Initial public source release
Split reusable rendering and format support into vectorail-core and vectorail-gc.
2026-08-02 17:05:27 +02:00

311 lines
13 KiB
Markdown

# game471 test mode
This note describes the operator/test mode in the arcade `game471.exe`
(4.74.00ENG). The structural and layout observations below come from the
executable and its shipped resources, not from screenshots.
## Live entry
The game exposes twenty logical cabinet inputs through `FUN_00634060`
(`0x00634060`). Logical input 0 is the TEST switch.
During normal operation, the main task at `FUN_006396f0` samples input 0 on
every update. Holding it for at least three updates starts the test-mode
transition. The game then:
1. stops the normal game/audio/render tasks;
2. waits 45 updates;
3. constructs the test-mode text renderer, sprite renderer, input adapter,
SE adapter, and BGM adapter;
4. initializes the test-mode form system and opens the main form.
The already generated `game471_bootskip.exe` can reach the original menu under
Wine. Once the `GameWare` window is active, press and briefly hold Caps Lock.
Caps Lock is the executable's built-in DirectInput fallback for the TEST
switch; no test-mode-specific patch is needed.
The original executable's keyboard fallback table starts at `0x007840f0`:
| Logical input | Cabinet control | FAST I/O mask | DirectInput fallback |
| --- | --- | ---: | --- |
| 0 | Test switch | `0x00000040` | `DIK_CAPITAL` (Caps Lock) |
| 1 | Service switch | `0x00000001` | `DIK_F1` |
| 2 | Coin switch | `0x00000004` | `DIK_F2` |
| 3 | Select switch | `0x00000010` | `DIK_F3` |
| 4 | Enter switch | `0x00000020` | `DIK_RBRACKET` (`]`) |
| 5 | Left booster up | `0x00000100` | `DIK_Q` |
| 6 | Left booster down | `0x00000200` | `DIK_A` |
| 7 | Left booster left | `0x00000400` | `DIK_LCONTROL` |
| 8 | Left booster right | `0x00000800` | `DIK_S` |
| 9 | Left booster button | `0x00100000` | `DIK_LMENU` (left Alt) |
| 10..14 | Right booster controls | `0x00010000` through `0x00200000` | raw scan codes `78,7d,7a,7b,6a` |
`FUN_00633d00` obtains the FAST I/O bitfield and masks it with the first table
above. `FUN_00633de0` applies the per-input active-high/active-low table.
`FUN_00634060` ORs that result with the DirectInput fallback.
## Test-mode input adapter
`GWTestModeInput_GW` is constructed by `FUN_00569d00`. Its update method,
`FUN_00569d20`, translates cabinet inputs to the generic test-mode flags:
- Test switch -> `0x20` (back/exit)
- Select switch -> `0x02`
- Enter switch -> `0x58`
- left booster up/down/left/right/button -> `0x01`, `0x02`, `0x84`, `0x48`,
`0x10`
The language table describes the intended behavior:
- booster up/down or Select moves the cursor;
- booster buttons or Enter confirms;
- booster left/right or Enter changes a setting;
- Test returns/backtracks.
Short synthetic X11 key taps may be sampled for several game updates because
the original runs uncapped. For automated navigation, inject an explicit
cabinet state for one update instead of relying on `xdotool key`.
## Resources
Test mode does not use the normal RVB/MTX menu assets. Its strings come from:
- `data/TestModeLaungage/Laungage_eng_sjis.csv`
- `data/TestModeLaungage/Laungage_jpn_sjis.csv`
Both are CP932/Shift-JIS. `Laungage` is the spelling used by the shipped game.
The glyphs themselves come from `data/font/Font.mtf` and the
`data/font/FontXXXXXXXX.mfi` atlas pages. `GWTestModeRenderText_GW`
constructs a pool of 256 regular GameWare text objects backed by that font.
It does not use a test-mode-specific bitmap font or GDI text.
`Font.mtf` starts with a 16-way Unicode radix table. A resolved entry stores
the MFI page in its low 24 bits and the high nibble of the atlas cell in bits
28..31; the low cell nibble comes from the Unicode codepoint. Each MFI contains
a 512x512 DXT5 atlas split into 16x16 cells. This is confirmed by
`FUN_00485f90`, which creates GameWare image format 3, and by
`GWPCImage2D::Create`, whose format table maps index 3 to `D3DFMT_DXT5`.
The font shader uses the DXT5 alpha channel as glyph coverage. The English
ASCII glyphs use page 0 and occupy the left half of each cell, producing 8x16
glyphs. The selection marker is U+2192 (`→`), resolved by the MTF to page 27,
cell `0xb2`, and is 16x16.
The renderer is a separate, mostly text-based framework represented by the
following RTTI classes:
- `GWTestModeWindowText`, `GWTestModeWindow`, `GWTestModeWindowList`
- `GWTestModeForm`, `GWTestModeSelectForm`
- `GWTestModeForm_YesNo`, `GWTestModeForm_ProcYesNo`
- `GWTestModeInput_GW`
- `GWTestModeRenderText_GW`, `GWTestModeRenderSprite_GW`
- `GWTestModeBGM_GW`, `GWTestModeSE_GW`
The framework screen is 720x1280 on black. `FUN_00577940` installs a logical
text size of 16x16, pure red `(1,0,0,1)` for the selected/help color, and pure
cyan `(0,1,1,1)` for normal selectable entries. The three centered common
title windows use normalized y coordinates `-0.95`, `-0.925`, and `-0.9`,
which map to pixel y positions 32, 48, and 64.
The main list is anchored at normalized y `-0.725` (176 px). Its default row
gap is `0.01` of the 640-pixel half-height, so rows advance by
`16 + 6.4 = 22.4` pixels. The selected row is red and has a separately drawn
`→`; unselected rows are cyan. The help form is centered at normalized
y `0.2` (768 px), while the main five-line build/machine/time information list
is centered at y `0.4` (896 px).
## Main menu in 4.74.00ENG
The live build displays:
1. Monitor Test
2. Input/Output Test
3. LED Test
4. Card Test
5. Audio Settings
6. Game Settings
7. Network Info
8. Bookkeeping
9. System Info
10. Restore factory settings
11. Exit Test Mode
The CSV also contains `Check Input Count`, `System Settings`, `Delete High
Scores`, and `Machine Connection Test`, but prefixes those entries with `#`.
It contains `Update Online` without `#`, although that item is still filtered
out by this build's runtime conditions.
## Forms and data exposed
- Monitor Test: color bars, white, red, green, blue, and cross-hatch patterns.
- Input/Output Test: all cabinet switches, both five-input boosters, headphone
volume/jack state, and Groove Stage connection. Booster buttons drive their
lamps; the coin switch changes lockout.
- LED Test: all lights and individual title, side, and booster light groups.
- Card Test: card-reader status and card ID.
- Audio Settings: test BGM, master/headphone/demo levels, five speaker channels,
Groove Stage status, and normal/demo vibration intensity.
- Game Settings: coin/song price, per-day operating hours, and score-attack
mode.
- Network Info: location identity/address/IP, cabinet IP/MAC, NESYS versions,
and relay server.
- Bookkeeping: uptime/play totals, free plays, service-switch count, player
statistics, 30-week and hourly histograms, play logs, the last twenty errors,
and service-switch history.
- System Info: program/system configuration values.
- Factory Settings: confirmation form followed by persistent-data reset.
Persistent test-mode data has separate RTTI classes for system settings, high
scores, play/weekly/daily/error/service-switch logs, and game-unique data. Log
paths embedded in the executable live under `TestModeFile\...\Log`.
### Coin/song presets
`CTestModeForm_GameSetting` chooses its coin/song table from the Type X
`HKLM\SOFTWARE\taito\typex\Country` registry value. `Country == 0` uses eight
entries, in this exact order:
1. 1 coin, 2 songs
2. 1 coin, 3 songs
3. 2 coins, 2 songs
4. 2 coins, 3 songs
5. Free play, 1 song
6. Free play, 2 songs
7. Free play, 3 songs
8. 1 coin, 1 song
For a non-zero country value it instead exposes 39 entries: every combination
of 1 through 12 coins with 1 through 3 songs, followed by the three free-play
variants. `FUN_00570a70` and `FUN_00570810` wrap the selected index forward and
backward respectively.
OpenRoller mirrors the registry value with `Country` in `openroller.cfg` beside
the executable. CMake creates the file on the first build but leaves an
existing copy untouched. The value is reread whenever test mode is entered, so
the application does not need to be rebuilt after changing it.
## Useful code addresses
| Address | Role |
| ---: | --- |
| `0x006396f0` | top-level boot/game/test-mode state machine |
| `0x00634060` | logical cabinet input + DirectInput fallback |
| `0x00633d00` | mask current FAST I/O input bitfield |
| `0x00633de0` | apply active-level table |
| `0x00569d00` | construct `GWTestModeInput_GW` |
| `0x00569d20` | translate game inputs to generic test-mode input flags |
| `0x00569610` | construct `GWTestModeRenderText_GW` |
| `0x00569ad0` | construct `GWTestModeRenderSprite_GW` |
| `0x00569440` | construct `GWTestModeSE_GW` |
| `0x005693f0` | construct `GWTestModeBGM_GW` |
| `0x00577940` | initialize test-mode form/render state |
| `0x00634fd0` | credit/service-credit handling and `SERVICE-SW LOCKED` |
`SERVICE-SW LOCKED` is unrelated to entering test mode. It is emitted by the
credit controller after repeated Service-switch credit pulses and holds the
service-credit lock for 180 updates.
## LED output pipeline
The LED test form's main methods are:
| Address | Role |
| ---: | --- |
| `0x0056e160` | construct `CTestModeForm_LEDTest` |
| `0x0056e390` | update the selected LED test pattern |
| `0x0056e6c0` | handle the seven LED-test menu items |
| `0x0062b0e0` | set logical LED `index` to `R,G,B,alpha` |
| `0x0062d4d0` | construct all logical LED objects and their hardware mapping |
| `0x0062a4d0` | update one `CLedDevice` and place its value in the cabinet output buffers |
| `0x004b3b40` | write one RGB pixel into the shared board buffer |
| `0x004b4c50` | pack shared LED buffers into the FIO transfer blocks |
| `0x004b55f0` | exchange registers and transfer blocks with `iDmacDrv32` |
| `0x004b6700` | wrapper for `iDmacDrvRegisterBufferWrite` |
### Logical groups
There are 118 logical LED objects:
| Logical indices | Test-mode group | Hardware coordinates |
| ---: | --- | --- |
| `0` | left booster button | simple output 4 |
| `1..8` | left booster RGB group 8 | board 0, group 8, positions 0..7 |
| `9..40` | left booster RGB groups 0..7 | board 0, groups 0..7, positions 0..3 |
| `41` | right booster button | simple output 5 |
| `42..49` | right booster RGB group 8 | board 1, group 8, positions 0..7 |
| `50..81` | right booster RGB groups 0..7 | board 1, groups 0..7, positions 0..3 |
| `82..93` | title | board 2, strips 0..1, pixels 0..5 |
| `94..105` | left side | board 2, strips 2..3, pixels 0..5 |
| `106..117` | right side | board 2, strips 4..5, pixels 0..5 |
The menu implements the groups directly:
- item 0 cycles all 118 LEDs through off, white, red, green, and blue;
- items 1, 2, and 3 fill the twelve title/left/right-side LEDs white one
address at a time, then clear them in the same order;
- items 4 and 5 perform the same fill/clear sequence across 41 LEDs on the
corresponding booster;
- item 6 starts the form-exit sequence.
The LED screen itself is only the generic seven-row `GWTestModeWindowList`,
anchored at y `-0.725` with an explicit row gap of `0.05` (48 px row
advance). It does not draw a schematic of the cabinet or booster rings.
The all-light loop special-cases logical LEDs 0 and 41 because the booster
button lamps are single-channel. For those two, it ORs `R|G|B` and writes the
same intensity as a simple lamp.
The executable establishes 40 independently controlled RGB positions plus one
simple button lamp per booster. The `group/position` coordinates above describe
the software/FIO mapping only; they do not establish the physical arrangement
of those RGB positions inside the arcade booster's plastic assembly.
### Color representation
`FUN_004b3b40(board, strip, pixel, R, G, B)` stores each full-color LED as
three bytes in this order:
```text
B, R, G
```
Each board reserves `9 * 8 * 3 = 0xd8` bytes even where fewer than eight
pixels are connected. Before placing a value in the hardware buffer,
`CLedDevice` applies the executable's integer gamma curve independently to
each channel:
```text
wire_channel = min(channel * channel / 255, 255)
```
The alpha/intensity field is applied by the LED object before this final
conversion. Test mode always passes alpha `0xff`.
### iDmac/FIO transfers for board type 0x825c
The current shim reports FIO type `0x825c`; the game selects its `0x25c`
packing branch. Each hardware update eventually makes these calls:
| Register-buffer address | Bytes | LED content |
| ---: | ---: | --- |
| `0x5000` | `0x1b0` | general FIO block; title bytes at offset `0x168`, button lamps at offsets 4 and 5 |
| `0x5200` | `0x1e0` | general FIO block; left/right-side bytes at offset `0x168` |
| `0x5400` | `0x0d8` | raw board-0 buffer: left booster |
| `0x5600` | `0x0d8` | raw board-1 buffer: right booster |
The board-2 source buffer is split as follows:
- its first `0x30` bytes (two 8-pixel strip slots) go to
`0x5000 + 0x168`;
- its following `0x60` bytes (four 8-pixel strip slots) go to
`0x5200 + 0x168`.
The unused bytes in those strip slots remain zero. This was confirmed both in
the decompiled `GWInputDeviceXioFio_BOOST` update path and in the live
`idmac_shim.log`: normal steady-state writes have sizes `1b0`, `1e0`, `d8`,
and `d8` respectively. Initial device negotiation first sends four `0x200`
byte blocks, then switches to the exact steady-state lengths above.
The game also contains a `0x23c` packing branch and generic transfer banks
through `0x5e00`, but those are not selected by the current emulated hardware
ID.