Commit Graph
13 Commits
Author SHA1 Message Date
CydandClaude Fable 5 9cca7c77bd serial: pipe:vrio named-pipe transport, no com0com needed
NamedPipeTransport connects as a client to vRIO's \\.\pipe\vrio (the
DOSBox-X fork's role) and speaks the shared typed-frame contract
(PipeFraming: 0x00 data / 0x01 modem lines, null-modem crossed). The COM
path's DTR reset pulse is replayed in-band on connect. Peer disconnects
and framing violations surface as the 0-byte transport-closed read the
link already understands.

RioTransportFactory routes endpoint strings — pipe:name (vRIO's own
picker syntax) to the pipe transport, everything else to
SerialPortTransport — and is wired into RioCoordinator and all three
RioSerialMonitor modes, so profiles (RioComPort/DefaultRioComPort) and
the bench tools take pipe endpoints anywhere a COM name went.

Gotcha baked into the design: named pipes here have 0-byte buffers, so a
write blocks until the peer reads it, and vRIO also writes its lines
frame before reading — the on-connect pulse frames are therefore queued
as overlapped writes (pipe writes drain in issue order, preserving the
edge positions) instead of blocking the constructor into a mutual
write-first deadlock.

Verified end-to-end against the real VRioDevice + VRioPipeService over
\\.\pipe\vrio: version 4.2 + check replies, 137 analog polls, lamp
commands ACKed, zero framing errors. 322 tests green, both flavors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 10:14:13 -05:00
CydandClaude Fable 5 3512c89dca tray: --import-profile merges a profile document into the user config
RioJoy.Tray --import-profile <profile.json> merges a single-profile
document (a repo's profiles/*.json) into %APPDATA%\RIOJoy\config.json
via ConfigStore.ImportProfile: same-name profiles are replaced in place
(FindProfile's case-insensitive convention), everything else appended,
all other config content preserved. Refuses (exit 3) while a tray
instance is running - a live tray rewrites the config from memory and
would silently discard the import. Documents that a Name must be stated
in the file itself: RioProfile defaults Name to 'Unnamed', so the import
checks the raw JSON, not the deserialized object. This is the profile
install story for bench machines and pods (previously: hand-paste into
the Profiles array).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 09:33:25 -05:00
CydandClaude Fable 5 2f2438717a core: per-profile ViGEm axis routing with unipolar output mode
RioProfile gains a nullable AxisRouting section mapping each calibrated
axis (X/Y/Z/Rx/Ry/Rz) to a pad target (thumbs, triggers, or None) with a
Centered or UnipolarPositive conversion; the default reproduces the old
hardcoded routing exactly, so existing profiles are untouched. Routing
resolution lives in a pure, ViGEm-free AxisRouter for testability; the
sink neutralizes the pad on routing change so stale trigger state cannot
leak across profile switches.

Motivation: Descent reads the pad via SDL GameController, where the
triggers are its stock fire axis-buttons - the old fixed routing put
throttle on LeftTrigger (fires) and detent would have read as full
reverse. descent-d1x.json now routes Z->RightThumbY (UnipolarPositive,
detent = center) and Rz->RightThumbX, triggers untargeted; guarded by
tests that parse the shipped JSON through the real deserializer and
byte-compare the dxx-rebirth reference copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 09:00:09 -05:00
CydandClaude Fable 5 30c1a85445 Move RIO hardware + firmware archive to the TeslaRel410 repo
The physical RIO board docs (photos, schematics, GAL decode) and the
board firmware (dumps, disassembly, make_patch.py, RIO 4.3 + FastRIO
images, analysis, testlogs) describe hardware shared across all Tesla
cockpits with a lifecycle independent of this Windows app — a
native-game-only cabinet runs the firmware and never touches RIOjoy.
They now live in TeslaRel410/restoration/{rio-hardware,rio-firmware}
with full history preserved (git subtree).

Kept here: docs/PROTOCOL.md (this app's interface contract) and the
RioSerialMonitor bench harness (--mash/--e0test, C# on RioJoy.Core).
README + code comments now point at the TeslaRel410 archive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:50:46 -05:00
CydandClaude Fable 5 38defb5246 FastRIO certified: 31250v2 final runs clean; config plumbing for baud/poll
Operator-verified zero lamp misses/hangs through the slow-chord test
(62500's killer) and 392/min mash. Final run: NAK=0, 25 timeout resends
silently healing inbound response loss, counters flat, zero wedges.
--poll 20 delivered ~31Hz analog (1.7x legacy); the shortfall vs 50Hz
is additive host pacing (delay after awaited exchange), not board
saturation - noted as a future host tweak.

AppConfig gains RioBaudRate (default 9600) + AnalogPollMs (default 55),
plumbed through RioCoordinator; existing configs unchanged. Production
matrix recorded in ANALYSIS.md: 9600 patched chip for native-game
cabinets, 31250v2 + poll 20-25 + FTDI for RIOJoy cockpits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 14:19:30 -05:00
CydandClaude Fable 5 1ff0b16015 Phase 8A (2/2): RioJoy.Core multi-targets net48 + net40 (Windows XP flavor)
- TargetFrameworks net48;net40. net48 keeps x64 + ViGEm + System.IO.Ports
  package; net40 adds Microsoft.Bcl.Async + System.ValueTuple and uses the
  in-box SerialPort.
- Compat/TaskCompat bridges Task.Run/Delay/WhenAny/WhenAll (TaskEx on
  net40) and SemaphoreSlim.WaitAsync (net40 blocks briefly - trivial at
  9600 baud).
- IReadOnlyList/IReadOnlyDictionary -> IList/IDictionary throughout
  (net40 predates the IReadOnly* interfaces and the Bcl backport cannot
  make arrays implement them).
- HashCode.Combine replaced with a manual combine (Bcl.HashCode has no
  net40 build); Marshal.SizeOf<T> -> typeof form; ViGEmJoystickSink
  gated #if !NET40. HidFeederJoystickSink stays on both flavors - it
  will drive RioGamepadXP.sys on XP via the same contract.

Both TFMs build; 275 tests green on net48.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:41:58 -05:00
CydandClaude Fable 5 3b2af7b79a Phase 8A (1/2): de-Span the serial layer, swap JSON to Newtonsoft
Prepares RioJoy.Core for the net40 (Windows XP) target, which has no
System.Memory, ValueTask, or System.Text.Json:
- IRioTransport and the whole protocol/framing layer now use byte[] +
  Task (RioPacket.Payload, PacketParser/Builder, RioChecksum, replies,
  AnalogReport, RioHidReport). At 9600 baud Span bought nothing; the
  SerialPortTransport bridge copies disappear entirely.
- ConfigStore/OverlayTemplateStore switch to Newtonsoft 13 with the
  same conventions (indented, PascalCase, string enums, null-skipping);
  verified against the real STJ-written config.json and regions.json
  (load + round-trip). System.Memory and System.Text.Json packages
  dropped.

275 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:36:19 -05:00
CydandClaude Opus 4.8 b275892451 Remove auto-start: launcher owns the app lifecycle
The TeslaConsole launcher starts and stops RIOJoy, so the app no longer
registers any auto-start entry:
- install-rio.ps1: drop the HKLM ...\Run registration and the post-install
  launch (and the -NoLaunch param).
- Tray: remove the "Start with Windows" menu item and delete AutoStartManager
  (HKCU ...\Run writer).
- AppConfig: drop the now-inert AutoStart field (+ test).
- Docs (PLAN.md, README-DEPLOY.txt) updated to reflect launcher-managed start/stop.

uninstall-rio.ps1 still clears any leftover Run entry from older auto-starting
builds. Solution builds; 241 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 14:36:32 -05:00
CydandClaude Opus 4.8 fe87c79f55 net48 port (test branch): retarget all projects to .NET Framework 4.8
Retargets RioJoy.Core/Overlay/Tray + tests from net8.0-windows to net48 so
the app can be tested as a framework-dependent build (relies on the in-box
.NET Framework 4.8 on Windows 10/11). Builds clean; all 241 tests pass.

Polyfills (no behavior change):
- PolySharp source generator for init/records/Index/Range/required members.
- System.Memory, System.Text.Json, Microsoft.Bcl.HashCode,
  System.Threading.Channels (tests) NuGet packages.
- Compat/Net48Polyfills.cs: GetValueOrDefault, KeyValuePair.Deconstruct,
  Math.Clamp; tests/TestPolyfills.cs: Task.WaitAsync.

Source adjustments for APIs absent on net48:
- ArgumentNullException/ArgumentException.ThrowIf* inlined to manual guards.
- Convert.ToHexString, Encoding.Latin1, Environment.ProcessPath,
  ApplicationConfiguration.Initialize, Enum.GetNames<T>/GetValues<T>,
  string.StartsWith(char), string.Split(char, opts), TextBox.PlaceholderText,
  PeriodicTimer, Memory-based Stream Read/WriteAsync, array range-slicing.
- Dropped [SupportedOSPlatform] hints (net48 is single-platform).

deploy/build-package.ps1: publish framework-dependent (no self-contained).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 12:34:47 -05:00
CydandClaude Opus 4.8 4994ab699f Editor cockpit rework + dormant-by-default serial port use
Profile editor:
- Cockpit-style encoder gauges centered over the Upper Middle MFD: Z left,
  X/Y right, and L/R/Rz arranged as a big "U".
- [JoyStick] section surfaced as a vertical "Axis" toggle column (6 inverts
  + ZR mix), written back to the profile's calibration.
- Tighten the panel layout (close gaps, trim empty border columns); colour the
  Secondary/Screen columns yellow; rename the "Joystick / Hat" board to
  "Joystick"; add an "Import .ini..." tray entry.
- "RIO commands (live)" button group (all RioCommandCodes) fired at the live RIO.

Serial-port handling:
- Start dormant; only take the COM port for a profiled game (auto-switch) or
  while editing, so the native games can open the port without clashing.
- Editor sessions hold the port but route keyboard/mouse/joystick to no-op
  sinks (NullInputSink), so button function can be checked without injecting
  input; RioRuntime.ButtonActivity drives a live cyan press indicator and
  lamp feedback still applies.
- AutoSwitchWatcher.Reset() re-syncs the watcher after an editor session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 22:55:53 -05:00
CydandClaude Opus 4.8 a4a3b58f7e INI import: strip greeting quotes; add gsheet reference
Strip a surrounding pair of double quotes from the [Plasma] Greeting on
import (the gsheet "INI_V2" output writes Greeting="..."), and add the
master button-layout spreadsheet that documents the RIO.ini format and the
16-bit map-word encoding. Test covers the real sheet output (modifier-stacked
words, keypad VK words, RIO command words) round-tripping verbatim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 22:55:36 -05:00
CydandClaude Opus 4.8 8d2d0b71aa Phase 7: cockpit overlay generator, region extraction, and profile editor
Overlay generator (pure, tested) — RioJoy.Core/Overlay:
- FontFitter ports the legacy calc-fontsize auto-fit search (validated against a
  brute-force oracle); OverlayLayoutEngine ports create-data-layer's fit + h/v
  justification and adds per-region 90 deg CCW rotation; OverlayTemplate/Region +
  OverlayTemplateStore hold cell geometry/colour/rotation (regions.json).
- GoobieDataImporter parses the legacy .data label sheet into label rows.
- src/RioJoy.Overlay: SkiaSharp rasterizer (SkiaTextMeasurer shared with the engine
  so measured layout == drawn output; SkiaOverlayRenderer -> PNG;
  ProfileWallpaperGenerator). Verified end-to-end on the real cockpit art
  (regions.json + riojoy.png + TEST.data) by OverlayRenderIntegrationTests.
- RioJoy.Tray/WallpaperApplier applies via SystemParametersInfo; RioCoordinator
  generates+applies on profile activation (opt-in via AppConfig.OverlayTemplatePath).

Region geometry — tools/XcfRegionExtract parses riojoy.xcf (a GIMP-format binary
reader, no GIMP needed) into the 119-cell regions.json: per-layer offsets/size/
font/colour, with 90 deg CCW rotation on a-00 + b-10..b-1F. Base image riojoy.png.
NOTE: the wallpaper positions are a VGA chroma-split display artifact (6 displays),
not the cockpit's logical layout.

Mapping editor (first cut) — RioJoy.Tray/Editor/ProfileEditorForm renders the
config sheet's logical grid (SheetLayout parses the sheet CSV export). The iRIO
word is edited via ButtonBinding <-> RioMapEntry.Create (no hex bit-twiddling), with
a context-sensitive picker: keyboard keys by name (KeyCatalog VK names), joystick
Button N, hat direction, mouse/RIO-command enum names; modifiers only for keyboard.
Opened from the tray ("Edit profile..."). The sheet-grid layout is a starting point
that needs rework from a better-formatted source.

~220 xUnit tests green. Docs (PLAN.md, README) updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:13:48 -05:00
CydandClaude Opus 4.8 1348040e1c Phase 5: tray app + profiles + runtime wiring
Wire the Core pieces into a runnable tray app with per-game profiles and the
three-state serial-yield auto-switch:

- Profiles/: RioProfile + AppConfig model; ConfigStore (System.Text.Json,
  round-tripped); RioIniImporter ports the legacy RIO.ini (button table, invert
  flags, plasma greeting); AutoSwitchResolver + AutoSwitchWatcher resolve the
  foreground executable into Yield (native game) / Activate (profile) / Idle, with
  native always winning and change-only notifications. IForegroundProcessProvider
  abstracts the OS.
- RioRuntime assembles a profile's live pipeline: serial ButtonPressed/Released +
  KeyPressed/Released → InputRouter (via RioAddress); AnalogReply → AxisCalibrator
  → the six joystick axes; RIO commands → calibration resets + version/check
  requests + lamp re-init. SerialLampSink sends lamp feedback over the link;
  NullJoystickSink is a placeholder until the Phase 1 HID feeder exists.
- RioJoy.Tray: NotifyIcon menu mirroring the legacy console menu (axis resets,
  version/status, raw-axes & poll-rate toggles, quit) + profile selection
  (auto vs. manual) + "start with Windows"; RioCoordinator owns the serial
  acquire/release tied to the watcher (native-game COM-port yield). OS adapters:
  ForegroundProcessProvider (Win32 foreground PID→exe) and AutoStartManager (HKCU
  Run key).
- tests: 18 new xUnit tests (123 total) for config round-trip, ini import,
  the three-state resolver + watcher, and RioRuntime end-to-end over the fake
  transport (button→joystick, keypad-offset→keyboard, analog→six axes).

The joystick output stays a no-op until the Phase 1 driver; on-cabinet
verification of the acquire/release lifecycle remains.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:36:58 -05:00