main menu.c
  1#include <stdint.h>
  2#include <stdlib.h>
  3#include <string.h>
  4#include <stdio.h>
  5#include <errno.h>
  6#include "genesis.h"
  7#include "menu.h"
  8#include "backend.h"
  9#include "util.h"
 10#include "gst.h"
 11#include "paths.h"
 12#include "saves.h"
 13#include "config.h"
 14
 15static menu_context *get_menu(genesis_context *gen)
 16{
 17	menu_context *menu = gen->extra;
 18	if (!menu) {
 19		gen->extra = menu = calloc(1, sizeof(menu_context));
 20		get_initial_browse_path(&menu->curpath);
 21	}
 22	return menu;
 23}
 24
 25uint16_t menu_read_w(uint32_t address, void * vcontext)
 26{
 27	if ((address >> 1) == 14) {
 28		m68k_context *context = vcontext;
 29		menu_context *menu = get_menu(context->system);
 30		uint16_t value = menu->external_game_load;
 31		if (value) {
 32			printf("Read: %X\n", value);
 33		}
 34		menu->external_game_load = 0;
 35		return value;
 36	} else {
 37		//This should return the status of the last request with 0
 38		//meaning either the request is complete or no request is pending
 39		//in the current implementation, the operations happen instantly
 40		//in emulated time so we can always return 0
 41		return 0;
 42	}
 43}
 44
 45void copy_string_from_guest(m68k_context *m68k, uint32_t guest_addr, char *buf, size_t maxchars)
 46{
 47	char *cur;
 48	char *src = NULL;
 49	for (cur = buf; cur < buf+maxchars; cur+=2, guest_addr+=2, src+=2)
 50	{
 51		if (!src || !(guest_addr & 0xFFFF)) {
 52			//we may have walked off the end of a memory block, get a fresh native pointer
 53			src = get_native_pointer(guest_addr, (void **)m68k->mem_pointers, &m68k->options->gen);
 54			if (!src) {
 55				break;
 56			}
 57		}
 58		*cur = src[1];
 59		cur[1] = *src;
 60		if (!*src || !src[1]) {
 61			break;
 62		}
 63	}
 64	//make sure we terminate the string even if we did not hit a null terminator in the source
 65	buf[maxchars-1] = 0;
 66}
 67
 68void copy_to_guest(m68k_context *m68k, uint32_t guest_addr, char *src, size_t tocopy)
 69{
 70	char *dst = NULL;
 71	for (char *cur = src; cur < src+tocopy; cur+=2, guest_addr+=2, dst+=2)
 72	{
 73		if (!dst || !(guest_addr & 0xFFFF)) {
 74			//we may have walked off the end of a memory block, get a fresh native pointer
 75			dst = get_native_pointer(guest_addr, (void **)m68k->mem_pointers, &m68k->options->gen);
 76			if (!dst) {
 77				break;
 78			}
 79		}
 80		dst[1] = *cur;
 81		*dst = cur[1];
 82	}
 83}
 84
 85#define SAVE_INFO_BUFFER_SIZE (11*40)
 86
 87uint32_t copy_dir_entry_to_guest(uint32_t dst, m68k_context *m68k, char *name, uint8_t is_dir)
 88{
 89	uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
 90	if (!dest) {
 91		return 0;
 92	}
 93	*(dest++) = is_dir;
 94	*(dest++) = 1;
 95	dst += 2;
 96	uint8_t term = 0;
 97	for (char *cpos = name; *cpos; cpos++)
 98	{
 99		dest[1] = *cpos;
100		dest[0] = cpos[1];
101		if (cpos[1]) {
102			cpos++;
103		} else {
104			term = 1;
105		}
106		dst += 2;
107		if (!(dst & 0xFFFF)) {
108			//we may have walked off the end of a memory block, get a fresh native pointer
109			dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
110			if (!dest) {
111				break;
112			}
113		} else {
114			dest += 2;
115		}
116	}
117	if (!term) {
118		*(dest++) = 0;
119		*dest = 0;
120		dst += 2;
121	}
122	return dst;
123}
124void * menu_write_w(uint32_t address, void * context, uint16_t value)
125{
126	m68k_context *m68k = context;
127	genesis_context *gen = m68k->system;
128	menu_context *menu = get_menu(gen);
129	if (menu->state) {
130		uint32_t dst = menu->latch << 16 | value;
131		switch (address >> 2)
132		{
133		case 0: {
134			size_t num_entries;
135			dir_entry *entries = get_dir_list(menu->curpath, &num_entries);
136			if (entries) {
137				sort_dir_list(entries, num_entries);
138			} else {
139				warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno));
140				entries = malloc(sizeof(dir_entry));
141				entries->name = strdup("..");
142				entries->is_dir = 1;
143				num_entries = 1;
144			}
145			uint32_t num_exts;
146			char **ext_list = get_extension_list(config, &num_exts);
147			for (size_t i = 0; dst && i < num_entries; i++)
148			{
149				if (num_exts && !entries[i].is_dir) {
150					if (!path_matches_extensions(entries[i].name, ext_list, num_exts)) {
151						continue;
152					}
153				}
154				dst = copy_dir_entry_to_guest(dst,  m68k, entries[i].name, entries[i].is_dir);
155			}
156			free(ext_list);
157			//terminate list
158			uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
159			if (dest) {
160				*dest = dest[1] = 0;
161			}
162			free_dir_list(entries, num_entries);
163			break;
164		}
165		case 1: {
166			char buf[4096];
167			copy_string_from_guest(m68k, dst, buf, sizeof(buf));
168			buf[sizeof(buf)-1] = 0;
169			char *tmp = menu->curpath;
170			menu->curpath = path_append(tmp, buf);
171			free(tmp);
172			break;
173		}
174		case 2:
175		case 8: {
176			char buf[4096];
177			copy_string_from_guest(m68k, dst, buf, sizeof(buf));
178			char const *pieces[] = {menu->curpath, PATH_SEP, buf};
179			char *selected = alloc_concat_m(3, pieces);
180			if ((address >> 2) == 2) {
181				gen->header.next_rom = selected;
182				m68k->should_return = 1;
183			} else {
184				lockon_media(selected);
185				free(selected);
186			}
187			break;
188		}
189		case 3: {
190			switch (dst)
191			{
192			case 1:
193				m68k->should_return = 1;
194				gen->header.should_exit = 1;
195				break;
196			case 2:
197				m68k->should_return = 1;
198				break;
199			}
200			
201			break;
202		}
203		case 4: {
204			char *buffer = malloc(SAVE_INFO_BUFFER_SIZE);
205			char *cur = buffer;
206			if (gen->header.next_context && gen->header.next_context->save_dir) {
207				char *end = buffer + SAVE_INFO_BUFFER_SIZE;
208				uint32_t num_slots;
209				save_slot_info *slots = get_slot_info(gen->header.next_context, &num_slots);
210				for (uint32_t i = 0; i < num_slots; i++)
211				{
212					size_t desc_len = strlen(slots[i].desc) + 1;//+1 for string terminator
213					char *after = cur + desc_len + 1;//+1 for list terminator
214					if (after > cur) {
215						desc_len -= after - cur;
216					}
217					memcpy(cur, slots[i].desc, desc_len);
218					cur = after;
219				}
220				*cur = 0;//terminate list
221			} else {
222				*(cur++) = 0;
223				*(cur++) = 0;
224			}
225			copy_to_guest(m68k, dst, buffer, cur-buffer);
226			break;
227		}
228		case 5:
229			//save state
230			if (gen->header.next_context) {
231				gen->header.next_context->save_state = dst + 1;
232			}
233			m68k->should_return = 1;
234			break;
235		case 6:
236			//load state
237			if (gen->header.next_context && gen->header.next_context->save_dir) {
238				if (!gen->header.next_context->load_state(gen->header.next_context, dst)) {
239					break;
240				}
241			}
242			m68k->should_return = 1;
243			break;
244		case 7: 
245			//read only port
246			break;
247		default:
248			fprintf(stderr, "WARNING: write to undefined menu port %X\n", address);
249		}
250		menu->state = 0;
251	} else {
252		menu->latch = value;
253		menu->state = 1;
254	}
255	if (m68k->should_return) {
256		m68k->target_cycle = m68k->current_cycle;
257	}
258
259	return context;
260}