/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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 #include #include #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 /* 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 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]; int LagFlag = 1; #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 ) { (*Keys).Value = controllers[Control].Value; LagFlag = 0; } /****************************************************************** 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; for( i = 0; i < 4; i++ ) { controller[i].control->Plugin = PLUGIN_MEMPAK; } controller[0].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 ResetLagFlag() { LagFlag = 1; } EXPORT int CALL CheckLagFlag() { return LagFlag; }