mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-12-27 13:11:48 +01:00
1bbdbfacd2
* Added Recomp Events for first-person aiming. * Mods can now force right-stick aiming. * Refactored aiming event code for formatting and clarity.
297 lines
9.5 KiB
C
297 lines
9.5 KiB
C
#include "patches.h"
|
|
#include "sys_cfb.h"
|
|
#include "buffers.h"
|
|
#include "fault.h"
|
|
#include "audiomgr.h"
|
|
#include "z64speed_meter.h"
|
|
#include "z64vimode.h"
|
|
#include "z64viscvg.h"
|
|
#include "z64vismono.h"
|
|
#include "z64viszbuf.h"
|
|
#include "input.h"
|
|
|
|
void recomp_set_current_frame_poll_id();
|
|
void PadMgr_HandleRetrace(void);
|
|
void PadMgr_LockPadData(void);
|
|
void PadMgr_UnlockPadData(void);
|
|
void PadMgr_UpdateRumble(void);
|
|
void PadMgr_UpdateConnections(void);
|
|
void PadMgr_UpdateInputs(void);
|
|
void PadMgr_InitVoice(void);
|
|
OSMesgQueue* PadMgr_AcquireSerialEventQueue(void);
|
|
void PadMgr_ReleaseSerialEventQueue(OSMesgQueue* serialEventQueue);
|
|
|
|
|
|
extern PadMgr* sPadMgrInstance;
|
|
extern s32 sPadMgrRetraceCount;
|
|
extern FaultMgr gFaultMgr;
|
|
extern s32 sVoiceInitStatus;
|
|
|
|
|
|
typedef enum {
|
|
/* 0 */ VOICE_INIT_FAILED, // voice initialization failed
|
|
/* 1 */ VOICE_INIT_TRY, // try to initialize voice
|
|
/* 2 */ VOICE_INIT_SUCCESS // voice initialized
|
|
} VoiceInitStatus;
|
|
|
|
RECOMP_PATCH void PadMgr_HandleRetrace(void) {
|
|
// Execute rumble callback
|
|
if (sPadMgrInstance->rumbleRetraceCallback != NULL) {
|
|
sPadMgrInstance->rumbleRetraceCallback(sPadMgrInstance->rumbleRetraceArg);
|
|
}
|
|
|
|
// Try and initialize a Voice Recognition Unit if not already attempted
|
|
if (sVoiceInitStatus != VOICE_INIT_FAILED) {
|
|
PadMgr_InitVoice();
|
|
}
|
|
|
|
// Rumble Pak
|
|
if (gFaultMgr.msgId != 0) {
|
|
// If fault is active, no rumble
|
|
PadMgr_RumbleStop();
|
|
} else if (sPadMgrInstance->rumbleOffTimer > 0) {
|
|
// If the rumble off timer is active, no rumble
|
|
--sPadMgrInstance->rumbleOffTimer;
|
|
PadMgr_RumbleStop();
|
|
} else if (sPadMgrInstance->rumbleOnTimer == 0) {
|
|
// If the rumble on timer is inactive, no rumble
|
|
PadMgr_RumbleStop();
|
|
} else if (!sPadMgrInstance->isResetting) {
|
|
// If not resetting, update rumble
|
|
PadMgr_UpdateRumble();
|
|
--sPadMgrInstance->rumbleOnTimer;
|
|
}
|
|
}
|
|
|
|
extern u8 sOcarinaInstrumentId;
|
|
|
|
void poll_inputs(void) {
|
|
OSMesgQueue* serialEventQueue = PadMgr_AcquireSerialEventQueue();
|
|
// Begin reading controller data
|
|
osContStartReadData(serialEventQueue);
|
|
|
|
bool needs_right_stick = recomp_analog_cam_enabled() || recomp_aiming_override_mode == RECOMP_AIMING_OVERRIDE_FORCE_RIGHT_STICK;
|
|
// Suppress the right analog stick if analog camera is active unless the ocarina is in use.
|
|
recomp_set_right_analog_suppressed(needs_right_stick && sOcarinaInstrumentId == OCARINA_INSTRUMENT_OFF);
|
|
// Resets this flag for the next frame;
|
|
recomp_aiming_override_mode = RECOMP_AIMING_OVERRIDE_OFF;
|
|
|
|
// Wait for controller data
|
|
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
|
osContGetReadData(sPadMgrInstance->pads);
|
|
|
|
// Clear all but controller 1
|
|
bzero(&sPadMgrInstance->pads[1], sizeof(*sPadMgrInstance->pads) * (MAXCONTROLLERS - 1));
|
|
|
|
// If in PreNMI, clear all controllers
|
|
if (sPadMgrInstance->isResetting) {
|
|
bzero(sPadMgrInstance->pads, sizeof(sPadMgrInstance->pads));
|
|
}
|
|
|
|
// Query controller statuses
|
|
osContStartQuery(serialEventQueue);
|
|
osRecvMesg(serialEventQueue, NULL, OS_MESG_BLOCK);
|
|
osContGetQuery(sPadMgrInstance->padStatus);
|
|
|
|
// Lock serial message queue
|
|
PadMgr_ReleaseSerialEventQueue(serialEventQueue);
|
|
|
|
// Update connections
|
|
PadMgr_UpdateConnections();
|
|
|
|
// Lock input data
|
|
PadMgr_LockPadData();
|
|
|
|
// Update input data
|
|
PadMgr_UpdateInputs();
|
|
|
|
// Execute input callback
|
|
if (sPadMgrInstance->inputRetraceCallback != NULL) {
|
|
sPadMgrInstance->inputRetraceCallback(sPadMgrInstance->inputRetraceArg);
|
|
}
|
|
|
|
// Unlock input data
|
|
PadMgr_UnlockPadData();
|
|
sPadMgrRetraceCount++;
|
|
}
|
|
|
|
// @recomp Patched to do the actual input polling.
|
|
RECOMP_PATCH void PadMgr_GetInput(Input* inputs, s32 gameRequest) {
|
|
// @recomp Do an actual poll if gameRequest is true.
|
|
if (gameRequest) {
|
|
poll_inputs();
|
|
// @recomp Tag the current frame's input polling id for latency tracking.
|
|
recomp_set_current_frame_poll_id();
|
|
}
|
|
PadMgr_LockPadData();
|
|
PadMgr_GetInputNoLock(inputs, gameRequest);
|
|
PadMgr_UnlockPadData();
|
|
}
|
|
|
|
// @recomp Just call PadMgr_GetInput.
|
|
RECOMP_PATCH void PadMgr_GetInput2(Input* inputs, s32 gameRequest) {
|
|
PadMgr_GetInput(inputs, gameRequest);
|
|
}
|
|
|
|
extern CfbInfo sGraphCfbInfos[3];
|
|
u32 recomp_time_us();
|
|
void recomp_measure_latency();
|
|
void* osViGetCurrentFramebuffer_recomp();
|
|
|
|
OSMesgQueue *rdp_queue_ptr = NULL;
|
|
|
|
// @recomp Immediately sends the graphics task instead of queueing it in the scheduler.
|
|
RECOMP_PATCH void Graph_TaskSet00(GraphicsContext* gfxCtx, GameState* gameState) {
|
|
static s32 retryCount = 10;
|
|
static s32 cfbIdx = 0;
|
|
OSTask_t* task = &gfxCtx->task.list.t;
|
|
OSScTask* scTask = &gfxCtx->task;
|
|
OSTimer timer;
|
|
OSMesg msg;
|
|
CfbInfo* cfb;
|
|
|
|
// @recomp Additional static members for extra scheduling purposes.
|
|
static IrqMgrClient irq_client = {0};
|
|
static OSMesgQueue vi_queue = {0};
|
|
static OSMesg vi_buf[8] = {0};
|
|
static bool created = false;
|
|
if (!created) {
|
|
created = true;
|
|
osCreateMesgQueue(&vi_queue, vi_buf, ARRAY_COUNT(vi_buf));
|
|
extern IrqMgr gIrqMgr;
|
|
IrqMgr_AddClient(&gIrqMgr, &irq_client, &vi_queue);
|
|
}
|
|
|
|
// @recomp Disable the wait here so that it can be moved after task submission for minimizing latency.
|
|
// retry:
|
|
// osSetTimer(&timer, OS_USEC_TO_CYCLES(3 * 1000 * 1000), 0, &gfxCtx->queue, (OSMesg)666);
|
|
// osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_BLOCK);
|
|
// osStopTimer(&timer);
|
|
|
|
// if (msg == (OSMesg)666) {
|
|
// osSyncPrintf("GRAPH SP TIMEOUT\n");
|
|
// if (retryCount >= 0) {
|
|
// retryCount--;
|
|
// Sched_SendGfxCancelMsg(&gSchedContext);
|
|
// goto retry;
|
|
// } else {
|
|
// // graph.c: No more! die!
|
|
// osSyncPrintf("graph.c:もうダメ!死ぬ!\n");
|
|
// Fault_AddHungupAndCrashImpl("RCP is HUNG UP!!", "Oh! MY GOD!!");
|
|
// }
|
|
// }
|
|
|
|
gfxCtx->masterList = gGfxMasterDL;
|
|
if (gfxCtx->callback != NULL) {
|
|
gfxCtx->callback(gfxCtx, gfxCtx->callbackArg);
|
|
}
|
|
|
|
task->type = M_GFXTASK;
|
|
task->flags = OS_SC_DRAM_DLIST;
|
|
task->ucodeBoot = SysUcode_GetUCodeBoot();
|
|
task->ucodeBootSize = SysUcode_GetUCodeBootSize();
|
|
task->ucode = SysUcode_GetUCode();
|
|
task->ucodeData = SysUcode_GetUCodeData();
|
|
task->ucodeSize = SP_UCODE_SIZE;
|
|
task->ucodeDataSize = SP_UCODE_DATA_SIZE;
|
|
task->dramStack = (u64*)gGfxSPTaskStack;
|
|
task->dramStackSize = sizeof(gGfxSPTaskStack);
|
|
task->outputBuff = gGfxSPTaskOutputBufferPtr;
|
|
task->outputBuffSize = gGfxSPTaskOutputBufferEnd;
|
|
task->dataPtr = (u64*)gGfxMasterDL;
|
|
task->dataSize = 0;
|
|
task->yieldDataPtr = (u64*)gGfxSPTaskYieldBuffer;
|
|
task->yieldDataSize = sizeof(gGfxSPTaskYieldBuffer);
|
|
|
|
scTask->next = NULL;
|
|
scTask->flags = OS_SC_RCP_MASK | OS_SC_SWAPBUFFER | OS_SC_LAST_TASK;
|
|
|
|
if (SREG(33) & 1) {
|
|
SREG(33) &= ~1;
|
|
scTask->flags &= ~OS_SC_SWAPBUFFER;
|
|
gfxCtx->framebufferIndex--;
|
|
}
|
|
|
|
scTask->msgQ = &gfxCtx->queue;
|
|
scTask->msg = NULL;
|
|
|
|
{ s32 pad; }
|
|
|
|
cfb = &sGraphCfbInfos[cfbIdx];
|
|
cfbIdx = (cfbIdx + 1) % ARRAY_COUNT(sGraphCfbInfos);
|
|
|
|
cfb->fb1 = gfxCtx->curFrameBuffer;
|
|
cfb->swapBuffer = gfxCtx->curFrameBuffer;
|
|
|
|
if (gfxCtx->updateViMode) {
|
|
gfxCtx->updateViMode = false;
|
|
cfb->viMode = gfxCtx->viMode;
|
|
cfb->features = gfxCtx->viConfigFeatures;
|
|
cfb->xScale = gfxCtx->xScale;
|
|
cfb->yScale = gfxCtx->yScale;
|
|
} else {
|
|
cfb->viMode = NULL;
|
|
}
|
|
cfb->unk_10 = 0;
|
|
cfb->updateRate = gameState->framerateDivisor;
|
|
|
|
scTask->framebuffer = cfb;
|
|
|
|
while (gfxCtx->queue.validCount != 0) {
|
|
osRecvMesg(&gfxCtx->queue, NULL, OS_MESG_NOBLOCK);
|
|
}
|
|
|
|
gfxCtx->schedMsgQ = &gSchedContext.cmdQ;
|
|
osSendMesg(&gSchedContext.cmdQ, scTask, OS_MESG_BLOCK);
|
|
Sched_SendEntryMsg(&gSchedContext);
|
|
|
|
// @recomp Immediately wait on the task to complete to minimize latency for the next one.
|
|
osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_BLOCK);
|
|
|
|
// @recomp Wait on the VI framebuffer to change if this task has a framebuffer swap.
|
|
if (scTask->flags & OS_SC_SWAPBUFFER) {
|
|
int viCounter = 0;
|
|
while (osViGetCurrentFramebuffer() != cfb->fb1) {
|
|
osRecvMesg(&vi_queue, NULL, OS_MESG_BLOCK);
|
|
viCounter++;
|
|
}
|
|
|
|
// If we didn't wait the full number of VIs needed between frames then wait one extra VI afterwards.
|
|
if (viCounter < gameState->framerateDivisor) {
|
|
osRecvMesg(&vi_queue, NULL, OS_MESG_BLOCK);
|
|
}
|
|
}
|
|
|
|
// @recomp Flush any extra messages from the VI queue.
|
|
while (osRecvMesg(&vi_queue, NULL, OS_MESG_NOBLOCK) == 0) {
|
|
;
|
|
}
|
|
}
|
|
|
|
extern SpeedMeter sGameSpeedMeter;
|
|
extern VisCvg sGameVisCvg;
|
|
extern VisZbuf sGameVisZbuf;
|
|
extern VisMono sGameVisMono;
|
|
extern ViMode sGameViMode;
|
|
|
|
RECOMP_PATCH void GameState_Destroy(GameState* gameState) {
|
|
AudioMgr_StopAllSfxExceptSystem();
|
|
Audio_Update();
|
|
|
|
// @recomp The wait for the gfx task was moved to directly after submission, so it's not needed here.
|
|
// osRecvMesg(&gameState->gfxCtx->queue, NULL, OS_MESG_BLOCK);
|
|
|
|
if (gameState->destroy != NULL) {
|
|
gameState->destroy(gameState);
|
|
}
|
|
|
|
Rumble_Destroy();
|
|
SpeedMeter_Destroy(&sGameSpeedMeter);
|
|
VisCvg_Destroy(&sGameVisCvg);
|
|
VisZbuf_Destroy(&sGameVisZbuf);
|
|
VisMono_Destroy(&sGameVisMono);
|
|
ViMode_Destroy(&sGameViMode);
|
|
THA_Destroy(&gameState->tha);
|
|
GameAlloc_Cleanup(&gameState->alloc);
|
|
}
|