Files
BT412/handoff/padrio/reference/L4RIO.h
T
CydandClaude Opus 4.8 2e475f4575 Handoff: PadRIO module + implementation guide for BT411
A self-contained kit so the BT411 author can add cockpit-less play (XInput
pad + keyboard driving the stock RIO path) upstream. Under handoff/padrio/:

  - PADRIO-IMPLEMENTATION.md -- the guide: the RIOBase seam (split RIO into an
    abstract control surface + serial RIO + PadRIO), the three-file integration
    (L4RIO split, L4CTRL.h rioPointer -> RIOBase*, the L4CTRL.cpp PAD token ->
    primaryControlType=PrimaryRIO so MechRIOMapper engages unchanged), the
    build/XInput note, the bindings.txt format, config, and verification.
  - src/ -- the two NEW drop-in files (L4PADRIO.*, L4PADBINDINGS.*).
  - reference/L4RIO.h -- the header after the RIOBase split.
  - bindings.default.txt -- a sample of the profile written on first run.

The guide also documents the two game-side gotchas BT411 shares and will hit
when a real device first drives the mapper: (1) keyboard bring-up bridges must
stand down when rioPointer != 0, and (2) the .CTL streamed mapping resolves one
attribute slot early under the shorter WinTesla parent chain (stick writes
throttle) -- a LATENT real-pod bug PadRIO exposes; fix = a named pad slot at the
front of the mapper's AttributePointers[] (reconstruction-gotchas §11).

Handoff files only; does not touch the BT411 remote.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:22:22 -05:00

390 lines
7.7 KiB
C++

#pragma once
#include "l4pcspak.h"
#include "..\munga\notation.h"
#include "..\munga\average.h"
// These are purposely grouped as two parameters in one equate
// (See PCSPAK.HH).
// The standard PC COM ports ALWAYS use these combinations.
//#define RIO_COM1 PCSP_COM1
//#define RIO_COM2 PCSP_COM2
//#define RIO_COM3 PCSP_COM3
//#define RIO_COM4 PCSP_COM4
class FilterChannel SIGNATURED
{
public:
static const char *highName;
static const char *centerName;
static const char *lowName;
static const char *joystickXName;
static const char *joystickYName;
static const char *throttleName;
static const char *leftPedalName;
static const char *rightPedalName;
enum AlignMode { unaligned, normal };
enum PolarMode { unipolar, bipolar };
FilterChannel();
FilterChannel(
NotationFile *init_file,
const char *page_name,
int default_min, int default_max
);
FilterChannel(
NotationFile *init_file,
const char *page_name,
int default_min, int default_center, int default_max
);
~FilterChannel();
void
SetPolarity(PolarMode newPolarity);
void
SetDeadBand(Scalar amount_of_deadband);
virtual void
BeginAlignment();
virtual void
EndAlignment(NotationFile *init_file=NULL);
virtual Scalar
Update(int value);
Logical
TestInstance() const;
protected:
void
CalculateDeadBands();
const char *pageName;
PolarMode polarity;
AlignMode mode;
int previousValue, min, max, center;
int lowerRange, lowerDeadband;
int upperRange, upperDeadband;
Scalar deadbandScalar;
Logical valuesFromFile;
AverageOf<int> average;
};
class RIO;
class Ranger SIGNATURED
{
friend class RIO;
protected:
Ranger(
const char *page_name,
int default_min,
int default_max,
Scalar deadband_scalar
);
~Ranger();
void
ForceToZero();
void
SetDeadBand(Scalar amount_of_deadband);
Scalar
Update(int input);
Logical
TestInstance() const;
void
Statistics(NotationFile *stat_file);
const char
*pageName;
Logical
sampledInputFlag;
int
offset,
hardwareMinimum,
hardwareMaximum,
hardwareRange,
deadbandInteger,
highestInput,
lowestInput;
};
//########################################################################
//############################### RIOBase ################################
//########################################################################
//
// The control surface the game consumes from the cockpit RIO board,
// independent of transport. RIO (below) is the serial-hardware
// implementation; PadRIO (l4padrio.h) synthesizes the same surface from
// an XInput controller + the PC keyboard for cockpit-less play.
//
class RIOBase
{
public:
enum RIOStatusType {
BoardOk=0, BoardMissing=1, BoardBad=2,
LampBad=3,
RestartCount=4, AbandonCount=5, FullBufferCount=6
};
enum LampState{
solid=0, flashSlow=1, flashMed=2, flashFast=3,
state1Off=0x00, state1Dim=0x04, state1Bright=0x0C,
state2Off=0x00, state2Dim=0x10, state2Bright=0x30,
};
enum RIOEventType {
ButtonPressedEvent,
ButtonReleasedEvent,
KeyEvent,
AnalogEvent,
VersionEvent
};
struct RIOKeyPair
{
int Unit;
int Key;
};
struct RIOEvent
{
RIOEventType Type;
union
{
int Unit;
RIOKeyPair Keyboard;
}Data;
};
RIOBase()
{
TestModeActive = 0;
Throttle = (Scalar) 0;
LeftPedal = (Scalar) 0;
RightPedal = (Scalar) 0;
JoystickX = (Scalar) 0;
JoystickY = (Scalar) 0;
MajorRevision = 0;
MinorRevision = 0;
}
virtual ~RIOBase() {}
virtual Logical
TestInstance() const
{ return True; }
virtual Logical
GetNextEvent(RIOEvent *destinationPointer) = 0;
virtual void
ForceCenterJoystick() {}
virtual void
SetJoystickDeadBand(Scalar) {}
virtual void
SetThrottleDeadBand(Scalar) {}
virtual void
SetPedalsDeadBand(Scalar) {}
virtual void
RequestCheck() {}
virtual void
RequestVersion() {}
virtual void
RequestAnalogUpdate() {}
virtual void
GeneralReset() {}
virtual void
ResetThrottle() {}
virtual void
ResetLeftPedal() {}
virtual void
ResetRightPedal() {}
virtual void
ResetVerticalJoystick() {}
virtual void
ResetHorizontalJoystick() {}
virtual void
SetLamp(int lampNumber, int state) = 0;
int
TestModeActive;
Scalar
Throttle, LeftPedal, RightPedal, JoystickX, JoystickY;
int
MajorRevision, MinorRevision;
};
class RIO :
public RIOBase,
public PCSerialPacket
{
protected:
enum RIOCommand{
CheckRequest=0x80,
VersionRequest,
AnalogRequest,
ResetRequest,
LampRequest,
CheckReply,
VersionReply,
AnalogReply,
ButtonPressed,
ButtonReleased,
KeyPressed,
KeyReleased,
TestModeChange
};
public:
//Win32 Serial support: ADB 02/13/07
//RIO(Word port, Word intNum, Logical perform_tests = True);
RIO(const char* port, Logical perform_tests = True);
~RIO();
Logical
TestInstance() const;
Logical
GetNextEvent(RIOEvent *destinationPointer);
void
ForceCenterJoystick(),
SetJoystickDeadBand(Scalar dead_band),
SetThrottleDeadBand(Scalar dead_band),
SetPedalsDeadBand(Scalar dead_band);
void
RequestCheck(),
RequestVersion(),
RequestAnalogUpdate();
void
GeneralReset(),
ResetThrottle(),
ResetLeftPedal(),
ResetRightPedal(),
ResetVerticalJoystick(),
ResetHorizontalJoystick();
void
SetLamp(int lampNumber, int state);
void
TestCheckReply(int type, int location)
{
RIO::reply_check_string[1] = (Byte) (type & 0x7F);
RIO::reply_check_string[2] = (Byte) (location & 0x7F);
SendPacket((Byte *)reply_check_string);
}
void
TestVersionReply(int major, int minor)
{
RIO::reply_version_string[1] = (Byte) (major & 0x7F);
RIO::reply_version_string[2] = (Byte) (minor & 0x7F);
SendPacket((Byte *)reply_version_string);
}
void
TestAnalogReply()
{
SendPacket((Byte *)reply_analog_string);
}
void
TestButtonPressed(int button)
{
RIO::reply_button_press_string[1] = (Byte) (button & 0x7F);
SendPacket((Byte *)reply_button_press_string);
}
void
TestButtonReleased(int button)
{
RIO::reply_button_release_string[1] = (Byte) (button & 0x7F);
SendPacket((Byte *)reply_button_release_string);
}
void
TestKeyPressed(int board, int key)
{
RIO::reply_key_press_string[1] = (Byte) (board & 0x7F);
RIO::reply_key_press_string[2] = (Byte) (key & 0x7F);
SendPacket((Byte *)reply_key_press_string);
}
void
TestKeyReleased(int board, int key)
{
RIO::reply_key_release_string[1] = (Byte) (board & 0x7F);
RIO::reply_key_release_string[2] = (Byte) (key & 0x7F);
SendPacket((Byte *)reply_key_release_string);
}
void
TestEnterTestMode()
{SendPacket((Byte *)reply_test_enter_string);}
void
TestExitTestMode()
{SendPacket((Byte *)reply_test_exit_string);}
int
ReceiveQueueCount()
{ return PCSerialPacket::ReceiveQueueCount(); }
int
TransmitQueueCount()
{ return PCSerialPacket::TransmitQueueCount(); }
// TestModeActive, the five analog Scalars, and Major/MinorRevision
// now live in RIOBase.
int remoteRetryCount;
int remoteAbandonCount;
int remoteFullBufferCount;
int lineErrorCount;
int abandonCount;
int overrunCount;
int discardCount; // used by BeginAnalogCalibration()
protected:
void
OpenFailureFile();
void
CloseFailureFile();
void
CheckErrors();
Logical
operational;
Ranger
*leftPedalRanger, *rightPedalRanger;
Ranger
*throttleRanger;
Ranger
*joystickXRanger, *joystickYRanger;
NotationFile
*failureFile;
int
failureFileOpenCount;
static Byte reply_check_string[];
static Byte reply_version_string[];
static Byte reply_analog_string[];
static Byte reply_button_press_string[];
static Byte reply_button_release_string[];
static Byte reply_key_press_string[];
static Byte reply_key_release_string[];
static Byte reply_test_enter_string[];
static Byte reply_test_exit_string[];
};