4.3 KiB
Architecture
Platform abstraction
Every subsystem that differs across platforms (display, input, asset loading, save, time, network, log) follows the same pattern:
src/dusk/<subsystem>/<subsystem>platform.h— included by the public header. Contains#include "path/to/platform-specific-header.h"resolved by the build system include path.src/dusk{platform}/<subsystem>/<subsystem>platform.h— the actual platform-specific header included above (e.g.src/duskgl/display/framebuffer/framebufferplatform.h).- The shared header (
src/dusk/<subsystem>/<subsystem>.h)#errors at compile time if the platform doesn't define the expected macros/types.
The active platform backends are selected by DUSK_TARGET_SYSTEM in CMake, which includes cmake/targets/<system>.cmake. That file sets compile definitions (DUSK_LINUX, DUSK_SDL2, DUSK_OPENGL, …) and links platform libraries.
Platform source directories:
src/duskgl/— OpenGL rendering (used on Linux and as the GL layer for SDL2)src/dusksdl2/— SDL2 window/input/time (Linux desktop)src/dusklinux/— Linux filesystem/save/networksrc/duskdolphin/— GameCube & Wii (GX renderer, libogc)src/duskpsp/— PSP (GU renderer, PSPSDK)src/duskvita/— PS Vita
Subsystem lifecycle
All subsystems follow init → update (per frame) → dispose. Engine initialization order matters and is centralized in engine.c:
systemInit → timeInit → consoleInit → inputInit → assetInit →
localeManagerInit → displayInit → uiInit → uiTextboxInit →
cutsceneInit → rpgInit → networkInit → sceneInit
Dispose runs in reverse. Each call uses errorChain() to propagate failures.
Error handling
Functions that can fail return errorret_t (a code + pointer to thread-local error state). Three core macros:
errorThrow("message %s", arg); // sets error, returns from current function
errorChain(someCall()); // if someCall() fails, propagates and returns
errorOk(); // returns success
Check with errorIsOk(ret) / errorIsNotOk(ret). The error state carries file/function/line info for a stack-like trace.
Fixed-point math
fixed_t is int32_t with Q24.8 format (8 fractional bits, ~0.004 resolution). Use it for all world/game values:
fixed_t x = FIXED(1.5); // compile-time literal
fixed_t y = fixedFromI32(3); // runtime conversion
fixed_t z = fixedMul(x, y); // arithmetic
float_t f = fixedToFloat(z); // only where float is needed (e.g. GL uniforms)
Code generation from CSV
Several subsystems define their data in CSV files and have corresponding Python tools that generate C headers at build time (via CMake add_custom_command):
| CSV | Tool | Output |
|---|---|---|
src/dusk/input/input.csv |
tools/input/csv/ |
input action enum + names |
src/dusk/display/color.csv |
tools/color/csv/ |
color constants |
src/dusk/rpg/item/item.csv |
tools/item/csv/ |
item enum + metadata |
src/dusk/rpg/story/storyflag.csv |
tools/story/csv/ |
story flag enum + initial values |
Generated headers are written to build-<target>/generated/ and included via target_include_directories.
Asset system
Assets are packed into dusk.dsk (a zip archive) at build time from the assets/ directory. At runtime asset.c opens the archive and serves files from it.
Loading is asynchronous: assetLock() registers a load request; the background thread calls the appropriate loader; call assetRequireLoaded() to block until ready. assetUnlock() / assetUnlockEntry() releases the entry so it can be reclaimed.
Loaders are registered per type (assetloadertype_t) and live under src/dusk/asset/loader/. Platform-specific asset init (finding the .dsk file) is in src/dusk{platform}/asset/.
Display subsystem
The display system is currently organized around immediate GPU-style rendering: mesh_t (vertex buffers), shader_t (GLSL on GL / TEV state on Dolphin), texture_t, and framebuffer_t. See display-refactor.md for the planned move to a render-queue model (needed for a future Saturn port).
The spritebatch_t (display/spritebatch/) accumulates 2D quads and flushes in batches — the primary 2D drawing primitive used by the RPG layer.