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}