summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--blastem.c5
-rw-r--r--blastem.h2
-rw-r--r--config.c229
-rw-r--r--config.h8
-rw-r--r--default.cfg50
6 files changed, 295 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index b593bb9..7c38e95 100644
--- a/Makefile
+++ b/Makefile
@@ -18,8 +18,8 @@ AUDIOOBJS=ym2612.o psg.o wave.o
all : dis trans stateview blastem
-blastem : blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
- $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
+blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS)
+ $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS)
dis : dis.o 68kinst.o
$(CC) -o dis dis.o 68kinst.o
diff --git a/blastem.c b/blastem.c
index dfa950d..2a02b31 100644
--- a/blastem.c
+++ b/blastem.c
@@ -35,6 +35,8 @@ int headless = 0;
int z80_enabled = 1;
int frame_limit = 0;
+tern_node * config;
+
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
@@ -1871,13 +1873,14 @@ void detect_region()
int main(int argc, char ** argv)
{
if (argc < 2) {
- fputs("Usage: blastem FILENAME\n", stderr);
+ fputs("Usage: blastem FILENAME [options]\n", stderr);
return 1;
}
if(!load_rom(argv[1])) {
fprintf(stderr, "Failed to open %s for reading\n", argv[1]);
return 1;
}
+ config = load_config(argv[0]);
detect_region();
int width = -1;
int height = -1;
diff --git a/blastem.h b/blastem.h
index 63571d2..635794d 100644
--- a/blastem.h
+++ b/blastem.h
@@ -8,6 +8,7 @@
#include "vdp.h"
#include "psg.h"
#include "io.h"
+#include "config.h"
#define RAM_FLAG_ODD 0x1800
#define RAM_FLAG_EVEN 0x1000
@@ -30,6 +31,7 @@ typedef struct {
extern genesis_context * genesis;
extern int break_on_sync;
+extern tern_node * config;
uint16_t read_dma_value(uint32_t address);
m68k_context * debugger(m68k_context * context, uint32_t address);
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..06e36cd
--- /dev/null
+++ b/config.c
@@ -0,0 +1,229 @@
+#include "tern.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+char * alloc_concat(char * first, char * second)
+{
+ int flen = strlen(first);
+ int slen = strlen(second);
+ char * ret = malloc(flen + slen + 1);
+ memcpy(ret, first, flen);
+ memcpy(ret+flen, second, slen+1);
+ return ret;
+}
+
+char * alloc_concat_m(int num_parts, char ** parts)
+{
+ int total = 0;
+ for (int i = 0; i < num_parts; i++) {
+ total += strlen(parts[i]);
+ }
+ char * ret = malloc(total + 1);
+ *ret = 0;
+ for (int i = 0; i < num_parts; i++) {
+ strcat(ret, parts[i]);
+ }
+ return ret;
+}
+
+long file_size(FILE * f)
+{
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ return fsize;
+}
+
+char * strip_ws(char * text)
+{
+ while (*text && (!isprint(*text) || isblank(*text)))
+ {
+ text++;
+ }
+ char * ret = text;
+ text = ret + strlen(ret) - 1;
+ while (text > ret && (!isprint(*text) || isblank(*text)))
+ {
+ *text = 0;
+ text--;
+ }
+ return ret;
+}
+
+char * split_keyval(char * text)
+{
+ while (*text && !isblank(*text))
+ {
+ text++;
+ }
+ if (!*text) {
+ return text;
+ }
+ *text = 0;
+ return text+1;
+}
+
+#define MAX_NEST 30 //way more than I'll ever need
+
+tern_node * parse_config(char * config_data)
+{
+ char *state, *curline;
+ char *prefix = NULL;
+ int nest_level = 0;
+ char * prefix_parts[MAX_NEST];
+ int line = 1;
+ tern_node * head = NULL;
+ while ((curline = strtok_r(config_data, "\n", &state)))
+ {
+ config_data = NULL;
+ curline = strip_ws(curline);
+ int len = strlen(curline);
+ if (!len) {
+ continue;
+ }
+ if (curline[0] == '#') {
+ continue;
+ }
+ if (curline[0] == '}') {
+ if (!nest_level) {
+ fprintf(stderr, "unexpected } on line %d\n", line);
+ exit(1);
+ }
+ if (prefix) {
+ free(prefix);
+ prefix = NULL;
+ }
+ nest_level--;
+ curline = strip_ws(curline+1);
+ }
+ char * end = curline + len - 1;
+ if (*end == '{') {
+ *end = 0;
+ curline = strip_ws(curline);
+ prefix_parts[nest_level++] = curline;
+ if (prefix) {
+ free(prefix);
+ prefix = NULL;
+ }
+ } else {
+ if (nest_level && !prefix) {
+ prefix = alloc_concat_m(nest_level, prefix_parts);
+ }
+ char * val = strip_ws(split_keyval(curline));
+ char * key = curline;
+ if (*key) {
+ if (prefix) {
+ key = alloc_concat(prefix, key);
+ }
+ head = tern_insert_ptr(head, key, val);
+ if (prefix) {
+ free(key);
+ }
+ }
+ }
+ }
+ if (prefix) {
+ free(prefix);
+ }
+ return head;
+}
+
+tern_node * parse_config_file(char * config_path)
+{
+ tern_node * ret = NULL;
+ FILE * config_file = fopen(config_path, "r");
+ if (!config_file) {
+ goto open_fail;
+ }
+ long config_size = file_size(config_file);
+ if (!config_size) {
+ goto config_empty;
+ }
+ char * config_data = malloc(config_size);
+ if (fread(config_data, 1, config_size, config_file) != config_size) {
+ goto config_read_fail;
+ }
+ ret = parse_config(config_data);
+config_read_fail:
+ free(config_data);
+config_empty:
+ fclose(config_file);
+open_fail:
+ return ret;
+}
+
+char * readlink_alloc(char * path)
+{
+ char * linktext = NULL;
+ ssize_t linksize = 512;
+ ssize_t cursize = 0;
+ do {
+ if (linksize > cursize) {
+ cursize = linksize;
+ if (linktext) {
+ free(linktext);
+ }
+ }
+ linktext = malloc(cursize);
+ linksize = readlink(path, linktext, cursize-1);
+ if (linksize == -1) {
+ perror("readlink");
+ free(linktext);
+ linktext = NULL;
+ }
+ } while (linksize > cursize);
+ return linktext;
+}
+
+tern_node * load_config(char * expath)
+{
+ char * linktext;
+ char * home = getenv("HOME");
+ if (!home) {
+ goto load_in_app_dir;
+ }
+ char * path = alloc_concat(home, "/.config/blastem/blastem.cfg");
+ tern_node * ret = parse_config_file(path);
+ if (ret) {
+ goto success;
+ }
+ free(path);
+load_in_app_dir:
+
+ linktext = readlink_alloc("/proc/self/exe");
+ if (!linktext) {
+ goto link_prob;
+ }
+ char * cur;
+ int linksize = strlen(linktext);
+ for(cur = linktext + linksize - 1; cur != linktext; cur--)
+ {
+ if (*cur == '/') {
+ *cur = 0;
+ break;
+ }
+ }
+ if (cur == linktext) {
+ goto link_prob;
+ }
+ path = alloc_concat(linktext, "/default.cfg");
+ ret = parse_config_file(path);
+success:
+ return ret;
+link_prob:
+ if (linktext) {
+ free(linktext);
+ }
+no_proc:
+ //TODO: Fall back to using expath if /proc is not available
+ fputs("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n", stderr);
+ exit(1);
+}
+
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..3850c53
--- /dev/null
+++ b/config.h
@@ -0,0 +1,8 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+#include "tern.h"
+
+tern_node * load_config(char * expath);
+
+#endif //CONFIG_H_
+
diff --git a/default.cfg b/default.cfg
new file mode 100644
index 0000000..ac75505
--- /dev/null
+++ b/default.cfg
@@ -0,0 +1,50 @@
+
+bindings {
+ keys {
+ UP gamepads.1.up
+ DOWN gamepads.1.down
+ LEFT gamepads.1.left
+ RIGHT gamepads.1.right
+ a gamepads.1.a
+ s gamepads.1.b
+ d gamepads.1.c
+ q gamepads.1.x
+ w gamepads.1.y
+ e gamepads.1.z
+ f gamepads.1.mode
+ ENTER gamepads.1.start
+
+ [ ui.vdp_debug_mode
+ ] ui.vdp_debug_pal
+ u ui.enter_debugger
+ }
+ pads {
+ 0 {
+ dpads {
+ 0 {
+ up gamepads.2.up
+ down gamepads.2.down
+ left gamepads.2.left
+ right gamepads.2.right
+ }
+ }
+ buttons {
+ 0 gamepads.2.a
+ 1 gamepads.2.b
+ 2 gamepads.2.c
+ 3 gamepads.2.x
+ 4 gamepads.2.y
+ 5 gamepads.2.z
+ 6 gamepads.2.mode
+ 7 gamepads.2.start
+ }
+ }
+ }
+}
+
+video {
+ width 640
+}
+
+default_region U
+