BizHawk/libmupen64plus/mupen64plus-input-bkm/plugin.c

450 lines
15 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus-input-bkm - plugin.c *
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
* Edited 2014 null_ptr *
* Copyright (C) 2008-2011 Richard Goedeken *
* Copyright (C) 2008 Tillin9 *
* Copyright (C) 2002 Blight *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M64P_PLUGIN_PROTOTYPES 1
#include "m64p_types.h"
#include "m64p_plugin.h"
#include "m64p_common.h"
#include "m64p_config.h"
#include "plugin.h"
#include "config.h"
#include "version.h"
#include "osal_dynamiclib.h"
#include <errno.h>
/* global data definitions */
SController controller[4]; // 4 controllers
/* static data definitions */
static void (*l_DebugCallback)(void *, int, const char *) = NULL;
static void *l_DebugCallContext = NULL;
static int l_PluginInit = 0;
/* Callbacks for data flow out of mupen */
static int (*l_inputCallback)(int i) = NULL;
static int (*l_setrumbleCallback)(int i, int on) = NULL;
static int romopen = 0; // is a rom opened
/* Global functions */
void DebugMessage(int level, const char *message, ...)
{
char msgbuf[1024];
va_list args;
if (l_DebugCallback == NULL)
return;
va_start(args, message);
vsprintf(msgbuf, message, args);
(*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
va_end(args);
}
#pragma region (De-)Initialization
/* Mupen64Plus plugin functions */
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
void (*DebugCallback)(void *, int, const char *))
{
ptr_CoreGetAPIVersions CoreAPIVersionFunc;
int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
if (l_PluginInit)
return M64ERR_ALREADY_INIT;
/* first thing is to set the callback function for debug info */
l_DebugCallback = DebugCallback;
l_DebugCallContext = Context;
/* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
if (CoreAPIVersionFunc == NULL)
{
DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
return M64ERR_INCOMPATIBLE;
}
(*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
{
DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
return M64ERR_INCOMPATIBLE;
}
/* reset controllers */
memset(controller, 0, sizeof(controller));
l_PluginInit = 1;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginShutdown(void)
{
if (!l_PluginInit)
return M64ERR_NOT_INIT;
/* reset some local variables */
l_DebugCallback = NULL;
l_DebugCallContext = NULL;
l_PluginInit = 0;
memset(controller, 0, sizeof(controller));
return M64ERR_SUCCESS;
}
/******************************************************************
Function: InitiateControllers
Purpose: This function initialises how each of the controllers
should be handled.
input: - The handle to the main window.
- A controller structure that needs to be filled for
the emulator to know how to handle each controller.
output: none
*******************************************************************/
EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
{
int i;
memset( controller, 0, sizeof(controller) );
for( i = 0; i < 4; i++ )
{
controller[i].control = ControlInfo.Controls + i;
controller[i].control->Plugin = PLUGIN_MEMPAK;
controller[i].control->Present = 1;
controller[i].control->RawData = 0;
}
DebugMessage(M64MSG_INFO, "%s version %i.%i.%i initialized.", PLUGIN_NAME, VERSION_PRINTF_SPLIT(PLUGIN_VERSION));
}
/******************************************************************
Function: RomClosed
Purpose: This function is called when a rom is closed.
input: none
output: none
*******************************************************************/
EXPORT void CALL RomClosed(void)
{
romopen = 0;
}
/******************************************************************
Function: RomOpen
Purpose: This function is called when a rom is open. (from the
emulation thread)
input: none
output: none
*******************************************************************/
EXPORT int CALL RomOpen(void)
{
romopen = 1;
return 1;
}
#pragma endregion
EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
{
/* set version info */
if (PluginType != NULL)
*PluginType = M64PLUGIN_INPUT;
if (PluginVersion != NULL)
*PluginVersion = PLUGIN_VERSION;
if (APIVersion != NULL)
*APIVersion = INPUT_PLUGIN_API_VERSION;
if (PluginNamePtr != NULL)
*PluginNamePtr = PLUGIN_NAME;
if (Capabilities != NULL)
{
*Capabilities = 0;
}
return M64ERR_SUCCESS;
}
#pragma region Raw read and write
/* ----------------------------------------------------------------------
-------------------------- Controller CRC ----------------------------
---------------------------------------------------------------------- */
static unsigned char DataCRC( unsigned char *Data, int iLenght )
{
unsigned char Remainder = Data[0];
int iByte = 1;
unsigned char bBit = 0;
while( iByte <= iLenght )
{
int HighBit = ((Remainder & 0x80) != 0);
Remainder = Remainder << 1;
Remainder += ( iByte < iLenght && Data[iByte] & (0x80 >> bBit )) ? 1 : 0;
Remainder ^= (HighBit) ? 0x85 : 0;
bBit++;
iByte += bBit/8;
bBit %= 8;
}
return Remainder;
}
/******************************************************************
Function: ControllerCommand
Purpose: To process the raw data that has just been sent to a
specific controller.
input: - Controller Number (0 to 3) and -1 signalling end of
processing the pif ram.
- Pointer of data to be processed.
output: none
note: This function is only needed if the DLL is allowing raw
data, or the plugin is set to raw
the data that is being processed looks like this:
initilize controller: 01 03 00 FF FF FF
read controller: 01 04 01 FF FF FF FF
*******************************************************************/
EXPORT void CALL ControllerCommand(int Control, unsigned char *Command)
{
if( Control == -1)
return;
}
/* ----------------------------------------------------------------------
----------- Handles raw data read from a rumble pak -------------
----------- Data read at address C01B is 32x the status -------------
----------- Data read at address 8001 is 32x 0x80 -------------
---------------------------------------------------------------------- */
void DoRumblePakRead(int Control, unsigned char* Command)
{
if(Command[3] == 0x80 && Command[4] == 0x01)
memset(Command+5, 0x80, 32);
else if(Command[3] == 0xC0 && Command[4] == 0x1B)
memset(Command+5, controller[Control].rumbling, 32);
else
memset(Command+5, 0x00, 32);
}
/* ----------------------------------------------------------------------
----------- Writes data to rumble pak -------------
----------- If writes 0x01 to address 0xC01B rumble on -------------
----------- If writes 0x00 to address 0xC01B rumble off -------------
---------------------------------------------------------------------- */
void DoRumblePakWrite(int Control, unsigned char* Command)
{
if(Command[3] == 0xC0 && Command[4] == 0x1B)
{
controller[Control].rumbling = Command[5];
if(l_setrumbleCallback != NULL)
l_setrumbleCallback(Control, Command[5]);
}
}
/* ----------------------------------------------------------------------
----------- Does a raw read of a controller pak -------------
----------- Currently only handles rumble paks -------------
---------------------------------------------------------------------- */
void DoRawRead(int Control, unsigned char* Command)
{
switch(controller[Control].control->Plugin)
{
case PLUGIN_RUMBLE_PAK:
DoRumblePakRead(Control, Command);
break;
case PLUGIN_NONE:
case PLUGIN_RAW:
case PLUGIN_MEMPAK:
case PLUGIN_TRANSFER_PAK:
default:
break;
}
}
/* ----------------------------------------------------------------------
----------- Handles raw write to the controller pak -------------
----------- Currently only handles rumble paks -------------
---------------------------------------------------------------------- */
void DoRawWrite(int Control, unsigned char* Command)
{
switch(controller[Control].control->Plugin)
{
case PLUGIN_RUMBLE_PAK:
DoRumblePakWrite(Control, Command);
break;
case PLUGIN_NONE:
case PLUGIN_RAW:
case PLUGIN_MEMPAK:
case PLUGIN_TRANSFER_PAK:
default:
break;
}
}
/******************************************************************
Function: ReadController
Purpose: To process the raw data in the pif ram that is about to
be read.
input: - Controller Number (0 to 3) and -1 signalling end of
processing the pif ram.
- Pointer of data to be processed.
output: none
note: This function is only needed if the DLL is allowing raw
data.
*******************************************************************/
EXPORT void CALL ReadController(int Control, unsigned char *Command)
{
unsigned char * Data = Command + 5;
int value;
if(Control == -1)
return;
switch(Command[2])
{
case RD_RESETCONTROLLER:
case RD_GETSTATUS:
Command[3] = RD_GAMEPAD | RD_ABSOLUTE;
Command[4] = RD_NOEEPROM;
Command[5] = controller[Control].control->Plugin != PLUGIN_NONE;
break;
case RD_READKEYS:
value = l_inputCallback(Control);
*((int*)(Command+3)) = value;
break;
case RD_READPAK:
DoRawRead(Control, Command);
Data[32] = DataCRC(Data, 32);
break;
case RD_WRITEPAK:
DoRawWrite(Control, Command);
Data[32] = DataCRC(Data, 32);
break;
case RD_READEEPROM:
case RD_WRITEEPROM:
default:
break;
}
}
#pragma endregion
#pragma region Useless stubs
/* ----------------------------------------------------------------------
-------------------- This functions are not used --------------------
-------------------- Plugin api just expects them --------------------
-------------------- to exist --------------------
---------------------------------------------------------------------- */
/******************************************************************
Function: SDL_KeyDown
Purpose: To pass the SDL_KeyDown message from the emulator to the
plugin.
input: keymod and keysym of the SDL_KEYDOWN message.
output: none
*******************************************************************/
EXPORT void CALL SDL_KeyDown(int keymod, int keysym)
{
}
/******************************************************************
Function: SDL_KeyUp
Purpose: To pass the SDL_KeyUp message from the emulator to the
plugin.
input: keymod and keysym of the SDL_KEYUP message.
output: none
*******************************************************************/
EXPORT void CALL SDL_KeyUp(int keymod, int keysym)
{
}
#pragma endregion
/******************************************************************
Function: GetKeys
Purpose: To get the current state of the controllers buttons.
input: - Controller Number (0 to 3)
- A pointer to a BUTTONS structure to be filled with
the controller state.
output: none
*******************************************************************/
EXPORT void CALL GetKeys( int Control, BUTTONS *Keys )
{
Keys->Value = (*l_inputCallback)(Control);
}
/* ----------------------------------------------------------------------
----------- Sets callback to retrieve button and axis data -----------
---------------------------------------------------------------------- */
EXPORT void CALL SetInputCallback(int (*inputCallback)(int i))
{
l_inputCallback = inputCallback;
}
/* ----------------------------------------------------------------------
----------- Sets a callback to set rumble on and off -------------
---------------------------------------------------------------------- */
EXPORT void CALL SetRumbleCallback(void (*rumbleCallback)(int Control, int on))
{
}
/* ----------------------------------------------------------------------
----------- Sets the type of the controller pak -------------
----------- Possible values for type: -------------
----------- 1 - No pak inserted -------------
----------- 2 - Memory card -------------
----------- 3 - Rumble pak (no default implementation) -------------
----------- 4 - Transfer pak (no default implementation) -------------
----------- 5 - Raw data -------------
---------------------------------------------------------------------- */
EXPORT void CALL SetControllerPakType(int idx, int type)
{
controller[idx].control->Plugin = type;
controller[idx].control->RawData = (type != PLUGIN_MEMPAK);
}
/* ----------------------------------------------------------------------
----------- Sets if a controller is connected -------------
---------------------------------------------------------------------- */
EXPORT void CALL SetControllerConnected(int idx, int connected)
{
controller[idx].control->Present = connected;
}