comparison menu.c @ 1692:5dacaef602a7 segacd

Merge from default
author Michael Pavone <pavone@retrodev.com>
date Sat, 05 Jan 2019 00:58:08 -0800
parents c362f2c7766a
children bdd83b47d78a
comparison
equal deleted inserted replaced
1504:95b3a1a8b26c 1692:5dacaef602a7
6 #include "genesis.h" 6 #include "genesis.h"
7 #include "menu.h" 7 #include "menu.h"
8 #include "backend.h" 8 #include "backend.h"
9 #include "util.h" 9 #include "util.h"
10 #include "gst.h" 10 #include "gst.h"
11 #include "m68k_internal.h" //needed for get_native_address_trans, should be eliminated once handling of PC is cleaned up 11 #include "paths.h"
12 12 #include "saves.h"
13 static menu_context *persist_path_menu; 13 #include "config.h"
14 static void persist_path(void)
15 {
16 char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
17 char *pathfname = alloc_concat_m(3, parts);
18 FILE *f = fopen(pathfname, "wb");
19 if (f) {
20 if (fwrite(persist_path_menu->curpath, 1, strlen(persist_path_menu->curpath), f) != strlen(persist_path_menu->curpath)) {
21 warning("Failed to save menu path");
22 }
23 fclose(f);
24 } else {
25 warning("Failed to save menu path: Could not open %s for writing\n", pathfname);
26
27 }
28 free(pathfname);
29 }
30 14
31 static menu_context *get_menu(genesis_context *gen) 15 static menu_context *get_menu(genesis_context *gen)
32 { 16 {
33 menu_context *menu = gen->extra; 17 menu_context *menu = gen->extra;
34 if (!menu) { 18 if (!menu) {
35 gen->extra = menu = calloc(1, sizeof(menu_context)); 19 gen->extra = menu = calloc(1, sizeof(menu_context));
36 menu->curpath = NULL; 20 get_initial_browse_path(&menu->curpath);
37 char *remember_path = tern_find_path(config, "ui\0remember_path\0", TVAL_PTR).ptrval;
38 if (!remember_path || !strcmp("on", remember_path)) {
39 char const *parts[] = {get_userdata_dir(), PATH_SEP, "sticky_path"};
40 char *pathfname = alloc_concat_m(3, parts);
41 FILE *f = fopen(pathfname, "rb");
42 if (f) {
43 long pathsize = file_size(f);
44 if (pathsize > 0) {
45 menu->curpath = malloc(pathsize + 1);
46 if (fread(menu->curpath, 1, pathsize, f) != pathsize) {
47 warning("Error restoring saved menu path");
48 free(menu->curpath);
49 menu->curpath = NULL;
50 } else {
51 menu->curpath[pathsize] = 0;
52 }
53 }
54 fclose(f);
55 }
56 free(pathfname);
57 if (!persist_path_menu) {
58 atexit(persist_path);
59 }
60 persist_path_menu = menu;
61 }
62 if (!menu->curpath) {
63 menu->curpath = tern_find_path(config, "ui\0initial_path\0", TVAL_PTR).ptrval;
64 }
65 if (!menu->curpath){
66 #ifdef __ANDROID__
67 menu->curpath = get_external_storage_path();
68 #else
69 menu->curpath = "$HOME";
70 #endif
71 }
72 tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
73 vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
74 menu->curpath = replace_vars(menu->curpath, vars, 1);
75 tern_free(vars);
76 } 21 }
77 return menu; 22 return menu;
78 } 23 }
79 24
80 uint16_t menu_read_w(uint32_t address, void * vcontext) 25 uint16_t menu_read_w(uint32_t address, void * vcontext)
95 //in emulated time so we can always return 0 40 //in emulated time so we can always return 0
96 return 0; 41 return 0;
97 } 42 }
98 } 43 }
99 44
100 int menu_dir_sort(const void *a, const void *b)
101 {
102 const dir_entry *da, *db;
103 da = a;
104 db = b;
105 if (da->is_dir != db->is_dir) {
106 return db->is_dir - da->is_dir;
107 }
108 return strcasecmp(((dir_entry *)a)->name, ((dir_entry *)b)->name);
109 }
110
111 void copy_string_from_guest(m68k_context *m68k, uint32_t guest_addr, char *buf, size_t maxchars) 45 void copy_string_from_guest(m68k_context *m68k, uint32_t guest_addr, char *buf, size_t maxchars)
112 { 46 {
113 char *cur; 47 char *cur;
114 char *src = NULL; 48 char *src = NULL;
115 for (cur = buf; cur < buf+maxchars; cur+=2, guest_addr+=2, src+=2) 49 for (cur = buf; cur < buf+maxchars; cur+=2, guest_addr+=2, src+=2)
147 *dst = cur[1]; 81 *dst = cur[1];
148 } 82 }
149 } 83 }
150 84
151 #define SAVE_INFO_BUFFER_SIZE (11*40) 85 #define SAVE_INFO_BUFFER_SIZE (11*40)
152
153 #ifdef __ANDROID__
154 #include <SDL.h>
155 #include <jni.h>
156 char *get_external_storage_path()
157 {
158 static char *ret;
159 if (ret) {
160 return ret;
161 }
162 JNIEnv *env = SDL_AndroidGetJNIEnv();
163 if ((*env)->PushLocalFrame(env, 8) < 0) {
164 return NULL;
165 }
166
167 jclass Environment = (*env)->FindClass(env, "android/os/Environment");
168 jmethodID getExternalStorageDirectory =
169 (*env)->GetStaticMethodID(env, Environment, "getExternalStorageDirectory", "()Ljava/io/File;");
170 jobject file = (*env)->CallStaticObjectMethod(env, Environment, getExternalStorageDirectory);
171 if (!file) {
172 goto cleanup;
173 }
174
175 jmethodID getAbsolutePath = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file),
176 "getAbsolutePath", "()Ljava/lang/String;");
177 jstring path = (*env)->CallObjectMethod(env, file, getAbsolutePath);
178
179 char const *tmp = (*env)->GetStringUTFChars(env, path, NULL);
180 ret = strdup(tmp);
181 (*env)->ReleaseStringUTFChars(env, path, tmp);
182
183 cleanup:
184 (*env)->PopLocalFrame(env, NULL);
185 return ret;
186 }
187 #endif
188
189 #ifdef _WIN32
190 #define localtime_r(a,b) localtime(a)
191 //windows inclues seem not to like certain single letter defines from m68k_internal.h
192 //get rid of them here
193 #undef X
194 #undef N
195 #undef Z
196 #undef V
197 #undef C
198 #include <windows.h>
199 #endif
200 86
201 uint32_t copy_dir_entry_to_guest(uint32_t dst, m68k_context *m68k, char *name, uint8_t is_dir) 87 uint32_t copy_dir_entry_to_guest(uint32_t dst, m68k_context *m68k, char *name, uint8_t is_dir)
202 { 88 {
203 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen); 89 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
204 if (!dest) { 90 if (!dest) {
233 *dest = 0; 119 *dest = 0;
234 dst += 2; 120 dst += 2;
235 } 121 }
236 return dst; 122 return dst;
237 } 123 }
238 124 #ifdef _WIN32
125 #include <windows.h>
126 #endif
239 void * menu_write_w(uint32_t address, void * context, uint16_t value) 127 void * menu_write_w(uint32_t address, void * context, uint16_t value)
240 { 128 {
241 m68k_context *m68k = context; 129 m68k_context *m68k = context;
242 genesis_context *gen = m68k->system; 130 genesis_context *gen = m68k->system;
243 menu_context *menu = get_menu(gen); 131 menu_context *menu = get_menu(gen);
244 if (menu->state) { 132 if (menu->state) {
245 uint32_t dst = menu->latch << 16 | value; 133 uint32_t dst = menu->latch << 16 | value;
246 switch (address >> 2) 134 switch (address >> 2)
247 { 135 {
248 case 0: { 136 case 0: {
249 #ifdef _WIN32
250 //handle virtual "drives" directory
251 if (menu->curpath[0] == PATH_SEP[0]) {
252 char drivestrings[4096];
253 if (sizeof(drivestrings) >= GetLogicalDriveStrings(sizeof(drivestrings), drivestrings)) {
254 for (char *cur = drivestrings; *cur; cur += strlen(cur) + 1)
255 {
256 dst = copy_dir_entry_to_guest(dst, m68k, cur, 1);
257 }
258 }
259 //terminate list
260 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
261 if (dest) {
262 *dest = dest[1] = 0;
263 }
264 break;
265 }
266 #endif
267 size_t num_entries; 137 size_t num_entries;
268 dir_entry *entries = get_dir_list(menu->curpath, &num_entries); 138 dir_entry *entries = get_dir_list(menu->curpath, &num_entries);
269 if (entries) { 139 if (entries) {
270 qsort(entries, num_entries, sizeof(dir_entry), menu_dir_sort); 140 sort_dir_list(entries, num_entries);
271 } else { 141 } else {
272 warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno)); 142 warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno));
273 entries = malloc(sizeof(dir_entry)); 143 entries = malloc(sizeof(dir_entry));
274 entries->name = strdup(".."); 144 entries->name = strdup("..");
275 entries->is_dir = 1; 145 entries->is_dir = 1;
276 num_entries = 1; 146 num_entries = 1;
277 } 147 }
278 #ifdef _WIN32 148 uint32_t num_exts;
279 if (menu->curpath[1] == ':' && !menu->curpath[2]) { 149 char **ext_list = get_extension_list(config, &num_exts);
280 //Add fake .. entry to allow navigation to virtual "drives" directory
281 dst = copy_dir_entry_to_guest(dst, m68k, "..", 1);
282 }
283 #endif
284 char *ext_filter = strdup(tern_find_path_default(config, "ui\0extensions\0", (tern_val){.ptrval = "bin gen md smd sms gg"}, TVAL_PTR).ptrval);
285 uint32_t num_exts = 0, ext_storage = 5;
286 char **ext_list = malloc(sizeof(char *) * ext_storage);
287 char *cur_filter = ext_filter;
288 while (*cur_filter)
289 {
290 if (num_exts == ext_storage) {
291 ext_storage *= 2;
292 ext_list = realloc(ext_list, sizeof(char *) * ext_storage);
293 }
294 ext_list[num_exts++] = cur_filter;
295 cur_filter = split_keyval(cur_filter);
296 }
297 for (size_t i = 0; dst && i < num_entries; i++) 150 for (size_t i = 0; dst && i < num_entries; i++)
298 { 151 {
299 if (num_exts && !entries[i].is_dir) { 152 if (num_exts && !entries[i].is_dir) {
300 char *ext = path_extension(entries[i].name); 153 if (!path_matches_extensions(entries[i].name, ext_list, num_exts)) {
301 if (!ext) {
302 continue;
303 }
304 uint32_t extidx;
305 for (extidx = 0; extidx < num_exts; extidx++)
306 {
307 if (!strcasecmp(ext, ext_list[extidx])) {
308 break;
309 }
310 }
311 if (extidx == num_exts) {
312 continue; 154 continue;
313 } 155 }
314 } 156 }
315 dst = copy_dir_entry_to_guest(dst, m68k, entries[i].name, entries[i].is_dir); 157 dst = copy_dir_entry_to_guest(dst, m68k, entries[i].name, entries[i].is_dir);
316 } 158 }
317 free(ext_filter);
318 free(ext_list); 159 free(ext_list);
319 //terminate list 160 //terminate list
320 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen); 161 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen);
321 if (dest) { 162 if (dest) {
322 *dest = dest[1] = 0; 163 *dest = dest[1] = 0;
325 break; 166 break;
326 } 167 }
327 case 1: { 168 case 1: {
328 char buf[4096]; 169 char buf[4096];
329 copy_string_from_guest(m68k, dst, buf, sizeof(buf)); 170 copy_string_from_guest(m68k, dst, buf, sizeof(buf));
330 if (!strcmp(buf, "..")) { 171 buf[sizeof(buf)-1] = 0;
331 #ifdef _WIN32 172 char *tmp = menu->curpath;
332 if (menu->curpath[1] == ':' && !menu->curpath[2]) { 173 menu->curpath = path_append(tmp, buf);
333 menu->curpath[0] = PATH_SEP[0]; 174 free(tmp);
334 menu->curpath[1] = 0;
335 break;
336 }
337 #endif
338 size_t len = strlen(menu->curpath);
339 while (len > 0) {
340 --len;
341 if (is_path_sep(menu->curpath[len])) {
342 if (!len) {
343 //special handling for /
344 menu->curpath[len+1] = 0;
345 } else {
346 menu->curpath[len] = 0;
347 }
348 break;
349 }
350 }
351 } else {
352 char *tmp = menu->curpath;
353 #ifdef _WIN32
354 if (menu->curpath[0] == PATH_SEP[0] && !menu->curpath[1]) {
355 menu->curpath = strdup(buf);
356 } else
357 #endif
358 if (is_path_sep(menu->curpath[strlen(menu->curpath) - 1])) {
359 menu->curpath = alloc_concat(menu->curpath, buf);
360 } else {
361 char const *pieces[] = {menu->curpath, PATH_SEP, buf};
362 menu->curpath = alloc_concat_m(3, pieces);
363 }
364 free(tmp);
365 }
366 break; 175 break;
367 } 176 }
368 case 2: 177 case 2:
369 case 8: { 178 case 8: {
370 char buf[4096]; 179 char buf[4096];
397 case 4: { 206 case 4: {
398 char *buffer = malloc(SAVE_INFO_BUFFER_SIZE); 207 char *buffer = malloc(SAVE_INFO_BUFFER_SIZE);
399 char *cur = buffer; 208 char *cur = buffer;
400 if (gen->header.next_context && gen->header.next_context->save_dir) { 209 if (gen->header.next_context && gen->header.next_context->save_dir) {
401 char *end = buffer + SAVE_INFO_BUFFER_SIZE; 210 char *end = buffer + SAVE_INFO_BUFFER_SIZE;
402 char slotfile[] = "slot_0.state"; 211 uint32_t num_slots;
403 char slotfilegst[] = "slot_0.gst"; 212 save_slot_info *slots = get_slot_info(gen->header.next_context, &num_slots);
404 char const * parts[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfile}; 213 for (uint32_t i = 0; i < num_slots; i++)
405 char const * partsgst[3] = {gen->header.next_context->save_dir, PATH_SEP, slotfilegst};
406 struct tm ltime;
407 char *fname;
408 time_t modtime;
409 for (int i = 0; i < 10 && cur < end; i++)
410 { 214 {
411 slotfile[5] = i + '0'; 215 size_t desc_len = strlen(slots[i].desc) + 1;//+1 for string terminator
412 fname = alloc_concat_m(3, parts); 216 char *after = cur + desc_len + 1;//+1 for list terminator
413 modtime = get_modification_time(fname); 217 if (after > cur) {
414 free(fname); 218 desc_len -= after - cur;
415 if (modtime) {
416 cur += snprintf(cur, end-cur, "Slot %d - ", i);
417 cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
418
419 } else {
420 slotfilegst[5] = i + '0';
421 fname = alloc_concat_m(3, partsgst);
422 modtime = get_modification_time(fname);
423 free(fname);
424 if (modtime) {
425 cur += snprintf(cur, end-cur, "Slot %d - ", i);
426 cur += strftime(cur, end-cur, "%c", localtime_r(&modtime, &ltime));
427 } else {
428 cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i);
429 }
430 } 219 }
431 //advance past the null terminator for this entry 220 memcpy(cur, slots[i].desc, desc_len);
432 cur++; 221 cur = after;
433 } 222 }
434 if (cur < end) { 223 *cur = 0;//terminate list
435 parts[2] = "quicksave.state";
436 fname = alloc_concat_m(3, parts);
437 modtime = get_modification_time(fname);
438 free(fname);
439 if (modtime) {
440 cur += strftime(cur, end-cur, "Quick - %c", localtime_r(&modtime, &ltime));
441 } else {
442 parts[2] = "quicksave.gst";
443 fname = alloc_concat_m(3, parts);
444 modtime = get_modification_time(fname);
445 free(fname);
446 if (modtime) {
447 cur += strftime(cur, end-cur, "Quick - %c", localtime_r(&modtime, &ltime));
448 } else if ((end-cur) > strlen("Quick - EMPTY")){
449 cur += strlen(strcpy(cur, "Quick - EMPTY"));
450 }
451 }
452 //advance past the null terminator for this entry
453 cur++;
454 if (cur < end) {
455 //terminate the list
456 *(cur++) = 0;
457 }
458 }
459 } else { 224 } else {
460 *(cur++) = 0; 225 *(cur++) = 0;
461 *(cur++) = 0; 226 *(cur++) = 0;
462 } 227 }
463 copy_to_guest(m68k, dst, buffer, cur-buffer); 228 copy_to_guest(m68k, dst, buffer, cur-buffer);
473 case 6: 238 case 6:
474 //load state 239 //load state
475 if (gen->header.next_context && gen->header.next_context->save_dir) { 240 if (gen->header.next_context && gen->header.next_context->save_dir) {
476 if (!gen->header.next_context->load_state(gen->header.next_context, dst)) { 241 if (!gen->header.next_context->load_state(gen->header.next_context, dst)) {
477 break; 242 break;
478 }/*
479 char numslotname[] = "slot_0.state";
480 char *slotname;
481 if (dst == QUICK_SAVE_SLOT) {
482 slotname = "quicksave.state";
483 } else {
484 numslotname[5] = '0' + dst;
485 slotname = numslotname;
486 } 243 }
487 char const *parts[] = {gen->header.next_context->save_dir, PATH_SEP, slotname};
488 char *statepath = alloc_concat_m(3, parts);
489 gen->header.next_context->load_state
490 genesis_context *next = (genesis_context *)gen->header.next_context;
491 deserialize_buffer state;
492 uint32_t pc = 0;
493 if (load_from_file(&state, statepath)) {
494 genesis_deserialize(&state, next);
495 free(state.data);
496 //HACK
497 pc = next->m68k->last_prefetch_address;
498 } else {
499 strcpy(statepath + strlen(statepath)-strlen("state"), "gst");
500 pc = load_gst(next, statepath);
501 }
502 free(statepath);
503 if (!pc) {
504 break;
505 }
506 next->m68k->resume_pc = get_native_address_trans(next->m68k, pc);
507 */
508 } 244 }
509 m68k->should_return = 1; 245 m68k->should_return = 1;
510 break; 246 break;
511 case 7: 247 case 7:
512 //read only port 248 //read only port