mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-29 22:20:48 +00:00
Qt: add savestate manager dialog
This commit is contained in:
parent
e2bbaa2430
commit
bd1ebb7a10
@ -439,6 +439,9 @@
|
|||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_trophy_manager_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_trophy_manager_dialog.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_savestate_manager_dialog.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_trophy_notification_frame.cpp">
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_trophy_notification_frame.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -712,6 +715,9 @@
|
|||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_manager_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_manager_dialog.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="QTGeneratedFiles\Release\moc_savestate_manager_dialog.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_notification_frame.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_notification_frame.cpp">
|
||||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -827,6 +833,7 @@
|
|||||||
<ClCompile Include="rpcs3qt\save_data_info_dialog.cpp" />
|
<ClCompile Include="rpcs3qt\save_data_info_dialog.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\save_manager_dialog.cpp" />
|
<ClCompile Include="rpcs3qt\save_manager_dialog.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\trophy_manager_dialog.cpp" />
|
<ClCompile Include="rpcs3qt\trophy_manager_dialog.cpp" />
|
||||||
|
<ClCompile Include="rpcs3qt\savestate_manager_dialog.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\trophy_notification_frame.cpp" />
|
<ClCompile Include="rpcs3qt\trophy_notification_frame.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\trophy_notification_helper.cpp" />
|
<ClCompile Include="rpcs3qt\trophy_notification_helper.cpp" />
|
||||||
<ClCompile Include="rpcs3qt\user_account.cpp" />
|
<ClCompile Include="rpcs3qt\user_account.cpp" />
|
||||||
@ -1819,6 +1826,16 @@
|
|||||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
|
<CustomBuild Include="rpcs3qt\savestate_manager_dialog.h">
|
||||||
|
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||||
|
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||||
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
|
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
|
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||||
|
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
|
||||||
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||||
|
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||||
|
</CustomBuild>
|
||||||
<CustomBuild Include="rpcs3qt\trophy_notification_frame.h">
|
<CustomBuild Include="rpcs3qt\trophy_notification_frame.h">
|
||||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||||
|
@ -193,6 +193,9 @@
|
|||||||
<Filter Include="Darwin">
|
<Filter Include="Darwin">
|
||||||
<UniqueIdentifier>{f6b701aa-7f4a-4816-b05f-80d24cb70e13}</UniqueIdentifier>
|
<UniqueIdentifier>{f6b701aa-7f4a-4816-b05f-80d24cb70e13}</UniqueIdentifier>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Gui\savestates">
|
||||||
|
<UniqueIdentifier>{9b51636c-b371-425b-86d3-be003774a1b7}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="main.cpp">
|
<ClCompile Include="main.cpp">
|
||||||
@ -471,6 +474,9 @@
|
|||||||
<ClCompile Include="rpcs3qt\trophy_manager_dialog.cpp">
|
<ClCompile Include="rpcs3qt\trophy_manager_dialog.cpp">
|
||||||
<Filter>Gui\trophy</Filter>
|
<Filter>Gui\trophy</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="rpcs3qt\savestate_manager_dialog.cpp">
|
||||||
|
<Filter>Gui\savestates</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="rpcs3qt\progress_dialog.cpp">
|
<ClCompile Include="rpcs3qt\progress_dialog.cpp">
|
||||||
<Filter>Gui\misc dialogs</Filter>
|
<Filter>Gui\misc dialogs</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -549,6 +555,12 @@
|
|||||||
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_manager_dialog.cpp">
|
<ClCompile Include="QTGeneratedFiles\Release\moc_trophy_manager_dialog.cpp">
|
||||||
<Filter>Generated Files\Release</Filter>
|
<Filter>Generated Files\Release</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="QTGeneratedFiles\Debug\moc_savestate_manager_dialog.cpp">
|
||||||
|
<Filter>Generated Files\Debug</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="QTGeneratedFiles\Release\moc_savestate_manager_dialog.cpp">
|
||||||
|
<Filter>Generated Files\Release</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="rpcs3qt\input_dialog.cpp">
|
<ClCompile Include="rpcs3qt\input_dialog.cpp">
|
||||||
<Filter>Gui\misc dialogs</Filter>
|
<Filter>Gui\misc dialogs</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -1480,6 +1492,9 @@
|
|||||||
<CustomBuild Include="rpcs3qt\trophy_manager_dialog.h">
|
<CustomBuild Include="rpcs3qt\trophy_manager_dialog.h">
|
||||||
<Filter>Gui\trophy</Filter>
|
<Filter>Gui\trophy</Filter>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
|
<CustomBuild Include="rpcs3qt\savestate_manager_dialog.h">
|
||||||
|
<Filter>Gui\savestates</Filter>
|
||||||
|
</CustomBuild>
|
||||||
<CustomBuild Include="rpcs3qt\input_dialog.h">
|
<CustomBuild Include="rpcs3qt\input_dialog.h">
|
||||||
<Filter>Gui\misc dialogs</Filter>
|
<Filter>Gui\misc dialogs</Filter>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
|
@ -80,6 +80,7 @@ add_library(rpcs3_ui STATIC
|
|||||||
save_data_info_dialog.cpp
|
save_data_info_dialog.cpp
|
||||||
save_data_list_dialog.cpp
|
save_data_list_dialog.cpp
|
||||||
save_manager_dialog.cpp
|
save_manager_dialog.cpp
|
||||||
|
savestate_manager_dialog.cpp
|
||||||
screenshot_item.cpp
|
screenshot_item.cpp
|
||||||
screenshot_manager_dialog.cpp
|
screenshot_manager_dialog.cpp
|
||||||
screenshot_preview.cpp
|
screenshot_preview.cpp
|
||||||
|
@ -1197,9 +1197,9 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool is_savestate_compatible(fs::file&& file, std::string_view filepath);
|
extern bool is_savestate_compatible(const std::string& filepath);
|
||||||
|
|
||||||
if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(fs::file(sstate), sstate))
|
if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(sstate))
|
||||||
{
|
{
|
||||||
QAction* boot_state = menu.addAction(is_current_running_game
|
QAction* boot_state = menu.addAction(is_current_running_game
|
||||||
? tr("&Reboot with savestate")
|
? tr("&Reboot with savestate")
|
||||||
|
@ -20,42 +20,53 @@ namespace gui
|
|||||||
QString stylesheet;
|
QString stylesheet;
|
||||||
bool custom_stylesheet_active = false;
|
bool custom_stylesheet_active = false;
|
||||||
|
|
||||||
|
QString get_savestate_list_column_name(savestate_list_columns col)
|
||||||
|
{
|
||||||
|
switch (col)
|
||||||
|
{
|
||||||
|
case gui::savestate_list_columns::name: return "savestate_column_name";
|
||||||
|
case gui::savestate_list_columns::compatible: return "savestate_column_compatible";
|
||||||
|
case gui::savestate_list_columns::date: return "savestate_column_date";
|
||||||
|
case gui::savestate_list_columns::path: return "savestate_column_path";
|
||||||
|
case gui::savestate_list_columns::count: return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::throw_exception("get_savestate_list_column_name: Invalid column");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString get_savestate_game_list_column_name(savestate_game_list_columns col)
|
||||||
|
{
|
||||||
|
switch (col)
|
||||||
|
{
|
||||||
|
case gui::savestate_game_list_columns::icon: return "savestate_game_column_icon";
|
||||||
|
case gui::savestate_game_list_columns::name: return "savestate_game_column_name";
|
||||||
|
case gui::savestate_game_list_columns::savestates: return "savestate_game_column_savestates";
|
||||||
|
case gui::savestate_game_list_columns::count: return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::throw_exception("get_savestate_game_list_column_name: Invalid column");
|
||||||
|
}
|
||||||
|
|
||||||
QString get_game_list_column_name(game_list_columns col)
|
QString get_game_list_column_name(game_list_columns col)
|
||||||
{
|
{
|
||||||
switch (col)
|
switch (col)
|
||||||
{
|
{
|
||||||
case game_list_columns::icon:
|
case game_list_columns::icon: return "column_icon";
|
||||||
return "column_icon";
|
case game_list_columns::name: return "column_name";
|
||||||
case game_list_columns::name:
|
case game_list_columns::serial: return "column_serial";
|
||||||
return "column_name";
|
case game_list_columns::firmware: return "column_firmware";
|
||||||
case game_list_columns::serial:
|
case game_list_columns::version: return "column_version";
|
||||||
return "column_serial";
|
case game_list_columns::category: return "column_category";
|
||||||
case game_list_columns::firmware:
|
case game_list_columns::path: return "column_path";
|
||||||
return "column_firmware";
|
case game_list_columns::move: return "column_move";
|
||||||
case game_list_columns::version:
|
case game_list_columns::resolution: return "column_resolution";
|
||||||
return "column_version";
|
case game_list_columns::sound: return "column_sound";
|
||||||
case game_list_columns::category:
|
case game_list_columns::parental: return "column_parental";
|
||||||
return "column_category";
|
case game_list_columns::last_play: return "column_last_play";
|
||||||
case game_list_columns::path:
|
case game_list_columns::playtime: return "column_playtime";
|
||||||
return "column_path";
|
case game_list_columns::compat: return "column_compat";
|
||||||
case game_list_columns::move:
|
case game_list_columns::dir_size: return "column_dir_size";
|
||||||
return "column_move";
|
case game_list_columns::count: return "";
|
||||||
case game_list_columns::resolution:
|
|
||||||
return "column_resolution";
|
|
||||||
case game_list_columns::sound:
|
|
||||||
return "column_sound";
|
|
||||||
case game_list_columns::parental:
|
|
||||||
return "column_parental";
|
|
||||||
case game_list_columns::last_play:
|
|
||||||
return "column_last_play";
|
|
||||||
case game_list_columns::playtime:
|
|
||||||
return "column_playtime";
|
|
||||||
case game_list_columns::compat:
|
|
||||||
return "column_compat";
|
|
||||||
case game_list_columns::dir_size:
|
|
||||||
return "column_dir_size";
|
|
||||||
case game_list_columns::count:
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::throw_exception("get_game_list_column_name: Invalid column");
|
fmt::throw_exception("get_game_list_column_name: Invalid column");
|
||||||
@ -65,24 +76,15 @@ namespace gui
|
|||||||
{
|
{
|
||||||
switch (col)
|
switch (col)
|
||||||
{
|
{
|
||||||
case trophy_list_columns::icon:
|
case trophy_list_columns::icon: return "trophy_column_icon";
|
||||||
return "trophy_column_icon";
|
case trophy_list_columns::name: return "trophy_column_name";
|
||||||
case trophy_list_columns::name:
|
case trophy_list_columns::description: return "trophy_column_description";
|
||||||
return "trophy_column_name";
|
case trophy_list_columns::type: return "trophy_column_type";
|
||||||
case trophy_list_columns::description:
|
case trophy_list_columns::is_unlocked: return "trophy_column_is_unlocked";
|
||||||
return "trophy_column_description";
|
case trophy_list_columns::id: return "trophy_column_id";
|
||||||
case trophy_list_columns::type:
|
case trophy_list_columns::platinum_link: return "trophy_column_platinum_link";
|
||||||
return "trophy_column_type";
|
case trophy_list_columns::time_unlocked: return "trophy_column_time_unlocked";
|
||||||
case trophy_list_columns::is_unlocked:
|
case trophy_list_columns::count: return "";
|
||||||
return "trophy_column_is_unlocked";
|
|
||||||
case trophy_list_columns::id:
|
|
||||||
return "trophy_column_id";
|
|
||||||
case trophy_list_columns::platinum_link:
|
|
||||||
return "trophy_column_platinum_link";
|
|
||||||
case trophy_list_columns::time_unlocked:
|
|
||||||
return "trophy_column_time_unlocked";
|
|
||||||
case trophy_list_columns::count:
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::throw_exception("get_trophy_list_column_name: Invalid column");
|
fmt::throw_exception("get_trophy_list_column_name: Invalid column");
|
||||||
@ -92,16 +94,11 @@ namespace gui
|
|||||||
{
|
{
|
||||||
switch (col)
|
switch (col)
|
||||||
{
|
{
|
||||||
case trophy_game_list_columns::icon:
|
case trophy_game_list_columns::icon: return "trophy_game_column_icon";
|
||||||
return "trophy_game_column_icon";
|
case trophy_game_list_columns::name: return "trophy_game_column_name";
|
||||||
case trophy_game_list_columns::name:
|
case trophy_game_list_columns::progress: return "trophy_game_column_progress";
|
||||||
return "trophy_game_column_name";
|
case trophy_game_list_columns::trophies: return "trophy_game_column_trophies";
|
||||||
case trophy_game_list_columns::progress:
|
case trophy_game_list_columns::count: return "";
|
||||||
return "trophy_game_column_progress";
|
|
||||||
case trophy_game_list_columns::trophies:
|
|
||||||
return "trophy_game_column_trophies";
|
|
||||||
case trophy_game_list_columns::count:
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::throw_exception("get_trophy_game_list_column_name: Invalid column");
|
fmt::throw_exception("get_trophy_game_list_column_name: Invalid column");
|
||||||
@ -115,21 +112,21 @@ gui_settings::gui_settings(QObject* parent) : settings(parent)
|
|||||||
|
|
||||||
QStringList gui_settings::GetGameListCategoryFilters(bool is_list_mode) const
|
QStringList gui_settings::GetGameListCategoryFilters(bool is_list_mode) const
|
||||||
{
|
{
|
||||||
QStringList filterList;
|
QStringList filters;
|
||||||
|
|
||||||
if (GetCategoryVisibility(Category::HDD_Game, is_list_mode)) filterList.append(cat::cat_hdd_game);
|
if (GetCategoryVisibility(Category::HDD_Game, is_list_mode)) filters.append(cat::cat_hdd_game);
|
||||||
if (GetCategoryVisibility(Category::Disc_Game, is_list_mode)) filterList.append(cat::cat_disc_game);
|
if (GetCategoryVisibility(Category::Disc_Game, is_list_mode)) filters.append(cat::cat_disc_game);
|
||||||
if (GetCategoryVisibility(Category::PS1_Game, is_list_mode)) filterList.append(cat::cat_ps1_game);
|
if (GetCategoryVisibility(Category::PS1_Game, is_list_mode)) filters.append(cat::cat_ps1_game);
|
||||||
if (GetCategoryVisibility(Category::PS2_Game, is_list_mode)) filterList.append(cat::ps2_games);
|
if (GetCategoryVisibility(Category::PS2_Game, is_list_mode)) filters.append(cat::ps2_games);
|
||||||
if (GetCategoryVisibility(Category::PSP_Game, is_list_mode)) filterList.append(cat::psp_games);
|
if (GetCategoryVisibility(Category::PSP_Game, is_list_mode)) filters.append(cat::psp_games);
|
||||||
if (GetCategoryVisibility(Category::Home, is_list_mode)) filterList.append(cat::cat_home);
|
if (GetCategoryVisibility(Category::Home, is_list_mode)) filters.append(cat::cat_home);
|
||||||
if (GetCategoryVisibility(Category::Media, is_list_mode)) filterList.append(cat::media);
|
if (GetCategoryVisibility(Category::Media, is_list_mode)) filters.append(cat::media);
|
||||||
if (GetCategoryVisibility(Category::Data, is_list_mode)) filterList.append(cat::data);
|
if (GetCategoryVisibility(Category::Data, is_list_mode)) filters.append(cat::data);
|
||||||
if (GetCategoryVisibility(Category::OS, is_list_mode)) filterList.append(cat::os);
|
if (GetCategoryVisibility(Category::OS, is_list_mode)) filters.append(cat::os);
|
||||||
if (GetCategoryVisibility(Category::Unknown_Cat, is_list_mode)) filterList.append(cat::cat_unknown);
|
if (GetCategoryVisibility(Category::Unknown_Cat, is_list_mode)) filters.append(cat::cat_unknown);
|
||||||
if (GetCategoryVisibility(Category::Others, is_list_mode)) filterList.append(cat::others);
|
if (GetCategoryVisibility(Category::Others, is_list_mode)) filters.append(cat::others);
|
||||||
|
|
||||||
return filterList;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gui_settings::GetCategoryVisibility(int cat, bool is_list_mode) const
|
bool gui_settings::GetCategoryVisibility(int cat, bool is_list_mode) const
|
||||||
@ -243,6 +240,16 @@ bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui_settings::SetSavestateGamelistColVisibility(gui::savestate_game_list_columns col, bool val) const
|
||||||
|
{
|
||||||
|
SetValue(GetGuiSaveForSavestateGameColumn(col), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gui_settings::SetSavestateListColVisibility(gui::savestate_list_columns col, bool val) const
|
||||||
|
{
|
||||||
|
SetValue(GetGuiSaveForSavestateColumn(col), val);
|
||||||
|
}
|
||||||
|
|
||||||
void gui_settings::SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const
|
void gui_settings::SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const
|
||||||
{
|
{
|
||||||
SetValue(GetGuiSaveForTrophyGameColumn(col), val);
|
SetValue(GetGuiSaveForTrophyGameColumn(col), val);
|
||||||
@ -268,6 +275,16 @@ logs::level gui_settings::GetLogLevel() const
|
|||||||
return logs::level(GetValue(gui::l_level).toUInt());
|
return logs::level(GetValue(gui::l_level).toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool gui_settings::GetSavestateGamelistColVisibility(gui::savestate_game_list_columns col) const
|
||||||
|
{
|
||||||
|
return GetValue(GetGuiSaveForSavestateGameColumn(col)).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool gui_settings::GetSavestateListColVisibility(gui::savestate_list_columns col) const
|
||||||
|
{
|
||||||
|
return GetValue(GetGuiSaveForSavestateColumn(col)).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
bool gui_settings::GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const
|
bool gui_settings::GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const
|
||||||
{
|
{
|
||||||
return GetValue(GetGuiSaveForTrophyGameColumn(col)).toBool();
|
return GetValue(GetGuiSaveForTrophyGameColumn(col)).toBool();
|
||||||
@ -316,6 +333,16 @@ QSize gui_settings::SizeFromSlider(int pos)
|
|||||||
return gui::gl_icon_size_min + (gui::gl_icon_size_max - gui::gl_icon_size_min) * (1.f * pos / gui::gl_max_slider_pos);
|
return gui::gl_icon_size_min + (gui::gl_icon_size_max - gui::gl_icon_size_min) * (1.f * pos / gui::gl_max_slider_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gui_save gui_settings::GetGuiSaveForSavestateGameColumn(gui::savestate_game_list_columns col)
|
||||||
|
{
|
||||||
|
return gui_save{ gui::savestate, "visibility_" + gui::get_savestate_game_list_column_name(col), true };
|
||||||
|
}
|
||||||
|
|
||||||
|
gui_save gui_settings::GetGuiSaveForSavestateColumn(gui::savestate_list_columns col)
|
||||||
|
{
|
||||||
|
return gui_save{ gui::savestate, "visibility_" + gui::get_savestate_list_column_name(col), true };
|
||||||
|
}
|
||||||
|
|
||||||
gui_save gui_settings::GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col)
|
gui_save gui_settings::GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col)
|
||||||
{
|
{
|
||||||
return gui_save{ gui::trophy, "visibility_" + gui::get_trophy_game_list_column_name(col), true };
|
return gui_save{ gui::trophy, "visibility_" + gui::get_trophy_game_list_column_name(col), true };
|
||||||
|
@ -64,6 +64,27 @@ namespace gui
|
|||||||
count
|
count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class savestate_game_list_columns
|
||||||
|
{
|
||||||
|
icon = 0,
|
||||||
|
name = 1,
|
||||||
|
savestates = 2,
|
||||||
|
|
||||||
|
count
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class savestate_list_columns
|
||||||
|
{
|
||||||
|
name = 0,
|
||||||
|
compatible = 1,
|
||||||
|
date = 2,
|
||||||
|
path = 3,
|
||||||
|
|
||||||
|
count
|
||||||
|
};
|
||||||
|
|
||||||
|
QString get_savestate_game_list_column_name(savestate_game_list_columns col);
|
||||||
|
QString get_savestate_list_column_name(savestate_list_columns col);
|
||||||
QString get_trophy_game_list_column_name(trophy_game_list_columns col);
|
QString get_trophy_game_list_column_name(trophy_game_list_columns col);
|
||||||
QString get_trophy_list_column_name(trophy_list_columns col);
|
QString get_trophy_list_column_name(trophy_list_columns col);
|
||||||
QString get_game_list_column_name(game_list_columns col);
|
QString get_game_list_column_name(game_list_columns col);
|
||||||
@ -109,6 +130,7 @@ namespace gui
|
|||||||
const QString log_viewer = "LogViewer";
|
const QString log_viewer = "LogViewer";
|
||||||
const QString sc = "Shortcuts";
|
const QString sc = "Shortcuts";
|
||||||
const QString navigation = "PadNavigation";
|
const QString navigation = "PadNavigation";
|
||||||
|
const QString savestate = "Savestate";
|
||||||
|
|
||||||
const QString update_on = "true";
|
const QString update_on = "true";
|
||||||
const QString update_off = "false";
|
const QString update_off = "false";
|
||||||
@ -246,6 +268,13 @@ namespace gui
|
|||||||
const gui_save gs_geometry = gui_save(gs_frame, "geometry", QRect());
|
const gui_save gs_geometry = gui_save(gs_frame, "geometry", QRect());
|
||||||
const gui_save gs_visibility = gui_save(gs_frame, "visibility", QWindow::Visibility::AutomaticVisibility);
|
const gui_save gs_visibility = gui_save(gs_frame, "visibility", QWindow::Visibility::AutomaticVisibility);
|
||||||
|
|
||||||
|
const gui_save ss_icon_color = gui_save(trophy, "icon_color", gl_icon_color);
|
||||||
|
const gui_save ss_game_icon_size = gui_save(trophy, "game_icon_size", 25);
|
||||||
|
const gui_save ss_geometry = gui_save(trophy, "geometry", QByteArray());
|
||||||
|
const gui_save ss_splitterState = gui_save(trophy, "splitterState", QByteArray());
|
||||||
|
const gui_save ss_games_state = gui_save(trophy, "games_state", QByteArray());
|
||||||
|
const gui_save ss_savestate_state = gui_save(trophy, "savestate_state", QByteArray());
|
||||||
|
|
||||||
const gui_save tr_icon_color = gui_save(trophy, "icon_color", gl_icon_color);
|
const gui_save tr_icon_color = gui_save(trophy, "icon_color", gl_icon_color);
|
||||||
const gui_save tr_icon_height = gui_save(trophy, "icon_height", 75);
|
const gui_save tr_icon_height = gui_save(trophy, "icon_height", 75);
|
||||||
const gui_save tr_game_iconSize = gui_save(trophy, "game_iconSize", 25);
|
const gui_save tr_game_iconSize = gui_save(trophy, "game_iconSize", 25);
|
||||||
@ -304,6 +333,8 @@ public:
|
|||||||
bool GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry = gui_save());
|
bool GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry = gui_save());
|
||||||
|
|
||||||
logs::level GetLogLevel() const;
|
logs::level GetLogLevel() const;
|
||||||
|
bool GetSavestateGamelistColVisibility(gui::savestate_game_list_columns col) const;
|
||||||
|
bool GetSavestateListColVisibility(gui::savestate_list_columns col) const;
|
||||||
bool GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const;
|
bool GetTrophyGamelistColVisibility(gui::trophy_game_list_columns col) const;
|
||||||
bool GetTrophylistColVisibility(gui::trophy_list_columns col) const;
|
bool GetTrophylistColVisibility(gui::trophy_list_columns col) const;
|
||||||
bool GetGamelistColVisibility(gui::game_list_columns col) const;
|
bool GetGamelistColVisibility(gui::game_list_columns col) const;
|
||||||
@ -316,6 +347,8 @@ public:
|
|||||||
/** Sets the visibility of the chosen category. */
|
/** Sets the visibility of the chosen category. */
|
||||||
void SetCategoryVisibility(int cat, bool val, bool is_list_mode) const;
|
void SetCategoryVisibility(int cat, bool val, bool is_list_mode) const;
|
||||||
|
|
||||||
|
void SetSavestateGamelistColVisibility(gui::savestate_game_list_columns col, bool val) const;
|
||||||
|
void SetSavestateListColVisibility(gui::savestate_list_columns col, bool val) const;
|
||||||
void SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const;
|
void SetTrophyGamelistColVisibility(gui::trophy_game_list_columns col, bool val) const;
|
||||||
void SetTrophylistColVisibility(gui::trophy_list_columns col, bool val) const;
|
void SetTrophylistColVisibility(gui::trophy_list_columns col, bool val) const;
|
||||||
void SetGamelistColVisibility(gui::game_list_columns col, bool val) const;
|
void SetGamelistColVisibility(gui::game_list_columns col, bool val) const;
|
||||||
@ -323,6 +356,8 @@ public:
|
|||||||
void SetCustomColor(int col, const QColor& val) const;
|
void SetCustomColor(int col, const QColor& val) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static gui_save GetGuiSaveForSavestateGameColumn(gui::savestate_game_list_columns col);
|
||||||
|
static gui_save GetGuiSaveForSavestateColumn(gui::savestate_list_columns col);
|
||||||
static gui_save GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col);
|
static gui_save GetGuiSaveForTrophyGameColumn(gui::trophy_game_list_columns col);
|
||||||
static gui_save GetGuiSaveForTrophyColumn(gui::trophy_list_columns col);
|
static gui_save GetGuiSaveForTrophyColumn(gui::trophy_list_columns col);
|
||||||
static gui_save GetGuiSaveForGameColumn(gui::game_list_columns col);
|
static gui_save GetGuiSaveForGameColumn(gui::game_list_columns col);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "vfs_dialog.h"
|
#include "vfs_dialog.h"
|
||||||
#include "save_manager_dialog.h"
|
#include "save_manager_dialog.h"
|
||||||
#include "trophy_manager_dialog.h"
|
#include "trophy_manager_dialog.h"
|
||||||
|
#include "savestate_manager_dialog.h"
|
||||||
#include "user_manager_dialog.h"
|
#include "user_manager_dialog.h"
|
||||||
#include "screenshot_manager_dialog.h"
|
#include "screenshot_manager_dialog.h"
|
||||||
#include "kernel_explorer.h"
|
#include "kernel_explorer.h"
|
||||||
@ -3054,6 +3055,14 @@ void main_window::CreateConnects()
|
|||||||
trop_manager->show();
|
trop_manager->show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ui->actionManage_Savestates, &QAction::triggered, this, [this]
|
||||||
|
{
|
||||||
|
savestate_manager_dialog* manager = new savestate_manager_dialog(m_gui_settings, m_game_list_frame->GetGameInfo());
|
||||||
|
connect(this, &main_window::RequestDialogRepaint, manager, &savestate_manager_dialog::HandleRepaintUiRequest);
|
||||||
|
connect(manager, &savestate_manager_dialog::RequestBoot, this, [this](const std::string& path) { Boot(path, "", true); });
|
||||||
|
manager->show();
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->actionManage_Skylanders_Portal, &QAction::triggered, this, [this]
|
connect(ui->actionManage_Skylanders_Portal, &QAction::triggered, this, [this]
|
||||||
{
|
{
|
||||||
skylander_dialog* sky_diag = skylander_dialog::get_dlg(this);
|
skylander_dialog* sky_diag = skylander_dialog::get_dlg(this);
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="frame">
|
<property name="frame">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
@ -119,16 +119,16 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="focusPolicy">
|
<property name="focusPolicy">
|
||||||
<enum>Qt::ClickFocus</enum>
|
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoFillBackground">
|
<property name="autoFillBackground">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="tickPosition">
|
<property name="tickPosition">
|
||||||
<enum>QSlider::NoTicks</enum>
|
<enum>QSlider::TickPosition::NoTicks</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -145,7 +145,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::PreventContextMenu</enum>
|
<enum>Qt::ContextMenuPolicy::PreventContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -297,6 +297,7 @@
|
|||||||
<addaction name="confSavedataManagerAct"/>
|
<addaction name="confSavedataManagerAct"/>
|
||||||
<addaction name="actionManage_RAP_Licenses"/>
|
<addaction name="actionManage_RAP_Licenses"/>
|
||||||
<addaction name="actionManage_Trophy_Data"/>
|
<addaction name="actionManage_Trophy_Data"/>
|
||||||
|
<addaction name="actionManage_Savestates"/>
|
||||||
<addaction name="actionManage_Skylanders_Portal"/>
|
<addaction name="actionManage_Skylanders_Portal"/>
|
||||||
<addaction name="actionManage_Infinity_Base"/>
|
<addaction name="actionManage_Infinity_Base"/>
|
||||||
<addaction name="actionManage_Dimensions_ToyPad"/>
|
<addaction name="actionManage_Dimensions_ToyPad"/>
|
||||||
@ -422,7 +423,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::PreventContextMenu</enum>
|
<enum>Qt::ContextMenuPolicy::PreventContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Show tool bar</string>
|
<string>Show tool bar</string>
|
||||||
@ -431,7 +432,7 @@
|
|||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="allowedAreas">
|
<property name="allowedAreas">
|
||||||
<set>Qt::TopToolBarArea</set>
|
<set>Qt::ToolBarArea::TopToolBarArea</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
@ -440,7 +441,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolButtonStyle">
|
<property name="toolButtonStyle">
|
||||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
<enum>Qt::ToolButtonStyle::ToolButtonTextUnderIcon</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="floatable">
|
<property name="floatable">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
@ -1406,6 +1407,11 @@
|
|||||||
<string>List Freeze</string>
|
<string>List Freeze</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionManage_Savestates">
|
||||||
|
<property name="text">
|
||||||
|
<string>Savestates</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources>
|
<resources>
|
||||||
|
794
rpcs3/rpcs3qt/savestate_manager_dialog.cpp
Normal file
794
rpcs3/rpcs3qt/savestate_manager_dialog.cpp
Normal file
@ -0,0 +1,794 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "savestate_manager_dialog.h"
|
||||||
|
#include "custom_table_widget_item.h"
|
||||||
|
#include "game_list_delegate.h"
|
||||||
|
#include "qt_utils.h"
|
||||||
|
#include "game_list.h"
|
||||||
|
#include "gui_settings.h"
|
||||||
|
#include "progress_dialog.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QScrollBar>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
LOG_CHANNEL(gui_log, "GUI");
|
||||||
|
|
||||||
|
enum GameUserRole
|
||||||
|
{
|
||||||
|
GameIndex = Qt::UserRole,
|
||||||
|
GamePixmapLoaded,
|
||||||
|
GamePixmap
|
||||||
|
};
|
||||||
|
|
||||||
|
savestate_manager_dialog::savestate_manager_dialog(std::shared_ptr<gui_settings> gui_settings, const std::vector<game_info>& games)
|
||||||
|
: QWidget()
|
||||||
|
, m_gui_settings(std::move(gui_settings))
|
||||||
|
, m_game_info(games)
|
||||||
|
{
|
||||||
|
// Nonspecific widget settings
|
||||||
|
setWindowTitle(tr("Savestate Manager"));
|
||||||
|
setObjectName("savestate_manager");
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setAttribute(Qt::WA_StyledBackground);
|
||||||
|
|
||||||
|
m_game_icon_size_index = m_gui_settings->GetValue(gui::ss_game_icon_size).toInt();
|
||||||
|
|
||||||
|
// Game chooser combo box
|
||||||
|
m_game_combo = new QComboBox();
|
||||||
|
m_game_combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
|
||||||
|
|
||||||
|
// Games Table
|
||||||
|
m_game_table = new game_list();
|
||||||
|
m_game_table->setObjectName("savestate_manager_game_table");
|
||||||
|
m_game_table->setShowGrid(false);
|
||||||
|
m_game_table->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
m_game_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
m_game_table->verticalScrollBar()->installEventFilter(this);
|
||||||
|
m_game_table->verticalScrollBar()->setSingleStep(20);
|
||||||
|
m_game_table->horizontalScrollBar()->setSingleStep(20);
|
||||||
|
m_game_table->setItemDelegate(new game_list_delegate(m_game_table));
|
||||||
|
m_game_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
m_game_table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
m_game_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
m_game_table->setColumnCount(static_cast<int>(gui::savestate_game_list_columns::count));
|
||||||
|
m_game_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||||
|
m_game_table->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
m_game_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||||
|
m_game_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
m_game_table->verticalHeader()->setVisible(false);
|
||||||
|
m_game_table->setAlternatingRowColors(true);
|
||||||
|
m_game_table->installEventFilter(this);
|
||||||
|
|
||||||
|
auto add_game_column = [this](gui::savestate_game_list_columns col, const QString& header_text, const QString& action_text)
|
||||||
|
{
|
||||||
|
m_game_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text));
|
||||||
|
m_game_column_acts.append(new QAction(action_text, this));
|
||||||
|
};
|
||||||
|
|
||||||
|
add_game_column(gui::savestate_game_list_columns::icon, tr("Icon"), tr("Show Icons"));
|
||||||
|
add_game_column(gui::savestate_game_list_columns::name, tr("Game"), tr("Show Games"));
|
||||||
|
add_game_column(gui::savestate_game_list_columns::savestates, tr("Savestates"), tr("Show Savestates"));
|
||||||
|
|
||||||
|
// Savestate Table
|
||||||
|
m_savestate_table = new game_list();
|
||||||
|
m_savestate_table->setObjectName("savestate_manager_savestate_table");
|
||||||
|
m_savestate_table->setShowGrid(false);
|
||||||
|
m_savestate_table->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
m_savestate_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
m_savestate_table->verticalScrollBar()->installEventFilter(this);
|
||||||
|
m_savestate_table->verticalScrollBar()->setSingleStep(20);
|
||||||
|
m_savestate_table->horizontalScrollBar()->setSingleStep(20);
|
||||||
|
m_savestate_table->setItemDelegate(new game_list_delegate(m_savestate_table));
|
||||||
|
m_savestate_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
m_savestate_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
m_savestate_table->setColumnCount(static_cast<int>(gui::savestate_list_columns::count));
|
||||||
|
m_savestate_table->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
|
||||||
|
m_savestate_table->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
m_savestate_table->verticalHeader()->setVisible(false);
|
||||||
|
m_savestate_table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||||
|
m_savestate_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
m_savestate_table->setAlternatingRowColors(true);
|
||||||
|
m_savestate_table->installEventFilter(this);
|
||||||
|
|
||||||
|
auto add_savestate_column = [this](gui::savestate_list_columns col, const QString& header_text, const QString& action_text)
|
||||||
|
{
|
||||||
|
m_savestate_table->setHorizontalHeaderItem(static_cast<int>(col), new QTableWidgetItem(header_text));
|
||||||
|
m_savestate_column_acts.append(new QAction(action_text, this));
|
||||||
|
};
|
||||||
|
|
||||||
|
add_savestate_column(gui::savestate_list_columns::name, tr("Name"), tr("Show Names"));
|
||||||
|
add_savestate_column(gui::savestate_list_columns::compatible, tr("Compatible"), tr("Show Compatible"));
|
||||||
|
add_savestate_column(gui::savestate_list_columns::date, tr("Created"), tr("Show Created"));
|
||||||
|
add_savestate_column(gui::savestate_list_columns::path, tr("Path"), tr("Show Paths"));
|
||||||
|
|
||||||
|
m_splitter = new QSplitter();
|
||||||
|
m_splitter->addWidget(m_game_table);
|
||||||
|
m_splitter->addWidget(m_savestate_table);
|
||||||
|
|
||||||
|
m_game_icon_size = gui_settings::SizeFromSlider(m_game_icon_size_index);
|
||||||
|
|
||||||
|
QLabel* game_slider_label = new QLabel();
|
||||||
|
game_slider_label->setText(tr("Game Icon Size: %0x%1").arg(m_game_icon_size.width()).arg(m_game_icon_size.height()));
|
||||||
|
|
||||||
|
m_game_icon_slider = new QSlider(Qt::Horizontal);
|
||||||
|
m_game_icon_slider->setRange(0, gui::gl_max_slider_pos);
|
||||||
|
m_game_icon_slider->setValue(m_game_icon_size_index);
|
||||||
|
|
||||||
|
// LAYOUTS
|
||||||
|
QGroupBox* choose_game = new QGroupBox(tr("Choose Game"));
|
||||||
|
QVBoxLayout* choose_layout = new QVBoxLayout();
|
||||||
|
choose_layout->addWidget(m_game_combo);
|
||||||
|
choose_game->setLayout(choose_layout);
|
||||||
|
|
||||||
|
QGroupBox* icon_settings = new QGroupBox(tr("Icon Options"));
|
||||||
|
QVBoxLayout* slider_layout = new QVBoxLayout();
|
||||||
|
slider_layout->addWidget(game_slider_label);
|
||||||
|
slider_layout->addWidget(m_game_icon_slider);
|
||||||
|
icon_settings->setLayout(slider_layout);
|
||||||
|
|
||||||
|
QVBoxLayout* options_layout = new QVBoxLayout();
|
||||||
|
options_layout->addWidget(choose_game);
|
||||||
|
options_layout->addWidget(icon_settings);
|
||||||
|
options_layout->addStretch();
|
||||||
|
|
||||||
|
QHBoxLayout* all_layout = new QHBoxLayout(this);
|
||||||
|
all_layout->addLayout(options_layout);
|
||||||
|
all_layout->addWidget(m_splitter);
|
||||||
|
all_layout->setStretch(1, 1);
|
||||||
|
setLayout(all_layout);
|
||||||
|
|
||||||
|
// Make connects
|
||||||
|
connect(m_game_icon_slider, &QSlider::valueChanged, this, [this, game_slider_label](int val)
|
||||||
|
{
|
||||||
|
m_game_icon_size_index = val;
|
||||||
|
m_game_icon_size = gui_settings::SizeFromSlider(val);
|
||||||
|
if (game_slider_label)
|
||||||
|
{
|
||||||
|
game_slider_label->setText(tr("Game Icon Size: %0x%1").arg(m_game_icon_size.width()).arg(m_game_icon_size.height()));
|
||||||
|
}
|
||||||
|
ResizeGameIcons();
|
||||||
|
if (m_save_game_icon_size)
|
||||||
|
{
|
||||||
|
m_save_game_icon_size = false;
|
||||||
|
m_gui_settings->SetValue(gui::ss_game_icon_size, val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_game_icon_slider, &QSlider::sliderReleased, this, [this]()
|
||||||
|
{
|
||||||
|
m_gui_settings->SetValue(gui::ss_game_icon_size, m_game_icon_slider->value());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_game_icon_slider, &QSlider::actionTriggered, this, [this](int action)
|
||||||
|
{
|
||||||
|
if (action != QAbstractSlider::SliderNoAction && action != QAbstractSlider::SliderMove)
|
||||||
|
{ // we only want to save on mouseclicks or slider release (the other connect handles this)
|
||||||
|
m_save_game_icon_size = true; // actionTriggered happens before the value was changed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_savestate_table, &QTableWidget::customContextMenuRequested, this, &savestate_manager_dialog::ShowSavestateTableContextMenu);
|
||||||
|
|
||||||
|
connect(m_game_combo, &QComboBox::currentTextChanged, this, [this]
|
||||||
|
{
|
||||||
|
PopulateSavestateTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_game_table, &QTableWidget::customContextMenuRequested, this, &savestate_manager_dialog::ShowGameTableContextMenu);
|
||||||
|
|
||||||
|
connect(m_game_table, &QTableWidget::itemSelectionChanged, this, [this]
|
||||||
|
{
|
||||||
|
if (m_game_table->selectedItems().isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QTableWidgetItem* item = m_game_table->item(m_game_table->selectedItems().first()->row(), static_cast<int>(gui::savestate_game_list_columns::name));
|
||||||
|
if (!item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_game_combo->setCurrentText(item->text());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &savestate_manager_dialog::GameIconReady, this, [this](int index, const QPixmap& pixmap)
|
||||||
|
{
|
||||||
|
if (QTableWidgetItem* icon_item = m_game_table->item(index, static_cast<int>(gui::savestate_game_list_columns::icon)))
|
||||||
|
{
|
||||||
|
icon_item->setData(Qt::DecorationRole, pixmap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_savestate_table->create_header_actions(m_savestate_column_acts,
|
||||||
|
[this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col)); },
|
||||||
|
[this](int col, bool visible) { m_gui_settings->SetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col), visible); });
|
||||||
|
|
||||||
|
m_game_table->create_header_actions(m_game_column_acts,
|
||||||
|
[this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col)); },
|
||||||
|
[this](int col, bool visible) { m_gui_settings->SetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col), visible); });
|
||||||
|
|
||||||
|
RepaintUI(true);
|
||||||
|
|
||||||
|
StartSavestateLoadThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
savestate_manager_dialog::~savestate_manager_dialog()
|
||||||
|
{
|
||||||
|
WaitAndAbortGameRepaintThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool savestate_manager_dialog::LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates)
|
||||||
|
{
|
||||||
|
ensure(!!game_savestates);
|
||||||
|
|
||||||
|
if (game_savestates->title_id.empty())
|
||||||
|
{
|
||||||
|
gui_log.error("Failed to load savestates. Path empty!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string dir_path = fs::get_config_dir() + "savestates/" + game_savestates->title_id + "/";
|
||||||
|
|
||||||
|
const QDir savestate_dir(QString::fromStdString(dir_path));
|
||||||
|
const QFileInfoList file_list = savestate_dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
if (file_list.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate game_savestates_data
|
||||||
|
game_savestates->savestates.resize(file_list.size());
|
||||||
|
game_savestates->dir_path = dir_path;
|
||||||
|
|
||||||
|
extern bool is_savestate_compatible(const std::string& filepath);
|
||||||
|
|
||||||
|
for (usz id = 0; id < game_savestates->savestates.size(); ++id)
|
||||||
|
{
|
||||||
|
game_savestates->savestates[id].name = file_list[id].baseName();
|
||||||
|
game_savestates->savestates[id].path = file_list[id].absoluteFilePath();
|
||||||
|
game_savestates->savestates[id].date = file_list[id].birthTime();
|
||||||
|
game_savestates->savestates[id].is_compatible = is_savestate_compatible(game_savestates->savestates[id].path.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_savestate_db_mtx);
|
||||||
|
m_savestate_db.push_back(std::move(game_savestates));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::RepaintUI(bool restore_layout)
|
||||||
|
{
|
||||||
|
if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool())
|
||||||
|
{
|
||||||
|
m_game_icon_color = m_gui_settings->GetValue(gui::ss_icon_color).value<QColor>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_game_icon_color = gui::utils::get_label_color("savestate_manager_icon_background_color", Qt::transparent, Qt::transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
PopulateGameTable();
|
||||||
|
|
||||||
|
if (restore_layout && !restoreGeometry(m_gui_settings->GetValue(gui::ss_geometry).toByteArray()))
|
||||||
|
{
|
||||||
|
resize(QGuiApplication::primaryScreen()->availableSize() * 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restore_layout && !m_splitter->restoreState(m_gui_settings->GetValue(gui::ss_splitterState).toByteArray()))
|
||||||
|
{
|
||||||
|
const int width_left = m_splitter->width() * 0.4;
|
||||||
|
const int width_right = m_splitter->width() - width_left;
|
||||||
|
m_splitter->setSizes({ width_left, width_right });
|
||||||
|
}
|
||||||
|
|
||||||
|
PopulateSavestateTable();
|
||||||
|
|
||||||
|
const QByteArray game_table_state = m_gui_settings->GetValue(gui::ss_games_state).toByteArray();
|
||||||
|
if (restore_layout && !m_game_table->horizontalHeader()->restoreState(game_table_state) && m_game_table->rowCount())
|
||||||
|
{
|
||||||
|
// If no settings exist, resize to contents. (disabled)
|
||||||
|
//m_game_table->verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
|
||||||
|
//m_game_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray savestate_table_state = m_gui_settings->GetValue(gui::ss_savestate_state).toByteArray();
|
||||||
|
if (restore_layout && !m_savestate_table->horizontalHeader()->restoreState(savestate_table_state) && m_savestate_table->rowCount())
|
||||||
|
{
|
||||||
|
// If no settings exist, resize to contents. (disabled)
|
||||||
|
//m_savestate_table->verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
|
||||||
|
//m_savestate_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restore_layout)
|
||||||
|
{
|
||||||
|
// Make sure the actions and the headers are synced
|
||||||
|
m_game_table->sync_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col)); });
|
||||||
|
m_savestate_table->sync_header_actions(m_savestate_column_acts, [this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col)); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show dialog and then paint gui in order to adjust headers correctly
|
||||||
|
show();
|
||||||
|
ReadjustGameTable();
|
||||||
|
ReadjustSavestateTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::HandleRepaintUiRequest()
|
||||||
|
{
|
||||||
|
const QSize window_size = size();
|
||||||
|
const QByteArray splitter_state = m_splitter->saveState();
|
||||||
|
const QByteArray game_table_state = m_game_table->horizontalHeader()->saveState();
|
||||||
|
const QByteArray savestate_table_state = m_savestate_table->horizontalHeader()->saveState();
|
||||||
|
|
||||||
|
RepaintUI(false);
|
||||||
|
|
||||||
|
m_splitter->restoreState(splitter_state);
|
||||||
|
m_game_table->horizontalHeader()->restoreState(game_table_state);
|
||||||
|
m_savestate_table->horizontalHeader()->restoreState(savestate_table_state);
|
||||||
|
|
||||||
|
// Make sure the actions and the headers are synced
|
||||||
|
m_game_table->sync_header_actions(m_game_column_acts, [this](int col) { return m_gui_settings->GetSavestateGamelistColVisibility(static_cast<gui::savestate_game_list_columns>(col)); });
|
||||||
|
m_savestate_table->sync_header_actions(m_savestate_column_acts, [this](int col) { return m_gui_settings->GetSavestateListColVisibility(static_cast<gui::savestate_list_columns>(col)); });
|
||||||
|
|
||||||
|
resize(window_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::ResizeGameIcons()
|
||||||
|
{
|
||||||
|
if (m_game_combo->count() <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WaitAndAbortGameRepaintThreads();
|
||||||
|
|
||||||
|
QPixmap placeholder(m_game_icon_size);
|
||||||
|
placeholder.fill(Qt::transparent);
|
||||||
|
|
||||||
|
qRegisterMetaType<QVector<int>>("QVector<int>");
|
||||||
|
for (int i = 0; i < m_game_table->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
if (QTableWidgetItem* icon_item = m_game_table->item(i, static_cast<int>(gui::savestate_game_list_columns::icon)))
|
||||||
|
{
|
||||||
|
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadjustGameTable();
|
||||||
|
|
||||||
|
for (int i = 0; i < m_game_table->rowCount(); ++i)
|
||||||
|
{
|
||||||
|
if (movie_item* item = static_cast<movie_item*>(m_game_table->item(i, static_cast<int>(gui::savestate_game_list_columns::icon))))
|
||||||
|
{
|
||||||
|
const qreal dpr = devicePixelRatioF();
|
||||||
|
const int savestate_index = item->data(GameUserRole::GameIndex).toInt();
|
||||||
|
const std::string icon_path = m_savestate_db[savestate_index]->game_icon_path;
|
||||||
|
|
||||||
|
item->set_icon_load_func([this, icon_path, savestate_index, cancel = item->icon_loading_aborted(), dpr](int index)
|
||||||
|
{
|
||||||
|
if (cancel && cancel->load())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap icon;
|
||||||
|
|
||||||
|
if (movie_item* item = static_cast<movie_item*>(m_game_table->item(index, static_cast<int>(gui::savestate_game_list_columns::icon))))
|
||||||
|
{
|
||||||
|
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
|
||||||
|
{
|
||||||
|
// Load game icon
|
||||||
|
if (!icon.load(QString::fromStdString(icon_path)))
|
||||||
|
{
|
||||||
|
gui_log.warning("Could not load savestate game icon from path %s", icon_path);
|
||||||
|
}
|
||||||
|
item->setData(GameUserRole::GamePixmapLoaded, true);
|
||||||
|
item->setData(GameUserRole::GamePixmap, icon);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
icon = item->data(GameUserRole::GamePixmap).value<QPixmap>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancel && cancel->load())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap new_icon(icon.size() * dpr);
|
||||||
|
new_icon.setDevicePixelRatio(dpr);
|
||||||
|
new_icon.fill(m_game_icon_color);
|
||||||
|
|
||||||
|
if (!icon.isNull())
|
||||||
|
{
|
||||||
|
QPainter painter(&new_icon);
|
||||||
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
|
painter.drawPixmap(QPoint(0, 0), icon);
|
||||||
|
painter.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
new_icon = new_icon.scaled(m_game_icon_size * dpr, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation);
|
||||||
|
|
||||||
|
if (!cancel || !cancel->load())
|
||||||
|
{
|
||||||
|
Q_EMIT GameIconReady(index, new_icon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::ShowSavestateTableContextMenu(const QPoint& pos)
|
||||||
|
{
|
||||||
|
const int row = m_savestate_table->currentRow();
|
||||||
|
const QTableWidgetItem* path_item = m_savestate_table->item(row, static_cast<int>(gui::savestate_list_columns::path));
|
||||||
|
|
||||||
|
if (!path_item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu* menu = new QMenu();
|
||||||
|
QAction* show_savestate_dir = new QAction(tr("&Open Savestate Directory"), menu);
|
||||||
|
QAction* boot_savestate = new QAction(tr("&Boot Savestate"), menu);
|
||||||
|
QAction* delete_savestate = new QAction(tr("&Delete Savestate"), menu);
|
||||||
|
|
||||||
|
if (const QTableWidgetItem* comp_item = m_savestate_table->item(row, static_cast<int>(gui::savestate_list_columns::compatible)))
|
||||||
|
{
|
||||||
|
boot_savestate->setEnabled(comp_item->data(Qt::UserRole).toBool());
|
||||||
|
}
|
||||||
|
|
||||||
|
const int db_ind = m_game_combo->currentData().toInt();
|
||||||
|
const std::string path = path_item->text().toStdString();
|
||||||
|
|
||||||
|
connect(show_savestate_dir, &QAction::triggered, this, [this, db_ind]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(QString::fromStdString(m_savestate_db[db_ind]->dir_path));
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(boot_savestate, &QAction::triggered, this, [this, path]()
|
||||||
|
{
|
||||||
|
gui_log.notice("Booting savestate from savestate manager...");
|
||||||
|
Q_EMIT RequestBoot(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(delete_savestate, &QAction::triggered, this, [this, path]()
|
||||||
|
{
|
||||||
|
gui_log.notice("Removing savestate '%s' from savestate manager...", path);
|
||||||
|
|
||||||
|
if (QMessageBox::question(this, tr("Confirm Deletion"), tr("Delete savestate '%0'?").arg(QString::fromStdString(path))) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fs::remove_file(path))
|
||||||
|
{
|
||||||
|
gui_log.success("Removed savestate '%s'", path);
|
||||||
|
StartSavestateLoadThreads(); // Reload the savestate list
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gui_log.error("Failed to remove file '%s' (%s)", path, fs::g_tls_error);
|
||||||
|
QMessageBox::warning(this, tr("Deletion Failed!"), tr("Failed to delete savestate '%0'!").arg(QString::fromStdString(path)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction(boot_savestate);
|
||||||
|
menu->addAction(show_savestate_dir);
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(delete_savestate);
|
||||||
|
|
||||||
|
menu->exec(m_savestate_table->viewport()->mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::ShowGameTableContextMenu(const QPoint& pos)
|
||||||
|
{
|
||||||
|
const int row = m_game_table->currentRow();
|
||||||
|
|
||||||
|
if (!m_game_table->item(row, static_cast<int>(gui::savestate_game_list_columns::icon)))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu* menu = new QMenu();
|
||||||
|
QAction* remove_savestate_dir = new QAction(tr("&Remove All Savestates"), this);
|
||||||
|
QAction* show_savestate_dir = new QAction(tr("&Open Savestate Directory"), menu);
|
||||||
|
|
||||||
|
const int db_ind = m_game_combo->currentData().toInt();
|
||||||
|
|
||||||
|
const QTableWidgetItem* name_item = m_game_table->item(row, static_cast<int>(gui::savestate_game_list_columns::name));
|
||||||
|
const QString name = name_item ? name_item->text() : "";
|
||||||
|
|
||||||
|
connect(remove_savestate_dir, &QAction::triggered, this, [this, name, db_ind]()
|
||||||
|
{
|
||||||
|
if (QMessageBox::question(this, tr("Delete Confirmation"), tr("Are you sure you want to delete the savestates for:\n%0?").arg(name), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
const std::string path = m_savestate_db[db_ind]->dir_path;
|
||||||
|
ensure(path != (fs::get_config_dir() + "savestates/")); // Make sure we aren't deleting the root path by accident
|
||||||
|
fs::remove_all(path); // Remove the game's savestate folder
|
||||||
|
StartSavestateLoadThreads(); // Reload the savestate list
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(show_savestate_dir, &QAction::triggered, this, [this, db_ind]()
|
||||||
|
{
|
||||||
|
gui::utils::open_dir(QString::fromStdString(m_savestate_db[db_ind]->dir_path));
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction(show_savestate_dir);
|
||||||
|
|
||||||
|
if (!name.isEmpty())
|
||||||
|
{
|
||||||
|
QAction* copy_name = new QAction(tr("&Copy Name"), menu);
|
||||||
|
connect(copy_name, &QAction::triggered, this, [this, name]()
|
||||||
|
{
|
||||||
|
QApplication::clipboard()->setText(name);
|
||||||
|
});
|
||||||
|
menu->addAction(copy_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->addSeparator();
|
||||||
|
menu->addAction(remove_savestate_dir);
|
||||||
|
|
||||||
|
menu->exec(m_game_table->viewport()->mapToGlobal(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::StartSavestateLoadThreads()
|
||||||
|
{
|
||||||
|
WaitAndAbortGameRepaintThreads();
|
||||||
|
|
||||||
|
m_savestate_db.clear();
|
||||||
|
|
||||||
|
const QString savestate_path = QString::fromStdString(fs::get_config_dir() + "savestates/");
|
||||||
|
const QDir savestate_dir(savestate_path);
|
||||||
|
const QStringList folder_list = savestate_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
const int count = folder_list.count();
|
||||||
|
|
||||||
|
if (count <= 0)
|
||||||
|
{
|
||||||
|
RepaintUI(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<game_savestates_data>> game_data(count);
|
||||||
|
|
||||||
|
qRegisterMetaType<QVector<int>>("QVector<int>");
|
||||||
|
QList<int> indices;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
indices.append(i);
|
||||||
|
|
||||||
|
game_data[i] = std::make_unique<game_savestates_data>();
|
||||||
|
game_data[i]->title_id = folder_list[i].toStdString();
|
||||||
|
|
||||||
|
for (const game_info& gameinfo : m_game_info)
|
||||||
|
{
|
||||||
|
if (gameinfo && gameinfo->info.serial == game_data[i]->title_id)
|
||||||
|
{
|
||||||
|
game_data[i]->game_name = gameinfo->info.name;
|
||||||
|
game_data[i]->game_icon_path = gameinfo->info.icon_path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game_data[i]->game_name.empty())
|
||||||
|
{
|
||||||
|
game_data[i]->game_name = game_data[i]->title_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFutureWatcher<void> future_watcher;
|
||||||
|
|
||||||
|
progress_dialog progress_dialog(tr("Loading savestates"), tr("Loading savestates, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||||
|
|
||||||
|
connect(&future_watcher, &QFutureWatcher<void>::progressRangeChanged, &progress_dialog, &QProgressDialog::setRange);
|
||||||
|
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress_dialog, &QProgressDialog::setValue);
|
||||||
|
connect(&future_watcher, &QFutureWatcher<void>::finished, this, [this]() { RepaintUI(true); });
|
||||||
|
connect(&progress_dialog, &QProgressDialog::canceled, this, [this, &future_watcher]()
|
||||||
|
{
|
||||||
|
future_watcher.cancel();
|
||||||
|
close(); // It's pointless to show an empty window
|
||||||
|
});
|
||||||
|
|
||||||
|
atomic_t<usz> error_count{};
|
||||||
|
future_watcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &game_data](const int& i)
|
||||||
|
{
|
||||||
|
gui_log.trace("Loading savestate dir: %s", game_data[i]->title_id);
|
||||||
|
|
||||||
|
if (!LoadSavestateFolderToDB(std::move(game_data[i])))
|
||||||
|
{
|
||||||
|
// TODO: add a way of showing the number of corrupted/invalid savestates in UI somewhere.
|
||||||
|
gui_log.error("Error occurred while parsing folder %s for savestates.", game_data[i]->title_id);
|
||||||
|
error_count++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
progress_dialog.exec();
|
||||||
|
|
||||||
|
future_watcher.waitForFinished();
|
||||||
|
|
||||||
|
if (error_count != 0)
|
||||||
|
{
|
||||||
|
gui_log.error("Failed to load %d of %d savestates!", error_count.load(), count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::PopulateGameTable()
|
||||||
|
{
|
||||||
|
WaitAndAbortGameRepaintThreads();
|
||||||
|
|
||||||
|
m_game_table->setSortingEnabled(false); // Disable sorting before using setItem calls
|
||||||
|
m_game_table->clearContents();
|
||||||
|
m_game_table->setRowCount(static_cast<int>(m_savestate_db.size()));
|
||||||
|
|
||||||
|
m_game_combo->clear();
|
||||||
|
m_game_combo->blockSignals(true);
|
||||||
|
|
||||||
|
qRegisterMetaType<QVector<int>>("QVector<int>");
|
||||||
|
QList<int> indices;
|
||||||
|
for (usz i = 0; i < m_savestate_db.size(); ++i)
|
||||||
|
indices.append(static_cast<int>(i));
|
||||||
|
|
||||||
|
QPixmap placeholder(m_game_icon_size);
|
||||||
|
placeholder.fill(Qt::transparent);
|
||||||
|
|
||||||
|
for (int i = 0; i < indices.count(); ++i)
|
||||||
|
{
|
||||||
|
const QString name = QString::fromStdString(m_savestate_db[i]->game_name).simplified();
|
||||||
|
const quint64 savestate_count = m_savestate_db[i]->savestates.size();
|
||||||
|
|
||||||
|
custom_table_widget_item* icon_item = new custom_table_widget_item;
|
||||||
|
icon_item->setData(Qt::DecorationRole, placeholder);
|
||||||
|
icon_item->setData(GameUserRole::GameIndex, i);
|
||||||
|
icon_item->setData(GameUserRole::GamePixmapLoaded, false);
|
||||||
|
icon_item->setData(GameUserRole::GamePixmap, QPixmap());
|
||||||
|
|
||||||
|
m_game_table->setItem(i, static_cast<int>(gui::savestate_game_list_columns::icon), icon_item);
|
||||||
|
m_game_table->setItem(i, static_cast<int>(gui::savestate_game_list_columns::name), new custom_table_widget_item(name));
|
||||||
|
m_game_table->setItem(i, static_cast<int>(gui::savestate_game_list_columns::savestates), new custom_table_widget_item(QString::number(savestate_count), Qt::UserRole, savestate_count));
|
||||||
|
|
||||||
|
m_game_combo->addItem(name, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_game_combo->model()->sort(0, Qt::AscendingOrder);
|
||||||
|
m_game_combo->blockSignals(false);
|
||||||
|
m_game_combo->setCurrentIndex(0);
|
||||||
|
|
||||||
|
m_game_table->setSortingEnabled(true); // Enable sorting only after using setItem calls
|
||||||
|
|
||||||
|
ResizeGameIcons();
|
||||||
|
|
||||||
|
gui::utils::resize_combo_box_view(m_game_combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::PopulateSavestateTable()
|
||||||
|
{
|
||||||
|
if (m_game_combo->count() <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto& data = m_savestate_db[m_game_combo->currentData().toInt()];
|
||||||
|
ensure(!!data);
|
||||||
|
|
||||||
|
gui_log.trace("Populating Savestate Manager UI with %s %s", data->game_name, data->dir_path);
|
||||||
|
|
||||||
|
const std::vector<savestate_data>& savestates = data->savestates;
|
||||||
|
|
||||||
|
m_savestate_table->clear_list();
|
||||||
|
m_savestate_table->setRowCount(static_cast<int>(savestates.size()));
|
||||||
|
m_savestate_table->setSortingEnabled(false); // Disable sorting before using setItem calls
|
||||||
|
|
||||||
|
const QLocale locale{};
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(savestates.size()); i++)
|
||||||
|
{
|
||||||
|
const savestate_data& savestate = savestates[i];
|
||||||
|
m_savestate_table->setItem(i, static_cast<int>(gui::savestate_list_columns::name), new custom_table_widget_item(savestate.name));
|
||||||
|
m_savestate_table->setItem(i, static_cast<int>(gui::savestate_list_columns::compatible), new custom_table_widget_item(savestate.is_compatible ? tr("Compatible") : tr("Not compatible"), Qt::UserRole, savestate.is_compatible));
|
||||||
|
m_savestate_table->setItem(i, static_cast<int>(gui::savestate_list_columns::date), new custom_table_widget_item(savestate.date.toString(), Qt::UserRole, savestate.date));
|
||||||
|
m_savestate_table->setItem(i, static_cast<int>(gui::savestate_list_columns::path), new custom_table_widget_item(savestate.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_savestate_table->setSortingEnabled(true); // Re-enable sorting after using setItem calls
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::ReadjustGameTable() const
|
||||||
|
{
|
||||||
|
// Fixate vertical header and row height
|
||||||
|
m_game_table->verticalHeader()->setMinimumSectionSize(m_game_icon_size.height());
|
||||||
|
m_game_table->verticalHeader()->setMaximumSectionSize(m_game_icon_size.height());
|
||||||
|
m_game_table->resizeRowsToContents();
|
||||||
|
|
||||||
|
// Resize and fixate icon column
|
||||||
|
m_game_table->resizeColumnToContents(static_cast<int>(gui::savestate_game_list_columns::icon));
|
||||||
|
m_game_table->horizontalHeader()->setSectionResizeMode(static_cast<int>(gui::savestate_game_list_columns::icon), QHeaderView::Fixed);
|
||||||
|
|
||||||
|
// Shorten the last section to remove horizontal scrollbar if possible
|
||||||
|
m_game_table->resizeColumnToContents(static_cast<int>(gui::savestate_game_list_columns::count) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::ReadjustSavestateTable() const
|
||||||
|
{
|
||||||
|
// Fixate vertical header and row height
|
||||||
|
m_savestate_table->resizeRowsToContents();
|
||||||
|
|
||||||
|
// Shorten the last section to remove horizontal scrollbar if possible
|
||||||
|
m_savestate_table->resizeColumnToContents(static_cast<int>(gui::savestate_list_columns::count) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool savestate_manager_dialog::eventFilter(QObject *object, QEvent *event)
|
||||||
|
{
|
||||||
|
const bool is_savestate_scroll = object == m_savestate_table->verticalScrollBar();
|
||||||
|
const bool is_savestate_table = object == m_savestate_table;
|
||||||
|
const bool is_game_scroll = object == m_game_table->verticalScrollBar();
|
||||||
|
const bool is_game_table = object == m_game_table;
|
||||||
|
int zoom_val = 0;
|
||||||
|
|
||||||
|
switch (event->type())
|
||||||
|
{
|
||||||
|
case QEvent::Wheel:
|
||||||
|
{
|
||||||
|
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
|
||||||
|
|
||||||
|
if (wheelEvent->modifiers() & Qt::ControlModifier && (is_savestate_scroll || is_game_scroll))
|
||||||
|
{
|
||||||
|
const QPoint numSteps = wheelEvent->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta
|
||||||
|
zoom_val = numSteps.y();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::KeyPress:
|
||||||
|
{
|
||||||
|
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||||
|
|
||||||
|
if (keyEvent && keyEvent->modifiers() == Qt::ControlModifier && (is_savestate_table || is_game_table))
|
||||||
|
{
|
||||||
|
if (keyEvent->key() == Qt::Key_Plus)
|
||||||
|
{
|
||||||
|
zoom_val = 1;
|
||||||
|
}
|
||||||
|
else if (keyEvent->key() == Qt::Key_Minus)
|
||||||
|
{
|
||||||
|
zoom_val = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zoom_val != 0)
|
||||||
|
{
|
||||||
|
if (m_game_icon_slider && (is_game_table || is_game_scroll))
|
||||||
|
{
|
||||||
|
m_save_game_icon_size = true;
|
||||||
|
m_game_icon_slider->setSliderPosition(zoom_val + m_game_icon_slider->value());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
// Save gui settings
|
||||||
|
m_gui_settings->SetValue(gui::ss_geometry, saveGeometry(), false);
|
||||||
|
m_gui_settings->SetValue(gui::ss_splitterState, m_splitter->saveState(), false);
|
||||||
|
m_gui_settings->SetValue(gui::ss_games_state, m_game_table->horizontalHeader()->saveState(), false);
|
||||||
|
m_gui_settings->SetValue(gui::ss_savestate_state, m_savestate_table->horizontalHeader()->saveState(), true);
|
||||||
|
|
||||||
|
QWidget::closeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void savestate_manager_dialog::WaitAndAbortGameRepaintThreads()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_game_table->rowCount(); i++)
|
||||||
|
{
|
||||||
|
if (movie_item* item = static_cast<movie_item*>(m_game_table->item(i, static_cast<int>(gui::savestate_game_list_columns::icon))))
|
||||||
|
{
|
||||||
|
item->wait_for_icon_loading(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
rpcs3/rpcs3qt/savestate_manager_dialog.h
Normal file
85
rpcs3/rpcs3qt/savestate_manager_dialog.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "game_list_base.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QSplitter>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
class game_list;
|
||||||
|
class gui_settings;
|
||||||
|
|
||||||
|
class savestate_manager_dialog : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit savestate_manager_dialog(std::shared_ptr<gui_settings> gui_settings, const std::vector<game_info>& games);
|
||||||
|
~savestate_manager_dialog() override;
|
||||||
|
void RepaintUI(bool restore_layout = true);
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void HandleRepaintUiRequest();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void ResizeGameIcons();
|
||||||
|
void ShowSavestateTableContextMenu(const QPoint& pos);
|
||||||
|
void ShowGameTableContextMenu(const QPoint& pos);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void GameIconReady(int index, const QPixmap& pixmap);
|
||||||
|
void RequestBoot(const std::string& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct savestate_data
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString path;
|
||||||
|
QDateTime date;
|
||||||
|
bool is_compatible = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct game_savestates_data
|
||||||
|
{
|
||||||
|
std::vector<savestate_data> savestates;
|
||||||
|
std::string title_id;
|
||||||
|
std::string game_name;
|
||||||
|
std::string game_icon_path;
|
||||||
|
std::string dir_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool LoadSavestateFolderToDB(std::unique_ptr<game_savestates_data>&& game_savestates);
|
||||||
|
void StartSavestateLoadThreads();
|
||||||
|
|
||||||
|
void PopulateGameTable();
|
||||||
|
void PopulateSavestateTable();
|
||||||
|
|
||||||
|
void ReadjustGameTable() const;
|
||||||
|
void ReadjustSavestateTable() const;
|
||||||
|
|
||||||
|
void WaitAndAbortGameRepaintThreads();
|
||||||
|
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
bool eventFilter(QObject *object, QEvent *event) override;
|
||||||
|
|
||||||
|
std::shared_ptr<gui_settings> m_gui_settings;
|
||||||
|
|
||||||
|
std::vector<game_info> m_game_info;
|
||||||
|
std::vector<std::unique_ptr<game_savestates_data>> m_savestate_db; //! Holds all the savestate information.
|
||||||
|
std::mutex m_savestate_db_mtx;
|
||||||
|
QComboBox* m_game_combo; //! Lets you choose a game
|
||||||
|
QSplitter* m_splitter; //! Contains the game and savestate tables
|
||||||
|
game_list* m_savestate_table; //! UI element to display savestate stuff.
|
||||||
|
game_list* m_game_table; //! UI element to display games.
|
||||||
|
|
||||||
|
QList<QAction*> m_savestate_column_acts;
|
||||||
|
QList<QAction*> m_game_column_acts;
|
||||||
|
|
||||||
|
int m_game_icon_size_index = 25;
|
||||||
|
QSize m_game_icon_size = QSize(m_game_icon_size_index, m_game_icon_size_index);
|
||||||
|
bool m_save_game_icon_size = false;
|
||||||
|
QSlider* m_game_icon_slider = nullptr;
|
||||||
|
QColor m_game_icon_color;
|
||||||
|
};
|
@ -601,7 +601,6 @@ void trophy_manager_dialog::ResizeGameIcons()
|
|||||||
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
|
if (!item->data(GameUserRole::GamePixmapLoaded).toBool())
|
||||||
{
|
{
|
||||||
// Load game icon
|
// Load game icon
|
||||||
const std::string icon_path = m_trophies_db[trophy_index]->path + "ICON0.PNG";
|
|
||||||
if (!icon.load(QString::fromStdString(icon_path)))
|
if (!icon.load(QString::fromStdString(icon_path)))
|
||||||
{
|
{
|
||||||
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
|
gui_log.warning("Could not load trophy game icon from path %s", icon_path);
|
||||||
@ -1110,6 +1109,8 @@ void trophy_manager_dialog::PopulateTrophyTable()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto& data = m_trophies_db[m_game_combo->currentData().toInt()];
|
auto& data = m_trophies_db[m_game_combo->currentData().toInt()];
|
||||||
|
ensure(!!data);
|
||||||
|
|
||||||
gui_log.trace("Populating Trophy Manager UI with %s %s", data->game_name, data->path);
|
gui_log.trace("Populating Trophy Manager UI with %s %s", data->game_name, data->path);
|
||||||
|
|
||||||
const int all_trophies = data->trop_usr->GetTrophiesCount();
|
const int all_trophies = data->trop_usr->GetTrophiesCount();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user