491 lines
18 KiB
C
491 lines
18 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Mupen64plus-input-sdl - plugin.c *
|
|
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
|
* 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>
|
|
|
|
/* definitions of pointers to Core config functions */
|
|
ptr_ConfigOpenSection ConfigOpenSection = NULL;
|
|
ptr_ConfigDeleteSection ConfigDeleteSection = NULL;
|
|
ptr_ConfigSetParameter ConfigSetParameter = NULL;
|
|
ptr_ConfigGetParameter ConfigGetParameter = NULL;
|
|
ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
|
|
ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL;
|
|
ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL;
|
|
ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL;
|
|
ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
|
|
ptr_ConfigGetParamInt ConfigGetParamInt = NULL;
|
|
ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL;
|
|
ptr_ConfigGetParamBool ConfigGetParamBool = NULL;
|
|
ptr_ConfigGetParamString ConfigGetParamString = NULL;
|
|
|
|
ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL;
|
|
ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath = NULL;
|
|
ptr_ConfigGetUserDataPath ConfigGetUserDataPath = NULL;
|
|
ptr_ConfigGetUserCachePath ConfigGetUserCachePath = NULL;
|
|
|
|
/* 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;
|
|
|
|
static void (*l_inputCallback)() = NULL;
|
|
|
|
static unsigned short button_bits[] = {
|
|
0x0001, // R_DPAD
|
|
0x0002, // L_DPAD
|
|
0x0004, // D_DPAD
|
|
0x0008, // U_DPAD
|
|
0x0010, // START_BUTTON
|
|
0x0020, // Z_TRIG
|
|
0x0040, // B_BUTTON
|
|
0x0080, // A_BUTTON
|
|
0x0100, // R_CBUTTON
|
|
0x0200, // L_CBUTTON
|
|
0x0400, // D_CBUTTON
|
|
0x0800, // U_CBUTTON
|
|
0x1000, // R_TRIG
|
|
0x2000, // L_TRIG
|
|
0x4000, // Mempak switch
|
|
0x8000 // Rumblepak switch
|
|
};
|
|
|
|
static int romopen = 0; // is a rom opened
|
|
|
|
static unsigned char myKeyState[SDL_NUM_SCANCODES];
|
|
|
|
BUTTONS controllers[4];
|
|
|
|
#ifdef __linux__
|
|
static struct ff_effect ffeffect[3];
|
|
static struct ff_effect ffstrong[3];
|
|
static struct ff_effect ffweak[3];
|
|
#endif //__linux__
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static CONTROL temp_core_controlinfo[4];
|
|
|
|
/* Mupen64Plus plugin functions */
|
|
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
|
|
void (*DebugCallback)(void *, int, const char *))
|
|
{
|
|
ptr_CoreGetAPIVersions CoreAPIVersionFunc;
|
|
|
|
int i, 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;
|
|
}
|
|
|
|
/* Get the core config function pointers from the library handle */
|
|
ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
|
|
ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
|
|
ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
|
|
ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
|
|
ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
|
|
ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
|
|
ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
|
|
ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
|
|
ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
|
|
ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
|
|
ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
|
|
ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");
|
|
|
|
ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetSharedDataFilepath");
|
|
ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserConfigPath");
|
|
ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserDataPath");
|
|
ConfigGetUserCachePath = (ptr_ConfigGetUserCachePath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserCachePath");
|
|
|
|
if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter ||
|
|
!ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
|
|
!ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString ||
|
|
!ConfigGetSharedDataFilepath || !ConfigGetUserConfigPath || !ConfigGetUserDataPath || !ConfigGetUserCachePath)
|
|
{
|
|
DebugMessage(M64MSG_ERROR, "Couldn't connect to Core configuration functions");
|
|
return M64ERR_INCOMPATIBLE;
|
|
}
|
|
|
|
/* reset controllers */
|
|
memset(controller, 0, sizeof(SController) * 4);
|
|
for (i = 0; i < SDL_NUM_SCANCODES; i++)
|
|
{
|
|
myKeyState[i] = 0;
|
|
}
|
|
/* set CONTROL struct pointers to the temporary static array */
|
|
/* this small struct is used to tell the core whether each controller is plugged in, and what type of pak is connected */
|
|
/* we only need it so that we can call load_configuration below, to auto-config for a GUI front-end */
|
|
for (i = 0; i < 4; i++)
|
|
controller[i].control = temp_core_controlinfo + i;
|
|
|
|
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;
|
|
return M64ERR_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
unsigned char *Data = &Command[5];
|
|
|
|
if (Control == -1)
|
|
return;
|
|
|
|
switch (Command[2])
|
|
{
|
|
case RD_GETSTATUS:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Get status");
|
|
#endif
|
|
break;
|
|
case RD_READKEYS:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Read keys");
|
|
#endif
|
|
break;
|
|
case RD_READPAK:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Read pak");
|
|
#endif
|
|
if (controller[Control].control->Plugin == PLUGIN_RAW)
|
|
{
|
|
unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
|
|
|
|
if(( dwAddress >= 0x8000 ) && ( dwAddress < 0x9000 ) )
|
|
memset( Data, 0x80, 32 );
|
|
else
|
|
memset( Data, 0x00, 32 );
|
|
|
|
Data[32] = DataCRC( Data, 32 );
|
|
}
|
|
break;
|
|
case RD_WRITEPAK:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Write pak");
|
|
#endif
|
|
if (controller[Control].control->Plugin == PLUGIN_RAW)
|
|
{
|
|
unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
|
|
if (dwAddress == PAK_IO_RUMBLE && *Data)
|
|
DebugMessage(M64MSG_VERBOSE, "Triggering rumble pack.");
|
|
#ifdef __linux__
|
|
struct input_event play;
|
|
if( dwAddress == PAK_IO_RUMBLE && controller[Control].event_joystick != 0)
|
|
{
|
|
if( *Data )
|
|
{
|
|
play.type = EV_FF;
|
|
play.code = ffeffect[Control].id;
|
|
play.value = 1;
|
|
|
|
if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
|
|
perror("Error starting rumble effect");
|
|
|
|
}
|
|
else
|
|
{
|
|
play.type = EV_FF;
|
|
play.code = ffeffect[Control].id;
|
|
play.value = 0;
|
|
|
|
if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
|
|
perror("Error stopping rumble effect");
|
|
}
|
|
}
|
|
#endif //__linux__
|
|
Data[32] = DataCRC( Data, 32 );
|
|
}
|
|
break;
|
|
case RD_RESETCONTROLLER:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Reset controller");
|
|
#endif
|
|
break;
|
|
case RD_READEEPROM:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Read eeprom");
|
|
#endif
|
|
break;
|
|
case RD_WRITEEPROM:
|
|
#ifdef _DEBUG
|
|
DebugMessage(M64MSG_INFO, "Write eeprom");
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
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 )
|
|
{
|
|
(*l_inputCallback)();
|
|
(*Keys).Value = controllers[Control].Value;
|
|
}
|
|
|
|
/******************************************************************
|
|
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( SController ) * 4 );
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
controller[i].control = ControlInfo.Controls + i;
|
|
controller[i].control->Plugin = PLUGIN_MEMPAK;
|
|
controller[i].control->Present = 1;
|
|
}
|
|
|
|
DebugMessage(M64MSG_INFO, "%s version %i.%i.%i initialized.", PLUGIN_NAME, VERSION_PRINTF_SPLIT(PLUGIN_VERSION));
|
|
}
|
|
|
|
/******************************************************************
|
|
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)
|
|
{
|
|
#ifdef _DEBUG
|
|
if (Command != NULL)
|
|
DebugMessage(M64MSG_INFO, "Raw Read (cont=%d): %02X %02X %02X %02X %02X %02X", Control,
|
|
Command[0], Command[1], Command[2], Command[3], Command[4], Command[5]);
|
|
#endif
|
|
}
|
|
|
|
/******************************************************************
|
|
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;
|
|
}
|
|
|
|
/******************************************************************
|
|
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)
|
|
{
|
|
}
|
|
|
|
EXPORT void CALL SetKeys(int num, int keys, char X, char Y)
|
|
{
|
|
controllers[num].R_DPAD = (keys >> 0) & 0x01;
|
|
controllers[num].L_DPAD = (keys >> 1) & 0x01;
|
|
controllers[num].D_DPAD = (keys >> 2) & 0x01;
|
|
controllers[num].U_DPAD = (keys >> 3) & 0x01;
|
|
controllers[num].START_BUTTON = (keys >> 4) & 0x01;
|
|
controllers[num].Z_TRIG = (keys >> 5) & 0x01;
|
|
controllers[num].B_BUTTON = (keys >> 6) & 0x01;
|
|
controllers[num].A_BUTTON = (keys >> 7) & 0x01;
|
|
controllers[num].R_CBUTTON = (keys >> 8) & 0x01;
|
|
controllers[num].L_CBUTTON = (keys >> 9) & 0x01;
|
|
controllers[num].D_CBUTTON = (keys >> 10) & 0x01;
|
|
controllers[num].U_CBUTTON = (keys >> 11) & 0x01;
|
|
controllers[num].R_TRIG = (keys >> 12) & 0x01;
|
|
controllers[num].L_TRIG = (keys >> 13) & 0x01;
|
|
|
|
controllers[num].X_AXIS = X;
|
|
controllers[num].Y_AXIS = Y;
|
|
}
|
|
|
|
EXPORT void CALL SetInputCallback(void (*inputCallback)())
|
|
{
|
|
l_inputCallback = inputCallback;
|
|
} |