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 #if defined(_WIN32)
19 #include "Common/CommonWindows.h"
20 #endif
21 #include <TimeUtil.h>
22 #include "Common/Data/Text/I18n.h"
23 #include "Common/Serialize/Serializer.h"
24 #include "Common/Serialize/SerializeFuncs.h"
25 #include "Core/Config.h"
26 #include "Core/MemMapHelpers.h"
27 #include "Core/Util/PPGeDraw.h"
28 #include "Core/HLE/sceKernelMemory.h"
29 #include "Core/HLE/sceCtrl.h"
30 #include "Core/HLE/sceUtility.h"
31 #include "Core/HLE/sceNet.h"
32 #include "Core/HLE/sceNetAdhoc.h"
33 #include "Core/Dialog/PSPNetconfDialog.h"
34 #include "Common/Data/Encoding/Utf8.h"
35
36
37 #define NETCONF_CONNECT_APNET 0
38 #define NETCONF_STATUS_APNET 1
39 #define NETCONF_CONNECT_ADHOC 2
40 #define NETCONF_CONNECT_APNET_LAST 3
41 #define NETCONF_CREATE_ADHOC 4
42 #define NETCONF_JOIN_ADHOC 5
43
44 static const float FONT_SCALE = 0.65f;
45
46 // Needs testing.
47 const static int NET_INIT_DELAY_US = 200000;
48 const static int NET_SHUTDOWN_DELAY_US = 200000;
49 const static int NET_CONNECT_TIMEOUT = 15000000; // Using 15 secs to match the timeout on Adhoc Server side (SERVER_USER_TIMEOUT)
50
51 struct ScanInfos {
52 s32_le sz;
53 SceNetAdhocctlScanInfoEmu si;
54 } PACK;
55
56
PSPNetconfDialog(UtilityDialogType type)57 PSPNetconfDialog::PSPNetconfDialog(UtilityDialogType type) : PSPDialog(type) {
58 }
59
~PSPNetconfDialog()60 PSPNetconfDialog::~PSPNetconfDialog() {
61 }
62
Init(u32 paramAddr)63 int PSPNetconfDialog::Init(u32 paramAddr) {
64 // Already running
65 if (ReadStatus() != SCE_UTILITY_STATUS_NONE)
66 return SCE_ERROR_UTILITY_INVALID_STATUS;
67
68 requestAddr = paramAddr;
69 int size = Memory::Read_U32(paramAddr);
70 memset(&request, 0, sizeof(request));
71 // Only copy the right size to support different request format
72 Memory::Memcpy(&request, paramAddr, size);
73
74 ChangeStatusInit(NET_INIT_DELAY_US);
75
76 // Eat any keys pressed before the dialog inited.
77 UpdateButtons();
78 okButtonImg = ImageID("I_CIRCLE");
79 cancelButtonImg = ImageID("I_CROSS");
80 okButtonFlag = CTRL_CIRCLE;
81 cancelButtonFlag = CTRL_CROSS;
82 if (request.common.buttonSwap == 1)
83 {
84 okButtonImg = ImageID("I_CROSS");
85 cancelButtonImg = ImageID("I_CIRCLE");
86 okButtonFlag = CTRL_CROSS;
87 cancelButtonFlag = CTRL_CIRCLE;
88 }
89
90 connResult = -1;
91 scanInfosAddr = 0;
92 scanStep = 0;
93 startTime = (u64)(time_now_d() * 1000000.0);
94
95 StartFade(true);
96 return 0;
97 }
98
DrawBanner()99 void PSPNetconfDialog::DrawBanner() {
100
101 PPGeDrawRect(0, 0, 480, 22, CalcFadedColor(0x65636358));
102
103 PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
104 textStyle.hasShadow = false;
105
106 // TODO: Draw a hexagon icon
107 PPGeDrawImage(10, 5, 11.0f, 10.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
108 auto di = GetI18NCategory("Dialog");
109 PPGeDrawText(di->T("Network Connection"), 31, 10, textStyle);
110 }
111
DrawIndicator()112 void PSPNetconfDialog::DrawIndicator() {
113 // TODO: Draw animated circle as processing indicator
114 PPGeDrawImage(456, 248, 20.0f, 20.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
115 }
116
DisplayMessage(std::string text1,std::string text2a,std::string text2b,std::string text3a,std::string text3b,bool hasYesNo,bool hasOK)117 void PSPNetconfDialog::DisplayMessage(std::string text1, std::string text2a, std::string text2b, std::string text3a, std::string text3b, bool hasYesNo, bool hasOK) {
118 auto di = GetI18NCategory("Dialog");
119
120 PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_CENTER, FONT_SCALE);
121 PPGeStyle messageStyle = FadedStyle(PPGeAlign::BOX_HCENTER, FONT_SCALE);
122 PPGeStyle messageStyleRight = FadedStyle(PPGeAlign::BOX_RIGHT, FONT_SCALE);
123 PPGeStyle messageStyleLeft = FadedStyle(PPGeAlign::BOX_LEFT, FONT_SCALE);
124
125 std::string text2 = text2a + " " + text2b;
126 std::string text3 = text3a + " " + text3b;
127
128 // Without the scrollbar, we have 350 total pixels.
129 float WRAP_WIDTH = 300.0f;
130 if (UTF8StringNonASCIICount(text1.c_str()) >= (int)text1.size() / 4) {
131 WRAP_WIDTH = 336.0f;
132 if (text1.size() > 12) {
133 messageStyle.scale = 0.6f;
134 }
135 }
136
137 float totalHeight1 = 0.0f;
138 PPGeMeasureText(nullptr, &totalHeight1, text1.c_str(), FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);
139 float totalHeight2 = 0.0f;
140 if (text2 != " ")
141 PPGeMeasureText(nullptr, &totalHeight2, text2.c_str(), FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH);
142 float totalHeight3 = 0.0f;
143 if (text3 != " ")
144 PPGeMeasureText(nullptr, &totalHeight3, text3.c_str(), FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH);
145 float marginTop = 0.0f;
146 if (text2 != " " || text3 != " ")
147 marginTop = 11.0f;
148 float totalHeight = totalHeight1 + totalHeight2 + totalHeight3 + marginTop;
149 // The PSP normally only shows about 8 lines at a time.
150 // For improved UX, we intentionally show part of the next line.
151 float visibleHeight = std::min(totalHeight, 175.0f);
152 float h2 = visibleHeight / 2.0f;
153
154 float centerY = 135.0f;
155 float sy = centerY - h2 - 15.0f;
156 float ey = centerY + h2 + 20.0f;
157 float buttonY = centerY + h2 + 5.0f;
158
159 auto drawSelectionBoxAndAdjust = [&](float x) {
160 // Box has a fixed size.
161 float w = 15.0f;
162 float h = 8.0f;
163 PPGeDrawRect(x - w, buttonY - h, x + w, buttonY + h, CalcFadedColor(0x6DCFCFCF));
164
165 centerY -= h + 5.0f;
166 sy -= h + 5.0f;
167 ey = buttonY + h * 2.0f + 5.0f;
168 };
169
170 if (hasYesNo) {
171 if (yesnoChoice == 1) {
172 drawSelectionBoxAndAdjust(204.0f);
173 }
174 else {
175 drawSelectionBoxAndAdjust(273.0f);
176 }
177
178 PPGeDrawText(di->T("Yes"), 203.0f, buttonY - 1.0f, buttonStyle);
179 PPGeDrawText(di->T("No"), 272.0f, buttonY - 1.0f, buttonStyle);
180 if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {
181 yesnoChoice = 1;
182 }
183 else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {
184 yesnoChoice = 0;
185 }
186 buttonY += 8.0f + 5.0f;
187 }
188
189 if (hasOK) {
190 drawSelectionBoxAndAdjust(240.0f);
191
192 PPGeDrawText(di->T("OK"), 239.0f, buttonY - 1.0f, buttonStyle);
193 buttonY += 8.0f + 5.0f;
194 }
195
196 PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2));
197 PPGeDrawTextWrapped(text1.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle);
198 if (text2a != "") {
199 if (text2b != "")
200 PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
201 else
202 PPGeDrawTextWrapped(text2a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle);
203 }
204 if (text2b != "")
205 PPGeDrawTextWrapped(text2b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
206 if (text3a != "") {
207 if (text3b != "")
208 PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight);
209 else
210 PPGeDrawTextWrapped(text3a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle);
211 }
212 if (text3b != "")
213 PPGeDrawTextWrapped(text3b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);
214 PPGeScissorReset();
215
216 // Do we need a scrollbar?
217 if (visibleHeight < totalHeight) {
218 float scrollSpeed = 5.0f;
219 float scrollMax = totalHeight - visibleHeight;
220
221 float bobHeight = (visibleHeight / totalHeight) * visibleHeight;
222 float bobOffset = (scrollPos_ / scrollMax) * (visibleHeight - bobHeight);
223 float bobY1 = centerY - h2 + bobOffset;
224 PPGeDrawRect(415.0f, bobY1, 420.0f, bobY1 + bobHeight, CalcFadedColor(0xFFCCCCCC));
225
226 auto buttonDown = [this](int btn, int& held) {
227 if (IsButtonPressed(btn)) {
228 held = 0;
229 return true;
230 }
231 return IsButtonHeld(btn, held, 1, 1);
232 };
233 if (buttonDown(CTRL_DOWN, framesDownHeld_) && scrollPos_ < scrollMax) {
234 scrollPos_ = std::min(scrollMax, scrollPos_ + scrollSpeed);
235 }
236 if (buttonDown(CTRL_UP, framesUpHeld_) && scrollPos_ > 0.0f) {
237 scrollPos_ = std::max(0.0f, scrollPos_ - scrollSpeed);
238 }
239 }
240
241 PPGeDrawRect(60.0f, sy, 420.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));
242 PPGeDrawRect(60.0f, ey, 420.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));
243 }
244
Update(int animSpeed)245 int PSPNetconfDialog::Update(int animSpeed) {
246 if (ReadStatus() != SCE_UTILITY_STATUS_RUNNING) {
247 return SCE_ERROR_UTILITY_INVALID_STATUS;
248 }
249
250 UpdateButtons();
251 auto di = GetI18NCategory("Dialog");
252 auto err = GetI18NCategory("Error");
253 u64 now = (u64)(time_now_d() * 1000000.0);
254
255 // It seems JPCSP doesn't check for NETCONF_STATUS_APNET
256 if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) {
257 int state = NetApctl_GetState();
258
259 UpdateFade(animSpeed);
260 StartDraw();
261
262 if (!hideNotice) {
263 const float WRAP_WIDTH = 254.0f;
264 const ImageID confirmBtnImage = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? ImageID("I_CROSS") : ImageID("I_CIRCLE");
265 const int confirmBtn = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CROSS : CTRL_CIRCLE;
266 const ImageID cancelBtnImage = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? ImageID("I_CIRCLE") : ImageID("I_CROSS");
267 const int cancelBtn = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CIRCLE : CTRL_CROSS;
268
269 PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);
270 PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
271
272 PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));
273 DrawBanner();
274 PPGeDrawTextWrapped(err->T("PPSSPPDoesNotSupportInternet", "PPSSPP currently does not support connecting to the Internet for DLC, PSN, or game updates.\nContinuing may cause unexpected behavior or freezes."), 241, 132, WRAP_WIDTH, 0, textStyle);
275 PPGeDrawImage(confirmBtnImage, 185, 240, 20, 20, buttonStyle);
276 PPGeDrawText(di->T("OK"), 215, 243, buttonStyle);
277 PPGeDrawImage(cancelBtnImage, 255, 240, 20, 20, buttonStyle);
278 PPGeDrawText(di->T("Cancel"), 285, 243, buttonStyle);
279
280 // Since we don't support Infrastructure API yet.. Let the Player read the message first and choose to continue or not (ie. for testing networks API)
281 if (IsButtonPressed(cancelBtn)) {
282 StartFade(false);
283 ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
284 // TODO: When the dialog is aborted, does it really set the result to this?
285 // It seems to make Phantasy Star Portable 2 happy, so it should be okay for now.
286 request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
287 }
288 else if (IsButtonPressed(confirmBtn)) {
289 hideNotice = true;
290 StartFade(true);
291 }
292 }
293 else {
294 PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
295 DrawBanner();
296 DrawIndicator();
297
298 if (state == PSP_NET_APCTL_STATE_GOT_IP || state == PSP_NET_APCTL_STATE_GETTING_IP) {
299 DisplayMessage(di->T("ObtainingIP", "Obtaining IP address.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);
300 }
301 else {
302 // Skipping the Select Connection screen since we only have 1 fake profile
303 DisplayMessage(di->T("ConnectingAP", "Connecting to the access point.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);
304 }
305 DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));
306
307 // The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP.
308 if (state == PSP_NET_APCTL_STATE_GOT_IP) {
309 if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {
310 StartFade(false);
311 ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
312 }
313 }
314
315 else if (state == PSP_NET_APCTL_STATE_JOINING) {
316 // Switch to the next message
317 StartFade(true);
318 }
319
320 else if (state == PSP_NET_APCTL_STATE_DISCONNECTED) {
321 // When connecting with infrastructure, simulate a connection using the first network configuration entry.
322 if (connResult < 0) {
323 connResult = sceNetApctlConnect(1);
324 }
325 }
326 }
327
328 EndDraw();
329 }
330 else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) {
331 int state = NetAdhocctl_GetState();
332 bool timedout = (state == ADHOCCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT);
333
334 UpdateFade(animSpeed);
335 StartDraw();
336 PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
337 DrawBanner();
338 DrawIndicator();
339
340 if (timedout) {
341 // FIXME: Do we need to show error message?
342 DisplayMessage(di->T("InternalError", "An internal error has occurred.") + StringFromFormat("\n(%08X)", connResult));
343 DisplayButtons(DS_BUTTON_CANCEL, di->T("Back"));
344 }
345 else {
346 std::string channel = std::to_string(g_Config.iWlanAdhocChannel);
347 if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC)
348 channel = "Automatic";
349
350 DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), di->T("Channel:") + std::string(" ") + di->T(channel));
351
352 // Only Join mode is showing Cancel button on KHBBS and the button will fade out before the dialog is fading out, probably because it's already connected thus can't be canceled anymore
353 if (request.netAction == NETCONF_JOIN_ADHOC)
354 DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));
355
356 // KHBBS will first enter the arena using NETCONF_CONNECT_ADHOC (auto-create group when not exist yet?), but when the event started the event's creator use NETCONF_CREATE_ADHOC while the joining players use NETCONF_JOIN_ADHOC
357 if (request.NetconfData.IsValid()) {
358 if (state == ADHOCCTL_STATE_DISCONNECTED) {
359 switch (request.netAction)
360 {
361 case NETCONF_CREATE_ADHOC:
362 if (connResult < 0) {
363 connResult = sceNetAdhocctlCreate(request.NetconfData->groupName);
364 }
365 break;
366 case NETCONF_JOIN_ADHOC:
367 // FIXME: Should we Scan for a matching group first before Joining a Group (like adhoc games normally do)? Or Is it really allowed to join non-existing group?
368 if (scanStep == 0) {
369 if (sceNetAdhocctlScan() >= 0) {
370 u32 structsz = sizeof(ScanInfos);
371 if (Memory::IsValidAddress(scanInfosAddr))
372 userMemory.Free(scanInfosAddr);
373 scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");
374 Memory::Write_U32(sizeof(SceNetAdhocctlScanInfoEmu), scanInfosAddr);
375 scanStep = 1;
376 }
377 }
378 else if (scanStep == 1) {
379 s32 sz = Memory::Read_U32(scanInfosAddr);
380 // Get required buffer size
381 if (sceNetAdhocctlGetScanInfo(scanInfosAddr, 0) >= 0) {
382 s32 reqsz = Memory::Read_U32(scanInfosAddr);
383 if (reqsz > sz) {
384 sz = reqsz;
385 if (Memory::IsValidAddress(scanInfosAddr))
386 userMemory.Free(scanInfosAddr);
387 u32 structsz = sz + sizeof(s32);
388 scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");
389 Memory::Write_U32(sz, scanInfosAddr);
390 }
391 if (reqsz > 0) {
392 if (sceNetAdhocctlGetScanInfo(scanInfosAddr, scanInfosAddr + sizeof(s32)) >= 0) {
393 ScanInfos* scanInfos = (ScanInfos*)Memory::GetPointer(scanInfosAddr);
394 int n = scanInfos->sz / sizeof(SceNetAdhocctlScanInfoEmu);
395 // Assuming returned SceNetAdhocctlScanInfoEmu(s) are contagious where next is pointing to current addr + sizeof(SceNetAdhocctlScanInfoEmu)
396 while (n > 0) {
397 SceNetAdhocctlScanInfoEmu* si = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfosAddr + sizeof(s32) + sizeof(SceNetAdhocctlScanInfoEmu) * (n - 1LL));
398 if (memcmp(si->group_name.data, request.NetconfData->groupName, ADHOCCTL_GROUPNAME_LEN) == 0) {
399 // Moving found group info to the front so we can use it on sceNetAdhocctlJoin easily
400 memcpy((char*)scanInfos + sizeof(s32), si, sizeof(SceNetAdhocctlScanInfoEmu));
401 scanStep = 2;
402 break;
403 }
404 n--;
405 }
406 // Target group not found, try to scan again later
407 if (n <= 0) {
408 scanStep = 0;
409 }
410 }
411 }
412 // No group found, try to scan again later
413 else {
414 scanStep = 0;
415 }
416 }
417 }
418 else if (scanStep == 2) {
419 if (connResult < 0) {
420 connResult = sceNetAdhocctlJoin(scanInfosAddr + sizeof(s32));
421 if (connResult >= 0) {
422 // We are done!
423 if (Memory::IsValidAddress(scanInfosAddr))
424 userMemory.Free(scanInfosAddr);
425 scanInfosAddr = 0;
426 }
427 }
428 }
429 break;
430 default:
431 if (connResult < 0) {
432 connResult = sceNetAdhocctlConnect(request.NetconfData->groupName);
433 }
434 break;
435 }
436 }
437 }
438 }
439
440 // The Netconf dialog stays visible until the network reaches the state ADHOCCTL_STATE_CONNECTED.
441 if (state == ADHOCCTL_STATE_CONNECTED) {
442 // Checking pendingStatus to make sure ChangeStatus not to continously extending the delay ticks on every call for eternity
443 if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {
444 StartFade(false);
445 ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
446 }
447
448 // Let's not leaks any memory
449 if (Memory::IsValidAddress(scanInfosAddr))
450 userMemory.Free(scanInfosAddr);
451 scanInfosAddr = 0;
452 }
453
454 if ((request.netAction == NETCONF_JOIN_ADHOC || timedout) && IsButtonPressed(cancelButtonFlag)) {
455 StartFade(false);
456 ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
457 request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
458 // Let's not leaks any memory
459 if (Memory::IsValidAddress(scanInfosAddr))
460 userMemory.Free(scanInfosAddr);
461 scanInfosAddr = 0;
462 }
463
464 EndDraw();
465 }
466
467 if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED)
468 Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam");
469
470 return 0;
471 }
472
Shutdown(bool force)473 int PSPNetconfDialog::Shutdown(bool force) {
474 if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
475 return SCE_ERROR_UTILITY_INVALID_STATUS;
476
477 PSPDialog::Shutdown(force);
478 if (!force) {
479 ChangeStatusShutdown(NET_SHUTDOWN_DELAY_US);
480 }
481
482 return 0;
483 }
484
DoState(PointerWrap & p)485 void PSPNetconfDialog::DoState(PointerWrap &p) {
486 PSPDialog::DoState(p);
487
488 auto s = p.Section("PSPNetconfigDialog", 0, 2);
489 if (!s)
490 return;
491
492 Do(p, request);
493 if (s >= 2) {
494 Do(p, scanInfosAddr);
495 Do(p, scanStep);
496 Do(p, connResult);
497 }
498 else {
499 scanInfosAddr = 0;
500 scanStep = 0;
501 connResult = -1;
502 }
503
504 if (p.mode == p.MODE_READ) {
505 startTime = 0;
506 }
507 }
508
GetCommonParam()509 pspUtilityDialogCommon* PSPNetconfDialog::GetCommonParam()
510 {
511 return &request.common;
512 }
513