Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / src / plugin / plugin.c
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