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