2 // profile.cpp - Global profile storage/definition/manipulation
5 // (C) 2013 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -------------------------------------------------------------
11 // JLH 05/01/2013 Created this file
22 #define MAX_DEVICES 64
25 Profile profile[MAX_PROFILES];
26 int controller1Profile;
27 int controller2Profile;
32 char deviceNames[MAX_DEVICES][128];
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'
40 // Function Prototypes
41 int ConnectProfileToDevice(int deviceNum);
42 int FindProfileForDevice(int deviceNum, int preferred, int * found);
45 void ReadProfiles(QSettings * set)
47 // Assume no profiles, until we read them
50 // There is always at least one device present, and it's the keyboard
51 // (hey, we're PC centric here ;-)
53 strcpy(deviceNames[0], "Keyboard");
55 // Read the rest of the devices (if any)
56 numberOfDevices += set->beginReadArray("devices");
58 for(int i=1; i<numberOfDevices; i++)
60 set->setArrayIndex(i - 1);
61 strcpy(deviceNames[i], set->value("deviceName").toString().toAscii().data());
62 //printf("Read device name: %s\n", deviceNames[i]);
66 numberOfProfiles = set->beginReadArray("profiles");
67 //printf("Number of profiles: %u\n", numberOfProfiles);
69 for(int i=0; i<numberOfProfiles; i++)
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();
76 for(int j=0; j<21; j++)
78 QString string = QString("map%1").arg(j);
79 profile[i].map[j] = set->value(string).toInt();
81 //printf("Profile #%u: device=%u (%s)\n", i, profile[i].device, deviceNames[profile[i].device]);
86 //printf("Number of profiles found: %u\n", numberOfProfiles);
87 // Set up a reasonable default if no profiles were found
88 if (numberOfProfiles == 0)
90 //printf("Setting up default profile...\n");
92 profile[0].device = 0; // Keyboard is always device #0
93 strcpy(profile[0].mapName, "Default");
94 profile[0].preferredController = CONTROLLER1;
96 for(int i=0; i<21; i++)
97 profile[0].map[i] = defaultMap[i];
102 void WriteProfiles(QSettings * set)
105 // Don't write anything for now...
108 // NB: Should only do this if something changed; otherwise, no need to do
110 set->beginWriteArray("devices");
112 for(int i=1; i<numberOfDevices; i++)
114 set->setArrayIndex(i - 1);
115 set->setValue("deviceName", deviceNames[i]);
119 set->beginWriteArray("profiles");
121 for(int i=0; i<numberOfProfiles; i++)
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);
128 for(int j=0; j<21; j++)
130 QString string = QString("map%1").arg(j);
131 set->setValue(string, profile[i].map[j]);
139 int GetFreeProfile(void)
141 // Check for too many, return -1 if so
142 if (numberOfProfiles == MAX_PROFILES)
145 int profileNum = numberOfProfiles;
151 void DeleteProfile(int profileToDelete)
154 if (profileToDelete >= numberOfProfiles)
157 // Trivial case: Profile at end of the array
158 if (profileToDelete == (numberOfProfiles - 1))
164 // memmove(dest, src, bytesToMove);
165 memmove(&profile[profileToDelete], &profile[profileToDelete + 1], ((numberOfProfiles - 1) - profileToDelete) * sizeof(Profile));
170 int FindDeviceNumberForName(const char * name)
172 for(int i=0; i<numberOfDevices; i++)
174 if (strcmp(deviceNames[i], name) == 0)
178 if (numberOfDevices == MAX_DEVICES)
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);
191 int FindMappingsForDevice(int deviceNum, QComboBox * combo)
195 for(int i=0; i<numberOfProfiles; i++)
197 //This should *never* be the case--all profiles in list are *good*
198 // if (profile[i].device == -1)
201 if (profile[i].device == deviceNum)
203 combo->addItem(profile[i].mapName, i);
208 // If no mappings were found, create a default one for it
211 profile[numberOfProfiles].device = deviceNum;
212 strcpy(profile[numberOfProfiles].mapName, "Default");
213 profile[numberOfProfiles].preferredController = CONTROLLER1;
215 for(int i=0; i<21; i++)
216 profile[numberOfProfiles].map[i] = defaultMap[i];
218 combo->addItem(profile[numberOfProfiles].mapName, numberOfProfiles);
227 int FindUsableProfiles(QComboBox * combo)
231 // Check for device #0 (keyboard) profiles first
232 for(int j=0; j<numberOfProfiles; j++)
234 // Check for device *and* usable configuration
235 if ((profile[j].device == 0) && (profile[j].preferredController))
237 combo->addItem(QString("Keyboard::%1").arg(profile[j].mapName), j);
242 // Check for connected host devices next
243 for(int i=0; i<Gamepad::numJoysticks; i++)
245 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
247 for(int j=0; j<numberOfProfiles; j++)
249 if ((profile[j].device == deviceNum) && (profile[j].preferredController))
251 combo->addItem(QString("%1::%2").arg(Gamepad::GetJoystickName(i)).arg(profile[j].mapName), j);
261 bool ConnectProfileToController(int profileNum, int controllerNum)
263 if (profile[profileNum].device == -1)
266 if (controllerNum < 0 || controllerNum > 2)
269 uint32_t * dest = (controllerNum == 0 ? &vjs.p1KeyBindings[0] : &vjs.p2KeyBindings[0]);
271 for(int i=0; i<21; i++)
272 dest[i] = profile[profileNum].map[i];
274 WriteLog("PROFILE: Successfully mapped device '%s' (%s) to controller #%u...\n", deviceNames[profile[profileNum].device], profile[profileNum].mapName, controllerNum);
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.
285 // There has to be a better way to do this, I just can't think of what it
286 // should be ATM... :-P
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.
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).]
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).]
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.
319 1) Make a list of all devices attached to the system.
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".
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
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
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".]
341 4) Connect profiles to controllers, and set gamepad slots (for the MainWin
345 void AutoConnectProfiles(void)
347 int foundProfiles[MAX_PROFILES];
348 int controller1Profile = -1;
349 int controller2Profile = -1;
351 // Check for Keyboard device profiles first, if anything else is plugged in
352 // it will default to it instead.
354 for(int i=0; i<numberOfProfiles; i++)
356 // Skip profile if it's not Keyboard device
357 if (profile[i].device != 0)
360 if (profile[i].preferredController & CONTROLLER1)
361 controller1Profile = i;
363 if (profile[i].preferredController & CONTROLLER2)
364 controller2Profile = i;
367 // Connect keyboard devices first...
368 ConnectProfileToDevice(0);
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++)
375 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
376 int numberProfilesFound = FindProfileForDevice(deviceNum, CONTROLLER1 | CONTROLLER2, foundProfiles);
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
383 for(int i=0; i<Gamepad::numJoysticks; i++)
385 int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
388 for(int j=0; j<numberOfProfiles; j++)
390 // Skip profile if it's not discovered device
391 if (profile[j].device != deviceNum)
394 if (profile[j].preferredController & CONTROLLER1)
395 controller1Profile = j;
397 if (profile[j].preferredController & CONTROLLER2)
398 controller2Profile = j;
401 ConnectProfileToDevice(deviceNum);
405 if (controller1Profile != -1)
406 ConnectProfileToController(controller1Profile, 0);
408 if (controller2Profile != -1)
409 ConnectProfileToController(controller2Profile, 1);
413 int ConnectProfileToDevice(int deviceNum)
415 // bool found1 = false;
416 // bool found2 = false;
417 int numberFoundForController1 = 0;
418 int numberFoundForController2 = 0;
420 for(int i=0; i<numberOfProfiles; i++)
422 // Skip profile if it's not our device
423 if (profile[i].device != deviceNum)
426 if (profile[i].preferredController & CONTROLLER1)
428 controller1Profile = i;
430 numberFoundForController1++;
433 if (profile[i].preferredController & CONTROLLER2)
435 controller2Profile = i;
437 numberFoundForController2++;
442 return numberFoundForController1 + numberFoundForController2;
446 int FindProfileForDevice(int deviceNum)
448 for(int i=0; i<numberOfProfiles; i++)
450 // Skip profile if it's not our device
451 if (profile[i].device != deviceNum)
461 int FindProfileForDevice(int deviceNum, int preferred, int * found)
465 for(int i=0; i<numberOfProfiles; i++)
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;