L04 — Assets on ROM (DFS)
Goal
Understand that game assets live inside the ROM, convert a PNG to a libdragon sprite, pack it with the Dragon File System (DFS), and draw it with RDPQ.
What you will see
A bobbing star sprite and text explaining the pipeline:
PNG → mksprite → DFS → sprite_load("rom:/…")Why not fopen("star.png") on your PC?
The finished game is a single .z64 image. On real hardware there is no access to your laptop’s files. Assets must be:
- Converted to console-friendly formats at build time
- Embedded in the ROM
- Loaded at runtime via paths like
rom:/star.sprite
That embedded filesystem is DFS (Dragon File System).
Build pipeline
assets/star.png
│
│ mksprite (host tool)
▼
filesystem/star.sprite
│
│ mkdfs + n64tool (link into .z64)
▼
rom:/star.sprite ← sprite_load() at runtimeCourse Makefiles do this automatically when assets/*.png exists (common/lesson.mk).
Lesson Makefile note
MKSPRITE_FLAGS := -f RGBA16
include ../../common/lesson.mkRGBA16 is a simple full-color format for teaching. Later you will meet CI4/CI8 palettes and TMEM limits (L05 + graphics modules).
Runtime code
dfs_init(DFS_DEFAULT_LOCATION);
sprite_t *star = sprite_load("rom:/star.sprite");
/* in render: */
rdpq_set_mode_standard();
rdpq_mode_alphacompare(1); /* skip fully transparent texels */
rdpq_sprite_blit(star, x, y, NULL);| Call | Role |
|---|---|
dfs_init | Mount the ROM filesystem (once at startup) |
sprite_load("rom:/…") | Load a packed sprite by ROM path |
rdpq_sprite_blit | Draw it with the RDP |
Path prefix
Always use the rom:/ prefix for DFS assets. A bare star.sprite or a host path will not work on console.
Build & run
source scripts/env.sh
make -C lessons/l04-dfs
# → lessons/l04-dfs/l04_dfs.z64make clean in the lesson folder also removes generated filesystem/.
Exercises
- Replace
assets/star.pngwith your own 32×32 or 64×64 PNG; rebuild. - Draw the sprite in two places (two blits).
- (Stretch) Add a second PNG and load both.
Troubleshooting
| Problem | Fix |
|---|---|
| Red screen / load failed | Asset not packed — check filesystem/star.sprite after build |
| Invisible sprite | Wrong path; or fully transparent PNG; enable alphacompare carefully |
| Build skips sprite | Put PNG under assets/ with .png extension |
Full lesson source
The blocks below are imported from the real repository files at build time (VitePress <<< snippets). They are not hand-copied into this markdown.
lessons/l04-dfs/Makefile · lessons/l04-dfs/src/main.c
lessons/l04-dfs/Makefile
# Lesson 04 — Assets on ROM (DFS)
ROMNAME := l04_dfs
ROM_TITLE := "L04 DFS"
# Prefer a simple RGBA16 sprite for the teaching asset.
MKSPRITE_FLAGS := -f RGBA16
include ../../common/lesson.mklessons/l04-dfs/src/main.c
/**
* L04 — Assets on ROM (DFS)
* ============================================================================
* Assets live *inside* the .z64, not on your PC at runtime.
* PNG → mksprite → filesystem/(name).sprite → packed DFS → rom:/name.sprite
* Always dfs_init() then sprite_load("rom:/...").
* DOCS: docs/guide/m0/l04-dfs.md
*/
#include <libdragon.h>
#include <stdio.h>
int main(void)
{
display_init(RESOLUTION_320x240, DEPTH_16_BPP, 2, GAMMA_NONE,
FILTERS_RESAMPLE);
dfs_init(DFS_DEFAULT_LOCATION);
rdpq_init();
rdpq_text_register_font(1, rdpq_font_load_builtin(FONT_BUILTIN_DEBUG_VAR));
/* Paths are on the *ROM* filesystem — not your PC's disk. */
sprite_t *star = sprite_load("rom:/star.sprite");
if (!star) {
/* Should not happen if Makefile packed filesystem/star.sprite */
while (1) {
surface_t *disp = display_get();
rdpq_attach(disp, NULL);
rdpq_clear((color_t){ .r = 64, .g = 0, .b = 0, .a = 255 });
rdpq_text_print(NULL, 1, 40, 100, "Failed to load rom:/star.sprite");
rdpq_detach_show();
}
}
float t = 0.0f;
while (1) {
t += 0.04f;
/* Gentle bob for juice */
float bob = fm_sinf(t) * 8.0f;
float x = 144.0f;
float y = 100.0f + bob;
surface_t *disp = display_get();
rdpq_attach(disp, NULL);
rdpq_clear((color_t){ .r = 16, .g = 20, .b = 40, .a = 255 });
rdpq_text_print(NULL, 1, 24, 28, "L04 — DFS assets");
rdpq_text_print(NULL, 1, 24, 48, "Loaded rom:/star.sprite");
rdpq_set_mode_standard();
rdpq_mode_alphacompare(1);
rdpq_sprite_blit(star, x, y, NULL);
rdpq_text_print(NULL, 1, 24, 200, "PNG -> mksprite -> DFS -> sprite_load");
rdpq_detach_show();
}
}What you learned
- Assets are build-time converted and ROM-embedded
- DFS +
rom:/paths mksprite+sprite_load+rdpq_sprite_blit
Next
L05 — N64 hardware tour names the chips you have been using, then the Module 0 checkpoint proves L01–L04 together.