diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 2deab40f..03a75469 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -72,6 +72,7 @@ add_subdirectory(locale) add_subdirectory(physics) add_subdirectory(scene) add_subdirectory(script) +add_subdirectory(system) add_subdirectory(time) add_subdirectory(ui) add_subdirectory(network) diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index a69dd72e..0e0a2715 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -20,46 +20,41 @@ #include "entity/component/physics/entityphysics.h" #include "game/game.h" #include "physics/physicsmanager.h" -#include "network/networkmanager.h" -#include "network/networkhttprequest.h" +#include "network/network.h" #include "display/mesh/cube.h" #include "display/mesh/plane.h" engine_t ENGINE; +entityid_t phBoxEnt; +componentid_t phBoxPhys; +float_t disconnectTime = FLT_MAX; -static entityid_t phBoxEnt; -static componentid_t phBoxPhys; - -static void onTestResponse( - const uint16_t status, - const char_t *body, - const networkhttpheader_t *responseHeaders, - const uint32_t responseHeaderCount, - void *user -) { - printf("HTTP %d: %s\n", status, body); +void onNetworkConnected(void *user) { + disconnectTime = TIME.time + 3.0f; + printf("Network connected, I will disconnect at: %.2f1.\n", disconnectTime); + // networkHTTPRequest( + // "http://10.0.0.94:3000/test", + // NETWORK_HTTP_REQUEST_METHOD_GET, + // NULL, NULL, 0, NULL, + // onTestResponse, + // onTestError + // ); } -static void onTestError(errorret_t error, void *user) { - 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) { +void onNetworkFailed(errorret_t error, void *user) { printf("Network connection failed: %s\n", error.state->message); } +void onNetworkDisconnected(errorret_t error, void *user) { + printf("Network disconnected.\n"); + errorCatch(errorPrint(error)); +} + +void onNetworkDisconnectFinished(void *user) { + printf("Finished disconnecting from network.\n"); +} + errorret_t engineInit(const int32_t argc, const char_t **argv) { memoryZero(&ENGINE, sizeof(engine_t)); ENGINE.running = true; @@ -77,11 +72,19 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { errorChain(sceneInit()); entityManagerInit(); physicsManagerInit(); - errorChain(networkManagerInit()); - networkManagerRequestConnection(onNetworkConnected, onNetworkFailed, NULL); + errorChain(networkInit()); errorChain(gameInit()); - /* ---- Camera ---- */ + printf("Going online...\n"); + networkRequestConnection( + onNetworkConnected, + onNetworkFailed, + onNetworkDisconnected, + NULL + ); + + + // Camera entityid_t cam = entityManagerAdd(); componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION); float_t distance = 6.0f; @@ -94,11 +97,11 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA); entityCameraSetZFar(cam, camCam, distance * 6.0f); - /* ---- Static floor (visual + physics) ---- */ - entityid_t floorEnt = entityManagerAdd(); - componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION); + // Floor + entityid_t floorEnt = entityManagerAdd(); + componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION); componentid_t floorMesh = entityAddComponent(floorEnt, COMPONENT_TYPE_MESH); - componentid_t floorMat = entityAddComponent(floorEnt, COMPONENT_TYPE_MATERIAL); + componentid_t floorMat = entityAddComponent(floorEnt, COMPONENT_TYPE_MATERIAL); componentid_t floorPhys = entityAddComponent(floorEnt, COMPONENT_TYPE_PHYSICS); entityPositionSetPosition(floorEnt, floorPos, (vec3){ -5.0f, 0.0f, -5.0f }); @@ -107,27 +110,24 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { entityMaterialGetShaderMaterial(floorEnt, floorMat)->unlit.color = COLOR_GREEN; entityphysics_t *floorPhysData = entityPhysicsGet(floorEnt, floorPhys); - floorPhysData->type = PHYSICS_BODY_STATIC; - floorPhysData->shape.type = PHYSICS_SHAPE_PLANE; + floorPhysData->type = PHYSICS_BODY_STATIC; + floorPhysData->shape.type = PHYSICS_SHAPE_PLANE; floorPhysData->shape.data.plane.normal[0] = 0.0f; floorPhysData->shape.data.plane.normal[1] = 1.0f; floorPhysData->shape.data.plane.normal[2] = 0.0f; - floorPhysData->shape.data.plane.distance = 0.0f; + floorPhysData->shape.data.plane.distance = 0.0f; /* ---- Dynamic box ---- */ phBoxEnt = entityManagerAdd(); - componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION); + componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION); componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH); - componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL); + componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL); phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS); entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE); entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED; - - /* Physics position lives in the POSITION component. CUBE_MESH_SIMPLE is - * centred at origin (-0.5..0.5), so entity position == physics centre. */ entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f }); - + /* Run the init script. */ scriptcontext_t ctx; errorChain(scriptContextInit(&ctx)); @@ -138,11 +138,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { } errorret_t engineUpdate(void) { - errorChain(networkManagerUpdate()); - if(networkManagerIsConnectionModalOpen()) { - errorChain(displayUpdate()); - errorOk(); - } + errorChain(networkUpdate()); timeUpdate(); inputUpdate(); @@ -165,6 +161,12 @@ errorret_t engineUpdate(void) { if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; + if(TIME.time >= disconnectTime) { + printf("Time to disconnect from the network.\n"); + networkRequestDisconnection(onNetworkDisconnectFinished, NULL); + disconnectTime = FLT_MAX; + } + errorOk(); } @@ -173,7 +175,7 @@ void engineExit(void) { } errorret_t engineDispose(void) { - errorChain(networkManagerDispose()); + errorChain(networkDispose()); sceneDispose(); errorChain(gameDispose()); entityManagerDispose(); diff --git a/src/dusk/network/CMakeLists.txt b/src/dusk/network/CMakeLists.txt index a5823f7a..6680fc6d 100644 --- a/src/dusk/network/CMakeLists.txt +++ b/src/dusk/network/CMakeLists.txt @@ -5,6 +5,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - networkmanager.c - networkhttprequest.c + network.c ) diff --git a/src/dusk/network/network.c b/src/dusk/network/network.c new file mode 100644 index 00000000..9ebd57fd --- /dev/null +++ b/src/dusk/network/network.c @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "network.h" +#include "util/memory.h" +#include "assert/assert.h" +#include "log/log.h" + +network_t NETWORK; + +errorret_t networkInit() { + memoryZero(&NETWORK, sizeof(network_t)); + + NETWORK.errorState.code = ERROR_OK; + NETWORK.onDisconnect = NULL; + + return networkPlatformInit(); +} + +errorret_t networkUpdate() { + errorChain(networkPlatformUpdate()); + + if(NETWORK.state == NETWORK_STATE_CONNECTED && !networkIsConnected()) { + NETWORK.state = NETWORK_STATE_DISCONNECTED; + + if(NETWORK.onDisconnect) { + errorret_t ret; + if(NETWORK.errorState.code == ERROR_OK) { + ret = errorThrowImpl( + &NETWORK.errorState, + ERROR_NOT_OK, + __FILE__, __func__, __LINE__, + "Network connection lost" + ); + } else { + ret.code = NETWORK.errorState.code; + ret.state = &NETWORK.errorState; + } + NETWORK.onDisconnect(ret, NETWORK.disconnectUser); + } + } + + errorOk(); +} + +bool_t networkIsConnected() { + return networkPlatformIsConnected(); +} + +void networkRequestConnection( + void (*onConnected)(void *user), + void (*onFailed)(errorret_t error, void *user), + void (*onDisconnect)(errorret_t error, void *user), + void *user +) { + assertNotNull(onConnected, "onConnected callback must not be null"); + assertNotNull(onFailed, "onFailed callback must not be null"); + assertNotNull(onDisconnect, "onDisconnect callback must not be null"); + + NETWORK.state = NETWORK_STATE_CONNECTING; + NETWORK.onDisconnect = onDisconnect; + NETWORK.disconnectUser = user; + + #ifndef networkPlatformRequestConnection + // This is a platform cannot be requested to go online, this would basically + // be for platforms like Linux or Windows where the OS is responsible for + // maintaining the network connection. + + if(networkIsConnected()) { + NETWORK.state = NETWORK_STATE_CONNECTED; + onConnected(user); + } else { + errorret_t ret = errorThrowImpl( + &NETWORK.errorState, + ERROR_NOT_OK, + __FILE__, __func__, __LINE__, + "No network connection available" + ); + onFailed(ret, user); + } + #else + networkPlatformRequestConnection(onConnected, onFailed, onDisconnect, user); + #endif +} + +void networkRequestDisconnection( + void (*onComplete)(void *user), + void *user +) { + assertNotNull(onComplete, "onComplete callback must not be null"); + NETWORK.state = NETWORK_STATE_DISCONNECTING; + + #ifndef networkPlatformRequestDisconnection + NETWORK.state = NETWORK_STATE_DISCONNECTED; + onComplete(user); + #else + networkPlatformRequestDisconnection(onComplete, user); + #endif +} + +void networkDisconnectedDuringDispose(void *u) { + logDebug("Network disconnected during dispose\n"); +} + +errorret_t networkDispose() { + if(NETWORK.state == NETWORK_STATE_CONNECTED) { + networkRequestDisconnection(networkDisconnectedDuringDispose, NULL); + } + + errorChain(networkPlatformDispose()); + errorOk(); +} diff --git a/src/dusk/network/network.h b/src/dusk/network/network.h new file mode 100644 index 00000000..122739af --- /dev/null +++ b/src/dusk/network/network.h @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" +#include "network/networkplatform.h" +#ifndef networkPlatformInit + #error "networkPlatformInit must be defined" +#endif +#ifndef networkPlatformUpdate + #error "networkPlatformUpdate must be defined" +#endif +#ifndef networkPlatformDispose + #error "networkPlatformDispose must be defined" +#endif +#ifndef networkPlatformIsConnected + #error "networkPlatformIsConnected must be defined" +#endif + +typedef enum { + NETWORK_STATE_DISCONNECTED, + NETWORK_STATE_CONNECTING, + NETWORK_STATE_CONNECTED, + NETWORK_STATE_DISCONNECTING, +} networkstate_t; + +typedef struct { + networkplatform_t platform; + errorstate_t errorState; + networkstate_t state; + + void (*onDisconnect)(errorret_t error, void *user); + void *disconnectUser; +} network_t; + +extern network_t NETWORK; + +/** + * Initializes the network system. This will NOT connect to the network. + * + * @return An error code indicating success or failure. + */ +errorret_t networkInit(); + +/** + * Updates the network manager, dispatching any completed async request + * callbacks on the main thread. + * + * @return An error code indicating success or failure. + */ +errorret_t networkUpdate(); + +/** + * Disposes of the network manager. This will NOT disconnect from the network. + * + * @return An error code indicating success or failure. + */ +errorret_t networkDispose(); + +/** + * 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 networkIsConnected(); + +/** + * See networkIsConnected 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. + * + * onDisconnect must be provided, and is called whenever the network is lost + * after a successful connection. This will NOT be called if disconnect is + * manually triggered, but WILL if an error occurs, or a network stack bug, etc. + * + * @param onConnected Callback to invoke when the network is connected. + * @param onFailed Callback to invoke if the network connection fails. + * @param onDisconnect Called after a successful connection, when disconnected. + * @param user User data to pass to the callbacks. + */ +void networkRequestConnection( + void (*onConnected)(void *user), + void (*onFailed)(errorret_t error, void *user), + void (*onDisconnect)(errorret_t error, void *user), + void *user +); + +/** + * Requests the system to disconnect from the network. This is basically just + * for PSP, but I guess it could be used on other platforms in future if they + * have some kind of "network connection mode" that needs to be exited. + * + * You should only call this once and assume that it is "pending" until + * onComplete is invoked. If you call this twice it is undefined behavior. + * + * If it fails, you still get onComplete called, but may fail if you try to + * reconnect later unfortunately. + * + * @param onComplete Callback to invoke when the network is disconnected. + * @param user User data to pass to the callback. + */ +void networkRequestDisconnection( + void (*onComplete)(void *user), + void *user +); \ No newline at end of file diff --git a/src/dusk/network/networkhttprequest.c b/src/dusk/network/networkhttprequest.c deleted file mode 100644 index c4017f52..00000000 --- a/src/dusk/network/networkhttprequest.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "networkhttprequest.h" -#include "assert/assert.h" -#include "network/networkplatform.h" - -void networkHTTPRequest( - 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"); - 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"); - - uint32_t count = (headers == NULL) ? 0 : headerCount; - - networkPlatformHTTPRequest( - url, method, bodyOrNull, headers, count, user, callback, errorCallback - ); -} diff --git a/src/dusk/network/networkhttprequest.h b/src/dusk/network/networkhttprequest.h deleted file mode 100644 index 953a81f7..00000000 --- a/src/dusk/network/networkhttprequest.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -typedef enum { - NETWORK_HTTP_REQUEST_METHOD_GET, - NETWORK_HTTP_REQUEST_METHOD_POST, - NETWORK_HTTP_REQUEST_METHOD_PUT, - NETWORK_HTTP_REQUEST_METHOD_DELETE, - NETWORK_HTTP_REQUEST_METHOD_HEAD, - NETWORK_HTTP_REQUEST_METHOD_OPTIONS, -} networkhttprequestmethod_t; - -typedef struct { - const char_t *key; - const char_t *value; -} networkhttpheader_t; - -typedef void (*networkhttpcallback_t)( - const uint16_t status, - const char_t *body, - const networkhttpheader_t *responseHeaders, - const uint32_t responseHeaderCount, - void *user -); - -typedef void (*networkhttperrorcallback_t)( - errorret_t error, - void *user -); - -/** - * Makes a HTTP Request. The request itself is asynchronous but the callback - * will be invoked on the main thread when next available. - * - * @param url URL to request. - * @param method Method to use for the request. - * @param bodyOrNull If POST or PUT, custom body string, can be NULL. - * @param headers Array of key-value headers. - * @param headerCount Count of headers, can be anything if headers is NULL. - * @param user Callback pointer received to the callback. - * @param callback The callback to invoke when the request completes. - * @param errorCallback The callback to invoke if the request fails. - * Note, this doesn't count Non-200 status codes, just - * network errors. - */ -void networkHTTPRequest( - 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 -); \ No newline at end of file diff --git a/src/dusk/network/networkmanager.c b/src/dusk/network/networkmanager.c deleted file mode 100644 index 9e7fce76..00000000 --- a/src/dusk/network/networkmanager.c +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "networkmanager.h" -#include "util/memory.h" -#include "network/networkplatform.h" - -networkmanager_t NETWORK_MANAGER; - -errorret_t networkManagerInit() { - memoryZero(&NETWORK_MANAGER, sizeof(networkmanager_t)); - errorChain(networkPlatformInit()); - errorOk(); -} - -errorret_t networkManagerUpdate() { - errorChain(networkPlatformUpdate()); - 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); -} - -bool_t networkManagerIsConnectionModalOpen() { - return networkPlatformIsConnectionModalOpen(); -} - -errorret_t networkManagerDispose() { - errorChain(networkPlatformDispose()); - errorOk(); -} diff --git a/src/dusk/network/networkmanager.h b/src/dusk/network/networkmanager.h deleted file mode 100644 index 33722cee..00000000 --- a/src/dusk/network/networkmanager.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -typedef struct { - void *nothing; -} networkmanager_t; - -extern networkmanager_t NETWORK_MANAGER; - -/** - * Initializes the network manager. This will NOT connect to the network. - * - * @return An error code indicating success or failure. - */ -errorret_t networkManagerInit(); - -/** - * Updates the network manager, dispatching any completed async request - * callbacks on the main thread. - * - * @return An error code indicating success or failure. - */ -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 -); - -/** - * This is highly platform specific unfortunately, but basically for PSP and - * maybe some other systems in future, I need a way to tell the engine that the - * network manager is blocking rendering while it lets the user connect. - * - * @return If true, then the network manager is asking the engine to not do any - * thing, including time stepping, until this complete. - */ -bool_t networkManagerIsConnectionModalOpen(); - -/** - * Disposes of the network manager. This will NOT disconnect from the network. - * - * @return An error code indicating success or failure. - */ -errorret_t networkManagerDispose(); \ No newline at end of file diff --git a/src/dusk/system/CMakeLists.txt b/src/dusk/system/CMakeLists.txt new file mode 100644 index 00000000..4dbc46f6 --- /dev/null +++ b/src/dusk/system/CMakeLists.txt @@ -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 + system.c +) diff --git a/src/dusk/system/system.c b/src/dusk/system/system.c new file mode 100644 index 00000000..94287825 --- /dev/null +++ b/src/dusk/system/system.c @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "system.h" +#include "system/systemplatform.h" + +#ifndef systemGetActiveDialogTypePlatform + #error "systemGetActiveDialogTypePlatform is not defined" +#endif + +systemdialogtype_t systemGetActiveDialogType() { + return systemGetActiveDialogTypePlatform(); +} \ No newline at end of file diff --git a/src/dusk/system/system.h b/src/dusk/system/system.h new file mode 100644 index 00000000..7479b360 --- /dev/null +++ b/src/dusk/system/system.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +typedef enum { + SYSTEM_DIALOG_TYPE_NONE, + SYSTEM_DIALOG_TYPE_RENDER_BLOCKING, + SYSTEM_DIALOG_TYPE_TICK_BLOCKING +} systemdialogtype_t; + +/** + * Basically this is only used on a few system types, it is to ask the plaform, + * e.g. PSP "What dialog is currently open?" and then the engine will change the + * behavior of the main loop to accomodate. + * + * For example, PSP can show dialogs for things like, save files, wifi, system + * information, etc. When these are open, the engine really can't do much and + * the system needs to finish processing. + * + * For most systems this will go unused. + * + * @return Dialog type currently open. + */ +systemdialogtype_t systemGetActiveDialogType(); \ No newline at end of file diff --git a/src/dusklinux/CMakeLists.txt b/src/dusklinux/CMakeLists.txt index cac82149..742683b0 100644 --- a/src/dusklinux/CMakeLists.txt +++ b/src/dusklinux/CMakeLists.txt @@ -13,4 +13,5 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} add_subdirectory(asset) add_subdirectory(log) add_subdirectory(input) -add_subdirectory(network) \ No newline at end of file +add_subdirectory(network) +add_subdirectory(system) \ No newline at end of file diff --git a/src/dusklinux/network/networklinux.c b/src/dusklinux/network/networklinux.c index 7349518e..1a9e20ed 100644 --- a/src/dusklinux/network/networklinux.c +++ b/src/dusklinux/network/networklinux.c @@ -9,130 +9,8 @@ #include "util/memory.h" #include "util/string.h" #include "assert/assert.h" -#include - -static networklinux_t NETWORK_LINUX; - -static size_t networkLinuxCurlWrite( - char *ptr, - size_t size, - size_t nmemb, - void *userdata -) { - assertNotNull(ptr, "curl write ptr must not be NULL"); - assertNotNull(userdata, "curl write userdata must not be NULL"); - networkhttppendingitem_t *item = (networkhttppendingitem_t *)userdata; - size_t incoming = size * nmemb; - size_t written = strlen(item->responseBody); - size_t space = NETWORK_HTTP_RESPONSE_MAX - 1 - written; - - if(incoming > space) incoming = space; - if(incoming > 0) { - memcpy(item->responseBody + written, ptr, incoming); - item->responseBody[written + incoming] = '\0'; - } - - /* Return the original byte count — curl treats anything else as an error. */ - return size * nmemb; -} - -static void networkLinuxHTTPThread(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; - - CURL *curl = curl_easy_init(); - if(!curl) { - stringCopy( - item->errorMessage, "curl_easy_init failed", NETWORK_ERROR_MESSAGE_MAX - 1 - ); - item->isError = true; - goto done; - } - - curl_easy_setopt(curl, CURLOPT_URL, item->url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, networkLinuxCurlWrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, item); - - switch(item->method) { - case NETWORK_HTTP_REQUEST_METHOD_POST: - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt( - curl, CURLOPT_POSTFIELDS, item->hasBody ? item->body : "" - ); - curl_easy_setopt( - curl, CURLOPT_POSTFIELDSIZE, - (long)(item->hasBody ? strlen(item->body) : 0) - ); - break; - - case NETWORK_HTTP_REQUEST_METHOD_PUT: - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - if(item->hasBody) { - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, item->body); - curl_easy_setopt( - curl, CURLOPT_POSTFIELDSIZE, (long)strlen(item->body) - ); - } - break; - - case NETWORK_HTTP_REQUEST_METHOD_DELETE: - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - break; - - case NETWORK_HTTP_REQUEST_METHOD_HEAD: - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); - break; - - case NETWORK_HTTP_REQUEST_METHOD_OPTIONS: - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS"); - break; - - case NETWORK_HTTP_REQUEST_METHOD_GET: - default: - break; - } - - /* Build header list */ - struct curl_slist *curlHeaders = NULL; - if(item->headerCount > 0) { - char_t line[NETWORK_HTTP_HEADER_KEY_MAX + NETWORK_HTTP_HEADER_VAL_MAX + 3]; - for(uint32_t i = 0; i < item->headerCount; i++) { - snprintf( - line, sizeof(line), "%s: %s", item->headerKeys[i], item->headerVals[i] - ); - curlHeaders = curl_slist_append(curlHeaders, line); - } - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curlHeaders); - } - - CURLcode res = curl_easy_perform(curl); - - if(res != CURLE_OK) { - stringCopy( - item->errorMessage, curl_easy_strerror(res), NETWORK_ERROR_MESSAGE_MAX - 1 - ); - item->isError = true; - } else { - long code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - item->status = (uint16_t)code; - item->isError = false; - } - - if(curlHeaders) curl_slist_free_all(curlHeaders); - curl_easy_cleanup(curl); - -done: - threadMutexLock(&NETWORK_LINUX.resultsMutex); - item->resultReady = true; - threadMutexUnlock(&NETWORK_LINUX.resultsMutex); -} errorret_t networkLinuxInit() { - memoryZero(&NETWORK_LINUX, sizeof(networklinux_t)); - threadMutexInit(&NETWORK_LINUX.resultsMutex); - CURLcode res = curl_global_init(CURL_GLOBAL_ALL); if(res != CURLE_OK) { errorThrow("curl_global_init failed: %s", curl_easy_strerror(res)); @@ -141,146 +19,39 @@ errorret_t networkLinuxInit() { } errorret_t networkLinuxUpdate() { - for(int32_t i = 0; i < NETWORK_HTTP_PENDING_MAX; i++) { - /* Check under lock whether this slot has a result waiting. */ - threadMutexLock(&NETWORK_LINUX.resultsMutex); - - networkhttppendingitem_t *item = &NETWORK_LINUX.requests[i]; - if(!item->used || !item->resultReady) { - threadMutexUnlock(&NETWORK_LINUX.resultsMutex); - continue; - } - - /* Snapshot the small values we need after we drop the lock. */ - bool_t isError = item->isError; - uint16_t status = item->status; - networkhttpcallback_t cb = item->callback; - networkhttperrorcallback_t errCb = item->errorCallback; - void *user = item->user; - - /* - * Keep pointers into item's string buffers — valid because item->used is - * still true here and only this (main) thread clears it. - */ - const char_t *responseBody = item->responseBody; - const char_t *errorMessage = item->errorMessage; - - threadMutexUnlock(&NETWORK_LINUX.resultsMutex); - - /* Fire the callback without holding the lock. */ - 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); - } - - /* Release the slot now that the callback has consumed it. */ - threadMutexLock(&NETWORK_LINUX.resultsMutex); - item->used = false; - item->resultReady = false; - threadMutexUnlock(&NETWORK_LINUX.resultsMutex); - } errorOk(); } bool_t networkLinuxIsConnected() { - return true; -} + // Call the OS network stack to check for connectivity. + struct ifaddrs *ifaddr, *ifa; + if(getifaddrs(&ifaddr) == -1) { + return false; + } -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); + // Check if any non loopback interfaces have running flag set. + bool_t connected = false; + for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_name || !ifa->ifa_flags) continue; + + // Skip loopback (localhost) + if(ifa->ifa_flags & IFF_LOOPBACK) continue; + + // Is interface up and running? + if(!(ifa->ifa_flags & IFF_UP)) continue; + if(!(ifa->ifa_flags & IFF_RUNNING)) continue; + + connected = true; + break; + } + + // Free the linked list of interfaces + freeifaddrs(ifaddr); + + return connected; } errorret_t networkLinuxDispose() { curl_global_cleanup(); - threadMutexDispose(&NETWORK_LINUX.resultsMutex); errorOk(); -} - -void networkLinuxHTTPRequest( - 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"); - - /* Allocate a slot. */ - threadMutexLock(&NETWORK_LINUX.resultsMutex); - - networkhttppendingitem_t *item = NULL; - for(int32_t i = 0; i < NETWORK_HTTP_PENDING_MAX; i++) { - if(!NETWORK_LINUX.requests[i].used) { - item = &NETWORK_LINUX.requests[i]; - break; - } - } - - if(!item) { - threadMutexUnlock(&NETWORK_LINUX.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_LINUX.resultsMutex); - - /* Fill the slot (safe: only we have access while used=true, resultReady=false). */ - 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; - - /* Kick off the background thread. */ - threadInit(&item->thread, networkLinuxHTTPThread); - item->thread.data = item; - threadStartRequest(&item->thread); -} +} \ No newline at end of file diff --git a/src/dusklinux/network/networklinux.h b/src/dusklinux/network/networklinux.h index 18e44327..508085f3 100644 --- a/src/dusklinux/network/networklinux.h +++ b/src/dusklinux/network/networklinux.h @@ -6,46 +6,14 @@ */ #pragma once -#include "dusk.h" -#include "network/networkhttprequest.h" -#include "thread/thread.h" +#include "error/error.h" +#include +#include +#include -#define NETWORK_HTTP_PENDING_MAX 16 -#define NETWORK_HTTP_URL_MAX 512 -#define NETWORK_HTTP_BODY_MAX 8192 -#define NETWORK_HTTP_RESPONSE_MAX 65536 -#define NETWORK_HTTP_HEADER_MAX 16 -#define NETWORK_HTTP_HEADER_KEY_MAX 64 -#define NETWORK_HTTP_HEADER_VAL_MAX 256 -#define NETWORK_ERROR_MESSAGE_MAX 256 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 { - networkhttppendingitem_t requests[NETWORK_HTTP_PENDING_MAX]; - threadmutex_t resultsMutex; + void *nothing; } networklinux_t; /** @@ -71,48 +39,9 @@ errorret_t networkLinuxUpdate(); */ 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. * * @return Any error that occurs. */ -errorret_t networkLinuxDispose(); - -/** - * Submits an asynchronous HTTP request. The callback will be invoked on the - * main thread when next available. - * - * @param url URL to request. - * @param method Method to use for the request. - * @param bodyOrNull If POST or PUT, custom body string, can be NULL. - * @param headers Array of key-value headers. - * @param headerCount Count of headers, can be anything if headers is NULL. - * @param user Callback pointer received to the callback. - * @param callback The callback to invoke when the request completes. - * @param errorCallback The callback to invoke if the request fails. - * Note, this doesn't count Non-200 status codes, just - * network errors. - */ -void networkLinuxHTTPRequest( - 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 -); +errorret_t networkLinuxDispose(); \ No newline at end of file diff --git a/src/dusklinux/network/networkplatform.h b/src/dusklinux/network/networkplatform.h index 5c1ad617..120e5ad8 100644 --- a/src/dusklinux/network/networkplatform.h +++ b/src/dusklinux/network/networkplatform.h @@ -8,9 +8,9 @@ #pragma once #include "networklinux.h" -#define networkPlatformInit networkLinuxInit -#define networkPlatformUpdate networkLinuxUpdate +#define networkPlatformInit networkLinuxInit +#define networkPlatformUpdate networkLinuxUpdate #define networkPlatformDispose networkLinuxDispose #define networkPlatformIsConnected networkLinuxIsConnected -#define networkPlatformRequestConnection networkLinuxRequestConnection -#define networkPlatformHTTPRequest networkLinuxHTTPRequest \ No newline at end of file + +typedef networklinux_t networkplatform_t; \ No newline at end of file diff --git a/src/dusklinux/system/CMakeLists.txt b/src/dusklinux/system/CMakeLists.txt new file mode 100644 index 00000000..6c2b20a2 --- /dev/null +++ b/src/dusklinux/system/CMakeLists.txt @@ -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 + systemlinux.c +) diff --git a/src/dusklinux/system/systemlinux.c b/src/dusklinux/system/systemlinux.c new file mode 100644 index 00000000..30987b5b --- /dev/null +++ b/src/dusklinux/system/systemlinux.c @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "systemlinux.h" + +systemdialogtype_t systemGetActiveDialogTypeLinux() { + return SYSTEM_DIALOG_TYPE_NONE; +} \ No newline at end of file diff --git a/src/dusklinux/system/systemlinux.h b/src/dusklinux/system/systemlinux.h new file mode 100644 index 00000000..7e8bf508 --- /dev/null +++ b/src/dusklinux/system/systemlinux.h @@ -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 "system/system.h" + +/** + * Currently just returns SYSTEM_DIALOG_TYPE_NONE. + * + * @return Currently open system dialog type. + */ +systemdialogtype_t systemGetActiveDialogTypeLinux(); \ No newline at end of file diff --git a/src/dusklinux/system/systemplatform.h b/src/dusklinux/system/systemplatform.h new file mode 100644 index 00000000..732db4d0 --- /dev/null +++ b/src/dusklinux/system/systemplatform.h @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "system/systemlinux.h" + +#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeLinux \ No newline at end of file diff --git a/src/duskpsp/CMakeLists.txt b/src/duskpsp/CMakeLists.txt index a7021af8..95f5ed65 100644 --- a/src/duskpsp/CMakeLists.txt +++ b/src/duskpsp/CMakeLists.txt @@ -18,4 +18,5 @@ target_sources(${DUSK_BINARY_TARGET_NAME} add_subdirectory(asset) add_subdirectory(input) add_subdirectory(log) -add_subdirectory(network) \ No newline at end of file +add_subdirectory(network) +add_subdirectory(system) \ No newline at end of file diff --git a/src/duskpsp/network/networkplatform.h b/src/duskpsp/network/networkplatform.h index ef02df4d..8b23ce09 100644 --- a/src/duskpsp/network/networkplatform.h +++ b/src/duskpsp/network/networkplatform.h @@ -8,10 +8,10 @@ #pragma once #include "networkpsp.h" -#define networkPlatformInit networkPspInit -#define networkPlatformUpdate networkPspUpdate -#define networkPlatformDispose networkPspDispose -#define networkPlatformIsConnected networkPspIsConnected -#define networkPlatformRequestConnection networkPspRequestConnection -#define networkPlatformHTTPRequest networkPspHTTPRequest -#define networkPlatformIsConnectionModalOpen networkPspIsConnectionModalOpen \ No newline at end of file +#define networkPlatformInit networkPSPInit +#define networkPlatformUpdate networkPSPUpdate +#define networkPlatformDispose networkPSPDispose +#define networkPlatformIsConnected networkPSPIsConnected +#define networkPlatformRequestConnection networkPSPRequestConnection + +typedef networkpsp_t networkplatform_t; \ No newline at end of file diff --git a/src/duskpsp/network/networkpsp.c b/src/duskpsp/network/networkpsp.c index 3af4659e..cae8ee67 100644 --- a/src/duskpsp/network/networkpsp.c +++ b/src/duskpsp/network/networkpsp.c @@ -5,390 +5,210 @@ * https://opensource.org/licenses/MIT */ -#include "networkpsp.h" +#include "network/network.h" #include "util/memory.h" #include "util/string.h" #include "assert/assert.h" -#include -#include -#include -#include +#include "display/displaysdl2.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); - } +#include +errorret_t networkPSPInit() { + // Requests the PSP to load the network modules. 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); - + if(ret < 0) errorThrow("Failed to init NET COMMON: 0x%08X", ret); + ret = sceUtilityLoadNetModule(PSP_NET_MODULE_INET); - if(ret < 0) errorThrow("sceUtilityLoadNetModule(INET) failed: 0x%08X", ret); + if(ret < 0) errorThrow("Failed to init NET INET: 0x%08X", ret); - /* Init the network stack. */ - ret = sceNetInit(0x20000, 0x20, 0x1000, 0x20, 0x1000); + // 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); + + // Init the PSP network stack. + ret = sceNetInit(0x20000, 0x20, 4096, 0x20, 4096); if(ret < 0) errorThrow("sceNetInit failed: 0x%08X", ret); ret = sceNetInetInit(); if(ret < 0) errorThrow("sceNetInetInit failed: 0x%08X", ret); + ret = sceNetResolverInit(); + if(ret < 0) errorThrow("sceNetResolverInit failed: 0x%08X", ret); + ret = sceNetApctlInit(0x1800, 0x30); if(ret < 0) errorThrow("sceNetApctlInit failed: 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(); } -bool_t networkPspIsConnected() { - return NETWORK_PSP.state == NETWORK_PSP_STATE_CONNECTED; -} +errorret_t networkPSPUpdate() { + int ret; + if( + NETWORK.state == NETWORK_STATE_CONNECTING || + NETWORK.state == NETWORK_STATE_DISCONNECTING + ) { + switch(sceUtilityNetconfGetStatus()) { + case PSP_UTILITY_DIALOG_INIT: + break; -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"); + case PSP_UTILITY_DIALOG_NONE: + printf("Diag none?\n"); + NETWORK.state = NETWORK_STATE_DISCONNECTED; + errorThrow("PSP Netconf dialog disappeared without result"); + break; - NETWORK_PSP.onConnected = onConnected; - NETWORK_PSP.onFailed = onFailed; - NETWORK_PSP.connectionUser = user; - NETWORK_PSP.state = NETWORK_PSP_STATE_DIALOG; + case PSP_UTILITY_DIALOG_VISIBLE: + // 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); - /* 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; + 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 { + printf("Offline.\n"); + NETWORK.state = NETWORK_STATE_DISCONNECTED; - 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); - } -} + assertNotNull( + NETWORK.platform.onFailed, + "Network platform onFailed callback should be set." + ); -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; + 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()); } - - 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); - } - +errorret_t networkPSPDispose() { sceNetApctlTerm(); + sceNetResolverTerm(); sceNetInetTerm(); sceNetTerm(); + + sceUtilityUnloadNetModule(PSP_NET_MODULE_HTTP); 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); +bool_t networkPSPIsConnected() { + return NETWORK.state == NETWORK_STATE_CONNECTED; } -bool_t networkPspIsConnectionModalOpen() { - return sceUtilityNetconfGetStatus() == PSP_UTILITY_DIALOG_VISIBLE; +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; + + memoryZero(&NETWORK.platform.dialogData, sizeof(NETWORK.platform.dialogData)); + memoryZero(&NETWORK.platform.dialogAdhoc, sizeof(NETWORK.platform.dialogAdhoc)); + + // 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. + 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 + ); + + + int ret = sceUtilityNetconfInitStart(&NETWORK.platform.dialogData); + if(ret < 0) { + assertUnreachable("Failed to init netconf"); + } + // At this point, PSP is in control. +} + +void networkPSPRequestDisconection( + void (*onComplete)(void *user), + void *user +) { + assertTrue( + NETWORK.state == NETWORK_STATE_DISCONNECTING, + "Network host should be in a disconnecting state." + ); + + NETWORK.platform.onComplete = onComplete; + NETWORK.platform.onCompleteUser = user; + + int ret = sceUtilityNetconfShutdownStart(); + if(ret < 0) { + assertUnreachable("Failed to start netconf shutdown"); + } } \ No newline at end of file diff --git a/src/duskpsp/network/networkpsp.h b/src/duskpsp/network/networkpsp.h index 6d1b9911..78ff487c 100644 --- a/src/duskpsp/network/networkpsp.h +++ b/src/duskpsp/network/networkpsp.h @@ -6,83 +6,82 @@ */ #pragma once -#include "dusk.h" -#include "network/networkhttprequest.h" -#include "thread/thread.h" -#include +#include "error/error.h" +#include "system/systempsp.h" +#include +#include +#include +#include +#include +#include +#include -#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; +// #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" 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; + + // Used during establishing connection + void *onConnectedUser; void (*onConnected)(void *user); void (*onFailed)(errorret_t error, void *user); - void *connectionUser; - networkhttppendingitem_t requests[NETWORK_HTTP_PENDING_MAX]; - threadmutex_t resultsMutex; + + // Used during disconnecting + void *onCompelteUser; + void (*onComplete)(void *user); } networkpsp_t; -errorret_t networkPspInit(); +/** + * Initializes the PSP Network manager. This will NOT do network connecting, + * only prep it for being able to connect in future. + * + * @return Error state (if any). + */ +errorret_t networkPSPInit(); -errorret_t networkPspUpdate(); +/** + * Called each frame for handling PSP requests, basically this is where all + * communication between the HTTP thread and the main thread happens. + * + * @return Error state (if any). + */ +errorret_t networkPSPUpdate(); -errorret_t networkPspDispose(); +/** + * Disposes the PSP Network manager, this will clean all resources and, if the + * network is connected, it will disconnect it safely. + * + * @return Error state (if any). + */ +errorret_t networkPSPDispose(); -bool_t networkPspIsConnected(); +/** + * Checks if the PSP network is connected. + * + * @return True if the PSP is connected to a network, false otherwise. + */ +bool_t networkPSPIsConnected(); -void networkPspRequestConnection( +/** + * Requests the PSP to connect to a network (Shows the Wi-Fi connected). + * + * @param onConnected Callback connected successfully. + * @param onFailed Callback if the connection failed. + * @param onDisconnect Callback when connection is lost. + * @param user User data to pass to the callbacks. + */ +void networkPSPRequestConnection( void (*onConnected)(void *user), void (*onFailed)(errorret_t error, void *user), + void (*onDisconnect)(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 -); - -bool_t networkPspIsConnectionModalOpen(); \ No newline at end of file +); \ No newline at end of file diff --git a/src/duskpsp/system/CMakeLists.txt b/src/duskpsp/system/CMakeLists.txt new file mode 100644 index 00000000..b83bd32f --- /dev/null +++ b/src/duskpsp/system/CMakeLists.txt @@ -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 + systempsp.c +) diff --git a/src/duskpsp/system/systemplatform.h b/src/duskpsp/system/systemplatform.h new file mode 100644 index 00000000..1f4f8f4e --- /dev/null +++ b/src/duskpsp/system/systemplatform.h @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "system/systempsp.h" + +#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypePSP \ No newline at end of file diff --git a/src/duskpsp/system/systempsp.c b/src/duskpsp/system/systempsp.c new file mode 100644 index 00000000..dbe9c2bb --- /dev/null +++ b/src/duskpsp/system/systempsp.c @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "systempsp.h" + +systemdialogtype_t systemGetActiveDialogTypePSP() { + + + return SYSTEM_DIALOG_TYPE_NONE; +} + +int systemPSPGetLanguage() { + int ret; + sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_LANGUAGE, &ret); + return ret; +} + +int systemPSPGetCrossButtonSetting() { + int ret; + // See: https://pspdev.github.io/pspsdk/psputility__sysparam_8h.html#ab588fd5a14adc025f065e09325ffe729 + sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_UNKNOWN, &ret); + return ( + ret == 1 ? PSP_UTILITY_ACCEPT_CROSS : PSP_UTILITY_ACCEPT_CIRCLE + ); +} \ No newline at end of file diff --git a/src/duskpsp/system/systempsp.h b/src/duskpsp/system/systempsp.h new file mode 100644 index 00000000..50805980 --- /dev/null +++ b/src/duskpsp/system/systempsp.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "system/system.h" +#include + +/** + * Returns which PSP system dialog is currently open (if any). + * + * @return Currently open system dialog type. + */ +systemdialogtype_t systemGetActiveDialogTypePSP(); + +/** + * Returns the PSP_SYSTEMPARAM language for the current system. + * + * @return PSP_SYSTEMPARAM language value. + */ +int systemPSPGetLanguage(); + +/** + * Returns the user's setting for the PSP "Cross Button" configuration, which + * determines whether the "Cross" or "Circle" button is used for "Accept" + * actions in system dialogs. + * + * @return PSP_UTILITY_ACCEPT_CROSS or PSP_UTILITY_ACCEPT_CIRCLE. + */ +int systemPSPGetCrossButtonSetting(); \ No newline at end of file