1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus - plugin.c * |
3 | * Mupen64Plus homepage: https://mupen64plus.org/ * |
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 <stdint.h> |
24 | #include <stdlib.h> |
25 | #include <string.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 | #include "device/memory/memory.h" |
32 | #include "device/rcp/ai/ai_controller.h" |
33 | #include "device/rcp/mi/mi_controller.h" |
34 | #include "device/rcp/rdp/rdp_core.h" |
35 | #include "device/rcp/rsp/rsp_core.h" |
36 | #include "device/rcp/vi/vi_controller.h" |
37 | #include "dummy_audio.h" |
38 | #include "dummy_input.h" |
39 | #include "dummy_rsp.h" |
40 | #include "dummy_video.h" |
41 | #include "main/main.h" |
42 | #include "main/rom.h" |
43 | #include "main/version.h" |
44 | #include "osal/dynamiclib.h" |
45 | #include "plugin.h" |
46 | |
47 | CONTROL Controls[4]; |
48 | |
49 | /* global function pointers - initialized on core startup */ |
50 | gfx_plugin_functions gfx; |
51 | audio_plugin_functions audio; |
52 | input_plugin_functions input; |
53 | rsp_plugin_functions rsp; |
54 | |
55 | /* local data structures and functions */ |
56 | static const gfx_plugin_functions dummy_gfx = { |
57 | dummyvideo_PluginGetVersion, |
58 | dummyvideo_ChangeWindow, |
59 | dummyvideo_InitiateGFX, |
60 | dummyvideo_MoveScreen, |
61 | dummyvideo_ProcessDList, |
62 | dummyvideo_ProcessRDPList, |
63 | dummyvideo_RomClosed, |
64 | dummyvideo_RomOpen, |
65 | dummyvideo_ShowCFB, |
66 | dummyvideo_UpdateScreen, |
67 | dummyvideo_ViStatusChanged, |
68 | dummyvideo_ViWidthChanged, |
69 | dummyvideo_ReadScreen2, |
70 | dummyvideo_SetRenderingCallback, |
71 | dummyvideo_ResizeVideoOutput, |
72 | dummyvideo_FBRead, |
73 | dummyvideo_FBWrite, |
74 | dummyvideo_FBGetFrameBufferInfo |
75 | }; |
76 | |
77 | static const audio_plugin_functions dummy_audio = { |
78 | dummyaudio_PluginGetVersion, |
79 | dummyaudio_AiDacrateChanged, |
80 | dummyaudio_AiLenChanged, |
81 | dummyaudio_InitiateAudio, |
82 | dummyaudio_ProcessAList, |
83 | dummyaudio_RomClosed, |
84 | dummyaudio_RomOpen, |
85 | dummyaudio_SetSpeedFactor, |
86 | dummyaudio_VolumeUp, |
87 | dummyaudio_VolumeDown, |
88 | dummyaudio_VolumeGetLevel, |
89 | dummyaudio_VolumeSetLevel, |
90 | dummyaudio_VolumeMute, |
91 | dummyaudio_VolumeGetString |
92 | }; |
93 | |
94 | static const input_plugin_functions dummy_input = { |
95 | dummyinput_PluginGetVersion, |
96 | dummyinput_ControllerCommand, |
97 | dummyinput_GetKeys, |
98 | dummyinput_InitiateControllers, |
99 | dummyinput_ReadController, |
100 | dummyinput_RomClosed, |
101 | dummyinput_RomOpen, |
102 | dummyinput_SDL_KeyDown, |
103 | dummyinput_SDL_KeyUp, |
104 | dummyinput_RenderCallback |
105 | }; |
106 | |
107 | static const rsp_plugin_functions dummy_rsp = { |
108 | dummyrsp_PluginGetVersion, |
109 | dummyrsp_DoRspCycles, |
110 | dummyrsp_InitiateRSP, |
111 | dummyrsp_RomClosed |
112 | }; |
113 | |
114 | static GFX_INFO gfx_info; |
115 | static AUDIO_INFO audio_info; |
116 | static CONTROL_INFO control_info; |
117 | static RSP_INFO rsp_info; |
118 | |
119 | static int l_RspAttached = 0; |
120 | static int l_InputAttached = 0; |
121 | static int l_AudioAttached = 0; |
122 | static int l_GfxAttached = 0; |
123 | |
124 | static unsigned int dummy; |
125 | |
126 | /* local functions */ |
127 | static void EmptyFunc(void) |
128 | { |
129 | } |
130 | |
131 | // Handy macro to avoid code bloat when loading symbols |
132 | #define GET_FUNC(type, field, name) \ |
133 | ((field = (type)osal_dynlib_getproc(plugin_handle, name)) != NULL) |
134 | |
135 | // code to handle backwards-compatibility to video plugins with API_VERSION < 02.1.0. This API version introduced a boolean |
136 | // flag in the rendering callback, which told the core whether or not the current screen has been freshly redrawn since the |
137 | // last time the callback was called. |
138 | static void (*l_mainRenderCallback)(int) = NULL; |
139 | static ptr_SetRenderingCallback l_old1SetRenderingCallback = NULL; |
140 | |
141 | static void backcompat_videoRenderCallback(int unused) // this function will be called by the video plugin as the render callback |
142 | { |
143 | if (l_mainRenderCallback != NULL) |
144 | l_mainRenderCallback(1); // assume screen is always freshly redrawn (otherwise screenshots won't work w/ OSD enabled) |
145 | } |
146 | |
147 | static void backcompat_setRenderCallbackIntercept(void (*callback)(int)) |
148 | { |
149 | l_mainRenderCallback = callback; |
150 | } |
151 | |
152 | static void plugin_disconnect_gfx(void) |
153 | { |
154 | gfx = dummy_gfx; |
155 | l_GfxAttached = 0; |
156 | l_mainRenderCallback = NULL; |
157 | } |
158 | |
159 | static m64p_error plugin_connect_gfx(m64p_dynlib_handle plugin_handle) |
160 | { |
161 | /* attach the Video plugin function pointers */ |
162 | if (plugin_handle != NULL) |
163 | { |
164 | m64p_plugin_type PluginType; |
165 | int PluginVersion, APIVersion; |
166 | |
167 | if (l_GfxAttached) |
168 | return M64ERR_INVALID_STATE; |
169 | |
170 | /* set function pointers for required functions */ |
171 | if (!GET_FUNC(ptr_PluginGetVersion, gfx.getVersion, "PluginGetVersion" ) || |
172 | !GET_FUNC(ptr_ChangeWindow, gfx.changeWindow, "ChangeWindow" ) || |
173 | !GET_FUNC(ptr_InitiateGFX, gfx.initiateGFX, "InitiateGFX" ) || |
174 | !GET_FUNC(ptr_MoveScreen, gfx.moveScreen, "MoveScreen" ) || |
175 | !GET_FUNC(ptr_ProcessDList, gfx.processDList, "ProcessDList" ) || |
176 | !GET_FUNC(ptr_ProcessRDPList, gfx.processRDPList, "ProcessRDPList" ) || |
177 | !GET_FUNC(ptr_RomClosed, gfx.romClosed, "RomClosed" ) || |
178 | !GET_FUNC(ptr_RomOpen, gfx.romOpen, "RomOpen" ) || |
179 | !GET_FUNC(ptr_ShowCFB, gfx.showCFB, "ShowCFB" ) || |
180 | !GET_FUNC(ptr_UpdateScreen, gfx.updateScreen, "UpdateScreen" ) || |
181 | !GET_FUNC(ptr_ViStatusChanged, gfx.viStatusChanged, "ViStatusChanged" ) || |
182 | !GET_FUNC(ptr_ViWidthChanged, gfx.viWidthChanged, "ViWidthChanged" ) || |
183 | !GET_FUNC(ptr_ReadScreen2, gfx.readScreen, "ReadScreen2" ) || |
184 | !GET_FUNC(ptr_SetRenderingCallback, gfx.setRenderingCallback, "SetRenderingCallback" ) || |
185 | !GET_FUNC(ptr_FBRead, gfx.fBRead, "FBRead" ) || |
186 | !GET_FUNC(ptr_FBWrite, gfx.fBWrite, "FBWrite" ) || |
187 | !GET_FUNC(ptr_FBGetFrameBufferInfo, gfx.fBGetFrameBufferInfo, "FBGetFrameBufferInfo" )) |
188 | { |
189 | DebugMessage(M64MSG_ERROR, "broken Video plugin; function(s) not found." ); |
190 | plugin_disconnect_gfx(); |
191 | return M64ERR_INPUT_INVALID; |
192 | } |
193 | |
194 | /* set function pointers for optional functions */ |
195 | gfx.resizeVideoOutput = (ptr_ResizeVideoOutput)osal_dynlib_getproc(plugin_handle, "ResizeVideoOutput" ); |
196 | |
197 | /* check the version info */ |
198 | (*gfx.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
199 | if (PluginType != M64PLUGIN_GFX || (APIVersion & 0xffff0000) != (GFX_API_VERSION & 0xffff0000)) |
200 | { |
201 | DebugMessage(M64MSG_ERROR, "incompatible Video plugin" ); |
202 | plugin_disconnect_gfx(); |
203 | return M64ERR_INCOMPATIBLE; |
204 | } |
205 | |
206 | /* handle backwards-compatibility */ |
207 | if (APIVersion < 0x020100) |
208 | { |
209 | 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)); |
210 | // tell the video plugin to make its rendering callback to me (it's old, and doesn't have the bScreenRedrawn flag) |
211 | gfx.setRenderingCallback(backcompat_videoRenderCallback); |
212 | l_old1SetRenderingCallback = gfx.setRenderingCallback; // save this just for future use |
213 | gfx.setRenderingCallback = (ptr_SetRenderingCallback) backcompat_setRenderCallbackIntercept; |
214 | } |
215 | if (APIVersion < 0x20200 || gfx.resizeVideoOutput == NULL) |
216 | { |
217 | DebugMessage(M64MSG_WARNING, "Fallback for Video plugin API (%02i.%02i.%02i) < 2.2.0. Resizable video will not work" , VERSION_PRINTF_SPLIT(APIVersion)); |
218 | gfx.resizeVideoOutput = dummyvideo_ResizeVideoOutput; |
219 | } |
220 | |
221 | l_GfxAttached = 1; |
222 | } |
223 | else |
224 | plugin_disconnect_gfx(); |
225 | |
226 | return M64ERR_SUCCESS; |
227 | } |
228 | |
229 | static m64p_error plugin_start_gfx(void) |
230 | { |
231 | uint8_t media = *((uint8_t*)mem_base_u32(g_mem_base, MM_CART_ROM) + (0x3b ^ S8)); |
232 | |
233 | /* Here we feed 64DD IPL ROM header to GFX plugin if 64DD is present. |
234 | * We use g_media_loader.get_dd_rom to detect 64DD presence |
235 | * instead of g_dev because the latter is not yet initialized at plugin_start time */ |
236 | /* XXX: Not sure it is the best way to convey which game is being played to the GFX plugin |
237 | * as 64DD IPL is the same for all 64DD games... */ |
238 | char* dd_ipl_rom_filename = (g_media_loader.get_dd_rom == NULL) |
239 | ? NULL |
240 | : g_media_loader.get_dd_rom(g_media_loader.cb_data); |
241 | |
242 | uint32_t rom_base = (dd_ipl_rom_filename != NULL && strlen(dd_ipl_rom_filename) != 0 && media != 'C') |
243 | ? MM_DD_ROM |
244 | : MM_CART_ROM; |
245 | |
246 | free(dd_ipl_rom_filename); |
247 | |
248 | /* fill in the GFX_INFO data structure */ |
249 | gfx_info.HEADER = (unsigned char *)mem_base_u32(g_mem_base, rom_base); |
250 | gfx_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM); |
251 | gfx_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM); |
252 | gfx_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000); |
253 | gfx_info.MI_INTR_REG = &(g_dev.mi.regs[MI_INTR_REG]); |
254 | gfx_info.DPC_START_REG = &(g_dev.dp.dpc_regs[DPC_START_REG]); |
255 | gfx_info.DPC_END_REG = &(g_dev.dp.dpc_regs[DPC_END_REG]); |
256 | gfx_info.DPC_CURRENT_REG = &(g_dev.dp.dpc_regs[DPC_CURRENT_REG]); |
257 | gfx_info.DPC_STATUS_REG = &(g_dev.dp.dpc_regs[DPC_STATUS_REG]); |
258 | gfx_info.DPC_CLOCK_REG = &(g_dev.dp.dpc_regs[DPC_CLOCK_REG]); |
259 | gfx_info.DPC_BUFBUSY_REG = &(g_dev.dp.dpc_regs[DPC_BUFBUSY_REG]); |
260 | gfx_info.DPC_PIPEBUSY_REG = &(g_dev.dp.dpc_regs[DPC_PIPEBUSY_REG]); |
261 | gfx_info.DPC_TMEM_REG = &(g_dev.dp.dpc_regs[DPC_TMEM_REG]); |
262 | gfx_info.VI_STATUS_REG = &(g_dev.vi.regs[VI_STATUS_REG]); |
263 | gfx_info.VI_ORIGIN_REG = &(g_dev.vi.regs[VI_ORIGIN_REG]); |
264 | gfx_info.VI_WIDTH_REG = &(g_dev.vi.regs[VI_WIDTH_REG]); |
265 | gfx_info.VI_INTR_REG = &(g_dev.vi.regs[VI_V_INTR_REG]); |
266 | gfx_info.VI_V_CURRENT_LINE_REG = &(g_dev.vi.regs[VI_CURRENT_REG]); |
267 | gfx_info.VI_TIMING_REG = &(g_dev.vi.regs[VI_BURST_REG]); |
268 | gfx_info.VI_V_SYNC_REG = &(g_dev.vi.regs[VI_V_SYNC_REG]); |
269 | gfx_info.VI_H_SYNC_REG = &(g_dev.vi.regs[VI_H_SYNC_REG]); |
270 | gfx_info.VI_LEAP_REG = &(g_dev.vi.regs[VI_LEAP_REG]); |
271 | gfx_info.VI_H_START_REG = &(g_dev.vi.regs[VI_H_START_REG]); |
272 | gfx_info.VI_V_START_REG = &(g_dev.vi.regs[VI_V_START_REG]); |
273 | gfx_info.VI_V_BURST_REG = &(g_dev.vi.regs[VI_V_BURST_REG]); |
274 | gfx_info.VI_X_SCALE_REG = &(g_dev.vi.regs[VI_X_SCALE_REG]); |
275 | gfx_info.VI_Y_SCALE_REG = &(g_dev.vi.regs[VI_Y_SCALE_REG]); |
276 | gfx_info.CheckInterrupts = EmptyFunc; |
277 | |
278 | gfx_info.version = 2; //Version 2 added SP_STATUS_REG and RDRAM_SIZE |
279 | gfx_info.SP_STATUS_REG = &g_dev.sp.regs[SP_STATUS_REG]; |
280 | gfx_info.RDRAM_SIZE = (unsigned int*) &g_dev.rdram.dram_size; |
281 | |
282 | /* call the audio plugin */ |
283 | if (!gfx.initiateGFX(gfx_info)) |
284 | return M64ERR_PLUGIN_FAIL; |
285 | |
286 | return M64ERR_SUCCESS; |
287 | } |
288 | |
289 | static void plugin_disconnect_audio(void) |
290 | { |
291 | audio = dummy_audio; |
292 | l_AudioAttached = 0; |
293 | } |
294 | |
295 | static m64p_error plugin_connect_audio(m64p_dynlib_handle plugin_handle) |
296 | { |
297 | /* attach the Audio plugin function pointers */ |
298 | if (plugin_handle != NULL) |
299 | { |
300 | m64p_plugin_type PluginType; |
301 | int PluginVersion, APIVersion; |
302 | |
303 | if (l_AudioAttached) |
304 | return M64ERR_INVALID_STATE; |
305 | |
306 | if (!GET_FUNC(ptr_PluginGetVersion, audio.getVersion, "PluginGetVersion" ) || |
307 | !GET_FUNC(ptr_AiDacrateChanged, audio.aiDacrateChanged, "AiDacrateChanged" ) || |
308 | !GET_FUNC(ptr_AiLenChanged, audio.aiLenChanged, "AiLenChanged" ) || |
309 | !GET_FUNC(ptr_InitiateAudio, audio.initiateAudio, "InitiateAudio" ) || |
310 | !GET_FUNC(ptr_ProcessAList, audio.processAList, "ProcessAList" ) || |
311 | !GET_FUNC(ptr_RomOpen, audio.romOpen, "RomOpen" ) || |
312 | !GET_FUNC(ptr_RomClosed, audio.romClosed, "RomClosed" ) || |
313 | !GET_FUNC(ptr_SetSpeedFactor, audio.setSpeedFactor, "SetSpeedFactor" ) || |
314 | !GET_FUNC(ptr_VolumeUp, audio.volumeUp, "VolumeUp" ) || |
315 | !GET_FUNC(ptr_VolumeDown, audio.volumeDown, "VolumeDown" ) || |
316 | !GET_FUNC(ptr_VolumeGetLevel, audio.volumeGetLevel, "VolumeGetLevel" ) || |
317 | !GET_FUNC(ptr_VolumeSetLevel, audio.volumeSetLevel, "VolumeSetLevel" ) || |
318 | !GET_FUNC(ptr_VolumeMute, audio.volumeMute, "VolumeMute" ) || |
319 | !GET_FUNC(ptr_VolumeGetString, audio.volumeGetString, "VolumeGetString" )) |
320 | { |
321 | DebugMessage(M64MSG_ERROR, "broken Audio plugin; function(s) not found." ); |
322 | plugin_disconnect_audio(); |
323 | return M64ERR_INPUT_INVALID; |
324 | } |
325 | |
326 | /* check the version info */ |
327 | (*audio.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
328 | if (PluginType != M64PLUGIN_AUDIO || (APIVersion & 0xffff0000) != (AUDIO_API_VERSION & 0xffff0000)) |
329 | { |
330 | DebugMessage(M64MSG_ERROR, "incompatible Audio plugin" ); |
331 | plugin_disconnect_audio(); |
332 | return M64ERR_INCOMPATIBLE; |
333 | } |
334 | |
335 | l_AudioAttached = 1; |
336 | } |
337 | else |
338 | plugin_disconnect_audio(); |
339 | |
340 | return M64ERR_SUCCESS; |
341 | } |
342 | |
343 | static m64p_error plugin_start_audio(void) |
344 | { |
345 | /* fill in the AUDIO_INFO data structure */ |
346 | audio_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM); |
347 | audio_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM); |
348 | audio_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000); |
349 | audio_info.MI_INTR_REG = &(g_dev.mi.regs[MI_INTR_REG]); |
350 | audio_info.AI_DRAM_ADDR_REG = &(g_dev.ai.regs[AI_DRAM_ADDR_REG]); |
351 | audio_info.AI_LEN_REG = &(g_dev.ai.regs[AI_LEN_REG]); |
352 | audio_info.AI_CONTROL_REG = &(g_dev.ai.regs[AI_CONTROL_REG]); |
353 | audio_info.AI_STATUS_REG = &dummy; |
354 | audio_info.AI_DACRATE_REG = &(g_dev.ai.regs[AI_DACRATE_REG]); |
355 | audio_info.AI_BITRATE_REG = &(g_dev.ai.regs[AI_BITRATE_REG]); |
356 | audio_info.CheckInterrupts = EmptyFunc; |
357 | |
358 | /* call the audio plugin */ |
359 | if (!audio.initiateAudio(audio_info)) |
360 | return M64ERR_PLUGIN_FAIL; |
361 | |
362 | return M64ERR_SUCCESS; |
363 | } |
364 | |
365 | static void plugin_disconnect_input(void) |
366 | { |
367 | input = dummy_input; |
368 | l_InputAttached = 0; |
369 | } |
370 | |
371 | static m64p_error plugin_connect_input(m64p_dynlib_handle plugin_handle) |
372 | { |
373 | /* attach the Input plugin function pointers */ |
374 | if (plugin_handle != NULL) |
375 | { |
376 | m64p_plugin_type PluginType; |
377 | int PluginVersion, APIVersion; |
378 | |
379 | if (l_InputAttached) |
380 | return M64ERR_INVALID_STATE; |
381 | |
382 | if (!GET_FUNC(ptr_PluginGetVersion, input.getVersion, "PluginGetVersion" ) || |
383 | !GET_FUNC(ptr_ControllerCommand, input.controllerCommand, "ControllerCommand" ) || |
384 | !GET_FUNC(ptr_GetKeys, input.getKeys, "GetKeys" ) || |
385 | !GET_FUNC(ptr_InitiateControllers, input.initiateControllers, "InitiateControllers" ) || |
386 | !GET_FUNC(ptr_ReadController, input.readController, "ReadController" ) || |
387 | !GET_FUNC(ptr_RomOpen, input.romOpen, "RomOpen" ) || |
388 | !GET_FUNC(ptr_RomClosed, input.romClosed, "RomClosed" ) || |
389 | !GET_FUNC(ptr_SDL_KeyDown, input.keyDown, "SDL_KeyDown" ) || |
390 | !GET_FUNC(ptr_SDL_KeyUp, input.keyUp, "SDL_KeyUp" )) |
391 | { |
392 | DebugMessage(M64MSG_ERROR, "broken Input plugin; function(s) not found." ); |
393 | plugin_disconnect_input(); |
394 | return M64ERR_INPUT_INVALID; |
395 | } |
396 | |
397 | /* check the version info */ |
398 | (*input.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
399 | if (PluginType != M64PLUGIN_INPUT || (APIVersion & 0xffff0000) != (INPUT_API_VERSION & 0xffff0000) || APIVersion < 0x020100) |
400 | { |
401 | DebugMessage(M64MSG_ERROR, "incompatible Input plugin" ); |
402 | plugin_disconnect_input(); |
403 | return M64ERR_INCOMPATIBLE; |
404 | } |
405 | |
406 | if (!GET_FUNC(ptr_RenderCallback, input.renderCallback, "RenderCallback" )) |
407 | { |
408 | DebugMessage(M64MSG_INFO, "input plugin did not specify a render callback; there will be no on screen display by the input plugin." ); |
409 | } |
410 | |
411 | l_InputAttached = 1; |
412 | } |
413 | else |
414 | plugin_disconnect_input(); |
415 | |
416 | return M64ERR_SUCCESS; |
417 | } |
418 | |
419 | static m64p_error plugin_start_input(void) |
420 | { |
421 | int i; |
422 | |
423 | /* fill in the CONTROL_INFO data structure */ |
424 | control_info.Controls = Controls; |
425 | for (i=0; i<4; i++) |
426 | { |
427 | Controls[i].Present = 0; |
428 | Controls[i].RawData = 0; |
429 | Controls[i].Plugin = PLUGIN_NONE; |
430 | } |
431 | |
432 | /* call the input plugin */ |
433 | input.initiateControllers(control_info); |
434 | |
435 | return M64ERR_SUCCESS; |
436 | } |
437 | |
438 | static void plugin_disconnect_rsp(void) |
439 | { |
440 | rsp = dummy_rsp; |
441 | l_RspAttached = 0; |
442 | } |
443 | |
444 | static m64p_error plugin_connect_rsp(m64p_dynlib_handle plugin_handle) |
445 | { |
446 | /* attach the RSP plugin function pointers */ |
447 | if (plugin_handle != NULL) |
448 | { |
449 | m64p_plugin_type PluginType; |
450 | int PluginVersion, APIVersion; |
451 | |
452 | if (l_RspAttached) |
453 | return M64ERR_INVALID_STATE; |
454 | |
455 | if (!GET_FUNC(ptr_PluginGetVersion, rsp.getVersion, "PluginGetVersion" ) || |
456 | !GET_FUNC(ptr_DoRspCycles, rsp.doRspCycles, "DoRspCycles" ) || |
457 | !GET_FUNC(ptr_InitiateRSP, rsp.initiateRSP, "InitiateRSP" ) || |
458 | !GET_FUNC(ptr_RomClosed, rsp.romClosed, "RomClosed" )) |
459 | { |
460 | DebugMessage(M64MSG_ERROR, "broken RSP plugin; function(s) not found." ); |
461 | plugin_disconnect_rsp(); |
462 | return M64ERR_INPUT_INVALID; |
463 | } |
464 | |
465 | /* check the version info */ |
466 | (*rsp.getVersion)(&PluginType, &PluginVersion, &APIVersion, NULL, NULL); |
467 | if (PluginType != M64PLUGIN_RSP || (APIVersion & 0xffff0000) != (RSP_API_VERSION & 0xffff0000)) |
468 | { |
469 | DebugMessage(M64MSG_ERROR, "incompatible RSP plugin" ); |
470 | plugin_disconnect_rsp(); |
471 | return M64ERR_INCOMPATIBLE; |
472 | } |
473 | |
474 | l_RspAttached = 1; |
475 | } |
476 | else |
477 | plugin_disconnect_rsp(); |
478 | |
479 | return M64ERR_SUCCESS; |
480 | } |
481 | |
482 | static m64p_error plugin_start_rsp(void) |
483 | { |
484 | /* fill in the RSP_INFO data structure */ |
485 | rsp_info.RDRAM = (unsigned char *)mem_base_u32(g_mem_base, MM_RDRAM_DRAM); |
486 | rsp_info.DMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM); |
487 | rsp_info.IMEM = (unsigned char *)mem_base_u32(g_mem_base, MM_RSP_MEM + 0x1000); |
488 | rsp_info.MI_INTR_REG = &g_dev.mi.regs[MI_INTR_REG]; |
489 | rsp_info.SP_MEM_ADDR_REG = &g_dev.sp.regs[SP_MEM_ADDR_REG]; |
490 | rsp_info.SP_DRAM_ADDR_REG = &g_dev.sp.regs[SP_DRAM_ADDR_REG]; |
491 | rsp_info.SP_RD_LEN_REG = &g_dev.sp.regs[SP_RD_LEN_REG]; |
492 | rsp_info.SP_WR_LEN_REG = &g_dev.sp.regs[SP_WR_LEN_REG]; |
493 | rsp_info.SP_STATUS_REG = &g_dev.sp.regs[SP_STATUS_REG]; |
494 | rsp_info.SP_DMA_FULL_REG = &g_dev.sp.regs[SP_DMA_FULL_REG]; |
495 | rsp_info.SP_DMA_BUSY_REG = &g_dev.sp.regs[SP_DMA_BUSY_REG]; |
496 | rsp_info.SP_PC_REG = &g_dev.sp.regs2[SP_PC_REG]; |
497 | rsp_info.SP_SEMAPHORE_REG = &g_dev.sp.regs[SP_SEMAPHORE_REG]; |
498 | rsp_info.DPC_START_REG = &g_dev.dp.dpc_regs[DPC_START_REG]; |
499 | rsp_info.DPC_END_REG = &g_dev.dp.dpc_regs[DPC_END_REG]; |
500 | rsp_info.DPC_CURRENT_REG = &g_dev.dp.dpc_regs[DPC_CURRENT_REG]; |
501 | rsp_info.DPC_STATUS_REG = &g_dev.dp.dpc_regs[DPC_STATUS_REG]; |
502 | rsp_info.DPC_CLOCK_REG = &g_dev.dp.dpc_regs[DPC_CLOCK_REG]; |
503 | rsp_info.DPC_BUFBUSY_REG = &g_dev.dp.dpc_regs[DPC_BUFBUSY_REG]; |
504 | rsp_info.DPC_PIPEBUSY_REG = &g_dev.dp.dpc_regs[DPC_PIPEBUSY_REG]; |
505 | rsp_info.DPC_TMEM_REG = &g_dev.dp.dpc_regs[DPC_TMEM_REG]; |
506 | rsp_info.CheckInterrupts = EmptyFunc; |
507 | rsp_info.ProcessDlistList = gfx.processDList; |
508 | rsp_info.ProcessAlistList = audio.processAList; |
509 | rsp_info.ProcessRdpList = gfx.processRDPList; |
510 | rsp_info.ShowCFB = gfx.showCFB; |
511 | |
512 | /* call the RSP plugin */ |
513 | rsp.initiateRSP(rsp_info, NULL); |
514 | |
515 | return M64ERR_SUCCESS; |
516 | } |
517 | |
518 | /* global functions */ |
519 | m64p_error plugin_connect(m64p_plugin_type type, m64p_dynlib_handle plugin_handle) |
520 | { |
521 | switch(type) |
522 | { |
523 | case M64PLUGIN_GFX: |
524 | if (plugin_handle != NULL && (l_AudioAttached || l_InputAttached || l_RspAttached)) |
525 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order." ); |
526 | return plugin_connect_gfx(plugin_handle); |
527 | case M64PLUGIN_AUDIO: |
528 | if (plugin_handle != NULL && (l_InputAttached || l_RspAttached)) |
529 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order." ); |
530 | return plugin_connect_audio(plugin_handle); |
531 | case M64PLUGIN_INPUT: |
532 | if (plugin_handle != NULL && (l_RspAttached)) |
533 | DebugMessage(M64MSG_WARNING, "Front-end bug: plugins are attached in wrong order." ); |
534 | return plugin_connect_input(plugin_handle); |
535 | case M64PLUGIN_RSP: |
536 | return plugin_connect_rsp(plugin_handle); |
537 | default: |
538 | return M64ERR_INPUT_INVALID; |
539 | } |
540 | |
541 | return M64ERR_INTERNAL; |
542 | } |
543 | |
544 | m64p_error plugin_start(m64p_plugin_type type) |
545 | { |
546 | switch(type) |
547 | { |
548 | case M64PLUGIN_RSP: |
549 | return plugin_start_rsp(); |
550 | case M64PLUGIN_GFX: |
551 | return plugin_start_gfx(); |
552 | case M64PLUGIN_AUDIO: |
553 | return plugin_start_audio(); |
554 | case M64PLUGIN_INPUT: |
555 | return plugin_start_input(); |
556 | default: |
557 | return M64ERR_INPUT_INVALID; |
558 | } |
559 | |
560 | return M64ERR_INTERNAL; |
561 | } |
562 | |
563 | m64p_error plugin_check(void) |
564 | { |
565 | if (!l_GfxAttached) |
566 | DebugMessage(M64MSG_WARNING, "No video plugin attached. There will be no video output." ); |
567 | if (!l_RspAttached) |
568 | DebugMessage(M64MSG_WARNING, "No RSP plugin attached. The video output will be corrupted." ); |
569 | if (!l_AudioAttached) |
570 | DebugMessage(M64MSG_WARNING, "No audio plugin attached. There will be no sound output." ); |
571 | if (!l_InputAttached) |
572 | DebugMessage(M64MSG_WARNING, "No input plugin attached. You won't be able to control the game." ); |
573 | |
574 | return M64ERR_SUCCESS; |
575 | } |
576 | |
577 | |