CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/Dialog/PSPNetconfDialog.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include "Common/CommonWindows.h"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/sceKernelMemory.h"27#include "Core/HLE/sceCtrl.h"28#include "Core/HLE/sceUtility.h"29#include "Core/HLE/sceNet.h"30#include "Core/HLE/sceNetAdhoc.h"31#include "Core/Dialog/PSPNetconfDialog.h"32#include "Common/Data/Encoding/Utf8.h"333435#define NETCONF_CONNECT_APNET 036#define NETCONF_STATUS_APNET 137#define NETCONF_CONNECT_ADHOC 238#define NETCONF_CONNECT_APNET_LAST 339#define NETCONF_CREATE_ADHOC 440#define NETCONF_JOIN_ADHOC 54142static const float FONT_SCALE = 0.65f;4344// Needs testing.45const static int NET_INIT_DELAY_US = 200000;46const static int NET_SHUTDOWN_DELAY_US = 200000;47const static int NET_CONNECT_TIMEOUT = 15000000; // Using 15 secs to match the timeout on Adhoc Server side (SERVER_USER_TIMEOUT)4849struct ScanInfos {50s32_le sz;51SceNetAdhocctlScanInfoEmu si;52} PACK;535455PSPNetconfDialog::PSPNetconfDialog(UtilityDialogType type) : PSPDialog(type) {56}5758PSPNetconfDialog::~PSPNetconfDialog() {59}6061int PSPNetconfDialog::Init(u32 paramAddr) {62// Already running63if (ReadStatus() != SCE_UTILITY_STATUS_NONE)64return SCE_ERROR_UTILITY_INVALID_STATUS;6566requestAddr = paramAddr;67int size = Memory::Read_U32(paramAddr);68memset(&request, 0, sizeof(request));69// Only copy the right size to support different request format70Memory::Memcpy(&request, paramAddr, size);7172ChangeStatusInit(NET_INIT_DELAY_US);7374// Eat any keys pressed before the dialog inited.75InitCommon();76UpdateButtons();7778connResult = -1;79scanInfosAddr = 0;80scanStep = 0;81startTime = (u64)(time_now_d() * 1000000.0);8283StartFade(true);84return 0;85}8687void PSPNetconfDialog::DrawBanner() {8889PPGeDrawRect(0, 0, 480, 22, CalcFadedColor(0x65636358));9091PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);92textStyle.hasShadow = false;9394// TODO: Draw a hexagon icon95PPGeDrawImage(10, 5, 11.0f, 10.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());96auto di = GetI18NCategory(I18NCat::DIALOG);97PPGeDrawText(di->T("Network Connection"), 31, 10, textStyle);98}99100void PSPNetconfDialog::DrawIndicator() {101// TODO: Draw animated circle as processing indicator102PPGeDrawImage(456, 248, 20.0f, 20.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());103}104105void PSPNetconfDialog::DisplayMessage(std::string_view text1, std::string_view text2a, std::string_view text2b, std::string_view text3a, std::string_view text3b, bool hasYesNo, bool hasOK) {106auto di = GetI18NCategory(I18NCat::DIALOG);107108PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_CENTER, FONT_SCALE);109PPGeStyle messageStyle = FadedStyle(PPGeAlign::BOX_HCENTER, FONT_SCALE);110PPGeStyle messageStyleRight = FadedStyle(PPGeAlign::BOX_RIGHT, FONT_SCALE);111PPGeStyle messageStyleLeft = FadedStyle(PPGeAlign::BOX_LEFT, FONT_SCALE);112113std::string text2 = std::string(text2a) + " " + std::string(text2b);114std::string text3 = std::string(text3a) + " " + std::string(text3b);115116// Without the scrollbar, we have 350 total pixels.117float WRAP_WIDTH = 300.0f;118if (UTF8StringNonASCIICount(text1) >= (int)text1.size() / 4) {119WRAP_WIDTH = 336.0f;120if (text1.size() > 12) {121messageStyle.scale = 0.6f;122}123}124125float totalHeight1 = 0.0f;126PPGeMeasureText(nullptr, &totalHeight1, text1, FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);127float totalHeight2 = 0.0f;128if (text2 != " ")129PPGeMeasureText(nullptr, &totalHeight2, text2, FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH);130float totalHeight3 = 0.0f;131if (text3 != " ")132PPGeMeasureText(nullptr, &totalHeight3, text3, FONT_SCALE, PPGE_LINE_USE_ELLIPSIS, WRAP_WIDTH);133float marginTop = 0.0f;134if (text2 != " " || text3 != " ")135marginTop = 11.0f;136float totalHeight = totalHeight1 + totalHeight2 + totalHeight3 + marginTop;137// The PSP normally only shows about 8 lines at a time.138// For improved UX, we intentionally show part of the next line.139float visibleHeight = std::min(totalHeight, 175.0f);140float h2 = visibleHeight / 2.0f;141142float centerY = 135.0f;143float sy = centerY - h2 - 15.0f;144float ey = centerY + h2 + 20.0f;145float buttonY = centerY + h2 + 5.0f;146147auto drawSelectionBoxAndAdjust = [&](float x) {148// Box has a fixed size.149float w = 15.0f;150float h = 8.0f;151PPGeDrawRect(x - w, buttonY - h, x + w, buttonY + h, CalcFadedColor(0x6DCFCFCF));152153centerY -= h + 5.0f;154sy -= h + 5.0f;155ey = buttonY + h * 2.0f + 5.0f;156};157158if (hasYesNo) {159if (yesnoChoice == 1) {160drawSelectionBoxAndAdjust(204.0f);161}162else {163drawSelectionBoxAndAdjust(273.0f);164}165166PPGeDrawText(di->T("Yes"), 203.0f, buttonY - 1.0f, buttonStyle);167PPGeDrawText(di->T("No"), 272.0f, buttonY - 1.0f, buttonStyle);168if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {169yesnoChoice = 1;170}171else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {172yesnoChoice = 0;173}174buttonY += 8.0f + 5.0f;175}176177if (hasOK) {178drawSelectionBoxAndAdjust(240.0f);179180PPGeDrawText(di->T("OK"), 239.0f, buttonY - 1.0f, buttonStyle);181buttonY += 8.0f + 5.0f;182}183184PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2));185PPGeDrawTextWrapped(text1, 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle);186if (!text2a.empty()) {187if (!text2b.empty())188PPGeDrawTextWrapped(text2a, 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight);189else190PPGeDrawTextWrapped(text2a, 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle);191}192if (!text2b.empty())193PPGeDrawTextWrapped(text2b, 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);194if (!text3a.empty()) {195if (!text3b.empty())196PPGeDrawTextWrapped(text3a, 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight);197else198PPGeDrawTextWrapped(text3a, 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle);199}200if (!text3b.empty())201PPGeDrawTextWrapped(text3b, 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft);202PPGeScissorReset();203204// Do we need a scrollbar?205if (visibleHeight < totalHeight) {206float scrollSpeed = 5.0f;207float scrollMax = totalHeight - visibleHeight;208209float bobHeight = (visibleHeight / totalHeight) * visibleHeight;210float bobOffset = (scrollPos_ / scrollMax) * (visibleHeight - bobHeight);211float bobY1 = centerY - h2 + bobOffset;212PPGeDrawRect(415.0f, bobY1, 420.0f, bobY1 + bobHeight, CalcFadedColor(0xFFCCCCCC));213214auto buttonDown = [this](int btn, int& held) {215if (IsButtonPressed(btn)) {216held = 0;217return true;218}219return IsButtonHeld(btn, held, 1, 1);220};221if (buttonDown(CTRL_DOWN, framesDownHeld_) && scrollPos_ < scrollMax) {222scrollPos_ = std::min(scrollMax, scrollPos_ + scrollSpeed);223}224if (buttonDown(CTRL_UP, framesUpHeld_) && scrollPos_ > 0.0f) {225scrollPos_ = std::max(0.0f, scrollPos_ - scrollSpeed);226}227}228229PPGeDrawRect(60.0f, sy, 420.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));230PPGeDrawRect(60.0f, ey, 420.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));231}232233int PSPNetconfDialog::Update(int animSpeed) {234if (ReadStatus() != SCE_UTILITY_STATUS_RUNNING) {235return SCE_ERROR_UTILITY_INVALID_STATUS;236}237238UpdateButtons();239UpdateCommon();240auto di = GetI18NCategory(I18NCat::DIALOG);241u64 now = (u64)(time_now_d() * 1000000.0);242243// It seems JPCSP doesn't check for NETCONF_STATUS_APNET244if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) {245int state = NetApctl_GetState();246247UpdateFade(animSpeed);248StartDraw();249250if (!hideNotice) {251auto err = GetI18NCategory(I18NCat::ERRORS);252const float WRAP_WIDTH = 254.0f;253const int confirmBtn = GetConfirmButton();254const int cancelBtn = GetCancelButton();255const ImageID confirmBtnImage = confirmBtn == CTRL_CROSS ? ImageID("I_CROSS") : ImageID("I_CIRCLE");256const ImageID cancelBtnImage = cancelBtn == CTRL_CIRCLE ? ImageID("I_CIRCLE") : ImageID("I_CROSS");257258PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_CENTER, 0.5f);259PPGeStyle buttonStyle = FadedStyle(PPGeAlign::BOX_LEFT, 0.5f);260261PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));262DrawBanner();263PPGeDrawTextWrapped(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);264PPGeDrawImage(confirmBtnImage, 185, 240, 20, 20, buttonStyle);265PPGeDrawText(di->T("OK"), 215, 243, buttonStyle);266PPGeDrawImage(cancelBtnImage, 255, 240, 20, 20, buttonStyle);267PPGeDrawText(di->T("Cancel"), 285, 243, buttonStyle);268269// 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)270if (IsButtonPressed(cancelBtn)) {271StartFade(false);272ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);273// TODO: When the dialog is aborted, does it really set the result to this?274// It seems to make Phantasy Star Portable 2 happy, so it should be okay for now.275request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;276}277else if (IsButtonPressed(confirmBtn)) {278hideNotice = true;279StartFade(true);280}281}282else {283PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));284DrawBanner();285DrawIndicator();286287if (state == PSP_NET_APCTL_STATE_GOT_IP || state == PSP_NET_APCTL_STATE_GETTING_IP) {288DisplayMessage(di->T("ObtainingIP", "Obtaining IP address.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);289}290else {291// Skipping the Select Connection screen since we only have 1 fake profile292DisplayMessage(di->T("ConnectingAP", "Connecting to the access point.\nPlease wait..."), di->T("ConnectionName", "Connection Name"), netApctlInfo.name, di->T("SSID"), netApctlInfo.ssid);293}294DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));295296// The Netconf dialog stays visible until the network reaches the state PSP_NET_APCTL_STATE_GOT_IP.297if (state == PSP_NET_APCTL_STATE_GOT_IP) {298if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {299StartFade(false);300ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);301}302}303304else if (state == PSP_NET_APCTL_STATE_JOINING) {305// Switch to the next message306StartFade(true);307}308309else if (state == PSP_NET_APCTL_STATE_DISCONNECTED) {310// When connecting with infrastructure, simulate a connection using the first network configuration entry.311if (connResult < 0) {312connResult = sceNetApctlConnect(1);313}314}315}316317EndDraw();318}319else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) {320int state = NetAdhocctl_GetState();321bool timedout = (state == ADHOCCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT);322323UpdateFade(animSpeed);324StartDraw();325PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));326DrawBanner();327DrawIndicator();328329if (timedout) {330// FIXME: Do we need to show error message?331std::string message(di->T("InternalError", "An internal error has occurred."));332DisplayMessage(message + StringFromFormat("\n(%08X)", connResult));333DisplayButtons(DS_BUTTON_CANCEL, di->T("Back"));334}335else {336std::string channel = std::to_string(g_Config.iWlanAdhocChannel);337if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC)338channel = "Automatic";339340DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), std::string(di->T("Channel:")) + std::string(" ") + std::string(di->T(channel)));341342// 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 anymore343if (request.netAction == NETCONF_JOIN_ADHOC)344DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));345346// 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_ADHOC347if (request.NetconfData.IsValid()) {348if (state == ADHOCCTL_STATE_DISCONNECTED) {349switch (request.netAction)350{351case NETCONF_CREATE_ADHOC:352if (connResult < 0) {353connResult = sceNetAdhocctlCreate(request.NetconfData->groupName);354}355break;356case NETCONF_JOIN_ADHOC:357// 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?358if (scanStep == 0) {359if (sceNetAdhocctlScan() >= 0) {360u32 structsz = sizeof(ScanInfos);361if (Memory::IsValidAddress(scanInfosAddr))362userMemory.Free(scanInfosAddr);363scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");364Memory::Write_U32(sizeof(SceNetAdhocctlScanInfoEmu), scanInfosAddr);365scanStep = 1;366}367}368else if (scanStep == 1) {369s32 sz = Memory::Read_U32(scanInfosAddr);370// Get required buffer size371if (sceNetAdhocctlGetScanInfo(scanInfosAddr, 0) >= 0) {372s32 reqsz = Memory::Read_U32(scanInfosAddr);373if (reqsz > sz) {374sz = reqsz;375if (Memory::IsValidAddress(scanInfosAddr))376userMemory.Free(scanInfosAddr);377u32 structsz = sz + sizeof(s32);378scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo");379Memory::Write_U32(sz, scanInfosAddr);380}381if (reqsz > 0) {382if (sceNetAdhocctlGetScanInfo(scanInfosAddr, scanInfosAddr + sizeof(s32)) >= 0) {383ScanInfos* scanInfos = (ScanInfos*)Memory::GetPointer(scanInfosAddr);384int n = scanInfos->sz / sizeof(SceNetAdhocctlScanInfoEmu);385// Assuming returned SceNetAdhocctlScanInfoEmu(s) are contagious where next is pointing to current addr + sizeof(SceNetAdhocctlScanInfoEmu)386while (n > 0) {387SceNetAdhocctlScanInfoEmu* si = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfosAddr + sizeof(s32) + sizeof(SceNetAdhocctlScanInfoEmu) * (n - 1LL));388if (memcmp(si->group_name.data, request.NetconfData->groupName, ADHOCCTL_GROUPNAME_LEN) == 0) {389// Moving found group info to the front so we can use it on sceNetAdhocctlJoin easily390memcpy((char*)scanInfos + sizeof(s32), si, sizeof(SceNetAdhocctlScanInfoEmu));391scanStep = 2;392break;393}394n--;395}396// Target group not found, try to scan again later397if (n <= 0) {398scanStep = 0;399}400}401}402// No group found, try to scan again later403else {404scanStep = 0;405}406}407}408else if (scanStep == 2) {409if (connResult < 0) {410connResult = sceNetAdhocctlJoin(scanInfosAddr + sizeof(s32));411if (connResult >= 0) {412// We are done!413if (Memory::IsValidAddress(scanInfosAddr))414userMemory.Free(scanInfosAddr);415scanInfosAddr = 0;416}417}418}419break;420default:421if (connResult < 0) {422connResult = sceNetAdhocctlConnect(request.NetconfData->groupName);423}424break;425}426}427}428}429430// The Netconf dialog stays visible until the network reaches the state ADHOCCTL_STATE_CONNECTED.431if (state == ADHOCCTL_STATE_CONNECTED) {432// Checking pendingStatus to make sure ChangeStatus not to continously extending the delay ticks on every call for eternity433if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {434StartFade(false);435ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);436}437438// Let's not leaks any memory439if (Memory::IsValidAddress(scanInfosAddr))440userMemory.Free(scanInfosAddr);441scanInfosAddr = 0;442}443444if ((request.netAction == NETCONF_JOIN_ADHOC || timedout) && IsButtonPressed(cancelButtonFlag)) {445StartFade(false);446ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US);447request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;448// Let's not leaks any memory449if (Memory::IsValidAddress(scanInfosAddr))450userMemory.Free(scanInfosAddr);451scanInfosAddr = 0;452}453454EndDraw();455}456457if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED)458Memory::Memcpy(requestAddr, &request, request.common.size, "NetConfDialogParam");459460return 0;461}462463int PSPNetconfDialog::Shutdown(bool force) {464if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force)465return SCE_ERROR_UTILITY_INVALID_STATUS;466467PSPDialog::Shutdown(force);468if (!force) {469ChangeStatusShutdown(NET_SHUTDOWN_DELAY_US);470}471472return 0;473}474475void PSPNetconfDialog::DoState(PointerWrap &p) {476PSPDialog::DoState(p);477478auto s = p.Section("PSPNetconfigDialog", 0, 2);479if (!s)480return;481482Do(p, request);483if (s >= 2) {484Do(p, scanInfosAddr);485Do(p, scanStep);486Do(p, connResult);487}488else {489scanInfosAddr = 0;490scanStep = 0;491connResult = -1;492}493494if (p.mode == p.MODE_READ) {495startTime = 0;496}497}498499pspUtilityDialogCommon* PSPNetconfDialog::GetCommonParam()500{501return &request.common;502}503504505