16
16
#include < string>
17
17
#include < utility>
18
18
19
+ #include " third_party/cpptoml/include/cpptoml.h"
19
20
#include " third_party/fmt/include/fmt/format.h"
20
21
#include " third_party/imgui/imgui.h"
21
22
#include " xenia/base/assert.h"
@@ -124,6 +125,11 @@ DEFINE_bool(
124
125
" depends on the 10bpc displaying capabilities of the actual display used." ,
125
126
" Display" );
126
127
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
+
127
133
namespace xe {
128
134
namespace app {
129
135
@@ -132,6 +138,7 @@ using xe::ui::KeyEvent;
132
138
using xe::ui::MenuItem;
133
139
using xe::ui::UIEvent;
134
140
141
+ const std::string kRecentlyPlayedTitlesFilename = " recent.toml" ;
135
142
const std::string kBaseTitle = " Xenia" ;
136
143
137
144
EmulatorWindow::EmulatorWindow (Emulator* emulator,
@@ -159,6 +166,8 @@ EmulatorWindow::EmulatorWindow(Emulator* emulator,
159
166
#endif
160
167
XE_BUILD_BRANCH " @" XE_BUILD_COMMIT_SHORT " on " XE_BUILD_DATE
161
168
" )" ;
169
+
170
+ LoadRecentlyLaunchedTitles ();
162
171
}
163
172
164
173
std::unique_ptr<EmulatorWindow> EmulatorWindow::Create (
@@ -502,10 +511,14 @@ bool EmulatorWindow::Initialize() {
502
511
// FIXME: This code is really messy.
503
512
auto main_menu = MenuItem::Create (MenuItem::Type::kNormal );
504
513
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 ());
505
516
{
506
517
file_menu->AddChild (
507
518
MenuItem::Create (MenuItem::Type::kString , " &Open..." , " Ctrl+O" ,
508
519
std::bind (&EmulatorWindow::FileOpen, this )));
520
+ file_menu->AddChild (std::move (recent_menu));
521
+
509
522
#ifdef DEBUG
510
523
file_menu->AddChild (
511
524
MenuItem::Create (MenuItem::Type::kString , " Close" ,
@@ -840,6 +853,8 @@ void EmulatorWindow::FileOpen() {
840
853
if (XFAILED (result)) {
841
854
// TODO: Display a message box.
842
855
XELOGE (" Failed to launch target: {:08X}" , result);
856
+ } else {
857
+ AddRecentlyLaunchedTitle (abs_path, emulator_->title_name ());
843
858
}
844
859
}
845
860
}
@@ -1016,5 +1031,113 @@ void EmulatorWindow::SetInitializingShaderStorage(bool initializing) {
1016
1031
UpdateTitle ();
1017
1032
}
1018
1033
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
+
1019
1142
} // namespace app
1020
1143
} // namespace xe
0 commit comments