INPUT: Added alternate C Button and mouse_keys
[mupen64plus-pandora.git] / source / mupen64plus-input-sdl / src / config.c
CommitLineData
48d52ab5 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus-input-sdl - config.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2009-2013 Richard Goedeken *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22#include <SDL.h>
23
24#define M64P_PLUGIN_PROTOTYPES 1
25#include "m64p_types.h"
26#include "m64p_plugin.h"
27#include "m64p_config.h"
28
29#include "osal_preproc.h"
30#include "autoconfig.h"
31#include "plugin.h"
32
33#include "config.h"
34
35#define HAT_POS_NAME( hat ) \
36 ((hat == SDL_HAT_UP) ? "Up" : \
37 ((hat == SDL_HAT_DOWN) ? "Down" : \
38 ((hat == SDL_HAT_LEFT) ? "Left" : \
39 ((hat == SDL_HAT_RIGHT) ? "Right" : \
40 "None"))))
41
42typedef enum {
43 E_MODE_MANUAL = 0,
44 E_MODE_NAMED_AUTO,
45 E_MODE_FULL_AUTO
46 } eModeType;
47
48static const char *button_names[] = {
49 "DPad R", // R_DPAD
50 "DPad L", // L_DPAD
51 "DPad D", // D_DPAD
52 "DPad U", // U_DPAD
53 "Start", // START_BUTTON
54 "Z Trig", // Z_TRIG
55 "B Button", // B_BUTTON
56 "A Button", // A_BUTTON
57 "C Button R", // R_CBUTTON
58 "C Button L", // L_CBUTTON
59 "C Button D", // D_CBUTTON
60 "C Button U", // U_CBUTTON
61 "R Trig", // R_TRIG
62 "L Trig", // L_TRIG
63 "Mempak switch",
64 "Rumblepak switch",
6ff3e5eb 65 "C Button2 R", // R_CBUTTON alternate
66 "C Button2 L", // L_CBUTTON alternate
67 "C Button2 D", // D_CBUTTON alternate
68 "C Button2 U", // U_CBUTTON alternate
48d52ab5 69 "X Axis", // X_AXIS
70 "Y Axis" // Y_AXIS
71};
72
73/* static functions */
74static int get_hat_pos_by_name( const char *name )
75{
76 if( !strcasecmp( name, "up" ) )
77 return SDL_HAT_UP;
78 if( !strcasecmp( name, "down" ) )
79 return SDL_HAT_DOWN;
80 if( !strcasecmp( name, "left" ) )
81 return SDL_HAT_LEFT;
82 if( !strcasecmp( name, "right" ) )
83 return SDL_HAT_RIGHT;
84 DebugMessage(M64MSG_WARNING, "get_hat_pos_by_name(): direction '%s' unknown", name);
85 return -1;
86}
87
88static void clear_controller(int iCtrlIdx)
89{
90 int b;
91
92 controller[iCtrlIdx].device = DEVICE_NO_JOYSTICK;
93 controller[iCtrlIdx].control->Present = 0;
94 controller[iCtrlIdx].control->RawData = 0;
95 controller[iCtrlIdx].control->Plugin = PLUGIN_NONE;
6ff3e5eb 96 for( b = 0; b < NUM_BUTTONS; b++ )
48d52ab5 97 {
98 controller[iCtrlIdx].button[b].button = -1;
99 controller[iCtrlIdx].button[b].key = SDL_SCANCODE_UNKNOWN;
100 controller[iCtrlIdx].button[b].axis = -1;
101 controller[iCtrlIdx].button[b].axis_deadzone = -1;
102 controller[iCtrlIdx].button[b].hat = -1;
103 controller[iCtrlIdx].button[b].hat_pos = -1;
104 controller[iCtrlIdx].button[b].mouse = -1;
6ff3e5eb 105 controller[iCtrlIdx].button[b].mouse_up = 0;
106 controller[iCtrlIdx].button[b].mouse_down = 0;
107 controller[iCtrlIdx].button[b].mouse_left = 0;
108 controller[iCtrlIdx].button[b].mouse_right = 0;
48d52ab5 109 }
110 for( b = 0; b < 2; b++ )
111 {
112 controller[iCtrlIdx].mouse_sens[b] = 2.0;
113 controller[iCtrlIdx].axis_deadzone[b] = 4096;
114 controller[iCtrlIdx].axis_peak[b] = 32768;
115 controller[iCtrlIdx].axis[b].button_a = controller[iCtrlIdx].axis[b].button_b = -1;
116 controller[iCtrlIdx].axis[b].key_a = controller[iCtrlIdx].axis[b].key_b = SDL_SCANCODE_UNKNOWN;
117 controller[iCtrlIdx].axis[b].axis_a = -1;
118 controller[iCtrlIdx].axis[b].axis_dir_a = 1;
119 controller[iCtrlIdx].axis[b].axis_b = -1;
120 controller[iCtrlIdx].axis[b].axis_dir_b = 1;
121 controller[iCtrlIdx].axis[b].hat = -1;
122 controller[iCtrlIdx].axis[b].hat_pos_a = -1;
123 controller[iCtrlIdx].axis[b].hat_pos_b = -1;
124 }
125}
126
127static const char * get_sdl_joystick_name(int iCtrlIdx)
128{
129 static char JoyName[256];
130 const char *joySDLName;
131 int joyWasInit = SDL_WasInit(SDL_INIT_JOYSTICK);
132
133 /* initialize the joystick subsystem if necessary */
134 if (!joyWasInit)
135 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
136 {
137 DebugMessage(M64MSG_ERROR, "Couldn't init SDL joystick subsystem: %s", SDL_GetError() );
138 return NULL;
139 }
140
141 /* get the name of the corresponding joystick */
142 joySDLName = SDL_JoystickName(iCtrlIdx);
143
144 /* copy the name to our local string */
145 if (joySDLName != NULL)
146 {
147 strncpy(JoyName, joySDLName, 255);
148 JoyName[255] = 0;
149 }
150
151 /* quit the joystick subsystem if necessary */
152 if (!joyWasInit)
153 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
154
155 /* if the SDL function had an error, then return NULL, otherwise return local copy of joystick name */
156 if (joySDLName == NULL)
157 return NULL;
158 else
159 return JoyName;
160}
161
162static int get_sdl_num_joysticks(void)
163{
164 int numJoysticks = 0;
165 int joyWasInit = SDL_WasInit(SDL_INIT_JOYSTICK);
166
167 /* initialize the joystick subsystem if necessary */
168 if (!joyWasInit)
169 if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
170 {
171 DebugMessage(M64MSG_ERROR, "Couldn't init SDL joystick subsystem: %s", SDL_GetError() );
172 return 0;
173 }
174
175 /* get thenumber of joysticks */
176 numJoysticks = SDL_NumJoysticks();
177
178 /* quit the joystick subsystem if necessary */
179 if (!joyWasInit)
180 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
181
182 return numJoysticks;
183}
184
185/////////////////////////////////////
186// load_controller_config()
187// return value: 1 = OK
188// 0 = fail: couldn't open config section
189
190static int load_controller_config(const char *SectionName, int i, int sdlDeviceIdx)
191{
192 m64p_handle pConfig;
6ff3e5eb 193 char input_str[256], value1_str[NUM_BUTTONS], value2_str[NUM_BUTTONS];
48d52ab5 194 const char *config_ptr;
195 int j;
196
197 /* Open the configuration section for this controller */
198 if (ConfigOpenSection(SectionName, &pConfig) != M64ERR_SUCCESS)
199 {
200 DebugMessage(M64MSG_ERROR, "Couldn't open config section '%s'", SectionName);
201 return 0;
202 }
203
204 /* set SDL device number */
205 controller[i].device = sdlDeviceIdx;
206
207 /* throw warnings if 'plugged' or 'plugin' are missing */
208 if (ConfigGetParameter(pConfig, "plugged", M64TYPE_BOOL, &controller[i].control->Present, sizeof(int)) != M64ERR_SUCCESS)
209 {
210 DebugMessage(M64MSG_WARNING, "missing 'plugged' parameter from config section %s. Setting to 1 (true).", SectionName);
211 controller[i].control->Present = 1;
212 }
213 if (ConfigGetParameter(pConfig, "plugin", M64TYPE_INT, &controller[i].control->Plugin, sizeof(int)) != M64ERR_SUCCESS)
214 {
215 DebugMessage(M64MSG_WARNING, "missing 'plugin' parameter from config section %s. Setting to 1 (none).", SectionName);
216 controller[i].control->Plugin = PLUGIN_NONE;
217 }
218 /* load optional parameters */
219 ConfigGetParameter(pConfig, "mouse", M64TYPE_BOOL, &controller[i].mouse, sizeof(int));
220 if (ConfigGetParameter(pConfig, "MouseSensitivity", M64TYPE_STRING, input_str, 256) == M64ERR_SUCCESS)
221 {
222 if (sscanf(input_str, "%f,%f", &controller[i].mouse_sens[0], &controller[i].mouse_sens[1]) != 2)
223 DebugMessage(M64MSG_WARNING, "parsing error in MouseSensitivity parameter for controller %i", i + 1);
224 }
225 if (ConfigGetParameter(pConfig, "AnalogDeadzone", M64TYPE_STRING, input_str, 256) == M64ERR_SUCCESS)
226 {
227 if (sscanf(input_str, "%i,%i", &controller[i].axis_deadzone[0], &controller[i].axis_deadzone[1]) != 2)
228 DebugMessage(M64MSG_WARNING, "parsing error in AnalogDeadzone parameter for controller %i", i + 1);
229 }
230 if (ConfigGetParameter(pConfig, "AnalogPeak", M64TYPE_STRING, input_str, 256) == M64ERR_SUCCESS)
231 {
232 if (sscanf(input_str, "%i,%i", &controller[i].axis_peak[0], &controller[i].axis_peak[1]) != 2)
233 DebugMessage(M64MSG_WARNING, "parsing error in AnalogPeak parameter for controller %i", i + 1);
234 }
235 /* load configuration for all the digital buttons */
236 for (j = 0; j < X_AXIS; j++)
237 {
238 if (ConfigGetParameter(pConfig, button_names[j], M64TYPE_STRING, input_str, 256) != M64ERR_SUCCESS)
239 {
240 DebugMessage(M64MSG_WARNING, "missing config key '%s' for controller %i button %i", button_names[j], i+1, j);
241 continue;
242 }
243 if ((config_ptr = strstr(input_str, "key")) != NULL)
244 if (sscanf(config_ptr, "key(%i)", (int *) &controller[i].button[j].key) != 1)
245 DebugMessage(M64MSG_WARNING, "parsing error in key() parameter of button '%s' for controller %i", button_names[j], i + 1);
246 if ((config_ptr = strstr(input_str, "button")) != NULL)
247 if (sscanf(config_ptr, "button(%i)", &controller[i].button[j].button) != 1)
248 DebugMessage(M64MSG_WARNING, "parsing error in button() parameter of button '%s' for controller %i", button_names[j], i + 1);
249 if ((config_ptr = strstr(input_str, "axis")) != NULL)
250 {
251 char chAxisDir;
252 if (sscanf(config_ptr, "axis(%d%c,%d", &controller[i].button[j].axis, &chAxisDir, &controller[i].button[j].axis_deadzone) != 3 &&
253 sscanf(config_ptr, "axis(%i%c", &controller[i].button[j].axis, &chAxisDir) != 2)
254 DebugMessage(M64MSG_WARNING, "parsing error in axis() parameter of button '%s' for controller %i", button_names[j], i + 1);
255 controller[i].button[j].axis_dir = (chAxisDir == '+' ? 1 : (chAxisDir == '-' ? -1 : 0));
256 }
257 if ((config_ptr = strstr(input_str, "hat")) != NULL)
258 {
259 char *lastchar = NULL;
260 if (sscanf(config_ptr, "hat(%i %15s", &controller[i].button[j].hat, value1_str) != 2)
261 DebugMessage(M64MSG_WARNING, "parsing error in hat() parameter of button '%s' for controller %i", button_names[j], i + 1);
262 value1_str[15] = 0;
263 /* chop off the last character of value1_str if it is the closing parenthesis */
264 lastchar = &value1_str[strlen(value1_str) - 1];
265 if (lastchar > value1_str && *lastchar == ')') *lastchar = 0;
266 controller[i].button[j].hat_pos = get_hat_pos_by_name(value1_str);
267 }
6ff3e5eb 268 if ((config_ptr = strstr(input_str, "mouse")) != NULL) {
269 // check for mouse_pseudo button first
270 if ((config_ptr = strstr(input_str, "mouse_up")) != NULL) {controller[i].button[j].mouse_up=1; controller[i].mouse_up=1;}
271 else if ((config_ptr = strstr(input_str, "mouse_down")) != NULL) {controller[i].button[j].mouse_down=1; controller[i].mouse_down=1;}
272 else if ((config_ptr = strstr(input_str, "mouse_left")) != NULL) {controller[i].button[j].mouse_left=1; controller[i].mouse_left=1;}
273 else if ((config_ptr = strstr(input_str, "mouse_right")) != NULL) {controller[i].button[j].mouse_right=1; controller[i].mouse_right=1;}
274 else if (sscanf(config_ptr, "mouse(%i)", &controller[i].button[j].mouse) != 1)
48d52ab5 275 DebugMessage(M64MSG_WARNING, "parsing error in mouse() parameter of button '%s' for controller %i", button_names[j], i + 1);
6ff3e5eb 276 }
48d52ab5 277 }
278 /* load configuration for the 2 analog joystick axes */
279 for (j = X_AXIS; j <= Y_AXIS; j++)
280 {
281 int axis_idx = j - X_AXIS;
282 if (ConfigGetParameter(pConfig, button_names[j], M64TYPE_STRING, input_str, 256) != M64ERR_SUCCESS)
283 {
284 DebugMessage(M64MSG_WARNING, "missing config key '%s' for controller %i axis %i", button_names[j], i+1, axis_idx);
285 continue;
286 }
287 if ((config_ptr = strstr(input_str, "key")) != NULL)
288 if (sscanf(config_ptr, "key(%i,%i)", (int *) &controller[i].axis[axis_idx].key_a, (int *) &controller[i].axis[axis_idx].key_b) != 2)
289 DebugMessage(M64MSG_WARNING, "parsing error in key() parameter of axis '%s' for controller %i", button_names[j], i + 1);
290 if ((config_ptr = strstr(input_str, "button")) != NULL)
291 if (sscanf(config_ptr, "button(%i,%i)", &controller[i].axis[axis_idx].button_a, &controller[i].axis[axis_idx].button_b) != 2)
292 DebugMessage(M64MSG_WARNING, "parsing error in button() parameter of axis '%s' for controller %i", button_names[j], i + 1);
293 if ((config_ptr = strstr(input_str, "axis")) != NULL)
294 {
295 char chAxisDir1, chAxisDir2;
296 if (sscanf(config_ptr, "axis(%i%c,%i%c)", &controller[i].axis[axis_idx].axis_a, &chAxisDir1,
297 &controller[i].axis[axis_idx].axis_b, &chAxisDir2) != 4)
298 DebugMessage(M64MSG_WARNING, "parsing error in axis() parameter of axis '%s' for controller %i", button_names[j], i + 1);
299 controller[i].axis[axis_idx].axis_dir_a = (chAxisDir1 == '+' ? 1 : (chAxisDir1 == '-' ? -1 : 0));
300 controller[i].axis[axis_idx].axis_dir_b = (chAxisDir2 == '+' ? 1 : (chAxisDir2 == '-' ? -1 : 0));
301 }
302 if ((config_ptr = strstr(input_str, "hat")) != NULL)
303 {
304 char *lastchar = NULL;
305 if (sscanf(config_ptr, "hat(%i %15s %15s", &controller[i].axis[axis_idx].hat, value1_str, value2_str) != 3)
306 DebugMessage(M64MSG_WARNING, "parsing error in hat() parameter of axis '%s' for controller %i", button_names[j], i + 1);
307 value1_str[15] = value2_str[15] = 0;
308 /* chop off the last character of value2_str if it is the closing parenthesis */
309 lastchar = &value2_str[strlen(value2_str) - 1];
310 if (lastchar > value2_str && *lastchar == ')') *lastchar = 0;
311 controller[i].axis[axis_idx].hat_pos_a = get_hat_pos_by_name(value1_str);
312 controller[i].axis[axis_idx].hat_pos_b = get_hat_pos_by_name(value2_str);
313 }
314 }
315
316 return 1;
317}
318
319static void init_controller_config(int iCtrlIdx, const char *pccDeviceName, eModeType mode)
320{
321 m64p_handle pConfig;
322 char SectionName[32], Param[32], ParamString[128];
323 int j;
324
325 /* Delete the configuration section for this controller, so we can use SetDefaults and save the help comments also */
326 sprintf(SectionName, "Input-SDL-Control%i", iCtrlIdx + 1);
327 ConfigDeleteSection(SectionName);
328 /* Open the configuration section for this controller (create a new one) */
329 if (ConfigOpenSection(SectionName, &pConfig) != M64ERR_SUCCESS)
330 {
331 DebugMessage(M64MSG_ERROR, "Couldn't open config section '%s'", SectionName);
332 return;
333 }
334
335 /* save the general controller parameters */
336 ConfigSetDefaultFloat(pConfig, "version", CONFIG_VERSION, "Mupen64Plus SDL Input Plugin config parameter version number. Please don't change this version number.");
337 ConfigSetDefaultInt(pConfig, "mode", (int) mode, "Controller configuration mode: 0=Fully Manual, 1=Auto with named SDL Device, 2=Fully automatic");
338 ConfigSetDefaultInt(pConfig, "device", controller[iCtrlIdx].device, "Specifies which joystick is bound to this controller: -1=No joystick, 0 or more= SDL Joystick number");
339 ConfigSetDefaultString(pConfig, "name", pccDeviceName, "SDL joystick name (or Keyboard)");
340 ConfigSetDefaultBool(pConfig, "plugged", controller[iCtrlIdx].control->Present, "Specifies whether this controller is 'plugged in' to the simulated N64");
341 ConfigSetDefaultInt(pConfig, "plugin", controller[iCtrlIdx].control->Plugin, "Specifies which type of expansion pak is in the controller: 1=None, 2=Mem pak, 5=Rumble pak");
342 ConfigSetDefaultBool(pConfig, "mouse", controller[iCtrlIdx].mouse, "If True, then mouse buttons may be used with this controller");
343
344 sprintf(Param, "%.2f,%.2f", controller[iCtrlIdx].mouse_sens[0], controller[iCtrlIdx].mouse_sens[1]);
345 ConfigSetDefaultString(pConfig, "MouseSensitivity", Param, "Scaling factor for mouse movements. For X, Y axes.");
346 sprintf(Param, "%i,%i", controller[iCtrlIdx].axis_deadzone[0], controller[iCtrlIdx].axis_deadzone[1]);
347 ConfigSetDefaultString(pConfig, "AnalogDeadzone", Param, "The minimum absolute value of the SDL analog joystick axis to move the N64 controller axis value from 0. For X, Y axes.");
348 sprintf(Param, "%i,%i", controller[iCtrlIdx].axis_peak[0], controller[iCtrlIdx].axis_peak[1]);
349 ConfigSetDefaultString(pConfig, "AnalogPeak", Param, "An absolute value of the SDL joystick axis >= AnalogPeak will saturate the N64 controller axis value (at 80). For X, Y axes. For each axis, this must be greater than the corresponding AnalogDeadzone value");
350
351 /* save configuration for all the digital buttons */
352 for (j = 0; j < X_AXIS; j++ )
353 {
354 const char *Help;
355 int len = 0;
356 ParamString[0] = 0;
357 if (controller[iCtrlIdx].button[j].key > 0)
358 {
359 sprintf(Param, "key(%i) ", controller[iCtrlIdx].button[j].key);
360 strcat(ParamString, Param);
361 }
362 if (controller[iCtrlIdx].button[j].button >= 0)
363 {
364 sprintf(Param, "button(%i) ", controller[iCtrlIdx].button[j].button);
365 strcat(ParamString, Param);
366 }
367 if (controller[iCtrlIdx].button[j].axis >= 0)
368 {
369 if (controller[iCtrlIdx].button[j].axis_deadzone >= 0)
370 sprintf(Param, "axis(%i%c,%i) ", controller[iCtrlIdx].button[j].axis, (controller[iCtrlIdx].button[j].axis_dir == -1) ? '-' : '+',
371 controller[iCtrlIdx].button[j].axis_deadzone);
372 else
373 sprintf(Param, "axis(%i%c) ", controller[iCtrlIdx].button[j].axis, (controller[iCtrlIdx].button[j].axis_dir == -1) ? '-' : '+');
374 strcat(ParamString, Param);
375 }
376 if (controller[iCtrlIdx].button[j].hat >= 0)
377 {
378 sprintf(Param, "hat(%i %s) ", controller[iCtrlIdx].button[j].hat, HAT_POS_NAME(controller[iCtrlIdx].button[j].hat_pos));
379 strcat(ParamString, Param);
380 }
381 if (controller[iCtrlIdx].button[j].mouse >= 0)
382 {
383 sprintf(Param, "mouse(%i) ", controller[iCtrlIdx].button[j].mouse);
384 strcat(ParamString, Param);
385 }
6ff3e5eb 386 if (controller[iCtrlIdx].button[j].mouse_up > 0)
387 {
388 sprintf(Param, "mouse_up ");
389 strcat(ParamString, Param);
390 }
391 if (controller[iCtrlIdx].button[j].mouse_down > 0)
392 {
393 sprintf(Param, "mouse_down ");
394 strcat(ParamString, Param);
395 }
396 if (controller[iCtrlIdx].button[j].mouse_right > 0)
397 {
398 sprintf(Param, "mouse_right ");
399 strcat(ParamString, Param);
400 }
401 if (controller[iCtrlIdx].button[j].mouse_left > 0)
402 {
403 sprintf(Param, "mouse_left ");
404 strcat(ParamString, Param);
405 }
48d52ab5 406 if (j == 0)
407 Help = "Digital button configuration mappings";
408 else
409 Help = NULL;
410 /* if last character is a space, chop it off */
411 len = strlen(ParamString);
412 if (len > 0 && ParamString[len-1] == ' ')
413 ParamString[len-1] = 0;
414 ConfigSetDefaultString(pConfig, button_names[j], ParamString, Help);
415 }
416
417 /* save configuration for the 2 analog axes */
418 for (j = 0; j < 2; j++ )
419 {
420 const char *Help;
421 int len = 0;
422 ParamString[0] = 0;
423 if (controller[iCtrlIdx].axis[j].key_a > 0 && controller[iCtrlIdx].axis[j].key_b > 0)
424 {
425 sprintf(Param, "key(%i,%i) ", controller[iCtrlIdx].axis[j].key_a, controller[iCtrlIdx].axis[j].key_b);
426 strcat(ParamString, Param);
427 }
428 if (controller[iCtrlIdx].axis[j].button_a >= 0 && controller[iCtrlIdx].axis[j].button_b >= 0)
429 {
430 sprintf(Param, "button(%i,%i) ", controller[iCtrlIdx].axis[j].button_a, controller[iCtrlIdx].axis[j].button_b);
431 strcat(ParamString, Param);
432 }
433 if (controller[iCtrlIdx].axis[j].axis_a >= 0 && controller[iCtrlIdx].axis[j].axis_b >= 0)
434 {
435 sprintf(Param, "axis(%i%c,%i%c) ", controller[iCtrlIdx].axis[j].axis_a, (controller[iCtrlIdx].axis[j].axis_dir_a <= 0) ? '-' : '+',
436 controller[iCtrlIdx].axis[j].axis_b, (controller[iCtrlIdx].axis[j].axis_dir_b <= 0) ? '-' : '+' );
437 strcat(ParamString, Param);
438 }
439 if (controller[iCtrlIdx].axis[j].hat >= 0)
440 {
441 sprintf(Param, "hat(%i %s %s) ", controller[iCtrlIdx].axis[j].hat,
442 HAT_POS_NAME(controller[iCtrlIdx].axis[j].hat_pos_a),
443 HAT_POS_NAME(controller[iCtrlIdx].axis[j].hat_pos_b));
444 strcat(ParamString, Param);
445 }
446 if (j == 0)
447 Help = "Analog axis configuration mappings";
448 else
449 Help = NULL;
450 /* if last character is a space, chop it off */
451 len = strlen(ParamString);
452 if (len > 0 && ParamString[len-1] == ' ')
453 ParamString[len-1] = 0;
454 ConfigSetDefaultString(pConfig, button_names[X_AXIS + j], ParamString, Help);
455 }
456
457}
458
459static int setup_auto_controllers(int bPreConfig, int n64CtrlStart, int sdlCtrlIdx, const char *sdlJoyName, eModeType ControlMode[], eModeType OrigControlMode[], char DeviceName[][256])
460{
461 char SectionName[32];
462 int ActiveControllers = 0;
463 int j;
464
465 /* create auto-config section(s) for this joystick (if this joystick is in the InputAutoConfig.ini) */
466 int ControllersFound = auto_set_defaults(sdlCtrlIdx, sdlJoyName);
467 if (ControllersFound == 0)
468 return 0;
469
470 /* copy the auto-config settings to the controller config section, and load our plugin joystick config from this */
471 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlStart + 1);
472 if (OrigControlMode[n64CtrlStart] == E_MODE_FULL_AUTO)
473 auto_copy_inputconfig("AutoConfig0", SectionName, sdlJoyName);
474 else
475 auto_copy_inputconfig("AutoConfig0", SectionName, NULL); // don't overwrite 'name' parameter if original mode was "named auto"
476 if (load_controller_config("AutoConfig0", n64CtrlStart, sdlCtrlIdx) > 0)
477 {
478 if (!bPreConfig)
479 DebugMessage(M64MSG_INFO, "N64 Controller #%i: Using auto-config with SDL joystick %i ('%s')", n64CtrlStart+1, sdlCtrlIdx, sdlJoyName);
480 ActiveControllers++;
481 ConfigSaveSection(SectionName);
482 }
483 else
484 {
485 if (!bPreConfig)
486 DebugMessage(M64MSG_ERROR, "Autoconfig data invalid for SDL joystick '%s'", sdlJoyName);
487 }
488 ConfigDeleteSection("AutoConfig0");
489
490 /* we have to handle the unfortunate case of a USB device mapping more than > 1 controller */
491 if (ControllersFound > 1)
492 {
493 for (j = 1; j < ControllersFound; j++)
494 {
495 char AutoSectionName[32];
496 sprintf(AutoSectionName, "AutoConfig%i", j);
497 /* if this would be > 4th controller, then just delete the auto-config */
498 if (n64CtrlStart + j >= 4)
499 {
500 ConfigDeleteSection(AutoSectionName);
501 continue;
502 }
503 /* look for another N64 controller that is in AUTO mode */
504 if (ControlMode[n64CtrlStart+j] == E_MODE_FULL_AUTO ||
505 (ControlMode[n64CtrlStart+j] == E_MODE_NAMED_AUTO && strncmp(DeviceName[n64CtrlStart+j], sdlJoyName, 255) == 0))
506 {
507 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlStart + j + 1);
508 /* load our plugin joystick settings from the autoconfig */
509 if (load_controller_config(AutoSectionName, n64CtrlStart+j, sdlCtrlIdx) > 0)
510 {
511 /* copy the auto-config settings to the controller config section */
512 if (OrigControlMode[n64CtrlStart+j] == E_MODE_FULL_AUTO)
513 auto_copy_inputconfig(AutoSectionName, SectionName, sdlJoyName);
514 else
515 auto_copy_inputconfig(AutoSectionName, SectionName, NULL); // don't overwrite 'name' parameter if original mode was "named auto"
516 if (!bPreConfig)
517 DebugMessage(M64MSG_INFO, "N64 Controller #%i: Using auto-config with SDL joystick %i ('%s')", n64CtrlStart+j+1, sdlCtrlIdx, sdlJoyName);
518 ActiveControllers++;
519 ConfigSaveSection(SectionName);
520 /* set the local controller mode to Manual so that we won't re-configure this controller in the next loop */
521 ControlMode[n64CtrlStart+j] = E_MODE_MANUAL;
522 }
523 else
524 {
525 if (!bPreConfig)
526 DebugMessage(M64MSG_ERROR, "Autoconfig data invalid for SDL device '%s'", sdlJoyName);
527 }
528 /* delete the autoconfig section */
529 ConfigDeleteSection(AutoSectionName);
530 }
531 }
532 }
533
534 return ActiveControllers;
535}
536
537/* global functions */
538
539/* There are 4 special section parameters: version, mode, device, and name. There are also 24 regular
540 * parameters: plugged, plugin, mouse, MouseSensitivity, DPad R/L/D/U, Start, Z/L/R Trigger, A/B button,
541 * C Button R/L/D/U, Mempak/Rumblepak switch, X/Y Axis, AnalogDeadzone, AnalogPeak.
542 *
543 * The N64 controller configuration behavior is regulated by the 'mode' parameter. If this parameter is
544 * 0 (Fully Manual), then all configuration data is loaded and parsed without changes or autoconfig from
545 * the configuration section. Any errors or warnings are printed. If the mode parameter is 1 (Auto with
546 * named SDL Device), then the code below searches through the available SDL joysticks for one which
547 * matches with the 'name' parameter in the config section. If a match is found, then the corresponding
548 * autoconfig is loaded and the regular parameters from the config section are updated with the
549 * autoconfig'ed settings. If the mode parameter is 2 (Fully Auto), then the code below searches through
550 * all available detected SDL joysticks to try and find an autoconfig for each. If an autoconfig match is
551 * found, then it is loaded and all config parameters (including device, name, and the regular parameters)
552 * are updated.
553 *
554 * This function is called with bPreConfig=true from the PluginStartup() function. The purpose of this
555 * call is to load the 4 configuration sections with autoconfig data if necessary for a GUI front-end.
556 * When we are called with bPreConfig=true, we should only print warning/error messages and not info/status.
557 * This function is also called right before running the ROM game from InitiateControllers() with
558 * bPreConfig=false. We should only print informational messages here, because we do not want to duplicate
559 * console output with the console-ui front-end (since the PluginStart() and InitiateControllers()
560 * functions are called in quick sequence from the console-ui).
561 */
562
563void load_configuration(int bPreConfig)
564{
565 char SectionName[32];
566 int joy_plugged = 0;
567 int n64CtrlIdx, sdlCtrlIdx, j;
568 int sdlNumDevUsed = 0;
569 int sdlDevicesUsed[4];
570 eModeType OrigControlMode[4], ControlMode[4];
571 int ControlDevice[4];
572 char DeviceName[4][256];
573 int ActiveControllers = 0;
574 int sdlNumJoysticks = get_sdl_num_joysticks();
575 float fVersion = 0.0f;
576 const char *sdl_name;
577 int ControllersFound = 0;
578
579 /* tell user how many SDL joysticks are available */
580 if (!bPreConfig)
581 DebugMessage(M64MSG_INFO, "%i SDL joysticks were found.", sdlNumJoysticks);
582
583 /* loop through 4 N64 controllers, initializing and validating special section parameters */
584 for (n64CtrlIdx=0; n64CtrlIdx < 4; n64CtrlIdx++)
585 {
586 m64p_handle pConfig;
587 /* reset the controller configuration */
588 clear_controller(n64CtrlIdx);
589
590 /* Open the configuration section for this controller */
591 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlIdx + 1);
592 if (ConfigOpenSection(SectionName, &pConfig) != M64ERR_SUCCESS)
593 {
594 // this should never happen
595 DebugMessage(M64MSG_ERROR, "Couldn't open config section '%s'. Aborting...", SectionName);
596 return;
597 }
598 /* Check version number, and if it doesn't match: delete the config section */
599 fVersion = 0.0f;
600 if (ConfigGetParameter(pConfig, "version", M64TYPE_FLOAT, &fVersion, sizeof(float)) != M64ERR_SUCCESS || ((int) fVersion) != ((int) CONFIG_VERSION))
601 {
602 DebugMessage(M64MSG_WARNING, "Missing or incompatible config section '%s'. Clearing.", SectionName);
603 ConfigDeleteSection(SectionName);
604 // set local controller default parameters
605 OrigControlMode[n64CtrlIdx] = ControlMode[n64CtrlIdx] = E_MODE_FULL_AUTO;
606 ControlDevice[n64CtrlIdx] = DEVICE_NO_JOYSTICK;
607 DeviceName[n64CtrlIdx][0] = 0;
608 // write blank config for GUI front-ends
609 init_controller_config(n64CtrlIdx, "", E_MODE_FULL_AUTO);
610 // save it to the file too
611 ConfigSaveSection(SectionName);
612 }
613 else
614 {
615 if (ConfigGetParameter(pConfig, "mode", M64TYPE_INT, &OrigControlMode[n64CtrlIdx], sizeof(int)) != M64ERR_SUCCESS ||
616 (int) OrigControlMode[n64CtrlIdx] < 0 || (int) OrigControlMode[n64CtrlIdx] > 2)
617 {
618 if (!bPreConfig)
619 DebugMessage(M64MSG_WARNING, "Missing or invalid 'mode' parameter in config section '%s'. Setting to 2 (Fully Auto)", SectionName);
620 OrigControlMode[n64CtrlIdx] = E_MODE_FULL_AUTO;
621 }
622 ControlMode[n64CtrlIdx] = OrigControlMode[n64CtrlIdx];
623 if (ConfigGetParameter(pConfig, "device", M64TYPE_INT, &ControlDevice[n64CtrlIdx], sizeof(int)) != M64ERR_SUCCESS)
624 {
625 if (!bPreConfig)
626 DebugMessage(M64MSG_WARNING, "Missing 'device' parameter in config section '%s'. Setting to -1 (No joystick)", SectionName);
627 ControlDevice[n64CtrlIdx] = DEVICE_NO_JOYSTICK;
628 }
629 if (ConfigGetParameter(pConfig, "name", M64TYPE_STRING, DeviceName[n64CtrlIdx], 256) != M64ERR_SUCCESS)
630 {
631 DeviceName[n64CtrlIdx][0] = 0;
632 }
633 }
634 }
635
636 /* loop through 4 N64 controllers and set up those in Fully Manual mode */
637 for (n64CtrlIdx=0; n64CtrlIdx < 4; n64CtrlIdx++)
638 {
639 if (ControlMode[n64CtrlIdx] != E_MODE_MANUAL)
640 continue;
641 /* load the stored configuration (disregard any errors) */
642 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlIdx + 1);
643 load_controller_config(SectionName, n64CtrlIdx, ControlDevice[n64CtrlIdx]);
644 /* if this config uses an SDL joystick, mark it as used */
645 if (ControlDevice[n64CtrlIdx] == DEVICE_NO_JOYSTICK)
646 {
647 if (!bPreConfig)
648 DebugMessage(M64MSG_INFO, "N64 Controller #%i: Using manual config with no SDL joystick (keyboard/mouse only)", n64CtrlIdx+1);
649 }
650 else
651 {
652 sdlDevicesUsed[sdlNumDevUsed++] = ControlDevice[n64CtrlIdx];
653 if (!bPreConfig)
654 DebugMessage(M64MSG_INFO, "N64 Controller #%i: Using manual config for SDL joystick %i", n64CtrlIdx+1, ControlDevice[n64CtrlIdx]);
655 }
656 ActiveControllers++;
657 }
658
659 /* now loop through again, setting up those in Named Auto mode */
660 for (n64CtrlIdx=0; n64CtrlIdx < 4; n64CtrlIdx++)
661 {
662 if (ControlMode[n64CtrlIdx] != E_MODE_NAMED_AUTO)
663 continue;
664 /* if name is empty, then use full auto mode instead */
665 if (DeviceName[n64CtrlIdx][0] == 0)
666 {
667 ControlMode[n64CtrlIdx] = E_MODE_FULL_AUTO;
668 continue;
669 }
670 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlIdx + 1);
671 /* if user is looking for a keyboard, set that up */
672 if (strcasecmp(DeviceName[n64CtrlIdx], "Keyboard") == 0)
673 {
674 auto_set_defaults(DEVICE_NO_JOYSTICK, "Keyboard");
675 if (load_controller_config("AutoConfig0", n64CtrlIdx, DEVICE_NO_JOYSTICK) > 0)
676 {
677 if (!bPreConfig)
678 DebugMessage(M64MSG_INFO, "N64 Controller #%i: Using auto-config for keyboard", n64CtrlIdx+1);
679 /* copy the auto-config settings to the controller config section */
680 auto_copy_inputconfig("AutoConfig0", SectionName, "Keyboard");
681 ActiveControllers++;
682 ConfigSaveSection(SectionName);
683 }
684 else
685 {
686 DebugMessage(M64MSG_ERROR, "Autoconfig keyboard setup invalid");
687 }
688 ConfigDeleteSection("AutoConfig0");
689 continue;
690 }
691 /* search for an unused SDL device with the matching name */
692 for (sdlCtrlIdx=0; sdlCtrlIdx < sdlNumJoysticks; sdlCtrlIdx++)
693 {
694 /* check if this one is in use */
695 int deviceAlreadyUsed = 0;
696 for (j = 0; j < sdlNumDevUsed; j++)
697 {
698 if (sdlDevicesUsed[j] == sdlCtrlIdx)
699 deviceAlreadyUsed = 1;
700 }
701 if (deviceAlreadyUsed)
702 continue;
703 /* check if the name matches */
704 sdl_name = get_sdl_joystick_name(sdlCtrlIdx);
705 if (sdl_name != NULL && strncmp(DeviceName[n64CtrlIdx], sdl_name, 255) == 0)
706 {
707 /* set up one or more controllers for this SDL device, if present in InputAutoConfig.ini */
708 int ControllersFound = setup_auto_controllers(bPreConfig, n64CtrlIdx, sdlCtrlIdx, sdl_name, ControlMode, OrigControlMode, DeviceName);
709 if (ControllersFound == 0)
710 {
711 // error: no auto-config found for this SDL device
712 DebugMessage(M64MSG_ERROR, "No auto-config found for joystick named '%s' in InputAutoConfig.ini", sdl_name);
713 // mark this device as being used just so we don't complain about it again
714 sdlDevicesUsed[sdlNumDevUsed++] = sdlCtrlIdx;
715 // quit looking for SDL joysticks which match the name, because there's no valid autoconfig for that name.
716 // this controller will be unused; skip to the next one
717 break;
718 }
719 /* mark this sdl device as used */
720 sdlDevicesUsed[sdlNumDevUsed++] = sdlCtrlIdx;
721 ActiveControllers += ControllersFound;
722 break;
723 }
724 }
725 /* if we didn't find a match for this joystick name, then set the controller to fully auto */
726 if (sdlCtrlIdx == sdlNumJoysticks)
727 {
728 if (!bPreConfig)
729 DebugMessage(M64MSG_WARNING, "N64 Controller #%i: No SDL joystick found matching name '%s'. Using full auto mode.", n64CtrlIdx+1, DeviceName[n64CtrlIdx]);
730 ControlMode[n64CtrlIdx] = E_MODE_FULL_AUTO;
731 }
732 }
733
734 /* Final loop through N64 controllers, setting up those in Full Auto mode */
735 for (n64CtrlIdx=0; n64CtrlIdx < 4; n64CtrlIdx++)
736 {
737 if (ControlMode[n64CtrlIdx] != E_MODE_FULL_AUTO)
738 continue;
739 sprintf(SectionName, "Input-SDL-Control%i", n64CtrlIdx + 1);
740 /* search for an unused SDL device */
741 for (sdlCtrlIdx=0; sdlCtrlIdx < sdlNumJoysticks; sdlCtrlIdx++)
742 {
743 /* check if this one is in use */
744 int deviceAlreadyUsed = 0;
745 for (j = 0; j < sdlNumDevUsed; j++)
746 {
747 if (sdlDevicesUsed[j] == sdlCtrlIdx)
748 deviceAlreadyUsed = 1;
749 }
750 if (deviceAlreadyUsed)
751 continue;
752 /* set up one or more controllers for this SDL device, if present in InputAutoConfig.ini */
753 sdl_name = get_sdl_joystick_name(sdlCtrlIdx);
754 ControllersFound = setup_auto_controllers(bPreConfig, n64CtrlIdx, sdlCtrlIdx, sdl_name, ControlMode, OrigControlMode, DeviceName);
755 if (!bPreConfig && ControllersFound == 0)
756 {
757 // error: no auto-config found for this SDL device
758 DebugMessage(M64MSG_ERROR, "No auto-config found for joystick named '%s' in InputAutoConfig.ini", sdl_name);
759 // mark this device as being used just so we don't complain about it again
760 sdlDevicesUsed[sdlNumDevUsed++] = sdlCtrlIdx;
761 // keep trying more SDL devices to see if we can auto-config one for this N64 controller
762 continue;
763 }
764 /* mark this sdl device as used */
765 sdlDevicesUsed[sdlNumDevUsed++] = sdlCtrlIdx;
766 ActiveControllers += ControllersFound;
767 break;
768 }
769 /* if this N64 controller was not activated, set device to -1 */
770 if (sdlCtrlIdx == sdlNumJoysticks)
771 {
772 m64p_handle section;
773 if (ConfigOpenSection(SectionName, &section) == M64ERR_SUCCESS)
774 {
775 const int iNoDevice = -1;
776 ConfigSetParameter(section, "device", M64TYPE_INT, &iNoDevice);
777 if (OrigControlMode[n64CtrlIdx] == E_MODE_FULL_AUTO)
778 ConfigSetParameter(section, "name", M64TYPE_STRING, "");
779 ConfigSaveSection(SectionName);
780 }
781 }
782 }
783
784 /* fallback to keyboard if no controllers were configured */
785 if (ActiveControllers == 0)
786 {
787 if (!bPreConfig)
788 DebugMessage(M64MSG_INFO, "N64 Controller #1: Forcing default keyboard configuration");
789 auto_set_defaults(DEVICE_NO_JOYSTICK, "Keyboard");
790 if (load_controller_config("AutoConfig0", 0, DEVICE_NO_JOYSTICK) > 0)
791 {
792 /* copy the auto-config settings to the controller config section */
793 if (OrigControlMode[0] == E_MODE_FULL_AUTO)
794 auto_copy_inputconfig("AutoConfig0", "Input-SDL-Control1", "Keyboard");
795 else
796 auto_copy_inputconfig("AutoConfig0", "Input-SDL-Control1", NULL); // don't overwrite 'name' parameter
797 ActiveControllers++;
798 ConfigSaveSection("Input-SDL-Control1");
799 }
800 else
801 {
802 DebugMessage(M64MSG_ERROR, "Autoconfig keyboard setup invalid");
803 }
804 ConfigDeleteSection("AutoConfig0");
805 }
806
807 /* see how many joysticks are plugged in */
808 joy_plugged = 0;
809 for (j = 0; j < 4; j++)
810 {
811 if (controller[j].control->Present)
812 joy_plugged++;
813 }
814
815 /* print out summary info message */
816 if (!bPreConfig)
817 {
818 if (joy_plugged > 0)
819 {
820 DebugMessage(M64MSG_INFO, "%i controller(s) found, %i plugged in and usable in the emulator", ActiveControllers, joy_plugged);
821 }
822 else
823 {
824 if (ActiveControllers == 0)
825 DebugMessage(M64MSG_WARNING, "No joysticks/controllers found");
826 else
827 DebugMessage(M64MSG_WARNING, "%i controllers found, but none were 'plugged in'", ActiveControllers);
828 }
829 }
830
831}
832
833