# Asset System Source: `src/dusk/asset/` ## Overview All game assets are packed into a single ZIP archive named `dusk.dsk` (`ASSET_FILE_NAME`). The asset system loads entries from this archive asynchronously on a background thread, caches them, and provides synchronous blocking access when an asset is required immediately. ## Key limits | Constant | Value | Meaning | |----------|-------|---------| | `ASSET_LOADING_COUNT_MAX` | 4 | Concurrent in-flight loads | | `ASSET_ENTRY_COUNT_MAX` | 128 | Cached entries | ## Top-level API (`asset.h`) ```c errorret_t assetInit(); // Open dusk.dsk, start background thread void assetUpdate(); // Dispatch completed-load callbacks (main thread) errorret_t assetDispose(); // Wait for loads, close archive assetentry_t *assetGetEntry( const char_t *path, assetloadertype_t type, assetloaderinput_t *input ); // Get (or create) a cache entry; does NOT start loading errorret_t assetRequireLoaded(assetentry_t *entry); // Block the calling thread until this entry is fully loaded. // Only safe to call from the main thread. void assetLock(assetentry_t *entry); void assetUnlock(assetentry_t *entry); // Reference counting. Lock before using loaded data; unlock when done. // The entry will not be evicted while locked. ``` ## Asset entry states Each cache entry goes through a state machine: ``` IDLE -> QUEUED -> READING (async) -> PROCESSING (sync, main thread) -> LOADED -> ERROR ``` - **READING** runs on the background loader thread (file I/O). - **PROCESSING** runs on the main thread (GPU uploads, parsing finalization). - Once LOADED, data is available in `entry->data`. ## Loader types Loader types are registered in the `ASSET_LOADER_CALLBACKS[]` table. Each type implements three callbacks: `loadSync`, `loadAsync`, `dispose`. | `assetloadertype_t` | Data read | Description | |---------------------|-----------|-------------| | `ASSET_LOADER_TYPE_TEXTURE` | STB image | Loads image bytes async, creates GPU texture sync | | `ASSET_LOADER_TYPE_TILESET` | `.dtf` binary | Custom tile format (magic, version, grid, UVs) | | `ASSET_LOADER_TYPE_MESH` | `.stl` | STL mesh with configurable axis orientation | | `ASSET_LOADER_TYPE_JSON` | yyjson | Up to 256 KB; parsed async | | `ASSET_LOADER_TYPE_LOCALE` | Gettext `.po` | PO parser with plural-form expression evaluation | | `ASSET_LOADER_TYPE_SCRIPT` | JS source | JerryScript module | ## Adding a new loader type 1. Add an enum value before `_COUNT` in `assetloadertype_t` (`src/dusk/asset/loader/assetloader.h`). 2. Add fields to the input/loading/output unions in `assetloader.h`. 3. Implement `assetXxxLoaderSync`, `assetXxxLoaderAsync`, and `assetXxxDispose` in `src/dusk/asset/loader/xxx/`. 4. Register the three callbacks in `ASSET_LOADER_CALLBACKS[]` in `src/dusk/asset/loader/assetloader.c`. 5. If user-facing, create a JS module and a `.d.ts` file (see `CLAUDE.md`). ## Asset batch (`assetbatch.h`) `assetbatch_t` groups multiple asset requests into a single logical load. All entries in the batch start loading concurrently. The batch fires a completion callback once every entry has reached LOADED (or ERROR). ```c assetbatch_t batch; assetBatchInit(&batch, entries, count, onComplete, user); assetBatchStart(&batch); // ... later, after assetUpdate() fires the callback ... assetBatchDispose(&batch); ``` ## Usage pattern ```c // 1. Get or create the cache entry (no I/O yet). assetentry_t *tex = assetGetEntry( "textures/hero.png", ASSET_LOADER_TYPE_TEXTURE, NULL ); assetLock(tex); // 2. Option A -- non-blocking: check tex->state each frame. // Option B -- blocking (main thread only): errorChain(assetRequireLoaded(tex)); // 3. Use the loaded data. texture_t *t = &tex->data.texture; // 4. Release when done. assetUnlock(tex); ``` ## Error macros (inside loader implementations) ```c assetLoaderErrorThrow("msg %d", val); // errorThrow equivalent assetLoaderErrorChain(someCall()); // errorChain equivalent ``` Use these instead of the bare error macros inside loader callbacks so that failures include the loader context in the stack trace.