CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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