perfect_dark/src/lib/joy.c

1119 lines
25 KiB
C

#include <ultra64.h>
#include "constants.h"
#include "game/pak.h"
#include "bss.h"
#include "lib/main.h"
#include "lib/joy.h"
#include "data.h"
#include "types.h"
/**
* PD polls the controllers from the scheduler's thread. The scheduler polls the
* controllers on each retrace and stores the results inside g_JoyData->samples.
* This allows the main thread to access a history of controller states since
* the last rendered frame. For example, under laggy conditions the player might
* press and release a button between two frames and the main thread can tell
* that this has happened even if the button was unpressed during both the
* previous and current frame.
*
* The samples array contains 20 elements and is written to in a cyclic manner.
* These samples are split into two partitions: cur and next. cur refers to
* samples which are currently visible to the main thread on this frame, and
* samples in next are samples which have been added since the start of the
* current frame and will be made visible on the next frame.
*
* At the start of a frame, the main thread informs the joy system that it's
* ready to consume more samples. The joy system then moves the partition
* boundaries so that the old next partition becomes the new cur, and everything
* else becomes available for next.
*
* If all 20 samples are in use, the joy system will overwrite the most recent
* sample in the next partition.
*/
struct joydata g_JoyData[2];
s32 g_JoyDisableCooldown[4];
OSMesgQueue g_PiMesgQueue;
OSMesg var80099e90[10];
OSMesg var80099eb8[2];
OSMesgQueue g_JoyStopCyclicPollingMesgQueue;
OSMesg var80099ed8[2];
OSMesgQueue g_JoyStopCyclicPollingDoneMesgQueue;
OSMesg var80099ef8[2];
OSMesgQueue g_JoyStartCyclicPollingMesgQueue;
OSMesg var80099f18[2];
OSMesgQueue g_JoyStartCyclicPollingDoneMesgQueue;
OSContStatus var80099f38[4];
#if VERSION >= VERSION_NTSC_1_0
u8 g_JoyPfsStates[100];
u32 var80099fac;
u32 var80099fb0;
#endif
const char var70054080[] = "joyReset\n";
const char var7005408c[] = "joyReset: doing nothing\n";
struct joydata *g_JoyDataPtr = &g_JoyData[0];
bool g_JoyBusy = false;
u32 var8005ee68 = 0;
// Number of times per pad that different inputs were attempted to be read
// when controller was disconnected or not ready.
u32 g_JoyBadReadsStickX[4] = {0};
u32 g_JoyBadReadsStickY[4] = {0};
u32 g_JoyBadReadsButtons[4] = {0};
u32 g_JoyBadReadsButtonsPressed[4] = {0};
u8 g_JoyConnectedControllers = 0;
bool g_JoyQueuesCreated = false;
bool g_JoyInitDone = false;
bool g_JoyNeedsInit = true;
u32 g_JoyCyclicPollDisableCount = 0;
u32 var8005eec0 = 1;
s32 (*var8005eec4)(struct contsample *samples, s32 samplenum) = NULL;
void (*var8005eec8)(struct contsample *samples, s32 samplenum, s32 samplenum2) = NULL;
s32 g_JoyNextPfsStateIndex = (VERSION >= VERSION_NTSC_1_0 ? 0 : 30);
s32 var8005eed0 = 0;
#if VERSION >= VERSION_NTSC_1_0
u32 var8005eed4 = 0;
#endif
u8 var8005eed8 = 0;
#if VERSION >= VERSION_NTSC_1_0
bool var8005eedc = true;
s32 var8005eee0 = 0;
s32 var8005eee4 = -1;
u32 var8005eee8 = 0;
u32 var8005eeec = 0;
u32 var8005eef0 = 1;
#else
u32 var800612c8nb = 3;
u8 var800612ccnb = 0;
#endif
#if VERSION >= VERSION_NTSC_1_0
void joy00013900(void)
{
if (var8005eef0) {
joyDisableCyclicPolling();
var8005eef0 = false;
}
}
void joy00013938(void)
{
if (!var8005eef0) {
joyEnableCyclicPolling();
var8005eef0 = true;
}
}
void joy00013974(u32 value)
{
var8005eeec = value;
}
#endif
#if VERSION >= VERSION_NTSC_1_0
u32 joy00013980(void)
{
return var8005eeec;
}
#endif
/**
* Note: Some of the variables in this file are misnamed in NTSC beta.
* @TODO: Untangle these.
*/
void joy0001398c(s32 value)
{
#if VERSION >= VERSION_NTSC_1_0
var8005eee4 = var8005eee0 = value * 11000;
#else
g_JoyNextPfsStateIndex = value;
#endif
}
void joy000139c8(void)
{
joy0001398c(VERSION >= VERSION_NTSC_1_0 ? 10 : 30);
}
#if VERSION < VERSION_NTSC_1_0
// Same function as the one a couple above, just relocated
u32 joy00013980(void)
{
#if VERSION >= VERSION_NTSC_1_0
return var8005eeec;
#else
return var8005eed8;
#endif
}
#endif
#if VERSION >= VERSION_NTSC_1_0
/**
* Remove an item from the beginning of the g_JoyPfsStates array,
* shift the rest of the array back and return the removed item.
*/
s32 joyShiftPfsStates(void)
{
s32 pfsstate = 0;
s32 i;
if (g_JoyNextPfsStateIndex) {
pfsstate = g_JoyPfsStates[0];
if (g_JoyNextPfsStateIndex > 1) {
for (i = 0; i < g_JoyNextPfsStateIndex; i++) {
g_JoyPfsStates[i] = g_JoyPfsStates[i + 1];
}
g_JoyNextPfsStateIndex--;
}
}
return pfsstate;
}
#endif
#if VERSION >= VERSION_NTSC_1_0
void joyRecordPfsState(u8 pfsstate)
{
if (g_JoyNextPfsStateIndex + 1 >= 100) {
joyShiftPfsStates();
}
if (g_JoyNextPfsStateIndex == 0 || pfsstate != g_JoyPfsStates[g_JoyNextPfsStateIndex - 1]) {
g_JoyPfsStates[g_JoyNextPfsStateIndex] = pfsstate;
g_JoyNextPfsStateIndex++;
}
}
#endif
#if VERSION >= VERSION_NTSC_1_0
/**
* Scan controllers for controller paks, but only under certain conditions.
* Seems to be timer based, or can be forced by passing 2 as arg0.
*/
void joyCheckPfs(s32 arg0)
{
static u32 thiscount = 0; // 8005eef4
static u32 prevcount = 0; // 8005eef8
static u32 doingit = false; // 8005eefc
u32 diffcount;
u32 value;
if (var8005eedc
&& (arg0 == 2 || (var8005eee0 && (arg0 || ((g_JoyCyclicPollDisableCount == 0 || var8005eef0 == 0) && var8005eeec))))
&& !doingit) {
doingit = true;
prevcount = thiscount;
thiscount = osGetCount();
diffcount = (thiscount - prevcount) / 256;
value = var8005eee0 * 2;
if (diffcount > value) {
diffcount = value;
}
var8005eee4 -= diffcount;
if (var8005eee4 < 0
|| arg0 == 2
|| (arg0 == 1 && var8005eee4 < 0 && var8005eee0 < -var8005eee4)) {
u8 bitpattern = 0;
var8005eee8++;
if (arg0) {
joyDisableCyclicPolling();
}
osPfsIsPlug(&g_PiMesgQueue, &bitpattern);
if (arg0) {
joyEnableCyclicPolling();
}
bitpattern |= 0x10;
joyRecordPfsState(bitpattern);
var8005eee4 = var8005eee0;
}
doingit = false;
}
#if VERSION < VERSION_PAL_BETA
if (arg0) {
// empty
}
#endif
}
#endif
/**
* "Temporarily" because the next time joyCheckPfs runs, the true state will be
* recorded.
*
* Note that var8005eed8 is always zero, so this record will suggest that this
* pak is the only one connected.
*/
void joySetPfsTemporarilyPlugged(s8 index)
{
#if VERSION >= VERSION_NTSC_1_0
u8 bitpattern = var8005eed8 & ~(1 << index);
joyRecordPfsState(bitpattern);
#else
var8005eed8 &= ~(1 << index);
#endif
}
void joyInit(void)
{
s32 i;
s32 j;
osCreateMesgQueue(&g_JoyStopCyclicPollingMesgQueue, var80099eb8, 1);
osCreateMesgQueue(&g_JoyStopCyclicPollingDoneMesgQueue, var80099ed8, 1);
osCreateMesgQueue(&g_JoyStartCyclicPollingMesgQueue, var80099ef8, 1);
osCreateMesgQueue(&g_JoyStartCyclicPollingDoneMesgQueue, var80099f18, 1);
osCreateMesgQueue(&g_PiMesgQueue, var80099e90, ARRAYCOUNT(var80099e90));
osSetEventMesg(OS_EVENT_SI, &g_PiMesgQueue, NULL);
g_JoyQueuesCreated = true;
var8005eec4 = NULL;
var8005eec8 = NULL;
for (i = 0; i < 2; i++) {
g_JoyData[i].curlast = 0;
g_JoyData[i].curstart = 0;
g_JoyData[i].nextlast = 0;
g_JoyData[i].nextsecondlast = 0;
g_JoyData[i].unk200 = -1;
for (j = 0; j < 4; j++) {
g_JoyData[i].samples[0].pads[j].button = 0;
g_JoyData[i].samples[0].pads[j].stick_x = 0;
g_JoyData[i].samples[0].pads[j].stick_y = 0;
g_JoyData[i].samples[0].pads[j].errno = 0;
}
}
for (i = 0; i < 4; i++) {
g_JoyDisableCooldown[i] = 0;
}
}
/**
* Disable all input on all controllers for 60 frames, or until the player has
* released all inputs.
*
* It's used to prevent the player from accidentally skipping cutscenes and
* progressing past endscreens if they are holding buttons when they are
* started.
*/
void joyDisableTemporarily(void)
{
s32 i;
for (i = 0; i < 4; i++) {
g_JoyDisableCooldown[i] = TICKS(60);
}
}
void joyReset(void)
{
OSMesg msg;
if (g_JoyQueuesCreated) {
osSendMesg(&g_JoyStopCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK);
osRecvMesg(&g_JoyStopCyclicPollingDoneMesgQueue, &msg, OS_MESG_BLOCK);
joy00013e84();
osSendMesg(&g_JoyStartCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK);
osRecvMesg(&g_JoyStartCyclicPollingDoneMesgQueue, &msg, OS_MESG_BLOCK);
var8005eec0 = 1;
}
}
void joy00013e84(void)
{
static u8 var8005ef00 = 0xff;
// osContInit should be called only once. The first time this function is
// called it'll take the first branch here, and all subsequent calls will
// take the second branch.
if (g_JoyNeedsInit) {
s32 i;
g_JoyNeedsInit = false;
osContInit(&g_PiMesgQueue, &g_JoyConnectedControllers, var80099f38);
g_JoyInitDone = true;
for (i = 0; i < 4; i++) {
joyStopRumble(i, false);
}
} else {
u32 slots = 0xf;
s32 i;
osContStartQuery(&g_PiMesgQueue);
osRecvMesg(&g_PiMesgQueue, NULL, OS_MESG_BLOCK);
osContGetQuery(var80099f38);
for (i = 0; i < 4; i++) {
if (var80099f38[i].errno & CONT_NO_RESPONSE_ERROR) {
slots -= 1 << i;
}
}
g_JoyConnectedControllers = slots;
}
if (var8005ef00 != g_JoyConnectedControllers) {
s32 i = 0;
s32 index = 0;
for (; i < 4; i++) {
if (g_JoyConnectedControllers & (1 << i)) {
g_Vars.playertojoymap[index++] = i;
}
}
var8005ef00 = g_JoyConnectedControllers;
}
}
s8 contGetFreeSlot(void)
{
s32 i;
if (g_JoyDataPtr->unk200 >= 0) {
return g_JoyDataPtr->unk200;
}
for (i = 0; i < 4; i++) {
if ((g_JoyConnectedControllers & (1 << i)) == 0) {
return i;
}
}
return 4;
}
u32 joyGetConnectedControllers(void)
{
return g_JoyConnectedControllers;
}
#if VERSION < VERSION_NTSC_1_0
void func00014e9cnb(void *callback, s32 value2)
{
var8005eec4 = callback;
g_JoyData[1].unk200 = value2;
}
#endif
#if VERSION < VERSION_NTSC_1_0
void func00014eb0nb(void *value)
{
var8005eec8 = value;
}
#endif
void joyConsumeSamples(struct joydata *joydata)
{
s8 i;
s32 samplenum;
u16 buttons1;
u16 buttons2;
joydata->curstart = joydata->curlast;
joydata->curlast = joydata->nextlast;
for (i = 0; i < 4; i++) {
joydata->buttonspressed[i] = 0;
joydata->buttonsreleased[i] = 0;
if (joydata->curlast != joydata->curstart) {
samplenum = (joydata->curstart + 1) % 20; while (true) {
buttons1 = joydata->samples[samplenum].pads[i].button;
buttons2 = joydata->samples[(samplenum + 19) % 20].pads[i].button;
joydata->buttonspressed[i] |= buttons1 & ~buttons2;
joydata->buttonsreleased[i] |= ~buttons1 & buttons2;
if (g_JoyDisableCooldown[i] > 0) {
if (joydata->samples[samplenum].pads[i].button == 0
&& joydata->samples[samplenum].pads[i].stick_x < 15
&& joydata->samples[samplenum].pads[i].stick_x > -15
&& joydata->samples[samplenum].pads[i].stick_y < 15
&& joydata->samples[samplenum].pads[i].stick_y > -15) {
g_JoyDisableCooldown[i] = 0;
} else {
g_JoyDisableCooldown[i]--;
}
}
if (samplenum == joydata->curlast) {
break;
}
samplenum = (samplenum + 1) % 20;
}
}
}
}
#if VERSION < VERSION_NTSC_1_0
void joy0001509cnb(void)
{
static bool doingit = false;
if (!doingit) {
doingit = true;
if (g_JoyNextPfsStateIndex < var8005eed0) {
var8005eed0 = 0;
joy00013e84();
osPfsIsPlug(&g_PiMesgQueue, &var8005eed8);
var8005eed8 |= 0x10;
}
doingit = false;
}
}
#endif
/**
* The use of the static variable suggests that the function is able to be
* called recursively, but its behaviour should not be run when recursing.
*/
void joy00014238(void)
{
static bool doingit = false;
s32 i;
if (!doingit) {
doingit = true;
for (i = 0; i < 4; i++) {
if (joy000155f4(i) == PAK010_13) {
pakSetUnk010(i, PAK010_11);
}
}
if (var8005eec4 == NULL) {
joysTickRumble();
}
doingit = false;
}
}
void joyDebugJoy(void)
{
#if VERSION >= VERSION_NTSC_1_0
static u32 var8005ef08 = 0;
mainOverrideVariable("debugjoy", &var8005ef08);
#else
mainOverrideVariable("joyforce", &var800612c8nb);
#endif
#if VERSION >= VERSION_NTSC_1_0
if (g_Vars.paksconnected) {
joyCheckPfs(1);
}
#endif
if (var8005eec4) {
g_JoyData[1].nextlast = var8005eec4(g_JoyData[1].samples, g_JoyData[1].curlast);
joyConsumeSamples(&g_JoyData[1]);
}
joyConsumeSamples(&g_JoyData[0]);
if (var8005eec8) {
var8005eec8(g_JoyData[0].samples, g_JoyData[0].curstart, g_JoyData[0].curlast);
}
if (joyIsCyclicPollingEnabled() && var8005eec0 && joyGetNumSamples() <= 0) {
#if VERSION >= VERSION_NTSC_FINAL
joyDisableCyclicPolling();
joy00014238();
joyEnableCyclicPolling();
joyConsumeSamples(&g_JoyData[0]);
#elif VERSION >= VERSION_NTSC_1_0
joyDisableCyclicPolling();
joyEnableCyclicPolling();
joyConsumeSamples(&g_JoyData[0]);
joy00014238();
#else
joyDisableCyclicPolling(500, "joy.c");
joy00014238();
joy0001509cnb();
joyEnableCyclicPolling(507, "joy.c");
joyConsumeSamples(&g_JoyData[0]);
#endif
}
}
#if VERSION >= VERSION_NTSC_1_0
const char var700540b4[] = "JOY : g_EnableCyclicPolling=%d";
const char var700540d4[] = "JOY : g_JoyReCheckInterval=%d";
const char var700540f4[] = "JOY : g_JoyReCheckEventIn=%d";
const char var70054114[] = "JOY : g_JoyRecheckDone=%d";
#endif
const char var70054130[] = "osContStartReadData -> Failed - CONT_NO_RESPONSE_ERROR\n";
const char var70054168[] = "osContStartReadData -> Failed - CONT_OVERRUN_ERROR\n";
const char var7005419c[] = "joyTickRetrace:joy%derrno%d->%d\n";
const char var700541c0[] = "joyTickRetrace:joy%derrno%d->%d\n";
s32 joyStartReadData(OSMesgQueue *mq)
{
return osContStartReadData(mq);
}
void joyReadData(void)
{
s32 index = (g_JoyData[0].nextlast + 1) % 20;
if (index == g_JoyData[0].curstart) {
// If the sample queue is full, don't overwrite the oldest sample.
// Instead, overwrite the most recent.
index = g_JoyData[0].nextlast;
}
osContGetReadData(g_JoyData[0].samples[index].pads);
g_JoyData[0].nextlast = index;
g_JoyData[0].nextsecondlast = (g_JoyData[0].nextlast + 19) % 20;
}
void joysTick(void)
{
OSMesg msg;
s8 i;
#if VERSION < VERSION_NTSC_1_0
var8005eed0++;
#endif
if (osRecvMesg(&g_JoyStopCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK) == 0) {
if (g_JoyBusy) {
osRecvMesg(&g_PiMesgQueue, &msg, OS_MESG_BLOCK);
g_JoyBusy = false;
joyReadData();
// Check if error state has changed for any controller
for (i = 0; i < 4; i++) {
if ((g_JoyData[0].samples[g_JoyData[0].nextlast].pads[i].errno == 0 && g_JoyData[0].samples[g_JoyData[0].nextsecondlast].pads[i].errno != 0)
|| (g_JoyData[0].samples[g_JoyData[0].nextlast].pads[i].errno != 0 && g_JoyData[0].samples[g_JoyData[0].nextsecondlast].pads[i].errno == 0)) {
joy00013e84();
break;
}
}
}
osSendMesg(&g_JoyStopCyclicPollingDoneMesgQueue, &msg, OS_MESG_NOBLOCK);
var8005ee68++;
#if VERSION >= VERSION_NTSC_1_0
joyCheckPfs(0);
#endif
return;
}
if (osRecvMesg(&g_JoyStartCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK) == 0) {
var8005ee68--;
if (var8005ee68 == 0) {
joyStartReadData(&g_PiMesgQueue);
g_JoyBusy = true;
}
osSendMesg(&g_JoyStartCyclicPollingDoneMesgQueue, &msg, OS_MESG_NOBLOCK);
return;
}
#if VERSION < VERSION_NTSC_1_0
if (var8005ee68) {
return;
}
#endif
if (g_JoyInitDone) {
#if VERSION >= VERSION_NTSC_1_0
if (var8005ee68) {
joyCheckPfs(0);
return;
}
#endif
if (osRecvMesg(&g_PiMesgQueue, &msg, OS_MESG_NOBLOCK) == 0) {
static s32 count = 0;
g_JoyBusy = false;
joyReadData();
// Check if error state has changed for any controller
for (i = 0; i < 4; i++) {
if ((g_JoyData[0].samples[g_JoyData[0].nextlast].pads[i].errno == 0 && g_JoyData[0].samples[g_JoyData[0].nextsecondlast].pads[i].errno != 0)
|| (g_JoyData[0].samples[g_JoyData[0].nextlast].pads[i].errno != 0 && g_JoyData[0].samples[g_JoyData[0].nextsecondlast].pads[i].errno == 0)) {
joy00013e84();
break;
}
}
joy00014238();
#if VERSION >= VERSION_NTSC_1_0
joyCheckPfs(0);
#else
joy0001509cnb();
#endif
joyStartReadData(&g_PiMesgQueue);
g_JoyBusy = true;
count++;
if (count >= 60) {
s32 i;
for (i = 0; i < 4; i++) {
if (g_JoyBadReadsStickX[i] || g_JoyBadReadsStickY[i] || g_JoyBadReadsButtons[i] || g_JoyBadReadsButtonsPressed[i]) {
g_JoyBadReadsStickX[i] = 0;
g_JoyBadReadsStickY[i] = 0;
g_JoyBadReadsButtons[i] = 0;
g_JoyBadReadsButtonsPressed[i] = 0;
}
}
count = 0;
}
}
}
}
void joy00014810(bool value)
{
var8005eec0 = value;
}
s32 joyGetNumSamples(void)
{
return (g_JoyDataPtr->curlast - g_JoyDataPtr->curstart + 20) % 20;
}
s32 joyGetStickXOnSample(s32 samplenum, s8 contpadnum)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsStickX[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum + 1) % 20].pads[contpadnum].stick_x;
}
s32 joyGetStickYOnSample(s32 samplenum, s8 contpadnum)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsStickY[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum + 1) % 20].pads[contpadnum].stick_y;
}
s32 joyGetStickYOnSampleIndex(s32 samplenum, s8 contpadnum)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsStickY[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum) % 20].pads[contpadnum].stick_y;
}
u16 joyGetButtonsOnSample(s32 samplenum, s8 contpadnum, u16 mask)
{
u16 button;
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtons[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
button = g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum + 1) % 20].pads[contpadnum].button;
return button & mask;
}
u16 joyGetButtonsPressedOnSample(s32 samplenum, s8 contpadnum, u16 mask)
{
u16 button1;
u16 button2;
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtonsPressed[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
button1 = g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum + 1) % 20].pads[contpadnum].button;
button2 = g_JoyDataPtr->samples[(g_JoyDataPtr->curstart + samplenum) % 20].pads[contpadnum].button;
return (button1 & ~button2) & mask;
}
/**
* Count the number of times the buttons specified by mask were held during the
* specific samples given in checksamples.
*
* For example, if checksamples[5] is nonzero and a button was pressed on
* samplenum 5 which matches the mask, count is incremented.
*/
s32 joyCountButtonsOnSpecificSamples(u32 *checksamples, s8 contpadnum, u16 mask)
{
s32 count = 0;
s32 index = 0;
s32 i;
u16 button;
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtons[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
i = (g_JoyDataPtr->curstart + 1) % 20;
while (true) {
if (checksamples == NULL || checksamples[index]) {
button = g_JoyDataPtr->samples[i].pads[contpadnum].button;
if (button & mask) {
count++;
}
}
if (i == g_JoyDataPtr->curlast) {
break;
}
i = (i + 1) % 20;
index++;
}
return count;
}
s8 joyGetStickX(s8 contpadnum)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsStickX[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[g_JoyDataPtr->curlast].pads[contpadnum].stick_x;
}
s8 joyGetStickY(s8 contpadnum)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsStickY[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[g_JoyDataPtr->curlast].pads[contpadnum].stick_y;
}
u16 joyGetButtons(s8 contpadnum, u16 mask)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtons[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->samples[g_JoyDataPtr->curlast].pads[contpadnum].button & mask;
}
u16 joyGetButtonsPressedThisFrame(s8 contpadnum, u16 mask)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtonsPressed[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->buttonspressed[contpadnum] & mask;
}
#if VERSION < VERSION_NTSC_1_0
u16 joyGetButtonsReleasedThisFrame(s8 contpadnum, u16 mask)
{
if (g_JoyDataPtr->unk200 < 0 && (g_JoyConnectedControllers >> contpadnum & 1) == 0) {
g_JoyBadReadsButtonsPressed[contpadnum]++;
return 0;
}
if (g_JoyDisableCooldown[contpadnum] > 0) {
return 0;
}
return g_JoyDataPtr->buttonsreleased[contpadnum] & mask;
}
#endif
bool joyIsCyclicPollingEnabled(void)
{
return g_JoyCyclicPollDisableCount ? false : true;
}
/**
* If cyclic polling is enabled, send a message to the scheduler thread telling
* it to update the joy state (connected controllers, PFS etc). Then block while
* waiting for its done message to come back, and increment the disable count.
*
* If cyclic polling was already disabled, simply increase the disable count.
*/
void joyDisableCyclicPolling(
#if VERSION >= VERSION_NTSC_1_0
void
#else
s32 line, char *file
#endif
)
{
OSMesg msg;
if (g_JoyCyclicPollDisableCount == 0) {
osSendMesg(&g_JoyStopCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK);
osRecvMesg(&g_JoyStopCyclicPollingDoneMesgQueue, &msg, OS_MESG_BLOCK);
}
g_JoyCyclicPollDisableCount++;
}
/**
* Indicate that the caller is done with cyclic polling being disabled,
* and enable cyclic polling if there are no callers left who want it disabled.
*/
void joyEnableCyclicPolling(
#if VERSION >= VERSION_NTSC_1_0
void
#else
s32 line, char *file
#endif
)
{
OSMesg msg;
g_JoyCyclicPollDisableCount--;
if (g_JoyCyclicPollDisableCount == 0) {
osSendMesg(&g_JoyStartCyclicPollingMesgQueue, &msg, OS_MESG_NOBLOCK);
osRecvMesg(&g_JoyStartCyclicPollingDoneMesgQueue, &msg, OS_MESG_BLOCK);
}
}
#if VERSION < VERSION_NTSC_1_0
void joySetDataIndex(s32 arg0)
{
g_JoyDataPtr = &g_JoyData[arg0];
}
s32 joyGetDataIndex(void)
{
return g_JoyDataPtr - g_JoyData;
}
#endif
void joyDestroy(void)
{
s32 i;
osCreateMesgQueue(&g_PiMesgQueue, var80099e90, ARRAYCOUNT(var80099e90));
osSetEventMesg(OS_EVENT_SI, &g_PiMesgQueue, 0);
for (i = 0; i < 4; i++) {
if (osMotorProbe(&g_PiMesgQueue, PFS(i), i) == 0) {
osMotorStop(PFS(i));
osMotorStop(PFS(i));
osMotorStop(PFS(i));
}
}
}
#if VERSION >= VERSION_NTSC_1_0
void joyGetContpadNumsForPlayer(s8 playernum, s32 *pad1, s32 *pad2)
{
if (g_Vars.normmplayerisrunning) {
*pad1 = g_Vars.playerstats[playernum].mpindex;
*pad2 = -1;
return;
}
*pad1 = playernum;
if (g_PlayerConfigsArray[g_Vars.playerstats[playernum].mpindex].controlmode >= CONTROLMODE_21) {
*pad2 = PLAYERCOUNT() + playernum;
return;
}
*pad2 = -1;
}
#endif
void joyStopRumble(s8 arg0, bool disablepolling)
{
if (arg0 != SAVEDEVICE_GAMEPAK) {
#if VERSION >= VERSION_NTSC_1_0
s32 device = arg0;
#else
s32 device = g_Vars.playertojoymap[arg0];
#endif
if (g_Paks[device].type != PAKTYPE_MEMORY && g_Paks[device].type != PAKTYPE_GAMEBOY) {
if (disablepolling) {
#if VERSION >= VERSION_NTSC_1_0
joyDisableCyclicPolling();
#else
joyDisableCyclicPolling(1054, "joy.c");
#endif
}
if (osMotorProbe(&g_PiMesgQueue, PFS(device), device) == 0) {
osMotorStop(PFS(device));
osMotorStop(PFS(device));
osMotorStop(PFS(device));
}
if (disablepolling) {
#if VERSION >= VERSION_NTSC_1_0
joyEnableCyclicPolling();
#else
joyEnableCyclicPolling(1066, "joy.c");
#endif
}
if (g_Paks[device].rumblestate != RUMBLESTATE_DISABLED_STOPPING
&& g_Paks[device].rumblestate != RUMBLESTATE_DISABLED_STOPPED) {
g_Paks[device].rumblestate = RUMBLESTATE_ENABLED_STOPPING;
}
g_Paks[device].rumblettl = -1;
}
}
}
s32 joy000155b4(s8 device)
{
return g_Paks[device].unk010;
}
s32 joy000155f4(s8 device)
{
return joy000155b4(device);
}
void joysTickRumble(void)
{
s32 i;
for (i = 0; i < 4; i++) {
if (g_Paks[i].unk010 == PAK010_11 && g_Paks[i].type == PAKTYPE_RUMBLE) {
switch (g_Paks[i].rumblestate) {
case RUMBLESTATE_ENABLED_STARTING:
g_Paks[i].rumblestate = RUMBLESTATE_ENABLED_RUMBLING;
osMotorStart(PFS(i));
break;
case RUMBLESTATE_ENABLED_RUMBLING:
if (g_Paks[i].rumblepulsestopat != -1) {
if (g_Paks[i].rumblepulsetimer == 0) {
osMotorStart(PFS(i));
} else if (g_Paks[i].rumblepulsestopat == g_Paks[i].rumblepulsetimer) {
osMotorStop(PFS(i));
}
g_Paks[i].rumblepulsetimer++;
if (g_Paks[i].rumblepulselen == g_Paks[i].rumblepulsetimer) {
g_Paks[i].rumblepulsetimer = 0;
}
}
g_Paks[i].rumblettl--;
if (g_Paks[i].rumblettl < 0) {
g_Paks[i].rumblestate = RUMBLESTATE_ENABLED_STOPPING;
}
break;
case RUMBLESTATE_ENABLED_STOPPING:
g_Paks[i].rumblestate = RUMBLESTATE_ENABLED_STOPPED;
osMotorStop(PFS(i));
break;
case RUMBLESTATE_DISABLED_STOPPING:
osMotorStop(PFS(i));
g_Paks[i].rumblestate = RUMBLESTATE_DISABLED_STOPPED;
break;
case RUMBLESTATE_ENABLING:
g_Paks[i].rumblestate = RUMBLESTATE_ENABLED_STOPPED;
g_Paks[i].rumblettl = -1;
break;
}
}
}
}