changeset 430:7f84090ab1cd

Add config file parser and default config file
author Mike Pavone <pavone@retrodev.com>
date Wed, 10 Jul 2013 09:38:05 -0700
parents f6fdde540791
children 440efd7d27a9
files Makefile blastem.c blastem.h config.c config.h default.cfg
diffstat 6 files changed, 295 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Jul 09 20:51:42 2013 -0700
+++ b/Makefile	Wed Jul 10 09:38:05 2013 -0700
@@ -18,8 +18,8 @@
 
 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
--- a/blastem.c	Tue Jul 09 20:51:42 2013 -0700
+++ b/blastem.c	Wed Jul 10 09:38:05 2013 -0700
@@ -35,6 +35,8 @@
 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 @@
 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;
--- a/blastem.h	Tue Jul 09 20:51:42 2013 -0700
+++ b/blastem.h	Wed Jul 10 09:38:05 2013 -0700
@@ -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 @@
 
 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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.c	Wed Jul 10 09:38:05 2013 -0700
@@ -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);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/config.h	Wed Jul 10 09:38:05 2013 -0700
@@ -0,0 +1,8 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+#include "tern.h"
+
+tern_node * load_config(char * expath);
+
+#endif //CONFIG_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/default.cfg	Wed Jul 10 09:38:05 2013 -0700
@@ -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
+