diff --git a/smw.ini b/smw.ini
index 5791e31..b776b0b 100644
--- a/smw.ini
+++ b/smw.ini
@@ -66,6 +66,9 @@ Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, c, v
# This one is suitable for AZERTY keyboards.
#Controls = Up, Down, Left, Right, Right Shift, Return, x, w, s, q, c, v
+# For player 2
+#ControlsP2 = Shift+Up, Shift+Down, Shift+Left, Shift+Right, Shift+Right Shift, Shift+Return, Shift+x, Shift+z, Shift+s, Shift+a, Shift+c, Shift+v
+
CheatLife = w
CheatJump = Ctrl+q
ClearKeyLog = k
@@ -90,7 +93,14 @@ LoadRef = 1,2,3,4,5,6,7,8,9,0,-,=,Backspace
ReplayRef = Ctrl+1,Ctrl+2,Ctrl+3,Ctrl+4,Ctrl+5,Ctrl+6,Ctrl+7,Ctrl+8,Ctrl+9,Ctrl+0,Ctrl+-,Ctrl+=,Ctrl+Backspace
[GamepadMap]
-# Any keys used in KeyMap can be used also in this section.
-# The shoulder button is called L1/Lb and L2, and the thumbstick button is called L3
-Controls = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb
+# Whether the gamepads will be enabled. The game will not use them unless they're on.
+EnableGamepad1 = true
+EnableGamepad2 = true
+# Any keys used in KeyMap can be used also in this section.
+
+# The shoulder button is called L1/Lb and L2, and the thumbstick button is called L3
+Controls = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb
+
+# For player 2.
+ControlsP2 = DpadUp, DpadDown, DpadLeft, DpadRight, Back, Start, B, A, Y, X, Lb, Rb
diff --git a/src/common_cpu_infra.c b/src/common_cpu_infra.c
index 571e8a8..478642d 100644
--- a/src/common_cpu_infra.c
+++ b/src/common_cpu_infra.c
@@ -32,7 +32,6 @@ static uint8 kPatchedCarrysOrg[1024];
static void VerifySnapshotsEq(Snapshot *b, Snapshot *a, Snapshot *prev);
static void MakeSnapshot(Snapshot *s);
static void RestoreSnapshot(Snapshot *s);
-static void RtlRunFrameCompare(uint16 input, int run_what);
uint8_t *SnesRomPtr(uint32 v) {
return (uint8 *)RomPtr(v);
@@ -252,8 +251,6 @@ Snes *SnesInit(const uint8 *data, int data_size) {
g_dma = g_snes->dma;
g_use_my_apu_code = (g_runmode != RM_THEIRS);
- RtlSetupEmuCallbacks(NULL, &RtlRunFrameCompare, NULL);
-
if (data_size != 0 && g_runmode != RM_MINE) {
bool loaded = snes_loadRom(g_snes, data, data_size);
if (!loaded) {
@@ -362,9 +359,7 @@ getout:
g_got_mismatch_count--;
}
-static void RtlRunFrameCompare(uint16 input, int run_what) {
- g_snes->input1->currentState = input;
-
+void RtlRunFrameCompare() {
g_use_my_apu_code = (g_runmode != RM_THEIRS);
if (g_runmode == RM_THEIRS) {
diff --git a/src/common_cpu_infra.h b/src/common_cpu_infra.h
index 0bc1ea5..4e6ee0f 100644
--- a/src/common_cpu_infra.h
+++ b/src/common_cpu_infra.h
@@ -21,6 +21,8 @@ typedef void CpuInfraInitializeFunc(void);
typedef void RunOneFrameOfGameFunc(void);
typedef void FixSnapshotForCompareFunc(Snapshot *b, Snapshot *a);
+void RtlRunFrameCompare(void);
+
typedef struct RtlGameInfo {
const char *title;
uint8 game_id;
diff --git a/src/common_rtl.c b/src/common_rtl.c
index 05fbadc..4556a34 100644
--- a/src/common_rtl.c
+++ b/src/common_rtl.c
@@ -26,30 +26,6 @@ Ppu *g_ppu, *g_my_ppu;
Dma *g_dma;
bool g_custom_music;
-static uint8 *g_rtl_memory_ptr;
-static RunFrameFunc *g_rtl_runframe;
-static SyncAllFunc *g_rtl_syncall;
-
-void RtlSetupEmuCallbacks(uint8 *emu_ram, RunFrameFunc *func, SyncAllFunc *sync_all) {
- g_rtl_memory_ptr = emu_ram;
- g_rtl_runframe = func;
- g_rtl_syncall = sync_all;
-}
-
-static void RtlSynchronizeWholeState(void) {
- if (g_rtl_syncall)
- g_rtl_syncall();
-}
-
-// |ptr| must be a pointer into g_ram, will synchronize the RAM memory with the
-// emulator.
-static void RtlSyncMemoryRegion(void *ptr, size_t n) {
- uint8 *data = (uint8 *)ptr;
- assert(data >= g_ram && data < g_ram + 0x20000);
- if (g_rtl_memory_ptr)
- memcpy(g_rtl_memory_ptr + (data - g_ram), data, n);
-}
-
void ByteArray_AppendVl(ByteArray *arr, uint32 v) {
for (; v >= 255; v -= 255)
ByteArray_AppendByte(arr, 255);
@@ -81,7 +57,6 @@ void loadFunc(SaveLoadInfo *sli, void *data, size_t data_size) {
static void LoadSnesState(SaveLoadInfo *sli) {
// Do the actual loading
snes_saveload(g_snes, sli);
- RtlSynchronizeWholeState();
}
static void SaveSnesState(SaveLoadInfo *sli) {
@@ -89,7 +64,7 @@ static void SaveSnesState(SaveLoadInfo *sli) {
}
typedef struct StateRecorder {
- uint16 last_inputs;
+ uint32 last_inputs;
uint32 frames_since_last;
uint32 total_frames;
@@ -100,6 +75,7 @@ typedef struct StateRecorder {
uint32 snapshot_flags;
uint8 replay_cmd;
bool replay_mode;
+ uint8 cur_player;
ByteArray log;
ByteArray base_snapshot;
@@ -122,25 +98,31 @@ void StateRecorder_RecordCmd(StateRecorder *sr, uint8 cmd) {
ByteArray_AppendVl(&sr->log, frames - x);
}
-void StateRecorder_Record(StateRecorder *sr, uint16 inputs) {
- uint16 diff = inputs ^ sr->last_inputs;
- if (diff != 0) {
- sr->last_inputs = inputs;
- // printf("0x%.4x %d: ", diff, sr->frames_since_last);
- // size_t lb = sr->log.size;
- for (int i = 0; i < 12; i++) {
- if ((diff >> i) & 1)
- StateRecorder_RecordCmd(sr, i << 4);
+static void StateRecorder_RecordKeyDiff(StateRecorder *sr, uint32 diff) {
+ for (int i = 0; diff; i++, diff >>= 1) {
+ if (diff & 1) {
+ int player = (i >= 12);
+ i -= player * 12;
+ if (player != sr->cur_player) {
+ sr->cur_player = player;
+ ByteArray_AppendByte(&sr->log, 0xfc + player);
+ }
+ StateRecorder_RecordCmd(sr, i << 4);
}
- // while (lb < sr->log.size)
- // printf("%.2x ", sr->log.data[lb++]);
- // printf("\n");
}
+}
+
+void StateRecorder_Record(StateRecorder *sr, uint32 inputs) {
+ uint32 diff = (inputs ^ sr->last_inputs) & 0xffffff;
+ sr->last_inputs = inputs;
+ if (diff)
+ StateRecorder_RecordKeyDiff(sr, diff);
sr->frames_since_last++;
sr->total_frames++;
}
-void StateRecorder_RecordPatchByte(StateRecorder *sr, uint32 addr, const uint8 *value, int num) {
+void StateRecorder_RecordPatchByte(StateRecorder *sr, const uint8 *value, int num) {
+ uint32 addr = value - g_ram;
assert(addr < 0x20000);
// printf("%d: PatchByte(0x%x, 0x%x. %d): ", sr->frames_since_last, addr, *value, num);
@@ -174,8 +156,6 @@ void RtlReset(int mode) {
RtlRestoreMusicAfterLoad_Locked(true);
RtlApuUnlock();
- RtlSynchronizeWholeState();
-
if ((mode & 2) == 0)
StateRecorder_Init(&state_recorder);
}
@@ -216,6 +196,7 @@ void StateRecorder_Load(StateRecorder *sr, FILE *f, bool replay_mode) {
sr->snapshot_flags = hdr[9];
sr->replay_next_cmd_at = 0;
sr->replay_mode = replay_mode;
+ sr->cur_player = 0;
if (replay_mode) {
sr->frames_since_last = 0;
sr->last_inputs = 0;
@@ -269,6 +250,12 @@ void StateRecorder_Save(StateRecorder *sr, FILE *f, bool saving_with_bug) {
assert(sr->base_snapshot.size == 0 ||
sr->base_snapshot.size == savest.array.size || sr->base_snapshot.size == g_sram_size);
+ // Before saving, reset the cur player
+ if (sr->cur_player) {
+ sr->cur_player = 0;
+ ByteArray_AppendVl(&sr->log, 0xfc);
+ }
+
hdr[0] = 2;
hdr[1] = sr->total_frames;
hdr[2] = (uint32)sr->log.size;
@@ -305,12 +292,9 @@ void StateRecorder_ClearKeyLog(StateRecorder *sr) {
memset(&sr->log, 0, sizeof(sr->log));
// If there are currently any active inputs, record them initially at timestamp 0.
sr->frames_since_last = 0;
- if (sr->last_inputs) {
- for (int i = 0; i < 12; i++) {
- if ((sr->last_inputs >> i) & 1)
- StateRecorder_RecordCmd(sr, i << 4);
- }
- }
+ sr->cur_player = 0;
+ StateRecorder_RecordKeyDiff(sr, sr->last_inputs);
+
if (sr->replay_mode) {
// When clearing the key log while in replay mode, we want to keep
// replaying but discarding all key history up until this point.
@@ -340,7 +324,7 @@ uint16 StateRecorder_ReadNextReplayState(StateRecorder *sr) {
// Apply next command
sr->frames_since_last = 0;
if (sr->replay_cmd < 0xc0) {
- sr->last_inputs ^= 1 << (sr->replay_cmd >> 4);
+ sr->last_inputs ^= 1 << ((sr->replay_cmd >> 4) + sr->cur_player * 12);
} else if (sr->replay_cmd < 0xd0) {
int nb = 1 + ((sr->replay_cmd >> 2) & 3);
uint8 t;
@@ -352,7 +336,6 @@ uint16 StateRecorder_ReadNextReplayState(StateRecorder *sr) {
addr |= sr->log.data[replay_pos++];
do {
g_ram[addr & 0x1ffff] = sr->log.data[replay_pos++];
- RtlSyncMemoryRegion(&g_ram[addr & 0x1ffff], 1);
} while (addr++, --nb);
} else {
assert(0);
@@ -365,7 +348,20 @@ uint16 StateRecorder_ReadNextReplayState(StateRecorder *sr) {
break;
}
// Read the next one
- uint8 cmd = sr->log.data[replay_pos++], t;
+ uint8 cmd, t;
+
+ for (;;) {
+ cmd = sr->log.data[replay_pos++];
+ if (cmd < 0xfc)
+ break;
+ switch (cmd) {
+ case 0xfc:
+ case 0xfd: sr->cur_player = cmd - 0xfc; break;
+ default:
+ assert(0);
+ }
+ }
+
int mask = (cmd < 0xc0) ? 0xf : 0x1;
int frames = cmd & mask;
if (frames == mask) do {
@@ -400,7 +396,8 @@ void RtlStopReplay(void) {
StateRecorder_StopReplay(&state_recorder);
}
-bool RtlRunFrame(int inputs) {
+bool RtlRunFrame(uint32 inputs) {
+
if (g_did_finish_level_hook) {
if (game_id == kGameID_SMW && !state_recorder.replay_mode && g_config.save_playthrough) {
SmwSavePlaythroughSnapshot();
@@ -414,6 +411,9 @@ bool RtlRunFrame(int inputs) {
// Avoid up/down and left/right from being pressed at the same time
if ((inputs & 0x30) == 0x30) inputs ^= 0x30;
if ((inputs & 0xc0) == 0xc0) inputs ^= 0xc0;
+ // Player2
+ if ((inputs & 0x30000) == 0x30000) inputs ^= 0x30000;
+ if ((inputs & 0xc0000) == 0xc0000) inputs ^= 0xc0000;
bool is_replay = state_recorder.replay_mode;
@@ -433,12 +433,21 @@ bool RtlRunFrame(int inputs) {
uint8 apui02 = RtlApuReadReg(2);
if (apui02 != g_ram[kSmwRam_APUI02]) {
g_ram[kSmwRam_APUI02] = apui02;
- StateRecorder_RecordPatchByte(&state_recorder, kSmwRam_APUI02, &apui02, 1);
+ StateRecorder_RecordPatchByte(&state_recorder, &g_ram[kSmwRam_APUI02], 1);
+ }
+ // Whether controllers are plugged in.
+ uint32 new_my_flags = inputs >> 30;
+ if (new_my_flags != g_ram[kSmwRam_my_flags]) {
+ assert(new_my_flags <= 255);
+ g_ram[kSmwRam_my_flags] = new_my_flags;
+ StateRecorder_RecordPatchByte(&state_recorder, &g_ram[kSmwRam_my_flags], 1);
}
}
}
+ g_snes->input1_currentState = inputs & 0xfff;
+ g_snes->input2_currentState = (inputs >> 12) & 0xfff;
- g_rtl_runframe(inputs, 0);
+ RtlRunFrameCompare();
snes_frame_counter++;
@@ -462,7 +471,6 @@ static void RtlLoadFromFile(FILE *f, bool replay) {
ppu_copy(g_my_ppu, g_snes->ppu);
RtlApuUnlock();
- RtlSynchronizeWholeState();
}
static const char *const kBugSaves[] = {
@@ -789,7 +797,6 @@ void RtlReadSram(void) {
if (fread(g_sram, 1, g_sram_size, f) != g_sram_size)
fprintf(stderr, "Error reading %s\n", filename);
fclose(f);
- RtlSynchronizeWholeState();
ByteArray_Resize(&state_recorder.base_snapshot, g_sram_size);
memcpy(state_recorder.base_snapshot.data, g_sram, g_sram_size);
}
diff --git a/src/common_rtl.h b/src/common_rtl.h
index a8ec55f..dafe41b 100644
--- a/src/common_rtl.h
+++ b/src/common_rtl.h
@@ -15,6 +15,7 @@ enum {
kCurrentBugFixCounter = 1,
kSmwRam_APUI02 = 0x18c5,
+ kSmwRam_my_flags = 0x19C7C,
};
typedef struct SimpleHdma {
@@ -103,12 +104,7 @@ uint8 ReadReg(uint16 reg);
uint8_t *IndirPtr(LongPtr ptr, uint16 offs);
void IndirWriteByte(LongPtr ptr, uint16 offs, uint8 value);
-
-typedef void RunFrameFunc(uint16 input, int run_what);
-typedef void SyncAllFunc();
-
void RtlReset(int mode);
-void RtlSetupEmuCallbacks(uint8 *emu_ram, RunFrameFunc *func, SyncAllFunc *sync_all);
void RtlClearKeyLog();
void RtlStopReplay();
@@ -127,7 +123,7 @@ void RtlSetUploadingApu(bool uploading);
void RtlApuUpload(const uint8 *p);
void RtlRenderAudio(int16 *audio_buffer, int samples, int channels);
void RtlPushApuState();
-bool RtlRunFrame(int inputs);
+bool RtlRunFrame(uint32 inputs);
void RtlReadSram();
void RtlWriteSram();
void RtlSaveSnapshot(const char *filename, bool saving_with_bug);
diff --git a/src/config.c b/src/config.c
index 4949233..a63d286 100644
--- a/src/config.c
+++ b/src/config.c
@@ -24,6 +24,8 @@ static const uint16 kDefaultKbdControls[kKeys_Total] = {
0,
// Controls
_(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_c), _(SDLK_v),
+ // ControlsP2
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// LoadState
_(SDLK_F1), _(SDLK_F2), _(SDLK_F3), _(SDLK_F4), _(SDLK_F5), _(SDLK_F6), _(SDLK_F7), _(SDLK_F8), _(SDLK_F9), _(SDLK_F10), N, N, N, N, N, N, N, N, N, N,
// SaveState
@@ -56,7 +58,8 @@ typedef struct KeyNameId {
#define S(n) {#n, kKeys_##n, 1}
static const KeyNameId kKeyNameId[] = {
{"Null", kKeys_Null, 65535},
- M(Controls), M(Load), M(Save), M(Replay), M(LoadRef), M(ReplayRef),
+ M(Controls), M(ControlsP2),
+ M(Load), M(Save), M(Replay), M(LoadRef), M(ReplayRef),
S(CheatLife), S(CheatJump), S(ToggleWhichFrame),
S(ClearKeyLog), S(StopReplay), S(Fullscreen), S(Reset),
S(Pause), S(PauseDimmed), S(Turbo), S(ReplayTurbo), S(WindowBigger), S(WindowSmaller), S(VolumeUp), S(VolumeDown), S(DisplayPerf), S(ToggleRenderer),
@@ -73,6 +76,14 @@ static int keymap_hash_size;
static bool has_keynameid[countof(kKeyNameId)];
static bool KeyMapHash_Add(uint16 key, uint16 cmd) {
+ if (!key)
+ return false;
+
+ if (cmd == kKeys_Controls)
+ g_config.has_keyboard_controls |= 1;
+ else if (cmd == kKeys_ControlsP2)
+ g_config.has_keyboard_controls |= 2;
+
if ((keymap_hash_size & 0xff) == 0) {
if (keymap_hash_size > 10000)
Die("Too many keys");
@@ -154,10 +165,10 @@ typedef struct GamepadMapEnt {
uint16 cmd, next;
} GamepadMapEnt;
-static uint16 joymap_first[kGamepadBtn_Count];
+static uint16 joymap_first[kGamepadBtn_Count * 2]; // 2 gamepads
static GamepadMapEnt *joymap_ents;
static int joymap_size;
-static bool has_joypad_controls;
+static uint8 has_assigned_joypad_controls;
static int CountBits32(uint32 n) {
int count = 0;
@@ -169,7 +180,7 @@ static int CountBits32(uint32 n) {
static void GamepadMap_Add(int button, uint32 modifiers, uint16 cmd) {
if ((joymap_size & 0xff) == 0) {
if (joymap_size > 1000)
- Die("Too many joypad keys");
+ Die("Too many joypad keys");
joymap_ents = (GamepadMapEnt*)realloc(joymap_ents, sizeof(GamepadMapEnt) * (joymap_size + 64));
if (!joymap_ents) Die("realloc failure");
}
@@ -224,12 +235,13 @@ static const uint8 kDefaultGamepadCmds[] = {
kGamepadBtn_B, kGamepadBtn_A, kGamepadBtn_Y, kGamepadBtn_X, kGamepadBtn_L1, kGamepadBtn_R1,
};
-static void ParseGamepadArray(char *value, int cmd, int size) {
+static void ParseGamepadArray(int gamepad, char *value, int cmd, int size) {
char *s;
int i = 0;
for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd += (cmd != 0)) {
if (*s == 0)
continue;
+ int gamepad_cur = gamepad;
uint32 modifiers = 0;
const char *ss = s;
for (;;) {
@@ -243,7 +255,7 @@ static void ParseGamepadArray(char *value, int cmd, int size) {
ss++;
modifiers |= 1 << button;
} else if (*ss == 0) {
- GamepadMap_Add(button, modifiers, cmd);
+ GamepadMap_Add(button + gamepad_cur * kGamepadBtn_Count, modifiers, cmd);
break;
} else
goto BAD;
@@ -259,10 +271,14 @@ static void RegisterDefaultKeys(void) {
KeyMapHash_Add(kDefaultKbdControls[k], k);
}
}
- if (!has_joypad_controls) {
+ if (!(has_assigned_joypad_controls & 1)) {
for (int i = 0; i < countof(kDefaultGamepadCmds); i++)
GamepadMap_Add(kDefaultGamepadCmds[i], 0, kKeys_Controls + i);
}
+ if (!(has_assigned_joypad_controls & 2)) {
+ for (int i = 0; i < countof(kDefaultGamepadCmds); i++)
+ GamepadMap_Add(kDefaultGamepadCmds[i] + kGamepadBtn_Count, 0, kKeys_ControlsP2 + i);
+ }
}
static int GetIniSection(const char *s) {
@@ -321,12 +337,18 @@ static bool HandleIniConfig(int section, const char *key, char *value) {
}
}
} else if (section == 5) {
- for (int i = 0; i < countof(kKeyNameId); i++) {
- if (StringEqualsNoCase(key, kKeyNameId[i].name)) {
- if (i == 1)
- has_joypad_controls = true;
- ParseGamepadArray(value, kKeyNameId[i].id, kKeyNameId[i].size);
- return true;
+ if (StringEqualsNoCase(key, "EnableGamepad1")) {
+ return ParseBool(value, &g_config.enable_gamepad[0]);
+ } else if (StringEqualsNoCase(key, "EnableGamepad2")) {
+ return ParseBool(value, &g_config.enable_gamepad[1]);
+ } else {
+ for (int i = 0; i < countof(kKeyNameId); i++) {
+ if (StringEqualsNoCase(key, kKeyNameId[i].name)) {
+ int id = kKeyNameId[i].id;
+ has_assigned_joypad_controls |= (id == kKeys_Controls) ? 1 : (id == kKeys_ControlsP2) ? 2 : 0;
+ ParseGamepadArray(id == kKeys_ControlsP2 ? 1 : 0, value, kKeyNameId[i].id, kKeyNameId[i].size);
+ return true;
+ }
}
}
} else if (section == 1) {
diff --git a/src/config.h b/src/config.h
index 4a341b6..bf5ca30 100644
--- a/src/config.h
+++ b/src/config.h
@@ -6,6 +6,10 @@ enum {
kKeys_Null,
kKeys_Controls,
kKeys_Controls_Last = kKeys_Controls + 11,
+
+ kKeys_ControlsP2,
+ kKeys_ControlsP2_Last = kKeys_ControlsP2 + 11,
+
kKeys_Load,
kKeys_Load_Last = kKeys_Load + 19,
kKeys_Save,
@@ -72,6 +76,11 @@ typedef struct Config {
char *memory_buffer;
const char *shader;
const char *msu_path;
+
+ bool enable_gamepad[2];
+
+ // Which players have keyboard controls
+ uint8 has_keyboard_controls;
} Config;
enum {
diff --git a/src/main.c b/src/main.c
index e7af279..7ea0de1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,15 +31,26 @@
#include "assets/smw_assets.h"
+typedef struct GamepadInfo {
+ uint32 modifiers;
+ SDL_JoystickID joystick_id;
+ uint8 index;
+ uint8 axis_buttons;
+ uint16 last_cmd[kGamepadBtn_Count];
+ Sint16 last_axis_x, last_axis_y;
+} GamepadInfo;
+
+
static void SDLCALL AudioCallback(void *userdata, Uint8 *stream, int len);
static void LoadAssets();
static void SwitchDirectory();
static void RenderNumber(uint8 *dst, size_t pitch, int n, uint8 big);
static void OpenOneGamepad(int i);
+static uint32 GetActiveControllers(void);
static void HandleVolumeAdjustment(int volume_adjustment);
-static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
+static void HandleGamepadAxisInput(GamepadInfo *gi, int axis, Sint16 value);
static int RemapSdlButton(int button);
-static void HandleGamepadInput(int button, bool pressed);
+static void HandleGamepadInput(GamepadInfo *gi, int button, bool pressed);
static void HandleInput(int keyCode, int keyMod, bool pressed);
static void HandleCommand(uint32 j, bool pressed);
void OpenGLRenderer_Create(struct RendererFuncs *funcs);
@@ -49,7 +60,6 @@ bool g_want_dump_memmap_flags;
bool g_new_ppu = true;
bool g_other_image = true;
struct SpcPlayer *g_spc_player;
-static uint32_t button_state;
static uint8_t g_pixels[256 * 4 * 240];
static uint8_t g_my_pixels[256 * 4 * 240];
@@ -70,16 +80,16 @@ static SDL_Window *g_window;
static uint8 g_paused, g_turbo, g_replay_turbo = true, g_cursor = true;
static uint8 g_current_window_scale;
-static uint8 g_gamepad_buttons;
-static int g_input1_state;
+static uint32 g_input_state;
static bool g_display_perf;
static int g_curr_fps;
static int g_ppu_render_flags = 0;
static int g_snes_width, g_snes_height;
static int g_sdl_audio_mixer_volume = SDL_MIX_MAXVOLUME;
static struct RendererFuncs g_renderer_funcs;
-static uint32 g_gamepad_modifiers;
-static uint16 g_gamepad_last_cmd[kGamepadBtn_Count];
+
+static GamepadInfo g_gamepad[2];
+
extern Snes *g_snes;
void NORETURN Die(const char *error) {
@@ -93,6 +103,10 @@ void Warning(const char *error) {
fprintf(stderr, "Warning: %s\n", error);
}
+static GamepadInfo *GetGamepadInfo(SDL_JoystickID id) {
+ return (g_gamepad[0].joystick_id == id) ? &g_gamepad[0] :
+ (g_gamepad[1].joystick_id == id) ? &g_gamepad[1] : NULL;
+}
void ChangeWindowScale(int scale_step) {
if ((SDL_GetWindowFlags(g_window) & (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED)) != 0)
@@ -340,6 +354,7 @@ int main(int argc, char** argv) {
LoadAssets();
+ g_gamepad[0].joystick_id = g_gamepad[1].joystick_id = -1;
g_snes_width = (g_config.extended_aspect_ratio * 2 + 256);
g_snes_height = 224;// (g_config.extend_y ? 240 : 224);
g_ppu_render_flags = g_config.new_renderer * kPpuRenderFlags_NewRenderer |
@@ -466,6 +481,7 @@ error_reading:;
uint32 frameCtr = 0;
uint8 audiopaused = true;
bool has_bug_in_title = false;
+ GamepadInfo *gi;
while (running) {
SDL_Event event;
@@ -475,14 +491,26 @@ error_reading:;
case SDL_CONTROLLERDEVICEADDED:
OpenOneGamepad(event.cdevice.which);
break;
+ case SDL_CONTROLLERDEVICEREMOVED:
+ gi = GetGamepadInfo(event.cdevice.which);
+ if (gi) {
+ memset(gi, 0, sizeof(GamepadInfo));
+ gi->joystick_id = -1;
+ }
+ break;
case SDL_CONTROLLERAXISMOTION:
- HandleGamepadAxisInput(event.caxis.which, event.caxis.axis, event.caxis.value);
+ gi = GetGamepadInfo(event.caxis.which);
+ if (gi)
+ HandleGamepadAxisInput(gi, event.caxis.axis, event.caxis.value);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: {
- int b = RemapSdlButton(event.cbutton.button);
- if (b >= 0)
- HandleGamepadInput(b, event.type == SDL_CONTROLLERBUTTONDOWN);
+ gi = GetGamepadInfo(event.cbutton.which);
+ if (gi) {
+ int b = RemapSdlButton(event.cbutton.button);
+ if (b >= 0)
+ HandleGamepadInput(gi, b, event.type == SDL_CONTROLLERBUTTONDOWN);
+ }
break;
}
case SDL_MOUSEWHEEL:
@@ -521,12 +549,12 @@ error_reading:;
}
// Clear gamepad inputs when joypad directional inputs to avoid wonkiness
- int inputs = g_input1_state;
- if (g_input1_state & 0xf0)
- g_gamepad_buttons = 0;
- inputs |= g_gamepad_buttons;
-
- uint8 is_replay = RtlRunFrame(inputs);
+ if (g_input_state & 0xf0)
+ g_gamepad[0].axis_buttons = 0;
+ if (g_input_state & 0xf0000)
+ g_gamepad[1].axis_buttons = 0;
+ uint32 inputs = g_input_state | g_gamepad[0].axis_buttons | g_gamepad[1].axis_buttons << 12;
+ uint8 is_replay = RtlRunFrame(inputs | GetActiveControllers());
frameCtr++;
g_snes->disableRender = (g_turbo ^ (is_replay & g_replay_turbo)) && (frameCtr & (g_turbo ? 0xf : 0x7f)) != 0;
@@ -634,12 +662,19 @@ static void RenderNumber(uint8 *dst, size_t pitch, int n, uint8 big) {
}
static void HandleCommand(uint32 j, bool pressed) {
+ static const uint8 kKbdRemap[] = { 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
+ if (j < kKeys_Controls)
+ return;
+
if (j <= kKeys_Controls_Last) {
- static const uint8 kKbdRemap[] = { 0, 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
- if (pressed)
- g_input1_state |= 1 << kKbdRemap[j];
- else
- g_input1_state &= ~(1 << kKbdRemap[j]);
+ uint32 m = 1 << kKbdRemap[j - kKeys_Controls];
+ g_input_state = pressed ? (g_input_state | m) : (g_input_state & ~m);
+ return;
+ }
+
+ if (j <= kKeys_ControlsP2_Last) {
+ uint32 m = 0x1000 << kKbdRemap[j - kKeys_ControlsP2];
+ g_input_state = pressed ? (g_input_state | m) : (g_input_state & ~m);
return;
}
@@ -715,11 +750,43 @@ static void HandleInput(int keyCode, int keyMod, bool pressed) {
HandleCommand(j, pressed);
}
+static uint32 GetActiveControllers() {
+ uint32 ctrl = g_config.has_keyboard_controls;
+ ctrl |= g_gamepad[0].joystick_id != -1 ? 1 : 0;
+ ctrl |= g_gamepad[1].joystick_id != -1 ? 2 : 0;
+ return ctrl << 30;
+}
+
static void OpenOneGamepad(int i) {
if (SDL_IsGameController(i)) {
SDL_GameController *controller = SDL_GameControllerOpen(i);
- if (!controller)
+ if (!controller) {
fprintf(stderr, "Could not open gamepad %d: %s\n", i, SDL_GetError());
+ return;
+ }
+
+ uint32 joystick_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
+ if (GetGamepadInfo(joystick_id))
+ return;
+
+ uint8 scan_order[3] = { SDL_GameControllerGetPlayerIndex(controller), 0, 1 };
+
+ int found_idx = -1;
+ for (int i = 0; i < 3; i++) {
+ uint8 j = scan_order[i];
+ if (j < 2 && g_config.enable_gamepad[j] && (i == 0 || g_gamepad[j].joystick_id == -1)) {
+ found_idx = j;
+ break;
+ }
+ }
+
+ printf("Found controller '%s' assigning to player %d\n", SDL_GameControllerName(controller), found_idx + 1);
+ if (found_idx >= 0) {
+ GamepadInfo *gi = &g_gamepad[found_idx];
+ memset(gi, 0, sizeof(GamepadInfo));
+ gi->index = found_idx;
+ gi->joystick_id = joystick_id;
+ }
}
}
@@ -744,14 +811,14 @@ static int RemapSdlButton(int button) {
}
}
-static void HandleGamepadInput(int button, bool pressed) {
- if (!!(g_gamepad_modifiers & (1 << button)) == pressed)
+static void HandleGamepadInput(GamepadInfo *gi, int button, bool pressed) {
+ if (!!(gi->modifiers & (1 << button)) == pressed)
return;
- g_gamepad_modifiers ^= 1 << button;
+ gi->modifiers ^= 1 << button;
if (pressed)
- g_gamepad_last_cmd[button] = FindCmdForGamepadButton(button, g_gamepad_modifiers);
- if (g_gamepad_last_cmd[button] != 0)
- HandleCommand(g_gamepad_last_cmd[button], pressed);
+ gi->last_cmd[button] = FindCmdForGamepadButton(button + gi->index * kGamepadBtn_Count, gi->modifiers);
+ if (gi->last_cmd[button] != 0)
+ HandleCommand(gi->last_cmd[button], pressed);
}
static void HandleVolumeAdjustment(int volume_adjustment) {
@@ -787,19 +854,11 @@ static float ApproximateAtan2(float y, float x) {
return q + *(float *)&uatan_2q;
}
-static void HandleGamepadAxisInput(int gamepad_id, int axis, int value) {
- static int last_gamepad_id, last_x, last_y;
+static void HandleGamepadAxisInput(GamepadInfo *gi, int axis, Sint16 value) {
if (axis == SDL_CONTROLLER_AXIS_LEFTX || axis == SDL_CONTROLLER_AXIS_LEFTY) {
- // ignore other gamepads unless they have a big input
- if (last_gamepad_id != gamepad_id) {
- if (value > -16000 && value < 16000)
- return;
- last_gamepad_id = gamepad_id;
- last_x = last_y = 0;
- }
- *(axis == SDL_CONTROLLER_AXIS_LEFTX ? &last_x : &last_y) = value;
+ *(axis == SDL_CONTROLLER_AXIS_LEFTX ? &gi->last_axis_x : &gi->last_axis_y) = value;
int buttons = 0;
- if (last_x * last_x + last_y * last_y >= 10000 * 10000) {
+ if (gi->last_axis_x * gi->last_axis_x + gi->last_axis_y * gi->last_axis_y >= 10000 * 10000) {
// in the non deadzone part, divide the circle into eight 45 degree
// segments rotated by 22.5 degrees that control which direction to move.
// todo: do this without floats?
@@ -813,13 +872,13 @@ static void HandleGamepadAxisInput(int gamepad_id, int axis, int value) {
1 << 6, // 6 = left
1 << 6 | 1 << 4, // 7 = left, up
};
- uint8 angle = (uint8)(int)(ApproximateAtan2(last_y, last_x) * 64.0f + 0.5f);
+ uint8 angle = (uint8)(int)(ApproximateAtan2(gi->last_axis_y, gi->last_axis_x) * 64.0f + 0.5f);
buttons = kSegmentToButtons[(uint8)(angle + 16 + 64) >> 5];
}
- g_gamepad_buttons = buttons;
+ gi->axis_buttons = buttons;
} else if ((axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
if (value < 12000 || value >= 16000) // hysteresis
- HandleGamepadInput(axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ? kGamepadBtn_L2 : kGamepadBtn_R2, value >= 12000);
+ HandleGamepadInput(gi, axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ? kGamepadBtn_L2 : kGamepadBtn_R2, value >= 12000);
}
}
diff --git a/src/smw.vcxproj b/src/smw.vcxproj
index c746ddb..7ee68d3 100644
--- a/src/smw.vcxproj
+++ b/src/smw.vcxproj
@@ -199,10 +199,6 @@
Disabled
Disabled
-
- MinSpace
- MinSpace
-
Disabled
Disabled
@@ -241,7 +237,6 @@
-
diff --git a/src/smw_00.c b/src/smw_00.c
index 5b0f72b..ad1b1e9 100644
--- a/src/smw_00.c
+++ b/src/smw_00.c
@@ -1587,14 +1587,9 @@ void GameMode12_PrepareLevel_009A3D(uint8 k) { // 009a3d
}
void CheckWhichControllersArePluggedIn() { // 009a74
- uint8 v1 = ReadReg(JOYA);
- uint8 Reg = ReadReg(JOYB);
- uint8 v2 = (2 * Reg + v1) & 3;
- if (v2) {
- if (v2 == 3)
- v2 = -125;
- --v2;
- }
+ uint8 v2 = my_flags & 3;
+ if (v2)
+ v2 += ((v2 == 3) ? 0x80 : 0) - 1;
io_controllers_plugged_in = v2;
}
diff --git a/src/smw_cpu_infra.c b/src/smw_cpu_infra.c
index 9d66071..ab6e0ea 100644
--- a/src/smw_cpu_infra.c
+++ b/src/smw_cpu_infra.c
@@ -82,6 +82,10 @@ uint32 PatchBugs_SMW1(void) {
} else if (FixBugHook(0x4e686)) {
// CheckIfDestroyTileEventIsActive doesn't zero Y
g_cpu->y = 0;
+ } else if (FixBugHook(0x9A74)) {
+ CheckWhichControllersArePluggedIn();
+ // Remove old CheckWhichControllersArePluggedIn
+ return 0x9A8A;
} else if (FixBugHook(0x058AFB) || FixBugHook(0x58CE0)) {
int lvl_setting = misc_level_mode_setting;
diff --git a/src/snes/input.c b/src/snes/input.c
deleted file mode 100644
index 1a3267e..0000000
--- a/src/snes/input.c
+++ /dev/null
@@ -1,42 +0,0 @@
-
-#include
-#include
-#include
-#include
-#include
-
-#include "input.h"
-#include "snes.h"
-
-Input* input_init(Snes* snes) {
- Input* input = malloc(sizeof(Input));
- input->snes = snes;
- // TODO: handle (where?)
- input->type = 1;
- input->currentState = 0;
- return input;
-}
-
-void input_free(Input* input) {
- free(input);
-}
-
-void input_reset(Input* input) {
- input->latchLine = false;
- input->latchedState = 0;
-}
-
-void input_cycle(Input *input) {
- if (input->latchLine) {
- input->latchedState = input->currentState;
- }
-}
-
-uint8_t input_read(Input *input) {
- uint8_t ret = input->latchedState & 1;
- input->latchedState >>= 1;
- input->latchedState |= 0x8000;
- return ret;
-}
-
-
diff --git a/src/snes/input.h b/src/snes/input.h
deleted file mode 100644
index a4c6f87..0000000
--- a/src/snes/input.h
+++ /dev/null
@@ -1,31 +0,0 @@
-
-#ifndef INPUT_H
-#define INPUT_H
-
-#include
-#include
-#include
-#include
-#include
-
-typedef struct Input Input;
-
-#include "snes.h"
-
-struct Input {
- Snes *snes;
- uint8_t type;
- // latchline
- bool latchLine;
- // for controller
- uint16_t currentState; // actual state
- uint16_t latchedState;
-};
-
-Input* input_init(Snes* snes);
-void input_free(Input* input);
-void input_reset(Input* input);
-void input_cycle(Input *input);
-uint8_t input_read(Input *input);
-
-#endif
diff --git a/src/snes/snes.c b/src/snes/snes.c
index 9f258ab..104f6ab 100644
--- a/src/snes/snes.c
+++ b/src/snes/snes.c
@@ -12,7 +12,6 @@
#include "dma.h"
#include "ppu.h"
#include "cart.h"
-#include "input.h"
#include "../tracing.h"
#include "../variables.h"
#include "../common_rtl.h"
@@ -38,8 +37,8 @@ Snes* snes_init(uint8_t *ram) {
snes->dma = dma_init(snes);
snes->ppu = ppu_init();
snes->cart = cart_init(snes);
- snes->input1 = input_init(snes);
- snes->input2 = input_init(snes);
+ snes->input1_currentState = 0;
+ snes->input2_currentState = 0;
return snes;
}
@@ -49,8 +48,6 @@ void snes_free(Snes* snes) {
dma_free(snes->dma);
ppu_free(snes->ppu);
cart_free(snes->cart);
- input_free(snes->input1);
- input_free(snes->input2);
free(snes);
}
@@ -75,8 +72,6 @@ void snes_reset(Snes* snes, bool hard) {
apu_reset(snes->apu);
dma_reset(snes->dma);
ppu_reset(snes->ppu);
- input_reset(snes->input1);
- input_reset(snes->input2);
if (hard)
memset(snes->ram, 0, 0x20000);
snes->ramAdr = 0;
@@ -106,19 +101,6 @@ void snes_reset(Snes* snes, bool hard) {
snes->openBus = 0;
}
-void snes_runFrame(Snes *snes) {
- snes_frame_counter++;
-
- bool old = true;
- for (;;) {
- snes_runCycle(snes);
- if (snes->inVblank != 0 && snes->inVblank != old) {
- break;
- }
- old = snes->inVblank;
- }
-}
-
void snes_handle_pos_stuff(Snes *snes) {
// check for h/v timer irq's
if (snes->vIrqEnabled && snes->hIrqEnabled) {
@@ -193,19 +175,6 @@ void snes_handle_pos_stuff(Snes *snes) {
}
}
-void snes_runCycle(Snes* snes) {
- snes->apuCatchupCycles += apuCyclesPerMaster * 2.0;
-
- input_cycle(snes->input1);
- input_cycle(snes->input2);
-
- // if not in dram refresh, if we are busy with hdma/dma, do that, else do cpu cycle
- if (snes->hPos < 536 || snes->hPos >= 576) {
- snes_runCpu(snes);
- }
-
- snes_handle_pos_stuff(snes);
- }
#define IS_ADR(x) (x == 0xfffff)
void snes_runCpu(Snes *snes) {
@@ -323,13 +292,15 @@ static uint8_t snes_readReg(Snes* snes, uint16_t adr) {
case 0x4217:
return snes->multiplyResult >> 8;
case 0x4218:
- return SwapInputBits(snes->input1->currentState) & 0xff;
+ return SwapInputBits(snes->input1_currentState) & 0xff;
case 0x4219:
- return SwapInputBits(snes->input1->currentState) >> 8;
+ return SwapInputBits(snes->input1_currentState) >> 8;
case 0x421a:
+ return SwapInputBits(snes->input2_currentState) & 0xff;
+ case 0x421b:
+ return SwapInputBits(snes->input2_currentState) >> 8;
case 0x421c:
case 0x421e:
- case 0x421b:
case 0x421d:
case 0x421f:
return 0;
@@ -447,7 +418,8 @@ uint8_t snes_read(Snes* snes, uint32_t adr) {
return snes_readBBus(snes, adr & 0xff); // B-bus
}
if (adr == 0x4016 || adr == 0x4017) {
- return input_read(snes->input1) | (snes->openBus & 0xfc);
+ // joypad read disabled
+ return 0;
}
if(adr >= 0x4200 && adr < 0x4220 || adr >= 0x4218 && adr < 0x4220) {
return snes_readReg(snes, adr); // internal registers
diff --git a/src/snes/snes.h b/src/snes/snes.h
index c7212ec..183a3b2 100644
--- a/src/snes/snes.h
+++ b/src/snes/snes.h
@@ -15,7 +15,6 @@ typedef struct Snes Snes;
#include "dma.h"
#include "ppu.h"
#include "cart.h"
-#include "input.h"
#include "saveload.h"
struct Snes {
@@ -24,8 +23,8 @@ struct Snes {
Ppu* ppu;
Dma* dma;
Cart* cart;
- Input *input1;
- Input *input2;
+ uint16 input1_currentState;
+ uint16 input2_currentState;
// input
bool debug_cycles;
bool debug_apu_cycles;
@@ -73,7 +72,6 @@ struct Snes {
Snes* snes_init(uint8_t *ram);
void snes_free(Snes* snes);
void snes_reset(Snes* snes, bool hard);
-void snes_runFrame(Snes* snes);
// used by dma, cpu
uint8_t snes_readBBus(Snes* snes, uint8_t adr);
void snes_writeBBus(Snes* snes, uint8_t adr, uint8_t val);
diff --git a/src/variables.h b/src/variables.h
index 72efe01..b1714d1 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -877,6 +877,9 @@ extern uint8 ptr_layer2_is_bg;
#define stripe_image_upload_data ((uint8*)(g_ram+0x1837D))
#define graphics_decompressed_loading_letters ((uint8*)(g_ram+0x1977B))
#define spr86_wiggler_segment_pos_table ((uint8*)(g_ram+0x19A7B))
+
+#define my_flags (*(uint8*)(g_ram+0x19C7C))
+
#define blocks_map16_table_hi ((uint8*)(g_ram+0x1C800))
#define lm_ptr0 (*(LongPtr*)(g_ram + 0x1c000))