#ifndef EGG_CONTROLLER_H #define EGG_CONTROLLER_H #include "common.h" #include "egg/core/eggSingleton.h" #include "egg/math/eggMatrix.h" #include "egg/math/eggVector.h" #include "egg/prim/eggBitFlag.h" #include "egg/prim/eggBuffer.h" #include "rvl/KPAD/KPAD.h" #include "rvl/KPAD.h" // IWYU pragma: exports #include "rvl/PAD.h" // IWYU pragma: exports #include "rvl/WPAD.h" // IWYU pragma: exports namespace EGG { class ControllerRumbleMgr; class CoreController; enum eCoreDevType { cDEV_CORE = WPAD_DEV_CORE, cDEV_FREESTYLE = WPAD_DEV_FREESTYLE, cDEV_CLASSIC = WPAD_DEV_CLASSIC, cDEV_MPLS = WPAD_DEV_MOTION_PLUS, cDEV_MPLS_PT_FS = WPAD_DEV_MPLS_PT_FS, cDEV_FUTURE = WPAD_DEV_FUTURE, cDEV_NOT_SUPPORTED = WPAD_DEV_NOT_SUPPORTED, cDEV_NOT_FOUND = WPAD_DEV_NOT_FOUND, cDEV_NULL = WPAD_DEV_NULL, cDEV_UNKNOWN = WPAD_DEV_UNKNOWN, }; enum eCoreButton { cCORE_BUTTON_UP = 1 << 3, cCORE_BUTTON_DOWN = 1 << 2, cCORE_BUTTON_LEFT = 1 << 0, cCORE_BUTTON_RIGHT = 1 << 1, cCORE_BUTTON_PLUS = 1 << 4, cCORE_BUTTON_MINUS = 1 << 12, cCORE_BUTTON_1 = 1 << 9, cCORE_BUTTON_2 = 1 << 8, cCORE_BUTTON_A = 1 << 11, cCORE_BUTTON_B = 1 << 10, cCORE_BUTTON_HOME = 1 << 15, cCORE_FSSTICK_UP = 1 << 16, cCORE_FSSTICK_DOWN = 1 << 17, cCORE_FSSTICK_LEFT = 1 << 18, cCORE_FSSTICK_RIGHT = 1 << 19, cCORE_FSSTICK_BUTTONS = cCORE_FSSTICK_UP | cCORE_FSSTICK_DOWN | cCORE_FSSTICK_LEFT | cCORE_FSSTICK_RIGHT }; typedef CoreController *(*CoreControllerFactory)(); struct CoreControllerConnectArg { s32 chan; // at 0x0 s32 result; // at 0x4 }; typedef void (*CoreControllerConnectCallback)(const CoreControllerConnectArg &rArg); struct CoreControllerExtensionArg { eCoreDevType newDevType; // at 0x0 eCoreDevType oldDevType; // at 0x4 s32 chan; // at 0x8 }; class CoreControllerExtensionCallback { public: virtual void onConnect(const CoreControllerExtensionArg &rArg); // at 0x8 }; /****************************************************************************** * * CoreStatus * ******************************************************************************/ class CoreStatus : public KPADStatus { friend class CoreController; public: CoreStatus() {} public: void init(); u32 getHold() const { return hold; } u32 getRelease() const { return release; } u32 getTrigger() const { return trig; } bool down(u32 buttons) const { return buttons & hold; } bool up(u32 buttons) const { return (buttons & hold) != buttons; } bool downAll(u32 buttons) const { return (buttons & hold) == buttons; } bool upAll(u32 buttons) const { return (buttons & hold) == 0; } bool downTrigger(u32 buttons) const { return buttons & trig; } bool upTrigger(u32 buttons) const { return buttons & release; } const Vector3f &getAccel() const { return *reinterpret_cast(&acc); } Vector3f getAccelBad() const { return Vector3f(acc.x, acc.y, acc.z); } Vector2f getAccelVertical() { return Vector2f(acc_vertical.x, acc_vertical.y); } eCoreDevType getDevType() const { return static_cast(dev_type); } bool isCore() const { return getDevType() == cDEV_CORE || isFreestyle(); } bool isFreestyle() const { return getDevType() == cDEV_FREESTYLE; } s8 getDPDValidFlag() const { return dpd_valid_fg; } f32 getFSStickX() const { return ex_status.fs.stick.x; } f32 getFSStickY() const { return ex_status.fs.stick.y; } u32 getFSStickButton(); }; /****************************************************************************** * * CoreController * ******************************************************************************/ class CoreController { friend class CoreControllerMgr; public: CoreController(); // vtable 0x0000 /* vt 0x08 */ virtual void setPosParam(f32 f1, f32 f2) { KPADSetPosParam(mChannelID, f1, f2); } /* vt 0x0C */ virtual void setHoriParam(f32 f1, f32 f2) { KPADSetHoriParam(mChannelID, f1, f2); } /* vt 0x10 */ virtual void setDistParam(f32 f1, f32 f2) { KPADSetDistParam(mChannelID, f1, f2); } /* vt 0x14 */ virtual void setAccParam(f32 f1, f32 f2) { KPADSetAccParam(mChannelID, f1, f2); } /* vt 0x18 */ virtual bool down(u32 buttons) const { return mCoreStatus[0].down(buttons); } /* vt 0x1C */ virtual bool up(u32 buttons) const { return mCoreStatus[0].up(buttons); } /* vt 0x20 */ virtual bool downTrigger(u32 buttons) const { return mCoreStatus[0].downTrigger(buttons); } /* vt 0x24 */ virtual bool upTrigger(u32 buttons) const { return mCoreStatus[0].upTrigger(buttons); } /* vt 0x28 */ virtual bool downAll(u32 buttons) const { return mCoreStatus[0].downAll(buttons); } /* vt 0x2C */ virtual bool upAll(u32 buttons) const { return mCoreStatus[0].upAll(buttons); } /* vt 0x30 */ virtual void beginFrame(PADStatus *padStatus); /* vt 0x34 */ virtual void endFrame(); Vector2f getDpdRawPos(); f32 getDpdDistance(); s32 getDpdValidFlag(); f32 getFreeStickX() const; f32 getFreeStickY() const; s32 getChannelID() const { return mChannelID; } const Vector3f &getAccel() const { return mCoreStatus[0].getAccel(); } Vector3f getAccelBad() const { return mCoreStatus[0].getAccelBad(); } eCoreDevType getDevType() const { return mCoreStatus[0].getDevType(); } bool isCore() const { return mCoreStatus[0].isCore(); } bool isFreestyle() const { return mCoreStatus[0].isFreestyle(); } u32 getHold() const { return mCoreStatus[0].hold; } s32 getReadLength() const { return mKPADReadLength; } bool isStable(u8 bits) const { return (mStableFlags & bits) == bits; } void setStableFrame(int frame) { mStableFrame = frame; } void setStableMag(f32 mag) { mStableMag = mag; } void sceneReset(); void startMotor(); void stopMotor(); void createRumbleMgr(u8 overlap_num); void startPatternRumble(const char *pPattern, int frame, bool force); void startPowerFrameRumble(f32 power, int frame, bool force); void stopRumbleMgr(); bool isConnected() const { return mFlag.onBit(0); } CoreStatus *getCoreStatus(int idx); CoreStatus *getCoreStatus() { return mCoreStatus; } private: enum StableAxis { STABLE_AXIS_X, STABLE_AXIS_Y, STABLE_AXIS_Z, STABLE_AXIS_MAX }; enum StableFlag { STABLE_FLAG_X = 1 << STABLE_AXIS_X, STABLE_FLAG_Y = 1 << STABLE_AXIS_Y, STABLE_FLAG_Z = 1 << STABLE_AXIS_Z, STABLE_FLAG_XYZ = STABLE_FLAG_X | STABLE_FLAG_Y | STABLE_FLAG_Z }; private: void calc_posture_matrix(Matrix34f &rPostureMtx, bool checkStable); private: /* 0x004 */ s32 mChannelID; /* 0x008 */ u32 mFSStickHold; /* 0x00C */ u32 mFSStickTrig; /* 0x010 */ u32 mFSStickRelease; /* 0x014 */ u32 field_0x14; /* 0x018 */ CoreStatus mCoreStatus[16]; /* 0xf18 */ int mKPADReadLength; /* 0xf1c */ TBitFlag mFlag; /* 0xf20 */ Vector3f mAccelPrev; /* 0xf2c */ Vector2f mDpdPosPrev; /* 0xf34 */ u32 mIdleTime; /* 0xf38 */ u32 mSimpleRumblePattern; /* 0xf3c */ bool mEnableSimpleRumble; /* 0xf40 */ s32 mSimpleRumbleFrame; /* 0xf44 */ u8 mSimpleRumbleSize; /* 0xf45 */ u8 mSimpleRumbleIndex; /* 0xf48 */ ControllerRumbleMgr *mRumbleMgr; /* 0xf4c */ Matrix34f mPostureMatrix; /* 0xf7c */ Matrix34f mPostureMatrixPrev; /* 0xfac */ u8 mStableFlags; // May not be TBitFlag? /* 0xfb0 */ s32 mStableFrame; /* 0xfb4 */ s32 mStableTimer[3]; /* 0xfc0 */ f32 mStableMag; /* 0xfc4 */ Vector3f mStableAccel; }; /****************************************************************************** * * CoreControllerMgr * ******************************************************************************/ class CoreControllerMgr { EGG_SINGLETON_DECL(CoreControllerMgr); struct UnkField0x10A0 { virtual void v_08(UNKTYPE *); }; public: // 0x0010 vtable /* vt 0x08 */ virtual void beginFrame(); /* vt 0x0C */ virtual void endFrame(); CoreController *getNthController(int index); eCoreDevType getDevType(const int i) const { return mDevTypes(i); } static void setConnectCallback(CoreControllerConnectCallback cb) { sConnectCallback = cb; } static void setWPADWorkSize(u32 size) { sWPADWorkSize = size; } private: CoreControllerMgr(); static void *allocThunk(size_t size); static int deleteThunk(void *ptr); static void connectCallback(s32, s32); private: /* 0x0014 */ TBuffer mControllers; /* 0x0020 */ u8 field_0x20[0x10a0 - 0x0020]; /* 0x10A0 */ UnkField0x10A0 *field_0x10A0; /* 0x10A4 */ TBuffer mDevTypes; /* 0x10b0 */ u8 field_0x10B0[0x10e0 - 0x10b0]; static CoreControllerFactory sCoreControllerFactory; static CoreControllerConnectCallback sConnectCallback; static bool sWPADRegisterAllocatorOFF; static s32 sWPADWorkSize; }; /****************************************************************************** * * NullController * ******************************************************************************/ class NullController : public CoreController { public: NullController() { unk[92] = 0xfe; } private: // idk this has NO effect on anything u8 unk[0x80674c00 - 0x80674b10]; }; /****************************************************************************** * * ControllerRumbleUnit * ******************************************************************************/ class ControllerRumbleUnit { friend class ControllerRumbleMgr; public: // 0x00 vtable ControllerRumbleUnit() : mFlags(0) { init(); } /* vt 0x08 */ virtual ~ControllerRumbleUnit() {} void startPattern(const char *pPattern, int frame); f32 calc(); private: enum { BIT_USE_PATTERN, BIT_USE_POWER, BIT_POWER_LOOP, BIT_ENABLED, BIT_PATTERN_LOOP, BIT_PATTERN_TIMER, }; private: void init(); void setFlags(u8 bit) { mFlags.setBit(bit); } void clrFlags(u8 bit) { mFlags.resetBit(bit); } bool tstFlags(u8 bit) { return mFlags.onBit(bit); } private: /* 0x04 */ const char *mRumblePattern; /* 0x08 */ const char *mRumblePatternPtr; /* 0x0C */ s32 mRumbleFrame; /* 0x10 */ f32 mRumblePower; /* 0x14 */ f32 mRumbleValue; /* 0x18 */ TBitFlag mFlags; /* 0x1C */ nw4r::ut::Node mNode; /* 0x24 */ u32 field_0x24; }; /****************************************************************************** * * ControllerRumbleMgr * ******************************************************************************/ class ControllerRumbleMgr { public: ControllerRumbleMgr(); virtual ~ControllerRumbleMgr() {} // at 0x8 void createUnit(u8 overlap_num, CoreController *pController); void stop(); void calc(); void startPattern(const char *pattern, int, bool); private: ControllerRumbleUnit *getUnitFromList(bool bGrabActive); private: /* 0x04 */ nw4r::ut::List mActiveUnitList; /* 0x10 */ nw4r::ut::List mStoppedUnitList; /* 0x1C */ CoreController *mController; }; } // namespace EGG #endif