Skip to content

Commit adffa48

Browse files
committed
[UI] Added recently opened titles list
1 parent 7dd715e commit adffa48

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

src/xenia/app/emulator_window.cc

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <string>
1717
#include <utility>
1818

19+
#include "third_party/cpptoml/include/cpptoml.h"
1920
#include "third_party/fmt/include/fmt/format.h"
2021
#include "third_party/imgui/imgui.h"
2122
#include "xenia/base/assert.h"
@@ -124,6 +125,11 @@ DEFINE_bool(
124125
"depends on the 10bpc displaying capabilities of the actual display used.",
125126
"Display");
126127

128+
DEFINE_int32(recent_titles_entry_amount, 10,
129+
"Allows user to define how many titles is saved in list of "
130+
"recently played titles.",
131+
"General");
132+
127133
namespace xe {
128134
namespace app {
129135

@@ -132,6 +138,7 @@ using xe::ui::KeyEvent;
132138
using xe::ui::MenuItem;
133139
using xe::ui::UIEvent;
134140

141+
const std::string kRecentlyPlayedTitlesFilename = "recent.toml";
135142
const std::string kBaseTitle = "Xenia";
136143

137144
EmulatorWindow::EmulatorWindow(Emulator* emulator,
@@ -159,6 +166,8 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator,
159166
#endif
160167
XE_BUILD_BRANCH "@" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE
161168
")";
169+
170+
LoadRecentlyLaunchedTitles();
162171
}
163172

164173
std::unique_ptr<EmulatorWindow> EmulatorWindow::Create(
@@ -502,10 +511,14 @@ bool EmulatorWindow::Initialize() {
502511
// FIXME: This code is really messy.
503512
auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
504513
auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, "&File");
514+
auto recent_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Open Recent");
515+
FillRecentlyLaunchedTitlesMenu(recent_menu.get());
505516
{
506517
file_menu->AddChild(
507518
MenuItem::Create(MenuItem::Type::kString, "&Open...", "Ctrl+O",
508519
std::bind(&EmulatorWindow::FileOpen, this)));
520+
file_menu->AddChild(std::move(recent_menu));
521+
509522
#ifdef DEBUG
510523
file_menu->AddChild(
511524
MenuItem::Create(MenuItem::Type::kString, "Close",
@@ -840,6 +853,8 @@ void EmulatorWindow::FileOpen() {
840853
if (XFAILED(result)) {
841854
// TODO: Display a message box.
842855
XELOGE("Failed to launch target: {:08X}", result);
856+
} else {
857+
AddRecentlyLaunchedTitle(abs_path, emulator_->title_name());
843858
}
844859
}
845860
}
@@ -1016,5 +1031,113 @@ void EmulatorWindow::SetInitializingShaderStorage(bool initializing) {
10161031
UpdateTitle();
10171032
}
10181033

1034+
void EmulatorWindow::RunRecentlyPlayedTitle(
1035+
std::filesystem::path path_to_file) {
1036+
if (path_to_file.empty()) {
1037+
return;
1038+
}
1039+
1040+
auto abs_path = std::filesystem::absolute(path_to_file);
1041+
auto result = emulator_->LaunchPath(abs_path);
1042+
if (XFAILED(result)) {
1043+
// TODO: Display a message box.
1044+
XELOGE("Failed to launch target: {:08X}", result);
1045+
return;
1046+
}
1047+
AddRecentlyLaunchedTitle(path_to_file, emulator_->title_name());
1048+
}
1049+
1050+
void EmulatorWindow::FillRecentlyLaunchedTitlesMenu(
1051+
xe::ui::MenuItem* recent_menu) {
1052+
for (const RecentTitleEntry& entry : recently_launched_titles_) {
1053+
std::string item_text = !entry.title_name.empty()
1054+
? entry.title_name
1055+
: entry.path_to_file.string();
1056+
recent_menu->AddChild(
1057+
MenuItem::Create(MenuItem::Type::kString, item_text,
1058+
std::bind(&EmulatorWindow::RunRecentlyPlayedTitle,
1059+
this, entry.path_to_file)));
1060+
}
1061+
}
1062+
1063+
void EmulatorWindow::LoadRecentlyLaunchedTitles() {
1064+
std::ifstream file(kRecentlyPlayedTitlesFilename);
1065+
if (!file.is_open()) {
1066+
return;
1067+
}
1068+
1069+
std::shared_ptr<cpptoml::table> parsed_file;
1070+
try {
1071+
cpptoml::parser p(file);
1072+
parsed_file = p.parse();
1073+
} catch (cpptoml::parse_exception exception) {
1074+
// TODO(Gliniak): Better handling of errors, but good enough for now.
1075+
return;
1076+
}
1077+
1078+
if (parsed_file->is_table()) {
1079+
for (const auto& [index, entry] : *parsed_file->as_table()) {
1080+
if (!entry->is_table()) {
1081+
continue;
1082+
}
1083+
1084+
const std::shared_ptr<cpptoml::table> entry_table = entry->as_table();
1085+
1086+
std::string title_name = *entry_table->get_as<std::string>("title_name");
1087+
std::string path = *entry_table->get_as<std::string>("path");
1088+
std::time_t last_run_time =
1089+
*entry_table->get_as<uint64_t>("last_run_time");
1090+
1091+
if (path.empty()) {
1092+
continue;
1093+
}
1094+
1095+
recently_launched_titles_.push_back({title_name, path, last_run_time});
1096+
}
1097+
}
1098+
}
1099+
1100+
void EmulatorWindow::AddRecentlyLaunchedTitle(
1101+
std::filesystem::path path_to_file, std::string title_name) {
1102+
// Check if game is already on list and pop it to front
1103+
auto entry_index = std::find_if(recently_launched_titles_.cbegin(),
1104+
recently_launched_titles_.cend(),
1105+
[&title_name](const RecentTitleEntry& entry) {
1106+
return entry.title_name == title_name;
1107+
});
1108+
if (entry_index != recently_launched_titles_.cend()) {
1109+
recently_launched_titles_.erase(entry_index);
1110+
}
1111+
1112+
recently_launched_titles_.insert(recently_launched_titles_.cbegin(),
1113+
{title_name, path_to_file, time(nullptr)});
1114+
// Serialize to toml
1115+
auto toml_table = cpptoml::make_table();
1116+
1117+
uint8_t index = 0;
1118+
for (const RecentTitleEntry& entry : recently_launched_titles_) {
1119+
auto entry_table = cpptoml::make_table();
1120+
1121+
// Fill entry under specific index.
1122+
std::string str_path = xe::path_to_utf8(entry.path_to_file);
1123+
entry_table->insert("title_name", entry.title_name);
1124+
entry_table->insert("path", str_path);
1125+
entry_table->insert("last_run_time", entry.last_run_time);
1126+
entry_table->end();
1127+
1128+
toml_table->insert(std::to_string(index++), entry_table);
1129+
1130+
if (index >= cvars::recent_titles_entry_amount) {
1131+
break;
1132+
}
1133+
}
1134+
toml_table->end();
1135+
1136+
// Open and write serialized data.
1137+
std::ofstream file(kRecentlyPlayedTitlesFilename, std::ofstream::trunc);
1138+
file << *toml_table;
1139+
file.close();
1140+
}
1141+
10191142
} // namespace app
10201143
} // namespace xe

src/xenia/app/emulator_window.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
namespace xe {
2929
namespace app {
3030

31+
struct RecentTitleEntry {
32+
std::string title_name;
33+
std::filesystem::path path_to_file;
34+
std::time_t last_run_time;
35+
};
36+
3137
class EmulatorWindow {
3238
public:
3339
enum : size_t {
@@ -143,6 +149,12 @@ class EmulatorWindow {
143149
void ShowFAQ();
144150
void ShowBuildCommit();
145151

152+
void RunRecentlyPlayedTitle(std::filesystem::path path_to_file);
153+
void FillRecentlyLaunchedTitlesMenu(xe::ui::MenuItem* recent_menu);
154+
void LoadRecentlyLaunchedTitles();
155+
void AddRecentlyLaunchedTitle(std::filesystem::path path_to_file,
156+
std::string title_name);
157+
146158
Emulator* emulator_;
147159
ui::WindowedAppContext& app_context_;
148160
EmulatorWindowListener window_listener_;
@@ -159,6 +171,8 @@ class EmulatorWindow {
159171
bool initializing_shader_storage_ = false;
160172

161173
std::unique_ptr<DisplayConfigDialog> display_config_dialog_;
174+
175+
std::vector<RecentTitleEntry> recently_launched_titles_;
162176
};
163177

164178
} // namespace app

0 commit comments

Comments
 (0)