Files
CydandClaude Opus 4.8 39a3dab1fc Phase 0: scaffold modern RIOJoy solution + plan
Modernization of the legacy vJoy-based RIO cockpit interface for Win10/11,
removing the vJoy dependency in favor of a custom VHF/UMDF HID driver,
rewritten in C#/.NET 8 as a background tray app with per-game profiles.

- Reorganize: legacy C++ -> legacy/, cockpit art -> docs/reference/
- RioJoy.sln: src/RioJoy.Core (lib) + src/RioJoy.Tray (tray app), net8.0-windows x64
- driver/ placeholder for the RioGamepad WDK driver
- docs/PLAN.md (7-phase plan; profiles + serial-yield model)
- docs/PROTOCOL.md (RIO wire format + iRIO input-map reference)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 12:43:01 -05:00

2553 lines
64 KiB
C++

// riovjoy2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <windows.h>
#include <iostream>
#include <thread>
#include <iomanip>
#include "SimpleIni.h"
#include "vjoyinterface.h"
#include "public.h"
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4996)
#define ESC 27
class C232Comm
{
private:
HANDLE m_hCom;
int m_nPort;
DCB m_dcb;
COMMTIMEOUTS m_commTimeOuts;
COMMCONFIG m_cc;
COMMPROP m_cp;
public:
C232Comm();
virtual ~C232Comm();
BOOL IsValid() const { return m_hCom != INVALID_HANDLE_VALUE; }
BOOL Open(int nPort, DWORD dwBaudRate = CBR_9600, BYTE dwParity = NOPARITY, BYTE bByteSize = 8, BYTE dwStopBits = ONESTOPBIT);
void Close();
int rxCom(void* pData, int nLen);
BOOL rxComLoop(void* pData, int nLen);
int txCom(const void* pData, int nLen);
BOOL txComLoop(const void* pData, int nLen);
void locProcessCommError();
int CommunicationConfig(HWND hWndParent);
};
const char g_szPortFormat[] = "\\\\.\\com%d"; // fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz
class CPlasma : public C232Comm
{
public:
DWORD m_dwStart;
int m_nOldRank;
int m_nOldScore;
enum {
ATTR_NORMAL = 0,
ATTR_HALF = 1,
ATTR_LINE = 2,
ATTR_REVERSE = 4,
ATTR_FLASH = 8,
ATTR_PROTECTED = 128
};
enum { PLASMA_OFF, PLASMA_ON, PLASMA_FLASH };
int m_nGlobalState;
// Time m_timeLastPlasma2;
public:
CPlasma() {
Open(2);
m_dwStart = GetTickCount();
PlasmaClear();
PlasmaCursor(CPlasma::PLASMA_OFF);
m_nOldRank = INT_MIN;
m_nOldScore = INT_MIN;
m_nGlobalState = -1;
}
virtual ~CPlasma() {
PlasmaCursor(CPlasma::PLASMA_OFF);
PlasmaClear();
Close();
}
public:
void SendPacket(const BYTE* pbPacket, char nLen);
void PlasmaClear();
void PlasmaCursor(char n = PLASMA_OFF);
void PlasmaCursorHome();
void PlasmaCursorX(char n);
void PlasmaCursorY(char n);
void PlasmaFontAttr(char n);
void PlasmaFont(char n);
void PlasmaText(const char* szText);
POINT GetFontSize(int Font);
void PlasmaBoxDraw(RECT tag);
void PlasmaBoxFill(RECT tag);
void PlasmaPosText(const char* szText, char x = 0, char y = 0, char Attr = 0, char Font = 5);
void PlasmaScoreDraw(const char* Rank, const char* Score, BOOL bFlag);
void DoPlasma(char nMode);
private:
void PlasmaDisplay(int Gamestate, const char* szName, const char* Rank, const char* Score);
};
const char* sINI{ new char[32] {"c:\\games\\RIO\\RIO.ini"} };
const char* sGreeting{ new char[16] {"RIOvJoy v.03"} };
const char* sPlayer{ new char[16] {"Player"} };
const char* sDisplay{ new char[24] {"Pod PowerUp Detected"} };
const char* newDesktop{ new char[32] {"C:\\games\\RIO\\VWE2.bmp"} };
bool* invertX{ new bool{0} };
bool* invertY{ new bool{0} };
bool* invertZ{ new bool{0} };
bool* invertXR{ new bool{0} };
bool* invertYR{ new bool{0} };
bool* enableZR{ new bool{0} };
bool* invertZR{ new bool{0} };
int* iRIO{ new int[112] {} };
LONG g_Throttle;
LONG g_LeftPedal;
LONG g_RightPedal;
LONG g_JoystickY;
LONG g_JoystickX;
LONG g_Rudder;
LONG g_ThrottleResult = -1L;
LONG g_HatResult = -1L;
LONG g_ThrottleStart = INT_MIN;
LONG g_LeftPedalStart = INT_MAX;
LONG g_RightPedalStart = INT_MAX;
LONG g_JoystickXLast = 16383L;
LONG g_JoystickYLast = 16383L;
LONG g_ThrottleLast = 16383L;
LONG g_LeftPedalLast = 16383L;
LONG g_RightPedalLast = 16383L;
LONG g_RudderLast = 16838L;
LONG maxt = 0;
LONG mint = 0;
LONG maxl = 0;
LONG minl = 0;
LONG maxr = 0;
LONG minr = 0;
LONG maxx = 0;
LONG minx = 0;
LONG maxy = 0;
LONG miny = 0;
BOOL g_rawaxes = false;
bool enablecount = false;
int count = 0;
int now = GetTickCount();
int last = GetTickCount();
int lastAnalogEvent = GetTickCount();
static int g_nOpenComState = 0;
#define ASCII_XON 0x11
#define ASCII_XOFF 0x13
#define ACK_CHAR 0xFC
#define NAK_CHAR 0xFD
#define RESTART_CHAR 0xFE
#define IDLE_CHAR 0xFF
static HANDLE g_hCom = INVALID_HANDLE_VALUE;
static OVERLAPPED wos;
static OVERLAPPED ros;
static OVERLAPPED wcos;//WaitCommEvent
static HANDLE g_hCommWatchThread = NULL;
static HANDLE g_hWatchEvent = NULL;
static HANDLE g_hLampEvent = NULL;
static DWORD g_dwThreadID;
static BYTE packetbytes[16];
static int packetbyteremain = 0;
static int chinpacket = 0;
static int g_naButtonThrottles[8];
static BYTE g_baRIOLengthsA[] = {
0, // 0.CheckRequest
0, // 1.VersionRequest
0, // 2.AnalogRequest
1, // 3.ResetRequest
2, // 4.LampRequest
2, // 5.CheckReply
2, // 6.VersionReply
10, // 7.AnalogReply
1, // 8.ButtonPressed
1, // 9.ButtonReleased
2, // 10.KeyPressed
2, // 11.KeyReleased
1, // 12.TestModeChange
};
int g_nRIOPacketCountA = (sizeof(g_baRIOLengthsA) / sizeof(g_baRIOLengthsA[0]));
BYTE* g_pbRIOLengths = NULL;
int g_nRIOPacketCount = 0;
enum RIOCommand {
rio_CheckRequest = 0x80, // 128
rio_VersionRequest, // 129
rio_AnalogRequest, // 130
rio_ResetRequest, // 131
rio_LampRequest, // 132
rio_CheckReply, // 133
rio_VersionReply, // 134
rio_AnalogReply, // 135
rio_ButtonPressed, // 136
rio_ButtonReleased, // 137
rio_KeyPressed, // 138
rio_KeyReleased, // 139
rio_TestModeChange, // 140
};
enum RIOStatusType {
BoardOk,
BoardMissing,
BoardBad,
LampBad,
RestartCount,
AbandonCount,
FullBufferCount
};
enum LampState {
solid = 0, flashSlow = 1, flashMed = 2, flashFast = 3,
state1Off = 0x00, state1Dim = 0x04, state1Bright = 0x0C,
state2Off = 0x00, state2Dim = 0x10, state2Bright = 0x30,
};
enum {
LampSolidOff = solid + state1Off + state2Off,
LampSolidDim = solid + state1Dim + state2Dim,
LampSolidBright = solid + state1Bright + state2Bright
};
DWORD g_dwLastBytesToWrite = 0;
DWORD g_dCancelCount = 0;
BYTE g_baLastPacket[20];
static bool _continue;
CSimpleIniA ini = new CSimpleIni;
///////////////////////////
// forward declarations //
///////////////////////////
void PrintMenu();
bool ReadyvJoy();
char* getCmdOption(char** begin, char** end, const std::string& option);
bool cmdOptionExists(char** begin, char** end, const std::string& option);
BOOL SetupConnection(HANDLE hCom);
HANDLE OpenConnection(int port);
BOOL CloseConnection();
void TransmitCommChar2(HANDLE g_hCom, int ch);
int ReadCommBlock(LPSTR lpszBlock, int nMaxLength);
BOOL WriteCommBlock(LPSTR lpByte, DWORD dwBytesToWrite);
DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
short CombinePair(BYTE low_value, BYTE high_value);
void AnalogEvent(BYTE* pData);
void ButtonEvent(BYTE* pData);
void KeypadEvent(BYTE* pData);
BOOL SendPacket(const BYTE* ba, int length);
BOOL SendCommand(const BYTE* pbPacket);
BOOL SetLamp(int lampNumber, int state);
void AnalogAllReset();
void GeneralReset();
void ResetThrottle();
void ResetLeftPedal();
void ResetRightPedal();
void ResetVerticalJoystick();
void ResetHorizontalJoystick();
void RequestCheck();
void CheckReply(BYTE* pData);
const char* GetLampName(int lamp_number);
const char* GetBoardName(int board_number);
void RequestVersion();
void VersionReply(BYTE* pData);
void RequestAnalogUpdate();
void UpdateJoystick();
void UpdateThrottle();
void UpdatePadal();
void TestModeChange(BYTE* pData);
void RIOcmd(unsigned char ucValue);
static void Press_V2(unsigned char iAddress);
static void Release_V2(unsigned char iAddress);
static void ModKeySHIFT(bool bIsPressed);
static void ModKeyCTRL(bool bIsPressed);
static void ModKeyALT(bool bIsPressed);
static void Key(unsigned char ucwVk, bool bIsPressed, bool bIsExtended);
static void Mouse(unsigned char ucValue, bool bIsPressed);
///////////////////
// Main function //
///////////////////
int main(int argc, char** argv)
{
_continue = true; // the show stopper set to false and watch the world burn
std::cout << "Useage: riovjoy.exe -f [ini file path] -p [\"Pilot\"] [console]\n";
// check the command line for any arguments
if (cmdOptionExists(argv, argv + argc, "console"))
{
std::cout << "console enabled" << std::endl;
}
else {
HWND hWnd = GetConsoleWindow();
ShowWindow(hWnd, SW_MINIMIZE);
}
char* inifilename = getCmdOption(argv, argv + argc, "-f");
if (inifilename)
{
std::cout << "Will try " << inifilename << std::endl;
sINI = inifilename;
}
else {
std::cout << "Will try default C:\\games\\RIO\\RIO.ini\n";
}
char* oPlayer{ new char[16] {} };
oPlayer = getCmdOption(argv, argv + argc, "-p");
if (oPlayer)
{
std::cout << "player name supplied: " << oPlayer << std::endl;
sPlayer = oPlayer;
}
else {
std::cout << "no player name supplied\n";
}
//Read in INI config data
switch (ini.LoadFile(sINI))
{
case SI_OK:
std::cout << "Found: " << sINI << std::endl;
break;
case SI_UPDATED:
std::cout << "updated INI file\n";
break;
case SI_INSERTED:
std::cout << "A new value was inserted\n";
break;
case SI_FAIL:
std::cout << "INI Generic failure\n";
break;
case SI_NOMEM:
std::cout << "Ran Out of memory error loading INI\n";
break;
case SI_FILE:
std::cout << "INI File not found\n";
break;
};
// set all of the varables
newDesktop = ini.GetValue("Desktop", "File", "C:\\games\\RIO\\VWE2.bmp", 0);
sGreeting = ini.GetValue("Plasma", "Greeting", "RIOvJoy2 v.03", 0);
*invertX = ini.GetBoolValue("JoyStick", "invertX", false);
*invertY = ini.GetBoolValue("JoyStick", "invertY", false);
*invertZ = ini.GetBoolValue("JoyStick", "invertZ", false);
*invertXR = ini.GetBoolValue("JoyStick", "invertXR", false);
*invertYR = ini.GetBoolValue("JoyStick", "invertYR", false);
*enableZR = ini.GetBoolValue("JoyStick", "enableZR", true);
*invertZR = ini.GetBoolValue("JoyStick", "invertZR", false);
iRIO[0] = ini.GetLongValue("Buttons", "RIO00", 0x8049);
iRIO[1] = ini.GetLongValue("Buttons", "RIO01", 0x805A);
iRIO[2] = ini.GetLongValue("Buttons", "RIO02", 0x8055);
iRIO[3] = ini.GetLongValue("Buttons", "RIO03", 0x804C);
iRIO[4] = ini.GetLongValue("Buttons", "RIO04", 0x8926);
iRIO[5] = ini.GetLongValue("Buttons", "RIO05", 0x8928);
iRIO[6] = ini.GetLongValue("Buttons", "RIO06", 0x8927);
iRIO[7] = ini.GetLongValue("Buttons", "RIO07", 0x8925);
iRIO[8] = ini.GetLongValue("Buttons", "RIO08", 0x8051);
iRIO[9] = ini.GetLongValue("Buttons", "RIO09", 0x8059);
iRIO[10] = ini.GetLongValue("Buttons", "RIO0A", 0x8045);
iRIO[11] = ini.GetLongValue("Buttons", "RIO0B", 0x8052);
iRIO[12] = ini.GetLongValue("Buttons", "RIO0C", 0x8823);
iRIO[13] = ini.GetLongValue("Buttons", "RIO0D", 0x80BE);
iRIO[14] = ini.GetLongValue("Buttons", "RIO0E", 0x80BC);
iRIO[15] = ini.GetLongValue("Buttons", "RIO0F", 0x8824);
iRIO[16] = ini.GetLongValue("Buttons", "RIO10", 0xC000);
iRIO[17] = ini.GetLongValue("Buttons", "RIO11", 0xC001);
iRIO[18] = ini.GetLongValue("Buttons", "RIO12", 0xC002);
iRIO[19] = ini.GetLongValue("Buttons", "RIO13", 0xC003);
iRIO[20] = ini.GetLongValue("Buttons", "RIO14", 0xC004);
iRIO[21] = ini.GetLongValue("Buttons", "RIO15", 0x8009);
iRIO[22] = ini.GetLongValue("Buttons", "RIO16", 0x0);
iRIO[23] = ini.GetLongValue("Buttons", "RIO17", 0x0);
iRIO[24] = ini.GetLongValue("Buttons", "RIO18", 0x800D);
iRIO[25] = ini.GetLongValue("Buttons", "RIO19", 0x8012);
iRIO[26] = ini.GetLongValue("Buttons", "RIO1A", 0x8011);
iRIO[27] = ini.GetLongValue("Buttons", "RIO1B", 0x8010);
iRIO[28] = ini.GetLongValue("Buttons", "RIO1C", 0x88AF);
iRIO[29] = ini.GetLongValue("Buttons", "RIO1D", 0x88AE);
iRIO[30] = ini.GetLongValue("Buttons", "RIO1E", 0x0);
iRIO[31] = ini.GetLongValue("Buttons", "RIO1F", 0x0);
iRIO[32] = ini.GetLongValue("Buttons", "RIO20", 0x8827);
iRIO[33] = ini.GetLongValue("Buttons", "RIO21", 0x8828);
iRIO[34] = ini.GetLongValue("Buttons", "RIO22", 0x8826);
iRIO[35] = ini.GetLongValue("Buttons", "RIO23", 0x8825);
iRIO[36] = ini.GetLongValue("Buttons", "RIO24", 0x804E);
iRIO[37] = ini.GetLongValue("Buttons", "RIO25", 0x804D);
iRIO[38] = ini.GetLongValue("Buttons", "RIO26", 0x804C);
iRIO[39] = ini.GetLongValue("Buttons", "RIO27", 0x804B);
iRIO[40] = ini.GetLongValue("Buttons", "RIO28", 0x8056);
iRIO[41] = ini.GetLongValue("Buttons", "RIO29", 0x8055);
iRIO[42] = ini.GetLongValue("Buttons", "RIO2A", 0x8054);
iRIO[43] = ini.GetLongValue("Buttons", "RIO2B", 0x8053);
iRIO[44] = ini.GetLongValue("Buttons", "RIO2C", 0x804A);
iRIO[45] = ini.GetLongValue("Buttons", "RIO2D", 0x8049);
iRIO[46] = ini.GetLongValue("Buttons", "RIO2E", 0x8048);
iRIO[47] = ini.GetLongValue("Buttons", "RIO2F", 0x8047);
iRIO[48] = ini.GetLongValue("Buttons", "RIO30", 0x805A);
iRIO[49] = ini.GetLongValue("Buttons", "RIO31", 0x8059);
iRIO[50] = ini.GetLongValue("Buttons", "RIO32", 0x8058);
iRIO[51] = ini.GetLongValue("Buttons", "RIO33", 0x8057);
iRIO[52] = ini.GetLongValue("Buttons", "RIO34", 0x8052);
iRIO[53] = ini.GetLongValue("Buttons", "RIO35", 0x8051);
iRIO[54] = ini.GetLongValue("Buttons", "RIO36", 0x8050);
iRIO[55] = ini.GetLongValue("Buttons", "RIO37", 0x804F);
iRIO[56] = ini.GetLongValue("Buttons", "RIO38", 0x4000);
iRIO[57] = ini.GetLongValue("Buttons", "RIO39", 0x4001);
iRIO[58] = ini.GetLongValue("Buttons", "RIO3A", 0x4002);
iRIO[59] = ini.GetLongValue("Buttons", "RIO3B", 0x4003);
iRIO[60] = ini.GetLongValue("Buttons", "RIO3C", 0x4004);
iRIO[61] = ini.GetLongValue("Buttons", "RIO3D", 0x801B);
iRIO[62] = ini.GetLongValue("Buttons", "RIO3E", 0x4005);
iRIO[63] = ini.GetLongValue("Buttons", "RIO3F", 0x1008);
iRIO[64] = ini.GetLongValue("Buttons", "RIO40", 0x1001);
iRIO[65] = ini.GetLongValue("Buttons", "RIO41", 0x2002);
iRIO[66] = ini.GetLongValue("Buttons", "RIO42", 0x2000);
iRIO[67] = ini.GetLongValue("Buttons", "RIO43", 0x2001);
iRIO[68] = ini.GetLongValue("Buttons", "RIO44", 0x2003);
iRIO[69] = ini.GetLongValue("Buttons", "RIO45", 0x1004);
iRIO[70] = ini.GetLongValue("Buttons", "RIO46", 0x1003);
iRIO[71] = ini.GetLongValue("Buttons", "RIO47", 0x1002);
iRIO[72] = ini.GetLongValue("Buttons", "RIO48", 0x0);
iRIO[73] = ini.GetLongValue("Buttons", "RIO49", 0x0);
iRIO[74] = ini.GetLongValue("Buttons", "RIO4A", 0x0);
iRIO[75] = ini.GetLongValue("Buttons", "RIO4B", 0x0);
iRIO[76] = ini.GetLongValue("Buttons", "RIO4C", 0x0);
iRIO[77] = ini.GetLongValue("Buttons", "RIO4D", 0x0);
iRIO[78] = ini.GetLongValue("Buttons", "RIO4E", 0x0);
iRIO[79] = ini.GetLongValue("Buttons", "RIO4F", 0x0);
iRIO[80] = ini.GetLongValue("Buttons", "RIO50", 0x30);
iRIO[81] = ini.GetLongValue("Buttons", "RIO51", 0x31);
iRIO[82] = ini.GetLongValue("Buttons", "RIO52", 0x32);
iRIO[83] = ini.GetLongValue("Buttons", "RIO53", 0x33);
iRIO[84] = ini.GetLongValue("Buttons", "RIO54", 0x34);
iRIO[85] = ini.GetLongValue("Buttons", "RIO55", 0x35);
iRIO[86] = ini.GetLongValue("Buttons", "RIO56", 0x36);
iRIO[87] = ini.GetLongValue("Buttons", "RIO57", 0x37);
iRIO[88] = ini.GetLongValue("Buttons", "RIO58", 0x38);
iRIO[89] = ini.GetLongValue("Buttons", "RIO59", 0x39);
iRIO[90] = ini.GetLongValue("Buttons", "RIO5A", 0x41);
iRIO[91] = ini.GetLongValue("Buttons", "RIO5B", 0x42);
iRIO[92] = ini.GetLongValue("Buttons", "RIO5C", 0x43);
iRIO[93] = ini.GetLongValue("Buttons", "RIO5D", 0x44);
iRIO[94] = ini.GetLongValue("Buttons", "RIO5E", 0x45);
iRIO[95] = ini.GetLongValue("Buttons", "RIO5F", 0x46);
iRIO[96] = ini.GetLongValue("Buttons", "RIO60", 0x1051);
iRIO[97] = ini.GetLongValue("Buttons", "RIO61", 0x1052);
iRIO[98] = ini.GetLongValue("Buttons", "RIO62", 0x1053);
iRIO[99] = ini.GetLongValue("Buttons", "RIO63", 0x1054);
iRIO[100] = ini.GetLongValue("Buttons", "RIO64", 0x1055);
iRIO[101] = ini.GetLongValue("Buttons", "RIO65", 0x1056);
iRIO[102] = ini.GetLongValue("Buttons", "RIO66", 0x1057);
iRIO[103] = ini.GetLongValue("Buttons", "RIO67", 0x1058);
iRIO[104] = ini.GetLongValue("Buttons", "RIO68", 0x1059);
iRIO[105] = ini.GetLongValue("Buttons", "RIO69", 0x105A);
iRIO[106] = ini.GetLongValue("Buttons", "RIO6A", 0x105B);
iRIO[107] = ini.GetLongValue("Buttons", "RIO6B", 0x105C);
iRIO[108] = ini.GetLongValue("Buttons", "RIO6C", 0x105D);
iRIO[109] = ini.GetLongValue("Buttons", "RIO6D", 0x105E);
iRIO[110] = ini.GetLongValue("Buttons", "RIO6E", 0x105F);
iRIO[111] = ini.GetLongValue("Buttons", "RIO6F", 0x1060);
// if the Vjoy setup fails set _continue to false and let the program die.
if (!ReadyvJoy()) {
// std::cout << "vJoy setup failed, exiting\n";
// _continue = false;
// return 1;
}
//start the RIO shinanigins
OpenConnection(1);
RequestVersion();
RequestCheck();
Sleep(1000); // to allow the comread thread to get set up and rolling the analog updates
for (int b = 0; b < 72; b++) {
if (iRIO[b] & 0x8000)
SetLamp(b, LampSolidDim);
}
// try to open com2 to plasma display, else no display for you
CPlasma* _Plasma = new CPlasma;
if (_Plasma->IsValid()) {
std::cout << "Com2 open\n";
_Plasma->PlasmaPosText(sDisplay);
std::string playergreeting = std::string(sGreeting) + "," + std::string(sPlayer);
Sleep(3000);
_Plasma->PlasmaClear();
_Plasma->PlasmaPosText(playergreeting.c_str());
}
else {
std::cout << "No Display for you" << std::endl;
}
/////////////////////////////////////////////
// code block to check the desktop image //
/////////////////////////////////////////////
//get current desktop image, store for replacement
wchar_t * worgDesktop = new wchar_t[MAX_PATH];
SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, worgDesktop, 0);
//set desktop from ini
int wchar_numnew = MultiByteToWideChar( CP_UTF8 , 0 , newDesktop , -1 , NULL , 0 );
wchar_t* wnewDesktop = new wchar_t[wchar_numnew];
MultiByteToWideChar(CP_UTF8, 0, newDesktop, -1, wnewDesktop, wchar_numnew);
bool setnewdesktop = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, wnewDesktop, SPIF_UPDATEINIFILE);
wnewDesktop = nullptr;
/////////////////////////////////////////////
//Main while wait loop
while (_continue) // wait loop
{
PrintMenu();
}
// quit signal clean up
// close com2 to plasma
if (_Plasma->IsValid()) {
std::cout << "Closing Com2 to Plasma\n";
_Plasma->~CPlasma();
}
// end RIO
GeneralReset();
CloseConnection();
// relinquish vjoy device
RelinquishVJD(1);
std::cout << "Relinquished vJoy Device\n";
// set desktop back to original
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, worgDesktop, SPIF_UPDATEINIFILE);
worgDesktop = nullptr;
return 0;
}
/////////////////
// Functions //
/////////////////
void PrintMenu()
{
char myChoice = 'Z';
std::cout << "********** Menu **********" << std::endl;
std::cout << "(0): Reset all analog axes" << std::endl;
std::cout << "(1): Reset Throttle analog axes" << std::endl;
std::cout << "(2): Reset LeftPedal analog axes" << std::endl;
std::cout << "(3): Reset RightPedal analog axes" << std::endl;
std::cout << "(4): Reset VerticalJoystick analog axes" << std::endl;
std::cout << "(5): Reset HorizontalJoystick analog axes" << std::endl;
std::cout << "(6): Reset Digital" << std::endl;
std::cout << "(7): RequestVersion and status" << std::endl;
std::cout << "(8): enable/disable axes readout" << std::endl;
std::cout << "(9): enable/disable analog Frequency readout" << std::endl;
std::cout << "(Q): quit" << std::endl;
std::cin >> myChoice;
switch (myChoice)
{
case '0':
RIOcmd(0);
break;
case '1':
RIOcmd(1);
break;
case '2':
RIOcmd(2);
break;
case '3':
RIOcmd(3);
break;
case '4':
RIOcmd(4);
break;
case '5':
RIOcmd(5);
break;
case '6':
RIOcmd(6);
break;
case '7':
RIOcmd(7);
break;
case '8':
RIOcmd(8);
break;
case '9':
RIOcmd(9);
break;
case 'Q':
case 'q':
_continue = false;
std::cout << "Closing Application" << std::endl;
break;
default:
std::cout << "ERROR! You have selected an invalid choice." << std::endl;
break;
}
}
bool ReadyvJoy() // make ready the vJoy interface
{
BYTE id = 1; // ID of the target vjoy device (Default is 1)
UINT iInterface = 1; // Default target vJoy device
// WORD VerDll, VerDrv;
if (!vJoyEnabled())
{
std::cout << "vJoy driver not enabled: Failed Getting vJoy attributes.\n";
}
else
{
// std::cout << "vJoyEnabled on this system\n";
};
//if (!DriverMatch(&VerDll, &VerDrv)) {
// std::cout << "vJoy Driver version " << VerDrv << " does not match vJoyInterface DLL version " << VerDll << std::endl;
//}
//else {
// std::cout << "Driver and DLL match version" << VerDrv;
//};
// Get the state of the requested device
VjdStat status = GetVJDStatus(iInterface);
switch (status)
{
case VJD_STAT_OWN:
// std::cout << "vJoy Device " << iInterface << " is already owned by this feeder";
break;
case VJD_STAT_FREE:
// std::cout << "vJoy Device " << iInterface << " is free";
break;
case VJD_STAT_BUSY:
std::cout << "vJoy Device " << iInterface << " is already owned by another feeder Cannot continue";
return false;
break;
case VJD_STAT_MISS:
std::cout << "vJoy Device " << iInterface << " is not installed or disabled\nCannot continue";
return false;
break;
default:
std::cout << "vJoy Device " << iInterface << " general error\nCannot continue";
return false;
break;
};
// Check which axes are supported
BOOL AxisX = GetVJDAxisExist(iInterface, HID_USAGE_X);
BOOL AxisY = GetVJDAxisExist(iInterface, HID_USAGE_Y);
BOOL AxisZ = GetVJDAxisExist(iInterface, HID_USAGE_Z);
BOOL AxisRX = GetVJDAxisExist(iInterface, HID_USAGE_RX);
BOOL AxisRY = GetVJDAxisExist(iInterface, HID_USAGE_RY);
BOOL AxisRZ = GetVJDAxisExist(iInterface, HID_USAGE_RZ);
// Get the number of buttons supported and hats by this vJoy device
int nButtons = GetVJDButtonNumber(iInterface);
int DiscPovNumber = GetVJDDiscPovNumber(iInterface);
if (!AxisX || !AxisY || !AxisZ || !AxisRX || !AxisRY || !AxisRZ || DiscPovNumber != 1 || nButtons != 96) {
std::cout << "vJoy Interface not corectly setup, Please enable X,Y,Z,RX,RY,RZ, one descrete Pov Hat, and 96 buttons";
// return false;
}
// Acquire the target
if ((status == VJD_STAT_OWN) || ((status == VJD_STAT_FREE) && (!AcquireVJD(iInterface))))
{
std::cout << "Failed to acquire vJoy device, exiting";
return false;
}
else
{
std::cout << "Acquired: vJoy device." << std::endl;
ResetVJD(1);
return true;
}
}
char* getCmdOption(char** begin, char** end, const std::string& option)
{
char** itr = std::find(begin, end, option);
if (itr != end && ++itr != end)
{
return *itr;
}
return 0;
}
bool cmdOptionExists(char** begin, char** end, const std::string& option)
{
return std::find(begin, end, option) != end;
}
BOOL SetupConnection(HANDLE hCom)
{
DCB dcb;
BOOL fRetVal;
dcb.DCBlength = sizeof(DCB);
GetCommState(hCom, &dcb);
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.Parity = 0;
dcb.StopBits = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fOutxCtsFlow = 0;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fInX = 0;
dcb.fOutX = 0;
dcb.XonChar = ASCII_XON;
dcb.XoffChar = ASCII_XOFF;
dcb.XonLim = 100;
dcb.XoffLim = 100;
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
fRetVal = SetCommState(hCom, &dcb);
if (fRetVal == 0)return FALSE;
// get any early notifications
fRetVal = SetCommMask(hCom, EV_RXCHAR);
if (fRetVal == 0)return FALSE;
// setup device buffers
fRetVal = SetupComm(hCom, 2, 2);
if (fRetVal == 0)return FALSE;
// purge any information in the buffer
fRetVal = PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
if (fRetVal == 0)return FALSE;
// set up for overlapped I/O
COMMTIMEOUTS CommTimeOuts = { 0, };
CommTimeOuts.ReadIntervalTimeout = 1;
CommTimeOuts.ReadTotalTimeoutMultiplier = 1;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 0;
fRetVal = SetCommTimeouts(hCom, &CommTimeOuts);
if (fRetVal == 0)return FALSE;
return TRUE;
}
HANDLE OpenConnection(int port)
{
g_pbRIOLengths = &g_baRIOLengthsA[0];
g_nRIOPacketCount = g_nRIOPacketCountA;
//port: 1~n
//char portname[8];
//wsprintf(portname, "COM%d", port);
//const char* portprefix = "COM";
//LPWSTR portname = std::strcat(portprefix, port);
DWORD dwFAs = FILE_ATTRIBUTE_NORMAL;
dwFAs |= FILE_FLAG_OVERLAPPED;
//g_hCom = CreateFile( portname, GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING, dwFAs, NULL );
g_hCom = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFAs, NULL);
if (g_hCom != INVALID_HANDLE_VALUE) {
if (SetupConnection(g_hCom)) {
ros.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
wos.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
g_hWatchEvent = CreateEvent(0, 0, 0, 0);
g_hLampEvent = CreateEvent(0, FALSE, 0, 0);
g_hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)CommWatchProc, NULL, CREATE_SUSPENDED, &g_dwThreadID);
if (g_hCommWatchThread) {
//"Normally, were produced by Thread."
//////////////////////////////////////////////
//All OK
//Exit Point <======
EscapeCommFunction(g_hCom, SETDTR);
Sleep(50);
EscapeCommFunction(g_hCom, CLRDTR);
Sleep(1000);
SetThreadPriority(g_hCommWatchThread, THREAD_PRIORITY_TIME_CRITICAL);
ResumeThread(g_hCommWatchThread);
g_nOpenComState = 1;
return g_hCom;
}
}
CloseConnection();
}
return INVALID_HANDLE_VALUE;
}
BOOL CloseConnection()
{
if (g_hCom != INVALID_HANDLE_VALUE) {
SetCommMask(g_hCom, 0);
//Terminate Watch Thread
if (g_hCommWatchThread) {
SetEvent(g_hWatchEvent);
WaitForSingleObject(g_hCommWatchThread, 1000 /*INFINITE*/); //comwatch thread not responding to temination signal
g_hCommWatchThread = NULL;
}
if (g_hWatchEvent) {
CloseHandle(g_hWatchEvent);
g_hWatchEvent = NULL;
}
if (g_hLampEvent) {
CloseHandle(g_hLampEvent);
g_hLampEvent = NULL;
}
EscapeCommFunction(g_hCom, CLRDTR);
// purge any outstanding reads/writes and close device handle
PurgeComm(g_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
CloseHandle(g_hCom);
g_hCom = INVALID_HANDLE_VALUE;
// clean up event objects
if (ros.hEvent) {
CloseHandle(ros.hEvent);
ros.hEvent = NULL;
}
if (wos.hEvent) {
CloseHandle(wos.hEvent);
wos.hEvent = NULL;
}
g_nOpenComState = 0;
}
return TRUE;
}
void TransmitCommChar2(HANDLE g_hCom, int ch)
{
TransmitCommChar(g_hCom, (char)ch);
}
int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
{
BOOL fReadStat;
COMSTAT ComStat;
DWORD dwErrorFlags;
DWORD dwLength;
DWORD dwLength2;
DWORD dwError;
// only try to read number of bytes in queue
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
dwLength = __min((DWORD)nMaxLength, ComStat.cbInQue);
dwLength2 = __min((DWORD)nMaxLength, ComStat.cbInQue);
if (dwLength > 0) {
fReadStat = ReadFile(g_hCom, lpszBlock, dwLength, &dwLength2, &ros);
for (int i = 0; i < (int)dwLength; i++) {
int ch = (BYTE)lpszBlock[i];
//"Loops as received letters".
//"Currently there is no packet receiving .. .. The state can come any control characters ."
if (packetbyteremain != 0) {
//"The letters arrived for the packet .. get the rest of the characters ."
if (ch & 0x80) {
packetbyteremain = 0;
chinpacket = 0;
continue;
}
packetbyteremain--;
packetbytes[chinpacket] = ch;
chinpacket++;
if (packetbyteremain == 0) {
//"A packet was completed . ... Respond immediately arrived ."
chinpacket--; // exclude check byte
int packettype = packetbytes[0];
BYTE bCheckByte = 0;
for (int kkk = 0; kkk < chinpacket; kkk++) {
bCheckByte += packetbytes[kkk] & 0x7f;
}
static bool s_bool = true;
if (s_bool || (bCheckByte & 0x7f) == packetbytes[chinpacket]) {
switch (packettype) {
//PC to RIO
case rio_CheckRequest:
case rio_VersionRequest:
case rio_AnalogRequest:
case rio_ResetRequest:
case rio_LampRequest:
//RIO to PC
case rio_CheckReply:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
CheckReply(packetbytes);
break;
case rio_VersionReply:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
VersionReply(packetbytes);
break;
case rio_TestModeChange:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
TestModeChange(packetbytes);
break;
case rio_AnalogReply:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
AnalogEvent(packetbytes);
break;
case rio_ButtonPressed:
case rio_ButtonReleased:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
ButtonEvent(packetbytes);
break;
case rio_KeyPressed:
case rio_KeyReleased:
if (ComStat.cbOutQue == 0)
TransmitCommChar2(g_hCom, ACK_CHAR);
KeypadEvent(packetbytes);
}
}
else {
if (ComStat.cbOutQue == 0) {
if ((packettype == rio_ButtonPressed) || (packettype == rio_ButtonReleased))
TransmitCommChar2(g_hCom, NAK_CHAR);
else
TransmitCommChar2(g_hCom, ACK_CHAR);
}
}
// TransmitCommChar2(g_hCom,0xFC);
// WriteTTYBlock(hWnd,"|",1);
chinpacket = 0;
//packetbytes"Clear the buffer."
}
else {
//"Packet has not yet arrived . The characters do nothing."
;
}
}
else {
chinpacket = 0;
if ((rio_CheckRequest <= ch) && (ch < (rio_CheckRequest + g_nRIOPacketCount))) {
packetbyteremain = g_pbRIOLengths[ch - rio_CheckRequest] + 1;
chinpacket = 0;
packetbytes[chinpacket] = ch;
chinpacket++;
}
else {
WriteCommBlock(NULL, ch);
}
}
}//for(int i=0;i<(int)dwLength;i++)
//"After awards"
if (!fReadStat) {
if (GetLastError() == ERROR_IO_PENDING) {
while (!GetOverlappedResult(g_hCom, &ros, &dwLength, TRUE)) {
dwError = GetLastError();
if (dwError == ERROR_IO_INCOMPLETE)
// normal result if not finished
continue;
else {
// an error occurred, try to recover
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
break;
}
}
}
else {
// some other error occurred
dwLength = 0;
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
}
}
}
return dwLength;
}
BOOL WriteCommBlock(LPSTR lpByte, DWORD dwBytesToWrite)
{
if (lpByte == NULL) {
switch (dwBytesToWrite) {
case ACK_CHAR:
g_dwLastBytesToWrite = 0;
break;
case NAK_CHAR:
if (g_dwLastBytesToWrite != 0) {
if (g_baLastPacket[0] == rio_LampRequest) {
}
g_dwLastBytesToWrite = 0;
}
break;
case RESTART_CHAR:
break;
case IDLE_CHAR:
break;
default:
break;
}
return FALSE;
}
//Normal Variables
BOOL fWriteStat;
DWORD dwBytesWritten;
DWORD dwBytesSent = 0;
//Error Report
DWORD dwErrorFlags;
COMSTAT ComStat;
if (g_dwLastBytesToWrite == 0) {
g_dCancelCount = 0;
// g_dwLastBytesToWrite = dwBytesToWrite;
memcpy(g_baLastPacket, lpByte, dwBytesToWrite);
}
else {
if (lpByte[0] == rio_LampRequest) {
}
if (++g_dCancelCount >= 5) {
g_dwLastBytesToWrite = 0;
}
return FALSE;
}
fWriteStat = WriteFile(g_hCom, lpByte, dwBytesToWrite, &dwBytesWritten, &wos);
if (fWriteStat == FALSE) {
if (GetLastError() == ERROR_IO_PENDING) {
while (!GetOverlappedResult(g_hCom, &wos, &dwBytesWritten, TRUE)) {
if (GetLastError() == ERROR_IO_INCOMPLETE) {
dwBytesSent += dwBytesWritten;
Sleep(0);//Yield the process to the others
}
else {
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
g_dwLastBytesToWrite = 0;
return FALSE;
}
}
}
else {
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
g_dwLastBytesToWrite = 0;
return FALSE;
}
}
return TRUE;
}
DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
{
wcos.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE events[3] = { wcos.hEvent,g_hWatchEvent,g_hLampEvent };
while (1) {
DWORD dwEvtMask = 0;
WaitCommEvent(g_hCom, &dwEvtMask, &wcos);
DWORD ret = WaitForMultipleObjects(3, events, FALSE, 55 ); // this timeout value is the defaultwait for requesting an analog update
if (ret == WAIT_OBJECT_0) {
//An Comm Event has arrived.
DWORD bytesread;
GetOverlappedResult(g_hCom, &wcos, &bytesread, FALSE);
BYTE abIn[16];
ReadCommBlock((LPSTR)abIn, 16);
//((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
}
else if (ret == WAIT_OBJECT_0 + 1) {
//Terminate This Thread.
goto terminate;
}
else if (ret == WAIT_OBJECT_0 + 2) {
//g_hLampEvent is set.
// g_pRIOMain->RunMain();
}
else if (ret == WAIT_FAILED) {
//can't be reached here.!!!
//break;
}
else if (ret == WAIT_TIMEOUT) {
if (packetbyteremain == 0) {
int currenttick = GetTickCount();
int timeouttick = lastAnalogEvent + 5000;
if (timeouttick < currenttick) { // more than 5000ms since last AnalogEvent was seen send digital reset to try to recover
std::cout << "more than 1000ms since last AnalogEvent was seen send digital reset to try to recover" << std::endl;
std::cout << "timeouttick:currenttick " << timeouttick << ":" << currenttick << std::endl;
RIOcmd(6);
}
RequestAnalogUpdate();
}
else {
}
}
}
terminate:
// get rid of event handle
CloseHandle(wcos.hEvent);
return(TRUE);
}
short CombinePair(BYTE low_value, BYTE high_value)
{
short result;
result = (short)(((short)(low_value & 0x7F)) | (((short)high_value) << 7));
if (result & 0x2000)
{
result |= ~0x3FFF;
}
return result;
}
void AnalogEvent(BYTE* pData)
{
for (int i = 1; i <= 10; i++) {
if (pData[i] == 0xFE) {
//AnalogAllReset ();
return;
}
}
g_Throttle = CombinePair(pData[1], pData[2]);
g_LeftPedal = CombinePair(pData[3], pData[4]);
g_RightPedal = CombinePair(pData[5], pData[6]);
g_JoystickY = CombinePair(pData[7], pData[8]);
g_JoystickX = CombinePair(pData[9], pData[10]);
maxt = (g_Throttle > maxt) ? g_Throttle : maxt;
mint = (g_Throttle < mint) ? g_Throttle : mint;
maxl = (g_LeftPedal > maxl) ? g_LeftPedal : maxl;
minl = (g_LeftPedal < minl) ? g_LeftPedal : minl;
maxr = (g_RightPedal > maxr) ? g_RightPedal : maxr;
minr = (g_RightPedal < minr) ? g_RightPedal : minr;
maxx = (g_JoystickX > maxx) ? g_JoystickX : maxx;
minx = (g_JoystickX < minx) ? g_JoystickX : minx;
maxy = (g_JoystickY > maxy) ? g_JoystickY : maxy;
miny = (g_JoystickY < miny) ? g_JoystickY : miny;
lastAnalogEvent = GetTickCount();
if (g_rawaxes) {
std::cout << "min:cur:max" <<
" T:" << std::setw(4) << mint << ":" << std::setw(4) << g_Throttle << ":" << std::setw(4) << maxt <<
" L:" << std::setw(4) << minl << ":" << std::setw(4) << g_LeftPedal << ":" << std::setw(4) << maxl <<
" R:" << std::setw(4) << minr << ":" << std::setw(4) << g_RightPedal << ":" << std::setw(4) << maxr <<
" Y:" << std::setw(4) << miny << ":" << std::setw(4) << g_JoystickY << ":" << std::setw(4) << maxy <<
" X:" << std::setw(4) << minx << ":" << std::setw(4) << g_JoystickX << ":" << std::setw(4) << maxx <<
":" << std::endl;
}
UpdateJoystick();
UpdateThrottle();
UpdatePadal();
}
void ButtonEvent(BYTE* pData)
{
BYTE state = pData[0];
BYTE index = pData[1];
switch (state)
{
case rio_ButtonPressed:
Press_V2(index);
break;
case rio_ButtonReleased:
Release_V2(index);
break;
default:
break;
}
}
void KeypadEvent(BYTE* pData)
{
BYTE state = pData[0];
BYTE pad = pData[1];
BYTE index = pData[2];
switch (state)
{
case rio_KeyPressed:
if (pad == 0)
index = index + 80;
if (pad == 1)
index = index + 96;
Press_V2(index);
break;
case rio_KeyReleased:
if (pad == 0)
index = index + 80;
if (pad == 1)
index = index + 96;
Release_V2(index);
break;
default:
break;
}
}
BOOL SendPacket(const BYTE* ba, int length)
{
return WriteCommBlock((LPSTR)ba, length);
}
BOOL SendCommand(const BYTE* pbPacket)
{
BYTE ba[256];
BYTE bCmd = *pbPacket;
int cmdindex = bCmd & 0x7F;
if (bCmd & 0x80) { /*reduce the command byte down to the index number listed in the g_baRIOLengths array */
if (cmdindex < g_nRIOPacketCount) {
int nLeft = 1/* 1byte command */ + g_pbRIOLengths[cmdindex]; /* plus any additional bytes for arguments to the commands*/
BYTE bCheck = 0;
BYTE* p = ba;
while (nLeft > 0) {
BYTE b = *pbPacket++;
*p++ = b;
bCheck += b & 0x7F;
nLeft--;
}
*p++ = bCheck & 0x7F;
return SendPacket(ba, p - ba);
}
else {
//unknown command
}
}
else {
;//error
}
return TRUE;
}
BOOL SetLamp(int lampNumber, int state)
{
static BYTE request_lamp_string[] = { rio_LampRequest, 0, 0, 0 };
request_lamp_string[1] = (BYTE)(lampNumber & 0x7F);
request_lamp_string[2] = (BYTE)(state & 0x7F);
return SendCommand(request_lamp_string);
}
void AnalogAllReset()
{
ResetThrottle();
ResetLeftPedal();
ResetRightPedal();
ResetVerticalJoystick();
ResetHorizontalJoystick();
}
void GeneralReset()
{
static BYTE request_reset_string[] = { rio_ResetRequest, 0 };
SendCommand(request_reset_string);
}
void ResetThrottle()
{
static BYTE request_throttle_string[] = { rio_ResetRequest, 1 };
SendCommand(request_throttle_string);
}
void ResetLeftPedal()
{
static BYTE request_lpedal_string[] = { rio_ResetRequest, 2 };
SendCommand(request_lpedal_string);
}
void ResetRightPedal()
{
static BYTE request_rpedal_string[] = { rio_ResetRequest, 3 };
SendCommand(request_rpedal_string);
}
void ResetVerticalJoystick()
{
static BYTE request_vstick_string[] = { rio_ResetRequest, 4 };
SendCommand(request_vstick_string);
}
void ResetHorizontalJoystick()
{
static BYTE request_hstick_string[] = { rio_ResetRequest, 5 };
SendCommand(request_hstick_string);
}
void RequestCheck()
{
static BYTE request_check_string[] = { rio_CheckRequest };
SendCommand(request_check_string);
}
void CheckReply(BYTE* pData) {
switch (pData[1])
{
case BoardMissing:
std::cout << "missing_board : " << int(pData[2]) << " : " << GetBoardName(int(pData[2])) << std::endl;
break;
case BoardBad:
std::cout << "dead_board : " << int(pData[2]) << std::endl;
break;
case LampBad:
std::cout << "RIODeadLamp : " << GetLampName(int(pData[2])) << std::endl;
break;
case RestartCount:
std::cout << "remoteRetryCount:" << int(pData[2]) << std::endl;
break;
case AbandonCount:
std::cout << "remoteAbandonCount:" << int(pData[2]) << std::endl;
break;
case FullBufferCount:
std::cout << "remoteFullBufferCount:" << int(pData[2]) << std::endl;
break;
}
}
const char* GetLampName(int lamp_number)
{
const char* lamp_name[] =
{
"AuxLowerRight8",
"AuxLowerRight7",
"AuxLowerRight6",
"AuxLowerRight5",
"AuxLowerRight4",
"AuxLowerRight3",
"AuxLowerRight2",
"AuxLowerRight1",
"AuxLowerLeft8",
"AuxLowerLeft7",
"AuxLowerLeft6",
"AuxLowerLeft5",
"AuxLowerLeft4",
"AuxLowerLeft3",
"AuxLowerLeft2",
"AuxLowerLeft1",
"Secondary1", //=0x10
"Secondary2",
"Secondary3",
"Secondary4",
"Secondary5",
"Secondary6",
"TeslaRelay3",
"undefined_0x17",
"Secondary7", //=0x18
"Secondary8",
"Secondary9",
"Secondary10",
"Secondary11",
"Secondary12",
"TeslaRelay1",
"TeslaRelay2",
"AuxUpperCenter8", //=0x20
"AuxUpperCenter7",
"AuxUpperCenter6",
"AuxUpperCenter5",
"AuxUpperCenter4",
"AuxUpperCenter3",
"AuxUpperCenter2",
"AuxUpperCenter1",
"AuxUpperLeft8", //=0x28
"AuxUpperLeft7",
"AuxUpperLeft6",
"AuxUpperLeft5",
"AuxUpperLeft4",
"AuxUpperLeft3",
"AuxUpperLeft2",
"AuxUpperLeft1",
"AuxUpperRight8", //=0x30
"AuxUpperRight7",
"AuxUpperRight6",
"AuxUpperRight5",
"AuxUpperRight4",
"AuxUpperRight3",
"AuxUpperRight2",
"AuxUpperRight1",
"PanicButton", //=0x38
"IcomAmpEnableRelay",//=0x39
"IcomIncRelay", //=0x3A
"IcomPTTRelay", //=0x03B
"IcomDecRelay", //=0x3C
"Eject", //=0x3D unimplemented convenience lamp
"FloorEntry", //=0x3E
"Reverse", //=0x3F
"Joystickboard1", //=0x41
"Joystickboard2", //=0x42
"Joystickboard3", //=0x43
"Joystickboard4", //=0x44
"Joystickboard5", //=0x45
"Joystickboard6", //=0x46
"Joystickboard7", //=0x47
"Joystickboard8", //=0x48
};
return lamp_name[lamp_number];
}
const char* GetBoardName(int board_number)
{
const char* board_name[] =
{
"AuxLowerRight",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"AuxLowerLeft",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Secondary1", //=0x10
"Secondary2",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"AuxUpperCenter", //=0x18
"AuxUpperLeft",
"AuxUpperRight",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"IntKeyPad", //=0x20
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"ExtKeyPad", //=0x28
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Throttel", //=0x30
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"Undefined",
"JoyStick", //=0x38
};
return board_name[board_number];
}
void RequestVersion()
{
static BYTE request_version_string[] = { rio_VersionRequest };
SendCommand(request_version_string);
}
void VersionReply(BYTE* pData) {
std::cout << "RIO Version : " << int(pData[1]) << " . " << int(pData[2]) << std::endl;
}
void RequestAnalogUpdate()
{
static BYTE request_analog_string[] = { rio_AnalogRequest };
SendCommand(request_analog_string);
if (enablecount) {
count++;
now = GetTickCount();
if (count == 10) {
std::cout << "10 analog requests took ," << (now - last) << ",MS about ," << (10000 / (now - last)) << ", per second" << std::endl;
last = now;
count = 0;
}
}
}
void UpdatePadal()
{
#define RANGE_PADAL 500L
#define DEADZONE_PADAL 10L
/*
== PADAL ==
Up : (-)
Down : (+)
------------------
Set Start Position
------------------
Down Up
======================
(+) (-)
======================
g_LeftPedalStart: INT_MAX (+)
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
"lP negative result is unconditional"
*/
LONG lP = 0;
LONG rudder = 16383L;
//
// LEFT PADAL
//
if ((g_LeftPedal < -RANGE_PADAL) || (g_LeftPedal > RANGE_PADAL))
{
if (g_LeftPedal > RANGE_PADAL) {
g_LeftPedalLast = -1000L;
}
else {
g_LeftPedalLast = 0L;
}
}
else
{
if (g_LeftPedalStart > g_LeftPedal) {
g_LeftPedalStart = g_LeftPedal;
}
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
if (lP != 0) {
lP = lP * 1000 / RANGE_PADAL;
}
// Dead Zone
if (abs(lP) < DEADZONE_PADAL) {
lP = 0;
}
g_LeftPedalLast = lP;
}
g_LeftPedalLast = std::abs(g_LeftPedalLast * 32L);
if (*invertXR) {
g_LeftPedalLast = (32766L - g_LeftPedalLast);
}
if (!*enableZR) {
SetAxis(g_LeftPedalLast, 1, HID_USAGE_RX);
}
else {
SetAxis(16383L, 1, HID_USAGE_RX);
}
//
// RIGHT PADAL
//
lP = 0;
if ((g_RightPedal < -RANGE_PADAL) || (g_RightPedal > RANGE_PADAL))
{
if (g_RightPedal > RANGE_PADAL) {
g_RightPedalLast = 1000L;
}
else {
g_RightPedalLast = 0L;
}
}
else
{
if (g_RightPedalStart > g_RightPedal) {
g_RightPedalStart = g_RightPedal;
}
lP = (LONG)g_RightPedalStart - g_RightPedal;
if (lP != 0) {
lP = lP * 1000 / RANGE_PADAL;
}
// Dead Zone
if (abs(lP) < DEADZONE_PADAL) {
lP = 0;
}
g_RightPedalLast = -lP;
}
g_RightPedalLast = std::abs(g_RightPedalLast * 32L);
if (*invertYR) {
g_RightPedalLast = (32766L - g_RightPedalLast);
}
if (!*enableZR) {
SetAxis(g_RightPedalLast, 1, HID_USAGE_RY);
}
else {
SetAxis(16383L, 1, HID_USAGE_RY);
}
rudder = 16383L - (g_LeftPedalLast / 2L) + (g_RightPedalLast / 2L);
if (*invertZR) {
rudder = (32766L - rudder);
}
if (*enableZR) {
SetAxis(rudder, 1, HID_USAGE_RZ);
}
else {
SetAxis(16383L, 1, HID_USAGE_RZ);
}
}
void UpdateThrottle()
{
// [[RIO button value range]]
// RIO board spec: (up) -800 ~ +800 (down)
// actually maximum throttle-value-range is 800, not 1600(-800 ~ 800)
// possible throttle value (g_ThrottleMax ~ (g_ThrottleMax - 800))
// g_ThrottleMax will be changedfrom input value...
//
// g_Throttle/g_ThrottleMax
#define RANGE_THROTTLE 800L
#define ZERO_THROTTLE 0L // see,,, control_mapping.cpp, 0.3
#define DEADZONE_THROTTLE 50L
/*
== THROTTLE ==
Up : (-)
Down : (+)
------------------
Set Start Position
------------------
Up Down
======================
(-) (+)
======================
g_ThrottleStart: INT_MIN (-)
*/
LONG lT;
if ((g_Throttle < -RANGE_THROTTLE) || (g_Throttle > RANGE_THROTTLE))
{
// error
if (g_Throttle < RANGE_THROTTLE) {
if (g_ThrottleResult > 0L) { // back
g_ThrottleLast = 1000L;
}
else {
g_ThrottleLast = -1000L;
}
}
else {
g_ThrottleLast = 0L;
}
}
else
{
// Set StartPosition
if (g_ThrottleStart < g_Throttle)
g_ThrottleStart = g_Throttle;
lT = (LONG)g_ThrottleStart - g_Throttle;
if (lT > 800L)
lT = 800L;
if (lT != 0) {
lT = lT * 1000L / 800L;
if ((lT > -DEADZONE_THROTTLE) && (lT < DEADZONE_THROTTLE)) {
g_ThrottleLast = ZERO_THROTTLE;
}
else {
if (g_ThrottleResult > 0L) { // back
g_ThrottleLast = 900L + (lT * g_ThrottleResult / 10L);
}
else { // front
g_ThrottleLast = lT * g_ThrottleResult;
}
}
}
}
g_ThrottleLast = std::abs(g_ThrottleLast * 32L);
if (*invertZ) {
g_ThrottleLast = 32766L - g_ThrottleLast;
}
SetAxis(g_ThrottleLast, 1, HID_USAGE_Z);
}
void UpdateJoystick()
{
//////////////////////////////////////////////////////////////////////////////////
/*
"The meaning of 80"
"RIO (LEFT: 120 ~ RIGHT: -80) home and promised to return the number of
(LEFT uigyeong right) cut away the rest of the maximum value of 80 or greater RIGHT
The LEFT and RIGHT case you can move at the same speed"
FH changed the method to make the rate adjust to the max/min value seen from the axis
starting with that default value of 80 like the original code but then not sniping off the
rest of the range
*/
//////////////////////////////////////////////////////////////////////////////////
LONG lJx;
LONG lJy;
#define DEADZONE_JOYSTICKX_LEFT 5L
#define DEADZONE_JOYSTICKX_RIGHT 5L
LONG startingLeftrate = -80;
LONG startingrightrate = 80;
startingLeftrate = (minx < startingLeftrate) ? minx : startingLeftrate;
startingrightrate = (maxx > startingrightrate) ? maxx : startingrightrate;
const LONG sLeftRate = (LONG)((16383L) / (LONG)(abs(startingLeftrate) - DEADZONE_JOYSTICKX_LEFT));
const LONG sRightRate = (LONG)((16383L) / (LONG)(startingrightrate - DEADZONE_JOYSTICKX_RIGHT));
lJx = g_JoystickX;
if (lJx < 0L) // LEFT
{
lJx += DEADZONE_JOYSTICKX_LEFT;
if (lJx < 0L) {
// if (lJx < (-80L + DEADZONE_JOYSTICKX_LEFT))
// lJx = (-80L + DEADZONE_JOYSTICKX_LEFT);
g_JoystickXLast = (16383L - (lJx * sLeftRate));
}
else {
g_JoystickXLast = 16383L;
}
}
else if (lJx > 0L) // RIGHT
{
lJx -= DEADZONE_JOYSTICKX_RIGHT;
if (lJx > 0L) {
// if (lJx > (80L - DEADZONE_JOYSTICKX_RIGHT))
// lJx = (80L - DEADZONE_JOYSTICKX_RIGHT);
g_JoystickXLast = (16838L - (( lJx + 2 ) * sRightRate)); // add in a -2 adjustment to prevent snaping alittle to the oposit direction.
}
else {
g_JoystickXLast = 16383L;
}
}
if (*invertX) {
g_JoystickXLast = 32766L - g_JoystickXLast;
}
SetAxis(g_JoystickXLast, 1, HID_USAGE_X);
// std::cout << " X:" << std::setw(4) << minx << ":" << std::setw(4) << g_JoystickX << ":" << std::setw(4) << maxx << ":" << sLeftRate << ":" << g_JoystickXLast << ":" << sRightRate << std::endl;
/////////////////////////
#define DEADZONE_JOYSTICKY_UP 5L
#define DEADZONE_JOYSTICKY_DOWN 5L
LONG startingUprate = -80;
LONG startingDownrate = 80;
startingUprate = (miny < startingUprate) ? miny : startingUprate;
startingDownrate = (maxy > startingDownrate) ? maxy : startingDownrate;
const LONG sUpRate = (LONG)((16383L) / (LONG)(abs(startingUprate) - DEADZONE_JOYSTICKY_UP));
const LONG sDownRate = (LONG)((16383L) / (LONG)(startingDownrate - DEADZONE_JOYSTICKY_DOWN));
lJy = g_JoystickY;
if (lJy < 0L) // UP
{
lJy += DEADZONE_JOYSTICKY_UP;
if (lJy < 0L) {
// if (lJy < (-80L + DEADZONE_JOYSTICKY_UP))
// lJy = (-80L + DEADZONE_JOYSTICKY_UP);
g_JoystickYLast = (16383L - (lJy * sUpRate));
}
else {
g_JoystickYLast = 16383L;
}
}
else if (lJy > 0L) // Down
{
lJy -= DEADZONE_JOYSTICKY_DOWN;
if (lJy > 0L) {
// if (lJy > (80L - DEADZONE_JOYSTICKY_DOWN))
// lJy = (80L - DEADZONE_JOYSTICKY_DOWN);
g_JoystickYLast = (16383L - (lJy * sDownRate));
}
else {
g_JoystickYLast = 16383L;
}
}
if (*invertY) {
g_JoystickYLast = 32766L - g_JoystickYLast;
}
SetAxis(g_JoystickYLast, 1, HID_USAGE_Y);
// std::cout << "Y:" << std::setw(4) << miny << ":" << std::setw(4) << g_JoystickY << ":" << std::setw(4) << maxy << ":" << sUpRate << ":" << g_JoystickYLast << ":" << sDownRate << std::endl;
}
void TestModeChange(BYTE* pData) {
if (pData[1] != 0)
{
std::cout << "RIO entered test mode : " << int(pData[1]) << std::endl;
// TestModeActive = 1;
}
else
{
std::cout << "RIO exited test mode : " << int(pData[1]) << std::endl;
// TestModeActive = 0;
}
}
void RIOcmd(unsigned char ucValue) { // all the things we may want the rio to do that are not outputs to the computer
switch (int(ucValue)) {
case 0:
std::cout << "Reset Analog axes and clabration data" << std::endl;
AnalogAllReset();
g_ThrottleResult = -1L;
g_ThrottleStart = INT_MIN;
g_LeftPedalStart = INT_MAX;
g_RightPedalStart = INT_MAX;
g_JoystickXLast = 16383L;
g_JoystickYLast = 16383L;
g_ThrottleLast = 0L;
g_LeftPedalLast = 0L;
g_RightPedalLast = 0L;
g_RudderLast = 16838L;
maxt = 0;
mint = 0;
maxl = 0;
minl = 0;
maxr = 0;
minr = 0;
maxx = 0;
minx = 0;
maxy = 0;
miny = 0;
break;
case 1:
std::cout << "ResetThrottle" << std::endl;
ResetThrottle();
g_ThrottleResult = -1L;
g_ThrottleStart = INT_MIN;
g_ThrottleLast = 0L;
maxt = 0;
mint = 0;
break;
case 2:
std::cout << "ResetLeftPedal" << std::endl;
ResetLeftPedal();
g_LeftPedalStart = INT_MAX;
g_LeftPedalLast = 0L;
maxl = 0;
minl = 0;
break;
case 3:
std::cout << "ResetRightPedal" << std::endl;
ResetRightPedal();
g_RightPedalStart = INT_MAX;
g_RightPedalLast = 0L;
maxr = 0;
minr = 0;
break;
case 4:
std::cout << "ResetVerticalJoystick" << std::endl;
ResetVerticalJoystick();
g_JoystickYLast = 16383L;
maxy = 0;
miny = 0;
break;
case 5:
std::cout << "ResetHorizontalJoystick" << std::endl;
ResetHorizontalJoystick();
g_JoystickXLast = 16383L;
maxx = 0;
minx = 0;
break;
case 6:
std::cout << "GeneralReset" << std::endl;
GeneralReset();
for (int b = 0; b < 72; b++) {
if (iRIO[b] & 0x8000)
SetLamp(b, LampSolidDim);
}
break;
case 7:
std::cout << "RequestVersion and RequestCheck" << std::endl;
RequestVersion();
RequestCheck();
break;
case 8:
std::cout << "enable/disable axes readout" << std::endl;
g_rawaxes = !g_rawaxes;
break;
case 9:
std::cout << "enable/disable polling Frequency output" << std::endl;
last = GetTickCount();// set the "last" time to be now so that the first output does not count all of the time since program start
enablecount = !enablecount;
break;
default:
std::cout << "unimplimented option : " << int(ucValue) << std::endl;
break;
}
}
static void Press_V2(unsigned char iAddress)
{
int iValue = iRIO[iAddress]; //
bool bIsDim = iValue & 0x8000; // would it be faster/better to just calculate the values as needed
bool bIsMouse = iValue & 0x4000; // or express them out like this and pass values?
bool bIsHat = iValue & 0x2000;
bool bIsJoy = iValue & 0x1000;
bool bIsExt = iValue & 0x0800;
bool bIsALT = iValue & 0x0400;
bool bIsCTRL = iValue & 0x0200;
bool bIsSHIFT = iValue & 0x0100;
unsigned char ucValue = iValue & 0xFF;
if (!bIsJoy && !bIsHat && !bIsMouse) //this must then be a Keyboard press
{
if (bIsSHIFT) { ModKeySHIFT(1); } //take care of any mod keys
if (bIsCTRL) { ModKeyCTRL(1); }
if (bIsALT) { ModKeyALT(1); }
Key(ucValue, 1, bIsExt); // Then press the Key
}
if (bIsJoy && bIsHat && bIsMouse) { //this must then be a RIO command
RIOcmd(ucValue);
return;
}
if (bIsJoy) { // call the vJoy button function with press and Value
SetBtn(TRUE, 1, ucValue);
}
else if (bIsHat) { // call the vJoy hat function with direction information
SetDiscPov(ucValue, 1, 1);
}
else if (bIsMouse) { // call the mouse function as a press (1)
Mouse(ucValue, 1);
}
// Work on button lighting
if (iRIO[iAddress] & 0x8000)
SetLamp(iAddress, LampSolidBright);
}
static void Release_V2(unsigned char iAddress)
{
int iValue = iRIO[iAddress];
bool bIsDim = iValue & 0x8000; // would it be faster/better to just calculate the values as needed
bool bIsMouse = iValue & 0x4000; // or express them out like this and pass values?
bool bIsHat = iValue & 0x2000;
bool bIsJoy = iValue & 0x1000;
bool bIsExt = iValue & 0x0800;
bool bIsALT = iValue & 0x0400;
bool bIsCTRL = iValue & 0x0200;
bool bIsSHIFT = iValue & 0x0100;
unsigned char ucValue = iValue & 0xFF;
if (!bIsJoy && !bIsHat && !bIsMouse) //this must then be a Keyboard press
{
Key(ucValue, 0, bIsExt); // release the Key
if (bIsALT) { ModKeyALT(0); } //take care of any mod keys
if (bIsCTRL) { ModKeyCTRL(0); }
if (bIsSHIFT) { ModKeySHIFT(0); }
}
if (bIsJoy && bIsHat && bIsMouse) { //this must then be a RIO command
return;
}
if (bIsJoy) { // call the vJoy button function with release and Value
SetBtn(FALSE, 1, ucValue);
}
else if (bIsHat) { // call the vJoy hat function with centering information
SetDiscPov(-1, 1, 1);
}
else if (bIsMouse) { // call the mouse function as a release (0)
Mouse(ucValue, 0);
}
//work on button lighting
if (iRIO[iAddress] & 0x8000)
SetLamp(iAddress, LampSolidDim);
}
static void ModKeySHIFT(bool bIsPressed)
{
INPUT sInput = { 0 };
sInput.type = INPUT_KEYBOARD;
if (!bIsPressed) { sInput.ki.dwFlags = KEYEVENTF_KEYUP; }
sInput.ki.wVk = VK_SHIFT;
sInput.ki.wScan = 0x2A;
SendInput(1, &sInput, sizeof(sInput));
}
static void ModKeyCTRL(bool bIsPressed)
{
INPUT cInput = { 0 };
cInput.type = INPUT_KEYBOARD;
if (!bIsPressed) { cInput.ki.dwFlags = KEYEVENTF_KEYUP; }
cInput.ki.wVk = VK_CONTROL;
cInput.ki.wScan = 0x1D;
SendInput(1, &cInput, sizeof(cInput));
}
static void ModKeyALT(bool bIsPressed)
{
INPUT aInput = { 0 };
aInput.type = INPUT_KEYBOARD;
if (!bIsPressed) { aInput.ki.dwFlags = KEYEVENTF_KEYUP; }
aInput.ki.wVk = VK_MENU;
aInput.ki.wScan = 0x38;
SendInput(1, &aInput, sizeof(aInput));
}
static void Key(unsigned char ucwVk, bool bIsPressed, bool bIsExtended)
{
//INPUT kInput = { 0 };
//kInput.type = INPUT_KEYBOARD;
//if (!bIsPressed) { kInput.ki.dwFlags = KEYEVENTF_KEYUP; }
//if (bIsExtended) { kInput.ki.dwFlags = kInput.ki.dwFlags | KEYEVENTF_EXTENDEDKEY; }
//kInput.ki.wVk = ucwVk;
//kInput.ki.wScan = MapVirtualKey(kInput.ki.wVk, MAPVK_VK_TO_VSC);
//SendInput(1, &kInput, sizeof(kInput));
INPUT kInput = { 0 };
kInput.type = INPUT_KEYBOARD;
kInput.ki.dwFlags = KEYEVENTF_SCANCODE;
if (!bIsPressed) { kInput.ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_SCANCODE; }
if (bIsExtended) { kInput.ki.dwFlags = kInput.ki.dwFlags | KEYEVENTF_EXTENDEDKEY; }
kInput.ki.wVk = 0;
kInput.ki.wScan = MapVirtualKey(ucwVk, MAPVK_VK_TO_VSC);
kInput.ki.time = 0;
SendInput(1, &kInput, sizeof(kInput));
}
static void Mouse(unsigned char ucValue, bool bIsPressed)
{
INPUT mInput = { 0 };
mInput.type = INPUT_MOUSE;
if (ucValue == 0) { //do the UP thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
mInput.mi.dx = -50;
SendInput(1, &mInput, sizeof(mInput));
}
else {
// do nothing?
}
}
else if (ucValue == 1) { //do the RIGHT thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
mInput.mi.dy = 50;
SendInput(1, &mInput, sizeof(mInput));
}
else {
//do nothing?
}
}
else if (ucValue == 2) { //do the DOWN thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
mInput.mi.dx = 50;
SendInput(1, &mInput, sizeof(mInput));
}
else {
// do mothing?
}
}
else if (ucValue == 3) { //do the LEFT thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
mInput.mi.dy = -50;
SendInput(1, &mInput, sizeof(mInput));
}
else {
// do nothing?
}
}
else if (ucValue == 4) { //do the Lclick thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | KEYEVENTF_EXTENDEDKEY;
}
else {
mInput.mi.dwFlags = MOUSEEVENTF_LEFTUP | KEYEVENTF_EXTENDEDKEY;
}
SendInput(1, &mInput, sizeof(mInput));
}
else if (ucValue == 5) { //do the Rclick thing
if (bIsPressed) {
mInput.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN | KEYEVENTF_EXTENDEDKEY;
}
else {
mInput.mi.dwFlags = MOUSEEVENTF_RIGHTUP | KEYEVENTF_EXTENDEDKEY;
}
SendInput(1, &mInput, sizeof(mInput));
}
}
void CPlasma::SendPacket(const BYTE* pbPacket, char nLen)
{
if (IsValid())
txComLoop(pbPacket, nLen);
}
void CPlasma::PlasmaClear()
{
char string[] = { ESC, '@' };
SendPacket((BYTE*)string, 2);
}
void CPlasma::PlasmaCursor(char n)
{
char string[] = { ESC, 'G', char(n) };
SendPacket((BYTE*)string, 3);
}
void CPlasma::PlasmaCursorHome()
{
char string[] = { ESC, 'L' };
SendPacket((BYTE*)string, 2);
}
void CPlasma::PlasmaCursorX(char n)
{
char string[] = { ESC, 'R', n };
SendPacket((BYTE*)string, 3);
}
void CPlasma::PlasmaCursorY(char n)
{
char string[] = { ESC, 'Q', n };
SendPacket((BYTE*)string, 3);
}
void CPlasma::PlasmaFontAttr(char n)
{
char string[] = { ESC, 'H', (BYTE)n };
SendPacket((BYTE*)string, 3);
}
void CPlasma::PlasmaFont(char n)
{
char string[] = { 27 /*ESC*/, 'K', n };
SendPacket((BYTE*)string, 3);
}
void CPlasma::PlasmaText(const char* szText)
{
if (!szText)
return;
int len = strlen(szText);
if (len >= 256)
return;
SendPacket((BYTE*)szText, (char)len);
}
POINT CPlasma::GetFontSize(int Font)
{
POINT size = { 0, 0 };
switch (Font)
{
case 0:
case 1:
case 2:
case 3:
size.x = 5;
size.y = 7;
break;
case 4:
case 5:
size.x = 10;
size.y = 14;
break;
case 6:
case 7:
size.x = 5;
size.y = 7;
break;
}
return size;
}
void CPlasma::PlasmaBoxDraw(RECT tag)
{
char string[] = { ESC, 'X', (char)tag.left, (char)tag.top, (char)tag.right, (char)tag.bottom };
SendPacket((BYTE*)string, 6);
}
void CPlasma::PlasmaBoxFill(RECT tag)
{
char string[] = { ESC, 'x', 0, (char)tag.left, (char)tag.top, (char)tag.right, (char)tag.bottom };
SendPacket((BYTE*)string, 7);
}
void CPlasma::PlasmaPosText(const char* szText, char x, char y, char Attr, char Font)
{
if (!szText)
return;
int len = strlen(szText);
if (len <= 0)
return;
if (Font != 2) // !Score
{
if (len <= 9) {
Font = 5;
}
else if (len <= 20) {
Font = 2;
}
else {
Font = 2;
len = 20;
}
}
else
{
if (len > 20)
len = 20;
}
if (x == 0 && y == 0) {
x = 56 - ((len * GetFontSize(Font).x) / 2);
y = 15 - ((GetFontSize(Font).y) / 2);
}
PlasmaCursorX(x);
PlasmaCursorY(y);
PlasmaFontAttr(Attr);
PlasmaFont(Font);
if (len >= 256)
return;
SendPacket((const BYTE*)szText, len);
}
void CPlasma::PlasmaScoreDraw(const char* Rank, const char* Score, BOOL bFlag)
{
int CurRank = atoi(Rank);
int CurScore = atoi(Score);
RECT tag1 = { 27,19,42,30 };
RECT tag2 = { 42,19,93,30 };
RECT tag3 = { 27,19,93,30 }; //Fill
if (bFlag == TRUE)
{
PlasmaBoxFill(tag3);
PlasmaBoxDraw(tag1);
PlasmaBoxDraw(tag2);
}
int len_Rank = strlen(Rank);
int len_Score = strlen(Score);
//{
if (len_Rank <= 2 && len_Rank > 0)
if (len_Score <= 8 && len_Score > 0)
{
if (CurRank != m_nOldRank)
{
PlasmaPosText(" ", 29, 20, 0, 2);
if (len_Rank == 1)
PlasmaPosText(Rank, 33, 20, 0, 2);
else
PlasmaPosText(Rank, 29, 20, 0, 2);
}
if (m_nOldScore != CurScore)
{
PlasmaPosText(" ", 44, 20, 0, 2);
PlasmaPosText(Score, (char)(85 - (len_Score * 5)), 20, 0, 2);
}
}
m_nOldRank = CurRank;
m_nOldScore = CurScore;
//}
}
void CPlasma::DoPlasma(char nMode)
{
switch (nMode)
{
case 0:
m_nGlobalState = -1;
break;
case 1:
if ((m_nGlobalState != 0) && (m_nGlobalState != 2))
return;
break;
case 2:
if ((m_nGlobalState != 1) && (m_nGlobalState != 2))
return;
break;
case 9:
if ((m_nGlobalState != 1) && (m_nGlobalState != 2))
return;
PlasmaDisplay(0, NULL, NULL, NULL);
m_nGlobalState = -1;
return;
default:
return;
}
}
void CPlasma::PlasmaDisplay(int Gamestate, const char* szName/*9*/, const char* Rank/*2*/, const char* Score/*8*/)
{
static BOOL BoxFlag = FALSE;
static BOOL NameFlag = FALSE;
if (Gamestate == 0) // end
{
NameFlag = TRUE;
PlasmaClear();
}
else if (Gamestate == 1) // ready
{
if (NameFlag) {
PlasmaPosText(" ");
PlasmaPosText(szName);
}
NameFlag = FALSE;
BoxFlag = TRUE;
m_nOldRank = INT_MIN;
m_nOldScore = INT_MIN;
}
else if (Gamestate == 2) // start
{
PlasmaScoreDraw(Rank, Score, BoxFlag);
BoxFlag = FALSE;
}
}
C232Comm::C232Comm()
: m_hCom(INVALID_HANDLE_VALUE), m_nPort(0)
{
}
C232Comm::~C232Comm()
{
Close();
}
BOOL C232Comm::Open(int nPort, DWORD dwBaudRate/* = CBR_9600*/, BYTE byParity/* = NOPARITY*/, BYTE bByteSize/* = 8*/, BYTE byStopBits/* = ONESTOPBIT*/)
{
m_nPort = nPort;
char sz[32];
sprintf_s(sz, g_szPortFormat, m_nPort);
// fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz,
wchar_t wsz[32];
std::mbstowcs(wsz, sz, std::strlen(sz) + 1);
LPWSTR lpwsz = wsz;
m_hCom = CreateFile(lpwsz, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (IsValid()) {
m_cp.wPacketLength = sizeof(COMMPROP); /* get comm properties */
if (GetCommProperties(m_hCom, &m_cp)) {
if (GetCommState(m_hCom, &m_dcb)) {
m_dcb.BaudRate = (DWORD)dwBaudRate;
m_dcb.ByteSize = bByteSize; // ONESTOPBIT TWOSTOPBITS
m_dcb.Parity = byParity; // NOPARITY EVENPARITY ODDPARITY
m_dcb.StopBits = byStopBits;
m_dcb.fOutxCtsFlow = 0;
m_dcb.fOutxDsrFlow = 0;
m_dcb.fRtsControl = 0;
m_dcb.fDtrControl = DTR_CONTROL_ENABLE; // HANDSHAKE; // 9;
m_dcb.fOutX = 0;
m_dcb.fInX = 0;
if (SetCommState(m_hCom, &m_dcb)) {
memset(&m_commTimeOuts, 0, sizeof(m_commTimeOuts));
m_commTimeOuts.ReadIntervalTimeout = MAXDWORD;
m_commTimeOuts.ReadTotalTimeoutMultiplier = 0;
m_commTimeOuts.ReadTotalTimeoutConstant = 0;
m_commTimeOuts.WriteTotalTimeoutMultiplier = 5;
m_commTimeOuts.WriteTotalTimeoutConstant = 5;
if (SetupComm(m_hCom, 2048 * 5, 1024)) {
if (SetCommTimeouts(m_hCom, &m_commTimeOuts)) {
if (GetCommTimeouts(m_hCom, &m_commTimeOuts)) {
if (GetCommState(m_hCom, &m_dcb)) {
m_dcb.fDtrControl = DTR_CONTROL_DISABLE;
Sleep(10);
SetCommState(m_hCom, &m_dcb);
return TRUE;
}
}
}
}
else {
}
}
else {
}
}
else {
}
}
else {
}
locProcessCommError();
Close();
}
else {
}
return FALSE;
}
void C232Comm::Close()
{
if (IsValid()) {
CloseHandle(m_hCom);
m_hCom = INVALID_HANDLE_VALUE;
}
}
int C232Comm::rxCom(void* pData, int nLen)
{
if (!ReadFile(m_hCom, pData, (DWORD)nLen, (LPDWORD)&nLen, (LPOVERLAPPED)NULL)) {
locProcessCommError();
return -1;
}
/* clean out any pending bytes in the receive buffer */
PurgeComm(m_hCom, PURGE_RXCLEAR);
return nLen;
}
BOOL C232Comm::rxComLoop(void* pData, int nLen)
{
while (nLen > 0) {
int nRead = rxCom(pData, nLen);
if (nRead == -1)
return FALSE;
nLen -= nRead;
pData = &((BYTE*)pData)[nRead];
}
return TRUE;
}
int C232Comm::txCom(const void* pData, int nLen)
{
if (!WriteFile(m_hCom, pData, (DWORD)nLen, (LPDWORD)&nLen, NULL)) {
locProcessCommError();
return -1;
}
return nLen;
}
int C232Comm::txComLoop(const void* pData, int nLen)
{
while (nLen > 0) {
int nSent = txCom(pData, nLen);
if (nSent == -1)
return FALSE;
nLen -= nSent;
pData = &((BYTE*)pData)[nSent];
}
return TRUE;
}
void C232Comm::locProcessCommError()
{
DWORD dwError;
COMSTAT cs;
/* clear error */
ClearCommError(m_hCom, &dwError, &cs);
}
int C232Comm::CommunicationConfig(HWND hWndParent)
{
if (IsValid()) {
DWORD dwCC = sizeof(m_cc);
m_cc.dwSize = dwCC;
if (GetCommConfig(m_hCom, &m_cc, &dwCC)) {
char sz[32];
sprintf_s(sz, g_szPortFormat, m_nPort);
// fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz,
wchar_t wsz[32];
std::mbstowcs(wsz, sz, std::strlen(sz) + 1);
LPWSTR lpwsz = wsz;
if (CommConfigDialog(lpwsz, hWndParent, &m_cc)) {
if (SetCommState(m_hCom, &m_cc.dcb)) {
// write new settings
return +1;
}
locProcessCommError();
return 0;
}
locProcessCommError();
return -1;
}
else {
locProcessCommError();
return -2;
}
}
else {
return -3;
}
}