PSP net code first pass
This commit is contained in:
@@ -24,6 +24,11 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
pspvfpu
|
pspvfpu
|
||||||
pspvram
|
pspvram
|
||||||
psphprm
|
psphprm
|
||||||
|
pspnet
|
||||||
|
pspnet_inet
|
||||||
|
pspnet_apctl
|
||||||
|
psphttp
|
||||||
|
pspssl
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||||
@@ -39,6 +44,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
DUSK_OPENGL_LEGACY
|
DUSK_OPENGL_LEGACY
|
||||||
DUSK_DISPLAY_WIDTH=480
|
DUSK_DISPLAY_WIDTH=480
|
||||||
DUSK_DISPLAY_HEIGHT=272
|
DUSK_DISPLAY_HEIGHT=272
|
||||||
|
THREAD_PTHREAD=1
|
||||||
)
|
)
|
||||||
|
|
||||||
# Postbuild, create .pbp file for PSP.
|
# Postbuild, create .pbp file for PSP.
|
||||||
|
|||||||
+16
-11
@@ -28,12 +28,9 @@
|
|||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
|
|
||||||
/* Kept module-level only because engineUpdate needs them for the reset. */
|
|
||||||
static entityid_t phBoxEnt;
|
static entityid_t phBoxEnt;
|
||||||
static componentid_t phBoxPhys;
|
static componentid_t phBoxPhys;
|
||||||
|
|
||||||
/* ---- test network callbacks ---- */
|
|
||||||
|
|
||||||
static void onTestResponse(
|
static void onTestResponse(
|
||||||
const uint16_t status,
|
const uint16_t status,
|
||||||
const char_t *body,
|
const char_t *body,
|
||||||
@@ -48,6 +45,21 @@ static void onTestError(errorret_t error, void *user) {
|
|||||||
printf("HTTP error: %s\n", error.state->message);
|
printf("HTTP error: %s\n", error.state->message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void onNetworkConnected(void *user) {
|
||||||
|
printf("Network connected.\n");
|
||||||
|
networkHTTPRequest(
|
||||||
|
"http://10.0.0.94:3000/test",
|
||||||
|
NETWORK_HTTP_REQUEST_METHOD_GET,
|
||||||
|
NULL, NULL, 0, NULL,
|
||||||
|
onTestResponse,
|
||||||
|
onTestError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onNetworkFailed(errorret_t error, void *user) {
|
||||||
|
printf("Network connection failed: %s\n", error.state->message);
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
ENGINE.running = true;
|
ENGINE.running = true;
|
||||||
@@ -66,16 +78,9 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
entityManagerInit();
|
entityManagerInit();
|
||||||
physicsManagerInit();
|
physicsManagerInit();
|
||||||
errorChain(networkManagerInit());
|
errorChain(networkManagerInit());
|
||||||
|
networkManagerRequestConnection(onNetworkConnected, onNetworkFailed, NULL);
|
||||||
errorChain(gameInit());
|
errorChain(gameInit());
|
||||||
|
|
||||||
networkHTTPRequest(
|
|
||||||
"http://localhost:3000/test",
|
|
||||||
NETWORK_HTTP_REQUEST_METHOD_GET,
|
|
||||||
NULL, NULL, 0, NULL,
|
|
||||||
onTestResponse,
|
|
||||||
onTestError
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ---- Camera ---- */
|
/* ---- Camera ---- */
|
||||||
entityid_t cam = entityManagerAdd();
|
entityid_t cam = entityManagerAdd();
|
||||||
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
|
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
|
||||||
|
|||||||
@@ -22,6 +22,18 @@ errorret_t networkManagerUpdate() {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t networkManagerIsConnected() {
|
||||||
|
return networkPlatformIsConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void networkManagerRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
) {
|
||||||
|
networkPlatformRequestConnection(onConnected, onFailed, user);
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t networkManagerDispose() {
|
errorret_t networkManagerDispose() {
|
||||||
errorChain(networkPlatformDispose());
|
errorChain(networkPlatformDispose());
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
@@ -29,6 +29,45 @@ errorret_t networkManagerInit();
|
|||||||
*/
|
*/
|
||||||
errorret_t networkManagerUpdate();
|
errorret_t networkManagerUpdate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the system is connected to AN network, this doesn't mean that
|
||||||
|
* requests will succeed, doesn't mean there's internet, just that we could
|
||||||
|
* possibly make network requests. If this is false, this usually means
|
||||||
|
* something like;
|
||||||
|
* - A network cable is not connnected
|
||||||
|
* - No Wi-Fi Connection has been established
|
||||||
|
* - No IP Address has been assigned
|
||||||
|
*
|
||||||
|
* That kinda stuff.
|
||||||
|
*
|
||||||
|
* In future I will probably have REASONS for why it's not connected, for
|
||||||
|
* example;
|
||||||
|
* - On PSP you need to "request" network access
|
||||||
|
* - On GameCube, network settings need to be defined, including DHCP, etc.
|
||||||
|
* - On Windows, this may require additional permissions
|
||||||
|
*
|
||||||
|
* @return True if some network connection is detected.
|
||||||
|
*/
|
||||||
|
bool_t networkManagerIsConnected();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See networkManagerIsConnected for a bit more info, but this is for some
|
||||||
|
* platforms (mainly PSP) to request the system to start doing networking.
|
||||||
|
*
|
||||||
|
* You should only call this once and assume that it is "pending" until either
|
||||||
|
* onComplete or onFailed is invoked. If you call this twice it is undefined
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* @param onConnected Callback to invoke when the network is connected.
|
||||||
|
* @param onFailed Callback to invoke if the network connection fails.
|
||||||
|
* @param user User data to pass to the callbacks.
|
||||||
|
*/
|
||||||
|
void networkManagerRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of the network manager. This will NOT disconnect from the network.
|
* Disposes of the network manager. This will NOT disconnect from the network.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -190,6 +190,20 @@ errorret_t networkLinuxUpdate() {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t networkLinuxIsConnected() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void networkLinuxRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
) {
|
||||||
|
assertNotNull(onConnected, "onConnected must not be NULL");
|
||||||
|
assertNotNull(onFailed, "onFailed must not be NULL");
|
||||||
|
onConnected(user);
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t networkLinuxDispose() {
|
errorret_t networkLinuxDispose() {
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
threadMutexDispose(&NETWORK_LINUX.resultsMutex);
|
threadMutexDispose(&NETWORK_LINUX.resultsMutex);
|
||||||
|
|||||||
@@ -64,6 +64,26 @@ errorret_t networkLinuxInit();
|
|||||||
*/
|
*/
|
||||||
errorret_t networkLinuxUpdate();
|
errorret_t networkLinuxUpdate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true — Linux uses the OS network stack which is always available.
|
||||||
|
*
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
bool_t networkLinuxIsConnected();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No-op — Linux connection is managed by the OS. Calls onConnected immediately.
|
||||||
|
*
|
||||||
|
* @param onConnected Callback invoked immediately.
|
||||||
|
* @param onFailed Unused.
|
||||||
|
* @param user Passed to onConnected.
|
||||||
|
*/
|
||||||
|
void networkLinuxRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes the network manager.
|
* Disposes the network manager.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "networklinux.h"
|
#include "networklinux.h"
|
||||||
|
|
||||||
#define networkPlatformInit networkLinuxInit
|
#define networkPlatformInit networkLinuxInit
|
||||||
#define networkPlatformUpdate networkLinuxUpdate
|
#define networkPlatformUpdate networkLinuxUpdate
|
||||||
#define networkPlatformDispose networkLinuxDispose
|
#define networkPlatformDispose networkLinuxDispose
|
||||||
|
#define networkPlatformIsConnected networkLinuxIsConnected
|
||||||
|
#define networkPlatformRequestConnection networkLinuxRequestConnection
|
||||||
#define networkPlatformHTTPRequest networkLinuxHTTPRequest
|
#define networkPlatformHTTPRequest networkLinuxHTTPRequest
|
||||||
@@ -18,3 +18,4 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
|||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(input)
|
add_subdirectory(input)
|
||||||
add_subdirectory(log)
|
add_subdirectory(log)
|
||||||
|
add_subdirectory(network)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
networkpsp.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "networkpsp.h"
|
||||||
|
|
||||||
|
#define networkPlatformInit networkPspInit
|
||||||
|
#define networkPlatformUpdate networkPspUpdate
|
||||||
|
#define networkPlatformDispose networkPspDispose
|
||||||
|
#define networkPlatformIsConnected networkPspIsConnected
|
||||||
|
#define networkPlatformRequestConnection networkPspRequestConnection
|
||||||
|
#define networkPlatformHTTPRequest networkPspHTTPRequest
|
||||||
@@ -0,0 +1,390 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "networkpsp.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include <psphttp.h>
|
||||||
|
#include <pspnet.h>
|
||||||
|
#include <pspnet_inet.h>
|
||||||
|
#include <pspnet_apctl.h>
|
||||||
|
|
||||||
|
static networkpsp_t NETWORK_PSP;
|
||||||
|
|
||||||
|
/* ---- HTTP thread ---- */
|
||||||
|
|
||||||
|
static int networkPspMapMethod(const networkhttprequestmethod_t method) {
|
||||||
|
switch(method) {
|
||||||
|
case NETWORK_HTTP_REQUEST_METHOD_POST: return PSP_HTTP_METHOD_POST;
|
||||||
|
case NETWORK_HTTP_REQUEST_METHOD_HEAD: return PSP_HTTP_METHOD_HEAD;
|
||||||
|
case NETWORK_HTTP_REQUEST_METHOD_GET:
|
||||||
|
default:
|
||||||
|
return PSP_HTTP_METHOD_GET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void networkPspHTTPThread(thread_t *thread) {
|
||||||
|
assertNotNull(thread, "Thread must not be NULL");
|
||||||
|
assertNotNull(thread->data, "Thread data must not be NULL");
|
||||||
|
|
||||||
|
networkhttppendingitem_t *item = (networkhttppendingitem_t *)thread->data;
|
||||||
|
int templateId = -1;
|
||||||
|
int connId = -1;
|
||||||
|
int reqId = -1;
|
||||||
|
|
||||||
|
templateId = sceHttpCreateTemplate(NETWORK_PSP_AGENT, PSP_HTTP_VERSION_1_1, 1);
|
||||||
|
if(templateId < 0) {
|
||||||
|
stringCopy(item->errorMessage, "sceHttpCreateTemplate failed",
|
||||||
|
NETWORK_ERROR_MESSAGE_MAX - 1);
|
||||||
|
item->isError = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
connId = sceHttpCreateConnectionWithURL(templateId, item->url, 0);
|
||||||
|
if(connId < 0) {
|
||||||
|
stringCopy(item->errorMessage, "sceHttpCreateConnectionWithURL failed",
|
||||||
|
NETWORK_ERROR_MESSAGE_MAX - 1);
|
||||||
|
item->isError = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int contentLength = item->hasBody ? (unsigned int)strlen(item->body) : 0;
|
||||||
|
reqId = sceHttpCreateRequestWithURL(
|
||||||
|
connId, networkPspMapMethod(item->method), item->url,
|
||||||
|
(SceULong64)contentLength
|
||||||
|
);
|
||||||
|
if(reqId < 0) {
|
||||||
|
stringCopy(item->errorMessage, "sceHttpCreateRequestWithURL failed",
|
||||||
|
NETWORK_ERROR_MESSAGE_MAX - 1);
|
||||||
|
item->isError = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < item->headerCount; i++) {
|
||||||
|
sceHttpAddExtraHeader(reqId, item->headerKeys[i], item->headerVals[i], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
if(item->hasBody) {
|
||||||
|
ret = sceHttpSendRequest(reqId, item->body, contentLength);
|
||||||
|
} else {
|
||||||
|
ret = sceHttpSendRequest(reqId, NULL, 0);
|
||||||
|
}
|
||||||
|
if(ret < 0) {
|
||||||
|
stringCopy(item->errorMessage, "sceHttpSendRequest failed",
|
||||||
|
NETWORK_ERROR_MESSAGE_MAX - 1);
|
||||||
|
item->isError = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
int statusCode = 0;
|
||||||
|
ret = sceHttpGetStatusCode(reqId, &statusCode);
|
||||||
|
if(ret < 0) {
|
||||||
|
stringCopy(item->errorMessage, "sceHttpGetStatusCode failed",
|
||||||
|
NETWORK_ERROR_MESSAGE_MAX - 1);
|
||||||
|
item->isError = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
item->status = (uint16_t)statusCode;
|
||||||
|
|
||||||
|
/* Read response body in chunks into the fixed buffer. */
|
||||||
|
size_t written = 0;
|
||||||
|
char_t buf[512];
|
||||||
|
while(1) {
|
||||||
|
int read = sceHttpReadData(reqId, buf, sizeof(buf));
|
||||||
|
if(read <= 0) break;
|
||||||
|
|
||||||
|
size_t space = NETWORK_HTTP_RESPONSE_MAX - 1 - written;
|
||||||
|
size_t copy = (size_t)read;
|
||||||
|
if(copy > space) copy = space;
|
||||||
|
if(copy > 0) {
|
||||||
|
memcpy(item->responseBody + written, buf, copy);
|
||||||
|
written += copy;
|
||||||
|
item->responseBody[written] = '\0';
|
||||||
|
}
|
||||||
|
if(space == 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
item->isError = false;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if(reqId >= 0) sceHttpDeleteRequest(reqId);
|
||||||
|
if(connId >= 0) sceHttpDeleteConnection(connId);
|
||||||
|
if(templateId >= 0) sceHttpDeleteTemplate(templateId);
|
||||||
|
|
||||||
|
threadMutexLock(&NETWORK_PSP.resultsMutex);
|
||||||
|
item->resultReady = true;
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Dialog state machine (pumped every frame) ---- */
|
||||||
|
|
||||||
|
static void networkPspPumpDialog() {
|
||||||
|
switch(sceUtilityNetconfGetStatus()) {
|
||||||
|
case PSP_UTILITY_DIALOG_VISIBLE:
|
||||||
|
sceUtilityNetconfUpdate(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSP_UTILITY_DIALOG_QUIT:
|
||||||
|
sceUtilityNetconfShutdownStart();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSP_UTILITY_DIALOG_FINISHED: {
|
||||||
|
int apState = 0;
|
||||||
|
sceNetApctlGetState(&apState);
|
||||||
|
if(apState == PSP_NET_APCTL_STATE_GOT_IP) {
|
||||||
|
sceUtilityLoadNetModule(PSP_NET_MODULE_HTTP);
|
||||||
|
sceUtilityLoadNetModule(PSP_NET_MODULE_SSL);
|
||||||
|
sceHttpInit(NETWORK_PSP_HTTP_HEAP_SIZE);
|
||||||
|
NETWORK_PSP.state = NETWORK_PSP_STATE_CONNECTED;
|
||||||
|
if(NETWORK_PSP.onConnected) NETWORK_PSP.onConnected(NETWORK_PSP.connectionUser);
|
||||||
|
} else {
|
||||||
|
NETWORK_PSP.state = NETWORK_PSP_STATE_FAILED;
|
||||||
|
if(NETWORK_PSP.onFailed) {
|
||||||
|
errorstate_t errState;
|
||||||
|
errState.code = ERROR_NOT_OK;
|
||||||
|
errState.message = (char_t *)"WiFi connection failed or cancelled";
|
||||||
|
errState.lines = (char_t *)"";
|
||||||
|
errorret_t err;
|
||||||
|
err.code = ERROR_NOT_OK;
|
||||||
|
err.state = &errState;
|
||||||
|
NETWORK_PSP.onFailed(err, NETWORK_PSP.connectionUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Public API ---- */
|
||||||
|
|
||||||
|
errorret_t networkPspInit() {
|
||||||
|
memoryZero(&NETWORK_PSP, sizeof(networkpsp_t));
|
||||||
|
threadMutexInit(&NETWORK_PSP.resultsMutex);
|
||||||
|
NETWORK_PSP.state = NETWORK_PSP_STATE_DISCONNECTED;
|
||||||
|
|
||||||
|
/* Load base net modules (FW 2.00+ style). HTTP/SSL loaded after connection. */
|
||||||
|
int ret;
|
||||||
|
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
|
||||||
|
if(ret < 0) errorThrow("sceUtilityLoadNetModule(COMMON) failed: 0x%08X", ret);
|
||||||
|
|
||||||
|
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
|
||||||
|
if(ret < 0) errorThrow("sceUtilityLoadNetModule(INET) failed: 0x%08X", ret);
|
||||||
|
|
||||||
|
/* Init the network stack. */
|
||||||
|
ret = sceNetInit(0x20000, 0x20, 0x1000, 0x20, 0x1000);
|
||||||
|
if(ret < 0) errorThrow("sceNetInit failed: 0x%08X", ret);
|
||||||
|
|
||||||
|
ret = sceNetInetInit();
|
||||||
|
if(ret < 0) errorThrow("sceNetInetInit failed: 0x%08X", ret);
|
||||||
|
|
||||||
|
ret = sceNetApctlInit(0x1800, 0x30);
|
||||||
|
if(ret < 0) errorThrow("sceNetApctlInit failed: 0x%08X", ret);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t networkPspIsConnected() {
|
||||||
|
return NETWORK_PSP.state == NETWORK_PSP_STATE_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void networkPspRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
) {
|
||||||
|
assertNotNull(onConnected, "onConnected must not be NULL");
|
||||||
|
assertNotNull(onFailed, "onFailed must not be NULL");
|
||||||
|
|
||||||
|
NETWORK_PSP.onConnected = onConnected;
|
||||||
|
NETWORK_PSP.onFailed = onFailed;
|
||||||
|
NETWORK_PSP.connectionUser = user;
|
||||||
|
NETWORK_PSP.state = NETWORK_PSP_STATE_DIALOG;
|
||||||
|
|
||||||
|
/* Configure and launch the netconf WiFi dialog.
|
||||||
|
* The dialog is pumped each frame in networkPspUpdate; it must not block. */
|
||||||
|
memoryZero(&NETWORK_PSP.dialogAdhoc, sizeof(NETWORK_PSP.dialogAdhoc));
|
||||||
|
memoryZero(&NETWORK_PSP.dialogData, sizeof(NETWORK_PSP.dialogData));
|
||||||
|
NETWORK_PSP.dialogData.base.size = sizeof(pspUtilityNetconfData);
|
||||||
|
NETWORK_PSP.dialogData.base.language = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
||||||
|
NETWORK_PSP.dialogData.base.buttonSwap = PSP_UTILITY_ACCEPT_CROSS;
|
||||||
|
NETWORK_PSP.dialogData.base.graphicsThread = 17;
|
||||||
|
NETWORK_PSP.dialogData.base.accessThread = 19;
|
||||||
|
NETWORK_PSP.dialogData.base.fontThread = 18;
|
||||||
|
NETWORK_PSP.dialogData.base.soundThread = 16;
|
||||||
|
NETWORK_PSP.dialogData.action = PSP_NETCONF_ACTION_CONNECTAP;
|
||||||
|
NETWORK_PSP.dialogData.adhocparam = &NETWORK_PSP.dialogAdhoc;
|
||||||
|
|
||||||
|
int ret = sceUtilityNetconfInitStart(&NETWORK_PSP.dialogData);
|
||||||
|
if(ret < 0) {
|
||||||
|
NETWORK_PSP.state = NETWORK_PSP_STATE_FAILED;
|
||||||
|
errorstate_t errState;
|
||||||
|
errState.code = ERROR_NOT_OK;
|
||||||
|
errState.message = (char_t *)"sceUtilityNetconfInitStart failed";
|
||||||
|
errState.lines = (char_t *)"";
|
||||||
|
errorret_t err;
|
||||||
|
err.code = ERROR_NOT_OK;
|
||||||
|
err.state = &errState;
|
||||||
|
onFailed(err, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t networkPspUpdate() {
|
||||||
|
if(NETWORK_PSP.state == NETWORK_PSP_STATE_DIALOG) {
|
||||||
|
networkPspPumpDialog();
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(NETWORK_PSP.state == NETWORK_PSP_STATE_FAILED) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drain completed HTTP requests on the main thread. */
|
||||||
|
for(int32_t i = 0; i < NETWORK_HTTP_PENDING_MAX; i++) {
|
||||||
|
threadMutexLock(&NETWORK_PSP.resultsMutex);
|
||||||
|
|
||||||
|
networkhttppendingitem_t *item = &NETWORK_PSP.requests[i];
|
||||||
|
if(!item->used || !item->resultReady) {
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t isError = item->isError;
|
||||||
|
uint16_t status = item->status;
|
||||||
|
networkhttpcallback_t cb = item->callback;
|
||||||
|
networkhttperrorcallback_t errCb = item->errorCallback;
|
||||||
|
void *user = item->user;
|
||||||
|
const char_t *responseBody = item->responseBody;
|
||||||
|
const char_t *errorMessage = item->errorMessage;
|
||||||
|
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
|
||||||
|
if(isError) {
|
||||||
|
errorstate_t errState;
|
||||||
|
errState.code = ERROR_NOT_OK;
|
||||||
|
errState.message = (char_t *)errorMessage;
|
||||||
|
errState.lines = (char_t *)"";
|
||||||
|
errorret_t err;
|
||||||
|
err.code = ERROR_NOT_OK;
|
||||||
|
err.state = &errState;
|
||||||
|
errCb(err, user);
|
||||||
|
} else {
|
||||||
|
cb(status, responseBody, NULL, 0, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
threadMutexLock(&NETWORK_PSP.resultsMutex);
|
||||||
|
item->used = false;
|
||||||
|
item->resultReady = false;
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
}
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t networkPspDispose() {
|
||||||
|
if(NETWORK_PSP.state == NETWORK_PSP_STATE_CONNECTED) {
|
||||||
|
sceHttpEnd();
|
||||||
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_SSL);
|
||||||
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_HTTP);
|
||||||
|
}
|
||||||
|
|
||||||
|
sceNetApctlTerm();
|
||||||
|
sceNetInetTerm();
|
||||||
|
sceNetTerm();
|
||||||
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
|
||||||
|
sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
|
||||||
|
|
||||||
|
threadMutexDispose(&NETWORK_PSP.resultsMutex);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void networkPspHTTPRequest(
|
||||||
|
const char_t *url,
|
||||||
|
const networkhttprequestmethod_t method,
|
||||||
|
const char_t *bodyOrNull,
|
||||||
|
const networkhttpheader_t *headers,
|
||||||
|
const uint32_t headerCount,
|
||||||
|
void *user,
|
||||||
|
networkhttpcallback_t callback,
|
||||||
|
networkhttperrorcallback_t errorCallback
|
||||||
|
) {
|
||||||
|
assertStrLenMin(url, 1, "URL must be non-empty");
|
||||||
|
assertStrLenMax(url, NETWORK_HTTP_URL_MAX, "URL exceeds maximum length");
|
||||||
|
assertNotNull(callback, "Callback must be non-NULL");
|
||||||
|
assertNotNull(errorCallback, "Error callback must be non-NULL");
|
||||||
|
assertTrue(headerCount == 0 || headers != NULL, "Headers must not be NULL when headerCount > 0");
|
||||||
|
|
||||||
|
if(NETWORK_PSP.state != NETWORK_PSP_STATE_CONNECTED) {
|
||||||
|
errorstate_t errState;
|
||||||
|
errState.code = ERROR_NOT_OK;
|
||||||
|
errState.message = (char_t *)"Network not connected";
|
||||||
|
errState.lines = (char_t *)"";
|
||||||
|
errorret_t err;
|
||||||
|
err.code = ERROR_NOT_OK;
|
||||||
|
err.state = &errState;
|
||||||
|
errorCallback(err, user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
threadMutexLock(&NETWORK_PSP.resultsMutex);
|
||||||
|
|
||||||
|
networkhttppendingitem_t *item = NULL;
|
||||||
|
for(int32_t i = 0; i < NETWORK_HTTP_PENDING_MAX; i++) {
|
||||||
|
if(!NETWORK_PSP.requests[i].used) {
|
||||||
|
item = &NETWORK_PSP.requests[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!item) {
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
errorstate_t errState;
|
||||||
|
errState.code = ERROR_NOT_OK;
|
||||||
|
errState.message = (char_t *)"No free HTTP request slots";
|
||||||
|
errState.lines = (char_t *)"";
|
||||||
|
errorret_t err;
|
||||||
|
err.code = ERROR_NOT_OK;
|
||||||
|
err.state = &errState;
|
||||||
|
errorCallback(err, user);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryZero(item, sizeof(networkhttppendingitem_t));
|
||||||
|
item->used = true;
|
||||||
|
|
||||||
|
threadMutexUnlock(&NETWORK_PSP.resultsMutex);
|
||||||
|
|
||||||
|
stringCopy(item->url, url, NETWORK_HTTP_URL_MAX - 1);
|
||||||
|
item->method = method;
|
||||||
|
|
||||||
|
if(bodyOrNull != NULL) {
|
||||||
|
assertStrLenMax(bodyOrNull, NETWORK_HTTP_BODY_MAX, "Body exceeds maximum length");
|
||||||
|
stringCopy(item->body, bodyOrNull, NETWORK_HTTP_BODY_MAX - 1);
|
||||||
|
item->hasBody = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hdrCount = headerCount;
|
||||||
|
if(hdrCount > NETWORK_HTTP_HEADER_MAX) hdrCount = NETWORK_HTTP_HEADER_MAX;
|
||||||
|
item->headerCount = hdrCount;
|
||||||
|
for(uint32_t i = 0; i < hdrCount; i++) {
|
||||||
|
assertStrLenMax(headers[i].key, NETWORK_HTTP_HEADER_KEY_MAX, "Header key exceeds maximum length");
|
||||||
|
assertStrLenMax(headers[i].value, NETWORK_HTTP_HEADER_VAL_MAX, "Header value exceeds maximum length");
|
||||||
|
stringCopy(item->headerKeys[i], headers[i].key, NETWORK_HTTP_HEADER_KEY_MAX - 1);
|
||||||
|
stringCopy(item->headerVals[i], headers[i].value, NETWORK_HTTP_HEADER_VAL_MAX - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
item->callback = callback;
|
||||||
|
item->errorCallback = errorCallback;
|
||||||
|
item->user = user;
|
||||||
|
|
||||||
|
threadInit(&item->thread, networkPspHTTPThread);
|
||||||
|
item->thread.data = item;
|
||||||
|
threadStartRequest(&item->thread);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "network/networkhttprequest.h"
|
||||||
|
#include "thread/thread.h"
|
||||||
|
#include <psputility.h>
|
||||||
|
|
||||||
|
#define NETWORK_HTTP_PENDING_MAX 4
|
||||||
|
#define NETWORK_HTTP_URL_MAX 512
|
||||||
|
#define NETWORK_HTTP_BODY_MAX 2048
|
||||||
|
#define NETWORK_HTTP_RESPONSE_MAX 16384
|
||||||
|
#define NETWORK_HTTP_HEADER_MAX 8
|
||||||
|
#define NETWORK_HTTP_HEADER_KEY_MAX 64
|
||||||
|
#define NETWORK_HTTP_HEADER_VAL_MAX 256
|
||||||
|
#define NETWORK_ERROR_MESSAGE_MAX 256
|
||||||
|
#define NETWORK_PSP_AGENT "DuskEngine/1.0"
|
||||||
|
#define NETWORK_PSP_HTTP_HEAP_SIZE 0x25800
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NETWORK_PSP_STATE_DISCONNECTED, /* Init done, no connection requested yet */
|
||||||
|
NETWORK_PSP_STATE_DIALOG, /* WiFi netconf dialog is active */
|
||||||
|
NETWORK_PSP_STATE_CONNECTED, /* AP connected, HTTP ready */
|
||||||
|
NETWORK_PSP_STATE_FAILED, /* Connection attempt failed or cancelled */
|
||||||
|
} networkpspstate_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool_t used;
|
||||||
|
volatile bool_t resultReady;
|
||||||
|
bool_t isError;
|
||||||
|
char_t url[NETWORK_HTTP_URL_MAX];
|
||||||
|
networkhttprequestmethod_t method;
|
||||||
|
char_t body[NETWORK_HTTP_BODY_MAX];
|
||||||
|
bool_t hasBody;
|
||||||
|
char_t headerKeys[NETWORK_HTTP_HEADER_MAX][NETWORK_HTTP_HEADER_KEY_MAX];
|
||||||
|
char_t headerVals[NETWORK_HTTP_HEADER_MAX][NETWORK_HTTP_HEADER_VAL_MAX];
|
||||||
|
uint32_t headerCount;
|
||||||
|
networkhttpcallback_t callback;
|
||||||
|
networkhttperrorcallback_t errorCallback;
|
||||||
|
void *user;
|
||||||
|
uint16_t status;
|
||||||
|
char_t responseBody[NETWORK_HTTP_RESPONSE_MAX];
|
||||||
|
char_t errorMessage[NETWORK_ERROR_MESSAGE_MAX];
|
||||||
|
thread_t thread;
|
||||||
|
} networkhttppendingitem_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
networkpspstate_t state;
|
||||||
|
pspUtilityNetconfData dialogData;
|
||||||
|
struct pspUtilityNetconfAdhoc dialogAdhoc;
|
||||||
|
void (*onConnected)(void *user);
|
||||||
|
void (*onFailed)(errorret_t error, void *user);
|
||||||
|
void *connectionUser;
|
||||||
|
networkhttppendingitem_t requests[NETWORK_HTTP_PENDING_MAX];
|
||||||
|
threadmutex_t resultsMutex;
|
||||||
|
} networkpsp_t;
|
||||||
|
|
||||||
|
errorret_t networkPspInit();
|
||||||
|
errorret_t networkPspUpdate();
|
||||||
|
errorret_t networkPspDispose();
|
||||||
|
bool_t networkPspIsConnected();
|
||||||
|
void networkPspRequestConnection(
|
||||||
|
void (*onConnected)(void *user),
|
||||||
|
void (*onFailed)(errorret_t error, void *user),
|
||||||
|
void *user
|
||||||
|
);
|
||||||
|
void networkPspHTTPRequest(
|
||||||
|
const char_t *url,
|
||||||
|
const networkhttprequestmethod_t method,
|
||||||
|
const char_t *bodyOrNull,
|
||||||
|
const networkhttpheader_t *headers,
|
||||||
|
const uint32_t headerCount,
|
||||||
|
void *user,
|
||||||
|
networkhttpcallback_t callback,
|
||||||
|
networkhttperrorcallback_t errorCallback
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user