diff --git a/file_io.cpp b/file_io.cpp index f303f8e..802db9a 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -1171,7 +1171,7 @@ int ScanDirectory(char* path, int mode, const char *extension, int options, cons if (!strcasecmp(dext.altname + strlen(dext.altname) - 4, ".zip")) dext.altname[strlen(dext.altname) - 4] = 0; full_path[path_len] = 0; - char *altname = neogeo_get_altname(full_path, &dext); + char *altname = neogeo_get_altname(full_path, dext.de.d_name, dext.altname); if (altname) { if (altname == (char*)-1) continue; diff --git a/input.cpp b/input.cpp index f5ba9d3..f9ef44a 100644 --- a/input.cpp +++ b/input.cpp @@ -2182,6 +2182,15 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int return; } + // Recent Files menu + // FIXME: temporary pass through of select joypad button. unsure of the best way to do this. + // updates here may require changes in menu.cpp to match the key mapping + if (ev->code == input[dev].mmap[SYS_BTN_SELECT] && !osd_event) { + struct input_event key_ev = *ev; + key_ev.code = KEY_GRAVE; + input_cb(&key_ev, 0, 0); + } + for (int i = 0; i < SYS_BTN_A; i++) { if (ev->code == input[dev].mmap[i]) diff --git a/menu.cpp b/menu.cpp index 22c3685..c9aaf17 100644 --- a/menu.cpp +++ b/menu.cpp @@ -56,6 +56,7 @@ along with this program. If not, see . #include "cheats.h" #include "video.h" #include "joymapping.h" +#include "recent.h" #include "support.h" @@ -125,6 +126,8 @@ enum MENU MENU_LGCAL2, MENU_CHEATS1, MENU_CHEATS2, + MENU_RECENT1, + MENU_RECENT2, // Mist/atari specific pages MENU_MIST_MAIN1, @@ -798,7 +801,7 @@ void HandleUI(void) static char ioctl_index; char *p; static char s[256]; - unsigned char m = 0, up, down, select, menu, right, left, plus, minus; + unsigned char m = 0, up, down, select, menu, right, left, plus, minus, recent; char enable; static int reboot_req = 0; static long helptext_timer; @@ -815,6 +818,10 @@ void HandleUI(void) static unsigned long flash_timer = 0; static int flash_state = 0; + // recent files menu state + static uint32_t recentsub = 0; + static enum MENU recentselect = MENU_NONE1; + static char cp_MenuCancel; // get user control codes @@ -829,6 +836,7 @@ void HandleUI(void) right = false; plus = false; minus = false; + recent = false; if (c && c != KEY_F12 && cfg.bootcore[0] != '\0') cfg.bootcore[0] = '\0'; @@ -963,6 +971,9 @@ void HandleUI(void) case KEY_MINUS: // -/_ minus = true; break; + case KEY_GRAVE: + recent = true; + break; } } @@ -1599,6 +1610,7 @@ void HandleUI(void) if (p[0] == 'R') menustate = MENU_NONE1; } } + } } } @@ -1612,6 +1624,69 @@ void HandleUI(void) menustate = MENU_8BIT_INFO; menusub = 1; } + else if (recent) + { + // parse F/S options since + char ext[256]; + + int h = 0, d = 0; + int i = 2; + p = user_io_get_confstr(i++); + recentselect = MENU_8BIT_MAIN1; + while (p) + { + h = 0; + d = 0; + + //Hide or Disable flag + while ((p[0] == 'H' || p[0] == 'D') && strlen(p) > 2) + { + int flg = (hdmask & (1 << getIdx(p))) ? 1 : 0; + if (p[0] == 'H') h |= flg; else d |= flg; + p += 2; + } + + // skip hidden or disabled entries + if (h || d) continue; + + if (p[0] == 'F') { + recentselect = MENU_8BIT_MAIN_FILE_SELECTED; + opensave = 0; + ioctl_index = menusub + 1; + int idx = 1; + + if (p[1] == 'S') + { + opensave = 1; + idx++; + } + + if (p[idx] >= '0' && p[idx] <= '9') ioctl_index = p[idx] - '0'; + substrcpy(ext, p, 1); + if (!strcasecmp(user_io_get_core_name(), "GBA") && FileExists(user_io_make_filepath(HomeDir, "goomba.rom"))) strcat(ext, "GB GBC"); + while (strlen(ext) % 3) strcat(ext, " "); + strcpy(fs_pFileExt, ext); + fs_ExtLen = strlen(fs_pFileExt); + + break; + } + else if (p[0] == 'S') + { + recentselect = MENU_8BIT_MAIN_IMAGE_SELECTED; + + break; + } + + p = user_io_get_confstr(i++); + } + + menustate = MENU_RECENT1; + recentsub = menusub; + menusub = 0; + fs_Options = is_neogeo_core() ? SCANO_NEOGEO : 0; + + recent_init(); + } break; case MENU_8BIT_MAIN_FILE_SELECTED: @@ -1629,6 +1704,8 @@ void HandleUI(void) if (user_io_use_cheats()) cheats_init(SelectedPath, user_io_get_file_crc()); menustate = MENU_NONE1; } + + recent_update(SelectedDir, SelectedPath); break; case MENU_8BIT_MAIN_IMAGE_SELECTED: @@ -1669,6 +1746,8 @@ void HandleUI(void) } menustate = SelectedPath[0] ? MENU_NONE1 : MENU_8BIT_MAIN1; + + recent_update(SelectedDir, SelectedPath); break; case MENU_8BIT_SYSTEM1: @@ -3423,6 +3502,71 @@ void HandleUI(void) } break; + /******************************************************************/ + /* last rom menu */ + /******************************************************************/ + case MENU_RECENT1: + helptext = helptexts[HELPTEXT_NONE]; + OsdSetTitle("Recent Files"); + recent_print(); + menustate = MENU_RECENT2; + parentstate = menustate; + break; + + case MENU_RECENT2: + menumask = 0; + + if (menu || recent) + { + menustate = MENU_8BIT_MAIN1; + menusub = recentsub; + break; + } + + recent_scroll_name(); + + if (c == KEY_HOME) + { + recent_scan(SCANF_INIT); + menustate = MENU_RECENT1; + } + + if (c == KEY_END) + { + recent_scan(SCANF_END); + menustate = MENU_RECENT1; + } + + if ((c == KEY_PAGEUP) || (c == KEY_LEFT)) + { + recent_scan(SCANF_PREV_PAGE); + menustate = MENU_RECENT1; + } + + if ((c == KEY_PAGEDOWN) || (c == KEY_RIGHT)) + { + recent_scan(SCANF_NEXT_PAGE); + menustate = MENU_RECENT1; + } + + if (down) // scroll down one entry + { + recent_scan(SCANF_NEXT); + menustate = MENU_RECENT1; + } + + if (up) // scroll up one entry + { + recent_scan(SCANF_PREV); + menustate = MENU_RECENT1; + } + + if (select) + { + menustate = recent_select(SelectedDir, SelectedPath) ? recentselect : MENU_RECENT1; + } + break; + /******************************************************************/ /* reset menu */ /******************************************************************/ diff --git a/recent.cpp b/recent.cpp new file mode 100644 index 0000000..05640c4 --- /dev/null +++ b/recent.cpp @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hardware.h" +#include "file_io.h" +#include "user_io.h" +#include "fpga_io.h" +#include "miniz_zip.h" +#include "osd.h" +#include "recent.h" +#include "support.h" + +#define RECENT_MAX 16 + +struct recent_rec_t +{ + char dir[1024]; + char name[256]; +}; + +struct display_name_t +{ + char name[256]; +}; + +typedef std::vector RecentVector; +static RecentVector recents(RECENT_MAX); +typedef std::vector RecentDisplayNameVector; +static RecentDisplayNameVector displaynames(RECENT_MAX); + +static int numlast = 0; + +static int iSelectedEntry = 0; +static int iFirstEntry = 0; + +void recent_init() +{ + recent_load(); + recent_scan(SCANF_INIT); +} + +void recent_scan(int mode) +{ + if (mode == SCANF_INIT) + { + iFirstEntry = 0; + iSelectedEntry = 0; + } + else + { + if (!recent_available()) return; + + if (mode == SCANF_END) + { + iSelectedEntry = recent_available() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + else if (mode == SCANF_NEXT) + { + if (iSelectedEntry + 1 < recent_available()) // scroll within visible items + { + iSelectedEntry++; + if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + } + } + else if (mode == SCANF_PREV) + { + if (iSelectedEntry > 0) // scroll within visible items + { + iSelectedEntry--; + if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry; + } + } + else if (mode == SCANF_NEXT_PAGE) + { + if (iSelectedEntry < iFirstEntry + OsdGetSize() - 1) + { + iSelectedEntry = iFirstEntry + OsdGetSize() - 1; + if (iSelectedEntry >= recent_available()) iSelectedEntry = recent_available() - 1; + } + else + { + iSelectedEntry += OsdGetSize(); + iFirstEntry += OsdGetSize(); + if (iSelectedEntry >= recent_available()) + { + iSelectedEntry = recent_available() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + else if (iFirstEntry + OsdGetSize() > recent_available()) + { + iFirstEntry = recent_available() - OsdGetSize(); + } + } + } + else if (mode == SCANF_PREV_PAGE) + { + if (iSelectedEntry != iFirstEntry) + { + iSelectedEntry = iFirstEntry; + } + else + { + iFirstEntry -= OsdGetSize(); + if (iFirstEntry < 0) iFirstEntry = 0; + iSelectedEntry = iFirstEntry; + } + } + } +} + +void recent_scroll_name() +{ + // this function is called periodically when file selection window is displayed + // it checks if predefined period of time has elapsed and scrolls the name if necessary + int len; + int max_len; + static char name[256 + 4]; + + // don't scroll if the file doesn't exist + if (!FileExists(recent_path(recents[iSelectedEntry].dir, recents[iSelectedEntry].name))) return; + + name[0] = 32; + strcpy(name + 1, displaynames[iSelectedEntry].name); + + len = strlen(name); // get name length + + max_len = 30; + ScrollText(iSelectedEntry - iFirstEntry, name, 1, len, max_len, 1); +} + +void recent_print() +{ + int k; + int len; + + static char s[256+4]; + + ScrollReset(); + + for (int i = 0; i < OsdGetSize(); i++) + { + char leftchar = 0; + unsigned char d = 1; + if (i < recent_available()) + { + k = iFirstEntry + i; + + s[0] = 32; + char* name = displaynames[k].name; + strcpy(s + 1, name); + + len = strlen(s); // get name length + + s[len] = 0; + + if (len > 28) + { + len = 27; // trim display length if longer than 30 characters + s[28] = 22; + } + + s[29] = 0; + + if (!i && k) leftchar = 17; + if ((i == OsdGetSize() - 1) && (k < recent_available() - 1)) leftchar = 16; + + // check if file exists + d = FileExists(recent_path(recents[k].dir, recents[k].name)) ? 0 : 1; + } + else + { + memset(s, ' ', 32); + } + + OsdWriteOffset(i, s, i == (iSelectedEntry - iFirstEntry) && recent_available(), d, 0, leftchar); + } +} + +int recent_available() +{ + return numlast; +} + +void recent_load() +{ + // initialize recent to empty strings + memset(recents.data(), 0, recents.size() * sizeof(recent_rec_t)); + + // load the config file into memory + FileLoadConfig(recent_create_config_name(), recents.data(), recents.size() * sizeof(recent_rec_t)); + + for (numlast = 0; numlast < (int)recents.size() && strlen(recents[numlast].name); numlast++) {} + + // init display names to file names + for (int i = 0; i < recent_available(); i++) memcpy(displaynames[i].name, recents[i].name, sizeof(displaynames[i].name)); + + if (is_neogeo_core()) { + for (int i = 0; i < recent_available(); i++) { + // update display names for neogeo neo files + char* altname = neogeo_get_altname(recents[i].dir, recents[i].name, recents[i].name); + if (altname) strcpy(displaynames[i].name, altname); + } + } +} + +void recent_save() +{ + // store the config file to storage + FileSaveConfig(recent_create_config_name(), recents.data(), recents.size() * sizeof(recent_rec_t)); +} + +int recent_select(char *dir, char *path) +{ + // copy directory and file name over + dir[0] = 0; + path[0] = 0; + + if (strlen(recents[iSelectedEntry].name)) + { + strcpy(dir, recents[iSelectedEntry].dir); + strcpy(path, dir); + strcat(path, "/"); + strcat(path, recents[iSelectedEntry].name); + } + + if (!FileExists(path)) return 0; + else return recent_available(); +} + +void recent_update(char* dir, char* path) +{ + if (is_neogeo_core()) + { + // only support neo files for now to simplify name parsing and locating files in recent files menu + char* ext = strrchr(path, '.'); + if (!ext || strcmp(ext, ".neo")) return; + } + + // separate the path into directory and filename + char* name = strrchr(path, '/'); + if (name) name++; else name = path; + + // load the current state. this is necessary because we may have started a ROM from multiple sources + recent_load(); + + // update the selection + int indexToErase = RECENT_MAX - 1; + recent_rec_t rec; + strcpy(rec.dir, dir); + strcpy(rec.name, name); + + for (unsigned i = 0; i < recents.size(); i++) + { + if (!strcmp(recents[i].dir, dir) && !strcmp(recents[i].name, name)) + { + indexToErase = i; + break; + } + } + recents.erase(recents.begin() + indexToErase); + recents.insert(recents.begin(), rec); + + // write update to storage + recent_save(); +} + +char* recent_create_config_name() +{ + static char str[80]; + str[0] = 0; + char* p = user_io_get_core_name(); + if (p[0]) + { + strcpy(str, p); + strcat(str, "_recent.CFG"); + } + return str; +} + +const char* recent_path(char* dir, char* name) +{ + static std::string fullname; + fullname = dir; + fullname += '/'; + fullname += name; + return fullname.c_str(); +} \ No newline at end of file diff --git a/recent.h b/recent.h new file mode 100644 index 0000000..f2b7e46 --- /dev/null +++ b/recent.h @@ -0,0 +1,16 @@ +#ifndef RECENT_H +#define RECENT_H + +void recent_init(); +void recent_scan(int mode); +void recent_scroll_name(); +void recent_print(); +int recent_available(); +void recent_load(); +void recent_save(); +int recent_select(char* dir, char* path); +void recent_update(char* dir, char* path); +char* recent_create_config_name(); +const char* recent_path(char* dir, char* path); + +#endif \ No newline at end of file diff --git a/support/neogeo/loader.cpp b/support/neogeo/loader.cpp index da10c21..28de896 100644 --- a/support/neogeo/loader.cpp +++ b/support/neogeo/loader.cpp @@ -584,14 +584,14 @@ int neogeo_scan_xml(char *path) return rom_cnt; } -char *neogeo_get_altname(char *path, direntext_t *de) +char *neogeo_get_altname(char *path, char *name, char *altname) { static char full_path[1024]; strcpy(full_path, path); strcat(full_path, "/"); - strcat(full_path, de->de.d_name); + strcat(full_path, name); - char *p = strrchr(de->de.d_name, '.'); + char *p = strrchr(name, '.'); if (p && !strcasecmp(p, ".neo")) { static NeoFile hdr; @@ -621,7 +621,7 @@ char *neogeo_get_altname(char *path, direntext_t *de) if (*altname) return altname; } - sprintf(full_path, ",%s,", de->altname); + sprintf(full_path, ",%s,", altname); for (uint32_t i = 0; i < rom_cnt; i++) { if (roms[i].name[0] == ',') @@ -632,11 +632,11 @@ char *neogeo_get_altname(char *path, direntext_t *de) if (roms[i].hide) return (char*)-1; if(p == roms[i].name) return roms[i].altname; - sprintf(full_path, "%s (%s)", roms[i].altname, de->altname); + sprintf(full_path, "%s (%s)", roms[i].altname, altname); return full_path; } } - else if (!strcasecmp(de->altname, roms[i].name)) + else if (!strcasecmp(altname, roms[i].name)) { if (roms[i].hide) return (char*)-1; return roms[i].altname; diff --git a/support/neogeo/loader.h b/support/neogeo/loader.h index 36b4fa0..4c87a24 100644 --- a/support/neogeo/loader.h +++ b/support/neogeo/loader.h @@ -7,4 +7,4 @@ int neogeo_romset_tx(char* name); int neogeo_scan_xml(char *path); -char *neogeo_get_altname(char *path, direntext_t *de); +char *neogeo_get_altname(char *path, char *name, char *altname);