# HG changeset patch # User Mike Pavone # Date 1373474285 25200 # Node ID 7f84090ab1cd93dfb18ba32477aaa49b2cda11f5 # Parent f6fdde5407913935024b809c935613d1c66b26c6 Add config file parser and default config file diff -r f6fdde540791 -r 7f84090ab1cd Makefile --- 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 diff -r f6fdde540791 -r 7f84090ab1cd blastem.c --- 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; diff -r f6fdde540791 -r 7f84090ab1cd blastem.h --- 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); diff -r f6fdde540791 -r 7f84090ab1cd config.c --- /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 +#include +#include +#include +#include + +#include +#include +#include + +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 -r f6fdde540791 -r 7f84090ab1cd config.h --- /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_ + diff -r f6fdde540791 -r 7f84090ab1cd default.cfg --- /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 +