diff --git a/Common/Render/DrawBuffer.cpp b/Common/Render/DrawBuffer.cpp index 9993853d06..ff5af6c8d2 100644 --- a/Common/Render/DrawBuffer.cpp +++ b/Common/Render/DrawBuffer.cpp @@ -412,6 +412,15 @@ void DrawBuffer::DrawImage4Grid(ImageID atlas_image, float x1, float y1, float x float vm = (v2 + v1) * 0.5f; float iw2 = (image->w * 0.5f) * corner_scale; float ih2 = (image->h * 0.5f) * corner_scale; + + // Automatically reduce corner scale radius if it can't fit. + if (iw2 > (x2 - x1) * 0.5f) { + iw2 = (x2 - x1) * 0.5f; + } + if (ih2 > (y2 - y1) * 0.5f) { + ih2 = (y2 - y1) * 0.5f; + } + float xa = x1 + iw2; float xb = x2 - iw2; float ya = y1 + ih2; diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index 9d1ef32547..4ce85a7f6f 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -113,7 +113,7 @@ std::string SanitizeString(std::string_view input, StringRestriction restriction std::string sanitized; sanitized.reserve(input.size()); bool lastWasLineBreak = false; - for (auto c : input) { + for (char c : input) { switch (restriction) { case StringRestriction::None: sanitized.push_back(c); @@ -127,15 +127,15 @@ std::string SanitizeString(std::string_view input, StringRestriction restriction } break; case StringRestriction::NoLineBreaksOrSpecials: - if (c >= 32) { + if ((uint8_t)c >= 32) { sanitized.push_back(c); lastWasLineBreak = false; } else if (c == 10 || c == 13) { // Collapse line breaks/feeds to single spaces. if (!lastWasLineBreak) { sanitized.push_back(' '); + lastWasLineBreak = true; } - lastWasLineBreak = true; } } } diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index d72d21ce54..eb2cf44b76 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -61,19 +61,22 @@ void PopupContextMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { auto category = GetI18NCategory(category_); for (size_t i = 0; i < itemCount_; i++) { + Choice *choice; if (items_[i].imageID) { - Choice *choice = new Choice(category->T(items_[i].text), ImageID(items_[i].imageID)); - parent->Add(choice); - if (enabled_[i]) { - choice->OnClick.Add([=](EventParams &p) { - TriggerFinish(DR_OK); - p.a = (uint32_t)i; - OnChoice.Dispatch(p); - return EVENT_DONE; - }); - } else { - choice->SetEnabled(false); - } + choice = new Choice(category->T(items_[i].text), ImageID(items_[i].imageID)); + } else { + choice = new Choice(category->T(items_[i].text)); + } + parent->Add(choice); + if (enabled_[i]) { + choice->OnClick.Add([=](EventParams &p) { + TriggerFinish(DR_OK); + p.a = (uint32_t)i; + OnChoice.Dispatch(p); + return EVENT_DONE; + }); + } else { + choice->SetEnabled(false); } } diff --git a/Common/UI/Screen.cpp b/Common/UI/Screen.cpp index 6fe0ffc7ba..f8e69e73c4 100644 --- a/Common/UI/Screen.cpp +++ b/Common/UI/Screen.cpp @@ -273,8 +273,10 @@ void ScreenManager::sendMessage(UIMessage message, const char *value) { backgroundScreen_->sendMessage(message, value); } - if (!stack_.empty()) { - stack_.back().screen->sendMessage(message, value); + // NOTE: Changed this to send the message to all screens, instead of just the top one, + // to allow EmuScreen to receive messages from popup menus. Hope this didn't break anything.. + for (auto &iter : stack_) { + iter.screen->sendMessage(message, value); } } diff --git a/Common/UI/Screen.h b/Common/UI/Screen.h index 478680da03..d81412c8e1 100644 --- a/Common/UI/Screen.h +++ b/Common/UI/Screen.h @@ -165,7 +165,6 @@ public: bool key(const KeyInput &key); void axis(const AxisInput *axes, size_t count); - // Generic facility for gross hacks :P void sendMessage(UIMessage message, const char *value); const Screen *topScreen() const { diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 4e7f3f12eb..9b62d3a733 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -164,7 +164,7 @@ void ViewGroup::Draw(UIContext &dc) { if (hasDropShadow_) { // Darken things behind. dc.FillRect(UI::Drawable(0x60000000), dc.GetBounds().Expand(dropShadowExpand_)); - float dropsize = 30.0f; + const float dropsize = 30.0f; dc.Draw()->DrawImage4Grid(dc.theme->dropShadow4Grid, bounds_.x - dropsize, bounds_.y, bounds_.x2() + dropsize, bounds_.y2()+dropsize*1.5f, 0xDF000000, 3.0f); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index ed16d32c1e..8c469dfd87 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -570,6 +570,8 @@ static void NotifyUserIfSlow() { } void __DisplayFlip(int cyclesLate) { + _dbg_assert_(gpu); + __DisplaySetFramerate(); flippedThisFrame = true; @@ -585,21 +587,19 @@ void __DisplayFlip(int cyclesLate) { bool fastForwardSkipFlip = g_Config.iFastForwardMode != (int)FastForwardMode::CONTINUOUS; - if (gpu) { - Draw::DrawContext *draw = gpu->GetDrawContext(); - - if (draw) { - g_frameTiming.presentMode = ComputePresentMode(draw, &g_frameTiming.presentInterval); - if (!draw->GetDeviceCaps().presentInstantModeChange && g_frameTiming.presentMode == Draw::PresentMode::FIFO) { - // Some backends can't just flip into MAILBOX/IMMEDIATE mode instantly. - // Vulkan doesn't support the interval setting, so we force skipping the flip. - // TODO: We'll clean this up in a more backend-independent way later. - fastForwardSkipFlip = true; - } - } else { - g_frameTiming.presentMode = Draw::PresentMode::FIFO; - g_frameTiming.presentInterval = 1; + Draw::DrawContext *draw = gpu->GetDrawContext(); + if (draw) { + g_frameTiming.presentMode = ComputePresentMode(draw, &g_frameTiming.presentInterval); + if (!draw->GetDeviceCaps().presentInstantModeChange && g_frameTiming.presentMode == Draw::PresentMode::FIFO) { + // Some backends can't just flip into MAILBOX/IMMEDIATE mode instantly. + // Vulkan doesn't support the interval setting, so we force skipping the flip. + // TODO: We'll clean this up in a more backend-independent way later. + fastForwardSkipFlip = true; } + } else { + // Surely can never get here? + g_frameTiming.presentMode = Draw::PresentMode::FIFO; + g_frameTiming.presentInterval = 1; } if (!g_Config.bSkipBufferEffects) { diff --git a/Core/System.cpp b/Core/System.cpp index e82bc954d5..b3e2b14acb 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -218,8 +218,6 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileTy g_symbolMap = new SymbolMap(); - g_lua.Init(); - // Default memory settings // Seems to be the safest place currently.. Memory::g_MemorySize = Memory::RAM_NORMAL_SIZE; // 32 MB of ram by default @@ -233,6 +231,8 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileTy MIPSAnalyst::Reset(); Replacement_Init(); + g_lua.Init(); + bool allowPlugins = true; std::string geDumpDiscID; @@ -242,6 +242,7 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileTy case IdentifiedFileType::PSP_DISC_DIRECTORY: if (!MountGameISO(loadedFile)) { *errorString = "Failed to mount ISO file - invalid format?"; + CPU_Shutdown(); return false; } if (LoadParamSFOFromDisc()) { @@ -271,8 +272,10 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileTy allowPlugins = false; break; default: - // Can we even get here? + // Can we even get here? Yes, by drag-dropping files while a game is underway. + // Should check earlier. ERROR_LOG(Log::Loader, "CPU_Init didn't recognize file. %s", errorString->c_str()); + CPU_Shutdown(); return false; } @@ -285,6 +288,7 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileTy if (!Memory::Init()) { // We're screwed. *errorString = "Memory init failed"; + CPU_Shutdown(); return false; } diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index c09638243d..b7ed910e71 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -325,7 +325,7 @@ void DisplayLayoutScreen::CreateViews() { settingsVisible_.resize(g_Config.vPostShaderNames.size()); } - static ContextMenuItem postShaderContextMenu[] = { + static const ContextMenuItem postShaderContextMenu[] = { { "Move Up", "I_ARROW_UP" }, { "Move Down", "I_ARROW_DOWN" }, { "Remove", "I_TRASHCAN" }, diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 5fe70d987c..b09662dfc2 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -255,7 +255,6 @@ void EmuScreen::bootGame(const Path &filename) { return; if (PSP_IsInited()) { bootPending_ = false; - invalid_ = false; bootComplete(); return; } @@ -266,8 +265,7 @@ void EmuScreen::bootGame(const Path &filename) { bootPending_ = !PSP_InitUpdate(&error_string); if (!bootPending_) { - invalid_ = !PSP_IsInited(); - if (invalid_) { + if (!PSP_IsInited()) { errorMessage_ = error_string; ERROR_LOG(Log::Boot, "isIniting bootGame error: %s", errorMessage_.c_str()); return; @@ -283,8 +281,6 @@ void EmuScreen::bootGame(const Path &filename) { if (!bootAllowStorage(filename)) return; - invalid_ = true; - // We don't want to boot with the wrong game specific config, so wait until info is ready. std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, filename, GameInfoFlags::PARAM_SFO); if (!info->Ready(GameInfoFlags::PARAM_SFO)) { @@ -362,7 +358,6 @@ void EmuScreen::bootGame(const Path &filename) { std::string error_string; if (!PSP_InitStart(coreParam, &error_string)) { bootPending_ = false; - invalid_ = true; errorMessage_ = error_string; ERROR_LOG(Log::Boot, "InitStart bootGame error: %s", errorMessage_.c_str()); } @@ -463,7 +458,7 @@ EmuScreen::~EmuScreen() { g_Config.TimeTracker().Stop(gameID); // If we were invalid, it would already be shutdown. - if (!invalid_ || bootPending_) { + if (!bootPending_) { PSP_Shutdown(); } @@ -552,12 +547,10 @@ void EmuScreen::sendMessage(UIMessage message, const char *value) { PSP_Shutdown(); bootPending_ = false; stopRender_ = true; - invalid_ = true; System_Notify(SystemNotification::DISASSEMBLY); } else if (message == UIMessage::REQUEST_GAME_RESET) { PSP_Shutdown(); bootPending_ = true; - invalid_ = true; System_Notify(SystemNotification::DISASSEMBLY); std::string resetError; @@ -1372,12 +1365,6 @@ void EmuScreen::update() { } } - if (bootPending_) { - // Keep trying the boot until bootPending_ is lifted. - // It may be delayed due to RetroAchievements or any other cause. - bootGame(gamePath_); - } - // Simply forcibly update to the current screen size every frame. Doesn't cost much. // If bounds is set to be smaller than the actual pixel resolution of the display, respect that. // TODO: Should be able to use g_dpi_scale here instead. Might want to store the dpi scale in the UI context too. @@ -1388,7 +1375,7 @@ void EmuScreen::update() { PSP_CoreParameter().pixelHeight = g_display.pixel_yres * bounds.h / g_display.dp_yres; #endif - if (!invalid_) { + if (PSP_IsInited()) { UpdateUIState(coreState != CORE_RUNTIME_ERROR ? UISTATE_INGAME : UISTATE_EXCEPTION); } @@ -1410,7 +1397,7 @@ void EmuScreen::update() { screenManager()->push(new GamePauseScreen(gamePath_)); } - if (invalid_) + if (!PSP_IsInited()) return; double now = time_now_d(); @@ -1451,7 +1438,6 @@ void EmuScreen::update() { bool EmuScreen::checkPowerDown() { if (PSP_IsRebooting()) { bootPending_ = true; - invalid_ = true; } if (coreState == CORE_POWERDOWN && !PSP_IsIniting() && !PSP_IsRebooting()) { @@ -1463,7 +1449,6 @@ bool EmuScreen::checkPowerDown() { INFO_LOG(Log::System, "SELF-POWERDOWN!"); screenManager()->switchScreen(new MainScreen()); bootPending_ = false; - invalid_ = true; return shutdown; } return false; @@ -1481,7 +1466,7 @@ ScreenRenderRole EmuScreen::renderRole(bool isTop) const { return false; } - if (invalid_) { + if (!PSP_IsInited() && !bootPending_) { return false; } @@ -1507,6 +1492,14 @@ void EmuScreen::darken() { } ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { + // Moved from update, because we want it to be possible for booting to happen even when the screen + // is in the background, like when choosing Reset from the pause menu. + if (bootPending_) { + // Keep trying the boot until bootPending_ is lifted. + // It may be delayed due to RetroAchievements or any other cause. + bootGame(gamePath_); + } + ScreenRenderFlags flags = ScreenRenderFlags::NONE; Draw::Viewport viewport{ 0.0f, 0.0f, (float)g_display.pixel_xres, (float)g_display.pixel_yres, 0.0f, 1.0f }; using namespace Draw; @@ -1565,7 +1558,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { gpu->CopyDisplayToOutput(true); PSP_EndHostFrame(); } - if (gpu->PresentedThisFrame()) { + if (gpu && gpu->PresentedThisFrame()) { framebufferBound = true; } if (!framebufferBound) { @@ -1589,7 +1582,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { return flags; } - if (invalid_) { + if (!PSP_IsInited()) { // It's possible this might be set outside PSP_RunLoopFor(). // In this case, we need to double check it here. if (mode & ScreenRenderMode::TOP) { @@ -1883,7 +1876,7 @@ void EmuScreen::renderUI() { root_->Draw(*ctx); } - if (!invalid_) { + if (!PSP_IsInited()) { if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::CONTROL) { DrawControlMapperOverlay(ctx, ctx->GetLayoutBounds(), controlMapper_); } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 7916317d04..0e47051942 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -100,8 +100,6 @@ private: bool bootPending_ = true; Path gamePath_; - // Something invalid was loaded, don't try to emulate - bool invalid_ = true; bool quit_ = false; bool stopRender_ = false; std::string errorMessage_; diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index fea0d3cd92..bc2f7a4200 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -540,6 +540,40 @@ void GamePauseScreen::CreateViews() { return UI::EVENT_DONE; }); + Button *menuButton = middleColumn->Add(new Button("", ImageID("I_THREE_DOTS"), new LinearLayoutParams(64, 64))); + + menuButton->OnClick.Add([this, menuButton](UI::EventParams &e) { + static const ContextMenuItem ingameContextMenu[] = { + { "Reset" }, + }; + PopupContextMenuScreen *contextMenu = new UI::PopupContextMenuScreen(ingameContextMenu, ARRAY_SIZE(ingameContextMenu), I18NCat::DIALOG, menuButton); + screenManager()->push(contextMenu); + contextMenu->OnChoice.Add([=](EventParams &e) -> UI::EventReturn { + switch (e.a) { + case 0: // Reset + { + std::string confirmMessage = GetConfirmExitMessage(); + if (!confirmMessage.empty()) { + auto di = GetI18NCategory(I18NCat::DIALOG); + screenManager()->push(new PromptScreen(gamePath_, confirmMessage, di->T("Reset"), di->T("Cancel"), [=](bool result) { + if (result) { + System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); + } + })); + } else { + System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); + break; + } + } + default: + break; + } + return UI::EVENT_DONE; + }); + + return UI::EVENT_DONE; + }); + // What's this for? rightColumnHolder->Add(new Spacer(10.0f)); } @@ -561,9 +595,10 @@ void GamePauseScreen::dialogFinished(const Screen *dialog, DialogResult dr) { } else { if (tag == "Game") { g_BackgroundAudio.SetGame(Path()); + } else if (tag != "ContextMenuPopup") { + // There may have been changes to our savestates, so let's recreate. + RecreateViews(); } - // There may have been changes to our savestates, so let's recreate. - RecreateViews(); } } @@ -609,7 +644,6 @@ std::string GetConfirmExitMessage() { // No need to ask for this type of confirmation for dumps. return confirmMessage; } - auto di = GetI18NCategory(I18NCat::DIALOG); confirmMessage = ApplySafeSubstitutions(di->T("You haven't saved your progress for %1."), NiceTimeFormat((int)unsavedSeconds)); confirmMessage += '\n'; diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 561c40f5fc..9f01bfaf2d 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -825,7 +825,7 @@ namespace MainWindow }; } - bool ConfirmExit(HWND hWnd) { + bool ConfirmAction(HWND hWnd, bool actionIsReset) { const GlobalUIState state = GetUIState(); if (state == UISTATE_MENU || state == UISTATE_EXIT) { return true; @@ -837,8 +837,12 @@ namespace MainWindow } auto di = GetI18NCategory(I18NCat::DIALOG); auto mm = GetI18NCategory(I18NCat::MAINMENU); - confirmExitMessage += '\n'; - confirmExitMessage += di->T("Are you sure you want to exit?"); + if (!actionIsReset) { + confirmExitMessage += '\n'; + confirmExitMessage += di->T("Are you sure you want to exit?"); + } else { + // Reset is bit rarer, let's just omit the extra message for now. + } return IDYES == MessageBox(hWnd, ConvertUTF8ToWString(confirmExitMessage).c_str(), ConvertUTF8ToWString(mm->T("Exit")).c_str(), MB_YESNO | MB_ICONQUESTION); } @@ -1121,7 +1125,7 @@ namespace MainWindow case WM_CLOSE: { - if (ConfirmExit(hWnd)) { + if (ConfirmAction(hWnd, false)) { DestroyWindow(hWnd); } return 0; diff --git a/Windows/MainWindow.h b/Windows/MainWindow.h index f27a5c2ec6..64f93d2b0a 100644 --- a/Windows/MainWindow.h +++ b/Windows/MainWindow.h @@ -83,7 +83,8 @@ namespace MainWindow void SetWindowSize(int zoom); void RunCallbackInWndProc(void (*callback)(void *window, void *userdata), void *userdata); void SetKeepScreenBright(bool keepBright); - bool ConfirmExit(HWND hWnd); + + bool ConfirmAction(HWND hWnd, bool actionIsReset); } #endif diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 1697ee288c..ccbda4e0f4 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -513,7 +513,9 @@ namespace MainWindow { break; case ID_EMULATION_RESET: - System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); + if (MainWindow::ConfirmAction(hWnd, true)) { + System_PostUIMessage(UIMessage::REQUEST_GAME_RESET); + } break; case ID_EMULATION_SWITCH_UMD: @@ -756,7 +758,7 @@ namespace MainWindow { case ID_OPTIONS_FRAMESKIPTYPE_PRCNT: setFrameSkippingType(FRAMESKIPTYPE_PRCNT); break; case ID_FILE_EXIT: - if (MainWindow::ConfirmExit(hWnd)) { + if (MainWindow::ConfirmAction(hWnd, false)) { DestroyWindow(hWnd); } break;