#include "egg/core/eggController.h" #include "egg/core/eggAllocator.h" #include "egg/core/eggExpHeap.h" #include "egg/core/eggSystem.h" #include "egg/math/eggMath.h" #include "egg/math/eggVector.h" #include "rvl/WPAD/WPAD.h" #include "string.h" #include "rvl/VI.h" // IWYU pragma: export namespace EGG { NullController null_controller; CoreControllerMgr *CoreControllerMgr::sInstance; CoreControllerMgr::T__Disposer *CoreControllerMgr::T__Disposer::sStaticDisposer; CoreControllerFactory CoreControllerMgr::sCoreControllerFactory; CoreControllerConnectCallback CoreControllerMgr::sConnectCallback; // This controls whether EggController registers an allocator within the WPAD driver bool CoreControllerMgr::sUseBuiltinWpadAllocator; s32 CoreControllerMgr::sWPADWorkSize = 0x32000; void CoreStatus::init() { memset(this, 0, sizeof(CoreStatus)); } u32 CoreStatus::getFSStickButton() { f32 stick = getFSStickX(); u32 result = 0; if (!(-0.25f < stick) || !(stick < 0.25f)) { if (stick <= -0.5f) { result = 0; result |= 0x40000; } else if (stick >= 0.5f) { result = 0; result |= 0x80000; } } stick = getFSStickY(); if (!(-0.25f < stick) || !(stick < 0.25f)) { if (stick <= -0.5f) { result &= ~0x10000; result |= 0x20000; } else if (stick >= 0.5f) { result &= ~0x20000; result |= 0x10000; } } return result; } CoreController::CoreController() : mDpdPosPrev(), mStableAccel(), mFlag(0) { mRumbleMgr = nullptr; mFSStickHold = 0; mFSStickTrig = 0; mFSStickRelease = 0; sceneReset(); mFlag.makeAllZero(); } void CoreController::sceneReset() { mAccelPrev.set(0.0, 0.0, 0.0); mDpdPosPrev.x = 0.0f; mDpdPosPrev.y = 0.0f; mIdleTime = 0; mPostureMatrixPrev.makeIdentity(); mPostureMatrix.makeIdentity(); mStableFrame = 10; mStableMag = 0.13; mStableAccel.set(0.0, 0.0, 0.0); mStableFlags = 0; mStableTimer[2] = 0; mStableTimer[1] = 0; mStableTimer[0] = 0; mSimpleRumblePattern = 0; mSimpleRumbleFrame = 0; mEnableSimpleRumble = false; mSimpleRumbleSize = 0x20; mSimpleRumbleIndex = 0x1f; stopRumbleMgr(); } Vector2f CoreController::getDpdRawPos() { return Vector2f(mCoreStatus[0].pos.x, mCoreStatus[0].pos.y); } f32 CoreController::getDpdDistance() { return mCoreStatus[0].dist; } s32 CoreController::getDpdValidFlag() { return mCoreStatus[0].getDPDValidFlag(); } void CoreController::startMotor() { WPADControlMotor(mChannelID, WPAD_MOTOR_RUMBLE); } void CoreController::stopMotor() { WPADControlMotor(mChannelID, WPAD_MOTOR_STOP); } void CoreController::createRumbleMgr(u8 numUnits) { mRumbleMgr = new ControllerRumbleMgr(); mRumbleMgr->createUnit(numUnits, this); } void CoreController::startPatternRumble(const char *pattern, int duration, bool bGrabActive) { if (mRumbleMgr) { mRumbleMgr->startPattern(pattern, duration, bGrabActive); } } void CoreController::stopRumbleMgr() { if (mRumbleMgr) { mRumbleMgr->stop(); } } CoreStatus *CoreController::getCoreStatus(int idx) { return &mCoreStatus[idx]; } void CoreController::calc_posture_matrix(Matrix34f &posture, bool checkStable) { if (checkStable && !isStable(7)) { return; } Vector3f acc = -getAccel(); Vector3f vy = acc; vy.normalise(); Vector3f vz(0.0f, 0.0f, 1.0f); Vector3f vx(1.0f, 0.0f, 0.0f); Vector3f bx, by, bz; mPostureMatrixPrev.getBase(0, bx); mPostureMatrixPrev.getBase(1, by); mPostureMatrixPrev.getBase(2, bz); Vector3f vz_n(0.0f, 0.0f, -1.0f); Vector3f vx_n(-1.0f, 0.0f, 0.0f); if (vz.dot(bz) < vz_n.dot(bz)) { vz = vz_n; } if (vx.dot(bx) < vx_n.dot(bx)) { vx = vx_n; } if (Math::abs(vy.dot(vz)) < Math::abs(vy.dot(vx))) { vx = vy.cross(vz); vx.normalise(); vz = vx.cross(vy); vz.normalise(); } else { vz = vx.cross(vy); vz.normalise(); vx = vy.cross(vz); vx.normalise(); } posture.setBase(0, vx); posture.setBase(1, vy); posture.setBase(2, vz); } void CoreController::beginFrame(PADStatus *padStatus) { s32 kpad_result; mKPADReadLength = KPADReadEx(mChannelID, mCoreStatus, 0x10, &kpad_result); if (mKPADReadLength == 0 && kpad_result == WPAD_ERR_NO_CONTROLLER) { mKPADReadLength = 1; } WPADDeviceType dev_type; switch (WPADProbe(mChannelID, &dev_type)) { case WPAD_ERR_OK: { if ((u32)dev_type == WPAD_DEV_NOT_FOUND) { mFlag.resetBit(0); } else { mFlag.setBit(0); } } break; case WPAD_ERR_NO_CONTROLLER: { mFlag.resetBit(0); } break; } if (mKPADReadLength > 0) { CoreStatus *pStatus = mCoreStatus; u32 prev_held = mFSStickHold; if (pStatus->isFreestyle()) { mFSStickHold = pStatus->getFSStickButton(); } else { mFSStickHold = 0; } mFSStickTrig = mFSStickHold & ~prev_held; mFSStickRelease = prev_held & ~mFSStickHold; pStatus->hold &= ~0xF0000; pStatus->trig &= ~0xF0000; pStatus->release &= ~0xF0000; pStatus->hold |= (mFSStickHold & 0xF0000); pStatus->trig |= (mFSStickTrig & 0xF0000); pStatus->release |= (mFSStickRelease & 0xF0000); } mPostureMatrixPrev.copyFrom(mPostureMatrix); mStableFlags = 0; Vector3f acc = mCoreStatus->getAccel(); for (int i = 0; i < 3; i++) { if (acc(i) - mAccelPrev(i) < mStableMag) { if (mStableFrame <= ++mStableTimer[i]) { mStableTimer[i] = mStableFrame; mStableFlags |= (1 << i); mStableAccel(i) = acc(i); } } else { mStableFlags &= ~(1 << i); mStableTimer[i] = 0; } } calc_posture_matrix(mPostureMatrix, true); if (mEnableSimpleRumble) { if (mSimpleRumblePattern & (1 << mSimpleRumbleIndex)) { startMotor(); } else { stopMotor(); } if (mSimpleRumbleIndex == 0) { mSimpleRumbleIndex = mSimpleRumbleSize - 1; } else { mSimpleRumbleIndex = mSimpleRumbleIndex - 1; } if (--mSimpleRumbleFrame == 0) { stopMotor(); mEnableSimpleRumble = false; } } if (mRumbleMgr) { mRumbleMgr->calc(); } bool increment = true; if (mCoreStatus->getHold() != 0) { increment = false; } if (increment) { Vector3f diff = (mAccelPrev - mCoreStatus->getAccel()); if (diff.squaredLength() > 0.01f) { increment = false; } } if (increment) { Vector2f diff = (mDpdPosPrev - getDpdRawPos()); if (diff.squaredLength() > 0.05f) { increment = false; } } if (increment) { // I dont know what this is for... // if no interaction, this increments? (30fps -> 2hrs) if (mIdleTime < 216000) { mIdleTime++; } } else { mIdleTime = 0; } } void CoreController::endFrame() { mAccelPrev = *reinterpret_cast(&mCoreStatus[0].acc); mDpdPosPrev = getDpdRawPos(); } /* 0x80499AC0 */ f32 CoreController::getFreeStickX() const { if (mCoreStatus->getDevType() == WPAD_DEV_CORE) { return 0.0f; } return mCoreStatus[0].getFSStickX(); } /* 0x80499AE0 */ f32 CoreController::getFreeStickY() const { if (mCoreStatus->getDevType() == WPAD_DEV_CORE) { return 0.0f; } return mCoreStatus[0].getFSStickY(); } /* 0x80499B00 */ CoreControllerMgr::T__Disposer::~T__Disposer() { if (this == CoreControllerMgr::T__Disposer::sStaticDisposer) { deleteInstance(); } } /* 0x80499B80 */ CoreControllerMgr *CoreControllerMgr::createInstance() { if (CoreControllerMgr::sInstance == nullptr) { CoreControllerMgr *mgr = new CoreControllerMgr(); CoreControllerMgr::sInstance = mgr; CoreControllerMgr::T__Disposer::sStaticDisposer = &mgr->mDisposer; } return CoreControllerMgr::sInstance; } /* 0x80499BD0 */ void CoreControllerMgr::deleteInstance() { CoreControllerMgr::sInstance = nullptr; CoreControllerMgr::T__Disposer::sStaticDisposer = nullptr; } /* 0x80499BE0 */ EGG::CoreController *CoreControllerMgr::getNthController(int n) { return mControllers(n); } namespace { static Allocator *sAllocator; void *alloc(size_t size) { return sAllocator->alloc(size); } int free(void *ptr) { sAllocator->free(ptr); return 1; } } // namespace /* 0x80499CD0 */ void CoreControllerMgr::connectCallback(s32 a1, s32 a2) { CoreControllerConnectArg args = {a1, a2}; if (sConnectCallback != nullptr) { (sConnectCallback)(args); } } /* 0x80499D10 */ CoreControllerMgr::CoreControllerMgr() { const int idxes[] = {0, 1, 2, 3}; if (sUseBuiltinWpadAllocator == false) { Heap *heap = ExpHeap::create(sWPADWorkSize, BaseSystem::mConfigData->mRootHeapMem2, 0); heap->mName = "EGG::CoreControllerMgr"; sAllocator = new Allocator(heap, 0x20); WPADRegisterAllocator(alloc, free); } KPADInitEx(field_0x20, 0x40); beginFrame(); endFrame(); VIWaitForRetrace(); for (int i = 0; i < 4; i++) { KPADSetConnectCallback(idxes[i], connectCallback); } mControllers.allocate(4, 0); for (int i = 0; i < 4; i++) { if (sCoreControllerFactory != nullptr) { mControllers(i) = (sCoreControllerFactory)(); } else { mControllers(i) = new CoreController(); } } mDevTypes.allocate(4, 0); for (int i = 0; i < 4; i++) { mControllers(i)->mChannelID = idxes[i]; mDevTypes(i) = (eCoreDevType)0xfd; } field_0x10A0 = nullptr; } /* 0x8049A130 */ void CoreControllerMgr::beginFrame() { for (int i = 0; i < mControllers.getSize(); ++i) { mControllers(i)->beginFrame(NULL); } } /* 0x8049A1E0 */ void CoreControllerMgr::endFrame() { for (int i = 0; i < mControllers.getSize(); i++) { mControllers(i)->endFrame(); WPADDeviceType dev_type; s32 result = WPADProbe(i, &dev_type); WPADDeviceType res_dev_type; if (result == WPAD_ERR_OK) { res_dev_type = dev_type; } else if (result == WPAD_ERR_NO_CONTROLLER) { res_dev_type = WPAD_DEV_NOT_FOUND; } else { continue; } if (field_0x10A0) { if (res_dev_type != mDevTypes(i)) { s32 sp0C[3]; sp0C[0] = res_dev_type; sp0C[1] = mDevTypes(i); sp0C[2] = i; field_0x10A0->v_08(sp0C); } } mDevTypes(i) = (eCoreDevType)res_dev_type; } } /* 0x8049A3B0 */ void ControllerRumbleUnit::init() { mRumblePattern = nullptr; mRumblePatternPtr = nullptr; mRumbleFrame = 0; mRumbleValue = 0.0f; mRumblePower = 0.0f; mFlags.makeAllZero(); } /* 0x8049A3E0 */ void ControllerRumbleUnit::startPattern(const char *pattern, int duration) { mRumblePattern = pattern; mRumblePatternPtr = pattern; mFlags.value &= 0xef; mFlags.value &= 0xdf; if (duration < 0) { mFlags.set(0x10); } else if (duration > 0) { mFlags.set(0x30); } mRumbleFrame = duration; mFlags.setBit(0); mFlags.resetBit(1); mFlags.setBit(3); } /* 0x8049A440 */ f32 ControllerRumbleUnit::calc() { f32 result = 0.0f; if (mFlags.onBit(3)) { if (mFlags.onBit(0)) { char x = *++mRumblePatternPtr; if (x == '\0') { if (mFlags.onBit(4)) { mRumblePatternPtr = mRumblePattern; } else { mFlags.resetBit(3); } } else if (x == '*') { result = 1.0f; } if (mFlags.offBit(5)) { return result; } if (--mRumbleFrame > 0) { return result; } mFlags.resetBit(3); return result; } else { f32 intensity = mRumbleValue + mRumblePower; mRumbleValue = intensity; if (intensity >= 1.0f) { result = 1.0f; mRumbleValue = 0.0f; } if (mFlags.onBit(2)) { return result; } if (--mRumbleFrame > 0) { return result; } } mFlags.resetBit(3); return result; } else { return -1.0f; } } /* 0x8049A530 */ ControllerRumbleMgr::ControllerRumbleMgr() { mController = nullptr; List_Init(&mActiveUnitList, offsetof(ControllerRumbleUnit, mNode)); List_Init(&mStoppedUnitList, offsetof(ControllerRumbleUnit, mNode)); } /* 0x8049A590 */ void ControllerRumbleMgr::createUnit(u8 numUnits, CoreController *ctrl) { for (u8 created = 0; created < numUnits; created++) { ControllerRumbleUnit *unit = new ControllerRumbleUnit(); List_Append(&mStoppedUnitList, unit); } mController = ctrl; } /* 0x8049A620 */ void ControllerRumbleMgr::stop() { mController->stopMotor(); while (List_GetSize(&mActiveUnitList) != 0) { ControllerRumbleUnit *unit = static_cast(List_GetNext(&mActiveUnitList, nullptr)); List_Remove(&mActiveUnitList, unit); List_Append(&mStoppedUnitList, unit); } } /* 0x8049A690 */ void ControllerRumbleMgr::calc() { if (List_GetSize(&mActiveUnitList) != 0) { void *object = List_GetFirst(&mActiveUnitList); f32 acc = 0.0f; while (object != nullptr) { ControllerRumbleUnit *unit = static_cast(object); f32 x = unit->calc(); void *nextObject = List_GetNext(&mActiveUnitList, object); if (x < 0.0f) { List_Remove(&mActiveUnitList, object); List_Append(&mStoppedUnitList, object); } else { acc += x; } object = nextObject; } if (acc >= 1.0f) { mController->startMotor(); } else { mController->stopMotor(); } } } /* 0x8049A7A0 */ void ControllerRumbleMgr::startPattern(const char *pattern, int duration, bool bGrabActive) { EGG::ControllerRumbleUnit *unit = getUnitFromList(bGrabActive); if (unit) { unit->startPattern(pattern, duration); } } /* 0x8049A7F0 */ ControllerRumbleUnit *ControllerRumbleMgr::getUnitFromList(bool bGrabActive) { void *first = List_GetFirst(&this->mStoppedUnitList); if (first) { List_Remove(&mStoppedUnitList, first); List_Append(&mActiveUnitList, first); } else if (bGrabActive && (first = List_GetFirst(&mActiveUnitList), first != nullptr)) { List_Remove(&mActiveUnitList, first); List_Append(&mActiveUnitList, first); } return static_cast(first); } } // namespace EGG