1
// Copyright (c) 2012- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18
#include "Core/Dialog/PSPMsgDialog.h"
19
#include "Core/Util/PPGeDraw.h"
20
#include "Core/HLE/sceCtrl.h"
21
#include "Core/MemMapHelpers.h"
22
#include "Core/Reporting.h"
23
#include "Common/ChunkFile.h"
24
#include "i18n/i18n.h"
25
#include "util/text/utf8.h"
27
static const float FONT_SCALE = 0.65f;
29
// These are rough, it seems to take a long time to init, and probably depends on threads.
30
// TODO: This takes like 700ms on a PSP but that's annoyingly long.
31
const static int MSG_INIT_DELAY_US = 300000;
32
const static int MSG_SHUTDOWN_DELAY_US = 26000;
34
PSPMsgDialog::PSPMsgDialog()
40
PSPMsgDialog::~PSPMsgDialog() {
43
int PSPMsgDialog::Init(unsigned int paramAddr) {
44
// Ignore if already running
45
if (GetStatus() != SCE_UTILITY_STATUS_NONE) {
46
ERROR_LOG_REPORT(SCEUTILITY, "sceUtilityMsgDialogInitStart: invalid status");
50
messageDialogAddr = paramAddr;
51
if (!Memory::IsValidAddress(messageDialogAddr))
55
int size = Memory::Read_U32(paramAddr);
56
memset(&messageDialog,0,sizeof(messageDialog));
57
// Only copy the right size to support different request format
58
Memory::Memcpy(&messageDialog,paramAddr,size);
61
int optionsNotCoded = messageDialog.options & ~SCE_UTILITY_MSGDIALOG_OPTION_SUPPORTED;
64
ERROR_LOG_REPORT(SCEUTILITY, "PSPMsgDialog options not coded : 0x%08x", optionsNotCoded);
69
// Check request invalidity
70
if(messageDialog.type == 0 && !(messageDialog.errorNum & 0x80000000))
73
messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_ERRORCODEINVALID;
75
else if(size == SCE_UTILITY_MSGDIALOG_SIZE_V2 && messageDialog.type == 1)
77
unsigned int validOp = SCE_UTILITY_MSGDIALOG_OPTION_TEXTSOUND |
78
SCE_UTILITY_MSGDIALOG_OPTION_YESNO |
79
SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO;
80
if (((messageDialog.options | validOp) ^ validOp) != 0)
83
messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;
86
else if(size == SCE_UTILITY_MSGDIALOG_SIZE_V3)
88
if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO) &&
89
!(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO))
92
messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;
94
if (messageDialog.options & ~SCE_UTILITY_MSGDIALOG_OPTION_SUPPORTED)
97
messageDialog.result = SCE_UTILITY_MSGDIALOG_ERROR_BADOPTION;
104
if(messageDialog.type == 1)
106
if(messageDialog.type == 0)
108
if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO) &&
109
((size == SCE_UTILITY_MSGDIALOG_SIZE_V3) ||
110
(size == SCE_UTILITY_MSGDIALOG_SIZE_V2 && messageDialog.type == 1)))
112
if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO)
117
if((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_OK) && (size == SCE_UTILITY_MSGDIALOG_SIZE_V3))
122
if((flag & DS_YESNO) || (flag & DS_OK))
123
flag |= DS_VALIDBUTTON;
124
if(!((messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_NOCANCEL) && (size == SCE_UTILITY_MSGDIALOG_SIZE_V3)))
125
flag |= DS_CANCELBUTTON;
126
if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_NOSOUND)
130
if (flag & DS_ERRORMSG) {
131
snprintf(msgText, 512, "Error code: %08x", messageDialog.errorNum);
133
strncpy(msgText, messageDialog.string, 512);
136
ChangeStatusInit(MSG_INIT_DELAY_US);
143
void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo, bool hasOK)
145
float WRAP_WIDTH = 300.0f;
146
if (UTF8StringNonASCIICount(text.c_str()) > 3)
152
PPGeMeasureText(0, &h, &n, text.c_str(), FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);
153
float h2 = h * n / 2.0f;
158
I18NCategory *di = GetI18NCategory("Dialog");
159
const char *choiceText;
160
u32 yesColor, noColor;
162
if (yesnoChoice == 1) {
163
choiceText = di->T("Yes");
165
yesColor = 0xFFFFFFFF;
166
noColor = 0xFFFFFFFF;
169
choiceText = di->T("No");
171
yesColor = 0xFFFFFFFF;
172
noColor = 0xFFFFFFFF;
174
PPGeMeasureText(&w, &h, 0, choiceText, FONT_SCALE);
177
float y2 = y + h2 + 8.0f;
180
PPGeDrawRect(x - w, y2 - h, x + w, y2 + h, CalcFadedColor(0x6DCFCFCF));
181
PPGeDrawText(di->T("Yes"), 204.0f, y2 + 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000));
182
PPGeDrawText(di->T("Yes"), 203.0f, y2 - 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(yesColor));
183
PPGeDrawText(di->T("No"), 273.0f, y2 + 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000));
184
PPGeDrawText(di->T("No"), 272.0f, y2 - 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(noColor));
185
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {
188
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {
195
I18NCategory *di = GetI18NCategory("Dialog");
200
float y2 = y + h2 + 8.0f;
203
PPGeDrawRect(x - w, y2 - h, x + w, y2 + h, CalcFadedColor(0x6DCFCFCF));
204
PPGeDrawText(di->T("OK"), 240.0f, y2 + 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000));
205
PPGeDrawText(di->T("OK"), 239.0f, y2 - 1.0f, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
209
PPGeDrawTextWrapped(text.c_str(), 241.0f, y+2, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0x80000000));
210
PPGeDrawTextWrapped(text.c_str(), 240.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
212
PPGeDrawRect(40.0f, sy, 440.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));
213
PPGeDrawRect(40.0f, ey, 440.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));
216
int PSPMsgDialog::Update(int animSpeed) {
217
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
218
return SCE_ERROR_UTILITY_INVALID_STATUS;
221
if (flag & (DS_ERROR | DS_ABORT)) {
222
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
225
UpdateFade(animSpeed);
227
okButtonImg = I_CIRCLE;
228
cancelButtonImg = I_CROSS;
229
okButtonFlag = CTRL_CIRCLE;
230
cancelButtonFlag = CTRL_CROSS;
231
if (messageDialog.common.buttonSwap == 1)
233
okButtonImg = I_CROSS;
234
cancelButtonImg = I_CIRCLE;
235
okButtonFlag = CTRL_CROSS;
236
cancelButtonFlag = CTRL_CIRCLE;
240
// white -> RGB(168,173,189), black -> RGB(129,134,150)
241
// (255 - a) + (x * a / 255) = 173, x * a / 255 = 134
242
// a = 255 - w + b = 158, x = b * 255 / a = ?
243
// but is not drawn using x * a + y * (255 - a) here?
244
//PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x9EF2D8D0));
245
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
247
if ((flag & DS_MSG) || (flag & DS_ERRORMSG))
248
DisplayMessage(msgText, (flag & DS_YESNO) != 0, (flag & DS_OK) != 0);
250
if (flag & (DS_OK | DS_VALIDBUTTON))
251
DisplayButtons(DS_BUTTON_OK, messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ? messageDialog.okayButton : NULL);
253
if (flag & DS_CANCELBUTTON)
254
DisplayButtons(DS_BUTTON_CANCEL, messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ? messageDialog.cancelButton : NULL);
256
if (IsButtonPressed(cancelButtonFlag) && (flag & DS_CANCELBUTTON))
258
if(messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V3 ||
259
((messageDialog.common.size == SCE_UTILITY_MSGDIALOG_SIZE_V2) && (flag & DS_YESNO)))
260
messageDialog.buttonPressed = 3;
262
messageDialog.buttonPressed = 0;
265
else if (IsButtonPressed(okButtonFlag) && (flag & DS_VALIDBUTTON))
267
if (yesnoChoice == 0)
269
messageDialog.buttonPressed = 2;
273
messageDialog.buttonPressed = 1;
281
messageDialog.result = 0;
284
Memory::Memcpy(messageDialogAddr, &messageDialog ,messageDialog.common.size);
288
int PSPMsgDialog::Abort() {
289
// Katekyoushi Hitman Reborn! Battle Arena expects this to fail when not running.
290
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
291
return SCE_ERROR_UTILITY_INVALID_STATUS;
293
// Status is not actually changed until Update().
299
int PSPMsgDialog::Shutdown(bool force) {
300
if (GetStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
301
return SCE_ERROR_UTILITY_INVALID_STATUS;
303
PSPDialog::Shutdown(force);
305
ChangeStatusShutdown(MSG_SHUTDOWN_DELAY_US);
311
void PSPMsgDialog::DoState(PointerWrap &p)
313
PSPDialog::DoState(p);
315
auto s = p.Section("PSPMsgDialog", 1);
321
p.Do(messageDialogAddr);
322
p.DoArray(msgText, sizeof(msgText));
326
pspUtilityDialogCommon *PSPMsgDialog::GetCommonParam()
328
return &messageDialog.common;