Compare commits

18 Commits

63 changed files with 1833 additions and 78 deletions
+2 -2
View File
@@ -9,8 +9,8 @@ if PSP then
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("circle", INPUT_ACTION_CANCEL)
inputBind("cross", INPUT_ACTION_ACCEPT)
inputBind("accept", INPUT_ACTION_ACCEPT)
inputBind("cancel", INPUT_ACTION_CANCEL)
inputBind("select", INPUT_ACTION_RAGEQUIT)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
+6 -1
View File
@@ -2,4 +2,9 @@ include(cmake/targets/dolphin.cmake)
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_GAMECUBE
)
)
# Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
# bba
)
+5 -1
View File
@@ -1,6 +1,7 @@
# Find link platform-specific libraries
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
# find_package(CURL REQUIRED)
# Setup endianess at compile time to optimize.
include(TestBigEndian)
@@ -16,12 +17,13 @@ else()
endif()
# Link required libraries.
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
SDL2
pthread
OpenGL::GL
GL
m
# CURL::libcurl
)
# Define platform-specific macros.
@@ -38,4 +40,6 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_INPUT_POINTER
DUSK_INPUT_GAMEPAD
DUSK_TIME_DYNAMIC
DUSK_NETWORK_IPV6
THREAD_PTHREAD=1
)
+6
View File
@@ -24,6 +24,11 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
pspvfpu
pspvram
psphprm
pspnet
pspnet_inet
pspnet_apctl
psphttp
pspssl
)
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
@@ -39,6 +44,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_OPENGL_LEGACY
DUSK_DISPLAY_WIDTH=480
DUSK_DISPLAY_HEIGHT=272
THREAD_PTHREAD=1
)
# Postbuild, create .pbp file for PSP.
+1 -1
View File
@@ -21,7 +21,7 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "vita")
add_subdirectory(dusksdl2)
add_subdirectory(duskgl)
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecube")
add_subdirectory(duskdolphin)
endif()
+3 -4
View File
@@ -72,10 +72,9 @@ add_subdirectory(locale)
add_subdirectory(physics)
add_subdirectory(scene)
add_subdirectory(script)
add_subdirectory(system)
add_subdirectory(time)
add_subdirectory(ui)
add_subdirectory(network)
add_subdirectory(util)
# if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "psp")
# add_subdirectory(thread)
# endif()
# add_subdirectory(thread)
+1 -4
View File
@@ -64,7 +64,7 @@ errorret_t displayInit(void) {
glm_perspective(
glm_rad(45.0f),
(float_t)SCREEN.width / (float_t)SCREEN.height,
SCREEN.aspect,
0.1f,
100.0f,
proj
@@ -101,9 +101,6 @@ errorret_t displayUpdate(void) {
errorChain(sceneRender());
// Render UI
// uiRender();
// Finish up
screenUnbind();
screenRender();
@@ -40,6 +40,17 @@ uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
return frameBufferPlatformGetHeight(framebuffer);
}
float_t frameBufferGetAspect(const framebuffer_t *framebuffer) {
#ifdef frameBufferPlatformGetAspect
return frameBufferPlatformGetAspect(framebuffer);
#endif
uint32_t width = frameBufferGetWidth(framebuffer);
uint32_t height = frameBufferGetHeight(framebuffer);
if(height == 0) return 1.0f; // Avoid divide by zero, just return 1:1 aspect.
return (float_t)width / (float_t)height;
}
void frameBufferClear(const uint8_t flags, const color_t color) {
frameBufferPlatformClear(flags, color);
}
@@ -58,6 +58,16 @@ uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
*/
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
/**
* Returns the aspect ratio of the framebuffer. This is ALMOST always just
* the width / height, however some platforms may choose to override this if
* they have stretched styled back buffers, e.g. 640x480 stretched.
*
* @param framebuffer The framebuffer to get the aspect ratio of.
* @return The aspect ratio of the framebuffer.
*/
float_t frameBufferGetAspect(const framebuffer_t *framebuffer);
/**
* Binds the framebuffer for rendering, or the backbuffer if the framebuffer
* provided is NULL.
+4 -4
View File
@@ -52,7 +52,7 @@ errorret_t screenBind() {
// Screen mode backbuffer uses the full display size
SCREEN.width = frameBufferGetWidth(FRAMEBUFFER_BOUND);
SCREEN.height = frameBufferGetHeight(FRAMEBUFFER_BOUND);
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
SCREEN.aspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
// No needd for a framebuffer.
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
@@ -100,8 +100,7 @@ errorret_t screenBind() {
int32_t fbWidth, fbHeight;
fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t currentAspect = (float_t)fbWidth / (float_t)fbHeight;
float_t currentAspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
if(currentAspect == SCREEN.aspectRatio.ratio) {
// No need to use framebuffer.
SCREEN.width = fbWidth;
@@ -129,13 +128,14 @@ errorret_t screenBind() {
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
float_t curFbAspect = frameBufferGetAspect(&SCREEN.framebuffer);
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
// Correct size, nothing to do.
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
SCREEN.aspect = curFbAspect;
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
+6 -3
View File
@@ -70,9 +70,7 @@ errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
#if MESH_ENABLE_COLOR
const color_t color,
#endif
const color_t color,
const tileset_t *tileset,
texture_t *texture
) {
@@ -83,6 +81,11 @@ errorret_t textDraw(
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, texture));
#if MESH_ENABLE_COLOR
#else
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color));
#endif
// errorChain(spriteBatchPush(
// // texture,
// posX, posY,
+1 -3
View File
@@ -66,9 +66,7 @@ errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
#if MESH_ENABLE_COLOR
const color_t color,
#endif
const color_t color,
const tileset_t *tileset,
texture_t *texture
);
+96 -19
View File
@@ -20,14 +20,77 @@
#include "entity/component/physics/entityphysics.h"
#include "game/game.h"
#include "physics/physicsmanager.h"
#include "network/network.h"
#include "network/networkinfo.h"
#include "system/system.h"
#include "display/mesh/cube.h"
#include "display/mesh/plane.h"
engine_t ENGINE;
entityid_t phBoxEnt;
componentid_t phBoxPhys;
float_t onlineSwapTime = FLT_MAX;
void goOnline();
void goOffline();
void onNetworkConnected(void *user) {
onlineSwapTime = TIME.time + 3.0f;
networkinfo_t info = networkGetInfo();
if(info.type == NETWORK_TYPE_IPV4) {
sceneLog(
"Connected to network with IPv4 address: " NETWORK_INFO_FORMAT_IPV4 "\n",
info.ipv4.ip[0], info.ipv4.ip[1], info.ipv4.ip[2], info.ipv4.ip[3]
);
#ifdef DUSK_NETWORK_IPV6
} else if(info.type == NETWORK_TYPE_IPV6) {
sceneLog(
"Connected to network with IPv6 address: " NETWORK_INFO_FORMAT_IPV6 "\n",
info.ipv6.ip[0], info.ipv6.ip[1], info.ipv6.ip[2], info.ipv6.ip[3],
info.ipv6.ip[4], info.ipv6.ip[5], info.ipv6.ip[6], info.ipv6.ip[7],
info.ipv6.ip[8], info.ipv6.ip[9], info.ipv6.ip[10], info.ipv6.ip[11],
info.ipv6.ip[12], info.ipv6.ip[13], info.ipv6.ip[14], info.ipv6.ip[15]
);
#endif
}
sceneLog("Network connected, I will disconnect at: %.2f1.\n", onlineSwapTime);
}
void onNetworkFailed(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Failed to connect to network, will try again at %.2f1.\n", onlineSwapTime);
}
void onNetworkDisconnected(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Network disconnected, will go online at %.2f1.\n", onlineSwapTime);
errorCatch(errorPrint(error));
}
void onNetworkDisconnectFinished(void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Finished disconnecting from network, will go online at %.2f1.\n", onlineSwapTime);
}
void goOnline() {
sceneLog("Going online...\n");
networkRequestConnection(
onNetworkConnected,
onNetworkFailed,
onNetworkDisconnected,
NULL
);
}
void goOffline() {
sceneLog("Going offline...\n");
networkRequestDisconnection(onNetworkDisconnectFinished, NULL);
}
/* Kept module-level only because engineUpdate needs them for the reset. */
static entityid_t phBoxEnt;
static componentid_t phBoxPhys;
errorret_t engineInit(const int32_t argc, const char_t **argv) {
memoryZero(&ENGINE, sizeof(engine_t));
@@ -36,6 +99,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
ENGINE.argv = argv;
// Init systems. Order is important.
errorChain(systemInit());
timeInit();
errorChain(inputInit());
errorChain(assetInit());
@@ -46,9 +110,13 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(sceneInit());
entityManagerInit();
physicsManagerInit();
// errorChain(networkInit());
errorChain(gameInit());
/* ---- Camera ---- */
sceneLog("Init done, going to queue online in 3 seconds...\n");
onlineSwapTime = TIME.time + 3.0f;
// Camera
entityid_t cam = entityManagerAdd();
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
float_t distance = 6.0f;
@@ -59,13 +127,13 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
(vec3){ distance, distance, distance }
);
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
entityCameraSetZFar(cam, camCam, distance * 6.0f);
entityCameraSetZFar(cam, camCam, 100.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 });
@@ -74,27 +142,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 ---- */
// 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));
@@ -105,6 +170,8 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
}
errorret_t engineUpdate(void) {
// errorChain(networkUpdate());
timeUpdate();
inputUpdate();
@@ -126,6 +193,15 @@ errorret_t engineUpdate(void) {
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
if(TIME.time >= onlineSwapTime) {
onlineSwapTime = FLT_MAX;
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
goOffline();
} else {
goOnline();
}
}
errorOk();
}
@@ -134,6 +210,7 @@ void engineExit(void) {
}
errorret_t engineDispose(void) {
// errorChain(networkDispose());
sceneDispose();
errorChain(gameDispose());
entityManagerDispose();
+2 -2
View File
@@ -8,8 +8,8 @@
#pragma once
#include "dusk.h"
#define ENTITY_COUNT_MAX 64
#define ENTITY_COMPONENT_COUNT_MAX 16
#define ENTITY_COUNT_MAX 6
#define ENTITY_COMPONENT_COUNT_MAX 6
typedef uint8_t entityid_t;
typedef uint8_t componentid_t;
+3 -2
View File
@@ -8,6 +8,7 @@
#include "entitymanager.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "scene/scene.h"
entitymanager_t ENTITY_MANAGER;
@@ -18,8 +19,8 @@ void entityManagerInit(void) {
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
);
printf(
"Entity Manager size is currently: %zu bytes (%.2f KB)\n",
sceneLog(
"Entity Manager size: %zu bytes (%.2f KB)\n",
sizeof(entitymanager_t),
sizeof(entitymanager_t) / 1024.0f
);
+10
View File
@@ -0,0 +1,10 @@
# 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
network.c
networkinfo.c
)
+116
View File
@@ -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();
}
+126
View File
@@ -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
);
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "network.h"
#include "networkinfo.h"
#include "network/networkplatform.h"
#include "assert/assert.h"
#ifndef networkPlatformGetInfo
#error "networkPlatformGetInfo must be defined"
#endif
networkinfo_t networkGetInfo() {
assertTrue(
NETWORK.state == NETWORK_STATE_CONNECTED,
"networkGetInfo called when not connected"
);
return networkPlatformGetInfo();
}
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define NETWORK_INFO_IPV4_DNS_COUNT_MAX 2
#define NETWORK_INFO_IPV4_OCTET_COUNT 4
#define NETWORK_INFO_IPV6_OCTET_COUNT 16
#define NETWORK_INFO_IPV6_DNS_COUNT_MAX 2
#define NETWORK_INFO_FORMAT_IPV4 "%u.%u.%u.%u"
#define NETWORK_INFO_FORMAT_IPV6 \
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x"
typedef enum {
NETWORK_TYPE_IPV4,
#ifdef DUSK_NETWORK_IPV6
NETWORK_TYPE_IPV6,
#endif
} networktype_t;
typedef struct {
uint8_t ip[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t subnet[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t gateway[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t dns[NETWORK_INFO_IPV4_OCTET_COUNT][NETWORK_INFO_IPV4_DNS_COUNT_MAX];
} networkinfoipv4_t;
#ifdef DUSK_NETWORK_IPV6
typedef struct {
uint8_t ip[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t subnet[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t gateway[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t dns[NETWORK_INFO_IPV6_OCTET_COUNT][NETWORK_INFO_IPV6_DNS_COUNT_MAX];
} networkinfoipv6_t;
#endif
typedef struct {
networktype_t type;
union {
networkinfoipv4_t ipv4;
#ifdef DUSK_NETWORK_IPV6
networkinfoipv6_t ipv6;
#endif
};
} networkinfo_t;
/**
* Returns the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkGetInfo();
+79
View File
@@ -12,12 +12,59 @@
#include "entity/entitymanager.h"
#include "display/shader/shaderunlit.h"
#include "display/mesh/cube.h"
#include "display/spritebatch/spritebatch.h"
#include "display/text/text.h"
#include "display/screen/screen.h"
scene_t SCENE;
char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...) {
char temp[512];
// 1. Format input like printf
va_list args;
va_start(args, fmt);
vsnprintf(temp, sizeof(temp), fmt, args);
va_end(args);
printf("%s", temp);
// 2. Split into lines
char *lines[64];
int line_count = 0;
char *ptr = temp;
while (*ptr && line_count < 64) {
lines[line_count++] = ptr;
char *nl = strchr(ptr, '\n');
if (!nl) break;
*nl = '\0';
ptr = nl + 1;
}
// 3. Prepend lines in reverse order (so final order is correct)
for (int i = 0; i < line_count; i++) {
char new_log[SCENE_LOG_SIZE];
snprintf(new_log, sizeof(new_log), "%s\n%s", lines[i], SCENE_LOG);
// Copy back safely
strncpy(SCENE_LOG, new_log, SCENE_LOG_SIZE - 1);
SCENE_LOG[SCENE_LOG_SIZE - 1] = '\0';
}
}
errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t));
memoryZero(SCENE_LOG, sizeof(SCENE_LOG));
sceneLog("Init\n");
errorOk();
}
@@ -52,6 +99,7 @@ errorret_t sceneRender(void) {
mat4 view, proj, model;
errorChain(shaderBind(&SHADER_UNLIT));
// For each camera.
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
entityid_t camEnt = camEnts[camIndex];
componentid_t camComp = camComps[camIndex];
@@ -110,6 +158,37 @@ errorret_t sceneRender(void) {
}
}
// Here is where UI will go
glm_ortho(
0.0f, SCREEN.width,
SCREEN.height, 0.0f,
0.1f, 100.0f,
proj
);
glm_lookat(
(vec3){ 0.0f, 0.0f, 1.0f },
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
view
);
glm_mat4_identity(model);
errorChain(shaderBind(&SHADER_UNLIT));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
// errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_0TEXTURE, &DEFAULT_FONT_TEXTURE));
// errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
errorChain(textDraw(
32, 32,
// "Hello World",
SCENE_LOG,
COLOR_WHITE,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
));
errorChain(spriteBatchFlush());
errorOk();
}
+4
View File
@@ -14,6 +14,10 @@ typedef struct {
extern scene_t SCENE;
#define SCENE_LOG_SIZE 1024
extern char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...);
/**
* Initialize the scene subsystem.
*
+1 -3
View File
@@ -52,9 +52,7 @@ int moduleTextDraw(lua_State *L) {
x,
y,
text,
#if MESH_ENABLE_COLOR
color == NULL ? COLOR_WHITE : *color,
#endif
color == NULL ? COLOR_WHITE : *color,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
);
+9
View File
@@ -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
)
+25
View File
@@ -0,0 +1,25 @@
/**
* 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
#ifndef systemInitPlatform
#error "systemInitPlatform is not defined"
#endif
errorret_t systemInit() {
return systemInitPlatform();
}
systemdialogtype_t systemGetActiveDialogType() {
return systemGetActiveDialogTypePlatform();
}
+38
View File
@@ -0,0 +1,38 @@
/**
* 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 {
SYSTEM_DIALOG_TYPE_NONE,
SYSTEM_DIALOG_TYPE_RENDER_BLOCKING,
SYSTEM_DIALOG_TYPE_TICK_BLOCKING
} systemdialogtype_t;
/**
* Initializes the system module. This is called really early in the init
* process of the engine.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInit(void);
/**
* 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();
+3 -1
View File
@@ -18,4 +18,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
add_subdirectory(asset)
add_subdirectory(log)
add_subdirectory(display)
add_subdirectory(input)
add_subdirectory(input)
add_subdirectory(network)
add_subdirectory(system)
+1 -1
View File
@@ -76,7 +76,7 @@ errorret_t displayInitDolphin(void) {
);
// Setup cull modes
GX_SetCullMode(GX_CULL_BACK);
GX_SetCullMode(GX_CULL_NONE);
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetZMode(GX_TRUE, GX_ALWAYS, GX_FALSE);
GX_SetDispCopyGamma(GX_GM_1_0);
@@ -8,6 +8,7 @@
#include "display/framebuffer/framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "system/systemdolphin.h"
errorret_t frameBufferInitBackBufferDolphin(void) {
errorOk();
@@ -15,16 +16,29 @@ errorret_t frameBufferInitBackBufferDolphin(void) {
uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get width of NULL framebuffer.");
return DISPLAY.screenMode->fbWidth;
}
uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get height of NULL framebuffer.");
return DISPLAY.screenMode->efbHeight;
}
float_t frameBufferGetAspectDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get aspect of NULL framebuffer.");
switch(systemGetAspectRatioDolphin()) {
case CONF_ASPECT_16_9:
return 16.0f / 9.0f;
case CONF_ASPECT_4_3:
return 4.0f / 3.0f;
default:
return (
(float_t)DISPLAY.screenMode->fbWidth /
(float_t)DISPLAY.screenMode->efbHeight
);
}
}
errorret_t frameBufferBindDolphin(framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot bind NULL framebuffer.");
assertTrue(
@@ -38,6 +38,15 @@ uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer);
*/
uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer);
/**
* Gets the aspect ratio of the framebuffer. (Dolphin implementation). Taking
* the Wii aspect setting into consideration.
*
* @param framebuffer The framebuffer to get the aspect ratio of.
* @return The aspect ratio of the framebuffer.
*/
float_t frameBufferGetAspectDolphin(const framebufferdolphin_t *framebuffer);
/**
* Binds the framebuffer for rendering. (Dolphin implementation).
*
@@ -13,5 +13,6 @@ typedef framebufferdolphin_t framebufferplatform_t;
#define frameBufferPlatformInitBackBuffer frameBufferInitBackBufferDolphin
#define frameBufferPlatformGetWidth frameBufferGetWidthDolphin
#define frameBufferPlatformGetHeight frameBufferGetHeightDolphin
#define frameBufferPlatformGetAspect frameBufferGetAspectDolphin
#define frameBufferPlatformBind frameBufferBindDolphin
#define frameBufferPlatformClear frameBufferClearDolphin
#define frameBufferPlatformClear frameBufferClearDolphin
@@ -52,6 +52,7 @@ errorret_t meshDrawDolphin(
assertTrue(offsetof(meshvertex_t, pos) == 8, "pos offset wrong");
#endif
// Flush vertex data to GPU. This is required before drawing with GX.
DCFlushRange(
(void*)&mesh->vertices[vertexOffset],
sizeof(meshvertex_t) * vertexCount
+13 -16
View File
@@ -43,10 +43,10 @@ errorret_t shaderBindDolphin(shaderdolphin_t *shader) {
GX_LoadProjectionMtx(
shader->matrixProjection,
shader->dolphinProj,
shader->isProjectionPerspective ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC
);
GX_LoadPosMtxImm(shader->matrixModelView, GX_PNMTX0);
GX_LoadPosMtxImm(shader->dolphinModelView, GX_PNMTX0);
errorOk();
}
@@ -193,13 +193,13 @@ errorret_t shaderUpdateMVPDolphin() {
// Need to update projection?
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_PROJ) != 0) {
shaderMat4ToMtx44(SHADER_BOUND->proj, SHADER_BOUND->matrixProjection);
shaderMat4ToMtx44(SHADER_BOUND->proj, SHADER_BOUND->dolphinProj);
// Fix projection Z mapping between GLM and GX.
float A = SHADER_BOUND->matrixProjection[2][2];
float B = SHADER_BOUND->matrixProjection[2][3];
SHADER_BOUND->matrixProjection[2][2] = 0.5f * (A + 1.0f);
SHADER_BOUND->matrixProjection[2][3] = 0.5f * B;
float A = SHADER_BOUND->dolphinProj[2][2];
float B = SHADER_BOUND->dolphinProj[2][3];
SHADER_BOUND->dolphinProj[2][2] = 0.5f * (A + 1.0f);
SHADER_BOUND->dolphinProj[2][3] = 0.5f * B;
// Is this perspective or ortho originally? Dolphin cares for some reason.
const float_t epsilon = 0.0001f;
@@ -209,7 +209,7 @@ errorret_t shaderUpdateMVPDolphin() {
);
GX_LoadProjectionMtx(
SHADER_BOUND->matrixProjection,
SHADER_BOUND->dolphinProj,
SHADER_BOUND->isProjectionPerspective ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC
);
}
@@ -217,23 +217,18 @@ errorret_t shaderUpdateMVPDolphin() {
// Need to update view or model?
bool_t mvDirt = false;
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_VIEW) != 0) {
shaderMat4ToMtx(SHADER_BOUND->view, SHADER_BOUND->matrixView);
mvDirt = true;
}
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_MODEL) != 0) {
shaderMat4ToMtx(SHADER_BOUND->model, SHADER_BOUND->matrixModel);
mvDirt = true;
}
// Set Model/View Matrix
if(mvDirt) {
guMtxConcat(
SHADER_BOUND->matrixView,
SHADER_BOUND->matrixModel,
SHADER_BOUND->matrixModelView
);
GX_LoadPosMtxImm(SHADER_BOUND->matrixModelView, GX_PNMTX0);
glm_mat4_mul(SHADER_BOUND->view, SHADER_BOUND->model, SHADER_BOUND->modelView);
shaderMat4ToMtx(SHADER_BOUND->modelView, SHADER_BOUND->dolphinModelView);
GX_LoadPosMtxImm(SHADER_BOUND->dolphinModelView, GX_PNMTX0);
}
SHADER_BOUND->dirtyMatrix = 0;
@@ -255,6 +250,8 @@ void shaderMat4ToMtx(const mat4 inGlmMatrix, Mtx outGXMatrix) {
assertNotNull(inGlmMatrix, "Input matrix cannot be null");
assertNotNull(outGXMatrix, "Output matrix cannot be null");
guMtxIdentity(outGXMatrix);
for(int row = 0; row < 3; ++row) {
for(int col = 0; col < 4; ++col) {
outGXMatrix[row][col] = inGlmMatrix[col][row];
@@ -24,13 +24,12 @@ typedef struct shaderdolphin_s {
mat4 view;
mat4 proj;
mat4 model;
mat4 modelView;
Mtx dolphinProj;
Mtx dolphinModelView;
bool_t isProjectionPerspective;
Mtx44 matrixProjection;
Mtx matrixView;
Mtx matrixModel;
Mtx matrixModelView;
uint_fast8_t dirtyMatrix;
} shaderdolphin_t;
+9 -1
View File
@@ -8,4 +8,12 @@
#pragma once
#include <ogcsys.h>
#include <gccore.h>
#include <malloc.h>
#include <malloc.h>
#ifdef DUSK_GAMECUBE
#define CONF_ASPECT_4_3 0
#define CONF_ASPECT_16_9 1
#define CONF_GetAspectRatio() CONF_ASPECT_4_3
#define CONF_GetLanguage() SYS_GetLanguage()
#endif
+9
View File
@@ -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
networkdolphin.c
)
+118
View File
@@ -0,0 +1,118 @@
// /**
// * Copyright (c) 2026 Dominic Masters
// *
// * This software is released under the MIT License.
// * https://opensource.org/licenses/MIT
// */
#include "network/network.h"
#include "util/memory.h"
#include "assert/assert.h"
errorret_t networkDolphinInit() {
// s32 ret = net_init();
// if(ret < 0) errorThrow("Failed to init network stack: %d", ret);
errorOk();
}
errorret_t networkDolphinUpdate() {
errorOk();
}
errorret_t networkDolphinDispose() {
// #ifdef DUSK_WII
// net_deinit();
// #endif
errorOk();
}
bool_t networkDolphinIsConnected() {
return NETWORK.state == NETWORK_STATE_CONNECTED;
}
void networkDolphinRequestConnection(
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.ip, sizeof(NETWORK.platform.ip));
memoryZero(NETWORK.platform.netmask, sizeof(NETWORK.platform.netmask));
memoryZero(NETWORK.platform.gateway, sizeof(NETWORK.platform.gateway));
// Negotiate DHCP using the Wi-Fi settings saved in Wii System Menu.
// This call blocks until the interface is configured or times out.
#ifdef DUSK_WII
s32 ret = if_config(
NETWORK.platform.ip,
NETWORK.platform.netmask,
NETWORK.platform.gateway,
true,
20
);
#else
s32 ret = -1;
#endif
if(ret >= 0) {
NETWORK.state = NETWORK_STATE_CONNECTED;
assertNotNull(
NETWORK.platform.onConnected,
"Network platform onConnected callback should be set."
);
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
} else {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
assertNotNull(
NETWORK.platform.onFailed,
"Network platform onFailed callback should be set."
);
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
}
}
void networkDolphinRequestDisconnection(
void (*onComplete)(void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_DISCONNECTING,
"Network host should be in a disconnecting state."
);
NETWORK.state = NETWORK_STATE_DISCONNECTED;
onComplete(user);
}
networkinfo_t networkDolphinGetInfo() {
networkinfo_t info;
memoryZero(&info, sizeof(networkinfo_t));
info.type = NETWORK_TYPE_IPV4;
int ret = sscanf(
NETWORK.platform.ip,
"%hhu.%hhu.%hhu.%hhu",
&info.ipv4.ip[0],
&info.ipv4.ip[1],
&info.ipv4.ip[2],
&info.ipv4.ip[3]
);
assertTrue(ret == 4, "Failed to parse IP address");
return info;
}
+86
View File
@@ -0,0 +1,86 @@
/**
* 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/networkinfo.h"
#include <network.h>
#define NETWORK_DOLPHIN_IP_MAX 16
typedef struct {
char_t ip[NETWORK_DOLPHIN_IP_MAX];
char_t netmask[NETWORK_DOLPHIN_IP_MAX];
char_t gateway[NETWORK_DOLPHIN_IP_MAX];
void *onConnectedUser;
void (*onConnected)(void *user);
void (*onFailed)(errorret_t error, void *user);
} networkdolphin_t;
/**
* Initializes the Wii network stack via IOS. Does not connect; use
* networkDolphinRequestConnection for that.
*
* @return Error state (if any).
*/
errorret_t networkDolphinInit();
/**
* Called each frame. No-op on Wii since connection is synchronous.
*
* @return Error state (if any).
*/
errorret_t networkDolphinUpdate();
/**
* Disposes the Wii network stack.
*
* @return Error state (if any).
*/
errorret_t networkDolphinDispose();
/**
* Returns true if the Wii is connected to a network.
*
* @return True if connected.
*/
bool_t networkDolphinIsConnected();
/**
* Requests the Wii to connect to the network using the Wi-Fi settings saved
* in the Wii System Menu. Blocks until connected or failed.
*
* @param onConnected Callback on successful connection.
* @param onFailed Callback if connection fails.
* @param onDisconnect Callback when connection is later lost.
* @param user User data passed to all callbacks.
*/
void networkDolphinRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
);
/**
* Requests the Wii to disconnect from the network.
*
* @param onComplete Callback when disconnection is complete.
* @param user User data passed to the callback.
*/
void networkDolphinRequestDisconnection(
void (*onComplete)(void *user),
void *user
);
/**
* Returns the current network IP information.
*
* @return Network info for the active connection.
*/
networkinfo_t networkDolphinGetInfo();
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "networkdolphin.h"
#define networkPlatformInit networkDolphinInit
#define networkPlatformUpdate networkDolphinUpdate
#define networkPlatformDispose networkDolphinDispose
#define networkPlatformIsConnected networkDolphinIsConnected
#define networkPlatformRequestConnection networkDolphinRequestConnection
#define networkPlatformRequestDisconnection networkDolphinRequestDisconnection
#define networkPlatformGetInfo networkDolphinGetInfo
typedef networkdolphin_t networkplatform_t;
+9
View File
@@ -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
systemdolphin.c
)
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systemdolphin.h"
#include "input/input.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t systemInitDolphin(void) {
errorOk();
}
systemdialogtype_t systemGetActiveDialogTypeDolphin(void) {
return SYSTEM_DIALOG_TYPE_NONE;
}
int32_t systemGetAspectRatioDolphin(void) {
return CONF_GetAspectRatio();
}
int32_t systemGetLanguageDolphin(void) {
return CONF_GetLanguage();
}
+51
View File
@@ -0,0 +1,51 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/system.h"
/**
* Initializes the Dolphin system module.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInitDolphin(void);
/**
* Returns which Dolphin system dialog is currently open (if any).
*
* @return Currently open system dialog type.
*/
systemdialogtype_t systemGetActiveDialogTypeDolphin(void);
/**
* Returns either CONF_ASPECT_4_3 or CONF_ASPECT_16_9 depending on the aspect
* ratio of the system. I do believe that Gamecube will only ever return 4:3.
*
* Refer to;
* https://github.com/devkitPro/libogc/blob/20d90e944b83c8991538e88b00b1e5f309428e85/gc/ogc/conf.h#L190
*
* @return Aspect ratio of the system.
*/
int32_t systemGetAspectRatioDolphin(void);
/**
* Returns the language the system is set to. This is used for things like
* locale management, to try to match the system language if possible.
*
* Refer to;
* https://github.com/devkitPro/libogc/blob/20d90e944b83c8991538e88b00b1e5f309428e85/gc/ogc/conf.h#L190
*
* On gamecube, refer to;
* https://libogc.devkitpro.org/system_8h.html
*
* @return System language.
*/
int32_t systemGetLanguageDolphin(void);
// There's actually a tonne more things Wii can return, this is it for now
// though.
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/systemdolphin.h"
#define systemInitPlatform systemInitDolphin
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeDolphin
+3 -1
View File
@@ -12,4 +12,6 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
# Subdirs
add_subdirectory(asset)
add_subdirectory(log)
add_subdirectory(input)
add_subdirectory(input)
add_subdirectory(network)
add_subdirectory(system)
+9
View File
@@ -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
networklinux.c
)
+120
View File
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "networklinux.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t networkLinuxInit() {
errorOk();
}
errorret_t networkLinuxUpdate() {
errorOk();
}
bool_t networkLinuxIsConnected() {
// Call the OS network stack to check for connectivity.
struct ifaddrs *ifaddr, *ifa;
if(getifaddrs(&ifaddr) == -1) {
return false;
}
// 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() {
errorOk();
}
networkinfo_t networkLinuxGetInfo() {
networkinfo_t info;
memset(&info, 0, sizeof(networkinfo_t));
bool_t found = false;
struct ifaddrs *ifaddr, *ifa;
if(getifaddrs(&ifaddr) == -1) {
assertUnreachable("getifaddrs failed");
}
// Find the first non-loopback interface with an IP address
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;
if(ifa->ifa_addr == NULL) continue;
// Check for IPv4 address
if(ifa->ifa_addr->sa_family == AF_INET) {
info.type = NETWORK_TYPE_IPV4;
struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr;
memoryCopy(
info.ipv4.ip,
&sa->sin_addr,
NETWORK_INFO_IPV4_OCTET_COUNT
);
found = true;
// sa = (struct sockaddr_in *)ifa->ifa_netmask;
// memoryCopy(
// info.ipv4.subnet,
// &sa->sin_addr,
// NETWORK_INFO_IPV4_OCTET_COUNT
// );
break;
}
// Check for IPv6 address
#ifdef DUSK_NETWORK_IPV6
if(ifa->ifa_addr->sa_family == AF_INET6) {
info.type = NETWORK_TYPE_IPV6;
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ifa->ifa_addr;
memoryCopy(
info.ipv6.ip,
&sa->sin6_addr,
NETWORK_INFO_IPV6_OCTET_COUNT
);
found = true;
break;
}
#endif
}
freeifaddrs(ifaddr);
assertTrue(found, "No active network interface found?");
return info;
}
+54
View File
@@ -0,0 +1,54 @@
/**
* 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/networkinfo.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
typedef struct {
void *nothing;
} networklinux_t;
/**
* Initializes the network manager. Must be called before any other network
* functions.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxInit();
/**
* Updates the network manager, called once per frame to handle completed
* HTTP requests.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxUpdate();
/**
* Returns true — Linux uses the OS network stack which is always available.
*
* @return true
*/
bool_t networkLinuxIsConnected();
/**
* Disposes the network manager.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxDispose();
/**
* Gets the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkLinuxGetInfo();
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "networklinux.h"
#define networkPlatformInit networkLinuxInit
#define networkPlatformUpdate networkLinuxUpdate
#define networkPlatformDispose networkLinuxDispose
#define networkPlatformIsConnected networkLinuxIsConnected
#define networkPlatformGetInfo networkLinuxGetInfo
typedef networklinux_t networkplatform_t;
+9
View File
@@ -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
)
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systemlinux.h"
errorret_t systemInitLinux() {
errorOk();
}
systemdialogtype_t systemGetActiveDialogTypeLinux() {
return SYSTEM_DIALOG_TYPE_NONE;
}
+21
View File
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/system.h"
/**
* Initializes the Linux system module.
*/
errorret_t systemInitLinux(void);
/**
* Currently just returns SYSTEM_DIALOG_TYPE_NONE.
*
* @return Currently open system dialog type.
*/
systemdialogtype_t systemGetActiveDialogTypeLinux();
+12
View File
@@ -0,0 +1,12 @@
/**
* 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 systemInitPlatform systemInitLinux
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeLinux
+3 -1
View File
@@ -17,4 +17,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
# Subdirs
add_subdirectory(asset)
add_subdirectory(input)
add_subdirectory(log)
add_subdirectory(log)
add_subdirectory(network)
add_subdirectory(system)
+7
View File
@@ -7,6 +7,9 @@
#include "input/input.h"
// #define INPUT_PSP_GAMEPAD_BUTTON_ACCEPT INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
// #define INPUT_PSP_GAMEPAD_BUTTON_CANCEL INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "triangle", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_Y } },
{ .name = "cross", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_A } },
@@ -21,6 +24,10 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_LEFTSHOULDER } },
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER } },
// Refer to systempsp.c for some extra info.
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_A } },
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_B } },
{ .name = "lstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = true } } },
{ .name = "lstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = false } } },
{ .name = "lstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTX, .positive = true } } },
+9
View File
@@ -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
)
+19
View File
@@ -0,0 +1,19 @@
/**
* 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 networkPlatformRequestDisconnection networkPSPRequestDisconnection
#define networkPlatformGetInfo networkPSPGetInfo
typedef networkpsp_t networkplatform_t;
+259
View File
@@ -0,0 +1,259 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "network/network.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#include "display/displaysdl2.h"
errorret_t networkPSPInit() {
// Requests the PSP to load the network modules.
int ret;
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
if(ret < 0) errorThrow("Failed to init NET COMMON: 0x%08X", ret);
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
if(ret < 0) errorThrow("Failed to init NET INET: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEURI);
// if(ret < 0) errorThrow("Failed to init NET PARSEURI: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEHTTP);
// if(ret < 0) errorThrow("Failed to init NET PARSEHTTP: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_SSL);
// if(ret < 0) errorThrow("Failed to init NET SSL: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_HTTP);
// if(ret < 0) errorThrow("Failed to init NET HTTP: 0x%08X", ret);
// ret = sceSslInit(0x28000);
// if(ret < 0) errorThrow("sceSslInit failed: 0x%08X", ret);
// ret = sceHttpInit(0x25800);
// if(ret < 0) errorThrow("sceHttpInit failed: 0x%08X", ret);
// ret = sceHttpsInit(0, 0, 0, 0);
// if(ret < 0) errorThrow("sceHttpsInit failed: 0x%08X", ret);
// ret = sceHttpsLoadDefaultCert(0, 0);
// if(ret < 0) errorThrow("sceHttpsLoadDefaultCert failed: 0x%08X", ret);
// ret = sceHttpLoadSystemCookie();
// if(ret < 0) errorThrow("sceHttpLoadSystemCookie failed: 0x%08X", ret);
// All good.
errorOk();
}
errorret_t networkPSPUpdate() {
int ret;
if(NETWORK.state == NETWORK_STATE_CONNECTING) {
switch(sceUtilityNetconfGetStatus()) {
case PSP_UTILITY_DIALOG_INIT:
break;
case PSP_UTILITY_DIALOG_NONE:
NETWORK.state = NETWORK_STATE_DISCONNECTED;
errorThrow("PSP Netconf dialog disappeared without result");
break;
case PSP_UTILITY_DIALOG_VISIBLE:
// 1 is mandatory?
ret = sceUtilityNetconfUpdate(1);
if(ret != 0) {
errorThrow("sceUtilityNetconfUpdate failed: 0x%08X", ret);
}
break;
case PSP_UTILITY_DIALOG_QUIT:
ret = sceUtilityNetconfShutdownStart();
if(ret != 0) {
errorThrow("sceUtilityNetconfShutdownStart failed: 0x%08X", ret);
}
break;
case PSP_UTILITY_DIALOG_FINISHED:
// Did we connect?
int apState = 0;
sceNetApctlGetState(&apState);
if(apState == PSP_NET_APCTL_STATE_GOT_IP) {
NETWORK.state = NETWORK_STATE_CONNECTED;
assertNotNull(
NETWORK.platform.onConnected,
"Network platform onConnected callback should be set."
);
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
} else {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
assertNotNull(
NETWORK.platform.onFailed,
"Network platform onFailed callback should be set."
);
// Kill the PSP network stack.
errorret_t err = networkPSPTerm();
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
}
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
}
break;
default:
errorThrow("Unknown PSP Netconf dialog status: %d", sceUtilityNetconfGetStatus());
}
}
errorOk();
}
errorret_t networkPSPDispose() {
sceUtilityNetconfGetStatus();
errorCatch(errorPrint(networkPSPTerm()));
sceUtilityUnloadNetModule(PSP_NET_MODULE_HTTP);
sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
errorOk();
}
bool_t networkPSPIsConnected() {
return NETWORK.state == NETWORK_STATE_CONNECTED;
}
void networkPSPRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_CONNECTING,
"Network host should be in a connecting state."
);
NETWORK.platform.onConnected = onConnected;
NETWORK.platform.onFailed = onFailed;
NETWORK.platform.onConnectedUser = user;
int ret;
// Init the PSP network stack.
ret = sceNetInit(0x20000, 0x20, 4096, 0x20, 4096);
assertTrue(ret >= 0, "Failed to init net: 0x%08X");
ret = sceNetInetInit();
assertTrue(ret >= 0, "Failed to init net inet: 0x%08X");
ret = sceNetResolverInit();
assertTrue(ret >= 0, "Failed to init net resolver: 0x%08X");
ret = sceNetApctlInit(0x1800, 0x30);
assertTrue(ret >= 0, "Failed to init net apctl: 0x%08X");
// This is all related to getting the PSP online, refer to;
// https://github.com/joel16/CMFileManager-PSP/blob/00dab16c64cd48bf6452fc274a3b898d77c39a8d/app/source/net.cpp#L97
// since I follow this implementation closely.
memoryZero(&NETWORK.platform.dialogData, sizeof(NETWORK.platform.dialogData));
memoryZero(&NETWORK.platform.dialogAdhoc, sizeof(NETWORK.platform.dialogAdhoc));
NETWORK.platform.dialogData.base.size = sizeof(pspUtilityNetconfData);
NETWORK.platform.dialogData.base.language = systemPSPGetLanguage();
NETWORK.platform.dialogData.base.buttonSwap = systemPSPGetCrossButtonSetting();
NETWORK.platform.dialogData.base.graphicsThread = 17;
NETWORK.platform.dialogData.base.accessThread = 19;
NETWORK.platform.dialogData.base.fontThread = 18;
NETWORK.platform.dialogData.base.soundThread = 16;
NETWORK.platform.dialogData.action = PSP_NETCONF_ACTION_CONNECTAP;
NETWORK.platform.dialogData.adhocparam = (
&NETWORK.platform.dialogAdhoc
);
ret = sceUtilityNetconfInitStart(&NETWORK.platform.dialogData);
assertTrue(ret >= 0, "Failed to init netconf");
}
void networkPSPRequestDisconnection(
void (*onComplete)(void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_DISCONNECTING,
"Network host should be in a disconnecting state."
);
errorret_t err = networkPSPTerm();
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
}
NETWORK.state = NETWORK_STATE_DISCONNECTED;
onComplete(user);
}
errorret_t networkPSPTerm() {
int ret;
ret = sceNetApctlTerm();
if(ret < 0) {
errorThrow("Failed to terminate netctl: 0x%08X", ret);
}
ret = sceNetResolverTerm();
if(ret < 0) {
errorThrow("Failed to terminate net resolver: 0x%08X", ret);
}
ret = sceNetInetTerm();
if(ret < 0) {
errorThrow("Failed to terminate net inet: 0x%08X", ret);
}
ret = sceNetTerm();
if(ret < 0) {
errorThrow("Failed to terminate net: 0x%08X", ret);
}
errorOk();
}
networkinfo_t networkPSPGetInfo() {
networkinfo_t netInfo;
memoryZero(&netInfo, sizeof(networkinfo_t));
// Get the IP address of the current connection.
union SceNetApctlInfo info;
int ret = sceNetApctlGetInfo(PSP_NET_APCTL_INFO_IP, &info);
assertTrue(ret >= 0, "Failed to get IP address");
// Parse the IP address string into octets.
netInfo.type = NETWORK_TYPE_IPV4;
ret = sscanf(
info.ip,
"%hhu.%hhu.%hhu.%hhu",
&netInfo.ipv4.ip[0],
&netInfo.ipv4.ip[1],
&netInfo.ipv4.ip[2],
&netInfo.ipv4.ip[3]
);
assertTrue(ret == 4, "Failed to parse IP address");
return netInfo;
}
+110
View File
@@ -0,0 +1,110 @@
/**
* 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/networkinfo.h"
#include "system/systempsp.h"
#include <psphttp.h>
#include <pspnet.h>
#include <pspnet_inet.h>
#include <pspnet_apctl.h>
#include <pspssl.h>
#include <pspnet_resolver.h>
#include <psphttp.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"
typedef struct {
pspUtilityNetconfData dialogData;
struct pspUtilityNetconfAdhoc dialogAdhoc;
// Used during establishing connection
void *onConnectedUser;
void (*onConnected)(void *user);
void (*onFailed)(errorret_t error, void *user);
} networkpsp_t;
/**
* 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();
/**
* 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();
/**
* 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();
/**
* Checks if the PSP network is connected.
*
* @return True if the PSP is connected to a network, false otherwise.
*/
bool_t networkPSPIsConnected();
/**
* 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
);
/**
* Requests the PSP to disconnect from the network (Shows the Wi-Fi disconnecting).
*
* @param onComplete Callback when disconnection is complete.
* @param user User data to pass to the callback.
*/
void networkPSPRequestDisconnection(
void (*onComplete)(void *user),
void *user
);
/**
* Disposes the PSP sce net libraries, doesn't unload the modules and won't
* term the dialog if it's active.
*
* @return Error state (if any).
*/
errorret_t networkPSPTerm();
/**
* Gets the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkPSPGetInfo();
+9
View File
@@ -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
)
+12
View File
@@ -0,0 +1,12 @@
/**
* 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 systemInitPlatform systemInitPSP
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypePSP
+64
View File
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systempsp.h"
#include "input/input.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t systemInitPSP() {
// Bind ACCEPT and CANCEL binds.
inputbuttondata_t *buttonCross, *buttonCircle, *buttonAccept, *buttonCancel;
inputbuttondata_t *i = INPUT_BUTTON_DATA;
while(i->name) {
if(stringCompare(i->name, "cross") == 0) {
buttonCross = i;
} else if(stringCompare(i->name, "circle") == 0) {
buttonCircle = i;
} else if(stringCompare(i->name, "accept") == 0) {
buttonAccept = i;
} else if(stringCompare(i->name, "cancel") == 0) {
buttonCancel = i;
}
i++;
}
assertNotNull(buttonCross, "Cross button not found!");
assertNotNull(buttonCircle, "Circle button not found!");
assertNotNull(buttonAccept, "Accept button not found!");
assertNotNull(buttonCancel, "Cancel button not found!");
if(systemPSPGetCrossButtonSetting() == PSP_UTILITY_ACCEPT_CROSS) {
buttonAccept->button.gpButton = buttonCross->button.gpButton;
buttonCancel->button.gpButton = buttonCircle->button.gpButton;
} else {
buttonAccept->button.gpButton = buttonCircle->button.gpButton;
buttonCancel->button.gpButton = buttonCross->button.gpButton;
}
errorOk();
}
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
);
}
+40
View File
@@ -0,0 +1,40 @@
/**
* 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 <psputility.h>
/**
* Initializes the PSP system module.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInitPSP(void);
/**
* 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();
+5
View File
@@ -73,6 +73,11 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
#ifdef DUSK_INPUT_GAMEPAD
case INPUT_BUTTON_TYPE_GAMEPAD: {
assertTrue(
button.gpButton < SDL_CONTROLLER_BUTTON_MAX,
"Gamepad button out of range"
);
if(SDL_GameControllerGetButton(
INPUT.platform.controller, button.gpButton
)) {