snes9x/controls.cpp
2023-03-07 13:58:21 -06:00

3730 lines
84 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 "movie.h"
#include "display.h"
#ifdef NETPLAY_SUPPORT
#include "netplay.h"
#endif
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 POLL_ALL NUMCTLS
#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_AXIS 2
#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
{
int16 x, y;
int16 V_adj;
bool8 V_var;
int16 H_adj;
bool8 H_var;
bool8 mapped;
} pseudopointer[8];
static struct
{
uint16 buttons;
uint16 turbos;
uint16 toggleturbo;
uint16 togglestick;
uint8 turbo_ct;
} 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 set<uint32> pollmap[NUMCTLS + 1];
static map<uint32, s9xcommand_t> keymap;
static vector<s9xcommand_t *> multis;
static uint8 turbo_time;
static uint8 pseudobuttons[256];
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 const char *speed_names[4] =
{
"Var",
"Slow",
"Med",
"Fast"
};
static const int ptrspeeds[4] = { 1, 1, 4, 8 };
// Note: these should be in asciibetical order!
#define THE_COMMANDS \
S(BeginRecordingMovie), \
S(ClipWindows), \
S(Debugger), \
S(DecEmuTurbo), \
S(DecFrameRate), \
S(DecFrameTime), \
S(DecTurboSpeed), \
S(EmuTurbo), \
S(EndRecordingMovie), \
S(ExitEmu), \
S(IncEmuTurbo), \
S(IncFrameRate), \
S(IncFrameTime), \
S(IncTurboSpeed), \
S(LoadFreezeFile), \
S(LoadMovie), \
S(LoadOopsFile), \
S(Pause), \
S(QuickLoad000), \
S(QuickLoad001), \
S(QuickLoad002), \
S(QuickLoad003), \
S(QuickLoad004), \
S(QuickLoad005), \
S(QuickLoad006), \
S(QuickLoad007), \
S(QuickLoad008), \
S(QuickLoad009), \
S(QuickLoad010), \
S(QuickSave000), \
S(QuickSave001), \
S(QuickSave002), \
S(QuickSave003), \
S(QuickSave004), \
S(QuickSave005), \
S(QuickSave006), \
S(QuickSave007), \
S(QuickSave008), \
S(QuickSave009), \
S(QuickSave010), \
S(Reset), \
S(SaveFreezeFile), \
S(SaveSPC), \
S(Screenshot), \
S(SeekToFrame), \
S(SoftReset), \
S(SoundChannel0), \
S(SoundChannel1), \
S(SoundChannel2), \
S(SoundChannel3), \
S(SoundChannel4), \
S(SoundChannel5), \
S(SoundChannel6), \
S(SoundChannel7), \
S(SoundChannelsOn), \
S(SwapJoypads), \
S(ToggleBG0), \
S(ToggleBG1), \
S(ToggleBG2), \
S(ToggleBG3), \
S(ToggleEmuTurbo), \
S(ToggleSprites), \
S(ToggleTransparency) \
#define S(x) x
enum command_numbers
{
THE_COMMANDS,
LAST_COMMAND
};
#undef S
#define S(x) #x
static const char *command_names[LAST_COMMAND + 1] =
{
THE_COMMANDS,
NULL
};
#undef S
#undef THE_COMMANDS
static void DisplayStateChange (const char *, bool8);
static void DoGunLatch (int, int);
static void DoMacsRifleLatch (int, int);
static int maptype (int);
static bool strless (const char *, const char *);
static int findstr (const char *, const char **, int);
static int get_threshold (const char **);
static const char * maptypename (int);
static int32 ApplyMulti (s9xcommand_t *, int32, int16);
static void do_polling (int);
static void UpdatePolledMouse (int);
static string& operator += (string &s, int i)
{
snprintf(buf, sizeof(buf), "%d", i);
s.append(buf);
return (s);
}
static string& operator += (string &s, double d)
{
snprintf(buf, sizeof(buf), "%g", d);
s.append(buf);
return (s);
}
static void DisplayStateChange (const char *str, bool8 on)
{
snprintf(buf, sizeof(buf), "%s: %s", str, on ? "on":"off");
S9xSetInfoString(buf);
}
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 S9xButtonJoypad:
case S9xButtonMouse:
case S9xButtonSuperscope:
case S9xButtonJustifier:
case S9xButtonMacsRifle:
case S9xButtonCommand:
case S9xButtonPseudopointer:
case S9xButtonPort:
case S9xButtonMulti:
return (MAP_BUTTON);
case S9xAxisJoypad:
case S9xAxisPseudopointer:
case S9xAxisPseudobuttons:
case S9xAxisPort:
return (MAP_AXIS);
case S9xPointer:
case S9xPointerPort:
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];
}
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 < NUMCTLS + 1; i++)
pollmap[i].clear();
for (int i = 0; i < 8; i++)
{
pseudopointer[i].x = 0;
pseudopointer[i].y = 0;
pseudopointer[i].H_adj = 0;
pseudopointer[i].V_adj = 0;
pseudopointer[i].H_var = 0;
pseudopointer[i].V_var = 0;
pseudopointer[i].mapped = false;
joypad[i].buttons = 0;
joypad[i].turbos = 0;
joypad[i].turbo_ct = 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;
memset(pseudobuttons, 0, sizeof(pseudobuttons));
turbo_time = 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);
}
void S9xGetController (int port, enum controllers *controller, int8 *id1, int8 *id2, int8 *id3, int8 *id4)
{
int i;
*controller = CTL_NONE;
*id1 = *id2 = *id3 = *id4 = -1;
if (port < 0 || port > 1)
return;
switch (i = newcontrollers[port])
{
case MP5:
*controller = CTL_MP5;
*id1 = (mp5[port].pads[0] == NONE) ? -1 : mp5[port].pads[0] - JOYPAD0;
*id2 = (mp5[port].pads[1] == NONE) ? -1 : mp5[port].pads[1] - JOYPAD0;
*id3 = (mp5[port].pads[2] == NONE) ? -1 : mp5[port].pads[2] - JOYPAD0;
*id4 = (mp5[port].pads[3] == NONE) ? -1 : mp5[port].pads[3] - JOYPAD0;
return;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
*controller = CTL_JOYPAD;
*id1 = i - JOYPAD0;
return;
case MOUSE0:
case MOUSE1:
*controller = CTL_MOUSE;
*id1 = i - MOUSE0;
return;
case SUPERSCOPE:
*controller = CTL_SUPERSCOPE;
*id1 = 1;
return;
case ONE_JUSTIFIER:
case TWO_JUSTIFIERS:
*controller = CTL_JUSTIFIER;
*id1 = i - ONE_JUSTIFIER;
return;
case MACSRIFLE:
*controller = CTL_MACSRIFLE;
*id1 = 1;
return;
}
}
void S9xReportControllers (void)
{
static char mes[128];
char *c = mes;
S9xVerifyControllers();
for (int port = 0; port < 2; port++)
{
c += sprintf(c, "Port %d: ", port + 1);
switch (newcontrollers[port])
{
case NONE:
c += sprintf(c, "<none>. ");
break;
case MP5:
c += sprintf(c, "MP5 with pads");
for (int i = 0; i < 4; i++)
{
if (mp5[port].pads[i] == NONE)
c += sprintf(c, " <none>. ");
else
c += sprintf(c, " #%d. ", mp5[port].pads[i] + 1 - JOYPAD0);
}
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
c += sprintf(c, "Pad #%d. ", (int) (newcontrollers[port] - JOYPAD0 + 1));
break;
case MOUSE0:
case MOUSE1:
c += sprintf(c, "Mouse #%d. ", (int) (newcontrollers[port] - MOUSE0 + 1));
break;
case SUPERSCOPE:
if (port == 0)
c += sprintf(c, "Superscope (cannot fire). ");
else
c += sprintf(c, "Superscope. ");
break;
case ONE_JUSTIFIER:
if (port == 0)
c += sprintf(c, "Blue Justifier (cannot fire). ");
else
c += sprintf(c, "Blue Justifier. ");
break;
case TWO_JUSTIFIERS:
if (port == 0)
c += sprintf(c, "Blue and Pink Justifiers (cannot fire). ");
else
c += sprintf(c, "Blue and Pink Justifiers. ");
break;
case MACSRIFLE:
if (port == 0)
c += sprintf(c, "M.A.C.S. Rifle (cannot fire). ");
else
c += sprintf(c, "M.A.C.S. Rifle. ");
break;
}
}
S9xMessage(S9X_INFO, S9X_CONFIG_INFO, mes);
}
char * S9xGetCommandName (s9xcommand_t command)
{
string s;
char c;
switch (command.type)
{
case S9xButtonJoypad:
if (command.button.joypad.buttons == 0)
return (strdup("None"));
if (command.button.joypad.buttons & 0x000f)
return (strdup("None"));
s = "Joypad";
s += command.button.joypad.idx + 1;
c = ' ';
if (command.button.joypad.toggle) { if (c) s += c; s += "Toggle"; c = 0; }
if (command.button.joypad.sticky) { if (c) s += c; s += "Sticky"; c = 0; }
if (command.button.joypad.turbo ) { if (c) s += c; s += "Turbo"; c = 0; }
c = ' ';
if (command.button.joypad.buttons & SNES_UP_MASK ) { s += c; s += "Up"; c = '+'; }
if (command.button.joypad.buttons & SNES_DOWN_MASK ) { s += c; s += "Down"; c = '+'; }
if (command.button.joypad.buttons & SNES_LEFT_MASK ) { s += c; s += "Left"; c = '+'; }
if (command.button.joypad.buttons & SNES_RIGHT_MASK ) { s += c; s += "Right"; c = '+'; }
if (command.button.joypad.buttons & SNES_A_MASK ) { s += c; s += "A"; c = '+'; }
if (command.button.joypad.buttons & SNES_B_MASK ) { s += c; s += "B"; c = '+'; }
if (command.button.joypad.buttons & SNES_X_MASK ) { s += c; s += "X"; c = '+'; }
if (command.button.joypad.buttons & SNES_Y_MASK ) { s += c; s += "Y"; c = '+'; }
if (command.button.joypad.buttons & SNES_TL_MASK ) { s += c; s += "L"; c = '+'; }
if (command.button.joypad.buttons & SNES_TR_MASK ) { s += c; s += "R"; c = '+'; }
if (command.button.joypad.buttons & SNES_START_MASK ) { s += c; s += "Start"; c = '+'; }
if (command.button.joypad.buttons & SNES_SELECT_MASK) { s += c; s += "Select"; c = '+'; }
break;
case S9xButtonMouse:
if (!command.button.mouse.left && !command.button.mouse.right)
return (strdup("None"));
s = "Mouse";
s += command.button.mouse.idx + 1;
s += " ";
if (command.button.mouse.left ) s += "L";
if (command.button.mouse.right) s += "R";
break;
case S9xButtonSuperscope:
if (!command.button.scope.fire && !command.button.scope.cursor && !command.button.scope.turbo && !command.button.scope.pause && !command.button.scope.aim_offscreen)
return (strdup("None"));
s = "Superscope";
if (command.button.scope.aim_offscreen) s += " AimOffscreen";
c = ' ';
if (command.button.scope.fire ) { s += c; s += "Fire"; c = '+'; }
if (command.button.scope.cursor) { s += c; s += "Cursor"; c = '+'; }
if (command.button.scope.turbo ) { s += c; s += "ToggleTurbo"; c = '+'; }
if (command.button.scope.pause ) { s += c; s += "Pause"; c = '+'; }
break;
case S9xButtonJustifier:
if (!command.button.justifier.trigger && !command.button.justifier.start && !command.button.justifier.aim_offscreen)
return (strdup("None"));
s = "Justifier";
s += command.button.justifier.idx + 1;
if (command.button.justifier.aim_offscreen) s += " AimOffscreen";
c = ' ';
if (command.button.justifier.trigger) { s += c; s += "Trigger"; c = '+'; }
if (command.button.justifier.start ) { s += c; s += "Start"; c = '+'; }
break;
case S9xButtonMacsRifle:
if (!command.button.macsrifle.trigger)
return (strdup("None"));
s = "MacsRifle";
c = ' ';
if (command.button.macsrifle.trigger) { s += c; s += "Trigger"; c = '+'; }
break;
case S9xButtonCommand:
if (command.button.command >= LAST_COMMAND)
return (strdup("None"));
return (strdup(command_names[command.button.command]));
case S9xPointer:
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)
return (strdup("None"));
s = "Pointer";
c = ' ';
if (command.pointer.aim_mouse0 ) { s += c; s += "Mouse1"; c = '+'; }
if (command.pointer.aim_mouse1 ) { s += c; s += "Mouse2"; c = '+'; }
if (command.pointer.aim_scope ) { s += c; s += "Superscope"; c = '+'; }
if (command.pointer.aim_justifier0) { s += c; s += "Justifier1"; c = '+'; }
if (command.pointer.aim_justifier1) { s += c; s += "Justifier2"; c = '+'; }
if (command.pointer.aim_macsrifle) { s += c; s += "MacsRifle"; c = '+'; }
break;
case S9xButtonPseudopointer:
if (!command.button.pointer.UD && !command.button.pointer.LR)
return (strdup("None"));
if (command.button.pointer.UD == -2 || command.button.pointer.LR == -2)
return (strdup("None"));
s = "ButtonToPointer ";
s += command.button.pointer.idx + 1;
if (command.button.pointer.UD) s += (command.button.pointer.UD == 1) ? 'd' : 'u';
if (command.button.pointer.LR) s += (command.button.pointer.LR == 1) ? 'r' : 'l';
s += " ";
s += speed_names[command.button.pointer.speed_type];
break;
case S9xAxisJoypad:
s = "Joypad";
s += command.axis.joypad.idx + 1;
s += " Axis ";
switch (command.axis.joypad.axis)
{
case 0: s += (command.axis.joypad.invert ? "Right/Left" : "Left/Right"); break;
case 1: s += (command.axis.joypad.invert ? "Down/Up" : "Up/Down" ); break;
case 2: s += (command.axis.joypad.invert ? "A/Y" : "Y/A" ); break;
case 3: s += (command.axis.joypad.invert ? "B/X" : "X/B" ); break;
case 4: s += (command.axis.joypad.invert ? "R/L" : "L/R" ); break;
default: return (strdup("None"));
}
s += " T=";
s += int((command.axis.joypad.threshold + 1) * 1000 / 256) / 10.0;
s += "%";
break;
case S9xAxisPseudopointer:
s = "AxisToPointer ";
s += command.axis.pointer.idx + 1;
s += command.axis.pointer.HV ? 'v' : 'h';
s += " ";
if (command.axis.pointer.invert) s += "-";
s += speed_names[command.axis.pointer.speed_type];
break;
case S9xAxisPseudobuttons:
s = "AxisToButtons ";
s += command.axis.button.negbutton;
s += "/";
s += command.axis.button.posbutton;
s += " T=";
s += int((command.axis.button.threshold + 1) * 1000 / 256) / 10.0;
s += "%";
break;
case S9xButtonPort:
case S9xAxisPort:
case S9xPointerPort:
return (strdup("BUG: Port should have handled this instead of calling S9xGetCommandName()"));
case S9xNoMapping:
return (strdup("None"));
case S9xButtonMulti:
{
if (command.button.multi_idx >= (int) multis.size())
return (strdup("None"));
s = "{";
if (multis[command.button.multi_idx]->multi_press) s = "+{";
bool sep = false;
for (s9xcommand_t *m = multis[command.button.multi_idx]; m->multi_press != 3; m++)
{
if (m->type == S9xNoMapping)
{
s += ";";
sep = false;
}
else
{
if (sep) s += ",";
if (m->multi_press == 1) s += "+";
if (m->multi_press == 2) s += "-";
s += S9xGetCommandName(*m);
sep = true;
}
}
s += "}";
break;
}
default:
return (strdup("BUG: Unknown command type"));
}
return (strdup(s.c_str()));
}
static bool strless (const char *a, const char *b)
{
return (strcmp(a, b) < 0);
}
static int findstr (const char *needle, const char **haystack, int numstr)
{
const char **r;
r = lower_bound(haystack, haystack + numstr, needle, strless);
if (r >= haystack + numstr || strcmp(needle, *r))
return (-1);
return (r - haystack);
}
static int get_threshold (const char **ss)
{
const char *s = *ss;
int i;
if (s[0] != 'T' || s[1] != '=')
return (-1);
s += 2;
i = 0;
if (s[0] == '0')
{
if (s[1] != '.')
return (-1);
s++;
}
else
{
do
{
if (*s < '0' || *s > '9')
return (-1);
i = i * 10 + 10 * (*s - '0');
if (i > 1000)
return (-1);
s++;
}
while (*s != '.' && *s != '%');
}
if (*s == '.')
{
if (s[1] < '0' || s[1] > '9' || s[2] != '%')
return (-1);
i += s[1] - '0';
}
if (i > 1000)
return (-1);
*ss = s;
return (i);
}
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;
cmd.button_norpt = 0;
if (!strcmp(name, "None"))
cmd.type = S9xNoMapping;
else if (!strncmp(name, "Joypad", 6))
{
if (name[6] < '1' || name[6] > '8' || name[7] != ' ')
return (cmd);
if (!strncmp(name + 8, "Axis ", 5))
{
cmd.axis.joypad.idx = name[6] - '1';
s = name + 13;
if (!strncmp(s, "Left/Right ", 11)) { j = 0; i = 0; s += 11; }
else if (!strncmp(s, "Right/Left ", 11)) { j = 0; i = 1; s += 11; }
else if (!strncmp(s, "Up/Down ", 8)) { j = 1; i = 0; s += 8; }
else if (!strncmp(s, "Down/Up ", 8)) { j = 1; i = 1; s += 8; }
else if (!strncmp(s, "Y/A ", 4)) { j = 2; i = 0; s += 4; }
else if (!strncmp(s, "A/Y ", 4)) { j = 2; i = 1; s += 4; }
else if (!strncmp(s, "X/B ", 4)) { j = 3; i = 0; s += 4; }
else if (!strncmp(s, "B/X ", 4)) { j = 3; i = 1; s += 4; }
else if (!strncmp(s, "L/R ", 4)) { j = 4; i = 0; s += 4; }
else if (!strncmp(s, "R/L ", 4)) { j = 4; i = 1; s += 4; }
else
return (cmd);
cmd.axis.joypad.axis = j;
cmd.axis.joypad.invert = i;
i = get_threshold(&s);
if (i < 0)
return (cmd);
cmd.axis.joypad.threshold = (i - 1) * 256 / 1000;
cmd.type = S9xAxisJoypad;
}
else
{
cmd.button.joypad.idx = name[6] - '1';
s = name + 8;
i = 0;
if ((cmd.button.joypad.toggle = strncmp(s, "Toggle", 6) ? 0 : 1)) s += i = 6;
if ((cmd.button.joypad.sticky = strncmp(s, "Sticky", 6) ? 0 : 1)) s += i = 6;
if ((cmd.button.joypad.turbo = strncmp(s, "Turbo", 5) ? 0 : 1)) s += i = 5;
if (cmd.button.joypad.toggle && !(cmd.button.joypad.sticky || cmd.button.joypad.turbo))
return (cmd);
if (i)
{
if (*s != ' ')
return (cmd);
s++;
}
i = 0;
if (!strncmp(s, "Up", 2)) { i |= SNES_UP_MASK; s += 2; if (*s == '+') s++; }
if (!strncmp(s, "Down", 4)) { i |= SNES_DOWN_MASK; s += 4; if (*s == '+') s++; }
if (!strncmp(s, "Left", 4)) { i |= SNES_LEFT_MASK; s += 4; if (*s == '+') s++; }
if (!strncmp(s, "Right", 5)) { i |= SNES_RIGHT_MASK; s += 5; if (*s == '+') s++; }
if (*s == 'A') { i |= SNES_A_MASK; s++; if (*s == '+') s++; }
if (*s == 'B') { i |= SNES_B_MASK; s++; if (*s == '+') s++; }
if (*s == 'X') { i |= SNES_X_MASK; s++; if (*s == '+') s++; }
if (*s == 'Y') { i |= SNES_Y_MASK; s++; if (*s == '+') s++; }
if (*s == 'L') { i |= SNES_TL_MASK; s++; if (*s == '+') s++; }
if (*s == 'R') { i |= SNES_TR_MASK; s++; if (*s == '+') s++; }
if (!strncmp(s, "Start", 5)) { i |= SNES_START_MASK; s += 5; if (*s == '+') s++; }
if (!strncmp(s, "Select", 6)) { i |= SNES_SELECT_MASK; s += 6; }
if (i == 0 || *s != 0 || *(s - 1) == '+')
return (cmd);
cmd.button.joypad.buttons = i;
cmd.type = S9xButtonJoypad;
}
}
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, "ButtonToPointer ", 16))
{
if (name[16] < '1' || name[16] > '8')
return (cmd);
cmd.button.pointer.idx = name[16] - '1';
s = name + 17;
i = 0;
if ((cmd.button.pointer.UD = (*s == 'u' ? -1 : (*s == 'd' ? 1 : 0)))) s += i = 1;
if ((cmd.button.pointer.LR = (*s == 'l' ? -1 : (*s == 'r' ? 1 : 0)))) s += i = 1;
if (i == 0 || *(s++) != ' ')
return (cmd);
for (i = 0; i < 4; i++)
if (!strcmp(s, speed_names[i]))
break;
if (i > 3)
return (cmd);
cmd.button.pointer.speed_type = i;
cmd.type = S9xButtonPseudopointer;
}
else
if (!strncmp(name, "AxisToPointer ", 14))
{
if (name[14] < '1' || name[14] > '8')
return (cmd);
cmd.axis.pointer.idx = name[14] - '1';
s= name + 15;
i = 0;
if (*s == 'h')
cmd.axis.pointer.HV = 0;
else if (*s == 'v')
cmd.axis.pointer.HV = 1;
else
return (cmd);
if (s[1] != ' ')
return (cmd);
s += 2;
if ((cmd.axis.pointer.invert = *s == '-'))
s++;
for (i = 0; i < 4; i++)
if (!strcmp(s, speed_names[i]))
break;
if (i > 3)
return (cmd);
cmd.axis.pointer.speed_type = i;
cmd.type = S9xAxisPseudopointer;
}
else
if (!strncmp(name, "AxisToButtons ", 14))
{
s = name + 14;
if (s[0] == '0')
{
if (s[1] != '/')
return (cmd);
cmd.axis.button.negbutton = 0;
s += 2;
}
else
{
i = 0;
do
{
if (*s < '0' || *s > '9')
return (cmd);
i = i * 10 + *s - '0';
if (i > 255)
return (cmd);
}
while (*++s != '/');
cmd.axis.button.negbutton = i;
s++;
}
if (s[0] == '0')
{
if (s[1] != ' ')
return (cmd);
cmd.axis.button.posbutton = 0;
s += 2;
}
else
{
i = 0;
do
{
if (*s < '0' || *s > '9')
return (cmd);
i = i * 10 + *s - '0';
if (i > 255)
return (cmd);
}
while (*++s != ' ');
cmd.axis.button.posbutton = i;
s++;
}
i = get_threshold(&s);
if (i < 0)
return (cmd);
cmd.axis.button.threshold = (i - 1) * 256 / 1000;
cmd.type = S9xAxisPseudobuttons;
}
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;
c[n].button_norpt = 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;
}
else
{
i = findstr(name, command_names, LAST_COMMAND);
if (i < 0)
return (cmd);
cmd.type = S9xButtonCommand;
cmd.button.command = i;
}
return (cmd);
}
const char ** S9xGetAllSnes9xCommands (void)
{
return (command_names);
}
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_AXIS: return ("axis");
case MAP_POINTER: return ("pointer");
default: return ("unknown");
}
}
void S9xUnmapID (uint32 id)
{
for (int i = 0; i < NUMCTLS + 1; i++)
pollmap[i].erase(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;
if (id >= PseudoPointerBase)
pseudopointer[id - PseudoPointerBase].mapped = false;
keymap.erase(id);
}
bool S9xMapButton (uint32 id, s9xcommand_t mapping, bool poll)
{
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));
if (id >= PseudoPointerBase)
{
fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as a button\n", id - PseudoPointerBase);
return (false);
}
t = -1;
if (poll)
{
if (id >= PseudoButtonBase)
fprintf(stderr, "INFO: Ignoring attempt to set pseudo-button #%d to polling\n", id - PseudoButtonBase);
else
{
switch (mapping.type)
{
case S9xButtonJoypad:
t = JOYPAD0 + mapping.button.joypad.idx;
break;
case S9xButtonMouse:
t = MOUSE0 + mapping.button.mouse.idx;
break;
case S9xButtonSuperscope:
t = SUPERSCOPE;
break;
case S9xButtonJustifier:
t = ONE_JUSTIFIER + mapping.button.justifier.idx;
break;
case S9xButtonMacsRifle:
t = MACSRIFLE;
break;
case S9xButtonCommand:
case S9xButtonPseudopointer:
case S9xButtonPort:
case S9xButtonMulti:
t = POLL_ALL;
break;
}
}
}
S9xUnmapID(id);
keymap[id] = mapping;
if (t >= 0)
pollmap[t].insert(id);
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;
}
if (keymap[id].type == S9xButtonCommand) // skips the "already-pressed check" unless it's a command, as a hack to work around the following problem:
if (keymap[id].button_norpt == pressed) // FIXME: this makes the controls "stick" after loading a savestate while recording a movie and holding any button
return;
keymap[id].button_norpt = pressed;
S9xApplyCommand(keymap[id], pressed, 0);
}
bool S9xMapPointer (uint32 id, s9xcommand_t mapping, bool poll)
{
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 (id < PseudoPointerBase && id >= PseudoButtonBase)
{
fprintf(stderr, "ERROR: Refusing to map pseudo-button #%d as a pointer\n", id - PseudoButtonBase);
return (false);
}
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);
if (poll)
{
if (id >= PseudoPointerBase)
fprintf(stderr, "INFO: Ignoring attempt to set pseudo-pointer #%d to polling\n", id - PseudoPointerBase);
else
{
switch (mapping.type)
{
case S9xPointer:
if (mapping.pointer.aim_mouse0 ) pollmap[MOUSE0 ].insert(id);
if (mapping.pointer.aim_mouse1 ) pollmap[MOUSE1 ].insert(id);
if (mapping.pointer.aim_scope ) pollmap[SUPERSCOPE ].insert(id);
if (mapping.pointer.aim_justifier0) pollmap[ONE_JUSTIFIER ].insert(id);
if (mapping.pointer.aim_justifier1) pollmap[TWO_JUSTIFIERS].insert(id);
if (mapping.pointer.aim_macsrifle ) pollmap[MACSRIFLE ].insert(id);
break;
case S9xPointerPort:
pollmap[POLL_ALL].insert(id);
break;
}
}
}
if (id >= PseudoPointerBase)
pseudopointer[id - PseudoPointerBase].mapped = true;
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);
}
bool S9xMapAxis (uint32 id, s9xcommand_t mapping, bool poll)
{
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_AXIS)
return (false);
t = maptype(S9xGetMapping(id).type);
if (t != MAP_NONE && t != MAP_AXIS)
fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to axis\n", id, maptypename(t));
if (id >= PseudoPointerBase)
{
fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as an axis\n", id - PseudoPointerBase);
return (false);
}
t = -1;
if (poll)
{
switch (mapping.type)
{
case S9xAxisJoypad:
t = JOYPAD0 + mapping.axis.joypad.idx;
break;
case S9xAxisPseudopointer:
case S9xAxisPseudobuttons:
case S9xAxisPort:
t=POLL_ALL;
break;
}
}
S9xUnmapID(id);
keymap[id] = mapping;
if (t >= 0)
pollmap[t].insert(id);
return (true);
}
void S9xReportAxis (uint32 id, int16 value)
{
if (keymap.count(id) == 0)
return;
if (keymap[id].type == S9xNoMapping)
return;
if (maptype(keymap[id].type) != MAP_AXIS)
{
fprintf(stderr, "ERROR: S9xReportAxis called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);
return;
}
S9xApplyCommand(keymap[id], value, 0);
}
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 S9xButtonJoypad:
if (cmd.button.joypad.toggle)
{
if (!data1)
return;
uint16 r = cmd.button.joypad.buttons;
if (cmd.button.joypad.turbo) joypad[cmd.button.joypad.idx].toggleturbo ^= r;
if (cmd.button.joypad.sticky) joypad[cmd.button.joypad.idx].togglestick ^= r;
}
else
{
uint16 r, s, t, st;
r = cmd.button.joypad.buttons;
st = r & joypad[cmd.button.joypad.idx].togglestick & joypad[cmd.button.joypad.idx].toggleturbo;
r ^= st;
t = r & joypad[cmd.button.joypad.idx].toggleturbo;
r ^= t;
s = r & joypad[cmd.button.joypad.idx].togglestick;
r ^= s;
if (cmd.button.joypad.turbo && cmd.button.joypad.sticky)
{
uint16 x = r; r = st; st = x;
x = s; s = t; t = x;
}
else if (cmd.button.joypad.turbo)
{
uint16 x = r; r = t; t = x;
x = s; s = st; st = x;
}
else if (cmd.button.joypad.sticky)
{
uint16 x = r; r = s; s = x;
x = t; t = st; st = x;
}
if (data1)
{
if (!Settings.UpAndDown && !S9xMoviePlaying()) // if up+down isn't allowed AND we are NOT playing a movie,
{
if (cmd.button.joypad.buttons & (SNES_LEFT_MASK | SNES_RIGHT_MASK))
{
// if we're pressing left or right, then unpress and unturbo them both first
// so we don't end up hittnig left AND right accidentally.
// Note though that the user can still do it on purpose, if Settings.UpAndDown = true.
// This is a feature, look up glitches in tLoZ:aLttP to find out why.
joypad[cmd.button.joypad.idx].buttons &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);
joypad[cmd.button.joypad.idx].turbos &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);
}
if (cmd.button.joypad.buttons & (SNES_UP_MASK | SNES_DOWN_MASK))
{
// and ditto for up/down
joypad[cmd.button.joypad.idx].buttons &= ~(SNES_UP_MASK | SNES_DOWN_MASK);
joypad[cmd.button.joypad.idx].turbos &= ~(SNES_UP_MASK | SNES_DOWN_MASK);
}
}
joypad[cmd.button.joypad.idx].buttons |= r;
joypad[cmd.button.joypad.idx].turbos |= t;
joypad[cmd.button.joypad.idx].buttons ^= s;
joypad[cmd.button.joypad.idx].buttons &= ~(joypad[cmd.button.joypad.idx].turbos & st);
joypad[cmd.button.joypad.idx].turbos ^= st;
}
else
{
joypad[cmd.button.joypad.idx].buttons &= ~r;
joypad[cmd.button.joypad.idx].buttons &= ~(joypad[cmd.button.joypad.idx].turbos & t);
joypad[cmd.button.joypad.idx].turbos &= ~t;
}
}
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 (!S9xMovieActive()) // PPU modification during non-recordable command screws up movie synchronization
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 S9xButtonCommand:
if (((enum command_numbers) cmd.button.command) >= LAST_COMMAND)
{
fprintf(stderr, "Unknown command %04x\n", cmd.button.command);
return;
}
if (!data1)
{
switch (i = cmd.button.command)
{
case EmuTurbo:
Settings.TurboMode = FALSE;
break;
}
}
else
{
switch ((enum command_numbers) (i = cmd.button.command))
{
case ExitEmu:
S9xExit();
break;
case Reset:
S9xReset();
break;
case SoftReset:
S9xMovieUpdateOnReset();
if (S9xMoviePlaying())
S9xMovieStop(TRUE);
S9xSoftReset();
break;
case EmuTurbo:
Settings.TurboMode = TRUE;
break;
case ToggleEmuTurbo:
Settings.TurboMode = !Settings.TurboMode;
DisplayStateChange("Turbo mode", Settings.TurboMode);
break;
case ClipWindows:
Settings.DisableGraphicWindows = !Settings.DisableGraphicWindows;
DisplayStateChange("Graphic clip windows", !Settings.DisableGraphicWindows);
break;
case Debugger:
#ifdef DEBUGGER
CPU.Flags |= DEBUG_MODE_FLAG;
#endif
break;
case IncFrameRate:
if (Settings.SkipFrames == AUTO_FRAMERATE)
Settings.SkipFrames = 1;
else
if (Settings.SkipFrames < 10)
Settings.SkipFrames++;
if (Settings.SkipFrames == AUTO_FRAMERATE)
S9xSetInfoString("Auto frame skip");
else
{
sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);
S9xSetInfoString(buf);
}
break;
case DecFrameRate:
if (Settings.SkipFrames <= 1)
Settings.SkipFrames = AUTO_FRAMERATE;
else
if (Settings.SkipFrames != AUTO_FRAMERATE)
Settings.SkipFrames--;
if (Settings.SkipFrames == AUTO_FRAMERATE)
S9xSetInfoString("Auto frame skip");
else
{
sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);
S9xSetInfoString(buf);
}
break;
case IncEmuTurbo:
if (Settings.TurboSkipFrames < 20)
Settings.TurboSkipFrames += 1;
else
if (Settings.TurboSkipFrames < 200)
Settings.TurboSkipFrames += 5;
sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);
S9xSetInfoString(buf);
break;
case DecEmuTurbo:
if (Settings.TurboSkipFrames > 20)
Settings.TurboSkipFrames -= 5;
else
if (Settings.TurboSkipFrames > 0)
Settings.TurboSkipFrames -= 1;
sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);
S9xSetInfoString(buf);
break;
case IncFrameTime: // Increase emulated frame time by 1ms
Settings.FrameTime += 1000;
sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);
S9xSetInfoString(buf);
break;
case DecFrameTime: // Decrease emulated frame time by 1ms
if (Settings.FrameTime >= 1000)
Settings.FrameTime -= 1000;
sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);
S9xSetInfoString(buf);
break;
case IncTurboSpeed:
if (turbo_time >= 120)
break;
turbo_time++;
sprintf(buf, "Turbo speed: %d", turbo_time);
S9xSetInfoString(buf);
break;
case DecTurboSpeed:
if (turbo_time <= 1)
break;
turbo_time--;
sprintf(buf, "Turbo speed: %d", turbo_time);
S9xSetInfoString(buf);
break;
case LoadFreezeFile:
break;
case SaveFreezeFile:
break;
case LoadOopsFile:
{
std::string filename = S9xGetFilename("oops", SNAPSHOT_DIR);
if (S9xUnfreezeGame(filename.c_str()))
{
snprintf(buf, 256, "%.240s.oops loaded", S9xBasename(Memory.ROMFilename).c_str());
S9xSetInfoString(buf);
}
else
S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Oops file not found");
break;
}
case Pause:
Settings.Paused = !Settings.Paused;
DisplayStateChange("Pause", Settings.Paused);
#if defined(NETPLAY_SUPPORT) && !defined(__WIN32__)
S9xNPSendPause(Settings.Paused);
#endif
break;
case QuickLoad000:
case QuickLoad001:
case QuickLoad002:
case QuickLoad003:
case QuickLoad004:
case QuickLoad005:
case QuickLoad006:
case QuickLoad007:
case QuickLoad008:
case QuickLoad009:
case QuickLoad010:
{
std::string ext = std::to_string(i - QuickLoad000);
while (ext.length() < 3)
ext = '0' + ext;
auto filename = S9xGetFilename(ext, SNAPSHOT_DIR);
if (S9xUnfreezeGame(filename.c_str()))
{
snprintf(buf, 256, "Quick save-state %s loaded", ext.c_str());
S9xSetInfoString(buf);
}
else
S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Freeze file not found");
break;
}
case QuickSave000:
case QuickSave001:
case QuickSave002:
case QuickSave003:
case QuickSave004:
case QuickSave005:
case QuickSave006:
case QuickSave007:
case QuickSave008:
case QuickSave009:
case QuickSave010:
{
std::string ext = std::to_string(i - QuickLoad000);
while (ext.length() < 3)
ext = '0' + ext;
auto filename = S9xGetFilename(ext, SNAPSHOT_DIR);
snprintf(buf, 256, "Quick save-state %s saved", ext.c_str());
S9xSetInfoString(buf);
S9xFreezeGame(filename.c_str());
break;
}
case SaveSPC:
S9xDumpSPCSnapshot();
break;
case Screenshot:
Settings.TakeScreenshot = TRUE;
break;
case SoundChannel0:
case SoundChannel1:
case SoundChannel2:
case SoundChannel3:
case SoundChannel4:
case SoundChannel5:
case SoundChannel6:
case SoundChannel7:
S9xToggleSoundChannel(i - SoundChannel0);
sprintf(buf, "Sound channel %d toggled", i - SoundChannel0);
S9xSetInfoString(buf);
break;
case SoundChannelsOn:
S9xToggleSoundChannel(8);
S9xSetInfoString("All sound channels on");
break;
case ToggleBG0:
Settings.BG_Forced ^= 1;
DisplayStateChange("BG#0", !(Settings.BG_Forced & 1));
break;
case ToggleBG1:
Settings.BG_Forced ^= 2;
DisplayStateChange("BG#1", !(Settings.BG_Forced & 2));
break;
case ToggleBG2:
Settings.BG_Forced ^= 4;
DisplayStateChange("BG#2", !(Settings.BG_Forced & 4));
break;
case ToggleBG3:
Settings.BG_Forced ^= 8;
DisplayStateChange("BG#3", !(Settings.BG_Forced & 8));
break;
case ToggleSprites:
Settings.BG_Forced ^= 16;
DisplayStateChange("Sprites", !(Settings.BG_Forced & 16));
break;
case ToggleTransparency:
Settings.Transparency = !Settings.Transparency;
DisplayStateChange("Transparency effects", Settings.Transparency);
break;
case BeginRecordingMovie:
// if (S9xMovieActive())
// S9xMovieStop(FALSE);
// S9xMovieCreate(S9xChooseMovieFilename(FALSE), 0xFF, MOVIE_OPT_FROM_RESET, NULL, 0);
break;
case LoadMovie:
// if (S9xMovieActive())
// S9xMovieStop(FALSE);
// S9xMovieOpen(S9xChooseMovieFilename(TRUE), FALSE);
break;
case EndRecordingMovie:
if (S9xMovieActive())
S9xMovieStop(FALSE);
break;
case SwapJoypads:
if ((curcontrollers[0] != NONE && !(curcontrollers[0] >= JOYPAD0 && curcontrollers[0] <= JOYPAD7)))
{
S9xSetInfoString("Cannot swap pads: port 1 is not a joypad");
break;
}
if ((curcontrollers[1] != NONE && !(curcontrollers[1] >= JOYPAD0 && curcontrollers[1] <= JOYPAD7)))
{
S9xSetInfoString("Cannot swap pads: port 2 is not a joypad");
break;
}
#ifdef NETPLAY_SUPPORT
if (Settings.NetPlay && data2 != 1) { //data2 == 1 means it's sent by the netplay code
if (Settings.NetPlayServer) {
S9xNPSendJoypadSwap();
} else {
S9xSetInfoString("Netplay Client cannot swap pads.");
break;
}
}
#endif
newcontrollers[1] = curcontrollers[0];
newcontrollers[0] = curcontrollers[1];
strcpy(buf, "Swap pads: P1=");
i = 14;
if (newcontrollers[0] == NONE)
{
strcpy(buf + i, "<none>");
i += 6;
}
else
{
sprintf(buf + i, "Joypad%d", newcontrollers[0] - JOYPAD0 + 1);
i += 7;
}
strcpy(buf + i, " P2=");
i += 4;
if (newcontrollers[1] == NONE)
strcpy(buf + i, "<none>");
else
sprintf(buf + i, "Joypad%d", newcontrollers[1] - JOYPAD0 + 1);
S9xSetInfoString(buf);
break;
case SeekToFrame:
if (S9xMovieActive())
{
sprintf(buf, "Select frame number (current: %d)", S9xMovieGetFrameCounter());
const char *frameno = S9xStringInput(buf);
if (!frameno)
return;
int frameDest = atoi(frameno);
if (frameDest > 0 && frameDest > (int) S9xMovieGetFrameCounter())
{
int distance = frameDest - S9xMovieGetFrameCounter();
Settings.HighSpeedSeek = distance;
}
}
break;
case LAST_COMMAND:
break;
}
}
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 S9xButtonPseudopointer:
if (data1)
{
if (cmd.button.pointer.UD)
{
if (!pseudopointer[cmd.button.pointer.idx].V_adj)
pseudopointer[cmd.button.pointer.idx].V_adj = cmd.button.pointer.UD * ptrspeeds[cmd.button.pointer.speed_type];
pseudopointer[cmd.button.pointer.idx].V_var = (cmd.button.pointer.speed_type == 0);
}
if (cmd.button.pointer.LR)
{
if (!pseudopointer[cmd.button.pointer.idx].H_adj)
pseudopointer[cmd.button.pointer.idx].H_adj = cmd.button.pointer.LR * ptrspeeds[cmd.button.pointer.speed_type];
pseudopointer[cmd.button.pointer.idx].H_var = (cmd.button.pointer.speed_type == 0);
}
}
else
{
if (cmd.button.pointer.UD)
{
pseudopointer[cmd.button.pointer.idx].V_adj = 0;
pseudopointer[cmd.button.pointer.idx].V_var = false;
}
if (cmd.button.pointer.LR)
{
pseudopointer[cmd.button.pointer.idx].H_adj = 0;
pseudopointer[cmd.button.pointer.idx].H_var = false;
}
}
return;
case S9xAxisJoypad:
{
uint16 pos, neg;
switch (cmd.axis.joypad.axis)
{
case 0: neg = SNES_LEFT_MASK; pos = SNES_RIGHT_MASK; break;
case 1: neg = SNES_UP_MASK; pos = SNES_DOWN_MASK; break;
case 2: neg = SNES_Y_MASK; pos = SNES_A_MASK; break;
case 3: neg = SNES_X_MASK; pos = SNES_B_MASK; break;
case 4: neg = SNES_TL_MASK; pos = SNES_TR_MASK; break;
default: return;
}
if (cmd.axis.joypad.invert)
data1 = -data1;
uint16 p, r;
p = r = 0;
if (data1 > ((cmd.axis.joypad.threshold + 1) * 127))
p |= pos;
else
r |= pos;
if (data1 <= ((cmd.axis.joypad.threshold + 1) * -127))
p |= neg;
else
r |= neg;
joypad[cmd.axis.joypad.idx].buttons |= p;
joypad[cmd.axis.joypad.idx].buttons &= ~r;
joypad[cmd.axis.joypad.idx].turbos &= ~(p | r);
return;
}
case S9xAxisPseudopointer:
if (data1 == 0)
{
if (cmd.axis.pointer.HV)
{
pseudopointer[cmd.axis.pointer.idx].V_adj = 0;
pseudopointer[cmd.axis.pointer.idx].V_var = false;
}
else
{
pseudopointer[cmd.axis.pointer.idx].H_adj = 0;
pseudopointer[cmd.axis.pointer.idx].H_var = false;
}
}
else
{
if (cmd.axis.pointer.invert)
data1 = -data1;
if (cmd.axis.pointer.HV)
{
if (!pseudopointer[cmd.axis.pointer.idx].V_adj)
pseudopointer[cmd.axis.pointer.idx].V_adj = (int16) ((int32) data1 * ptrspeeds[cmd.axis.pointer.speed_type] / 32767);
pseudopointer[cmd.axis.pointer.idx].V_var = (cmd.axis.pointer.speed_type == 0);
}
else
{
if (!pseudopointer[cmd.axis.pointer.idx].H_adj)
pseudopointer[cmd.axis.pointer.idx].H_adj = (int16) ((int32) data1 * ptrspeeds[cmd.axis.pointer.speed_type] / 32767);
pseudopointer[cmd.axis.pointer.idx].H_var = (cmd.axis.pointer.speed_type == 0);
}
}
return;
case S9xAxisPseudobuttons:
if (data1 > ((cmd.axis.button.threshold + 1) * 127))
{
if (!pseudobuttons[cmd.axis.button.posbutton])
{
pseudobuttons[cmd.axis.button.posbutton] = 1;
S9xReportButton(PseudoButtonBase + cmd.axis.button.posbutton, true);
}
}
else
{
if (pseudobuttons[cmd.axis.button.posbutton])
{
pseudobuttons[cmd.axis.button.posbutton] = 0;
S9xReportButton(PseudoButtonBase + cmd.axis.button.posbutton, false);
}
}
if (data1 <= ((cmd.axis.button.threshold + 1) * -127))
{
if (!pseudobuttons[cmd.axis.button.negbutton])
{
pseudobuttons[cmd.axis.button.negbutton] = 1;
S9xReportButton(PseudoButtonBase + cmd.axis.button.negbutton, true);
}
}
else
{
if (pseudobuttons[cmd.axis.button.negbutton])
{
pseudobuttons[cmd.axis.button.negbutton] = 0;
S9xReportButton(PseudoButtonBase + cmd.axis.button.negbutton, false);
}
}
return;
case S9xButtonPort:
case S9xAxisPort:
case S9xPointerPort:
S9xHandlePortCommand(cmd, data1, 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 do_polling (int mp)
{
set<uint32>::iterator itr;
if (S9xMoviePlaying())
return;
if (pollmap[mp].empty())
return;
for (itr = pollmap[mp].begin(); itr != pollmap[mp].end(); itr++)
{
switch (maptype(keymap[*itr].type))
{
case MAP_BUTTON:
{
bool pressed = false;
if (S9xPollButton(*itr, &pressed))
S9xReportButton(*itr, pressed);
break;
}
case MAP_AXIS:
{
int16 value = 0;
if (S9xPollAxis(*itr, &value))
S9xReportAxis(*itr, value);
break;
}
case MAP_POINTER:
{
int16 x = 0, y = 0;
if (S9xPollPointer(*itr, &x, &y))
S9xReportPointer(*itr, x, y);
break;
}
default:
break;
}
}
}
static void UpdatePolledMouse (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;
do_polling(k);
}
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
do_polling(i);
break;
case MOUSE0:
case MOUSE1:
do_polling(i);
if (!S9xMoviePlaying())
UpdatePolledMouse(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);
do_polling(i);
break;
case TWO_JUSTIFIERS:
do_polling(TWO_JUSTIFIERS);
// fall through
case ONE_JUSTIFIER:
justifier.buttons ^= JUSTIFIER_SELECT;
do_polling(ONE_JUSTIFIER);
break;
case MACSRIFLE:
do_polling(i);
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:
do_polling(i);
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:
do_polling(i);
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);
S9xMovieUpdate(false);
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, j;
PPU.GunVLatch = 1000; // i.e., never latch
PPU.GunHLatch = 0;
for (int n = 0; n < 2; n++)
{
switch (i = curcontrollers[n])
{
case MP5:
for (j = 0; j < 4; ++j)
{
i = mp5[n].pads[j];
if (i == NONE)
continue;
if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)
{
joypad[i - JOYPAD0].turbo_ct = 0;
joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;
}
}
break;
case JOYPAD0:
case JOYPAD1:
case JOYPAD2:
case JOYPAD3:
case JOYPAD4:
case JOYPAD5:
case JOYPAD6:
case JOYPAD7:
if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)
{
joypad[i - JOYPAD0].turbo_ct = 0;
joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;
}
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;
}
}
for (int n = 0; n < 8; n++)
{
if (!pseudopointer[n].mapped)
continue;
if (pseudopointer[n].H_adj)
{
pseudopointer[n].x += pseudopointer[n].H_adj;
if (pseudopointer[n].x < 0)
pseudopointer[n].x = 0;
else if (pseudopointer[n].x > 255)
pseudopointer[n].x = 255;
if (pseudopointer[n].H_var)
{
if (pseudopointer[n].H_adj < 0)
{
if (pseudopointer[n].H_adj > -ptrspeeds[3])
pseudopointer[n].H_adj--;
}
else
{
if (pseudopointer[n].H_adj < ptrspeeds[3])
pseudopointer[n].H_adj++;
}
}
}
if (pseudopointer[n].V_adj)
{
pseudopointer[n].y += pseudopointer[n].V_adj;
if (pseudopointer[n].y < 0)
pseudopointer[n].y = 0;
else if (pseudopointer[n].y > PPU.ScreenHeight - 1)
pseudopointer[n].y = PPU.ScreenHeight - 1;
if (pseudopointer[n].V_var)
{
if (pseudopointer[n].V_adj < 0)
{
if (pseudopointer[n].V_adj > -ptrspeeds[3])
pseudopointer[n].V_adj--;
}
else
{
if (pseudopointer[n].V_adj < ptrspeeds[3])
pseudopointer[n].V_adj++;
}
}
}
S9xReportPointer(PseudoPointerBase + n, pseudopointer[n].x, pseudopointer[n].y);
}
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);
}
}
do_polling(POLL_ALL);
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 S9xGetControllerCrosshair (enum crosscontrols ctl, int8 *idx, const char **fg, const char **bg)
{
struct crosshair *c;
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, "S9xGetControllerCrosshair() called with an invalid controller ID %d\n", ctl);
return;
}
if (idx)
*idx = c->img;
if (fg)
*fg = color_names[c->fg];
if (bg)
*bg = color_names[c->bg];
}
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;
}
}
uint16 MovieGetJoypad (int i)
{
if (i < 0 || i > 7)
return (0);
return (joypad[i].buttons);
}
void MovieSetJoypad (int i, uint16 buttons)
{
if (i < 0 || i > 7)
return;
joypad[i].buttons = buttons;
}
bool MovieGetMouse (int i, uint8 out[5])
{
if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))
return (false);
int n = curcontrollers[i] - MOUSE0;
uint8 *ptr = out;
WRITE_WORD(ptr, mouse[n].cur_x); ptr += 2;
WRITE_WORD(ptr, mouse[n].cur_y); ptr += 2;
*ptr = mouse[n].buttons;
return (true);
}
void MovieSetMouse (int i, uint8 in[5], bool inPolling)
{
if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))
return;
int n = curcontrollers[i] - MOUSE0;
uint8 *ptr = in;
mouse[n].cur_x = READ_WORD(ptr); ptr += 2;
mouse[n].cur_y = READ_WORD(ptr); ptr += 2;
mouse[n].buttons = *ptr;
if (inPolling)
UpdatePolledMouse(curcontrollers[i]);
}
bool MovieGetScope (int i, uint8 out[6])
{
if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)
return (false);
uint8 *ptr = out;
WRITE_WORD(ptr, superscope.x); ptr += 2;
WRITE_WORD(ptr, superscope.y); ptr += 2;
*ptr++ = superscope.phys_buttons;
*ptr = superscope.next_buttons;
return (true);
}
void MovieSetScope (int i, uint8 in[6])
{
if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)
return;
uint8 *ptr = in;
superscope.x = READ_WORD(ptr); ptr += 2;
superscope.y = READ_WORD(ptr); ptr += 2;
superscope.phys_buttons = *ptr++;
superscope.next_buttons = *ptr;
}
bool MovieGetJustifier (int i, uint8 out[11])
{
if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))
return (false);
uint8 *ptr = out;
WRITE_WORD(ptr, justifier.x[0]); ptr += 2;
WRITE_WORD(ptr, justifier.x[1]); ptr += 2;
WRITE_WORD(ptr, justifier.y[0]); ptr += 2;
WRITE_WORD(ptr, justifier.y[1]); ptr += 2;
*ptr++ = justifier.buttons;
*ptr++ = justifier.offscreen[0];
*ptr = justifier.offscreen[1];
return (true);
}
void MovieSetJustifier (int i, uint8 in[11])
{
if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))
return;
uint8 *ptr = in;
justifier.x[0] = READ_WORD(ptr); ptr += 2;
justifier.x[1] = READ_WORD(ptr); ptr += 2;
justifier.y[0] = READ_WORD(ptr); ptr += 2;
justifier.y[1] = READ_WORD(ptr); ptr += 2;
justifier.buttons = *ptr++;
justifier.offscreen[0] = *ptr++;
justifier.offscreen[1] = *ptr;
}
bool MovieGetMacsRifle (int i, uint8 out[5])
{
if (i < 0 || i > 1 || curcontrollers[i] != MACSRIFLE)
return (false);
uint8 *ptr = out;
WRITE_WORD(ptr, macsrifle.x); ptr += 2;
WRITE_WORD(ptr, macsrifle.y); ptr += 2;
*ptr = macsrifle.buttons;
return (true);
}
void MovieSetMacsRifle (int i, uint8 in[5])
{
if (i < 0 || i > 1 || curcontrollers[i] != MACSRIFLE)
return;
uint8 *ptr = in;
macsrifle.x = READ_WORD(ptr); ptr += 2;
macsrifle.y = READ_WORD(ptr); ptr += 2;
macsrifle.buttons = *ptr;
}