Files
dusk/.claude/platform-dolphin.md
T
2026-06-16 10:15:59 -05:00

289 lines
8.9 KiB
Markdown

# Platform -- Dolphin (GameCube and Wii)
`DUSK_TARGET_SYSTEM`: `gamecube` / `wii`
Source layer: `src/duskdolphin/`
Renderer: libogc GX (native Nintendo hardware)
---
## Overview
GameCube and Wii are collectively called the **Dolphin** targets. They
share a single source layer (`src/duskdolphin/`) and a shared CMake base
(`cmake/targets/dolphin.cmake`). Individual targets add `DUSK_GAMECUBE`
or `DUSK_WII` on top.
Both are **big-endian** PowerPC platforms. They do **not** use SDL2 or
OpenGL -- rendering and input go through `libogc` (the open-source
GameCube/Wii SDK) and the GX hardware API directly.
---
## Hardware
| Attribute | GameCube | Wii |
|-----------|---------|-----|
| CPU | IBM PowerPC 750CL (Gekko), 485 MHz | IBM Broadway (Wii CPU), 729 MHz |
| RAM | 24 MB (16 MB MEM1 + 8 MB ARAM) | 88 MB (24 MB MEM1 + 64 MB MEM2) |
| GPU | ATI Flipper (GX) | ATI Hollywood (GX) |
| Display | 640x480 (480p max) | 640x480 (480p/576i), 480p/1080i via component |
| Storage | Memory Card (slots A/B), SD Gecko | SD card, USB, NAND |
| Endian | Big-endian | Big-endian |
Treat the **GameCube 16 MB MEM1** as the worst-case RAM budget for data
structures shared between both targets.
---
## Compile-time macros
| Macro | GameCube | Wii | Notes |
|-------|---------|-----|-------|
| `DUSK_DOLPHIN` | yes | yes | Set by `dolphin.cmake` |
| `DUSK_GAMECUBE` | yes | no | |
| `DUSK_WII` | no | yes | |
| `DUSK_INPUT_GAMEPAD` | yes | yes | |
| `DUSK_DISPLAY_WIDTH` | 640 | 640 | |
| `DUSK_DISPLAY_HEIGHT` | 480 | 480 | |
| `DUSK_THREAD_PTHREAD` | yes | yes | devkitPPC pthreads |
| `DUSK_PLATFORM_ENDIAN_BIG` | yes | yes | Not set by cmake -- apply manually |
| `DOL` | 1 | 1 | Build type token |
| `ISO` | 2 | 2 | Build type token |
| `DUSK_DOLPHIN_BUILD_TYPE` | `DOL` or `ISO` | `DOL` or `ISO` | |
| `DUSK_DOLPHIN_BUILD_ISO` | if ISO mode | if ISO mode | |
No `DUSK_SDL2`, no `DUSK_OPENGL`, no `DUSK_INPUT_KEYBOARD`,
no `DUSK_INPUT_POINTER`, no `DUSK_TIME_DYNAMIC`.
Attempting to use `DUSK_INPUT_KEYBOARD` or `DUSK_INPUT_POINTER` causes
a compile-time `#error` in `inputdolphin.h`.
---
## Endianness
**Both GameCube and Wii are big-endian.** This is the most critical
platform difference from all other targets.
- All binary asset data (`.dtf` tilesets, STL meshes, DTF headers, etc.)
must be byte-swapped when read on Dolphin.
- Use `endianLittleToHost32` / `endianLittleToHost16` etc. from
`util/endian.h` when reading any multi-byte value from a file.
- Save files are stored in little-endian order; the save stream handles
this transparently via the `saveFile*` macros.
- Network data likewise needs endian conversion.
See `.claude/util.md` (Endian section) for the full API.
---
## Display
- Fixed 640x480 resolution, driven by GX (the hardware rasteriser).
- Uses double-buffered framebuffers:
```c
typedef struct {
void *frameBuffer[2]; // double-buffered
int_t whichFrameBuffer;
GXRModeObj *screenMode;
void *fifoBuffer; // GX command FIFO, 256 KB
} displaydolphin_t;
```
- The GX pipeline uses display lists for efficient draw call batching --
avoid immediate-mode GX calls in the hot path.
- `CONF_GetAspectRatio()` returns `CONF_ASPECT_4_3` on GameCube (always)
and the user's setting on Wii. Use `systemGetAspectRatioDolphin()`.
---
## Asset loading
Two modes are selected at CMake configure time via
`DUSK_DOLPHIN_BUILD_TYPE`:
### DOL mode (default -- `DUSK_DOLPHIN_BUILD_TYPE=DOL`)
Assets are loaded from `dusk.dsk` on a FAT filesystem -- SD card on Wii
(via SD slot), or SD Gecko / SD adapter on GameCube. The loader searches
these paths in order:
```c
"/", "/Dusk", "/dusk", "/DUSK",
"/apps", "/apps/Dusk", "/apps/dusk", "/apps/DUSK",
".", "./Dusk", "./dusk", ...
```
Uses `libfat` for filesystem access.
### ISO mode (`DUSK_DOLPHIN_BUILD_TYPE=ISO`)
`dusk.dsk` is read directly off the DVD disc via the libogc DVD driver
(`assetdolphindvd.c`). Reads are 32-byte aligned:
```c
#define ASSET_DOLPHIN_DVD_ALIGN 32u
```
The DVD FST (file-system table) is parsed at init to locate the data
file. All reads go through `assetDolphinDVDRead(offset, size)` which
returns an aligned heap buffer that the caller must free.
Post-build in ISO mode, `makedolphiniso.py` produces **three disc
images** (NTSC-J, NTSC-U, PAL).
---
## Input
Uses libogc `PAD` API. Only GameCube controllers are supported (port 0
by default; up to 4 via `PAD_CHANMAX`).
Available axes (6 total per controller):
| Axis | Enum |
|------|------|
| Left stick X/Y | `INPUT_GAMEPAD_AXIS_LEFT_X/Y` |
| C-stick X/Y | `INPUT_GAMEPAD_AXIS_C_X/Y` |
| L trigger | `INPUT_GAMEPAD_AXIS_TRIGGER_LEFT` |
| R trigger | `INPUT_GAMEPAD_AXIS_TRIGGER_RIGHT` |
Axis values are normalised by dividing the raw 8-bit value by 128.0.
Deadzone: 0.2 (hardcoded).
Default bindings set at init: D-pad/left stick = directional actions,
A = ACCEPT, B = CANCEL, X = CONSOLE, Start = RAGEQUIT.
Wii Remote / Nunchuk / Classic Controller / Pro Controller are not yet
implemented (noted as TODO in `inputdolphin.h`).
---
## Save system
Uses the libogc Memory Card API (`CARD_*`) to read/write save slots.
```c
typedef struct {
card_file cardFile;
uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32)));
bool_t mounted;
} savedolphin_t;
```
- Default channel: `CARD_SLOTA` (Memory Card slot A).
Override via `SAVE_DOLPHIN_CHANNEL`.
- Sector size: 8192 bytes (`SAVE_DOLPHIN_SECTOR_SIZE`).
- Buffers must be 32-byte aligned (enforced by `__attribute__((aligned(32)))`).
- Game code: `DUSK` (4 chars, override via `SAVE_DOLPHIN_GAME_CODE`).
- The card must be mounted before any read/write. `saveInitDolphin()`
mounts slot A; failures are treated as "no save present".
- Save stream handles little-endian encoding transparently -- all data
stored little-endian on the card even though the CPU is big-endian.
---
## Network
### GameCube
`net_init()` is commented out. Networking is **non-functional** on
GameCube in the current codebase. The BBA (Broadband Adapter) link
library is in a commented `# bba` in `gamecube.cmake`.
### Wii
Uses `if_config()` from libogc which reads Wi-Fi settings saved in the
Wii System Menu. The call **blocks** the main thread until DHCP
completes or fails. Wii network is available only when `DUSK_WII` is
defined; the GameCube path always fails immediately.
IPv6 is not supported on either Dolphin target.
---
## Time
- No `DUSK_TIME_DYNAMIC`. All ticks are fixed 16 ms steps.
- Tick source: `__SYS_GetSystemTime()` returns PowerPC bus ticks.
- Real time: ticks converted to microseconds via `ticks_to_microsecs()`,
then offset from the GameCube epoch (2000-01-01 00:00:00) to the UNIX
epoch (1970-01-01 00:00:00) by adding **946 684 800 seconds**.
- Timezone: always returned as 0 -- no timezone data without network time.
---
## System
Language and aspect ratio queries:
```c
// Language (used for locale selection):
systemGetLanguageDolphin();
// -> SYS_GetLanguage() on GameCube
// -> CONF_GetLanguage() on Wii
// Aspect ratio:
systemGetAspectRatioDolphin();
// -> CONF_ASPECT_4_3 always on GameCube
// -> CONF_GetAspectRatio() on Wii (4:3 or 16:9)
```
---
## Build and toolchain
Requires [devkitPro](https://devkitpro.org/) with `devkitPPC` and
`libogc` installed.
```sh
# GameCube (SD card / DOL mode)
cmake -B build \
-DDUSK_TARGET_SYSTEM=gamecube \
-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/GameCube.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build build
# Wii (SD card / DOL mode)
cmake -B build \
-DDUSK_TARGET_SYSTEM=wii \
-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Wii.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build build
# Either target in ISO mode
cmake -B build \
-DDUSK_TARGET_SYSTEM=gamecube \
-DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/GameCube.cmake \
-DDUSK_DOLPHIN_BUILD_TYPE=ISO \
-DCMAKE_BUILD_TYPE=Release
cmake --build build
```
Post-build outputs (DOL mode): `Dusk.elf` + `Dusk.dol` (generated by
`elf2dol`). Copy `Dusk.dol` and `dusk.dsk` to the SD card.
Post-build outputs (ISO mode): `Dusk.dol` + disc images in
`NTSC-J/`, `NTSC-U/`, `PAL/` subdirectories.
Dependencies: libogc, devkitPPC, `fat` (DOL mode), cglm, zip, bz2,
zstd, z, lzma, m.
---
## Gotchas
- **Big-endian is the most common source of bugs** when porting code
from Linux. Always use `endian.h` utilities for file I/O and network.
- Memory is tight on GameCube -- 16 MB MEM1 must hold code, stack, heap,
framebuffers (2x 640x480x2 bytes), and the GX FIFO (256 KB).
- GX display lists are the correct rendering path; immediate-mode GX
calls carry heavy CPU overhead on the short FIFO pipeline.
- The GameCube has no FPU for integer paths. Avoid `double`; use
`float_t` throughout.
- `consoleInit` is shadowed to `consoleInitDolphin` to avoid conflicts
with the devkitPPC console API.
- On GameCube `CONF_GetAspectRatio()` is always 4:3; the macro is
defined to return `CONF_ASPECT_4_3` unconditionally.
- DVD reads must be 32-byte aligned and padded -- use
`ASSET_DOLPHIN_DVD_ALIGN_UP(n)` when computing read sizes in ISO mode.