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))