48d52ab5 |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus-input-sdl - autoconfig.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 <string.h> |
23 | |
24 | #include "m64p_types.h" |
25 | #include "m64p_config.h" |
26 | #include "osal_preproc.h" |
27 | #include "autoconfig.h" |
28 | #include "plugin.h" |
29 | |
30 | /* local definitions */ |
31 | #define INI_FILE_NAME "InputAutoCfg.ini" |
32 | typedef struct { |
33 | m64p_handle pSrc; |
34 | m64p_handle pDst; |
35 | } SCopySection; |
36 | |
37 | /* local functions */ |
38 | static char *StripSpace(char *pIn) |
39 | { |
40 | char *pEnd = pIn + strlen(pIn) - 1; |
41 | |
42 | while (*pIn == ' ' || *pIn == '\t' || *pIn == '\r' || *pIn == '\n') |
43 | pIn++; |
44 | |
45 | while (pIn <= pEnd && (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\r' || *pEnd == '\n')) |
46 | *pEnd-- = 0; |
47 | |
48 | return pIn; |
49 | } |
50 | |
51 | static void CopyParamCallback(void * context, const char *ParamName, m64p_type ParamType) |
52 | { |
53 | SCopySection *pCpyContext = (SCopySection *) context; |
54 | int paramInt; |
55 | float paramFloat; |
56 | char paramString[1024]; |
57 | |
58 | // handle the parameter copy depending upon type |
59 | switch (ParamType) |
60 | { |
61 | case M64TYPE_INT: |
62 | case M64TYPE_BOOL: |
63 | if (ConfigGetParameter(pCpyContext->pSrc, ParamName, ParamType, ¶mInt, sizeof(int)) == M64ERR_SUCCESS) |
64 | ConfigSetParameter(pCpyContext->pDst, ParamName, ParamType, ¶mInt); |
65 | break; |
66 | case M64TYPE_FLOAT: |
67 | if (ConfigGetParameter(pCpyContext->pSrc, ParamName, ParamType, ¶mFloat, sizeof(float)) == M64ERR_SUCCESS) |
68 | ConfigSetParameter(pCpyContext->pDst, ParamName, ParamType, ¶mFloat); |
69 | break; |
70 | case M64TYPE_STRING: |
71 | if (ConfigGetParameter(pCpyContext->pSrc, ParamName, ParamType, paramString, 1024) == M64ERR_SUCCESS) |
72 | ConfigSetParameter(pCpyContext->pDst, ParamName, ParamType, paramString); |
73 | break; |
74 | default: |
75 | // this should never happen |
76 | DebugMessage(M64MSG_ERROR, "Unknown source parameter type %i in copy callback", (int) ParamType); |
77 | return; |
78 | } |
79 | } |
80 | |
81 | /* global functions */ |
82 | int auto_copy_inputconfig(const char *pccSourceSectionName, const char *pccDestSectionName, const char *sdlJoyName) |
83 | { |
84 | SCopySection cpyContext; |
85 | |
86 | if (ConfigOpenSection(pccSourceSectionName, &cpyContext.pSrc) != M64ERR_SUCCESS) |
87 | { |
88 | DebugMessage(M64MSG_ERROR, "auto_copy_inputconfig: Couldn't open source config section '%s' for copying", pccSourceSectionName); |
89 | return 0; |
90 | } |
91 | |
92 | if (ConfigOpenSection(pccDestSectionName, &cpyContext.pDst) != M64ERR_SUCCESS) |
93 | { |
94 | DebugMessage(M64MSG_ERROR, "auto_copy_inputconfig: Couldn't open destination config section '%s' for copying", pccDestSectionName); |
95 | return 0; |
96 | } |
97 | |
98 | // set the 'name' parameter |
99 | if (sdlJoyName != NULL) |
100 | { |
101 | if (ConfigSetParameter(cpyContext.pDst, "name", M64TYPE_STRING, sdlJoyName) != M64ERR_SUCCESS) |
102 | { |
103 | DebugMessage(M64MSG_ERROR, "auto_copy_inputconfig: Couldn't set 'name' parameter to '%s' in section '%s'", sdlJoyName, pccDestSectionName); |
104 | return 0; |
105 | } |
106 | } |
107 | |
108 | // the copy gets done by the callback function |
109 | if (ConfigListParameters(cpyContext.pSrc, (void *) &cpyContext, CopyParamCallback) != M64ERR_SUCCESS) |
110 | { |
111 | DebugMessage(M64MSG_ERROR, "auto_copy_inputconfig: parameter list copy failed"); |
112 | return 0; |
113 | } |
114 | |
115 | return 1; |
116 | } |
117 | |
118 | int auto_set_defaults(int iDeviceIdx, const char *joySDLName) |
119 | { |
120 | FILE *pfIn; |
121 | m64p_handle pConfig = NULL; |
122 | const char *CfgFilePath = ConfigGetSharedDataFilepath(INI_FILE_NAME); |
123 | enum { E_NAME_SEARCH, E_NAME_FOUND, E_PARAM_READ } eParseState; |
124 | char *pchIni, *pchNextLine, *pchCurLine; |
125 | long iniLength; |
126 | int ControllersFound = 0; |
127 | |
128 | /* if we couldn't get a name (no joystick plugged in to given port), then return with a failure */ |
129 | if (joySDLName == NULL) |
130 | return 0; |
131 | /* if we couldn't find the shared data file, dump an error and return */ |
132 | if (CfgFilePath == NULL || strlen(CfgFilePath) < 1) |
133 | { |
134 | DebugMessage(M64MSG_ERROR, "Couldn't find config file '%s'", INI_FILE_NAME); |
135 | return 0; |
136 | } |
137 | |
138 | /* read the input auto-config .ini file */ |
139 | pfIn = fopen(CfgFilePath, "rb"); |
140 | if (pfIn == NULL) |
141 | { |
142 | DebugMessage(M64MSG_ERROR, "Couldn't open config file '%s'", CfgFilePath); |
143 | return 0; |
144 | } |
145 | fseek(pfIn, 0L, SEEK_END); |
146 | iniLength = ftell(pfIn); |
147 | fseek(pfIn, 0L, SEEK_SET); |
148 | pchIni = (char *) malloc(iniLength + 1); |
149 | if (pchIni == NULL) |
150 | { |
151 | DebugMessage(M64MSG_ERROR, "Couldn't allocate %li bytes for config file '%s'", iniLength, CfgFilePath); |
152 | fclose(pfIn); |
153 | return 0; |
154 | } |
155 | if (fread(pchIni, 1, iniLength, pfIn) != iniLength) |
156 | { |
157 | DebugMessage(M64MSG_ERROR, "File read failed for %li bytes of config file '%s'", iniLength, CfgFilePath); |
158 | free(pchIni); |
159 | fclose(pfIn); |
160 | return 0; |
161 | } |
162 | fclose(pfIn); |
163 | pchIni[iniLength] = 0; |
164 | |
165 | /* parse the INI file, line by line */ |
166 | pchNextLine = pchIni; |
167 | eParseState = E_NAME_SEARCH; |
168 | while (pchNextLine != NULL && *pchNextLine != 0) |
169 | { |
170 | char *pivot = NULL; |
171 | /* set up character pointers */ |
172 | pchCurLine = pchNextLine; |
173 | pchNextLine = strchr(pchNextLine, '\n'); |
174 | if (pchNextLine != NULL) |
175 | *pchNextLine++ = 0; |
176 | pchCurLine = StripSpace(pchCurLine); |
177 | |
178 | /* handle blank/comment lines */ |
179 | if (strlen(pchCurLine) < 1 || *pchCurLine == ';' || *pchCurLine == '#') |
180 | continue; |
181 | |
182 | /* handle section (joystick name in ini file) */ |
183 | if (*pchCurLine == '[' && pchCurLine[strlen(pchCurLine)-1] == ']') |
184 | { |
185 | char Word[64]; |
186 | char *wordPtr; |
187 | int joyFound = 1; |
188 | |
189 | if (eParseState == E_PARAM_READ) |
190 | { |
191 | /* we've finished parsing all parameters for the discovered input device */ |
192 | free(pchIni); |
193 | return ControllersFound; |
194 | } |
195 | else if (eParseState == E_NAME_FOUND) |
196 | { |
197 | /* this is an equivalent device name to the one we're looking for (and found); keep looking for parameters */ |
198 | continue; |
199 | } |
200 | /* we need to look through the device name word by word to see if it matches the joySDLName that we're looking for */ |
201 | pchCurLine[strlen(pchCurLine)-1] = 0; |
202 | wordPtr = StripSpace(pchCurLine + 1); |
203 | /* first, if there is a preceding system name in this .ini device name, and the system matches, then strip out */ |
204 | #if defined(__unix__) |
205 | if (strncmp(wordPtr, "Unix:", 5) == 0) |
206 | wordPtr = StripSpace(wordPtr + 5); |
207 | #endif |
208 | #if defined(__linux__) |
209 | if (strncmp(wordPtr, "Linux:", 6) == 0) |
210 | wordPtr = StripSpace(wordPtr + 6); |
211 | #endif |
212 | #if defined(__APPLE__) |
213 | if (strncmp(wordPtr, "OSX:", 4) == 0) |
214 | wordPtr = StripSpace(wordPtr + 4); |
215 | #endif |
216 | #if defined(WIN32) |
217 | if (strncmp(wordPtr, "Win32:", 6) == 0) |
218 | wordPtr = StripSpace(wordPtr + 6); |
219 | #endif |
220 | /* search in the .ini device name for all the words in the joystick name. If any are missing, then this is not the right joystick model */ |
221 | while (wordPtr != NULL && strlen(wordPtr) > 0) |
222 | { |
223 | char *nextSpace = strchr(wordPtr, ' '); |
224 | if (nextSpace == NULL) |
225 | { |
226 | strncpy(Word, wordPtr, 63); |
227 | Word[63] = 0; |
228 | wordPtr = NULL; |
229 | } |
230 | else |
231 | { |
232 | int length = (int) (nextSpace - wordPtr); |
233 | if (length > 63) length = 63; |
234 | strncpy(Word, wordPtr, length); |
235 | Word[length] = 0; |
236 | wordPtr = nextSpace + 1; |
237 | } |
238 | if (strcasestr(joySDLName, Word) == NULL) |
239 | joyFound = 0; |
240 | } |
241 | /* if we found the right joystick, then open up the core config section to store parameters and set the 'device' param */ |
242 | if (joyFound) |
243 | { |
244 | char SectionName[32]; |
245 | sprintf(SectionName, "AutoConfig%i", ControllersFound); |
246 | if (ConfigOpenSection(SectionName, &pConfig) != M64ERR_SUCCESS) |
247 | { |
248 | DebugMessage(M64MSG_ERROR, "auto_set_defaults(): Couldn't open config section '%s'", SectionName); |
249 | free(pchIni); |
250 | return 0; |
251 | } |
252 | eParseState = E_NAME_FOUND; |
253 | ControllersFound++; |
254 | ConfigSetParameter(pConfig, "device", M64TYPE_INT, &iDeviceIdx); |
255 | } |
256 | continue; |
257 | } |
258 | |
259 | /* handle parameters */ |
260 | pivot = strchr(pchCurLine, '='); |
261 | if (pivot != NULL) |
262 | { |
263 | /* if we haven't found the correct section yet, just skip this */ |
264 | if (eParseState == E_NAME_SEARCH) |
265 | continue; |
266 | eParseState = E_PARAM_READ; |
267 | /* otherwise, store this parameter in the current active joystick config */ |
268 | *pivot++ = 0; |
269 | pchCurLine = StripSpace(pchCurLine); |
270 | pivot = StripSpace(pivot); |
271 | if (strcasecmp(pchCurLine, "plugin") == 0 || strcasecmp(pchCurLine, "device") == 0) |
272 | { |
273 | int iVal = atoi(pivot); |
274 | ConfigSetParameter(pConfig, pchCurLine, M64TYPE_INT, &iVal); |
275 | } |
276 | else if (strcasecmp(pchCurLine, "plugged") == 0 || strcasecmp(pchCurLine, "mouse") == 0) |
277 | { |
278 | int bVal = (strcasecmp(pivot, "true") == 0); |
279 | ConfigSetParameter(pConfig, pchCurLine, M64TYPE_BOOL, &bVal); |
280 | } |
281 | else |
282 | { |
283 | ConfigSetParameter(pConfig, pchCurLine, M64TYPE_STRING, pivot); |
284 | } |
285 | continue; |
286 | } |
287 | |
288 | /* handle keywords */ |
289 | if (pchCurLine[strlen(pchCurLine)-1] == ':') |
290 | { |
291 | /* if we haven't found the correct section yet, just skip this */ |
292 | if (eParseState == E_NAME_SEARCH) |
293 | continue; |
294 | /* otherwise parse the keyword */ |
295 | if (strcmp(pchCurLine, "__NextController:") == 0) |
296 | { |
297 | char SectionName[32]; |
298 | /* if there are no more N64 controller spaces left, then exit */ |
299 | if (ControllersFound == 4) |
300 | { |
301 | free(pchIni); |
302 | return ControllersFound; |
303 | } |
304 | /* otherwise go to the next N64 controller */ |
305 | sprintf(SectionName, "AutoConfig%i", ControllersFound); |
306 | if (ConfigOpenSection(SectionName, &pConfig) != M64ERR_SUCCESS) |
307 | { |
308 | DebugMessage(M64MSG_ERROR, "auto_set_defaults(): Couldn't open config section '%s'", SectionName); |
309 | free(pchIni); |
310 | return ControllersFound; |
311 | } |
312 | ControllersFound++; |
313 | ConfigSetParameter(pConfig, "device", M64TYPE_INT, &iDeviceIdx); |
314 | } |
315 | else |
316 | { |
317 | DebugMessage(M64MSG_ERROR, "Unknown keyword '%s' in %s", pchCurLine, INI_FILE_NAME); |
318 | } |
319 | continue; |
320 | } |
321 | |
322 | /* unhandled line in .ini file */ |
323 | DebugMessage(M64MSG_ERROR, "Invalid line in %s: '%s'", INI_FILE_NAME, pchCurLine); |
324 | } |
325 | |
326 | if (eParseState == E_PARAM_READ) |
327 | { |
328 | /* we've finished parsing all parameters for the discovered input device, which is the last in the .ini file */ |
329 | free(pchIni); |
330 | return ControllersFound; |
331 | } |
332 | |
333 | free(pchIni); |
334 | return 0; |
335 | } |
336 | |
337 | |