CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Dialog/PSPMsgDialog.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include "Common/Serialize/Serializer.h"19#include "Common/Serialize/SerializeFuncs.h"20#include "Common/StringUtils.h"21#include "Core/Dialog/PSPMsgDialog.h"22#include "Core/Dialog/PSPSaveDialog.h"23#include "Core/Util/PPGeDraw.h"24#include "Core/HLE/sceCtrl.h"25#include "Core/MemMapHelpers.h"26#include "Core/Reporting.h"27#include "Common/Data/Text/I18n.h"28#include "Common/Data/Encoding/Utf8.h"2930static const float FONT_SCALE = 0.65f;3132// These are rough, it seems to take a long time to init, and probably depends on threads.33// TODO: This takes like 700ms on a PSP but that's annoyingly long.34const static int MSG_INIT_DELAY_US = 300000;35const static int MSG_SHUTDOWN_DELAY_US = 26000;3637PSPMsgDialog::PSPMsgDialog(UtilityDialogType type) : PSPDialog(type) {38}3940PSPMsgDialog::~PSPMsgDialog() {41}4243int PSPMsgDialog::Init(unsigned int paramAddr) {44// Ignore if already running45if (GetStatus() != SCE_UTILITY_STATUS_NONE) {46ERROR_LOG_REPORT(Log::sceUtility, "sceUtilityMsgDialogInitStart: invalid status");47return SCE_ERROR_UTILITY_INVALID_STATUS;48}4950messageDialogAddr = paramAddr;51if (!Memory::IsValidAddress(messageDialogAddr))52{53return 0;54}55int size = Memory::Read_U32(paramAddr);56memset(&messageDialog,0,sizeof(messageDialog));57// Only copy the right size to support different request format58Memory::Memcpy(&messageDialog,paramAddr,size);5960// debug info61int optionsNotCoded = messageDialog.options & ~SCE_UTILITY_MSGDIALOG_OPTION_SUPPORTED;62if(optionsNotCoded)63{64ERROR_LOG_REPORT(Log::sceUtility, "PSPMsgDialog options not coded : 0x%08x", optionsNotCoded);65}6667flag = 0;68scrollPos_ = 0.0f;69framesUpHeld_ = 0;70framesDownHeld_ = 0;7172// Check request invalidity73if(messageDialog.type == 0 && !(messageDialog.errorNum & 0x80000000))74{75flag |= DS_ERROR;76messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_ERRORCODEINVALID;77}78else if(size == SCE_UTILITY_MSGDIALOG_SIZE_V2 && messageDialog.type == 1)79{80unsigned int validOp = SCE_UTILITY_MSGDIALOG_OPTION_TEXTSOUND |81SCE_UTILITY_MSGDIALOG_OPTION_YESNO |82SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO;83if (((messageDialog.options | validOp) ^ validOp) != 0)84{85flag |= DS_ERROR;86messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;87}88}89else if(size == SCE_UTILITY_MSGDIALOG_SIZE_V3)90{91if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO) &&92!(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO))93{94flag |= DS_ERROR;95messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;96}97if (messageDialog.options & ~SCE_UTILITY_MSGDIALOG_OPTION_SUPPORTED)98{99flag |= DS_ERROR;100messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;101}102}103104if(flag == 0)105{106yesnoChoice = 1;107if(messageDialog.type == 1)108flag |= DS_MSG;109if(messageDialog.type == 0)110flag |= DS_ERRORMSG;111if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO) &&112((size == SCE_UTILITY_MSGDIALOG_SIZE_V3) ||113(size == SCE_UTILITY_MSGDIALOG_SIZE_V2 && messageDialog.type == 1)))114flag |= DS_YESNO;115if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO)116{117yesnoChoice = 0;118flag |= DS_DEFNO;119}120if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_OK) && (size == SCE_UTILITY_MSGDIALOG_SIZE_V3))121{122yesnoChoice = 1;123flag |= DS_OK;124}125if((flag & DS_YESNO) || (flag & DS_OK))126flag |= DS_VALIDBUTTON;127if(!((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_NOCANCEL) && (size == SCE_UTILITY_MSGDIALOG_SIZE_V3)))128flag |= DS_CANCELBUTTON;129if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_NOSOUND)130flag |= DS_NOSOUND;131}132133if (flag & DS_ERRORMSG) {134FormatErrorCode(messageDialog.errorNum);135} else {136truncate_cpy(msgText, messageDialog.string);137}138139ChangeStatusInit(MSG_INIT_DELAY_US);140141UpdateButtons();142InitCommon();143StartFade(true);144return 0;145}146147148void PSPMsgDialog::FormatErrorCode(uint32_t code) {149auto err = GetI18NCategory(I18NCat::DIALOG);150151switch (code) {152case SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN:153snprintf(msgText, 512, "%s (%08x)", err->T_cstr("MsgErrorSavedataDataBroken", "Save data was corrupt."), code);154break;155156case SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_MS:157case SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK:158case SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS:159case SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_MS:160case SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_MS:161snprintf(msgText, 512, "%s (%08x)", err->T_cstr("MsgErrorSavedataNoMS", "Memory stick not inserted."), code);162break;163164case SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA:165case SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA:166case SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA:167case SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA:168snprintf(msgText, 512, "%s (%08x)", err->T_cstr("MsgErrorSavedataNoData", "Warning: no save data was found."), code);169break;170171case SCE_UTILITY_SAVEDATA_ERROR_RW_MEMSTICK_FULL:172case SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE:173snprintf(msgText, 512, "%s (%08x)", err->T_cstr("MsgErrorSavedataMSFull", "Memory stick full. Check your storage space."), code);174break;175176default:177snprintf(msgText, 512, "%s %08x", err->T_cstr("MsgErrorCode", "Error code:"), code);178}179}180181void PSPMsgDialog::DisplayMessage(const std::string &text, bool hasYesNo, bool hasOK) {182auto di = GetI18NCategory(I18NCat::DIALOG);183184PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_CENTER, FONT_SCALE);185PPGeStyle messageStyle = FadedStyle(PPGeAlign::BOX_HCENTER, FONT_SCALE);186187// Without the scrollbar, we have 390 total pixels.188float WRAP_WIDTH = 340.0f;189if ((size_t)UTF8StringNonASCIICount(text) >= text.size() / 4) {190WRAP_WIDTH = 376.0f;191if (text.size() > 12) {192messageStyle.scale = 0.6f;193}194}195196float totalHeight = 0.0f;197PPGeMeasureText(nullptr, &totalHeight, text, FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);198// The PSP normally only shows about 8 lines at a time.199// For improved UX, we intentionally show part of the next line.200float visibleHeight = std::min(totalHeight, 175.0f);201float h2 = visibleHeight / 2.0f;202203float centerY = 135.0f;204float sy = centerY - h2 - 15.0f;205float ey = centerY + h2 + 20.0f;206float buttonY = centerY + h2 + 5.0f;207208auto drawSelectionBoxAndAdjust = [&](float x) {209// Box has a fixed size.210float w = 15.0f;211float h = 8.0f;212PPGeDrawRect(x - w, buttonY - h, x + w, buttonY + h, CalcFadedColor(0x6DCFCFCF));213214centerY -= h + 5.0f;215sy -= h + 5.0f;216ey = buttonY + h * 2.0f + 5.0f;217};218219if (hasYesNo) {220if (yesnoChoice == 1) {221drawSelectionBoxAndAdjust(204.0f);222} else {223drawSelectionBoxAndAdjust(273.0f);224}225226PPGeDrawText(di->T("Yes"), 203.0f, buttonY - 1.0f, buttonStyle);227PPGeDrawText(di->T("No"), 272.0f, buttonY - 1.0f, buttonStyle);228if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {229yesnoChoice = 1;230} else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {231yesnoChoice = 0;232}233buttonY += 8.0f + 5.0f;234}235236if (hasOK) {237drawSelectionBoxAndAdjust(240.0f);238239PPGeDrawText(di->T("OK"), 239.0f, buttonY - 1.0f, buttonStyle);240buttonY += 8.0f + 5.0f;241}242243PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2));244PPGeDrawTextWrapped(text.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle);245PPGeScissorReset();246247// Do we need a scrollbar?248if (visibleHeight < totalHeight) {249float scrollSpeed = 5.0f;250float scrollMax = totalHeight - visibleHeight;251252float bobHeight = (visibleHeight / totalHeight) * visibleHeight;253float bobOffset = (scrollPos_ / scrollMax) * (visibleHeight - bobHeight);254float bobY1 = centerY - h2 + bobOffset;255PPGeDrawRect(435.0f, bobY1, 440.0f, bobY1 + bobHeight, CalcFadedColor(0xFFCCCCCC));256257auto buttonDown = [this](int btn, int &held) {258if (IsButtonPressed(btn)) {259held = 0;260return true;261}262return IsButtonHeld(btn, held, 1, 1);263};264if (buttonDown(CTRL_DOWN, framesDownHeld_) && scrollPos_ < scrollMax) {265scrollPos_ = std::min(scrollMax, scrollPos_ + scrollSpeed);266}267if (buttonDown(CTRL_UP, framesUpHeld_) && scrollPos_ > 0.0f) {268scrollPos_ = std::max(0.0f, scrollPos_ - scrollSpeed);269}270}271272PPGeDrawRect(40.0f, sy, 440.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));273PPGeDrawRect(40.0f, ey, 440.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));274}275276int PSPMsgDialog::Update(int animSpeed) {277if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {278return SCE_ERROR_UTILITY_INVALID_STATUS;279}280281if (flag & (DS_ERROR | DS_ABORT)) {282ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);283} else {284UpdateButtons();285UpdateCommon();286UpdateFade(animSpeed);287288StartDraw();289// white -> RGB(168,173,189), black -> RGB(129,134,150)290// (255 - a) + (x * a / 255) = 173, x * a / 255 = 134291// a = 255 - w + b = 158, x = b * 255 / a = ?292// but is not drawn using x * a + y * (255 - a) here?293//PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x9EF2D8D0));294PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));295296if ((flag & DS_MSG) || (flag & DS_ERRORMSG))297DisplayMessage(msgText, (flag & DS_YESNO) != 0, (flag & DS_OK) != 0);298299if (flag & (DS_OK | DS_VALIDBUTTON))300DisplayButtons(DS_BUTTON_OK, messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ? messageDialog.okayButton : "");301302if (flag & DS_CANCELBUTTON)303DisplayButtons(DS_BUTTON_CANCEL, messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ? messageDialog.cancelButton : "");304305if (IsButtonPressed(cancelButtonFlag) && (flag & DS_CANCELBUTTON))306{307if(messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ||308((messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V2) && (flag & DS_YESNO)))309messageDialog.buttonPressed = 3;310else311messageDialog.buttonPressed = 0;312StartFade(false);313}314else if (IsButtonPressed(okButtonFlag) && (flag & DS_VALIDBUTTON))315{316if (yesnoChoice == 0)317{318messageDialog.buttonPressed = 2;319}320else321{322messageDialog.buttonPressed = 1;323}324StartFade(false);325}326327328EndDraw();329330messageDialog.result = 0;331}332333Memory::Memcpy(messageDialogAddr, &messageDialog, messageDialog.common.size, "MsgDialogParam");334return 0;335}336337int PSPMsgDialog::Abort() {338// Katekyoushi Hitman Reborn! Battle Arena expects this to fail when not running.339if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {340return SCE_ERROR_UTILITY_INVALID_STATUS;341} else {342// Status is not actually changed until Update().343flag |= DS_ABORT;344return 0;345}346}347348int PSPMsgDialog::Shutdown(bool force) {349if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)350return SCE_ERROR_UTILITY_INVALID_STATUS;351352PSPDialog::Shutdown(force);353if (!force) {354ChangeStatusShutdown(MSG_SHUTDOWN_DELAY_US);355}356357return 0;358}359360void PSPMsgDialog::DoState(PointerWrap &p)361{362PSPDialog::DoState(p);363364auto s = p.Section("PSPMsgDialog", 1);365if (!s)366return;367368Do(p, flag);369Do(p, messageDialog);370Do(p, messageDialogAddr);371DoArray(p, msgText, sizeof(msgText));372Do(p, yesnoChoice);373374// We don't save state this, you'll just have to scroll down again.375if (p.mode == p.MODE_READ) {376scrollPos_ = 0.0f;377framesUpHeld_ = 0;378framesDownHeld_ = 0;379}380}381382pspUtilityDialogCommon *PSPMsgDialog::GetCommonParam()383{384return &messageDialog.common;385}386387388