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);