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>
390 lines
7.7 KiB
C++
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[];
|
|
};
|