450 lines
15 KiB
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;
|
|
} |