changeset 1326:071e761bcdcf

Fix a deficiency in the way types were handled in my ternary tree. Fixes in which some paths that were constructed from a template with variables would sometimes get an extra garbage character thrown in
author Michael Pavone <pavone@retrodev.com>
date Fri, 21 Apr 2017 23:35:32 -0700
parents 58bfbed6cdb5
children 57637d17b59e
files blastem.c genesis.c io.c menu.c render_sdl.c romdb.c sms.c tern.c tern.h
diffstat 9 files changed, 138 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/blastem.c	Fri Apr 21 23:35:32 2017 -0700
@@ -140,7 +140,7 @@
 void setup_saves(char *fname, rom_info *info, system_header *context)
 {
 	static uint8_t persist_save_registered;
-	char *savedir_template = tern_find_path(config, "ui\0save_path\0").ptrval;
+	char *savedir_template = tern_find_path(config, "ui\0save_path\0", TVAL_PTR).ptrval;
 	if (!savedir_template) {
 		savedir_template = "$USERDATA/blastem/$ROMNAME";
 	}
@@ -322,7 +322,7 @@
 	uint8_t menu = !loaded;
 	if (!loaded) {
 		//load menu
-		romfname = tern_find_path(config, "ui\0rom\0").ptrval;
+		romfname = tern_find_path(config, "ui\0rom\0", TVAL_PTR).ptrval;
 		if (!romfname) {
 			romfname = "menu.bin";
 		}
@@ -349,7 +349,7 @@
 	}
 	
 	int def_width = 0;
-	char *config_width = tern_find_path(config, "video\0width\0").ptrval;
+	char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
 	if (config_width) {
 		def_width = atoi(config_width);
 	}
@@ -359,7 +359,7 @@
 	width = width < 320 ? def_width : width;
 	height = height < 240 ? (width/320) * 240 : height;
 
-	char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0").ptrval;
+	char *config_fullscreen = tern_find_path(config, "video\0fullscreen\0", TVAL_PTR).ptrval;
 	if (config_fullscreen && !strcmp("on", config_fullscreen)) {
 		fullscreen = !fullscreen;
 	}
--- a/genesis.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/genesis.c	Fri Apr 21 23:35:32 2017 -0700
@@ -822,7 +822,7 @@
 void set_region(genesis_context *gen, rom_info *info, uint8_t region)
 {
 	if (!region) {
-		char * def_region = tern_find_path_default(config, "system\0default_region\0", (tern_val){.ptrval = "U"}).ptrval;
+		char * def_region = tern_find_path_default(config, "system\0default_region\0", (tern_val){.ptrval = "U"}, TVAL_PTR).ptrval;
 		if (!info->regions || (info->regions & translate_region_char(toupper(*def_region)))) {
 			region = translate_region_char(toupper(*def_region));
 		} else {
@@ -1001,10 +1001,10 @@
 	init_vdp_context(gen->vdp, gen->version_reg & 0x40);
 	gen->vdp->system = &gen->header;
 	gen->frame_end = vdp_cycles_to_frame_end(gen->vdp);
-	char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval;
+	char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0", TVAL_PTR).ptrval;
 	gen->max_cycles = config_cycles ? atoi(config_cycles) : DEFAULT_SYNC_INTERVAL;
 
-	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval;
+	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
 	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : DEFAULT_LOWPASS_CUTOFF;
 	
 	gen->ym = malloc(sizeof(ym2612_context));
@@ -1032,7 +1032,7 @@
 	gen->cart = main_rom;
 	gen->lock_on = lock_on;
 	gen->work_ram = calloc(2, RAM_WORDS);
-	if (!strcmp("random", tern_find_path_default(config, "system\0ram_init\0", (tern_val){.ptrval = "zero"}).ptrval))
+	if (!strcmp("random", tern_find_path_default(config, "system\0ram_init\0", (tern_val){.ptrval = "zero"}, TVAL_PTR).ptrval))
 	{
 		srand(time(NULL));
 		for (int i = 0; i < RAM_WORDS; i++)
@@ -1124,7 +1124,7 @@
 		byteswap_rom(lock_on_size, lock_on);
 	}
 #endif
-	char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0").ptrval;
+	char *m68k_divider = tern_find_path(config, "clocks\0m68k_divider\0", TVAL_PTR).ptrval;
 	if (!m68k_divider) {
 		m68k_divider = "7";
 	}
--- a/io.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/io.c	Fri Apr 21 23:35:32 2017 -0700
@@ -494,7 +494,7 @@
 			current_system->soft_reset(current_system);
 			break;
 		case UI_SCREENSHOT: {
-			char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0").ptrval;
+			char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0", TVAL_PTR).ptrval;
 			if (!screenshot_base) {
 				screenshot_base = "$HOME";
 			}
@@ -505,7 +505,7 @@
 			time_t now = time(NULL);
 			struct tm local_store;
 			char fname_part[256];
-			char *template = tern_find_path(config, "ui\0screenshot_template\0").ptrval;
+			char *template = tern_find_path(config, "ui\0screenshot_template\0", TVAL_PTR).ptrval;
 			if (!template) {
 				template = "blastem_%c.ppm";
 			}
@@ -876,10 +876,10 @@
 {
 	current_io = io;
 	io_port * ports = current_io->ports;
-	tern_node *io_nodes = tern_get_node(tern_find_path(config, "io\0devices\0"));
-	char * io_1 = rom->port1_override ? rom->port1_override : tern_find_ptr(io_nodes, "1");
-	char * io_2 = rom->port2_override ? rom->port2_override : tern_find_ptr(io_nodes, "2");
-	char * io_ext = rom->ext_override ? rom->ext_override : tern_find_ptr(io_nodes, "ext");
+	tern_node *io_nodes = tern_find_path(config, "io\0devices\0", TVAL_NODE).ptrval;
+	char * io_1 = rom->port1_override ? rom->port1_override : io_nodes ? tern_find_ptr(io_nodes, "1") : NULL;
+	char * io_2 = rom->port2_override ? rom->port2_override : io_nodes ? tern_find_ptr(io_nodes, "2") : NULL;
+	char * io_ext = rom->ext_override ? rom->ext_override : io_nodes ? tern_find_ptr(io_nodes, "ext") : NULL;
 
 	process_device(io_1, ports);
 	process_device(io_2, ports+1);
@@ -901,7 +901,7 @@
 #ifndef _WIN32
 		if (ports[i].device_type == IO_SEGA_PARALLEL)
 		{
-			char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0").ptrval;
+			char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0", TVAL_PTR).ptrval;
 			if (!pipe_name)
 			{
 				warning("IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i));
@@ -927,7 +927,7 @@
 				}
 			}
 		} else if (ports[i].device_type == IO_GENERIC) {
-			char *sock_name = tern_find_path(config, "io\0socket\0").ptrval;
+			char *sock_name = tern_find_path(config, "io\0socket\0", TVAL_PTR).ptrval;
 			if (!sock_name)
 			{
 				warning("IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i));
@@ -1013,7 +1013,7 @@
 	int       mouseidx;
 } pmb_state;
 
-void process_mouse_button(char *buttonstr, tern_val value, void *data)
+void process_mouse_button(char *buttonstr, tern_val value, uint8_t valtype, void *data)
 {
 	pmb_state *state = data;
 	int buttonnum = atoi(buttonstr);
@@ -1021,6 +1021,10 @@
 		warning("Mouse button %s is out of the supported range of 1-8\n", buttonstr);
 		return;
 	}
+	if (valtype != TVAL_PTR) {
+		warning("Mouse button %s is not a scalar value!\n", buttonstr);
+		return;
+	}
 	buttonnum--;
 	int ui_func, devicenum, button;
 	int bindtype = parse_binding_target(value.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &devicenum, &button);
@@ -1045,17 +1049,17 @@
 
 }
 
-void process_mouse(char *mousenum, tern_val value, void *data)
+void process_mouse(char *mousenum, tern_val value, uint8_t valtype, void *data)
 {
 	tern_node **buttonmaps = data;
-	tern_node *mousedef = tern_get_node(value);
+	if (valtype != TVAL_NODE) {
+		warning("Binding for mouse %s is a scalar!\n", mousenum);
+		return;
+	}
+	tern_node *mousedef = value.ptrval;
 	tern_node *padbuttons = buttonmaps[0];
 	tern_node *mousebuttons = buttonmaps[1];
 
-	if (!mousedef) {
-		warning("Binding for mouse %s is a scalar!\n", mousenum);
-		return;
-	}
 	int mouseidx = atoi(mousenum);
 	if (mouseidx < 0 || mouseidx >= MAX_MICE) {
 		warning("Mouse numbers must be between 0 and %d, but %d is not\n", MAX_MICE, mouseidx);
@@ -1074,7 +1078,7 @@
 			warning("Mouse motion can't be bound to target %s\n", motion);
 		}
 	}
-	tern_node *buttons = tern_get_node(tern_find_path(mousedef, "buttons\0\0"));
+	tern_node *buttons = tern_find_path(mousedef, "buttons\0\0", TVAL_NODE).ptrval;
 	if (buttons) {
 		pmb_state state = {padbuttons, mousebuttons, mouseidx};
 		tern_foreach(buttons, process_mouse_button, &state);
@@ -1087,11 +1091,15 @@
 	tern_node *mousebuttons;
 } pad_button_state;
 
-void process_pad_button(char *key, tern_val val, void *data)
+void process_pad_button(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	pad_button_state *state = data;
 	int hostpadnum = state->padnum;
 	int ui_func, padnum, button;
+	if (valtype != TVAL_PTR) {
+		warning("Pad button %s has a non-scalar value\n", key);
+		return;
+	}
 	int bindtype = parse_binding_target(val.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &padnum, &button);
 	char *end;
 	long hostbutton = strtol(key, &end, 10);
@@ -1129,12 +1137,16 @@
 	}
 }
 
-void process_pad_axis(char *key, tern_val val, void *data)
+void process_pad_axis(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	key = strdup(key);
 	pad_button_state *state = data;
 	int hostpadnum = state->padnum;
 	int ui_func, padnum, button;
+	if (valtype != TVAL_PTR) {
+		warning("Mapping for axis %s has a non-scalar value", key);
+		return;
+	}
 	int bindtype = parse_binding_target(val.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &padnum, &button);
 	char *modifier = strchr(key, '.');
 	int positive = 1;
@@ -1226,19 +1238,19 @@
 	if (joystick > MAX_JOYSTICKS) {
 		return;
 	}
-	tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0"));
+	tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
 	if (pads) {
 		char numstr[11];
 		sprintf(numstr, "%d", joystick);
-		tern_node * pad = tern_find_ptr(pads, numstr);
+		tern_node * pad = tern_find_node(pads, numstr);
 		if (pad) {
-			tern_node * dpad_node = tern_find_ptr(pad, "dpads");
+			tern_node * dpad_node = tern_find_node(pad, "dpads");
 			if (dpad_node) {
 				for (int dpad = 0; dpad < 10; dpad++)
 				{
 					numstr[0] = dpad + '0';
 					numstr[1] = 0;
-					tern_node * pad_dpad = tern_find_ptr(dpad_node, numstr);
+					tern_node * pad_dpad = tern_find_node(dpad_node, numstr);
 					char * dirs[] = {"up", "down", "left", "right"};
 					int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
 					for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
@@ -1255,7 +1267,7 @@
 					}
 				}
 			}
-			tern_node *button_node = tern_find_ptr(pad, "buttons");
+			tern_node *button_node = tern_find_node(pad, "buttons");
 			if (button_node) {
 				pad_button_state state = {
 					.padnum = joystick,
@@ -1264,7 +1276,7 @@
 				};
 				tern_foreach(button_node, process_pad_button, &state);
 			}
-			tern_node *axes_node = tern_find_ptr(pad, "axes");
+			tern_node *axes_node = tern_find_node(pad, "axes");
 			if (axes_node) {
 				pad_button_state state = {
 					.padnum = joystick,
@@ -1347,10 +1359,10 @@
 
 	tern_node *mousebuttons = get_mouse_buttons();
 	
-	tern_node * keys = tern_get_node(tern_find_path(config, "bindings\0keys\0"));
+	tern_node * keys = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval;
 	process_keys(keys, special, padbuttons, mousebuttons, NULL);
 	char numstr[] = "00";
-	tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0"));
+	tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
 	if (pads) {
 		for (int i = 0; i < MAX_JOYSTICKS; i++)
 		{
@@ -1366,12 +1378,12 @@
 		}
 	}
 	memset(mice, 0, sizeof(mice));
-	tern_node * mice = tern_get_node(tern_find_path(config, "bindings\0mice\0"));
+	tern_node * mice = tern_find_path(config, "bindings\0mice\0", TVAL_NODE).ptrval;
 	if (mice) {
 		tern_node *buttonmaps[2] = {padbuttons, mousebuttons};
 		tern_foreach(mice, process_mouse, buttonmaps);
 	}
-	tern_node * speed_nodes = tern_get_node(tern_find_path(config, "clocks\0speeds\0"));
+	tern_node * speed_nodes = tern_find_path(config, "clocks\0speeds\0", TVAL_NODE).ptrval;
 	speeds = malloc(sizeof(uint32_t));
 	speeds[0] = 100;
 	process_speeds(speed_nodes, NULL);
--- a/menu.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/menu.c	Fri Apr 21 23:35:32 2017 -0700
@@ -166,7 +166,7 @@
 	menu_context *menu = gen->extra;
 	if (!menu) {
 		gen->extra = menu = calloc(1, sizeof(menu_context));
-		menu->curpath = tern_find_path(config, "ui\0initial_path\0").ptrval;
+		menu->curpath = tern_find_path(config, "ui\0initial_path\0", TVAL_PTR).ptrval;
 		if (!menu->curpath){
 #ifdef __ANDROID__
 			menu->curpath = get_external_storage_path();
--- a/render_sdl.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/render_sdl.c	Fri Apr 21 23:35:32 2017 -0700
@@ -217,9 +217,9 @@
 	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
 	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW);
 	tern_val def = {.ptrval = "default.v.glsl"};
-	vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER);
+	vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def, TVAL_PTR).ptrval, GL_VERTEX_SHADER);
 	def.ptrval = "default.f.glsl";
-	fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER);
+	fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def, TVAL_PTR).ptrval, GL_FRAGMENT_SHADER);
 	program = glCreateProgram();
 	glAttachShader(program, vshader);
 	glAttachShader(program, fshader);
@@ -284,7 +284,7 @@
 	main_clip.h = main_height;
 	main_clip.x = main_clip.y = 0;
 	//calculate configured aspect ratio
-	char *config_aspect = tern_find_path_default(config, "video\0aspect\0", (tern_val){.ptrval = "4:3"}).ptrval;
+	char *config_aspect = tern_find_path_default(config, "video\0aspect\0", (tern_val){.ptrval = "4:3"}, TVAL_PTR).ptrval;
 	if (strcmp("stretch", config_aspect)) {
 		float src_aspect = 4.0f/3.0f;
 		char *end;
@@ -356,32 +356,28 @@
 
 	render_gl = 0;
 	tern_val def = {.ptrval = "off"};
-	char *vsync = tern_find_path_default(config, "video\0vsync\0", def).ptrval;
+	char *vsync = tern_find_path_default(config, "video\0vsync\0", def, TVAL_PTR).ptrval;
 	
-	tern_val video_node = {.ptrval = NULL};
-	tern_find(config, "video", &video_node);
-	tern_node *video = tern_get_node(video_node);
+	tern_node *video = tern_find_node(config, "video");
 	if (video)
 	{
 		for (int i = 0; i < NUM_VID_STD; i++)
 		{
-			video_node.ptrval = NULL;
-			tern_find(video, vid_std_names[i], &video_node);
-			tern_node *std_settings = tern_get_node(video_node);
+			tern_node *std_settings = tern_find_node(video, vid_std_names[i]);
 			if (std_settings) {
-				char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}).ptrval;
+				char *val = tern_find_path_default(std_settings, "overscan\0top\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
 				if (val) {
 					overscan_top[i] = atoi(val);
 				}
-				val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}).ptrval;
+				val = tern_find_path_default(std_settings, "overscan\0bottom\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
 				if (val) {
 					overscan_bot[i] = atoi(val);
 				}
-				val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}).ptrval;
+				val = tern_find_path_default(std_settings, "overscan\0left\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
 				if (val) {
 					overscan_left[i] = atoi(val);
 				}
-				val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}).ptrval;
+				val = tern_find_path_default(std_settings, "overscan\0right\0", (tern_val){.ptrval = NULL}, TVAL_PTR).ptrval;
 				if (val) {
 					overscan_right[i] = atoi(val);
 				}
@@ -447,7 +443,7 @@
 	update_aspect();
 	render_alloc_surfaces();
 	def.ptrval = "off";
-	scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def).ptrval, "on");
+	scanlines = !strcmp(tern_find_path_default(config, "video\0scanlines\0", def, TVAL_PTR).ptrval, "on");
 
 	caption = title;
 
@@ -457,7 +453,7 @@
 	audio_ready = SDL_CreateCond();
 
 	SDL_AudioSpec desired, actual;
-    char * rate_str = tern_find_path(config, "audio\0rate\0").ptrval;
+    char * rate_str = tern_find_path(config, "audio\0rate\0", TVAL_PTR).ptrval;
    	int rate = rate_str ? atoi(rate_str) : 0;
    	if (!rate) {
    		rate = 48000;
@@ -465,7 +461,7 @@
     desired.freq = rate;
 	desired.format = AUDIO_S16SYS;
 	desired.channels = 2;
-    char * samples_str = tern_find_path(config, "audio\0buffer\0").ptrval;
+    char * samples_str = tern_find_path(config, "audio\0buffer\0", TVAL_PTR).ptrval;
    	int samples = samples_str ? atoi(samples_str) : 0;
    	if (!samples) {
    		samples = 512;
--- a/romdb.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/romdb.c	Fri Apr 21 23:35:32 2017 -0700
@@ -643,13 +643,17 @@
 	uint16_t     ptr_index;
 } map_iter_state;
 
-void eeprom_read_fun(char *key, tern_val val, void *data)
+void eeprom_read_fun(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	int bit = atoi(key);
 	if (bit < 0 || bit > 15) {
 		fprintf(stderr, "bit %s is out of range", key);
 		return;
 	}
+	if (valtype != TVAL_PTR) {
+		fprintf(stderr, "bit %s has a non-scalar value", key);
+		return;
+	}
 	char *pin = val.ptrval;
 	if (strcmp(pin, "sda")) {
 		fprintf(stderr, "bit %s is connected to unrecognized read pin %s", key, pin);
@@ -659,13 +663,17 @@
 	map->sda_read_bit = bit;
 }
 
-void eeprom_write_fun(char *key, tern_val val, void *data)
+void eeprom_write_fun(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	int bit = atoi(key);
 	if (bit < 0 || bit > 15) {
 		fprintf(stderr, "bit %s is out of range", key);
 		return;
 	}
+	if (valtype != TVAL_PTR) {
+		fprintf(stderr, "bit %s has a non-scalar value", key);
+		return;
+	}
 	char *pin = val.ptrval;
 	eeprom_map *map = data;
 	if (!strcmp(pin, "sda")) {
@@ -682,7 +690,7 @@
 void process_sram_def(char *key, map_iter_state *state)
 {
 	if (!state->info->save_size) {
-		char * size = tern_find_path(state->root, "SRAM\0size\0").ptrval;
+		char * size = tern_find_path(state->root, "SRAM\0size\0", TVAL_PTR).ptrval;
 		if (!size) {
 			fatal_error("ROM DB map entry %d with address %s has device type SRAM, but the SRAM size is not defined\n", state->index, key);
 		}
@@ -693,7 +701,7 @@
 		state->info->save_mask = nearest_pow2(state->info->save_size)-1;
 		state->info->save_buffer = malloc(state->info->save_size);
 		memset(state->info->save_buffer, 0, state->info->save_size);
-		char *bus = tern_find_path(state->root, "SRAM\0bus\0").ptrval;
+		char *bus = tern_find_path(state->root, "SRAM\0bus\0", TVAL_PTR).ptrval;
 		if (!strcmp(bus, "odd")) {
 			state->info->save_type = RAM_FLAG_ODD;
 		} else if(!strcmp(bus, "even")) {
@@ -707,7 +715,7 @@
 void process_eeprom_def(char * key, map_iter_state *state)
 {
 	if (!state->info->save_size) {
-		char * size = tern_find_path(state->root, "EEPROM\0size\0").ptrval;
+		char * size = tern_find_path(state->root, "EEPROM\0size\0", TVAL_PTR).ptrval;
 		if (!size) {
 			fatal_error("ROM DB map entry %d with address %s has device type EEPROM, but the EEPROM size is not defined\n", state->index, key);
 		}
@@ -715,7 +723,7 @@
 		if (!state->info->save_size) {
 			fatal_error("EEPROM size %s is invalid\n", size);
 		}
-		char *etype = tern_find_path(state->root, "EEPROM\0type\0").ptrval;
+		char *etype = tern_find_path(state->root, "EEPROM\0type\0", TVAL_PTR).ptrval;
 		if (!etype) {
 			etype = "i2c";
 		}
@@ -737,11 +745,11 @@
 	eep_map->start = start;
 	eep_map->end = end;
 	eep_map->sda_read_bit = 0xFF;
-	tern_node * bits_read = tern_find_ptr(node, "bits_read");
+	tern_node * bits_read = tern_find_node(node, "bits_read");
 	if (bits_read) {
 		tern_foreach(bits_read, eeprom_read_fun, eep_map);
 	}
-	tern_node * bits_write = tern_find_ptr(node, "bits_write");
+	tern_node * bits_write = tern_find_node(node, "bits_write");
 	if (bits_write) {
 		tern_foreach(bits_write, eeprom_write_fun, eep_map);
 	}
@@ -749,13 +757,13 @@
 	state->info->num_eeprom++;
 }
 
-void map_iter_fun(char *key, tern_val val, void *data)
+void map_iter_fun(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	map_iter_state *state = data;
-	tern_node *node = tern_get_node(val);
-	if (!node) {
+	if (valtype != TVAL_NODE) {
 		fatal_error("ROM DB map entry %d with address %s is not a node\n", state->index, key);
 	}
+	tern_node *node = val.ptrval;
 	uint32_t start = strtol(key, NULL, 16);
 	uint32_t end = strtol(tern_find_ptr_default(node, "last", "0"), NULL, 16);
 	if (!end || end < start) {
@@ -819,7 +827,7 @@
 		state->info->map = realloc(state->info->map, sizeof(memmap_chunk) * state->info->map_chunks);
 		memset(state->info->map + state->info->map_chunks - 7, 0, sizeof(memmap_chunk) * 7);
 		map = state->info->map + state->index;
-		char *save_device = tern_find_path(node, "save\0device\0").ptrval;
+		char *save_device = tern_find_path(node, "save\0device\0", TVAL_PTR).ptrval;
 		if (save_device && !strcmp(save_device, "EEPROM")) {
 			process_eeprom_def(key, state);
 			add_eeprom_map(node, start & map->mask, end & map->mask, state);
@@ -892,9 +900,9 @@
 	uint8_t hex_hash[41];
 	bin_to_hex(hex_hash, raw_hash, 20);
 	printf("SHA1: %s\n", hex_hash);
-	tern_node * entry = tern_find_ptr(rom_db, hex_hash);
+	tern_node * entry = tern_find_node(rom_db, hex_hash);
 	if (!entry) {
-		entry = tern_find_ptr(rom_db, product_id);
+		entry = tern_find_node(rom_db, product_id);
 	}
 	if (!entry) {
 		puts("Not found in ROM DB, examining header\n");
@@ -927,7 +935,7 @@
 		info.regions = get_header_regions(rom);
 	}
 
-	tern_node *map = tern_find_ptr(entry, "map");
+	tern_node *map = tern_find_node(entry, "map");
 	if (map) {
 		info.save_type = SAVE_NONE;
 		info.map_chunks = tern_count(map);
@@ -959,7 +967,7 @@
 		add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
 	}
 
-	tern_node *device_overrides = tern_find_ptr(entry, "device_overrides");
+	tern_node *device_overrides = tern_find_node(entry, "device_overrides");
 	if (device_overrides) {
 		info.port1_override = tern_find_ptr(device_overrides, "1");
 		info.port2_override = tern_find_ptr(device_overrides, "2");
--- a/sms.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/sms.c	Fri Apr 21 23:35:32 2017 -0700
@@ -324,7 +324,7 @@
 		sms->bank_regs[3] = 0x8000 >> 14;
 	}
 	
-	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0").ptrval;
+	char * lowpass_cutoff_str = tern_find_path(config, "audio\0lowpass_cutoff\0", TVAL_PTR).ptrval;
 	uint32_t lowpass_cutoff = lowpass_cutoff_str ? atoi(lowpass_cutoff_str) : 3390;
 	
 	//TODO: Detect region and pick master clock based off of that
--- a/tern.c	Fri Apr 21 01:22:52 2017 -0700
+++ b/tern.c	Fri Apr 21 23:35:32 2017 -0700
@@ -10,7 +10,7 @@
 #include <stdio.h>
 #include "util.h"
 
-tern_node * tern_insert(tern_node * head, char const * key, tern_val value)
+tern_node * tern_insert(tern_node * head, char const * key, tern_val value, uint8_t valtype)
 {
 	tern_node ** cur = &head;
 	while(*key)
@@ -31,6 +31,7 @@
 			(*cur)->right = NULL;
 			(*cur)->straight.next = NULL;
 			(*cur)->el = *key;
+			(*cur)->valtype = TVAL_NONE;
 		}
 		cur = &((*cur)->straight.next);
 		key++;
@@ -46,10 +47,11 @@
 		(*cur)->el = 0;
 	}
 	(*cur)->straight.value = value;
+	(*cur)->valtype = valtype;
 	return head;
 }
 
-int tern_find(tern_node * head, char const * key, tern_val *ret)
+uint8_t tern_find(tern_node * head, char const * key, tern_val *ret)
 {
 	tern_node * cur = head;
 	while (cur)
@@ -60,7 +62,7 @@
 				key++;
 			} else {
 				*ret = cur->straight.value;
-				return 1;
+				return cur->valtype;
 			}
 		} else if (*key < cur->el) {
 			cur = cur->left;
@@ -68,7 +70,7 @@
 			cur = cur->right;
 		}
 	}
-	return 0;
+	return TVAL_NONE;
 }
 
 tern_node * tern_find_prefix(tern_node * head, char const * key)
@@ -91,7 +93,8 @@
 intptr_t tern_find_int(tern_node * head, char const * key, intptr_t def)
 {
 	tern_val ret;
-	if (tern_find(head, key, &ret)) {
+	uint8_t valtype = tern_find(head, key, &ret);
+	if (valtype == TVAL_INT) {
 		return ret.intval;
 	}
 	return def;
@@ -101,18 +104,15 @@
 {
 	tern_val val;
 	val.intval = value;
-	return tern_insert(head, key, val);
+	return tern_insert(head, key, val, TVAL_INT);
 }
 
 void * tern_find_ptr_default(tern_node * head, char const * key, void * def)
 {
 	tern_val ret;
-	if (tern_find(head, key, &ret)) {
-		if (ret.intval & 1) {
-			return (void *)(ret.intval & ~1);
-		} else {
-			return ret.ptrval;
-		}
+	uint8_t valtype = tern_find(head, key, &ret);
+	if (valtype == TVAL_PTR) {
+		return ret.ptrval;
 	}
 	return def;
 }
@@ -122,44 +122,57 @@
 	return tern_find_ptr_default(head, key, NULL);
 }
 
-tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def)
+tern_node *tern_find_node(tern_node *head, char const *key)
+{
+	tern_val ret;
+	uint8_t valtype = tern_find(head, key, &ret);
+	if (valtype == TVAL_NODE) {
+		return ret.ptrval;
+	}
+	return NULL;
+}
+
+tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def, uint8_t req_valtype)
 {
 	tern_val ret;
 	while (*key)
 	{
-		if (!tern_find(head, key, &ret)) {
+		uint8_t valtype = tern_find(head, key, &ret);
+		if (!valtype) {
 			return def;
 		}
 		key = key + strlen(key) + 1;
 		if (*key) {
-			head = tern_get_node(ret);
-			if (!head) {
+			if (valtype != TVAL_NODE) {
 				return def;
 			}
+			head = ret.ptrval;
+		} else if (req_valtype && req_valtype != valtype) {
+			return def;
 		}
 	}
 	return ret;
 }
 
-tern_val tern_find_path(tern_node *head, char const *key)
+tern_val tern_find_path(tern_node *head, char const *key, uint8_t valtype)
 {
 	tern_val def;
 	def.ptrval = NULL;
-	return tern_find_path_default(head, key, def);
+	return tern_find_path_default(head, key, def, valtype);
 }
 
 tern_node * tern_insert_ptr(tern_node * head, char const * key, void * value)
 {
 	tern_val val;
 	val.ptrval = value;
-	return tern_insert(head, key, val);
+	return tern_insert(head, key, val, TVAL_PTR);
 }
 
 tern_node * tern_insert_node(tern_node *head, char const *key, tern_node *value)
 {
 	tern_val val;
-	val.intval = ((intptr_t)value) | 1;
-	return tern_insert(head, key, val);
+	val.ptrval = value;
+	return tern_insert(head, key, val, TVAL_NODE);
 }
 
 uint32_t tern_count(tern_node *head)
@@ -184,7 +197,7 @@
 {
 	if (!head->el) {
 		keybuf[pos] = 0;
-		fun(keybuf, head->straight.value, data);
+		fun(keybuf, head->straight.value, head->valtype, data);
 	}
 	if (head->left) {
 		tern_foreach_int(head->left, fun, data, keybuf, pos);
@@ -220,11 +233,6 @@
 	return buf;
 }
 
-tern_node * tern_get_node(tern_val value)
-{
-	return value.intval & 1 ? (tern_node *)(value.intval & ~1) : NULL;
-}
-
 void tern_free(tern_node *head)
 {
 	if (head->left) {
@@ -236,4 +244,5 @@
 	if (head->el) {
 		tern_free(head->straight.next);
 	}
+	free(head);
 }
--- a/tern.h	Fri Apr 21 01:22:52 2017 -0700
+++ b/tern.h	Fri Apr 21 23:35:32 2017 -0700
@@ -23,25 +23,33 @@
 	} straight;
 	struct tern_node *right;
 	char             el;
+	uint8_t          valtype;
 } tern_node;
 
-typedef void (*iter_fun)(char *key, tern_val val, void *data);
+enum {
+	TVAL_NONE=0,
+	TVAL_INT,
+	TVAL_PTR,
+	TVAL_NODE
+};
 
-tern_node * tern_insert(tern_node * head, char const * key, tern_val value);
-int tern_find(tern_node * head, char const * key, tern_val *ret);
+typedef void (*iter_fun)(char *key, tern_val val, uint8_t valtype, void *data);
+
+tern_node * tern_insert(tern_node * head, char const * key, tern_val value, uint8_t valtype);
+uint8_t tern_find(tern_node * head, char const * key, tern_val *ret);
 tern_node * tern_find_prefix(tern_node * head, char const * key);
 intptr_t tern_find_int(tern_node * head, char const * key, intptr_t def);
 tern_node * tern_insert_int(tern_node * head, char const * key, intptr_t value);
 void * tern_find_ptr_default(tern_node * head, char const * key, void * def);
 void * tern_find_ptr(tern_node * head, char const * key);
-tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def);
-tern_val tern_find_path(tern_node *head, char const *key);
+tern_node *tern_find_node(tern_node *head, char const *key);
+tern_val tern_find_path_default(tern_node *head, char const *key, tern_val def, uint8_t req_valtype);
+tern_val tern_find_path(tern_node *head, char const *key, uint8_t valtype);
 tern_node * tern_insert_ptr(tern_node * head, char const * key, void * value);
 tern_node * tern_insert_node(tern_node *head, char const *key, tern_node *value);
 uint32_t tern_count(tern_node *head);
 void tern_foreach(tern_node *head, iter_fun fun, void *data);
 char * tern_int_key(uint32_t key, char * buf);
-tern_node * tern_get_node(tern_val value);
 void tern_free(tern_node *head);
 
 #endif //TERN_H_