Files
lr-snes9x/controls.cpp
2026-08-10 19:32:31 +02:00

1868 lines
40 KiB
C++

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <map>
#include <set>
#include <vector>
#include <string>
#include <algorithm>
#include <assert.h>
#include <ctype.h>
#include "snes9x.h"
#include "memmap.h"
#include "apu/apu.h"
#include "snapshot.h"
#include "controls.h"
#include "crosshairs.h"
#include "display.h"
using namespace std;
#define NONE (-2)
#define MP5 (-1)
#define JOYPAD0 0
#define JOYPAD1 1
#define JOYPAD2 2
#define JOYPAD3 3
#define JOYPAD4 4
#define JOYPAD5 5
#define JOYPAD6 6
#define JOYPAD7 7
#define MOUSE0 8
#define MOUSE1 9
#define SUPERSCOPE 10
#define ONE_JUSTIFIER 11
#define TWO_JUSTIFIERS 12
#define MACSRIFLE 13
#define NUMCTLS 14 // This must be LAST
#define SUPERSCOPE_FIRE 0x80
#define SUPERSCOPE_CURSOR 0x40
#define SUPERSCOPE_TURBO 0x20
#define SUPERSCOPE_PAUSE 0x10
#define SUPERSCOPE_OFFSCREEN 0x02
#define JUSTIFIER_TRIGGER 0x80
#define JUSTIFIER_START 0x20
#define JUSTIFIER_SELECT 0x08
#define MACSRIFLE_TRIGGER 0x01
#define MAP_UNKNOWN (-1)
#define MAP_NONE 0
#define MAP_BUTTON 1
#define MAP_POINTER 3
#define FLAG_IOBIT0 (Memory.FillRAM[0x4213] & 0x40)
#define FLAG_IOBIT1 (Memory.FillRAM[0x4213] & 0x80)
#define FLAG_IOBIT(n) ((n) ? (FLAG_IOBIT1) : (FLAG_IOBIT0))
bool8 pad_read = 0, pad_read_last = 0;
uint8 read_idx[2 /* ports */][2 /* per port */];
struct exemulti
{
int32 pos;
bool8 data1;
s9xcommand_t *script;
};
struct crosshair
{
uint8 set;
uint8 img;
uint8 fg, bg;
};
static struct
{
uint16 buttons;
} joypad[8];
static struct
{
uint8 delta_x, delta_y;
int16 old_x, old_y;
int16 cur_x, cur_y;
uint8 buttons;
uint32 ID;
struct crosshair crosshair;
} mouse[2];
static struct
{
int16 x, y;
uint8 phys_buttons;
uint8 next_buttons;
uint8 read_buttons;
uint32 ID;
struct crosshair crosshair;
} superscope;
static struct
{
int16 x[2], y[2];
uint8 buttons;
bool8 offscreen[2];
uint32 ID[2];
struct crosshair crosshair[2];
} justifier;
static struct
{
int8 pads[4];
} mp5[2];
static struct
{
int16 x, y;
uint8 buttons;
uint32 ID;
struct crosshair crosshair;
} macsrifle;
static set<struct exemulti *> exemultis;
static map<uint32, s9xcommand_t> keymap;
static vector<s9xcommand_t *> multis;
static bool8 FLAG_LATCH = FALSE;
static int32 curcontrollers[2] = { NONE, NONE };
static int32 newcontrollers[2] = { JOYPAD0, NONE };
static char buf[256];
static const char *color_names[32] =
{
"Trans",
"Black",
"25Grey",
"50Grey",
"75Grey",
"White",
"Red",
"Orange",
"Yellow",
"Green",
"Cyan",
"Sky",
"Blue",
"Violet",
"MagicPink",
"Purple",
NULL,
"tBlack",
"t25Grey",
"t50Grey",
"t75Grey",
"tWhite",
"tRed",
"tOrange",
"tYellow",
"tGreen",
"tCyan",
"tSky",
"tBlue",
"tViolet",
"tMagicPink",
"tPurple"
};
static void DoGunLatch (int x, int y)
{
x += 40;
if (x > 295)
x = 295;
else if (x < 40)
x = 40;
if (y > PPU.ScreenHeight - 1)
y = PPU.ScreenHeight - 1;
else if (y < 0)
y = 0;
PPU.GunVLatch = (uint16) (y + 1);
PPU.GunHLatch = (uint16) x;
}
static void DoMacsRifleLatch (int x, int y)
{
PPU.GunVLatch = (uint16) (y + 42);// + (int16) macsrifle.adjust_y;
PPU.GunHLatch = (uint16) (x + 76);// + (int16) macsrifle.adjust_x;
}
static int maptype (int t)
{
switch (t)
{
case S9xNoMapping:
return (MAP_NONE);
case S9xButtonMouse:
case S9xButtonSuperscope:
case S9xButtonJustifier:
case S9xButtonMacsRifle:
case S9xButtonMulti:
return (MAP_BUTTON);
case S9xPointer:
return (MAP_POINTER);
default:
return (MAP_UNKNOWN);
}
}
void S9xControlsReset (void)
{
S9xControlsSoftReset();
mouse[0].buttons &= ~0x30;
mouse[1].buttons &= ~0x30;
justifier.buttons &= ~JUSTIFIER_SELECT;
macsrifle.buttons = 0;
}
void S9xControlsSoftReset (void)
{
for (set<struct exemulti *>::iterator it = exemultis.begin(); it != exemultis.end(); it++)
delete *it;
exemultis.clear();
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
read_idx[i][j]=0;
FLAG_LATCH = FALSE;
curcontrollers[0] = newcontrollers[0];
curcontrollers[1] = newcontrollers[1];
}
/* Raw absolute pad state, written once per frame by the libretro port.
No edges, no toggles, no per-button command dispatch: the frontend's
view of the pad IS the pad. The only filtering is the hardware-
impossible opposing-direction case, honouring Settings.UpAndDown the
same way S9xApplyCommand used to. */
void S9xSetJoypadButtons (int pad, uint16 buttons)
{
if (pad < 0 || pad > 7)
return;
if (!Settings.UpAndDown)
{
if ((buttons & (SNES_LEFT_MASK | SNES_RIGHT_MASK)) == (SNES_LEFT_MASK | SNES_RIGHT_MASK))
buttons &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);
if ((buttons & (SNES_UP_MASK | SNES_DOWN_MASK)) == (SNES_UP_MASK | SNES_DOWN_MASK))
buttons &= ~(SNES_UP_MASK | SNES_DOWN_MASK);
}
joypad[pad].buttons = buttons;
}
void S9xUnmapAllControls (void)
{
S9xControlsReset();
keymap.clear();
for (int i = 0; i < (int) multis.size(); i++)
free(multis[i]);
multis.clear();
for (int i = 0; i < 8; i++)
joypad[i].buttons = 0;
for (int i = 0; i < 2; i++)
{
mouse[i].old_x = mouse[i].old_y = 0;
mouse[i].cur_x = mouse[i].cur_y = 0;
mouse[i].buttons = 1;
mouse[i].ID = InvalidControlID;
if (!(mouse[i].crosshair.set & 1))
mouse[i].crosshair.img = 0; // no image for mouse because its only logical position is game-specific, not known by the emulator
if (!(mouse[i].crosshair.set & 2))
mouse[i].crosshair.fg = 5;
if (!(mouse[i].crosshair.set & 4))
mouse[i].crosshair.bg = 1;
justifier.x[i] = justifier.y[i] = 0;
justifier.offscreen[i] = 0;
justifier.ID[i] = InvalidControlID;
if (!(justifier.crosshair[i].set & 1))
justifier.crosshair[i].img = 4;
if (!(justifier.crosshair[i].set & 2))
justifier.crosshair[i].fg = i ? 14 : 12;
if (!(justifier.crosshair[i].set & 4))
justifier.crosshair[i].bg = 1;
}
justifier.buttons = 0;
superscope.x = superscope.y = 0;
superscope.phys_buttons = 0;
superscope.next_buttons = 0;
superscope.read_buttons = 0;
superscope.ID = InvalidControlID;
if (!(superscope.crosshair.set & 1))
superscope.crosshair.img = 2;
if (!(superscope.crosshair.set & 2))
superscope.crosshair.fg = 5;
if (!(superscope.crosshair.set & 4))
superscope.crosshair.bg = 1;
macsrifle.x = macsrifle.y = 0;
macsrifle.buttons = 0;
macsrifle.ID = InvalidControlID;
if (!(macsrifle.crosshair.set & 1))
macsrifle.crosshair.img = 2;
if (!(macsrifle.crosshair.set & 2))
macsrifle.crosshair.fg = 5;
if (!(macsrifle.crosshair.set & 4))
macsrifle.crosshair.bg = 1;
}
void S9xSetController (int port, enum controllers controller, int8 id1, int8 id2, int8 id3, int8 id4)
{
if (port < 0 || port > 1)
return;
switch (controller)
{
case CTL_NONE:
break;
case CTL_JOYPAD:
if (id1 < 0 || id1 > 7)
break;
newcontrollers[port] = JOYPAD0 + id1;
return;
case CTL_MOUSE:
if (id1 < 0 || id1 > 1)
break;
if (!Settings.MouseMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");
break;
}
newcontrollers[port] = MOUSE0 + id1;
return;
case CTL_SUPERSCOPE:
if (!Settings.SuperScopeMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");
break;
}
newcontrollers[port] = SUPERSCOPE;
return;
case CTL_JUSTIFIER:
if (id1 < 0 || id1 > 1)
break;
if (!Settings.JustifierMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");
break;
}
newcontrollers[port] = ONE_JUSTIFIER + id1;
return;
case CTL_MACSRIFLE:
if (!Settings.MacsRifleMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES M.A.C.S. Rifle: MacsRifleMaster disabled");
break;
}
newcontrollers[port] = MACSRIFLE;
return;
case CTL_MP5:
if (id1 < -1 || id1 > 7)
break;
if (id2 < -1 || id2 > 7)
break;
if (id3 < -1 || id3 > 7)
break;
if (id4 < -1 || id4 > 7)
break;
if (!Settings.MultiPlayer5Master)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");
break;
}
newcontrollers[port] = MP5;
mp5[port].pads[0] = (id1 < 0) ? NONE : JOYPAD0 + id1;
mp5[port].pads[1] = (id2 < 0) ? NONE : JOYPAD0 + id2;
mp5[port].pads[2] = (id3 < 0) ? NONE : JOYPAD0 + id3;
mp5[port].pads[3] = (id4 < 0) ? NONE : JOYPAD0 + id4;
return;
default:
fprintf(stderr, "Unknown controller type %d\n", controller);
break;
}
newcontrollers[port] = NONE;
}
bool S9xVerifyControllers (void)
{
bool ret = false;
int port, i, used[NUMCTLS];
for (i = 0; i < NUMCTLS; used[i++] = 0) ;
for (port = 0; port < 2; port++)
{
switch (i = newcontrollers[port])
{
case MOUSE0:
case MOUSE1:
if (!Settings.MouseMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");
newcontrollers[port] = NONE;
ret = true;
break;
}
if (used[i]++ > 0)
{
snprintf(buf, sizeof(buf), "Mouse%d used more than once! Disabling extra instances", i - MOUSE0 + 1);
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
newcontrollers[port] = NONE;
ret = true;
break;
}
break;
case SUPERSCOPE:
if (!Settings.SuperScopeMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");
newcontrollers[port] = NONE;
ret = true;
break;
}
if (used[i]++ > 0)
{
snprintf(buf, sizeof(buf), "Superscope used more than once! Disabling extra instances");
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
newcontrollers[port] = NONE;
ret = true;
break;
}
break;
case ONE_JUSTIFIER:
case TWO_JUSTIFIERS:
if (!Settings.JustifierMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");
newcontrollers[port] = NONE;
ret = true;
break;
}
if (used[ONE_JUSTIFIER]++ > 0)
{
snprintf(buf, sizeof(buf), "Justifier used more than once! Disabling extra instances");
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
newcontrollers[port] = NONE;
ret = true;
break;
}
break;
case MACSRIFLE:
if (!Settings.MacsRifleMaster)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES M.A.C.S. Rifle: MacsRifleMaster disabled");
newcontrollers[port] = NONE;
ret = true;
break;
}
if (used[i]++ > 0)
{
snprintf(buf, sizeof(buf), "M.A.C.S. Rifle used more than once! Disabling extra instances");
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
newcontrollers[port] = NONE;
ret = true;
break;
}
break;
case MP5:
if (!Settings.MultiPlayer5Master)
{
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");
newcontrollers[port] = NONE;
ret = true;
break;
}
for (i = 0; i < 4; i++)
{
if (mp5[port].pads[i] != NONE)
{
if (used[mp5[port].pads[i] - JOYPAD0]++ > 0)
{
snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", mp5[port].pads[i] - JOYPAD0 + 1);
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
mp5[port].pads[i] = NONE;
ret = true;
break;
}
}
}
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
if (used[i - JOYPAD0]++ > 0)
{
snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", i - JOYPAD0 + 1);
S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);
newcontrollers[port] = NONE;
ret = true;
break;
}
break;
default:
break;
}
}
return (ret);
}
s9xcommand_t S9xGetCommandT (const char *name)
{
s9xcommand_t cmd;
int i, j;
const char *s;
memset(&cmd, 0, sizeof(cmd));
cmd.type = S9xBadMapping;
cmd.multi_press = 0;
if (!strcmp(name, "None"))
cmd.type = S9xNoMapping;
else
if (!strncmp(name, "Mouse", 5))
{
if (name[5] < '1' || name[5] > '2' || name[6] != ' ')
return (cmd);
cmd.button.mouse.idx = name[5] - '1';
s = name + 7;
i = 0;
if ((cmd.button.mouse.left = (*s == 'L'))) s += i = 1;
if ((cmd.button.mouse.right = (*s == 'R'))) s += i = 1;
if (i == 0 || *s != 0)
return (cmd);
cmd.type = S9xButtonMouse;
}
else
if (!strncmp(name, "Superscope ", 11))
{
s = name + 11;
i = 0;
if ((cmd.button.scope.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }
if ((cmd.button.scope.fire = strncmp(s, "Fire", 4) ? 0 : 1)) { s += i = 4; if (*s == '+') s++; }
if ((cmd.button.scope.cursor = strncmp(s, "Cursor", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
if ((cmd.button.scope.turbo = strncmp(s, "ToggleTurbo", 11) ? 0 : 1)) { s += i = 11; if (*s == '+') s++; }
if ((cmd.button.scope.pause = strncmp(s, "Pause", 5) ? 0 : 1)) { s += i = 5; }
if (i == 0 || *s != 0 || *(s - 1) == '+')
return (cmd);
cmd.type = S9xButtonSuperscope;
}
else
if (!strncmp(name, "Justifier", 9))
{
if (name[9] < '1' || name[9] > '2' || name[10] != ' ')
return (cmd);
cmd.button.justifier.idx = name[9] - '1';
s = name + 11;
i = 0;
if ((cmd.button.justifier.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }
if ((cmd.button.justifier.trigger = strncmp(s, "Trigger", 7) ? 0 : 1)) { s += i = 7; if (*s == '+') s++; }
if ((cmd.button.justifier.start = strncmp(s, "Start", 5) ? 0 : 1)) { s += i = 5; }
if (i == 0 || *s != 0 || *(s - 1) == '+')
return (cmd);
cmd.type = S9xButtonJustifier;
}
else
if (!strncmp(name, "MacsRifle ", 10))
{
s = name + 10;
i = 0;
if ((cmd.button.macsrifle.trigger = strncmp(s, "Trigger", 7) ? 0 : 1)) { s += i = 7; }
if (i == 0 || *s != 0 || *(s - 1) == '+')
return (cmd);
cmd.type = S9xButtonMacsRifle;
}
else
if (!strncmp(name, "Pointer ", 8))
{
s = name + 8;
i = 0;
if ((cmd.pointer.aim_mouse0 = strncmp(s, "Mouse1", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
if ((cmd.pointer.aim_mouse1 = strncmp(s, "Mouse2", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }
if ((cmd.pointer.aim_scope = strncmp(s, "Superscope", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
if ((cmd.pointer.aim_justifier0 = strncmp(s, "Justifier1", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
if ((cmd.pointer.aim_justifier1 = strncmp(s, "Justifier2", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }
if ((cmd.pointer.aim_macsrifle = strncmp(s, "MacsRifle", 9) ? 0 : 1)) { s += i = 9; }
if (i == 0 || *s != 0 || *(s - 1) == '+')
return (cmd);
cmd.type = S9xPointer;
}
else
if (!strncmp(name, "MULTI#", 6))
{
i = strtol(name + 6, (char **) &s, 10);
if (s != NULL && *s != '\0')
return (cmd);
if (i >= (int) multis.size())
return (cmd);
cmd.button.multi_idx = i;
cmd.type = S9xButtonMulti;
}
else
if (((name[0] == '+' && name[1] == '{') || name[0] == '{') && name[strlen(name) - 1] == '}')
{
if (multis.size() > 2147483640)
{
fprintf(stderr, "Too many multis!");
return (cmd);
}
string x;
int n;
j = 2;
for (i = (name[0] == '+') ? 2 : 1; name[i] != '\0'; i++)
{
if (name[i] == ',' || name[i] == ';')
{
if (name[i] == ';')
j++;
if (++j > 2147483640)
{
fprintf(stderr, "Multi too long!");
return (cmd);
}
}
if (name[i] == '{')
return (cmd);
}
s9xcommand_t *c = (s9xcommand_t *) calloc(j, sizeof(s9xcommand_t));
if (c == NULL)
{
perror("malloc error while parsing multi");
return (cmd);
}
n = 0;
i = (name[0] == '+') ? 2 : 1;
do
{
if (name[i] == ';')
{
c[n].type = S9xNoMapping;
c[n].multi_press = 0;
j = i;
}
else if (name[i] == ',')
{
free(c);
return (cmd);
}
else
{
uint8 press = 0;
if (name[0] == '+')
{
if (name[i] == '+')
press = 1;
else if (name[i] == '-')
press = 2;
else
{
free(c);
return (cmd);
}
i++;
}
for (j = i; name[j] != ';' && name[j] != ',' && name[j] != '}'; j++) ;
x.assign(name + i, j - i);
c[n] = S9xGetCommandT(x.c_str());
c[n].multi_press = press;
if (maptype(c[n].type) != MAP_BUTTON)
{
free(c);
return (cmd);
}
if (name[j] == ';')
j--;
}
i = j + 1;
n++;
}
while (name[i] != '\0');
c[n].type = S9xNoMapping;
c[n].multi_press = 3;
multis.push_back(c);
cmd.button.multi_idx = multis.size() - 1;
cmd.type = S9xButtonMulti;
}
return (cmd);
}
static s9xcommand_t S9xGetMapping (uint32 id)
{
if (keymap.count(id) == 0)
{
s9xcommand_t cmd;
cmd.type = S9xNoMapping;
return (cmd);
}
else
return (keymap[id]);
}
static const char * maptypename (int t)
{
switch (t)
{
case MAP_NONE: return ("unmapped");
case MAP_BUTTON: return ("button");
case MAP_POINTER: return ("pointer");
default: return ("unknown");
}
}
void S9xUnmapID (uint32 id)
{
if (mouse[0].ID == id) mouse[0].ID = InvalidControlID;
if (mouse[1].ID == id) mouse[1].ID = InvalidControlID;
if (superscope.ID == id) superscope.ID = InvalidControlID;
if (justifier.ID[0] == id) justifier.ID[0] = InvalidControlID;
if (justifier.ID[1] == id) justifier.ID[1] = InvalidControlID;
if (macsrifle.ID == id) macsrifle.ID = InvalidControlID;
keymap.erase(id);
}
bool S9xMapButton (uint32 id, s9xcommand_t mapping)
{
int t;
if (id == InvalidControlID)
{
fprintf(stderr, "Cannot map InvalidControlID\n");
return (false);
}
t = maptype(mapping.type);
if (t == MAP_NONE)
{
S9xUnmapID(id);
return (true);
}
if (t != MAP_BUTTON)
return (false);
t = maptype(S9xGetMapping(id).type);
if (t != MAP_NONE && t != MAP_BUTTON)
fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to button\n", id, maptypename(t));
S9xUnmapID(id);
keymap[id] = mapping;
return (true);
}
void S9xReportButton (uint32 id, bool pressed)
{
if (keymap.count(id) == 0)
return;
if (keymap[id].type == S9xNoMapping)
return;
if (maptype(keymap[id].type) != MAP_BUTTON)
{
fprintf(stderr, "ERROR: S9xReportButton called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
return;
}
S9xApplyCommand(keymap[id], pressed, 0);
}
bool S9xMapPointer (uint32 id, s9xcommand_t mapping)
{
int t;
if (id == InvalidControlID)
{
fprintf(stderr, "Cannot map InvalidControlID\n");
return (false);
}
t = maptype(mapping.type);
if (t == MAP_NONE)
{
S9xUnmapID(id);
return (true);
}
if (t != MAP_POINTER)
return (false);
t = maptype(S9xGetMapping(id).type);
if (t != MAP_NONE && t != MAP_POINTER)
fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to pointer\n", id, maptypename(t));
if (mapping.type == S9xPointer)
{
if (mapping.pointer.aim_mouse0 && mouse[0].ID != InvalidControlID && mouse[0].ID != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control Mouse1 with two pointers\n");
return (false);
}
if (mapping.pointer.aim_mouse1 && mouse[1].ID != InvalidControlID && mouse[1].ID != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control Mouse2 with two pointers\n");
return (false);
}
if (mapping.pointer.aim_scope && superscope.ID != InvalidControlID && superscope.ID != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control SuperScope with two pointers\n");
return (false);
}
if (mapping.pointer.aim_justifier0 && justifier.ID[0] != InvalidControlID && justifier.ID[0] != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control Justifier1 with two pointers\n");
return (false);
}
if (mapping.pointer.aim_justifier1 && justifier.ID[1] != InvalidControlID && justifier.ID[1] != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control Justifier2 with two pointers\n");
return (false);
}
if (mapping.pointer.aim_macsrifle && macsrifle.ID != InvalidControlID && macsrifle.ID != id)
{
fprintf(stderr, "ERROR: Rejecting attempt to control M.A.C.S. Rifle with two pointers\n");
return (false);
}
}
S9xUnmapID(id);
keymap[id] = mapping;
if (mapping.pointer.aim_mouse0 ) mouse[0].ID = id;
if (mapping.pointer.aim_mouse1 ) mouse[1].ID = id;
if (mapping.pointer.aim_scope ) superscope.ID = id;
if (mapping.pointer.aim_justifier0) justifier.ID[0] = id;
if (mapping.pointer.aim_justifier1) justifier.ID[1] = id;
if (mapping.pointer.aim_macsrifle ) macsrifle.ID = id;
return (true);
}
void S9xReportPointer (uint32 id, int16 x, int16 y)
{
if (keymap.count(id) == 0)
return;
if (keymap[id].type == S9xNoMapping)
return;
if (maptype(keymap[id].type) != MAP_POINTER)
{
fprintf(stderr, "ERROR: S9xReportPointer called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
return;
}
S9xApplyCommand(keymap[id], x, y);
}
static int32 ApplyMulti (s9xcommand_t *multi, int32 pos, int16 data1)
{
while (1)
{
if (multi[pos].multi_press == 3)
return (-1);
if (multi[pos].type == S9xNoMapping)
break;
if (multi[pos].multi_press)
S9xApplyCommand(multi[pos], multi[pos].multi_press == 1, 0);
else
S9xApplyCommand(multi[pos], data1, 0);
pos++;
}
return (pos + 1);
}
void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)
{
int i;
switch (cmd.type)
{
case S9xNoMapping:
return;
case S9xButtonMouse:
i = 0;
if (cmd.button.mouse.left ) i |= 0x40;
if (cmd.button.mouse.right) i |= 0x80;
if (data1)
mouse[cmd.button.mouse.idx].buttons |= i;
else
mouse[cmd.button.mouse.idx].buttons &= ~i;
return;
case S9xButtonSuperscope:
i = 0;
if (cmd.button.scope.fire ) i |= SUPERSCOPE_FIRE;
if (cmd.button.scope.cursor ) i |= SUPERSCOPE_CURSOR;
if (cmd.button.scope.pause ) i |= SUPERSCOPE_PAUSE;
if (cmd.button.scope.aim_offscreen) i |= SUPERSCOPE_OFFSCREEN;
if (data1)
{
superscope.phys_buttons |= i;
if (cmd.button.scope.turbo)
{
superscope.phys_buttons ^= SUPERSCOPE_TURBO;
if (superscope.phys_buttons & SUPERSCOPE_TURBO)
superscope.next_buttons |= superscope.phys_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);
else
superscope.next_buttons &= ~(SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);
}
superscope.next_buttons |= i & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR | SUPERSCOPE_PAUSE);
if ((superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR)) && curcontrollers[1] == SUPERSCOPE && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))
DoGunLatch(superscope.x, superscope.y);
}
else
{
superscope.phys_buttons &= ~i;
superscope.next_buttons &= SUPERSCOPE_OFFSCREEN | ~i;
}
return;
case S9xButtonJustifier:
i = 0;
if (cmd.button.justifier.trigger) i |= JUSTIFIER_TRIGGER;
if (cmd.button.justifier.start ) i |= JUSTIFIER_START;
if (cmd.button.justifier.aim_offscreen) justifier.offscreen[cmd.button.justifier.idx] = data1 ? 1 : 0;
i >>= cmd.button.justifier.idx;
if (data1)
justifier.buttons |= i;
else
justifier.buttons &= ~i;
return;
case S9xButtonMacsRifle:
i = 0;
if (cmd.button.macsrifle.trigger) i |= MACSRIFLE_TRIGGER;
if(data1)
macsrifle.buttons |= i;
else
macsrifle.buttons &= ~i;
return;
case S9xPointer:
if (cmd.pointer.aim_mouse0)
{
mouse[0].cur_x = data1;
mouse[0].cur_y = data2;
}
if (cmd.pointer.aim_mouse1)
{
mouse[1].cur_x = data1;
mouse[1].cur_y = data2;
}
if (cmd.pointer.aim_scope)
{
superscope.x = data1;
superscope.y = data2;
}
if (cmd.pointer.aim_justifier0)
{
justifier.x[0] = data1;
justifier.y[0] = data2;
}
if (cmd.pointer.aim_justifier1)
{
justifier.x[1] = data1;
justifier.y[1] = data2;
}
if (cmd.pointer.aim_macsrifle)
{
macsrifle.x = data1;
macsrifle.y = data2;
}
return;
case S9xButtonMulti:
if (cmd.button.multi_idx >= (int) multis.size())
return;
if (multis[cmd.button.multi_idx]->multi_press && !data1)
return;
i = ApplyMulti(multis[cmd.button.multi_idx], 0, data1);
if (i >= 0)
{
struct exemulti *e = new struct exemulti;
e->pos = i;
e->data1 = data1 != 0;
e->script = multis[cmd.button.multi_idx];
exemultis.insert(e);
}
return;
default:
fprintf(stderr, "WARNING: Unknown command type %d\n", cmd.type);
return;
}
}
static void UpdateMouseDelta (int i)
{
int16 j;
j = mouse[i - MOUSE0].cur_x - mouse[i - MOUSE0].old_x;
if (j < -127)
{
mouse[i - MOUSE0].delta_x = 0xff;
mouse[i - MOUSE0].old_x -= 127;
}
else if (j < 0)
{
mouse[i - MOUSE0].delta_x = 0x80 | -j;
mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;
}
else if (j > 127)
{
mouse[i - MOUSE0].delta_x = 0x7f;
mouse[i - MOUSE0].old_x += 127;
}
else
{
mouse[i - MOUSE0].delta_x = (uint8) j;
mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;
}
j = mouse[i - MOUSE0].cur_y - mouse[i - MOUSE0].old_y;
if (j < -127)
{
mouse[i - MOUSE0].delta_y = 0xff;
mouse[i - MOUSE0].old_y -= 127;
}
else if (j < 0)
{
mouse[i - MOUSE0].delta_y = 0x80 | -j;
mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;
}
else if (j > 127)
{
mouse[i - MOUSE0].delta_y = 0x7f;
mouse[i - MOUSE0].old_y += 127;
}
else
{
mouse[i - MOUSE0].delta_y = (uint8) j;
mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;
}
}
void S9xSetJoypadLatch (bool latch)
{
if (!latch && FLAG_LATCH)
{
// 1 written, 'plug in' new controllers now
curcontrollers[0] = newcontrollers[0];
curcontrollers[1] = newcontrollers[1];
}
if (latch && !FLAG_LATCH)
{
int i;
for (int n = 0; n < 2; n++)
{
for (int j = 0; j < 2; j++)
read_idx[n][j] = 0;
switch (i = curcontrollers[n])
{
case MP5:
for (int j = 0, k; j < 4; ++j)
{
k = mp5[n].pads[j];
if (k == NONE)
continue;
}
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
break;
case MOUSE0:
case MOUSE1:
UpdateMouseDelta(i);
break;
case SUPERSCOPE:
if (superscope.next_buttons & SUPERSCOPE_FIRE)
{
superscope.next_buttons &= ~SUPERSCOPE_TURBO;
superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_TURBO;
}
if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))
{
superscope.next_buttons &= ~SUPERSCOPE_OFFSCREEN;
superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_OFFSCREEN;
}
superscope.read_buttons = superscope.next_buttons;
superscope.next_buttons &= ~SUPERSCOPE_PAUSE;
if (!(superscope.phys_buttons & SUPERSCOPE_TURBO))
superscope.next_buttons &= ~(SUPERSCOPE_CURSOR | SUPERSCOPE_FIRE);
break;
case TWO_JUSTIFIERS:
// fall through
case ONE_JUSTIFIER:
justifier.buttons ^= JUSTIFIER_SELECT;
break;
case MACSRIFLE:
break;
default:
break;
}
}
}
FLAG_LATCH = latch;
}
// prevent read_idx from overflowing (only latching resets it)
// otherwise $4016/7 reads will start returning input data again
static inline uint8 IncreaseReadIdxPost(uint8 &var)
{
uint8 oldval = var;
if (var < 255)
var++;
return oldval;
}
uint8 S9xReadJOYSERn (int n)
{
int i, j, r;
if (n > 1)
n -= 0x4016;
assert(n == 0 || n == 1);
uint8 bits = (OpenBus & ~3) | ((n == 1) ? 0x1c : 0);
if (FLAG_LATCH)
{
switch (i = curcontrollers[n])
{
case MP5:
return (bits | 2);
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
return (bits | ((joypad[i - JOYPAD0].buttons & 0x8000) ? 1 : 0));
case MOUSE0:
case MOUSE1:
mouse[i - MOUSE0].buttons += 0x10;
if ((mouse[i - MOUSE0].buttons & 0x30) == 0x30)
mouse[i - MOUSE0].buttons &= 0xcf;
return (bits);
case SUPERSCOPE:
return (bits | ((superscope.read_buttons & 0x80) ? 1 : 0));
case ONE_JUSTIFIER:
case TWO_JUSTIFIERS:
return (bits);
case MACSRIFLE:
return (bits | ((macsrifle.buttons & 0x01) ? 1 : 0));
default:
return (bits);
}
}
else
{
switch (i = curcontrollers[n])
{
case MP5:
r = IncreaseReadIdxPost(read_idx[n][FLAG_IOBIT(n) ? 0 : 1]);
j = FLAG_IOBIT(n) ? 0 : 2;
for (i = 0; i < 2; i++, j++)
{
if (mp5[n].pads[j] == NONE)
continue;
if (r >= 16)
bits |= 1 << i;
else
bits |= ((joypad[mp5[n].pads[j] - JOYPAD0].buttons & (0x8000 >> r)) ? 1 : 0) << i;
}
return (bits);
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
if (read_idx[n][0] >= 16)
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits | 1);
}
else
return (bits | ((joypad[i - JOYPAD0].buttons & (0x8000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
case MOUSE0:
case MOUSE1:
if (read_idx[n][0] < 8)
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits);
}
else
if (read_idx[n][0] < 16)
return (bits | ((mouse[i - MOUSE0].buttons & (0x8000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
if (read_idx[n][0] < 24)
return (bits | ((mouse[i - MOUSE0].delta_y & (0x800000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
if (read_idx[n][0] < 32)
return (bits | ((mouse[i - MOUSE0].delta_x & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits | 1);
}
case SUPERSCOPE:
if (read_idx[n][0] < 8)
return (bits | ((superscope.read_buttons & (0x80 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits | 1);
}
case ONE_JUSTIFIER:
if (read_idx[n][0] < 24)
return (bits | ((0xaa7000 >> IncreaseReadIdxPost(read_idx[n][0])) & 1));
else
if (read_idx[n][0] < 32)
return (bits | ((justifier.buttons & (JUSTIFIER_TRIGGER | JUSTIFIER_START | JUSTIFIER_SELECT) & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits | 1);
}
case TWO_JUSTIFIERS:
if (read_idx[n][0] < 24)
return (bits | ((0xaa7000 >> IncreaseReadIdxPost(read_idx[n][0])) & 1));
else
if (read_idx[n][0] < 32)
return (bits | ((justifier.buttons & (0x80000000 >> IncreaseReadIdxPost(read_idx[n][0]))) ? 1 : 0));
else
{
IncreaseReadIdxPost(read_idx[n][0]);
return (bits | 1);
}
case MACSRIFLE:
return (bits | ((macsrifle.buttons & 0x01) ? 1 : 0));
default:
IncreaseReadIdxPost(read_idx[n][0]);
return (bits);
}
}
}
void S9xDoAutoJoypad (void)
{
int i, j;
S9xSetJoypadLatch(1);
S9xSetJoypadLatch(0);
for (int n = 0; n < 2; n++)
{
switch (i = curcontrollers[n])
{
case MP5:
j = FLAG_IOBIT(n) ? 0 : 2;
for (i = 0; i < 2; i++, j++)
{
if (mp5[n].pads[j] == NONE)
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, 0);
else
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, joypad[mp5[n].pads[j] - JOYPAD0].buttons);
}
read_idx[n][FLAG_IOBIT(n) ? 0 : 1] = 16;
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
read_idx[n][0] = 16;
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, joypad[i - JOYPAD0].buttons);
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
case MOUSE0:
case MOUSE1:
read_idx[n][0] = 16;
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, mouse[i - MOUSE0].buttons);
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
case SUPERSCOPE:
read_idx[n][0] = 16;
Memory.FillRAM[0x4218 + n * 2] = 0xff;
Memory.FillRAM[0x4219 + n * 2] = superscope.read_buttons;
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
case ONE_JUSTIFIER:
case TWO_JUSTIFIERS:
read_idx[n][0] = 16;
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0x000e);
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
case MACSRIFLE:
read_idx[n][0] = 16;
Memory.FillRAM[0x4218 + n * 2] = 0xff;
Memory.FillRAM[0x4219 + n * 2] = macsrifle.buttons;
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
default:
WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0);
WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);
break;
}
}
}
void S9xControlEOF (void)
{
struct crosshair *c;
int i;
PPU.GunVLatch = 1000; // i.e., never latch
PPU.GunHLatch = 0;
for (int n = 0; n < 2; n++)
{
switch (i = curcontrollers[n])
{
case MP5:
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
break;
case MOUSE0:
case MOUSE1:
c = &mouse[i - MOUSE0].crosshair;
if (IPPU.RenderThisFrame)
S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, mouse[i - MOUSE0].cur_x, mouse[i - MOUSE0].cur_y);
break;
case SUPERSCOPE:
if (n == 1 && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))
{
if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))
DoGunLatch(superscope.x, superscope.y);
c = &superscope.crosshair;
if (IPPU.RenderThisFrame)
S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, superscope.x, superscope.y);
}
break;
case TWO_JUSTIFIERS:
if (n == 1 && !justifier.offscreen[1])
{
c = &justifier.crosshair[1];
if (IPPU.RenderThisFrame)
S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[1], justifier.y[1]);
}
i = (justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0;
goto do_justifier;
case ONE_JUSTIFIER:
i = (justifier.buttons & JUSTIFIER_SELECT) ? -1 : 0;
do_justifier:
if (n == 1)
{
if (i >= 0 && !justifier.offscreen[i])
DoGunLatch(justifier.x[i], justifier.y[i]);
if (!justifier.offscreen[0])
{
c = &justifier.crosshair[0];
if (IPPU.RenderThisFrame)
S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[0], justifier.y[0]);
}
}
break;
case MACSRIFLE:
if (n == 1)
{
DoMacsRifleLatch(macsrifle.x, macsrifle.y);
c = &macsrifle.crosshair;
if (IPPU.RenderThisFrame)
S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, macsrifle.x, macsrifle.y);
}
break;
default:
break;
}
}
set<struct exemulti *>::iterator it, jt;
for (it = exemultis.begin(); it != exemultis.end(); it++)
{
i = ApplyMulti((*it)->script, (*it)->pos, (*it)->data1);
if (i >= 0)
(*it)->pos = i;
else
{
jt = it;
it--;
delete *jt;
exemultis.erase(jt);
}
}
pad_read_last = pad_read;
pad_read = false;
}
void S9xSetControllerCrosshair (enum crosscontrols ctl, int8 idx, const char *fg, const char *bg)
{
struct crosshair *c;
int8 fgcolor = -1, bgcolor = -1;
int i, j;
if (idx < -1 || idx > 31)
{
fprintf(stderr, "S9xSetControllerCrosshair() called with invalid index\n");
return;
}
switch (ctl)
{
case X_MOUSE1: c = &mouse[0].crosshair; break;
case X_MOUSE2: c = &mouse[1].crosshair; break;
case X_SUPERSCOPE: c = &superscope.crosshair; break;
case X_JUSTIFIER1: c = &justifier.crosshair[0]; break;
case X_JUSTIFIER2: c = &justifier.crosshair[1]; break;
case X_MACSRIFLE: c = &macsrifle.crosshair; break;
default:
fprintf(stderr, "S9xSetControllerCrosshair() called with an invalid controller ID %d\n", ctl);
return;
}
if (fg)
{
fgcolor = 0;
if (*fg == 't')
{
fg++;
fgcolor = 16;
}
for (i = 0; i < 16; i++)
{
for (j = 0; color_names[i][j] && fg[j] == color_names[i][j]; j++) ;
if (isalnum(fg[j]))
continue;
if (!color_names[i][j])
break;
}
fgcolor |= i;
if (i > 15 || fgcolor == 16)
{
fprintf(stderr, "S9xSetControllerCrosshair() called with invalid fgcolor\n");
return;
}
}
if (bg)
{
bgcolor = 0;
if (*bg == 't')
{
bg++;
bgcolor = 16;
}
for (i = 0; i < 16; i++)
{
for (j = 0; color_names[i][j] && bg[j] == color_names[i][j]; j++) ;
if (isalnum(bg[j]))
continue;
if (!color_names[i][j])
break;
}
bgcolor |= i;
if (i > 15 || bgcolor == 16)
{
fprintf(stderr, "S9xSetControllerCrosshair() called with invalid bgcolor\n");
return;
}
}
if (idx != -1)
{
c->set |= 1;
c->img = idx;
}
if (fgcolor != -1)
{
c->set |= 2;
c->fg = fgcolor;
}
if (bgcolor != -1)
{
c->set |= 4;
c->bg = bgcolor;
}
}
void S9xControlPreSaveState (struct SControlSnapshot *s)
{
memset(s, 0, sizeof(*s));
s->ver = 4;
for (int j = 0; j < 2; j++)
{
s->port1_read_idx[j] = read_idx[0][j];
s->port2_read_idx[j] = read_idx[1][j];
}
for (int j = 0; j < 2; j++)
s->mouse_speed[j] = (mouse[j].buttons & 0x30) >> 4;
s->justifier_select = ((justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0);
#define COPY(x) { memcpy((char *) s->internal + i, &(x), sizeof(x)); i += sizeof(x); }
int i = 0;
for (int j = 0; j < 8; j++)
COPY(joypad[j].buttons);
for (int j = 0; j < 2; j++)
{
COPY(mouse[j].delta_x);
COPY(mouse[j].delta_y);
COPY(mouse[j].old_x);
COPY(mouse[j].old_y);
COPY(mouse[j].cur_x);
COPY(mouse[j].cur_y);
COPY(mouse[j].buttons);
}
COPY(superscope.x);
COPY(superscope.y);
COPY(superscope.phys_buttons);
COPY(superscope.next_buttons);
COPY(superscope.read_buttons);
for (int j = 0; j < 2; j++)
COPY(justifier.x[j]);
for (int j = 0; j < 2; j++)
COPY(justifier.y[j]);
COPY(justifier.buttons);
for (int j = 0; j < 2; j++)
COPY(justifier.offscreen[j]);
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
COPY(mp5[j].pads[k]);
assert(i == sizeof(s->internal));
#undef COPY
#define COPY(x) { memcpy((char *) s->internal_macs + i, &(x), sizeof(x)); i += sizeof(x); }
i = 0;
COPY(macsrifle.x);
COPY(macsrifle.y);
COPY(macsrifle.buttons);
assert(i == sizeof(s->internal_macs));
#undef COPY
s->pad_read = pad_read;
s->pad_read_last = pad_read_last;
}
void S9xControlPostLoadState (struct SControlSnapshot *s)
{
if (curcontrollers[0] == MP5 && s->ver < 1)
{
// Crap. Old snes9x didn't support this.
S9xMessage(S9X_WARNING, S9X_FREEZE_FILE_INFO, "Old savestate has no support for MP5 in port 1.");
newcontrollers[0] = curcontrollers[0];
curcontrollers[0] = mp5[0].pads[0];
}
for (int j = 0; j < 2; j++)
{
read_idx[0][j] = s->port1_read_idx[j];
read_idx[1][j] = s->port2_read_idx[j];
}
for (int j = 0; j < 2; j++)
mouse[j].buttons |= (s->mouse_speed[j] & 3) << 4;
if (s->justifier_select & 1)
justifier.buttons |= JUSTIFIER_SELECT;
else
justifier.buttons &= ~JUSTIFIER_SELECT;
FLAG_LATCH = (Memory.FillRAM[0x4016] & 1) == 1;
if (s->ver > 1)
{
#define COPY(x) { memcpy(&(x), (char *) s->internal + i, sizeof(x)); i += sizeof(x); }
int i = 0;
for (int j = 0; j < 8; j++)
COPY(joypad[j].buttons);
for (int j = 0; j < 2; j++)
{
COPY(mouse[j].delta_x);
COPY(mouse[j].delta_y);
COPY(mouse[j].old_x);
COPY(mouse[j].old_y);
COPY(mouse[j].cur_x);
COPY(mouse[j].cur_y);
COPY(mouse[j].buttons);
}
COPY(superscope.x);
COPY(superscope.y);
COPY(superscope.phys_buttons);
COPY(superscope.next_buttons);
COPY(superscope.read_buttons);
for (int j = 0; j < 2; j++)
COPY(justifier.x[j]);
for (int j = 0; j < 2; j++)
COPY(justifier.y[j]);
COPY(justifier.buttons);
for (int j = 0; j < 2; j++)
COPY(justifier.offscreen[j]);
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
COPY(mp5[j].pads[k]);
assert(i == sizeof(s->internal));
if (s->ver > 3)
{
#undef COPY
#define COPY(x) { memcpy(&(x), (char *) s->internal_macs + i, sizeof(x)); i += sizeof(x); }
i = 0;
COPY(macsrifle.x);
COPY(macsrifle.y);
COPY(macsrifle.buttons);
assert(i == sizeof(s->internal_macs));
}
#undef COPY
}
if (s->ver > 2)
{
pad_read = s->pad_read;
pad_read_last = s->pad_read_last;
}
}