]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/profile.cpp
Fix for RISC SUBC opcode, more tweaks to controller config.
[virtualjaguar] / src / gui / profile.cpp
1 //
2 // profile.cpp - Global profile storage/definition/manipulation
3 //
4 // by James Hammons
5 // (C) 2013 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  05/01/2013  Created this file
12 //
13
14
15 #include "profile.h"
16 #include <QtGui>
17 #include "gamepad.h"
18 #include "log.h"
19 #include "settings.h"
20
21
22 #define MAX_DEVICES  64
23
24
25 Profile profile[MAX_PROFILES];
26 int controller1Profile;
27 int controller2Profile;
28 int gamepad1Slot;
29 int gamepad2Slot;
30 int numberOfProfiles;
31 int numberOfDevices;
32 char deviceNames[MAX_DEVICES][128];
33
34 // This is so that new devices have something reasonable to show for default
35 uint32_t defaultMap[21] = {
36         'S', 'X', 'Z', 'C', '-','7', '4', '1', '0', '8', '5', '2', '=', '9', '6',
37         '3', 'L', 'K', 'J', 'O', 'P'
38 };
39
40 // Function Prototypes
41 int ConnectProfileToDevice(int deviceNum);
42 int FindProfileForDevice(int deviceNum, int preferred, int * found);
43
44
45 void ReadProfiles(QSettings * set)
46 {
47         // Assume no profiles, until we read them
48         numberOfProfiles = 0;
49
50         // There is always at least one device present, and it's the keyboard
51         // (hey, we're PC centric here ;-)
52         numberOfDevices = 1;
53         strcpy(deviceNames[0], "Keyboard");
54
55         // Read the rest of the devices (if any)
56         numberOfDevices += set->beginReadArray("devices");
57
58         for(int i=1; i<numberOfDevices; i++)
59         {
60                 set->setArrayIndex(i - 1);
61                 strcpy(deviceNames[i], set->value("deviceName").toString().toAscii().data());
62 //printf("Read device name: %s\n", deviceNames[i]);
63         }
64
65         set->endArray();
66         numberOfProfiles = set->beginReadArray("profiles");
67 //printf("Number of profiles: %u\n", numberOfProfiles);
68
69         for(int i=0; i<numberOfProfiles; i++)
70         {
71                 set->setArrayIndex(i);
72                 profile[i].device = set->value("deviceNum").toInt();
73                 strcpy(profile[i].mapName, set->value("mapName").toString().toAscii().data());
74                 profile[i].preferredController = set->value("preferredController").toInt();
75
76                 for(int j=0; j<21; j++)
77                 {
78                         QString string = QString("map%1").arg(j);
79                         profile[i].map[j] = set->value(string).toInt();
80                 }
81 //printf("Profile #%u: device=%u (%s)\n", i, profile[i].device, deviceNames[profile[i].device]);
82         }
83
84         set->endArray();
85
86 //printf("Number of profiles found: %u\n", numberOfProfiles);
87         // Set up a reasonable default if no profiles were found
88         if (numberOfProfiles == 0)
89         {
90 //printf("Setting up default profile...\n");
91                 numberOfProfiles++;
92                 profile[0].device = 0;  // Keyboard is always device #0
93                 strcpy(profile[0].mapName, "Default");
94                 profile[0].preferredController = CONTROLLER1;
95
96                 for(int i=0; i<21; i++)
97                         profile[0].map[i] = defaultMap[i];
98         }
99 }
100
101
102 void WriteProfiles(QSettings * set)
103 {
104 #if 0
105         // Don't write anything for now...
106         return;
107 #endif
108         // NB: Should only do this if something changed; otherwise, no need to do
109         //     this.
110         set->beginWriteArray("devices");
111
112         for(int i=1; i<numberOfDevices; i++)
113         {
114                 set->setArrayIndex(i - 1);
115                 set->setValue("deviceName", deviceNames[i]);
116         }
117
118         set->endArray();
119         set->beginWriteArray("profiles");
120
121         for(int i=0; i<numberOfProfiles; i++)
122         {
123                 set->setArrayIndex(i);
124                 set->setValue("deviceNum", profile[i].device);
125                 set->setValue("mapName", profile[i].mapName);
126                 set->setValue("preferredController", profile[i].preferredController);
127
128                 for(int j=0; j<21; j++)
129                 {
130                         QString string = QString("map%1").arg(j);
131                         set->setValue(string, profile[i].map[j]);
132                 }
133         }
134
135         set->endArray();
136 }
137
138
139 int GetFreeProfile(void)
140 {
141         // Check for too many, return -1 if so
142         if (numberOfProfiles == MAX_PROFILES)
143                 return -1;
144
145         int profileNum = numberOfProfiles;
146         numberOfProfiles++;
147         return profileNum;
148 }
149
150
151 void DeleteProfile(int profileToDelete)
152 {
153         // Sanity check
154         if (profileToDelete >= numberOfProfiles)
155                 return;
156
157         // Trivial case: Profile at end of the array
158         if (profileToDelete == (numberOfProfiles - 1))
159         {
160                 numberOfProfiles--;
161                 return;
162         }
163
164 //      memmove(dest, src, bytesToMove);
165         memmove(&profile[profileToDelete], &profile[profileToDelete + 1], ((numberOfProfiles - 1) - profileToDelete) * sizeof(Profile));
166         numberOfProfiles--;
167 }
168
169
170 int FindDeviceNumberForName(const char * name)
171 {
172         for(int i=0; i<numberOfDevices; i++)
173         {
174                 if (strcmp(deviceNames[i], name) == 0)
175                         return i;
176         }
177
178         if (numberOfDevices == MAX_DEVICES)
179                 return -1;
180
181         // If the device wasn't found, it must be new; so add it to the list.
182         int deviceNum = numberOfDevices;
183         deviceNames[deviceNum][127] = 0;
184         strncpy(deviceNames[deviceNum], name, 127);
185         numberOfDevices++;
186
187         return deviceNum;
188 }
189
190
191 int FindMappingsForDevice(int deviceNum, QComboBox * combo)
192 {
193         int found = 0;
194
195         for(int i=0; i<numberOfProfiles; i++)
196         {
197 //This should *never* be the case--all profiles in list are *good*
198 //              if (profile[i].device == -1)
199 //                      continue;
200
201                 if (profile[i].device == deviceNum)
202                 {
203                         combo->addItem(profile[i].mapName, i);
204                         found++;
205                 }
206         }
207
208         // If no mappings were found, create a default one for it
209         if (found == 0)
210         {
211                 profile[numberOfProfiles].device = deviceNum;
212                 strcpy(profile[numberOfProfiles].mapName, "Default");
213                 profile[numberOfProfiles].preferredController = CONTROLLER1;
214
215                 for(int i=0; i<21; i++)
216                         profile[numberOfProfiles].map[i] = defaultMap[i];
217
218                 combo->addItem(profile[numberOfProfiles].mapName, numberOfProfiles);
219                 numberOfProfiles++;
220                 found++;
221         }
222
223         return found;
224 }
225
226
227 int FindUsableProfiles(QComboBox * combo)
228 {
229         int found = 0;
230
231         // Check for device #0 (keyboard) profiles first
232         for(int j=0; j<numberOfProfiles; j++)
233         {
234                 // Check for device *and* usable configuration
235                 if ((profile[j].device == 0) && (profile[j].preferredController))
236                 {
237                         combo->addItem(QString("Keyboard::%1").arg(profile[j].mapName), j);
238                         found++;
239                 }
240         }
241
242         // Check for connected host devices next
243         for(int i=0; i<Gamepad::numJoysticks; i++)
244         {
245                 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
246
247                 for(int j=0; j<numberOfProfiles; j++)
248                 {
249                         if ((profile[j].device == deviceNum) && (profile[j].preferredController))
250                         {
251                                 combo->addItem(QString("%1::%2").arg(Gamepad::GetJoystickName(i)).arg(profile[j].mapName), j);
252                                 found++;
253                         }
254                 }
255         }
256
257         return found;
258 }
259
260
261 bool ConnectProfileToController(int profileNum, int controllerNum)
262 {
263         if (profile[profileNum].device == -1)
264                 return false;
265
266         if (controllerNum < 0 || controllerNum > 2)
267                 return false;
268
269         uint32_t * dest = (controllerNum == 0 ? &vjs.p1KeyBindings[0] : &vjs.p2KeyBindings[0]);
270
271         for(int i=0; i<21; i++)
272                 dest[i] = profile[profileNum].map[i];
273
274         WriteLog("PROFILE: Successfully mapped device '%s' (%s) to controller #%u...\n", deviceNames[profile[profileNum].device], profile[profileNum].mapName, controllerNum);
275         return true;
276 }
277
278
279 //
280 // This is a pretty crappy way of doing autodetection. What it does is scan for
281 // keyboard profiles first, then look for plugged in gamepads next. If more
282 // than one plugged in gamepad matches a preferred controller slot, the last
283 // one found is chosen.
284 //
285 // There has to be a better way to do this, I just can't think of what it
286 // should be ATM... :-P
287 //
288 /*
289 Here's the rules: If preferred Jaguar controller is not checked, the profile is
290 skipped. If one or the other is checked, it's put into that slot. If *both* are
291 checked, it will take over any slot that isn't claimed by another gamepad. If
292 there are ties, present it to the user *once* and ask them which gamepad should
293 be #1; don't ask again unless a), they change the profiles and b), the
294 situation warrants it.
295
296 Also, there is a problem with this approach and having multiple devices
297 that are the same. Currently, if two of the same device are plugged in
298 and the profile is set to both controllers, it will broadcast buttons
299 pressed from either gamepad, no matter who is pressing them. This is
300 BAD(tm). [Not true, but there's a different problem described under 'How to
301 solve?', so GOOD(tm).]
302
303 Also, the gamepad logic doesn't distinguish inputs by controller, it just
304 grabs them all regardless. This is also BAD(tm). [Actually, it doesn't. It
305 properly segregates the inputs. So this is GOOD(tm).]
306
307 How to solve?
308
309 Seems there's yet ANOTHER dimension to all this: The physical gamepads
310 plugged into their ports. Now the device # can map these fine if they're
311 different, but we still run into problems with the handling in the MainWin
312 because it's hardwired to take pad 0 in slot 0 and pad 1 in slot 1. If you have
313 them configured other than this, you won't get anything. So we need to also
314 map the physical devices to their respective slots.
315
316
317 Steps:
318
319 1) Make a list of all devices attached to the system.
320
321 2) Make a list of all profiles belonging to those devices, as long as they have
322    one or more Jaguar controllers that are "mapped to".
323
324 3) See if there are any conflicts. If there are, see if the user has already
325    been asked to resolve and chosen a resolution; otherwise, ask the user to
326    resolve.
327
328    a) Loop through all found profiles. If they are set to a single controller,
329       set it in the appropriate list (one list for controller 1, another for
330       controller 2).
331
332    b) Loop through all found profiles. If they are set to both controllers,
333       ... (first stab at it:)
334       Check for list #1. If nothing there, assign it to list #1.
335       Else, check for List #2. If nothing there, assign to list #2.
336       [But the wording of it implies that it will assign it to both.
337        Does that mean we should make another combobox will all the possible
338        combinations laid out? Probably. Not many people will understand that
339        checking both means "assign to either one that's free".]
340
341 4) Connect profiles to controllers, and set gamepad slots (for the MainWin
342    handler).
343
344 */
345 void AutoConnectProfiles(void)
346 {
347         int foundProfiles[MAX_PROFILES];
348         int controller1Profile = -1;
349         int controller2Profile = -1;
350
351         // Check for Keyboard device profiles first, if anything else is plugged in
352         // it will default to it instead.
353 #if 0
354         for(int i=0; i<numberOfProfiles; i++)
355         {
356                 // Skip profile if it's not Keyboard device
357                 if (profile[i].device != 0)
358                         continue;
359
360                 if (profile[i].preferredController & CONTROLLER1)
361                         controller1Profile = i;
362
363                 if (profile[i].preferredController & CONTROLLER2)
364                         controller2Profile = i;
365         }
366 #else
367         // Connect keyboard devices first...
368         ConnectProfileToDevice(0);
369 #endif
370
371         // Next, check for the "don't care" condition of both jaguar controllers
372         // checked for connected host devices
373         for(int i=0; i<Gamepad::numJoysticks; i++)
374         {
375                 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
376                 int numberProfilesFound = FindProfileForDevice(deviceNum, CONTROLLER1 | CONTROLLER2, foundProfiles);
377
378                 // We need to grab pairs here, host device # paired up with profiles
379                 // so we can then determine if there are any conflicts that can't be
380                 // resolved...
381         }
382
383         for(int i=0; i<Gamepad::numJoysticks; i++)
384         {
385                 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
386
387 #if 0
388                 for(int j=0; j<numberOfProfiles; j++)
389                 {
390                         // Skip profile if it's not discovered device
391                         if (profile[j].device != deviceNum)
392                                 continue;
393
394                         if (profile[j].preferredController & CONTROLLER1)
395                                 controller1Profile = j;
396
397                         if (profile[j].preferredController & CONTROLLER2)
398                                 controller2Profile = j;
399                 }
400 #else
401                 ConnectProfileToDevice(deviceNum);
402 #endif
403         }
404
405         if (controller1Profile != -1)
406                 ConnectProfileToController(controller1Profile, 0);
407
408         if (controller2Profile != -1)
409                 ConnectProfileToController(controller2Profile, 1);
410 }
411
412
413 int ConnectProfileToDevice(int deviceNum)
414 {
415 //      bool found1 = false;
416 //      bool found2 = false;
417         int numberFoundForController1 = 0;
418         int numberFoundForController2 = 0;
419
420         for(int i=0; i<numberOfProfiles; i++)
421         {
422                 // Skip profile if it's not our device
423                 if (profile[i].device != deviceNum)
424                         continue;
425
426                 if (profile[i].preferredController & CONTROLLER1)
427                 {
428                         controller1Profile = i;
429 //                      found1 = true;
430                         numberFoundForController1++;
431                 }
432
433                 if (profile[i].preferredController & CONTROLLER2)
434                 {
435                         controller2Profile = i;
436 //                      found2 = true;
437                         numberFoundForController2++;
438                 }
439         }
440
441 //      return found;
442         return numberFoundForController1 + numberFoundForController2;
443 }
444
445 /*
446 int FindProfileForDevice(int deviceNum)
447 {
448         for(int i=0; i<numberOfProfiles; i++)
449         {
450                 // Skip profile if it's not our device
451                 if (profile[i].device != deviceNum)
452                         continue;
453
454                 return i;
455         }
456
457         return -1;
458 }
459 */
460
461 int FindProfileForDevice(int deviceNum, int preferred, int * found)
462 {
463         int numFound = 0;
464
465         for(int i=0; i<numberOfProfiles; i++)
466         {
467                 // Return the profile only if it matches the passed in device and
468                 // matches the passed in prefence...
469                 if ((profile[i].device == deviceNum) && (profile[i].preferredController == preferred))
470                         found[numFound++] = i;
471         }
472
473         return numFound;
474 }
475