ref: b9408616fb5bcab9de2f2aa19fa43ad94dd54e71
parent: d5a3d7f76cc92928222a981981ce8b8f682d2085
author: Snesrev <snesrev@protonmail.com>
date: Wed Aug 31 21:58:08 EDT 2022
Added gamepad support (thanks LukeUsher)
--- a/main.c
+++ b/main.c
@@ -10,7 +10,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#endif
-
+#include <math.h>
#include "snes/snes.h"
#include "tracing.h"
@@ -25,7 +25,7 @@
extern uint8 g_emulated_ram[0x20000];
bool g_run_without_emu = false;
-void PatchRom(uint8_t *rom);
+void PatchRom(uint8 *rom);
void SetSnes(Snes *snes);
void RunAudioPlayer();
void CopyStateAfterSnapshotRestore(bool is_reset);
@@ -33,11 +33,14 @@
void PatchCommand(char cmd);
bool RunOneFrame(Snes *snes, int input_state, bool turbo);
-static uint8_t* readFile(char* name, size_t* length);
-static bool loadRom(char* name, Snes* snes);
-static void playAudio(Snes *snes, SDL_AudioDeviceID device, int16_t* audioBuffer);
-static void renderScreen(SDL_Renderer* renderer, SDL_Texture* texture);
-static void handleInput(int keyCode, int modCode, bool pressed);
+static uint8 *ReadFile(char* name, size_t* length);
+static bool LoadRom(char* name, Snes* snes);
+static void PlayAudio(Snes *snes, SDL_AudioDeviceID device, int16 *audioBuffer);
+static void RenderScreen(SDL_Renderer *renderer, SDL_Texture *texture);
+static void HandleInput(int keyCode, int modCode, bool pressed);
+static void HandleGamepadInput(int button, bool pressed);
+static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
+static void OpenOneGamepad(int i);
int input1_current_state;
@@ -46,7 +49,7 @@
exit(1);
}
-void setButtonState(int button, bool pressed) {
+void SetButtonState(int button, bool pressed) {
// set key in constroller
if (pressed) {
input1_current_state |= 1 << button;
@@ -58,7 +61,7 @@
#undef main
int main(int argc, char** argv) {
// set up SDL
- if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) != 0) {
printf("Failed to init SDL: %s\n", SDL_GetError());
return 1;
}
@@ -97,7 +100,7 @@
Snes *snes = snes_init(g_emulated_ram), *snes_run = NULL;
if (argc >= 2 && !g_run_without_emu) {
// init snes, load rom
- bool loaded = loadRom(argv[1], snes);
+ bool loaded = LoadRom(argv[1], snes);
if (!loaded) {
puts("No rom loaded");
return 1;
@@ -117,6 +120,9 @@
ZeldaInitialize();
ZeldaReadSram(snes);
+ for (int i = 0; i < SDL_NumJoysticks(); i++)
+ OpenOneGamepad(i);
+
bool hooks = true;
// sdl loop
bool running = true;
@@ -135,6 +141,18 @@
while(running) {
while(SDL_PollEvent(&event)) {
switch(event.type) {
+ case SDL_CONTROLLERDEVICEADDED:
+ OpenOneGamepad(event.cdevice.which);
+ break;
+ case SDL_CONTROLLERAXISMOTION:
+ HandleGamepadAxisInput(event.caxis.which, event.caxis.axis, event.caxis.value);
+ break;
+ case SDL_CONTROLLERBUTTONDOWN:
+ HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
+ break;
+ case SDL_CONTROLLERBUTTONUP:
+ HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
+ break;
case SDL_KEYDOWN: {
bool skip_default = false;
switch(event.key.keysym.sym) {
@@ -166,11 +184,11 @@
break;
}
if (!skip_default)
- handleInput(event.key.keysym.sym, event.key.keysym.mod, true);
+ HandleInput(event.key.keysym.sym, event.key.keysym.mod, true);
break;
}
case SDL_KEYUP: {
- handleInput(event.key.keysym.sym, event.key.keysym.mod, false);
+ HandleInput(event.key.keysym.sym, event.key.keysym.mod, false);
break;
}
case SDL_QUIT: {
@@ -192,8 +210,8 @@
ZeldaDrawPpuFrame();
- playAudio(snes_run, device, audioBuffer);
- renderScreen(renderer, texture);
+ PlayAudio(snes_run, device, audioBuffer);
+ RenderScreen(renderer, texture);
SDL_RenderPresent(renderer); // vsyncs to 60 FPS
// if vsync isn't working, delay manually
@@ -228,7 +246,7 @@
return 0;
}
-static void playAudio(Snes *snes, SDL_AudioDeviceID device, int16_t* audioBuffer) {
+static void PlayAudio(Snes *snes, SDL_AudioDeviceID device, int16 *audioBuffer) {
// generate enough samples
if (!kIsOrigEmu && snes) {
while (snes->apu->dsp->sampleOffset < 534)
@@ -245,7 +263,7 @@
}
}
-static void renderScreen(SDL_Renderer* renderer, SDL_Texture* texture) {
+static void RenderScreen(SDL_Renderer* renderer, SDL_Texture* texture) {
void* pixels = NULL;
int pitch = 0;
if(SDL_LockTexture(texture, NULL, &pixels, &pitch) != 0) {
@@ -259,20 +277,20 @@
}
-static void handleInput(int keyCode, int keyMod, bool pressed) {
+static void HandleInput(int keyCode, int keyMod, bool pressed) {
switch(keyCode) {
- case SDLK_z: setButtonState(0, pressed); break;
- case SDLK_a: setButtonState(1, pressed); break;
- case SDLK_RSHIFT: setButtonState(2, pressed); break;
- case SDLK_RETURN: setButtonState(3, pressed); break;
- case SDLK_UP: setButtonState(4, pressed); break;
- case SDLK_DOWN: setButtonState(5, pressed); break;
- case SDLK_LEFT: setButtonState(6, pressed); break;
- case SDLK_RIGHT: setButtonState(7, pressed); break;
- case SDLK_x: setButtonState(8, pressed); break;
- case SDLK_s: setButtonState(9, pressed); break;
- case SDLK_d: setButtonState(10, pressed); break;
- case SDLK_c: setButtonState(11, pressed); break;
+ case SDLK_z: SetButtonState(0, pressed); break;
+ case SDLK_a: SetButtonState(1, pressed); break;
+ case SDLK_RSHIFT: SetButtonState(2, pressed); break;
+ case SDLK_RETURN: SetButtonState(3, pressed); break;
+ case SDLK_UP: SetButtonState(4, pressed); break;
+ case SDLK_DOWN: SetButtonState(5, pressed); break;
+ case SDLK_LEFT: SetButtonState(6, pressed); break;
+ case SDLK_RIGHT: SetButtonState(7, pressed); break;
+ case SDLK_x: SetButtonState(8, pressed); break;
+ case SDLK_s: SetButtonState(9, pressed); break;
+ case SDLK_d: SetButtonState(10, pressed); break;
+ case SDLK_c: SetButtonState(11, pressed); break;
case SDLK_BACKSPACE:
case SDLK_1:
case SDLK_2:
@@ -317,10 +335,66 @@
}
}
-static bool loadRom(char* name, Snes* snes) {
+static void OpenOneGamepad(int i) {
+ if (SDL_IsGameController(i)) {
+ SDL_GameController *controller = SDL_GameControllerOpen(i);
+ if (!controller)
+ fprintf(stderr, "Could not open gamepad %d: %s\n", i, SDL_GetError());
+ }
+}
+
+static void HandleGamepadInput(int button, bool pressed) {
+ switch (button) {
+ case SDL_CONTROLLER_BUTTON_A: SetButtonState(0, pressed); break;
+ case SDL_CONTROLLER_BUTTON_X: SetButtonState(1, pressed); break;
+ case SDL_CONTROLLER_BUTTON_BACK: SetButtonState(2, pressed); break;
+ case SDL_CONTROLLER_BUTTON_START: SetButtonState(3, pressed); break;
+ case SDL_CONTROLLER_BUTTON_DPAD_UP: SetButtonState(4, pressed); break;
+ case SDL_CONTROLLER_BUTTON_DPAD_DOWN: SetButtonState(5, pressed); break;
+ case SDL_CONTROLLER_BUTTON_DPAD_LEFT: SetButtonState(6, pressed); break;
+ case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: SetButtonState(7, pressed); break;
+ case SDL_CONTROLLER_BUTTON_B: SetButtonState(8, pressed); break;
+ case SDL_CONTROLLER_BUTTON_Y: SetButtonState(9, pressed); break;
+ case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: SetButtonState(10, pressed); break;
+ case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: SetButtonState(11, pressed); break;
+ }
+}
+
+static void HandleGamepadAxisInput(int gamepad_id, int axis, int value) {
+ static int last_gamepad_id, last_x, last_y;
+ if (axis == SDL_CONTROLLER_AXIS_LEFTX || axis == SDL_CONTROLLER_AXIS_LEFTY) {
+ // try to be smart if there's more than one gamepad
+ if (last_gamepad_id != gamepad_id && (value < -10000 || value > 10000)) {
+ last_gamepad_id = gamepad_id;
+ last_x = last_y = 0;
+ }
+ *(axis == SDL_CONTROLLER_AXIS_LEFTX ? &last_x : &last_y) = value;
+ int buttons = 0;
+ if (last_x * last_x + last_y * last_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?
+ static const uint8 kSegmentToButtons[8] = {
+ 1 << 4, // 0 = up
+ 1 << 4 | 1 << 7, // 1 = up, right
+ 1 << 7, // 2 = right
+ 1 << 7 | 1 << 5, // 3 = right, down
+ 1 << 5, // 4 = down
+ 1 << 5 | 1 << 6, // 5 = down, left
+ 1 << 6, // 6 = left
+ 1 << 6 | 1 << 4, // 7 = left, up
+ };
+ int angle = (int)(atan2f(last_y, last_x) * (float)(128 / M_PI) + 0.5f);
+ buttons = kSegmentToButtons[(uint8)(angle + 16 + 64) >> 5];
+ }
+ input1_current_state = input1_current_state & ~0xf0 | buttons;
+ }
+}
+
+static bool LoadRom(char* name, Snes* snes) {
size_t length = 0;
uint8_t* file = NULL;
- file = readFile(name, &length);
+ file = ReadFile(name, &length);
if(!file) Die("Failed to read file");
PatchRom(file);
@@ -331,7 +405,7 @@
}
-static uint8_t* readFile(char* name, size_t* length) {
+static uint8_t* ReadFile(char* name, size_t* length) {
FILE* f = fopen(name, "rb");
if(f == NULL) {
return NULL;