1 // Copyright (c) 2012- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include <cmath>
19 #include <mutex>
20
21 #include "Common/Serialize/Serializer.h"
22 #include "Common/Serialize/SerializeFuncs.h"
23 #include "Core/HLE/HLE.h"
24 #include "Core/HLE/FunctionWrappers.h"
25 #include "Core/MIPS/MIPS.h"
26 #include "Core/CoreTiming.h"
27 #include "Core/MemMapHelpers.h"
28 #include "Core/Replay.h"
29 #include "Core/Util/AudioFormat.h" // for clamp_u8
30 #include "Core/HLE/sceCtrl.h"
31 #include "Core/HLE/sceDisplay.h"
32 #include "Core/HLE/sceKernel.h"
33 #include "Core/HLE/sceKernelThread.h"
34 #include "Core/HLE/sceKernelInterrupt.h"
35
36 /* Index for the two analog directions */
37 #define CTRL_ANALOG_X 0
38 #define CTRL_ANALOG_Y 1
39 #define CTRL_ANALOG_CENTER 128
40
41 #define CTRL_MODE_DIGITAL 0
42 #define CTRL_MODE_ANALOG 1
43
44 const u32 NUM_CTRL_BUFFERS = 64;
45
46 enum {
47 CTRL_WAIT_POSITIVE = 1,
48 CTRL_WAIT_NEGATIVE = 2,
49 };
50
51 struct CtrlData {
52 u32_le frame;
53 u32_le buttons;
54 // The PSP has only one stick, but has space for more info.
55 // The second stick is populated for HD remasters and possibly in the PSP emulator on PS3/Vita.
56 u8 analog[2][2];
57 u8 unused[4];
58 };
59
60 struct CtrlLatch {
61 u32_le btnMake;
62 u32_le btnBreak;
63 u32_le btnPress;
64 u32_le btnRelease;
65 };
66
67
68 //////////////////////////////////////////////////////////////////////////
69 // STATE BEGIN
70 static bool analogEnabled = false;
71 static int ctrlLatchBufs = 0;
72 static u32 ctrlOldButtons = 0;
73
74 static CtrlData ctrlBufs[NUM_CTRL_BUFFERS];
75 static CtrlData ctrlCurrent;
76 static u32 ctrlBuf = 0;
77 static u32 ctrlBufRead = 0;
78 static CtrlLatch latch;
79 static u32 dialogBtnMake = 0;
80
81 static int ctrlIdleReset = -1;
82 static int ctrlIdleBack = -1;
83
84 static int ctrlCycle = 0;
85
86 static std::vector<SceUID> waitingThreads;
87 static std::mutex ctrlMutex;
88
89 static int ctrlTimer = -1;
90
91 static u16 leftVibration = 0;
92 static u16 rightVibration = 0;
93 // The higher the dropout, the longer Vibration will run
94 static u8 vibrationLeftDropout = 160;
95 static u8 vibrationRightDropout = 160;
96
97 // STATE END
98 //////////////////////////////////////////////////////////////////////////
99
100 // Not savestated, this is emu state.
101 // Not related to sceCtrl*RapidFire(), although it may do the same thing.
102 static bool emuRapidFire = false;
103 static u32 emuRapidFireFrames = 0;
104
105 // These buttons are not affected by rapid fire (neither is analog.)
106 const u32 CTRL_EMU_RAPIDFIRE_MASK = CTRL_UP | CTRL_DOWN | CTRL_LEFT | CTRL_RIGHT;
107
__CtrlUpdateLatch()108 static void __CtrlUpdateLatch()
109 {
110 std::lock_guard<std::mutex> guard(ctrlMutex);
111 u64 t = CoreTiming::GetGlobalTimeUs();
112
113 u32 buttons = ctrlCurrent.buttons;
114 if (emuRapidFire && (emuRapidFireFrames % 10) < 5)
115 buttons &= CTRL_EMU_RAPIDFIRE_MASK;
116
117 ReplayApplyCtrl(buttons, ctrlCurrent.analog, t);
118
119 // Copy in the current data to the current buffer.
120 ctrlBufs[ctrlBuf] = ctrlCurrent;
121 ctrlBufs[ctrlBuf].buttons = buttons;
122
123 u32 changed = buttons ^ ctrlOldButtons;
124 latch.btnMake |= buttons & changed;
125 latch.btnBreak |= ctrlOldButtons & changed;
126 latch.btnPress |= buttons;
127 latch.btnRelease |= ~buttons;
128 dialogBtnMake |= buttons & changed;
129 ctrlLatchBufs++;
130
131 ctrlOldButtons = buttons;
132
133 ctrlBufs[ctrlBuf].frame = (u32)t;
134 if (!analogEnabled)
135 memset(ctrlBufs[ctrlBuf].analog, CTRL_ANALOG_CENTER, sizeof(ctrlBufs[ctrlBuf].analog));
136
137 ctrlBuf = (ctrlBuf + 1) % NUM_CTRL_BUFFERS;
138
139 // If we wrapped around, push the read head forward.
140 // TODO: Is this right?
141 if (ctrlBufRead == ctrlBuf)
142 ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;
143 }
144
__CtrlResetLatch()145 static int __CtrlResetLatch()
146 {
147 int oldBufs = ctrlLatchBufs;
148 memset(&latch, 0, sizeof(CtrlLatch));
149 ctrlLatchBufs = 0;
150 return oldBufs;
151 }
152
__CtrlPeekButtons()153 u32 __CtrlPeekButtons()
154 {
155 std::lock_guard<std::mutex> guard(ctrlMutex);
156
157 return ctrlCurrent.buttons;
158 }
159
__CtrlPeekAnalog(int stick,float * x,float * y)160 void __CtrlPeekAnalog(int stick, float *x, float *y)
161 {
162 std::lock_guard<std::mutex> guard(ctrlMutex);
163
164 *x = (ctrlCurrent.analog[stick][CTRL_ANALOG_X] - 127.5f) / 127.5f;
165 *y = -(ctrlCurrent.analog[stick][CTRL_ANALOG_Y] - 127.5f) / 127.5f;
166 }
167
168
__CtrlReadLatch()169 u32 __CtrlReadLatch()
170 {
171 u32 ret = dialogBtnMake;
172 dialogBtnMake = 0;
173 return ret;
174 }
175
176 // Functions so that the rest of the emulator can control what the sceCtrl interface should return
177 // to the game:
178
__CtrlButtonDown(u32 buttonBit)179 void __CtrlButtonDown(u32 buttonBit)
180 {
181 std::lock_guard<std::mutex> guard(ctrlMutex);
182 ctrlCurrent.buttons |= buttonBit;
183 }
184
__CtrlButtonUp(u32 buttonBit)185 void __CtrlButtonUp(u32 buttonBit)
186 {
187 std::lock_guard<std::mutex> guard(ctrlMutex);
188 ctrlCurrent.buttons &= ~buttonBit;
189 }
190
__CtrlSetAnalogXY(int stick,float x,float y)191 void __CtrlSetAnalogXY(int stick, float x, float y)
192 {
193 u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f));
194 // TODO: We might have too many negations of Y...
195 u8 scaledY = clamp_u8((int)ceilf(-y * 127.5f + 127.5f));
196 std::lock_guard<std::mutex> guard(ctrlMutex);
197 ctrlCurrent.analog[stick][CTRL_ANALOG_X] = scaledX;
198 ctrlCurrent.analog[stick][CTRL_ANALOG_Y] = scaledY;
199 }
200
__CtrlSetRapidFire(bool state)201 void __CtrlSetRapidFire(bool state)
202 {
203 emuRapidFire = state;
204 }
205
__CtrlGetRapidFire()206 bool __CtrlGetRapidFire()
207 {
208 return emuRapidFire;
209 }
210
__CtrlReadSingleBuffer(PSPPointer<CtrlData> data,bool negative)211 static int __CtrlReadSingleBuffer(PSPPointer<CtrlData> data, bool negative)
212 {
213 if (data.IsValid())
214 {
215 *data = ctrlBufs[ctrlBufRead];
216 ctrlBufRead = (ctrlBufRead + 1) % NUM_CTRL_BUFFERS;
217
218 // Mask out buttons games aren't allowed to see.
219 data->buttons &= CTRL_MASK_USER;
220 if (negative)
221 data->buttons = ~data->buttons;
222
223 return 1;
224 }
225
226 return 0;
227 }
228
__CtrlReadBuffer(u32 ctrlDataPtr,u32 nBufs,bool negative,bool peek)229 static int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek)
230 {
231 if (nBufs > NUM_CTRL_BUFFERS)
232 return SCE_KERNEL_ERROR_INVALID_SIZE;
233
234 if (!peek && !__KernelIsDispatchEnabled())
235 return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
236 if (!peek && __IsInInterrupt())
237 return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
238
239 u32 resetRead = ctrlBufRead;
240
241 u32 availBufs;
242 // Peeks always work, they just go go from now X buffers.
243 if (peek)
244 availBufs = nBufs;
245 else
246 {
247 availBufs = (ctrlBuf - ctrlBufRead + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;
248 if (availBufs > nBufs)
249 availBufs = nBufs;
250 }
251 ctrlBufRead = (ctrlBuf - availBufs + NUM_CTRL_BUFFERS) % NUM_CTRL_BUFFERS;
252
253 int done = 0;
254 auto data = PSPPointer<CtrlData>::Create(ctrlDataPtr);
255 for (u32 i = 0; i < availBufs; ++i)
256 done += __CtrlReadSingleBuffer(data++, negative);
257
258 if (peek)
259 ctrlBufRead = resetRead;
260
261 return done;
262 }
263
__CtrlDoSample()264 static void __CtrlDoSample()
265 {
266 // This samples the ctrl data into the buffers and updates the latch.
267 __CtrlUpdateLatch();
268
269 // Wake up a single thread that was waiting for the buffer.
270 retry:
271 if (!waitingThreads.empty() && ctrlBuf != ctrlBufRead)
272 {
273 SceUID threadID = waitingThreads[0];
274 waitingThreads.erase(waitingThreads.begin());
275
276 u32 error;
277 SceUID wVal = __KernelGetWaitID(threadID, WAITTYPE_CTRL, error);
278 // Make sure it didn't get woken or something.
279 if (wVal == 0)
280 goto retry;
281
282 PSPPointer<CtrlData> ctrlDataPtr;
283 ctrlDataPtr = __KernelGetWaitValue(threadID, error);
284 int retVal = __CtrlReadSingleBuffer(ctrlDataPtr, wVal == CTRL_WAIT_NEGATIVE);
285 __KernelResumeThreadFromWait(threadID, retVal);
286 __KernelReSchedule("ctrl buffers updated");
287 }
288 }
289
__CtrlVblank()290 static void __CtrlVblank()
291 {
292 emuRapidFireFrames++;
293
294 // Reduce gamepad Vibration by set % each frame
295 leftVibration *= (float)vibrationLeftDropout / 256.0f;
296 rightVibration *= (float)vibrationRightDropout / 256.0f;
297
298 // This always runs, so make sure we're in vblank mode.
299 if (ctrlCycle == 0)
300 __CtrlDoSample();
301 }
302
__CtrlTimerUpdate(u64 userdata,int cyclesLate)303 static void __CtrlTimerUpdate(u64 userdata, int cyclesLate)
304 {
305 // This only runs in timer mode (ctrlCycle > 0.)
306 _dbg_assert_msg_(ctrlCycle > 0, "Ctrl: sampling cycle should be > 0");
307
308 CoreTiming::ScheduleEvent(usToCycles(ctrlCycle) - cyclesLate, ctrlTimer, 0);
309
310 __CtrlDoSample();
311 }
312
__CtrlInit()313 void __CtrlInit()
314 {
315 ctrlTimer = CoreTiming::RegisterEvent("CtrlSampleTimer", __CtrlTimerUpdate);
316 __DisplayListenVblank(__CtrlVblank);
317
318 ctrlIdleReset = -1;
319 ctrlIdleBack = -1;
320 ctrlCycle = 0;
321
322 std::lock_guard<std::mutex> guard(ctrlMutex);
323
324 ctrlBuf = 1;
325 ctrlBufRead = 0;
326 ctrlOldButtons = 0;
327 ctrlLatchBufs = 0;
328 dialogBtnMake = 0;
329
330 memset(&latch, 0, sizeof(latch));
331 // Start with everything released.
332 latch.btnRelease = 0xffffffff;
333
334 memset(&ctrlCurrent, 0, sizeof(ctrlCurrent));
335 memset(ctrlCurrent.analog, CTRL_ANALOG_CENTER, sizeof(ctrlCurrent.analog));
336 analogEnabled = false;
337
338 for (u32 i = 0; i < NUM_CTRL_BUFFERS; i++)
339 memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(CtrlData));
340 }
341
__CtrlDoState(PointerWrap & p)342 void __CtrlDoState(PointerWrap &p)
343 {
344 std::lock_guard<std::mutex> guard(ctrlMutex);
345
346 auto s = p.Section("sceCtrl", 1, 3);
347 if (!s)
348 return;
349
350 Do(p, analogEnabled);
351 Do(p, ctrlLatchBufs);
352 Do(p, ctrlOldButtons);
353
354 p.DoVoid(ctrlBufs, sizeof(ctrlBufs));
355 if (s <= 2) {
356 CtrlData dummy = {0};
357 Do(p, dummy);
358 }
359 Do(p, ctrlBuf);
360 Do(p, ctrlBufRead);
361 Do(p, latch);
362 if (s == 1) {
363 dialogBtnMake = 0;
364 } else {
365 Do(p, dialogBtnMake);
366 }
367
368 Do(p, ctrlIdleReset);
369 Do(p, ctrlIdleBack);
370
371 Do(p, ctrlCycle);
372
373 SceUID dv = 0;
374 Do(p, waitingThreads, dv);
375
376 Do(p, ctrlTimer);
377 CoreTiming::RestoreRegisterEvent(ctrlTimer, "CtrlSampleTimer", __CtrlTimerUpdate);
378 }
379
__CtrlShutdown()380 void __CtrlShutdown()
381 {
382 waitingThreads.clear();
383 }
384
sceCtrlSetSamplingCycle(u32 cycle)385 static u32 sceCtrlSetSamplingCycle(u32 cycle)
386 {
387 DEBUG_LOG(SCECTRL, "sceCtrlSetSamplingCycle(%u)", cycle);
388
389 if ((cycle > 0 && cycle < 5555) || cycle > 20000)
390 {
391 WARN_LOG(SCECTRL, "SCE_KERNEL_ERROR_INVALID_VALUE=sceCtrlSetSamplingCycle(%u)", cycle);
392 return SCE_KERNEL_ERROR_INVALID_VALUE;
393 }
394
395 u32 prev = ctrlCycle;
396 ctrlCycle = cycle;
397
398 if (prev > 0)
399 CoreTiming::UnscheduleEvent(ctrlTimer, 0);
400 if (cycle > 0)
401 CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0);
402
403 return prev;
404 }
405
sceCtrlGetSamplingCycle(u32 cyclePtr)406 static int sceCtrlGetSamplingCycle(u32 cyclePtr)
407 {
408 DEBUG_LOG(SCECTRL, "sceCtrlGetSamplingCycle(%08x)", cyclePtr);
409 if (Memory::IsValidAddress(cyclePtr))
410 Memory::Write_U32(ctrlCycle, cyclePtr);
411 return 0;
412 }
413
sceCtrlSetSamplingMode(u32 mode)414 static u32 sceCtrlSetSamplingMode(u32 mode)
415 {
416 u32 retVal = 0;
417
418 DEBUG_LOG(SCECTRL, "sceCtrlSetSamplingMode(%i)", mode);
419 if (mode > 1)
420 return SCE_KERNEL_ERROR_INVALID_MODE;
421
422 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;
423 analogEnabled = mode == CTRL_MODE_ANALOG ? true : false;
424 return retVal;
425 }
426
sceCtrlGetSamplingMode(u32 modePtr)427 static int sceCtrlGetSamplingMode(u32 modePtr)
428 {
429 u32 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;
430 DEBUG_LOG(SCECTRL, "%d=sceCtrlGetSamplingMode(%08x)", retVal, modePtr);
431
432 if (Memory::IsValidAddress(modePtr))
433 Memory::Write_U32(retVal, modePtr);
434
435 return 0;
436 }
437
sceCtrlSetIdleCancelThreshold(int idleReset,int idleBack)438 static int sceCtrlSetIdleCancelThreshold(int idleReset, int idleBack)
439 {
440 DEBUG_LOG(SCECTRL, "FAKE sceCtrlSetIdleCancelThreshold(%d, %d)", idleReset, idleBack);
441
442 if (idleReset < -1 || idleBack < -1 || idleReset > 128 || idleBack > 128)
443 return SCE_KERNEL_ERROR_INVALID_VALUE;
444
445 ctrlIdleReset = idleReset;
446 ctrlIdleBack = idleBack;
447 return 0;
448 }
449
sceCtrlGetIdleCancelThreshold(u32 idleResetPtr,u32 idleBackPtr)450 static int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr)
451 {
452 DEBUG_LOG(SCECTRL, "sceCtrlSetIdleCancelThreshold(%08x, %08x)", idleResetPtr, idleBackPtr);
453
454 if (idleResetPtr && !Memory::IsValidAddress(idleResetPtr))
455 return SCE_KERNEL_ERROR_PRIV_REQUIRED;
456 if (idleBackPtr && !Memory::IsValidAddress(idleBackPtr))
457 return SCE_KERNEL_ERROR_PRIV_REQUIRED;
458
459 if (idleResetPtr)
460 Memory::Write_U32(ctrlIdleReset, idleResetPtr);
461 if (idleBackPtr)
462 Memory::Write_U32(ctrlIdleBack, idleBackPtr);
463
464 return 0;
465 }
466
sceCtrlReadBufferPositive(u32 ctrlDataPtr,u32 nBufs)467 static int sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs)
468 {
469 int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, false);
470 hleEatCycles(330);
471 if (done != 0)
472 {
473 DEBUG_LOG(SCECTRL, "%d=sceCtrlReadBufferPositive(%08x, %i)", done, ctrlDataPtr, nBufs);
474 }
475 else
476 {
477 waitingThreads.push_back(__KernelGetCurThread());
478 __KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_POSITIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");
479 DEBUG_LOG(SCECTRL, "sceCtrlReadBufferPositive(%08x, %i) - waiting", ctrlDataPtr, nBufs);
480 }
481 return done;
482 }
483
sceCtrlReadBufferNegative(u32 ctrlDataPtr,u32 nBufs)484 static int sceCtrlReadBufferNegative(u32 ctrlDataPtr, u32 nBufs)
485 {
486 int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, false);
487 hleEatCycles(330);
488 if (done != 0)
489 {
490 DEBUG_LOG(SCECTRL, "%d=sceCtrlReadBufferNegative(%08x, %i)", done, ctrlDataPtr, nBufs);
491 }
492 else
493 {
494 waitingThreads.push_back(__KernelGetCurThread());
495 __KernelWaitCurThread(WAITTYPE_CTRL, CTRL_WAIT_NEGATIVE, ctrlDataPtr, 0, false, "ctrl buffer waited");
496 DEBUG_LOG(SCECTRL, "sceCtrlReadBufferNegative(%08x, %i) - waiting", ctrlDataPtr, nBufs);
497 }
498 return done;
499 }
500
sceCtrlPeekBufferPositive(u32 ctrlDataPtr,u32 nBufs)501 static int sceCtrlPeekBufferPositive(u32 ctrlDataPtr, u32 nBufs)
502 {
503 int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, false, true);
504 // Some homebrew call this in a tight loop - so VERBOSE it is.
505 VERBOSE_LOG(SCECTRL, "%d=sceCtrlPeekBufferPositive(%08x, %i)", done, ctrlDataPtr, nBufs);
506 hleEatCycles(330);
507 return done;
508 }
509
sceCtrlPeekBufferNegative(u32 ctrlDataPtr,u32 nBufs)510 static int sceCtrlPeekBufferNegative(u32 ctrlDataPtr, u32 nBufs)
511 {
512 int done = __CtrlReadBuffer(ctrlDataPtr, nBufs, true, true);
513 // Some homebrew call this in a tight loop - so VERBOSE it is.
514 VERBOSE_LOG(SCECTRL, "%d=sceCtrlPeekBufferNegative(%08x, %i)", done, ctrlDataPtr, nBufs);
515 hleEatCycles(330);
516 return done;
517 }
518
__CtrlWriteUserLatch(CtrlLatch * userLatch,int bufs)519 static void __CtrlWriteUserLatch(CtrlLatch *userLatch, int bufs) {
520 *userLatch = latch;
521 userLatch->btnBreak &= CTRL_MASK_USER;
522 userLatch->btnMake &= CTRL_MASK_USER;
523 userLatch->btnPress &= CTRL_MASK_USER;
524 if (bufs > 0) {
525 userLatch->btnRelease |= ~CTRL_MASK_USER;
526 }
527 }
528
sceCtrlPeekLatch(u32 latchDataPtr)529 static u32 sceCtrlPeekLatch(u32 latchDataPtr) {
530 auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);
531 if (userLatch.IsValid()) {
532 __CtrlWriteUserLatch(userLatch, ctrlLatchBufs);
533 }
534 return hleLogSuccessI(SCECTRL, ctrlLatchBufs);
535 }
536
sceCtrlReadLatch(u32 latchDataPtr)537 static u32 sceCtrlReadLatch(u32 latchDataPtr) {
538 auto userLatch = PSPPointer<CtrlLatch>::Create(latchDataPtr);
539 if (userLatch.IsValid()) {
540 __CtrlWriteUserLatch(userLatch, ctrlLatchBufs);
541 }
542 return hleLogSuccessI(SCECTRL, __CtrlResetLatch());
543 }
544
545 static const HLEFunction sceCtrl[] =
546 {
547 {0X3E65A0EA, nullptr, "sceCtrlInit", '?', "" }, //(int unknown), init with 0
548 {0X1F4011E6, &WrapU_U<sceCtrlSetSamplingMode>, "sceCtrlSetSamplingMode", 'x', "x" },
549 {0X6A2774F3, &WrapU_U<sceCtrlSetSamplingCycle>, "sceCtrlSetSamplingCycle", 'x', "x" },
550 {0X02BAAD91, &WrapI_U<sceCtrlGetSamplingCycle>, "sceCtrlGetSamplingCycle", 'i', "x" },
551 {0XDA6B76A1, &WrapI_U<sceCtrlGetSamplingMode>, "sceCtrlGetSamplingMode", 'i', "x" },
552 {0X1F803938, &WrapI_UU<sceCtrlReadBufferPositive>, "sceCtrlReadBufferPositive", 'i', "xx"},
553 {0X3A622550, &WrapI_UU<sceCtrlPeekBufferPositive>, "sceCtrlPeekBufferPositive", 'i', "xx"},
554 {0XC152080A, &WrapI_UU<sceCtrlPeekBufferNegative>, "sceCtrlPeekBufferNegative", 'i', "xx"},
555 {0X60B81F86, &WrapI_UU<sceCtrlReadBufferNegative>, "sceCtrlReadBufferNegative", 'i', "xx"},
556 {0XB1D0E5CD, &WrapU_U<sceCtrlPeekLatch>, "sceCtrlPeekLatch", 'i', "x" },
557 {0X0B588501, &WrapU_U<sceCtrlReadLatch>, "sceCtrlReadLatch", 'i', "x" },
558 {0X348D99D4, nullptr, "sceCtrlSetSuspendingExtraSamples", '?', "" },
559 {0XAF5960F3, nullptr, "sceCtrlGetSuspendingExtraSamples", '?', "" },
560 {0XA68FD260, nullptr, "sceCtrlClearRapidFire", '?', "" },
561 {0X6841BE1A, nullptr, "sceCtrlSetRapidFire", '?', "" },
562 {0XA7144800, &WrapI_II<sceCtrlSetIdleCancelThreshold>, "sceCtrlSetIdleCancelThreshold", 'i', "ii"},
563 {0X687660FA, &WrapI_UU<sceCtrlGetIdleCancelThreshold>, "sceCtrlGetIdleCancelThreshold", 'i', "xx"},
564 };
565
Register_sceCtrl()566 void Register_sceCtrl()
567 {
568 RegisterModule("sceCtrl", ARRAY_SIZE(sceCtrl), sceCtrl);
569 }
570
Register_sceCtrl_driver()571 void Register_sceCtrl_driver()
572 {
573 RegisterModule("sceCtrl_driver", ARRAY_SIZE(sceCtrl), sceCtrl);
574 }
575
sceCtrlGetRightVibration()576 u16 sceCtrlGetRightVibration() {
577 return rightVibration;
578 }
579
sceCtrlGetLeftVibration()580 u16 sceCtrlGetLeftVibration() {
581 return leftVibration;
582 }
583
584 namespace SceCtrl {
SetRightVibration(u16 rVibration)585 void SetRightVibration(u16 rVibration) {
586 rightVibration = rVibration;
587 }
SetLeftVibration(u16 lVibration)588 void SetLeftVibration(u16 lVibration) {
589 leftVibration = lVibration;
590 }
SetVibrationRightDropout(u8 vibrationRDropout)591 void SetVibrationRightDropout(u8 vibrationRDropout) {
592 vibrationRightDropout = vibrationRDropout;
593 }
SetVibrationLeftDropout(u8 vibrationLDropout)594 void SetVibrationLeftDropout(u8 vibrationLDropout) {
595 vibrationLeftDropout = vibrationLDropout;
596 }
597 }
598