Path: blob/main/misc/emulator/xnes/snes9x/controls.cpp
28515 views
/***********************************************************************************1Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.23(c) Copyright 1996 - 2002 Gary Henderson ([email protected]),4Jerremy Koot ([email protected])56(c) Copyright 2002 - 2004 Matthew Kendora78(c) Copyright 2002 - 2005 Peter Bortas ([email protected])910(c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)1112(c) Copyright 2001 - 2006 John Weidman ([email protected])1314(c) Copyright 2002 - 2006 funkyass ([email protected]),15Kris Bleakley ([email protected])1617(c) Copyright 2002 - 2010 Brad Jorsch ([email protected]),18Nach ([email protected]),1920(c) Copyright 2002 - 2011 zones ([email protected])2122(c) Copyright 2006 - 2007 nitsuja2324(c) Copyright 2009 - 2011 BearOso,25OV2262728BS-X C emulator code29(c) Copyright 2005 - 2006 Dreamer Nom,30zones3132C4 x86 assembler and some C emulation code33(c) Copyright 2000 - 2003 _Demo_ ([email protected]),34Nach,35zsKnight ([email protected])3637C4 C++ code38(c) Copyright 2003 - 2006 Brad Jorsch,39Nach4041DSP-1 emulator code42(c) Copyright 1998 - 2006 _Demo_,43Andreas Naive ([email protected]),44Gary Henderson,45Ivar ([email protected]),46John Weidman,47Kris Bleakley,48Matthew Kendora,49Nach,50neviksti ([email protected])5152DSP-2 emulator code53(c) Copyright 2003 John Weidman,54Kris Bleakley,55Lord Nightmare ([email protected]),56Matthew Kendora,57neviksti5859DSP-3 emulator code60(c) Copyright 2003 - 2006 John Weidman,61Kris Bleakley,62Lancer,63z80 gaiden6465DSP-4 emulator code66(c) Copyright 2004 - 2006 Dreamer Nom,67John Weidman,68Kris Bleakley,69Nach,70z80 gaiden7172OBC1 emulator code73(c) Copyright 2001 - 2004 zsKnight,74pagefault ([email protected]),75Kris Bleakley76Ported from x86 assembler to C by sanmaiwashi7778SPC7110 and RTC C++ emulator code used in 1.39-1.5179(c) Copyright 2002 Matthew Kendora with research by80zsKnight,81John Weidman,82Dark Force8384SPC7110 and RTC C++ emulator code used in 1.52+85(c) Copyright 2009 byuu,86neviksti8788S-DD1 C emulator code89(c) Copyright 2003 Brad Jorsch with research by90Andreas Naive,91John Weidman9293S-RTC C emulator code94(c) Copyright 2001 - 2006 byuu,95John Weidman9697ST010 C++ emulator code98(c) Copyright 2003 Feather,99John Weidman,100Kris Bleakley,101Matthew Kendora102103Super FX x86 assembler emulator code104(c) Copyright 1998 - 2003 _Demo_,105pagefault,106zsKnight107108Super FX C emulator code109(c) Copyright 1997 - 1999 Ivar,110Gary Henderson,111John Weidman112113Sound emulator code used in 1.5-1.51114(c) Copyright 1998 - 2003 Brad Martin115(c) Copyright 1998 - 2006 Charles Bilyue'116117Sound emulator code used in 1.52+118(c) Copyright 2004 - 2007 Shay Green ([email protected])119120SH assembler code partly based on x86 assembler code121(c) Copyright 2002 - 2004 Marcus Comstedt ([email protected])1221232xSaI filter124(c) Copyright 1999 - 2001 Derek Liauw Kie Fa125126HQ2x, HQ3x, HQ4x filters127(c) Copyright 2003 Maxim Stepin ([email protected])128129NTSC filter130(c) Copyright 2006 - 2007 Shay Green131132GTK+ GUI code133(c) Copyright 2004 - 2011 BearOso134135Win32 GUI code136(c) Copyright 2003 - 2006 blip,137funkyass,138Matthew Kendora,139Nach,140nitsuja141(c) Copyright 2009 - 2011 OV2142143Mac OS GUI code144(c) Copyright 1998 - 2001 John Stiles145(c) Copyright 2001 - 2011 zones146147148Specific ports contains the works of other authors. See headers in149individual files.150151152Snes9x homepage: http://www.snes9x.com/153154Permission to use, copy, modify and/or distribute Snes9x in both binary155and source form, for non-commercial purposes, is hereby granted without156fee, providing that this license information and copyright notice appear157with all copies and any derived work.158159This software is provided 'as-is', without any express or implied160warranty. In no event shall the authors be held liable for any damages161arising from the use of this software or it's derivatives.162163Snes9x is freeware for PERSONAL USE only. Commercial users should164seek permission of the copyright holders first. Commercial use includes,165but is not limited to, charging money for Snes9x or software derived from166Snes9x, including Snes9x or derivatives in commercial game bundles, and/or167using Snes9x as a promotion for your commercial product.168169The copyright holders request that bug fixes and improvements to the code170should be forwarded to them so everyone can benefit from the modifications171in future versions.172173Super NES and Super Nintendo Entertainment System are trademarks of174Nintendo Co., Limited and its subsidiary companies.175***********************************************************************************/176177178#include <map>179#include <set>180#include <vector>181#include <string>182#include <algorithm>183#include <assert.h>184#include <ctype.h>185186#include "snes9x.h"187#include "memmap.h"188#include "apu/apu.h"189#ifdef FANCY190#include "snapshot.h"191#include "movie.h"192#else193#define S9xMoviePlaying() FALSE194#define S9xMovieActive() FALSE195#endif196#include "controls.h"197#ifdef FANCY198#include "crosshairs.h"199#endif200#include "display.h"201#ifdef NETPLAY_SUPPORT202#include "netplay.h"203#endif204205using namespace std;206207#define NONE (-2)208#define MP5 (-1)209#define JOYPAD0 0210#define JOYPAD1 1211#define JOYPAD2 2212#define JOYPAD3 3213#define JOYPAD4 4214#define JOYPAD5 5215#define JOYPAD6 6216#define JOYPAD7 7217#define MOUSE0 8218#define MOUSE1 9219#define SUPERSCOPE 10220#define ONE_JUSTIFIER 11221#define TWO_JUSTIFIERS 12222#define NUMCTLS 13 // This must be LAST223224#define POLL_ALL NUMCTLS225226#define SUPERSCOPE_FIRE 0x80227#define SUPERSCOPE_CURSOR 0x40228#define SUPERSCOPE_TURBO 0x20229#define SUPERSCOPE_PAUSE 0x10230#define SUPERSCOPE_OFFSCREEN 0x02231232#define JUSTIFIER_TRIGGER 0x80233#define JUSTIFIER_START 0x20234#define JUSTIFIER_SELECT 0x08235236#define MAP_UNKNOWN (-1)237#define MAP_NONE 0238#define MAP_BUTTON 1239#define MAP_AXIS 2240#define MAP_POINTER 3241242#define FLAG_IOBIT0 (Memory.FillRAM[0x4213] & 0x40)243#define FLAG_IOBIT1 (Memory.FillRAM[0x4213] & 0x80)244#define FLAG_IOBIT(n) ((n) ? (FLAG_IOBIT1) : (FLAG_IOBIT0))245246bool8 pad_read = 0, pad_read_last = 0;247uint8 read_idx[2 /* ports */][2 /* per port */];248249struct exemulti250{251int32 pos;252bool8 data1;253s9xcommand_t *script;254};255256struct crosshair257{258uint8 set;259uint8 img;260uint8 fg, bg;261};262263static struct264{265int16 x, y;266int16 V_adj;267bool8 V_var;268int16 H_adj;269bool8 H_var;270bool8 mapped;271} pseudopointer[8];272273static struct274{275uint16 buttons;276uint16 turbos;277uint16 toggleturbo;278uint16 togglestick;279uint8 turbo_ct;280} joypad[8];281282static struct283{284uint8 delta_x, delta_y;285int16 old_x, old_y;286int16 cur_x, cur_y;287uint8 buttons;288uint32 ID;289struct crosshair crosshair;290} mouse[2];291292static struct293{294int16 x, y;295uint8 phys_buttons;296uint8 next_buttons;297uint8 read_buttons;298uint32 ID;299struct crosshair crosshair;300} superscope;301302static struct303{304int16 x[2], y[2];305uint8 buttons;306bool8 offscreen[2];307uint32 ID[2];308struct crosshair crosshair[2];309} justifier;310311static struct312{313int8 pads[4];314} mp5[2];315316static set<struct exemulti *> exemultis;317static set<uint32> pollmap[NUMCTLS + 1];318static map<uint32, s9xcommand_t> keymap;319static vector<s9xcommand_t *> multis;320static uint8 turbo_time;321static uint8 pseudobuttons[256];322static bool8 FLAG_LATCH = FALSE;323static int32 curcontrollers[2] = { NONE, NONE };324static int32 newcontrollers[2] = { JOYPAD0, NONE };325static char buf[256];326327#ifdef FANCY328static const char *color_names[32] =329{330"Trans",331"Black",332"25Grey",333"50Grey",334"75Grey",335"White",336"Red",337"Orange",338"Yellow",339"Green",340"Cyan",341"Sky",342"Blue",343"Violet",344"MagicPink",345"Purple",346NULL,347"tBlack",348"t25Grey",349"t50Grey",350"t75Grey",351"tWhite",352"tRed",353"tOrange",354"tYellow",355"tGreen",356"tCyan",357"tSky",358"tBlue",359"tViolet",360"tMagicPink",361"tPurple"362};363#endif364365static const char *speed_names[4] =366{367"Var",368"Slow",369"Med",370"Fast"371};372373static const int ptrspeeds[4] = { 1, 1, 4, 8 };374375// Note: these should be in asciibetical order!376#define THE_COMMANDS \377S(BeginRecordingMovie), \378S(ClipWindows), \379S(Debugger), \380S(DecEmuTurbo), \381S(DecFrameRate), \382S(DecFrameTime), \383S(DecTurboSpeed), \384S(EmuTurbo), \385S(EndRecordingMovie), \386S(ExitEmu), \387S(IncEmuTurbo), \388S(IncFrameRate), \389S(IncFrameTime), \390S(IncTurboSpeed), \391S(LoadFreezeFile), \392S(LoadMovie), \393S(LoadOopsFile), \394S(Pause), \395S(QuickLoad000), \396S(QuickLoad001), \397S(QuickLoad002), \398S(QuickLoad003), \399S(QuickLoad004), \400S(QuickLoad005), \401S(QuickLoad006), \402S(QuickLoad007), \403S(QuickLoad008), \404S(QuickLoad009), \405S(QuickLoad010), \406S(QuickSave000), \407S(QuickSave001), \408S(QuickSave002), \409S(QuickSave003), \410S(QuickSave004), \411S(QuickSave005), \412S(QuickSave006), \413S(QuickSave007), \414S(QuickSave008), \415S(QuickSave009), \416S(QuickSave010), \417S(Reset), \418S(SaveFreezeFile), \419S(SaveSPC), \420S(Screenshot), \421S(SeekToFrame), \422S(SoftReset), \423S(SoundChannel0), \424S(SoundChannel1), \425S(SoundChannel2), \426S(SoundChannel3), \427S(SoundChannel4), \428S(SoundChannel5), \429S(SoundChannel6), \430S(SoundChannel7), \431S(SoundChannelsOn), \432S(SwapJoypads), \433S(ToggleBG0), \434S(ToggleBG1), \435S(ToggleBG2), \436S(ToggleBG3), \437S(ToggleEmuTurbo), \438S(ToggleSprites), \439S(ToggleTransparency) \440441#define S(x) x442443enum command_numbers444{445THE_COMMANDS,446LAST_COMMAND447};448449#undef S450#define S(x) #x451452static const char *command_names[LAST_COMMAND + 1] =453{454THE_COMMANDS,455NULL456};457458#undef S459#undef THE_COMMANDS460461static void DisplayStateChange (const char *, bool8);462static void DoGunLatch (int, int);463static int maptype (int);464static bool strless (const char *, const char *);465static int findstr (const char *, const char **, int);466static int get_threshold (const char **);467static const char * maptypename (int);468static int32 ApplyMulti (s9xcommand_t *, int32, int16);469static void do_polling (int);470static void UpdatePolledMouse (int);471472473static string& operator += (string &s, int i)474{475snprintf(buf, sizeof(buf), "%d", i);476s.append(buf);477return (s);478}479480static string& operator += (string &s, double d)481{482snprintf(buf, sizeof(buf), "%g", d);483s.append(buf);484return (s);485}486487static void DisplayStateChange (const char *str, bool8 on)488{489snprintf(buf, sizeof(buf), "%s: %s", str, on ? "on":"off");490S9xSetInfoString(buf);491}492493static void DoGunLatch (int x, int y)494{495x += 40;496497if (x > 295)498x = 295;499else500if (x < 40)501x = 40;502503if (y > PPU.ScreenHeight - 1)504y = PPU.ScreenHeight - 1;505else506if (y < 0)507y = 0;508509PPU.GunVLatch = (uint16) (y + 1);510PPU.GunHLatch = (uint16) x;511}512513static int maptype (int t)514{515switch (t)516{517case S9xNoMapping:518return (MAP_NONE);519520case S9xButtonJoypad:521case S9xButtonMouse:522case S9xButtonSuperscope:523case S9xButtonJustifier:524case S9xButtonCommand:525case S9xButtonPseudopointer:526case S9xButtonPort:527case S9xButtonMulti:528return (MAP_BUTTON);529530case S9xAxisJoypad:531case S9xAxisPseudopointer:532case S9xAxisPseudobuttons:533case S9xAxisPort:534return (MAP_AXIS);535536case S9xPointer:537case S9xPointerPort:538return (MAP_POINTER);539540default:541return (MAP_UNKNOWN);542}543}544545void S9xControlsReset (void)546{547S9xControlsSoftReset();548mouse[0].buttons &= ~0x30;549mouse[1].buttons &= ~0x30;550justifier.buttons &= ~JUSTIFIER_SELECT;551}552553void S9xControlsSoftReset (void)554{555for (set<struct exemulti *>::iterator it = exemultis.begin(); it != exemultis.end(); it++)556delete *it;557exemultis.clear();558559for (int i = 0; i < 2; i++)560for (int j = 0; j < 2; j++)561read_idx[i][j]=0;562563FLAG_LATCH = FALSE;564}565566void S9xUnmapAllControls (void)567{568S9xControlsReset();569570keymap.clear();571572for (int i = 0; i < (int) multis.size(); i++)573free(multis[i]);574multis.clear();575576for (int i = 0; i < NUMCTLS + 1; i++)577pollmap[i].clear();578579for (int i = 0; i < 8; i++)580{581pseudopointer[i].x = 0;582pseudopointer[i].y = 0;583pseudopointer[i].H_adj = 0;584pseudopointer[i].V_adj = 0;585pseudopointer[i].H_var = 0;586pseudopointer[i].V_var = 0;587pseudopointer[i].mapped = false;588589joypad[i].buttons = 0;590joypad[i].turbos = 0;591joypad[i].turbo_ct = 0;592}593594for (int i = 0; i < 2; i++)595{596mouse[i].old_x = mouse[i].old_y = 0;597mouse[i].cur_x = mouse[i].cur_y = 0;598mouse[i].buttons = 1;599mouse[i].ID = InvalidControlID;600601if (!(mouse[i].crosshair.set & 1))602mouse[i].crosshair.img = 0; // no image for mouse because its only logical position is game-specific, not known by the emulator603if (!(mouse[i].crosshair.set & 2))604mouse[i].crosshair.fg = 5;605if (!(mouse[i].crosshair.set & 4))606mouse[i].crosshair.bg = 1;607608justifier.x[i] = justifier.y[i] = 0;609justifier.offscreen[i] = 0;610justifier.ID[i] = InvalidControlID;611612if (!(justifier.crosshair[i].set & 1))613justifier.crosshair[i].img = 4;614if (!(justifier.crosshair[i].set & 2))615justifier.crosshair[i].fg = i ? 14 : 12;616if (!(justifier.crosshair[i].set & 4))617justifier.crosshair[i].bg = 1;618}619620justifier.buttons = 0;621622superscope.x = superscope.y = 0;623superscope.phys_buttons = 0;624superscope.next_buttons = 0;625superscope.read_buttons = 0;626superscope.ID = InvalidControlID;627628if (!(superscope.crosshair.set & 1))629superscope.crosshair.img = 2;630if (!(superscope.crosshair.set & 2))631superscope.crosshair.fg = 5;632if (!(superscope.crosshair.set & 4))633superscope.crosshair.bg = 1;634635ZeroMemory(pseudobuttons, sizeof(pseudobuttons));636637turbo_time = 1;638}639640void S9xSetController (int port, enum controllers controller, int8 id1, int8 id2, int8 id3, int8 id4)641{642if (port < 0 || port > 1)643return;644645switch (controller)646{647case CTL_NONE:648break;649650case CTL_JOYPAD:651if (id1 < 0 || id1 > 7)652break;653654newcontrollers[port] = JOYPAD0 + id1;655return;656657case CTL_MOUSE:658if (id1 < 0 || id1 > 1)659break;660if (!Settings.MouseMaster)661{662S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");663break;664}665666newcontrollers[port] = MOUSE0 + id1;667return;668669case CTL_SUPERSCOPE:670if (!Settings.SuperScopeMaster)671{672S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");673break;674}675676newcontrollers[port] = SUPERSCOPE;677return;678679case CTL_JUSTIFIER:680if (id1 < 0 || id1 > 1)681break;682if (!Settings.JustifierMaster)683{684S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");685break;686}687688newcontrollers[port] = ONE_JUSTIFIER + id1;689return;690691case CTL_MP5:692if (id1 < -1 || id1 > 7)693break;694if (id2 < -1 || id2 > 7)695break;696if (id3 < -1 || id3 > 7)697break;698if (id4 < -1 || id4 > 7)699break;700if (!Settings.MultiPlayer5Master)701{702S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");703break;704}705706newcontrollers[port] = MP5;707mp5[port].pads[0] = (id1 < 0) ? NONE : JOYPAD0 + id1;708mp5[port].pads[1] = (id2 < 0) ? NONE : JOYPAD0 + id2;709mp5[port].pads[2] = (id3 < 0) ? NONE : JOYPAD0 + id3;710mp5[port].pads[3] = (id4 < 0) ? NONE : JOYPAD0 + id4;711return;712713default:714fprintf(stderr, "Unknown controller type %d\n", controller);715break;716}717718newcontrollers[port] = NONE;719}720721bool S9xVerifyControllers (void)722{723bool ret = false;724int port, i, used[NUMCTLS];725726for (i = 0; i < NUMCTLS; used[i++] = 0) ;727728for (port = 0; port < 2; port++)729{730switch (i = newcontrollers[port])731{732case MOUSE0:733case MOUSE1:734if (!Settings.MouseMaster)735{736S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Mouse: MouseMaster disabled");737newcontrollers[port] = NONE;738ret = true;739break;740}741742if (used[i]++ > 0)743{744snprintf(buf, sizeof(buf), "Mouse%d used more than once! Disabling extra instances", i - MOUSE0 + 1);745S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);746newcontrollers[port] = NONE;747ret = true;748break;749}750751break;752753case SUPERSCOPE:754if (!Settings.SuperScopeMaster)755{756S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select SNES Superscope: SuperScopeMaster disabled");757newcontrollers[port] = NONE;758ret = true;759break;760}761762if (used[i]++ > 0)763{764snprintf(buf, sizeof(buf), "Superscope used more than once! Disabling extra instances");765S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);766newcontrollers[port] = NONE;767ret = true;768break;769}770771break;772773case ONE_JUSTIFIER:774case TWO_JUSTIFIERS:775if (!Settings.JustifierMaster)776{777S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select Konami Justifier: JustifierMaster disabled");778newcontrollers[port] = NONE;779ret = true;780break;781}782783if (used[ONE_JUSTIFIER]++ > 0)784{785snprintf(buf, sizeof(buf), "Justifier used more than once! Disabling extra instances");786S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);787newcontrollers[port] = NONE;788ret = true;789break;790}791792break;793794case MP5:795if (!Settings.MultiPlayer5Master)796{797S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, "Cannot select MP5: MultiPlayer5Master disabled");798newcontrollers[port] = NONE;799ret = true;800break;801}802803for (i = 0; i < 4; i++)804{805if (mp5[port].pads[i] != NONE)806{807if (used[mp5[port].pads[i] - JOYPAD0]++ > 0)808{809snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", mp5[port].pads[i] - JOYPAD0 + 1);810S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);811mp5[port].pads[i] = NONE;812ret = true;813break;814}815}816}817818break;819820case JOYPAD0:821case JOYPAD1:822case JOYPAD2:823case JOYPAD3:824case JOYPAD4:825case JOYPAD5:826case JOYPAD6:827case JOYPAD7:828if (used[i - JOYPAD0]++ > 0)829{830snprintf(buf, sizeof(buf), "Joypad%d used more than once! Disabling extra instances", i - JOYPAD0 + 1);831S9xMessage(S9X_CONFIG_INFO, S9X_ERROR, buf);832newcontrollers[port] = NONE;833ret = true;834break;835}836837break;838839default:840break;841}842}843844return (ret);845}846847void S9xGetController (int port, enum controllers *controller, int8 *id1, int8 *id2, int8 *id3, int8 *id4)848{849int i;850851*controller = CTL_NONE;852*id1 = *id2 = *id3 = *id4 = -1;853854if (port < 0 || port > 1)855return;856857switch (i = newcontrollers[port])858{859case MP5:860*controller = CTL_MP5;861*id1 = (mp5[port].pads[0] == NONE) ? -1 : mp5[port].pads[0] - JOYPAD0;862*id2 = (mp5[port].pads[1] == NONE) ? -1 : mp5[port].pads[1] - JOYPAD0;863*id3 = (mp5[port].pads[2] == NONE) ? -1 : mp5[port].pads[2] - JOYPAD0;864*id4 = (mp5[port].pads[3] == NONE) ? -1 : mp5[port].pads[3] - JOYPAD0;865return;866867case JOYPAD0:868case JOYPAD1:869case JOYPAD2:870case JOYPAD3:871case JOYPAD4:872case JOYPAD5:873case JOYPAD6:874case JOYPAD7:875*controller = CTL_JOYPAD;876*id1 = i - JOYPAD0;877return;878879case MOUSE0:880case MOUSE1:881*controller = CTL_MOUSE;882*id1 = i - MOUSE0;883return;884885case SUPERSCOPE:886*controller = CTL_SUPERSCOPE;887*id1 = 1;888return;889890case ONE_JUSTIFIER:891case TWO_JUSTIFIERS:892*controller = CTL_JUSTIFIER;893*id1 = i - ONE_JUSTIFIER;894return;895}896}897898void S9xReportControllers (void)899{900static char mes[128];901char *c = mes;902903S9xVerifyControllers();904905for (int port = 0; port < 2; port++)906{907c += sprintf(c, "Port %d: ", port + 1);908909switch (newcontrollers[port])910{911case NONE:912c += sprintf(c, "<none>. ");913break;914915case MP5:916c += sprintf(c, "MP5 with pads");917for (int i = 0; i < 4; i++)918{919if (mp5[port].pads[i] == NONE)920c += sprintf(c, " <none>. ");921else922c += sprintf(c, " #%d. ", mp5[port].pads[i] + 1 - JOYPAD0);923}924925break;926927case JOYPAD0:928case JOYPAD1:929case JOYPAD2:930case JOYPAD3:931case JOYPAD4:932case JOYPAD5:933case JOYPAD6:934case JOYPAD7:935c += sprintf(c, "Pad #%d. ", (int) (newcontrollers[port] - JOYPAD0 + 1));936break;937938case MOUSE0:939case MOUSE1:940c += sprintf(c, "Mouse #%d. ", (int) (newcontrollers[port] - MOUSE0 + 1));941break;942943case SUPERSCOPE:944if (port == 0)945c += sprintf(c, "Superscope (cannot fire). ");946else947c += sprintf(c, "Superscope. ");948break;949950case ONE_JUSTIFIER:951if (port == 0)952c += sprintf(c, "Blue Justifier (cannot fire). ");953else954c += sprintf(c, "Blue Justifier. ");955break;956957case TWO_JUSTIFIERS:958if (port == 0)959c += sprintf(c, "Blue and Pink Justifiers (cannot fire). ");960else961c += sprintf(c, "Blue and Pink Justifiers. ");962break;963}964}965966S9xMessage(S9X_INFO, S9X_CONFIG_INFO, mes);967}968969char * S9xGetCommandName (s9xcommand_t command)970{971string s;972char c;973974switch (command.type)975{976case S9xButtonJoypad:977if (command.input.button.joypad.buttons == 0)978return (strdup("None"));979if (command.input.button.joypad.buttons & 0x000f)980return (strdup("None"));981982s = "Joypad";983s += command.input.button.joypad.idx + 1;984985c = ' ';986if (command.input.button.joypad.toggle) { if (c) s += c; s += "Toggle"; c = 0; }987if (command.input.button.joypad.sticky) { if (c) s += c; s += "Sticky"; c = 0; }988if (command.input.button.joypad.turbo ) { if (c) s += c; s += "Turbo"; c = 0; }989990c = ' ';991if (command.input.button.joypad.buttons & SNES_UP_MASK ) { s += c; s += "Up"; c = '+'; }992if (command.input.button.joypad.buttons & SNES_DOWN_MASK ) { s += c; s += "Down"; c = '+'; }993if (command.input.button.joypad.buttons & SNES_LEFT_MASK ) { s += c; s += "Left"; c = '+'; }994if (command.input.button.joypad.buttons & SNES_RIGHT_MASK ) { s += c; s += "Right"; c = '+'; }995if (command.input.button.joypad.buttons & SNES_A_MASK ) { s += c; s += "A"; c = '+'; }996if (command.input.button.joypad.buttons & SNES_B_MASK ) { s += c; s += "B"; c = '+'; }997if (command.input.button.joypad.buttons & SNES_X_MASK ) { s += c; s += "X"; c = '+'; }998if (command.input.button.joypad.buttons & SNES_Y_MASK ) { s += c; s += "Y"; c = '+'; }999if (command.input.button.joypad.buttons & SNES_TL_MASK ) { s += c; s += "L"; c = '+'; }1000if (command.input.button.joypad.buttons & SNES_TR_MASK ) { s += c; s += "R"; c = '+'; }1001if (command.input.button.joypad.buttons & SNES_START_MASK ) { s += c; s += "Start"; c = '+'; }1002if (command.input.button.joypad.buttons & SNES_SELECT_MASK) { s += c; s += "Select"; c = '+'; }10031004break;10051006case S9xButtonMouse:1007if (!command.input.button.mouse.left && !command.input.button.mouse.right)1008return (strdup("None"));10091010s = "Mouse";1011s += command.input.button.mouse.idx + 1;1012s += " ";10131014if (command.input.button.mouse.left ) s += "L";1015if (command.input.button.mouse.right) s += "R";10161017break;10181019case S9xButtonSuperscope:1020if (!command.input.button.scope.fire && !command.input.button.scope.cursor && !command.input.button.scope.turbo && !command.input.button.scope.pause && !command.input.button.scope.aim_offscreen)1021return (strdup("None"));10221023s = "Superscope";10241025if (command.input.button.scope.aim_offscreen) s += " AimOffscreen";10261027c = ' ';1028if (command.input.button.scope.fire ) { s += c; s += "Fire"; c = '+'; }1029if (command.input.button.scope.cursor) { s += c; s += "Cursor"; c = '+'; }1030if (command.input.button.scope.turbo ) { s += c; s += "ToggleTurbo"; c = '+'; }1031if (command.input.button.scope.pause ) { s += c; s += "Pause"; c = '+'; }10321033break;10341035case S9xButtonJustifier:1036if (!command.input.button.justifier.trigger && !command.input.button.justifier.start && !command.input.button.justifier.aim_offscreen)1037return (strdup("None"));10381039s = "Justifier";1040s += command.input.button.justifier.idx + 1;10411042if (command.input.button.justifier.aim_offscreen) s += " AimOffscreen";10431044c = ' ';1045if (command.input.button.justifier.trigger) { s += c; s += "Trigger"; c = '+'; }1046if (command.input.button.justifier.start ) { s += c; s += "Start"; c = '+'; }10471048break;10491050case S9xButtonCommand:1051if (command.input.button.command >= LAST_COMMAND)1052return (strdup("None"));10531054return (strdup(command_names[command.input.button.command]));10551056case S9xPointer:1057if (!command.input.pointer.aim_mouse0 && !command.input.pointer.aim_mouse1 && !command.input.pointer.aim_scope && !command.input.pointer.aim_justifier0 && !command.input.pointer.aim_justifier1)1058return (strdup("None"));10591060s = "Pointer";10611062c = ' ';1063if (command.input.pointer.aim_mouse0 ) { s += c; s += "Mouse1"; c = '+'; }1064if (command.input.pointer.aim_mouse1 ) { s += c; s += "Mouse2"; c = '+'; }1065if (command.input.pointer.aim_scope ) { s += c; s += "Superscope"; c = '+'; }1066if (command.input.pointer.aim_justifier0) { s += c; s += "Justifier1"; c = '+'; }1067if (command.input.pointer.aim_justifier1) { s += c; s += "Justifier2"; c = '+'; }10681069break;10701071case S9xButtonPseudopointer:1072if (!command.input.button.pointer.UD && !command.input.button.pointer.LR)1073return (strdup("None"));1074if (command.input.button.pointer.UD == -2 || command.input.button.pointer.LR == -2)1075return (strdup("None"));10761077s = "ButtonToPointer ";1078s += command.input.button.pointer.idx + 1;10791080if (command.input.button.pointer.UD) s += (command.input.button.pointer.UD == 1) ? 'd' : 'u';1081if (command.input.button.pointer.LR) s += (command.input.button.pointer.LR == 1) ? 'r' : 'l';10821083s += " ";1084s += speed_names[command.input.button.pointer.speed_type];10851086break;10871088case S9xAxisJoypad:1089s = "Joypad";1090s += command.input.axis.joypad.idx + 1;1091s += " Axis ";10921093switch (command.input.axis.joypad.axis)1094{1095case 0: s += (command.input.axis.joypad.invert ? "Right/Left" : "Left/Right"); break;1096case 1: s += (command.input.axis.joypad.invert ? "Down/Up" : "Up/Down" ); break;1097case 2: s += (command.input.axis.joypad.invert ? "A/Y" : "Y/A" ); break;1098case 3: s += (command.input.axis.joypad.invert ? "B/X" : "X/B" ); break;1099case 4: s += (command.input.axis.joypad.invert ? "R/L" : "L/R" ); break;1100default: return (strdup("None"));1101}11021103s += " T=";1104s += int((command.input.axis.joypad.threshold + 1) * 1000 / 256) / 10.0;1105s += "%";11061107break;11081109case S9xAxisPseudopointer:1110s = "AxisToPointer ";1111s += command.input.axis.pointer.idx + 1;1112s += command.input.axis.pointer.HV ? 'v' : 'h';1113s += " ";11141115if (command.input.axis.pointer.invert) s += "-";11161117s += speed_names[command.input.axis.pointer.speed_type];11181119break;11201121case S9xAxisPseudobuttons:1122s = "AxisToButtons ";1123s += command.input.axis.button.negbutton;1124s += "/";1125s += command.input.axis.button.posbutton;1126s += " T=";1127s += int((command.input.axis.button.threshold + 1) * 1000 / 256) / 10.0;1128s += "%";11291130break;11311132case S9xButtonPort:1133case S9xAxisPort:1134case S9xPointerPort:1135return (strdup("BUG: Port should have handled this instead of calling S9xGetCommandName()"));11361137case S9xNoMapping:1138return (strdup("None"));11391140case S9xButtonMulti:1141{1142if (command.input.button.multi_idx >= (int) multis.size())1143return (strdup("None"));11441145s = "{";1146if (multis[command.input.button.multi_idx]->multi_press) s = "+{";11471148bool sep = false;11491150for (s9xcommand_t *m = multis[command.input.button.multi_idx]; m->multi_press != 3; m++)1151{1152if (m->type == S9xNoMapping)1153{1154s += ";";1155sep = false;1156}1157else1158{1159if (sep) s += ",";1160if (m->multi_press == 1) s += "+";1161if (m->multi_press == 2) s += "-";11621163s += S9xGetCommandName(*m);1164sep = true;1165}1166}11671168s += "}";11691170break;1171}11721173default:1174return (strdup("BUG: Unknown command type"));1175}11761177return (strdup(s.c_str()));1178}11791180static bool strless (const char *a, const char *b)1181{1182return (strcmp(a, b) < 0);1183}11841185static int findstr (const char *needle, const char **haystack, int numstr)1186{1187const char **r;11881189r = lower_bound(haystack, haystack + numstr, needle, strless);1190if (r >= haystack + numstr || strcmp(needle, *r))1191return (-1);11921193return (r - haystack);1194}11951196static int get_threshold (const char **ss)1197{1198const char *s = *ss;1199int i;12001201if (s[0] != 'T' || s[1] != '=')1202return (-1);12031204s += 2;1205i = 0;12061207if (s[0] == '0')1208{1209if (s[1] != '.')1210return (-1);12111212s++;1213}1214else1215{1216do1217{1218if (*s < '0' || *s > '9')1219return (-1);12201221i = i * 10 + 10 * (*s - '0');1222if (i > 1000)1223return (-1);12241225s++;1226}1227while (*s != '.' && *s != '%');1228}12291230if (*s == '.')1231{1232if (s[1] < '0' || s[1] > '9' || s[2] != '%')1233return (-1);12341235i += s[1] - '0';1236}12371238if (i > 1000)1239return (-1);12401241*ss = s;12421243return (i);1244}12451246s9xcommand_t S9xGetCommandT (const char *name)1247{1248s9xcommand_t cmd;1249int i, j;1250const char *s;12511252ZeroMemory(&cmd, sizeof(cmd));1253cmd.type = S9xBadMapping;1254cmd.multi_press = 0;1255cmd.button_norpt = 0;12561257if (!strcmp(name, "None"))1258cmd.type = S9xNoMapping;1259else1260if (!strncmp(name, "Joypad", 6))1261{1262if (name[6] < '1' || name[6] > '8' || name[7] != ' ')1263return (cmd);12641265if (!strncmp(name + 8, "Axis ", 5))1266{1267cmd.input.axis.joypad.idx = name[6] - '1';1268s = name + 13;12691270if (!strncmp(s, "Left/Right ", 11)) { j = 0; i = 0; s += 11; }1271else1272if (!strncmp(s, "Right/Left ", 11)) { j = 0; i = 1; s += 11; }1273else1274if (!strncmp(s, "Up/Down ", 8)) { j = 1; i = 0; s += 8; }1275else1276if (!strncmp(s, "Down/Up ", 8)) { j = 1; i = 1; s += 8; }1277else1278if (!strncmp(s, "Y/A ", 4)) { j = 2; i = 0; s += 4; }1279else1280if (!strncmp(s, "A/Y ", 4)) { j = 2; i = 1; s += 4; }1281else1282if (!strncmp(s, "X/B ", 4)) { j = 3; i = 0; s += 4; }1283else1284if (!strncmp(s, "B/X ", 4)) { j = 3; i = 1; s += 4; }1285else1286if (!strncmp(s, "L/R ", 4)) { j = 4; i = 0; s += 4; }1287else1288if (!strncmp(s, "R/L ", 4)) { j = 4; i = 1; s += 4; }1289else1290return (cmd);12911292cmd.input.axis.joypad.axis = j;1293cmd.input.axis.joypad.invert = i;1294i = get_threshold(&s);1295if (i < 0)1296return (cmd);1297cmd.input.axis.joypad.threshold = (i - 1) * 256 / 1000;12981299cmd.type = S9xAxisJoypad;1300}1301else1302{1303cmd.input.button.joypad.idx = name[6] - '1';1304s = name + 8;1305i = 0;13061307if ((cmd.input.button.joypad.toggle = strncmp(s, "Toggle", 6) ? 0 : 1)) s += i = 6;1308if ((cmd.input.button.joypad.sticky = strncmp(s, "Sticky", 6) ? 0 : 1)) s += i = 6;1309if ((cmd.input.button.joypad.turbo = strncmp(s, "Turbo", 5) ? 0 : 1)) s += i = 5;13101311if (cmd.input.button.joypad.toggle && !(cmd.input.button.joypad.sticky || cmd.input.button.joypad.turbo))1312return (cmd);13131314if (i)1315{1316if (*s != ' ')1317return (cmd);1318s++;1319}13201321i = 0;13221323if (!strncmp(s, "Up", 2)) { i |= SNES_UP_MASK; s += 2; if (*s == '+') s++; }1324if (!strncmp(s, "Down", 4)) { i |= SNES_DOWN_MASK; s += 4; if (*s == '+') s++; }1325if (!strncmp(s, "Left", 4)) { i |= SNES_LEFT_MASK; s += 4; if (*s == '+') s++; }1326if (!strncmp(s, "Right", 5)) { i |= SNES_RIGHT_MASK; s += 5; if (*s == '+') s++; }13271328if (*s == 'A') { i |= SNES_A_MASK; s++; if (*s == '+') s++; }1329if (*s == 'B') { i |= SNES_B_MASK; s++; if (*s == '+') s++; }1330if (*s == 'X') { i |= SNES_X_MASK; s++; if (*s == '+') s++; }1331if (*s == 'Y') { i |= SNES_Y_MASK; s++; if (*s == '+') s++; }1332if (*s == 'L') { i |= SNES_TL_MASK; s++; if (*s == '+') s++; }1333if (*s == 'R') { i |= SNES_TR_MASK; s++; if (*s == '+') s++; }13341335if (!strncmp(s, "Start", 5)) { i |= SNES_START_MASK; s += 5; if (*s == '+') s++; }1336if (!strncmp(s, "Select", 6)) { i |= SNES_SELECT_MASK; s += 6; }13371338if (i == 0 || *s != 0 || *(s - 1) == '+')1339return (cmd);13401341cmd.input.button.joypad.buttons = i;13421343cmd.type = S9xButtonJoypad;1344}1345}1346else1347if (!strncmp(name, "Mouse", 5))1348{1349if (name[5] < '1' || name[5] > '2' || name[6] != ' ')1350return (cmd);13511352cmd.input.button.mouse.idx = name[5] - '1';1353s = name + 7;1354i = 0;13551356if ((cmd.input.button.mouse.left = (*s == 'L'))) s += i = 1;1357if ((cmd.input.button.mouse.right = (*s == 'R'))) s += i = 1;13581359if (i == 0 || *s != 0)1360return (cmd);13611362cmd.type = S9xButtonMouse;1363}1364else1365if (!strncmp(name, "Superscope ", 11))1366{1367s = name + 11;1368i = 0;13691370if ((cmd.input.button.scope.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }1371if ((cmd.input.button.scope.fire = strncmp(s, "Fire", 4) ? 0 : 1)) { s += i = 4; if (*s == '+') s++; }1372if ((cmd.input.button.scope.cursor = strncmp(s, "Cursor", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }1373if ((cmd.input.button.scope.turbo = strncmp(s, "ToggleTurbo", 11) ? 0 : 1)) { s += i = 11; if (*s == '+') s++; }1374if ((cmd.input.button.scope.pause = strncmp(s, "Pause", 5) ? 0 : 1)) { s += i = 5; }13751376if (i == 0 || *s != 0 || *(s - 1) == '+')1377return (cmd);13781379cmd.type = S9xButtonSuperscope;1380}1381else1382if (!strncmp(name, "Justifier", 9))1383{1384if (name[9] < '1' || name[9] > '2' || name[10] != ' ')1385return (cmd);13861387cmd.input.button.justifier.idx = name[9] - '1';1388s = name + 11;1389i = 0;13901391if ((cmd.input.button.justifier.aim_offscreen = strncmp(s, "AimOffscreen", 12) ? 0 : 1)) { s += i = 12; if (*s == ' ') s++; else if (*s != 0) return (cmd); }1392if ((cmd.input.button.justifier.trigger = strncmp(s, "Trigger", 7) ? 0 : 1)) { s += i = 7; if (*s == '+') s++; }1393if ((cmd.input.button.justifier.start = strncmp(s, "Start", 5) ? 0 : 1)) { s += i = 5; }13941395if (i == 0 || *s != 0 || *(s - 1) == '+')1396return (cmd);13971398cmd.type = S9xButtonJustifier;1399}1400else1401if (!strncmp(name, "Pointer ", 8))1402{1403s = name + 8;1404i = 0;14051406if ((cmd.input.pointer.aim_mouse0 = strncmp(s, "Mouse1", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }1407if ((cmd.input.pointer.aim_mouse1 = strncmp(s, "Mouse2", 6) ? 0 : 1)) { s += i = 6; if (*s == '+') s++; }1408if ((cmd.input.pointer.aim_scope = strncmp(s, "Superscope", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }1409if ((cmd.input.pointer.aim_justifier0 = strncmp(s, "Justifier1", 10) ? 0 : 1)) { s += i = 10; if (*s == '+') s++; }1410if ((cmd.input.pointer.aim_justifier1 = strncmp(s, "Justifier2", 10) ? 0 : 1)) { s += i = 10; }14111412if (i == 0 || *s != 0 || *(s - 1) == '+')1413return (cmd);14141415cmd.type = S9xPointer;1416}1417else1418if (!strncmp(name, "ButtonToPointer ", 16))1419{1420if (name[16] < '1' || name[16] > '8')1421return (cmd);14221423cmd.input.button.pointer.idx = name[16] - '1';1424s = name + 17;1425i = 0;14261427if ((cmd.input.button.pointer.UD = (*s == 'u' ? -1 : (*s == 'd' ? 1 : 0)))) s += i = 1;1428if ((cmd.input.button.pointer.LR = (*s == 'l' ? -1 : (*s == 'r' ? 1 : 0)))) s += i = 1;14291430if (i == 0 || *(s++) != ' ')1431return (cmd);14321433for (i = 0; i < 4; i++)1434if (!strcmp(s, speed_names[i]))1435break;1436if (i > 3)1437return (cmd);14381439cmd.input.button.pointer.speed_type = i;14401441cmd.type = S9xButtonPseudopointer;1442}1443else1444if (!strncmp(name, "AxisToPointer ", 14))1445{1446if (name[14] < '1' || name[14] > '8')1447return (cmd);14481449cmd.input.axis.pointer.idx = name[14] - '1';1450s= name + 15;1451i = 0;14521453if (*s == 'h')1454cmd.input.axis.pointer.HV = 0;1455else1456if (*s == 'v')1457cmd.input.axis.pointer.HV = 1;1458else1459return (cmd);14601461if (s[1] != ' ')1462return (cmd);14631464s += 2;1465if ((cmd.input.axis.pointer.invert = *s == '-'))1466s++;14671468for (i = 0; i < 4; i++)1469if (!strcmp(s, speed_names[i]))1470break;1471if (i > 3)1472return (cmd);14731474cmd.input.axis.pointer.speed_type = i;14751476cmd.type = S9xAxisPseudopointer;1477}1478else1479if (!strncmp(name, "AxisToButtons ", 14))1480{1481s = name + 14;14821483if (s[0] == '0')1484{1485if (s[1] != '/')1486return (cmd);14871488cmd.input.axis.button.negbutton = 0;1489s += 2;1490}1491else1492{1493i = 0;1494do1495{1496if (*s < '0' || *s > '9')1497return (cmd);14981499i = i * 10 + *s - '0';1500if (i > 255)1501return (cmd);1502}1503while (*++s != '/');15041505cmd.input.axis.button.negbutton = i;1506s++;1507}15081509if (s[0] == '0')1510{1511if (s[1] != ' ')1512return (cmd);15131514cmd.input.axis.button.posbutton = 0;1515s += 2;1516}1517else1518{1519i = 0;1520do1521{1522if (*s < '0' || *s > '9')1523return (cmd);15241525i = i * 10 + *s - '0';1526if (i > 255)1527return (cmd);1528}1529while (*++s != ' ');15301531cmd.input.axis.button.posbutton = i;1532s++;1533}15341535i = get_threshold(&s);1536if (i < 0)1537return (cmd);1538cmd.input.axis.button.threshold = (i - 1) * 256 / 1000;15391540cmd.type = S9xAxisPseudobuttons;1541}1542else1543if (!strncmp(name, "MULTI#", 6))1544{1545i = strtol(name + 6, (char **) &s, 10);1546if (s != NULL && *s != '\0')1547return (cmd);1548if (i >= (int) multis.size())1549return (cmd);15501551cmd.input.button.multi_idx = i;1552cmd.type = S9xButtonMulti;1553}1554else1555if (((name[0] == '+' && name[1] == '{') || name[0] == '{') && name[strlen(name) - 1] == '}')1556{1557if (multis.size() > 2147483640)1558{1559fprintf(stderr, "Too many multis!");1560return (cmd);1561}15621563string x;1564int n;15651566j = 2;1567for (i = (name[0] == '+') ? 2 : 1; name[i] != '\0'; i++)1568{1569if (name[i] == ',' || name[i] == ';')1570{1571if (name[i] == ';')1572j++;1573if (++j > 2147483640)1574{1575fprintf(stderr, "Multi too long!");1576return (cmd);1577}1578}15791580if (name[i] == '{')1581return (cmd);1582}15831584s9xcommand_t *c = (s9xcommand_t *) calloc(j, sizeof(s9xcommand_t));1585if (c == NULL)1586{1587perror("malloc error while parsing multi");1588return (cmd);1589}15901591n = 0;1592i = (name[0] == '+') ? 2 : 1;15931594do1595{1596if (name[i] == ';')1597{1598c[n].type = S9xNoMapping;1599c[n].multi_press = 0;1600c[n].button_norpt = 0;16011602j = i;1603}1604else1605if (name[i] == ',')1606{1607free(c);1608return (cmd);1609}1610else1611{1612uint8 press = 0;16131614if (name[0] == '+')1615{1616if (name[i] == '+')1617press = 1;1618else1619if (name[i] == '-')1620press = 2;1621else1622{1623free(c);1624return (cmd);1625}16261627i++;1628}16291630for (j = i; name[j] != ';' && name[j] != ',' && name[j] != '}'; j++) ;16311632x.assign(name + i, j - i);1633c[n] = S9xGetCommandT(x.c_str());1634c[n].multi_press = press;16351636if (maptype(c[n].type) != MAP_BUTTON)1637{1638free(c);1639return (cmd);1640}16411642if (name[j] == ';')1643j--;1644}16451646i = j + 1;1647n++;1648}1649while (name[i] != '\0');16501651c[n].type = S9xNoMapping;1652c[n].multi_press = 3;16531654multis.push_back(c);16551656cmd.input.button.multi_idx = multis.size() - 1;1657cmd.type = S9xButtonMulti;1658}1659else1660{1661i = findstr(name, command_names, LAST_COMMAND);1662if (i < 0)1663return (cmd);16641665cmd.type = S9xButtonCommand;1666cmd.input.button.command = i;1667}16681669return (cmd);1670}16711672const char ** S9xGetAllSnes9xCommands (void)1673{1674return (command_names);1675}16761677s9xcommand_t S9xGetMapping (uint32 id)1678{1679if (keymap.count(id) == 0)1680{1681s9xcommand_t cmd;1682cmd.type = S9xNoMapping;1683return (cmd);1684}1685else1686return (keymap[id]);1687}16881689static const char * maptypename (int t)1690{1691switch (t)1692{1693case MAP_NONE: return ("unmapped");1694case MAP_BUTTON: return ("button");1695case MAP_AXIS: return ("axis");1696case MAP_POINTER: return ("pointer");1697default: return ("unknown");1698}1699}17001701void S9xUnmapID (uint32 id)1702{1703for (int i = 0; i < NUMCTLS + 1; i++)1704pollmap[i].erase(id);17051706if (mouse[0].ID == id) mouse[0].ID = InvalidControlID;1707if (mouse[1].ID == id) mouse[1].ID = InvalidControlID;1708if (superscope.ID == id) superscope.ID = InvalidControlID;1709if (justifier.ID[0] == id) justifier.ID[0] = InvalidControlID;1710if (justifier.ID[1] == id) justifier.ID[1] = InvalidControlID;17111712if (id >= PseudoPointerBase)1713pseudopointer[id - PseudoPointerBase].mapped = false;17141715keymap.erase(id);1716}17171718bool S9xMapButton (uint32 id, s9xcommand_t mapping, bool poll)1719{1720int t;17211722if (id == InvalidControlID)1723{1724fprintf(stderr, "Cannot map InvalidControlID\n");1725return (false);1726}17271728t = maptype(mapping.type);17291730if (t == MAP_NONE)1731{1732S9xUnmapID(id);1733return (true);1734}17351736if (t != MAP_BUTTON)1737return (false);17381739t = maptype(S9xGetMapping(id).type);17401741if (t != MAP_NONE && t != MAP_BUTTON)1742fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to button\n", id, maptypename(t));17431744if (id >= PseudoPointerBase)1745{1746fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as a button\n", id - PseudoPointerBase);1747return (false);1748}17491750t = -1;17511752if (poll)1753{1754if (id >= PseudoButtonBase)1755fprintf(stderr, "INFO: Ignoring attempt to set pseudo-button #%d to polling\n", id - PseudoButtonBase);1756else1757{1758switch (mapping.type)1759{1760case S9xButtonJoypad:1761t = JOYPAD0 + mapping.input.button.joypad.idx;1762break;17631764case S9xButtonMouse:1765t = MOUSE0 + mapping.input.button.mouse.idx;1766break;17671768case S9xButtonSuperscope:1769t = SUPERSCOPE;1770break;17711772case S9xButtonJustifier:1773t = ONE_JUSTIFIER + mapping.input.button.justifier.idx;1774break;17751776case S9xButtonCommand:1777case S9xButtonPseudopointer:1778case S9xButtonPort:1779case S9xButtonMulti:1780t = POLL_ALL;1781break;1782}1783}1784}17851786S9xUnmapID(id);17871788keymap[id] = mapping;17891790if (t >= 0)1791pollmap[t].insert(id);17921793return (true);1794}17951796void S9xReportButton (uint32 id, bool pressed)1797{1798if (keymap.count(id) == 0)1799return;18001801if (keymap[id].type == S9xNoMapping)1802return;18031804if (maptype(keymap[id].type) != MAP_BUTTON)1805{1806fprintf(stderr, "ERROR: S9xReportButton called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);1807return;1808}18091810if (keymap[id].type == S9xButtonCommand) // skips the "already-pressed check" unless it's a command, as a hack to work around the following problem:1811if (keymap[id].button_norpt == pressed) // FIXME: this makes the controls "stick" after loading a savestate while recording a movie and holding any button1812return;18131814keymap[id].button_norpt = pressed;18151816S9xApplyCommand(keymap[id], pressed, 0);1817}18181819bool S9xMapPointer (uint32 id, s9xcommand_t mapping, bool poll)1820{1821int t;18221823if (id == InvalidControlID)1824{1825fprintf(stderr, "Cannot map InvalidControlID\n");1826return (false);1827}18281829t = maptype(mapping.type);18301831if (t == MAP_NONE)1832{1833S9xUnmapID(id);1834return (true);1835}18361837if (t != MAP_POINTER)1838return (false);18391840t = maptype(S9xGetMapping(id).type);18411842if (t != MAP_NONE && t != MAP_POINTER)1843fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to pointer\n", id, maptypename(t));18441845if (id < PseudoPointerBase && id >= PseudoButtonBase)1846{1847fprintf(stderr, "ERROR: Refusing to map pseudo-button #%d as a pointer\n", id - PseudoButtonBase);1848return (false);1849}18501851if (mapping.type == S9xPointer)1852{1853if (mapping.input.pointer.aim_mouse0 && mouse[0].ID != InvalidControlID && mouse[0].ID != id)1854{1855fprintf(stderr, "ERROR: Rejecting attempt to control Mouse1 with two pointers\n");1856return (false);1857}18581859if (mapping.input.pointer.aim_mouse1 && mouse[1].ID != InvalidControlID && mouse[1].ID != id)1860{1861fprintf(stderr, "ERROR: Rejecting attempt to control Mouse2 with two pointers\n");1862return (false);1863}18641865if (mapping.input.pointer.aim_scope && superscope.ID != InvalidControlID && superscope.ID != id)1866{1867fprintf(stderr, "ERROR: Rejecting attempt to control SuperScope with two pointers\n");1868return (false);1869}18701871if (mapping.input.pointer.aim_justifier0 && justifier.ID[0] != InvalidControlID && justifier.ID[0] != id)1872{1873fprintf(stderr, "ERROR: Rejecting attempt to control Justifier1 with two pointers\n");1874return (false);1875}18761877if (mapping.input.pointer.aim_justifier1 && justifier.ID[1] != InvalidControlID && justifier.ID[1] != id)1878{1879fprintf(stderr, "ERROR: Rejecting attempt to control Justifier2 with two pointers\n");1880return (false);1881}1882}18831884S9xUnmapID(id);18851886if (poll)1887{1888if (id >= PseudoPointerBase)1889fprintf(stderr, "INFO: Ignoring attempt to set pseudo-pointer #%d to polling\n", id - PseudoPointerBase);1890else1891{1892switch (mapping.type)1893{1894case S9xPointer:1895if (mapping.input.pointer.aim_mouse0 ) pollmap[MOUSE0 ].insert(id);1896if (mapping.input.pointer.aim_mouse1 ) pollmap[MOUSE1 ].insert(id);1897if (mapping.input.pointer.aim_scope ) pollmap[SUPERSCOPE ].insert(id);1898if (mapping.input.pointer.aim_justifier0) pollmap[ONE_JUSTIFIER ].insert(id);1899if (mapping.input.pointer.aim_justifier1) pollmap[TWO_JUSTIFIERS].insert(id);1900break;19011902case S9xPointerPort:1903pollmap[POLL_ALL].insert(id);1904break;1905}1906}1907}19081909if (id >= PseudoPointerBase)1910pseudopointer[id - PseudoPointerBase].mapped = true;19111912keymap[id] = mapping;19131914if (mapping.input.pointer.aim_mouse0 ) mouse[0].ID = id;1915if (mapping.input.pointer.aim_mouse1 ) mouse[1].ID = id;1916if (mapping.input.pointer.aim_scope ) superscope.ID = id;1917if (mapping.input.pointer.aim_justifier0) justifier.ID[0] = id;1918if (mapping.input.pointer.aim_justifier1) justifier.ID[1] = id;19191920return (true);1921}19221923void S9xReportPointer (uint32 id, int16 x, int16 y)1924{1925if (keymap.count(id) == 0)1926return;19271928if (keymap[id].type == S9xNoMapping)1929return;19301931if (maptype(keymap[id].type) != MAP_POINTER)1932{1933fprintf(stderr, "ERROR: S9xReportPointer called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);1934return;1935}19361937S9xApplyCommand(keymap[id], x, y);1938}19391940bool S9xMapAxis (uint32 id, s9xcommand_t mapping, bool poll)1941{1942int t;19431944if (id == InvalidControlID)1945{1946fprintf(stderr, "Cannot map InvalidControlID\n");1947return (false);1948}19491950t = maptype(mapping.type);19511952if (t == MAP_NONE)1953{1954S9xUnmapID(id);1955return (true);1956}19571958if (t != MAP_AXIS)1959return (false);19601961t = maptype(S9xGetMapping(id).type);19621963if (t != MAP_NONE && t != MAP_AXIS)1964fprintf(stderr, "WARNING: Remapping ID 0x%08x from %s to axis\n", id, maptypename(t));19651966if (id >= PseudoPointerBase)1967{1968fprintf(stderr, "ERROR: Refusing to map pseudo-pointer #%d as an axis\n", id - PseudoPointerBase);1969return (false);1970}19711972t = -1;19731974if (poll)1975{1976switch (mapping.type)1977{1978case S9xAxisJoypad:1979t = JOYPAD0 + mapping.input.axis.joypad.idx;1980break;19811982case S9xAxisPseudopointer:1983case S9xAxisPseudobuttons:1984case S9xAxisPort:1985t=POLL_ALL;1986break;1987}1988}19891990S9xUnmapID(id);19911992keymap[id] = mapping;19931994if (t >= 0)1995pollmap[t].insert(id);19961997return (true);1998}19992000void S9xReportAxis (uint32 id, int16 value)2001{2002if (keymap.count(id) == 0)2003return;20042005if (keymap[id].type == S9xNoMapping)2006return;20072008if (maptype(keymap[id].type) != MAP_AXIS)2009{2010fprintf(stderr, "ERROR: S9xReportAxis called on %s ID 0x%08x\n", maptypename(maptype(keymap[id].type)), id);2011return;2012}20132014S9xApplyCommand(keymap[id], value, 0);2015}20162017static int32 ApplyMulti (s9xcommand_t *multi, int32 pos, int16 data1)2018{2019while (1)2020{2021if (multi[pos].multi_press == 3)2022return (-1);20232024if (multi[pos].type == S9xNoMapping)2025break;20262027if (multi[pos].multi_press)2028S9xApplyCommand(multi[pos], multi[pos].multi_press == 1, 0);2029else2030S9xApplyCommand(multi[pos], data1, 0);20312032pos++;2033}20342035return (pos + 1);2036}20372038void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)2039{2040int i;20412042switch (cmd.type)2043{2044case S9xNoMapping:2045return;20462047case S9xButtonJoypad:2048if (cmd.input.button.joypad.toggle)2049{2050if (!data1)2051return;20522053uint16 r = cmd.input.button.joypad.buttons;20542055if (cmd.input.button.joypad.turbo) joypad[cmd.input.button.joypad.idx].toggleturbo ^= r;2056if (cmd.input.button.joypad.sticky) joypad[cmd.input.button.joypad.idx].togglestick ^= r;2057}2058else2059{2060uint16 r, s, t, st;20612062s = t = st = 0;2063r = cmd.input.button.joypad.buttons;2064st = r & joypad[cmd.input.button.joypad.idx].togglestick & joypad[cmd.input.button.joypad.idx].toggleturbo;2065r ^= st;2066t = r & joypad[cmd.input.button.joypad.idx].toggleturbo;2067r ^= t;2068s = r & joypad[cmd.input.button.joypad.idx].togglestick;2069r ^= s;20702071if (cmd.input.button.joypad.turbo && cmd.input.button.joypad.sticky)2072{2073uint16 x = r; r = st; st = x;2074x = s; s = t; t = x;2075}2076else2077if (cmd.input.button.joypad.turbo)2078{2079uint16 x = r; r = t; t = x;2080x = s; s = st; st = x;2081}2082else2083if (cmd.input.button.joypad.sticky)2084{2085uint16 x = r; r = s; s = x;2086x = t; t = st; st = x;2087}20882089if (data1)2090{2091if (!Settings.UpAndDown && !S9xMoviePlaying()) // if up+down isn't allowed AND we are NOT playing a movie,2092{2093if (cmd.input.button.joypad.buttons & (SNES_LEFT_MASK | SNES_RIGHT_MASK))2094{2095// if we're pressing left or right, then unpress and unturbo them both first2096// so we don't end up hittnig left AND right accidentally.2097// Note though that the user can still do it on purpose, if Settings.UpAndDown = true.2098// This is a feature, look up glitches in tLoZ:aLttP to find out why.2099joypad[cmd.input.button.joypad.idx].buttons &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);2100joypad[cmd.input.button.joypad.idx].turbos &= ~(SNES_LEFT_MASK | SNES_RIGHT_MASK);2101}21022103if (cmd.input.button.joypad.buttons & (SNES_UP_MASK | SNES_DOWN_MASK))2104{2105// and ditto for up/down2106joypad[cmd.input.button.joypad.idx].buttons &= ~(SNES_UP_MASK | SNES_DOWN_MASK);2107joypad[cmd.input.button.joypad.idx].turbos &= ~(SNES_UP_MASK | SNES_DOWN_MASK);2108}2109}21102111joypad[cmd.input.button.joypad.idx].buttons |= r;2112joypad[cmd.input.button.joypad.idx].turbos |= t;2113joypad[cmd.input.button.joypad.idx].buttons ^= s;2114joypad[cmd.input.button.joypad.idx].buttons &= ~(joypad[cmd.input.button.joypad.idx].turbos & st);2115joypad[cmd.input.button.joypad.idx].turbos ^= st;2116}2117else2118{2119joypad[cmd.input.button.joypad.idx].buttons &= ~r;2120joypad[cmd.input.button.joypad.idx].buttons &= ~(joypad[cmd.input.button.joypad.idx].turbos & t);2121joypad[cmd.input.button.joypad.idx].turbos &= ~t;2122}2123}21242125return;21262127case S9xButtonMouse:2128i = 0;2129if (cmd.input.button.mouse.left ) i |= 0x40;2130if (cmd.input.button.mouse.right) i |= 0x80;21312132if (data1)2133mouse[cmd.input.button.mouse.idx].buttons |= i;2134else2135mouse[cmd.input.button.mouse.idx].buttons &= ~i;21362137return;21382139case S9xButtonSuperscope:2140i = 0;2141if (cmd.input.button.scope.fire ) i |= SUPERSCOPE_FIRE;2142if (cmd.input.button.scope.cursor ) i |= SUPERSCOPE_CURSOR;2143if (cmd.input.button.scope.pause ) i |= SUPERSCOPE_PAUSE;2144if (cmd.input.button.scope.aim_offscreen) i |= SUPERSCOPE_OFFSCREEN;21452146if (data1)2147{2148superscope.phys_buttons |= i;21492150if (cmd.input.button.scope.turbo)2151{2152superscope.phys_buttons ^= SUPERSCOPE_TURBO;21532154if (superscope.phys_buttons & SUPERSCOPE_TURBO)2155superscope.next_buttons |= superscope.phys_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);2156else2157superscope.next_buttons &= ~(SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR);2158}21592160superscope.next_buttons |= i & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR | SUPERSCOPE_PAUSE);21612162if (!S9xMovieActive()) // PPU modification during non-recordable command screws up movie synchronization2163if ((superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR)) && curcontrollers[1] == SUPERSCOPE && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))2164DoGunLatch(superscope.x, superscope.y);2165}2166else2167{2168superscope.phys_buttons &= ~i;2169superscope.next_buttons &= SUPERSCOPE_OFFSCREEN | ~i;2170}21712172return;21732174case S9xButtonJustifier:2175i = 0;2176if (cmd.input.button.justifier.trigger) i |= JUSTIFIER_TRIGGER;2177if (cmd.input.button.justifier.start ) i |= JUSTIFIER_START;2178if (cmd.input.button.justifier.aim_offscreen) justifier.offscreen[cmd.input.button.justifier.idx] = data1 ? 1 : 0;2179i >>= cmd.input.button.justifier.idx;21802181if (data1)2182justifier.buttons |= i;2183else2184justifier.buttons &= ~i;21852186return;21872188case S9xButtonCommand:2189if (((enum command_numbers) cmd.input.button.command) >= LAST_COMMAND)2190{2191fprintf(stderr, "Unknown command %04x\n", cmd.input.button.command);2192return;2193}21942195if (!data1)2196{2197switch (i = cmd.input.button.command)2198{2199case EmuTurbo:2200Settings.TurboMode = FALSE;2201break;2202}2203}2204else2205{2206switch ((enum command_numbers) (i = cmd.input.button.command))2207{2208case ExitEmu:2209S9xExit();2210break;22112212case Reset:2213S9xReset();2214break;22152216case SoftReset:2217#ifdef FANCY2218S9xMovieUpdateOnReset();2219if (S9xMoviePlaying())2220S9xMovieStop(TRUE);2221#endif2222S9xSoftReset();2223break;22242225case EmuTurbo:2226Settings.TurboMode = TRUE;2227break;22282229case ToggleEmuTurbo:2230Settings.TurboMode = !Settings.TurboMode;2231DisplayStateChange("Turbo mode", Settings.TurboMode);2232break;22332234case ClipWindows:2235Settings.DisableGraphicWindows = !Settings.DisableGraphicWindows;2236DisplayStateChange("Graphic clip windows", !Settings.DisableGraphicWindows);2237break;22382239case Debugger:2240#ifdef DEBUGGER2241CPU.Flags |= DEBUG_MODE_FLAG;2242#endif2243break;22442245case IncFrameRate:2246if (Settings.SkipFrames == AUTO_FRAMERATE)2247Settings.SkipFrames = 1;2248else2249if (Settings.SkipFrames < 10)2250Settings.SkipFrames++;22512252if (Settings.SkipFrames == AUTO_FRAMERATE)2253S9xSetInfoString("Auto frame skip");2254else2255{2256sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);2257S9xSetInfoString(buf);2258}22592260break;22612262case DecFrameRate:2263if (Settings.SkipFrames <= 1)2264Settings.SkipFrames = AUTO_FRAMERATE;2265else2266if (Settings.SkipFrames != AUTO_FRAMERATE)2267Settings.SkipFrames--;22682269if (Settings.SkipFrames == AUTO_FRAMERATE)2270S9xSetInfoString("Auto frame skip");2271else2272{2273sprintf(buf, "Frame skip: %d", Settings.SkipFrames - 1);2274S9xSetInfoString(buf);2275}22762277break;22782279case IncEmuTurbo:2280if (Settings.TurboSkipFrames < 20)2281Settings.TurboSkipFrames += 1;2282else2283if (Settings.TurboSkipFrames < 200)2284Settings.TurboSkipFrames += 5;2285sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);2286S9xSetInfoString(buf);2287break;22882289case DecEmuTurbo:2290if (Settings.TurboSkipFrames > 20)2291Settings.TurboSkipFrames -= 5;2292else2293if (Settings.TurboSkipFrames > 0)2294Settings.TurboSkipFrames -= 1;2295sprintf(buf, "Turbo frame skip: %d", Settings.TurboSkipFrames);2296S9xSetInfoString(buf);2297break;22982299case IncFrameTime: // Increase emulated frame time by 1ms2300Settings.FrameTime += 1000;2301sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);2302S9xSetInfoString(buf);2303break;23042305case DecFrameTime: // Decrease emulated frame time by 1ms2306if (Settings.FrameTime >= 1000)2307Settings.FrameTime -= 1000;2308sprintf(buf, "Emulated frame time: %dms", Settings.FrameTime / 1000);2309S9xSetInfoString(buf);2310break;23112312case IncTurboSpeed:2313if (turbo_time >= 120)2314break;2315turbo_time++;2316sprintf(buf, "Turbo speed: %d", turbo_time);2317S9xSetInfoString(buf);2318break;23192320case DecTurboSpeed:2321if (turbo_time <= 1)2322break;2323turbo_time--;2324sprintf(buf, "Turbo speed: %d", turbo_time);2325S9xSetInfoString(buf);2326break;23272328case LoadFreezeFile:2329#ifdef FANCY2330S9xUnfreezeGame(S9xChooseFilename(TRUE));2331#endif2332break;23332334case SaveFreezeFile:2335#ifdef FANCY2336S9xFreezeGame(S9xChooseFilename(FALSE));2337#endif2338break;23392340case LoadOopsFile:2341#ifdef FANCY2342{2343char filename[PATH_MAX + 1];2344char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];23452346_splitpath(Memory.ROMFilename, drive, dir, def, ext);2347snprintf(filename, PATH_MAX + 1, "%s%s%s.%.*s", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, _MAX_EXT - 1, "oops");23482349if (S9xUnfreezeGame(filename))2350{2351sprintf(buf, "%s.%.*s loaded", def, _MAX_EXT - 1, "oops");2352S9xSetInfoString (buf);2353}2354else2355S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Oops file not found");23562357break;2358}2359#else2360break;2361#endif2362case Pause:2363Settings.Paused = !Settings.Paused;2364DisplayStateChange("Pause", Settings.Paused);2365#if defined(NETPLAY_SUPPORT) && !defined(__WIN32__)2366S9xNPSendPause(Settings.Paused);2367#endif2368break;23692370case QuickLoad000:2371case QuickLoad001:2372case QuickLoad002:2373case QuickLoad003:2374case QuickLoad004:2375case QuickLoad005:2376case QuickLoad006:2377case QuickLoad007:2378case QuickLoad008:2379case QuickLoad009:2380case QuickLoad010:2381#ifdef FANCY2382{2383char filename[PATH_MAX + 1];2384char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];23852386_splitpath(Memory.ROMFilename, drive, dir, def, ext);2387snprintf(filename, PATH_MAX + 1, "%s%s%s.%03d", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, i - QuickLoad000);23882389if (S9xUnfreezeGame(filename))2390{2391sprintf(buf, "%s.%03d loaded", def, i - QuickLoad000);2392S9xSetInfoString(buf);2393}2394else2395S9xMessage(S9X_ERROR, S9X_FREEZE_FILE_NOT_FOUND, "Freeze file not found");23962397break;2398}2399#endif24002401case QuickSave000:2402case QuickSave001:2403case QuickSave002:2404case QuickSave003:2405case QuickSave004:2406case QuickSave005:2407case QuickSave006:2408case QuickSave007:2409case QuickSave008:2410case QuickSave009:2411case QuickSave010:2412#ifdef FANCY2413{2414char filename[PATH_MAX + 1];2415char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], def[_MAX_FNAME + 1], ext[_MAX_EXT + 1];24162417_splitpath(Memory.ROMFilename, drive, dir, def, ext);2418snprintf(filename, PATH_MAX + 1, "%s%s%s.%03d", S9xGetDirectory(SNAPSHOT_DIR), SLASH_STR, def, i - QuickSave000);24192420sprintf(buf, "%s.%03d saved", def, i - QuickSave000);2421S9xSetInfoString(buf);24222423S9xFreezeGame(filename);2424break;2425}2426#else2427break;2428#endif2429case SaveSPC:2430#ifdef FANCY2431S9xDumpSPCSnapshot();2432#endif2433break;24342435case Screenshot:2436#ifdef FANCY2437Settings.TakeScreenshot = TRUE;2438#endif2439break;24402441case SoundChannel0:2442case SoundChannel1:2443case SoundChannel2:2444case SoundChannel3:2445case SoundChannel4:2446case SoundChannel5:2447case SoundChannel6:2448case SoundChannel7:2449S9xToggleSoundChannel(i - SoundChannel0);2450sprintf(buf, "Sound channel %d toggled", i - SoundChannel0);2451S9xSetInfoString(buf);2452break;24532454case SoundChannelsOn:2455S9xToggleSoundChannel(8);2456S9xSetInfoString("All sound channels on");2457break;24582459case ToggleBG0:2460Settings.BG_Forced ^= 1;2461DisplayStateChange("BG#0", !(Settings.BG_Forced & 1));2462break;24632464case ToggleBG1:2465Settings.BG_Forced ^= 2;2466DisplayStateChange("BG#1", !(Settings.BG_Forced & 2));2467break;24682469case ToggleBG2:2470Settings.BG_Forced ^= 4;2471DisplayStateChange("BG#2", !(Settings.BG_Forced & 4));2472break;24732474case ToggleBG3:2475Settings.BG_Forced ^= 8;2476DisplayStateChange("BG#3", !(Settings.BG_Forced & 8));2477break;24782479case ToggleSprites:2480Settings.BG_Forced ^= 16;2481DisplayStateChange("Sprites", !(Settings.BG_Forced & 16));2482break;24832484case ToggleTransparency:2485Settings.Transparency = !Settings.Transparency;2486DisplayStateChange("Transparency effects", Settings.Transparency);2487break;24882489case BeginRecordingMovie:2490#ifdef FANCY2491if (S9xMovieActive())2492S9xMovieStop(FALSE);2493S9xMovieCreate(S9xChooseMovieFilename(FALSE), 0xFF, MOVIE_OPT_FROM_RESET, NULL, 0);2494#endif2495break;24962497case LoadMovie:2498#ifdef FANCY2499if (S9xMovieActive())2500S9xMovieStop(FALSE);2501S9xMovieOpen(S9xChooseMovieFilename(TRUE), FALSE);2502#endif2503break;25042505case EndRecordingMovie:2506#ifdef FANCY2507if (S9xMovieActive())2508S9xMovieStop(FALSE);2509break;2510#endif2511case SwapJoypads:2512if ((curcontrollers[0] != NONE && !(curcontrollers[0] >= JOYPAD0 && curcontrollers[0] <= JOYPAD7)))2513{2514S9xSetInfoString("Cannot swap pads: port 1 is not a joypad");2515break;2516}25172518if ((curcontrollers[1] != NONE && !(curcontrollers[1] >= JOYPAD0 && curcontrollers[1] <= JOYPAD7)))2519{2520S9xSetInfoString("Cannot swap pads: port 2 is not a joypad");2521break;2522}25232524newcontrollers[1] = curcontrollers[0];2525newcontrollers[0] = curcontrollers[1];25262527strcpy(buf, "Swap pads: P1=");2528i = 14;2529if (newcontrollers[0] == NONE)2530{2531strcpy(buf + i, "<none>");2532i += 6;2533}2534else2535{2536sprintf(buf + i, "Joypad%d", newcontrollers[0] - JOYPAD0 + 1);2537i += 7;2538}25392540strcpy(buf + i, " P2=");2541i += 4;2542if (newcontrollers[1] == NONE)2543strcpy(buf + i, "<none>");2544else2545sprintf(buf + i, "Joypad%d", newcontrollers[1] - JOYPAD0 + 1);25462547S9xSetInfoString(buf);2548break;25492550case SeekToFrame:2551#ifdef FANCY2552if (S9xMovieActive())2553{2554sprintf(buf, "Select frame number (current: %d)", S9xMovieGetFrameCounter());2555const char *frameno = S9xStringInput(buf);2556if (!frameno)2557return;25582559int frameDest = atoi(frameno);2560if (frameDest > 0 && frameDest > (int) S9xMovieGetFrameCounter())2561{2562int distance = frameDest - S9xMovieGetFrameCounter();2563Settings.HighSpeedSeek = distance;2564}2565}2566#endif2567break;25682569case LAST_COMMAND:2570break;2571}2572}25732574return;25752576case S9xPointer:2577if (cmd.input.pointer.aim_mouse0)2578{2579mouse[0].cur_x = data1;2580mouse[0].cur_y = data2;2581}25822583if (cmd.input.pointer.aim_mouse1)2584{2585mouse[1].cur_x = data1;2586mouse[1].cur_y = data2;2587}25882589if (cmd.input.pointer.aim_scope)2590{2591superscope.x = data1;2592superscope.y = data2;2593}25942595if (cmd.input.pointer.aim_justifier0)2596{2597justifier.x[0] = data1;2598justifier.y[0] = data2;2599}26002601if (cmd.input.pointer.aim_justifier1)2602{2603justifier.x[1] = data1;2604justifier.y[1] = data2;2605}26062607return;26082609case S9xButtonPseudopointer:2610if (data1)2611{2612if (cmd.input.button.pointer.UD)2613{2614if (!pseudopointer[cmd.input.button.pointer.idx].V_adj)2615pseudopointer[cmd.input.button.pointer.idx].V_adj = cmd.input.button.pointer.UD * ptrspeeds[cmd.input.button.pointer.speed_type];2616pseudopointer[cmd.input.button.pointer.idx].V_var = (cmd.input.button.pointer.speed_type == 0);2617}26182619if (cmd.input.button.pointer.LR)2620{2621if (!pseudopointer[cmd.input.button.pointer.idx].H_adj)2622pseudopointer[cmd.input.button.pointer.idx].H_adj = cmd.input.button.pointer.LR * ptrspeeds[cmd.input.button.pointer.speed_type];2623pseudopointer[cmd.input.button.pointer.idx].H_var = (cmd.input.button.pointer.speed_type == 0);2624}2625}2626else2627{2628if (cmd.input.button.pointer.UD)2629{2630pseudopointer[cmd.input.button.pointer.idx].V_adj = 0;2631pseudopointer[cmd.input.button.pointer.idx].V_var = false;2632}26332634if (cmd.input.button.pointer.LR)2635{2636pseudopointer[cmd.input.button.pointer.idx].H_adj = 0;2637pseudopointer[cmd.input.button.pointer.idx].H_var = false;2638}2639}26402641return;26422643case S9xAxisJoypad:2644{2645uint16 pos, neg;26462647switch (cmd.input.axis.joypad.axis)2648{2649case 0: neg = SNES_LEFT_MASK; pos = SNES_RIGHT_MASK; break;2650case 1: neg = SNES_UP_MASK; pos = SNES_DOWN_MASK; break;2651case 2: neg = SNES_Y_MASK; pos = SNES_A_MASK; break;2652case 3: neg = SNES_X_MASK; pos = SNES_B_MASK; break;2653case 4: neg = SNES_TL_MASK; pos = SNES_TR_MASK; break;2654default: return;2655}26562657if (cmd.input.axis.joypad.invert)2658data1 = -data1;26592660uint16 p, r;26612662p = r = 0;2663if (data1 > ((cmd.input.axis.joypad.threshold + 1) * 127))2664p |= pos;2665else2666r |= pos;26672668if (data1 <= ((cmd.input.axis.joypad.threshold + 1) * -127))2669p |= neg;2670else2671r |= neg;26722673joypad[cmd.input.axis.joypad.idx].buttons |= p;2674joypad[cmd.input.axis.joypad.idx].buttons &= ~r;2675joypad[cmd.input.axis.joypad.idx].turbos &= ~(p | r);26762677return;2678}26792680case S9xAxisPseudopointer:2681if (data1 == 0)2682{2683if (cmd.input.axis.pointer.HV)2684{2685pseudopointer[cmd.input.axis.pointer.idx].V_adj = 0;2686pseudopointer[cmd.input.axis.pointer.idx].V_var = false;2687}2688else2689{2690pseudopointer[cmd.input.axis.pointer.idx].H_adj = 0;2691pseudopointer[cmd.input.axis.pointer.idx].H_var = false;2692}2693}2694else2695{2696if (cmd.input.axis.pointer.invert)2697data1 = -data1;26982699if (cmd.input.axis.pointer.HV)2700{2701if (!pseudopointer[cmd.input.axis.pointer.idx].V_adj)2702pseudopointer[cmd.input.axis.pointer.idx].V_adj = (int16) ((int32) data1 * ptrspeeds[cmd.input.axis.pointer.speed_type] / 32767);2703pseudopointer[cmd.input.axis.pointer.idx].V_var = (cmd.input.axis.pointer.speed_type == 0);2704}2705else2706{2707if (!pseudopointer[cmd.input.axis.pointer.idx].H_adj)2708pseudopointer[cmd.input.axis.pointer.idx].H_adj = (int16) ((int32) data1 * ptrspeeds[cmd.input.axis.pointer.speed_type] / 32767);2709pseudopointer[cmd.input.axis.pointer.idx].H_var = (cmd.input.axis.pointer.speed_type == 0);2710}2711}27122713return;27142715case S9xAxisPseudobuttons:2716if (data1 > ((cmd.input.axis.button.threshold + 1) * 127))2717{2718if (!pseudobuttons[cmd.input.axis.button.posbutton])2719{2720pseudobuttons[cmd.input.axis.button.posbutton] = 1;2721S9xReportButton(PseudoButtonBase + cmd.input.axis.button.posbutton, true);2722}2723}2724else2725{2726if (pseudobuttons[cmd.input.axis.button.posbutton])2727{2728pseudobuttons[cmd.input.axis.button.posbutton] = 0;2729S9xReportButton(PseudoButtonBase + cmd.input.axis.button.posbutton, false);2730}2731}27322733if (data1 <= ((cmd.input.axis.button.threshold + 1) * -127))2734{2735if (!pseudobuttons[cmd.input.axis.button.negbutton])2736{2737pseudobuttons[cmd.input.axis.button.negbutton] = 1;2738S9xReportButton(PseudoButtonBase + cmd.input.axis.button.negbutton, true);2739}2740}2741else2742{2743if (pseudobuttons[cmd.input.axis.button.negbutton])2744{2745pseudobuttons[cmd.input.axis.button.negbutton] = 0;2746S9xReportButton(PseudoButtonBase + cmd.input.axis.button.negbutton, false);2747}2748}27492750return;27512752case S9xButtonPort:2753case S9xAxisPort:2754case S9xPointerPort:2755S9xHandlePortCommand(cmd, data1, data2);2756return;27572758case S9xButtonMulti:2759if (cmd.input.button.multi_idx >= (int) multis.size())2760return;27612762if (multis[cmd.input.button.multi_idx]->multi_press && !data1)2763return;27642765i = ApplyMulti(multis[cmd.input.button.multi_idx], 0, data1);2766if (i >= 0)2767{2768struct exemulti *e = new struct exemulti;2769e->pos = i;2770e->data1 = data1 != 0;2771e->script = multis[cmd.input.button.multi_idx];2772exemultis.insert(e);2773}27742775return;27762777default:2778fprintf(stderr, "WARNING: Unknown command type %d\n", cmd.type);2779return;2780}2781}27822783static void do_polling (int mp)2784{2785set<uint32>::iterator itr;2786#ifdef FANCY2787if (S9xMoviePlaying())2788return;2789#endif2790if (pollmap[mp].empty())2791return;27922793for (itr = pollmap[mp].begin(); itr != pollmap[mp].end(); itr++)2794{2795switch (maptype(keymap[*itr].type))2796{2797case MAP_BUTTON:2798{2799bool pressed;2800if (S9xPollButton(*itr, &pressed))2801S9xReportButton(*itr, pressed);2802break;2803}28042805case MAP_AXIS:2806{2807int16 value;2808if (S9xPollAxis(*itr, &value))2809S9xReportAxis(*itr, value);2810break;2811}28122813case MAP_POINTER:2814{2815int16 x, y;2816if (S9xPollPointer(*itr, &x, &y))2817S9xReportPointer(*itr, x, y);2818break;2819}28202821default:2822break;2823}2824}2825}28262827static void UpdatePolledMouse (int i)2828{2829int16 j;28302831j = mouse[i - MOUSE0].cur_x - mouse[i - MOUSE0].old_x;28322833if (j < -127)2834{2835mouse[i - MOUSE0].delta_x = 0xff;2836mouse[i - MOUSE0].old_x -= 127;2837}2838else2839if (j < 0)2840{2841mouse[i - MOUSE0].delta_x = 0x80 | -j;2842mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;2843}2844else2845if (j > 127)2846{2847mouse[i - MOUSE0].delta_x = 0x7f;2848mouse[i - MOUSE0].old_x += 127;2849}2850else2851{2852mouse[i - MOUSE0].delta_x = (uint8) j;2853mouse[i - MOUSE0].old_x = mouse[i - MOUSE0].cur_x;2854}28552856j = mouse[i - MOUSE0].cur_y - mouse[i - MOUSE0].old_y;28572858if (j < -127)2859{2860mouse[i - MOUSE0].delta_y = 0xff;2861mouse[i - MOUSE0].old_y -= 127;2862}2863else2864if (j < 0)2865{2866mouse[i - MOUSE0].delta_y = 0x80 | -j;2867mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;2868}2869else2870if (j > 127)2871{2872mouse[i - MOUSE0].delta_y = 0x7f;2873mouse[i - MOUSE0].old_y += 127;2874}2875else2876{2877mouse[i - MOUSE0].delta_y = (uint8) j;2878mouse[i - MOUSE0].old_y = mouse[i - MOUSE0].cur_y;2879}2880}28812882void S9xSetJoypadLatch (bool latch)2883{2884if (!latch && FLAG_LATCH)2885{2886// 1 written, 'plug in' new controllers now2887curcontrollers[0] = newcontrollers[0];2888curcontrollers[1] = newcontrollers[1];2889}28902891if (latch && !FLAG_LATCH)2892{2893int i;28942895for (int n = 0; n < 2; n++)2896{2897for (int j = 0; j < 2; j++)2898read_idx[n][j] = 0;28992900switch (i = curcontrollers[n])2901{2902case MP5:2903for (int j = 0, k = mp5[n].pads[j]; j < 4; k = mp5[n].pads[++j])2904{2905if (k == NONE)2906continue;2907do_polling(k);2908}29092910break;29112912case JOYPAD0:2913case JOYPAD1:2914case JOYPAD2:2915case JOYPAD3:2916case JOYPAD4:2917case JOYPAD5:2918case JOYPAD6:2919case JOYPAD7:2920do_polling(i);2921break;29222923case MOUSE0:2924case MOUSE1:2925do_polling(i);2926if (!S9xMoviePlaying())2927UpdatePolledMouse(i);2928break;29292930case SUPERSCOPE:2931if (superscope.next_buttons & SUPERSCOPE_FIRE)2932{2933superscope.next_buttons &= ~SUPERSCOPE_TURBO;2934superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_TURBO;2935}29362937if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))2938{2939superscope.next_buttons &= ~SUPERSCOPE_OFFSCREEN;2940superscope.next_buttons |= superscope.phys_buttons & SUPERSCOPE_OFFSCREEN;2941}29422943superscope.read_buttons = superscope.next_buttons;29442945superscope.next_buttons &= ~SUPERSCOPE_PAUSE;2946if (!(superscope.phys_buttons & SUPERSCOPE_TURBO))2947superscope.next_buttons &= ~(SUPERSCOPE_CURSOR | SUPERSCOPE_FIRE);29482949do_polling(i);2950break;29512952case TWO_JUSTIFIERS:2953do_polling(TWO_JUSTIFIERS);2954// fall through29552956case ONE_JUSTIFIER:2957justifier.buttons ^= JUSTIFIER_SELECT;2958do_polling(ONE_JUSTIFIER);2959break;29602961default:2962break;2963}2964}2965}29662967FLAG_LATCH = latch;2968}29692970uint8 S9xReadJOYSERn (int n)2971{2972int i, j, r;29732974if (n > 1)2975n -= 0x4016;2976assert(n == 0 || n == 1);29772978uint8 bits = (OpenBus & ~3) | ((n == 1) ? 0x1c : 0);29792980if (FLAG_LATCH)2981{2982switch (i = curcontrollers[n])2983{2984case MP5:2985return (bits | 2);29862987case JOYPAD0:2988case JOYPAD1:2989case JOYPAD2:2990case JOYPAD3:2991case JOYPAD4:2992case JOYPAD5:2993case JOYPAD6:2994case JOYPAD7:2995return (bits | ((joypad[i - JOYPAD0].buttons & 0x8000) ? 1 : 0));29962997case MOUSE0:2998case MOUSE1:2999mouse[i - MOUSE0].buttons += 0x10;3000if ((mouse[i - MOUSE0].buttons & 0x30) == 0x30)3001mouse[i - MOUSE0].buttons &= 0xcf;3002return (bits);30033004case SUPERSCOPE:3005return (bits | ((superscope.read_buttons & 0x80) ? 1 : 0));30063007case ONE_JUSTIFIER:3008case TWO_JUSTIFIERS:3009return (bits);30103011default:3012return (bits);3013}3014}3015else3016{3017switch (i = curcontrollers[n])3018{3019case MP5:3020r = read_idx[n][FLAG_IOBIT(n) ? 0 : 1]++;3021j = FLAG_IOBIT(n) ? 0 : 2;30223023for (i = 0; i < 2; i++, j++)3024{3025if (mp5[n].pads[j] == NONE)3026continue;3027if (r >= 16)3028bits |= 1 << i;3029else3030bits |= ((joypad[mp5[n].pads[j] - JOYPAD0].buttons & (0x8000 >> r)) ? 1 : 0) << i;3031}30323033return (bits);30343035case JOYPAD0:3036case JOYPAD1:3037case JOYPAD2:3038case JOYPAD3:3039case JOYPAD4:3040case JOYPAD5:3041case JOYPAD6:3042case JOYPAD7:3043if (read_idx[n][0] >= 16)3044{3045read_idx[n][0]++;3046return (bits | 1);3047}3048else3049return (bits | ((joypad[i - JOYPAD0].buttons & (0x8000 >> read_idx[n][0]++)) ? 1 : 0));30503051case MOUSE0:3052case MOUSE1:3053if (read_idx[n][0] < 8)3054{3055read_idx[n][0]++;3056return (bits);3057}3058else3059if (read_idx[n][0] < 16)3060return (bits | ((mouse[i - MOUSE0].buttons & (0x8000 >> read_idx[n][0]++)) ? 1 : 0));3061else3062if (read_idx[n][0] < 24)3063return (bits | ((mouse[i - MOUSE0].delta_y & (0x800000 >> read_idx[n][0]++)) ? 1 : 0));3064else3065if (read_idx[n][0] < 32)3066return (bits | ((mouse[i - MOUSE0].delta_x & (0x80000000 >> read_idx[n][0]++)) ? 1 : 0));3067else3068{3069read_idx[n][0]++;3070return (bits | 1);3071}30723073case SUPERSCOPE:3074if (read_idx[n][0] < 8)3075return (bits | ((superscope.read_buttons & (0x80 >> read_idx[n][0]++)) ? 1 : 0));3076else3077{3078read_idx[n][0]++;3079return (bits | 1);3080}30813082case ONE_JUSTIFIER:3083if (read_idx[n][0] < 24)3084return (bits | ((0xaa7000 >> read_idx[n][0]++) & 1));3085else3086if (read_idx[n][0] < 32)3087return (bits | ((justifier.buttons & (JUSTIFIER_TRIGGER | JUSTIFIER_START | JUSTIFIER_SELECT) & (0x80000000 >> read_idx[n][0]++)) ? 1 : 0));3088else3089{3090read_idx[n][0]++;3091return (bits | 1);3092}30933094case TWO_JUSTIFIERS:3095if (read_idx[n][0] < 24)3096return (bits | ((0xaa7000 >> read_idx[n][0]++) & 1));3097else3098if (read_idx[n][0] < 32)3099return (bits | ((justifier.buttons & (0x80000000 >> read_idx[n][0]++)) ? 1 : 0));3100else3101{3102read_idx[n][0]++;3103return (bits | 1);3104}31053106default:3107read_idx[n][0]++;3108return (bits);3109}3110}3111}31123113void S9xDoAutoJoypad (void)3114{3115int i, j;31163117S9xSetJoypadLatch(1);3118S9xSetJoypadLatch(0);3119#ifdef FANCY3120S9xMovieUpdate(false);3121#endif3122for (int n = 0; n < 2; n++)3123{3124switch (i = curcontrollers[n])3125{3126case MP5:3127j = FLAG_IOBIT(n) ? 0 : 2;3128for (i = 0; i < 2; i++, j++)3129{3130if (mp5[n].pads[j] == NONE)3131WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, 0);3132else3133WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2 + i * 4, joypad[mp5[n].pads[j] - JOYPAD0].buttons);3134}31353136read_idx[n][FLAG_IOBIT(n) ? 0 : 1] = 16;3137break;31383139case JOYPAD0:3140case JOYPAD1:3141case JOYPAD2:3142case JOYPAD3:3143case JOYPAD4:3144case JOYPAD5:3145case JOYPAD6:3146case JOYPAD7:3147read_idx[n][0] = 16;3148WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, joypad[i - JOYPAD0].buttons);3149WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);3150break;31513152case MOUSE0:3153case MOUSE1:3154read_idx[n][0] = 16;3155WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, mouse[i - MOUSE0].buttons);3156WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);3157break;31583159case SUPERSCOPE:3160read_idx[n][0] = 16;3161Memory.FillRAM[0x4218 + n * 2] = 0xff;3162Memory.FillRAM[0x4219 + n * 2] = superscope.read_buttons;3163WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);3164break;31653166case ONE_JUSTIFIER:3167case TWO_JUSTIFIERS:3168read_idx[n][0] = 16;3169WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0x000e);3170WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);3171break;31723173default:3174WRITE_WORD(Memory.FillRAM + 0x4218 + n * 2, 0);3175WRITE_WORD(Memory.FillRAM + 0x421c + n * 2, 0);3176break;3177}3178}3179}31803181void S9xControlEOF (void)3182{3183#ifdef FANCY3184struct crosshair *c;3185#endif3186int i, j;31873188PPU.GunVLatch = 1000; // i.e., never latch3189PPU.GunHLatch = 0;31903191for (int n = 0; n < 2; n++)3192{3193switch (i = curcontrollers[n])3194{3195case MP5:3196for (j = 0, i = mp5[n].pads[j]; j < 4; i = mp5[n].pads[++j])3197{3198if (i == NONE)3199continue;32003201if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)3202{3203joypad[i - JOYPAD0].turbo_ct = 0;3204joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;3205}3206}32073208break;32093210case JOYPAD0:3211case JOYPAD1:3212case JOYPAD2:3213case JOYPAD3:3214case JOYPAD4:3215case JOYPAD5:3216case JOYPAD6:3217case JOYPAD7:3218if (++joypad[i - JOYPAD0].turbo_ct >= turbo_time)3219{3220joypad[i - JOYPAD0].turbo_ct = 0;3221joypad[i - JOYPAD0].buttons ^= joypad[i - JOYPAD0].turbos;3222}32233224break;3225#ifdef FANCY3226case MOUSE0:3227case MOUSE1:3228c = &mouse[i - MOUSE0].crosshair;3229if (IPPU.RenderThisFrame)3230S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, mouse[i - MOUSE0].cur_x, mouse[i - MOUSE0].cur_y);3231break;32323233case SUPERSCOPE:3234if (n == 1 && !(superscope.phys_buttons & SUPERSCOPE_OFFSCREEN))3235{3236if (superscope.next_buttons & (SUPERSCOPE_FIRE | SUPERSCOPE_CURSOR))3237DoGunLatch(superscope.x, superscope.y);32383239c = &superscope.crosshair;3240if (IPPU.RenderThisFrame)3241S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, superscope.x, superscope.y);3242}32433244break;32453246case TWO_JUSTIFIERS:3247if (n == 1 && !justifier.offscreen[1])3248{3249c = &justifier.crosshair[1];3250if (IPPU.RenderThisFrame)3251S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[1], justifier.y[1]);3252}32533254i = (justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0;3255goto do_justifier;32563257case ONE_JUSTIFIER:3258i = (justifier.buttons & JUSTIFIER_SELECT) ? -1 : 0;32593260do_justifier:3261if (n == 1)3262{3263if (i >= 0 && !justifier.offscreen[i])3264DoGunLatch(justifier.x[i], justifier.y[i]);32653266if (!justifier.offscreen[0])3267{3268c = &justifier.crosshair[0];3269if (IPPU.RenderThisFrame)3270S9xDrawCrosshair(S9xGetCrosshair(c->img), c->fg, c->bg, justifier.x[0], justifier.y[0]);3271}3272}32733274break;3275#endif3276default:3277break;3278}3279}32803281for (int n = 0; n < 8; n++)3282{3283if (!pseudopointer[n].mapped)3284continue;32853286if (pseudopointer[n].H_adj)3287{3288pseudopointer[n].x += pseudopointer[n].H_adj;3289if (pseudopointer[n].x < 0)3290pseudopointer[n].x = 0;3291else3292if (pseudopointer[n].x > 255)3293pseudopointer[n].x = 255;32943295if (pseudopointer[n].H_var)3296{3297if (pseudopointer[n].H_adj < 0)3298{3299if (pseudopointer[n].H_adj > -ptrspeeds[3])3300pseudopointer[n].H_adj--;3301}3302else3303{3304if (pseudopointer[n].H_adj < ptrspeeds[3])3305pseudopointer[n].H_adj++;3306}3307}3308}33093310if (pseudopointer[n].V_adj)3311{3312pseudopointer[n].y += pseudopointer[n].V_adj;3313if (pseudopointer[n].y < 0)3314pseudopointer[n].y = 0;3315else3316if (pseudopointer[n].y > PPU.ScreenHeight - 1)3317pseudopointer[n].y = PPU.ScreenHeight - 1;33183319if (pseudopointer[n].V_var)3320{3321if (pseudopointer[n].V_adj < 0)3322{3323if (pseudopointer[n].V_adj > -ptrspeeds[3])3324pseudopointer[n].V_adj--;3325}3326else3327{3328if (pseudopointer[n].V_adj < ptrspeeds[3])3329pseudopointer[n].V_adj++;3330}3331}3332}33333334S9xReportPointer(PseudoPointerBase + n, pseudopointer[n].x, pseudopointer[n].y);3335}33363337set<struct exemulti *>::iterator it, jt;33383339for (it = exemultis.begin(); it != exemultis.end(); it++)3340{3341i = ApplyMulti((*it)->script, (*it)->pos, (*it)->data1);33423343if (i >= 0)3344(*it)->pos = i;3345else3346{3347jt = it;3348it--;3349delete *jt;3350exemultis.erase(jt);3351}3352}33533354do_polling(POLL_ALL);3355#ifdef FANCY3356S9xMovieUpdate();3357#endif3358pad_read_last = pad_read;3359pad_read = false;3360}3361#ifdef FANCY3362void S9xSetControllerCrosshair (enum crosscontrols ctl, int8 idx, const char *fg, const char *bg)3363{3364struct crosshair *c;3365int8 fgcolor = -1, bgcolor = -1;3366int i, j;33673368if (idx < -1 || idx > 31)3369{3370fprintf(stderr, "S9xSetControllerCrosshair() called with invalid index\n");3371return;3372}33733374switch (ctl)3375{3376case X_MOUSE1: c = &mouse[0].crosshair; break;3377case X_MOUSE2: c = &mouse[1].crosshair; break;3378case X_SUPERSCOPE: c = &superscope.crosshair; break;3379case X_JUSTIFIER1: c = &justifier.crosshair[0]; break;3380case X_JUSTIFIER2: c = &justifier.crosshair[1]; break;3381default:3382fprintf(stderr, "S9xSetControllerCrosshair() called with an invalid controller ID %d\n", ctl);3383return;3384}33853386if (fg)3387{3388fgcolor = 0;3389if (*fg == 't')3390{3391fg++;3392fgcolor = 16;3393}33943395for (i = 0; i < 16; i++)3396{3397for (j = 0; color_names[i][j] && fg[j] == color_names[i][j]; j++) ;3398if (isalnum(fg[j]))3399continue;34003401if (!color_names[i][j])3402break;3403}34043405fgcolor |= i;3406if (i > 15 || fgcolor == 16)3407{3408fprintf(stderr, "S9xSetControllerCrosshair() called with invalid fgcolor\n");3409return;3410}3411}34123413if (bg)3414{3415bgcolor = 0;3416if (*bg == 't')3417{3418bg++;3419bgcolor = 16;3420}34213422for (i = 0; i < 16; i++)3423{3424for (j = 0; color_names[i][j] && bg[j] == color_names[i][j]; j++) ;3425if (isalnum(bg[j]))3426continue;34273428if (!color_names[i][j])3429break;3430}34313432bgcolor |= i;3433if (i > 15 || bgcolor == 16)3434{3435fprintf(stderr, "S9xSetControllerCrosshair() called with invalid bgcolor\n");3436return;3437}3438}34393440if (idx != -1)3441{3442c->set |= 1;3443c->img = idx;3444}34453446if (fgcolor != -1)3447{3448c->set |= 2;3449c->fg = fgcolor;3450}34513452if (bgcolor != -1)3453{3454c->set |= 4;3455c->bg = bgcolor;3456}3457}34583459void S9xGetControllerCrosshair (enum crosscontrols ctl, int8 *idx, const char **fg, const char **bg)3460{3461struct crosshair *c;34623463switch (ctl)3464{3465case X_MOUSE1: c = &mouse[0].crosshair; break;3466case X_MOUSE2: c = &mouse[1].crosshair; break;3467case X_SUPERSCOPE: c = &superscope.crosshair; break;3468case X_JUSTIFIER1: c = &justifier.crosshair[0]; break;3469case X_JUSTIFIER2: c = &justifier.crosshair[1]; break;3470default:3471fprintf(stderr, "S9xGetControllerCrosshair() called with an invalid controller ID %d\n", ctl);3472return;3473}34743475if (idx)3476*idx = c->img;34773478if (fg)3479*fg = color_names[c->fg];34803481if (bg)3482*bg = color_names[c->bg];3483}3484#endif3485void S9xControlPreSaveState (struct SControlSnapshot *s)3486{3487ZeroMemory(s, sizeof(*s));3488s->ver = 3;34893490for (int j = 0; j < 2; j++)3491{3492s->port1_read_idx[j] = read_idx[0][j];3493s->port2_read_idx[j] = read_idx[1][j];3494}34953496for (int j = 0; j < 2; j++)3497s->mouse_speed[j] = (mouse[j].buttons & 0x30) >> 4;34983499s->justifier_select = ((justifier.buttons & JUSTIFIER_SELECT) ? 1 : 0);35003501#define COPY(x) { memcpy((char *) s->internal + i, &(x), sizeof(x)); i += sizeof(x); }35023503int i = 0;35043505for (int j = 0; j < 8; j++)3506COPY(joypad[j].buttons);35073508for (int j = 0; j < 2; j++)3509{3510COPY(mouse[j].delta_x);3511COPY(mouse[j].delta_y);3512COPY(mouse[j].old_x);3513COPY(mouse[j].old_y);3514COPY(mouse[j].cur_x);3515COPY(mouse[j].cur_y);3516COPY(mouse[j].buttons);3517}35183519COPY(superscope.x);3520COPY(superscope.y);3521COPY(superscope.phys_buttons);3522COPY(superscope.next_buttons);3523COPY(superscope.read_buttons);35243525for (int j = 0; j < 2; j++)3526COPY(justifier.x[j]);3527for (int j = 0; j < 2; j++)3528COPY(justifier.y[j]);3529COPY(justifier.buttons);3530for (int j = 0; j < 2; j++)3531COPY(justifier.offscreen[j]);35323533for (int j = 0; j < 2; j++)3534for (int k = 0; k < 2; k++)3535COPY(mp5[j].pads[k]);35363537assert(i == sizeof(s->internal));35383539#undef COPY35403541s->pad_read = pad_read;3542s->pad_read_last = pad_read_last;3543}35443545void S9xControlPostLoadState (struct SControlSnapshot *s)3546{3547if (curcontrollers[0] == MP5 && s->ver < 1)3548{3549// Crap. Old snes9x didn't support this.3550S9xMessage(S9X_WARNING, S9X_FREEZE_FILE_INFO, "Old savestate has no support for MP5 in port 1.");3551newcontrollers[0] = curcontrollers[0];3552curcontrollers[0] = mp5[0].pads[0];3553}35543555for (int j = 0; j < 2; j++)3556{3557read_idx[0][j] = s->port1_read_idx[j];3558read_idx[1][j] = s->port2_read_idx[j];3559}35603561for (int j = 0; j < 2; j++)3562mouse[j].buttons |= (s->mouse_speed[j] & 3) << 4;35633564if (s->justifier_select & 1)3565justifier.buttons |= JUSTIFIER_SELECT;3566else3567justifier.buttons &= ~JUSTIFIER_SELECT;35683569FLAG_LATCH = (Memory.FillRAM[0x4016] & 1) == 1;35703571if (s->ver > 1)3572{3573#define COPY(x) { memcpy(&(x), (char *) s->internal + i, sizeof(x)); i += sizeof(x); }35743575int i = 0;35763577for (int j = 0; j < 8; j++)3578COPY(joypad[j].buttons);35793580for (int j = 0; j < 2; j++)3581{3582COPY(mouse[j].delta_x);3583COPY(mouse[j].delta_y);3584COPY(mouse[j].old_x);3585COPY(mouse[j].old_y);3586COPY(mouse[j].cur_x);3587COPY(mouse[j].cur_y);3588COPY(mouse[j].buttons);3589}35903591COPY(superscope.x);3592COPY(superscope.y);3593COPY(superscope.phys_buttons);3594COPY(superscope.next_buttons);3595COPY(superscope.read_buttons);35963597for (int j = 0; j < 2; j++)3598COPY(justifier.x[j]);3599for (int j = 0; j < 2; j++)3600COPY(justifier.y[j]);3601COPY(justifier.buttons);3602for (int j = 0; j < 2; j++)3603COPY(justifier.offscreen[j]);3604for (int j = 0; j < 2; j++)3605for (int k = 0; k < 2; k++)3606COPY(mp5[j].pads[k]);36073608assert(i == sizeof(s->internal));36093610#undef COPY3611}36123613if (s->ver > 2)3614{3615pad_read = s->pad_read;3616pad_read_last = s->pad_read_last;3617}3618}3619#ifdef FANCY3620uint16 MovieGetJoypad (int i)3621{3622if (i < 0 || i > 7)3623return (0);36243625return (joypad[i].buttons);3626}36273628void MovieSetJoypad (int i, uint16 buttons)3629{3630if (i < 0 || i > 7)3631return;36323633joypad[i].buttons = buttons;3634}36353636bool MovieGetMouse (int i, uint8 out[5])3637{3638if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))3639return (false);36403641int n = curcontrollers[i] - MOUSE0;3642uint8 *ptr = out;36433644WRITE_WORD(ptr, mouse[n].cur_x); ptr += 2;3645WRITE_WORD(ptr, mouse[n].cur_y); ptr += 2;3646*ptr = mouse[n].buttons;36473648return (true);3649}36503651void MovieSetMouse (int i, uint8 in[5], bool inPolling)3652{3653if (i < 0 || i > 1 || (curcontrollers[i] != MOUSE0 && curcontrollers[i] != MOUSE1))3654return;36553656int n = curcontrollers[i] - MOUSE0;3657uint8 *ptr = in;36583659mouse[n].cur_x = READ_WORD(ptr); ptr += 2;3660mouse[n].cur_y = READ_WORD(ptr); ptr += 2;3661mouse[n].buttons = *ptr;36623663if (inPolling)3664UpdatePolledMouse(curcontrollers[i]);3665}36663667bool MovieGetScope (int i, uint8 out[6])3668{3669if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)3670return (false);36713672uint8 *ptr = out;36733674WRITE_WORD(ptr, superscope.x); ptr += 2;3675WRITE_WORD(ptr, superscope.y); ptr += 2;3676*ptr++ = superscope.phys_buttons;3677*ptr = superscope.next_buttons;36783679return (true);3680}36813682void MovieSetScope (int i, uint8 in[6])3683{3684if (i < 0 || i > 1 || curcontrollers[i] != SUPERSCOPE)3685return;36863687uint8 *ptr = in;36883689superscope.x = READ_WORD(ptr); ptr += 2;3690superscope.y = READ_WORD(ptr); ptr += 2;3691superscope.phys_buttons = *ptr++;3692superscope.next_buttons = *ptr;3693}36943695bool MovieGetJustifier (int i, uint8 out[11])3696{3697if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))3698return (false);36993700uint8 *ptr = out;37013702WRITE_WORD(ptr, justifier.x[0]); ptr += 2;3703WRITE_WORD(ptr, justifier.x[1]); ptr += 2;3704WRITE_WORD(ptr, justifier.y[0]); ptr += 2;3705WRITE_WORD(ptr, justifier.y[1]); ptr += 2;3706*ptr++ = justifier.buttons;3707*ptr++ = justifier.offscreen[0];3708*ptr = justifier.offscreen[1];37093710return (true);3711}37123713void MovieSetJustifier (int i, uint8 in[11])3714{3715if (i < 0 || i > 1 || (curcontrollers[i] != ONE_JUSTIFIER && curcontrollers[i] != TWO_JUSTIFIERS))3716return;37173718uint8 *ptr = in;37193720justifier.x[0] = READ_WORD(ptr); ptr += 2;3721justifier.x[1] = READ_WORD(ptr); ptr += 2;3722justifier.y[0] = READ_WORD(ptr); ptr += 2;3723justifier.y[1] = READ_WORD(ptr); ptr += 2;3724justifier.buttons = *ptr++;3725justifier.offscreen[0] = *ptr++;3726justifier.offscreen[1] = *ptr;3727}3728#endif372937303731