diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index faa67c5c9c..6ba78a0b47 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -77,9 +77,6 @@ #if !PPSSPP_PLATFORM(UWP) #include "gfx/gl_common.h" #endif -#if !PPSSPP_PLATFORM(UWP) -#include "gfx/gl_common.h" -#endif #ifndef MOBILE_DEVICE AVIDump avi; diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index c2de1c7d98..b5b394409f 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -673,7 +673,7 @@ void GameInfoCache::FlushBGs() { iter->second->sndFileData.clear(); iter->second->sndDataLoaded = false; } - iter->second->wantFlags &= ~(GAMEINFO_WANTBG | GAMEINFO_WANTSND); + iter->second->wantFlags &= ~(GAMEINFO_WANTBG | GAMEINFO_WANTSND | GAMEINFO_WANTBGDATA); } } @@ -758,7 +758,9 @@ void GameInfoCache::SetupTexture(GameInfo *info, Draw::DrawContext *thin3d, Game icon.timeLoaded = time_now_d(); } } - icon.data.clear(); - icon.dataLoaded = false; + if ((info->wantFlags & GAMEINFO_WANTBGDATA) == 0) { + icon.data.clear(); + icon.dataLoaded = false; + } } } diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index de3f4db3fa..9191345202 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -53,6 +53,7 @@ enum GameInfoWantFlags { GAMEINFO_WANTBG = 0x01, GAMEINFO_WANTSIZE = 0x02, GAMEINFO_WANTSND = 0x04, + GAMEINFO_WANTBGDATA = 0x08, // Use with WANTBG. }; class FileLoader; diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 5867e0915b..f5710f9034 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -27,6 +27,10 @@ #include "ui/ui_context.h" #include "ui/view.h" #include "ui/viewgroup.h" +#include "Common/FileUtil.h" +#include "Core/Host.h" +#include "Core/Config.h" +#include "Core/System.h" #include "UI/CwCheatScreen.h" #include "UI/EmuScreen.h" #include "UI/GameScreen.h" @@ -36,9 +40,6 @@ #include "UI/MainScreen.h" #include "UI/BackgroundAudio.h" -#include "Core/Host.h" -#include "Core/Config.h" - GameScreen::GameScreen(const std::string &gamePath) : UIDialogScreenWithGameBackground(gamePath) { SetBackgroundAudioGame(gamePath); } @@ -107,27 +108,20 @@ void GameScreen::CreateViews() { rightColumnItems->Add(new Choice(ga->T("Play")))->OnClick.Handle(this, &GameScreen::OnPlay); - if (info) { - btnGameSettings_ = rightColumnItems->Add(new Choice(ga->T("Game Settings"))); - btnGameSettings_->OnClick.Handle(this, &GameScreen::OnGameSettings); - btnDeleteGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Delete Game Config"))); - btnDeleteGameConfig_->OnClick.Handle(this, &GameScreen::OnDeleteConfig); - btnCreateGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Create Game Config"))); - btnCreateGameConfig_->OnClick.Handle(this, &GameScreen::OnCreateConfig); + btnGameSettings_ = rightColumnItems->Add(new Choice(ga->T("Game Settings"))); + btnGameSettings_->OnClick.Handle(this, &GameScreen::OnGameSettings); + btnDeleteGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Delete Game Config"))); + btnDeleteGameConfig_->OnClick.Handle(this, &GameScreen::OnDeleteConfig); + btnCreateGameConfig_ = rightColumnItems->Add(new Choice(ga->T("Create Game Config"))); + btnCreateGameConfig_->OnClick.Handle(this, &GameScreen::OnCreateConfig); - btnGameSettings_->SetVisibility(V_GONE); - btnDeleteGameConfig_->SetVisibility(V_GONE); - btnCreateGameConfig_->SetVisibility(V_GONE); + btnGameSettings_->SetVisibility(V_GONE); + btnDeleteGameConfig_->SetVisibility(V_GONE); + btnCreateGameConfig_->SetVisibility(V_GONE); - btnDeleteSaveData_ = new Choice(ga->T("Delete Save Data")); - rightColumnItems->Add(btnDeleteSaveData_)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); - btnDeleteSaveData_->SetVisibility(V_GONE); - } else { - btnGameSettings_ = nullptr; - btnCreateGameConfig_ = nullptr; - btnDeleteGameConfig_ = nullptr; - btnDeleteSaveData_ = nullptr; - } + btnDeleteSaveData_ = new Choice(ga->T("Delete Save Data")); + rightColumnItems->Add(btnDeleteSaveData_)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); + btnDeleteSaveData_->SetVisibility(V_GONE); if (info && !info->IsPending()) { otherChoices_.clear(); @@ -146,6 +140,10 @@ void GameScreen::CreateViews() { if (g_Config.bEnableCheats) { rightColumnItems->Add(AddOtherChoice(new Choice(pa->T("Cheats"))))->OnClick.Handle(this, &GameScreen::OnCwCheat); } + + btnSetBackground_ = rightColumnItems->Add(new Choice(ga->T("Use UI background"))); + btnSetBackground_->OnClick.Handle(this, &GameScreen::OnSetBackground); + btnSetBackground_->SetVisibility(V_GONE); } UI::Choice *GameScreen::AddOtherChoice(UI::Choice *choice) { @@ -248,6 +246,9 @@ void GameScreen::update() { if (saveDirs.size()) { btnDeleteSaveData_->SetVisibility(UI::V_VISIBLE); } + if (info->pic0.texture || info->pic1.texture) { + btnSetBackground_->SetVisibility(UI::V_VISIBLE); + } } if (!info->IsPending()) { // At this point, the above buttons won't become visible. We can show these now. @@ -376,3 +377,77 @@ UI::EventReturn GameScreen::OnRemoveFromRecent(UI::EventParams &e) { } return UI::EVENT_DONE; } + +class SetBackgroundPopupScreen : public PopupScreen { +public: + SetBackgroundPopupScreen(const std::string &title, const std::string &gamePath); + +protected: + bool FillVertical() const override { return false; } + bool ShowButtons() const override { return false; } + void CreatePopupContents(UI::ViewGroup *parent) override; + void update() override; + +private: + std::string gamePath_; + double timeStart_; + double timeDone_ = 0.0; + + enum class Status { + PENDING, + DELAY, + DONE, + }; + Status status_ = Status::PENDING; +}; + +SetBackgroundPopupScreen::SetBackgroundPopupScreen(const std::string &title, const std::string &gamePath) + : PopupScreen(title), gamePath_(gamePath) { + timeStart_ = time_now_d(); +} + +void SetBackgroundPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { + I18NCategory *ga = GetI18NCategory("Game"); + parent->Add(new UI::TextView(ga->T("One moment please..."), ALIGN_LEFT | ALIGN_VCENTER, false, new UI::LinearLayoutParams(UI::Margins(10, 0, 10, 10)))); +} + +void SetBackgroundPopupScreen::update() { + PopupScreen::update(); + + GameInfo *info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTBGDATA); + if (status_ == Status::PENDING && info && !info->IsPending()) { + GameInfoTex *pic = nullptr; + if (info->pic1.dataLoaded && info->pic1.data.size()) { + pic = &info->pic1; + } else if (info->pic0.dataLoaded && info->pic0.data.size()) { + pic = &info->pic0; + } + + if (pic) { + const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; + writeStringToFile(false, pic->data, bgPng.c_str()); + } + + NativeMessageReceived("bgImage_updated", ""); + + // It's worse if it flickers, stay open for at least 1s. + timeDone_ = timeStart_ + 1.0; + status_ = Status::DELAY; + } + + if (status_ == Status::DELAY && timeDone_ <= time_now_d()) { + TriggerFinish(DR_OK); + status_ = Status::DONE; + } +} + +UI::EventReturn GameScreen::OnSetBackground(UI::EventParams &e) { + I18NCategory *ga = GetI18NCategory("Game"); + // This popup is used to prevent any race condition: + // g_gameInfoCache may take time to load the data, and a crash could happen if they exit before then. + SetBackgroundPopupScreen *pop = new SetBackgroundPopupScreen(ga->T("Setting Background"), gamePath_); + if (e.v) + pop->SetPopupOrigin(e.v); + screenManager()->push(pop); + return UI::EVENT_DONE; +} diff --git a/UI/GameScreen.h b/UI/GameScreen.h index 2d70834d0d..a830dc9db7 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -59,6 +59,7 @@ private: UI::EventReturn OnCreateConfig(UI::EventParams &e); UI::EventReturn OnDeleteConfig(UI::EventParams &e); UI::EventReturn OnCwCheat(UI::EventParams &e); + UI::EventReturn OnSetBackground(UI::EventParams &e); // As we load metadata in the background, we need to be able to update these after the fact. UI::TextureView *texvGameIcon_; @@ -72,6 +73,7 @@ private: UI::Choice *btnCreateGameConfig_; UI::Choice *btnDeleteGameConfig_; UI::Choice *btnDeleteSaveData_; + UI::Choice *btnSetBackground_; std::vector otherChoices_; std::vector saveDirs; }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 2fec1774cb..87be377c97 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -667,6 +667,20 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new CheckBox(&g_Config.bCheckForNewVersion, sy->T("VersionCheck", "Check for new versions of PPSSPP"))); if (g_Config.iMaxRecent > 0) systemSettings->Add(new Choice(sy->T("Clear Recent Games List")))->OnClick.Handle(this, &GameSettingsScreen::OnClearRecents); + + const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; + const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"; + if (File::Exists(bgPng) || File::Exists(bgJpg)) { + backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Clear UI background"))); + } else if (System_GetPropertyInt(SYSPROP_HAS_IMAGE_BROWSER)) { + backgroundChoice_ = systemSettings->Add(new Choice(sy->T("Set UI background..."))); + } else { + backgroundChoice_ = nullptr; + } + if (backgroundChoice_ != nullptr) { + backgroundChoice_->OnClick.Handle(this, &GameSettingsScreen::OnChangeBackground); + } + systemSettings->Add(new Choice(sy->T("Restore Default Settings")))->OnClick.Handle(this, &GameSettingsScreen::OnRestoreDefaultSettings); systemSettings->Add(new CheckBox(&g_Config.bEnableAutoLoad, sy->T("Auto Load Newest Savestate"))); @@ -908,6 +922,29 @@ UI::EventReturn GameSettingsScreen::OnClearRecents(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnChangeBackground(UI::EventParams &e) { + const std::string bgPng = GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"; + const std::string bgJpg = GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"; + if (File::Exists(bgPng) || File::Exists(bgJpg)) { + if (File::Exists(bgPng)) { + File::Delete(bgPng); + } + if (File::Exists(bgJpg)) { + File::Delete(bgJpg); + } + + NativeMessageReceived("bgImage_updated", ""); + } else { + if (System_GetPropertyInt(SYSPROP_HAS_IMAGE_BROWSER)) { + System_SendMessage("bgImage_browse", ""); + } + } + + // Change to a browse or clear button. + RecreateViews(); + return UI::EVENT_DONE; +} + UI::EventReturn GameSettingsScreen::OnReloadCheats(UI::EventParams &e) { // Hmm, strange mechanism. g_Config.bReloadCheats = true; diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 6ed2a8542e..196dd04309 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -47,6 +47,7 @@ private: UI::Choice *layoutEditorChoice_; UI::Choice *postProcChoice_; UI::Choice *displayEditor_; + UI::Choice *backgroundChoice_ = nullptr; UI::PopupMultiChoice *resolutionChoice_; UI::CheckBox *frameSkipAuto_; SettingInfoMessage *settingInfo_; @@ -79,6 +80,7 @@ private: UI::EventReturn OnChangeproAdhocServerAddress(UI::EventParams &e); UI::EventReturn OnChangeMacAddress(UI::EventParams &e); UI::EventReturn OnClearRecents(UI::EventParams &e); + UI::EventReturn OnChangeBackground(UI::EventParams &e); UI::EventReturn OnFullscreenChange(UI::EventParams &e); UI::EventReturn OnDisplayLayoutEditor(UI::EventParams &e); UI::EventReturn OnResolutionChange(UI::EventParams &e); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index a8fbe806ea..eed5eeada3 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -977,11 +977,11 @@ UI::EventReturn MainScreen::OnLoadFile(UI::EventParams &e) { g_Config.Save(); screenManager()->switchScreen(new EmuScreen(fileName.toStdString())); } -#elif PPSSPP_PLATFORM(UWP) - System_SendMessage("browse_file", ""); -#elif defined(USING_WIN_UI) - MainWindow::BrowseAndBoot(""); #endif + + if (System_GetPropertyInt(SYSPROP_HAS_FILE_BROWSER)) { + System_SendMessage("browse_file", ""); + } return UI::EVENT_DONE; } diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 3990a249fd..1e7012ccfe 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -875,6 +875,10 @@ namespace MainWindow BrowseAndBootDone(); break; + case WM_USER_BROWSE_BG_DONE: + BrowseBackgroundDone(); + break; + case WM_MENUSELECT: // Unfortunately, accelerate keys (hotkeys) shares the same enabled/disabled states // with corresponding menu items. diff --git a/Windows/MainWindow.h b/Windows/MainWindow.h index 17d8a8651e..48eb344d38 100644 --- a/Windows/MainWindow.h +++ b/Windows/MainWindow.h @@ -16,6 +16,7 @@ namespace MainWindow enum { WM_USER_SAVESTATE_FINISH = WM_USER + 100, WM_USER_UPDATE_UI = WM_USER + 101, + WM_USER_BROWSE_BG_DONE = WM_USER + 102, WM_USER_WINDOW_TITLE_CHANGED = WM_USER + 103, WM_USER_BROWSE_BOOT_DONE = WM_USER + 104, WM_USER_TOGGLE_FULLSCREEN = WM_USER + 105, @@ -74,4 +75,4 @@ namespace MainWindow void SetWindowSize(int zoom); } -#endif \ No newline at end of file +#endif diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 2b88acc7c1..dc408b7687 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -41,6 +41,7 @@ namespace MainWindow { static const int numCPUs = 1; // what? extern bool noFocusPause; static W32Util::AsyncBrowseDialog *browseDialog; + static W32Util::AsyncBrowseDialog *browseImageDialog; static bool browsePauseAfter; static std::map initialMenuKeys; @@ -399,6 +400,37 @@ namespace MainWindow { browseDialog = 0; } + void BrowseBackground() { + static std::wstring filter = L"All supported images (*.jpg *.png)|*.jpg;*.png|All files (*.*)|*.*||"; + for (size_t i = 0; i < filter.length(); i++) { + if (filter[i] == '|') + filter[i] = '\0'; + } + + W32Util::MakeTopMost(GetHWND(), false); + browseImageDialog = new W32Util::AsyncBrowseDialog(W32Util::AsyncBrowseDialog::OPEN, GetHWND(), WM_USER_BROWSE_BG_DONE, L"LoadFile", L"", filter, L"*.jpg;*.png;"); + } + + void BrowseBackgroundDone() { + std::string filename; + if (browseImageDialog->GetResult(filename)) { + std::wstring src = ConvertUTF8ToWString(filename); + std::wstring dest; + if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".jpg") { + dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.jpg"); + } else { + dest = ConvertUTF8ToWString(GetSysDirectory(DIRECTORY_SYSTEM) + "background.png"); + } + + CopyFileW(src.c_str(), dest.c_str(), FALSE); + NativeMessageReceived("bgImage_updated", ""); + } + + W32Util::MakeTopMost(GetHWND(), g_Config.bTopMost); + + delete browseImageDialog; + browseImageDialog = nullptr; + } static void UmdSwitchAction() { std::string fn; diff --git a/Windows/MainWindowMenu.h b/Windows/MainWindowMenu.h index 03e04baf18..01115aee41 100644 --- a/Windows/MainWindowMenu.h +++ b/Windows/MainWindowMenu.h @@ -9,5 +9,7 @@ namespace MainWindow { void TranslateMenus(HWND hWnd, HMENU menu); void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); void BrowseAndBootDone(); + void BrowseBackground(); + void BrowseBackgroundDone(); void setTexScalingMultiplier(int level); } diff --git a/Windows/main.cpp b/Windows/main.cpp index bf77d68fcb..c84275e0a4 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -213,7 +213,9 @@ int System_GetPropertyInt(SystemProperty prop) { case SYSPROP_DISPLAY_DPI: return ScreenDPI(); case SYSPROP_HAS_FILE_BROWSER: - return true; + return 1; + case SYSPROP_HAS_IMAGE_BROWSER: + return 1; case SYSPROP_HAS_BACK_BUTTON: return 1; default: @@ -235,6 +237,10 @@ void System_SendMessage(const char *command, const char *parameter) { GlobalFree(handle); CloseClipboard(); } + } else if (!strcmp(command, "browse_file")) { + MainWindow::BrowseAndBoot(""); + } else if (!strcmp(command, "bgImage_browse")) { + MainWindow::BrowseBackground(); } } diff --git a/ext/native/base/NativeApp.h b/ext/native/base/NativeApp.h index 6de2e92ed2..1b1191a4a4 100644 --- a/ext/native/base/NativeApp.h +++ b/ext/native/base/NativeApp.h @@ -153,6 +153,7 @@ enum SystemProperty { SYSPROP_GPUDRIVER_VERSION, SYSPROP_HAS_FILE_BROWSER, + SYSPROP_HAS_IMAGE_BROWSER, SYSPROP_HAS_BACK_BUTTON, // Available as Int: