Files
2023-12-06 23:14:20 +01:00

772 lines
20 KiB
C++

#include "JSystem/JUtility/JUTGamePad.h"
#include "JSystem/JKernel/JKRHeap.h"
#include <math.h>
#include <std/math.h>
u32 JUTGamePad::CRumble::sChannelMask[PAD_CONTROLLER_NUM] = {
0x80000000 >> 0,
0x80000000 >> 1,
0x80000000 >> 2,
0x80000000 >> 3};
static u32 channel_mask[PAD_CONTROLLER_NUM] = {
0x80000000 >> 0,
0x80000000 >> 1,
0x80000000 >> 2,
0x80000000 >> 3};
JUTGamePad::EStickMode JUTGamePad::sStickMode = Clamped;
JUTGamePad::EClampMode JUTGamePad::sClampMode = Clamp;
f32 JUTGamePad::sPressPoint = 0.5f;
f32 JUTGamePad::sReleasePoint = 0.25f;
u32 JUTGamePad::C3ButtonReset::sResetPattern = START | X | B;
u32 JUTGamePad::C3ButtonReset::sResetMaskPattern = 0xFFFF;
JSUList<JUTGamePad> JUTGamePad::mPadList(false);
PADStatus JUTGamePad::mPadStatus[PAD_CONTROLLER_NUM];
JUTGamePad::CButton JUTGamePad::mPadButton[PAD_CONTROLLER_NUM];
JUTGamePad::CStick JUTGamePad::mPadMStick[PAD_CONTROLLER_NUM];
JUTGamePad::CStick JUTGamePad::mPadSStick[PAD_CONTROLLER_NUM];
s32 JUTGamePad::msaSIType[PAD_CONTROLLER_NUM];
bool JUTGamePad::mListInitialized = false;
u8 JUTGamePad::mPadAssign[PAD_CONTROLLER_NUM];
LGWheels *JUTGamePad::mspLGWheels;
u32 JUTGamePad::sSuppressPadReset = 0;
bool JUTGamePad::sErrorReport = false;
u32 JUTGamePad::sAnalogMode = 0;
u32 JUTGamePad::sRumbleSupported = 0;
u8 JUTGamePad::CRumble::mStatus[PAD_CONTROLLER_NUM];
u32 JUTGamePad::CRumble::mEnabled = 0;
JUTResetBtnCb JUTGamePad::C3ButtonReset::sCallback = nullptr;
void *JUTGamePad::C3ButtonReset::sCallbackArg = nullptr;
OSTime JUTGamePad::C3ButtonReset::sThreshold = (OSTime)(OS_TIMER_CLOCK / 60) * 30;
bool JUTGamePad::C3ButtonReset::sResetSwitchPushing = false;
bool JUTGamePad::C3ButtonReset::sResetOccurred = false;
s32 JUTGamePad::C3ButtonReset::sResetOccurredPort = 0;
JSUList<JUTGamePadLongPress> JUTGamePadLongPress::sPatternList(false);
JUTGamePad::JUTGamePad(EPadPort port)
: mButtons(),
mMainStick(),
mSubStick(),
mRumble(this),
mLink(this),
mButtonReset()
{
mPortNum = port;
if (mPortNum >= 0)
{
mPadAssign[port]++;
}
initList();
JUTGamePad::mPadList.append(&mLink);
update();
mPadRecord = nullptr;
mPadReplay = nullptr;
clear();
}
JUTGamePad::JUTGamePad()
: mButtons(),
mMainStick(),
mSubStick(),
mRumble(this),
mLink(this),
mButtonReset()
{
mPortNum = -1;
initList();
JUTGamePad::mPadList.append(&mLink);
mPadRecord = nullptr;
mPadReplay = nullptr;
clear();
}
JUTGamePad::~JUTGamePad()
{
if (mPortNum >= 0)
{
mPadAssign[mPortNum]--;
mPortNum = -1;
}
JUTGamePad::mPadList.remove(&mLink);
}
void JUTGamePad::initList()
{
if (!JUTGamePad::mListInitialized)
{
JUTGamePad::mPadList.initiate();
JUTGamePad::mListInitialized = true;
}
}
void JUTGamePad::init()
{
PADSetSpec(5);
JUTGamePad::sAnalogMode = 3;
PADSetAnalogMode(3);
PADInit();
if (mspLGWheels == nullptr)
mspLGWheels = new (JKRGetSystemHeap(), 0) LGWheels();
}
void JUTGamePad::clear()
{
mButtonReset.mReset = false;
}
u32 JUTGamePad::read()
{
sRumbleSupported = PADRead(mPadStatus);
mspLGWheels->ReadAll();
switch (sClampMode)
{
case 1:
PADClamp(mPadStatus);
break;
case 2:
PADClampCircle(mPadStatus);
break;
}
u32 mask_tmp;
u32 mask = 0;
for (int i = 0; i < PAD_CONTROLLER_NUM; i++)
{
u32 stick_buttons;
u32 siType = SIProbe(i);
msaSIType[i] = siType;
switch (siType)
{
case SI_TYPE_GC:
if(mspLGWheels->IsConnected(i))
{
LGPosition *lgPosition = &mspLGWheels->mPosition[i];
stick_buttons = mPadMStick[i].update(lgPosition->steerDirection, mPadButton[i].mButton) << 24;
mPadButton[i].update(lgPosition, stick_buttons);
}
break;
case SI_GC_CONTROLLER:
case SI_GC_WAVEBIRD:
mask_tmp = 0x80000000 >> i;
if (mPadStatus[i].err == PAD_ERR_NONE)
{
PADStatus *padStatus = &mPadStatus[i];
stick_buttons = (mPadMStick[i].update(padStatus->stickX, padStatus->stickY,
sStickMode, WhichStick_MainStick, mPadButton[i].mButton)
<< 0x18);
stick_buttons |= (mPadSStick[i].update(padStatus->substickX, padStatus->substickY,
sStickMode, WhichStick_SubStick, mPadButton[i].mButton)
<< 0x10);
mPadButton[i].update(padStatus, stick_buttons);
}
else if (mPadStatus[i].err == PAD_ERR_NO_CONTROLLER)
{
mPadMStick[i].update(0, 0, sStickMode, WhichStick_MainStick, 0);
mPadSStick[i].update(0, 0, sStickMode, WhichStick_SubStick, 0);
mPadButton[i].update((PADStatus*)0, 0);
if (!(sSuppressPadReset & (mask_tmp)))
{
mask |= mask_tmp;
}
}
else
{
#ifdef DEBUG
if (sErrorReport)
OSReport("game pad read error (%d)\n", mPadStatus[i].err);
#endif
mPadButton[i].mTrigger = 0;
mPadButton[i].mRelease = 0;
mPadButton[i].mRepeat = 0;
}
break;
default:
break;
}
}
JSUListIterator<JUTGamePad> pad = (mPadList.getFirst());
for (; pad != mPadList.getEnd(); ++pad)
{
if (pad->getPadReplay() && pad->getPadReplay()->isActive())
{
PADStatus status;
pad->getPadReplay()->read(&status);
u32 stick = pad->mMainStick.update(status.stickX, status.stickY, sStickMode,
WhichStick_MainStick, pad->mButtons.mButton)
<< 0x18;
stick |= pad->mSubStick.update(status.substickX, status.substickY, sStickMode,
WhichStick_SubStick, pad->mButtons.mButton)
<< 0x10;
pad->mButtons.update(&status, stick);
}
else
{
if (pad->mPortNum == -1)
pad->assign();
if (pad->mPortNum != -999)
pad->update();
}
if (pad->getPadRecord() && pad->getPadRecord()->isActive())
{
if (pad->mPortNum >= 0)
{
int portNum = pad->mPortNum;
if (mPadStatus[portNum].err == 0)
pad->getPadRecord()->write(&mPadStatus[portNum]);
}
}
}
if (mask != 0)
{
PADReset(mask);
}
checkResetSwitch();
return sRumbleSupported;
}
void JUTGamePad::assign()
{
for (s32 i = 0; i < PAD_CONTROLLER_NUM; i++)
{
if (JUTGamePad::mPadStatus[i].err == 0 && JUTGamePad::mPadAssign[i] == 0)
{
mPortNum = i;
JUTGamePad::mPadAssign[i] = 1;
JUTGamePad::mPadButton[i].setRepeat(mButtons.mRepeatMask, mButtons.mRepeatDelay, mButtons.mRepeatFrequency);
mRumble.clear(this);
break;
}
}
}
void JUTGamePad::checkResetCallback(OSTime time)
{
if (mPortNum >= 0 && time >= JUTGamePad::C3ButtonReset::sThreshold)
{
JUTGamePad::C3ButtonReset::sResetOccurred = true;
JUTGamePad::C3ButtonReset::sResetOccurredPort = mPortNum;
if (JUTGamePad::C3ButtonReset::sCallback)
{
(*JUTGamePad::C3ButtonReset::sCallback)(mPortNum, JUTGamePad::C3ButtonReset::sCallbackArg);
}
}
}
void JUTGamePad::update()
{
if (mPortNum >= 0)
{
mButtons = JUTGamePad::mPadButton[mPortNum];
mMainStick = JUTGamePad::mPadMStick[mPortNum];
mSubStick = JUTGamePad::mPadSStick[mPortNum];
mErrorStatus = JUTGamePad::mPadStatus[mPortNum].err;
if (JUTGamePad::C3ButtonReset::sResetPattern != (mButtons.mButton & JUTGamePad::C3ButtonReset::sResetMaskPattern))
{
mButtonReset.mReset = false;
}
else if (JUTGamePad::C3ButtonReset::sResetOccurred == false)
{
if (mButtonReset.mReset == true)
{
s64 diff = OSGetTime() - mResetTime;
checkResetCallback(diff);
}
else
{
mButtonReset.mReset = true;
mResetTime = OSGetTime();
}
}
JSUListIterator<JUTGamePadLongPress> pad = JUTGamePadLongPress::sPatternList.getFirst();
for (; pad != JUTGamePadLongPress::sPatternList.getEnd(); ++pad)
{
if (mPortNum >= 0 && mPortNum < 4)
{
if (pad->isValid())
{
if ((mButtons.mButton & pad->getMaskPattern()) == pad->getPattern())
{
if (pad->_20[mPortNum] == true)
{
s64 diff = OSGetTime() - pad->mTimer[mPortNum];
pad->checkCallback(mPortNum, diff);
continue;
}
pad->_20[mPortNum] = true;
pad->mTimer[mPortNum] = OSGetTime();
continue;
}
pad->_20[mPortNum] = false;
}
}
}
mRumble.update(mPortNum);
}
}
void JUTGamePad::checkResetSwitch()
{
if (!JUTGamePad::C3ButtonReset::sResetOccurred)
{
if (OSGetResetSwitchState() != 0)
{
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
}
else
{
if (JUTGamePad::C3ButtonReset::sResetSwitchPushing == true)
{
JUTGamePad::C3ButtonReset::sResetOccurred = true;
JUTGamePad::C3ButtonReset::sResetOccurredPort = -1;
if (*JUTGamePad::C3ButtonReset::sCallback != nullptr)
{
(*JUTGamePad::C3ButtonReset::sCallback)(-1, JUTGamePad::C3ButtonReset::sCallbackArg);
}
}
JUTGamePad::C3ButtonReset::sResetSwitchPushing = false;
}
}
}
void JUTGamePad::CButton::clear()
{
mButton = 0;
mTrigger = 0;
mRelease = 0;
mRepeat = 0;
mAnalogA = 0;
mAnalogB = 0;
mAnalogL = 0;
mAnalogR = 0;
mRepeatTimer = 0;
mRepeatLastButton = 0;
mRepeatMask = 0;
mRepeatDelay = 0;
mRepeatFrequency = 0;
}
void JUTGamePad::CButton::update(const PADStatus *padStatus, u32 stick_buttons)
{
u32 tempButtons;
u32 buttons;
if (padStatus != nullptr)
{
tempButtons = padStatus->button;
}
else
{
tempButtons = 0;
}
buttons = stick_buttons | tempButtons;
mRepeat = 0;
if (mRepeatDelay != 0)
{
if (mRepeatMask != 0)
{
u32 repeatButtons = buttons & mRepeatMask;
mRepeat = 0;
if (repeatButtons == 0)
{
mRepeatLastButton = 0;
mRepeatTimer = 0;
}
else if (mRepeatLastButton == repeatButtons)
{
mRepeatTimer++;
if (
mRepeatTimer == mRepeatDelay ||
(mRepeatTimer > mRepeatDelay && ((mRepeatTimer - mRepeatDelay) % mRepeatFrequency) == 0))
{
mRepeat = repeatButtons;
}
}
else
{
mRepeat = repeatButtons & (mRepeatLastButton ^ 0xFFFFFFFF);
mRepeatLastButton = repeatButtons;
mRepeatTimer = 0;
}
}
}
mTrigger = buttons & (buttons ^ mButton);
mRelease = mButton & (buttons ^ mButton);
mButton = buttons;
mRepeat |= (mRepeatMask ^ 0xFFFFFFFF) & mTrigger;
if (padStatus != nullptr)
{
mAnalogA = padStatus->analogA;
mAnalogB = padStatus->analogB;
mAnalogL = padStatus->triggerLeft;
mAnalogR = padStatus->triggerRight;
}
else
{
mAnalogA = 0;
mAnalogB = 0;
mAnalogL = 0;
mAnalogR = 0;
}
mAnalogLf = (f32)(int)mAnalogL / 150.0f;
mAnalogRf = (f32)(int)mAnalogR / 150.0f;
}
void JUTGamePad::CButton::update(const LGPosition *lgPosition, u32 mask)
{
if (lgPosition->button & PAD_BUTTON_A)
mask |= PAD_BUTTON_A;
if (lgPosition->button & PAD_BUTTON_B)
mask |= PAD_BUTTON_B;
if (lgPosition->button & PAD_BUTTON_X)
mask |= PAD_BUTTON_X;
if (lgPosition->button & PAD_BUTTON_Y)
mask |= PAD_BUTTON_Y;
if (lgPosition->button & PAD_BUTTON_START)
mask |= PAD_BUTTON_START;
if (lgPosition->button & PAD_TRIGGER_L)
mask |= PAD_TRIGGER_L;
if (lgPosition->button & PAD_TRIGGER_R)
mask |= PAD_TRIGGER_R;
if (lgPosition->button & PAD_TRIGGER_Z)
mask |= PAD_TRIGGER_Z;
if (lgPosition->button & PAD_BUTTON_LEFT)
mask |= (PAD_BUTTON_LEFT | PAD_STICK_LEFT);
if (lgPosition->button & PAD_BUTTON_RIGHT)
mask |= (PAD_BUTTON_RIGHT | PAD_STICK_RIGHT);
if (lgPosition->button & PAD_BUTTON_DOWN)
mask |= (PAD_BUTTON_DOWN | PAD_STICK_DOWN);
if (lgPosition->button & PAD_BUTTON_UP)
mask |= (PAD_BUTTON_UP | PAD_STICK_UP);
if (lgPosition->gasPedal == 255)
mask |= PAD_BUTTON_A;
if (lgPosition->brakePedal == 255)
mask |= PAD_BUTTON_B;
if (mRepeatDelay != 0 && mRepeatMask != 0)
{
u32 pressedButtons = mask & mRepeatMask;
mRepeat = 0;
if(pressedButtons == 0) {
mRepeatLastButton = 0;
mRepeatTimer = 0;
}
else if (mRepeatLastButton == pressedButtons)
{
mRepeatTimer++;
if (mRepeatTimer == mRepeatDelay || (mRepeatTimer > mRepeatDelay && !((mRepeatTimer - mRepeatDelay) % mRepeatFrequency)))
mRepeat = pressedButtons;
}
else {
mRepeat = pressedButtons & (mRepeatLastButton ^ 0xffffffff);
mRepeatLastButton = pressedButtons;
mRepeatTimer = 0;
}
}
mTrigger = mask & (mask ^mButton);
mRelease = mButton & (mask ^ mButton);
mButton = mask;
mRepeat = mRepeat | (mRepeatMask ^ 0xffffffff) & mTrigger;
mAnalogA = lgPosition->gasPedal;
mAnalogB = lgPosition->brakePedal;
mAnalogL = lgPosition->analogL;
mAnalogR = lgPosition->analogR;
mAnalogLf = mAnalogL / 255.0f;
mAnalogRf = mAnalogR / 255.0f;
}
void JUTGamePad::CStick::clear()
{
mX = 0.0f;
mY = 0.0f;
mValue = 0.0f;
mAngle = 0;
}
u32 JUTGamePad::CStick::update(s8 x, s8 y, EStickMode stickMode, EWhichStick whichStick, u32 mask)
{
s32 clamp;
switch (getClampMode())
{
case Clamp:
clamp = whichStick == WhichStick_MainStick ? 54 : 42;
break;
case ClampCircle:
clamp = whichStick == WhichStick_MainStick ? 38 : 29;
break;
default:
clamp = whichStick == WhichStick_MainStick ? 69 : 57;
break;
}
mAnalogX = x;
mAnalogY = y;
mX = (f32)x / clamp;
mY = (f32)y / clamp;
mValue = std::sqrtf(mX * mX + mY * mY); // calls real sqrtf
if (mValue > 1.0f)
{
if (stickMode == Clamped)
{
mX /= mValue;
mY /= mValue;
}
mValue = 1.0f;
}
if (mValue > 0.0f)
{
if (mY == 0.0f)
{
if (mX > 0.0f)
{
mAngle = 0x4000;
}
else
{
mAngle = -0x4000;
}
}
else
{
mAngle = atan2f(mX, -mY) * 10430.379f; //((f32)0x8000 / F_PI);
}
}
return getButton(mask >> (whichStick == WhichStick_MainStick ? 0x18 : 0x10));
}
u32 JUTGamePad::CStick::update(s8 x, u32 mask)
{
mAnalogX = x;
if (mAnalogX < -127)
mAnalogX = -127;
mAnalogY = 0;
mX = x / 127.0f;
mY = 0.0f;
mValue = mX;
return getButton(mask >> 24);
}
u32 JUTGamePad::CStick::getButton(u32 mask)
{
u32 button = mask & 0xf;
if (-sReleasePoint < mX && mX < sReleasePoint)
{
button &= ~(DPAD_LEFT | DPAD_RIGHT);
}
else if (mX <= -sPressPoint)
{
button &= ~DPAD_RIGHT;
button |= DPAD_LEFT;
}
else if (mX >= sPressPoint)
{
button &= ~DPAD_LEFT;
button |= DPAD_RIGHT;
}
if (-sReleasePoint < mY && mY < sReleasePoint)
{
button &= ~(DPAD_DOWN | DPAD_UP);
}
else if (mY <= -sPressPoint)
{
button &= ~DPAD_UP;
button |= DPAD_DOWN;
}
else if (mY >= sPressPoint)
{
button &= ~DPAD_DOWN;
button |= DPAD_UP;
}
return button;
}
void JUTGamePad::CRumble::clear()
{
mFrame = 0;
mLength = 0;
mData = nullptr;
mFrameCount = 0;
_10 = 0;
JUTGamePad::CRumble::mEnabled = 0xF0000000;
}
void JUTGamePad::CRumble::clear(JUTGamePad *gamePad)
{
if (0 <= (s16)gamePad->getPortNum() && (s16)gamePad->getPortNum() < PAD_CONTROLLER_NUM)
{
JUTGamePad::CRumble::mStatus[(s16)gamePad->getPortNum()] = 0;
gamePad->stopMotorHard();
}
clear();
}
void JUTGamePad::CRumble::startMotor(int port)
{
if (JUTGamePad::CRumble::isEnabledPort(port))
{
PADControlMotor(port, PAD_MOTOR_RUMBLE);
JUTGamePad::CRumble::mStatus[port] = 1;
}
}
void JUTGamePad::CRumble::stopMotor(int port, bool hard)
{
if (JUTGamePad::CRumble::isEnabledPort(port))
{
PADControlMotor(port, (u8)(hard ? PAD_MOTOR_STOP_HARD : PAD_MOTOR_STOP));
JUTGamePad::CRumble::mStatus[port] = 0;
}
}
static bool getNumBit(u8 *data, int bit)
{
u8 numBit = ((0x80 >> (bit & 7)) & data[bit >> 3] );
return numBit != 0;
}
void JUTGamePad::CRumble::update(s16 port)
{
if (!isEnabledPort(port))
{
mFrame = 0;
mLength = 0;
mData = nullptr;
mFrameCount = 0;
_10 = 0;
}
if (mLength != 0)
{
if (mFrame >= mLength)
{
JUTGamePad::CRumble::stopMotorHard(port);
mLength = 0;
}
else
{
if (mFrameCount == 0)
{
if (JUTGamePad::CRumble::mStatus[port] == 0)
{
JUTGamePad::CRumble::startMotor(port);
}
return;
}
bool enable = getNumBit(mData, mFrame % mFrameCount);
bool status = !JUTGamePad::CRumble::mStatus[port];
if (enable && !status)
{
JUTGamePad::CRumble::startMotor(port);
}
else if (!enable)
{
bool hardstop = getNumBit(_10, mFrame % mFrameCount);
if (status)
JUTGamePad::CRumble::stopMotor(port, hardstop);
else if (hardstop)
{
JUTGamePad::CRumble::stopMotor(port, true);
}
}
}
mFrame++;
}
}
void JUTGamePad::CButton::setRepeat(u32 repeatMask, u32 repeatDelay, u32 repeatFreq)
{
mRepeatLastButton = 0;
mRepeatTimer = 0;
mRepeatMask = repeatMask;
mRepeatDelay = repeatDelay;
mRepeatFrequency = repeatFreq;
}
void JUTGamePad::setButtonRepeat(u32 repeatMask, u32 repeatDelay, u32 repeatFreq)
{
mButtons.setRepeat(repeatMask, repeatDelay, repeatFreq);
if (mPortNum >= 0)
mPadButton[mPortNum].setRepeat(repeatMask, repeatDelay, repeatFreq);
}
bool JUTGamePad::recalibrate(u32 channels)
{
for (int i = 0; i < PAD_CONTROLLER_NUM; i++)
{
if ((JUTGamePad::sSuppressPadReset & channel_mask[i]) != 0)
{
channels &= channel_mask[i] ^ 0xFFFFFFFF;
}
}
BOOL res = PADRecalibrate(channels);
return res;
}
void JUTGamePadLongPress::checkCallback(int port, u32 timer)
{
if (port >= 0)
{
// assert not present in mkdd debug? interesting
// JUT_ASSERT(0 <= port && port < 4)
if (timer >= _1C)
{
_11 = true;
_48[port] = true;
if (mCallback)
{
mCallback(port, this, _50);
}
}
}
}