/** * 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; }