Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Dialog/PSPNetconfDialog.cpp
3186 views
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 <algorithm>
19
#include "Common/TimeUtil.h"
20
#include "Common/Data/Text/I18n.h"
21
#include "Common/Serialize/Serializer.h"
22
#include "Common/Serialize/SerializeFuncs.h"
23
#include "Core/Config.h"
24
#include "Core/MemMapHelpers.h"
25
#include "Core/Util/PPGeDraw.h"
26
#include "Core/HLE/HLE.h"
27
#include "Core/HLE/ErrorCodes.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/HLE/sceNetApctl.h"
34
#include "Core/Dialog/PSPNetconfDialog.h"
35
#include "Common/Data/Encoding/Utf8.h"
36
37
38
#define NETCONF_CONNECT_APNET 0
39
#define NETCONF_STATUS_APNET 1
40
#define NETCONF_CONNECT_ADHOC 2
41
#define NETCONF_CONNECT_APNET_LAST 3
42
#define NETCONF_CREATE_ADHOC 4
43
#define NETCONF_JOIN_ADHOC 5
44
45
static const float FONT_SCALE = 0.65f;
46
47
// Needs testing.
48
const static int NET_INIT_DELAY_US = 200000;
49
const static int NET_SHUTDOWN_DELAY_US = 200000;
50
const static int NET_CONNECT_TIMEOUT = 15000000; // Using 15 secs to match the timeout on Adhoc Server side (SERVER_USER_TIMEOUT)
51
52
struct ScanInfos {
53
s32_le sz;
54
SceNetAdhocctlScanInfoEmu si;
55
} PACK;
56
57
int PSPNetconfDialog::Init(u32 paramAddr) {
58
// Already running
59
if (ReadStatus() != SCE_UTILITY_STATUS_NONE)
60
return SCE_ERROR_UTILITY_INVALID_STATUS;
61
62
NOTICE_LOG(Log::sceNet, "PSPNetConfDialog Init");
63
jsonReady_ = false;
64
// Kick off a request to the infra-dns.json since we'll need it later.
65
StartInfraJsonDownload();
66
67
requestAddr = paramAddr;
68
int size = Memory::Read_U32(paramAddr);
69
memset(&request, 0, sizeof(request));
70
// Only copy the right size to support different request format
71
Memory::Memcpy(&request, paramAddr, size);
72
73
ChangeStatusInit(NET_INIT_DELAY_US);
74
75
// Eat any keys pressed before the dialog inited.
76
InitCommon();
77
UpdateButtons();
78
79
connResult = -1;
80
scanInfosAddr = 0;
81
scanStep = 0;
82
startTime = (u64)(time_now_d() * 1000000.0);
83
showNoWlanNotice_ = !g_Config.bEnableWlan;
84
85
StartFade(true);
86
return 0;
87
}
88
89
void PSPNetconfDialog::DrawBanner() {
90
PPGeDrawRect(0, 0, 480, 22, CalcFadedColor(0x65636358));
91
92
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
93
textStyle.hasShadow = false;
94
95
// TODO: Draw a hexagon icon
96
PPGeDrawImage(10, 5, 11.0f, 10.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
97
auto di = GetI18NCategory(I18NCat::DIALOG);
98
PPGeDrawText(di->T("Network Connection"), 31, 10, textStyle);
99
}
100
101
void PSPNetconfDialog::DrawIndicator() {
102
// TODO: Draw animated circle as processing indicator
103
PPGeDrawImage(456, 248, 20.0f, 20.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
104
}
105
106
int PSPNetconfDialog::Update(int animSpeed) {
107
if (ReadStatus() != SCE_UTILITY_STATUS_RUNNING) {
108
return SCE_ERROR_UTILITY_INVALID_STATUS;
109
}
110
111
UpdateButtons();
112
UpdateCommon();
113
auto di = GetI18NCategory(I18NCat::DIALOG);
114
u64 now = (u64)(time_now_d() * 1000000.0);
115
116
std::string json;
117
if (!jsonReady_ && PollInfraJsonDownload(&json)) {
118
if (!json.empty()) {
119
if (!LoadAutoDNS(json)) {
120
// If the JSON parse fails, throw away the cache file at least.
121
ERROR_LOG(Log::sceNet, "Failed to parse bad json. Deleting cache file.");
122
DeleteAutoDNSCacheFile();
123
} else {
124
DEBUG_LOG(Log::sceNet, "Got and processed the AutoDNS json.");
125
}
126
} else {
127
// TODO: Show a notice?
128
WARN_LOG(Log::sceNet, "Failed to get json file. Autoconfig will not work.");
129
}
130
jsonReady_ = true;
131
}
132
133
// It seems JPCSP doesn't check for NETCONF_STATUS_APNET
134
if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) {
135
UpdateFade(animSpeed);
136
StartDraw();
137
138
// This disables the notice that we don't support the internet below.
139
// Keeping the code in case we need it for something later.
140
if (showNoWlanNotice_) {
141
auto err = GetI18NCategory(I18NCat::ERRORS);
142
const float WRAP_WIDTH = 254.0f;
143
const int confirmBtn = GetConfirmButton();
144
const int cancelBtn = GetCancelButton();
145
const ImageID confirmBtnImage = confirmBtn == CTRL_CROSS ? ImageID("I_CROSS") : ImageID("I_CIRCLE");
146
const ImageID cancelBtnImage = cancelBtn == CTRL_CIRCLE ? ImageID("I_CIRCLE") : ImageID("I_CROSS");
147
148
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);
149
PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);
150
151
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));
152
DrawBanner();
153
PPGeDrawTextWrapped(err->T("WirelessSwitchOffError", "A connection error has occurred.\nThe Wireless switch on the PSP system is off (network is disabled)."), 241, 132, WRAP_WIDTH, 0, textStyle);
154
// PPGeDrawImage(confirmBtnImage, 185, 240, 20, 20, buttonStyle);
155
// PPGeDrawText(di->T("OK"), 215, 243, buttonStyle);
156
PPGeDrawImage(cancelBtnImage, 255, 240, 20, 20, buttonStyle);
157
PPGeDrawText(di->T("Cancel"), 285, 243, buttonStyle);
158
159
// 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)
160
if (IsButtonPressed(cancelBtn)) {
161
StartFade(false);
162
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
163
// TODO: When the dialog is aborted, does it really set the result to this?
164
// It seems to make Phantasy Star Portable 2 happy, so it should be okay for now.
165
request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
166
}
167
} else {
168
int state = NetApctl_GetState();
169
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
170
DrawBanner();
171
DrawIndicator();
172
173
if (state == PSP_NET_APCTL_STATE_GOT_IP || state == PSP_NET_APCTL_STATE_GETTING_IP) {
174
DisplayMessage2(di->T("ObtainingIP", "Obtaining IP address.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);
175
} else {
176
// Skipping the Select Connection screen since we only have 1 fake profile
177
DisplayMessage2(di->T("ConnectingAP", "Connecting to the access point.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);
178
}
179
DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));
180
181
// The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP,
182
// *AND* we have the json.
183
if (state == PSP_NET_APCTL_STATE_GOT_IP && jsonReady_) {
184
if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {
185
StartFade(false);
186
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
187
}
188
} else if (state == PSP_NET_APCTL_STATE_JOINING) {
189
// Switch to the next message
190
StartFade(true);
191
}
192
193
else if (state == PSP_NET_APCTL_STATE_DISCONNECTED) {
194
// When connecting with infrastructure, simulate a connection using the first network configuration entry.
195
if (connResult < 0) {
196
connResult = hleCall(sceNetApctl, int, sceNetApctlConnect, 1);
197
}
198
}
199
}
200
201
EndDraw();
202
}
203
else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) {
204
int state = NetAdhocctl_GetState();
205
bool timedout = (state == ADHOCCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT);
206
207
UpdateFade(animSpeed);
208
StartDraw();
209
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
210
DrawBanner();
211
DrawIndicator();
212
213
if (timedout) {
214
// FIXME: Do we need to show error message?
215
std::string message(di->T("InternalError", "An internal error has occurred."));
216
DisplayMessage2(message + StringFromFormat("\n(%08X)", connResult));
217
DisplayButtons(DS_BUTTON_CANCEL, di->T("Back"));
218
}
219
else {
220
std::string channel = std::to_string(g_Config.iWlanAdhocChannel);
221
if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC)
222
channel = "Automatic";
223
224
DisplayMessage2(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), std::string(di->T("Channel:")) + std::string(" ") + std::string(di->T(channel)));
225
226
// 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
227
if (request.netAction == NETCONF_JOIN_ADHOC)
228
DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));
229
230
// 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
231
if (request.NetconfData.IsValid()) {
232
if (state == ADHOCCTL_STATE_DISCONNECTED) {
233
switch (request.netAction)
234
{
235
case NETCONF_CREATE_ADHOC:
236
if (connResult < 0) {
237
connResult = hleCall(sceNetAdhocctl, int, sceNetAdhocctlCreate, request.NetconfData->groupName);
238
}
239
break;
240
case NETCONF_JOIN_ADHOC:
241
// 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?
242
if (scanStep == 0) {
243
if (hleCall(sceNetAdhocctl, int, sceNetAdhocctlScan) >= 0) {
244
u32 structsz = sizeof(ScanInfos);
245
if (Memory::IsValidAddress(scanInfosAddr))
246
userMemory.Free(scanInfosAddr);
247
scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");
248
Memory::Write_U32(sizeof(SceNetAdhocctlScanInfoEmu), scanInfosAddr);
249
scanStep = 1;
250
}
251
}
252
else if (scanStep == 1) {
253
s32 sz = Memory::Read_U32(scanInfosAddr);
254
// Get required buffer size
255
if (hleCall(sceNetAdhocctl, int, sceNetAdhocctlGetScanInfo, scanInfosAddr, 0) >= 0) {
256
s32 reqsz = Memory::Read_U32(scanInfosAddr);
257
if (reqsz > sz) {
258
sz = reqsz;
259
if (Memory::IsValidAddress(scanInfosAddr))
260
userMemory.Free(scanInfosAddr);
261
u32 structsz = sz + sizeof(s32);
262
scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");
263
Memory::Write_U32(sz, scanInfosAddr);
264
}
265
if (reqsz > 0) {
266
if (hleCall(sceNetAdhocctl, int, sceNetAdhocctlGetScanInfo, scanInfosAddr, scanInfosAddr + (u32)sizeof(s32)) >= 0) {
267
ScanInfos* scanInfos = (ScanInfos*)Memory::GetPointer(scanInfosAddr);
268
int n = scanInfos->sz / sizeof(SceNetAdhocctlScanInfoEmu);
269
// Assuming returned SceNetAdhocctlScanInfoEmu(s) are contagious where next is pointing to current addr + sizeof(SceNetAdhocctlScanInfoEmu)
270
while (n > 0) {
271
SceNetAdhocctlScanInfoEmu* si = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfosAddr + sizeof(s32) + sizeof(SceNetAdhocctlScanInfoEmu) * (n - 1LL));
272
if (memcmp(si->group_name.data, request.NetconfData->groupName, ADHOCCTL_GROUPNAME_LEN) == 0) {
273
// Moving found group info to the front so we can use it on sceNetAdhocctlJoin easily
274
memcpy((char*)scanInfos + sizeof(s32), si, sizeof(SceNetAdhocctlScanInfoEmu));
275
scanStep = 2;
276
break;
277
}
278
n--;
279
}
280
// Target group not found, try to scan again later
281
if (n <= 0) {
282
scanStep = 0;
283
}
284
}
285
}
286
// No group found, try to scan again later
287
else {
288
scanStep = 0;
289
}
290
}
291
}
292
else if (scanStep == 2) {
293
if (connResult < 0) {
294
connResult = hleCall(sceNetAdhocctl, int, sceNetAdhocctlJoin, scanInfosAddr + (u32)sizeof(s32));
295
if (connResult >= 0) {
296
// We are done!
297
if (Memory::IsValidAddress(scanInfosAddr))
298
userMemory.Free(scanInfosAddr);
299
scanInfosAddr = 0;
300
}
301
}
302
}
303
break;
304
default:
305
if (connResult < 0) {
306
connResult = hleCall(sceNetAdhocctl, int, sceNetAdhocctlConnect, request.NetconfData->groupName);
307
}
308
break;
309
}
310
}
311
}
312
}
313
314
// The Netconf dialog stays visible until the network reaches the state ADHOCCTL_STATE_CONNECTED.
315
if (state == ADHOCCTL_STATE_CONNECTED) {
316
// Checking pendingStatus to make sure ChangeStatus not to continously extending the delay ticks on every call for eternity
317
if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {
318
StartFade(false);
319
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
320
}
321
322
// Let's not leaks any memory
323
if (Memory::IsValidAddress(scanInfosAddr))
324
userMemory.Free(scanInfosAddr);
325
scanInfosAddr = 0;
326
}
327
328
if ((request.netAction == NETCONF_JOIN_ADHOC || timedout) && IsButtonPressed(cancelButtonFlag)) {
329
StartFade(false);
330
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);
331
request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
332
// Let's not leaks any memory
333
if (Memory::IsValidAddress(scanInfosAddr))
334
userMemory.Free(scanInfosAddr);
335
scanInfosAddr = 0;
336
}
337
338
EndDraw();
339
}
340
341
if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED)
342
Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam");
343
344
return 0;
345
}
346
347
int PSPNetconfDialog::Shutdown(bool force) {
348
if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
349
return SCE_ERROR_UTILITY_INVALID_STATUS;
350
351
PSPDialog::Shutdown(force);
352
if (!force) {
353
ChangeStatusShutdown(NET_SHUTDOWN_DELAY_US);
354
}
355
356
return 0;
357
}
358
359
void PSPNetconfDialog::DoState(PointerWrap &p) {
360
PSPDialog::DoState(p);
361
362
auto s = p.Section("PSPNetconfigDialog", 0, 2);
363
if (!s)
364
return;
365
366
Do(p, request);
367
if (s >= 2) {
368
Do(p, scanInfosAddr);
369
Do(p, scanStep);
370
Do(p, connResult);
371
}
372
else {
373
scanInfosAddr = 0;
374
scanStep = 0;
375
connResult = -1;
376
}
377
378
if (p.mode == p.MODE_READ) {
379
startTime = 0;
380
}
381
}
382
383
pspUtilityDialogCommon* PSPNetconfDialog::GetCommonParam()
384
{
385
return &request.common;
386
}
387
388