| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
| 2 | * Mupen64plus - plugin.c * |
| 3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
| 4 | * Copyright (C) 2002 Hacktarux * |
| 5 | * Copyright (C) 2009 Richard Goedeken * |
| 6 | * * |
| 7 | * This program is free software; you can redistribute it and/or modify * |
| 8 | * it under the terms of the GNU General Public License as published by * |
| 9 | * the Free Software Foundation; either version 2 of the License, or * |
| 10 | * (at your option) any later version. * |
| 11 | * * |
| 12 | * This program is distributed in the hope that it will be useful, * |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 15 | * GNU General Public License for more details. * |
| 16 | * * |
| 17 | * You should have received a copy of the GNU General Public License * |
| 18 | * along with this program; if not, write to the * |
| 19 | * Free Software Foundation, Inc., * |
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
| 21 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 22 | |
| 23 | #include <stdlib.h> |
| 24 | |
| 25 | #include "plugin.h" |
| 26 | |
| 27 | #include "api/callbacks.h" |
| 28 | #include "api/m64p_common.h" |
| 29 | #include "api/m64p_plugin.h" |
| 30 | #include "api/m64p_types.h" |
| 31 | |
| 32 | #include "main/rom.h" |
| 33 | #include "main/version.h" |
| 34 | #include "memory/memory.h" |
| 35 | |
| 36 | #include "osal/dynamiclib.h" |
| 37 | |
| 38 | #include "dummy_audio.h" |
| 39 | #include "dummy_video.h" |
| 40 | #include "dummy_input.h" |
| 41 | #include "dummy_rsp.h" |
| 42 | |
| 43 | CONTROL Controls[4]; |
| 44 | |
| 45 | /* global function pointers - initialized on core startup */ |
| 46 | gfx_plugin_functions gfx; |
| 47 | audio_plugin_functions audio; |
| 48 | input_plugin_functions input; |
| 49 | rsp_plugin_functions rsp; |
| 50 | |
| 51 | /* local data structures and functions */ |
| 52 | static const gfx_plugin_functions dummy_gfx = { |
| 53 | dummyvideo_PluginGetVersion, |
| 54 | dummyvideo_ChangeWindow, |
| 55 | dummyvideo_InitiateGFX, |
| 56 | dummyvideo_MoveScreen, |
| 57 | dummyvideo_ProcessDList, |
| 58 | dummyvideo_ProcessRDPList, |
| 59 | dummyvideo_RomClosed, |
| 60 | dummyvideo_RomOpen, |
| 61 | dummyvideo_ShowCFB, |
| 62 | dummyvideo_UpdateScreen, |
| 63 | dummyvideo_ViStatusChanged, |
| 64 | dummyvideo_ViWidthChanged, |
| 65 | dummyvideo_ReadScreen2, |
| 66 | dummyvideo_SetRenderingCallback, |
| 67 | dummyvideo_ResizeVideoOutput, |
| 68 | dummyvideo_FBRead, |
| 69 | dummyvideo_FBWrite, |
| 70 | dummyvideo_FBGetFrameBufferInfo |
| 71 | }; |
| 72 | |
| 73 | static const audio_plugin_functions dummy_audio = { |
| 74 | dummyaudio_PluginGetVersion, |
| 75 | dummyaudio_AiDacrateChanged, |
| 76 | dummyaudio_AiLenChanged, |
| 77 | dummyaudio_InitiateAudio, |
| 78 | dummyaudio_ProcessAList, |
| 79 | dummyaudio_RomClosed, |
| 80 | dummyaudio_RomOpen, |
| 81 | dummyaudio_SetSpeedFactor, |
| 82 | dummyaudio_VolumeUp, |
| 83 | dummyaudio_VolumeDown, |
| 84 | dummyaudio_VolumeGetLevel, |
| 85 | dummyaudio_VolumeSetLevel, |
| 86 | dummyaudio_VolumeMute, |
| 87 | dummyaudio_VolumeGetString |
| 88 | }; |
| 89 | |
| 90 | static const input_plugin_functions dummy_input = { |
| 91 | dummyinput_PluginGetVersion, |
| 92 | dummyinput_ControllerCommand, |
| 93 | dummyinput_GetKeys, |
| 94 | dummyinput_InitiateControllers, |
| 95 | dummyinput_ReadController, |
| 96 | dummyinput_RomClosed, |
| 97 | dummyinput_RomOpen, |
| 98 | dummyinput_SDL_KeyDown, |
| 99 | dummyinput_SDL_KeyUp |
| 100 | }; |
| 101 | |
| 102 | static const rsp_plugin_functions dummy_rsp = { |
| 103 | dummyrsp_PluginGetVersion, |
| 104 | dummyrsp_DoRspCycles, |
| 105 | dummyrsp_InitiateRSP, |
| 106 | dummyrsp_RomClosed |
| 107 | }; |
| 108 | |
| 109 | static GFX_INFO gfx_info; |
| 110 | static AUDIO_INFO audio_info; |
| 111 | static CONTROL_INFO control_info; |
| 112 | static RSP_INFO rsp_info; |
| 113 | |
| 114 | static int l_RspAttached = 0; |
| 115 | static int l_InputAttached = 0; |
| 116 | static int l_AudioAttached = 0; |
| 117 | static int l_GfxAttached = 0; |
| 118 | |
| 119 | static unsigned int dummy; |
| 120 | |
| 121 | /* local functions */ |
| 122 | static void EmptyFunc(void) |
| 123 | { |
| 124 | } |
| 125 | |
| 126 | // Handy macro to avoid code bloat when loading symbols |
| 127 | #define GET_FUNC(type, field, name) \ |
| 128 | ((field = (type)osal_dynlib_getproc(plugin_handle, name)) != NULL) |
| 129 | |
| 130 | // code to handle backwards-compatibility to video plugins with API_VERSION < 02.1.0. This API version introduced a boolean |
| 131 | // flag in the rendering callback, which told the core whether or not the current screen has been freshly redrawn since the |
| 132 | // last time the callback was called. |
| 133 | static void (*l_mainRenderCallback)(int) = NULL; |
| 134 | static ptr_SetRenderingCallback l_old1SetRenderingCallback = NULL; |
| 135 | |
| 136 | static void backcompat_videoRenderCallback(int unused) // this function will be called by the video plugin as the render callback |
| 137 | { |
| 138 | if (l_mainRenderCallback != NULL) |
| 139 | l_mainRenderCallback(1); // assume screen is always freshly redrawn (otherwise screenshots won't work w/ OSD enabled) |
| 140 | } |
| 141 | |
| 142 | static void backcompat_setRenderCallbackIntercept(void (*callback)(int)) |
| 143 | { |
| 144 | l_mainRenderCallback = callback; |
| 145 | } |
| 146 | |
| 147 | static void plugin_disconnect_gfx(void) |
| 148 | { |
| 149 | gfx = dummy_gfx; |
| 150 | l_GfxAttached = 0; |
| 151 | l_mainRenderCallback = NULL; |
| 152 | } |
| 153 | |
| 154 | static m64p_error plugin_connect_gfx(m64p_dynlib_handle plugin_handle) |
| 155 | { |
| 156 | /* attach the Video plugin function pointers */ |
| 157 | if (plugin_handle != NULL) |
| 158 | { |
| 159 | m64p_plugin_type PluginType; |
| 160 | int PluginVersion, APIVersion; |
| 161 | |
| 162 | if (l_GfxAttached) |
| 163 | return M64ERR_INVALID_STATE; |
| 164 | |
| 165 | /* set function pointers for required functions */ |
| 166 | if (!GET_FUNC(ptr_PluginGetVersion, gfx.getVersion, "PluginGetVersion") || |
| 167 | !GET_FUNC(ptr_ChangeWindow, gfx.changeWindow, "ChangeWindow") || |
| 168 | !GET_FUNC(ptr_InitiateGFX, gfx.initiateGFX, "InitiateGFX") || |
| 169 | !GET_FUNC(ptr_MoveScreen, gfx.moveScreen, "MoveScreen") || |
| 170 | !GET_FUNC(ptr_ProcessDList, gfx.processDList, "ProcessDList") || |
| 171 | !GET_FUNC(ptr_ProcessRDPList, gfx.processRDPList, "ProcessRDPList") || |
| 172 | !GET_FUNC(ptr_RomClosed, gfx.romClosed, "RomClosed") || |
| 173 | !GET_FUNC(ptr_RomOpen, gfx.romOpen, "RomOpen") || |
| 174 | !GET_FUNC(ptr_ShowCFB, gfx.showCFB, "ShowCFB") || |
| 175 | !GET_FUNC(ptr_UpdateScreen, gfx.updateScreen, "UpdateScreen") || |
| 176 | !GET_FUNC(ptr_ViStatusChanged, gfx.viStatusChanged, "ViStatusChanged") || |
| 177 | !GET_FUNC(ptr_ViWidthChanged, gfx.viWidthChanged, "ViWidthChanged") || |
| 178 | !GET_FUNC(ptr_ReadScreen2, gfx.readScreen, "ReadScreen2") || |
| 179 | !GET_FUNC(ptr_SetRenderingCallback, gfx.setRenderingCallback, "SetRenderingCallback") || |
| 180 | !GET_FUNC(ptr_FBRead, gfx.fBRead, "FBRead") || |
| 181 | !GET_FUNC(ptr_FBWrite, gfx.fBWrite, "FBWrite") || |
| 182 | !GET_FUNC(ptr_FBGetFrameBufferInfo, gfx.fBGetFrameBufferInfo, "FBGetFrameBufferInfo")) |
| 183 | { |
| 184 | DebugMessage(M64MSG_ERROR, "broken Video plugin; function(s) not found."); |
| 185 | plugin_disconnect_gfx(); |
| 186 | return M64ERR_INPUT_INVALID; |
| 187 | } |
| 188 | |
| 189 | /* set function pointers for optional functions */ |
| 190 | gfx.resizeVideoOutput = (ptr_ResizeVideoOutput) osal_dynlib_getproc(plugin_handle, "ResizeVideoOutput"); |
| 191 | |
| 192 | /* check the version info */ |
| 193 | (*gfx.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
| 194 | if (PluginType != M64PLUGIN_GFX || (APIVersion & 0xffff0000) != (GFX_API_VERSION & 0xffff0000)) |
| 195 | { |
| 196 | DebugMessage(M64MSG_ERROR, "incompatible Video plugin"); |
| 197 | plugin_disconnect_gfx(); |
| 198 | return M64ERR_INCOMPATIBLE; |
| 199 | } |
| 200 | |
| 201 | /* handle backwards-compatibility */ |
| 202 | if (APIVersion < 0x020100) |
| 203 | { |
| 204 | DebugMessage(M64MSG_WARNING, "Fallback for Video plugin API (%02i.%02i.%02i) < 2.1.0. Screenshots may contain On Screen Display text", VERSION_PRINTF_SPLIT(APIVersion)); |
| 205 | // tell the video plugin to make its rendering callback to me (it's old, and doesn't have the bScreenRedrawn flag) |
| 206 | gfx.setRenderingCallback(backcompat_videoRenderCallback); |
| 207 | l_old1SetRenderingCallback = gfx.setRenderingCallback; // save this just for future use |
| 208 | gfx.setRenderingCallback = (ptr_SetRenderingCallback) backcompat_setRenderCallbackIntercept; |
| 209 | } |
| 210 | if (APIVersion < 0x20200 || gfx.resizeVideoOutput == NULL) |
| 211 | { |
| 212 | DebugMessage(M64MSG_WARNING, "Fallback for Video plugin API (%02i.%02i.%02i) < 2.2.0. Resizable video will not work", VERSION_PRINTF_SPLIT(APIVersion)); |
| 213 | gfx.resizeVideoOutput = dummyvideo_ResizeVideoOutput; |
| 214 | } |
| 215 | |
| 216 | l_GfxAttached = 1; |
| 217 | } |
| 218 | else |
| 219 | plugin_disconnect_gfx(); |
| 220 | |
| 221 | return M64ERR_SUCCESS; |
| 222 | } |
| 223 | |
| 224 | static m64p_error plugin_start_gfx(void) |
| 225 | { |
| 226 | /* fill in the GFX_INFO data structure */ |
| 227 | gfx_info.HEADER = (unsigned char *) rom; |
| 228 | gfx_info.RDRAM = (unsigned char *) rdram; |
| 229 | gfx_info.DMEM = (unsigned char *) SP_DMEM; |
| 230 | gfx_info.IMEM = (unsigned char *) SP_IMEM; |
| 231 | gfx_info.MI_INTR_REG = &(MI_register.mi_intr_reg); |
| 232 | gfx_info.DPC_START_REG = &(dpc_register.dpc_start); |
| 233 | gfx_info.DPC_END_REG = &(dpc_register.dpc_end); |
| 234 | gfx_info.DPC_CURRENT_REG = &(dpc_register.dpc_current); |
| 235 | gfx_info.DPC_STATUS_REG = &(dpc_register.dpc_status); |
| 236 | gfx_info.DPC_CLOCK_REG = &(dpc_register.dpc_clock); |
| 237 | gfx_info.DPC_BUFBUSY_REG = &(dpc_register.dpc_bufbusy); |
| 238 | gfx_info.DPC_PIPEBUSY_REG = &(dpc_register.dpc_pipebusy); |
| 239 | gfx_info.DPC_TMEM_REG = &(dpc_register.dpc_tmem); |
| 240 | gfx_info.VI_STATUS_REG = &(vi_register.vi_status); |
| 241 | gfx_info.VI_ORIGIN_REG = &(vi_register.vi_origin); |
| 242 | gfx_info.VI_WIDTH_REG = &(vi_register.vi_width); |
| 243 | gfx_info.VI_INTR_REG = &(vi_register.vi_v_intr); |
| 244 | gfx_info.VI_V_CURRENT_LINE_REG = &(vi_register.vi_current); |
| 245 | gfx_info.VI_TIMING_REG = &(vi_register.vi_burst); |
| 246 | gfx_info.VI_V_SYNC_REG = &(vi_register.vi_v_sync); |
| 247 | gfx_info.VI_H_SYNC_REG = &(vi_register.vi_h_sync); |
| 248 | gfx_info.VI_LEAP_REG = &(vi_register.vi_leap); |
| 249 | gfx_info.VI_H_START_REG = &(vi_register.vi_h_start); |
| 250 | gfx_info.VI_V_START_REG = &(vi_register.vi_v_start); |
| 251 | gfx_info.VI_V_BURST_REG = &(vi_register.vi_v_burst); |
| 252 | gfx_info.VI_X_SCALE_REG = &(vi_register.vi_x_scale); |
| 253 | gfx_info.VI_Y_SCALE_REG = &(vi_register.vi_y_scale); |
| 254 | gfx_info.CheckInterrupts = EmptyFunc; |
| 255 | |
| 256 | /* call the audio plugin */ |
| 257 | if (!gfx.initiateGFX(gfx_info)) |
| 258 | return M64ERR_PLUGIN_FAIL; |
| 259 | |
| 260 | return M64ERR_SUCCESS; |
| 261 | } |
| 262 | |
| 263 | static void plugin_disconnect_audio(void) |
| 264 | { |
| 265 | audio = dummy_audio; |
| 266 | l_AudioAttached = 0; |
| 267 | } |
| 268 | |
| 269 | static m64p_error plugin_connect_audio(m64p_dynlib_handle plugin_handle) |
| 270 | { |
| 271 | /* attach the Audio plugin function pointers */ |
| 272 | if (plugin_handle != NULL) |
| 273 | { |
| 274 | m64p_plugin_type PluginType; |
| 275 | int PluginVersion, APIVersion; |
| 276 | |
| 277 | if (l_AudioAttached) |
| 278 | return M64ERR_INVALID_STATE; |
| 279 | |
| 280 | if (!GET_FUNC(ptr_PluginGetVersion, audio.getVersion, "PluginGetVersion") || |
| 281 | !GET_FUNC(ptr_AiDacrateChanged, audio.aiDacrateChanged, "AiDacrateChanged") || |
| 282 | !GET_FUNC(ptr_AiLenChanged, audio.aiLenChanged, "AiLenChanged") || |
| 283 | !GET_FUNC(ptr_InitiateAudio, audio.initiateAudio, "InitiateAudio") || |
| 284 | !GET_FUNC(ptr_ProcessAList, audio.processAList, "ProcessAList") || |
| 285 | !GET_FUNC(ptr_RomOpen, audio.romOpen, "RomOpen") || |
| 286 | !GET_FUNC(ptr_RomClosed, audio.romClosed, "RomClosed") || |
| 287 | !GET_FUNC(ptr_SetSpeedFactor, audio.setSpeedFactor, "SetSpeedFactor") || |
| 288 | !GET_FUNC(ptr_VolumeUp, audio.volumeUp, "VolumeUp") || |
| 289 | !GET_FUNC(ptr_VolumeDown, audio.volumeDown, "VolumeDown") || |
| 290 | !GET_FUNC(ptr_VolumeGetLevel, audio.volumeGetLevel, "VolumeGetLevel") || |
| 291 | !GET_FUNC(ptr_VolumeSetLevel, audio.volumeSetLevel, "VolumeSetLevel") || |
| 292 | !GET_FUNC(ptr_VolumeMute, audio.volumeMute, "VolumeMute") || |
| 293 | !GET_FUNC(ptr_VolumeGetString, audio.volumeGetString, "VolumeGetString")) |
| 294 | { |
| 295 | DebugMessage(M64MSG_ERROR, "broken Audio plugin; function(s) not found."); |
| 296 | plugin_disconnect_audio(); |
| 297 | return M64ERR_INPUT_INVALID; |
| 298 | } |
| 299 | |
| 300 | /* check the version info */ |
| 301 | (*audio.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
| 302 | if (PluginType != M64PLUGIN_AUDIO || (APIVersion & 0xffff0000) != (AUDIO_API_VERSION & 0xffff0000)) |
| 303 | { |
| 304 | DebugMessage(M64MSG_ERROR, "incompatible Audio plugin"); |
| 305 | plugin_disconnect_audio(); |
| 306 | return M64ERR_INCOMPATIBLE; |
| 307 | } |
| 308 | |
| 309 | l_AudioAttached = 1; |
| 310 | } |
| 311 | else |
| 312 | plugin_disconnect_audio(); |
| 313 | |
| 314 | return M64ERR_SUCCESS; |
| 315 | } |
| 316 | |
| 317 | static m64p_error plugin_start_audio(void) |
| 318 | { |
| 319 | /* fill in the AUDIO_INFO data structure */ |
| 320 | audio_info.RDRAM = (unsigned char *) rdram; |
| 321 | audio_info.DMEM = (unsigned char *) SP_DMEM; |
| 322 | audio_info.IMEM = (unsigned char *) SP_IMEM; |
| 323 | audio_info.MI_INTR_REG = &(MI_register.mi_intr_reg); |
| 324 | audio_info.AI_DRAM_ADDR_REG = &(ai_register.ai_dram_addr); |
| 325 | audio_info.AI_LEN_REG = &(ai_register.ai_len); |
| 326 | audio_info.AI_CONTROL_REG = &(ai_register.ai_control); |
| 327 | audio_info.AI_STATUS_REG = &dummy; |
| 328 | audio_info.AI_DACRATE_REG = &(ai_register.ai_dacrate); |
| 329 | audio_info.AI_BITRATE_REG = &(ai_register.ai_bitrate); |
| 330 | audio_info.CheckInterrupts = EmptyFunc; |
| 331 | |
| 332 | /* call the audio plugin */ |
| 333 | if (!audio.initiateAudio(audio_info)) |
| 334 | return M64ERR_PLUGIN_FAIL; |
| 335 | |
| 336 | return M64ERR_SUCCESS; |
| 337 | } |
| 338 | |
| 339 | static void plugin_disconnect_input(void) |
| 340 | { |
| 341 | input = dummy_input; |
| 342 | l_InputAttached = 0; |
| 343 | } |
| 344 | |
| 345 | static m64p_error plugin_connect_input(m64p_dynlib_handle plugin_handle) |
| 346 | { |
| 347 | /* attach the Input plugin function pointers */ |
| 348 | if (plugin_handle != NULL) |
| 349 | { |
| 350 | m64p_plugin_type PluginType; |
| 351 | int PluginVersion, APIVersion; |
| 352 | |
| 353 | if (l_InputAttached) |
| 354 | return M64ERR_INVALID_STATE; |
| 355 | |
| 356 | if (!GET_FUNC(ptr_PluginGetVersion, input.getVersion, "PluginGetVersion") || |
| 357 | !GET_FUNC(ptr_ControllerCommand, input.controllerCommand, "ControllerCommand") || |
| 358 | !GET_FUNC(ptr_GetKeys, input.getKeys, "GetKeys") || |
| 359 | !GET_FUNC(ptr_InitiateControllers, input.initiateControllers, "InitiateControllers") || |
| 360 | !GET_FUNC(ptr_ReadController, input.readController, "ReadController") || |
| 361 | !GET_FUNC(ptr_RomOpen, input.romOpen, "RomOpen") || |
| 362 | !GET_FUNC(ptr_RomClosed, input.romClosed, "RomClosed") || |
| 363 | !GET_FUNC(ptr_SDL_KeyDown, input.keyDown, "SDL_KeyDown") || |
| 364 | !GET_FUNC(ptr_SDL_KeyUp, input.keyUp, "SDL_KeyUp")) |
| 365 | { |
| 366 | DebugMessage(M64MSG_ERROR, "broken Input plugin; function(s) not found."); |
| 367 | plugin_disconnect_input(); |
| 368 | return M64ERR_INPUT_INVALID; |
| 369 | } |
| 370 | |
| 371 | /* check the version info */ |
| 372 | (*input.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
| 373 | if (PluginType != M64PLUGIN_INPUT || (APIVersion & 0xffff0000) != (INPUT_API_VERSION & 0xffff0000)) |
| 374 | { |
| 375 | DebugMessage(M64MSG_ERROR, "incompatible Input plugin"); |
| 376 | plugin_disconnect_input(); |
| 377 | return M64ERR_INCOMPATIBLE; |
| 378 | } |
| 379 | |
| 380 | l_InputAttached = 1; |
| 381 | } |
| 382 | else |
| 383 | plugin_disconnect_input(); |
| 384 | |
| 385 | return M64ERR_SUCCESS; |
| 386 | } |
| 387 | |
| 388 | static m64p_error plugin_start_input(void) |
| 389 | { |
| 390 | int i; |
| 391 | |
| 392 | /* fill in the CONTROL_INFO data structure */ |
| 393 | control_info.Controls = Controls; |
| 394 | for (i=0; i<4; i++) |
| 395 | { |
| 396 | Controls[i].Present = 0; |
| 397 | Controls[i].RawData = 0; |
| 398 | Controls[i].Plugin = PLUGIN_NONE; |
| 399 | } |
| 400 | |
| 401 | /* call the input plugin */ |
| 402 | input.initiateControllers(control_info); |
| 403 | |
| 404 | return M64ERR_SUCCESS; |
| 405 | } |
| 406 | |
| 407 | static void plugin_disconnect_rsp(void) |
| 408 | { |
| 409 | rsp = dummy_rsp; |
| 410 | l_RspAttached = 0; |
| 411 | } |
| 412 | |
| 413 | static m64p_error plugin_connect_rsp(m64p_dynlib_handle plugin_handle) |
| 414 | { |
| 415 | /* attach the RSP plugin function pointers */ |
| 416 | if (plugin_handle != NULL) |
| 417 | { |
| 418 | m64p_plugin_type PluginType; |
| 419 | int PluginVersion, APIVersion; |
| 420 | |
| 421 | if (l_RspAttached) |
| 422 | return M64ERR_INVALID_STATE; |
| 423 | |
| 424 | if (!GET_FUNC(ptr_PluginGetVersion, rsp.getVersion, "PluginGetVersion") || |
| 425 | !GET_FUNC(ptr_DoRspCycles, rsp.doRspCycles, "DoRspCycles") || |
| 426 | !GET_FUNC(ptr_InitiateRSP, rsp.initiateRSP, "InitiateRSP") || |
| 427 | !GET_FUNC(ptr_RomClosed, rsp.romClosed, "RomClosed")) |
| 428 | { |
| 429 | DebugMessage(M64MSG_ERROR, "broken RSP plugin; function(s) not found."); |
| 430 | plugin_disconnect_rsp(); |
| 431 | return M64ERR_INPUT_INVALID; |
| 432 | } |
| 433 | |
| 434 | /* check the version info */ |
| 435 | (*rsp.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
| 436 | if (PluginType != M64PLUGIN_RSP || (APIVersion & 0xffff0000) != (RSP_API_VERSION & 0xffff0000)) |
| 437 | { |
| 438 | DebugMessage(M64MSG_ERROR, "incompatible RSP plugin"); |
| 439 | plugin_disconnect_rsp(); |
| 440 | return M64ERR_INCOMPATIBLE; |
| 441 | } |
| 442 | |
| 443 | l_RspAttached = 1; |
| 444 | } |
| 445 | else |
| 446 | plugin_disconnect_rsp(); |
| 447 | |
| 448 | return M64ERR_SUCCESS; |
| 449 | } |
| 450 | |
| 451 | static m64p_error plugin_start_rsp(void) |
| 452 | { |
| 453 | /* fill in the RSP_INFO data structure */ |
| 454 | rsp_info.RDRAM = (unsigned char *) rdram; |
| 455 | rsp_info.DMEM = (unsigned char *) SP_DMEM; |
| 456 | rsp_info.IMEM = (unsigned char *) SP_IMEM; |
| 457 | rsp_info.MI_INTR_REG = &MI_register.mi_intr_reg; |
| 458 | rsp_info.SP_MEM_ADDR_REG = &sp_register.sp_mem_addr_reg; |
| 459 | rsp_info.SP_DRAM_ADDR_REG = &sp_register.sp_dram_addr_reg; |
| 460 | rsp_info.SP_RD_LEN_REG = &sp_register.sp_rd_len_reg; |
| 461 | rsp_info.SP_WR_LEN_REG = &sp_register.sp_wr_len_reg; |
| 462 | rsp_info.SP_STATUS_REG = &sp_register.sp_status_reg; |
| 463 | rsp_info.SP_DMA_FULL_REG = &sp_register.sp_dma_full_reg; |
| 464 | rsp_info.SP_DMA_BUSY_REG = &sp_register.sp_dma_busy_reg; |
| 465 | rsp_info.SP_PC_REG = &rsp_register.rsp_pc; |
| 466 | rsp_info.SP_SEMAPHORE_REG = &sp_register.sp_semaphore_reg; |
| 467 | rsp_info.DPC_START_REG = &dpc_register.dpc_start; |
| 468 | rsp_info.DPC_END_REG = &dpc_register.dpc_end; |
| 469 | rsp_info.DPC_CURRENT_REG = &dpc_register.dpc_current; |
| 470 | rsp_info.DPC_STATUS_REG = &dpc_register.dpc_status; |
| 471 | rsp_info.DPC_CLOCK_REG = &dpc_register.dpc_clock; |
| 472 | rsp_info.DPC_BUFBUSY_REG = &dpc_register.dpc_bufbusy; |
| 473 | rsp_info.DPC_PIPEBUSY_REG = &dpc_register.dpc_pipebusy; |
| 474 | rsp_info.DPC_TMEM_REG = &dpc_register.dpc_tmem; |
| 475 | rsp_info.CheckInterrupts = EmptyFunc; |
| 476 | rsp_info.ProcessDlistList = gfx.processDList; |
| 477 | rsp_info.ProcessAlistList = audio.processAList; |
| 478 | rsp_info.ProcessRdpList = gfx.processRDPList; |
| 479 | rsp_info.ShowCFB = gfx.showCFB; |
| 480 | |
| 481 | /* call the RSP plugin */ |
| 482 | rsp.initiateRSP(rsp_info, NULL); |
| 483 | |
| 484 | return M64ERR_SUCCESS; |
| 485 | } |
| 486 | |
| 487 | /* global functions */ |
| 488 | m64p_error plugin_connect(m64p_plugin_type type, m64p_dynlib_handle plugin_handle) |
| 489 | { |
| 490 | switch(type) |
| 491 | { |
| 492 | case M64PLUGIN_GFX: |
| 493 | if (plugin_handle != NULL && (l_AudioAttached || l_InputAttached || l_RspAttached)) |
| 494 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order."); |
| 495 | return plugin_connect_gfx(plugin_handle); |
| 496 | case M64PLUGIN_AUDIO: |
| 497 | if (plugin_handle != NULL && (l_InputAttached || l_RspAttached)) |
| 498 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order."); |
| 499 | return plugin_connect_audio(plugin_handle); |
| 500 | case M64PLUGIN_INPUT: |
| 501 | if (plugin_handle != NULL && (l_RspAttached)) |
| 502 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order."); |
| 503 | return plugin_connect_input(plugin_handle); |
| 504 | case M64PLUGIN_RSP: |
| 505 | return plugin_connect_rsp(plugin_handle); |
| 506 | default: |
| 507 | return M64ERR_INPUT_INVALID; |
| 508 | } |
| 509 | |
| 510 | return M64ERR_INTERNAL; |
| 511 | } |
| 512 | |
| 513 | m64p_error plugin_start(m64p_plugin_type type) |
| 514 | { |
| 515 | switch(type) |
| 516 | { |
| 517 | case M64PLUGIN_RSP: |
| 518 | return plugin_start_rsp(); |
| 519 | case M64PLUGIN_GFX: |
| 520 | return plugin_start_gfx(); |
| 521 | case M64PLUGIN_AUDIO: |
| 522 | return plugin_start_audio(); |
| 523 | case M64PLUGIN_INPUT: |
| 524 | return plugin_start_input(); |
| 525 | default: |
| 526 | return M64ERR_INPUT_INVALID; |
| 527 | } |
| 528 | |
| 529 | return M64ERR_INTERNAL; |
| 530 | } |
| 531 | |
| 532 | m64p_error plugin_check(void) |
| 533 | { |
| 534 | if (!l_GfxAttached) |
| 535 | DebugMessage(M64MSG_WARNING, "No video plugin attached. There will be no video output."); |
| 536 | if (!l_RspAttached) |
| 537 | DebugMessage(M64MSG_WARNING, "No RSP plugin attached. The video output will be corrupted."); |
| 538 | if (!l_AudioAttached) |
| 539 | DebugMessage(M64MSG_WARNING, "No audio plugin attached. There will be no sound output."); |
| 540 | if (!l_InputAttached) |
| 541 | DebugMessage(M64MSG_WARNING, "No input plugin attached. You won't be able to control the game."); |
| 542 | |
| 543 | return M64ERR_SUCCESS; |
| 544 | } |
| 545 | |