Mercurial > repos > blastem
comparison menu.c @ 1648:b7ecd0d6a77b mame_interp
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 25 Dec 2018 11:12:26 -0800 |
parents | 3629366616da |
children | c362f2c7766a |
comparison
equal
deleted
inserted
replaced
1509:36732f5c2281 | 1648:b7ecd0d6a77b |
---|---|
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 | 137 #if _WIN32 |
250 //handle virtual "drives" directory | 138 //handle virtual "drives" directory |
251 if (menu->curpath[0] == PATH_SEP[0]) { | 139 if (menu->curpath[0] == PATH_SEP[0]) { |
252 char drivestrings[4096]; | 140 char drivestrings[4096]; |
253 if (sizeof(drivestrings) >= GetLogicalDriveStrings(sizeof(drivestrings), drivestrings)) { | 141 if (sizeof(drivestrings) >= GetLogicalDriveStrings(sizeof(drivestrings), drivestrings)) { |
254 for (char *cur = drivestrings; *cur; cur += strlen(cur) + 1) | 142 for (char *cur = drivestrings; *cur; cur += strlen(cur) + 1) |
265 } | 153 } |
266 #endif | 154 #endif |
267 size_t num_entries; | 155 size_t num_entries; |
268 dir_entry *entries = get_dir_list(menu->curpath, &num_entries); | 156 dir_entry *entries = get_dir_list(menu->curpath, &num_entries); |
269 if (entries) { | 157 if (entries) { |
270 qsort(entries, num_entries, sizeof(dir_entry), menu_dir_sort); | 158 sort_dir_list(entries, num_entries); |
271 } else { | 159 } else { |
272 warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno)); | 160 warning("Failed to open directory %s: %s\n", menu->curpath, strerror(errno)); |
273 entries = malloc(sizeof(dir_entry)); | 161 entries = malloc(sizeof(dir_entry)); |
274 entries->name = strdup(".."); | 162 entries->name = strdup(".."); |
275 entries->is_dir = 1; | 163 entries->is_dir = 1; |
279 if (menu->curpath[1] == ':' && !menu->curpath[2]) { | 167 if (menu->curpath[1] == ':' && !menu->curpath[2]) { |
280 //Add fake .. entry to allow navigation to virtual "drives" directory | 168 //Add fake .. entry to allow navigation to virtual "drives" directory |
281 dst = copy_dir_entry_to_guest(dst, m68k, "..", 1); | 169 dst = copy_dir_entry_to_guest(dst, m68k, "..", 1); |
282 } | 170 } |
283 #endif | 171 #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); | 172 uint32_t num_exts; |
285 uint32_t num_exts = 0, ext_storage = 5; | 173 char **ext_list = get_extension_list(config, &num_exts); |
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++) | 174 for (size_t i = 0; dst && i < num_entries; i++) |
298 { | 175 { |
299 if (num_exts && !entries[i].is_dir) { | 176 if (num_exts && !entries[i].is_dir) { |
300 char *ext = path_extension(entries[i].name); | 177 if (!path_matches_extensions(entries[i].name, ext_list, num_exts)) { |
301 if (!ext) { | |
302 continue; | 178 continue; |
303 } | 179 } |
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; | |
313 } | |
314 } | 180 } |
315 dst = copy_dir_entry_to_guest(dst, m68k, entries[i].name, entries[i].is_dir); | 181 dst = copy_dir_entry_to_guest(dst, m68k, entries[i].name, entries[i].is_dir); |
316 } | 182 } |
317 free(ext_filter); | |
318 free(ext_list); | 183 free(ext_list); |
319 //terminate list | 184 //terminate list |
320 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen); | 185 uint8_t *dest = get_native_pointer(dst, (void **)m68k->mem_pointers, &m68k->options->gen); |
321 if (dest) { | 186 if (dest) { |
322 *dest = dest[1] = 0; | 187 *dest = dest[1] = 0; |
325 break; | 190 break; |
326 } | 191 } |
327 case 1: { | 192 case 1: { |
328 char buf[4096]; | 193 char buf[4096]; |
329 copy_string_from_guest(m68k, dst, buf, sizeof(buf)); | 194 copy_string_from_guest(m68k, dst, buf, sizeof(buf)); |
330 if (!strcmp(buf, "..")) { | 195 buf[sizeof(buf)-1] = 0; |
331 #ifdef _WIN32 | 196 char *tmp = menu->curpath; |
332 if (menu->curpath[1] == ':' && !menu->curpath[2]) { | 197 menu->curpath = path_append(tmp, buf); |
333 menu->curpath[0] = PATH_SEP[0]; | 198 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; | 199 break; |
367 } | 200 } |
368 case 2: | 201 case 2: |
369 case 8: { | 202 case 8: { |
370 char buf[4096]; | 203 char buf[4096]; |
397 case 4: { | 230 case 4: { |
398 char *buffer = malloc(SAVE_INFO_BUFFER_SIZE); | 231 char *buffer = malloc(SAVE_INFO_BUFFER_SIZE); |
399 char *cur = buffer; | 232 char *cur = buffer; |
400 if (gen->header.next_context && gen->header.next_context->save_dir) { | 233 if (gen->header.next_context && gen->header.next_context->save_dir) { |
401 char *end = buffer + SAVE_INFO_BUFFER_SIZE; | 234 char *end = buffer + SAVE_INFO_BUFFER_SIZE; |
402 char slotfile[] = "slot_0.state"; | 235 uint32_t num_slots; |
403 char slotfilegst[] = "slot_0.gst"; | 236 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}; | 237 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 { | 238 { |
411 slotfile[5] = i + '0'; | 239 size_t desc_len = strlen(slots[i].desc) + 1;//+1 for string terminator |
412 fname = alloc_concat_m(3, parts); | 240 char *after = cur + desc_len + 1;//+1 for list terminator |
413 modtime = get_modification_time(fname); | 241 if (after > cur) { |
414 free(fname); | 242 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, <ime)); | |
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, <ime)); | |
427 } else { | |
428 cur += snprintf(cur, end-cur, "Slot %d - EMPTY", i); | |
429 } | |
430 } | 243 } |
431 //advance past the null terminator for this entry | 244 memcpy(cur, slots[i].desc, desc_len); |
432 cur++; | 245 cur = after; |
433 } | 246 } |
434 if (cur < end) { | 247 *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, <ime)); | |
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, <ime)); | |
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 { | 248 } else { |
460 *(cur++) = 0; | 249 *(cur++) = 0; |
461 *(cur++) = 0; | 250 *(cur++) = 0; |
462 } | 251 } |
463 copy_to_guest(m68k, dst, buffer, cur-buffer); | 252 copy_to_guest(m68k, dst, buffer, cur-buffer); |
473 case 6: | 262 case 6: |
474 //load state | 263 //load state |
475 if (gen->header.next_context && gen->header.next_context->save_dir) { | 264 if (gen->header.next_context && gen->header.next_context->save_dir) { |
476 if (!gen->header.next_context->load_state(gen->header.next_context, dst)) { | 265 if (!gen->header.next_context->load_state(gen->header.next_context, dst)) { |
477 break; | 266 break; |
478 }/* | 267 } |
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 } | |
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 } | 268 } |
509 m68k->should_return = 1; | 269 m68k->should_return = 1; |
510 break; | 270 break; |
511 case 7: | 271 case 7: |
512 //read only port | 272 //read only port |