1 // Copyright (c) 2012- PPSSPP Project.
2
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.
6
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.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include "Common/Data/Text/I18n.h"
19
20 #include "Common/Serialize/Serializer.h"
21 #include "Common/Serialize/SerializeFuncs.h"
22 #include "Common/StringUtils.h"
23 #include "Core/CoreTiming.h"
24 #include "Core/Dialog/PSPDialog.h"
25 #include "Core/HLE/sceCtrl.h"
26 #include "Core/HLE/scePower.h"
27 #include "Core/HLE/sceUtility.h"
28 #include "Core/MemMapHelpers.h"
29 #include "Core/Util/PPGeDraw.h"
30
31 #define FADE_TIME 1.0
32 const float FONT_SCALE = 0.55f;
33
PSPDialog(UtilityDialogType type)34 PSPDialog::PSPDialog(UtilityDialogType type) : dialogType_(type) {
35 }
36
~PSPDialog()37 PSPDialog::~PSPDialog() {
38 }
39
GetStatus()40 PSPDialog::DialogStatus PSPDialog::GetStatus() {
41 if (pendingStatusTicks != 0 && CoreTiming::GetTicks() >= pendingStatusTicks) {
42 bool changeAllowed = true;
43 if (pendingStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) {
44 FinishVolatile();
45 } else if (pendingStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) {
46 if (!volatileLocked_) {
47 volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0;
48 changeAllowed = volatileLocked_;
49 }
50 }
51 if (changeAllowed) {
52 status = pendingStatus;
53 pendingStatusTicks = 0;
54 }
55 }
56
57 PSPDialog::DialogStatus retval = status;
58 if (UseAutoStatus()) {
59 if (status == SCE_UTILITY_STATUS_SHUTDOWN)
60 status = SCE_UTILITY_STATUS_NONE;
61 if (status == SCE_UTILITY_STATUS_INITIALIZE)
62 status = SCE_UTILITY_STATUS_RUNNING;
63 }
64 return retval;
65 }
66
ChangeStatus(DialogStatus newStatus,int delayUs)67 void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) {
68 if (delayUs <= 0) {
69 if (newStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) {
70 FinishVolatile();
71 } else if (newStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) {
72 if (!volatileLocked_) {
73 // TODO: Should probably make the status pending instead?
74 volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0;
75 }
76 }
77 status = newStatus;
78 pendingStatus = newStatus;
79 pendingStatusTicks = 0;
80 } else {
81 pendingStatus = newStatus;
82 pendingStatusTicks = CoreTiming::GetTicks() + usToCycles(delayUs);
83 }
84 }
85
FinishVolatile()86 void PSPDialog::FinishVolatile() {
87 if (!volatileLocked_)
88 return;
89
90 if (KernelVolatileMemUnlock(0) == 0) {
91 volatileLocked_ = false;
92 // Simulate modifications to volatile memory.
93 Memory::Memset(PSP_GetVolatileMemoryStart(), 0, PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart());
94 }
95 }
96
FinishInit()97 int PSPDialog::FinishInit() {
98 if (ReadStatus() != SCE_UTILITY_STATUS_INITIALIZE)
99 return -1;
100 // The thread already locked.
101 volatileLocked_ = true;
102 ChangeStatus(SCE_UTILITY_STATUS_RUNNING, 0);
103 return 0;
104 }
105
FinishShutdown()106 int PSPDialog::FinishShutdown() {
107 if (ReadStatus() != SCE_UTILITY_STATUS_SHUTDOWN)
108 return -1;
109 ChangeStatus(SCE_UTILITY_STATUS_NONE, 0);
110 return 0;
111 }
112
ChangeStatusInit(int delayUs)113 void PSPDialog::ChangeStatusInit(int delayUs) {
114 ChangeStatus(SCE_UTILITY_STATUS_INITIALIZE, 0);
115
116 auto params = GetCommonParam();
117 if (params)
118 UtilityDialogInitialize(DialogType(), delayUs, params->accessThread);
119 else
120 ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs);
121 }
122
ChangeStatusShutdown(int delayUs)123 void PSPDialog::ChangeStatusShutdown(int delayUs) {
124 // If we're doing shutdown right away and skipped start, we don't run the dialog thread.
125 bool skipDialogShutdown = status == SCE_UTILITY_STATUS_NONE && pendingStatus == SCE_UTILITY_STATUS_NONE;
126 ChangeStatus(SCE_UTILITY_STATUS_SHUTDOWN, 0);
127
128 auto params = GetCommonParam();
129 if (params && !skipDialogShutdown)
130 UtilityDialogShutdown(DialogType(), delayUs, params->accessThread);
131 else
132 ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs);
133 }
134
StartDraw()135 void PSPDialog::StartDraw()
136 {
137 PPGeBegin();
138 PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x20000000));
139 }
140
EndDraw()141 void PSPDialog::EndDraw()
142 {
143 PPGeEnd();
144 }
145
Shutdown(bool force)146 int PSPDialog::Shutdown(bool force)
147 {
148 if (force) {
149 ChangeStatus(SCE_UTILITY_STATUS_NONE, 0);
150 } else {
151 ChangeStatus(SCE_UTILITY_STATUS_SHUTDOWN, 0);
152 }
153 return 0;
154 }
155
StartFade(bool fadeIn_)156 void PSPDialog::StartFade(bool fadeIn_)
157 {
158 isFading = true;
159 fadeTimer = 0;
160 fadeIn = fadeIn_;
161 }
162
UpdateFade(int animSpeed)163 void PSPDialog::UpdateFade(int animSpeed) {
164 if (isFading) {
165 fadeTimer += 1.0f/30.0f * animSpeed; // Probably need a more real value of delta time
166 if (fadeTimer < FADE_TIME) {
167 if (fadeIn)
168 fadeValue = (u32) (fadeTimer / FADE_TIME * 255);
169 else
170 fadeValue = 255 - (u32) (fadeTimer / FADE_TIME * 255);
171 } else {
172 fadeValue = (fadeIn ? 255 : 0);
173 isFading = false;
174 if (!fadeIn) {
175 FinishFadeOut();
176 }
177 }
178 }
179 }
180
FinishFadeOut()181 void PSPDialog::FinishFadeOut() {
182 ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
183 }
184
CalcFadedColor(u32 inColor)185 u32 PSPDialog::CalcFadedColor(u32 inColor)
186 {
187 u32 alpha = inColor >> 24;
188 alpha = alpha * fadeValue / 255;
189 return (inColor & 0x00FFFFFF) | (alpha << 24);
190 }
191
DoState(PointerWrap & p)192 void PSPDialog::DoState(PointerWrap &p) {
193 auto s = p.Section("PSPDialog", 1, 3);
194 if (!s)
195 return;
196
197 Do(p, status);
198 Do(p, lastButtons);
199 Do(p, buttons);
200 Do(p, fadeTimer);
201 Do(p, isFading);
202 Do(p, fadeIn);
203 Do(p, fadeValue);
204
205 // I don't think we should save these two... Let's just ignore them for now for compat.
206 int okButtonImg = 0;
207 Do(p, okButtonImg);
208 int cancelButtonImg = 0;
209 Do(p, cancelButtonImg);
210
211 Do(p, okButtonFlag);
212 Do(p, cancelButtonFlag);
213
214 if (s >= 2) {
215 Do(p, pendingStatus);
216 Do(p, pendingStatusTicks);
217 } else {
218 pendingStatusTicks = 0;
219 }
220
221 if (s >= 3) {
222 Do(p, volatileLocked_);
223 } else {
224 volatileLocked_ = false;
225 }
226 }
227
GetCommonParam()228 pspUtilityDialogCommon *PSPDialog::GetCommonParam()
229 {
230 // FIXME
231 return 0;
232 }
233
UpdateButtons()234 void PSPDialog::UpdateButtons()
235 {
236 lastButtons = __CtrlPeekButtons();
237 buttons = __CtrlReadLatch();
238 }
239
IsButtonPressed(int checkButton)240 bool PSPDialog::IsButtonPressed(int checkButton)
241 {
242 return !isFading && (buttons & checkButton);
243 }
244
IsButtonHeld(int checkButton,int & framesHeld,int framesHeldThreshold,int framesHeldRepeatRate)245 bool PSPDialog::IsButtonHeld(int checkButton, int &framesHeld, int framesHeldThreshold, int framesHeldRepeatRate)
246 {
247 bool btnWasHeldLastFrame = (lastButtons & checkButton) && (__CtrlPeekButtons() & checkButton);
248 if (!isFading && btnWasHeldLastFrame) {
249 framesHeld++;
250 }
251 else {
252 framesHeld = 0;
253 return false;
254 }
255
256 // It's considered held for dialog purposes after 30 frames (~0.5 seconds),
257 // and set to repeat every 10 frames, by default.
258 if (framesHeld >= framesHeldThreshold && ((framesHeld % framesHeldRepeatRate) == 0))
259 return true;
260
261 return false;
262 }
263
FadedStyle(PPGeAlign align,float scale)264 PPGeStyle PSPDialog::FadedStyle(PPGeAlign align, float scale) {
265 PPGeStyle textStyle;
266 textStyle.align = align;
267 textStyle.scale = scale;
268 textStyle.color = CalcFadedColor(textStyle.color);
269 textStyle.hasShadow = true;
270 textStyle.shadowColor = CalcFadedColor(textStyle.shadowColor);
271 return textStyle;
272 }
273
FadedImageStyle()274 PPGeImageStyle PSPDialog::FadedImageStyle() {
275 PPGeImageStyle style;
276 style.color = CalcFadedColor(style.color);
277 return style;
278 }
279
DisplayButtons(int flags,const char * caption)280 void PSPDialog::DisplayButtons(int flags, const char *caption)
281 {
282 bool useCaption = false;
283 char safeCaption[65] = {0};
284 if (caption != NULL && *caption != '\0') {
285 useCaption = true;
286 truncate_cpy(safeCaption, caption);
287 }
288
289 PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_LEFT, FONT_SCALE);
290
291 auto di = GetI18NCategory("Dialog");
292 float x1 = 183.5f, x2 = 261.5f;
293 if (GetCommonParam()->buttonSwap == 1) {
294 x1 = 261.5f;
295 x2 = 183.5f;
296 }
297 if (flags & DS_BUTTON_OK) {
298 const char *text = useCaption ? safeCaption : di->T("Enter");
299 PPGeDrawImage(okButtonImg, x2, 256, 11.5f, 11.5f, textStyle);
300 PPGeDrawText(text, x2 + 14.5f, 252, textStyle);
301 }
302 if (flags & DS_BUTTON_CANCEL) {
303 const char *text = useCaption ? safeCaption : di->T("Back");
304 PPGeDrawImage(cancelButtonImg, x1, 256, 11.5f, 11.5f, textStyle);
305 PPGeDrawText(text, x1 + 14.5f, 252, textStyle);
306 }
307 }
308