1/*****************************************************************************\
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 This file is licensed under the Snes9x License.
4 For further information, consult the LICENSE file in the root directory.
5\*****************************************************************************/
6
7#include <map>
8#include <set>
9#include <vector>
10#include <string>
11#include <algorithm>
12#include <assert.h>
13#include <ctype.h>
14
15#include "snes9x.h"
16#include "memmap.h"
17#include "apu/apu.h"
18#include "snapshot.h"
19#include "controls.h"
20#include "crosshairs.h"
21#include "movie.h"
22#include "display.h"
23#ifdef NETPLAY_SUPPORT
24#include "netplay.h"
25#endif
26
27using namespace std;
28
29#define NONE (-2)
30#define MP5 (-1)
31#define JOYPAD0 0
32#define JOYPAD1 1
33#define JOYPAD2 2
34#define JOYPAD3 3
35#define JOYPAD4 4
36#define JOYPAD5 5
37#define JOYPAD6 6
38#define JOYPAD7 7
39#define MOUSE0 8
40#define MOUSE1 9
41#define SUPERSCOPE 10
42#define ONE_JUSTIFIER 11
43#define TWO_JUSTIFIERS 12
44#define MACSRIFLE 13
45#define NUMCTLS 14 // This must be LAST
46
47#define POLL_ALL NUMCTLS
48
49#define SUPERSCOPE_FIRE 0x80
50#define SUPERSCOPE_CURSOR 0x40
51#define SUPERSCOPE_TURBO 0x20
52#define SUPERSCOPE_PAUSE 0x10
53#define SUPERSCOPE_OFFSCREEN 0x02
54
55#define JUSTIFIER_TRIGGER 0x80
56#define JUSTIFIER_START 0x20
57#define JUSTIFIER_SELECT 0x08
58
59#define MACSRIFLE_TRIGGER 0x01
60
61#define MAP_UNKNOWN (-1)
62#define MAP_NONE 0
63#define MAP_BUTTON 1
64#define MAP_AXIS 2
65#define MAP_POINTER 3
66
67#define FLAG_IOBIT0 (Memory.FillRAM[0x4213] & 0x40)
68#define FLAG_IOBIT1 (Memory.FillRAM[0x4213] & 0x80)
69#define FLAG_IOBIT(n) ((n) ? (FLAG_IOBIT1) : (FLAG_IOBIT0))
70
71bool8 pad_read = 0, pad_read_last = 0;
72uint8 read_idx[2 /* ports */][2 /* per port */];
73
74struct exemulti
75{
76 int32 pos;
77 bool8 data1;
78 s9xcommand_t *script;
79};
80
81struct crosshair
82{
83 uint8 set;
84 uint8 img;
85 uint8 fg, bg;
86};
87
88static struct
89{
90 int16 x, y;
91 int16 V_adj;
92 bool8 V_var;
93 int16 H_adj;
94 bool8 H_var;
95 bool8 mapped;
96} pseudopointer[8];
97
98static struct
99{
100 uint16 buttons;
101 uint16 turbos;
102 uint16 toggleturbo;
103 uint16 togglestick;
104 uint8 turbo_ct;
105} joypad[8];
106
107static struct
108{
109 uint8 delta_x, delta_y;
110 int16 old_x, old_y;
111 int16 cur_x, cur_y;
112 uint8 buttons;
113 uint32 ID;
114 struct crosshair crosshair;
115} mouse[2];
116
117static struct
118{
119 int16 x, y;
120 uint8 phys_buttons;
121 uint8 next_buttons;
122 uint8 read_buttons;
123 uint32 ID;
124 struct crosshair crosshair;
125} superscope;
126
127static struct
128{
129 int16 x[2], y[2];
130 uint8 buttons;
131 bool8 offscreen[2];
132 uint32 ID[2];
133 struct crosshair crosshair[2];
134} justifier;
135
136static struct
137{
138 int8 pads[4];
139} mp5[2];
140
141static struct
142{
143 int16 x, y;
144 uint8 buttons;
145 uint32 ID;
146 struct crosshair crosshair;
147} macsrifle;
148
149static set<struct exemulti *> exemultis;
150static set<uint32> pollmap[NUMCTLS + 1];
151static map<uint32, s9xcommand_t> keymap;
152static vector<s9xcommand_t *> multis;
153static uint8 turbo_time;
154static uint8 pseudobuttons[256];
155static bool8 FLAG_LATCH = FALSE;
156static int32 curcontrollers[2] = { NONE, NONE };
157static int32 newcontrollers[2] = { JOYPAD0, NONE };
158static char buf[256];
159
160static const char *color_names[32] =
161{
162 "Trans",
163 "Black",
164 "25Grey",
165 "50Grey",
166 "75Grey",
167 "White",
168 "Red",
169 "Orange",
170 "Yellow",
171 "Green",
172 "Cyan",
173 "Sky",
174 "Blue",
175 "Violet",
176 "MagicPink",
177 "Purple",
178 NULL,
179 "tBlack",
180 "t25Grey",
181 "t50Grey",
182 "t75Grey",
183 "tWhite",
184 "tRed",
185 "tOrange",
186 "tYellow",
187 "tGreen",
188 "tCyan",
189 "tSky",
190 "tBlue",
191 "tViolet",
192 "tMagicPink",
193 "tPurple"
194};
195
196static const char *speed_names[4] =
197{
198 "Var",
199 "Slow",
200 "Med",
201 "Fast"
202};
203
204static const int ptrspeeds[4] = { 1, 1, 4, 8 };
205
206// Note: these should be in asciibetical order!
207#define THE_COMMANDS \
208 S(BeginRecordingMovie), \
209 S(ClipWindows), \
210 S(Debugger), \
211 S(DecEmuTurbo), \
212 S(DecFrameRate), \
213 S(DecFrameTime), \
214 S(DecTurboSpeed), \
215 S(EmuTurbo), \
216 S(EndRecordingMovie), \
217 S(ExitEmu), \
218 S(IncEmuTurbo), \
219 S(IncFrameRate), \
220 S(IncFrameTime), \
221 S(IncTurboSpeed), \
222 S(LoadFreezeFile), \
223 S(LoadMovie), \
224 S(LoadOopsFile), \
225 S(Pause), \
226 S(QuickLoad000), \
227 S(QuickLoad001), \
228 S(QuickLoad002), \
229 S(QuickLoad003), \
230 S(QuickLoad004), \
231 S(QuickLoad005), \
232 S(QuickLoad006), \
233 S(QuickLoad007), \
234 S(QuickLoad008), \
235 S(QuickLoad009), \
236 S(QuickLoad010), \
237 S(QuickSave000), \
238 S(QuickSave001), \
239 S(QuickSave002), \
240 S(QuickSave003), \
241 S(QuickSave004), \
242 S(QuickSave005), \
243 S(QuickSave006), \
244 S(QuickSave007), \
245 S(QuickSave008), \
246 S(QuickSave009), \
247 S(QuickSave010), \
248 S(Reset), \
249 S(SaveFreezeFile), \
250 S(SaveSPC), \
251 S(Screenshot), \
252 S(SeekToFrame), \
253 S(SoftReset), \
254 S(SoundChannel0), \
255 S(SoundChannel1), \
256 S(SoundChannel2), \
257 S(SoundChannel3), \
258 S(SoundChannel4), \
259 S(SoundChannel5), \
260 S(SoundChannel6), \
261 S(SoundChannel7), \
262 S(SoundChannelsOn), \
263 S(SwapJoypads), \
264 S(ToggleBG0), \
265 S(ToggleBG1), \
266 S(ToggleBG2), \
267 S(ToggleBG3), \
268 S(ToggleEmuTurbo), \
269 S(ToggleSprites), \
270 S(ToggleTransparency) \
271
272#define S(x) x
273
274enum command_numbers
275{
276 THE_COMMANDS,
277 LAST_COMMAND
278};
279
280#undef S
281#define S(x) #x
282
283static const char *command_names[LAST_COMMAND + 1] =
284{
285 THE_COMMANDS,
286 NULL
287};
288
289#undef S
290#undef THE_COMMANDS
291
292static void DisplayStateChange (const char *, bool8);
293static void DoGunLatch (int, int);
294static void DoMacsRifleLatch (int, int);
295static int maptype (int);
296static bool strless (const char *, const char *);
297static int findstr (const char *, const char **, int);
298static int get_threshold (const char **);
299static const char * maptypename (int);
300static int32 ApplyMulti (s9xcommand_t *, int32, int16);
301static void do_polling (int);
302static void UpdatePolledMouse (int);
303
304
305static string& operator += (string &s, int i)
306{
307 snprintf(buf, sizeof(buf), "%d", i);
308 s.append(buf);
309 return (s);
310}
311
312static string& operator += (string &s, double d)
313{
314 snprintf(buf, sizeof(buf), "%g", d);
315 s.append(buf);
316 return (s);
317}
318
319static void DisplayStateChange (const char *str, bool8 on)
320{
321 snprintf(buf, sizeof(buf), "%s: %s", str, on ? "on":"off");
322 S9xSetInfoString(buf);
323}
324
325static void DoGunLatch (int x, int y)
326{
327 x += 40;
328
329 if (x > 295)
330 x = 295;
331 else
332 if (x < 40)
333 x = 40;
334
335 if (y > PPU.ScreenHeight - 1)
336 y = PPU.ScreenHeight - 1;
337 else
338 if (y < 0)
339 y = 0;
340
341 PPU.GunVLatch = (uint16) (y + 1);
342 PPU.GunHLatch = (uint16) x;
343}
344
345static void DoMacsRifleLatch (int x, int y)
346{
347 PPU.GunVLatch = (uint16) (y + 42);// + (int16) macsrifle.adjust_y;
348 PPU.GunHLatch = (uint16) (x + 76);// + (int16) macsrifle.adjust_x;
349}
350
351static int maptype (int t)
352{
353 switch (t)
354 {
355 case S9xNoMapping:
356 return (MAP_NONE);
357
358 case S9xButtonJoypad:
359 case S9xButtonMouse:
360 case S9xButtonSuperscope:
361 case S9xButtonJustifier:
362 case S9xButtonMacsRifle:
363 case S9xButtonCommand:
364 case S9xButtonPseudopointer:
365 case S9xButtonPort:
366 case S9xButtonMulti:
367 return (MAP_BUTTON);
368
369 case S9xAxisJoypad:
370 case S9xAxisPseudopointer:
371 case S9xAxisPseudobuttons:
372 case S9xAxisPort:
373 return (MAP_AXIS);
374
375 case S9xPointer:
376 case S9xPointerPort:
377 return (MAP_POINTER);
378
379 default:
380 return (MAP_UNKNOWN);
381 }
382}
383
384void S9xControlsReset (void)
385{
386 S9xControlsSoftReset();
387 mouse[0].buttons &= ~0x30;
388 mouse[1].buttons &= ~0x30;
389 justifier.buttons &= ~JUSTIFIER_SELECT;
390 macsrifle.buttons = 0;
391}
392
393void S9xControlsSoftReset (void)
394{
395 for (set<struct exemulti *>::iterator it = exemultis.begin(); it != exemultis.end(); it++)
396 delete *it;
397 exemultis.clear();
398
399 for (int i = 0; i < 2; i++)
400 for (int j = 0; j < 2; j++)
401 read_idx[i][j]=0;
402
403 FLAG_LATCH = FALSE;
404
405 curcontrollers[0] = newcontrollers[0];
406 curcontrollers[1] = newcontrollers[1];
407}
408
409void S9xUnmapAllControls (void)
410{
411 S9xControlsReset();
412
413 keymap.clear();
414
415 for (int i = 0; i < (int) multis.size(); i++)
416 free(multis[i]);
417 multis.clear();
418
419 for (int i = 0; i < NUMCTLS + 1; i++)
420 pollmap[i].clear();
421
422 for (int i = 0; i < 8; i++)
423 {
424 pseudopointer[i].x = 0;
425 pseudopointer[i].y = 0;
426 pseudopointer[i].H_adj = 0;
427 pseudopointer[i].V_adj = 0;
428 pseudopointer[i].H_var = 0;
429 pseudopointer[i].V_var = 0;
430 pseudopointer[i].mapped = false;
431
432 joypad[i].buttons = 0;
433 joypad[i].turbos = 0;
434 joypad[i].turbo_ct = 0;
435 }
436
437 for (int i = 0; i < 2; i++)
438 {
439 mouse[i].old_x = mouse[i].old_y = 0;
440 mouse[i].cur_x = mouse[i].cur_y = 0;
441 mouse[i].buttons = 1;
442 mouse[i].ID = InvalidControlID;
443
444 if (!(mouse[i].crosshair.set & 1))
445 mouse[i].crosshair.img = 0; // no image for mouse because its only logical position is game-specific, not known by the emulator
446 if (!(mouse[i].crosshair.set & 2))
447 mouse[i].crosshair.fg = 5;
448 if (!(mouse[i].crosshair.set & 4))
449 mouse[i].crosshair.bg = 1;
450
451 justifier.x[i] = justifier.y[i] = 0;
452 justifier.offscreen[i] = 0;
453 justifier.ID[i] = InvalidControlID;
454
455 if (!(justifier.crosshair[i].set & 1))
456 justifier.crosshair[i].img = 4;
457 if (!(justifier.crosshair[i].set & 2))
458 justifier.crosshair[i].fg = i ? 14 : 12;
459 if (!(justifier.crosshair[i].set & 4))
460 justifier.crosshair[i].bg = 1;
461 }
462
463 justifier.buttons = 0;
464
465 superscope.x = superscope.y = 0;
466 superscope.phys_buttons = 0;
467 superscope.next_buttons = 0;
468 superscope.read_buttons = 0;
469 superscope.ID = InvalidControlID;
470
471 if (!(superscope.crosshair.set & 1))
472 superscope.crosshair.img = 2;
473 if (!(superscope.crosshair.set & 2))
474 superscope.crosshair.fg = 5;
475 if (!(superscope.crosshair.set & 4))
476 superscope.crosshair.bg = 1;
477
478 macsrifle.x = macsrifle.y = 0;
479 macsrifle.buttons = 0;
480 macsrifle.ID = InvalidControlID;
481
482 if (!(macsrifle.crosshair.set & 1))
483 macsrifle.crosshair.img = 2;
484 if (!(macsrifle.crosshair.set & 2))
485 macsrifle.crosshair.fg = 5;
486 if (!(macsrifle.crosshair.set & 4))
487 macsrifle.crosshair.bg = 1;
488
489 memset(pseudobuttons, 0, sizeof(pseudobuttons));
490
491 turbo_time = 1;
492}
493
494void S9xSetController (int port, enum controllers controller, int8 id1, int8 id2, int8 id3, int8 id4)
495{
496 if (port < 0 || port > 1)
497 return;
498
499 switch (controller)
500 {
501 case CTL_NONE:
502 break;
503
504 case CTL_JOYPAD:
505 if (id1 < 0 || id1 > 7)
506 break;
507
508 newcontrollers[port] = JOYPAD0 + id1;
509 return;
510
511 case CTL_MOUSE:
512 if (id1 < 0 || id1 > 1)
513 break;
514 if (!Settings.MouseMaster)
515 {
516 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");
517 break;
518 }
519
520 newcontrollers[port] = MOUSE0 + id1;
521 return;
522
523 case CTL_SUPERSCOPE:
524 if (!Settings.SuperScopeMaster)
525 {
526 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");
527 break;
528 }
529
530 newcontrollers[port] = SUPERSCOPE;
531 return;
532
533 case CTL_JUSTIFIER:
534 if (id1 < 0 || id1 > 1)
535 break;
536 if (!Settings.JustifierMaster)
537 {
538 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");
539 break;
540 }
541
542 newcontrollers[port] = ONE_JUSTIFIER + id1;
543 return;
544
545 case CTL_MACSRIFLE:
546 if (!Settings.MacsRifleMaster)
547 {
548 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES M.A.C.S. Rifle: MacsRifleMaster disabled");
549 break;
550 }
551
552 newcontrollers[port] = MACSRIFLE;
553 return;
554
555 case CTL_MP5:
556 if (id1 < -1 || id1 > 7)
557 break;
558 if (id2 < -1 || id2 > 7)
559 break;
560 if (id3 < -1 || id3 > 7)
561 break;
562 if (id4 < -1 || id4 > 7)
563 break;
564 if (!Settings.MultiPlayer5Master)
565 {
566 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");
567 break;
568 }
569
570 newcontrollers[port] = MP5;
571 mp5[port].pads[0] = (id1 < 0) ? NONE : JOYPAD0 + id1;
572 mp5[port].pads[1] = (id2 < 0) ? NONE : JOYPAD0 + id2;
573 mp5[port].pads[2] = (id3 < 0) ? NONE : JOYPAD0 + id3;
574 mp5[port].pads[3] = (id4 < 0) ? NONE : JOYPAD0 + id4;
575 return;
576
577 default:
578 fprintf(stderr, "Unknown controller type %d\n", controller);
579 break;
580 }
581
582 newcontrollers[port] = NONE;
583}
584
585bool S9xVerifyControllers (void)
586{
587 bool ret = false;
588 int port, i, used[NUMCTLS];
589
590 for (i = 0; i < NUMCTLS; used[i++] = 0) ;
591
592 for (port = 0; port < 2; port++)
593 {
594 switch (i = newcontrollers[port])
595 {
596 case MOUSE0:
597 case MOUSE1:
598 if (!Settings.MouseMaster)
599 {
600 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");
601 newcontrollers[port] = NONE;
602 ret = true;
603 break;
604 }
605
606 if (used[i]++ > 0)
607 {
608 snprintf(buf, sizeof(buf), "Mouse%d used more than once! Disabling extra instances", i - MOUSE0 + 1);
609 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
610 newcontrollers[port] = NONE;
611 ret = true;
612 break;
613 }
614
615 break;
616
617 case SUPERSCOPE:
618 if (!Settings.SuperScopeMaster)
619 {
620 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");
621 newcontrollers[port] = NONE;
622 ret = true;
623 break;
624 }
625
626 if (used[i]++ > 0)
627 {
628 snprintf(buf, sizeof(buf), "Superscope used more than once! Disabling extra instances");
629 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
630 newcontrollers[port] = NONE;
631 ret = true;
632 break;
633 }
634
635 break;
636
637 case ONE_JUSTIFIER:
638 case TWO_JUSTIFIERS:
639 if (!Settings.JustifierMaster)
640 {
641 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");
642 newcontrollers[port] = NONE;
643 ret = true;
644 break;
645 }
646
647 if (used[ONE_JUSTIFIER]++ > 0)
648 {
649 snprintf(buf, sizeof(buf), "Justifier used more than once! Disabling extra instances");
650 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
651 newcontrollers[port] = NONE;
652 ret = true;
653 break;
654 }
655
656 break;
657
658 case MACSRIFLE:
659 if (!Settings.MacsRifleMaster)
660 {
661 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES M.A.C.S. Rifle: MacsRifleMaster disabled");
662 newcontrollers[port] = NONE;
663 ret = true;
664 break;
665 }
666
667 if (used[i]++ > 0)
668 {
669 snprintf(buf, sizeof(buf), "M.A.C.S. Rifle used more than once! Disabling extra instances");
670 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
671 newcontrollers[port] = NONE;
672 ret = true;
673 break;
674 }
675
676 break;
677
678 case MP5:
679 if (!Settings.MultiPlayer5Master)
680 {
681 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");
682 newcontrollers[port] = NONE;
683 ret = true;
684 break;
685 }
686
687 for (i = 0; i < 4; i++)
688 {
689 if (mp5[port].pads[i] != NONE)
690 {
691 if (used[mp5[port].pads[i] - JOYPAD0]++ > 0)
692 {
693 snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", mp5[port].pads[i] - JOYPAD0 + 1);
694 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
695 mp5[port].pads[i] = NONE;
696 ret = true;
697 break;
698 }
699 }
700 }
701
702 break;
703
704 case JOYPAD0:
705 case JOYPAD1:
706 case JOYPAD2:
707 case JOYPAD3:
708 case JOYPAD4:
709 case JOYPAD5:
710 case JOYPAD6:
711 case JOYPAD7:
712 if (used[i - JOYPAD0]++ > 0)
713 {
714 snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", i - JOYPAD0 + 1);
715 S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
716 newcontrollers[port] = NONE;
717 ret = true;
718 break;
719 }
720
721 break;
722
723 default:
724 break;
725 }
726 }
727
728 return (ret);
729}
730
731void S9xGetController (int port, enum controllers *controller, int8 *id1, int8 *id2, int8 *id3, int8 *id4)
732{
733 int i;
734
735 *controller = CTL_NONE;
736 *id1 = *id2 = *id3 = *id4 = -1;
737
738 if (port < 0 || port > 1)
739 return;
740
741 switch (i = newcontrollers[port])
742 {
743 case MP5:
744 *controller = CTL_MP5;
745 *id1 = (mp5[port].pads[0] == NONE) ? -1 : mp5[port].pads[0] - JOYPAD0;
746 *id2 = (mp5[port].pads[1] == NONE) ? -1 : mp5[port].pads[1] - JOYPAD0;
747 *id3 = (mp5[port].pads[2] == NONE) ? -1 : mp5[port].pads[2] - JOYPAD0;
748 *id4 = (mp5[port].pads[3] == NONE) ? -1 : mp5[port].pads[3] - JOYPAD0;
749 return;
750
751 case JOYPAD0:
752 case JOYPAD1:
753 case JOYPAD2:
754 case JOYPAD3:
755 case JOYPAD4:
756 case JOYPAD5:
757 case JOYPAD6:
758 case JOYPAD7:
759 *controller = CTL_JOYPAD;
760 *id1 = i - JOYPAD0;
761 return;
762
763 case MOUSE0:
764 case MOUSE1:
765 *controller = CTL_MOUSE;
766 *id1 = i - MOUSE0;
767 return;
768
769 case SUPERSCOPE:
770 *controller = CTL_SUPERSCOPE;
771 *id1 = 1;
772 return;
773
774 case ONE_JUSTIFIER:
775 case TWO_JUSTIFIERS:
776 *controller = CTL_JUSTIFIER;
777 *id1 = i - ONE_JUSTIFIER;
778 return;
779
780 case MACSRIFLE:
781 *controller = CTL_MACSRIFLE;
782 *id1 = 1;
783 return;
784 }
785}
786
787void S9xReportControllers (void)
788{
789 static char mes[128];
790 char *c = mes;
791
792 S9xVerifyControllers();
793
794 for (int port = 0; port < 2; port++)
795 {
796 c += sprintf(c, "Port %d: ", port + 1);
797
798 switch (newcontrollers[port])
799 {
800 case NONE:
801 c += sprintf(c, "<none>. ");
802 break;
803
804 case MP5:
805 c += sprintf(c, "MP5 with pads");
806 for (int i = 0; i < 4; i++)
807 {
808 if (mp5[port].pads[i] == NONE)
809 c += sprintf(c, " <none>. ");
810 else
811 c += sprintf(c, " #%d. ", mp5[port].pads[i] + 1 - JOYPAD0);
812 }
813
814 break;
815
816 case JOYPAD0:
817 case JOYPAD1:
818 case JOYPAD2:
819 case JOYPAD3:
820 case JOYPAD4:
821 case JOYPAD5:
822 case JOYPAD6:
823 case JOYPAD7:
824 c += sprintf(c, "Pad #%d. ", (int) (newcontrollers[port] - JOYPAD0 + 1));
825 break;
826
827 case MOUSE0:
828 case MOUSE1:
829 c += sprintf(c, "Mouse #%d. ", (int) (newcontrollers[port] - MOUSE0 + 1));
830 break;
831
832 case SUPERSCOPE:
833 if (port == 0)
834 c += sprintf(c, "Superscope (cannot fire). ");
835 else
836 c += sprintf(c, "Superscope. ");
837 break;
838
839 case ONE_JUSTIFIER:
840 if (port == 0)
841 c += sprintf(c, "Blue Justifier (cannot fire). ");
842 else
843 c += sprintf(c, "Blue Justifier. ");
844 break;
845
846 case TWO_JUSTIFIERS:
847 if (port == 0)
848 c += sprintf(c, "Blue and Pink Justifiers (cannot fire). ");
849 else
850 c += sprintf(c, "Blue and Pink Justifiers. ");
851 break;
852
853 case MACSRIFLE:
854 if (port == 0)
855 c += sprintf(c, "M.A.C.S. Rifle (cannot fire). ");
856 else
857 c += sprintf(c, "M.A.C.S. Rifle. ");
858 break;
859 }
860 }
861
862 S9xMessage(S9X_INFO, S9X_CONFIG_INFO, mes);
863}
864
865char * S9xGetCommandName (s9xcommand_t command)
866{
867 string s;
868 char c;
869
870 switch (command.type)
871 {
872 case S9xButtonJoypad:
873 if (command.button.joypad.buttons == 0)
874 return (strdup("None"));
875 if (command.button.joypad.buttons & 0x000f)
876 return (strdup("None"));
877
878 s = "Joypad";
879 s += command.button.joypad.idx + 1;
880
881 c = ' ';
882 if (command.button.joypad.toggle) { if (c) s += c; s += "Toggle"; c = 0; }
883 if (command.button.joypad.sticky) { if (c) s += c; s += "Sticky"; c = 0; }
884 if (command.button.joypad.turbo ) { if (c) s += c; s += "Turbo"; c = 0; }
885
886 c = ' ';
887 if (command.button.joypad.buttons & SNES_UP_MASK ) { s += c; s += "Up"; c = '+'; }
888 if (command.button.joypad.buttons & SNES_DOWN_MASK ) { s += c; s += "Down"; c = '+'; }
889 if (command.button.joypad.buttons & SNES_LEFT_MASK ) { s += c; s += "Left"; c = '+'; }
890 if (command.button.joypad.buttons & SNES_RIGHT_MASK ) { s += c; s += "Right"; c = '+'; }
891 if (command.button.joypad.buttons & SNES_A_MASK ) { s += c; s += "A"; c = '+'; }
892 if (command.button.joypad.buttons & SNES_B_MASK ) { s += c; s += "B"; c = '+'; }
893 if (command.button.joypad.buttons & SNES_X_MASK ) { s += c; s += "X"; c = '+'; }
894 if (command.button.joypad.buttons & SNES_Y_MASK ) { s += c; s += "Y"; c = '+'; }
895 if (command.button.joypad.buttons & SNES_TL_MASK ) { s += c; s += "L"; c = '+'; }
896 if (command.button.joypad.buttons & SNES_TR_MASK ) { s += c; s += "R"; c = '+'; }
897 if (command.button.joypad.buttons & SNES_START_MASK ) { s += c; s += "Start"; c = '+'; }
898 if (command.button.joypad.buttons & SNES_SELECT_MASK) { s += c; s += "Select"; c = '+'; }
899
900 break;
901
902 case S9xButtonMouse:
903 if (!command.button.mouse.left && !command.button.mouse.right)
904 return (strdup("None"));
905
906 s = "Mouse";
907 s += command.button.mouse.idx + 1;
908 s += " ";
909
910 if (command.button.mouse.left ) s += "L";
911 if (command.button.mouse.right) s += "R";
912
913 break;
914
915 case S9xButtonSuperscope:
916 if (!command.button.scope.fire && !command.button.scope.cursor && !command.button.scope.turbo && !command.button.scope.pause && !command.button.scope.aim_offscreen)
917 return (strdup("None"));
918
919 s = "Superscope";
920
921 if (command.button.scope.aim_offscreen) s += " AimOffscreen";
922
923 c = ' ';
924 if (command.button.scope.fire ) { s += c; s += "Fire"; c = '+'; }
925 if (command.button.scope.cursor) { s += c; s += "Cursor"; c = '+'; }
926 if (command.button.scope.turbo ) { s += c; s += "ToggleTurbo"; c = '+'; }
927 if (command.button.scope.pause ) { s += c; s += "Pause"; c = '+'; }
928
929 break;
930
931 case S9xButtonJustifier:
932 if (!command.button.justifier.trigger && !command.button.justifier.start && !command.button.justifier.aim_offscreen)
933 return (strdup("None"));
934
935 s = "Justifier";
936 s += command.button.justifier.idx + 1;
937
938 if (command.button.justifier.aim_offscreen) s += " AimOffscreen";
939
940 c = ' ';
941 if (command.button.justifier.trigger) { s += c; s += "Trigger"; c = '+'; }
942 if (command.button.justifier.start ) { s += c; s += "Start"; c = '+'; }
943
944 break;
945
946 case S9xButtonMacsRifle:
947 if (!command.button.macsrifle.trigger)
948 return (strdup("None"));
949
950 s = "MacsRifle";
951
952 c = ' ';
953 if (command.button.macsrifle.trigger) { s += c; s += "Trigger"; c = '+'; }
954
955 break;
956
957 case S9xButtonCommand:
958 if (command.button.command >= LAST_COMMAND)
959 return (strdup("None"));
960
961 return (strdup(command_names[command.button.command]));
962
963 case S9xPointer:
964 if (!command.pointer.aim_mouse0 && !command.pointer.aim_mouse1 && !command.pointer.aim_scope && !command.pointer.aim_justifier0 && !command.pointer.aim_justifier1 && !command.pointer.aim_macsrifle)
965 return (strdup("None"));
966
967 s = "Pointer";
968
969 c = ' ';
970 if (command.pointer.aim_mouse0 ) { s += c; s += "Mouse1"; c = '+'; }
971 if (command.pointer.aim_mouse1 ) { s += c; s += "Mouse2"; c = '+'; }
972 if (command.pointer.aim_scope ) { s += c; s += "Superscope"; c = '+'; }
973 if (command.pointer.aim_justifier0) { s += c; s += "Justifier1"; c = '+'; }
974 if (command.pointer.aim_justifier1) { s += c; s += "Justifier2"; c = '+'; }
975 if (command.pointer.aim_macsrifle) { s += c; s += "MacsRifle"; c = '+'; }
976
977 break;
978
979 case S9xButtonPseudopointer:
980 if (!command.button.pointer.UD && !command.button.pointer.LR)
981 return (strdup("None"));
982 if (command.button.pointer.UD == -2 || command.button.pointer.LR == -2)
983 return (strdup("None"));
984
985 s = "ButtonToPointer ";
986 s += command.button.pointer.idx + 1;
987
988 if (command.button.pointer.UD) s += (command.button.pointer.UD == 1) ? 'd' : 'u';
989 if (command.button.pointer.LR) s += (command.button.pointer.LR == 1) ? 'r' : 'l';
990
991 s += " ";
992 s += speed_names[command.button.pointer.speed_type];
993
994 break;
995
996 case S9xAxisJoypad:
997 s = "Joypad";
998 s += command.axis.joypad.idx + 1;
999 s += " Axis ";
1000
1001 switch (command.axis.joypad.axis)
1002 {
1003 case 0: s += (command.axis.joypad.invert ? "Right/Left" : "Left/Right"); break;
1004 case 1: s += (command.axis.joypad.invert ? "Down/Up" : "Up/Down" ); break;
1005 case 2: s += (command.axis.joypad.invert ? "A/Y" : "Y/A" ); break;
1006 case 3: s += (command.axis.joypad.invert ? "B/X" : "X/B" ); break;
1007 case 4: s += (command.axis.joypad.invert ? "R/L" : "L/R" ); break;
1008 default: return (strdup("None"));
1009 }
1010
1011 s += " T=";
1012 s += int((command.axis.joypad.threshold + 1) * 1000 / 256) / 10.0;
1013 s += "%";
1014
1015 break;
1016
1017 case S9xAxisPseudopointer:
1018 s = "AxisToPointer ";
1019 s += command.axis.pointer.idx + 1;
1020 s += command.axis.pointer.HV ? 'v' : 'h';
1021 s += " ";
1022
1023 if (command.axis.pointer.invert) s += "-";
1024
1025 s += speed_names[command.axis.pointer.speed_type];
1026
1027 break;
1028
1029 case S9xAxisPseudobuttons:
1030 s = "AxisToButtons ";
1031 s += command.axis.button.negbutton;
1032 s += "/";
1033 s += command.axis.button.posbutton;
1034 s += " T=";
1035 s += int((command.axis.button.threshold + 1) * 1000 / 256) / 10.0;
1036 s += "%";
1037
1038 break;
1039
1040 case S9xButtonPort:
1041 case S9xAxisPort:
1042 case S9xPointerPort:
1043 return (strdup("BUG: Port should have handled this instead of calling S9xGetCommandName()"));
1044
1045 case S9xNoMapping:
1046 return (strdup("None"));
1047
1048 case S9xButtonMulti:
1049 {
1050 if (command.button.multi_idx >= (int) multis.size())
1051 return (strdup("None"));
1052
1053 s = "{";
1054 if (multis[command.button.multi_idx]->multi_press) s = "+{";
1055
1056 bool sep = false;
1057
1058 for (s9xcommand_t *m = multis[command.button.multi_idx]; m->multi_press != 3; m++)
1059 {
1060 if (m->type == S9xNoMapping)
1061 {
1062 s += ";";
1063 sep = false;
1064 }
1065 else
1066 {
1067 if (sep) s += ",";
1068 if (m->multi_press == 1) s += "+";
1069 if (m->multi_press == 2) s += "-";
1070
1071 s += S9xGetCommandName(*m);
1072 sep = true;
1073 }
1074 }
1075
1076 s += "}";
1077
1078 break;
1079 }
1080
1081 default:
1082 return (strdup("BUG: Unknown command type"));
1083 }
1084
1085 return (strdup(s.c_str()));
1086}
1087
1088static bool strless (const char *a, const char *b)
1089{
1090 return (strcmp(a, b) < 0);
1091}
1092
1093static int findstr (const char *needle, const char **haystack, int numstr)
1094{
1095 const char **r;
1096
1097 r = lower_bound(haystack, haystack + numstr, needle, strless);
1098 if (r >= haystack + numstr || strcmp(needle, *r))
1099 return (-1);
1100
1101 return (r - haystack);
1102}
1103
1104static int get_threshold (const char **ss)
1105{
1106 const char *s = *ss;
1107 int i;
1108
1109 if (s[0] != 'T' || s[1] != '=')
1110 return (-1);
1111
1112 s += 2;
1113 i = 0;
1114
1115 if (s[0] == '0')
1116 {
1117 if (s[1] != '.')
1118 return (-1);
1119
1120 s++;
1121 }
1122 else
1123 {
1124 do
1125 {
1126 if (*s < '0' || *s > '9')
1127 return (-1);
1128
1129 i = i * 10 + 10 * (*s - '0');
1130 if (i > 1000)
1131 return (-1);
1132
1133 s++;
1134 }
1135 while (*s != '.' && *s != '%');
1136 }
1137
1138 if (*s == '.')
1139 {
1140 if (s[1] < '0' || s[1] > '9' || s[2] != '%')
1141 return (-1);
1142
1143 i += s[1] - '0';
1144 }
1145
1146 if (i > 1000)
1147 return (-1);
1148
1149 *ss = s;
1150
1151 return (i);
1152}
1153
1154s9xcommand_t S9xGetCommandT (const char *name)
1155{
1156 s9xcommand_t cmd;
1157 int i, j;
1158 const char *s;
1159
1160 memset(&cmd, 0, sizeof(cmd));
1161 cmd.type = S9xBadMapping;
1162 cmd.multi_press = 0;
1163 cmd.button_norpt = 0;
1164
1165 if (!strcmp(name, "None"))
1166 cmd.type = S9xNoMapping;
1167 else
1168 if (!strncmp(name, "Joypad", 6))
1169 {
1170 if (name[6] < '1' || name[6] > '8' || name[7] != ' ')
1171 return (cmd);
1172
1173 if (!strncmp(name + 8, "Axis ", 5))
1174 {
1175 cmd.axis.joypad.idx = name[6] - '1';
1176 s = name + 13;
1177
1178 if (!strncmp(s, "Left/Right ", 11)) { j = 0; i = 0; s += 11; }
1179 else
1180 if (!strncmp(s, "Right/Left ", 11)) { j = 0; i = 1; s += 11; }
1181 else
1182 if (!strncmp(s, "Up/Down ", 8)) { j = 1; i = 0; s += 8; }
1183 else
1184 if (!strncmp(s, "Down/Up ", 8)) { j = 1; i = 1; s += 8; }
1185 else
1186 if (!strncmp(s, "Y/A ", 4)) { j = 2; i = 0; s += 4; }
1187 else
1188 if (!strncmp(s, "A/Y ", 4)) { j = 2; i = 1; s += 4; }
1189 else
1190 if (!strncmp(s, "X/B ", 4)) { j = 3; i = 0; s += 4; }
1191 else
1192 if (!strncmp(s, "B/X ", 4)) { j = 3; i = 1; s += 4; }
1193 else
1194 if (!strncmp(s, "L/R ", 4)) { j = 4; i = 0; s += 4; }
1195 else
1196 if (!strncmp(s, "R/L ", 4)) { j = 4; i = 1; s += 4; }
1197 else
1198 return (cmd);
1199
1200 cmd.axis.joypad.axis = j;
1201 cmd.axis.joypad.invert = i;
1202 i = get_threshold(&s);
1203 if (i < 0)
1204 return (cmd);
1205 cmd.axis.joypad.threshold = (i - 1) * 256 / 1000;
1206
1207 cmd.type = S9xAxisJoypad;
1208 }
1209 else
1210 {
1211 cmd.button.joypad.idx = name[6] - '1';
1212 s = name + 8;
1213 i = 0;
1214
1215 if ((cmd.button.joypad.toggle = strncmp(s, "Toggle", 6) ? 0 : 1)) s += i = 6;
1216 if ((cmd.button.joypad.sticky = strncmp(s, "Sticky", 6) ? 0 : 1)) s += i = 6;
1217 if ((cmd.button.joypad.turbo = strncmp(s, "Turbo", 5) ? 0 : 1)) s += i = 5;
1218
1219 if (cmd.button.joypad.toggle && !(cmd.button.joypad.sticky || cmd.button.joypad.turbo))
1220 return (cmd);
1221
1222 if (i)
1223 {
1224 if (*s != ' ')
1225 return (cmd);
1226 s++;
1227 }
1228
1229 i = 0;
1230
1231 if (!strncmp(s, "Up", 2)) { i |= SNES_UP_MASK; s += 2; if (*s == '+') s++; }
1232 if (!strncmp(s, "Down", 4)) { i |= SNES_DOWN_MASK; s += 4; if (*s == '+') s++; }
1233 if (!strncmp(s, "Left", 4)) { i |= SNES_LEFT_MASK; s += 4; if (*s == '+') s++; }
1234 if (!strncmp(s, "Right", 5)) { i |= SNES_RIGHT_MASK; s += 5; if (*s == '+') s++; }
1235
1236 if (*s == 'A') { i |= SNES_A_MASK; s++; if (*s == '+') s++; }
1237 if (*s == 'B') { i |= SNES_B_MASK; s++; if (*s == '+') s++; }
1238 if (*s == 'X') { i |= SNES_X_MASK; s++; if (*s == '+') s++; }
1239 if (*s == 'Y') { i |= SNES_Y_MASK; s++; if (*s == '+') s++; }
1240 if (*s == 'L') { i |= SNES_TL_MASK; s++; if (*s == '+') s++; }
1241 if (*s == 'R') { i |= SNES_TR_MASK; s++; if (*s == '+') s++; }
1242
1243 if (!strncmp(s, "Start", 5)) { i |= SNES_START_MASK; s += 5; if (*s == '+') s++; }
1244 if (!strncmp(s, "Select", 6)) { i |= SNES_SELECT_MASK; s += 6; }
1245
1246 if (i == 0 || *s != 0 || *(s - 1) == '+')
1247 return (cmd);
1248
1249 cmd.button.joypad.buttons = i;
1250
1251 cmd.type = S9xButtonJoypad;
1252 }
1253 }
1254 else
1255 if (!strncmp(name, "Mouse", 5))
1256 {
1257 if (name[5] < '1' || name[5] > '2' || name[6] != ' ')
1258 return (cmd);
1259
1260 cmd.button.mouse.idx = name[5] - '1';
1261 s = name + 7;
1262 i = 0;
1263
1264 if ((cmd.button.mouse.left = (*s == 'L'))) s += i = 1;
1265 if ((cmd.button.mouse.right = (*s == 'R'))) s += i = 1;
1266
1267 if (i == 0 || *s != 0)
1268 return (cmd);
1269
1270 cmd.type = S9xButtonMouse;
1271 }
1272 else
1273 if (!strncmp(name, "Superscope ", 11))
1274 {
1275 s = name + 11;
1276 i = 0;
1277
1278 if ((cmd.button.scope.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }
1279 if ((cmd.button.scope.fire = strncmp(s, "Fire", 4) ? 0 : 1)) { s += i = 4; if (*s == '+') s++; }
1280 if ((cmd.button.scope.cursor = strncmp(s, "Cursor", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
1281 if ((cmd.button.scope.turbo = strncmp(s, "ToggleTurbo", 11) ? 0 : 1)) { s += i = 11; if (*s == '+') s++; }
1282 if ((cmd.button.scope.pause = strncmp(s, "Pause", 5) ? 0 : 1)) { s += i = 5; }
1283
1284 if (i == 0 || *s != 0 || *(s - 1) == '+')
1285 return (cmd);
1286
1287 cmd.type = S9xButtonSuperscope;
1288 }
1289 else
1290 if (!strncmp(name, "Justifier", 9))
1291 {
1292 if (name[9] < '1' || name[9] > '2' || name[10] != ' ')
1293 return (cmd);
1294
1295 cmd.button.justifier.idx = name[9] - '1';
1296 s = name + 11;
1297 i = 0;
1298
1299 if ((cmd.button.justifier.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }
1300 if ((cmd.button.justifier.trigger = strncmp(s, "Trigger", 7) ? 0 : 1)) { s += i = 7; if (*s == '+') s++; }
1301 if ((cmd.button.justifier.start = strncmp(s, "Start", 5) ? 0 : 1)) { s += i = 5; }
1302
1303 if (i == 0 || *s != 0 || *(s - 1) == '+')
1304 return (cmd);
1305
1306 cmd.type = S9xButtonJustifier;
1307 }
1308 else
1309 if (!strncmp(name, "MacsRifle ", 10))
1310 {
1311 s = name + 10;
1312 i = 0;
1313
1314 if ((cmd.button.macsrifle.trigger = strncmp(s, "Trigger", 7) ? 0 : 1)) { s += i = 7; }
1315
1316 if (i == 0 || *s != 0 || *(s - 1) == '+')
1317 return (cmd);
1318
1319 cmd.type = S9xButtonMacsRifle;
1320 }
1321 else
1322 if (!strncmp(name, "Pointer ", 8))
1323 {
1324 s = name + 8;
1325 i = 0;
1326
1327 if ((cmd.pointer.aim_mouse0 = strncmp(s, "Mouse1", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
1328 if ((cmd.pointer.aim_mouse1 = strncmp(s, "Mouse2", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
1329 if ((cmd.pointer.aim_scope = strncmp(s, "Superscope", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
1330 if ((cmd.pointer.aim_justifier0 = strncmp(s, "Justifier1", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
1331 if ((cmd.pointer.aim_justifier1 = strncmp(s, "Justifier2", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
1332 if ((cmd.pointer.aim_macsrifle = strncmp(s, "MacsRifle", 9) ? 0 : 1)) { s += i = 9; }
1333
1334 if (i == 0 || *s != 0 || *(s - 1) == '+')
1335 return (cmd);
1336
1337 cmd.type = S9xPointer;
1338 }
1339 else
1340 if (!strncmp(name, "ButtonToPointer ", 16))
1341 {
1342 if (name[16] < '1' || name[16] > '8')
1343 return (cmd);
1344
1345 cmd.button.pointer.idx = name[16] - '1';
1346 s = name + 17;
1347 i = 0;
1348
1349 if ((cmd.button.pointer.UD = (*s == 'u' ? -1 : (*s == 'd' ? 1 : 0)))) s += i = 1;
1350 if ((cmd.button.pointer.LR = (*s == 'l' ? -1 : (*s == 'r' ? 1 : 0)))) s += i = 1;
1351
1352 if (i == 0 || *(s++) != ' ')
1353 return (cmd);
1354
1355 for (i = 0; i < 4; i++)
1356 if (!strcmp(s, speed_names[i]))
1357 break;
1358 if (i > 3)
1359 return (cmd);
1360
1361 cmd.button.pointer.speed_type = i;
1362
1363 cmd.type = S9xButtonPseudopointer;
1364 }
1365 else
1366 if (!strncmp(name, "AxisToPointer ", 14))
1367 {
1368 if (name[14] < '1' || name[14] > '8')
1369 return (cmd);
1370
1371 cmd.axis.pointer.idx = name[14] - '1';
1372 s= name + 15;
1373 i = 0;
1374
1375 if (*s == 'h')
1376 cmd.axis.pointer.HV = 0;
1377 else
1378 if (*s == 'v')
1379 cmd.axis.pointer.HV = 1;
1380 else
1381 return (cmd);
1382
1383 if (s[1] != ' ')
1384 return (cmd);
1385
1386 s += 2;
1387 if ((cmd.axis.pointer.invert = *s == '-'))
1388 s++;
1389
1390 for (i = 0; i < 4; i++)
1391 if (!strcmp(s, speed_names[i]))
1392 break;
1393 if (i > 3)
1394 return (cmd);
1395
1396 cmd.axis.pointer.speed_type = i;
1397
1398 cmd.type = S9xAxisPseudopointer;
1399 }
1400 else
1401 if (!strncmp(name, "AxisToButtons ", 14))
1402 {
1403 s = name + 14;
1404
1405 if (s[0] == '0')
1406 {
1407 if (s[1] != '/')
1408 return (cmd);
1409
1410 cmd.axis.button.negbutton = 0;
1411 s += 2;
1412 }
1413 else
1414 {
1415 i = 0;
1416 do
1417 {
1418 if (*s < '0' || *s > '9')
1419 return (cmd);
1420
1421 i = i * 10 + *s - '0';
1422 if (i > 255)
1423 return (cmd);
1424 }
1425 while (*++s != '/');
1426
1427 cmd.axis.button.negbutton = i;
1428 s++;
1429 }
1430
1431 if (s[0] == '0')
1432 {
1433 if (s[1] != ' ')
1434 return (cmd);
1435
1436 cmd.axis.button.posbutton = 0;
1437 s += 2;
1438 }
1439 else
1440 {
1441 i = 0;
1442 do
1443 {
1444 if (*s < '0' || *s > '9')
1445 return (cmd);
1446
1447 i = i * 10 + *s - '0';
1448 if (i > 255)
1449 return (cmd);
1450 }
1451 while (*++s != ' ');
1452
1453 cmd.axis.button.posbutton = i;
1454 s++;
1455 }
1456
1457 i = get_threshold(&s);
1458 if (i < 0)
1459 return (cmd);
1460 cmd.axis.button.threshold = (i - 1) * 256 / 1000;
1461
1462 cmd.type = S9xAxisPseudobuttons;
1463 }
1464 else
1465 if (!strncmp(name, "MULTI#", 6))
1466 {
1467 i = strtol(name + 6, (char **) &s, 10);
1468 if (s != NULL && *s != '\0')
1469 return (cmd);
1470 if (i >= (int) multis.size())
1471 return (cmd);
1472
1473 cmd.button.multi_idx = i;
1474 cmd.type = S9xButtonMulti;
1475 }
1476 else
1477 if (((name[0] == '+' && name[1] == '{') || name[0] == '{') && name[strlen(name) - 1] == '}')
1478 {
1479 if (multis.size() > 2147483640)
1480 {
1481 fprintf(stderr, "Too many multis!");
1482 return (cmd);
1483 }
1484
1485 string x;
1486 int n;
1487
1488 j = 2;
1489 for (i = (name[0] == '+') ? 2 : 1; name[i] != '\0'; i++)
1490 {
1491 if (name[i] == ',' || name[i] == ';')
1492 {
1493 if (name[i] == ';')
1494 j++;
1495 if (++j > 2147483640)
1496 {
1497 fprintf(stderr, "Multi too long!");
1498 return (cmd);
1499 }
1500 }
1501
1502 if (name[i] == '{')
1503 return (cmd);
1504 }
1505
1506 s9xcommand_t *c = (s9xcommand_t *) calloc(j, sizeof(s9xcommand_t));
1507 if (c == NULL)
1508 {
1509 perror("malloc error while parsing multi");
1510 return (cmd);
1511 }
1512
1513 n = 0;
1514 i = (name[0] == '+') ? 2 : 1;
1515
1516 do
1517 {
1518 if (name[i] == ';')
1519 {
1520 c[n].type = S9xNoMapping;
1521 c[n].multi_press = 0;
1522 c[n].button_norpt = 0;
1523
1524 j = i;
1525 }
1526 else
1527 if (name[i] == ',')
1528 {
1529 free(c);
1530 return (cmd);
1531 }
1532 else
1533 {
1534 uint8 press = 0;
1535
1536 if (name[0] == '+')
1537 {
1538 if (name[i] == '+')
1539 press = 1;
1540 else
1541 if (name[i] == '-')
1542 press = 2;
1543 else
1544 {
1545 free(c);
1546 return (cmd);
1547 }
1548
1549 i++;
1550 }
1551
1552 for (j = i; name[j] != ';' && name[j] != ',' && name[j] != '}'; j++) ;
1553
1554 x.assign(name + i, j - i);
1555 c[n] = S9xGetCommandT(x.c_str());
1556 c[n].multi_press = press;
1557
1558 if (maptype(c[n].type) != MAP_BUTTON)
1559 {
1560 free(c);
1561 return (cmd);
1562 }
1563
1564 if (name[j] == ';')
1565 j--;
1566 }
1567
1568 i = j + 1;
1569 n++;
1570 }
1571 while (name[i] != '\0');
1572
1573 c[n].type = S9xNoMapping;
1574 c[n].multi_press = 3;
1575
1576 multis.push_back(c);
1577
1578 cmd.button.multi_idx = multis.size() - 1;
1579 cmd.type = S9xButtonMulti;
1580 }
1581 else
1582 {
1583 i = findstr(name, command_names, LAST_COMMAND);
1584 if (i < 0)
1585 return (cmd);
1586
1587 cmd.type = S9xButtonCommand;
1588 cmd.button.command = i;
1589 }
1590
1591 return (cmd);
1592}
1593
1594const char ** S9xGetAllSnes9xCommands (void)
1595{
1596 return (command_names);
1597}
1598
1599s9xcommand_t S9xGetMapping (uint32 id)
1600{
1601 if (keymap.count(id) == 0)
1602 {
1603 s9xcommand_t cmd;
1604 cmd.type = S9xNoMapping;
1605 return (cmd);
1606 }
1607 else
1608 return (keymap[id]);
1609}
1610
1611static const char * maptypename (int t)
1612{
1613 switch (t)
1614 {
1615 case MAP_NONE: return ("unmapped");
1616 case MAP_BUTTON: return ("button");
1617 case MAP_AXIS: return ("axis");
1618 case MAP_POINTER: return ("pointer");
1619 default: return ("unknown");
1620 }
1621}
1622
1623void S9xUnmapID (uint32 id)
1624{
1625 for (int i = 0; i < NUMCTLS + 1; i++)
1626 pollmap[i].erase(id);
1627
1628 if (mouse[0].ID == id) mouse[0].ID = InvalidControlID;
1629 if (mouse[1].ID == id) mouse[1].ID = InvalidControlID;
1630 if (superscope.ID == id) superscope.ID = InvalidControlID;
1631 if (justifier.ID[0] == id) justifier.ID[0] = InvalidControlID;
1632 if (justifier.ID[1] == id) justifier.ID[1] = InvalidControlID;
1633 if (macsrifle.ID == id) macsrifle.ID = InvalidControlID;
1634
1635 if (id >= PseudoPointerBase)
1636 pseudopointer[id - PseudoPointerBase].mapped = false;
1637
1638 keymap.erase(id);
1639}
1640
1641bool S9xMapButton (uint32 id, s9xcommand_t mapping, bool poll)
1642{
1643 int t;
1644
1645 if (id == InvalidControlID)
1646 {
1647 fprintf(stderr, "Cannot map InvalidControlID\n");
1648 return (false);
1649 }
1650
1651 t = maptype(mapping.type);
1652
1653 if (t == MAP_NONE)
1654 {
1655 S9xUnmapID(id);
1656 return (true);
1657 }
1658
1659 if (t != MAP_BUTTON)
1660 return (false);
1661
1662 t = maptype(S9xGetMapping(id).type);
1663
1664 if (t != MAP_NONE && t != MAP_BUTTON)
1665 fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to button\n", id, maptypename(t));
1666
1667 if (id >= PseudoPointerBase)
1668 {
1669 fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as a button\n", id - PseudoPointerBase);
1670 return (false);
1671 }
1672
1673 t = -1;
1674
1675 if (poll)
1676 {
1677 if (id >= PseudoButtonBase)
1678 fprintf(stderr, "INFO: Ignoring attempt to set pseudo-button #%d to polling\n", id - PseudoButtonBase);
1679 else
1680 {
1681 switch (mapping.type)
1682 {
1683 case S9xButtonJoypad:
1684 t = JOYPAD0 + mapping.button.joypad.idx;
1685 break;
1686
1687 case S9xButtonMouse:
1688 t = MOUSE0 + mapping.button.mouse.idx;
1689 break;
1690
1691 case S9xButtonSuperscope:
1692 t = SUPERSCOPE;
1693 break;
1694
1695 case S9xButtonJustifier:
1696 t = ONE_JUSTIFIER + mapping.button.justifier.idx;
1697 break;
1698
1699 case S9xButtonMacsRifle:
1700 t = MACSRIFLE;
1701 break;
1702
1703 case S9xButtonCommand:
1704 case S9xButtonPseudopointer:
1705 case S9xButtonPort:
1706 case S9xButtonMulti:
1707 t = POLL_ALL;
1708 break;
1709 }
1710 }
1711 }
1712
1713 S9xUnmapID(id);
1714
1715 keymap[id] = mapping;
1716
1717 if (t >= 0)
1718 pollmap[t].insert(id);
1719
1720 return (true);
1721}
1722
1723void S9xReportButton (uint32 id, bool pressed)
1724{
1725 if (keymap.count(id) == 0)
1726 return;
1727
1728 if (keymap[id].type == S9xNoMapping)
1729 return;
1730
1731 if (maptype(keymap[id].type) != MAP_BUTTON)
1732 {
1733 fprintf(stderr, "ERROR: S9xReportButton called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
1734 return;
1735 }
1736
1737 if (keymap[id].type == S9xButtonCommand) // skips the "already-pressed check" unless it's a command, as a hack to work around the following problem:
1738 if (keymap[id].button_norpt == pressed) // FIXME: this makes the controls "stick" after loading a savestate while recording a movie and holding any button
1739 return;
1740
1741 keymap[id].button_norpt = pressed;
1742
1743 S9xApplyCommand(keymap[id], pressed, 0);
1744}
1745
1746bool S9xMapPointer (uint32 id, s9xcommand_t mapping, bool poll)
1747{
1748 int t;
1749
1750 if (id == InvalidControlID)
1751 {
1752 fprintf(stderr, "Cannot map InvalidControlID\n");
1753 return (false);
1754 }
1755
1756 t = maptype(mapping.type);
1757
1758 if (t == MAP_NONE)
1759 {
1760 S9xUnmapID(id);
1761 return (true);
1762 }
1763
1764 if (t != MAP_POINTER)
1765 return (false);
1766
1767 t = maptype(S9xGetMapping(id).type);
1768
1769 if (t != MAP_NONE && t != MAP_POINTER)
1770 fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to pointer\n", id, maptypename(t));
1771
1772 if (id < PseudoPointerBase && id >= PseudoButtonBase)
1773 {
1774 fprintf(stderr, "ERROR: Refusing to map pseudo-button #%d as a pointer\n", id - PseudoButtonBase);
1775 return (false);
1776 }
1777
1778 if (mapping.type == S9xPointer)
1779 {
1780 if (mapping.pointer.aim_mouse0 && mouse[0].ID != InvalidControlID && mouse[0].ID != id)
1781 {
1782 fprintf(stderr, "ERROR: Rejecting attempt to control Mouse1 with two pointers\n");
1783 return (false);
1784 }
1785
1786 if (mapping.pointer.aim_mouse1 && mouse[1].ID != InvalidControlID && mouse[1].ID != id)
1787 {
1788 fprintf(stderr, "ERROR: Rejecting attempt to control Mouse2 with two pointers\n");
1789 return (false);
1790 }
1791
1792 if (mapping.pointer.aim_scope && superscope.ID != InvalidControlID && superscope.ID != id)
1793 {
1794 fprintf(stderr, "ERROR: Rejecting attempt to control SuperScope with two pointers\n");
1795 return (false);
1796 }
1797
1798 if (mapping.pointer.aim_justifier0 && justifier.ID[0] != InvalidControlID && justifier.ID[0] != id)
1799 {
1800 fprintf(stderr, "ERROR: Rejecting attempt to control Justifier1 with two pointers\n");
1801 return (false);
1802 }
1803
1804 if (mapping.pointer.aim_justifier1 && justifier.ID[1] != InvalidControlID && justifier.ID[1] != id)
1805 {
1806 fprintf(stderr, "ERROR: Rejecting attempt to control Justifier2 with two pointers\n");
1807 return (false);
1808 }
1809
1810 if (mapping.pointer.aim_macsrifle && macsrifle.ID != InvalidControlID && macsrifle.ID != id)
1811 {
1812 fprintf(stderr, "ERROR: Rejecting attempt to control M.A.C.S. Rifle with two pointers\n");
1813 return (false);
1814 }
1815 }
1816
1817 S9xUnmapID(id);
1818
1819 if (poll)
1820 {
1821 if (id >= PseudoPointerBase)
1822 fprintf(stderr, "INFO: Ignoring attempt to set pseudo-pointer #%d to polling\n", id - PseudoPointerBase);
1823 else
1824 {
1825 switch (mapping.type)
1826 {
1827 case S9xPointer:
1828 if (mapping.pointer.aim_mouse0 ) pollmap[MOUSE0 ].insert(id);
1829 if (mapping.pointer.aim_mouse1 ) pollmap[MOUSE1 ].insert(id);
1830 if (mapping.pointer.aim_scope ) pollmap[SUPERSCOPE ].insert(id);
1831 if (mapping.pointer.aim_justifier0) pollmap[ONE_JUSTIFIER ].insert(id);
1832 if (mapping.pointer.aim_justifier1) pollmap[TWO_JUSTIFIERS].insert(id);
1833 if (mapping.pointer.aim_macsrifle ) pollmap[MACSRIFLE ].insert(id);
1834 break;
1835
1836 case S9xPointerPort:
1837 pollmap[POLL_ALL].insert(id);
1838 break;
1839 }
1840 }
1841 }
1842
1843 if (id >= PseudoPointerBase)
1844 pseudopointer[id - PseudoPointerBase].mapped = true;
1845
1846 keymap[id] = mapping;
1847
1848 if (mapping.pointer.aim_mouse0 ) mouse[0].ID = id;
1849 if (mapping.pointer.aim_mouse1 ) mouse[1].ID = id;
1850 if (mapping.pointer.aim_scope ) superscope.ID = id;
1851 if (mapping.pointer.aim_justifier0) justifier.ID[0] = id;
1852 if (mapping.pointer.aim_justifier1) justifier.ID[1] = id;
1853 if (mapping.pointer.aim_macsrifle ) macsrifle.ID = id;
1854
1855 return (true);
1856}
1857
1858void S9xReportPointer (uint32 id, int16 x, int16 y)
1859{
1860 if (keymap.count(id) == 0)
1861 return;
1862
1863 if (keymap[id].type == S9xNoMapping)
1864 return;
1865
1866 if (maptype(keymap[id].type) != MAP_POINTER)
1867 {
1868 fprintf(stderr, "ERROR: S9xReportPointer called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
1869 return;
1870 }
1871
1872 S9xApplyCommand(keymap[id], x, y);
1873}
1874
1875bool S9xMapAxis (uint32 id, s9xcommand_t mapping, bool poll)
1876{
1877 int t;
1878
1879 if (id == InvalidControlID)
1880 {
1881 fprintf(stderr, "Cannot map InvalidControlID\n");
1882 return (false);
1883 }
1884
1885 t = maptype(mapping.type);
1886
1887 if (t == MAP_NONE)
1888 {
1889 S9xUnmapID(id);
1890 return (true);
1891 }
1892
1893 if (t != MAP_AXIS)
1894 return (false);
1895
1896 t = maptype(S9xGetMapping(id).type);
1897
1898 if (t != MAP_NONE && t != MAP_AXIS)
1899 fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to axis\n", id, maptypename(t));
1900
1901 if (id >= PseudoPointerBase)
1902 {
1903 fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as an axis\n", id - PseudoPointerBase);
1904 return (false);
1905 }
1906
1907 t = -1;
1908
1909 if (poll)
1910 {
1911 switch (mapping.type)
1912 {
1913 case S9xAxisJoypad:
1914 t = JOYPAD0 + mapping.axis.joypad.idx;
1915 break;
1916
1917 case S9xAxisPseudopointer:
1918 case S9xAxisPseudobuttons:
1919 case S9xAxisPort:
1920 t=POLL_ALL;
1921 break;
1922 }
1923 }
1924
1925 S9xUnmapID(id);
1926
1927 keymap[id] = mapping;
1928
1929 if (t >= 0)
1930 pollmap[t].insert(id);
1931
1932 return (true);
1933}
1934
1935void S9xReportAxis (uint32 id, int16 value)
1936{
1937 if (keymap.count(id) == 0)
1938 return;
1939
1940 if (keymap[id].type == S9xNoMapping)
1941 return;
1942
1943 if (maptype(keymap[id].type) != MAP_AXIS)
1944 {
1945 fprintf(stderr, "ERROR: S9xReportAxis called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
1946 return;
1947 }
1948
1949 S9xApplyCommand(keymap[id], value, 0);
1950}
1951
1952static int32 ApplyMulti (s9xcommand_t *multi, int32 pos, int16 data1)
1953{
1954 while (1)
1955 {
1956 if (multi[pos].multi_press == 3)
1957 return (-1);
1958
1959 if (multi[pos].type == S9xNoMapping)
1960 break;
1961
1962 if (multi[pos].multi_press)
1963 S9xApplyCommand(multi[pos], multi[pos].multi_press == 1, 0);
1964 else
1965 S9xApplyCommand(multi[pos], data1, 0);
1966
1967 pos++;
1968 }
1969
1970 return (pos + 1);
1971}
1972
1973void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)
1974{
1975 int i;
1976
1977 switch (cmd.type)
1978 {
1979 case S9xNoMapping:
1980 return;
1981
1982 case S9xButtonJoypad:
1983 if (cmd.button.joypad.toggle)
1984 {
1985 if (!data1)
1986 return;
1987
1988 uint16 r = cmd.button.joypad.buttons;
1989
1990 if (cmd.button.joypad.turbo) joypad[cmd.button.joypad.idx].toggleturbo ^= r;
1991 if (cmd.button.joypad.sticky) joypad[cmd.button.joypad.idx].togglestick ^= r;
1992 }
1993 else
1994 {
1995 uint16 r, s, t, st;
1996
1997 r = cmd.button.joypad.buttons;
1998 st = r & joypad[cmd.button.joypad.idx].togglestick & joypad[cmd.button.joypad.idx].toggleturbo;
1999 r ^= st;
2000 t = r & joypad[cmd.button.joypad.idx].toggleturbo;
2001 r ^= t;
2002 s = r & joypad[cmd.button.joypad.idx].togglestick;
2003 r ^= s;
2004
2005 if (cmd.button.joypad.turbo && cmd.button.joypad.sticky)
2006 {
2007 uint16 x = r; r = st; st = x;
2008 x = s; s = t; t = x;
2009 }
2010 else
2011 if (cmd.button.joypad.turbo)
2012 {
2013 uint16 x = r; r = t; t = x;
2014 x = s; s = st; st = x;
2015 }
2016 else
2017 if (cmd.button.joypad.sticky)
2018 {
2019 uint16 x = r; r = s; s = x;
2020 x = t; t = st; st = x;
2021 }
2022
2023 if (data1)
2024 {
2025 if (!Settings.UpAndDown && !S9xMoviePlaying()) // if up+down isn't allowed AND we are NOT playing a movie,
2026 {
2027 if (cmd.button.joypad.buttons & (SNES_LEFT_MASK | SNES_RIGHT_MASK))
2028 {
2029 // if we're pressing left or right, then unpress and unturbo them both first
2030 // so we don't end up hittnig left AND right accidentally.
2031 // Note though that the user can still do it on purpose, if Settings.UpAndDown = true.
2032 // This is a feature, look up glitches in tLoZ:aLttP to find out why.
2033 joypad[cmd.button.joypad.idx].buttons &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);
2034 joypad[cmd.button.joypad.idx].turbos &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);
2035 }
2036
2037 if (cmd.button.joypad.buttons & (SNES_UP_MASK | SNES_DOWN_MASK))
2038 {
2039 // and ditto for up/down
2040 joypad[cmd.button.joypad.idx].buttons &= ~(SNES_UP_MASK | SNES_DOWN_MASK);
2041 joypad[cmd.button.joypad.idx].turbos &= ~(SNES_UP_MASK | SNES_DOWN_MASK);
2042 }
2043 }
2044
2045 joypad[cmd.button.joypad.idx].buttons |= r;
2046 joypad[cmd.button.joypad.idx].turbos |= t;
2047 joypad[cmd.button.joypad.idx].buttons ^= s;
2048 joypad[cmd.button.joypad.idx].buttons &= ~(joypad[cmd.button.joypad.idx].turbos & st);
2049 joypad[cmd.button.joypad.idx].turbos ^= st;
2050 }
2051 else
2052 {
2053 joypad[cmd.button.joypad.idx].buttons &= ~r;
2054 joypad[cmd.button.joypad.idx].buttons &= ~(joypad[cmd.button.joypad.idx].turbos & t);
2055 joypad[cmd.button.joypad.idx].turbos &= ~t;
2056 }
2057 }
2058
2059 return;
2060
2061 case S9xButtonMouse:
2062 i = 0;
2063 if (cmd.button.mouse.left ) i |= 0x40;
2064 if (cmd.button.mouse.right) i |= 0x80;
2065
2066 if (data1)
2067 mouse[cmd.button.mouse.idx].buttons |= i;
2068 else
2069 mouse[cmd.button.mouse.idx].buttons &= ~i;
2070
2071 return;
2072
2073 case S9xButtonSuperscope:
2074 i = 0;
2075 if (cmd.button.scope.fire ) i |= SUPERSCOPE_FIRE;
2076 if (cmd.button.scope.cursor ) i |= SUPERSCOPE_CURSOR;
2077 if (cmd.button.scope.pause ) i |= SUPERSCOPE_PAUSE;
2078 if (cmd.button.scope.aim_offscreen) i |= SUPERSCOPE_OFFSCREEN;
2079
2080 if (data1)
2081 {
2082 superscope.phys_buttons |= i;
2083
2084 if (cmd.button.scope.turbo)
2085 {
2086 superscope.phys_buttons ^= SUPERSCOPE_TURBO;
2087
2088 if (superscope.phys_buttons & SUPERSCOPE_TURBO)
2089 superscope.next_buttons |= superscope.phys_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);
2090 else
2091 superscope.next_buttons &= ~(SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);
2092 }
2093
2094 superscope.next_buttons |= i & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR | SUPERSCOPE_PAUSE);
2095
2096 if (!S9xMovieActive()) // PPU modification during non-recordable command screws up movie synchronization
2097 if ((superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR)) && curcontrollers[1] == SUPERSCOPE && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))
2098 DoGunLatch(superscope.x, superscope.y);
2099 }
2100 else
2101 {
2102 superscope.phys_buttons &= ~i;
2103 superscope.next_buttons &= SUPERSCOPE_OFFSCREEN | ~i;
2104 }
2105
2106 return;
2107
2108 case S9xButtonJustifier:
2109 i = 0;
2110 if (cmd.button.justifier.trigger) i |= JUSTIFIER_TRIGGER;
2111 if (cmd.button.justifier.start ) i |= JUSTIFIER_START;
2112 if (cmd.button.justifier.aim_offscreen) justifier.offscreen[cmd.button.justifier.idx] = data1 ? 1 : 0;
2113 i >>= cmd.button.justifier.idx;
2114
2115 if (data1)
2116 justifier.buttons |= i;
2117 else
2118 justifier.buttons &= ~i;
2119
2120 return;
2121
2122 case S9xButtonMacsRifle:
2123 i = 0;
2124 if (cmd.button.macsrifle.trigger) i |= MACSRIFLE_TRIGGER;
2125
2126 if(data1)
2127 macsrifle.buttons |= i;
2128 else
2129 macsrifle.buttons &= ~i;
2130
2131 return;
2132
2133 case S9xButtonCommand:
2134 if (((enum command_numbers) cmd.button.command) >= LAST_COMMAND)
2135 {
2136 fprintf(stderr, "Unknown command %04x\n", cmd.button.command);
2137 return;
2138 }
2139
2140 if (!data1)
2141 {
2142 switch (i = cmd.button.command)
2143 {
2144 case EmuTurbo:
2145 Settings.TurboMode = FALSE;
2146 break;
2147 }
2148 }
2149 else
2150 {
2151 switch ((enum command_numbers) (i = cmd.button.command))
2152 {
2153 case ExitEmu:
2154 S9xExit();
2155 break;
2156
2157 case Reset:
2158 S9xReset();
2159 break;
2160
2161 case SoftReset:
2162 S9xMovieUpdateOnReset();
2163 if (S9xMoviePlaying())
2164 S9xMovieStop(TRUE);
2165 S9xSoftReset();
2166 break;
2167
2168 case EmuTurbo:
2169 Settings.TurboMode = TRUE;
2170 break;
2171
2172 case ToggleEmuTurbo:
2173 Settings.TurboMode = !Settings.TurboMode;
2174 DisplayStateChange("Turbo mode", Settings.TurboMode);
2175 break;
2176
2177 case ClipWindows:
2178 Settings.DisableGraphicWindows = !Settings.DisableGraphicWindows;
2179 DisplayStateChange("Graphic clip windows", !Settings.DisableGraphicWindows);
2180 break;
2181
2182 case Debugger:
2183 #ifdef DEBUGGER
2184 CPU.Flags |= DEBUG_MODE_FLAG;
2185 #endif
2186 break;
2187
2188 case IncFrameRate:
2189 if (Settings.SkipFrames == AUTO_FRAMERATE)
2190 Settings.SkipFrames = 1;
2191 else
2192 if (Settings.SkipFrames < 10)
2193 Settings.SkipFrames++;
2194
2195 if (Settings.SkipFrames == AUTO_FRAMERATE)
2196 S9xSetInfoString("Auto frame skip");
2197 else
2198 {
2199 sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);
2200 S9xSetInfoString(buf);
2201 }
2202
2203 break;
2204
2205 case DecFrameRate:
2206 if (Settings.SkipFrames <= 1)
2207 Settings.SkipFrames = AUTO_FRAMERATE;
2208 else
2209 if (Settings.SkipFrames != AUTO_FRAMERATE)
2210 Settings.SkipFrames--;
2211
2212 if (Settings.SkipFrames == AUTO_FRAMERATE)
2213 S9xSetInfoString("Auto frame skip");
2214 else
2215 {
2216 sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);
2217 S9xSetInfoString(buf);
2218 }
2219
2220 break;
2221
2222 case IncEmuTurbo:
2223 if (Settings.TurboSkipFrames < 20)
2224 Settings.TurboSkipFrames += 1;
2225 else
2226 if (Settings.TurboSkipFrames < 200)
2227 Settings.TurboSkipFrames += 5;
2228 sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);
2229 S9xSetInfoString(buf);
2230 break;
2231
2232 case DecEmuTurbo:
2233 if (Settings.TurboSkipFrames > 20)
2234 Settings.TurboSkipFrames -= 5;
2235 else
2236 if (Settings.TurboSkipFrames > 0)
2237 Settings.TurboSkipFrames -= 1;
2238 sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);
2239 S9xSetInfoString(buf);
2240 break;
2241
2242 case IncFrameTime: // Increase emulated frame time by 1ms
2243 Settings.FrameTime += 1000;
2244 sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);
2245 S9xSetInfoString(buf);
2246 break;
2247
2248 case DecFrameTime: // Decrease emulated frame time by 1ms
2249 if (Settings.FrameTime >= 1000)
2250 Settings.FrameTime -= 1000;
2251 sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);
2252 S9xSetInfoString(buf);
2253 break;
2254
2255 case IncTurboSpeed:
2256 if (turbo_time >= 120)
2257 break;
2258 turbo_time++;
2259 sprintf(buf, "Turbo speed: %d", turbo_time);
2260 S9xSetInfoString(buf);
2261 break;
2262
2263 case DecTurboSpeed:
2264 if (turbo_time <= 1)
2265 break;
2266 turbo_time--;
2267 sprintf(buf, "Turbo speed: %d", turbo_time);
2268 S9xSetInfoString(buf);
2269 break;
2270
2271 case LoadFreezeFile:
2272 S9xUnfreezeGame(S9xChooseFilename(TRUE));
2273 break;
2274
2275 case SaveFreezeFile:
2276 S9xFreezeGame(S9xChooseFilename(FALSE));
2277 break;
2278
2279 case LoadOopsFile:
2280 {
2281 char filename[PATH_MAX + 1];
2282 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
2283
2284 _splitpath(Memory.ROMFilename, drive, dir, def, ext);
2285 snprintf(filename, PATH_MAX + 1, "%s%s%s.%.*s", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, _MAX_EXT - 1, "oops");
2286
2287 if (S9xUnfreezeGame(filename))
2288 {
2289 snprintf(buf, 256, "%s.%.*s loaded", def, _MAX_EXT - 1, "oops");
2290 S9xSetInfoString (buf);
2291 }
2292 else
2293 S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Oops file not found");
2294
2295 break;
2296 }
2297
2298 case Pause:
2299 Settings.Paused = !Settings.Paused;
2300 DisplayStateChange("Pause", Settings.Paused);
2301 #if defined(NETPLAY_SUPPORT) && !defined(__WIN32__)
2302 S9xNPSendPause(Settings.Paused);
2303 #endif
2304 break;
2305
2306 case QuickLoad000:
2307 case QuickLoad001:
2308 case QuickLoad002:
2309 case QuickLoad003:
2310 case QuickLoad004:
2311 case QuickLoad005:
2312 case QuickLoad006:
2313 case QuickLoad007:
2314 case QuickLoad008:
2315 case QuickLoad009:
2316 case QuickLoad010:
2317 {
2318 char filename[PATH_MAX + 1];
2319 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
2320
2321 _splitpath(Memory.ROMFilename, drive, dir, def, ext);
2322 snprintf(filename, PATH_MAX + 1, "%s%s%s.%03d", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, i - QuickLoad000);
2323
2324 if (S9xUnfreezeGame(filename))
2325 {
2326 snprintf(buf, 256, "%s.%03d loaded", def, i - QuickLoad000);
2327 S9xSetInfoString(buf);
2328 }
2329 else
2330 S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Freeze file not found");
2331
2332 break;
2333 }
2334
2335 case QuickSave000:
2336 case QuickSave001:
2337 case QuickSave002:
2338 case QuickSave003:
2339 case QuickSave004:
2340 case QuickSave005:
2341 case QuickSave006:
2342 case QuickSave007:
2343 case QuickSave008:
2344 case QuickSave009:
2345 case QuickSave010:
2346 {
2347 char filename[PATH_MAX + 1];
2348 char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
2349
2350 _splitpath(Memory.ROMFilename, drive, dir, def, ext);
2351 snprintf(filename, PATH_MAX + 1, "%s%s%s.%03d", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, i - QuickSave000);
2352
2353 snprintf(buf, 256, "%s.%03d saved", def, i - QuickSave000);
2354 S9xSetInfoString(buf);
2355
2356 S9xFreezeGame(filename);
2357 break;
2358 }
2359
2360 case SaveSPC:
2361 S9xDumpSPCSnapshot();
2362 break;
2363
2364 case Screenshot:
2365 Settings.TakeScreenshot = TRUE;
2366 break;
2367
2368 case SoundChannel0:
2369 case SoundChannel1:
2370 case SoundChannel2:
2371 case SoundChannel3:
2372 case SoundChannel4:
2373 case SoundChannel5:
2374 case SoundChannel6:
2375 case SoundChannel7:
2376 S9xToggleSoundChannel(i - SoundChannel0);
2377 sprintf(buf, "Sound channel %d toggled", i - SoundChannel0);
2378 S9xSetInfoString(buf);
2379 break;
2380
2381 case SoundChannelsOn:
2382 S9xToggleSoundChannel(8);
2383 S9xSetInfoString("All sound channels on");
2384 break;
2385
2386 case ToggleBG0:
2387 Settings.BG_Forced ^= 1;
2388 DisplayStateChange("BG#0", !(Settings.BG_Forced & 1));
2389 break;
2390
2391 case ToggleBG1:
2392 Settings.BG_Forced ^= 2;
2393 DisplayStateChange("BG#1", !(Settings.BG_Forced & 2));
2394 break;
2395
2396 case ToggleBG2:
2397 Settings.BG_Forced ^= 4;
2398 DisplayStateChange("BG#2", !(Settings.BG_Forced & 4));
2399 break;
2400
2401 case ToggleBG3:
2402 Settings.BG_Forced ^= 8;
2403 DisplayStateChange("BG#3", !(Settings.BG_Forced & 8));
2404 break;
2405
2406 case ToggleSprites:
2407 Settings.BG_Forced ^= 16;
2408 DisplayStateChange("Sprites", !(Settings.BG_Forced & 16));
2409 break;
2410
2411 case ToggleTransparency:
2412 Settings.Transparency = !Settings.Transparency;
2413 DisplayStateChange("Transparency effects", Settings.Transparency);
2414 break;
2415
2416 case BeginRecordingMovie:
2417 if (S9xMovieActive())
2418 S9xMovieStop(FALSE);
2419 S9xMovieCreate(S9xChooseMovieFilename(FALSE), 0xFF, MOVIE_OPT_FROM_RESET, NULL, 0);
2420 break;
2421
2422 case LoadMovie:
2423 if (S9xMovieActive())
2424 S9xMovieStop(FALSE);
2425 S9xMovieOpen(S9xChooseMovieFilename(TRUE), FALSE);
2426 break;
2427
2428 case EndRecordingMovie:
2429 if (S9xMovieActive())
2430 S9xMovieStop(FALSE);
2431 break;
2432
2433 case SwapJoypads:
2434 if ((curcontrollers[0] != NONE && !(curcontrollers[0] >= JOYPAD0 && curcontrollers[0] <= JOYPAD7)))
2435 {
2436 S9xSetInfoString("Cannot swap pads: port 1 is not a joypad");
2437 break;
2438 }
2439
2440 if ((curcontrollers[1] != NONE && !(curcontrollers[1] >= JOYPAD0 && curcontrollers[1] <= JOYPAD7)))
2441 {
2442 S9xSetInfoString("Cannot swap pads: port 2 is not a joypad");
2443 break;
2444 }
2445
2446#ifdef NETPLAY_SUPPORT
2447 if (Settings.NetPlay && data2 != 1) { //data2 == 1 means it's sent by the netplay code
2448 if (Settings.NetPlayServer) {
2449 S9xNPSendJoypadSwap();
2450 } else {
2451 S9xSetInfoString("Netplay Client cannot swap pads.");
2452 break;
2453 }
2454 }
2455#endif
2456
2457 newcontrollers[1] = curcontrollers[0];
2458 newcontrollers[0] = curcontrollers[1];
2459
2460 strcpy(buf, "Swap pads: P1=");
2461 i = 14;
2462 if (newcontrollers[0] == NONE)
2463 {
2464 strcpy(buf + i, "<none>");
2465 i += 6;
2466 }
2467 else
2468 {
2469 sprintf(buf + i, "Joypad%d", newcontrollers[0] - JOYPAD0 + 1);
2470 i += 7;
2471 }
2472
2473 strcpy(buf + i, " P2=");
2474 i += 4;
2475 if (newcontrollers[1] == NONE)
2476 strcpy(buf + i, "<none>");
2477 else
2478 sprintf(buf + i, "Joypad%d", newcontrollers[1] - JOYPAD0 + 1);
2479
2480 S9xSetInfoString(buf);
2481 break;
2482
2483 case SeekToFrame:
2484 if (S9xMovieActive())
2485 {
2486 sprintf(buf, "Select frame number (current: %d)", S9xMovieGetFrameCounter());
2487 const char *frameno = S9xStringInput(buf);
2488 if (!frameno)
2489 return;
2490
2491 int frameDest = atoi(frameno);
2492 if (frameDest > 0 && frameDest > (int) S9xMovieGetFrameCounter())
2493 {
2494 int distance = frameDest - S9xMovieGetFrameCounter();
2495 Settings.HighSpeedSeek = distance;
2496 }
2497 }
2498
2499 break;
2500
2501 case LAST_COMMAND:
2502 break;
2503 }
2504 }
2505
2506 return;
2507
2508 case S9xPointer:
2509 if (cmd.pointer.aim_mouse0)
2510 {
2511 mouse[0].cur_x = data1;
2512 mouse[0].cur_y = data2;
2513 }
2514
2515 if (cmd.pointer.aim_mouse1)
2516 {
2517 mouse[1].cur_x = data1;
2518 mouse[1].cur_y = data2;
2519 }
2520
2521 if (cmd.pointer.aim_scope)
2522 {
2523 superscope.x = data1;
2524 superscope.y = data2;
2525 }
2526
2527 if (cmd.pointer.aim_justifier0)
2528 {
2529 justifier.x[0] = data1;
2530 justifier.y[0] = data2;
2531 }
2532
2533 if (cmd.pointer.aim_justifier1)
2534 {
2535 justifier.x[1] = data1;
2536 justifier.y[1] = data2;
2537 }
2538
2539 if (cmd.pointer.aim_macsrifle)
2540 {
2541 macsrifle.x = data1;
2542 macsrifle.y = data2;
2543 }
2544
2545 return;
2546
2547 case S9xButtonPseudopointer:
2548 if (data1)
2549 {
2550 if (cmd.button.pointer.UD)
2551 {
2552 if (!pseudopointer[cmd.button.pointer.idx].V_adj)
2553 pseudopointer[cmd.button.pointer.idx].V_adj = cmd.button.pointer.UD * ptrspeeds[cmd.button.pointer.speed_type];
2554 pseudopointer[cmd.button.pointer.idx].V_var = (cmd.button.pointer.speed_type == 0);
2555 }
2556
2557 if (cmd.button.pointer.LR)
2558 {
2559 if (!pseudopointer[cmd.button.pointer.idx].H_adj)
2560 pseudopointer[cmd.button.pointer.idx].H_adj = cmd.button.pointer.LR * ptrspeeds[cmd.button.pointer.speed_type];
2561 pseudopointer[cmd.button.pointer.idx].H_var = (cmd.button.pointer.speed_type == 0);
2562 }
2563 }
2564 else
2565 {
2566 if (cmd.button.pointer.UD)
2567 {
2568 pseudopointer[cmd.button.pointer.idx].V_adj = 0;
2569 pseudopointer[cmd.button.pointer.idx].V_var = false;
2570 }
2571
2572 if (cmd.button.pointer.LR)
2573 {
2574 pseudopointer[cmd.button.pointer.idx].H_adj = 0;
2575 pseudopointer[cmd.button.pointer.idx].H_var = false;
2576 }
2577 }
2578
2579 return;
2580
2581 case S9xAxisJoypad:
2582 {
2583 uint16 pos, neg;
2584
2585 switch (cmd.axis.joypad.axis)
2586 {
2587 case 0: neg = SNES_LEFT_MASK; pos = SNES_RIGHT_MASK; break;
2588 case 1: neg = SNES_UP_MASK; pos = SNES_DOWN_MASK; break;
2589 case 2: neg = SNES_Y_MASK; pos = SNES_A_MASK; break;
2590 case 3: neg = SNES_X_MASK; pos = SNES_B_MASK; break;
2591 case 4: neg = SNES_TL_MASK; pos = SNES_TR_MASK; break;
2592 default: return;
2593 }
2594
2595 if (cmd.axis.joypad.invert)
2596 data1 = -data1;
2597
2598 uint16 p, r;
2599
2600 p = r = 0;
2601 if (data1 > ((cmd.axis.joypad.threshold + 1) * 127))
2602 p |= pos;
2603 else
2604 r |= pos;
2605
2606 if (data1 <= ((cmd.axis.joypad.threshold + 1) * -127))
2607 p |= neg;
2608 else
2609 r |= neg;
2610
2611 joypad[cmd.axis.joypad.idx].buttons |= p;
2612 joypad[cmd.axis.joypad.idx].buttons &= ~r;
2613 joypad[cmd.axis.joypad.idx].turbos &= ~(p | r);
2614
2615 return;
2616 }
2617
2618 case S9xAxisPseudopointer:
2619 if (data1 == 0)
2620 {
2621 if (cmd.axis.pointer.HV)
2622 {
2623 pseudopointer[cmd.axis.pointer.idx].V_adj = 0;
2624 pseudopointer[cmd.axis.pointer.idx].V_var = false;
2625 }
2626 else
2627 {
2628 pseudopointer[cmd.axis.pointer.idx].H_adj = 0;
2629 pseudopointer[cmd.axis.pointer.idx].H_var = false;
2630 }
2631 }
2632 else
2633 {
2634 if (cmd.axis.pointer.invert)
2635 data1 = -data1;
2636
2637 if (cmd.axis.pointer.HV)
2638 {
2639 if (!pseudopointer[cmd.axis.pointer.idx].V_adj)
2640 pseudopointer[cmd.axis.pointer.idx].V_adj = (int16) ((int32) data1 * ptrspeeds[cmd.axis.pointer.speed_type] / 32767);
2641 pseudopointer[cmd.axis.pointer.idx].V_var = (cmd.axis.pointer.speed_type == 0);
2642 }
2643 else
2644 {
2645 if (!pseudopointer[cmd.axis.pointer.idx].H_adj)
2646 pseudopointer[cmd.axis.pointer.idx].H_adj = (int16) ((int32) data1 * ptrspeeds[cmd.axis.pointer.speed_type] / 32767);
2647 pseudopointer[cmd.axis.pointer.idx].H_var = (cmd.axis.pointer.speed_type == 0);
2648 }
2649 }
2650
2651 return;
2652
2653 case S9xAxisPseudobuttons:
2654 if (data1 > ((cmd.axis.button.threshold + 1) * 127))
2655 {
2656 if (!pseudobuttons[cmd.axis.button.posbutton])
2657 {
2658 pseudobuttons[cmd.axis.button.posbutton] = 1;
2659 S9xReportButton(PseudoButtonBase + cmd.axis.button.posbutton, true);
2660 }
2661 }
2662 else
2663 {
2664 if (pseudobuttons[cmd.axis.button.posbutton])
2665 {
2666 pseudobuttons[cmd.axis.button.posbutton] = 0;
2667 S9xReportButton(PseudoButtonBase + cmd.axis.button.posbutton, false);
2668 }
2669 }
2670
2671 if (data1 <= ((cmd.axis.button.threshold + 1) * -127))
2672 {
2673 if (!pseudobuttons[cmd.axis.button.negbutton])
2674 {
2675 pseudobuttons[cmd.axis.button.negbutton] = 1;
2676 S9xReportButton(PseudoButtonBase + cmd.axis.button.negbutton, true);
2677 }
2678 }
2679 else
2680 {
2681 if (pseudobuttons[cmd.axis.button.negbutton])
2682 {
2683 pseudobuttons[cmd.axis.button.negbutton] = 0;
2684 S9xReportButton(PseudoButtonBase + cmd.axis.button.negbutton, false);
2685 }
2686 }
2687
2688 return;
2689
2690 case S9xButtonPort:
2691 case S9xAxisPort:
2692 case S9xPointerPort:
2693 S9xHandlePortCommand(cmd, data1, data2);
2694 return;
2695
2696 case S9xButtonMulti:
2697 if (cmd.button.multi_idx >= (int) multis.size())
2698 return;
2699
2700 if (multis[cmd.button.multi_idx]->multi_press && !data1)
2701 return;
2702
2703 i = ApplyMulti(multis[cmd.button.multi_idx], 0, data1);
2704 if (i >= 0)
2705 {
2706 struct exemulti *e = new struct exemulti;
2707 e->pos = i;
2708 e->data1 = data1 != 0;
2709 e->script = multis[cmd.button.multi_idx];
2710 exemultis.insert(e);
2711 }
2712
2713 return;
2714
2715 default:
2716 fprintf(stderr, "WARNING: Unknown command type %d\n", cmd.type);
2717 return;
2718 }
2719}
2720
2721static void do_polling (int mp)
2722{
2723 set<uint32>::iterator itr;
2724
2725 if (S9xMoviePlaying())
2726 return;
2727
2728 if (pollmap[mp].empty())
2729 return;
2730
2731 for (itr = pollmap[mp].begin(); itr != pollmap[mp].end(); itr++)
2732 {
2733 switch (maptype(keymap[*itr].type))
2734 {
2735 case MAP_BUTTON:
2736 {
2737 bool pressed = false;
2738 if (S9xPollButton(*itr, &pressed))
2739 S9xReportButton(*itr, pressed);
2740 break;
2741 }
2742
2743 case MAP_AXIS:
2744 {
2745 int16 value = 0;
2746 if (S9xPollAxis(*itr, &value))
2747 S9xReportAxis(*itr, value);
2748 break;
2749 }
2750
2751 case MAP_POINTER:
2752 {
2753 int16 x = 0, y = 0;
2754 if (S9xPollPointer(*itr, &x, &y))
2755 S9xReportPointer(*itr, x, y);
2756 break;
2757 }
2758
2759 default:
2760 break;
2761 }
2762 }
2763}
2764
2765static void UpdatePolledMouse (int i)
2766{
2767 int16 j;
2768
2769 j = mouse[i - MOUSE0].cur_x - mouse[i - MOUSE0].old_x;
2770
2771 if (j < -127)
2772 {
2773 mouse[i - MOUSE0].delta_x = 0xff;
2774 mouse[i - MOUSE0].old_x -= 127;
2775 }
2776 else
2777 if (j < 0)
2778 {
2779 mouse[i - MOUSE0].delta_x = 0x80 | -j;
2780 mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;
2781 }
2782 else
2783 if (j > 127)
2784 {
2785 mouse[i - MOUSE0].delta_x = 0x7f;
2786 mouse[i - MOUSE0].old_x += 127;
2787 }
2788 else
2789 {
2790 mouse[i - MOUSE0].delta_x = (uint8) j;
2791 mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;
2792 }
2793
2794 j = mouse[i - MOUSE0].cur_y - mouse[i - MOUSE0].old_y;
2795
2796 if (j < -127)
2797 {
2798 mouse[i - MOUSE0].delta_y = 0xff;
2799 mouse[i - MOUSE0].old_y -= 127;
2800 }
2801 else
2802 if (j < 0)
2803 {
2804 mouse[i - MOUSE0].delta_y = 0x80 | -j;
2805 mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;
2806 }
2807 else
2808 if (j > 127)
2809 {
2810 mouse[i - MOUSE0].delta_y = 0x7f;
2811 mouse[i - MOUSE0].old_y += 127;
2812 }
2813 else
2814 {
2815 mouse[i - MOUSE0].delta_y = (uint8) j;
2816 mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;
2817 }
2818}
2819
2820void S9xSetJoypadLatch (bool latch)
2821{
2822 if (!latch && FLAG_LATCH)
2823 {
2824 // 1 written, 'plug in' new controllers now
2825 curcontrollers[0] = newcontrollers[0];
2826 curcontrollers[1] = newcontrollers[1];
2827 }
2828
2829 if (latch && !FLAG_LATCH)
2830 {
2831 int i;
2832
2833 for (int n = 0; n < 2; n++)
2834 {
2835 for (int j = 0; j < 2; j++)
2836 read_idx[n][j] = 0;
2837
2838 switch (i = curcontrollers[n])
2839 {
2840 case MP5:
2841 for (int j = 0, k; j < 4; ++j)
2842 {
2843 k = mp5[n].pads[j];
2844 if (k == NONE)
2845 continue;
2846 do_polling(k);
2847 }
2848
2849 break;
2850
2851 case JOYPAD0:
2852 case JOYPAD1:
2853 case JOYPAD2:
2854 case JOYPAD3:
2855 case JOYPAD4:
2856 case JOYPAD5:
2857 case JOYPAD6:
2858 case JOYPAD7:
2859 do_polling(i);
2860 break;
2861
2862 case MOUSE0:
2863 case MOUSE1:
2864 do_polling(i);
2865 if (!S9xMoviePlaying())
2866 UpdatePolledMouse(i);
2867 break;
2868
2869 case SUPERSCOPE:
2870 if (superscope.next_buttons & SUPERSCOPE_FIRE)
2871 {
2872 superscope.next_buttons &= ~SUPERSCOPE_TURBO;
2873 superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_TURBO;
2874 }
2875
2876 if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))
2877 {
2878 superscope.next_buttons &= ~SUPERSCOPE_OFFSCREEN;
2879 superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_OFFSCREEN;
2880 }
2881
2882 superscope.read_buttons = superscope.next_buttons;
2883
2884 superscope.next_buttons &= ~SUPERSCOPE_PAUSE;
2885 if (!(superscope.phys_buttons & SUPERSCOPE_TURBO))
2886 superscope.next_buttons &= ~(SUPERSCOPE_CURSOR | SUPERSCOPE_FIRE);
2887
2888 do_polling(i);
2889 break;
2890
2891 case TWO_JUSTIFIERS:
2892 do_polling(TWO_JUSTIFIERS);
2893 // fall through
2894
2895 case ONE_JUSTIFIER:
2896 justifier.buttons ^= JUSTIFIER_SELECT;
2897 do_polling(ONE_JUSTIFIER);
2898 break;
2899
2900 case MACSRIFLE:
2901 do_polling(i);
2902 break;
2903
2904 default:
2905 break;
2906 }
2907 }
2908 }
2909
2910 FLAG_LATCH = latch;
2911}
2912
2913// prevent read_idx from overflowing (only latching resets it)
2914// otherwise $4016/7 reads will start returning input data again
2915static inline uint8 IncreaseReadIdxPost(uint8 &var)
2916{
2917 uint8 oldval = var;
2918 if (var < 255)
2919 var++;
2920 return oldval;
2921}
2922
2923uint8 S9xReadJOYSERn (int n)
2924{
2925 int i, j, r;
2926
2927 if (n > 1)
2928 n -= 0x4016;
2929 assert(n == 0 || n == 1);
2930
2931 uint8 bits = (OpenBus & ~3) | ((n == 1) ? 0x1c : 0);
2932
2933 if (FLAG_LATCH)
2934 {
2935 switch (i = curcontrollers[n])
2936 {
2937 case MP5:
2938 return (bits | 2);
2939
2940 case JOYPAD0:
2941 case JOYPAD1:
2942 case JOYPAD2:
2943 case JOYPAD3:
2944 case JOYPAD4:
2945 case JOYPAD5:
2946 case JOYPAD6:
2947 case JOYPAD7:
2948 return (bits | ((joypad[i - JOYPAD0].buttons & 0x8000) ? 1 : 0));
2949
2950 case MOUSE0:
2951 case MOUSE1:
2952 mouse[i - MOUSE0].buttons += 0x10;
2953 if ((mouse[i - MOUSE0].buttons & 0x30) == 0x30)
2954 mouse[i - MOUSE0].buttons &= 0xcf;
2955 return (bits);
2956
2957 case SUPERSCOPE:
2958 return (bits | ((superscope.read_buttons & 0x80) ? 1 : 0));
2959
2960 case ONE_JUSTIFIER:
2961 case TWO_JUSTIFIERS:
2962 return (bits);
2963
2964 case MACSRIFLE:
2965 do_polling(i);
2966 return (bits | ((macsrifle.buttons & 0x01) ? 1 : 0));
2967
2968 default:
2969 return (bits);
2970 }
2971 }
2972 else
2973 {
2974 switch (i = curcontrollers[n])
2975 {
2976 case MP5:
2977 r = IncreaseReadIdxPost(read_idx[n][FLAG_IOBIT(n) ? 0 : 1]);
2978 j = FLAG_IOBIT(n) ? 0 : 2;
2979
2980 for (i = 0; i < 2; i++, j++)
2981 {
2982 if (mp5[n].pads[j] == NONE)
2983 continue;
2984 if (r >= 16)
2985 bits |= 1 << i;
2986 else
2987 bits |= ((joypad[mp5[n].pads[j] - JOYPAD0].buttons & (0x8000 >> r)) ? 1 : 0) << i;
2988 }
2989
2990 return (bits);
2991
2992 case JOYPAD0:
2993 case JOYPAD1:
2994 case JOYPAD2:
2995 case JOYPAD3:
2996 case JOYPAD4:
2997 case JOYPAD5:
2998 case JOYPAD6:
2999 case JOYPAD7:
3000 if (read_idx[n][0] >= 16)
3001 {
3002 IncreaseReadIdxPost(read_idx[n][0]);
3003 return (bits | 1);
3004 }
3005 else
3006 return (bits | ((joypad[i - JOYPAD0].buttons & (0x8000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3007
3008 case MOUSE0:
3009 case MOUSE1:
3010 if (read_idx[n][0] < 8)
3011 {
3012 IncreaseReadIdxPost(read_idx[n][0]);
3013 return (bits);
3014 }
3015 else
3016 if (read_idx[n][0] < 16)
3017 return (bits | ((mouse[i - MOUSE0].buttons & (0x8000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3018 else
3019 if (read_idx[n][0] < 24)
3020 return (bits | ((mouse[i - MOUSE0].delta_y & (0x800000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3021 else
3022 if (read_idx[n][0] < 32)
3023 return (bits | ((mouse[i - MOUSE0].delta_x & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3024 else
3025 {
3026 IncreaseReadIdxPost(read_idx[n][0]);
3027 return (bits | 1);
3028 }
3029
3030 case SUPERSCOPE:
3031 if (read_idx[n][0] < 8)
3032 return (bits | ((superscope.read_buttons & (0x80 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3033 else
3034 {
3035 IncreaseReadIdxPost(read_idx[n][0]);
3036 return (bits | 1);
3037 }
3038
3039 case ONE_JUSTIFIER:
3040 if (read_idx[n][0] < 24)
3041 return (bits | ((0xaa7000 >> IncreaseReadIdxPost(read_idx[n][0])) & 1));
3042 else
3043 if (read_idx[n][0] < 32)
3044 return (bits | ((justifier.buttons & (JUSTIFIER_TRIGGER | JUSTIFIER_START | JUSTIFIER_SELECT) & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3045 else
3046 {
3047 IncreaseReadIdxPost(read_idx[n][0]);
3048 return (bits | 1);
3049 }
3050
3051 case TWO_JUSTIFIERS:
3052 if (read_idx[n][0] < 24)
3053 return (bits | ((0xaa7000 >> IncreaseReadIdxPost(read_idx[n][0])) & 1));
3054 else
3055 if (read_idx[n][0] < 32)
3056 return (bits | ((justifier.buttons & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
3057 else
3058 {
3059 IncreaseReadIdxPost(read_idx[n][0]);
3060 return (bits | 1);
3061 }
3062
3063 case MACSRIFLE:
3064 do_polling(i);
3065 return (bits | ((macsrifle.buttons & 0x01) ? 1 : 0));
3066
3067 default:
3068 IncreaseReadIdxPost(read_idx[n][0]);
3069 return (bits);
3070 }
3071 }
3072}
3073
3074void S9xDoAutoJoypad (void)
3075{
3076 int i, j;
3077
3078 S9xSetJoypadLatch(1);
3079 S9xSetJoypadLatch(0);
3080
3081 S9xMovieUpdate(false);
3082
3083 for (int n = 0; n < 2; n++)
3084 {
3085 switch (i = curcontrollers[n])
3086 {
3087 case MP5:
3088 j = FLAG_IOBIT(n) ? 0 : 2;
3089 for (i = 0; i < 2; i++, j++)
3090 {
3091 if (mp5[n].pads[j] == NONE)
3092 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, 0);
3093 else
3094 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, joypad[mp5[n].pads[j] - JOYPAD0].buttons);
3095 }
3096
3097 read_idx[n][FLAG_IOBIT(n) ? 0 : 1] = 16;
3098 break;
3099
3100 case JOYPAD0:
3101 case JOYPAD1:
3102 case JOYPAD2:
3103 case JOYPAD3:
3104 case JOYPAD4:
3105 case JOYPAD5:
3106 case JOYPAD6:
3107 case JOYPAD7:
3108 read_idx[n][0] = 16;
3109 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, joypad[i - JOYPAD0].buttons);
3110 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3111 break;
3112
3113 case MOUSE0:
3114 case MOUSE1:
3115 read_idx[n][0] = 16;
3116 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, mouse[i - MOUSE0].buttons);
3117 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3118 break;
3119
3120 case SUPERSCOPE:
3121 read_idx[n][0] = 16;
3122 Memory.FillRAM[0x4218 + n * 2] = 0xff;
3123 Memory.FillRAM[0x4219 + n * 2] = superscope.read_buttons;
3124 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3125 break;
3126
3127 case ONE_JUSTIFIER:
3128 case TWO_JUSTIFIERS:
3129 read_idx[n][0] = 16;
3130 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0x000e);
3131 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3132 break;
3133
3134 case MACSRIFLE:
3135 read_idx[n][0] = 16;
3136 Memory.FillRAM[0x4218 + n * 2] = 0xff;
3137 Memory.FillRAM[0x4219 + n * 2] = macsrifle.buttons;
3138 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3139 break;
3140
3141 default:
3142 WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0);
3143 WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
3144 break;
3145 }
3146 }
3147}
3148
3149void S9xControlEOF (void)
3150{
3151 struct crosshair *c;
3152 int i, j;
3153
3154 PPU.GunVLatch = 1000; // i.e., never latch
3155 PPU.GunHLatch = 0;
3156
3157 for (int n = 0; n < 2; n++)
3158 {
3159 switch (i = curcontrollers[n])
3160 {
3161 case MP5:
3162 for (j = 0; j < 4; ++j)
3163 {
3164 i = mp5[n].pads[j];
3165 if (i == NONE)
3166 continue;
3167
3168 if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)
3169 {
3170 joypad[i - JOYPAD0].turbo_ct = 0;
3171 joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;
3172 }
3173 }
3174
3175 break;
3176
3177 case JOYPAD0:
3178 case JOYPAD1:
3179 case JOYPAD2:
3180 case JOYPAD3:
3181 case JOYPAD4:
3182 case JOYPAD5:
3183 case JOYPAD6:
3184 case JOYPAD7:
3185 if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)
3186 {
3187 joypad[i - JOYPAD0].turbo_ct = 0;
3188 joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;
3189 }
3190
3191 break;
3192
3193 case MOUSE0:
3194 case MOUSE1:
3195 c = &mouse[i - MOUSE0].crosshair;
3196 if (IPPU.RenderThisFrame)
3197 S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, mouse[i - MOUSE0].cur_x, mouse[i - MOUSE0].cur_y);
3198 break;
3199
3200 case SUPERSCOPE:
3201 if (n == 1 && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))
3202 {
3203 if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))
3204 DoGunLatch(superscope.x, superscope.y);
3205
3206 c = &superscope.crosshair;
3207 if (IPPU.RenderThisFrame)
3208 S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, superscope.x, superscope.y);
3209 }
3210
3211 break;
3212
3213 case TWO_JUSTIFIERS:
3214 if (n == 1 && !justifier.offscreen[1])
3215 {
3216 c = &justifier.crosshair[1];
3217 if (IPPU.RenderThisFrame)
3218 S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[1], justifier.y[1]);
3219 }
3220
3221 i = (justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0;
3222 goto do_justifier;
3223
3224 case ONE_JUSTIFIER:
3225 i = (justifier.buttons & JUSTIFIER_SELECT) ? -1 : 0;
3226
3227 do_justifier:
3228 if (n == 1)
3229 {
3230 if (i >= 0 && !justifier.offscreen[i])
3231 DoGunLatch(justifier.x[i], justifier.y[i]);
3232
3233 if (!justifier.offscreen[0])
3234 {
3235 c = &justifier.crosshair[0];
3236 if (IPPU.RenderThisFrame)
3237 S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[0], justifier.y[0]);
3238 }
3239 }
3240
3241 break;
3242
3243 case MACSRIFLE:
3244 if (n == 1)
3245 {
3246 DoMacsRifleLatch(macsrifle.x, macsrifle.y);
3247
3248 c = &macsrifle.crosshair;
3249 if (IPPU.RenderThisFrame)
3250 S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, macsrifle.x, macsrifle.y);
3251 }
3252
3253 break;
3254
3255 default:
3256 break;
3257 }
3258 }
3259
3260 for (int n = 0; n < 8; n++)
3261 {
3262 if (!pseudopointer[n].mapped)
3263 continue;
3264
3265 if (pseudopointer[n].H_adj)
3266 {
3267 pseudopointer[n].x += pseudopointer[n].H_adj;
3268 if (pseudopointer[n].x < 0)
3269 pseudopointer[n].x = 0;
3270 else
3271 if (pseudopointer[n].x > 255)
3272 pseudopointer[n].x = 255;
3273
3274 if (pseudopointer[n].H_var)
3275 {
3276 if (pseudopointer[n].H_adj < 0)
3277 {
3278 if (pseudopointer[n].H_adj > -ptrspeeds[3])
3279 pseudopointer[n].H_adj--;
3280 }
3281 else
3282 {
3283 if (pseudopointer[n].H_adj < ptrspeeds[3])
3284 pseudopointer[n].H_adj++;
3285 }
3286 }
3287 }
3288
3289 if (pseudopointer[n].V_adj)
3290 {
3291 pseudopointer[n].y += pseudopointer[n].V_adj;
3292 if (pseudopointer[n].y < 0)
3293 pseudopointer[n].y = 0;
3294 else
3295 if (pseudopointer[n].y > PPU.ScreenHeight - 1)
3296 pseudopointer[n].y = PPU.ScreenHeight - 1;
3297
3298 if (pseudopointer[n].V_var)
3299 {
3300 if (pseudopointer[n].V_adj < 0)
3301 {
3302 if (pseudopointer[n].V_adj > -ptrspeeds[3])
3303 pseudopointer[n].V_adj--;
3304 }
3305 else
3306 {
3307 if (pseudopointer[n].V_adj < ptrspeeds[3])
3308 pseudopointer[n].V_adj++;
3309 }
3310 }
3311 }
3312
3313 S9xReportPointer(PseudoPointerBase + n, pseudopointer[n].x, pseudopointer[n].y);
3314 }
3315
3316 set<struct exemulti *>::iterator it, jt;
3317
3318 for (it = exemultis.begin(); it != exemultis.end(); it++)
3319 {
3320 i = ApplyMulti((*it)->script, (*it)->pos, (*it)->data1);
3321
3322 if (i >= 0)
3323 (*it)->pos = i;
3324 else
3325 {
3326 jt = it;
3327 it--;
3328 delete *jt;
3329 exemultis.erase(jt);
3330 }
3331 }
3332
3333 do_polling(POLL_ALL);
3334
3335 pad_read_last = pad_read;
3336 pad_read = false;
3337}
3338
3339void S9xSetControllerCrosshair (enum crosscontrols ctl, int8 idx, const char *fg, const char *bg)
3340{
3341 struct crosshair *c;
3342 int8 fgcolor = -1, bgcolor = -1;
3343 int i, j;
3344
3345 if (idx < -1 || idx > 31)
3346 {
3347 fprintf(stderr, "S9xSetControllerCrosshair() called with invalid index\n");
3348 return;
3349 }
3350
3351 switch (ctl)
3352 {
3353 case X_MOUSE1: c = &mouse[0].crosshair; break;
3354 case X_MOUSE2: c = &mouse[1].crosshair; break;
3355 case X_SUPERSCOPE: c = &superscope.crosshair; break;
3356 case X_JUSTIFIER1: c = &justifier.crosshair[0]; break;
3357 case X_JUSTIFIER2: c = &justifier.crosshair[1]; break;
3358 case X_MACSRIFLE: c = &macsrifle.crosshair; break;
3359 default:
3360 fprintf(stderr, "S9xSetControllerCrosshair() called with an invalid controller ID %d\n", ctl);
3361 return;
3362 }
3363
3364 if (fg)
3365 {
3366 fgcolor = 0;
3367 if (*fg == 't')
3368 {
3369 fg++;
3370 fgcolor = 16;
3371 }
3372
3373 for (i = 0; i < 16; i++)
3374 {
3375 for (j = 0; color_names[i][j] && fg[j] == color_names[i][j]; j++) ;
3376
3377 if (isalnum(fg[j]))
3378 continue;
3379
3380 if (!color_names[i][j])
3381 break;
3382 }
3383
3384 fgcolor |= i;
3385 if (i > 15 || fgcolor == 16)
3386 {
3387 fprintf(stderr, "S9xSetControllerCrosshair() called with invalid fgcolor\n");
3388 return;
3389 }
3390 }
3391
3392 if (bg)
3393 {
3394 bgcolor = 0;
3395 if (*bg == 't')
3396 {
3397 bg++;
3398 bgcolor = 16;
3399 }
3400
3401 for (i = 0; i < 16; i++)
3402 {
3403 for (j = 0; color_names[i][j] && bg[j] == color_names[i][j]; j++) ;
3404
3405 if (isalnum(bg[j]))
3406 continue;
3407
3408 if (!color_names[i][j])
3409 break;
3410 }
3411
3412 bgcolor |= i;
3413 if (i > 15 || bgcolor == 16)
3414 {
3415 fprintf(stderr, "S9xSetControllerCrosshair() called with invalid bgcolor\n");
3416 return;
3417 }
3418 }
3419
3420 if (idx != -1)
3421 {
3422 c->set |= 1;
3423 c->img = idx;
3424 }
3425
3426 if (fgcolor != -1)
3427 {
3428 c->set |= 2;
3429 c->fg = fgcolor;
3430 }
3431
3432 if (bgcolor != -1)
3433 {
3434 c->set |= 4;
3435 c->bg = bgcolor;
3436 }
3437}
3438
3439void S9xGetControllerCrosshair (enum crosscontrols ctl, int8 *idx, const char **fg, const char **bg)
3440{
3441 struct crosshair *c;
3442
3443 switch (ctl)
3444 {
3445 case X_MOUSE1: c = &mouse[0].crosshair; break;
3446 case X_MOUSE2: c = &mouse[1].crosshair; break;
3447 case X_SUPERSCOPE: c = &superscope.crosshair; break;
3448 case X_JUSTIFIER1: c = &justifier.crosshair[0]; break;
3449 case X_JUSTIFIER2: c = &justifier.crosshair[1]; break;
3450 case X_MACSRIFLE: c = &macsrifle.crosshair; break;
3451 default:
3452 fprintf(stderr, "S9xGetControllerCrosshair() called with an invalid controller ID %d\n", ctl);
3453 return;
3454 }
3455
3456 if (idx)
3457 *idx = c->img;
3458
3459 if (fg)
3460 *fg = color_names[c->fg];
3461
3462 if (bg)
3463 *bg = color_names[c->bg];
3464}
3465
3466void S9xControlPreSaveState (struct SControlSnapshot *s)
3467{
3468 memset(s, 0, sizeof(*s));
3469 s->ver = 4;
3470
3471 for (int j = 0; j < 2; j++)
3472 {
3473 s->port1_read_idx[j] = read_idx[0][j];
3474 s->port2_read_idx[j] = read_idx[1][j];
3475 }
3476
3477 for (int j = 0; j < 2; j++)
3478 s->mouse_speed[j] = (mouse[j].buttons & 0x30) >> 4;
3479
3480 s->justifier_select = ((justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0);
3481
3482#define COPY(x) { memcpy((char *) s->internal + i, &(x), sizeof(x)); i += sizeof(x); }
3483
3484 int i = 0;
3485
3486 for (int j = 0; j < 8; j++)
3487 COPY(joypad[j].buttons);
3488
3489 for (int j = 0; j < 2; j++)
3490 {
3491 COPY(mouse[j].delta_x);
3492 COPY(mouse[j].delta_y);
3493 COPY(mouse[j].old_x);
3494 COPY(mouse[j].old_y);
3495 COPY(mouse[j].cur_x);
3496 COPY(mouse[j].cur_y);
3497 COPY(mouse[j].buttons);
3498 }
3499
3500 COPY(superscope.x);
3501 COPY(superscope.y);
3502 COPY(superscope.phys_buttons);
3503 COPY(superscope.next_buttons);
3504 COPY(superscope.read_buttons);
3505
3506 for (int j = 0; j < 2; j++)
3507 COPY(justifier.x[j]);
3508 for (int j = 0; j < 2; j++)
3509 COPY(justifier.y[j]);
3510 COPY(justifier.buttons);
3511 for (int j = 0; j < 2; j++)
3512 COPY(justifier.offscreen[j]);
3513
3514 for (int j = 0; j < 2; j++)
3515 for (int k = 0; k < 2; k++)
3516 COPY(mp5[j].pads[k]);
3517
3518 COPY(macsrifle.x);
3519 COPY(macsrifle.y);
3520 COPY(macsrifle.buttons);
3521
3522 assert(i == sizeof(s->internal) + sizeof(s->internal_macs));
3523
3524#undef COPY
3525
3526 s->pad_read = pad_read;
3527 s->pad_read_last = pad_read_last;
3528}
3529
3530void S9xControlPostLoadState (struct SControlSnapshot *s)
3531{
3532 if (curcontrollers[0] == MP5 && s->ver < 1)
3533 {
3534 // Crap. Old snes9x didn't support this.
3535 S9xMessage(S9X_WARNING, S9X_FREEZE_FILE_INFO, "Old savestate has no support for MP5 in port 1.");
3536 newcontrollers[0] = curcontrollers[0];
3537 curcontrollers[0] = mp5[0].pads[0];
3538 }
3539
3540 for (int j = 0; j < 2; j++)
3541 {
3542 read_idx[0][j] = s->port1_read_idx[j];
3543 read_idx[1][j] = s->port2_read_idx[j];
3544 }
3545
3546 for (int j = 0; j < 2; j++)
3547 mouse[j].buttons |= (s->mouse_speed[j] & 3) << 4;
3548
3549 if (s->justifier_select & 1)
3550 justifier.buttons |= JUSTIFIER_SELECT;
3551 else
3552 justifier.buttons &= ~JUSTIFIER_SELECT;
3553
3554 FLAG_LATCH = (Memory.FillRAM[0x4016] & 1) == 1;
3555
3556 if (s->ver > 1)
3557 {
3558 #define COPY(x) { memcpy(&(x), (char *) s->internal + i, sizeof(x)); i += sizeof(x); }
3559
3560 int i = 0;
3561
3562 for (int j = 0; j < 8; j++)
3563 COPY(joypad[j].buttons);
3564
3565 for (int j = 0; j < 2; j++)
3566 {
3567 COPY(mouse[j].delta_x);
3568 COPY(mouse[j].delta_y);
3569 COPY(mouse[j].old_x);
3570 COPY(mouse[j].old_y);
3571 COPY(mouse[j].cur_x);
3572 COPY(mouse[j].cur_y);
3573 COPY(mouse[j].buttons);
3574 }
3575
3576 COPY(superscope.x);
3577 COPY(superscope.y);
3578 COPY(superscope.phys_buttons);
3579 COPY(superscope.next_buttons);
3580 COPY(superscope.read_buttons);
3581
3582 for (int j = 0; j < 2; j++)
3583 COPY(justifier.x[j]);
3584 for (int j = 0; j < 2; j++)
3585 COPY(justifier.y[j]);
3586 COPY(justifier.buttons);
3587 for (int j = 0; j < 2; j++)
3588 COPY(justifier.offscreen[j]);
3589 for (int j = 0; j < 2; j++)
3590 for (int k = 0; k < 2; k++)
3591 COPY(mp5[j].pads[k]);
3592
3593 assert(i == sizeof(s->internal));
3594
3595 if (s->ver > 3)
3596 {
3597 COPY(macsrifle.x);
3598 COPY(macsrifle.y);
3599 COPY(macsrifle.buttons);
3600
3601 assert(i == sizeof(s->internal) + sizeof(s->internal_macs));
3602 }
3603
3604 #undef COPY
3605 }
3606
3607 if (s->ver > 2)
3608 {
3609 pad_read = s->pad_read;
3610 pad_read_last = s->pad_read_last;
3611 }
3612}
3613
3614uint16 MovieGetJoypad (int i)
3615{
3616 if (i < 0 || i > 7)
3617 return (0);
3618
3619 return (joypad[i].buttons);
3620}
3621
3622void MovieSetJoypad (int i, uint16 buttons)
3623{
3624 if (i < 0 || i > 7)
3625 return;
3626
3627 joypad[i].buttons = buttons;
3628}
3629
3630bool MovieGetMouse (int i, uint8 out[5])
3631{
3632 if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))
3633 return (false);
3634
3635 int n = curcontrollers[i] - MOUSE0;
3636 uint8 *ptr = out;
3637
3638 WRITE_WORD(ptr, mouse[n].cur_x); ptr += 2;
3639 WRITE_WORD(ptr, mouse[n].cur_y); ptr += 2;
3640 *ptr = mouse[n].buttons;
3641
3642 return (true);
3643}
3644
3645void MovieSetMouse (int i, uint8 in[5], bool inPolling)
3646{
3647 if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))
3648 return;
3649
3650 int n = curcontrollers[i] - MOUSE0;
3651 uint8 *ptr = in;
3652
3653 mouse[n].cur_x = READ_WORD(ptr); ptr += 2;
3654 mouse[n].cur_y = READ_WORD(ptr); ptr += 2;
3655 mouse[n].buttons = *ptr;
3656
3657 if (inPolling)
3658 UpdatePolledMouse(curcontrollers[i]);
3659}
3660
3661bool MovieGetScope (int i, uint8 out[6])
3662{
3663 if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)
3664 return (false);
3665
3666 uint8 *ptr = out;
3667
3668 WRITE_WORD(ptr, superscope.x); ptr += 2;
3669 WRITE_WORD(ptr, superscope.y); ptr += 2;
3670 *ptr++ = superscope.phys_buttons;
3671 *ptr = superscope.next_buttons;
3672
3673 return (true);
3674}
3675
3676void MovieSetScope (int i, uint8 in[6])
3677{
3678 if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)
3679 return;
3680
3681 uint8 *ptr = in;
3682
3683 superscope.x = READ_WORD(ptr); ptr += 2;
3684 superscope.y = READ_WORD(ptr); ptr += 2;
3685 superscope.phys_buttons = *ptr++;
3686 superscope.next_buttons = *ptr;
3687}
3688
3689bool MovieGetJustifier (int i, uint8 out[11])
3690{
3691 if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))
3692 return (false);
3693
3694 uint8 *ptr = out;
3695
3696 WRITE_WORD(ptr, justifier.x[0]); ptr += 2;
3697 WRITE_WORD(ptr, justifier.x[1]); ptr += 2;
3698 WRITE_WORD(ptr, justifier.y[0]); ptr += 2;
3699 WRITE_WORD(ptr, justifier.y[1]); ptr += 2;
3700 *ptr++ = justifier.buttons;
3701 *ptr++ = justifier.offscreen[0];
3702 *ptr = justifier.offscreen[1];
3703
3704 return (true);
3705}
3706
3707void MovieSetJustifier (int i, uint8 in[11])
3708{
3709 if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))
3710 return;
3711
3712 uint8 *ptr = in;
3713
3714 justifier.x[0] = READ_WORD(ptr); ptr += 2;
3715 justifier.x[1] = READ_WORD(ptr); ptr += 2;
3716 justifier.y[0] = READ_WORD(ptr); ptr += 2;
3717 justifier.y[1] = READ_WORD(ptr); ptr += 2;
3718 justifier.buttons = *ptr++;
3719 justifier.offscreen[0] = *ptr++;
3720 justifier.offscreen[1] = *ptr;
3721}
3722
3723bool MovieGetMacsRifle (int i, uint8 out[5])
3724{
3725 if (i < 0 || i > 1 || curcontrollers[i] != MACSRIFLE)
3726 return (false);
3727
3728 uint8 *ptr = out;
3729
3730 WRITE_WORD(ptr, macsrifle.x); ptr += 2;
3731 WRITE_WORD(ptr, macsrifle.y); ptr += 2;
3732 *ptr = macsrifle.buttons;
3733
3734 return (true);
3735}
3736
3737void MovieSetMacsRifle (int i, uint8 in[5])
3738{
3739 if (i < 0 || i > 1 || curcontrollers[i] != MACSRIFLE)
3740 return;
3741
3742 uint8 *ptr = in;
3743
3744 macsrifle.x = READ_WORD(ptr); ptr += 2;
3745 macsrifle.y = READ_WORD(ptr); ptr += 2;
3746 macsrifle.buttons = *ptr;
3747}
3748
3749