1 /*
2  * joyport.c - control port handling.
3  *
4  * Written by
5  *  Marco van den Heuvel <blackystardust68@yahoo.com>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include <string.h>
30 
31 #include "cmdline.h"
32 #include "joyport.h"
33 #include "lib.h"
34 #include "resources.h"
35 #include "uiapi.h"
36 #include "util.h"
37 
38 static joyport_t joyport_device[JOYPORT_MAX_DEVICES];
39 static uint8_t joyport_display[6] = { 0, 0, 0, 0, 0, 0};
40 
41 static int joy_port[JOYPORT_MAX_PORTS];
42 static joyport_port_props_t port_props[JOYPORT_MAX_PORTS];
43 static int pot_port_mask = 1;
44 
45 static uint8_t joyport_dig_stored[JOYPORT_MAX_PORTS];
46 
47 typedef struct resid2text_s {
48     int resid;
49     char *text;
50 } resid2text_t;
51 
52 static resid2text_t ids[] = {
53     { JOYPORT_RES_ID_MOUSE, "host mouse" },
54     { JOYPORT_RES_ID_SAMPLER, "host sampler (audio input device)" },
55     { -1, NULL }
56 };
57 
res2text(int joyport_id)58 static char *res2text(int joyport_id)
59 {
60     int i;
61     char *retval = "Unknown joyport resource";
62 
63     for (i = 0; ids[i].resid != -1; ++i) {
64         if (ids[i].resid == joyport_id) {
65             retval = ids[i].text;
66         }
67     }
68     return retval;
69 }
70 
set_joyport_pot_mask(int mask)71 void set_joyport_pot_mask(int mask)
72 {
73     pot_port_mask = mask;
74 }
75 
joyport_set_device(int port,int id)76 static int joyport_set_device(int port, int id)
77 {
78     int i;
79 
80     /* 1st some sanity checks */
81     if (id < JOYPORT_ID_NONE || id >= JOYPORT_MAX_DEVICES) {
82         return -1;
83     }
84     if (port < 0 || port >= JOYPORT_MAX_PORTS) {
85         return -1;
86     }
87 
88     /* Nothing changes */
89     if (id == joy_port[port]) {
90         return 0;
91     }
92 
93     /* check if port is present */
94     if (!port_props[port].name) {
95         ui_error("Selected port (%d) is not present on this emulator", port);
96         return -1;
97     }
98 
99     /* check if id is registered */
100     if (id != JOYPORT_ID_NONE && !joyport_device[id].name) {
101         ui_error("Selected control port device %d is not registered", id);
102         return -1;
103     }
104 
105     /* check if id conflicts with devices on other ports */
106     if (id != JOYPORT_ID_NONE && id != JOYPORT_ID_JOYSTICK) {
107         for (i = 0; i < JOYPORT_MAX_PORTS; ++i) {
108             if (port != i && joy_port[i] == id) {
109                 ui_error("Selected control port device %s on %s is already attached to %s", joyport_device[id].name, port_props[port].name, port_props[i].name);
110                 return -1;
111             }
112         }
113     }
114 
115     /* check if input resource conflicts with device on the other port */
116     if (id != JOYPORT_ID_NONE && id != JOYPORT_ID_JOYSTICK && joyport_device[id].resource_id != JOYPORT_RES_ID_NONE) {
117         for (i = 0; i < JOYPORT_MAX_PORTS; ++i) {
118             if (port != i && joyport_device[id].resource_id == joyport_device[joy_port[i]].resource_id) {
119                 ui_error("Selected control port device %s on %s uses same host input resource (%s) as the device attached to %s", joyport_device[id].name, port_props[port].name, res2text(joyport_device[id].resource_id), port_props[i].name);
120                 return -1;
121             }
122         }
123     }
124 
125     /* check if device can be connected to this port */
126     if (id != JOYPORT_ID_NONE && id != JOYPORT_ID_JOYSTICK && joyport_device[id].is_lp && !port_props[port].has_lp_support) {
127         ui_error("Selected control port device %s cannot be attached to %s", joyport_device[id].name, port_props[port].name);
128         return -1;
129     }
130 
131     /* all checks done, now disable the current device and enable the new device */
132     if (joyport_device[joy_port[port]].enable) {
133         joyport_device[joy_port[port]].enable(port, 0);
134     }
135     if (joyport_device[id].enable) {
136         joyport_device[id].enable(port, id);
137     }
138     joy_port[port] = id;
139 
140     return 0;
141 }
142 
joyport_clear_devices(void)143 void joyport_clear_devices(void)
144 {
145     int i;
146 
147     for (i = 0; i < JOYPORT_MAX_PORTS; ++i) {
148         if (port_props[i].name) {
149             joyport_set_device(i, JOYPORT_ID_NONE);
150         }
151     }
152 }
153 
read_joyport_dig(int port)154 uint8_t read_joyport_dig(int port)
155 {
156     int id = joy_port[port];
157 
158     if (id == JOYPORT_ID_NONE) {
159         return 0xff;
160     }
161 
162     if (!joyport_device[id].read_digital) {
163         return 0xff;
164     }
165     return joyport_device[id].read_digital(port);
166 }
167 
store_joyport_dig(int port,uint8_t val,uint8_t mask)168 void store_joyport_dig(int port, uint8_t val, uint8_t mask)
169 {
170     int id = joy_port[port];
171     uint8_t store_val;
172 
173     if (id == JOYPORT_ID_NONE) {
174         return;
175     }
176 
177     if (!joyport_device[id].store_digital) {
178         return;
179     }
180 
181     store_val = joyport_dig_stored[port];
182 
183     store_val &= (uint8_t)~mask;
184     store_val |= val;
185 
186     joyport_device[id].store_digital(store_val);
187 
188     joyport_dig_stored[port] = store_val;
189 }
190 
191 static int pot_port1 = -1;
192 static int pot_port2 = -1;
193 
find_pot_ports(void)194 static void find_pot_ports(void)
195 {
196     int i;
197 
198     for (i = 0; i < JOYPORT_MAX_PORTS; ++i) {
199         if (port_props[i].has_pot) {
200             if (pot_port1 == -1) {
201                 pot_port1 = i;
202             } else {
203                 pot_port2 = i;
204             }
205         }
206     }
207     if (pot_port1 == -1) {
208         pot_port1 = -2;
209     }
210     if (pot_port2 == -1) {
211         pot_port2 = -2;
212     }
213 }
214 
read_joyport_potx(void)215 uint8_t read_joyport_potx(void)
216 {
217     int id1 = JOYPORT_ID_NONE;
218     int id2 = JOYPORT_ID_NONE;
219     uint8_t ret1 = 0xff;
220     uint8_t ret2 = 0xff;
221 
222     /* first find the pot ports if needed */
223     if (pot_port1 == -1 || pot_port2 == -1) {
224         find_pot_ports();
225     }
226 
227     if (pot_port_mask == 1 || pot_port_mask == 3) {
228         if (pot_port1 != -2) {
229             id1 = joy_port[pot_port1];
230         }
231     }
232 
233     if (pot_port_mask == 2 || pot_port_mask == 3) {
234         if (pot_port2 != -2) {
235             id2 = joy_port[pot_port2];
236         }
237     }
238 
239     if (id1 != JOYPORT_ID_NONE) {
240         if (joyport_device[id1].read_potx) {
241             ret1 = joyport_device[id1].read_potx();
242         }
243     }
244 
245     if (id2 != JOYPORT_ID_NONE) {
246         if (joyport_device[id2].read_potx) {
247             ret2 = joyport_device[id2].read_potx();
248         }
249     }
250 
251     switch (pot_port_mask) {
252         case 1:
253             return ret1;
254         case 2:
255             return ret2;
256         case 3:
257             return ret1 & ret2;
258         default:
259             return 0xff;
260     }
261 }
262 
read_joyport_poty(void)263 uint8_t read_joyport_poty(void)
264 {
265     int id1 = JOYPORT_ID_NONE;
266     int id2 = JOYPORT_ID_NONE;
267     uint8_t ret1 = 0xff;
268     uint8_t ret2 = 0xff;
269 
270     /* first find the pot ports if needed */
271     if (pot_port1 == -1 || pot_port2 == -1) {
272         find_pot_ports();
273     }
274 
275     if (pot_port_mask == 1 || pot_port_mask == 3) {
276         if (pot_port1 != -2) {
277             id1 = joy_port[pot_port1];
278         }
279     }
280 
281     if (pot_port_mask == 2 || pot_port_mask == 3) {
282         if (pot_port2 != -2) {
283             id2 = joy_port[pot_port2];
284         }
285     }
286 
287     if (id1 != JOYPORT_ID_NONE) {
288         if (joyport_device[id1].read_poty) {
289             ret1 = joyport_device[id1].read_poty();
290         }
291     }
292 
293     if (id2 != JOYPORT_ID_NONE) {
294         if (joyport_device[id2].read_poty) {
295             ret2 = joyport_device[id2].read_poty();
296         }
297     }
298 
299     switch (pot_port_mask) {
300         case 1:
301             return ret1;
302         case 2:
303             return ret2;
304         case 3:
305             return ret1 & ret2;
306         default:
307             return 0xff;
308     }
309 }
310 
311 static int pot_present = -1;
312 
joyport_device_register(int id,joyport_t * device)313 int joyport_device_register(int id, joyport_t *device)
314 {
315     int i;
316 
317     if (id < 1 || id > JOYPORT_MAX_DEVICES) {
318         return -1;
319     }
320 
321     /* check for pot ports if needed */
322     if (pot_present == -1) {
323         for (i = 0; i < JOYPORT_MAX_PORTS && pot_present == -1; ++i) {
324             if (port_props[i].has_pot) {
325                 pot_present = 1;
326             }
327         }
328         if (pot_present == -1) {
329             pot_present = 0;
330         }
331     }
332 
333     /* skip pot devices if no pot is present */
334     if ((device->read_potx || device->read_poty) && !pot_present && !device->pot_optional) {
335         return 0;
336     }
337 
338     joyport_device[id].name = device->name;
339     joyport_device[id].resource_id = device->resource_id;
340     joyport_device[id].is_lp = device->is_lp;
341     joyport_device[id].pot_optional = device->pot_optional;
342     joyport_device[id].enable = device->enable;
343     joyport_device[id].read_digital = device->read_digital;
344     joyport_device[id].store_digital = device->store_digital;
345     joyport_device[id].read_potx = device->read_potx;
346     joyport_device[id].read_poty = device->read_poty;
347     joyport_device[id].write_snapshot = device->write_snapshot;
348     joyport_device[id].read_snapshot = device->read_snapshot;
349     return 0;
350 }
351 
joyport_port_register(int port,joyport_port_props_t * props)352 int joyport_port_register(int port, joyport_port_props_t *props)
353 {
354     if (port < 0 || port >= JOYPORT_MAX_PORTS) {
355         return -1;
356     }
357 
358     if (!port) {
359         memset(port_props, 0, sizeof(port_props));
360     }
361 
362     port_props[port].name = props->name;
363     port_props[port].has_pot = props->has_pot;
364     port_props[port].has_lp_support = props->has_lp_support;
365     port_props[port].active = props->active;
366 
367     return 0;
368 }
369 
check_valid_lightpen(int port,int index)370 static int check_valid_lightpen(int port, int index)
371 {
372     if (!joyport_device[index].is_lp) {
373         return 1;
374     }
375     if (port_props[port].has_lp_support) {
376         return 1;
377     }
378     return 0;
379 }
380 
check_valid_pot(int port,int index)381 static int check_valid_pot(int port, int index)
382 {
383     if (!joyport_device[index].read_potx && !joyport_device[index].read_poty) {
384         return 1;
385     }
386     if (port_props[port].has_pot || joyport_device[index].pot_optional) {
387         return 1;
388     }
389     return 0;
390 }
391 
joyport_get_valid_devices(int port)392 joyport_desc_t *joyport_get_valid_devices(int port)
393 {
394     joyport_desc_t *retval = NULL;
395     int i;
396     int valid = 0;
397     int j = 0;
398 
399     for (i = 0; i < JOYPORT_MAX_DEVICES; ++i) {
400         if (joyport_device[i].name) {
401             if (check_valid_lightpen(port, i) && check_valid_pot(port, i)) {
402                 ++valid;
403             }
404         }
405     }
406 
407     retval = lib_malloc(((size_t)valid + 1) * sizeof(joyport_desc_t));
408     for (i = 0; i < JOYPORT_MAX_DEVICES; ++i) {
409         if (joyport_device[i].name) {
410             if (check_valid_lightpen(port, i) && check_valid_pot(port, i)) {
411                 retval[j].name = joyport_device[i].name;
412                 retval[j].id = i;
413                 ++j;
414             }
415         }
416     }
417     retval[j].name = NULL;
418 
419     return retval;
420 }
421 
joyport_display_joyport(int id,uint8_t status)422 void joyport_display_joyport(int id, uint8_t status)
423 {
424     if (id == JOYPORT_ID_JOY1 || id == JOYPORT_ID_JOY2 || id == JOYPORT_ID_JOY3 || id == JOYPORT_ID_JOY4 || id == JOYPORT_ID_JOY5) {
425         if (id == JOYPORT_ID_JOY1 && joy_port[0] == JOYPORT_ID_JOYSTICK) {
426             joyport_display[1] = status;
427         }
428         if (id == JOYPORT_ID_JOY2 && joy_port[1] == JOYPORT_ID_JOYSTICK) {
429             joyport_display[2] = status;
430         }
431         if (id == JOYPORT_ID_JOY3 && joy_port[2] == JOYPORT_ID_JOYSTICK) {
432             joyport_display[3] = status;
433         }
434         if (id == JOYPORT_ID_JOY4 && joy_port[3] == JOYPORT_ID_JOYSTICK) {
435             joyport_display[4] = status;
436         }
437         if (id == JOYPORT_ID_JOY5 && joy_port[4] == JOYPORT_ID_JOYSTICK) {
438             joyport_display[5] = status;
439         }
440     } else {
441         if (id != joy_port[0] && id != joy_port[1] && id != joy_port[2] && id != joy_port[3] && id != joy_port[4]) {
442             return;
443         }
444 
445         if (id == joy_port[0]) {
446             joyport_display[1] = status;
447         }
448 
449         if (id == joy_port[1]) {
450             joyport_display[2] = status;
451         }
452 
453         if (id == joy_port[2]) {
454             joyport_display[3] = status;
455         }
456 
457         if (id == joy_port[3]) {
458             joyport_display[4] = status;
459         }
460 
461         if (id == joy_port[4]) {
462             joyport_display[5] = status;
463         }
464     }
465     ui_display_joyport(joyport_display);
466 }
467 
joyport_get_port_name(int port)468 char *joyport_get_port_name(int port)
469 {
470     return port_props[port].name;
471 }
472 
473 /* ------------------------------------------------------------------------- */
474 
set_joyport_device(int val,void * param)475 static int set_joyport_device(int val, void *param)
476 {
477     int port = vice_ptr_to_int(param);
478 
479     return joyport_set_device(port, val);
480 }
481 
482 static const resource_int_t resources_int_port1[] = {
483     { "JoyPort1Device", JOYPORT_ID_JOYSTICK, RES_EVENT_NO, NULL,
484       &joy_port[JOYPORT_1], set_joyport_device, (void *)JOYPORT_1 },
485     RESOURCE_INT_LIST_END
486 };
487 
488 static const resource_int_t resources_int_port2[] = {
489     { "JoyPort2Device", JOYPORT_ID_JOYSTICK, RES_EVENT_NO, NULL,
490       &joy_port[JOYPORT_2], set_joyport_device, (void *)JOYPORT_2 },
491     RESOURCE_INT_LIST_END
492 };
493 
494 static const resource_int_t resources_int_port3[] = {
495     { "JoyPort3Device", JOYPORT_ID_JOYSTICK, RES_EVENT_NO, NULL,
496       &joy_port[JOYPORT_3], set_joyport_device, (void *)JOYPORT_3 },
497     RESOURCE_INT_LIST_END
498 };
499 
500 static const resource_int_t resources_int_port4[] = {
501     { "JoyPort4Device", JOYPORT_ID_JOYSTICK, RES_EVENT_NO, NULL,
502       &joy_port[JOYPORT_4], set_joyport_device, (void *)JOYPORT_4 },
503     RESOURCE_INT_LIST_END
504 };
505 
506 static const resource_int_t resources_int_port5[] = {
507     { "JoyPort5Device", JOYPORT_ID_JOYSTICK, RES_EVENT_NO, NULL,
508       &joy_port[JOYPORT_5], set_joyport_device, (void *)JOYPORT_5 },
509     RESOURCE_INT_LIST_END
510 };
511 
joyport_resources_init(void)512 int joyport_resources_init(void)
513 {
514     int i;
515 
516     memset(joyport_device, 0, sizeof(joyport_device));
517     joyport_device[0].name = "None";
518     joyport_device[0].is_lp = JOYPORT_IS_NOT_LIGHTPEN;
519     for (i = 0; i < JOYPORT_MAX_PORTS; ++i) {
520         joy_port[i] = JOYPORT_ID_NONE;
521     }
522 
523     if (port_props[JOYPORT_5].name) {
524         if (resources_register_int(resources_int_port5) < 0) {
525             return -1;
526         }
527     }
528 
529     if (port_props[JOYPORT_4].name) {
530         if (resources_register_int(resources_int_port4) < 0) {
531             return -1;
532         }
533     }
534 
535     if (port_props[JOYPORT_3].name) {
536         if (resources_register_int(resources_int_port3) < 0) {
537             return -1;
538         }
539     }
540 
541     if (port_props[JOYPORT_2].name) {
542         if (resources_register_int(resources_int_port2) < 0) {
543             return -1;
544         }
545     }
546 
547     if (port_props[JOYPORT_1].name) {
548         if (resources_register_int(resources_int_port1) < 0) {
549             return -1;
550         }
551     }
552 
553     return 0;
554 }
555 
556 /* ------------------------------------------------------------------------- */
557 
558 struct joyport_opt_s {
559     const char *name;
560     int id;
561 };
562 
563 static struct joyport_opt_s id_match[] = {
564     { "0",               JOYPORT_ID_NONE },
565     { "none",            JOYPORT_ID_NONE },
566     { "1",               JOYPORT_ID_JOYSTICK },
567     { "joy",             JOYPORT_ID_JOYSTICK },
568     { "joystick",        JOYPORT_ID_JOYSTICK },
569     { "2",               JOYPORT_ID_PADDLES },
570     { "paddles",         JOYPORT_ID_PADDLES },
571     { "3",               JOYPORT_ID_MOUSE_1351 },
572     { "1351",            JOYPORT_ID_MOUSE_1351 },
573     { "1351mouse",       JOYPORT_ID_MOUSE_1351 },
574     { "4",               JOYPORT_ID_MOUSE_NEOS },
575     { "neos",            JOYPORT_ID_MOUSE_NEOS },
576     { "neosmouse",       JOYPORT_ID_MOUSE_NEOS },
577     { "5",               JOYPORT_ID_MOUSE_AMIGA },
578     { "amiga",           JOYPORT_ID_MOUSE_AMIGA },
579     { "amigamouse",      JOYPORT_ID_MOUSE_AMIGA },
580     { "6",               JOYPORT_ID_MOUSE_CX22 },
581     { "cx22",            JOYPORT_ID_MOUSE_CX22 },
582     { "cx22mouse",       JOYPORT_ID_MOUSE_CX22 },
583     { "7",               JOYPORT_ID_MOUSE_ST },
584     { "st",              JOYPORT_ID_MOUSE_ST },
585     { "atarist",         JOYPORT_ID_MOUSE_ST },
586     { "stmouse",         JOYPORT_ID_MOUSE_ST },
587     { "ataristmouse",    JOYPORT_ID_MOUSE_ST },
588     { "8",               JOYPORT_ID_MOUSE_SMART },
589     { "smart",           JOYPORT_ID_MOUSE_SMART },
590     { "smartmouse",      JOYPORT_ID_MOUSE_SMART },
591     { "9",               JOYPORT_ID_MOUSE_MICROMYS },
592     { "micromys",        JOYPORT_ID_MOUSE_MICROMYS },
593     { "micromysmouse",   JOYPORT_ID_MOUSE_MICROMYS },
594     { "10",              JOYPORT_ID_KOALAPAD },
595     { "koalapad",        JOYPORT_ID_KOALAPAD },
596     { "11",              JOYPORT_ID_LIGHTPEN_U },
597     { "lpup",            JOYPORT_ID_LIGHTPEN_U },
598     { "lightpenup",      JOYPORT_ID_LIGHTPEN_U },
599     { "12",              JOYPORT_ID_LIGHTPEN_L },
600     { "lpleft",          JOYPORT_ID_LIGHTPEN_L },
601     { "lightpenleft",    JOYPORT_ID_LIGHTPEN_L },
602     { "13",              JOYPORT_ID_LIGHTPEN_DATEL },
603     { "lpdatel",         JOYPORT_ID_LIGHTPEN_DATEL },
604     { "lightpendatel",   JOYPORT_ID_LIGHTPEN_DATEL },
605     { "datellightpen",   JOYPORT_ID_LIGHTPEN_DATEL },
606     { "14",              JOYPORT_ID_LIGHTGUN_Y },
607     { "magnum",          JOYPORT_ID_LIGHTGUN_Y },
608     { "15",              JOYPORT_ID_LIGHTGUN_L },
609     { "stack",           JOYPORT_ID_LIGHTGUN_L },
610     { "slr",             JOYPORT_ID_LIGHTGUN_L },
611     { "16",              JOYPORT_ID_LIGHTPEN_INKWELL },
612     { "lpinkwell",       JOYPORT_ID_LIGHTPEN_INKWELL },
613     { "lightpeninkwell", JOYPORT_ID_LIGHTPEN_INKWELL },
614     { "inkwelllightpen", JOYPORT_ID_LIGHTPEN_INKWELL },
615     { "17",              JOYPORT_ID_SAMPLER_2BIT },
616     { "2bitsampler",     JOYPORT_ID_SAMPLER_2BIT },
617     { "18",              JOYPORT_ID_SAMPLER_4BIT },
618     { "4bitsampler",     JOYPORT_ID_SAMPLER_4BIT },
619     { "19",              JOYPORT_ID_BBRTC },
620     { "bbrtc",           JOYPORT_ID_BBRTC },
621     { "20",              JOYPORT_ID_PAPERCLIP64 },
622     { "paperclip64",     JOYPORT_ID_PAPERCLIP64 },
623     { "paperclip",       JOYPORT_ID_PAPERCLIP64 },
624     { "pc64",            JOYPORT_ID_PAPERCLIP64 },
625     { "snespad",         JOYPORT_ID_SNESPAD },
626     { NULL, -1 }
627 };
628 
set_joyport_cmdline_device(const char * param,void * extra_param)629 static int set_joyport_cmdline_device(const char *param, void *extra_param)
630 {
631     int temp = -1;
632     int i = 0;
633     int port = vice_ptr_to_int(extra_param);
634 
635     if (!param) {
636         return -1;
637     }
638 
639     do {
640         if (strcmp(id_match[i].name, param) == 0) {
641             temp = id_match[i].id;
642         }
643         i++;
644     } while ((temp == -1) && (id_match[i].name != NULL));
645 
646     if (temp == -1) {
647         return -1;
648     }
649 
650     return set_joyport_device(temp, int_to_void_ptr(port));
651 }
652 
653 /* ------------------------------------------------------------------------- */
654 
build_joyport_string(int port)655 static char *build_joyport_string(int port)
656 {
657     int i = 0;
658     char *tmp1;
659     char *tmp2;
660     char number[4];
661     joyport_desc_t *devices = joyport_get_valid_devices(port);
662 
663     tmp1 = lib_msprintf("Set %s device (0: None", port_props[port].name);
664 
665     for (i = 1; devices[i].name; ++i) {
666         sprintf(number, "%d", devices[i].id);
667         tmp2 = util_concat(tmp1, ", ", number, ": ", devices[i].name, NULL);
668         lib_free(tmp1);
669         tmp1 = tmp2;
670     }
671     tmp2 = util_concat(tmp1, ")", NULL);
672     lib_free(tmp1);
673     lib_free(devices);
674     return tmp2;
675 }
676 
677 static cmdline_option_t cmdline_options_port1[] =
678 {
679     { "-controlport1device", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_DYNAMIC_DESCRIPTION,
680       set_joyport_cmdline_device, (void *)JOYPORT_1, NULL, NULL,
681       "Device", NULL },
682     CMDLINE_LIST_END
683 };
684 
685 static cmdline_option_t cmdline_options_port2[] =
686 {
687     { "-controlport2device", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_DYNAMIC_DESCRIPTION,
688       set_joyport_cmdline_device, (void *)JOYPORT_2, NULL, NULL,
689       "Device", NULL },
690     CMDLINE_LIST_END
691 };
692 
693 static cmdline_option_t cmdline_options_port3[] =
694 {
695     { "-controlport3device", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_DYNAMIC_DESCRIPTION,
696       set_joyport_cmdline_device, (void *)JOYPORT_3, NULL, NULL,
697       "Device", NULL },
698     CMDLINE_LIST_END
699 };
700 
701 static cmdline_option_t cmdline_options_port4[] =
702 {
703     { "-controlport4device", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_DYNAMIC_DESCRIPTION,
704       set_joyport_cmdline_device, (void *)JOYPORT_4, NULL, NULL,
705       "Device", NULL },
706     CMDLINE_LIST_END
707 };
708 
709 static cmdline_option_t cmdline_options_port5[] =
710 {
711     { "-controlport5device", CALL_FUNCTION, CMDLINE_ATTRIB_NEED_ARGS | CMDLINE_ATTRIB_DYNAMIC_DESCRIPTION,
712       set_joyport_cmdline_device, (void *)JOYPORT_5, NULL, NULL,
713       "Device", NULL },
714     CMDLINE_LIST_END
715 };
716 
joyport_cmdline_options_init(void)717 int joyport_cmdline_options_init(void)
718 {
719     union char_func cf;
720 
721     if (port_props[JOYPORT_1].name) {
722         cf.f = build_joyport_string;
723         cmdline_options_port1[0].description = cf.c;
724         cmdline_options_port1[0].attributes |= (JOYPORT_1 << 8);
725         if (cmdline_register_options(cmdline_options_port1) < 0) {
726             return -1;
727         }
728     }
729 
730     if (port_props[JOYPORT_2].name) {
731         cf.f = build_joyport_string;
732         cmdline_options_port2[0].description = cf.c;
733         cmdline_options_port2[0].attributes |= (JOYPORT_2 << 8);
734         if (cmdline_register_options(cmdline_options_port2) < 0) {
735             return -1;
736         }
737     }
738 
739     if (port_props[JOYPORT_3].name) {
740         cf.f = build_joyport_string;
741         cmdline_options_port3[0].description = cf.c;
742         cmdline_options_port3[0].attributes |= (JOYPORT_3 << 8);
743         if (cmdline_register_options(cmdline_options_port3) < 0) {
744             return -1;
745         }
746     }
747 
748     if (port_props[JOYPORT_4].name) {
749         cf.f = build_joyport_string;
750         cmdline_options_port4[0].description = cf.c;
751         cmdline_options_port4[0].attributes |= (JOYPORT_4 << 8);
752         if (cmdline_register_options(cmdline_options_port4) < 0) {
753             return -1;
754         }
755     }
756 
757     if (port_props[JOYPORT_5].name) {
758         cf.f = build_joyport_string;
759         cmdline_options_port5[0].description = cf.c;
760         cmdline_options_port5[0].attributes |= (JOYPORT_5 << 8);
761         if (cmdline_register_options(cmdline_options_port5) < 0) {
762             return -1;
763         }
764     }
765     return 0;
766 }
767 
768 /* ------------------------------------------------------------------------- */
769 
770 #define DUMP_VER_MAJOR   0
771 #define DUMP_VER_MINOR   0
772 
joyport_snapshot_write_module(struct snapshot_s * s,int port)773 int joyport_snapshot_write_module(struct snapshot_s *s, int port)
774 {
775     snapshot_module_t *m;
776     char snapshot_name[16];
777 
778     sprintf(snapshot_name, "JOYPORT%d", port);
779 
780     m = snapshot_module_create(s, snapshot_name, DUMP_VER_MAJOR, DUMP_VER_MINOR);
781 
782     if (m == NULL) {
783         return -1;
784     }
785 
786     /* save device id */
787     if (SMW_B(m, (uint8_t)joy_port[port]) < 0) {
788         snapshot_module_close(m);
789         return -1;
790     }
791 
792     snapshot_module_close(m);
793 
794     /* save seperate joyport device module */
795     switch (joy_port[port]) {
796         case JOYPORT_ID_NONE:
797             break;
798         default:
799             if (joyport_device[joy_port[port]].write_snapshot) {
800                 if (joyport_device[joy_port[port]].write_snapshot(s, port) < 0) {
801                     return -1;
802                 }
803             }
804             break;
805     }
806 
807     return 0;
808 }
809 
joyport_snapshot_read_module(struct snapshot_s * s,int port)810 int joyport_snapshot_read_module(struct snapshot_s *s, int port)
811 {
812     uint8_t major_version, minor_version;
813     snapshot_module_t *m;
814     int temp_joy_port;
815     char snapshot_name[16];
816 
817     sprintf(snapshot_name, "JOYPORT%d", port);
818 
819     m = snapshot_module_open(s, snapshot_name, &major_version, &minor_version);
820     if (m == NULL) {
821         return -1;
822     }
823 
824     if (major_version != DUMP_VER_MAJOR || minor_version != DUMP_VER_MINOR) {
825         snapshot_module_close(m);
826         return -1;
827     }
828 
829     /* load device id */
830     if (SMR_B_INT(m, &temp_joy_port) < 0) {
831         snapshot_module_close(m);
832         return -1;
833     }
834 
835     snapshot_module_close(m);
836 
837     /* enable device */
838     joyport_set_device(port, temp_joy_port);
839 
840     /* load device snapshot */
841     switch (joy_port[port]) {
842         case JOYPORT_ID_NONE:
843             break;
844         default:
845             if (joyport_device[joy_port[port]].read_snapshot) {
846                 if (joyport_device[joy_port[port]].read_snapshot(s, port) < 0) {
847                     return -1;
848                 }
849             }
850             break;
851     }
852 
853     return 0;
854 }
855