INPUT: Added alternate C Button and mouse_keys
[mupen64plus-pandora.git] / source / mupen64plus-input-sdl / src / config.c
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
42 typedef enum {
43     E_MODE_MANUAL = 0,
44     E_MODE_NAMED_AUTO,
45     E_MODE_FULL_AUTO
46  } eModeType;
47
48 static 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",
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
69     "X Axis",       // X_AXIS
70     "Y Axis"        // Y_AXIS
71 };
72
73 /* static functions */
74 static 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
88 static 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;
96     for( b = 0; b < NUM_BUTTONS; b++ )
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;
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;
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
127 static 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
162 static 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
190 static int load_controller_config(const char *SectionName, int i, int sdlDeviceIdx)
191 {
192     m64p_handle pConfig;
193     char input_str[256], value1_str[NUM_BUTTONS], value2_str[NUM_BUTTONS];
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         }
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)
275                 DebugMessage(M64MSG_WARNING, "parsing error in mouse() parameter of button '%s' for controller %i", button_names[j], i + 1);
276                 }
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
319 static 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         }
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         }
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
459 static 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   
563 void 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