ref: c9e2b4ced16118446f6901a4a3026bc06aa90f5e
parent: 46ae80aa51eebd835fb3467c8b89659649d533f8
author: Snesrev <snesrev@protonmail.com>
date: Thu Sep 1 23:22:45 EDT 2022
Add config file. Also fixed wonkiness in dpad and changed rendering. Dim's screen on pause (Thanks Nutzzz) Fixes #42 #40 #12 #43 #29
--- /dev/null
+++ b/config.c
@@ -1,0 +1,280 @@
+#include "config.h"
+#include "types.h"
+#include <stdio.h>
+#include <string.h>
+#include <SDL.h>
+
+
+enum {
+ kKeyMod_ScanCode = 0x200,
+ kKeyMod_Alt = 0x400,
+ kKeyMod_Shift = 0x800,
+ kKeyMod_Ctrl = 0x1000,
+};
+
+#define REMAP_SDL_KEYCODE(key) ((key) & SDLK_SCANCODE_MASK ? kKeyMod_ScanCode : 0) | (key) & (kKeyMod_ScanCode - 1)
+#define _(x) REMAP_SDL_KEYCODE(x)
+#define S(x) REMAP_SDL_KEYCODE(x) | kKeyMod_Shift
+#define C(x) REMAP_SDL_KEYCODE(x) | kKeyMod_Alt
+#define A(x) REMAP_SDL_KEYCODE(x) | kKeyMod_Ctrl
+#define N 0
+static const uint16 kDefaultKbdControls[kKeys_Total] = {
+ // Controls
+ _(SDLK_UP), _(SDLK_DOWN), _(SDLK_LEFT), _(SDLK_RIGHT), _(SDLK_RSHIFT), _(SDLK_RETURN), _(SDLK_x), _(SDLK_z), _(SDLK_s), _(SDLK_a), _(SDLK_d), _(SDLK_c),
+ // 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
+ S(SDLK_F1), S(SDLK_F2), S(SDLK_F3), S(SDLK_F4), S(SDLK_F5), S(SDLK_F6), S(SDLK_F7), S(SDLK_F8), S(SDLK_F9), S(SDLK_F10), N, N, N, N, N, N, N, N, N, N,
+ // Replay State
+ C(SDLK_F1), C(SDLK_F2), C(SDLK_F3), C(SDLK_F4), C(SDLK_F5), C(SDLK_F6), C(SDLK_F7), C(SDLK_F8), C(SDLK_F9), C(SDLK_F10), N, N, N, N, N, N, N, N, N, N,
+ // Load Ref State
+ _(SDLK_1), _(SDLK_2), _(SDLK_3), _(SDLK_4), _(SDLK_5), _(SDLK_6), _(SDLK_7), _(SDLK_8), _(SDLK_9), _(SDLK_0), _(SDLK_MINUS), _(SDLK_EQUALS), _(SDLK_BACKSPACE), N, N, N, N, N, N, N,
+ // Replay Ref State
+ C(SDLK_1), C(SDLK_2), C(SDLK_3), C(SDLK_4), C(SDLK_5), C(SDLK_6), C(SDLK_7), C(SDLK_8), C(SDLK_9), C(SDLK_0), C(SDLK_MINUS), C(SDLK_EQUALS), C(SDLK_BACKSPACE), N, N, N, N, N, N, N,
+ // CheatLife, CheatKeys, MigrateSnapshot, Fullscreen, Reset, Pause, PauseDimmed, Turbo, ZoomIn, ZoomOut
+ _(SDLK_w), _(SDLK_o), _(SDLK_k), A(SDLK_RETURN), _(SDLK_e), S(SDLK_p), _(SDLK_p), _(SDLK_t), N, N
+};
+#undef _
+#undef A
+#undef C
+#undef S
+#undef N
+
+typedef struct KeyNameId {
+ const char *name;
+ uint16 id, size;
+} KeyNameId;
+
+#define M(n) {#n, kKeys_##n, kKeys_##n##_Last - kKeys_##n + 1}
+#define S(n) {#n, kKeys_##n, 1}
+static const KeyNameId kKeyNameId[] = {
+ M(Controls), M(Load), M(Save), M(Replay), M(LoadRef), M(ReplayRef),
+ S(CheatLife), S(CheatKeys), S(MigrateSnapshot), S(Fullscreen), S(Reset),
+ S(Pause), S(PauseDimmed), S(Turbo), S(ZoomIn), S(ZoomOut),
+};
+#undef S
+#undef M
+typedef struct KeyMapHashEnt {
+ uint16 key, cmd, next;
+} KeyMapHashEnt;
+
+static uint16 keymap_hash_first[255];
+static KeyMapHashEnt *keymap_hash;
+static int keymap_hash_size;
+static bool has_keynameid[countof(kKeyNameId)];
+
+bool KeyMapHash_Add(uint16 key, uint16 cmd) {
+ if ((keymap_hash_size & 0xff) == 0) {
+ if (keymap_hash_size > 10000)
+ Die("Too many keys");
+ keymap_hash = realloc(keymap_hash, sizeof(KeyMapHashEnt) * (keymap_hash_size + 256));
+ }
+ int i = keymap_hash_size++;
+ KeyMapHashEnt *ent = &keymap_hash[i];
+ ent->key = key;
+ ent->cmd = cmd;
+ ent->next = 0;
+ int j = (uint32)key % 255;
+
+ uint16 *cur = &keymap_hash_first[j];
+ while (*cur) {
+ KeyMapHashEnt *ent = &keymap_hash[*cur - 1];
+ if (ent->key == key)
+ return false;
+ cur = &ent->next;
+ }
+ *cur = i + 1;
+ return true;
+}
+
+static int KeyMapHash_Find(uint16 key) {
+ int i = keymap_hash_first[key % 255];
+ while (i) {
+ KeyMapHashEnt *ent = &keymap_hash[i - 1];
+ if (ent->key == key)
+ return ent->cmd;
+ i = ent->next;
+ }
+ return -1;
+}
+
+int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod) {
+ if (code & ~(SDLK_SCANCODE_MASK | 0x1ff))
+ return -1;
+ int key = mod & KMOD_ALT ? kKeyMod_Alt : 0;
+ key |= mod & KMOD_CTRL ? kKeyMod_Ctrl : 0;
+ key |= mod & KMOD_SHIFT ? kKeyMod_Shift : 0;
+ key |= REMAP_SDL_KEYCODE(code);
+ return KeyMapHash_Find(key);
+}
+
+static char *NextDelim(char **s, int sep) {
+ char *r = *s;
+ if (r) {
+ while (r[0] == ' ' || r[0] == '\t')
+ r++;
+ char *t = strchr(r, sep);
+ *s = t ? (*t++ = 0, t) : NULL;
+ }
+ return r;
+}
+
+static inline int ToLower(int a) {
+ return a + (a >= 'A' && a <= 'Z') * 32;
+}
+
+static bool StringEqualsNoCase(const char *a, const char *b) {
+ for (;;) {
+ int aa = ToLower(*a++), bb = ToLower(*b++);
+ if (aa != bb)
+ return false;
+ if (aa == 0)
+ return true;
+ }
+}
+
+static bool StringStartsWithNoCase(const char *a, const char *b) {
+ for (;;) {
+ int aa = ToLower(*a++), bb = ToLower(*b++);
+ if (bb == 0)
+ return true;
+ if (aa != bb)
+ return false;
+ }
+}
+
+static void ParseKeyArray(char *value, int cmd, int size) {
+ char *s;
+ int i = 0;
+ for (; i < size && (s = NextDelim(&value, ',')) != NULL; i++, cmd++) {
+ if (*s == 0)
+ continue;
+ int key_with_mod = 0;
+ for (;;) {
+ if (StringStartsWithNoCase(s, "Shift+")) {
+ key_with_mod |= kKeyMod_Shift, s += 6;
+ } else if (StringStartsWithNoCase(s, "Ctrl+")) {
+ key_with_mod |= kKeyMod_Ctrl, s += 5;
+ } else if (StringStartsWithNoCase(s, "Alt+")) {
+ key_with_mod |= kKeyMod_Alt, s += 4;
+ } else {
+ break;
+ }
+ }
+ SDL_Keycode key = SDL_GetKeyFromName(s);
+ if (key == SDLK_UNKNOWN) {
+ fprintf(stderr, "Unknown key: '%s'\n", s);
+ continue;
+ }
+ if (!KeyMapHash_Add(key_with_mod | REMAP_SDL_KEYCODE(key), cmd))
+ fprintf(stderr, "Duplicate key: '%s'\n", s);
+ }
+}
+
+
+static void RegisterDefaultKeys() {
+ for (int i = 0; i < countof(kKeyNameId); i++) {
+ if (!has_keynameid[i]) {
+ int size = kKeyNameId[i].size, k = kKeyNameId[i].id;
+ for (int j = 0; j < size; j++, k++)
+ KeyMapHash_Add(kDefaultKbdControls[k], k);
+ }
+ }
+}
+
+
+static int GetIniSection(const char *s) {
+ if (StringEqualsNoCase(s, "[KeyMap]"))
+ return 0;
+ return -1;
+}
+
+static bool HandleIniConfig(int section, const char *key, char *value) {
+ if (section == 0) {
+ for (int i = 0; i < countof(kKeyNameId); i++) {
+ if (StringEqualsNoCase(key, kKeyNameId[i].name)) {
+ has_keynameid[i] = true;
+ ParseKeyArray(value, kKeyNameId[i].id, kKeyNameId[i].size);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+uint8 *ReadFile(const char *name, size_t *length) {
+ FILE *f = fopen(name, "rb");
+ if (f == NULL)
+ return NULL;
+ fseek(f, 0, SEEK_END);
+ size_t size = ftell(f);
+ rewind(f);
+ uint8 *buffer = (uint8 *)malloc(size + 1);
+ if (!buffer) Die("malloc failed");
+ // Always zero terminate so this function can be used also for strings.
+ buffer[size] = 0;
+ if (fread(buffer, 1, size, f) != size)
+ Die("fread failed");
+ fclose(f);
+ if (length) *length = size;
+ return buffer;
+}
+
+void ParseConfigFile() {
+ uint8 *file = ReadFile("zelda3.user.ini", NULL);
+ if (!file) {
+ file = ReadFile("zelda3.ini", NULL);
+ if (!file)
+ return;
+ }
+ fprintf(stderr, "Loading zelda3.ini\n");
+ int section = -2;
+ int lineno = 1;
+ char *p, *next_p = (char*)file;
+ for (; (p = next_p) != NULL; lineno++) {
+ // find end of line
+ char *eol = strchr(p, '\n');
+ next_p = eol ? eol + 1 : NULL;
+ eol = eol ? eol : p + strlen(p);
+ // strip comments
+ char *comment = memchr(p, '#', eol - p);
+ eol = (comment != 0) ? comment : eol;
+ // strip trailing whitespace
+ while (eol > p && (eol[-1] == '\r' || eol[-1] == ' ' || eol[-1] == '\t'))
+ eol--;
+ *eol = 0;
+ if (p == eol)
+ continue; // empty line
+ // strip leading whitespace
+ while (p[0] == ' ' || p[0] == '\t')
+ p++;
+ if (*p == '[') {
+ section = GetIniSection(p);
+ if (section < 0)
+ fprintf(stderr, "zelda3.ini:%d: Invalid .ini section %s\n", lineno, p);
+ } else if (section == -2) {
+ fprintf(stderr, "zelda3.ini:%d: Expecting [section]\n", lineno);
+ } else {
+ char *equals = memchr(p, '=', eol - p);
+ if (equals == NULL) {
+ fprintf(stderr, "zelda3.ini:%d: Expecting 'key=value'\n", lineno);
+ } else {
+ char *kr = equals;
+ while (kr > p && (kr[-1] == ' ' || kr[-1] == '\t'))
+ kr--;
+ *kr = 0;
+ char *v = equals + 1;
+ while (v[0] == ' ' || v[0] == '\t')
+ v++;
+ if (section >= 0 && !HandleIniConfig(section, p, v))
+ fprintf(stderr, "zelda3.ini:%d: Can't parse '%s'\n", lineno, p);
+ }
+ }
+ }
+ free(file);
+}
+
+void AfterConfigParse() {
+ RegisterDefaultKeys();
+}
--- /dev/null
+++ b/config.h
@@ -1,0 +1,35 @@
+#pragma once
+#include "types.h"
+#include <SDL_keycode.h>
+
+enum {
+ kKeys_Controls = 0,
+ kKeys_Controls_Last = kKeys_Controls + 11,
+ kKeys_Load,
+ kKeys_Load_Last = kKeys_Load + 19,
+ kKeys_Save,
+ kKeys_Save_Last = kKeys_Save + 19,
+ kKeys_Replay,
+ kKeys_Replay_Last = kKeys_Replay + 19,
+ kKeys_LoadRef,
+ kKeys_LoadRef_Last = kKeys_LoadRef + 19,
+ kKeys_ReplayRef,
+ kKeys_ReplayRef_Last = kKeys_ReplayRef + 19,
+ kKeys_CheatLife,
+ kKeys_CheatKeys,
+ kKeys_MigrateSnapshot,
+ kKeys_Fullscreen,
+ kKeys_Reset,
+ kKeys_Pause,
+ kKeys_PauseDimmed,
+ kKeys_Turbo,
+ kKeys_ZoomIn,
+ kKeys_ZoomOut,
+ kKeys_Total,
+};
+
+uint8 *ReadFile(const char *name, size_t *length);
+void ParseConfigFile();
+void AfterConfigParse();
+
+int FindCmdForSdlKey(SDL_Keycode code, SDL_Keymod mod);
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -18,13 +18,16 @@
#include "variables.h"
#include "zelda_rtl.h"
+#include "config.h"
extern Ppu *GetPpuForRendering();
extern Dsp *GetDspForRendering();
-
+extern Snes *g_snes;
extern uint8 g_emulated_ram[0x20000];
bool g_run_without_emu = false;
+static int g_input1_state;
+
void PatchRom(uint8 *rom);
void SetSnes(Snes *snes);
void RunAudioPlayer();
@@ -33,8 +36,7 @@
void PatchCommand(char cmd);
bool RunOneFrame(Snes *snes, int input_state, bool turbo);
-static uint8 *ReadFile(char* name, size_t* length);
-static bool LoadRom(char* name, Snes* snes);
+static bool LoadRom(const char *name, Snes *snes);
static void PlayAudio(Snes *snes, SDL_AudioDeviceID device, int16 *audioBuffer);
static void RenderScreen(SDL_Window *window, SDL_Renderer *renderer, SDL_Texture *texture, bool fullscreen);
static void HandleInput(int keyCode, int modCode, bool pressed);
@@ -41,11 +43,24 @@
static void HandleGamepadInput(int button, bool pressed);
static void HandleGamepadAxisInput(int gamepad_id, int axis, int value);
static void OpenOneGamepad(int i);
+
static inline int IntMin(int a, int b) { return a < b ? a : b; }
static inline int IntMax(int a, int b) { return a > b ? a : b; }
-static int input1_current_state;
+enum {
+ kRenderWidth = 512,
+ kRenderHeight = 480,
+ kDefaultZoom = 2,
+};
+
+static uint32 g_win_flags = SDL_WINDOW_RESIZABLE;
+static SDL_Window *g_window;
+static SDL_Renderer *g_renderer;
+static uint8 g_paused, g_turbo = true;
+static int current_zoom = kDefaultZoom;
+static uint8 g_gamepad_buttons;
+
void NORETURN Die(const char *error) {
fprintf(stderr, "Error: %s\n", error);
exit(1);
@@ -54,22 +69,15 @@
void SetButtonState(int button, bool pressed) {
// set key in constroller
if (pressed) {
- input1_current_state |= 1 << button;
+ g_input1_state |= 1 << button;
} else {
- input1_current_state &= ~(1 << button);
+ g_input1_state &= ~(1 << button);
}
}
-enum {
- kRenderWidth = 512,
- kRenderHeight = 480,
- kDefaultZoom = 2,
-};
-static int current_zoom = kDefaultZoom;
-
-void DoZoom(SDL_Window *window, SDL_Renderer *renderer, int zoom_step) {
- int screen = SDL_GetWindowDisplayIndex(window);
+void DoZoom(int zoom_step) {
+ int screen = SDL_GetWindowDisplayIndex(g_window);
if (screen < 0) screen = 0;
int max_zoom = kDefaultZoom * 5;
SDL_Rect bounds;
@@ -77,7 +85,7 @@
// note this takes into effect Windows display scaling, i.e., resolution is divided by scale factor
if (SDL_GetDisplayUsableBounds(screen, &bounds) == 0) {
// this call may take a while before it is reported by Windows (or not at all in my testing)
- if (SDL_GetWindowBordersSize(window, &bt, &bl, &bb, &br) != 0) {
+ if (SDL_GetWindowBordersSize(g_window, &bt, &bl, &bb, &br) != 0) {
// guess based on Windows 10/11 defaults
bl = br = bb = 1;
bt = 31;
@@ -91,9 +99,9 @@
current_zoom = new_zoom;
int w = new_zoom * (kRenderWidth / kDefaultZoom);
int h = new_zoom * (kRenderHeight / kDefaultZoom);
- SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
- SDL_RenderSetLogicalSize(renderer, w, h);
- SDL_SetWindowSize(window, w, h);
+
+ //SDL_RenderSetLogicalSize(g_renderer, w, h);
+ SDL_SetWindowSize(g_window, w, h);
if (bt >= 0) {
// Center the window on top of the mouse
int mx, my;
@@ -100,9 +108,9 @@
SDL_GetGlobalMouseState(&mx, &my);
int wx = IntMax(IntMin(mx - w / 2, bounds.x + bounds.w - bl - br - w), bounds.x + bl);
int wy = IntMax(IntMin(my - h / 2, bounds.y + bounds.h - bt - bb - h), bounds.y + bt);
- SDL_SetWindowPosition(window, wx, wy);
+ SDL_SetWindowPosition(g_window, wx, wy);
} else {
- SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+ SDL_SetWindowPosition(g_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
}
}
@@ -114,17 +122,20 @@
#undef main
int main(int argc, char** argv) {
+ ParseConfigFile();
+ AfterConfigParse();
+
// set up SDL
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER) != 0) {
printf("Failed to init SDL: %s\n", SDL_GetError());
return 1;
}
- uint32 win_flags = SDL_WINDOWPOS_UNDEFINED;
- SDL_Window* window = SDL_CreateWindow("Zelda3", SDL_WINDOWPOS_UNDEFINED, win_flags, kRenderWidth, kRenderHeight, 0);
+ SDL_Window* window = SDL_CreateWindow("Zelda3", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, kRenderWidth, kRenderHeight, g_win_flags);
if(window == NULL) {
printf("Failed to create window: %s\n", SDL_GetError());
return 1;
}
+ g_window = window;
SDL_SetWindowHitTest(window, HitTestCallback, NULL);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if(renderer == NULL) {
@@ -131,11 +142,15 @@
printf("Failed to create renderer: %s\n", SDL_GetError());
return 1;
}
+ g_renderer = renderer;
+ SDL_RenderSetLogicalSize(renderer, 512, 480);
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, kRenderWidth, kRenderHeight);
if(texture == NULL) {
printf("Failed to create texture: %s\n", SDL_GetError());
return 1;
}
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
+
SDL_AudioSpec want, have;
SDL_AudioDeviceID device;
SDL_memset(&want, 0, sizeof(want));
@@ -178,19 +193,10 @@
for (int i = 0; i < SDL_NumJoysticks(); i++)
OpenOneGamepad(i);
- bool hooks = true;
- // sdl loop
bool running = true;
SDL_Event event;
uint32 lastTick = SDL_GetTicks();
uint32 curTick = 0;
- uint32 delta = 0;
- int numFrames = 0;
- bool cpuNext = false;
- bool spcNext = false;
- int counter = 0;
- bool paused = false;
- bool turbo = true;
uint32 frameCtr = 0;
while(running) {
@@ -209,69 +215,41 @@
HandleGamepadInput(event.cbutton.button, event.cbutton.state == SDL_PRESSED);
break;
case SDL_MOUSEWHEEL:
- if ((win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 && event.wheel.y != 0 && SDL_GetModState() & KMOD_CTRL)
- DoZoom(window, renderer, event.wheel.y > 0 ? 1 : -1);
+ if ((g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 && event.wheel.y != 0 && SDL_GetModState() & KMOD_CTRL)
+ DoZoom(event.wheel.y > 0 ? 1 : -1);
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT && event.button.state == SDL_PRESSED && event.button.clicks == 2) {
- if ((win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 && SDL_GetModState() & KMOD_SHIFT) {
- win_flags ^= SDL_WINDOW_BORDERLESS;
- SDL_SetWindowBordered(window, (win_flags & SDL_WINDOW_BORDERLESS) == 0);
+ if ((g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == 0 && SDL_GetModState() & KMOD_SHIFT) {
+ g_win_flags ^= SDL_WINDOW_BORDERLESS;
+ SDL_SetWindowBordered(g_window, (g_win_flags & SDL_WINDOW_BORDERLESS) == 0);
}
}
break;
- case SDL_KEYDOWN: {
- bool skip_default = false;
- switch(event.key.keysym.sym) {
- case SDLK_e:
- if (snes) {
- snes_reset(snes, event.key.keysym.sym == SDLK_e);
- CopyStateAfterSnapshotRestore(true);
- }
- break;
- case SDLK_p: paused ^= true; break;
- case SDLK_w:
- PatchCommand('w');
- break;
- case SDLK_o:
- PatchCommand('o');
- break;
- case SDLK_k:
- PatchCommand('k');
- break;
- case SDLK_t:
- turbo = !turbo;
- break;
- case SDLK_RETURN:
- if (event.key.keysym.mod & KMOD_ALT) {
- win_flags ^= SDL_WINDOW_FULLSCREEN_DESKTOP;
- SDL_SetWindowFullscreen(window, win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP);
- skip_default = true;
- }
- break;
- }
- if (!skip_default)
- HandleInput(event.key.keysym.sym, event.key.keysym.mod, true);
+ case SDL_KEYDOWN:
+ HandleInput(event.key.keysym.sym, event.key.keysym.mod, true);
break;
- }
- case SDL_KEYUP: {
+ case SDL_KEYUP:
HandleInput(event.key.keysym.sym, event.key.keysym.mod, false);
break;
- }
- case SDL_QUIT: {
+ case SDL_QUIT:
running = false;
break;
}
}
- }
- if (paused) {
+ if (g_paused) {
SDL_Delay(16);
continue;
}
- bool is_turbo = RunOneFrame(snes_run, input1_current_state, (counter++ & 0x7f) != 0 && turbo);
+ // Clear gamepad inputs when joypad directional inputs to avoid wonkiness
+ int inputs = g_input1_state;
+ if (g_input1_state & 0xf0)
+ g_gamepad_buttons = 0;
+ bool is_turbo = RunOneFrame(snes_run, g_input1_state | g_gamepad_buttons, (frameCtr++ & 0x7f) != 0 && g_turbo);
+
if (is_turbo)
continue;
@@ -278,7 +256,7 @@
ZeldaDrawPpuFrame();
PlayAudio(snes_run, device, audioBuffer);
- RenderScreen(window, renderer, texture, (win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0);
+ RenderScreen(window, renderer, texture, (g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0);
SDL_RenderPresent(renderer); // vsyncs to 60 FPS
// if vsync isn't working, delay manually
@@ -286,10 +264,10 @@
static const uint8 delays[3] = { 17, 17, 16 }; // 60 fps
#if 1
- lastTick += delays[frameCtr++ % 3];
+ lastTick += delays[frameCtr % 3];
if (lastTick > curTick) {
- delta = lastTick - curTick;
+ uint32 delta = lastTick - curTick;
if (delta > 500) {
lastTick = curTick - 500;
delta = 500;
@@ -337,83 +315,66 @@
printf("Failed to lock texture: %s\n", SDL_GetError());
return;
}
-
ppu_putPixels(GetPpuForRendering(), (uint8_t*) pixels);
SDL_UnlockTexture(texture);
-
- SDL_DisplayMode display_mode;
- if (fullscreen && SDL_GetWindowDisplayMode(window, &display_mode) == 0) {
- uint32 w = display_mode.w, h = display_mode.h;
- if (w * 15 < h * 16)
- h = w * 15 / 16; // limit height
- else
- w = h * 16 / 15; // limit width
- SDL_Rect r = { (display_mode.w - w) / 2, (display_mode.h - h) / 2, w, h };
- SDL_RenderCopy(renderer, texture, NULL, &r);
- } else {
- SDL_RenderCopy(renderer, texture, NULL, NULL);
- }
+ SDL_RenderCopy(renderer, texture, NULL, NULL);
}
-
-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_BACKSPACE:
- case SDLK_1:
- case SDLK_2:
- case SDLK_3:
- case SDLK_4:
- case SDLK_5:
- case SDLK_6:
- case SDLK_7:
- case SDLK_8:
- case SDLK_9:
- case SDLK_0:
- case SDLK_MINUS:
- case SDLK_EQUALS:
- if (pressed) {
- SaveLoadSlot(
- (keyMod & KMOD_CTRL) != 0 ? kSaveLoad_Replay : kSaveLoad_Load,
- 256 + (keyCode == SDLK_0 ? 9 :
- keyCode == SDLK_MINUS ? 10 :
- keyCode == SDLK_EQUALS ? 11 :
- keyCode == SDLK_BACKSPACE ? 12 :
- keyCode - SDLK_1));
+static void HandleCommand(uint32 j, bool pressed) {
+ if (j <= kKeys_Controls_Last) {
+ static const uint8 kKbdRemap[12] = { 4, 5, 6, 7, 2, 3, 8, 0, 9, 1, 10, 11 };
+ SetButtonState(kKbdRemap[j], pressed);
+ return;
+ }
+ if (!pressed)
+ return;
+ if (j <= kKeys_Load_Last) {
+ SaveLoadSlot(kSaveLoad_Load, j - kKeys_Load);
+ } else if (j <= kKeys_Save_Last) {
+ SaveLoadSlot(kSaveLoad_Save, j - kKeys_Save);
+ } else if (j <= kKeys_Replay_Last) {
+ SaveLoadSlot(kSaveLoad_Replay, j - kKeys_Replay);
+ } else if (j <= kKeys_LoadRef_Last) {
+ SaveLoadSlot(kSaveLoad_Load, 256 + j - kKeys_LoadRef);
+ } else if (j <= kKeys_ReplayRef_Last) {
+ SaveLoadSlot(kSaveLoad_Replay, 256 + j - kKeys_ReplayRef);
+ } else {
+ switch (j) {
+ case kKeys_CheatLife: PatchCommand('w'); break;
+ case kKeys_CheatKeys: PatchCommand('o'); break;
+ case kKeys_MigrateSnapshot: PatchCommand('k'); break;
+ case kKeys_Fullscreen:
+ g_win_flags ^= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ SDL_SetWindowFullscreen(g_window, g_win_flags & SDL_WINDOW_FULLSCREEN_DESKTOP);
+ break;
+ case kKeys_Reset:
+ snes_reset(g_snes, true);
+ CopyStateAfterSnapshotRestore(true);
+ break;
+ case kKeys_Pause: g_paused = !g_paused; break;
+ case kKeys_PauseDimmed:
+ g_paused = !g_paused;
+ if (g_paused) {
+ SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 159);
+ SDL_RenderFillRect(g_renderer, NULL);
+ SDL_RenderPresent(g_renderer);
}
break;
-
- case SDLK_F1:
- case SDLK_F2:
- case SDLK_F3:
- case SDLK_F4:
- case SDLK_F5:
- case SDLK_F6:
- case SDLK_F7:
- case SDLK_F8:
- case SDLK_F9:
- case SDLK_F10:
- if (pressed) {
- SaveLoadSlot(
- (keyMod & KMOD_CTRL) != 0 ? kSaveLoad_Replay :
- (keyMod & KMOD_SHIFT) != 0 ? kSaveLoad_Save : kSaveLoad_Load,
- keyCode - SDLK_F1);
- }
- break;
+ case kKeys_Turbo: g_turbo = !g_turbo; break;
+ case kKeys_ZoomIn: DoZoom(1); break;
+ case kKeys_ZoomOut: DoZoom(-1); break;
+ default: assert(0);
+ }
}
}
+static void HandleInput(int keyCode, int keyMod, bool pressed) {
+ int j = FindCmdForSdlKey(keyCode, keyMod);
+ if (j >= 0)
+ HandleCommand(j, pressed);
+}
+
static void OpenOneGamepad(int i) {
if (SDL_IsGameController(i)) {
SDL_GameController *controller = SDL_GameControllerOpen(i);
@@ -466,14 +427,13 @@
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;
+ g_gamepad_buttons = buttons;
}
}
-static bool LoadRom(char* name, Snes* snes) {
+static bool LoadRom(const char *name, Snes *snes) {
size_t length = 0;
- uint8_t* file = NULL;
- file = ReadFile(name, &length);
+ uint8 *file = ReadFile(name, &length);
if(!file) Die("Failed to read file");
PatchRom(file);
@@ -484,18 +444,3 @@
}
-static uint8_t* ReadFile(char* name, size_t* length) {
- FILE* f = fopen(name, "rb");
- if(f == NULL) {
- return NULL;
- }
- fseek(f, 0, SEEK_END);
- int size = ftell(f);
- rewind(f);
- uint8_t* buffer = (uint8_t *)malloc(size);
- if (!buffer) Die("malloc failed");
- fread(buffer, size, 1, f);
- fclose(f);
- *length = size;
- return buffer;
-}
--- a/types.h
+++ b/types.h
@@ -72,4 +72,6 @@
#define uvram (*(UploadVram_3*)(&g_ram[0x1000]))
typedef void PlayerHandlerFunc();
-typedef void HandlerFuncK(int k);
\ No newline at end of file
+typedef void HandlerFuncK(int k);
+
+void NORETURN Die(const char *error);
--- /dev/null
+++ b/zelda3.ini
@@ -1,0 +1,27 @@
+[KeyMap]
+# Change what keyboard keys map to the joypad
+# Order: Up, Down, Left, Right, Select, Start, A, B, X, Y, L, R
+
+# This default is suitable for QWERTY keyboards.
+Controls = Up, Down, Left, Right, Right Shift, Return, x, z, s, a, d, c
+
+# This one is suitable for AZERTY keyboards.
+#Controls = Up, Down, Left, Right, Right Shift, Return, x, w, s, q, d, c
+
+CheatLife = w
+CheatKeys = o
+MigrateSnapshot = k
+Fullscreen = Alt+Return
+Reset = Ctrl+e
+Pause = Shift+p
+PauseDimmed = p
+Turbo = t
+ZoomIn = Ctrl+Up
+ZoomOut = Ctrl+Down
+
+Load = F1, F2, F3, F4, F5, F6, F7, F8, F9, F10
+Save = Shift+F1,Shift+F2,Shift+F3,Shift+F4,Shift+F5,Shift+F6,Shift+F7,Shift+F8,Shift+F9,Shift+F10
+Replay= Ctrl+F1,Ctrl+F2,Ctrl+F3,Ctrl+F4,Ctrl+F5,Ctrl+F6,Ctrl+F7,Ctrl+F8,Ctrl+F9,Ctrl+F10
+
+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
--- a/zelda3.vcxproj
+++ b/zelda3.vcxproj
@@ -125,6 +125,7 @@
<ItemGroup>
<ClCompile Include="ancilla.c" />
<ClCompile Include="attract.c" />
+ <ClCompile Include="config.c" />
<ClCompile Include="dungeon.c" />
<ClCompile Include="ending.c" />
<ClCompile Include="hud.c" />
@@ -198,6 +199,7 @@
<ItemGroup>
<ClInclude Include="ancilla.h" />
<ClInclude Include="attract.h" />
+ <ClInclude Include="config.h" />
<ClInclude Include="dungeon.h" />
<ClInclude Include="ending.h" />
<ClInclude Include="hud.h" />
--- a/zelda3.vcxproj.filters
+++ b/zelda3.vcxproj.filters
@@ -115,6 +115,9 @@
<ClCompile Include="zelda_cpu_infra.c">
<Filter>Zelda</Filter>
</ClCompile>
+ <ClCompile Include="config.c">
+ <Filter>Zelda</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="snes\apu.h">
@@ -230,6 +233,9 @@
</ClInclude>
<ClInclude Include="snes\dsp_regs.h">
<Filter>Snes</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Zelda</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
--- a/zelda_rtl.h
+++ b/zelda_rtl.h
@@ -195,7 +195,6 @@
void ClearOamBuffer();
void Startup_InitializeMemory();
void LoadSongBank(const uint8 *p);
-void NORETURN Die(const char *error);
void ZeldaWriteSram();
void ZeldaReadSram(Snes *snes);