This commit is contained in:
Stern 2025-12-15 21:56:48 -05:00 committed by GitHub
commit 2b58331006
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 401 additions and 45 deletions

View File

@ -372,16 +372,6 @@ void Host::OnAchievementsRefreshed()
// noop // noop
} }
void Host::OnCoverDownloaderOpenRequested()
{
// noop
}
void Host::OnCreateMemoryCardOpenRequested()
{
// noop
}
bool Host::InBatchMode() bool Host::InBatchMode()
{ {
return false; return false;

View File

@ -444,8 +444,6 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(thread, &EmuThread::onCaptureStopped, this, &MainWindow::onCaptureStopped); connect(thread, &EmuThread::onCaptureStopped, this, &MainWindow::onCaptureStopped);
connect(thread, &EmuThread::onAchievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested); connect(thread, &EmuThread::onAchievementsLoginRequested, this, &MainWindow::onAchievementsLoginRequested);
connect(thread, &EmuThread::onAchievementsHardcoreModeChanged, this, &MainWindow::onAchievementsHardcoreModeChanged); connect(thread, &EmuThread::onAchievementsHardcoreModeChanged, this, &MainWindow::onAchievementsHardcoreModeChanged);
connect(thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
connect(thread, &EmuThread::onCreateMemoryCardOpenRequested, this, &MainWindow::onCreateMemoryCardOpenRequested);
connect(m_ui.actionReset, &QAction::triggered, this, &MainWindow::requestReset); connect(m_ui.actionReset, &QAction::triggered, this, &MainWindow::requestReset);
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused); connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);

View File

@ -1130,16 +1130,6 @@ void Host::OnAchievementsHardcoreModeChanged(bool enabled)
emit g_emu_thread->onAchievementsHardcoreModeChanged(enabled); emit g_emu_thread->onAchievementsHardcoreModeChanged(enabled);
} }
void Host::OnCoverDownloaderOpenRequested()
{
emit g_emu_thread->onCoverDownloaderOpenRequested();
}
void Host::OnCreateMemoryCardOpenRequested()
{
emit g_emu_thread->onCreateMemoryCardOpenRequested();
}
bool Host::ShouldPreferHostFileSelector() bool Host::ShouldPreferHostFileSelector()
{ {
#ifdef __linux__ #ifdef __linux__

View File

@ -164,10 +164,6 @@ Q_SIGNALS:
/// Called when hardcore mode is enabled or disabled. /// Called when hardcore mode is enabled or disabled.
void onAchievementsHardcoreModeChanged(bool enabled); void onAchievementsHardcoreModeChanged(bool enabled);
/// Big Picture UI requests.
void onCoverDownloaderOpenRequested();
void onCreateMemoryCardOpenRequested();
/// Called when video capture starts/stops. /// Called when video capture starts/stops.
void onCaptureStarted(const QString& filename); void onCaptureStarted(const QString& filename);
void onCaptureStopped(); void onCaptureStopped();

View File

@ -1355,6 +1355,12 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
return false; return false;
} }
if (!FileSystem::CreateDirectoryPath(EmuFolders::Covers.c_str(), false))
{
progress->DisplayError(fmt::format("Failed to create covers directory: {}", EmuFolders::Covers).c_str());
return false;
}
std::vector<std::pair<std::string, std::string>> download_urls; std::vector<std::pair<std::string, std::string>> download_urls;
{ {
std::unique_lock lock(s_mutex); std::unique_lock lock(s_mutex);

View File

@ -439,6 +439,10 @@ namespace FullscreenUI
static void CopyTextToClipboard(std::string title, const std::string_view text); static void CopyTextToClipboard(std::string title, const std::string_view text);
static void DrawAboutWindow(); static void DrawAboutWindow();
static void OpenAboutWindow(); static void OpenAboutWindow();
static void DrawCoverDownloaderWindow();
static void OpenCoverDownloaderWindow();
static void CloseCoverDownloaderWindow();
static void CoverDownloaderThreadFunc(const std::vector<std::string>& urls);
static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel); static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
static void ApplyLayoutSettings(const SettingsInterface* bsi = nullptr); static void ApplyLayoutSettings(const SettingsInterface* bsi = nullptr);
@ -462,6 +466,18 @@ namespace FullscreenUI
static char s_achievements_login_password[256] = {}; static char s_achievements_login_password[256] = {};
static Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated; static Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
// cover downloader dialog state
static bool s_cover_downloader_open = false;
static std::array<char, 4096> s_cover_downloader_urls_buffer = {};
static bool s_cover_downloader_use_title_filenames = false;
static bool s_cover_downloader_downloading = false;
static std::unique_ptr<std::thread> s_cover_downloader_thread;
static std::mutex s_cover_downloader_mutex;
static std::string s_cover_downloader_status;
static bool s_cover_downloader_has_error = false;
static s32 s_cover_downloader_progress_max = 0;
static s32 s_cover_downloader_progress_value = 0;
// local copies of the currently-running game // local copies of the currently-running game
static std::string s_current_game_title; static std::string s_current_game_title;
static std::string s_current_game_subtitle; static std::string s_current_game_subtitle;
@ -1028,7 +1044,7 @@ bool FullscreenUI::HasActiveWindow()
bool FullscreenUI::AreAnyDialogsOpen() bool FullscreenUI::AreAnyDialogsOpen()
{ {
return (s_save_state_selector_open || s_about_window_open || return (s_save_state_selector_open || s_about_window_open || s_cover_downloader_open ||
s_input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() || s_input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() ||
ImGuiFullscreen::IsFileSelectorOpen()); ImGuiFullscreen::IsFileSelectorOpen());
} }
@ -1181,6 +1197,7 @@ void FullscreenUI::Shutdown(bool clear_state)
{ {
CancelAllHddOperations(); CancelAllHddOperations();
CloseSaveStateSelector(); CloseSaveStateSelector();
CloseCoverDownloaderWindow();
s_cover_image_map.clear(); s_cover_image_map.clear();
s_game_list_sorted_entries = {}; s_game_list_sorted_entries = {};
s_game_list_directories_cache = {}; s_game_list_directories_cache = {};
@ -1298,6 +1315,9 @@ void FullscreenUI::Render()
if (s_about_window_open) if (s_about_window_open)
DrawAboutWindow(); DrawAboutWindow();
if (s_cover_downloader_open)
DrawCoverDownloaderWindow();
if (s_achievements_login_open) if (s_achievements_login_open)
DrawAchievementsLoginWindow(); DrawAchievementsLoginWindow();
@ -7865,6 +7885,10 @@ void FullscreenUI::DrawGameListWindow()
s_current_main_window = MainWindowType::GameListSettings; s_current_main_window = MainWindowType::GameListSettings;
QueueResetFocus(FocusResetType::WindowChanged); QueueResetFocus(FocusResetType::WindowChanged);
} }
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack, false) || ImGui::IsKeyPressed(ImGuiKey_F4, false))
{
OpenCoverDownloaderWindow();
}
switch (s_game_list_view) switch (s_game_list_view)
{ {
@ -7896,6 +7920,7 @@ void FullscreenUI::DrawGameListWindow()
const bool swapNorthWest = ImGuiManager::IsGamepadNorthWestSwapped(); const bool swapNorthWest = ImGuiManager::IsGamepadNorthWestSwapped();
SetFullscreenFooterText(std::array{ SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_DPAD, FSUI_VSTR("Select Game")), std::make_pair(ICON_PF_DPAD, FSUI_VSTR("Select Game")),
std::make_pair(ICON_PF_SELECT_SHARE, FSUI_VSTR("Cover Downloader")),
std::make_pair(ICON_PF_START, FSUI_VSTR("Settings")), std::make_pair(ICON_PF_START, FSUI_VSTR("Settings")),
std::make_pair(swapNorthWest ? ICON_PF_BUTTON_SQUARE : ICON_PF_BUTTON_TRIANGLE, FSUI_VSTR("Change View")), std::make_pair(swapNorthWest ? ICON_PF_BUTTON_SQUARE : ICON_PF_BUTTON_TRIANGLE, FSUI_VSTR("Change View")),
std::make_pair(swapNorthWest ? ICON_PF_BUTTON_TRIANGLE : ICON_PF_BUTTON_SQUARE, FSUI_VSTR("Launch Options")), std::make_pair(swapNorthWest ? ICON_PF_BUTTON_TRIANGLE : ICON_PF_BUTTON_SQUARE, FSUI_VSTR("Launch Options")),
@ -7910,6 +7935,7 @@ void FullscreenUI::DrawGameListWindow()
std::make_pair(ICON_PF_F1, FSUI_VSTR("Change View")), std::make_pair(ICON_PF_F1, FSUI_VSTR("Change View")),
std::make_pair(ICON_PF_F2, FSUI_VSTR("Settings")), std::make_pair(ICON_PF_F2, FSUI_VSTR("Settings")),
std::make_pair(ICON_PF_F3, FSUI_VSTR("Launch Options")), std::make_pair(ICON_PF_F3, FSUI_VSTR("Launch Options")),
std::make_pair(ICON_PF_F4, FSUI_VSTR("Cover Downloader")),
std::make_pair(ICON_PF_ENTER, FSUI_VSTR("Start Game")), std::make_pair(ICON_PF_ENTER, FSUI_VSTR("Start Game")),
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Back")), std::make_pair(ICON_PF_ESC, FSUI_VSTR("Back")),
}); });
@ -8466,16 +8492,6 @@ void FullscreenUI::DrawGameListSettingsWindow()
"FullscreenUIShowGameGridTitles", true); "FullscreenUIShowGameGridTitles", true);
} }
MenuHeading(FSUI_CSTR("Cover Settings"));
{
DrawFolderSetting(bsi, FSUI_ICONSTR(ICON_FA_FOLDER, "Covers Directory"), "Folders", "Covers", EmuFolders::Covers);
if (MenuButton(
FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Download Covers"), FSUI_CSTR("Downloads covers from a user-specified URL template.")))
{
Host::OnCoverDownloaderOpenRequested();
}
}
MenuHeading(FSUI_CSTR("Operations")); MenuHeading(FSUI_CSTR("Operations"));
{ {
if (MenuButton( if (MenuButton(
@ -8716,6 +8732,374 @@ void FullscreenUI::DrawAboutWindow()
ImGui::PopFont(); ImGui::PopFont();
} }
void FullscreenUI::OpenCoverDownloaderWindow()
{
s_cover_downloader_open = true;
s_cover_downloader_urls_buffer.fill('\0');
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_use_title_filenames = false;
s_cover_downloader_downloading = false;
s_cover_downloader_has_error = false;
}
QueueResetFocus(FocusResetType::PopupOpened);
}
void FullscreenUI::CloseCoverDownloaderWindow()
{
if (s_cover_downloader_thread)
{
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_downloading = false;
}
if (s_cover_downloader_thread->joinable())
{
s_cover_downloader_thread->join();
}
s_cover_downloader_thread.reset();
}
s_cover_downloader_open = false;
s_cover_downloader_urls_buffer.fill('\0');
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_use_title_filenames = false;
s_cover_downloader_has_error = false;
}
}
void FullscreenUI::CoverDownloaderThreadFunc(const std::vector<std::string>& urls)
{
class CoverDownloaderProgressCallback : public ProgressCallback
{
public:
CoverDownloaderProgressCallback() = default;
~CoverDownloaderProgressCallback() = default;
void PushState() override {}
void PopState() override {}
bool IsCancelled() const override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
return !s_cover_downloader_downloading;
}
bool IsCancellable() const override
{
return true;
}
void SetCancellable(bool cancellable) override
{
}
void SetTitle(const char* title) override
{
}
void SetStatusText(const char* text) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_status = text;
}
void SetProgressRange(u32 range) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_progress_max = range;
s_cover_downloader_progress_value = 0;
}
void SetProgressValue(u32 value) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_progress_value = value;
}
void IncrementProgressValue() override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_progress_value++;
}
void SetProgressState(ProgressState state) override
{
}
void DisplayError(const char* message) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_status = fmt::format(FSUI_FSTR("Error: {}"), message);
s_cover_downloader_has_error = true;
}
void DisplayWarning(const char* message) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_status = fmt::format(FSUI_FSTR("Warning: {}"), message);
s_cover_downloader_has_error = false;
}
void DisplayInformation(const char* message) override
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_status = message;
s_cover_downloader_has_error = false;
}
void DisplayDebugMessage(const char* message) override
{
}
void ModalError(const char* message) override
{
DisplayError(message);
}
bool ModalConfirmation(const char* message) override
{
return false;
}
void ModalInformation(const char* message) override
{
DisplayInformation(message);
}
};
CoverDownloaderProgressCallback callback;
bool use_title_filenames;
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
use_title_filenames = s_cover_downloader_use_title_filenames;
}
GameList::DownloadCovers(urls, !use_title_filenames, &callback);
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
if (s_cover_downloader_has_error && !s_cover_downloader_status.empty())
{
std::string error_message = s_cover_downloader_status;
MTGS::RunOnGSThread([error_message]() {
ShowToast(FSUI_STR("Download Failed"), error_message, 5.0f);
});
}
// We clear the cover image cache so the newly downloaded covers are picked up
MTGS::RunOnGSThread([]() {
s_cover_image_map.clear();
});
s_cover_downloader_downloading = false;
}
}
void FullscreenUI::DrawCoverDownloaderWindow()
{
ImGui::SetNextWindowSize(LayoutScale(800.0f, 640.0f));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Cover Downloader"));
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING));
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar;
if (ImGui::BeginPopupModal(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Cover Downloader"), &s_cover_downloader_open, flags))
{
BeginMenuButtons();
ImGui::TextWrapped("%s", FSUI_CSTR("PCSX2 can automatically download covers for games which do not currently have a cover set. We do not host any cover images, the user must provide their own source for images."));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(8.0f));
ImGui::TextWrapped("%s", FSUI_CSTR("In the box below, specify the URLs to download covers from, with one template URL per line. The following variables are available:"));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(6.0f));
ImGui::Text("${title}: %s", FSUI_CSTR("Title of the game."));
ImGui::Text("${filetitle}: %s", FSUI_CSTR("Name component of the game's filename."));
ImGui::Text("${serial}: %s", FSUI_CSTR("Serial of the game."));
ImGui::TextWrapped("%s https://www.example-not-a-real-domain.com/covers/${serial}.jpg", FSUI_CSTR("Example:"));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
// URLs input section
{
ImGui::TextUnformatted(FSUI_CSTR("URLs:"));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(4.0f));
const float text_box_height = LayoutScale(55.0f);
ImGui::SetNextItemWidth(-1.0f);
ImGuiInputTextFlags text_flags = ImGuiInputTextFlags_AllowTabInput;
bool is_downloading;
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
is_downloading = s_cover_downloader_downloading;
}
if (!is_downloading)
{
ImGui::InputTextMultiline("##urls", s_cover_downloader_urls_buffer.data(), s_cover_downloader_urls_buffer.size(), ImVec2(-1.0f, text_box_height), text_flags);
}
else
{
ImGui::BeginDisabled();
ImGui::InputTextMultiline("##urls", s_cover_downloader_urls_buffer.data(), s_cover_downloader_urls_buffer.size(), ImVec2(-1.0f, text_box_height), text_flags);
ImGui::EndDisabled();
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
bool use_title;
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
use_title = s_cover_downloader_use_title_filenames;
}
ImGui::SetNextItemWidth(-1.0f);
if (ImGui::Checkbox(FSUI_CSTR("Use Title File Names"), &use_title))
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
s_cover_downloader_use_title_filenames = use_title;
}
const bool is_hovering_checkbox = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay);
ImGui::SameLine();
ImGui::TextDisabled("(?)");
const bool is_hovering_indicator = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay);
if (is_hovering_checkbox || is_hovering_indicator)
{
if (ImGui::BeginTooltip())
{
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(FSUI_CSTR(
"By default, the downloaded covers will be saved with the game's serial to ensure covers do not break with GameDB changes "
"and that titles with multiple regions do not conflict. If this is not desired, you can check the \"Use Title File Names\" box above."));
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
// Progress display
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
if (s_cover_downloader_downloading)
{
if (!s_cover_downloader_status.empty())
{
ImGui::TextUnformatted(s_cover_downloader_status.c_str());
}
else
{
ImGui::TextUnformatted(FSUI_CSTR("Downloading covers..."));
}
// Show progress bar if we have progress info
if (s_cover_downloader_progress_max > 0)
{
const float progress = static_cast<float>(s_cover_downloader_progress_value) / static_cast<float>(s_cover_downloader_progress_max);
const int percent = static_cast<int>(std::round(progress * 100.0f));
ImGui::ProgressBar(progress, ImVec2(-1.0f, LayoutScale(30.0f)), fmt::format("{}%", percent).c_str());
}
else
{
// Indeterminate progress bar
const float fraction = std::fmod(ImGui::GetTime(), 2.0f) * 0.5f;
ImGui::ProgressBar(fraction, ImVec2(-1.0f, LayoutScale(30.0f)));
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(4.0f));
}
}
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(12.0f));
const bool has_urls = (s_cover_downloader_urls_buffer[0] != '\0');
bool start_enabled;
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
start_enabled = !s_cover_downloader_downloading && has_urls;
}
{
std::unique_lock<std::mutex> lock(s_cover_downloader_mutex);
const bool is_downloading = s_cover_downloader_downloading;
lock.unlock();
if (is_downloading)
{
if (ActiveButton(FSUI_ICONSTR(ICON_FA_STOP, "Stop"), false))
{
{
std::lock_guard<std::mutex> lock2(s_cover_downloader_mutex);
s_cover_downloader_downloading = false;
}
if (s_cover_downloader_thread && s_cover_downloader_thread->joinable())
s_cover_downloader_thread->join();
s_cover_downloader_thread.reset();
}
}
else
{
if (ActiveButton(FSUI_ICONSTR(ICON_FA_PLAY, "Start"), false, start_enabled))
{
std::vector<std::string> urls;
std::string urls_text(s_cover_downloader_urls_buffer.data());
for (const auto& line : StringUtil::SplitString(urls_text, '\n', true))
{
if (!line.empty())
urls.push_back(std::string(line));
}
// Start download in a background thread
// Clean up any existing thread before creating a new one
if (s_cover_downloader_thread && s_cover_downloader_thread->joinable())
{
s_cover_downloader_thread->join();
s_cover_downloader_thread.reset();
}
bool should_start_download = false;
{
std::lock_guard<std::mutex> lock(s_cover_downloader_mutex);
if (!s_cover_downloader_downloading)
{
// Reset progress values for new download
s_cover_downloader_progress_max = 0;
s_cover_downloader_progress_value = 0;
s_cover_downloader_status.clear();
s_cover_downloader_has_error = false;
s_cover_downloader_downloading = true;
should_start_download = true;
}
}
if (should_start_download)
{
s_cover_downloader_thread = std::make_unique<std::thread>(CoverDownloaderThreadFunc, urls);
}
}
}
}
if (ActiveButton(FSUI_ICONSTR(ICON_FA_XMARK, "Close"), false) || WantsToCloseMenu())
{
ImGui::CloseCurrentPopup();
s_cover_downloader_open = false;
CloseCoverDownloaderWindow();
}
EndMenuButtons();
ImGui::EndPopup();
}
ImGui::PopStyleVar(4);
ImGui::PopFont();
}
bool FullscreenUI::OpenAchievementsWindow() bool FullscreenUI::OpenAchievementsWindow()
{ {
if (!VMManager::HasValidVM() || !Achievements::IsActive()) if (!VMManager::HasValidVM() || !Achievements::IsActive())

View File

@ -238,14 +238,6 @@ void Host::OnAchievementsHardcoreModeChanged(bool enabled)
{ {
} }
void Host::OnCoverDownloaderOpenRequested()
{
}
void Host::OnCreateMemoryCardOpenRequested()
{
}
bool Host::LocaleCircleConfirm() bool Host::LocaleCircleConfirm()
{ {
return false; return false;