changeset 2202:ee6d30c56eeb

Add separate model/IO selection settings for SMS/GG
author Michael Pavone <pavone@retrodev.com>
date Mon, 22 Aug 2022 22:56:05 -0700
parents 2f8984ff5c85
children 9826d50061a0
files config.c default.cfg nuklear_ui/blastem_nuklear.c sms.c systems.cfg
diffstat 5 files changed, 201 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/config.c	Mon Aug 22 20:43:19 2022 -0700
+++ b/config.c	Mon Aug 22 22:56:05 2022 -0700
@@ -140,7 +140,7 @@
 	ensure_buf_capacity(1, state);
 	state->buf[state->size++] = '{';
 	state->indent++;
-	
+
 	tern_foreach(config, serialize_iter, state);
 
 	--state->indent;
@@ -239,7 +239,7 @@
 	if (used_config_dir) {
 		*used_config_dir = ret != NULL;
 	}
-	
+
 	if (!ret) {
 		ret = parse_bundled_config(name);
 		if (!ret) {
@@ -285,7 +285,7 @@
 	*pads = tern_insert_node(*pads, key, dupe_tree(val.ptrval));
 }
 
-#define CONFIG_VERSION 2
+#define CONFIG_VERSION 3
 static tern_node *migrate_config(tern_node *config, int from_version)
 {
 	tern_node *def_config = parse_bundled_config("default.cfg");
@@ -324,14 +324,22 @@
 		tern_node *def_pads = tern_find_path(def_config, "bindings\0pads\0", TVAL_NODE).ptrval;
 		tern_foreach(def_pads, migrate_pads, &pads);
 		config = tern_insert_path(config, "bindings\0pads\0", (tern_val){.ptrval = pads}, TVAL_NODE);
-		break;
 	}
 	case 1: {
 		char *l_bind = tern_find_path(config, "bindings\0keys\0l\0", TVAL_PTR).ptrval;
 		if (!l_bind) {
 			config = tern_insert_path(config, "bindings\0keys\0l\0", (tern_val){.ptrval = strdup("ui.load_state")}, TVAL_PTR);
 		}
-		break;
+	}
+	case 2: {
+		tern_node *sms = tern_find_node(config, "sms");
+		char *model = tern_find_path_default(sms, "system\0model\0", (tern_val){.ptrval = "md1va3"}, TVAL_PTR).ptrval;
+		char *io1 = tern_find_path_default(sms, "io\0devices\0""1\0", (tern_val){.ptrval = "gamepad2.1"}, TVAL_PTR).ptrval;
+		char *io2 = tern_find_path_default(sms, "io\0devices\0""1\0", (tern_val){.ptrval = "gamepad2.2"}, TVAL_PTR).ptrval;
+		sms = tern_insert_path(sms, "system\0model\0", (tern_val){.ptrval = strdup(model)}, TVAL_PTR);
+		sms = tern_insert_path(sms, "io\0devices\0""1\0", (tern_val){.ptrval = strdup(io1)}, TVAL_PTR);
+		sms = tern_insert_path(sms, "io\0devices\0""2\0", (tern_val){.ptrval = strdup(io2)}, TVAL_PTR);
+		config = tern_insert_node(config, "sms", sms);
 	}
 	}
 	char buffer[16];
@@ -343,7 +351,7 @@
 tern_node *load_config()
 {
 	tern_node *ret = load_overrideable_config("blastem.cfg", "default.cfg", &app_config_in_config_dir);
-	
+
 	if (!ret) {
 		if (get_config_dir()) {
 			fatal_error("Failed to find a config file at %s or in the blastem executable directory\n", get_config_dir());
@@ -443,6 +451,6 @@
 
 tern_node *get_model(tern_node *config, system_type stype)
 {
-	char *model = tern_find_path_default(config, "system\0model\0", (tern_val){.ptrval = "md1va3"}, TVAL_PTR).ptrval;
+	char *model = tern_find_path_default(config, stype == SYSTEM_SMS ? "sms\0system\0model\0" : "system\0model\0", (tern_val){.ptrval = "md1va3"}, TVAL_PTR).ptrval;
 	return tern_find_node(get_systems_config(), model);
 }
--- a/default.cfg	Mon Aug 22 20:43:19 2022 -0700
+++ b/default.cfg	Mon Aug 22 22:56:05 2022 -0700
@@ -407,5 +407,18 @@
 	#Model of the emulated Gen/MD system, see systems.cfg for a list of options
 	model md1va3
 }
+
+sms {
+	system {
+		model md1va3
+	}
+	io {
+		devices {
+			1 gamepad2.1
+			2 gamepad2.2
+		}
+	}
+}
+
 #Don't manually edit `version`, it's used for automatic config migration
-version 1
+version 3
--- a/nuklear_ui/blastem_nuklear.c	Mon Aug 22 20:43:19 2022 -0700
+++ b/nuklear_ui/blastem_nuklear.c	Mon Aug 22 22:56:05 2022 -0700
@@ -31,7 +31,7 @@
 	struct nk_image  ui;
 } ui_image;
 
-static ui_image **ui_images, *controller_360, *controller_ps4, 
+static ui_image **ui_images, *controller_360, *controller_ps4,
 	*controller_ps4_6b, *controller_wiiu, *controller_gen_6b;
 static uint32_t num_ui_images, ui_image_storage;
 
@@ -69,7 +69,7 @@
 
 void view_play(struct nk_context *context)
 {
-	
+
 }
 
 static char *browser_cur_path;
@@ -163,7 +163,7 @@
 						init_system_with_media(full_path, SYSTEM_UNKNOWN);
 						free(full_path);
 					}
-					
+
 					clear_view_stack();
 					show_play_view();
 				} else if (browser_setting_path) {
@@ -174,7 +174,7 @@
 				} else {
 					lockon_media(full_path);
 					free(full_path);
-					
+
 					clear_view_stack();
 					show_play_view();
 				}
@@ -322,13 +322,13 @@
 	const uint32_t button_height = context->style.font->height * 1.75;
 	const uint32_t ideal_button_width = context->style.font->height * 10;
 	const uint32_t button_space = 6;
-	
+
 	uint32_t width = render_width();
 	uint32_t height = render_height();
 	uint32_t top = height/2 - (button_height * num_entries)/2;
 	uint32_t button_width = width > ideal_button_width ? ideal_button_width : width;
 	uint32_t left = width/2 - button_width/2;
-	
+
 	nk_layout_space_begin(context, NK_STATIC, top + button_height * num_entries, num_entries);
 	for (uint32_t i = 0; i < num_entries; i++)
 	{
@@ -369,7 +369,7 @@
 	nk_layout_row_static(context, (context->style.font->height + 4)*num_binds+context->style.font->height+30, render_width() - 80, 1);
 	if (nk_group_begin(context, name, NK_WINDOW_TITLE)) {
 		nk_layout_row_static(context, context->style.font->height, render_width()/2 - 80, 2);
-		
+
 		for (int i = 0; i < num_binds; i++)
 		{
 			char *label_alloc = bind_names ? NULL : path_extension(binds[i]);
@@ -500,7 +500,7 @@
 		"ui.compositing_debug", "ui.vdp_debug_mode"
 	};
 	const char *debug_names[] = {
-		"CPU Debugger", "Plane Debugger", "VRAM Debugger", "CRAM Debugger", 
+		"CPU Debugger", "Plane Debugger", "VRAM Debugger", "CRAM Debugger",
 		"Layer Debugger", "Cycle Mode/Pal"
 	};
 	const uint32_t NUM_C1_BINDS = sizeof(controller1_binds)/sizeof(*controller1_binds);
@@ -557,7 +557,7 @@
 				memcpy(path, "bindings\0keys\0", prefix_len);
 				memcpy(path + prefix_len, name, suffix_len);
 				path[prefix_len + suffix_len] = 0;
-				
+
 				config_dirty = 1;
 				config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(set_binding)}, TVAL_PTR);
 				free(path);
@@ -600,7 +600,7 @@
 	[SDL_CONTROLLER_BUTTON_RIGHTSTICK] = "r3",
 };
 
-typedef struct {	
+typedef struct {
 	const char *button_binds[SDL_CONTROLLER_BUTTON_MAX];
 	const char *left_stick[NUM_AXIS_DIRS];
 	const char *right_stick[NUM_AXIS_DIRS];
@@ -723,14 +723,14 @@
 		"ui.set_speed.8",
 		"ui.set_speed.9"
 	};
-		
+
 	if (nk_begin(context, "Button Binding", nk_rect(0, 0, render_width(), render_height()), 0)) {
 		bind_option_group(context, "Controller Buttons", pad_opts, sizeof(pad_opts)/sizeof(*pad_opts));
 		bind_option_group(context, "System Buttons", system_buttons, sizeof(system_buttons)/sizeof(*system_buttons));
 		bind_option_group(context, "Emulator Control", emu_control, sizeof(emu_control)/sizeof(*emu_control));
 		bind_option_group(context, "Debugging", debugger, sizeof(debugger)/sizeof(*debugger));
 		bind_option_group(context, "Speed Control", speeds, sizeof(speeds)/sizeof(*speeds));
-		
+
 		nk_layout_row_static(context, context->style.font->height, (render_width() - 80)/4, 1);
 		if (nk_button_label(context, "Back")) {
 			pop_view();
@@ -743,11 +743,11 @@
 {
 	const struct nk_user_font *font = context->style.font;
 	float row_height = font->height * 2;
-	
+
 	char const **labels = calloc(sizeof(char *), num_binds);
 	char const ***conf_vals = calloc(sizeof(char *), num_binds);
 	float max_width = 0.0f;
-	
+
 	int skipped = 0;
 	for (int i = 0; i < num_binds; i++)
 	{
@@ -771,7 +771,7 @@
 	}
 	nk_layout_space_push(context, nk_rect(x, y, width, (num_binds - skipped) * (row_height + 4) + 4));
 	nk_group_begin(context, name, NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR);
-	
+
 	float widths[] = {max_width + 3, width - (max_width + 6)};
 	nk_layout_row(context, NK_STATIC, row_height, 2, widths);
 	for (int i = 0; i < num_binds; i++)
@@ -874,7 +874,7 @@
 		full_key[axes_key_size+prefix_len] = axis;
 		memcpy(full_key + axes_key_size + prefix_len + 1, suffix, suffix_len  +1);
 		full_key[axes_key_size + prefix_len + 1 + suffix_len + 1] = 0;
-		
+
 		if (bindings[i]) {
 			tern_insert_path(config, full_key, (tern_val){.ptrval = strdup(bindings[i])}, TVAL_PTR);
 		} else {
@@ -884,7 +884,7 @@
 				free(prev_val.ptrval);
 			}
 		}
-		
+
 		free(full_key);
 	}
 }
@@ -925,7 +925,7 @@
 	char *button_key = malloc(button_key_size);
 	memcpy(button_key, pad_key, pad_key_size);
 	memcpy(button_key + pad_key_size, button_base, sizeof(button_base));
-	
+
 	char *final_key;
 	for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
 	{
@@ -936,8 +936,8 @@
 			suffix = SDL_GameControllerGetStringForButton(i);
 			base_key_len = button_key_size;
 			base = button_key;
-			
-			
+
+
 		} else {
 			static const char *dir_keys[] = {"up", "down", "left", "right"};
 			suffix = dir_keys[i - SDL_CONTROLLER_BUTTON_DPAD_UP];
@@ -962,13 +962,13 @@
 	}
 	free(button_key);
 	free(dpad_key);
-	
+
 	static const char axes_base[] = "axes";
 	size_t axes_key_size = pad_key_size + sizeof(axes_base);
 	char *axes_key = malloc(axes_key_size);
 	memcpy(axes_key, pad_key, pad_key_size);
 	memcpy(axes_key + pad_key_size, axes_base, sizeof(axes_base));
-	
+
 	save_stick_binds(axes_key, axes_key_size,bindings->left_stick, "left");
 	save_stick_binds(axes_key, axes_key_size,bindings->right_stick, "right");
 	for (int i = SDL_CONTROLLER_AXIS_TRIGGERLEFT; i < SDL_CONTROLLER_AXIS_MAX; i++)
@@ -991,7 +991,7 @@
 		free(final_key);
 	}
 	free(axes_key);
-	
+
 	free(pad_key);
 	if (dest == SIMILAR_CONTROLLERS) {
 		free(key);
@@ -1007,7 +1007,7 @@
 	options[DEFAULT].title = "Use as default";
 	options[BY_INDEX].title = by_index_names[selected_controller];
 	options[SIMILAR_CONTROLLERS].title = make_human_readable_type_name(&selected_controller_info);
-	
+
 	if (nk_begin(context, "Select Binding Dest", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
 		menu(context, NUM_DEST_TYPES, options, handle_dest_clicked);
 		nk_end(context);
@@ -1050,24 +1050,24 @@
 				}
 			}
 		}
-	
+
 		float orig_height = def_font->handle.height;
 		def_font->handle.height *= 0.5f;
-		
+
 		uint32_t avail_height = render_height() - 2 * orig_height;
 		float desired_width = render_width() * 0.5f, desired_height = avail_height * 0.5f;
 		ui_image *controller_image = select_best_image(&selected_controller_info);
-		
+
 		float controller_ratio = (float)controller_image->width / (float)controller_image->height;
-		
+
 		const struct nk_user_font *font = context->style.font;
 		int MIN_BIND_BOX_WIDTH = font->width(font->userdata, font->height, "Right", strlen("Right"))
 			+ def_font->handle.width(font->userdata, font->height, "Internal Screenshot", strlen("Internal Screenshot"));
-		
+
 		if (render_width() - desired_width < 2.5f*MIN_BIND_BOX_WIDTH) {
 			desired_width = render_width() - 2.5f*MIN_BIND_BOX_WIDTH;
 		}
-		
+
 		if (desired_width / desired_height > controller_ratio) {
 			desired_width = desired_height * controller_ratio;
 		} else {
@@ -1080,7 +1080,7 @@
 		nk_layout_space_begin(context, NK_STATIC, avail_height, INT_MAX);
 		nk_layout_space_push(context, nk_rect(img_left, img_top, desired_width, desired_height));
 		nk_image(context, controller_image->ui);
-		
+
 		float bind_box_width = (render_width() - img_right) * 0.8f;
 		if (bind_box_width < MIN_BIND_BOX_WIDTH) {
 			bind_box_width = render_width() - img_right;
@@ -1096,7 +1096,7 @@
 		} else {
 			bind_box_left = img_right + (render_width() - img_right) / 2.0f - bind_box_width / 2.0f;
 		}
-		
+
 		if (selected_controller_info.variant == VARIANT_NORMAL) {
 			binding_box(context, bindings, "Action Buttons", bind_box_left, img_top, bind_box_width, 4, (int[]){
 				SDL_CONTROLLER_BUTTON_A,
@@ -1114,7 +1114,7 @@
 				selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_RIGHTSHOULDER : SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
 			});
 		}
-		
+
 		if (selected_controller_info.variant == VARIANT_NORMAL) {
 			binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width, 2, (int[]){
 				SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
@@ -1122,19 +1122,19 @@
 			});
 		} else {
 			binding_box(context, bindings, "Right Shoulder", bind_box_left, font->height/2, bind_box_width,
-				selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, 
+				selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2,
 				(int[]){
 				selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSHOULDER : AXIS | SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
 				AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT
 			});
 		}
-		
+
 		binding_box(context, bindings, "Misc Buttons", (render_width() - bind_box_width) / 2, font->height/2, bind_box_width, 3, (int[]){
 			SDL_CONTROLLER_BUTTON_BACK,
 			SDL_CONTROLLER_BUTTON_GUIDE,
 			SDL_CONTROLLER_BUTTON_START
 		});
-		
+
 		if (selected_controller_info.variant == VARIANT_NORMAL)
 		{
 			binding_box(context, bindings, "Right Stick", img_right - desired_width/3, img_bot, bind_box_width, 5, (int[]){
@@ -1145,7 +1145,7 @@
 				SDL_CONTROLLER_BUTTON_RIGHTSTICK
 			});
 		}
-		
+
 		bind_box_left -= img_right;
 		float dpad_left, dpad_top;
 		if (selected_controller_info.variant == VARIANT_NORMAL)
@@ -1163,30 +1163,30 @@
 			dpad_left = bind_box_left;
 			dpad_top = img_top;
 		}
-		
+
 		if (selected_controller_info.variant == VARIANT_NORMAL) {
 			binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, 2, (int[]){
 				SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
 				AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT
 			});
 		} else {
-			binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width, 
-				selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2, 
+			binding_box(context, bindings, "Left Shoulder", bind_box_left, font->height/2, bind_box_width,
+				selected_controller_info.variant == VARIANT_6B_BUMPERS ? 1 : 2,
 				(int[]){
 				selected_controller_info.variant == VARIANT_6B_RIGHT ? SDL_CONTROLLER_BUTTON_LEFTSTICK : AXIS | SDL_CONTROLLER_AXIS_TRIGGERLEFT,
 				SDL_CONTROLLER_BUTTON_RIGHTSTICK
 			});
 		}
-		
+
 		binding_box(context, bindings, "D-pad", dpad_left, dpad_top, bind_box_width, 4, (int[]){
 			SDL_CONTROLLER_BUTTON_DPAD_UP,
 			SDL_CONTROLLER_BUTTON_DPAD_DOWN,
 			SDL_CONTROLLER_BUTTON_DPAD_LEFT,
 			SDL_CONTROLLER_BUTTON_DPAD_RIGHT
 		});
-		
+
 		nk_layout_space_end(context);
-		
+
 		def_font->handle.height = orig_height;
 		nk_layout_row_static(context, orig_height + 4, (render_width() - 2*orig_height) / 4, 1);
 		if (nk_button_label(context, controller_binding_changed ? "Save" : "Back")) {
@@ -1230,30 +1230,30 @@
 	static int quiet, button_a = -1, button_a_axis = -1;
 	uint8_t added_mapping = 0;
 	if (nk_begin(context, "Controllers", nk_rect(0, 0, render_width(), render_height()), NK_WINDOW_NO_SCROLLBAR)) {
-		
+
 		nk_layout_space_begin(context, NK_STATIC, render_height() - context->style.font->height, 3);
-		
+
 		if (current_button < SDL_CONTROLLER_BUTTON_MAX) {
 			snprintf(buffer, sizeof(buffer), "Press Button %s", get_button_label(&selected_controller_info, current_button));
 		} else {
 			snprintf(buffer, sizeof(buffer), "Move Axis %s", get_axis_label(&selected_controller_info, current_axis));
 		}
-		
+
 		float height = context->style.font->height * 1.25;
 		float top = render_height()/2 - 1.5 * height;
 		float width = render_width() - context->style.font->height;
-		
+
 		nk_layout_space_push(context, nk_rect(0, top, width, height));
 		nk_label(context, buffer, NK_TEXT_CENTERED);
 		if (current_button > SDL_CONTROLLER_BUTTON_B) {
 			nk_layout_space_push(context, nk_rect(0, top + height, width, height));
 			nk_label(context, "OR", NK_TEXT_CENTERED);
-		
+
 			nk_layout_space_push(context, nk_rect(0, top + 2.0 * height, width, height));
 			snprintf(buffer, sizeof(buffer), "Press Button %s to skip", get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_A));
 			nk_label(context, buffer, NK_TEXT_CENTERED);
 		}
-		
+
 		nk_layout_space_end(context);
 		if (quiet) {
 			--quiet;
@@ -1279,7 +1279,7 @@
 				mapping_string[mapping_pos++] = '.';
 				mapping_string[mapping_pos++] = '0' + hat_value;
 				added_mapping = 1;
-				
+
 				last_hat = hat_moved;
 				last_hat_value = hat_value;
 			} else if (axis_moved >= 0 && abs(axis_value) > 1000 && (
@@ -1303,7 +1303,7 @@
 				added_mapping = 1;
 			}
 		}
-			
+
 		while (added_mapping) {
 			quiet = QUIET_FRAMES;
 			if (current_button < SDL_CONTROLLER_BUTTON_MAX) {
@@ -1363,7 +1363,7 @@
 		}
 		mapping_string[mapping_pos] = c;
 	}
-	
+
 	push_view(view_controller_mappings);
 }
 
@@ -1395,16 +1395,16 @@
 				selected = 1;
 			}
 			char buffer[512];
-			snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", 
-				get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), 
+			snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s",
+				get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER),
 				get_axis_label(&selected_controller_info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
 			);
 			if (nk_button_label(context, buffer)) {
 				selected_controller_info.variant = VARIANT_6B_RIGHT;
 				selected = 1;
 			}
-			snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s", 
-				get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER), 
+			snprintf(buffer, sizeof(buffer), "6 face buttons including %s and %s",
+				get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER),
 				get_button_label(&selected_controller_info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)
 			);
 			if (nk_button_label(context, buffer)) {
@@ -1495,8 +1495,8 @@
 		float bindings_ratio = (float)bindings_width / total;
 		float remap_ratio = (float)remap_width / total;
 		float change_type_ratio = (float)change_type_width / total;
-		
-		
+
+
 		uint8_t found_controller = 0;
 		for (int i = 0; i < MAX_JOYSTICKS; i++)
 		{
@@ -1515,7 +1515,7 @@
 				}
 				int button_start = image_width + context->style.font->height;
 				int button_area_width = render_width() - image_width - 2 * context->style.font->height;
-				
+
 				nk_layout_space_push(context, nk_rect(button_start, 0, button_area_width, inner_height/2));
 				nk_label(context, info.name, NK_TEXT_CENTERED);
 				const struct nk_user_font *font = context->style.font;
@@ -1637,7 +1637,7 @@
 		config_dirty = 1;
 		config = tern_insert_path(config, path, (tern_val){.ptrval = strdup(buffer)}, TVAL_PTR);
 	}
-	
+
 	nk_spacing(context, 1);
 	if (nk_button_label(context, "Browse")) {
 		browser_label = label;
@@ -1692,7 +1692,7 @@
 	uint32_t num_progs = *num_existing;
 	uint32_t prog_storage = *storage;
 	uint32_t starting = num_progs;
-	
+
 	for (uint32_t i = 0; i < num_entries; i++) {
 		if (entries[i].is_dir) {
 			continue;
@@ -1713,12 +1713,12 @@
 					progs = realloc(progs, sizeof(*progs) * prog_storage);
 				}
 				progs[num_progs].vertex = NULL;
-				progs[num_progs++].fragment = strdup(entries[i].name); 
+				progs[num_progs++].fragment = strdup(entries[i].name);
 			}
 		}
 		free(no_ext);
 	}
-	
+
 	for (uint32_t i = 0; i < num_entries; i++) {
 		if (entries[i].is_dir) {
 			continue;
@@ -1874,7 +1874,7 @@
 		settings_int_property(context, "", "Bottom", "video\0pal\0overscan\0bottom\0", 17, 0, 32);
 		settings_int_property(context, "", "Left", "video\0pal\0overscan\0left\0", 13, 0, 32);
 		settings_int_property(context, "", "Right", "video\0pal\0overscan\0right\0", 14, 0, 32);
-		
+
 		if (nk_button_label(context, "Back")) {
 			pop_view();
 		}
@@ -1943,13 +1943,20 @@
 	const char **names;
 	uint32_t   num_models;
 	uint32_t   storage;
+	uint8_t    genesis_only;
 } model_foreach_state;
 void model_iter(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	if (valtype != TVAL_NODE) {
 		return;
 	}
+	if (!strcmp(tern_find_ptr_default(val.ptrval, "show", "yes"), "no")) {
+		return;
+	}
 	model_foreach_state *state = data;
+	if (state->genesis_only && strcmp(tern_find_ptr_default(val.ptrval, "vdp", "genesis"), "genesis")) {
+		return;
+	}
 	if (state->num_models == state->storage) {
 		state->storage *= 2;
 		state->models = realloc(state->models, state->storage * sizeof(char *));
@@ -1965,14 +1972,15 @@
 	const char **names;
 } models;
 
-models get_models(uint32_t *num_out)
+models get_models(uint32_t *num_out, uint8_t genesis_only)
 {
 	tern_node *systems = get_systems_config();
 	model_foreach_state state = {
 		.models = calloc(4, sizeof(char *)),
 		.names = calloc(4, sizeof(char *)),
 		.num_models = 0,
-		.storage = 4
+		.storage = 4,
+		.genesis_only = genesis_only
 	};
 	tern_foreach(systems, model_iter, &state);
 	*num_out = state.num_models;
@@ -2004,19 +2012,29 @@
 	if (selected_region < 0) {
 		selected_region = find_match(region_codes, num_regions, "system\0default_region\0", "U");
 	}
-	static const char **model_opts;
-	static const char **model_names;
-	static uint32_t num_models;
+	static const char **model_opts, **sms_model_opts;
+	static const char **model_names, **sms_model_names;
+	static uint32_t num_models, num_sms_models;
 	if (!model_opts) {
-		models m = get_models(&num_models);
+		models m = get_models(&num_models, 1);
 		model_opts = m.models;
 		model_names = m.names;
+		m = get_models(&num_sms_models, 0);
+		sms_model_opts = m.models;
+		sms_model_names = m.names;
 	}
+	static uint8_t old_show_sms;
+	uint8_t show_sms = current_system && current_system->type == SYSTEM_SMS;
+
 	static int32_t selected_model = -1;
-	if (selected_model < 0) {
-		selected_model = find_match(model_opts, num_models, "system\0model\0", "md1va3");
+	if (selected_model < 0 || show_sms != old_show_sms) {
+		if (show_sms) {
+			selected_model = find_match(sms_model_opts, num_sms_models, "sms\0system\0model\0", "md1va3");
+		} else {
+			selected_model = find_match(model_opts, num_models, "system\0model\0", "md1va3");
+		}
 	}
-	
+
 	const char *formats[] = {
 		"native",
 		"gst"
@@ -2056,29 +2074,41 @@
 	static int32_t selected_io_1 = -1;
 	static int32_t selected_io_2 = -1;
 	const uint32_t num_io = sizeof(io_opts_1)/sizeof(*io_opts_1);
-	if (selected_io_1 < 0 || selected_io_2 < 0) {
-		selected_io_1 = find_match(io_opts_1, num_io, "io\0devices\0""1\0", "gamepad6.1");
-		selected_io_2 = find_match(io_opts_2, num_io, "io\0devices\0""2\0", "gamepad6.2");
+	if (selected_io_1 < 0 || selected_io_2 < 0 || show_sms != old_show_sms) {
+		if (show_sms) {
+			selected_io_1 = find_match(io_opts_1, num_io, "sms\0io\0devices\0""1\0", "gamepad2.1");
+			selected_io_2 = find_match(io_opts_2, num_io, "sms\0io\0devices\0""2\0", "gamepad2.2");
+		} else {
+			selected_io_1 = find_match(io_opts_1, num_io, "io\0devices\0""1\0", "gamepad6.1");
+			selected_io_2 = find_match(io_opts_2, num_io, "io\0devices\0""2\0", "gamepad6.2");
+		}
 	}
-	
+	old_show_sms = show_sms;
+
 	uint32_t width = render_width();
 	uint32_t height = render_height();
 	uint32_t desired_width = context->style.font->height * 10;
 	if (nk_begin(context, "System Settings", nk_rect(0, 0, width, height), 0)) {
 		nk_layout_row_static(context, context->style.font->height, desired_width, 2);
-		
-		selected_model = settings_dropdown_ex(context, "Model", model_opts, model_names, num_models, selected_model, "system\0model\0");
-		selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, device_type_names, num_io, selected_io_1, "io\0devices\0""1\0");
-		selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, device_type_names, num_io, selected_io_2, "io\0devices\0""2\0");
+
+		if (show_sms) {
+			selected_model = settings_dropdown_ex(context, "Model", sms_model_opts, sms_model_names, num_sms_models, selected_model, "sms\0system\0model\0");
+		} else {
+			selected_model = settings_dropdown_ex(context, "Model", model_opts, model_names, num_models, selected_model, "system\0model\0");
+		}
+		selected_io_1 = settings_dropdown_ex(context, "IO Port 1 Device", io_opts_1, device_type_names, num_io, selected_io_1, show_sms ? "sms\0io\0devices\0""1\0" : "io\0devices\0""1\0");
+		selected_io_2 = settings_dropdown_ex(context, "IO Port 2 Device", io_opts_2, device_type_names, num_io, selected_io_2, show_sms ? "sms\0io\0devices\0""2\0" : "io\0devices\0""2\0");
 		selected_region = settings_dropdown_ex(context, "Default Region", region_codes, regions, num_regions, selected_region, "system\0default_region\0");
 		selected_sync = settings_dropdown(context, "Sync Source", sync_opts, num_sync_opts, selected_sync, "system\0sync_source\0");
-		settings_int_property(context, "68000 Clock Divider", "", "clocks\0m68k_divider\0", 7, 1, 53);
-		selected_format = settings_dropdown(context, "Save State Format", formats, num_formats, selected_format, "ui\0state_format\0");
+		if (!show_sms) {
+			settings_int_property(context, "68000 Clock Divider", "", "clocks\0m68k_divider\0", 7, 1, 53);
+			selected_format = settings_dropdown(context, "Save State Format", formats, num_formats, selected_format, "ui\0state_format\0");
+		}
 		selected_init = settings_dropdown(context, "Initial RAM Value", ram_inits, num_inits, selected_init, "system\0ram_init\0");
 		settings_toggle(context, "Remember ROM Path", "ui\0remember_path\0", 1);
 		settings_toggle(context, "Save config with EXE", "ui\0config_in_exe_dir\0", 0);
 		settings_string(context, "Game Save Path", "ui\0save_path\0", "$USERDATA/blastem/$ROMNAME");
-		
+
 		if (nk_button_label(context, "Back")) {
 			pop_view();
 		}
@@ -2145,7 +2175,7 @@
 		{"Reset to Defaults", view_confirm_reset},
 		{"Back", view_back}
 	};
-	
+
 	if (nk_begin(context, "Settings Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
 		menu(context, sizeof(items)/sizeof(*items), items, NULL);
 		nk_end(context);
@@ -2168,7 +2198,7 @@
 		{"Settings", view_settings},
 		{"Exit", NULL}
 	};
-	
+
 	if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
 		menu(context, sizeof(items)/sizeof(*items), items, exit_handler);
 		nk_end(context);
@@ -2183,7 +2213,7 @@
 		{"About", view_about},
 		{"Exit", NULL}
 	};
-	
+
 	if (nk_begin(context, "Main Menu", nk_rect(0, 0, render_width(), render_height()), 0)) {
 		menu(context, sizeof(items)/sizeof(*items), items, exit_handler);
 		nk_end(context);
@@ -2456,15 +2486,15 @@
 	}
 #endif
 	style_init();
-	
+
 	controller_360 = load_ui_image("images/360.png");
 	controller_ps4 = load_ui_image("images/ps4.png");
 	controller_ps4_6b = load_ui_image("images/ps4_6b.png");
 	controller_wiiu = load_ui_image("images/wiiu.png");
 	controller_gen_6b = load_ui_image("images/genesis_6b.png");
-	
+
 	texture_init();
-	
+
 	if (file_loaded) {
 		current_view = view_play;
 	} else {
@@ -2474,9 +2504,9 @@
 	render_set_ui_render_fun(blastem_nuklear_render);
 	render_set_event_handler(handle_event);
 	render_set_gl_context_handlers(context_destroyed, context_created);
-	
+
 	atexit(persist_config_exit);
-	
+
 	active = 1;
 	ui_idle_loop();
 }
--- a/sms.c	Mon Aug 22 20:43:19 2022 -0700
+++ b/sms.c	Mon Aug 22 22:56:05 2022 -0700
@@ -1,6 +1,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stddef.h>
+#include "config.h"
 #include "sms.h"
 #include "blastem.h"
 #include "render.h"
@@ -670,8 +671,26 @@
 	sms->header.info.map = malloc(sizeof(memmap_chunk) * sms->header.info.map_chunks);
 	memcpy(sms->header.info.map, memory_map, sizeof(memmap_chunk) * sms->header.info.map_chunks);
 	z80_options *zopts = malloc(sizeof(z80_options));
-	uint8_t vdp_type = strcasecmp(media->extension, "gg") ? VDP_GENESIS : VDP_GAMEGEAR;
-	if (vdp_type == VDP_GAMEGEAR) {
+	uint8_t is_gamegear = !strcasecmp(media->extension, "gg");
+	tern_node *model_def = is_gamegear ? tern_find_node(get_systems_config(), "gg") : get_model(config, SYSTEM_SMS);
+	char *vdp_str = tern_find_ptr(model_def, "vdp");
+	uint8_t vdp_type = is_gamegear ? VDP_GENESIS : VDP_GAMEGEAR;
+	if (vdp_str) {
+		if (!strcmp(vdp_str, "sms1")) {
+			vdp_type = VDP_SMS;
+		} else if (!strcmp(vdp_str, "sms2")) {
+			vdp_type = VDP_SMS2;
+		} else if (!strcmp(vdp_str, "tms9918a")) {
+			vdp_type = VDP_TMS9918A;
+		} else if (!strcmp(vdp_str, "gamegear")) {
+			vdp_type = VDP_GAMEGEAR;
+		} else if (!strcmp(vdp_str, "genesis")) {
+			vdp_type = VDP_GENESIS;
+		} else {
+			warning("Unrecognized VDP type %s\n", vdp_str);
+		}
+	}
+	if (is_gamegear) {
 		init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_gg, 6, 15, 0xFF);
 		sms->start_button_region = 0xC0;
 	} else {
--- a/systems.cfg	Mon Aug 22 20:43:19 2022 -0700
+++ b/systems.cfg	Mon Aug 22 22:56:05 2022 -0700
@@ -1,5 +1,5 @@
 md1va0 {
-	name Model 1 VA0
+	name Gen/MD 1 VA0
 	vram 64
 	vsram 40
 	zram 8
@@ -7,9 +7,10 @@
 	z80_open_bus float
 	fm discrete 2612
 	tmss off
+	vdp genesis
 }
 md1va3 {
-	name Model 1 VA3
+	name Gen/MD 1 VA3
 	vram 64
 	vsram 40
 	zram 8
@@ -17,9 +18,10 @@
 	z80_open_bus FF
 	fm discrete 2612
 	tmss off
+	vdp genesis
 }
 md1va6 {
-	name Model 1 VA6
+	name Gen/MD 1 VA6
 	vram 64
 	vsram 40
 	zram 8
@@ -27,9 +29,10 @@
 	z80_open_bus FF
 	fm discrete 2612
 	tmss on
+	vdp genesis
 }
 md2va1 {
-	name Model 2 VA1
+	name Gen/MD 2 VA1
 	vram 64
 	vsram 40
 	zram 8
@@ -37,9 +40,10 @@
 	z80_open_bus FF
 	fm integrated 3834
 	tmss on
+	vdp genesis
 }
 md2va2 {
-	name Model 2 VA2
+	name Gen/MD 2 VA2
 	vram 64
 	vsram 40
 	zram 8
@@ -47,9 +51,10 @@
 	z80_open_bus FF
 	fm discrete 2612
 	tmss on
+	vdp genesis
 }
 md3va1 {
-	name Model 3 VA1
+	name Genesis 3 VA1
 	vram 64
 	vsram 64
 	zram 8
@@ -57,9 +62,10 @@
 	z80_open_bus FF
 	fm integrated 3834
 	tmss on
+	vdp genesis
 }
 md3va2 {
-	name Model 3 VA2
+	name Genesis 3 VA2
 	vram 64
 	vsram 64
 	zram 8
@@ -67,6 +73,7 @@
 	z80_open_bus FF
 	fm integrated 3834
 	tmss on
+	vdp genesis
 }
 teradrive {
 	name Teradrive
@@ -77,4 +84,18 @@
 	z80_open_bus FF
 	fm discrete 3834
 	tmss off
-}
\ No newline at end of file
+	vdp genesis
+}
+sms1 {
+	name SMS 1
+	vdp sms1
+}
+sms2 {
+	name SMS 2
+	vdp sms2
+}
+gg {
+	name Game Gear
+	vdp gamegear
+	show no
+}