308 lines
9.4 KiB
C
308 lines
9.4 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "network/network.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include "assert/assert.h"
|
|
#include "display/displaysdl2.h"
|
|
#include "display/shader/shadergl.h"
|
|
|
|
errorret_t networkPSPInit() {
|
|
// Requests the PSP to load the network modules.
|
|
int ret;
|
|
|
|
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
|
|
if(ret < 0) errorThrow("Failed to init NET COMMON: 0x%08X", ret);
|
|
|
|
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
|
|
if(ret < 0) errorThrow("Failed to init NET INET: 0x%08X", ret);
|
|
|
|
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEURI);
|
|
// if(ret < 0) errorThrow("Failed to init NET PARSEURI: 0x%08X", ret);
|
|
|
|
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEHTTP);
|
|
// if(ret < 0) errorThrow("Failed to init NET PARSEHTTP: 0x%08X", ret);
|
|
|
|
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_SSL);
|
|
// if(ret < 0) errorThrow("Failed to init NET SSL: 0x%08X", ret);
|
|
|
|
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_HTTP);
|
|
// if(ret < 0) errorThrow("Failed to init NET HTTP: 0x%08X", ret);
|
|
|
|
// ret = sceSslInit(0x28000);
|
|
// if(ret < 0) errorThrow("sceSslInit failed: 0x%08X", ret);
|
|
|
|
// ret = sceHttpInit(0x25800);
|
|
// if(ret < 0) errorThrow("sceHttpInit failed: 0x%08X", ret);
|
|
|
|
// ret = sceHttpsInit(0, 0, 0, 0);
|
|
// if(ret < 0) errorThrow("sceHttpsInit failed: 0x%08X", ret);
|
|
|
|
// ret = sceHttpsLoadDefaultCert(0, 0);
|
|
// if(ret < 0) errorThrow("sceHttpsLoadDefaultCert failed: 0x%08X", ret);
|
|
|
|
// ret = sceHttpLoadSystemCookie();
|
|
// if(ret < 0) errorThrow("sceHttpLoadSystemCookie failed: 0x%08X", ret);
|
|
|
|
// All good.
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t networkPSPUpdate() {
|
|
int ret;
|
|
if(NETWORK.state == NETWORK_STATE_CONNECTING) {
|
|
switch(sceUtilityNetconfGetStatus()) {
|
|
case PSP_UTILITY_DIALOG_INIT:
|
|
break;
|
|
|
|
case PSP_UTILITY_DIALOG_NONE:
|
|
NETWORK.state = NETWORK_STATE_DISCONNECTED;
|
|
errorThrow("PSP Netconf dialog disappeared without result");
|
|
break;
|
|
|
|
case PSP_UTILITY_DIALOG_VISIBLE:
|
|
// The dialog renders using whatever GL state we leave lying
|
|
// around, immediately before it draws - it doesn't set up its
|
|
// own blend/cull/depth/texture state from scratch. Match exactly
|
|
// what our own text rendering uses (displaySetState's
|
|
// BLEND-only flags, plus shaderUnlitSetMaterial's texture/color
|
|
// setup), otherwise its text/icons render missing while its
|
|
// untextured chrome still appears fine.
|
|
glDisable(GL_CULL_FACE);
|
|
errorChain(errorGLCheck());
|
|
glDisable(GL_DEPTH_TEST);
|
|
errorChain(errorGLCheck());
|
|
glEnable(GL_BLEND);
|
|
errorChain(errorGLCheck());
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
errorChain(errorGLCheck());
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
errorChain(errorGLCheck());
|
|
glBindTexture(GL_TEXTURE_2D, TEXTURE_WHITE.id);
|
|
errorChain(errorGLCheck());
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
errorChain(errorGLCheck());
|
|
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
errorChain(errorGLCheck());
|
|
SHADER_LEGACY.boundTextureId = TEXTURE_WHITE.id;
|
|
|
|
// pspGL defers actually applying state to the GPU until the
|
|
// next real draw call - the calls above alone may be no-ops
|
|
// on hardware. Force a flush with a degenerate (zero-area, so
|
|
// nothing actually rasterizes) triangle.
|
|
const float_t degenerate[3 * 5] = {
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
|
};
|
|
const GLsizei stride = sizeof(float_t) * 5;
|
|
glVertexPointer(3, GL_FLOAT, stride, °enerate[0]);
|
|
errorChain(errorGLCheck());
|
|
glTexCoordPointer(2, GL_FLOAT, stride, °enerate[3]);
|
|
errorChain(errorGLCheck());
|
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
errorChain(errorGLCheck());
|
|
|
|
// 1 is mandatory?
|
|
ret = sceUtilityNetconfUpdate(1);
|
|
if(ret != 0) {
|
|
errorThrow("sceUtilityNetconfUpdate failed: 0x%08X", ret);
|
|
}
|
|
break;
|
|
|
|
case PSP_UTILITY_DIALOG_QUIT:
|
|
ret = sceUtilityNetconfShutdownStart();
|
|
if(ret != 0) {
|
|
errorThrow("sceUtilityNetconfShutdownStart failed: 0x%08X", ret);
|
|
}
|
|
break;
|
|
|
|
case PSP_UTILITY_DIALOG_FINISHED:
|
|
// Did we connect?
|
|
int apState = 0;
|
|
sceNetApctlGetState(&apState);
|
|
|
|
if(apState == PSP_NET_APCTL_STATE_GOT_IP) {
|
|
NETWORK.state = NETWORK_STATE_CONNECTED;
|
|
assertNotNull(
|
|
NETWORK.platform.onConnected,
|
|
"Network platform onConnected callback should be set."
|
|
);
|
|
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
|
|
} else {
|
|
NETWORK.state = NETWORK_STATE_DISCONNECTED;
|
|
|
|
assertNotNull(
|
|
NETWORK.platform.onFailed,
|
|
"Network platform onFailed callback should be set."
|
|
);
|
|
|
|
// Kill the PSP network stack.
|
|
errorret_t err = networkPSPTerm();
|
|
if(errorIsNotOk(err)) {
|
|
errorCatch(errorPrint(err));
|
|
}
|
|
|
|
errorret_t error = errorThrowImpl(
|
|
&NETWORK.errorState,
|
|
ERROR_NOT_OK,
|
|
__FILE__, __func__, __LINE__,
|
|
"Failed to connect to network"
|
|
);
|
|
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
errorThrow(
|
|
"Unknown PSP Netconf dialog status: %d", sceUtilityNetconfGetStatus()
|
|
);
|
|
}
|
|
}
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t networkPSPDispose() {
|
|
sceUtilityNetconfGetStatus();
|
|
errorCatch(errorPrint(networkPSPTerm()));
|
|
|
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_HTTP);
|
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
|
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
|
|
|
|
errorOk();
|
|
}
|
|
|
|
bool_t networkPSPIsConnected() {
|
|
return NETWORK.state == NETWORK_STATE_CONNECTED;
|
|
}
|
|
|
|
void networkPSPRequestConnection(
|
|
void (*onConnected)(void *user),
|
|
void (*onFailed)(errorret_t error, void *user),
|
|
void (*onDisconnect)(errorret_t error, void *user),
|
|
void *user
|
|
) {
|
|
assertTrue(
|
|
NETWORK.state == NETWORK_STATE_CONNECTING,
|
|
"Network host should be in a connecting state."
|
|
);
|
|
|
|
NETWORK.platform.onConnected = onConnected;
|
|
NETWORK.platform.onFailed = onFailed;
|
|
NETWORK.platform.onConnectedUser = user;
|
|
|
|
int ret;
|
|
|
|
// Init the PSP network stack.
|
|
ret = sceNetInit(0x20000, 0x20, 4096, 0x20, 4096);
|
|
assertTrue(ret >= 0, "Failed to init net: 0x%08X");
|
|
|
|
ret = sceNetInetInit();
|
|
assertTrue(ret >= 0, "Failed to init net inet: 0x%08X");
|
|
|
|
ret = sceNetResolverInit();
|
|
assertTrue(ret >= 0, "Failed to init net resolver: 0x%08X");
|
|
|
|
ret = sceNetApctlInit(0x1800, 0x30);
|
|
assertTrue(ret >= 0, "Failed to init net apctl: 0x%08X");
|
|
|
|
|
|
// This is all related to getting the PSP online, refer to;
|
|
// https://github.com/joel16/CMFileManager-PSP/blob/00dab16c64cd48bf6452fc274a3b898d77c39a8d/app/source/net.cpp#L97
|
|
// since I follow this implementation closely.
|
|
memoryZero(&NETWORK.platform.dialogData, sizeof(NETWORK.platform.dialogData));
|
|
memoryZero(
|
|
&NETWORK.platform.dialogAdhoc, sizeof(NETWORK.platform.dialogAdhoc)
|
|
);
|
|
|
|
NETWORK.platform.dialogData.base.size = sizeof(pspUtilityNetconfData);
|
|
NETWORK.platform.dialogData.base.language = systemPSPGetLanguage();
|
|
NETWORK.platform.dialogData.base.buttonSwap =
|
|
systemPSPGetCrossButtonSetting();
|
|
NETWORK.platform.dialogData.base.graphicsThread = 17;
|
|
NETWORK.platform.dialogData.base.accessThread = 19;
|
|
NETWORK.platform.dialogData.base.fontThread = 18;
|
|
NETWORK.platform.dialogData.base.soundThread = 16;
|
|
NETWORK.platform.dialogData.action = PSP_NETCONF_ACTION_CONNECTAP;
|
|
NETWORK.platform.dialogData.adhocparam = (
|
|
&NETWORK.platform.dialogAdhoc
|
|
);
|
|
|
|
ret = sceUtilityNetconfInitStart(&NETWORK.platform.dialogData);
|
|
assertTrue(ret >= 0, "Failed to init netconf");
|
|
}
|
|
|
|
void networkPSPRequestDisconnection(
|
|
void (*onComplete)(void *user),
|
|
void *user
|
|
) {
|
|
assertTrue(
|
|
NETWORK.state == NETWORK_STATE_DISCONNECTING,
|
|
"Network host should be in a disconnecting state."
|
|
);
|
|
|
|
errorret_t err = networkPSPTerm();
|
|
if(errorIsNotOk(err)) {
|
|
errorCatch(errorPrint(err));
|
|
}
|
|
|
|
NETWORK.state = NETWORK_STATE_DISCONNECTED;
|
|
onComplete(user);
|
|
}
|
|
|
|
errorret_t networkPSPTerm() {
|
|
int_t ret;
|
|
ret = sceNetApctlTerm();
|
|
if(ret < 0) {
|
|
errorThrow("Failed to terminate netctl: 0x%08X", ret);
|
|
}
|
|
|
|
ret = sceNetResolverTerm();
|
|
if(ret < 0) {
|
|
errorThrow("Failed to terminate net resolver: 0x%08X", ret);
|
|
}
|
|
|
|
ret = sceNetInetTerm();
|
|
if(ret < 0) {
|
|
errorThrow("Failed to terminate net inet: 0x%08X", ret);
|
|
}
|
|
|
|
ret = sceNetTerm();
|
|
if(ret < 0) {
|
|
errorThrow("Failed to terminate net: 0x%08X", ret);
|
|
}
|
|
|
|
errorOk();
|
|
}
|
|
|
|
networkinfo_t networkPSPGetInfo() {
|
|
networkinfo_t netInfo;
|
|
memoryZero(&netInfo, sizeof(networkinfo_t));
|
|
|
|
// Get the IP address of the current connection.
|
|
union SceNetApctlInfo info;
|
|
int ret = sceNetApctlGetInfo(PSP_NET_APCTL_INFO_IP, &info);
|
|
assertTrue(ret >= 0, "Failed to get IP address");
|
|
|
|
// Parse the IP address string into octets.
|
|
netInfo.type = NETWORK_TYPE_IPV4;
|
|
ret = sscanf(
|
|
info.ip,
|
|
"%hhu.%hhu.%hhu.%hhu",
|
|
&netInfo.ipv4.ip[0],
|
|
&netInfo.ipv4.ip[1],
|
|
&netInfo.ipv4.ip[2],
|
|
&netInfo.ipv4.ip[3]
|
|
);
|
|
assertTrue(ret == 4, "Failed to parse IP address");
|
|
|
|
return netInfo;
|
|
} |