CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HLE/sceCtrl.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cmath>18#include <mutex>1920#include "Common/Serialize/Serializer.h"21#include "Common/Serialize/SerializeFuncs.h"22#include "Common/Math/math_util.h"23#include "Core/CoreTiming.h"24#include "Core/HLE/HLE.h"25#include "Core/HLE/FunctionWrappers.h"26#include "Core/HLE/sceCtrl.h"27#include "Core/HLE/sceKernel.h"28#include "Core/HLE/sceKernelThread.h"29#include "Core/HLE/sceKernelInterrupt.h"30#include "Core/HW/Display.h"31#include "Core/System.h"32#include "Core/MemMapHelpers.h"33#include "Core/MIPS/MIPS.h"34#include "Core/Replay.h"35#include "Core/Util/AudioFormat.h" // for clamp_u83637/* Index for the two analog directions */38#define CTRL_ANALOG_X 039#define CTRL_ANALOG_Y 140#define CTRL_ANALOG_CENTER 1284142#define CTRL_MODE_DIGITAL 043#define CTRL_MODE_ANALOG 14445const u32 NUM_CTRL_BUFFERS = 64;4647enum {48CTRL_WAIT_POSITIVE = 1,49CTRL_WAIT_NEGATIVE = 2,50};5152struct CtrlData {53u32_le frame;54u32_le buttons;55// The PSP has only one stick, but has space for more info.56// The second stick is populated for HD remasters and possibly in the PSP emulator on PS3/Vita.57u8 analog[2][2];58u8 unused[4];59};6061struct CtrlLatch {62u32_le btnMake;63u32_le btnBreak;64u32_le btnPress;65u32_le btnRelease;66};676869//////////////////////////////////////////////////////////////////////////70// STATE BEGIN71static bool analogEnabled = false;72static int ctrlLatchBufs = 0;73static u32 ctrlOldButtons = 0;7475static CtrlData ctrlBufs[NUM_CTRL_BUFFERS];76static CtrlData ctrlCurrent;77static u32 ctrlBuf = 0;78static u32 ctrlBufRead = 0;79static CtrlLatch latch;80static u32 dialogBtnMake = 0;8182static int ctrlIdleReset = -1;83static int ctrlIdleBack = -1;8485static int ctrlCycle = 0;8687static std::vector<SceUID> waitingThreads;88static std::mutex ctrlMutex;8990static int ctrlTimer = -1;9192static u16 leftVibration = 0;93static u16 rightVibration = 0;94// The higher the dropout, the longer Vibration will run95static u8 vibrationLeftDropout = 160;96static u8 vibrationRightDropout = 160;9798// STATE END99//////////////////////////////////////////////////////////////////////////100101// Not savestated, this is emu state.102// Not related to sceCtrl*RapidFire(), although it may do the same thing.103static bool emuRapidFire = false;104static u32 emuRapidFireFrames = 0;105static bool emuRapidFireToggle = true;106static u32 emuRapidFireInterval = 5;107108// These buttons are not affected by rapid fire (neither is analog.)109const u32 CTRL_EMU_RAPIDFIRE_MASK = CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT;110111static void __CtrlUpdateLatch()112{113std::lock_guard<std::mutex> guard(ctrlMutex);114u64 t = CoreTiming::GetGlobalTimeUs();115116u32 buttons = ctrlCurrent.buttons;117if (emuRapidFire && emuRapidFireToggle)118buttons &= CTRL_EMU_RAPIDFIRE_MASK;119120ReplayApplyCtrl(buttons, ctrlCurrent.analog, t);121122// Copy in the current data to the current buffer.123ctrlBufs[ctrlBuf] = ctrlCurrent;124125if (PSP_CoreParameter().compat.flags().DaxterRotatedAnalogStick) {126// For some reason, Daxter rotates the analog input. See #17015127float angle = (15.0f / 360.0f) * (2.0f * M_PI);128float cosAngle = cosf(angle);129float sinAngle = sinf(angle);130float x = ctrlBufs[ctrlBuf].analog[0][0] - 128;131float y = ctrlBufs[ctrlBuf].analog[0][1] - 128;132float rX = x * cosAngle - y * sinAngle;133float rY = x * sinAngle + y * cosAngle;134ctrlBufs[ctrlBuf].analog[0][0] = (u8)Clamp(rX + 128, 0.0f, 255.0f);135ctrlBufs[ctrlBuf].analog[0][1] = (u8)Clamp(rY + 128, 0.0f, 255.0f);136}137138ctrlBufs[ctrlBuf].buttons = buttons;139140u32 changed = buttons ^ ctrlOldButtons;141latch.btnMake |= buttons & changed;142latch.btnBreak |= ctrlOldButtons & changed;143latch.btnPress |= buttons;144latch.btnRelease |= ~buttons;145dialogBtnMake |= buttons & changed;146ctrlLatchBufs++;147148ctrlOldButtons = buttons;149150ctrlBufs[ctrlBuf].frame = (u32)t;151if (!analogEnabled)152memset(ctrlBufs[ctrlBuf].analog, CTRL_ANALOG_CENTER, sizeof(ctrlBufs[ctrlBuf].analog));153154ctrlBuf = (ctrlBuf + 1) % NUM_CTRL_BUFFERS;155156// If we wrapped around, push the read head forward.157// TODO: Is this right?158if (ctrlBufRead == ctrlBuf)159ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;160}161162static int __CtrlResetLatch()163{164int oldBufs = ctrlLatchBufs;165memset(&latch, 0, sizeof(CtrlLatch));166ctrlLatchBufs = 0;167return oldBufs;168}169170u32 __CtrlPeekButtons()171{172std::lock_guard<std::mutex> guard(ctrlMutex);173174return ctrlCurrent.buttons;175}176177u32 __CtrlPeekButtonsVisual()178{179u32 buttons;180{181std::lock_guard<std::mutex> guard(ctrlMutex);182buttons = ctrlCurrent.buttons;183}184185if (emuRapidFire && emuRapidFireToggle)186buttons &= CTRL_EMU_RAPIDFIRE_MASK;187return buttons;188}189190void __CtrlPeekAnalog(int stick, float *x, float *y)191{192std::lock_guard<std::mutex> guard(ctrlMutex);193194*x = (ctrlCurrent.analog[stick][CTRL_ANALOG_X] - 127.5f) / 127.5f;195*y = -(ctrlCurrent.analog[stick][CTRL_ANALOG_Y] - 127.5f) / 127.5f;196}197198199u32 __CtrlReadLatch()200{201u32 ret = dialogBtnMake;202dialogBtnMake = 0;203return ret;204}205206void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear)207{208bitsToClear &= CTRL_MASK_USER;209bitsToSet &= CTRL_MASK_USER;210211std::lock_guard<std::mutex> guard(ctrlMutex);212// There's no atomic operation for this, so mutex it is.213ctrlCurrent.buttons = (ctrlCurrent.buttons & ~bitsToClear) | bitsToSet;214}215216void __CtrlSetAnalogXY(int stick, float x, float y)217{218u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f));219// TODO: We might have too many negations of Y...220u8 scaledY = clamp_u8((int)ceilf(-y * 127.5f + 127.5f));221222std::lock_guard<std::mutex> guard(ctrlMutex);223ctrlCurrent.analog[stick][CTRL_ANALOG_X] = scaledX;224ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY;225}226227// not making XY to use these due to mutex guard usage228void __CtrlSetAnalogX(int stick, float x) {229u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f));230std::lock_guard<std::mutex> guard(ctrlMutex);231ctrlCurrent.analog[stick][CTRL_ANALOG_X] = scaledX;232}233234void __CtrlSetAnalogY(int stick, float y) {235u8 scaledY = clamp_u8((int)ceilf(-y * 127.5f + 127.5f));236std::lock_guard<std::mutex> guard(ctrlMutex);237ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY;238}239240void __CtrlSetRapidFire(bool state, int interval)241{242emuRapidFire = state;243emuRapidFireToggle = true;244emuRapidFireInterval = interval;245}246247bool __CtrlGetRapidFire()248{249return emuRapidFire;250}251252static int __CtrlReadSingleBuffer(PSPPointer<CtrlData> data, bool negative)253{254if (data.IsValid())255{256*data = ctrlBufs[ctrlBufRead];257ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;258259// Mask out buttons games aren't allowed to see.260data->buttons &= CTRL_MASK_USER;261if (negative)262data->buttons = ~data->buttons;263264return 1;265}266267return 0;268}269270static int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek)271{272if (nBufs > NUM_CTRL_BUFFERS)273return SCE_KERNEL_ERROR_INVALID_SIZE;274275if (!peek && !__KernelIsDispatchEnabled())276return SCE_KERNEL_ERROR_CAN_NOT_WAIT;277if (!peek && __IsInInterrupt())278return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;279280u32 resetRead = ctrlBufRead;281282u32 availBufs;283// Peeks always work, they just go go from now X buffers.284if (peek)285availBufs = nBufs;286else287{288availBufs = (ctrlBuf - ctrlBufRead + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;289if (availBufs > nBufs)290availBufs = nBufs;291}292ctrlBufRead = (ctrlBuf - availBufs + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;293294int done = 0;295auto data = PSPPointer<CtrlData>::Create(ctrlDataPtr);296for (u32 i = 0; i < availBufs; ++i)297done += __CtrlReadSingleBuffer(data++, negative);298299if (peek)300ctrlBufRead = resetRead;301302return done;303}304305static void __CtrlDoSample()306{307// This samples the ctrl data into the buffers and updates the latch.308__CtrlUpdateLatch();309310// Wake up a single thread that was waiting for the buffer.311retry:312if (!waitingThreads.empty() && ctrlBuf != ctrlBufRead)313{314SceUID threadID = waitingThreads[0];315waitingThreads.erase(waitingThreads.begin());316317u32 error;318SceUID wVal = __KernelGetWaitID(threadID, WAITTYPE_CTRL, error);319// Make sure it didn't get woken or something.320if (wVal == 0)321goto retry;322323PSPPointer<CtrlData> ctrlDataPtr;324ctrlDataPtr = __KernelGetWaitValue(threadID, error);325int retVal = __CtrlReadSingleBuffer(ctrlDataPtr, wVal == CTRL_WAIT_NEGATIVE);326__KernelResumeThreadFromWait(threadID, retVal);327__KernelReSchedule("ctrl buffers updated");328}329}330331static void __CtrlVblank()332{333emuRapidFireFrames++;334if (emuRapidFireFrames >= emuRapidFireInterval) {335emuRapidFireFrames = 0;336emuRapidFireToggle = !emuRapidFireToggle;337}338339// Reduce gamepad Vibration by set % each frame340leftVibration *= (float)vibrationLeftDropout / 256.0f;341rightVibration *= (float)vibrationRightDropout / 256.0f;342343// This always runs, so make sure we're in vblank mode.344if (ctrlCycle == 0)345__CtrlDoSample();346}347348static void __CtrlTimerUpdate(u64 userdata, int cyclesLate)349{350// This only runs in timer mode (ctrlCycle > 0.)351_dbg_assert_msg_(ctrlCycle > 0, "Ctrl: sampling cycle should be > 0");352353CoreTiming::ScheduleEvent(usToCycles(ctrlCycle) - cyclesLate, ctrlTimer, 0);354355__CtrlDoSample();356}357358void __CtrlInit()359{360ctrlTimer = CoreTiming::RegisterEvent("CtrlSampleTimer", __CtrlTimerUpdate);361__DisplayListenVblank(__CtrlVblank);362363ctrlIdleReset = -1;364ctrlIdleBack = -1;365ctrlCycle = 0;366367std::lock_guard<std::mutex> guard(ctrlMutex);368369ctrlBuf = 1;370ctrlBufRead = 0;371ctrlOldButtons = 0;372ctrlLatchBufs = 0;373dialogBtnMake = 0;374375memset(&latch, 0, sizeof(latch));376// Start with everything released.377latch.btnRelease = 0xffffffff;378379memset(&ctrlCurrent, 0, sizeof(ctrlCurrent));380memset(ctrlCurrent.analog, CTRL_ANALOG_CENTER, sizeof(ctrlCurrent.analog));381analogEnabled = false;382383for (u32 i = 0; i < NUM_CTRL_BUFFERS; i++)384memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(CtrlData));385}386387void __CtrlDoState(PointerWrap &p)388{389std::lock_guard<std::mutex> guard(ctrlMutex);390391auto s = p.Section("sceCtrl", 1, 3);392if (!s)393return;394395Do(p, analogEnabled);396Do(p, ctrlLatchBufs);397Do(p, ctrlOldButtons);398399p.DoVoid(ctrlBufs, sizeof(ctrlBufs));400if (s <= 2) {401CtrlData dummy = {0};402Do(p, dummy);403}404Do(p, ctrlBuf);405Do(p, ctrlBufRead);406Do(p, latch);407if (s == 1) {408dialogBtnMake = 0;409} else {410Do(p, dialogBtnMake);411}412413Do(p, ctrlIdleReset);414Do(p, ctrlIdleBack);415416Do(p, ctrlCycle);417418SceUID dv = 0;419Do(p, waitingThreads, dv);420421Do(p, ctrlTimer);422CoreTiming::RestoreRegisterEvent(ctrlTimer, "CtrlSampleTimer", __CtrlTimerUpdate);423}424425void __CtrlShutdown()426{427waitingThreads.clear();428}429430static u32 sceCtrlSetSamplingCycle(u32 cycle)431{432DEBUG_LOG(Log::sceCtrl, "sceCtrlSetSamplingCycle(%u)", cycle);433434if ((cycle > 0 && cycle < 5555) || cycle > 20000)435{436WARN_LOG(Log::sceCtrl, "SCE_KERNEL_ERROR_INVALID_VALUE=sceCtrlSetSamplingCycle(%u)", cycle);437return SCE_KERNEL_ERROR_INVALID_VALUE;438}439440u32 prev = ctrlCycle;441ctrlCycle = cycle;442443if (prev > 0)444CoreTiming::UnscheduleEvent(ctrlTimer, 0);445if (cycle > 0)446CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0);447448return prev;449}450451static int sceCtrlGetSamplingCycle(u32 cyclePtr)452{453DEBUG_LOG(Log::sceCtrl, "sceCtrlGetSamplingCycle(%08x)", cyclePtr);454if (Memory::IsValidAddress(cyclePtr))455Memory::Write_U32(ctrlCycle, cyclePtr);456return 0;457}458459static u32 sceCtrlSetSamplingMode(u32 mode)460{461u32 retVal = 0;462463DEBUG_LOG(Log::sceCtrl, "sceCtrlSetSamplingMode(%i)", mode);464if (mode > 1)465return SCE_KERNEL_ERROR_INVALID_MODE;466467retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;468analogEnabled = mode == CTRL_MODE_ANALOG ? true : false;469return retVal;470}471472static int sceCtrlGetSamplingMode(u32 modePtr)473{474u32 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;475DEBUG_LOG(Log::sceCtrl, "%d=sceCtrlGetSamplingMode(%08x)", retVal, modePtr);476477if (Memory::IsValidAddress(modePtr))478Memory::Write_U32(retVal, modePtr);479480return 0;481}482483static int sceCtrlSetIdleCancelThreshold(int idleReset, int idleBack)484{485DEBUG_LOG(Log::sceCtrl, "FAKE sceCtrlSetIdleCancelThreshold(%d, %d)", idleReset, idleBack);486487if (idleReset < -1 || idleBack < -1 || idleReset > 128 || idleBack > 128)488return SCE_KERNEL_ERROR_INVALID_VALUE;489490ctrlIdleReset = idleReset;491ctrlIdleBack = idleBack;492return 0;493}494495static int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr)496{497DEBUG_LOG(Log::sceCtrl, "sceCtrlSetIdleCancelThreshold(%08x, %08x)", idleResetPtr, idleBackPtr);498499if (idleResetPtr && !Memory::IsValidAddress(idleResetPtr))500return SCE_KERNEL_ERROR_PRIV_REQUIRED;501if (idleBackPtr && !Memory::IsValidAddress(idleBackPtr))502return SCE_KERNEL_ERROR_PRIV_REQUIRED;503504if (idleResetPtr)505Memory::Write_U32(ctrlIdleReset, idleResetPtr);506if (idleBackPtr)507Memory::Write_U32(ctrlIdleBack, idleBackPtr);508509return 0;510}511512static int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs)513{514int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, false);515hleEatCycles(330);516if (done != 0)517{518DEBUG_LOG(Log::sceCtrl, "%d=sceCtrlReadBufferPositive(%08x, %i)", done, ctrlDataPtr, nBufs);519}520else521{522waitingThreads.push_back(__KernelGetCurThread());523__KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_POSITIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");524DEBUG_LOG(Log::sceCtrl, "sceCtrlReadBufferPositive(%08x, %i) - waiting", ctrlDataPtr, nBufs);525}526return done;527}528529static int sceCtrlReadBufferNegative(u32 ctrlDataPtr, u32 nBufs)530{531int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, false);532hleEatCycles(330);533if (done != 0)534{535DEBUG_LOG(Log::sceCtrl, "%d=sceCtrlReadBufferNegative(%08x, %i)", done, ctrlDataPtr, nBufs);536}537else538{539waitingThreads.push_back(__KernelGetCurThread());540__KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_NEGATIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");541DEBUG_LOG(Log::sceCtrl, "sceCtrlReadBufferNegative(%08x, %i) - waiting", ctrlDataPtr, nBufs);542}543return done;544}545546static int sceCtrlPeekBufferPositive(u32 ctrlDataPtr, u32 nBufs)547{548int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, true);549// Some homebrew call this in a tight loop - so VERBOSE it is.550VERBOSE_LOG(Log::sceCtrl, "%d=sceCtrlPeekBufferPositive(%08x, %i)", done, ctrlDataPtr, nBufs);551hleEatCycles(330);552return done;553}554555static int sceCtrlPeekBufferNegative(u32 ctrlDataPtr, u32 nBufs)556{557int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, true);558// Some homebrew call this in a tight loop - so VERBOSE it is.559VERBOSE_LOG(Log::sceCtrl, "%d=sceCtrlPeekBufferNegative(%08x, %i)", done, ctrlDataPtr, nBufs);560hleEatCycles(330);561return done;562}563564static void __CtrlWriteUserLatch(CtrlLatch *userLatch, int bufs) {565*userLatch = latch;566userLatch->btnBreak &= CTRL_MASK_USER;567userLatch->btnMake &= CTRL_MASK_USER;568userLatch->btnPress &= CTRL_MASK_USER;569if (bufs > 0) {570userLatch->btnRelease |= ~CTRL_MASK_USER;571}572}573574static u32 sceCtrlPeekLatch(u32 latchDataPtr) {575auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);576if (userLatch.IsValid()) {577__CtrlWriteUserLatch(userLatch, ctrlLatchBufs);578}579return hleLogSuccessI(Log::sceCtrl, ctrlLatchBufs);580}581582static u32 sceCtrlReadLatch(u32 latchDataPtr) {583auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);584if (userLatch.IsValid()) {585__CtrlWriteUserLatch(userLatch, ctrlLatchBufs);586}587return hleLogSuccessI(Log::sceCtrl, __CtrlResetLatch());588}589590static const HLEFunction sceCtrl[] =591{592{0X3E65A0EA, nullptr, "sceCtrlInit", '?', "" }, //(int unknown), init with 0593{0X1F4011E6, &WrapU_U<sceCtrlSetSamplingMode>, "sceCtrlSetSamplingMode", 'x', "x" },594{0X6A2774F3, &WrapU_U<sceCtrlSetSamplingCycle>, "sceCtrlSetSamplingCycle", 'x', "x" },595{0X02BAAD91, &WrapI_U<sceCtrlGetSamplingCycle>, "sceCtrlGetSamplingCycle", 'i', "x" },596{0XDA6B76A1, &WrapI_U<sceCtrlGetSamplingMode>, "sceCtrlGetSamplingMode", 'i', "x" },597{0X1F803938, &WrapI_UU<sceCtrlReadBufferPositive>, "sceCtrlReadBufferPositive", 'i', "xx"},598{0X3A622550, &WrapI_UU<sceCtrlPeekBufferPositive>, "sceCtrlPeekBufferPositive", 'i', "xx"},599{0XC152080A, &WrapI_UU<sceCtrlPeekBufferNegative>, "sceCtrlPeekBufferNegative", 'i', "xx"},600{0X60B81F86, &WrapI_UU<sceCtrlReadBufferNegative>, "sceCtrlReadBufferNegative", 'i', "xx"},601{0XB1D0E5CD, &WrapU_U<sceCtrlPeekLatch>, "sceCtrlPeekLatch", 'i', "x" },602{0X0B588501, &WrapU_U<sceCtrlReadLatch>, "sceCtrlReadLatch", 'i', "x" },603{0X348D99D4, nullptr, "sceCtrlSetSuspendingExtraSamples", '?', "" },604{0XAF5960F3, nullptr, "sceCtrlGetSuspendingExtraSamples", '?', "" },605{0XA68FD260, nullptr, "sceCtrlClearRapidFire", '?', "" },606{0X6841BE1A, nullptr, "sceCtrlSetRapidFire", '?', "" },607{0XA7144800, &WrapI_II<sceCtrlSetIdleCancelThreshold>, "sceCtrlSetIdleCancelThreshold", 'i', "ii"},608{0X687660FA, &WrapI_UU<sceCtrlGetIdleCancelThreshold>, "sceCtrlGetIdleCancelThreshold", 'i', "xx"},609};610611void Register_sceCtrl()612{613RegisterModule("sceCtrl", ARRAY_SIZE(sceCtrl), sceCtrl);614}615616void Register_sceCtrl_driver()617{618RegisterModule("sceCtrl_driver", ARRAY_SIZE(sceCtrl), sceCtrl);619}620621u16 sceCtrlGetRightVibration() {622return rightVibration;623}624625u16 sceCtrlGetLeftVibration() {626return leftVibration;627}628629namespace SceCtrl {630void SetRightVibration(u16 rVibration) {631rightVibration = rVibration;632}633void SetLeftVibration(u16 lVibration) {634leftVibration = lVibration;635}636void SetVibrationRightDropout(u8 vibrationRDropout) {637vibrationRightDropout = vibrationRDropout;638}639void SetVibrationLeftDropout(u8 vibrationLDropout) {640vibrationLeftDropout = vibrationLDropout;641}642}643644645