diff --git a/assets/init.js b/assets/init.js deleted file mode 100644 index 210ad6ff..00000000 --- a/assets/init.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -const platformNames = { - [System.PLATFORM_LINUX]: 'Linux', - [System.PLATFORM_KNULLI]: 'Knulli', - [System.PLATFORM_PSP]: 'PSP', - [System.PLATFORM_GAMECUBE]: 'GameCube', - [System.PLATFORM_WII]: 'Wii', -}; - -Console.print('Platform: ' + (platformNames[System.platform] || 'Unknown')); - -UIFullboxOver.setColor(Color.BLACK); - -requireAsync('testscene.js').then(Scene.set).catch(err => { - Console.print('Error loading scene: ' + err); - Engine.exit(); -}); \ No newline at end of file diff --git a/assets/player.js b/assets/player.js deleted file mode 100644 index 3582b60c..00000000 --- a/assets/player.js +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -const PLAYER_SPEED = 5.0; -// 1 world unit = 16 pixels. -const PIXEL_SCALE = 1.0 / 16.0; -// Player sprite is 32x32 px (test.png dimensions). -const PLAYER_W = 32 * PIXEL_SCALE; -const PLAYER_H = 32 * PIXEL_SCALE; - -var player = {}; - -player.getAssets = () => { - return [ - { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA } - ]; -} - -player.init = function(scene) { - var texture = scene.assets.getAssetByPath('test.png'); - Console.print('Player init: got texture ' + texture); - - _entity = Entity.create(); - _position = _entity.add(Component.POSITION); - _physics = _entity.add(Component.PHYSICS); - - _physics.bodyType = Physics.DYNAMIC; - _physics.shape = Physics.SHAPE_CUBE; - _physics.gravityScale = 1.0; - - var r = _entity.add(Component.RENDERABLE); - r.texture = texture.texture; - r.type = Renderable.SPRITEBATCH; - r.color = new Color(220, 80, 80); - // Upright quad centered on X, bottom-aligned on Y. - r.sprites = [[-PLAYER_W/2, 0, 0, PLAYER_W/2, PLAYER_H, 0, 0, 1, 1, 0]]; - - _position.localPosition = new Vec3(0, PLAYER_H, 0); -}; - -player.getPosition = function() { - return _position; -}; - -player.update = function() { - if(!_physics) return; - var vx = Input.axis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT) * PLAYER_SPEED; - var vz = Input.axis(INPUT_ACTION_DOWN, INPUT_ACTION_UP) * PLAYER_SPEED; - // Preserve vertical velocity so gravity and landing work correctly. - var vy = _physics.velocity.y; - _physics.velocity = new Vec3(vx, vy, vz); -}; - -player.dispose = function() { - Entity.dispose(_entity); - _entity = null; - _position = null; - _physics = null; -}; - -module.exports = player; diff --git a/assets/testscene.js b/assets/testscene.js deleted file mode 100644 index 87696156..00000000 --- a/assets/testscene.js +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -var scene = {}; - -// Pokemon DS-style camera: ~34 degrees elevation (atan(6/9)). -// CAM_HEIGHT / CAM_DIST ratio controls the tilt - keep it under 0.7 for -// the characteristically shallow DS angle. -const CAM_HEIGHT = 6; -const CAM_DIST = 9; - -scene.init = async function() { - // Camera - scene.cam = Entity.create(); - var camPos = scene.cam.add(Component.POSITION); - var cam = scene.cam.add(Component.CAMERA); - camPos.localPosition = new Vec3(3, 3, 3); - camPos.lookAt(new Vec3(0, 0, 0)); - - // Floor - large flat slab, no texture needed. - scene.floor = Entity.create(); - var floorPos = scene.floor.add(Component.POSITION); - var floorR = scene.floor.add(Component.RENDERABLE); - floorR.type = Renderable.SHADER_MATERIAL; - floorR.color = Color.BLUE; - // floorPos.localScale = new Vec3(16, 0.2, 16); - // floorPos.localPosition = new Vec3(0, -0.1, 0); - - await UIFullboxOver.transition(Color.BLACK, Color.TRANSPARENT, 1.0); -}; - -scene.update = function() { -}; - -scene.dispose = function() { - Entity.dispose(scene.floor); - Entity.dispose(scene.cam); -}; - -module.exports = scene; \ No newline at end of file diff --git a/assets/ui/test.js b/assets/ui/test.js deleted file mode 100644 index e202801c..00000000 --- a/assets/ui/test.js +++ /dev/null @@ -1,6 +0,0 @@ -module = { - render() { - Text.draw(0, 0, "Hello World"); - SpriteBatch.flush(); - } -}; diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 141f7256..324ab552 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -9,37 +9,6 @@ if(NOT cglm_FOUND) target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC cglm) endif() -if(NOT libzip_FOUND) - find_package(libzip REQUIRED) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC zip) -endif() - -if(NOT stb_image_FOUND) - find_package(stb REQUIRED) - if(STB_IMAGE_FOUND) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC stb_image) - else() - message(FATAL_ERROR "stb_image not found. Please ensure stb is correctly fetched.") - endif() -endif() - -if(NOT yyjson_FOUND) - find_package(yyjson REQUIRED) - if(yyjson_FOUND) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC yyjson::yyjson) - else() - message(FATAL_ERROR "yyjson not found. Please ensure yyjson is correctly fetched.") - endif() -endif() - -if(NOT jerryscript_FOUND) - find_package(jerryscript REQUIRED) - target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - jerryscript::core - jerryscript::ext - jerryscript::port - ) -endif() if(DUSK_BACKTRACE) target_link_options(${DUSK_LIBRARY_TARGET_NAME} PUBLIC -rdynamic) @@ -64,8 +33,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME} add_subdirectory(animation) add_subdirectory(event) add_subdirectory(assert) -add_subdirectory(asset) -add_subdirectory(cutscene) add_subdirectory(item) add_subdirectory(story) add_subdirectory(console) @@ -77,8 +44,6 @@ add_subdirectory(error) add_subdirectory(input) add_subdirectory(locale) add_subdirectory(physics) -add_subdirectory(scene) -add_subdirectory(script) add_subdirectory(system) add_subdirectory(time) add_subdirectory(ui) diff --git a/src/dusk/asset/CMakeLists.txt b/src/dusk/asset/CMakeLists.txt deleted file mode 100644 index 0e87c96f..00000000 --- a/src/dusk/asset/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2025 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - asset.c - assetbatch.c - assetfile.c -) - -# Subdirs -add_subdirectory(loader) \ No newline at end of file diff --git a/src/dusk/asset/asset.c b/src/dusk/asset/asset.c deleted file mode 100644 index c60f8c80..00000000 --- a/src/dusk/asset/asset.c +++ /dev/null @@ -1,450 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" -#include "engine/engine.h" -#include "util/string.h" -#include "console/console.h" -#include - -asset_t ASSET; - -errorret_t assetInit(void) { - memoryZero(&ASSET, sizeof(asset_t)); - - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - threadMutexInit(&ASSET.loading[i].mutex); - } - - // assetInitPlatform must either define ASSET.zip or throw an error. - errorChain(assetInitPlatform()); - assertNotNull(ASSET.zip, "Asset zip null without error."); - threadInit(&ASSET.loadThread, assetUpdateAsync); - threadStart(&ASSET.loadThread); - - errorOk(); -} - -bool_t assetFileExists(const char_t *filename) { - assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long."); - - zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0); - if(idx < 0) return false; - return true; -} - -assetentry_t * assetGetEntry( - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -) { - // Is there an existing asset? - assetentry_t *entry = ASSET.entries; - do { - if(entry->type == ASSET_LOADER_TYPE_NULL) { - entry++; - continue; - } - if(stringEquals(entry->name, name)) { - assertTrue(entry->type == type, "Asset entry type mismatch."); - return entry; - } - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - // We did not find one existing, Find first available slot. - entry = ASSET.entries; - do { - if(entry->type != ASSET_LOADER_TYPE_NULL) { - entry++; - continue; - } - - if(entry->state == ASSET_ENTRY_STATE_NOT_STARTED) { - assetEntryInit(entry, name, type, input); - return entry; - } - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - assertUnreachable("No available asset entry slots."); - return NULL; -} - -uint32_t assetGetEntriesOfType( - assetentry_t **outEntries, - const assetloadertype_t type -) { - assertNotNull(outEntries, "Output entries cannot be NULL."); - - uint32_t count = 0; - assetentry_t *entry = ASSET.entries; - do { - if(entry->type == type) { - outEntries[count++] = entry; - } - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - return count; -} - -errorret_t assetRequireLoaded(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL."); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - assertIsMainThread("Currently only works on main thread."); - - if(entry->state == ASSET_ENTRY_STATE_LOADED) { - errorOk(); - } - - // Lock to prevent the reaper from collecting the entry mid-spin. - assetEntryLock(entry); - - while(entry->state != ASSET_ENTRY_STATE_LOADED) { - usleep(1000); - errorret_t ret = assetUpdate(); - if(errorIsNotOk(ret)) { - assetEntryUnlock(entry); - errorChain(ret); - } - } - - assetEntryUnlock(entry); - errorOk(); -} - -errorret_t assetRequireDisposed(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL."); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - assertIsMainThread("Currently only works on main thread."); - - if(entry->type == ASSET_LOADER_TYPE_NULL) { - errorOk(); - } - - if( - entry->state == ASSET_ENTRY_STATE_NOT_STARTED || - entry->state == ASSET_ENTRY_STATE_ERROR - ) { - errorOk(); - } - - assertTrue( - entry->refs.count == 0, - "Cannot require disposal of an entry with active references." - ); - - // Lock to prevent the reaper from collecting the entry mid-spin. - assetEntryLock(entry); - - while(entry->type != ASSET_LOADER_TYPE_NULL) { - usleep(1000); - errorret_t ret = assetUpdate(); - if(errorIsNotOk(ret)) { - assetEntryUnlock(entry); - errorChain(ret); - } - } - - assetEntryUnlock(entry); - errorOk(); -} - -assetentry_t * assetLock( - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -) { - assetentry_t *entry = assetGetEntry(name, type, input); - assetEntryLock(entry); - return entry; -} - -void assetUnlock(const char_t *name) { - assertNotNull(name, "Name cannot be NULL."); - - assetentry_t *entry = ASSET.entries; - do { - if( - entry->type != ASSET_LOADER_TYPE_NULL && - stringEquals(entry->name, name) - ) { - assetEntryUnlock(entry); - return; - } - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - assertUnreachable("Asset entry not found for unlock."); -} - -void assetUnlockEntry(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL."); - assetEntryUnlock(entry); -} - -errorret_t assetUpdate(void) { - assertIsMainThread("assetUpdate must be called from the main thread."); - - // Determine how many available loading slots we have. - assetloading_t *availableLoading[ASSET_LOADING_COUNT_MAX]; - uint8_t availableLoadingCount = 0; - assetloading_t *loading = ASSET.loading; - assetentry_t *entry; - - - do { - // We only care about NULL entry references. Nothing async touches this so - // it's fine to use raw here. - if(loading->entry != NULL) { - loading++; - continue; - } - availableLoading[availableLoadingCount++] = loading; - loading++; - } while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX); - - - // Now we can check for pending asset entries, we can't do anything if there - // is no available slots though. - if(availableLoadingCount > 0) { - entry = ASSET.entries; - do { - // Is this asset "ready to start loading" ? - if(entry->type == ASSET_LOADER_TYPE_NULL) { - entry++; - continue; - } - - // We only care about assets not started. - if(entry->state != ASSET_ENTRY_STATE_NOT_STARTED) { - entry++; - continue; - } - - // Pop a loading slot for this asset entry. - loading = availableLoading[--availableLoadingCount]; - - // Start loading this asset. - assetEntryStartLoading(entry, loading); - entry++; - - // Did we run out of loading slots? - if(availableLoadingCount == 0) { - break; - } - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - } - - // Now walk over all the loading slots and see what needs to be done. - loading = ASSET.loading; - do { - // Is the loading slot in use? Entry can only be modified synchronously. - if(loading->entry == NULL) { - loading++; - continue; - } - - // Lock the loading slot. This will prevent any async modifications. - threadMutexLock(&loading->mutex); - - // Check the state of the entry. - switch(loading->entry->state) { - // This thing is pending synchronous loading. - case ASSET_ENTRY_STATE_PENDING_SYNC: - loading->entry->state = ASSET_ENTRY_STATE_LOADING_SYNC; - // Unlock before calling loadSync. The sync loader may re-enter - // assetUpdate (e.g. a script loading another asset), and the async - // thread never touches LOADING_SYNC entries, so this is safe. - threadMutexUnlock(&loading->mutex); - - errorret_t ret = ( - ASSET_LOADER_CALLBACKS[loading->type].loadSync(loading) - ); - - // After a sync load, these are the only valid states. - assertTrue( - loading->entry->state == ASSET_ENTRY_STATE_LOADED || - loading->entry->state == ASSET_ENTRY_STATE_ERROR || - loading->entry->state == ASSET_ENTRY_STATE_PENDING_SYNC || - loading->entry->state == ASSET_ENTRY_STATE_PENDING_ASYNC, - "Loader did not set entry state to loaded or error on finished load." - ); - - if(errorIsNotOk(ret)) { - errorCatch(errorPrint(ret)); - assertTrue( - loading->entry->state == ASSET_ENTRY_STATE_ERROR, - "Loader did not set entry state to error on failed load." - ); - } else if(loading->entry->state == ASSET_ENTRY_STATE_LOADED) { - eventInvoke(&loading->entry->onLoaded, loading->entry); - } - - loading++; - break; - - case ASSET_ENTRY_STATE_LOADING_SYNC: - // A re-entrant assetUpdate call (e.g. from a script loading another - // asset) will see this entry mid-sync-load. Skip it. - threadMutexUnlock(&loading->mutex); - loading++; - continue; - - // Done loading, we can just free it up. - case ASSET_ENTRY_STATE_LOADED: - loading->entry = NULL; - threadMutexUnlock(&loading->mutex); - loading++; - break; - - case ASSET_ENTRY_STATE_ERROR: { - assetentry_t *errEntry = loading->entry; - loading->entry = NULL; - threadMutexUnlock(&loading->mutex); - eventInvoke(&errEntry->onError, errEntry); - errorThrow("Failed to load asset asynchronously."); - break; - } - - default: - threadMutexUnlock(&loading->mutex); - loading++; - continue; - } - } while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX); - - - // Reap unused entries. - entry = ASSET.entries; - do { - if(entry->state != ASSET_ENTRY_STATE_LOADED) { - entry++; - continue; - } - - if(entry->type == ASSET_LOADER_TYPE_NULL) { - entry++; - continue; - } - - if(entry->refs.count > 0) { - entry++; - continue; - } - - consolePrint("Reaping asset %s", entry->name); - errorChain(assetEntryDispose(entry)); - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - errorOk(); -} - -void assetUpdateAsync(thread_t *thread) { - assertNotMainThread("assetUpdateAsync must not run on the main thread."); - - while(!threadShouldStop(thread)) { - // Walk over each asset - assetloading_t *loading; - loading = ASSET.loading; - - do { - threadMutexLock(&loading->mutex); - - if(loading->entry == NULL) { - threadMutexUnlock(&loading->mutex); - loading++; - continue; - } - - switch(loading->entry->state) { - case ASSET_ENTRY_STATE_PENDING_ASYNC: - loading->entry->state = ASSET_ENTRY_STATE_LOADING_ASYNC; - assertNotNull( - ASSET_LOADER_CALLBACKS[loading->type].loadAsync, - "Loader does not support async loading." - ); - errorret_t ret = ( - ASSET_LOADER_CALLBACKS[loading->type].loadAsync(loading) - ); - - if(errorIsNotOk(ret)) { - errorCatch(errorPrint(ret)); - assertTrue( - loading->entry->state == ASSET_ENTRY_STATE_ERROR, - "Loader did not set entry state to error on failed load." - ); - } - - threadMutexUnlock(&loading->mutex); - loading++; - break; - - case ASSET_ENTRY_STATE_LOADING_ASYNC: - assertUnreachable( - "Entry is in a pending async state still?" - ); - break; - - default: - threadMutexUnlock(&loading->mutex); - loading++; - continue; - } - } while(loading < ASSET.loading + ASSET_LOADING_COUNT_MAX); - - if(threadShouldStop(thread)) break; - usleep(1000); - } -} - -errorret_t assetDispose(void) { - assertIsMainThread("Must be called from the main thread."); - threadStop(&ASSET.loadThread); - - // Free any script read-buffers left behind by an in-flight async load - // that was interrupted before the sync eval phase ran. - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assetloading_t *loading = &ASSET.loading[i]; - if( - loading->entry != NULL && - loading->type == ASSET_LOADER_TYPE_SCRIPT && - loading->loading.script.buffer != NULL - ) { - memoryFree(loading->loading.script.buffer); - loading->loading.script.buffer = NULL; - } - threadMutexDispose(&loading->mutex); - } - - // Dispose every non-null entry so type-specific dispose callbacks - // (e.g. assetScriptDispose freeing jerry values) run before the - // scripting engine is torn down. - assetentry_t *entry = ASSET.entries; - do { - if(entry->type != ASSET_LOADER_TYPE_NULL) { - errorChain(assetEntryDispose(entry)); - } - entry++; - } while(entry < ASSET.entries + ASSET_ENTRY_COUNT_MAX); - - // Cleanup zip file. - if(ASSET.zip != NULL) { - if(zip_close(ASSET.zip) != 0) { - errorThrow("Failed to close asset zip archive."); - } - ASSET.zip = NULL; - } - - errorChain(assetDisposePlatform()); - errorOk(); -} \ No newline at end of file diff --git a/src/dusk/asset/asset.h b/src/dusk/asset/asset.h deleted file mode 100644 index a560aa63..00000000 --- a/src/dusk/asset/asset.h +++ /dev/null @@ -1,153 +0,0 @@ -/** - * 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 "asset/assetplatform.h" -#include "assetfile.h" -#include "thread/thread.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloading.h" - -#ifndef assetInitPlatform - #error "Platform must define assetInitPlatform function." -#endif -#ifndef assetDisposePlatform - #error "Platform must define assetDisposePlatform function." -#endif - -#define ASSET_FILE_NAME "dusk.dsk" -#define ASSET_HEADER_SIZE 3 - -#define ASSET_LOADING_COUNT_MAX 4 -#define ASSET_ENTRY_COUNT_MAX 128 - -typedef struct asset_s { - zip_t *zip; - assetplatform_t platform; - - // Background loading thread. - thread_t loadThread; - - // Assets that are mid loading. - assetloading_t loading[ASSET_LOADING_COUNT_MAX]; - assetentry_t entries[ASSET_ENTRY_COUNT_MAX]; -} asset_t; - -extern asset_t ASSET; - -/** - * Initializes the asset system. - * - * @return An error code if the asset system could not be initialized properly. - */ -errorret_t assetInit(void); - -/** - * Checks if an asset file exists. - * - * @param filename The filename of the asset to check. - * @return true if the asset file exists, false otherwise. - */ -bool_t assetFileExists(const char_t *filename); - -/** - * Gets, or creates, a new asset entry. Internal - prefer assetLock. - * - * @param name Filename of the asset. - * @param type Type of the asset. - * @param input Loader-specific parameters. - */ -assetentry_t * assetGetEntry( - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -); - -/** - * Gets all asset entries of a given type. - * - * @param outEntries Output array to write the entries to. - * @param type Type of the asset entries to get. - * @return The number of entries written to outEntries. - */ -uint32_t assetGetEntriesOfType( - assetentry_t **outEntries, - const assetloadertype_t type -); - -/** - * Gets, creates, and locks an asset entry. The asset will begin loading on - * the next assetUpdate. Call assetUnlock when done to allow the entry to be - * reclaimed. - * - * @param name Filename of the asset. - * @param type Type of the asset. - * @param input Loader-specific parameters. - * @return The locked asset entry. - */ -assetentry_t * assetLock( - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -); - -/** - * Releases a lock on an asset entry by name. When all locks are released the - * entry will be reclaimed at the start of the next assetUpdate. - * - * @param name Filename of the asset to unlock. - */ -void assetUnlock(const char_t *name); - -/** - * Releases a lock on an asset entry by pointer. When all locks are released - * the entry will be reclaimed at the start of the next assetUpdate. - * - * @param entry The asset entry to unlock. - */ -void assetUnlockEntry(assetentry_t *entry); - -/** - * Requires an asset entry to be loaded. This will block until the asset entry - * is fully loaded. - * - * @param entry The asset entry to require. - * @return An error code if the asset entry could not be loaded properly. - */ -errorret_t assetRequireLoaded(assetentry_t *entry); - -/** - * Requires an asset entry to be disposed. This will block until the asset entry - * is fully disposed. - * - * @param entry The asset entry to require disposal of. - * @return An error code if the asset entry could not be disposed properly. - */ -errorret_t assetRequireDisposed(assetentry_t *entry); - -/** - * Updates the asset system. - * - * @return An error code if the asset system could not be updated properly. - */ -errorret_t assetUpdate(void); - -/** - * Starts the background asset loading thread. The thread runs assetUpdate - * in a loop with a short sleep until stopped. - * - * @param thread The thread runner. - */ -void assetUpdateAsync(thread_t *thread); - -/** - * Disposes/cleans up the asset system. - * - * @return An error code if the asset system could not be disposed properly. - */ -errorret_t assetDispose(void); \ No newline at end of file diff --git a/src/dusk/asset/assetbatch.c b/src/dusk/asset/assetbatch.c deleted file mode 100644 index 3bb27927..00000000 --- a/src/dusk/asset/assetbatch.c +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetbatch.h" -#include "asset.h" -#include "assert/assert.h" -#include "util/memory.h" -#include - -void assetBatchInit( - assetbatch_t *batch, - const uint16_t count, - const assetbatchdesc_t *descs -) { - assertNotNull(batch, "Batch cannot be NULL."); - assertNotNull(descs, "Descs cannot be NULL."); - assertTrue(count > 0, "Count must be greater than 0."); - assertTrue( - count <= ASSET_BATCH_COUNT_MAX, "Count exceeds ASSET_BATCH_COUNT_MAX." - ); - - memoryZero(batch, sizeof(assetbatch_t)); - batch->count = count; - - eventInit( - &batch->onLoaded, - batch->onLoadedCallbacks, batch->onLoadedUsers, ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onEntryLoaded, - batch->onEntryLoadedCallbacks, - batch->onEntryLoadedUsers, - ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onError, - batch->onErrorCallbacks, batch->onErrorUsers, ASSET_BATCH_EVENT_MAX - ); - eventInit( - &batch->onEntryError, - batch->onEntryErrorCallbacks, - batch->onEntryErrorUsers, - ASSET_BATCH_EVENT_MAX - ); - - for(uint16_t i = 0; i < count; i++) { - batch->inputs[i] = descs[i].input; - batch->entries[i] = assetLock( - descs[i].path, descs[i].type, &batch->inputs[i] - ); - - if(batch->entries[i]->state == ASSET_ENTRY_STATE_LOADED) { - // Already loaded (cached) - count it now, no subscription needed. - batch->loadedCount++; - } else if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) { - batch->errorCount++; - } else { - eventSubscribe( - &batch->entries[i]->onLoaded, assetBatchEntryOnLoadedCb, batch - ); - eventSubscribe( - &batch->entries[i]->onError, assetBatchEntryOnErrorCb, batch - ); - } - } -} - -void assetBatchLock(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - assetEntryLock(batch->entries[i]); - } -} - -void assetBatchUnlock(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - assetEntryUnlock(batch->entries[i]); - } -} - -bool_t assetBatchIsLoaded(const assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]->state != ASSET_ENTRY_STATE_LOADED) return false; - } - return true; -} - -bool_t assetBatchHasError(const assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]->state == ASSET_ENTRY_STATE_ERROR) return true; - } - return false; -} - -errorret_t assetBatchRequireLoaded(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - - bool_t allDone; - do { - allDone = true; - for(uint16_t i = 0; i < batch->count; i++) { - const assetentrystate_t state = batch->entries[i]->state; - if(state == ASSET_ENTRY_STATE_ERROR) { - errorThrow("Asset '%s' failed to load.", batch->entries[i]->name); - } - if(state != ASSET_ENTRY_STATE_LOADED) { - allDone = false; - } - } - if(!allDone) { - usleep(1000); - errorChain(assetUpdate()); - } - } while(!allDone); - - errorOk(); -} - -void assetBatchDispose(assetbatch_t *batch) { - assertNotNull(batch, "Batch cannot be NULL."); - for(uint16_t i = 0; i < batch->count; i++) { - if(batch->entries[i]) { - // Unsubscribe while we still hold a lock so the entry is live. - eventUnsubscribe(&batch->entries[i]->onLoaded, assetBatchEntryOnLoadedCb); - eventUnsubscribe(&batch->entries[i]->onError, assetBatchEntryOnErrorCb); - assetUnlockEntry(batch->entries[i]); - } - } - memoryZero(batch, sizeof(assetbatch_t)); -} - -void assetBatchEntryOnLoadedCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->loadedCount++; - eventInvoke(&batch->onEntryLoaded, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - if(batch->errorCount == 0) { - eventInvoke(&batch->onLoaded, batch); - } else { - eventInvoke(&batch->onError, batch); - } - } -} - -void assetBatchEntryOnErrorCb(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)params; - assetbatch_t *batch = (assetbatch_t *)user; - - batch->errorCount++; - eventInvoke(&batch->onEntryError, entry); - - if((uint16_t)(batch->loadedCount + batch->errorCount) >= batch->count) { - eventInvoke(&batch->onError, batch); - } -} diff --git a/src/dusk/asset/assetbatch.h b/src/dusk/asset/assetbatch.h deleted file mode 100644 index 20aaac2e..00000000 --- a/src/dusk/asset/assetbatch.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloader.h" -#include "event/event.h" - -#define ASSET_BATCH_COUNT_MAX 64 -#define ASSET_BATCH_EVENT_MAX 4 - -typedef struct { - const char_t *path; - assetloadertype_t type; - assetloaderinput_t input; -} assetbatchdesc_t; - -typedef struct { - assetentry_t *entries[ASSET_BATCH_COUNT_MAX]; - assetloaderinput_t inputs[ASSET_BATCH_COUNT_MAX]; - uint16_t count; - uint16_t loadedCount; - uint16_t errorCount; - - /** Fires once when every entry loaded. params = assetbatch_t * */ - event_t onLoaded; - eventcallback_t onLoadedCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onLoadedUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires each time a single entry loads. params = assetentry_t * */ - event_t onEntryLoaded; - eventcallback_t onEntryLoadedCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onEntryLoadedUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires when all entries finish (any with errors). params: assetbatch_t * */ - event_t onError; - eventcallback_t onErrorCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onErrorUsers[ASSET_BATCH_EVENT_MAX]; - - /** Fires each time a single entry errors. params = assetentry_t * */ - event_t onEntryError; - eventcallback_t onEntryErrorCallbacks[ASSET_BATCH_EVENT_MAX]; - void *onEntryErrorUsers[ASSET_BATCH_EVENT_MAX]; -} assetbatch_t; - -/** - * Initialises the batch from an array of descriptors. Each entry is locked - * and queued for loading immediately. - * - * @param batch Batch to initialise. - * @param descs Array of entry descriptors (need not outlive this call). - * @param count Number of descriptors (must be <= ASSET_BATCH_COUNT_MAX). - */ -void assetBatchInit( - assetbatch_t *batch, - uint16_t count, - const assetbatchdesc_t *descs -); - -/** - * Acquires one additional lock on every entry in the batch. - * - * @param batch Batch to lock. - */ -void assetBatchLock(assetbatch_t *batch); - -/** - * Releases one lock from every entry in the batch. When an entry's lock - * count reaches zero it will be reaped on the next assetUpdate. - * - * @param batch Batch to unlock. - */ -void assetBatchUnlock(assetbatch_t *batch); - -/** - * Returns true if every entry in the batch has finished loading. - * - * @param batch Batch to query. - */ -bool_t assetBatchIsLoaded(const assetbatch_t *batch); - -/** - * Returns true if any entry in the batch is in an error state. - * - * @param batch Batch to query. - */ -bool_t assetBatchHasError(const assetbatch_t *batch); - -/** - * Blocks until every entry is loaded. Returns an error if any entry fails. - * - * @param batch Batch to wait on. - */ -errorret_t assetBatchRequireLoaded(assetbatch_t *batch); - -/** - * Releases the batch's lock on every entry and clears the batch. After this - * call the batch struct may be reused with assetBatchInit. - * - * @param batch Batch to dispose. - */ -void assetBatchDispose(assetbatch_t *batch); - -/** - * Event trampoline invoked when a batch entry finishes loading. - * Increments the loaded counter and fires batch-level events. - * - * @param params The loaded assetentry_t pointer. - * @param user The owning assetbatch_t pointer. - */ -void assetBatchEntryOnLoadedCb(void *params, void *user); - -/** - * Event trampoline invoked when a batch entry fails to load. - * Increments the error counter and fires batch-level events. - * - * @param params The errored assetentry_t pointer. - * @param user The owning assetbatch_t pointer. - */ -void assetBatchEntryOnErrorCb(void *params, void *user); diff --git a/src/dusk/asset/assetfile.c b/src/dusk/asset/assetfile.c deleted file mode 100644 index a348eb0d..00000000 --- a/src/dusk/asset/assetfile.c +++ /dev/null @@ -1,380 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset/asset.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/math.h" - -errorret_t assetFileInit( - assetfile_t *file, - const char_t *filename, - void *params, - void *output -) { - assertNotNull(file, "Asset file cannot be NULL."); - assertStrLenMax(filename, ASSET_FILE_NAME_MAX, "Filename too long."); - - memoryZero(file, sizeof(assetfile_t)); - memoryCopy(file->filename, filename, ASSET_FILE_NAME_MAX); - file->params = params; - file->output = output; - - // Stat the file - zip_stat_init(&file->stat); - if(!zip_stat(ASSET.zip, filename, 0, &file->stat) == 0) { - errorThrow("Failed to stat asset file: %s", filename); - } - - // Minimum file size. - file->size = (zip_int64_t)file->stat.size; - if(file->size <= 0) { - errorThrow("Invalid asset file size: %s", filename); - } - - errorOk(); -} - -errorret_t assetFileRewind(assetfile_t *file) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(file->zipFile, "Asset file must be opened before rewinding."); - - if(file->position == 0) { - errorOk(); - } - - errorChain(assetFileClose(file)); - errorChain(assetFileOpen(file)); - errorOk(); -} - -errorret_t assetFileOpen(assetfile_t *file) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(file->filename, "Asset file filename cannot be NULL."); - assertNotNull(ASSET.zip, "Asset zip cannot be NULL."); - assertNull(file->zipFile, "Asset file already open."); - - file->zipFile = zip_fopen(ASSET.zip, file->filename, 0); - if(file->zipFile == NULL) { - errorThrow("Failed to open asset file: %s", file->filename); - } - file->position = 0; - errorOk(); -} - -errorret_t assetFileRead( - assetfile_t *file, - void *buffer, - const size_t bufferSize -) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(file->zipFile, "Asset file must be opened before reading."); - - if(buffer == NULL) { - size_t bytesRemaining = bufferSize; - uint8_t tempBuffer[256]; - while(bytesRemaining > 0) { - size_t chunkSize = mathMin(bytesRemaining, sizeof(tempBuffer)); - errorChain(assetFileRead(file, tempBuffer, chunkSize)); - file->position += chunkSize; - bytesRemaining -= chunkSize; - } - file->lastRead = bufferSize; - errorOk(); - } - - // I assume zip_fread takes buffer NULL for skipping? - zip_int64_t bytesRead = zip_fread(file->zipFile, buffer, bufferSize); - if(bytesRead < 0) { - errorThrow("Failed to read from asset file: %s", file->filename); - } - file->position += bytesRead; - file->lastRead = bytesRead; - errorOk(); -} - -errorret_t assetFileClose(assetfile_t *file) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(file->zipFile, "Asset file must be opened before closing."); - - if(zip_fclose(file->zipFile) != 0) { - errorThrow("Failed to close asset file: %s", file->filename); - } - file->zipFile = NULL; - file->position = 0; - errorOk(); -} - -errorret_t assetFileDispose(assetfile_t *file) { - if(file->zipFile != NULL) { - errorChain(assetFileClose(file)); - } - memoryZero(file, sizeof(assetfile_t)); - errorOk(); -} - -errorret_t assetFileReadEntire( - assetfile_t *file, - uint8_t **outBuffer, - size_t *outSize -) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(outBuffer, "outBuffer cannot be NULL."); - assertNotNull(outSize, "outSize cannot be NULL."); - assertTrue( - file->size > 0, - "Asset file has no size; call assetFileInit first." - ); - - // File should be closed currently. - assertNull(file->zipFile, "Asset file must be closed before reading entire."); - - // Open file - errorret_t ret = assetFileOpen(file); - if(errorIsNotOk(ret)) { - errorChain(ret); - } - - // Set output. - size_t size = (size_t)file->size; - uint8_t *buffer = (uint8_t *)memoryAllocate(size); - - // Read entire file. - ret = assetFileRead(file, buffer, size); - if(errorIsNotOk(ret)) { - memoryFree(buffer); - errorChain(ret); - } - - // Close the file. - ret = assetFileClose(file); - if(errorIsNotOk(ret)) { - memoryFree(buffer); - errorChain(ret); - } - - *outBuffer = buffer; - *outSize = size; - errorOk(); -} - -// Line Reader; -void assetFileLineReaderInit( - assetfilelinereader_t *reader, - assetfile_t *file, - uint8_t *readBuffer, - const size_t readBufferSize, - uint8_t *outBuffer, - const size_t outBufferSize -) { - assertNotNull(reader, "Line reader cannot be NULL."); - assertNotNull(file, " File cannot be NULL."); - assertNotNull(readBuffer, "Read buffer cannot be NULL."); - assertNotNull(outBuffer, "Output buffer cannot be NULL."); - assertTrue(readBufferSize > 0, "Read buffer size must be greater than 0."); - assertTrue(outBufferSize > 0, "Output buffer size must be greater than 0."); - - memoryZero(reader, sizeof(assetfilelinereader_t)); - - reader->file = file; - reader->readBuffer = readBuffer; - reader->readBufferSize = readBufferSize; - reader->outBuffer = outBuffer; - reader->outBufferSize = outBufferSize; -} - -size_t assetFileLineReaderUnreadBytes(const assetfilelinereader_t *reader) { - assertNotNull(reader, "Reader cannot be NULL."); - assertTrue(reader->bufferEnd >= reader->bufferStart, "Invalid buffer state."); - return reader->bufferEnd - reader->bufferStart; -} - -const uint8_t *assetFileLineReaderUnreadPtr( - const assetfilelinereader_t *reader -) { - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->readBuffer, "Read buffer cannot be NULL."); - return reader->readBuffer + reader->bufferStart; -} - -static errorret_t assetFileLineReaderAppend( - assetfilelinereader_t *reader, - const uint8_t *src, - size_t srcLength -) { - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->outBuffer, "Out buffer cannot be NULL."); - - if(srcLength == 0) { - errorOk(); - } - - /* reserve room for optional NUL terminator */ - if(reader->lineLength + srcLength >= reader->outBufferSize) { - errorThrow("Line length exceeds output buffer size."); - } - - memoryCopy(reader->outBuffer + reader->lineLength, src, srcLength); - reader->lineLength += srcLength; - errorOk(); -} - -static void assetFileLineReaderTerminate(assetfilelinereader_t *reader) { - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->outBuffer, "Out buffer cannot be NULL."); - assertTrue( - reader->lineLength < reader->outBufferSize, - "Line length exceeds out buffer." - ); - reader->outBuffer[reader->lineLength] = '\0'; -} - -static ssize_t assetFileLineReaderFindNewline( - const assetfilelinereader_t *reader -) { - size_t i; - - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->readBuffer, "Read buffer cannot be NULL."); - - for(i = reader->bufferStart; i < reader->bufferEnd; ++i) { - if(reader->readBuffer[i] == '\n') { - return (ssize_t)i; - } - } - - return -1; -} - -errorret_t assetFileLineReaderFill(assetfilelinereader_t *reader) { - - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->file, "File cannot be NULL."); - assertNotNull(reader->readBuffer, "Read buffer cannot be NULL."); - - if(reader->eof) errorOk(); - - errorret_t ret; - - size_t unreadBytes = assetFileLineReaderUnreadBytes(reader); - - /* If buffer is fully consumed, refill from start. */ - if(unreadBytes == 0) { - reader->bufferStart = 0; - reader->bufferEnd = 0; - - errorChain(assetFileRead( - reader->file, - reader->readBuffer, - reader->readBufferSize - )); - - if(reader->file->lastRead == 0) { - reader->eof = true; - errorOk(); - } - - reader->bufferStart = 0; - reader->bufferEnd = reader->file->lastRead; - errorOk(); - } - - /* - * There are unread bytes left but no newline in them. - * If bufferStart > 0, slide unread bytes to front so we can read more. - * This only happens when necessary to make space. - */ - if(reader->bufferEnd == reader->readBufferSize) { - if(reader->bufferStart == 0) { - /* - * Entire read buffer is unread and contains no newline. - * Caller must have a large enough outBuffer to accumulate across fills, - * so we consume these bytes into outBuffer before refilling. - */ - errorOk(); - } - - memoryMove( - reader->readBuffer, - reader->readBuffer + reader->bufferStart, - unreadBytes - ); - reader->bufferStart = 0; - reader->bufferEnd = unreadBytes; - } - - errorChain(assetFileRead( - reader->file, - reader->readBuffer + reader->bufferEnd, - reader->readBufferSize - reader->bufferEnd - )); - - if(reader->file->lastRead == 0) { - reader->eof = true; - errorOk(); - } - - reader->bufferEnd += reader->file->lastRead; - errorOk(); -} - -errorret_t assetFileLineReaderNext(assetfilelinereader_t *reader) { - assertNotNull(reader, "Reader cannot be NULL."); - assertNotNull(reader->file, "File cannot be NULL."); - assertNotNull(reader->readBuffer, "Read buffer cannot be NULL."); - assertNotNull(reader->outBuffer, "Out buffer cannot be NULL."); - - reader->lineLength = 0; - - for(;;) { - ssize_t newlineIndex = assetFileLineReaderFindNewline(reader); - - if(newlineIndex >= 0) { - size_t chunkLength = (size_t)newlineIndex - reader->bufferStart; - errorret_t ret; - - /* strip CR in CRLF */ - if( - chunkLength > 0 && - reader->readBuffer[(size_t)newlineIndex - 1] == '\r' - ) { - chunkLength--; - } - - errorChain(assetFileLineReaderAppend( - reader, - reader->readBuffer + reader->bufferStart, - chunkLength - )); - - reader->bufferStart = (size_t)newlineIndex + 1; - assetFileLineReaderTerminate(reader); - errorOk(); - } - - if(assetFileLineReaderUnreadBytes(reader) > 0) { - errorChain(assetFileLineReaderAppend( - reader, - assetFileLineReaderUnreadPtr(reader), - assetFileLineReaderUnreadBytes(reader) - )); - - reader->bufferStart = reader->bufferEnd; - } - - if(reader->eof) { - if(reader->lineLength > 0) { - assetFileLineReaderTerminate(reader); - errorOk(); - } - - errorThrow("End of file reached."); - } - - errorChain(assetFileLineReaderFill(reader)); - } -} \ No newline at end of file diff --git a/src/dusk/asset/assetfile.h b/src/dusk/asset/assetfile.h deleted file mode 100644 index 806b9cb9..00000000 --- a/src/dusk/asset/assetfile.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * 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 - -#define ASSET_FILE_NAME_MAX 48 - -typedef struct assetfile_s assetfile_t; - -typedef errorret_t (*assetfileloader_t)(assetfile_t *file); - -// Describes a file not yet loaded. -typedef struct assetfile_s { - char_t filename[ASSET_FILE_NAME_MAX]; - void *params; - void *output; - - zip_stat_t stat; - zip_int64_t size; - zip_int64_t position; - zip_int64_t lastRead; - zip_file_t *zipFile; -} assetfile_t; - -/** - * Initializes the asset file structure in preparation for loading. This will - * stat the file but not open the handle. - * - * @param file The asset file structure to initialize. - * @param filename The name of the asset file to load. - * @param params Optional loader params. - * @param output Output pointer for the loader. - * @return Error indicating success or failure. - */ -errorret_t assetFileInit( - assetfile_t *file, - const char_t *filename, - void *params, - void *output -); - -/** - * Opens the asset file for reading. After opening the loader is responsible - * for closing the file. - * - * @param file The asset file to open. - * @return An error code if the file could not be opened. - */ -errorret_t assetFileOpen(assetfile_t *file); - -/** - * Rewind the file to the initial position. - * - * @param file The asset file to rewind. - */ -errorret_t assetFileRewind(assetfile_t *file); - -/** - * Read bytes from the asset file. Assumes the file has already been opened - * prior to trying to read anything. - * - * @param file File to read from. - * @param buffer Buffer to read the file data into., or NULL to skip bytes. - * @param size Size of the buffer to read into. - */ -errorret_t assetFileRead( - assetfile_t *file, - void *buffer, - const size_t bufferSize -); - -/** - * Closes the asset file and releases any resources associated with it. - * - * @param file The asset file to close. - * @return An error code if the file could not be closed properly. - */ -errorret_t assetFileClose(assetfile_t *file); - -/** - * Disposes the asset file structure, closing any open handles and zeroing - * out the structure. - * - * @param file The asset file to dispose. - * @return An error code if the file could not be disposed properly. - */ -errorret_t assetFileDispose(assetfile_t *file); - -/** - * Reads the entire contents of the asset file into a newly allocated buffer. - * The caller is responsible for freeing the buffer with memoryFree. - * - * @param file The asset file to read. Must be initialized but not open. - * @param outBuffer Receives a pointer to the allocated buffer. - * @param outSize Receives the number of bytes written to the buffer. - * @return An error code if the file could not be read. - */ -errorret_t assetFileReadEntire( - assetfile_t *file, - uint8_t **outBuffer, - size_t *outSize -); - -typedef struct { - assetfile_t *file; - uint8_t *readBuffer; - size_t readBufferSize; - uint8_t *outBuffer; - size_t outBufferSize; - - // A - size_t bufferStart; - size_t bufferEnd; - bool_t eof;//? - - // Updated each reach: - size_t lineLength; -} assetfilelinereader_t; - -/** - * Initializes a line reader for the given asset file. The line reader will read - * lines from the file into the provided line buffer, using the provided buffer - * for reading chunks of the file. - * - * @param file The asset file to read from. Must already be opened. - * @param readBuffer Buffer to use for reading chunks of the file. - * @param readBufferSize Size of the read buffer. - * @param outBuffer Buffer to read lines into. Lines will be null-terminated. - * @param outBufferSize Size of the output buffer. - * @return An initialized line reader structure. - */ -void assetFileLineReaderInit( - assetfilelinereader_t *reader, - assetfile_t *file, - uint8_t *readBuffer, - const size_t readBufferSize, - uint8_t *outBuffer, - const size_t outBufferSize -); - -/** - * Reads the next line from the asset file into the line buffer. The line - * buffer is null-terminated and does not include the newline character. - * - * @param reader The line reader to read from. - * @return An error code if a failure occurs, or errorOk() if a line was read - * successfully. If the end of the file is reached, errorEndOfFile() is - * returned. - */ -errorret_t assetFileLineReaderNext(assetfilelinereader_t *reader); \ No newline at end of file diff --git a/src/dusk/asset/loader/CMakeLists.txt b/src/dusk/asset/loader/CMakeLists.txt deleted file mode 100644 index 21f5928c..00000000 --- a/src/dusk/asset/loader/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetentry.c - assetloader.c -) - - -# Subdirs -add_subdirectory(display) -add_subdirectory(locale) -add_subdirectory(json) -add_subdirectory(script) \ No newline at end of file diff --git a/src/dusk/asset/loader/assetentry.c b/src/dusk/asset/loader/assetentry.c deleted file mode 100644 index 9e9ad477..00000000 --- a/src/dusk/asset/loader/assetentry.c +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetentry.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/string.h" - -void assetEntryInit( - assetentry_t *entry, - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -) { - assertNotNull(entry, "Entry cannot be NULL"); - assertStrLenMin(name, 1, "Name cannot be empty"); - assertStrLenMax(name, ASSET_FILE_NAME_MAX - 1, "Name too long"); - assertTrue(type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - assertTrue(type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type."); - assertIsMainThread("Must be called from the main thread."); - - memoryZero(entry, sizeof(assetentry_t)); - stringCopy(entry->name, name, ASSET_FILE_NAME_MAX); - entry->type = type; - entry->state = ASSET_ENTRY_STATE_NOT_STARTED; - if(input) { - entry->inputData = *input; - entry->input = &entry->inputData; - } else { - memoryZero(&entry->inputData, sizeof(assetloaderinput_t)); - entry->input = NULL; - } - refInit(&entry->refs, entry, NULL, NULL, NULL); - - eventInit( - &entry->onLoaded, - entry->onLoadedCallbacks, entry->onLoadedUsers, - ASSET_ENTRY_EVENT_MAX - ); - eventInit( - &entry->onUnloaded, - entry->onUnloadedCallbacks, entry->onUnloadedUsers, - ASSET_ENTRY_EVENT_MAX - ); - eventInit( - &entry->onError, - entry->onErrorCallbacks, entry->onErrorUsers, - ASSET_ENTRY_EVENT_MAX - ); -} - -void assetEntryLock(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL"); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - - refLock(&entry->refs); -} - -void assetEntryUnlock(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL"); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - - refUnlock(&entry->refs); -} - -void assetEntryStartLoading( - assetentry_t *entry, - assetloading_t *loading -) { - assertNotNull(entry, "Entry cannot be NULL"); - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - assertTrue( - entry->state == ASSET_ENTRY_STATE_NOT_STARTED, - "Can only start loading from NOT_STARTED state." - ); - assertIsMainThread("Must be called from the main thread."); - - entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - memoryZero(&loading->loading, sizeof(assetloaderloading_t)); - loading->type = entry->type; - loading->entry = entry; - // At this point the asset manager will manage this thing's loading -} - -errorret_t assetEntryDispose(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL"); - assertTrue(entry->type != ASSET_LOADER_TYPE_NULL, "Invalid loader type."); - assertTrue(entry->type < ASSET_LOADER_TYPE_COUNT, "Invalid loader type."); - assertIsMainThread("Must be called from the main thread."); - assertTrue( - entry->refs.count == 0, - "Asset entry still refed at dispose time." - ); - - eventInvoke(&entry->onUnloaded, entry); - errorChain(ASSET_LOADER_CALLBACKS[entry->type].dispose(entry)); - memoryZero(entry, sizeof(assetentry_t)); - errorOk(); -} diff --git a/src/dusk/asset/loader/assetentry.h b/src/dusk/asset/loader/assetentry.h deleted file mode 100644 index 94eff3fc..00000000 --- a/src/dusk/asset/loader/assetentry.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/loader/assetloading.h" -#include "event/event.h" -#include "util/ref.h" - -typedef enum { - ASSET_ENTRY_STATE_NOT_STARTED, - ASSET_ENTRY_STATE_PENDING_ASYNC, - ASSET_ENTRY_STATE_LOADING_ASYNC, - ASSET_ENTRY_STATE_PENDING_SYNC, - ASSET_ENTRY_STATE_LOADING_SYNC, - ASSET_ENTRY_STATE_LOADED, - ASSET_ENTRY_STATE_ERROR -} assetentrystate_t; - -/** Maximum number of subscribers for each per-entry event. */ -#define ASSET_ENTRY_EVENT_MAX 2 - -typedef struct assetentry_s assetentry_t; - -struct assetentry_s { - char_t name[ASSET_FILE_NAME_MAX]; - assetloadertype_t type; - assetloaderoutput_t data; - assetentrystate_t state; - ref_t refs; - assetloaderinput_t *input; - assetloaderinput_t inputData; - /** - * Fired once when loading completes successfully (params = assetentry_t *). - * Always invoked on the main thread. - */ - event_t onLoaded; - eventcallback_t onLoadedCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onLoadedUsers[ASSET_ENTRY_EVENT_MAX]; - - /** - * Fired once when the entry is disposed/reaped (params = assetentry_t *). - * The asset data is still accessible when the callback runs. - * Always invoked on the main thread. - */ - event_t onUnloaded; - eventcallback_t onUnloadedCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onUnloadedUsers[ASSET_ENTRY_EVENT_MAX]; - - /** - * Fired once when loading fails (params = assetentry_t *). - * Always invoked on the main thread. - */ - event_t onError; - eventcallback_t onErrorCallbacks[ASSET_ENTRY_EVENT_MAX]; - void *onErrorUsers[ASSET_ENTRY_EVENT_MAX]; -}; - -/** - * Initializes an asset entry with the given name and type. This does not load - * the asset. - * - * @param entry The asset entry to initialize. - * @param name The name of the asset, used as a key for loading and caching. - * @param type The type of asset this entry represents. - * @param input Data that will be passed to the loader about how it should load. - */ -void assetEntryInit( - assetentry_t *entry, - const char_t *name, - const assetloadertype_t type, - assetloaderinput_t *input -); - -/** - * Locks an asset entry, preventing it from being freed until it is unlocked. - * - * @param entry The asset entry to lock. - */ -void assetEntryLock(assetentry_t *entry); - -/** - * Unlocks an asset entry, allowing it to be freed if there are no more locks. - * - * @param entry The asset entry to unlock. - */ -void assetEntryUnlock(assetentry_t *entry); - -/** - * Starts loading the given asset entry using an assetloading slot. This will - * be called by the asset manager when it deems it's a good time to begin the - * loading of this asset entry. - * - * Currently we return the error but in future this will not be returned. - * - * @param entry The asset entry to start loading. - * @param loading The assetloading slot to use for loading this asset entry. - * @return Any error that occurs during loading. - */ -void assetEntryStartLoading(assetentry_t *entry, assetloading_t *loading); - -/** - * Disposes an asset entry, freeing any resources it holds. - * Fires the onUnloaded event before releasing asset data. - * - * @param entry The asset entry to dispose. - * @return Any error that occurs during disposal. - */ -errorret_t assetEntryDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/assetloader.c b/src/dusk/asset/loader/assetloader.c deleted file mode 100644 index bce09b0e..00000000 --- a/src/dusk/asset/loader/assetloader.c +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetloader.h" - -assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = { - [ASSET_LOADER_TYPE_NULL] = { 0 }, - - [ASSET_LOADER_TYPE_MESH] = { - .loadSync = assetMeshLoaderSync, - .loadAsync = assetMeshLoaderAsync, - .dispose = assetMeshDispose - }, - - [ASSET_LOADER_TYPE_TEXTURE] = { - .loadSync = assetTextureLoaderSync, - .loadAsync = assetTextureLoaderAsync, - .dispose = assetTextureDispose - }, - - [ASSET_LOADER_TYPE_TILESET] = { - .loadSync = assetTilesetLoaderSync, - .loadAsync = assetTilesetLoaderAsync, - .dispose = assetTilesetDispose - }, - - [ASSET_LOADER_TYPE_LOCALE] = { - .loadSync = assetLocaleLoaderSync, - .loadAsync = assetLocaleLoaderAsync, - .dispose = assetLocaleDispose - }, - - [ASSET_LOADER_TYPE_JSON] = { - .loadSync = assetJsonLoaderSync, - .loadAsync = assetJsonLoaderAsync, - .dispose = assetJsonDispose - }, - - [ASSET_LOADER_TYPE_SCRIPT] = { - .loadSync = assetScriptLoaderSync, - .loadAsync = assetScriptLoaderAsync, - .dispose = assetScriptDispose - }, -}; diff --git a/src/dusk/asset/loader/assetloader.h b/src/dusk/asset/loader/assetloader.h deleted file mode 100644 index 14e983bb..00000000 --- a/src/dusk/asset/loader/assetloader.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/loader/display/assetmeshloader.h" -#include "asset/loader/display/assettextureloader.h" -#include "asset/loader/display/assettilesetloader.h" -#include "asset/loader/locale/assetlocaleloader.h" -#include "asset/loader/json/assetjsonloader.h" -#include "asset/loader/script/assetscriptloader.h" - -typedef enum { - ASSET_LOADER_TYPE_NULL, - - ASSET_LOADER_TYPE_MESH, - ASSET_LOADER_TYPE_TEXTURE, - ASSET_LOADER_TYPE_TILESET, - ASSET_LOADER_TYPE_LOCALE, - ASSET_LOADER_TYPE_JSON, - ASSET_LOADER_TYPE_SCRIPT, - - ASSET_LOADER_TYPE_COUNT -} assetloadertype_t; - -typedef union { - assetmeshloaderinput_t mesh; - assettextureloaderinput_t texture; - assettilesetloaderinput_t tileset; - assetlocaleloaderinput_t locale; - assetjsonloaderinput_t json; - assetscriptloaderinput_t script; -} assetloaderinput_t; - -typedef union { - assetmeshloaderloading_t mesh; - assettextureloaderloading_t texture; - assettilesetloaderloading_t tileset; - assetlocaleloaderloading_t locale; - assetjsonloaderloading_t json; - assetscriptloaderloading_t script; -} assetloaderloading_t; - -typedef union { - assetmeshoutput_t mesh; - assettextureoutput_t texture; - assettilesetoutput_t tileset; - assetlocaleoutput_t locale; - assetjsonoutput_t json; - assetscriptoutput_t script; -} assetloaderoutput_t; - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef errorret_t (assetloadersynccallback_t)(assetloading_t *loading); -typedef errorret_t (assetloaderasynccallback_t)(assetloading_t *loading); -typedef errorret_t (assetloaderdisposecallback_t)(assetentry_t *entry); - -typedef struct { - assetloadersynccallback_t *loadSync; - assetloaderasynccallback_t *loadAsync; - assetloaderdisposecallback_t *dispose; -} assetloadercallbacks_t; - -extern assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT]; - -/** - * Shorthand method to both chain an error (against the loader state) and to - * set the asset entry state to error. - * - * @param loading The asset loading slot. - * @param ret The error return value to check and chain if it's an error. - */ -#define assetLoaderErrorChain(loading, _expr) {\ - errorret_t _alec = (_expr); \ - if(errorIsNotOk(_alec)) { \ - (loading)->entry->state = ASSET_ENTRY_STATE_ERROR; \ - errorChain(_alec); \ - } \ -} - -/** - * Shorthand method to both throw an error (against the loader state) and to - * set the asset entry state to error. - * - * @param loading The asset loading slot. - * @param ... Format string and arguments for the error message. - */ -#define assetLoaderErrorThrow(loading, ...) {\ - loading->entry->state = ASSET_ENTRY_STATE_ERROR; \ - errorThrow(__VA_ARGS__); \ -} diff --git a/src/dusk/asset/loader/assetloading.h b/src/dusk/asset/loader/assetloading.h deleted file mode 100644 index bf15937f..00000000 --- a/src/dusk/asset/loader/assetloading.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetloader.h" -#include "asset/assetfile.h" -#include "thread/threadmutex.h" - -typedef struct assetentry_s assetentry_t; - -typedef struct assetloading_s { - threadmutex_t mutex; - assetloadertype_t type; - assetentry_t *entry; - assetloaderloading_t loading; -} assetloading_t; - -typedef errorret_t (assetloadingcallback_t)(assetloading_t *loading); - -typedef struct { - assetloadingcallback_t *loadSync; -} assetloadingcallbacks_t; diff --git a/src/dusk/asset/loader/display/CMakeLists.txt b/src/dusk/asset/loader/display/CMakeLists.txt deleted file mode 100644 index 391df145..00000000 --- a/src/dusk/asset/loader/display/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetmeshloader.c - assettextureloader.c - assettilesetloader.c -) \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assetmeshloader.c b/src/dusk/asset/loader/display/assetmeshloader.c deleted file mode 100644 index 8d0a950c..00000000 --- a/src/dusk/asset/loader/display/assetmeshloader.c +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetmeshloader.h" -#include "assert/assert.h" -#include "util/endian.h" -#include "util/memory.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" - -errorret_t assetMeshLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - - if(loading->loading.mesh.state != ASSET_MESH_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assetmeshoutput_t *out = &loading->entry->data.mesh; - assetfile_t *file = &loading->loading.mesh.file; - assetmeshinputaxis_t axis = loading->entry->inputData.mesh; - - assetLoaderErrorChain(loading, - assetFileInit(file, loading->entry->name, NULL, NULL) - ); - assetLoaderErrorChain(loading, assetFileOpen(file)); - - // Skip the 80-byte STL header. - assetLoaderErrorChain(loading, assetFileRead(file, NULL, 80)); - if(file->lastRead != 80) { - assetLoaderErrorThrow(loading, "Failed to skip STL header."); - } - - uint32_t triangleCount; - assetLoaderErrorChain(loading, - assetFileRead(file, &triangleCount, sizeof(uint32_t)) - ); - if(file->lastRead != sizeof(uint32_t)) { - assetLoaderErrorThrow(loading, "Failed to read tri count"); - } - triangleCount = endianLittleToHost32(triangleCount); - - out->vertices = memoryAllocate(sizeof(meshvertex_t) * triangleCount * 3); - meshvertex_t *verts = out->vertices; - - errorret_t ret; - for(uint32_t i = 0; i < triangleCount; i++) { - assetmeshstltriangle_t triData; - ret = assetFileRead(file, &triData, sizeof(triData)); - if(errorIsNotOk(ret)) { - memoryFree(verts); - out->vertices = NULL; - assetLoaderErrorChain(loading, ret); - } - if(file->lastRead != sizeof(triData)) { - memoryFree(verts); - out->vertices = NULL; - assetLoaderErrorThrow(loading, "Failed to read triangle data"); - } - - for(uint8_t j = 0; j < 3; j++) { - #if MESH_ENABLE_COLOR - verts[i * 3 + j].color.r = ( - (uint8_t)(endianLittleToHostFloat(triData.normal[0]) * 255.0f) - ); - verts[i * 3 + j].color.g = ( - (uint8_t)(endianLittleToHostFloat(triData.normal[1]) * 255.0f) - ); - verts[i * 3 + j].color.b = ( - (uint8_t)(endianLittleToHostFloat(triData.normal[2]) * 255.0f) - ); - verts[i * 3 + j].color.a = 0xFF; - #endif - - verts[i * 3 + j].uv[0] = 0.0f; - verts[i * 3 + j].uv[1] = 0.0f; - - for(uint8_t k = 0; k < 3; k++) { - verts[i * 3 + j].pos[k] = endianLittleToHostFloat( - triData.positions[j][k] - ); - } - - switch(axis) { - case MESH_INPUT_AXIS_Z_UP: { - float_t temp = verts[i * 3 + j].pos[1]; - verts[i * 3 + j].pos[1] = verts[i * 3 + j].pos[2]; - verts[i * 3 + j].pos[2] = temp; - break; - } - case MESH_INPUT_AXIS_X_UP: { - float_t temp = verts[i * 3 + j].pos[0]; - verts[i * 3 + j].pos[0] = verts[i * 3 + j].pos[1]; - verts[i * 3 + j].pos[1] = temp; - break; - } - case MESH_INPUT_AXIS_Y_DOWN: - verts[i * 3 + j].pos[1] = -verts[i * 3 + j].pos[1]; - break; - case MESH_INPUT_AXIS_Z_DOWN: { - float_t temp = verts[i * 3 + j].pos[1]; - verts[i * 3 + j].pos[1] = -verts[i * 3 + j].pos[2]; - verts[i * 3 + j].pos[2] = temp; - break; - } - case MESH_INPUT_AXIS_X_DOWN: { - float_t temp = verts[i * 3 + j].pos[0]; - verts[i * 3 + j].pos[0] = verts[i * 3 + j].pos[1]; - verts[i * 3 + j].pos[1] = -temp; - break; - } - case MESH_INPUT_AXIS_Y_UP: - default: - break; - } - } - } - - ret = assetFileClose(file); - if(errorIsNotOk(ret)) { - memoryFree(verts); - out->vertices = NULL; - assetLoaderErrorChain(loading, ret); - } - assetFileDispose(file); - - loading->loading.mesh.triangleCount = triangleCount; - loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_CREATE_MESH; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetMeshLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_MESH, "Invalid type."); - - switch(loading->loading.mesh.state) { - case ASSET_MESH_LOADING_STATE_INITIAL: - loading->loading.mesh.state = ASSET_MESH_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_MESH_LOADING_STATE_CREATE_MESH: - break; - - default: - errorOk(); - } - - assetmeshoutput_t *out = &loading->entry->data.mesh; - assertNotNull(out->vertices, "Mesh vertices should have been loaded by now."); - - errorret_t ret = meshInit( - &out->mesh, - MESH_PRIMITIVE_TYPE_TRIANGLES, - loading->loading.mesh.triangleCount * 3, - out->vertices - ); - if(errorIsNotOk(ret)) { - loading->entry->state = ASSET_ENTRY_STATE_ERROR; - memoryFree(out->vertices); - out->vertices = NULL; - assetLoaderErrorChain(loading, ret); - } - - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetMeshDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_MESH, "Invalid type."); - errorChain(meshDispose(&entry->data.mesh.mesh)); - memoryFree(entry->data.mesh.vertices); - errorOk(); -} diff --git a/src/dusk/asset/loader/display/assetmeshloader.h b/src/dusk/asset/loader/display/assetmeshloader.h deleted file mode 100644 index c803c526..00000000 --- a/src/dusk/asset/loader/display/assetmeshloader.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "display/mesh/mesh.h" -#include "assert/assert.h" - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef enum { - MESH_INPUT_AXIS_Y_UP, - MESH_INPUT_AXIS_Z_UP, - MESH_INPUT_AXIS_X_UP, - - MESH_INPUT_AXIS_Y_DOWN, - MESH_INPUT_AXIS_Z_DOWN, - MESH_INPUT_AXIS_X_DOWN, -} assetmeshinputaxis_t; - -typedef assetmeshinputaxis_t assetmeshloaderinput_t; - -typedef enum { - ASSET_MESH_LOADING_STATE_INITIAL, - ASSET_MESH_LOADING_STATE_READ_FILE, - ASSET_MESH_LOADING_STATE_CREATE_MESH, - ASSET_MESH_LOADING_STATE_DONE -} assetmeshloadingstate_t; - -typedef struct { - assetfile_t file; - assetmeshloadingstate_t state; - uint32_t triangleCount; -} assetmeshloaderloading_t; - -typedef struct { - mesh_t mesh; - meshvertex_t *vertices; -} assetmeshoutput_t; - -#pragma pack(push, 1) -typedef struct { - vec3 normal; - float_t positions[3][3]; - uint16_t attributeByteCount; -} assetmeshstltriangle_t; -#pragma pack(pop) - -assertStructSize(assetmeshstltriangle_t, 50); - -errorret_t assetMeshLoaderAsync(assetloading_t *loading); -errorret_t assetMeshLoaderSync(assetloading_t *loading); -errorret_t assetMeshDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/display/assettextureloader.c b/src/dusk/asset/loader/display/assettextureloader.c deleted file mode 100644 index 733b12fb..00000000 --- a/src/dusk/asset/loader/display/assettextureloader.c +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assettextureloader.h" -#include "assert/assert.h" -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" -#include "log/log.h" -#include "util/endian.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" - -stbi_io_callbacks ASSET_TEXTURE_STB_CALLBACKS = { - .read = assetTextureReader, - .skip = assetTextureSkipper, - .eof = assetTextureEOF -}; - -int assetTextureReader(void *user, char *data, int size) { - assertNotNull(data, "Data buffer for stb_image callbacks cannot be NULL."); - - assetfile_t *file = (assetfile_t*)user; - assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL."); - - errorret_t ret = assetFileRead(file, data, (size_t)size); - if(errorIsNotOk(ret)) { - errorCatch(errorPrint(ret)); - return -1; - } - - return file->lastRead; -} - -void assetTextureSkipper(void *user, int n) { - assetfile_t *file = (assetfile_t*)user; - assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL."); - - errorret_t ret = assetFileRead(file, NULL, (size_t)n); - if(errorIsNotOk(ret)) { - errorCatch(errorPrint(ret)); - } -} - -int assetTextureEOF(void *user) { - assetfile_t *file = (assetfile_t*)user; - assertNotNull(file, "Asset file in stb_image callbacks cannot be NULL."); - - return file->position >= file->size; -} - -errorret_t assetTextureLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Should be called from an async thread."); - - // Only care about loading pixels. - if(loading->loading.texture.state != ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS){ - errorOk(); - } - - // Init the file - assertNull( - loading->loading.texture.data, "Pixels already defined?" - ); - - assetfile_t *file = &loading->loading.texture.file; - assetLoaderErrorChain(loading, assetFileInit( - file, - loading->entry->name, - NULL, - &loading->entry->data.texture - )); - assetLoaderErrorChain(loading, assetFileOpen(file)); - - // Determine channels - int channelsDesired; - switch(loading->entry->inputData.texture) { - case TEXTURE_FORMAT_RGBA: - channelsDesired = 4; - break; - - default: - assetLoaderErrorThrow(loading, "Bad texture format."); - } - - // Load image pixels. - loading->loading.texture.data = stbi_load_from_callbacks( - &ASSET_TEXTURE_STB_CALLBACKS, - file, - &loading->loading.texture.width, - &loading->loading.texture.height, - &loading->loading.texture.channels, - channelsDesired - ); - - // Close out the file. - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - - // Ensure we loaded correctly. - if(loading->loading.texture.data == NULL) { - const char_t *errorStr = stbi_failure_reason(); - assetLoaderErrorThrow( - loading, "Failed to load texture from file %s.", errorStr - ); - } - - // Fixes a specific bug probably with Dolphin but for now just assuming endian - if(!isHostLittleEndian()) { - stbi__vertical_flip( - loading->loading.texture.data, - loading->loading.texture.width, - loading->loading.texture.height, - loading->loading.texture.channels - ); - } - - loading->loading.texture.state = ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetTextureLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.texture.state) { - case ASSET_TEXTURE_LOADING_STATE_INITIAL: - loading->loading.texture.state = ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE: - break; - - default: - errorOk(); - } - - // Create the texture. - assertNotNull( - loading->loading.texture.data, "Pixels should have been loaded by now." - ); - - assetLoaderErrorChain(loading, textureInit( - (texture_t*)&loading->entry->data.texture, - loading->loading.texture.width, - loading->loading.texture.height, - loading->entry->inputData.texture, - (texturedata_t){ - .rgbaColors = (color_t*)loading->loading.texture.data - } - )); - - // Free the pixels. - stbi_image_free(loading->loading.texture.data); - - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetTextureDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertIsMainThread("Must be called from the main thread."); - - return textureDispose(&entry->data.texture); -} \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettextureloader.h b/src/dusk/asset/loader/display/assettextureloader.h deleted file mode 100644 index b29ac5dc..00000000 --- a/src/dusk/asset/loader/display/assettextureloader.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "display/texture/texture.h" - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; -typedef textureformat_t assettextureloaderinput_t; - -typedef enum { - ASSET_TEXTURE_LOADING_STATE_INITIAL, - ASSET_TEXTURE_LOADING_STATE_LOAD_PIXELS, - ASSET_TEXTURE_LOADING_STATE_CREATE_TEXTURE, - ASSET_TEXTURE_LOADING_STATE_DONE -} assettextureloadingstate_t; - -typedef struct { - assetfile_t file; - assettextureloadingstate_t state; - - int channels, width, height; - uint8_t *data; -} assettextureloaderloading_t; - -typedef texture_t assettextureoutput_t; - -/** - * STB image read callback for asset files. - * - * @param user User data passed to the callback, should be an assetfile_t*. - * @param data Buffer to read the file data into. - * @param size Size of the buffer to read into. - * @return Number of bytes read, or -1 on error. - */ -int assetTextureReader(void *user, char *data, int size); - -/** - * STB image skip callback for asset files. - * - * @param user User data passed to the callback, should be an assetfile_t*. - * @param n Number of bytes to skip in the file. - */ -void assetTextureSkipper(void *user, int n); - -/** - * STB image EOF callback for asset files. - * - * @param user User data passed to the callback, should be an assetfile_t*. - * @return Non-zero if end of file has been reached, zero otherwise. - */ -int assetTextureEOF(void *user); - -/** - * Synchronous loader for texture assets. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetTextureLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader for texture assets. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetTextureLoaderSync(assetloading_t *loading); - -/** - * Disposer for texture assets. - * - * @param entry Asset entry containing the texture to dispose. - * @return Error code indicating success or failure of the dispose operation. - */ -errorret_t assetTextureDispose(assetentry_t *entry); \ No newline at end of file diff --git a/src/dusk/asset/loader/display/assettilesetloader.c b/src/dusk/asset/loader/display/assettilesetloader.c deleted file mode 100644 index 3e9cba92..00000000 --- a/src/dusk/asset/loader/display/assettilesetloader.c +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assettilesetloader.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "util/endian.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" - -errorret_t assetTilesetLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Should be called from an async thread."); - - if(loading->loading.tileset.state != ASSET_TILESET_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assertNull(loading->loading.tileset.data, "Data already defined?"); - - assetfile_t *file = &loading->loading.tileset.file; - assetLoaderErrorChain(loading, - assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - uint8_t *data = memoryAllocate(file->size); - assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, data, file->size)); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - assertTrue( - file->lastRead == file->size, - "Failed to read entire tileset file." - ); - - loading->loading.tileset.data = data; - loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_PARSE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetTilesetLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_TILESET, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.tileset.state) { - case ASSET_TILESET_LOADING_STATE_INITIAL: - loading->loading.tileset.state = ASSET_TILESET_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_TILESET_LOADING_STATE_PARSE: - break; - - default: - errorOk(); - } - - uint8_t *data = loading->loading.tileset.data; - assertNotNull(data, "Tileset data should have been loaded by now."); - - tileset_t *out = &loading->entry->data.tileset; - - if(data[0] != 'D' || data[1] != 'T' || data[2] != 'F') { - memoryFree(data); - assetLoaderErrorThrow(loading, "Invalid tileset header"); - } - - if(data[3] != 0x00) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Unsupported tileset version"); - } - - out->tileWidth = endianLittleToHost16(*(uint16_t *)(data + 4)); - out->tileHeight = endianLittleToHost16(*(uint16_t *)(data + 6)); - out->columns = endianLittleToHost16(*(uint16_t *)(data + 8)); - out->rows = endianLittleToHost16(*(uint16_t *)(data + 10)); - - if(out->tileWidth == 0) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Tile width cannot be 0"); - } - if(out->tileHeight == 0) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Tile height cannot be 0"); - } - if(out->columns == 0) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Column count cannot be 0"); - } - if(out->rows == 0) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Row count cannot be 0"); - } - - out->uv[0] = endianLittleToHostFloat(*(float *)(data + 16)); - out->uv[1] = endianLittleToHostFloat(*(float *)(data + 20)); - - if(out->uv[1] < 0.0f || out->uv[1] > 1.0f) { - memoryFree(data); - assetLoaderErrorThrow(loading, "Invalid v0 value in tileset"); - } - - out->tileCount = out->columns * out->rows; - memoryFree(data); - loading->loading.tileset.data = NULL; - - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetTilesetDispose(assetentry_t *entry) { - assertNotNull(entry, "Entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_TILESET, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - errorOk(); -} diff --git a/src/dusk/asset/loader/display/assettilesetloader.h b/src/dusk/asset/loader/display/assettilesetloader.h deleted file mode 100644 index 646b4064..00000000 --- a/src/dusk/asset/loader/display/assettilesetloader.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "display/texture/tileset.h" - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef struct { - void *nothing; -} assettilesetloaderinput_t; - -typedef enum { - ASSET_TILESET_LOADING_STATE_INITIAL, - ASSET_TILESET_LOADING_STATE_READ_FILE, - ASSET_TILESET_LOADING_STATE_PARSE, - ASSET_TILESET_LOADING_STATE_DONE -} assettilesetloadingstate_t; - -typedef struct { - assetfile_t file; - assettilesetloadingstate_t state; - - uint8_t *data; -} assettilesetloaderloading_t; - -typedef tileset_t assettilesetoutput_t; - -/** - * Asynchronous loader for tileset assets. Reads the raw DTF file bytes into - * the loading buffer so the sync phase can parse without blocking the main - * thread on I/O. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetTilesetLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader for tileset assets. Parses the DTF binary previously - * read by the async phase and populates the output tileset_t. - * - * @param loading Loading information for the asset being loaded. - * @return Error code indicating success or failure of the load operation. - */ -errorret_t assetTilesetLoaderSync(assetloading_t *loading); - -/** - * Disposer for tileset assets. - * - * @param entry Asset entry containing the tileset to dispose. - * @return Error code indicating success or failure of the dispose operation. - */ -errorret_t assetTilesetDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/json/CMakeLists.txt b/src/dusk/asset/loader/json/CMakeLists.txt deleted file mode 100644 index 7215ecbf..00000000 --- a/src/dusk/asset/loader/json/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetjsonloader.c -) \ No newline at end of file diff --git a/src/dusk/asset/loader/json/assetjsonloader.c b/src/dusk/asset/loader/json/assetjsonloader.c deleted file mode 100644 index c1d3ea9a..00000000 --- a/src/dusk/asset/loader/json/assetjsonloader.c +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetjsonloader.h" -#include "util/memory.h" -#include "assert/assert.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" - -errorret_t assetJsonLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Async loader should not be on main thread."); - - if(loading->loading.json.state != ASSET_JSON_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assertNull(loading->loading.json.buffer, "Buffer already defined?"); - - assetfile_t *file = &loading->loading.json.file; - assetLoaderErrorChain(loading, - assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - if(file->size > ASSET_JSON_FILE_SIZE_MAX) { - assetLoaderErrorThrow(loading, "JSON exceeds maximum allowed size"); - } - - size_t fileSize = (size_t)file->size; - uint8_t *buffer = memoryAllocate(fileSize); - assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, buffer, fileSize)); - assertTrue(file->lastRead == file->size, "Failed to read entire JSON file."); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - - loading->loading.json.buffer = buffer; - loading->loading.json.size = fileSize; - loading->loading.json.state = ASSET_JSON_LOADING_STATE_PARSE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetJsonLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_JSON, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.json.state) { - case ASSET_JSON_LOADING_STATE_INITIAL: - loading->loading.json.state = ASSET_JSON_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_JSON_LOADING_STATE_PARSE: - break; - - default: - errorOk(); - } - - uint8_t *buffer = loading->loading.json.buffer; - assertNotNull(buffer, "JSON buffer should have been loaded by now."); - - loading->entry->data.json = yyjson_read( - (char *)buffer, - loading->loading.json.size, - YYJSON_READ_ALLOW_COMMENTS | YYJSON_READ_ALLOW_TRAILING_COMMAS - ); - memoryFree(buffer); - loading->loading.json.buffer = NULL; - - if(!loading->entry->data.json) { - assetLoaderErrorThrow(loading, "Failed to parse JSON"); - } - - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetJsonDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_JSON, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - yyjson_doc_free(entry->data.json); - entry->data.json = NULL; - - errorOk(); -} diff --git a/src/dusk/asset/loader/json/assetjsonloader.h b/src/dusk/asset/loader/json/assetjsonloader.h deleted file mode 100644 index 4508064d..00000000 --- a/src/dusk/asset/loader/json/assetjsonloader.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "yyjson.h" - -#define ASSET_JSON_FILE_SIZE_MAX 1024*256 - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef struct { void *nothing; } assetjsonloaderinput_t; - -typedef enum { - ASSET_JSON_LOADING_STATE_INITIAL, - ASSET_JSON_LOADING_STATE_READ_FILE, - ASSET_JSON_LOADING_STATE_PARSE, - ASSET_JSON_LOADING_STATE_DONE -} assetjsonloadingstate_t; - -typedef struct { - assetfile_t file; - assetjsonloadingstate_t state; - uint8_t *buffer; - size_t size; -} assetjsonloaderloading_t; - -typedef yyjson_doc * assetjsonoutput_t; - -errorret_t assetJsonLoaderAsync(assetloading_t *loading); -errorret_t assetJsonLoaderSync(assetloading_t *loading); -errorret_t assetJsonDispose(assetentry_t *entry); diff --git a/src/dusk/asset/loader/locale/CMakeLists.txt b/src/dusk/asset/loader/locale/CMakeLists.txt deleted file mode 100644 index 256a5142..00000000 --- a/src/dusk/asset/loader/locale/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetlocaleloader.c -) \ No newline at end of file diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.c b/src/dusk/asset/loader/locale/assetlocaleloader.c deleted file mode 100644 index 6d7a825c..00000000 --- a/src/dusk/asset/loader/locale/assetlocaleloader.c +++ /dev/null @@ -1,860 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetlocaleloader.h" -#include "util/memory.h" -#include "util/math.h" -#include "util/string.h" -#include "assert/assert.h" - -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" - -errorret_t assetLocaleLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Async loader should not be on main thread."); - - if(loading->loading.locale.state != ASSET_LOCALE_LOADER_STATE_LOAD_HEADER) { - errorOk(); - } - - assetlocalefile_t *localeFile = &loading->entry->data.locale; - memoryZero(localeFile, sizeof(assetlocalefile_t)); - assetLoaderErrorChain(loading, assetFileInit( - &localeFile->file, loading->entry->name, NULL, NULL - )); - assetLoaderErrorChain(loading, assetFileOpen(&localeFile->file)); - - char_t buffer[1024]; - assetLoaderErrorChain(loading, assetLocaleGetString( - localeFile, "", 0, buffer, sizeof(buffer) - )); - assetLoaderErrorChain(loading, assetLocaleParseHeader( - localeFile, buffer, sizeof(buffer) - )); - - loading->loading.locale.state = ASSET_LOCALE_LOADER_STATE_DONE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetLocaleLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_LOCALE, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.locale.state) { - case ASSET_LOCALE_LOADER_STATE_INITIAL: - loading->loading.locale.state = ASSET_LOCALE_LOADER_STATE_LOAD_HEADER; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_LOCALE_LOADER_STATE_DONE: - break; - - default: - errorOk(); - } - - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetLocaleDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_LOCALE, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - assetlocalefile_t *localeFile = &entry->data.locale; - errorChain(assetFileClose(&localeFile->file)); - return assetFileDispose(&localeFile->file); -} - -// These functions probably need some cleaning; -errorret_t assetLocaleParseHeader( - assetlocalefile_t *localeFile, - char_t *headerBuffer, - const size_t headerBufferSize -) { - assertNotNull(localeFile, "Locale file cannot be NULL."); - assertNotNull(headerBuffer, "Header buffer cannot be NULL."); - assertTrue(headerBufferSize > 0, "Header buffer size must be > 0."); - - // Find "Plural-Forms: " line and parse out plural form info - char_t *pluralFormsLine = strstr(headerBuffer, "Plural-Forms:"); - if(!pluralFormsLine) { - errorOk(); - } - - pluralFormsLine += strlen("Plural-Forms:"); - - // Expect nplurals - char_t *npluralsStr = strstr(pluralFormsLine, "nplurals="); - if(!npluralsStr) { - errorThrow("Failed to find nplurals in Plural-Forms header."); - } - npluralsStr += strlen("nplurals="); - localeFile->pluralStateCount = (uint8_t)atoi(npluralsStr); - - if(localeFile->pluralStateCount == 0) { - errorThrow("nplurals must be greater than 0."); - } - if(localeFile->pluralStateCount > ASSET_LOCALE_FILE_PLURAL_FORM_COUNT) { - errorThrow( - "nplurals exceeds maximum supported plural forms: %d > %d", - localeFile->pluralStateCount, - ASSET_LOCALE_FILE_PLURAL_FORM_COUNT - ); - } - - // Expect plural= - char_t *pluralStr = strstr(pluralFormsLine, "plural="); - if(!pluralStr) { - errorThrow("Failed to find plural in Plural-Forms header."); - } - pluralStr += strlen("plural="); - - // Expect ( [expressions] ) - char_t *openParen = strchr(pluralStr, '('); - char_t *closeParen = strrchr(pluralStr, ')'); - if(!openParen || !closeParen || closeParen < openParen) { - errorThrow("Failed to find plural expression in Plural-Forms header."); - } - - // Parse: - // n [op] value ? index : n [op] value ? index : ... : final_index - char_t *ptr = openParen + 1; - uint8_t pluralIndex = 0; - uint8_t definedCount = 0; - - while(1) { - while(*ptr == ' ') ptr++; - - // Allow grouped subexpressions like: - // (n<7 ? 2 : 3) - // or - // (((3))) - uint8_t parenDepth = 0; - while(*ptr == '(') { - parenDepth++; - ptr++; - while(*ptr == ' ') ptr++; - } - - // Final fallback: just an integer - if(*ptr != 'n') { - char_t *endPtr = NULL; - int32_t finalIndex = (int32_t)strtol(ptr, &endPtr, 10); - if(endPtr == ptr) { - errorThrow("Expected final plural index."); - } - ptr = endPtr; - - while(*ptr == ' ') ptr++; - while(parenDepth > 0) { - if(*ptr != ')') { - errorThrow("Expected ')' after final plural index."); - } - ptr++; - parenDepth--; - - while(*ptr == ' ') ptr++; - } - - if(*ptr != ')') { - errorThrow("Expected ')' at end of plural expression."); - } - - if(finalIndex < 0 || finalIndex >= localeFile->pluralStateCount) { - errorThrow( - "Final plural expression index out of bounds: %d (nplurals: %d)", - finalIndex, - localeFile->pluralStateCount - ); - } - - localeFile->pluralDefaultIndex = (uint8_t)finalIndex; - definedCount++; - break; - } - - if(pluralIndex >= localeFile->pluralStateCount - 1) { - errorThrow( - "Too many plural conditions. Expected %d clauses for nplurals=%d.", - localeFile->pluralStateCount - 1, - localeFile->pluralStateCount - ); - } - - ptr++; // skip 'n' - - while(*ptr == ' ') ptr++; - - // Determine operator - assetlocalepluraloperation_t op; - if(strncmp(ptr, "==", 2) == 0) { - op = ASSET_LOCALE_PLURAL_OP_EQUAL; - ptr += 2; - } else if(strncmp(ptr, "!=", 2) == 0) { - op = ASSET_LOCALE_PLURAL_OP_NOT_EQUAL; - ptr += 2; - } else if(strncmp(ptr, "<=", 2) == 0) { - op = ASSET_LOCALE_PLURAL_OP_LESS_EQUAL; - ptr += 2; - } else if(strncmp(ptr, ">=", 2) == 0) { - op = ASSET_LOCALE_PLURAL_OP_GREATER_EQUAL; - ptr += 2; - } else if(*ptr == '<') { - op = ASSET_LOCALE_PLURAL_OP_LESS; - ptr++; - } else if(*ptr == '>') { - op = ASSET_LOCALE_PLURAL_OP_GREATER; - ptr++; - } else { - errorThrow("Unsupported plural operator."); - } - - while(*ptr == ' ') ptr++; - - // Parse the comparitor value - char_t *endPtr = NULL; - int32_t value = (int32_t)strtol(ptr, &endPtr, 10); - if(endPtr == ptr) { - errorThrow("Expected value for plural expression."); - } - ptr = endPtr; - - while(*ptr == ' ') ptr++; - - // Parse ternary operator - if(*ptr != '?') { - errorThrow("Expected '?' after plural expression."); - } - ptr++; - - while(*ptr == ' ') ptr++; - - // Parse the indice - endPtr = NULL; - int32_t index = (int32_t)strtol(ptr, &endPtr, 10); - if(endPtr == ptr) { - errorThrow("Expected index for plural expression."); - } - ptr = endPtr; - - if(index < 0 || index >= localeFile->pluralStateCount) { - errorThrow( - "Plural expression index out of bounds: %d (nplurals: %d)", - index, - localeFile->pluralStateCount - ); - } - - // Store plural expression. - localeFile->pluralIndices[pluralIndex] = (uint8_t)index; - localeFile->pluralOps[pluralIndex] = op; - localeFile->pluralValues[pluralIndex] = value; - pluralIndex++; - definedCount++; - - while(*ptr == ' ') ptr++; - - // Close any grouping parens that wrapped this conditional branch - while(parenDepth > 0) { - if(*ptr != ')') { - break; - } - ptr++; - parenDepth--; - - while(*ptr == ' ') ptr++; - } - - if(*ptr != ':') { - errorThrow("Expected ':' after plural expression."); - } - ptr++; - } - - // Must define exactly nplurals outcomes: - // (nplurals - 1) conditional results + 1 final fallback result - if( - pluralIndex != localeFile->pluralStateCount - 1 || - definedCount != localeFile->pluralStateCount - ) { - errorThrow("Plural expression count does not match nplurals."); - } - - errorOk(); -} - - -uint8_t assetLocaleEvaluatePlural( - assetlocalefile_t *file, - const int32_t pluralCount -) { - assertNotNull(file, "Locale file cannot be NULL."); - assertTrue(pluralCount >= 0, "Plural count cannot be negative."); - - for(uint8_t i = 0; i < file->pluralStateCount - 1; i++) { - int32_t value = file->pluralValues[i]; - switch(file->pluralOps[i]) { - case ASSET_LOCALE_PLURAL_OP_EQUAL: - if(pluralCount == value) return file->pluralIndices[i]; - break; - - case ASSET_LOCALE_PLURAL_OP_NOT_EQUAL: - if(pluralCount != value) return file->pluralIndices[i]; - break; - - case ASSET_LOCALE_PLURAL_OP_LESS: - if(pluralCount < value) return file->pluralIndices[i]; - break; - - case ASSET_LOCALE_PLURAL_OP_LESS_EQUAL: - if(pluralCount <= value) return file->pluralIndices[i]; - break; - - case ASSET_LOCALE_PLURAL_OP_GREATER: - if(pluralCount > value) return file->pluralIndices[i]; - break; - - case ASSET_LOCALE_PLURAL_OP_GREATER_EQUAL: - if(pluralCount >= value) return file->pluralIndices[i]; - break; - } - } - - return file->pluralDefaultIndex; -} - -errorret_t assetLocaleLineSkipBlanks( - assetfilelinereader_t *reader, - uint8_t *lineBuffer -) { - while(!reader->eof) { - // Skip blank lines - if(lineBuffer[0] == '\0') { - errorret_t r = assetFileLineReaderNext(reader); - if(errorIsNotOk(r)) { - errorCatch(r); - break; - } - continue; - } - - // Skip comment lines - if(lineBuffer[0] == '#') { - errorret_t r = assetFileLineReaderNext(reader); - if(errorIsNotOk(r)) { - errorCatch(r); - break; - } - continue; - } - - // Is line only spaces? - size_t lineLength = strlen((char_t *)lineBuffer); - size_t i; - bool_t onlySpaces = true; - for(i = 0; i < lineLength; i++) { - if(lineBuffer[i] != ' ') { - onlySpaces = false; - break; - } - } - - if(onlySpaces) { - errorret_t r = assetFileLineReaderNext(reader); - if(errorIsNotOk(r)) { - errorCatch(r); - break; - } - continue; - } - break; - } - - errorOk(); -} - -errorret_t assetLocaleLineUnbuffer( - assetfilelinereader_t *reader, - uint8_t *lineBuffer, - uint8_t *stringBuffer, - const size_t stringBufferSize -) { - stringBuffer[0] = '\0'; - - // At the point this funciton is called, we are looking for an opening quote. - char_t *start = strchr((char_t *)lineBuffer, '"'); - if(!start) { - errorThrow("Expected open (0) \""); - } - - char *end = strchr(start + 1, '"'); - if(!end) { - errorThrow("Expected close (0) \""); - } - *end = '\0'; - - if(strlen(start) >= stringBufferSize) { - errorThrow("String buffer overflow"); - } - memoryCopy(stringBuffer, start + 1, strlen(start)); - - // Now start buffering lines out - while(!reader->eof) { - errorret_t r = assetFileLineReaderNext(reader); - if(errorIsNotOk(r)) { - errorCatch(r); - break; - } - - // Skip blank lines - r = assetLocaleLineSkipBlanks(reader, lineBuffer); - if(errorIsNotOk(r)) { - errorCatch(r); - break; - } - - // Skip starting spaces - char_t *ptr = (char_t *)lineBuffer; - while(*ptr == ' ') { - ptr++; - } - - // Only consider lines starting with quote - if(*ptr != '"') { - break; - } - - ptr++; // move past first quote - - bool_t escaping = false; - char_t *dest = (char_t *)stringBuffer + strlen((char_t *)stringBuffer); - while(*ptr) { - if(escaping) { - // Handle escape sequences - switch(*ptr) { - case 'n': *dest++ = '\n'; break; - case 't': *dest++ = '\t'; break; - case '\\': *dest++ = '\\'; break; - case '"': *dest++ = '"'; break; - default: - errorThrow("Unknown escape sequence: \\%c", *ptr); - } - escaping = false; - } else if(*ptr == '\\') { - escaping = true; - } else if(*ptr == '"') { - // End of string - break; - } else { - // Regular character - *dest++ = *ptr; - } - if((size_t)(dest - (char_t *)stringBuffer) >= stringBufferSize) { - errorThrow("String buffer overflow"); - } - ptr++; - } - *dest = '\0'; - } - - errorOk(); -} - -errorret_t assetLocaleGetString( - assetlocalefile_t *file, - const char_t *messageId, - const int32_t pluralCount, - char_t *stringBuffer, - const size_t stringBufferSize -) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(messageId, "Message ID cannot be NULL."); - assertTrue(pluralCount >= 0, "Plural index cannot be negative."); - assertNotNull(stringBuffer, "String buffer cannot be NULL."); - assertTrue(stringBufferSize > 0, "String buffer size must be > 0"); - assetfilelinereader_t reader; - - bool_t msgidFound = false, msgidPluralFound = false, msgstrFound = false; - uint8_t msgidBuffer[256]; - uint8_t msgidPluralBuffer[256]; - uint8_t readBuffer[1024]; - uint8_t lineBuffer[1024]; - uint8_t pluralIndex = 0xFF; - - msgidBuffer[0] = '\0'; - msgidPluralBuffer[0] = '\0'; - stringBuffer[0] = '\0'; - - // Rewind and start reading lines. - errorChain(assetFileRewind(&file->file)); - assetFileLineReaderInit( - &reader, - &file->file, - readBuffer, - sizeof(readBuffer), - lineBuffer, - sizeof(lineBuffer) - ); - - // Skip blanks, comments, etc and start looking for msgid's - errorChain(assetLocaleLineSkipBlanks(&reader, lineBuffer)); - - while(!reader.eof) { - // Is this msgid? - if(memoryCompare(lineBuffer, "msgid", 5) != 0) { - errorChain(assetFileLineReaderNext(&reader)); - msgidBuffer[0] = '\0'; - continue; - } - - // Unbuffer the msgid - assetLocaleLineUnbuffer( - &reader, lineBuffer, (uint8_t *)msgidBuffer, sizeof(msgidBuffer) - ); - - // Is this the needle? - if(memoryCompare(msgidBuffer, messageId, strlen(messageId)) != 0) { - continue; - } - - msgidFound = true; - break; - } - if(!msgidFound) { - errorThrow("Failed to find message ID: %s", messageId); - } - - // We are either going to see a msgstr or a msgid_plural - while(!reader.eof) { - errorChain(assetLocaleLineSkipBlanks(&reader, lineBuffer)); - - // Is msgid_plural? - if( - !msgidPluralFound && - memoryCompare(lineBuffer, "msgid_plural", 12) == 0 - ) { - // Yes, start reading plural ID - assetLocaleLineUnbuffer( - &reader, - lineBuffer, - (uint8_t *)msgidPluralBuffer, - sizeof(msgidPluralBuffer) - ); - msgidPluralFound = true; - - // At this point we determine the plural index to use by running the - // plural formula - pluralIndex = assetLocaleEvaluatePlural( - file, - pluralCount - ); - continue; - } - - // Should be msgstr if not plural. - if(memoryCompare(lineBuffer, "msgstr", 6) != 0) { - errorThrow("Expected msgstr after msgid, found: %s", lineBuffer); - continue; - } - - // If plural we need an index - if(msgidPluralFound) { - // Skip blank chars - char_t *ptr = (char_t *)lineBuffer + 6; - while(*ptr == ' ') { - ptr++; - } - - if(*ptr != '[') { - errorThrow("Expected [ for plural form, found: %s", lineBuffer); - } - - ptr++; // move past [ - - // Parse until ] - char *end = strchr(ptr, ']'); - if(!end) { - errorThrow("Expected ] for plural form, found: %s", lineBuffer); - } - *end = '\0'; - - int32_t index = atoi(ptr); - if(index != pluralIndex) { - // Not the plural form we want, skip to the next useable line - while(!reader.eof) { - errorChain(assetFileLineReaderNext(&reader)); - errorChain(assetLocaleLineSkipBlanks(&reader, lineBuffer)); - if( - lineBuffer[0] == '\"' || - lineBuffer[0] == '\0' || - lineBuffer[0] == '#' - ) continue; - break; - } - continue; - } - - // Undo damage to line buffer from unbuffering. - *end = ']'; - } - - // Parse out msgstr - errorChain(assetLocaleLineUnbuffer( - &reader, lineBuffer, (uint8_t *)stringBuffer, stringBufferSize - )); - msgstrFound = true; - break; - }; - - if(!msgstrFound) { - errorThrow("Failed to find msgstr for message ID: %s", messageId); - } - - errorOk(); -} - -errorret_t assetLocaleGetStringWithVA( - assetlocalefile_t *file, - const char_t *messageId, - const int32_t pluralIndex, - char_t *buffer, - const size_t bufferSize, - ... -) { - assertNotNull(file, "Asset file cannot be NULL."); - assertNotNull(messageId, "Message ID cannot be NULL."); - assertNotNull(buffer, "Buffer cannot be NULL."); - assertTrue(bufferSize > 0, "Buffer size must be > 0."); - assertTrue(pluralIndex >= 0, "Plural cannot be negative."); - - char_t *tempBuffer; - tempBuffer = memoryAllocate(bufferSize); - errorret_t ret = assetLocaleGetString( - file, - messageId, - pluralIndex, - tempBuffer, - bufferSize - ); - if(errorIsNotOk(ret)) { - memoryFree(tempBuffer); - return ret; - } - - va_list args; - va_start(args, bufferSize); - int result = vsnprintf(buffer, bufferSize, tempBuffer, args); - va_end(args); - memoryFree(tempBuffer); - - if(result < 0) { - errorThrow("Failed to format locale string for ID: %s", messageId); - } - - return ret; -} - -errorret_t assetLocaleGetStringWithArgs( - assetlocalefile_t *file, - const char_t *id, - const int32_t plural, - char_t *buffer, - const size_t bufferSize, - const assetlocalearg_t *args, - const size_t argCount -) { - assertNotNull(id, "Message ID cannot be NULL."); - assertNotNull(buffer, "Buffer cannot be NULL."); - assertTrue(bufferSize > 0, "Buffer size must be > 0."); - assertTrue(plural >= 0, "Plural cannot be negative."); - assertTrue( - argCount == 0 || args != NULL, - "Args cannot be NULL when argCount > 0." - ); - - char_t *format = memoryAllocate(bufferSize); - if(format == NULL) { - errorThrow("Failed to allocate format buffer."); - } - - errorret_t ret = assetLocaleGetString( - file, - id, - plural, - format, - bufferSize - ); - if(errorIsNotOk(ret)) { - memoryFree(format); - return ret; - } - - size_t inIndex = 0; - size_t outIndex = 0; - size_t nextArg = 0; - - buffer[0] = '\0'; - - while(format[inIndex] != '\0') { - if(format[inIndex] != '%') { - if(outIndex + 1 >= bufferSize) { - memoryFree(format); - errorThrow("Formatted locale string buffer overflow for ID: %s", id); - } - - buffer[outIndex++] = format[inIndex++]; - continue; - } - - inIndex++; - - /* Escaped percent */ - if(format[inIndex] == '%') { - if(outIndex + 1 >= bufferSize) { - memoryFree(format); - errorThrow("Formatted locale string buffer overflow for ID: %s", id); - } - - buffer[outIndex++] = '%'; - inIndex++; - continue; - } - - if(nextArg >= argCount) { - memoryFree(format); - errorThrow("Not enough locale arguments for ID: %s", id); - } - - { - char_t specBuffer[32]; - char_t valueBuffer[256]; - size_t specLength = 0; - int written = 0; - char_t specifier; - - specBuffer[specLength++] = '%'; - - /* Allow flags / width / precision */ - while(format[inIndex] != '\0') { - char_t ch = format[inIndex]; - - if( - ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0' || - ch == '.' || (ch >= '0' && ch <= '9') - ) { - if(specLength + 1 >= sizeof(specBuffer)) { - memoryFree(format); - errorThrow("Format specifier too long for ID: %s", id); - } - - specBuffer[specLength++] = ch; - inIndex++; - continue; - } - - break; - } - - if(format[inIndex] == '\0') { - memoryFree(format); - errorThrow("Incomplete format specifier for ID: %s", id); - } - - specifier = format[inIndex++]; - - if(specifier != 's' && specifier != 'd' && specifier != 'f') { - memoryFree(format); - errorThrow( - "Unsupported format specifier '%%%c' for ID: %s", - specifier, - id - ); - } - - specBuffer[specLength++] = specifier; - specBuffer[specLength] = '\0'; - - switch(specifier) { - case 's': - if(args[nextArg].type != ASSET_LOCALE_ARG_STRING) { - memoryFree(format); - errorThrow("Expected string locale argument for ID: %s", id); - } - - written = snprintf( - valueBuffer, - sizeof(valueBuffer), - specBuffer, - args[nextArg].stringValue ? args[nextArg].stringValue : "" - ); - break; - - case 'd': - if(args[nextArg].type != ASSET_LOCALE_ARG_INT) { - memoryFree(format); - errorThrow("Expected int locale argument for ID: %s", id); - } - - written = snprintf( - valueBuffer, - sizeof(valueBuffer), - specBuffer, - args[nextArg].intValue - ); - break; - - case 'f': - if( - args[nextArg].type != ASSET_LOCALE_ARG_FLOAT && - args[nextArg].type != ASSET_LOCALE_ARG_INT - ) { - memoryFree(format); - errorThrow("Expected float or int locale argument for ID: %s", id); - } - - float_t floatValue = ( - args[nextArg].type == ASSET_LOCALE_ARG_FLOAT ? - args[nextArg].floatValue : - (float_t)args[nextArg].intValue - ); - - written = snprintf( - valueBuffer, - sizeof(valueBuffer), - specBuffer, - floatValue - ); - break; - } - - nextArg++; - - if(written < 0) { - memoryFree(format); - errorThrow("Failed to format locale string for ID: %s", id); - } - - if(outIndex + (size_t)written >= bufferSize) { - memoryFree(format); - errorThrow("Formatted locale string buffer overflow for ID: %s", id); - } - - memoryCopy(buffer + outIndex, valueBuffer, (size_t)written); - outIndex += (size_t)written; - } - } - - buffer[outIndex] = '\0'; - memoryFree(format); - errorOk(); -} \ No newline at end of file diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.h b/src/dusk/asset/loader/locale/assetlocaleloader.h deleted file mode 100644 index 79dfa31e..00000000 --- a/src/dusk/asset/loader/locale/assetlocaleloader.h +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -/** Input passed to the locale loader - currently unused. */ -typedef struct { void *nothing; } assetlocaleloaderinput_t; - -typedef enum { - ASSET_LOCALE_LOADER_STATE_INITIAL, - ASSET_LOCALE_LOADER_STATE_LOAD_HEADER, - ASSET_LOCALE_LOADER_STATE_DONE -} assetlocaleloaderstate_t; - -/** Per-slot scratch data used while the locale file is loading. */ -typedef struct { - assetlocaleloaderstate_t state; -} assetlocaleloaderloading_t; - -/** Maximum number of distinct plural forms a locale file may declare. */ -#define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6 - -/** - * Comparison operator used in a plural-form expression. - * - * Each condition in the plural-form header is evaluated as - * `n value` - * where `n` is the runtime plural count. - */ -typedef enum { - ASSET_LOCALE_PLURAL_OP_EQUAL, - ASSET_LOCALE_PLURAL_OP_NOT_EQUAL, - ASSET_LOCALE_PLURAL_OP_LESS, - ASSET_LOCALE_PLURAL_OP_LESS_EQUAL, - ASSET_LOCALE_PLURAL_OP_GREATER, - ASSET_LOCALE_PLURAL_OP_GREATER_EQUAL -} assetlocalepluraloperation_t; - -/** - * Discriminator tag for a locale string argument. - * @see assetlocalearg_t - */ -typedef enum { - ASSET_LOCALE_ARG_STRING, - ASSET_LOCALE_ARG_INT, - ASSET_LOCALE_ARG_FLOAT -} assetlocaleargtype_t; - -/** - * A single typed argument for locale string substitution. - * - * Used with @ref assetLocaleGetStringWithArgs to fill `%s`, `%d`, or `%f` - * placeholders inside a translated string. - */ -typedef struct { - /** Which union member is active. */ - assetlocaleargtype_t type; - union { - const char_t *stringValue; - int32_t intValue; - float_t floatValue; - }; -} assetlocalearg_t; - -/** - * Runtime state for an open locale file. - * - * Loaded once by @ref assetLocaleLoaderSync and kept alive for the lifetime - * of the asset entry. The plural-form fields are populated from the PO header - * by @ref assetLocaleParseHeader. - * - * Plural evaluation works as a linear scan over `pluralStateCount - 1` - * conditions; if none match, `pluralDefaultIndex` is returned. - */ -typedef struct { - /** Underlying file handle used to rewind and re-read the PO data. */ - assetfile_t file; - - /** Comparison operator for each conditional plural clause. */ - assetlocalepluraloperation_t pluralOps[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT]; - - /** Right-hand value for each conditional plural clause. */ - int32_t pluralValues[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT]; - - /** Form index returned when the corresponding condition is true. */ - int32_t pluralIndices[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT]; - - /** Total number of plural forms declared by `nplurals=`. */ - uint8_t pluralStateCount; - - /** Form index used when no conditional clause matches. */ - uint8_t pluralDefaultIndex; -} assetlocalefile_t; - -/** Convenience alias - the loaded output type of a locale asset entry. */ -typedef assetlocalefile_t assetlocaleoutput_t; - -/** - * Asynchronous loader callback. Opens the locale file, reads the PO header, - * and parses plural-form rules. All I/O happens here so the main thread is - * not blocked. Sets entry state to `ASSET_ENTRY_STATE_PENDING_SYNC` on - * success or `ASSET_ENTRY_STATE_ERROR` on failure. - * - * @param loading The loading slot for this asset entry. - * @return OK on success, error otherwise. - */ -errorret_t assetLocaleLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader callback. Confirms the async phase completed and marks - * the entry as `ASSET_ENTRY_STATE_LOADED`. - * - * @param loading The loading slot for this asset entry. - * @return OK on success, error otherwise. - */ -errorret_t assetLocaleLoaderSync(assetloading_t *loading); - -/** - * Dispose callback. Closes the open file handle and zeros the locale data. - * - * @param entry The asset entry to dispose. - * @return OK on success, error otherwise. - */ -errorret_t assetLocaleDispose(assetentry_t *entry); - -/** - * Parses the `Plural-Forms:` line from a PO header string and populates the - * plural-form fields of `localeFile`. If no `Plural-Forms:` key is present - * the function returns OK without modifying the struct. - * - * Supports the `nplurals=N; plural=();` syntax where `` is a - * chain of `n value ? index : ...` ternary conditions ending with a - * fallback index. - * - * @param localeFile Struct whose plural fields will be filled in. - * @param headerBuffer The raw PO header string (msgstr of msgid ""). - * @param headerBufferSize Size of `headerBuffer` in bytes. - * @return OK on success, error if the plural expression is malformed. - */ -errorret_t assetLocaleParseHeader( - assetlocalefile_t *localeFile, - char_t *headerBuffer, - const size_t headerBufferSize -); - -/** - * Evaluates which plural form index to use for a given count. - * - * Walks the conditions stored in `file` in order; returns the index for the - * first condition that matches `pluralCount`. Falls back to - * `file->pluralDefaultIndex` if none match. - * - * @param file Locale file with parsed plural rules. - * @param pluralCount The runtime count (e.g. number of items). - * @return Zero-based plural form index. - */ -uint8_t assetLocaleEvaluatePlural( - assetlocalefile_t *file, - const int32_t pluralCount -); - -/** - * Advances the line reader past blank lines, comment lines (starting with - * `#`), and lines containing only spaces. Stops at the first content line or - * end of file. - * - * @param reader Line reader positioned at the current line. - * @param lineBuffer Output buffer that the reader writes each line into. - * @return OK on success, error if reading fails. - */ -errorret_t assetLocaleLineSkipBlanks( - assetfilelinereader_t *reader, - uint8_t *lineBuffer -); - -/** - * Reads a PO quoted string value from the current line and any immediately - * following continuation lines that begin with `"`. - * - * Handles standard C escape sequences (`\n`, `\t`, `\\`, `\"`). - * - * @param reader Line reader positioned at the line containing the opening - * quote (e.g. `msgstr "..."`). - * @param lineBuffer Buffer filled on each @ref assetFileLineReaderNext - * call; also used to detect continuation lines. - * @param stringBuffer Destination for the unescaped string content. - * @param stringBufferSize Capacity of `stringBuffer` in bytes. - * @return OK on success, error if a quote or escape sequence is malformed or - * the buffer would overflow. - */ -errorret_t assetLocaleLineUnbuffer( - assetfilelinereader_t *reader, - uint8_t *lineBuffer, - uint8_t *stringBuffer, - const size_t stringBufferSize -); - -/** - * Looks up a translated string by message ID from the open locale file. - * - * Rewinds the file and scans from the beginning on every call. For plural - * entries (`msgid_plural`) the `pluralCount` is evaluated against the loaded - * plural rules to select the correct `msgstr[N]` form. - * - * @param file Locale file to search. Must be open. - * @param messageId PO message ID to find (`""` retrieves the header entry). - * @param pluralCount Count used to select the plural form (ignored for - * singular entries). - * @param stringBuffer Buffer to receive the translated string. - * @param stringBufferSize Capacity of `stringBuffer` in bytes. - * @return OK on success, error if the message ID is not found or I/O fails. - */ -errorret_t assetLocaleGetString( - assetlocalefile_t *file, - const char_t *messageId, - const int32_t pluralCount, - char_t *stringBuffer, - const size_t stringBufferSize -); - -/** - * Looks up a translated string and formats it with a `printf`-style variadic - * argument list. - * - * Retrieves the raw format string via @ref assetLocaleGetString then applies - * `vsnprintf` with the provided arguments. - * - * @param file Locale file to search. Must be open. - * @param messageId PO message ID to find. - * @param pluralCount Count used to select the plural form. - * @param buffer Buffer to receive the formatted string. - * @param bufferSize Capacity of `buffer` in bytes. - * @param ... Format arguments matching the specifiers in the translated - * string. - * @return OK on success, error if the message is not found or formatting - * fails. - */ -errorret_t assetLocaleGetStringWithVA( - assetlocalefile_t *file, - const char_t *messageId, - const int32_t pluralCount, - char_t *buffer, - const size_t bufferSize, - ... -); - -/** - * Looks up a translated string and substitutes typed arguments into its - * `%s`, `%d`, and `%f` placeholders. - * - * Unlike @ref assetLocaleGetStringWithVA this variant accepts an explicit - * array of @ref assetlocalearg_t values, which is safer to use from - * generated or scripted call sites. - * - * @param file Locale file to search. Must be open. - * @param messageId PO message ID to find. - * @param pluralCount Count used to select the plural form. - * @param buffer Buffer to receive the formatted string. - * @param bufferSize Capacity of `buffer` in bytes. - * @param args Array of typed arguments; may be NULL when `argCount` is 0. - * @param argCount Number of elements in `args`. - * @return OK on success, error if the message is not found, an argument type - * mismatches the specifier, or the buffer would overflow. - */ -errorret_t assetLocaleGetStringWithArgs( - assetlocalefile_t *file, - const char_t *messageId, - const int32_t pluralCount, - char_t *buffer, - const size_t bufferSize, - const assetlocalearg_t *args, - const size_t argCount -); diff --git a/src/dusk/asset/loader/script/CMakeLists.txt b/src/dusk/asset/loader/script/CMakeLists.txt deleted file mode 100644 index 76d9bd92..00000000 --- a/src/dusk/asset/loader/script/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - assetscriptloader.c -) diff --git a/src/dusk/asset/loader/script/assetscriptloader.c b/src/dusk/asset/loader/script/assetscriptloader.c deleted file mode 100644 index eafe190e..00000000 --- a/src/dusk/asset/loader/script/assetscriptloader.c +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetscriptloader.h" -#include "asset/loader/assetloading.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/assetloader.h" -#include "script/module/require/modulerequire.h" -#include "util/memory.h" -#include "assert/assert.h" -#include - -errorret_t assetScriptLoaderAsync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertNotMainThread("Async loader should not be on main thread."); - - if(loading->loading.script.state != ASSET_SCRIPT_LOADING_STATE_READ_FILE) { - errorOk(); - } - - assertNull(loading->loading.script.buffer, "Buffer already defined?"); - - assetfile_t *file = &loading->loading.script.file; - assetLoaderErrorChain( - loading, assetFileInit(file, loading->entry->name, NULL, NULL) - ); - - uint8_t *buffer = NULL; - size_t size = 0; - assetLoaderErrorChain(loading, assetFileReadEntire(file, &buffer, &size)); - assetLoaderErrorChain(loading, assetFileDispose(file)); - - // Null-terminate for jerry_eval. - memoryResize((void **)&buffer, size, size + 1); - buffer[size] = '\0'; - - loading->loading.script.buffer = buffer; - loading->loading.script.size = size; - loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_EXEC; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC; - errorOk(); -} - -errorret_t assetScriptLoaderSync(assetloading_t *loading) { - assertNotNull(loading, "Loading cannot be NULL"); - assertTrue(loading->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - switch(loading->loading.script.state) { - case ASSET_SCRIPT_LOADING_STATE_INITIAL: - loading->loading.script.state = ASSET_SCRIPT_LOADING_STATE_READ_FILE; - loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC; - errorOk(); - break; - - case ASSET_SCRIPT_LOADING_STATE_EXEC: - break; - - default: - errorOk(); - } - - // Get read buffer - uint8_t *buffer = loading->loading.script.buffer; - assertNotNull(buffer, "Script buffer should have been loaded by now."); - - // Get the current global script realm - jerry_value_t global = jerry_current_realm(); - - // Replace globalThis.module with a new `module = {}` - jerry_value_t oldModule = jerry_object_get_sz(global, "module"); - - jerry_value_t module = jerry_object(); - jerry_object_set_sz(global, "module", module); - - // Eval the script, we handle failure later down the code. - jerry_value_t result = jerry_eval( - buffer, - loading->loading.script.size, - JERRY_PARSE_NO_OPTS - ); - - // Free the read buffer - memoryFree(buffer); - loading->loading.script.buffer = NULL; - - // Restore globalThis.module - jerry_object_set_sz(global, "module", oldModule); - jerry_value_free(oldModule); - jerry_value_free(global); - - if(jerry_value_is_exception(result)) { - jerry_value_free(module); - - loading->entry->data.script.exports = jerry_undefined(); - loading->entry->state = ASSET_ENTRY_STATE_ERROR; - - // Get error string - char_t buf[256]; - moduleBaseExceptionMessage(result, buf, sizeof(buf)); - jerry_value_free(result); - assetLoaderErrorThrow( - loading, - "Script execution failed: %s: %s", loading->entry->name, buf - ); - } - - // Get module.exports - jerry_value_t exports = jerry_object_get_sz(module, "exports"); - jerry_value_free(result); - jerry_value_free(module); - - // Store the exports. - loading->entry->data.script.exports = exports; - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -errorret_t assetScriptDispose(assetentry_t *entry) { - assertNotNull(entry, "Asset entry cannot be NULL"); - assertTrue(entry->type == ASSET_LOADER_TYPE_SCRIPT, "Invalid type."); - assertIsMainThread("Must be called from the main thread."); - - if( - entry->data.script.exports != 0 && - !jerry_value_is_undefined(entry->data.script.exports) - ) { - jerry_value_free(entry->data.script.exports); - entry->data.script.exports = 0; - } - - errorOk(); -} diff --git a/src/dusk/asset/loader/script/assetscriptloader.h b/src/dusk/asset/loader/script/assetscriptloader.h deleted file mode 100644 index b38362bf..00000000 --- a/src/dusk/asset/loader/script/assetscriptloader.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetfile.h" -#include "script/scriptmodule.h" - -#define ASSET_SCRIPT_CHUNK_SIZE 1024 - -typedef struct assetloading_s assetloading_t; -typedef struct assetentry_s assetentry_t; - -typedef struct { - void *nothing; -} assetscriptloaderinput_t; - -typedef scriptmodule_t assetscriptoutput_t; - -typedef enum { - ASSET_SCRIPT_LOADING_STATE_INITIAL, - ASSET_SCRIPT_LOADING_STATE_READ_FILE, - ASSET_SCRIPT_LOADING_STATE_EXEC, - ASSET_SCRIPT_LOADING_STATE_DONE -} assetscriptloadingstate_t; - -typedef struct { - assetfile_t file; - assetscriptloadingstate_t state; - uint8_t *buffer; - size_t size; -} assetscriptloaderloading_t; - -/** - * Asynchronous loader for a script asset/module. - * - * @param loading The loading context. - * @returns An error code and state. - */ -errorret_t assetScriptLoaderAsync(assetloading_t *loading); - -/** - * Synchronous loader for a script asset/module. This executes the script after - * it has been loaded by the async loader. - * - * @param loading The loading context. - * @returns An error code and state. - */ -errorret_t assetScriptLoaderSync(assetloading_t *loading); - -/** - * Disposes of a loaded script asset/module. - * - * @param entry The asset entry to dispose. - * @returns An error code and state. - */ -errorret_t assetScriptDispose(assetentry_t *entry); diff --git a/src/dusk/console/console.c b/src/dusk/console/console.c index 5d50e1ed..bd160c59 100644 --- a/src/dusk/console/console.c +++ b/src/dusk/console/console.c @@ -65,6 +65,7 @@ void consoleUpdate(void) { errorret_t consoleDraw(void) { if(!CONSOLE.visible) errorOk(); + if(!FONT_DEFAULT.tileset) errorOk(); for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) { errorChain(textDraw( diff --git a/src/dusk/cutscene/CMakeLists.txt b/src/dusk/cutscene/CMakeLists.txt deleted file mode 100644 index cf650948..00000000 --- a/src/dusk/cutscene/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - cutscene.c -) diff --git a/src/dusk/cutscene/cutscene.c b/src/dusk/cutscene/cutscene.c deleted file mode 100644 index 33ff6af2..00000000 --- a/src/dusk/cutscene/cutscene.c +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#include "cutscene.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "console/console.h" -#include "time/time.h" - -cutscene_t CUTSCENE; - -errorret_t cutsceneInit(void) { - memoryZero(&CUTSCENE, sizeof(cutscene_t)); - errorOk(); -} - -errorret_t cutsceneUpdate(void) { - #ifdef DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) { - errorOk(); - } - #endif - - if(!CUTSCENE.active) errorOk(); - - cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent]; - if(event->onUpdate) errorChain(event->onUpdate()); - errorOk(); -} - -errorret_t cutscenePlay( - const cutsceneevent_t *events, - const uint8_t eventCount -) { - assertNotNull(events, "Events cannot be null"); - assertTrue(eventCount > 0, "Event count must be greater than zero"); - assertTrue( - eventCount <= CUTSCENE_EVENT_COUNT_MAX, - "Event count exceeds CUTSCENE_EVENT_COUNT_MAX" - ); - - if(CUTSCENE.active) { - errorChain(cutsceneStop()); - } - - memoryCopy(CUTSCENE.events, events, sizeof(cutsceneevent_t) * eventCount); - CUTSCENE.eventCount = eventCount; - CUTSCENE.eventCurrent = 0; - CUTSCENE.active = true; - - cutsceneevent_t *firstEvent = &CUTSCENE.events[0]; - if(firstEvent->onStart) errorChain(firstEvent->onStart()); - errorOk(); -} - -errorret_t cutsceneAdvance(void) { - if(!CUTSCENE.active) errorOk(); - - cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent]; - if(currentEvent->onEnd) errorChain(currentEvent->onEnd()); - CUTSCENE.eventCurrent++; - - if(CUTSCENE.eventCurrent >= CUTSCENE.eventCount) { - if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop()); - CUTSCENE.active = false; - errorOk(); - } - - cutsceneevent_t *nextEvent = &CUTSCENE.events[CUTSCENE.eventCurrent]; - if(nextEvent->onStart) errorChain(nextEvent->onStart()); - consolePrint("Cutscene advance"); - errorOk(); -} - -errorret_t cutsceneStop(void) { - if(!CUTSCENE.active) errorOk(); - - cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent]; - if(currentEvent->onEnd) errorChain(currentEvent->onEnd()); - - if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop()); - - CUTSCENE.active = false; - errorOk(); -} - -errorret_t cutsceneDispose(void) { - errorChain(cutsceneStop()); - errorOk(); -} - -bool_t cutsceneIsActive(void) { - return CUTSCENE.active; -} \ No newline at end of file diff --git a/src/dusk/cutscene/cutscene.h b/src/dusk/cutscene/cutscene.h deleted file mode 100644 index 1a294eed..00000000 --- a/src/dusk/cutscene/cutscene.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "error/error.h" - -#define CUTSCENE_EVENT_COUNT_MAX 16 - -typedef struct { - errorret_t (*onStart)(void); - errorret_t (*onEnd)(void); - errorret_t (*onUpdate)(void); -} cutsceneevent_t; - -typedef struct { - cutsceneevent_t events[CUTSCENE_EVENT_COUNT_MAX]; - uint8_t eventCount; - uint8_t eventCurrent; - errorret_t (*onStop)(void); - bool_t active; -} cutscene_t; - -extern cutscene_t CUTSCENE; - -/** - * Initializes the cutscene manager. - * - * @return Any error state that happened. - */ -errorret_t cutsceneInit(void); - -/** - * Ticks the active cutscene event, calling its onUpdate callback. - * Does nothing when no cutscene is playing. - * - * @return Any error state that happened. - */ -errorret_t cutsceneUpdate(void); - -/** - * Copies the given event array and begins playing from the first - * event. If a cutscene is already playing it is stopped first. - * - * @param events Array of events to copy. - * @param eventCount Number of events. Must be > 0 and - * <= CUTSCENE_EVENT_COUNT_MAX. - * @return Any error state that happened. - */ -errorret_t cutscenePlay( - const cutsceneevent_t *events, - const uint8_t eventCount -); - -/** - * Ends the current event and starts the next one. - * Marks the cutscene as inactive after the last event ends. - * Does nothing when no cutscene is playing. - * - * @return Any error state that happened. - */ -errorret_t cutsceneAdvance(void); - -/** - * Ends the current event and stops the cutscene immediately. - * Does nothing when no cutscene is playing. - * - * @return Any error state that happened. - */ -errorret_t cutsceneStop(void); - -/** - * Disposes of the cutscene manager, stopping any active cutscene. - * - * @return Any error state that happened. - */ -errorret_t cutsceneDispose(void); - -/** - * Returns whether a cutscene is currently playing. - * - * @return true if a cutscene is active. - */ -bool_t cutsceneIsActive(void); diff --git a/src/dusk/display/CMakeLists.txt b/src/dusk/display/CMakeLists.txt index 2597fc08..a7a393b3 100644 --- a/src/dusk/display/CMakeLists.txt +++ b/src/dusk/display/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC display.c + renderpipeline.c ) # Subdirectories diff --git a/src/dusk/display/display.c b/src/dusk/display/display.c index 49d3cbc5..3c60d646 100644 --- a/src/dusk/display/display.c +++ b/src/dusk/display/display.c @@ -1,13 +1,13 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "display/display.h" #include "display/framebuffer/framebuffer.h" -#include "scene/scene.h" +#include "display/renderpipeline.h" #include "display/spritebatch/spritebatch.h" #include "display/mesh/quad.h" #include "display/mesh/cube.h" @@ -21,8 +21,10 @@ #include "assert/assert.h" #include "util/memory.h" #include "util/string.h" -#include "asset/asset.h" #include "display/shader/shaderlist.h" +#include "display/shader/shaderunlit.h" +#include "entity/component/display/entitycamera.h" +#include "entity/component.h" #include "time/time.h" display_t DISPLAY = { 0 }; @@ -46,14 +48,12 @@ errorret_t displayInit(void) { errorChain(planeInit()); errorChain(capsuleInit()); errorChain(triPrismInit()); - + errorChain(frameBufferInitBackBuffer()); errorChain(spriteBatchInit()); errorChain(textInit()); errorChain(screenInit()); - // Setup initial shader with default values - errorChain(shaderListInit()); errorOk(); @@ -67,15 +67,43 @@ errorret_t displayUpdate(void) { // Reset state spriteBatchClear(); errorChain(frameBufferBind(NULL)); - - // Bind screen and render scene + + // Bind screen and render errorChain(screenBind()); frameBufferClear( FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH, SCREEN.background ); - errorChain(sceneRender()); + errorChain(componentRenderAll()); + errorChain(renderPipeline(entityCameraGetCurrent())); + + // UI Rendering + mat4 proj, view, ident; + glm_mat4_identity(ident); + errorChain(shaderBind(&SHADER_UNLIT)); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident)); + + glm_ortho( + 0.0f, SCREEN.width, + SCREEN.height, 0.0f, + 0.1f, 100.0f, + proj + ); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, 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 + ); + errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); + + errorChain(displaySetState((displaystate_t){ + .flags = DISPLAY_STATE_FLAG_BLEND + })); + errorChain(uiRender()); // Finish up screenUnbind(); @@ -100,11 +128,10 @@ errorret_t displayDispose(void) { errorChain(spriteBatchDispose()); screenDispose(); errorChain(textDispose()); - + #ifdef displayPlatformDispose displayPlatformDispose(); #endif - // For now, we just return an OK error. errorOk(); -} \ No newline at end of file +} diff --git a/src/dusk/scene/scenerenderpipeline.c b/src/dusk/display/renderpipeline.c similarity index 83% rename from src/dusk/scene/scenerenderpipeline.c rename to src/dusk/display/renderpipeline.c index 0ed2c8e8..006f4be0 100644 --- a/src/dusk/scene/scenerenderpipeline.c +++ b/src/dusk/display/renderpipeline.c @@ -5,7 +5,7 @@ * https://opensource.org/licenses/MIT */ -#include "scenerenderpipeline.h" +#include "renderpipeline.h" #include "entity/entitymanager.h" #include "entity/component/display/entityposition.h" #include "entity/component/display/entitycamera.h" @@ -14,7 +14,7 @@ #include "display/displaystate.h" #include "assert/assert.h" -int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r) { +int8_t renderPipelineGetPriority(const entityrenderable_t *r) { assertNotNull(r, "Renderable cannot be null"); if(r->priority != 0) return r->priority; switch(r->type) { @@ -29,15 +29,15 @@ int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r) { } } -int_t sceneRenderPipelineCompare(const void *a, const void *b) { +int_t renderPipelineCompare(const void *a, const void *b) { assertNotNull(a, "Entry a cannot be null"); assertNotNull(b, "Entry b cannot be null"); - const scenerenderpipelineentry_t *ea = (const scenerenderpipelineentry_t *)a; - const scenerenderpipelineentry_t *eb = (const scenerenderpipelineentry_t *)b; + const renderpipelineentry_t *ea = (const renderpipelineentry_t *)a; + const renderpipelineentry_t *eb = (const renderpipelineentry_t *)b; return (int_t)ea->effectivePriority - (int_t)eb->effectivePriority; } -shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r) { +shader_t *renderPipelineGetShader(const entityrenderable_t *r) { assertNotNull(r, "Renderable cannot be null"); switch(r->type) { case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: @@ -50,7 +50,7 @@ shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r) { } } -errorret_t sceneRenderPipeline(const entityid_t cameraId) { +errorret_t renderPipeline(const entityid_t cameraId) { mat4 proj, view, model, ident; glm_mat4_identity(ident); @@ -62,20 +62,20 @@ errorret_t sceneRenderPipeline(const entityid_t cameraId) { if(cameraId == ENTITY_ID_INVALID || entCount == 0) errorOk(); - scenerenderpipelineentry_t pipeline[ENTITY_COUNT_MAX]; + renderpipelineentry_t pipeline[ENTITY_COUNT_MAX]; for(entityid_t i = 0; i < entCount; i++) { entityrenderable_t *r = componentGetData( entities[i], components[i], COMPONENT_TYPE_RENDERABLE ); pipeline[i].entityId = entities[i]; pipeline[i].componentId = components[i]; - pipeline[i].effectivePriority = sceneRenderPipelineGetPriority(r); + pipeline[i].effectivePriority = renderPipelineGetPriority(r); } sort( pipeline, (size_t)entCount, - sizeof(scenerenderpipelineentry_t), - sceneRenderPipelineCompare + sizeof(renderpipelineentry_t), + renderPipelineCompare ); componentid_t camPos = entityGetComponent(cameraId, COMPONENT_TYPE_POSITION); @@ -111,7 +111,7 @@ errorret_t sceneRenderPipeline(const entityid_t cameraId) { entityrenderable_t *r = componentGetData( eid, cid, COMPONENT_TYPE_RENDERABLE ); - shader_t *s = sceneRenderPipelineGetShader(r); + shader_t *s = renderPipelineGetShader(r); componentid_t posComp = entityGetComponent(eid, COMPONENT_TYPE_POSITION); if(posComp == COMPONENT_ID_INVALID) { diff --git a/src/dusk/scene/scenerenderpipeline.h b/src/dusk/display/renderpipeline.h similarity index 68% rename from src/dusk/scene/scenerenderpipeline.h rename to src/dusk/display/renderpipeline.h index 637ee591..7e85fc7f 100644 --- a/src/dusk/scene/scenerenderpipeline.h +++ b/src/dusk/display/renderpipeline.h @@ -15,27 +15,28 @@ typedef struct { entityid_t entityId; componentid_t componentId; int8_t effectivePriority; -} scenerenderpipelineentry_t; +} renderpipelineentry_t; /** - * Returns the effective render priority for a renderable. When the renderable's - * explicit priority is non-zero that value is returned directly; otherwise an - * automatic value is derived from the renderable type and display state flags. + * Returns the effective render priority for a renderable. When the + * renderable's explicit priority is non-zero that value is returned directly; + * otherwise an automatic value is derived from the renderable type and display + * state flags. * * @param r The renderable component data. * @return Effective priority: lower renders first, higher renders last. */ -int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r); +int8_t renderPipelineGetPriority(const entityrenderable_t *r); /** - * sortcompare_t comparator for scenerenderpipelineentry_t. Compares by + * sortcompare_t comparator for renderpipelineentry_t. Compares by * effectivePriority ascending so lower-priority entries sort first. * - * @param a Pointer to the first scenerenderpipelineentry_t. - * @param b Pointer to the second scenerenderpipelineentry_t. + * @param a Pointer to the first renderpipelineentry_t. + * @param b Pointer to the second renderpipelineentry_t. * @return Negative, zero, or positive. */ -int_t sceneRenderPipelineCompare(const void *a, const void *b); +int_t renderPipelineCompare(const void *a, const void *b); /** * Returns the shader that will be used to render the given renderable. @@ -45,7 +46,7 @@ int_t sceneRenderPipelineCompare(const void *a, const void *b); * @param r The renderable component data. * @return Pointer to the shader, never NULL. */ -shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r); +shader_t *renderPipelineGetShader(const entityrenderable_t *r); /** * Renders all entities with renderable components in priority order. @@ -56,4 +57,4 @@ shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r); * @param cameraId The entity ID of the active camera, or ENTITY_ID_INVALID. * @return Error state. */ -errorret_t sceneRenderPipeline(const entityid_t cameraId); +errorret_t renderPipeline(const entityid_t cameraId); diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index e7878df7..63d74525 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -9,34 +9,18 @@ #include "assert/assert.h" #include "util/memory.h" #include "display/spritebatch/spritebatch.h" -#include "asset/asset.h" -#include "asset/loader/display/assettextureloader.h" -#include "asset/loader/display/assettilesetloader.h" #include "display/shader/shaderunlit.h" font_t FONT_DEFAULT; errorret_t textInit(void) { - assetloaderinput_t input = { .texture = TEXTURE_FORMAT_RGBA }; - assetentry_t *entryTexture = assetLock( - "ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input - ); - assetentry_t *entryTileset = assetLock( - "ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL - ); - errorChain(assetRequireLoaded(entryTexture)); - errorChain(assetRequireLoaded(entryTileset)); - - FONT_DEFAULT.texture = &entryTexture->data.texture; - FONT_DEFAULT.tileset = &entryTileset->data.tileset; + memoryZero(&FONT_DEFAULT, sizeof(font_t)); errorOk(); } errorret_t textDispose(void) { FONT_DEFAULT.texture = NULL; FONT_DEFAULT.tileset = NULL; - assetUnlock("ui/minogram.png"); - assetUnlock("ui/minogram.dtf"); errorOk(); } @@ -44,6 +28,7 @@ spritebatchsprite_t textGetSprite( const vec2 pos, const char_t c, const font_t *font ) { assertNotNull(font, "Font cannot be NULL"); + assertNotNull(font->tileset, "Font tileset not loaded."); // Change char from ASCII to a tile index. int32_t tileIndex = (int32_t)(c) - TEXT_CHAR_START; @@ -81,6 +66,8 @@ errorret_t textDraw( font_t *font ) { assertNotNull(text, "Text cannot be NULL"); + assertNotNull(font, "Font cannot be NULL"); + if(!font->tileset) errorOk(); spritebatchsprite_t sprite; shadermaterial_t material = { @@ -130,8 +117,10 @@ void textMeasure( int32_t *outHeight ) { assertNotNull(text, "Text cannot be NULL"); + assertNotNull(font, "Font cannot be NULL"); assertNotNull(outWidth, "Output width pointer cannot be NULL"); assertNotNull(outHeight, "Output height pointer cannot be NULL"); + if(!font->tileset) { *outWidth = 0; *outHeight = 0; return; } int32_t width = 0; int32_t height = font->tileset->tileHeight; diff --git a/src/dusk/display/text/text.h b/src/dusk/display/text/text.h index a62bf3bd..3dc59675 100644 --- a/src/dusk/display/text/text.h +++ b/src/dusk/display/text/text.h @@ -6,7 +6,6 @@ */ #pragma once -#include "asset/asset.h" #include "display/text/font.h" #include "display/spritebatch/spritebatch.h" diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index ec57a4e2..00622171 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -11,9 +11,6 @@ #include "input/input.h" #include "locale/localemanager.h" #include "display/display.h" -#include "scene/scene.h" -#include "cutscene/cutscene.h" -#include "asset/asset.h" #include "ui/ui.h" #include "ui/uitextbox.h" #include "assert/assert.h" @@ -23,7 +20,6 @@ #include "network/network.h" #include "system/system.h" #include "console/console.h" -#include "script/script.h" #include "item/backpack.h" #include "save/save.h" @@ -42,22 +38,17 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { timeInit(); consoleInit(); errorChain(inputInit()); - errorChain(assetInit()); // errorChain(saveInit()); errorChain(localeManagerInit()); errorChain(displayInit()); errorChain(uiInit()); errorChain(uiTextboxInit()); - errorChain(cutsceneInit()); entityManagerInit(); backpackInit(); physicsManagerInit(); errorChain(networkInit()); - errorChain(scriptInit()); - errorChain(sceneInit()); consolePrint("Engine initialized"); - errorChain(scriptExecFile("init.js")); errorOk(); } @@ -73,10 +64,6 @@ errorret_t engineUpdate(void) { errorChain(uiTextboxUpdate()); physicsManagerUpdate(); errorChain(displayUpdate()); - errorChain(cutsceneUpdate()); - errorChain(sceneUpdate()); - errorChain(assetUpdate()); - errorChain(scriptUpdate()); if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false; errorOk(); @@ -88,9 +75,6 @@ void engineExit(void) { errorret_t engineDispose(void) { uiTextboxDispose(); - cutsceneDispose(); - errorChain(sceneDispose()); - errorChain(scriptDispose()); errorChain(networkDispose()); entityManagerDispose(); localeManagerDispose(); @@ -98,7 +82,6 @@ errorret_t engineDispose(void) { consoleDispose(); errorChain(displayDispose()); // errorChain(saveDispose()); - errorChain(assetDispose()); errorOk(); } diff --git a/src/dusk/locale/localemanager.c b/src/dusk/locale/localemanager.c index e13095b3..5554bdcf 100644 --- a/src/dusk/locale/localemanager.c +++ b/src/dusk/locale/localemanager.c @@ -1,6 +1,6 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ @@ -19,25 +19,10 @@ errorret_t localeManagerInit() { errorret_t localeManagerSetLocale(const localeinfo_t *locale) { assertNotNull(locale, "Locale cannot be NULL"); - - if(LOCALE.entry != NULL) { - assetEntryUnlock(LOCALE.entry); - LOCALE.entry = NULL; - } - LOCALE.locale = locale; - LOCALE.entry = assetGetEntry(locale->file, ASSET_LOADER_TYPE_LOCALE, NULL); - assetEntryLock(LOCALE.entry); - errorChain(assetRequireLoaded(LOCALE.entry)); - errorOk(); } void localeManagerDispose() { - if(LOCALE.entry != NULL) { - assetEntryUnlock(LOCALE.entry); - LOCALE.entry = NULL; - } - LOCALE.locale = NULL; -} \ No newline at end of file +} diff --git a/src/dusk/locale/localemanager.h b/src/dusk/locale/localemanager.h index ca1a10a3..2e2fd632 100644 --- a/src/dusk/locale/localemanager.h +++ b/src/dusk/locale/localemanager.h @@ -1,6 +1,6 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ @@ -9,75 +9,29 @@ #include "error/error.h" #include "localemanager.h" #include "locale/localeinfo.h" -#include "asset/asset.h" typedef struct { const localeinfo_t *locale; - assetentry_t *entry; } localemanager_t; extern localemanager_t LOCALE; /** * Initialize the locale system. - * + * * @return An error code if a failure occurs. */ errorret_t localeManagerInit(); /** * Set the current locale. - * + * * @param locale The locale to set. * @return An error code if a failure occurs. */ errorret_t localeManagerSetLocale(const localeinfo_t *locale); -/** - * Get a localized string for the given message ID. - * - * @param id The message ID to retrieve. - * @param buffer Buffer to write the retrieved string to. - * @param bufferSize Size of the buffer. - * @param plural Plural index to retrieve. - * @param ... Additional arguments for formatting the string. - * @return An error code if a failure occurs. - */ -#define localeManagerGetText(id, buffer, bufferSize, plural, ...) \ - assetLocaleGetStringWithVA( \ - &LOCALE.entry->data.locale, \ - id, \ - plural, \ - buffer, \ - bufferSize, \ - __VA_ARGS__ \ - ) - -/** - * Get a localized string for the given message ID with a list of arguments. - * - * @param id The message ID to retrieve. - * @param buffer Buffer to write the retrieved string to. - * @param bufferSize Size of the buffer. - * @param plural Plural index to retrieve. - * @param args List of arguments for formatting the string. - * @param argCount Number of arguments in the list. - * @return An error code if a failure occurs. - */ -#define localeManagerGetTextArgs( \ - id, buffer, bufferSize, plural, args, argCount \ -) \ - assetLocaleGetStringWithArgs( \ - &LOCALE.entry->data.locale, \ - id, \ - plural, \ - buffer, \ - bufferSize, \ - args, \ - argCount \ - ) - /** * Dispose of the locale system. */ -void localeManagerDispose(); \ No newline at end of file +void localeManagerDispose(); diff --git a/src/dusk/overworld/map.c b/src/dusk/overworld/map.c index cc63c2c3..8bcfea36 100644 --- a/src/dusk/overworld/map.c +++ b/src/dusk/overworld/map.c @@ -7,11 +7,12 @@ #include "map.h" #include "assert/assert.h" -#include "asset/assetfile.h" #include "util/memory.h" #include "util/string.h" #include "console/console.h" +#define MAP_PATH_MAX 128 + map_t MAP; chunkindex_t mapChunkRelToIndex( @@ -39,7 +40,7 @@ errorret_t mapLoad(const char_t *handle) { memoryZero(&MAP, sizeof(map_t)); stringCopy(MAP.handle, handle, MAP_HANDLE_MAX); - char_t path[ASSET_FILE_NAME_MAX]; + char_t path[MAP_PATH_MAX]; stringFormat(path, sizeof(path), "maps/%s/init.js", handle); consolePrint("Map loaded: %s", handle); diff --git a/src/dusk/overworld/mapchunk.c b/src/dusk/overworld/mapchunk.c index a7fa78f3..551db903 100644 --- a/src/dusk/overworld/mapchunk.c +++ b/src/dusk/overworld/mapchunk.c @@ -10,34 +10,11 @@ #include "entity/entitymanager.h" #include "util/memory.h" #include "util/string.h" -#include "asset/asset.h" #include "console/console.h" errorret_t mapChunkLoad(mapchunk_t *chunk) { chunk->entityCount = 0; memoryZero(chunk->entities, sizeof(chunk->entities)); - - if(MAP.handle[0] == '\0') errorOk(); - - char_t path[ASSET_FILE_NAME_MAX]; - stringFormat( - path, sizeof(path), - "maps/%s/chunks/%d_%d_%d.js", - MAP.handle, - (int)chunk->position.x, - (int)chunk->position.y, - (int)chunk->position.z - ); - - if(!assetFileExists(path)) errorOk(); - - consolePrint( - "Chunk loaded: %s [%d,%d,%d]", - path, - (int)chunk->position.x, - (int)chunk->position.y, - (int)chunk->position.z - ); errorOk(); } diff --git a/src/dusk/scene/CMakeLists.txt b/src/dusk/scene/CMakeLists.txt deleted file mode 100644 index 771c68bd..00000000 --- a/src/dusk/scene/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -include(duskjs2c) -dusk_embed_js(${DUSK_LIBRARY_TARGET_NAME} - ${CMAKE_CURRENT_SOURCE_DIR}/scene.js -) - -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - scene.c - scenerenderpipeline.c -) \ No newline at end of file diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c deleted file mode 100644 index 03f37def..00000000 --- a/src/dusk/scene/scene.c +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#include "scene.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "time/time.h" -#include "display/screen/screen.h" -#include "entity/entitymanager.h" -#include "display/shader/shaderunlit.h" -#include "display/display.h" -#include "ui/ui.h" -#include "scene/scenerenderpipeline.h" -#include "entity/component.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "script/module/modulebase.h" -#include "console/console.h" -#include "script/script.h" -#include "scene_js.h" - -scene_t SCENE; - -errorret_t sceneInit(void) { - memoryZero(&SCENE, sizeof(scene_t)); - errorChain(scriptExecString(SCENE_JS)); - errorOk(); -} - -errorret_t sceneUpdate(void) { - #ifdef DUSK_TIME_DYNAMIC - if(TIME.dynamicUpdate) { - errorChain(scriptExecString("Scene.dynamicUpdate();")); - } else { - errorChain(scriptExecString("Scene.update();")); - } - #else - errorChain(scriptExecString("Scene.update();")); - errorChain(scriptExecString("Scene.dynamicUpdate();")); - #endif - - errorOk(); -} - -errorret_t sceneRender(void) { - mat4 proj, view, ident; - glm_mat4_identity(ident); - - errorChain(componentRenderAll()); - errorChain(sceneRenderPipeline(entityCameraGetCurrent())); - - // UI Rendering - errorChain(shaderBind(&SHADER_UNLIT)); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident)); - - glm_ortho( - 0.0f, SCREEN.width, - SCREEN.height, 0.0f, - 0.1f, 100.0f, - proj - ); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, 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 - ); - errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); - - errorChain(displaySetState((displaystate_t){ - .flags = DISPLAY_STATE_FLAG_BLEND - })); - errorChain(uiRender()); - - errorOk(); -} - -errorret_t sceneDispose(void) { - errorChain(scriptExecString("Scene.dispose();")); - errorOk(); -} diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h deleted file mode 100644 index e7ecfe39..00000000 --- a/src/dusk/scene/scene.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 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 struct { - void *nothing; -} scene_t; - -extern scene_t SCENE; - -/** - * Initialises the scene manager. - * - * @return An error if the init failed, or errorOk() if it succeeded. - */ -errorret_t sceneInit(void); - -/** - * Ticks the scene manager. Processes any pending scene transition, then - * calls scriptSceneUpdate on the active scene. - * - * @return An error if the update failed, or errorOk() if it succeeded. - */ -errorret_t sceneUpdate(void); - -/** - * Renders the current scene (entities, render pipeline, UI). - * - * @return An error if the render failed, or errorOk() if it succeeded. - */ -errorret_t sceneRender(void); - -/** - * Disposes the active scene immediately. - * - * @return An error if the dispose failed, or errorOk() if it succeeded. - */ -errorret_t sceneDispose(void); diff --git a/src/dusk/scene/scene.js b/src/dusk/scene/scene.js deleted file mode 100644 index c133b448..00000000 --- a/src/dusk/scene/scene.js +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2026 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -var Scene = { -}; - -Scene.update = () => { - if(!Scene.current || !Scene.current.update) return; - Scene.current.update(); -} - -Scene.dynamicUpdate = () => { - if(!Scene.current || !Scene.current.dynamicUpdate) return; - Scene.current.dynamicUpdate(); -} - -Scene.set = (newScene) => { - // Current scene active? - if(Scene.current && Scene.current.dispose) { - Scene.current.dispose(); - } - - // Set new scene - Scene.current = newScene; - if(!newScene) return; - - // Init - if(newScene.init) { - newScene.init(); - } -}; - -Scene.dispose = () => { - if(Scene.current && Scene.current.dispose) { - Scene.current.dispose(); - } - Scene.current = null; -} \ No newline at end of file diff --git a/src/dusk/script/CMakeLists.txt b/src/dusk/script/CMakeLists.txt deleted file mode 100644 index 9f110960..00000000 --- a/src/dusk/script/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - script.c - scriptpromisepend.c - scriptproto.c -) - -# Subdirectories -add_subdirectory(module) \ No newline at end of file diff --git a/src/dusk/script/module/CMakeLists.txt b/src/dusk/script/module/CMakeLists.txt deleted file mode 100644 index 746538c2..00000000 --- a/src/dusk/script/module/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -# 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 - modulebase.c - modulelist.c -) - -# Subdirs -add_subdirectory(animation) -add_subdirectory(asset) -add_subdirectory(console) -add_subdirectory(display) -add_subdirectory(engine) -add_subdirectory(entity) -add_subdirectory(event) -add_subdirectory(input) -add_subdirectory(item) -add_subdirectory(locale) -add_subdirectory(math) -add_subdirectory(overworld) -add_subdirectory(require) -add_subdirectory(save) -add_subdirectory(scene) -add_subdirectory(story) -add_subdirectory(system) -add_subdirectory(ui) \ No newline at end of file diff --git a/src/dusk/script/module/animation/CMakeLists.txt b/src/dusk/script/module/animation/CMakeLists.txt deleted file mode 100644 index 3483063d..00000000 --- a/src/dusk/script/module/animation/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - moduleanimation.c - moduleeasing.c -) diff --git a/src/dusk/script/module/animation/moduleanimation.c b/src/dusk/script/module/animation/moduleanimation.c deleted file mode 100644 index dcab0c14..00000000 --- a/src/dusk/script/module/animation/moduleanimation.c +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleanimation.h" -#include "animation/animation.h" - -typedef struct { - keyframe_t keyframes[MODULE_ANIMATION_KEYFRAME_MAX]; - animation_t anim; -} moduleanimationdata_t; - -scriptproto_t MODULE_ANIMATION_PROTO; - -moduleBaseFunction(moduleAnimationCtor) { - if(argc < 1 || !jerry_value_is_array(args[0])) { - return moduleBaseThrow( - "Animation: expected array of keyframe objects" - ); - } - uint32_t len = jerry_array_length(args[0]); - if(len == 0) { - return moduleBaseThrow( - "Animation: keyframe array must not be empty" - ); - } - const uint16_t count = len > (uint32_t)MODULE_ANIMATION_KEYFRAME_MAX - ? (uint16_t)MODULE_ANIMATION_KEYFRAME_MAX - : (uint16_t)len; - - moduleanimationdata_t *d = (moduleanimationdata_t *)memoryAllocate( - sizeof(moduleanimationdata_t) - ); - - for(uint16_t i = 0; i < count; i++) { - jerry_value_t elem = jerry_object_get_index(args[0], (uint32_t)i); - jerry_value_t jtm = moduleBaseGetProp(elem, "time"); - jerry_value_t jvl = moduleBaseGetProp(elem, "value"); - jerry_value_t jea = moduleBaseGetProp(elem, "easing"); - - d->keyframes[i].time = moduleBaseValueFloat(jtm); - d->keyframes[i].value = moduleBaseValueFloat(jvl); - d->keyframes[i].easing = jerry_value_is_number(jea) - ? (easingtype_t)moduleBaseValueInt(jea) - : EASING_LINEAR; - - jerry_value_free(jea); - jerry_value_free(jvl); - jerry_value_free(jtm); - jerry_value_free(elem); - } - - animationInit(&d->anim, d->keyframes, count); - - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_ANIMATION_PROTO.info, d - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleAnimationGetValue) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - moduleanimationdata_t *d = (moduleanimationdata_t *)scriptProtoGetValue( - &MODULE_ANIMATION_PROTO, callInfo->this_value - ); - if(!d) return jerry_undefined(); - return jerry_number( - (double)animationGetValue(&d->anim, moduleBaseArgFloat(0)) - ); -} - -void moduleAnimationInit(void) { - scriptProtoInit( - &MODULE_ANIMATION_PROTO, "Animation", - sizeof(moduleanimationdata_t), moduleAnimationCtor - ); - scriptProtoDefineFunc( - &MODULE_ANIMATION_PROTO, "getValue", moduleAnimationGetValue - ); -} - -void moduleAnimationDispose(void) { - scriptProtoDispose(&MODULE_ANIMATION_PROTO); -} diff --git a/src/dusk/script/module/animation/moduleanimation.h b/src/dusk/script/module/animation/moduleanimation.h deleted file mode 100644 index cd2b3def..00000000 --- a/src/dusk/script/module/animation/moduleanimation.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" - -/** Maximum keyframes per Animation instance. */ -#define MODULE_ANIMATION_KEYFRAME_MAX 64 - -extern scriptproto_t MODULE_ANIMATION_PROTO; - -/** - * new Animation(keyframes) - creates an Animation from a JS array of - * `{time, value, easing?}` descriptor objects. - * - * @param args[0] Array of keyframe descriptors. - */ -moduleBaseFunction(moduleAnimationCtor); - -/** - * animation.getValue(time) - interpolates the animation at `time`. - * - * @param args[0] Time in seconds (number). - * @return Interpolated float value. - */ -moduleBaseFunction(moduleAnimationGetValue); - -/** - * Initializes the Animation module and registers the Animation constructor. - */ -void moduleAnimationInit(void); - -/** - * Disposes the Animation module. - */ -void moduleAnimationDispose(void); diff --git a/src/dusk/script/module/animation/moduleeasing.c b/src/dusk/script/module/animation/moduleeasing.c deleted file mode 100644 index 65bdcaea..00000000 --- a/src/dusk/script/module/animation/moduleeasing.c +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleeasing.h" -#include "animation/easing.h" - -scriptproto_t MODULE_EASING_PROTO; - -moduleBaseFunction(moduleEasingApply) { - moduleBaseRequireArgs(2); - moduleBaseRequireNumber(0); - moduleBaseRequireNumber(1); - const int32_t type = moduleBaseArgInt(0); - if(type < 0 || type >= (int32_t)EASING_COUNT) { - return moduleBaseThrow("Easing.apply: invalid easing type"); - } - const float_t t = moduleBaseArgFloat(1); - return jerry_number((double)easingApply((easingtype_t)type, t)); -} - -void moduleEasingInit(void) { - scriptProtoInit(&MODULE_EASING_PROTO, "Easing", 0, NULL); - scriptProtoDefineStaticFunc( - &MODULE_EASING_PROTO, "apply", moduleEasingApply - ); - - moduleBaseSetInt("EASING_LINEAR", EASING_LINEAR); - moduleBaseSetInt("EASING_IN_SINE", EASING_IN_SINE); - moduleBaseSetInt("EASING_OUT_SINE", EASING_OUT_SINE); - moduleBaseSetInt("EASING_IN_OUT_SINE", EASING_IN_OUT_SINE); - moduleBaseSetInt("EASING_IN_QUAD", EASING_IN_QUAD); - moduleBaseSetInt("EASING_OUT_QUAD", EASING_OUT_QUAD); - moduleBaseSetInt("EASING_IN_OUT_QUAD", EASING_IN_OUT_QUAD); - moduleBaseSetInt("EASING_IN_CUBIC", EASING_IN_CUBIC); - moduleBaseSetInt("EASING_OUT_CUBIC", EASING_OUT_CUBIC); - moduleBaseSetInt("EASING_IN_OUT_CUBIC", EASING_IN_OUT_CUBIC); - moduleBaseSetInt("EASING_IN_QUART", EASING_IN_QUART); - moduleBaseSetInt("EASING_OUT_QUART", EASING_OUT_QUART); - moduleBaseSetInt("EASING_IN_OUT_QUART", EASING_IN_OUT_QUART); - moduleBaseSetInt("EASING_IN_BACK", EASING_IN_BACK); - moduleBaseSetInt("EASING_OUT_BACK", EASING_OUT_BACK); - moduleBaseSetInt("EASING_IN_OUT_BACK", EASING_IN_OUT_BACK); -} - -void moduleEasingDispose(void) { - scriptProtoDispose(&MODULE_EASING_PROTO); -} diff --git a/src/dusk/script/module/animation/moduleeasing.h b/src/dusk/script/module/animation/moduleeasing.h deleted file mode 100644 index fe9f48a2..00000000 --- a/src/dusk/script/module/animation/moduleeasing.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_EASING_PROTO; - -/** - * Easing.apply(type, t) - applies easing function `type` to normalized - * time `t` and returns the eased value. - * - * @param args[0] Easing type constant (EASING_*). - * @param args[1] Normalized input time in [0, 1]. - * @return Eased value as a number. - */ -moduleBaseFunction(moduleEasingApply); - -/** - * Initializes the Easing module and injects all EASING_* constants. - */ -void moduleEasingInit(void); - -/** - * Disposes the Easing module. - */ -void moduleEasingDispose(void); diff --git a/src/dusk/script/module/asset/CMakeLists.txt b/src/dusk/script/module/asset/CMakeLists.txt deleted file mode 100644 index 801d99ea..00000000 --- a/src/dusk/script/module/asset/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - moduleassetentry.c - moduleassetbatch.c - moduleasset.c -) diff --git a/src/dusk/script/module/asset/moduleasset.c b/src/dusk/script/module/asset/moduleasset.c deleted file mode 100644 index 58fe8f62..00000000 --- a/src/dusk/script/module/asset/moduleasset.c +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleasset.h" - -scriptproto_t MODULE_ASSET_PROTO; - -moduleBaseFunction(moduleAssetExists) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - return jerry_boolean(assetFileExists(buf)); -} - -moduleBaseFunction(moduleAssetLock) { - moduleBaseRequireArgs(2); - moduleBaseRequireString(0); - moduleBaseRequireNumber(1); - - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - assetloadertype_t type = (assetloadertype_t)moduleBaseArgInt(1); - - assetloaderinput_t input; - assetloaderinput_t *inputPtr = NULL; - - if(argc >= 3 && jerry_value_is_number(args[2])) { - int32_t inputVal = moduleBaseArgInt(2); - switch(type) { - case ASSET_LOADER_TYPE_TEXTURE: - input.texture = (textureformat_t)inputVal; - inputPtr = &input; - break; - case ASSET_LOADER_TYPE_MESH: - input.mesh = (assetmeshinputaxis_t)inputVal; - inputPtr = &input; - break; - default: - break; - } - } - - assetentry_t *entry = assetLock(buf, type, inputPtr); - if(!entry) return moduleBaseThrow("Asset.lock: failed to lock asset"); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); -} - -moduleBaseFunction(moduleAssetRequireLoaded) { - moduleBaseRequireArgs(1); - jsassetentry_t *e = (jsassetentry_t *)scriptProtoGetValue( - &MODULE_ASSET_ENTRY_PROTO, args[0] - ); - if(!e || !e->entry) { - return moduleBaseThrow("Asset.requireLoaded: expected AssetEntry"); - } - errorret_t err = assetRequireLoaded(e->entry); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(args[0]); -} - -moduleBaseFunction(moduleAssetUnlock) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[256]; - moduleBaseToString(args[0], buf, sizeof(buf)); - assetUnlock(buf); - return jerry_undefined(); -} - -void moduleAssetInit(void) { - moduleAssetEntryInit(); - moduleAssetBatchInit(); - scriptProtoInit(&MODULE_ASSET_PROTO, "Asset", sizeof(uint8_t), NULL); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "exists", moduleAssetExists - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "lock", moduleAssetLock - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "unlock", moduleAssetUnlock - ); - scriptProtoDefineStaticFunc( - &MODULE_ASSET_PROTO, "requireLoaded", moduleAssetRequireLoaded - ); - - jerry_value_t global = MODULE_ASSET_PROTO.prototype; - - struct { const char_t *name; int val; } types[] = { - { "TYPE_MESH", ASSET_LOADER_TYPE_MESH }, - { "TYPE_TEXTURE", ASSET_LOADER_TYPE_TEXTURE }, - { "TYPE_TILESET", ASSET_LOADER_TYPE_TILESET }, - { "TYPE_LOCALE", ASSET_LOADER_TYPE_LOCALE }, - { "TYPE_JSON", ASSET_LOADER_TYPE_JSON }, - { "TYPE_SCRIPT", ASSET_LOADER_TYPE_SCRIPT }, - }; - for(int i = 0; i < 6; i++) { - jerry_value_t k = jerry_string_sz(types[i].name); - jerry_value_t v = jerry_number((double)types[i].val); - jerry_object_set(global, k, v); - jerry_value_free(v); - jerry_value_free(k); - } - - struct { const char_t *name; int val; } axes[] = { - { "MESH_AXIS_Y_UP", MESH_INPUT_AXIS_Y_UP }, - { "MESH_AXIS_Z_UP", MESH_INPUT_AXIS_Z_UP }, - { "MESH_AXIS_X_UP", MESH_INPUT_AXIS_X_UP }, - { "MESH_AXIS_Y_DOWN", MESH_INPUT_AXIS_Y_DOWN }, - { "MESH_AXIS_Z_DOWN", MESH_INPUT_AXIS_Z_DOWN }, - { "MESH_AXIS_X_DOWN", MESH_INPUT_AXIS_X_DOWN }, - }; - for(int i = 0; i < 6; i++) { - jerry_value_t k = jerry_string_sz(axes[i].name); - jerry_value_t v = jerry_number((double)axes[i].val); - jerry_object_set(global, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleAssetDispose(void) { - scriptProtoDispose(&MODULE_ASSET_PROTO); - moduleAssetBatchDispose(); - moduleAssetEntryDispose(); -} diff --git a/src/dusk/script/module/asset/moduleasset.h b/src/dusk/script/module/asset/moduleasset.h deleted file mode 100644 index cef603b2..00000000 --- a/src/dusk/script/module/asset/moduleasset.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/display/moduletexture.h" -#include "script/module/asset/moduleassetentry.h" -#include "script/module/asset/moduleassetbatch.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" - -extern scriptproto_t MODULE_ASSET_PROTO; - -/** - * Asset.exists(path) - returns true if the path exists in the asset archive. - * @param args[0] Archive-relative path string. - */ -moduleBaseFunction(moduleAssetExists); - -/** - * Asset.lock(path, type, input?) - locks an asset entry and returns an - * AssetEntry. The entry begins loading in the background. - * - * @param args[0] Path string. - * @param args[1] Loader type constant (Asset.TYPE_*). - * @param args[2] Optional loader input (Texture.FORMAT_* or Asset.MESH_AXIS_*). - */ -moduleBaseFunction(moduleAssetLock); - -/** - * Asset.requireLoaded(entry) - blocks until the entry is fully loaded. - * @param args[0] An AssetEntry object. - * @return The same AssetEntry for chaining. - * @throws If the load fails. - */ -moduleBaseFunction(moduleAssetRequireLoaded); - -/** - * Asset.unlock(path) - releases the asset lock for the given path. - * Prefer entry.unlock() on the AssetEntry object directly. - * @param args[0] Path string originally passed to Asset.lock(). - */ -moduleBaseFunction(moduleAssetUnlock); - -/** - * Initializes the Asset module. Also calls moduleAssetEntryInit() and - * moduleAssetBatchInit(), then registers the global Asset object with - * exists/lock/unlock/requireLoaded and all TYPE_* / MESH_AXIS_* constants. - */ -void moduleAssetInit(void); - -/** - * Disposes the Asset module and calls moduleAssetBatchDispose() and - * moduleAssetEntryDispose(). - */ -void moduleAssetDispose(void); diff --git a/src/dusk/script/module/asset/moduleassetbatch.c b/src/dusk/script/module/asset/moduleassetbatch.c deleted file mode 100644 index ea145c69..00000000 --- a/src/dusk/script/module/asset/moduleassetbatch.c +++ /dev/null @@ -1,388 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleassetbatch.h" -#include "util/string.h" - -#define ASSET_BATCH_PEND_MAX 8 - -scriptproto_t MODULE_ASSET_BATCH_PROTO; - -static scriptpromisepend_t ASSET_BATCH_PEND[ASSET_BATCH_PEND_MAX]; -static uint32_t ASSET_BATCH_PEND_COUNT = 0; - -void assetBatchOnLoadedFire(void *params, void *user) { - assetbatch_t *batch = (assetbatch_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, batch, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(&batch->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&batch->onError, assetBatchOnErrorFire); -} - -void assetBatchOnErrorFire(void *params, void *user) { - assetbatch_t *batch = (assetbatch_t *)user; - jerry_value_t err = jerry_string_sz("Asset batch failed to load"); - uint32_t n = scriptPromisePendReject( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, batch, err - ); - jerry_value_free(err); - if(!n) return; - eventUnsubscribe(&batch->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&batch->onError, assetBatchOnErrorFire); -} - -void moduleAssetBatchFree(void *ptr, jerry_object_native_info_t *info) { - jsassetbatch_t *b = (jsassetbatch_t *)ptr; - if(b && b->batch) { - assetBatchDispose(b->batch); - memoryFree(b->batch); - } - memoryFree(ptr); -} - -jsassetbatch_t *moduleAssetBatchSelf(const jerry_call_info_t *callInfo) { - return (jsassetbatch_t *)scriptProtoGetValue( - &MODULE_ASSET_BATCH_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleAssetBatchCtor) { - if(argc < 1 || !jerry_value_is_array(args[0])) { - return moduleBaseThrow("AssetBatch: expected an array of descriptors"); - } - - uint32_t count = jerry_array_length(args[0]); - if(count == 0 || count > (uint32_t)ASSET_BATCH_COUNT_MAX) { - return moduleBaseThrow("AssetBatch: descriptor count out of range"); - } - - assetbatchdesc_t descs[ASSET_BATCH_COUNT_MAX]; - char_t paths[ASSET_BATCH_COUNT_MAX][ASSET_FILE_NAME_MAX]; - - for(uint32_t i = 0; i < count; i++) { - jerry_value_t desc = jerry_object_get_index(args[0], i); - if(!jerry_value_is_object(desc)) { - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: each descriptor must be an object" - ); - } - - jerry_value_t pathProp = moduleBaseGetProp(desc, "path"); - if(!jerry_value_is_string(pathProp)) { - jerry_value_free(pathProp); - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: descriptor.path must be a string" - ); - } - jerry_size_t pathLen = jerry_string_to_buffer( - pathProp, JERRY_ENCODING_UTF8, - (jerry_char_t *)paths[i], ASSET_FILE_NAME_MAX - 1 - ); - paths[i][pathLen] = '\0'; - jerry_value_free(pathProp); - descs[i].path = paths[i]; - - jerry_value_t typeProp = moduleBaseGetProp(desc, "type"); - if(!jerry_value_is_number(typeProp)) { - jerry_value_free(typeProp); - jerry_value_free(desc); - return moduleBaseThrow( - "AssetBatch: descriptor.type must be a number" - ); - } - descs[i].type = (assetloadertype_t)moduleBaseValueInt(typeProp); - jerry_value_free(typeProp); - - memoryZero(&descs[i].input, sizeof(assetloaderinput_t)); - jerry_value_t inputProp = moduleBaseGetProp(desc, "format"); - if(jerry_value_is_undefined(inputProp)) { - jerry_value_free(inputProp); - inputProp = moduleBaseGetProp(desc, "input"); - } - if(jerry_value_is_undefined(inputProp)) { - jerry_value_free(inputProp); - inputProp = moduleBaseGetProp(desc, "axis"); - } - if(jerry_value_is_number(inputProp)) { - int32_t v = moduleBaseValueInt(inputProp); - switch(descs[i].type) { - case ASSET_LOADER_TYPE_TEXTURE: - descs[i].input.texture = (textureformat_t)v; - break; - case ASSET_LOADER_TYPE_MESH: - descs[i].input.mesh = (assetmeshinputaxis_t)v; - break; - default: - break; - } - } - jerry_value_free(inputProp); - jerry_value_free(desc); - } - - assetbatch_t *batch = (assetbatch_t *)memoryAllocate(sizeof(assetbatch_t)); - assetBatchInit(batch, (uint16_t)count, descs); - - jsassetbatch_t init = { .batch = batch }; - return scriptProtoCreateValue(&MODULE_ASSET_BATCH_PROTO, &init); -} - -moduleBaseFunction(moduleAssetBatchGetCount) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_number(0.0); - return jerry_number((double)b->batch->count); -} - -moduleBaseFunction(moduleAssetBatchGetIsLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_boolean(false); - return jerry_boolean(assetBatchIsLoaded(b->batch)); -} - -moduleBaseFunction(moduleAssetBatchGetHasError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_boolean(false); - return jerry_boolean(assetBatchHasError(b->batch)); -} - -moduleBaseFunction(moduleAssetBatchRequireLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) { - return moduleBaseThrow( - "AssetBatch.requireLoaded: batch already disposed" - ); - } - errorret_t err = assetBatchRequireLoaded(b->batch); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetBatchLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) { - return moduleBaseThrow("AssetBatch.loaded: batch disposed"); - } - - jerry_value_t promise = jerry_promise(); - - if(assetBatchIsLoaded(b->batch)) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - - if(assetBatchHasError(b->batch)) { - jerry_value_t err = jerry_string_sz("Asset batch failed to load"); - jerry_value_t r = jerry_promise_reject(promise, err); - jerry_value_free(err); - jerry_value_free(r); - return promise; - } - - if(ASSET_BATCH_PEND_COUNT >= ASSET_BATCH_PEND_MAX) { - jerry_value_free(promise); - return moduleBaseThrow("AssetBatch.loaded: too many pending"); - } - - if(!scriptPromisePendHas( - ASSET_BATCH_PEND, ASSET_BATCH_PEND_COUNT, b->batch - )) { - eventSubscribe( - &b->batch->onLoaded, assetBatchOnLoadedFire, b->batch - ); - eventSubscribe( - &b->batch->onError, assetBatchOnErrorFire, b->batch - ); - } - - scriptPromisePendAdd( - ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT, - ASSET_BATCH_PEND_MAX, b->batch, promise - ); - return promise; -} - -moduleBaseFunction(moduleAssetBatchLock) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - assetBatchLock(b->batch); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetBatchUnlock) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - assetBatchUnlock(b->batch); - assetBatchDispose(b->batch); - memoryFree(b->batch); - b->batch = NULL; - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetBatchEntry) { - moduleBaseRequireArgs(1); - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - uint32_t idx = (uint32_t)moduleBaseArgInt(0); - if(idx >= (uint32_t)b->batch->count) return jerry_undefined(); - assetentry_t *entry = b->batch->entries[idx]; - if(!entry) return jerry_undefined(); - assetEntryLock(entry); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); -} - -moduleBaseFunction(moduleAssetBatchGetAssetByPath) { - moduleBaseRequireArgs(1); - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - if(!jerry_value_is_string(args[0])) return jerry_undefined(); - - char_t path[ASSET_FILE_NAME_MAX]; - jerry_size_t pathLen = jerry_string_to_buffer( - args[0], JERRY_ENCODING_UTF8, - (jerry_char_t *)path, ASSET_FILE_NAME_MAX - 1 - ); - path[pathLen] = '\0'; - - for(uint16_t i = 0; i < b->batch->count; i++) { - assetentry_t *entry = b->batch->entries[i]; - if(!entry) continue; - if(!stringEquals(entry->name, path)) continue; - assetEntryLock(entry); - jsassetentry_t e = { .entry = entry }; - return scriptProtoCreateValue(&MODULE_ASSET_ENTRY_PROTO, &e); - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetBatchGetOnLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onLoaded, "_onLoaded" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnEntryLoaded) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onEntryLoaded, "_onEntryLoaded" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onError, "_onError" - ); -} - -moduleBaseFunction(moduleAssetBatchGetOnEntryError) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &b->batch->onEntryError, "_onEntryError" - ); -} - -moduleBaseFunction(moduleAssetBatchToString) { - jsassetbatch_t *b = moduleAssetBatchSelf(callInfo); - if(!b || !b->batch) return jerry_string_sz("AssetBatch:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "AssetBatch(%u)", (unsigned)b->batch->count); - return jerry_string_sz(buf); -} - -void moduleAssetBatchInit(void) { - ASSET_BATCH_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_ASSET_BATCH_PROTO, "AssetBatch", - sizeof(jsassetbatch_t), moduleAssetBatchCtor - ); - MODULE_ASSET_BATCH_PROTO.info.free_cb = moduleAssetBatchFree; - - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "count", - moduleAssetBatchGetCount, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "isLoaded", - moduleAssetBatchGetIsLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "hasError", - moduleAssetBatchGetHasError, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "requireLoaded", - moduleAssetBatchRequireLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "loaded", moduleAssetBatchLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "lock", moduleAssetBatchLock - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "unlock", moduleAssetBatchUnlock - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "entry", moduleAssetBatchEntry - ); - scriptProtoDefineFunc( - &MODULE_ASSET_BATCH_PROTO, "getAssetByPath", - moduleAssetBatchGetAssetByPath - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onLoaded", - moduleAssetBatchGetOnLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onEntryLoaded", - moduleAssetBatchGetOnEntryLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onError", - moduleAssetBatchGetOnError, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_BATCH_PROTO, "onEntryError", - moduleAssetBatchGetOnEntryError, NULL - ); - scriptProtoDefineToString( - &MODULE_ASSET_BATCH_PROTO, moduleAssetBatchToString - ); -} - -void moduleAssetBatchDispose(void) { - for(uint32_t i = 0; i < ASSET_BATCH_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(ASSET_BATCH_PEND[j].key == ASSET_BATCH_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - assetbatch_t *b = (assetbatch_t *)ASSET_BATCH_PEND[i].key; - eventUnsubscribe(&b->onLoaded, assetBatchOnLoadedFire); - eventUnsubscribe(&b->onError, assetBatchOnErrorFire); - } - } - scriptPromisePendFreeAll(ASSET_BATCH_PEND, &ASSET_BATCH_PEND_COUNT); - scriptProtoDispose(&MODULE_ASSET_BATCH_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleassetbatch.h b/src/dusk/script/module/asset/moduleassetbatch.h deleted file mode 100644 index 4067594c..00000000 --- a/src/dusk/script/module/asset/moduleassetbatch.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "script/module/asset/moduleassetentry.h" -#include "asset/assetbatch.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_ASSET_BATCH_PROTO; - -/** C struct wrapped by every AssetBatch JS instance. */ -typedef struct { - assetbatch_t *batch; -} jsassetbatch_t; - -/** - * Resolves all pending loaded() promises for this batch, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetbatch_t pointer that finished loading. - */ -void assetBatchOnLoadedFire(void *params, void *user); - -/** - * Rejects all pending loaded() promises for this batch, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetbatch_t pointer that errored. - */ -void assetBatchOnErrorFire(void *params, void *user); - -/** - * GC free callback - disposes and frees the batch when the JS object is - * garbage collected. - * - * @param ptr Native jsassetbatch_t pointer. - * @param info Native info (unused). - */ -void moduleAssetBatchFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jsassetbatch_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jsassetbatch_t, or NULL if invalid. - */ -jsassetbatch_t *moduleAssetBatchSelf(const jerry_call_info_t *callInfo); - -/** - * AssetBatch(descriptors[]) constructor. Parses an array of - * {path, type, format?/input?/axis?} descriptor objects, locks all assets, - * and returns an AssetBatch JS object. Works with and without `new`. - * - * @param args[0] Array of AssetBatchDescriptor objects. - */ -moduleBaseFunction(moduleAssetBatchCtor); - -/** @return Number of entries in the batch. */ -moduleBaseFunction(moduleAssetBatchGetCount); - -/** @return True when every entry has reached LOADED. */ -moduleBaseFunction(moduleAssetBatchGetIsLoaded); - -/** @return True if any entry is in an ERROR state. */ -moduleBaseFunction(moduleAssetBatchGetHasError); - -/** - * requireLoaded() - blocks until every entry is loaded. - * @return this for chaining. - * @throws If any entry fails to load. - */ -moduleBaseFunction(moduleAssetBatchRequireLoaded); - -/** - * loaded() - returns a Promise that resolves when all entries load, or - * rejects if any entry errors. Resolves immediately if already loaded. - */ -moduleBaseFunction(moduleAssetBatchLoaded); - -/** - * lock() - acquires one additional lock on every entry. - * @return this for chaining. - */ -moduleBaseFunction(moduleAssetBatchLock); - -/** - * unlock() - releases all locks and disposes the batch. The object is - * invalid after this call. - */ -moduleBaseFunction(moduleAssetBatchUnlock); - -/** - * entry(index) - returns the AssetEntry at index, adding an independent - * lock. - * @param args[0] Index (number). - * @return AssetEntry, or undefined if out of range. - */ -moduleBaseFunction(moduleAssetBatchEntry); - -/** - * getAssetByPath(path) - finds the first entry whose name matches path - * and returns it as a locked AssetEntry, or undefined if not found. - * The returned entry must be unlocked separately when no longer needed. - * @param args[0] Path string to match against entry names. - * @return AssetEntry, or undefined if no entry matches. - */ -moduleBaseFunction(moduleAssetBatchGetAssetByPath); - -/** @return The onLoaded Event (fires once when all entries load). */ -moduleBaseFunction(moduleAssetBatchGetOnLoaded); - -/** @return The onEntryLoaded Event (fires per entry). */ -moduleBaseFunction(moduleAssetBatchGetOnEntryLoaded); - -/** @return The onError Event (fires once when any entry errors). */ -moduleBaseFunction(moduleAssetBatchGetOnError); - -/** @return The onEntryError Event (fires per errored entry). */ -moduleBaseFunction(moduleAssetBatchGetOnEntryError); - -/** @return "AssetBatch(n)" string. */ -moduleBaseFunction(moduleAssetBatchToString); - -/** - * Initializes the AssetBatch module and registers the global AssetBatch - * class. - */ -void moduleAssetBatchInit(void); - -/** Disposes the AssetBatch module. */ -void moduleAssetBatchDispose(void); diff --git a/src/dusk/script/module/asset/moduleassetentry.c b/src/dusk/script/module/asset/moduleassetentry.c deleted file mode 100644 index 0b228378..00000000 --- a/src/dusk/script/module/asset/moduleassetentry.c +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleassetentry.h" - -#define ASSET_ENTRY_PEND_MAX 16 - -scriptproto_t MODULE_ASSET_ENTRY_PROTO; - -static scriptpromisepend_t ASSET_ENTRY_PEND[ASSET_ENTRY_PEND_MAX]; -static uint32_t ASSET_ENTRY_PEND_COUNT = 0; - -void assetEntryOnLoadedFire(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, entry, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(&entry->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&entry->onError, assetEntryOnErrorFire); -} - -void assetEntryOnErrorFire(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - jerry_value_t err = jerry_string_sz("Asset failed to load"); - uint32_t n = scriptPromisePendReject( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, entry, err - ); - jerry_value_free(err); - if(!n) return; - eventUnsubscribe(&entry->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&entry->onError, assetEntryOnErrorFire); -} - -void moduleAssetEntryFree(void *ptr, jerry_object_native_info_t *info) { - jsassetentry_t *e = (jsassetentry_t *)ptr; - if(e && e->entry) { - assetUnlockEntry(e->entry); - e->entry = NULL; - } - memoryFree(ptr); -} - -jsassetentry_t *moduleAssetEntrySelf(const jerry_call_info_t *callInfo) { - return (jsassetentry_t *)scriptProtoGetValue( - &MODULE_ASSET_ENTRY_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleAssetEntryCtor) { - return moduleBaseThrow("AssetEntry cannot be instantiated with new"); -} - -moduleBaseFunction(moduleAssetEntryGetName) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_string_sz(e->entry->name); -} - -moduleBaseFunction(moduleAssetEntryGetState) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_number((double)e->entry->state); -} - -moduleBaseFunction(moduleAssetEntryGetType) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return jerry_number((double)e->entry->type); -} - -moduleBaseFunction(moduleAssetEntryGetIsLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_boolean(false); - return jerry_boolean(e->entry->state == ASSET_ENTRY_STATE_LOADED); -} - -moduleBaseFunction(moduleAssetEntryGetTexture) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - if(e->entry->type != ASSET_LOADER_TYPE_TEXTURE) return jerry_undefined(); - if(e->entry->state != ASSET_ENTRY_STATE_LOADED) return jerry_undefined(); - assetEntryLock(e->entry); - jstexture_t tex = { .entry = e->entry }; - return scriptProtoCreateValue(&MODULE_TEXTURE_PROTO, &tex); -} - -moduleBaseFunction(moduleAssetEntryRequireLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) { - return moduleBaseThrow("AssetEntry.requireLoaded: invalid entry"); - } - errorret_t err = assetRequireLoaded(e->entry); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleAssetEntryUnlock) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - assetUnlockEntry(e->entry); - e->entry = NULL; - return jerry_undefined(); -} - -moduleBaseFunction(moduleAssetEntryGetOnLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onLoaded, "_onLoaded" - ); -} - -moduleBaseFunction(moduleAssetEntryGetOnUnloaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onUnloaded, "_onUnloaded" - ); -} - -moduleBaseFunction(moduleAssetEntryGetOnError) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_undefined(); - return moduleEventGetOrCreate( - callInfo, &e->entry->onError, "_onError" - ); -} - -moduleBaseFunction(moduleAssetEntryLoaded) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) { - return moduleBaseThrow("AssetEntry.loaded: invalid entry"); - } - jerry_value_t promise = jerry_promise(); - if(e->entry->state == ASSET_ENTRY_STATE_LOADED) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - if(e->entry->state == ASSET_ENTRY_STATE_ERROR) { - jerry_value_t err = jerry_string_sz("Asset failed to load"); - jerry_value_t r = jerry_promise_reject(promise, err); - jerry_value_free(err); - jerry_value_free(r); - return promise; - } - if(ASSET_ENTRY_PEND_COUNT >= ASSET_ENTRY_PEND_MAX) { - jerry_value_free(promise); - return moduleBaseThrow("AssetEntry.loaded: too many pending"); - } - if(!scriptPromisePendHas( - ASSET_ENTRY_PEND, ASSET_ENTRY_PEND_COUNT, e->entry - )) { - eventSubscribe( - &e->entry->onLoaded, assetEntryOnLoadedFire, e->entry - ); - eventSubscribe( - &e->entry->onError, assetEntryOnErrorFire, e->entry - ); - } - scriptPromisePendAdd( - ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT, - ASSET_ENTRY_PEND_MAX, e->entry, promise - ); - return promise; -} - -moduleBaseFunction(moduleAssetEntryToString) { - jsassetentry_t *e = moduleAssetEntrySelf(callInfo); - if(!e || !e->entry) return jerry_string_sz("AssetEntry:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "AssetEntry(%s)", e->entry->name); - return jerry_string_sz(buf); -} - -void moduleAssetEntryInit(void) { - ASSET_ENTRY_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_ASSET_ENTRY_PROTO, "AssetEntry", - sizeof(jsassetentry_t), moduleAssetEntryCtor - ); - MODULE_ASSET_ENTRY_PROTO.info.free_cb = moduleAssetEntryFree; - - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "name", moduleAssetEntryGetName, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "state", moduleAssetEntryGetState, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "type", moduleAssetEntryGetType, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "isLoaded", - moduleAssetEntryGetIsLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "texture", - moduleAssetEntryGetTexture, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "requireLoaded", - moduleAssetEntryRequireLoaded - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "unlock", moduleAssetEntryUnlock - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onLoaded", - moduleAssetEntryGetOnLoaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onUnloaded", - moduleAssetEntryGetOnUnloaded, NULL - ); - scriptProtoDefineProp( - &MODULE_ASSET_ENTRY_PROTO, "onError", - moduleAssetEntryGetOnError, NULL - ); - scriptProtoDefineFunc( - &MODULE_ASSET_ENTRY_PROTO, "loaded", moduleAssetEntryLoaded - ); - scriptProtoDefineToString( - &MODULE_ASSET_ENTRY_PROTO, moduleAssetEntryToString - ); - - jerry_value_t ctor = MODULE_ASSET_ENTRY_PROTO.constructor; - struct { const char_t *name; int val; } states[] = { - { "NOT_STARTED", ASSET_ENTRY_STATE_NOT_STARTED }, - { "PENDING", ASSET_ENTRY_STATE_PENDING_ASYNC }, - { "LOADING", ASSET_ENTRY_STATE_LOADING_ASYNC }, - { "LOADED", ASSET_ENTRY_STATE_LOADED }, - { "ERROR", ASSET_ENTRY_STATE_ERROR }, - }; - for(int i = 0; i < 5; i++) { - jerry_value_t k = jerry_string_sz(states[i].name); - jerry_value_t v = jerry_number((double)states[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleAssetEntryDispose(void) { - for(uint32_t i = 0; i < ASSET_ENTRY_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(ASSET_ENTRY_PEND[j].key == ASSET_ENTRY_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - assetentry_t *e = (assetentry_t *)ASSET_ENTRY_PEND[i].key; - eventUnsubscribe(&e->onLoaded, assetEntryOnLoadedFire); - eventUnsubscribe(&e->onError, assetEntryOnErrorFire); - } - } - scriptPromisePendFreeAll(ASSET_ENTRY_PEND, &ASSET_ENTRY_PEND_COUNT); - scriptProtoDispose(&MODULE_ASSET_ENTRY_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleassetentry.h b/src/dusk/script/module/asset/moduleassetentry.h deleted file mode 100644 index 096fae60..00000000 --- a/src/dusk/script/module/asset/moduleassetentry.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "script/module/display/moduletexture.h" -#include "script/module/event/moduleevent.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_ASSET_ENTRY_PROTO; - -/** C struct wrapped by every AssetEntry JS instance. */ -typedef struct { - assetentry_t *entry; -} jsassetentry_t; - -/** - * Resolves all pending loaded() promises for this entry, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetentry_t pointer that finished loading. - */ -void assetEntryOnLoadedFire(void *params, void *user); - -/** - * Rejects all pending loaded() promises for this entry, then - * unsubscribes from both onLoaded and onError. - * - * @param params Unused. - * @param user The assetentry_t pointer that errored. - */ -void assetEntryOnErrorFire(void *params, void *user); - -/** - * GC free callback - releases the asset lock when the AssetEntry JS object - * is garbage collected. - * - * @param ptr Native jsassetentry_t pointer. - * @param info Native info (unused). - */ -void moduleAssetEntryFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jsassetentry_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jsassetentry_t, or NULL if invalid. - */ -jsassetentry_t *moduleAssetEntrySelf(const jerry_call_info_t *callInfo); - -/** AssetEntry() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleAssetEntryCtor); - -/** @return Archive-relative path used as the cache key. */ -moduleBaseFunction(moduleAssetEntryGetName); - -/** @return Current loading state as a number (AssetEntry.* constants). */ -moduleBaseFunction(moduleAssetEntryGetState); - -/** @return Loader type constant. */ -moduleBaseFunction(moduleAssetEntryGetType); - -/** @return True when the entry has fully loaded. */ -moduleBaseFunction(moduleAssetEntryGetIsLoaded); - -/** - * Returns a Texture wrapping this entry's texture data. The Texture holds - * its own asset lock independently of this AssetEntry. - * @return A Texture JS object, or undefined if not a loaded texture. - */ -moduleBaseFunction(moduleAssetEntryGetTexture); - -/** - * requireLoaded() - blocks until the entry is LOADED or ERROR. - * @return this for chaining. - * @throws If the load fails. - */ -moduleBaseFunction(moduleAssetEntryRequireLoaded); - -/** unlock() - releases the asset lock immediately. */ -moduleBaseFunction(moduleAssetEntryUnlock); - -/** @return The onLoaded Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnLoaded); - -/** @return The onUnloaded Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnUnloaded); - -/** @return The onError Event for this entry. */ -moduleBaseFunction(moduleAssetEntryGetOnError); - -/** - * loaded() - returns a Promise that resolves when the entry loads, or - * rejects on error. Resolves immediately if already loaded. - */ -moduleBaseFunction(moduleAssetEntryLoaded); - -/** @return "AssetEntry(name)" string. */ -moduleBaseFunction(moduleAssetEntryToString); - -/** Initializes the AssetEntry module. */ -void moduleAssetEntryInit(void); - -/** Disposes the AssetEntry module. */ -void moduleAssetEntryDispose(void); diff --git a/src/dusk/script/module/asset/moduleeventproxy.c b/src/dusk/script/module/asset/moduleeventproxy.c deleted file mode 100644 index fdfc965d..00000000 --- a/src/dusk/script/module/asset/moduleeventproxy.c +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleeventproxy.h" -#include "util/memory.h" - -scriptproto_t MODULE_EVENT_PROXY_PROTO; - -void moduleEventProxyTrampoline0(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline1(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline2(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventProxyTrampoline3(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -eventcallback_t MODULE_EVENT_PROXY_TRAMPOLINES[MODULE_EVENT_PROXY_MAX_SLOTS] = { - moduleEventProxyTrampoline0, - moduleEventProxyTrampoline1, - moduleEventProxyTrampoline2, - moduleEventProxyTrampoline3, -}; - -void moduleEventProxyFree(void *ptr, jerry_object_native_info_t *info) { - jseventproxy_t *ep = (jseventproxy_t *)ptr; - if(ep) { - for(uint32_t i = 0; i < MODULE_EVENT_PROXY_MAX_SLOTS; i++) { - if(jerry_value_is_function(ep->fns[i])) { - if(ep->event) { - eventUnsubscribe(ep->event, MODULE_EVENT_PROXY_TRAMPOLINES[i]); - } - jerry_value_free(ep->fns[i]); - } - } - } - memoryFree(ptr); -} - -jseventproxy_t *moduleEventProxySelf(const jerry_call_info_t *callInfo) { - return (jseventproxy_t *)scriptProtoGetValue( - &MODULE_EVENT_PROXY_PROTO, callInfo->this_value - ); -} - -static jerry_value_t moduleEventProxyGetSlot( - const jerry_call_info_t *callInfo, - const uint32_t slot -) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event || slot >= ep->event->size) return jerry_null(); - return jerry_value_is_function(ep->fns[slot]) - ? jerry_value_copy(ep->fns[slot]) - : jerry_null(); -} - -static jerry_value_t moduleEventProxySetSlot( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc, - const uint32_t slot -) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event || slot >= ep->event->size) return jerry_undefined(); - - if(jerry_value_is_function(ep->fns[slot])) { - eventUnsubscribe(ep->event, MODULE_EVENT_PROXY_TRAMPOLINES[slot]); - jerry_value_free(ep->fns[slot]); - ep->fns[slot] = jerry_undefined(); - } - - jerry_value_t val = (argc > 0) ? args[0] : jerry_undefined(); - if(jerry_value_is_function(val)) { - ep->fns[slot] = jerry_value_copy(val); - eventSubscribe( - ep->event, - MODULE_EVENT_PROXY_TRAMPOLINES[slot], - (void *)(uintptr_t)ep->fns[slot] - ); - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleEventProxyGet0) { - return moduleEventProxyGetSlot(callInfo, 0); -} -moduleBaseFunction(moduleEventProxySet0) { - return moduleEventProxySetSlot(callInfo, args, argc, 0); -} - -moduleBaseFunction(moduleEventProxyGet1) { - return moduleEventProxyGetSlot(callInfo, 1); -} -moduleBaseFunction(moduleEventProxySet1) { - return moduleEventProxySetSlot(callInfo, args, argc, 1); -} - -moduleBaseFunction(moduleEventProxyGet2) { - return moduleEventProxyGetSlot(callInfo, 2); -} -moduleBaseFunction(moduleEventProxySet2) { - return moduleEventProxySetSlot(callInfo, args, argc, 2); -} - -moduleBaseFunction(moduleEventProxyGet3) { - return moduleEventProxyGetSlot(callInfo, 3); -} -moduleBaseFunction(moduleEventProxySet3) { - return moduleEventProxySetSlot(callInfo, args, argc, 3); -} - -moduleBaseFunction(moduleEventProxyGetLength) { - jseventproxy_t *ep = moduleEventProxySelf(callInfo); - if(!ep || !ep->event) return jerry_number(0.0); - return jerry_number((double)ep->event->size); -} - -moduleBaseFunction(moduleEventProxyToString) { - return jerry_string_sz("EventProxy"); -} - -jerry_value_t moduleEventProxyGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -) { - jerry_value_t keyStr = jerry_string_sz(pinKey); - jerry_value_t existing = jerry_object_get(callInfo->this_value, keyStr); - if(!jerry_value_is_undefined(existing)) { - jerry_value_free(keyStr); - return existing; - } - jerry_value_free(existing); - - jseventproxy_t ep; - ep.event = event; - for(uint32_t i = 0; i < MODULE_EVENT_PROXY_MAX_SLOTS; i++) { - ep.fns[i] = jerry_undefined(); - } - - jerry_value_t proxy = scriptProtoCreateValue( - &MODULE_EVENT_PROXY_PROTO, &ep - ); - jerry_object_set(callInfo->this_value, keyStr, proxy); - jerry_value_free(keyStr); - return proxy; -} - -void moduleEventProxyInit(void) { - scriptProtoInit( - &MODULE_EVENT_PROXY_PROTO, NULL, - sizeof(jseventproxy_t), NULL - ); - MODULE_EVENT_PROXY_PROTO.info.free_cb = moduleEventProxyFree; - - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "0", - moduleEventProxyGet0, moduleEventProxySet0 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "1", - moduleEventProxyGet1, moduleEventProxySet1 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "2", - moduleEventProxyGet2, moduleEventProxySet2 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "3", - moduleEventProxyGet3, moduleEventProxySet3 - ); - scriptProtoDefineProp( - &MODULE_EVENT_PROXY_PROTO, "length", - moduleEventProxyGetLength, NULL - ); - scriptProtoDefineToString( - &MODULE_EVENT_PROXY_PROTO, moduleEventProxyToString - ); -} - -void moduleEventProxyDispose(void) { - scriptProtoDispose(&MODULE_EVENT_PROXY_PROTO); -} diff --git a/src/dusk/script/module/asset/moduleeventproxy.h b/src/dusk/script/module/asset/moduleeventproxy.h deleted file mode 100644 index f1c785b2..00000000 --- a/src/dusk/script/module/asset/moduleeventproxy.h +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/* Merged into script/module/event/moduleevent.h */ -#pragma once -#include "script/module/event/moduleevent.h" diff --git a/src/dusk/script/module/console/CMakeLists.txt b/src/dusk/script/module/console/CMakeLists.txt deleted file mode 100644 index 9867bfde..00000000 --- a/src/dusk/script/module/console/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - moduleconsole.c -) diff --git a/src/dusk/script/module/console/moduleconsole.c b/src/dusk/script/module/console/moduleconsole.c deleted file mode 100644 index 15fd17ed..00000000 --- a/src/dusk/script/module/console/moduleconsole.c +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleconsole.h" -#include "util/string.h" -#include - -scriptproto_t MODULE_CONSOLE_PROTO; - -moduleBaseFunction(moduleConsolePrint) { - char_t buf[512]; - char_t msg[4096]; - size_t msgLen = 0; - - for(jerry_length_t i = 0; i < argc; ++i) { - jerry_value_t strVal = jerry_value_to_string(args[i]); - moduleBaseToString(strVal, buf, sizeof(buf)); - jerry_value_free(strVal); - - size_t partLen = strlen(buf); - if(msgLen + partLen + 1 < sizeof(msg)) { - stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen); - msgLen += partLen; - } - - if(i + 1 < argc && msgLen + 1 < sizeof(msg)) { - msg[msgLen++] = '\t'; - msg[msgLen] = '\0'; - } - } - - consolePrint("%s", msg); - return jerry_undefined(); -} - -moduleBaseFunction(moduleConsoleGetVisible) { - return jerry_boolean(CONSOLE.visible); -} - -moduleBaseFunction(moduleConsoleSetVisible) { - moduleBaseRequireArgs(1); - CONSOLE.visible = moduleBaseArgBool(0); - return jerry_undefined(); -} - -void moduleConsoleInit(void) { - scriptProtoInit( - &MODULE_CONSOLE_PROTO, "Console", - sizeof(uint8_t), NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_CONSOLE_PROTO, "print", moduleConsolePrint - ); - scriptProtoDefineStaticProp( - &MODULE_CONSOLE_PROTO, "visible", - moduleConsoleGetVisible, moduleConsoleSetVisible - ); -} - -void moduleConsoleDispose(void) { - scriptProtoDispose(&MODULE_CONSOLE_PROTO); -} diff --git a/src/dusk/script/module/console/moduleconsole.h b/src/dusk/script/module/console/moduleconsole.h deleted file mode 100644 index 8388cecd..00000000 --- a/src/dusk/script/module/console/moduleconsole.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "console/console.h" - -extern scriptproto_t MODULE_CONSOLE_PROTO; - -/** - * Console.print(...args) - concatenates all args tab-separated and prints - * to the engine console. - */ -moduleBaseFunction(moduleConsolePrint); - -/** @return Whether the console overlay is currently visible. */ -moduleBaseFunction(moduleConsoleGetVisible); - -/** Sets console visibility. @param args[0] Boolean visible state. */ -moduleBaseFunction(moduleConsoleSetVisible); - -/** - * Initializes the Console module and registers the global Console object with - * print() and the visible property. - */ -void moduleConsoleInit(void); - -/** - * Disposes the Console module. - */ -void moduleConsoleDispose(void); diff --git a/src/dusk/script/module/display/CMakeLists.txt b/src/dusk/script/module/display/CMakeLists.txt deleted file mode 100644 index f009d77d..00000000 --- a/src/dusk/script/module/display/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - modulecolor.c - modulescreen.c - moduletexture.c -) diff --git a/src/dusk/script/module/display/modulecolor.c b/src/dusk/script/module/display/modulecolor.c deleted file mode 100644 index 09d77127..00000000 --- a/src/dusk/script/module/display/modulecolor.c +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecolor.h" - -scriptproto_t MODULE_COLOR_PROTO; - -color_t *moduleColorFrom(const jerry_value_t val) { - return (color_t *)scriptProtoGetValue(&MODULE_COLOR_PROTO, val); -} - -jerry_value_t moduleColorPush(const color_t c) { - return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &c); -} - -moduleBaseFunction(moduleColorConstructor) { - color_t *ptr = (color_t *)memoryAllocate(sizeof(color_t)); - ptr->r = (uint8_t)moduleBaseOptInt(0, 0); - ptr->g = (uint8_t)moduleBaseOptInt(1, 0); - ptr->b = (uint8_t)moduleBaseOptInt(2, 0); - ptr->a = (uint8_t)moduleBaseOptInt(3, 255); - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_COLOR_PROTO.info, ptr - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetR) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->r); -} - -moduleBaseFunction(moduleColorSetR) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->r = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetG) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->g); -} - -moduleBaseFunction(moduleColorSetG) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->g = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetB) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->b); -} - -moduleBaseFunction(moduleColorSetB) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->b = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorGetA) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - return jerry_number((double)c->a); -} - -moduleBaseFunction(moduleColorSetA) { - moduleBaseRequireArgs(1); - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_undefined(); - c->a = (uint8_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleColorToString) { - color_t *c = moduleColorFrom(callInfo->this_value); - if(!c) return jerry_string_sz("Color:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Color(%u,%u,%u,%u)", - (unsigned)c->r, (unsigned)c->g, - (unsigned)c->b, (unsigned)c->a - ); - return jerry_string_sz(buf); -} - -void moduleColorInit(void) { - scriptProtoInit( - &MODULE_COLOR_PROTO, "Color", - sizeof(color_t), moduleColorConstructor - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "r", moduleColorGetR, moduleColorSetR - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "g", moduleColorGetG, moduleColorSetG - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "b", moduleColorGetB, moduleColorSetB - ); - scriptProtoDefineProp( - &MODULE_COLOR_PROTO, "a", moduleColorGetA, moduleColorSetA - ); - scriptProtoDefineToString(&MODULE_COLOR_PROTO, moduleColorToString); - - struct { const char_t *name; color_t val; } constants[] = { - { "WHITE", COLOR_WHITE }, - { "BLACK", COLOR_BLACK }, - { "RED", COLOR_RED }, - { "GREEN", COLOR_GREEN }, - { "BLUE", COLOR_BLUE }, - { "YELLOW", COLOR_YELLOW }, - { "CYAN", COLOR_CYAN }, - { "MAGENTA", COLOR_MAGENTA }, - { "TRANSPARENT", COLOR_TRANSPARENT }, - { "GRAY", COLOR_GRAY }, - { "LIGHT_GRAY", COLOR_LIGHT_GRAY }, - { "DARK_GRAY", COLOR_DARK_GRAY }, - { "ORANGE", COLOR_ORANGE }, - { "PURPLE", COLOR_PURPLE }, - { "PINK", COLOR_PINK }, - { "TEAL", COLOR_TEAL }, - { "CORNFLOWER_BLUE", COLOR_CORNFLOWER_BLUE }, - }; - jerry_value_t ctor = MODULE_COLOR_PROTO.constructor; - for(int i = 0; i < (int)(sizeof(constants)/sizeof(constants[0])); i++) { - jerry_value_t k = jerry_string_sz(constants[i].name); - jerry_value_t v = moduleColorPush(constants[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleColorDispose(void) { - scriptProtoDispose(&MODULE_COLOR_PROTO); -} diff --git a/src/dusk/script/module/display/modulecolor.h b/src/dusk/script/module/display/modulecolor.h deleted file mode 100644 index df92f23e..00000000 --- a/src/dusk/script/module/display/modulecolor.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" -#include "display/color.h" - -extern scriptproto_t MODULE_COLOR_PROTO; - -/** - * Returns the native color_t pointer from a Color JS value. - * - * @param val The JS value to extract from. - * @return Pointer to the color data, or NULL if not a Color. - */ -color_t *moduleColorFrom(const jerry_value_t val); - -/** - * Creates a Color JS object wrapping a copy of a C color_t. - * - * @param c The source color. - * @return A new Color JS object. - */ -jerry_value_t moduleColorPush(const color_t c); - -/** Color(r?, g?, b?, a?) constructor. */ -moduleBaseFunction(moduleColorConstructor); - -/** @return The red channel as a number. */ -moduleBaseFunction(moduleColorGetR); -/** Sets the red channel. @param args[0] New r value (0-255). */ -moduleBaseFunction(moduleColorSetR); - -/** @return The green channel as a number. */ -moduleBaseFunction(moduleColorGetG); -/** Sets the green channel. @param args[0] New g value (0-255). */ -moduleBaseFunction(moduleColorSetG); - -/** @return The blue channel as a number. */ -moduleBaseFunction(moduleColorGetB); -/** Sets the blue channel. @param args[0] New b value (0-255). */ -moduleBaseFunction(moduleColorSetB); - -/** @return The alpha channel as a number. */ -moduleBaseFunction(moduleColorGetA); -/** Sets the alpha channel. @param args[0] New a value (0-255). */ -moduleBaseFunction(moduleColorSetA); - -/** @return "Color(r,g,b,a)" string. */ -moduleBaseFunction(moduleColorToString); - -/** - * Initializes the Color module and registers the global Color class with named - * color constants (Color.WHITE, Color.RED, etc.). - */ -void moduleColorInit(void); - -/** - * Disposes the Color module. - */ -void moduleColorDispose(void); diff --git a/src/dusk/script/module/display/modulescreen.c b/src/dusk/script/module/display/modulescreen.c deleted file mode 100644 index 71842da7..00000000 --- a/src/dusk/script/module/display/modulescreen.c +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescreen.h" - -scriptproto_t MODULE_SCREEN_PROTO; - -moduleBaseFunction(moduleScreenGetWidth) { - return jerry_number((double)SCREEN.width); -} - -moduleBaseFunction(moduleScreenGetHeight) { - return jerry_number((double)SCREEN.height); -} - -moduleBaseFunction(moduleScreenGetAspect) { - return jerry_number((double)SCREEN.aspect); -} - -void moduleScreenInit(void) { - scriptProtoInit(&MODULE_SCREEN_PROTO, "Screen", sizeof(uint8_t), NULL); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "width", moduleScreenGetWidth, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "height", moduleScreenGetHeight, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_SCREEN_PROTO, "aspect", moduleScreenGetAspect, NULL - ); -} - -void moduleScreenDispose(void) { - scriptProtoDispose(&MODULE_SCREEN_PROTO); -} diff --git a/src/dusk/script/module/display/modulescreen.h b/src/dusk/script/module/display/modulescreen.h deleted file mode 100644 index e15a3c03..00000000 --- a/src/dusk/script/module/display/modulescreen.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "display/screen/screen.h" - -extern scriptproto_t MODULE_SCREEN_PROTO; - -/** @return Current screen width in pixels. */ -moduleBaseFunction(moduleScreenGetWidth); - -/** @return Current screen height in pixels. */ -moduleBaseFunction(moduleScreenGetHeight); - -/** @return Current screen aspect ratio (width / height). */ -moduleBaseFunction(moduleScreenGetAspect); - -/** - * Initializes the Screen module and registers read-only width/height/aspect - * properties on the global Screen object. - */ -void moduleScreenInit(void); - -/** - * Disposes the Screen module. - */ -void moduleScreenDispose(void); diff --git a/src/dusk/script/module/display/moduletexture.c b/src/dusk/script/module/display/moduletexture.c deleted file mode 100644 index e787e103..00000000 --- a/src/dusk/script/module/display/moduletexture.c +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletexture.h" - -scriptproto_t MODULE_TEXTURE_PROTO; - -void moduleTextureFree(void *ptr, jerry_object_native_info_t *info) { - jstexture_t *tex = (jstexture_t *)ptr; - if(tex && tex->entry) { - assetUnlockEntry(tex->entry); - tex->entry = NULL; - } - memoryFree(ptr); -} - -jstexture_t *moduleTextureSelf(const jerry_call_info_t *callInfo) { - return (jstexture_t *)scriptProtoGetValue( - &MODULE_TEXTURE_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleTextureCtor) { - return moduleBaseThrow("Texture cannot be instantiated with new"); -} - -moduleBaseFunction(moduleTextureGetWidth) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_undefined(); - return jerry_number((double)t->entry->data.texture.width); -} - -moduleBaseFunction(moduleTextureGetHeight) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_undefined(); - return jerry_number((double)t->entry->data.texture.height); -} - -moduleBaseFunction(moduleTextureToString) { - jstexture_t *t = moduleTextureSelf(callInfo); - if(!t || !t->entry) return jerry_string_sz("Texture:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "Texture(%dx%d)", - t->entry->data.texture.width, - t->entry->data.texture.height - ); - return jerry_string_sz(buf); -} - -void moduleTextureInit(void) { - scriptProtoInit( - &MODULE_TEXTURE_PROTO, "Texture", - sizeof(jstexture_t), moduleTextureCtor - ); - MODULE_TEXTURE_PROTO.info.free_cb = moduleTextureFree; - - scriptProtoDefineProp( - &MODULE_TEXTURE_PROTO, "width", moduleTextureGetWidth, NULL - ); - scriptProtoDefineProp( - &MODULE_TEXTURE_PROTO, "height", moduleTextureGetHeight, NULL - ); - scriptProtoDefineToString(&MODULE_TEXTURE_PROTO, moduleTextureToString); - - jerry_value_t ctor = MODULE_TEXTURE_PROTO.constructor; - struct { const char_t *name; int val; } formats[] = { - { "FORMAT_RGBA", TEXTURE_FORMAT_RGBA }, - { "FORMAT_PALETTE", TEXTURE_FORMAT_PALETTE }, - }; - for(int i = 0; i < 2; i++) { - jerry_value_t k = jerry_string_sz(formats[i].name); - jerry_value_t v = jerry_number((double)formats[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleTextureDispose(void) { - scriptProtoDispose(&MODULE_TEXTURE_PROTO); -} diff --git a/src/dusk/script/module/display/moduletexture.h b/src/dusk/script/module/display/moduletexture.h deleted file mode 100644 index 01159877..00000000 --- a/src/dusk/script/module/display/moduletexture.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "display/texture/texture.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_TEXTURE_PROTO; - -/** C struct wrapped by every Texture JS instance. */ -typedef struct { - assetentry_t *entry; -} jstexture_t; - -/** - * GC free callback - unlocks the asset entry when the Texture JS object is - * garbage collected. - * - * @param ptr Native jstexture_t pointer. - * @param info Native info (unused). - */ -void moduleTextureFree(void *ptr, jerry_object_native_info_t *info); - -/** - * Returns the jstexture_t pointer from the current this_value. - * - * @param callInfo The call info. - * @return Pointer to the jstexture_t, or NULL if invalid. - */ -jstexture_t *moduleTextureSelf(const jerry_call_info_t *callInfo); - -/** Texture() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleTextureCtor); - -/** @return Texture width in pixels, or undefined if not loaded. */ -moduleBaseFunction(moduleTextureGetWidth); - -/** @return Texture height in pixels, or undefined if not loaded. */ -moduleBaseFunction(moduleTextureGetHeight); - -/** @return "Texture(WxH)" string. */ -moduleBaseFunction(moduleTextureToString); - -/** - * Initializes the Texture module and registers the global Texture class with - * FORMAT_RGBA and FORMAT_PALETTE constants. - */ -void moduleTextureInit(void); - -/** - * Disposes the Texture module. - */ -void moduleTextureDispose(void); diff --git a/src/dusk/script/module/engine/CMakeLists.txt b/src/dusk/script/module/engine/CMakeLists.txt deleted file mode 100644 index 87bf0ea6..00000000 --- a/src/dusk/script/module/engine/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - moduleengine.c - moduleframe.c - moduletimeout.c -) diff --git a/src/dusk/script/module/engine/moduleengine.c b/src/dusk/script/module/engine/moduleengine.c deleted file mode 100644 index 9cccc1c7..00000000 --- a/src/dusk/script/module/engine/moduleengine.c +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleengine.h" - -scriptproto_t MODULE_ENGINE_PROTO; - -moduleBaseFunction(moduleEngineGetRunning) { - return jerry_boolean(ENGINE.running); -} - -moduleBaseFunction(moduleEngineExit) { - ENGINE.running = false; - return jerry_undefined(); -} - -void moduleEngineInit(void) { - scriptProtoInit(&MODULE_ENGINE_PROTO, "Engine", sizeof(uint8_t), NULL); - scriptProtoDefineStaticProp( - &MODULE_ENGINE_PROTO, "running", - moduleEngineGetRunning, NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_ENGINE_PROTO, "exit", moduleEngineExit - ); -} - -void moduleEngineDispose(void) { - scriptProtoDispose(&MODULE_ENGINE_PROTO); -} diff --git a/src/dusk/script/module/engine/moduleengine.h b/src/dusk/script/module/engine/moduleengine.h deleted file mode 100644 index 11b7aa3d..00000000 --- a/src/dusk/script/module/engine/moduleengine.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "engine/engine.h" - -extern scriptproto_t MODULE_ENGINE_PROTO; - -/** @return True if the engine main loop is still running. */ -moduleBaseFunction(moduleEngineGetRunning); - -/** Signals the engine to stop on the next frame. */ -moduleBaseFunction(moduleEngineExit); - -/** - * Initializes the Engine module and registers the global Engine object with the - * running property and exit() method. - */ -void moduleEngineInit(void); - -/** - * Disposes the Engine module. - */ -void moduleEngineDispose(void); diff --git a/src/dusk/script/module/engine/moduleframe.c b/src/dusk/script/module/engine/moduleframe.c deleted file mode 100644 index 5ab00613..00000000 --- a/src/dusk/script/module/engine/moduleframe.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleframe.h" - -jerry_value_t MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_MAX]; -uint32_t MODULE_FRAME_PENDING_COUNT = 0; - -moduleBaseFunction(moduleFrameFrame) { - if(MODULE_FRAME_PENDING_COUNT >= MODULE_FRAME_PENDING_MAX) { - return moduleBaseThrow("Too many pending frame() calls"); - } - jerry_value_t promise = jerry_promise(); - MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_COUNT++] = - jerry_value_copy(promise); - return promise; -} - -void moduleFrameFlush(void) { - uint32_t count = MODULE_FRAME_PENDING_COUNT; - MODULE_FRAME_PENDING_COUNT = 0; - for(uint32_t i = 0; i < count; i++) { - jerry_value_t ret = jerry_promise_resolve( - MODULE_FRAME_PENDING[i], jerry_undefined() - ); - jerry_value_free(ret); - jerry_value_free(MODULE_FRAME_PENDING[i]); - } -} - -void moduleFrameInit(void) { - MODULE_FRAME_PENDING_COUNT = 0; - moduleBaseDefineGlobalMethod("frame", moduleFrameFrame); -} - -void moduleFrameDispose(void) { - for(uint32_t i = 0; i < MODULE_FRAME_PENDING_COUNT; i++) { - jerry_value_free(MODULE_FRAME_PENDING[i]); - } - MODULE_FRAME_PENDING_COUNT = 0; -} diff --git a/src/dusk/script/module/engine/moduleframe.h b/src/dusk/script/module/engine/moduleframe.h deleted file mode 100644 index 136d6e32..00000000 --- a/src/dusk/script/module/engine/moduleframe.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" - -/** Maximum number of concurrent frame() awaits. */ -#define MODULE_FRAME_PENDING_MAX 64 - -extern jerry_value_t MODULE_FRAME_PENDING[MODULE_FRAME_PENDING_MAX]; -extern uint32_t MODULE_FRAME_PENDING_COUNT; - -/** - * frame() - returns a Promise that resolves at the start of the next frame. - * Used as `await frame()` inside an async script loop. - */ -moduleBaseFunction(moduleFrameFrame); - -/** - * Resolves all pending frame() promises. Must be called once per frame before - * jerry_run_jobs() so that awaiting scripts resume in the same tick. - */ -void moduleFrameFlush(void); - -/** - * Initializes the frame module and registers the global frame() function. - */ -void moduleFrameInit(void); - -/** - * Disposes the frame module, releasing any unresolved pending promises. - */ -void moduleFrameDispose(void); diff --git a/src/dusk/script/module/engine/moduletimeout.c b/src/dusk/script/module/engine/moduletimeout.c deleted file mode 100644 index 7417e408..00000000 --- a/src/dusk/script/module/engine/moduletimeout.c +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletimeout.h" - -moduletimeoutentry_t MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_MAX]; -uint32_t MODULE_TIMEOUT_PENDING_COUNT = 0; - -moduleBaseFunction(moduleTimeoutTimeout) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - - if(MODULE_TIMEOUT_PENDING_COUNT >= MODULE_TIMEOUT_PENDING_MAX) { - return moduleBaseThrow("Too many pending timeout() calls"); - } - - float_t ms = moduleBaseArgFloat(0); - jerry_value_t promise = jerry_promise(); - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT].promise = - jerry_value_copy(promise); - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT].targetTime = - TIME.time + ms / 1000.0f; - MODULE_TIMEOUT_PENDING_COUNT++; - return promise; -} - -void moduleTimeoutFlush(void) { - uint32_t i = 0; - while(i < MODULE_TIMEOUT_PENDING_COUNT) { - if(TIME.time >= MODULE_TIMEOUT_PENDING[i].targetTime) { - jerry_value_t ret = jerry_promise_resolve( - MODULE_TIMEOUT_PENDING[i].promise, jerry_undefined() - ); - jerry_value_free(ret); - jerry_value_free(MODULE_TIMEOUT_PENDING[i].promise); - MODULE_TIMEOUT_PENDING_COUNT--; - if(i < MODULE_TIMEOUT_PENDING_COUNT) { - MODULE_TIMEOUT_PENDING[i] = - MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_COUNT]; - } - } else { - i++; - } - } -} - -void moduleTimeoutInit(void) { - MODULE_TIMEOUT_PENDING_COUNT = 0; - moduleBaseDefineGlobalMethod("timeout", moduleTimeoutTimeout); -} - -void moduleTimeoutDispose(void) { - for(uint32_t i = 0; i < MODULE_TIMEOUT_PENDING_COUNT; i++) { - jerry_value_free(MODULE_TIMEOUT_PENDING[i].promise); - } - MODULE_TIMEOUT_PENDING_COUNT = 0; -} diff --git a/src/dusk/script/module/engine/moduletimeout.h b/src/dusk/script/module/engine/moduletimeout.h deleted file mode 100644 index 09eaf940..00000000 --- a/src/dusk/script/module/engine/moduletimeout.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "time/time.h" - -/** Maximum number of concurrent timeout() awaits. */ -#define MODULE_TIMEOUT_PENDING_MAX 64 - -/** Entry tracking one pending timeout() promise. */ -typedef struct { - jerry_value_t promise; - float_t targetTime; -} moduletimeoutentry_t; - -extern moduletimeoutentry_t MODULE_TIMEOUT_PENDING[MODULE_TIMEOUT_PENDING_MAX]; -extern uint32_t MODULE_TIMEOUT_PENDING_COUNT; - -/** - * timeout(ms) - returns a Promise that resolves after at least `ms` - * milliseconds have elapsed. Used as `await timeout(500)`. - * - * @param args[0] Delay in milliseconds (number). - */ -moduleBaseFunction(moduleTimeoutTimeout); - -/** - * Resolves any pending timeout() promises whose target time has passed. - * Must be called once per frame before jerry_run_jobs(). - */ -void moduleTimeoutFlush(void); - -/** - * Initializes the timeout module and registers the global timeout() function. - */ -void moduleTimeoutInit(void); - -/** - * Disposes the timeout module, releasing any unresolved pending promises. - */ -void moduleTimeoutDispose(void); diff --git a/src/dusk/script/module/entity/CMakeLists.txt b/src/dusk/script/module/entity/CMakeLists.txt deleted file mode 100644 index a5b59035..00000000 --- a/src/dusk/script/module/entity/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# 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 - modulecomponent.c - moduleentity.c -) - -# Subdirs -add_subdirectory(component) diff --git a/src/dusk/script/module/entity/component/CMakeLists.txt b/src/dusk/script/module/entity/component/CMakeLists.txt deleted file mode 100644 index 18ba1045..00000000 --- a/src/dusk/script/module/entity/component/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# 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 - modulecomponentlist.c -) - -# Subdirs -add_subdirectory(display) -add_subdirectory(physics) -add_subdirectory(trigger) diff --git a/src/dusk/script/module/entity/component/display/CMakeLists.txt b/src/dusk/script/module/entity/component/display/CMakeLists.txt deleted file mode 100644 index 76c71939..00000000 --- a/src/dusk/script/module/entity/component/display/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# 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 - modulecamera.c - moduleposition.c - modulerenderable.c -) diff --git a/src/dusk/script/module/entity/component/display/modulecamera.c b/src/dusk/script/module/entity/component/display/modulecamera.c deleted file mode 100644 index df143525..00000000 --- a/src/dusk/script/module/entity/component/display/modulecamera.c +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecamera.h" - -scriptproto_t MODULE_CAMERA_PROTO; - -jscomponent_t *moduleCameraSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_CAMERA_PROTO, callInfo->this_value - ); -} - -entitycamera_t *moduleCameraData(const jscomponent_t *c) { - return (entitycamera_t *)componentGetData( - c->entityId, c->componentId, COMPONENT_TYPE_CAMERA - ); -} - -moduleBaseFunction(moduleCameraCtor) { - return moduleBaseThrow("Camera cannot be instantiated with new"); -} - -moduleBaseFunction(moduleCameraGetEntity) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleCameraGetId) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleCameraGetFov) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->perspective.fov); -} - -moduleBaseFunction(moduleCameraSetFov) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->perspective.fov = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetNearClip) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->nearClip); -} - -moduleBaseFunction(moduleCameraSetNearClip) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->nearClip = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetFarClip) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->farClip); -} - -moduleBaseFunction(moduleCameraSetFarClip) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->farClip = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraGetProjType) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - return jerry_number((double)cam->projType); -} - -moduleBaseFunction(moduleCameraSetProjType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_undefined(); - entitycamera_t *cam = moduleCameraData(c); - if(!cam) return jerry_undefined(); - cam->projType = (entitycameraprojectiontype_t)moduleBaseArgInt(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleCameraToString) { - jscomponent_t *c = moduleCameraSelf(callInfo); - if(!c) return jerry_string_sz("Camera:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Camera(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleCameraInit(void) { - scriptProtoInit( - &MODULE_CAMERA_PROTO, "Camera", - sizeof(jscomponent_t), moduleCameraCtor - ); - - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "entity", moduleCameraGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "id", moduleCameraGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "fov", moduleCameraGetFov, moduleCameraSetFov - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "nearClip", - moduleCameraGetNearClip, moduleCameraSetNearClip - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "farClip", - moduleCameraGetFarClip, moduleCameraSetFarClip - ); - scriptProtoDefineProp( - &MODULE_CAMERA_PROTO, "projType", - moduleCameraGetProjType, moduleCameraSetProjType - ); - scriptProtoDefineToString(&MODULE_CAMERA_PROTO, moduleCameraToString); - - jerry_value_t ctor = MODULE_CAMERA_PROTO.constructor; - struct { const char_t *name; int val; } projtypes[] = { - { "PERSPECTIVE", ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE }, - { - "PERSPECTIVE_FLIPPED", - ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED - }, - { "ORTHOGRAPHIC", ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(projtypes[i].name); - jerry_value_t v = jerry_number((double)projtypes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleCameraDispose(void) { - scriptProtoDispose(&MODULE_CAMERA_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/modulecamera.h b/src/dusk/script/module/entity/component/display/modulecamera.h deleted file mode 100644 index b3f4af5e..00000000 --- a/src/dusk/script/module/entity/component/display/modulecamera.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/display/entitycamera.h" - -extern scriptproto_t MODULE_CAMERA_PROTO; - -/** Camera() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleCameraCtor); - -/** @return Entity ID that owns this camera component. */ -moduleBaseFunction(moduleCameraGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleCameraGetId); - -/** @return Field of view in degrees. */ -moduleBaseFunction(moduleCameraGetFov); - -/** Sets field of view. @param args[0] FOV in degrees. */ -moduleBaseFunction(moduleCameraSetFov); - -/** @return Near clip plane distance. */ -moduleBaseFunction(moduleCameraGetNearClip); - -/** Sets near clip plane. @param args[0] Near clip distance. */ -moduleBaseFunction(moduleCameraSetNearClip); - -/** @return Far clip plane distance. */ -moduleBaseFunction(moduleCameraGetFarClip); - -/** Sets far clip plane. @param args[0] Far clip distance. */ -moduleBaseFunction(moduleCameraSetFarClip); - -/** @return Projection type constant (Camera.PERSPECTIVE etc.). */ -moduleBaseFunction(moduleCameraGetProjType); - -/** Sets projection type. @param args[0] entitycameraprojectiontype_t value. */ -moduleBaseFunction(moduleCameraSetProjType); - -/** @return "Camera(id)" string. */ -moduleBaseFunction(moduleCameraToString); - -/** - * Initializes the Camera module and registers the global Camera class with - * fov/nearClip/farClip/projType properties and PERSPECTIVE/ORTHOGRAPHIC - * constants. - */ -void moduleCameraInit(void); - -/** - * Disposes the Camera module. - */ -void moduleCameraDispose(void); diff --git a/src/dusk/script/module/entity/component/display/moduleposition.c b/src/dusk/script/module/entity/component/display/moduleposition.c deleted file mode 100644 index ccdfb1a0..00000000 --- a/src/dusk/script/module/entity/component/display/moduleposition.c +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleposition.h" - -scriptproto_t MODULE_POSITION_PROTO; - -jscomponent_t *modulePositionSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_POSITION_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(modulePositionCtor) { - return moduleBaseThrow("Position cannot be instantiated with new"); -} - -moduleBaseFunction(modulePositionGetEntity) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(modulePositionGetId) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(modulePositionGetLocalPos) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalPosition(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalPos) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localPosition: expected Vec3"); - entityPositionSetLocalPosition(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldPos) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldPosition(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldPos) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldPosition: expected Vec3"); - entityPositionSetWorldPosition(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetLocalRot) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalRotation(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalRot) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localRotation: expected Vec3"); - entityPositionSetLocalRotation(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldRot) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldRotation(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldRot) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldRotation: expected Vec3"); - entityPositionSetWorldRotation(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetLocalScale) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetLocalScale(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetLocalScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.localScale: expected Vec3"); - entityPositionSetLocalScale(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionGetWorldScale) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPositionGetWorldScale(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePositionSetWorldScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Position.worldScale: expected Vec3"); - entityPositionSetWorldScale(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionLookAt) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - moduleBaseRequireArgs(1); - float_t *target = moduleVec3From(args[0]); - if(!target) return moduleBaseThrow("Position.lookAt: expected Vec3 target"); - - vec3 eye; - entityPositionGetLocalPosition(c->entityId, c->componentId, eye); - - vec3 up = { 0.0f, 1.0f, 0.0f }; - if(argc >= 2) { - float_t *upArg = moduleVec3From(args[1]); - if(upArg) glm_vec3_copy(upArg, up); - } - - entityPositionLookAt(c->entityId, c->componentId, eye, target, up); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionSetParent) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_undefined(); - if(argc == 0 || - jerry_value_is_null(args[0]) || - jerry_value_is_undefined(args[0])) { - entityPositionSetParent( - c->entityId, c->componentId, - ENTITY_ID_INVALID, COMPONENT_ID_INVALID - ); - return jerry_undefined(); - } - jscomponent_t *parent = (jscomponent_t *)scriptProtoGetValue( - &MODULE_POSITION_PROTO, args[0] - ); - if(!parent) { - return moduleBaseThrow( - "Position.setParent: expected Position or null" - ); - } - entityPositionSetParent( - c->entityId, c->componentId, - parent->entityId, parent->componentId - ); - return jerry_undefined(); -} - -moduleBaseFunction(modulePositionToString) { - jscomponent_t *c = modulePositionSelf(callInfo); - if(!c) return jerry_string_sz("Position:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Position(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void modulePositionInit(void) { - scriptProtoInit( - &MODULE_POSITION_PROTO, "Position", - sizeof(jscomponent_t), modulePositionCtor - ); - - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "entity", modulePositionGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "id", modulePositionGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localPosition", - modulePositionGetLocalPos, modulePositionSetLocalPos - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldPosition", - modulePositionGetWorldPos, modulePositionSetWorldPos - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localRotation", - modulePositionGetLocalRot, modulePositionSetLocalRot - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldRotation", - modulePositionGetWorldRot, modulePositionSetWorldRot - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "localScale", - modulePositionGetLocalScale, modulePositionSetLocalScale - ); - scriptProtoDefineProp( - &MODULE_POSITION_PROTO, "worldScale", - modulePositionGetWorldScale, modulePositionSetWorldScale - ); - scriptProtoDefineFunc( - &MODULE_POSITION_PROTO, "lookAt", modulePositionLookAt - ); - scriptProtoDefineFunc( - &MODULE_POSITION_PROTO, "setParent", modulePositionSetParent - ); - scriptProtoDefineToString(&MODULE_POSITION_PROTO, modulePositionToString); -} - -void modulePositionDispose(void) { - scriptProtoDispose(&MODULE_POSITION_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/moduleposition.h b/src/dusk/script/module/entity/component/display/moduleposition.h deleted file mode 100644 index d24bcdcf..00000000 --- a/src/dusk/script/module/entity/component/display/moduleposition.h +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/display/entityposition.h" - -extern scriptproto_t MODULE_POSITION_PROTO; - -/** Position() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(modulePositionCtor); - -/** @return Entity ID that owns this position component. */ -moduleBaseFunction(modulePositionGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(modulePositionGetId); - -/** @return Local position as a Vec3. */ -moduleBaseFunction(modulePositionGetLocalPos); - -/** Sets local position. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalPos); - -/** @return World position as a Vec3. */ -moduleBaseFunction(modulePositionGetWorldPos); - -/** Sets world position. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldPos); - -/** @return Local rotation as a Vec3 (Euler angles). */ -moduleBaseFunction(modulePositionGetLocalRot); - -/** Sets local rotation. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalRot); - -/** @return World rotation as a Vec3 (Euler angles). */ -moduleBaseFunction(modulePositionGetWorldRot); - -/** Sets world rotation. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldRot); - -/** @return Local scale as a Vec3. */ -moduleBaseFunction(modulePositionGetLocalScale); - -/** Sets local scale. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetLocalScale); - -/** @return World scale as a Vec3. */ -moduleBaseFunction(modulePositionGetWorldScale); - -/** Sets world scale. @param args[0] Vec3. */ -moduleBaseFunction(modulePositionSetWorldScale); - -/** - * lookAt(target, up?) - orients the component toward a target point. - * @param args[0] Vec3 target position. - * @param args[1] Optional Vec3 up vector (defaults to world up). - */ -moduleBaseFunction(modulePositionLookAt); - -/** - * setParent(position?) - parents this component to another Position, or - * unparents when called with null/undefined. - * @param args[0] Position component or null/undefined. - */ -moduleBaseFunction(modulePositionSetParent); - -/** @return "Position(id)" string. */ -moduleBaseFunction(modulePositionToString); - -/** - * Initializes the Position module and registers the global Position class with - * localPosition/worldPosition/localRotation/worldRotation/localScale/worldScale - * properties and lookAt/setParent methods. - */ -void modulePositionInit(void); - -/** - * Disposes the Position module. - */ -void modulePositionDispose(void); diff --git a/src/dusk/script/module/entity/component/display/modulerenderable.c b/src/dusk/script/module/entity/component/display/modulerenderable.c deleted file mode 100644 index a0e23a8c..00000000 --- a/src/dusk/script/module/entity/component/display/modulerenderable.c +++ /dev/null @@ -1,278 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulerenderable.h" - -scriptproto_t MODULE_RENDERABLE_PROTO; - -jscomponent_t *moduleRenderableSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_RENDERABLE_PROTO, callInfo->this_value - ); -} - -entityrenderable_t *moduleRenderableData(const jscomponent_t *c) { - return (entityrenderable_t *)componentGetData( - c->entityId, c->componentId, COMPONENT_TYPE_RENDERABLE - ); -} - -float_t moduleRenderableArrayFloat( - const jerry_value_t arr, - const uint32_t idx, - const float_t def -) { - if(idx >= jerry_array_length(arr)) return def; - jerry_value_t v = jerry_object_get_index(arr, idx); - float_t f = jerry_value_is_number(v) - ? (float_t)jerry_value_as_number(v) : def; - jerry_value_free(v); - return f; -} - -moduleBaseFunction(moduleRenderableCtor) { - return moduleBaseThrow("Renderable cannot be instantiated with new"); -} - -moduleBaseFunction(moduleRenderableGetEntity) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleRenderableGetId) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleRenderableGetType) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return jerry_number((double)r->type); -} - -moduleBaseFunction(moduleRenderableSetType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityRenderableSetType( - c->entityId, c->componentId, - (entityrenderabletype_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetPriority) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return jerry_number((double)r->priority); -} - -moduleBaseFunction(moduleRenderableSetPriority) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityRenderableSetPriority( - c->entityId, c->componentId, - (int8_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetColor) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - return moduleColorPush(r->data.material.material.unlit.color); -} - -moduleBaseFunction(moduleRenderableSetColor) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - color_t *col = moduleColorFrom(args[0]); - if(!col) return moduleBaseThrow("Renderable.color: expected Color"); - r->data.material.material.unlit.color = *col; - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetTexture) { - jerry_value_t key = jerry_string_sz("_tex"); - jerry_value_t val = jerry_object_get(callInfo->this_value, key); - jerry_value_free(key); - return val; -} - -moduleBaseFunction(moduleRenderableSetTexture) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - jstexture_t *tex = (jstexture_t *)scriptProtoGetValue( - &MODULE_TEXTURE_PROTO, args[0] - ); - if(!tex || !tex->entry) { - return moduleBaseThrow("Renderable.texture: expected Texture"); - } - r->type = ENTITY_RENDERABLE_TYPE_SPRITEBATCH; - r->data.spritebatch.texture = &tex->entry->data.texture; - jerry_value_t pinKey = jerry_string_sz("_tex"); - jerry_object_set(callInfo->this_value, pinKey, args[0]); - jerry_value_free(pinKey); - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableGetSprites) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - const entityrenderablespritebatch_t *sb = &r->data.spritebatch; - - jerry_value_t arr = jerry_array((uint32_t)sb->spriteCount); - for(uint32_t i = 0; i < (uint32_t)sb->spriteCount; i++) { - const spritebatchsprite_t *s = &sb->sprites[i]; - float_t vals[10] = { - s->min[0], s->min[1], s->min[2], - s->max[0], s->max[1], s->max[2], - s->uvMin[0], s->uvMin[1], - s->uvMax[0], s->uvMax[1], - }; - jerry_value_t sprite = jerry_array(10); - for(uint32_t j = 0; j < 10; j++) { - jerry_value_t num = jerry_number((double)vals[j]); - jerry_object_set_index(sprite, j, num); - jerry_value_free(num); - } - jerry_object_set_index(arr, i, sprite); - jerry_value_free(sprite); - } - return arr; -} - -moduleBaseFunction(moduleRenderableSetSprites) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_undefined(); - entityrenderable_t *r = moduleRenderableData(c); - if(!r) return jerry_undefined(); - if(!jerry_value_is_array(args[0])) { - return moduleBaseThrow("Renderable.sprites: expected Array"); - } - entityrenderablespritebatch_t *sb = &r->data.spritebatch; - uint32_t count = jerry_array_length(args[0]); - if(count > ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX) { - return moduleBaseThrow("Renderable.sprites: exceeds sprite capacity"); - } - sb->spriteCount = 0; - for(uint32_t i = 0; i < count; i++) { - jerry_value_t elem = jerry_object_get_index(args[0], i); - if(!jerry_value_is_array(elem)) { - jerry_value_free(elem); - return moduleBaseThrow( - "Renderable.sprites: each element must be an Array" - ); - } - spritebatchsprite_t s; - if(jerry_array_length(elem) >= 10) { - s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); - s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); - s.min[2] = moduleRenderableArrayFloat(elem, 2, 0.0f); - s.max[0] = moduleRenderableArrayFloat(elem, 3, 0.0f); - s.max[1] = moduleRenderableArrayFloat(elem, 4, 0.0f); - s.max[2] = moduleRenderableArrayFloat(elem, 5, 0.0f); - s.uvMin[0] = moduleRenderableArrayFloat(elem, 6, 0.0f); - s.uvMin[1] = moduleRenderableArrayFloat(elem, 7, 0.0f); - s.uvMax[0] = moduleRenderableArrayFloat(elem, 8, 1.0f); - s.uvMax[1] = moduleRenderableArrayFloat(elem, 9, 1.0f); - } else { - s.min[0] = moduleRenderableArrayFloat(elem, 0, 0.0f); - s.min[1] = moduleRenderableArrayFloat(elem, 1, 0.0f); - s.min[2] = 0.0f; - s.max[0] = moduleRenderableArrayFloat(elem, 2, 0.0f); - s.max[1] = moduleRenderableArrayFloat(elem, 3, 0.0f); - s.max[2] = 0.0f; - s.uvMin[0] = moduleRenderableArrayFloat(elem, 4, 0.0f); - s.uvMin[1] = moduleRenderableArrayFloat(elem, 5, 0.0f); - s.uvMax[0] = moduleRenderableArrayFloat(elem, 6, 1.0f); - s.uvMax[1] = moduleRenderableArrayFloat(elem, 7, 1.0f); - } - jerry_value_free(elem); - sb->sprites[sb->spriteCount++] = s; - } - return jerry_undefined(); -} - -moduleBaseFunction(moduleRenderableToString) { - jscomponent_t *c = moduleRenderableSelf(callInfo); - if(!c) return jerry_string_sz("Renderable:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Renderable(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleRenderableInit(void) { - scriptProtoInit( - &MODULE_RENDERABLE_PROTO, "Renderable", - sizeof(jscomponent_t), moduleRenderableCtor - ); - - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "entity", moduleRenderableGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "id", moduleRenderableGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "type", - moduleRenderableGetType, moduleRenderableSetType - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "priority", - moduleRenderableGetPriority, moduleRenderableSetPriority - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "color", - moduleRenderableGetColor, moduleRenderableSetColor - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "texture", - moduleRenderableGetTexture, moduleRenderableSetTexture - ); - scriptProtoDefineProp( - &MODULE_RENDERABLE_PROTO, "sprites", - moduleRenderableGetSprites, moduleRenderableSetSprites - ); - scriptProtoDefineToString(&MODULE_RENDERABLE_PROTO, moduleRenderableToString); - - jerry_value_t ctor = MODULE_RENDERABLE_PROTO.constructor; - struct { const char_t *name; int val; } types[] = { - { "SHADER_MATERIAL", ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL }, - { "SPRITEBATCH", ENTITY_RENDERABLE_TYPE_SPRITEBATCH }, - { "CUSTOM", ENTITY_RENDERABLE_TYPE_CUSTOM }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(types[i].name); - jerry_value_t v = jerry_number((double)types[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void moduleRenderableDispose(void) { - scriptProtoDispose(&MODULE_RENDERABLE_PROTO); -} diff --git a/src/dusk/script/module/entity/component/display/modulerenderable.h b/src/dusk/script/module/entity/component/display/modulerenderable.h deleted file mode 100644 index 39c1ef64..00000000 --- a/src/dusk/script/module/entity/component/display/modulerenderable.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/module/display/modulecolor.h" -#include "script/module/display/moduletexture.h" -#include "script/module/entity/modulecomponent.h" -#include "script/scriptproto.h" -#include "entity/component/display/entityrenderable.h" - -extern scriptproto_t MODULE_RENDERABLE_PROTO; - -/** Renderable() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleRenderableCtor); - -/** @return Entity ID that owns this renderable component. */ -moduleBaseFunction(moduleRenderableGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleRenderableGetId); - -/** @return Render type constant (Renderable.SPRITEBATCH etc.). */ -moduleBaseFunction(moduleRenderableGetType); - -/** Sets render type. @param args[0] entityrenderabletype_t value. */ -moduleBaseFunction(moduleRenderableSetType); - -/** @return Render priority (signed integer). */ -moduleBaseFunction(moduleRenderableGetPriority); - -/** Sets render priority. @param args[0] Priority integer. */ -moduleBaseFunction(moduleRenderableSetPriority); - -/** @return Material color as a Color object. */ -moduleBaseFunction(moduleRenderableGetColor); - -/** Sets material color. @param args[0] Color object. */ -moduleBaseFunction(moduleRenderableSetColor); - -/** @return The pinned Texture JS instance, or undefined if none set. */ -moduleBaseFunction(moduleRenderableGetTexture); - -/** - * Sets the texture for SPRITEBATCH rendering. Also switches the render type to - * SPRITEBATCH and pins the JS Texture object to prevent GC. - * @param args[0] Texture JS object. - */ -moduleBaseFunction(moduleRenderableSetTexture); - -/** - * @return JS array of sprite sub-arrays, each with 10 numbers: - * [x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]. - */ -moduleBaseFunction(moduleRenderableGetSprites); - -/** - * Sets sprite data. Accepts an array of sub-arrays - 10 elements (3D) or - * 8 elements (2D, z defaults to 0). - * @param args[0] Array of sprite sub-arrays. - */ -moduleBaseFunction(moduleRenderableSetSprites); - -/** @return "Renderable(id)" string. */ -moduleBaseFunction(moduleRenderableToString); - -/** - * Initializes the Renderable module and registers the global Renderable class - * with type/priority/color/texture/sprites properties and SPRITEBATCH/ - * SHADER_MATERIAL/CUSTOM constants. - */ -void moduleRenderableInit(void); - -/** - * Disposes the Renderable module. - */ -void moduleRenderableDispose(void); diff --git a/src/dusk/script/module/entity/component/modulecomponentlist.c b/src/dusk/script/module/entity/component/modulecomponentlist.c deleted file mode 100644 index 472b3605..00000000 --- a/src/dusk/script/module/entity/component/modulecomponentlist.c +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecomponentlist.h" - -jerry_value_t moduleComponentListCreateInstance( - const componenttype_t type, - const jscomponent_t *comp -) { - switch(type) { - case COMPONENT_TYPE_CAMERA: - return scriptProtoCreateValue(&MODULE_CAMERA_PROTO, comp); - case COMPONENT_TYPE_PHYSICS: - return scriptProtoCreateValue(&MODULE_PHYSICS_PROTO, comp); - case COMPONENT_TYPE_POSITION: - return scriptProtoCreateValue(&MODULE_POSITION_PROTO, comp); - case COMPONENT_TYPE_RENDERABLE: - return scriptProtoCreateValue(&MODULE_RENDERABLE_PROTO, comp); - case COMPONENT_TYPE_TRIGGER: - return scriptProtoCreateValue(&MODULE_TRIGGER_PROTO, comp); - default: - return scriptProtoCreateValue(&MODULE_COMPONENT_PROTO, comp); - } -} - -void moduleComponentListInit(void) { - moduleCameraInit(); - modulePhysicsInit(); - modulePositionInit(); - moduleRenderableInit(); - moduleTriggerInit(); -} - -void moduleComponentListDispose(void) { - moduleTriggerDispose(); - moduleRenderableDispose(); - modulePositionDispose(); - modulePhysicsDispose(); - moduleCameraDispose(); -} diff --git a/src/dusk/script/module/entity/component/modulecomponentlist.h b/src/dusk/script/module/entity/component/modulecomponentlist.h deleted file mode 100644 index 0399ebb6..00000000 --- a/src/dusk/script/module/entity/component/modulecomponentlist.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/entity/modulecomponent.h" -#include "display/modulecamera.h" -#include "display/moduleposition.h" -#include "display/modulerenderable.h" -#include "physics/modulephysics.h" -#include "trigger/moduletrigger.h" - -/** - * Returns a typed JS instance for a newly-added component. Falls back to the - * generic Component proto for types that have no specific module yet. - * - * @param type Component type constant. - * @param comp Initialized jscomponent_t with entityId and componentId. - * @return A jerry_value_t JS object of the appropriate subtype. - */ -jerry_value_t moduleComponentListCreateInstance( - const componenttype_t type, - const jscomponent_t *comp -); - -/** - * Initializes all component sub-modules (camera, physics, position, renderable, - * trigger). - */ -void moduleComponentListInit(void); - -/** - * Disposes all component sub-modules in reverse init order. - */ -void moduleComponentListDispose(void); diff --git a/src/dusk/script/module/entity/component/physics/CMakeLists.txt b/src/dusk/script/module/entity/component/physics/CMakeLists.txt deleted file mode 100644 index 4bb31417..00000000 --- a/src/dusk/script/module/entity/component/physics/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulephysics.c -) diff --git a/src/dusk/script/module/entity/component/physics/modulephysics.c b/src/dusk/script/module/entity/component/physics/modulephysics.c deleted file mode 100644 index ebd20035..00000000 --- a/src/dusk/script/module/entity/component/physics/modulephysics.c +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulephysics.h" -#include "util/memory.h" - -scriptproto_t MODULE_PHYSICS_PROTO; - -jscomponent_t *modulePhysicsSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_PHYSICS_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(modulePhysicsCtor) { - return moduleBaseThrow("Physics cannot be instantiated with new"); -} - -moduleBaseFunction(modulePhysicsGetEntity) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(modulePhysicsGetId) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(modulePhysicsGetBodyType) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number( - (double)entityPhysicsGetBodyType(c->entityId, c->componentId) - ); -} - -moduleBaseFunction(modulePhysicsSetBodyType) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityPhysicsSetBodyType( - c->entityId, c->componentId, - (physicsbodytype_t)moduleBaseArgInt(0) - ); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetShape) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number( - (double)entityPhysicsGetShape( - c->entityId, c->componentId - ).type - ); -} - -moduleBaseFunction(modulePhysicsSetShape) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - physicsshape_t shape; - memoryZero(&shape, sizeof(physicsshape_t)); - shape.type = (physicshapetype_t)moduleBaseArgInt(0); - switch(shape.type) { - case PHYSICS_SHAPE_CUBE: - shape.data.cube.halfExtents[0] = 0.5f; - shape.data.cube.halfExtents[1] = 0.5f; - shape.data.cube.halfExtents[2] = 0.5f; - break; - case PHYSICS_SHAPE_SPHERE: - shape.data.sphere.radius = 0.5f; - break; - case PHYSICS_SHAPE_CAPSULE: - shape.data.capsule.radius = 0.5f; - shape.data.capsule.halfHeight = 0.5f; - break; - case PHYSICS_SHAPE_PLANE: - shape.data.plane.normal[1] = 1.0f; - shape.data.plane.distance = 0.0f; - break; - default: - break; - } - entityPhysicsSetShape(c->entityId, c->componentId, shape); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetVelocity) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - vec3 v; - entityPhysicsGetVelocity(c->entityId, c->componentId, v); - return moduleVec3Push(v); -} - -moduleBaseFunction(modulePhysicsSetVelocity) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Physics.velocity: expected Vec3"); - entityPhysicsSetVelocity(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetGravityScale) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityphysics_t *p = entityPhysicsGet(c->entityId, c->componentId); - if(!p) return jerry_undefined(); - return jerry_number((double)p->gravityScale); -} - -moduleBaseFunction(modulePhysicsSetGravityScale) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - entityphysics_t *p = entityPhysicsGet(c->entityId, c->componentId); - if(!p) return jerry_undefined(); - p->gravityScale = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsGetOnGround) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_boolean(entityPhysicsIsOnGround(c->entityId, c->componentId)); -} - -moduleBaseFunction(modulePhysicsApplyImpulse) { - moduleBaseRequireArgs(1); - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Physics.applyImpulse: expected Vec3"); - entityPhysicsApplyImpulse(c->entityId, c->componentId, v); - return jerry_undefined(); -} - -moduleBaseFunction(modulePhysicsToString) { - jscomponent_t *c = modulePhysicsSelf(callInfo); - if(!c) return jerry_string_sz("Physics:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Physics(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void modulePhysicsInit(void) { - scriptProtoInit( - &MODULE_PHYSICS_PROTO, "Physics", - sizeof(jscomponent_t), modulePhysicsCtor - ); - - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "entity", modulePhysicsGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "id", modulePhysicsGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "bodyType", - modulePhysicsGetBodyType, modulePhysicsSetBodyType - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "shape", - modulePhysicsGetShape, modulePhysicsSetShape - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "velocity", - modulePhysicsGetVelocity, modulePhysicsSetVelocity - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "gravityScale", - modulePhysicsGetGravityScale, modulePhysicsSetGravityScale - ); - scriptProtoDefineProp( - &MODULE_PHYSICS_PROTO, "onGround", modulePhysicsGetOnGround, NULL - ); - scriptProtoDefineFunc( - &MODULE_PHYSICS_PROTO, "applyImpulse", modulePhysicsApplyImpulse - ); - scriptProtoDefineToString(&MODULE_PHYSICS_PROTO, modulePhysicsToString); - - jerry_value_t ctor = MODULE_PHYSICS_PROTO.constructor; - struct { const char_t *name; int val; } bodyTypes[] = { - { "STATIC", PHYSICS_BODY_STATIC }, - { "DYNAMIC", PHYSICS_BODY_DYNAMIC }, - { "KINEMATIC", PHYSICS_BODY_KINEMATIC }, - }; - for(int i = 0; i < 3; i++) { - jerry_value_t k = jerry_string_sz(bodyTypes[i].name); - jerry_value_t v = jerry_number((double)bodyTypes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } - - struct { const char_t *name; int val; } shapes[] = { - { "SHAPE_CUBE", PHYSICS_SHAPE_CUBE }, - { "SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE }, - { "SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE }, - { "SHAPE_PLANE", PHYSICS_SHAPE_PLANE }, - }; - for(int i = 0; i < 4; i++) { - jerry_value_t k = jerry_string_sz(shapes[i].name); - jerry_value_t v = jerry_number((double)shapes[i].val); - jerry_object_set(ctor, k, v); - jerry_value_free(v); - jerry_value_free(k); - } -} - -void modulePhysicsDispose(void) { - scriptProtoDispose(&MODULE_PHYSICS_PROTO); -} diff --git a/src/dusk/script/module/entity/component/physics/modulephysics.h b/src/dusk/script/module/entity/component/physics/modulephysics.h deleted file mode 100644 index c4fbcafa..00000000 --- a/src/dusk/script/module/entity/component/physics/modulephysics.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/physics/entityphysics.h" - -extern scriptproto_t MODULE_PHYSICS_PROTO; - -/** Physics() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(modulePhysicsCtor); - -/** @return Entity ID that owns this physics component. */ -moduleBaseFunction(modulePhysicsGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(modulePhysicsGetId); - -/** @return Body type constant (Physics.STATIC, .DYNAMIC, .KINEMATIC). */ -moduleBaseFunction(modulePhysicsGetBodyType); - -/** Sets body type. @param args[0] physicsbodytype_t value. */ -moduleBaseFunction(modulePhysicsSetBodyType); - -/** @return Collision shape type constant (Physics.SHAPE_*). */ -moduleBaseFunction(modulePhysicsGetShape); - -/** Sets collision shape type. @param args[0] physicshapetype_t value. */ -moduleBaseFunction(modulePhysicsSetShape); - -/** @return Linear velocity as a Vec3. */ -moduleBaseFunction(modulePhysicsGetVelocity); - -/** Sets linear velocity. @param args[0] Vec3. */ -moduleBaseFunction(modulePhysicsSetVelocity); - -/** @return Gravity scale multiplier (float). */ -moduleBaseFunction(modulePhysicsGetGravityScale); - -/** Sets gravity scale. @param args[0] Float multiplier. */ -moduleBaseFunction(modulePhysicsSetGravityScale); - -/** @return True when the body is resting on a surface. */ -moduleBaseFunction(modulePhysicsGetOnGround); - -/** - * applyImpulse(impulse) - applies an instantaneous force to the body. - * @param args[0] Vec3 impulse vector. - */ -moduleBaseFunction(modulePhysicsApplyImpulse); - -/** @return "Physics(id)" string. */ -moduleBaseFunction(modulePhysicsToString); - -/** - * Initializes the Physics module and registers the global Physics class with - * bodyType/shape/velocity/gravityScale/onGround properties, applyImpulse - * method, and STATIC/DYNAMIC/KINEMATIC and SHAPE_* constants. - */ -void modulePhysicsInit(void); - -/** - * Disposes the Physics module. - */ -void modulePhysicsDispose(void); diff --git a/src/dusk/script/module/entity/component/trigger/CMakeLists.txt b/src/dusk/script/module/entity/component/trigger/CMakeLists.txt deleted file mode 100644 index 915db5ac..00000000 --- a/src/dusk/script/module/entity/component/trigger/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - moduletrigger.c -) diff --git a/src/dusk/script/module/entity/component/trigger/moduletrigger.c b/src/dusk/script/module/entity/component/trigger/moduletrigger.c deleted file mode 100644 index 1cf5c5b6..00000000 --- a/src/dusk/script/module/entity/component/trigger/moduletrigger.c +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletrigger.h" - -scriptproto_t MODULE_TRIGGER_PROTO; - -jscomponent_t *moduleTriggerSelf(const jerry_call_info_t *callInfo) { - return (jscomponent_t *)scriptProtoGetValue( - &MODULE_TRIGGER_PROTO, callInfo->this_value - ); -} - -moduleBaseFunction(moduleTriggerCtor) { - return moduleBaseThrow("Trigger cannot be instantiated with new"); -} - -moduleBaseFunction(moduleTriggerGetEntity) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleTriggerGetId) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleTriggerGetMin) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - return moduleVec3Push(t->min); -} - -moduleBaseFunction(moduleTriggerSetMin) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.min: expected Vec3"); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - glm_vec3_copy(v, t->min); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerGetMax) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - return moduleVec3Push(t->max); -} - -moduleBaseFunction(moduleTriggerSetMax) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.max: expected Vec3"); - entitytrigger_t *t = entityTriggerGet(c->entityId, c->componentId); - if(!t) return jerry_undefined(); - glm_vec3_copy(v, t->max); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerSetBounds) { - moduleBaseRequireArgs(2); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *minV = moduleVec3From(args[0]); - float_t *maxV = moduleVec3From(args[1]); - if(!minV) return moduleBaseThrow("Trigger.setBounds: expected Vec3 for min"); - if(!maxV) return moduleBaseThrow("Trigger.setBounds: expected Vec3 for max"); - entityTriggerSetBounds(c->entityId, c->componentId, minV, maxV); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTriggerContains) { - moduleBaseRequireArgs(1); - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_undefined(); - float_t *v = moduleVec3From(args[0]); - if(!v) return moduleBaseThrow("Trigger.contains: expected Vec3"); - return jerry_boolean(entityTriggerContains(c->entityId, c->componentId, v)); -} - -moduleBaseFunction(moduleTriggerToString) { - jscomponent_t *c = moduleTriggerSelf(callInfo); - if(!c) return jerry_string_sz("Trigger:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Trigger(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleTriggerInit(void) { - scriptProtoInit( - &MODULE_TRIGGER_PROTO, "Trigger", - sizeof(jscomponent_t), moduleTriggerCtor - ); - - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "entity", moduleTriggerGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "id", moduleTriggerGetId, NULL - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "min", moduleTriggerGetMin, moduleTriggerSetMin - ); - scriptProtoDefineProp( - &MODULE_TRIGGER_PROTO, "max", moduleTriggerGetMax, moduleTriggerSetMax - ); - scriptProtoDefineFunc( - &MODULE_TRIGGER_PROTO, "setBounds", moduleTriggerSetBounds - ); - scriptProtoDefineFunc( - &MODULE_TRIGGER_PROTO, "contains", moduleTriggerContains - ); - scriptProtoDefineToString(&MODULE_TRIGGER_PROTO, moduleTriggerToString); -} - -void moduleTriggerDispose(void) { - scriptProtoDispose(&MODULE_TRIGGER_PROTO); -} diff --git a/src/dusk/script/module/entity/component/trigger/moduletrigger.h b/src/dusk/script/module/entity/component/trigger/moduletrigger.h deleted file mode 100644 index f493d83e..00000000 --- a/src/dusk/script/module/entity/component/trigger/moduletrigger.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/math/modulevec3.h" -#include "script/module/entity/modulecomponent.h" -#include "entity/component/trigger/entitytrigger.h" - -extern scriptproto_t MODULE_TRIGGER_PROTO; - -/** Trigger() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleTriggerCtor); - -/** @return Entity ID that owns this trigger component. */ -moduleBaseFunction(moduleTriggerGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleTriggerGetId); - -/** @return AABB minimum corner as a Vec3. */ -moduleBaseFunction(moduleTriggerGetMin); - -/** Sets AABB minimum corner. @param args[0] Vec3. */ -moduleBaseFunction(moduleTriggerSetMin); - -/** @return AABB maximum corner as a Vec3. */ -moduleBaseFunction(moduleTriggerGetMax); - -/** Sets AABB maximum corner. @param args[0] Vec3. */ -moduleBaseFunction(moduleTriggerSetMax); - -/** - * setBounds(min, max) - sets both AABB corners at once. - * @param args[0] Vec3 minimum corner. - * @param args[1] Vec3 maximum corner. - */ -moduleBaseFunction(moduleTriggerSetBounds); - -/** - * contains(point) - tests whether a world-space point is inside the AABB. - * @param args[0] Vec3 point. - * @return true if the point is inside the trigger volume. - */ -moduleBaseFunction(moduleTriggerContains); - -/** @return "Trigger(id)" string. */ -moduleBaseFunction(moduleTriggerToString); - -/** - * Initializes the Trigger module and registers the global Trigger class with - * min/max properties and setBounds/contains methods. - */ -void moduleTriggerInit(void); - -/** - * Disposes the Trigger module. - */ -void moduleTriggerDispose(void); diff --git a/src/dusk/script/module/entity/modulecomponent.c b/src/dusk/script/module/entity/modulecomponent.c deleted file mode 100644 index 3326c949..00000000 --- a/src/dusk/script/module/entity/modulecomponent.c +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulecomponent.h" - -scriptproto_t MODULE_COMPONENT_PROTO; - -moduleBaseFunction(moduleComponentCtor) { - return moduleBaseThrow("Component cannot be instantiated with new"); -} - -moduleBaseFunction(moduleComponentGetEntity) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_undefined(); - return jerry_number((double)c->entityId); -} - -moduleBaseFunction(moduleComponentGetId) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_undefined(); - return jerry_number((double)c->componentId); -} - -moduleBaseFunction(moduleComponentToString) { - jscomponent_t *c = (jscomponent_t *)scriptProtoGetValue( - &MODULE_COMPONENT_PROTO, callInfo->this_value - ); - if(!c) return jerry_string_sz("Component:invalid"); - char_t buf[32]; - snprintf(buf, sizeof(buf), "Component(%u)", (unsigned)c->componentId); - return jerry_string_sz(buf); -} - -void moduleComponentInit(void) { - scriptProtoInit( - &MODULE_COMPONENT_PROTO, "Component", - sizeof(jscomponent_t), moduleComponentCtor - ); - scriptProtoDefineProp( - &MODULE_COMPONENT_PROTO, "entity", moduleComponentGetEntity, NULL - ); - scriptProtoDefineProp( - &MODULE_COMPONENT_PROTO, "id", moduleComponentGetId, NULL - ); - scriptProtoDefineToString(&MODULE_COMPONENT_PROTO, moduleComponentToString); - - jerry_value_t ctor = MODULE_COMPONENT_PROTO.constructor; - #define X(enumName, type, field, init, dispose, render) { \ - jerry_value_t _k = jerry_string_sz(#enumName); \ - jerry_value_t _v = jerry_number((double)COMPONENT_TYPE_##enumName); \ - jerry_object_set(ctor, _k, _v); \ - jerry_value_free(_v); \ - jerry_value_free(_k); \ - } - #include "entity/componentlist.h" - #undef X -} - -void moduleComponentDispose(void) { - scriptProtoDispose(&MODULE_COMPONENT_PROTO); -} diff --git a/src/dusk/script/module/entity/modulecomponent.h b/src/dusk/script/module/entity/modulecomponent.h deleted file mode 100644 index 878b4a8f..00000000 --- a/src/dusk/script/module/entity/modulecomponent.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "entity/component.h" - -extern scriptproto_t MODULE_COMPONENT_PROTO; - -/** C struct wrapped by every Component JS instance. */ -typedef struct { - entityid_t entityId; - componentid_t componentId; -} jscomponent_t; - -/** Component() constructor - always throws; not directly instantiable. */ -moduleBaseFunction(moduleComponentCtor); - -/** @return The entity ID that owns this component. */ -moduleBaseFunction(moduleComponentGetEntity); - -/** @return This component's ID. */ -moduleBaseFunction(moduleComponentGetId); - -/** @return Component ID as a string. */ -moduleBaseFunction(moduleComponentToString); - -/** - * Initializes the Component module, registers the global Component class with - * entity/id properties and TYPE_* / INVALID constants. - */ -void moduleComponentInit(void); - -/** - * Disposes the Component module. - */ -void moduleComponentDispose(void); diff --git a/src/dusk/script/module/entity/moduleentity.c b/src/dusk/script/module/entity/moduleentity.c deleted file mode 100644 index 7897af05..00000000 --- a/src/dusk/script/module/entity/moduleentity.c +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleentity.h" - -scriptproto_t MODULE_ENTITY_PROTO; - -moduleBaseFunction(moduleEntityCtor) { - return moduleBaseThrow("Entity cannot be instantiated with new"); -} - -moduleBaseFunction(moduleEntityCreate) { - entityid_t id = entityManagerAdd(); - if(id == ENTITY_ID_INVALID) { - return moduleBaseThrow("Entity.create: no entity slots available"); - } - jsentity_t ent = { .id = id }; - return scriptProtoCreateValue(&MODULE_ENTITY_PROTO, &ent); -} - -moduleBaseFunction(moduleEntityDisposeEntity) { - moduleBaseRequireArgs(1); - jsentity_t *ent = scriptProtoGetValue(&MODULE_ENTITY_PROTO, args[0]); - if(!ent) return moduleBaseThrow("Entity.dispose: expected Entity object"); - entityDispose(ent->id); - return jerry_undefined(); -} - -moduleBaseFunction(moduleEntityAdd) { - jsentity_t *ent = scriptProtoGetValue( - &MODULE_ENTITY_PROTO, callInfo->this_value - ); - if(!ent) return moduleBaseThrow("Entity.add: invalid this"); - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - - const componenttype_t type = (componenttype_t)moduleBaseArgInt(0); - if(type <= COMPONENT_TYPE_NULL || type >= COMPONENT_TYPE_COUNT) { - return moduleBaseThrow("Entity.add: invalid component type"); - } - - componentid_t cid = entityAddComponent(ent->id, type); - if(cid == COMPONENT_ID_INVALID) { - return moduleBaseThrow("Entity.add: failed to add component"); - } - - jscomponent_t comp = { .entityId = ent->id, .componentId = cid }; - return moduleComponentListCreateInstance(type, &comp); -} - -moduleBaseFunction(moduleEntityToString) { - jsentity_t *ent = scriptProtoGetValue( - &MODULE_ENTITY_PROTO, callInfo->this_value - ); - if(!ent) return jerry_string_sz("Entity:invalid"); - jerry_value_t num = jerry_number((double)ent->id); - jerry_value_t str = jerry_value_to_string(num); - jerry_value_free(num); - return str; -} - -void moduleEntityInit(void) { - moduleComponentInit(); - moduleComponentListInit(); - - scriptProtoInit( - &MODULE_ENTITY_PROTO, "Entity", - sizeof(jsentity_t), moduleEntityCtor - ); - - scriptProtoDefineStaticFunc( - &MODULE_ENTITY_PROTO, "create", moduleEntityCreate - ); - scriptProtoDefineStaticFunc( - &MODULE_ENTITY_PROTO, "dispose", moduleEntityDisposeEntity - ); - - jerry_value_t ctor = MODULE_ENTITY_PROTO.constructor; - jerry_value_t _key = jerry_string_sz("INVALID"); - jerry_value_t _val = jerry_number((double)ENTITY_ID_INVALID); - jerry_object_set(ctor, _key, _val); - jerry_value_free(_val); - jerry_value_free(_key); - - scriptProtoDefineFunc(&MODULE_ENTITY_PROTO, "add", moduleEntityAdd); - scriptProtoDefineToString(&MODULE_ENTITY_PROTO, moduleEntityToString); -} - -void moduleEntityDispose(void) { - scriptProtoDispose(&MODULE_ENTITY_PROTO); - moduleComponentListDispose(); - moduleComponentDispose(); -} diff --git a/src/dusk/script/module/entity/moduleentity.h b/src/dusk/script/module/entity/moduleentity.h deleted file mode 100644 index 9dc63ec9..00000000 --- a/src/dusk/script/module/entity/moduleentity.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/module/entity/modulecomponent.h" -#include "script/module/entity/component/modulecomponentlist.h" -#include "entity/entitymanager.h" - -extern scriptproto_t MODULE_ENTITY_PROTO; - -/** C struct wrapped by every Entity JS instance. */ -typedef struct { - entityid_t id; -} jsentity_t; - -/** Entity() constructor - always throws; use Entity.create() instead. */ -moduleBaseFunction(moduleEntityCtor); - -/** - * Entity.create() - allocates a new entity slot and returns an - * Entity JS object. - * @return Entity JS object. - * @throws If no entity slots are available. - */ -moduleBaseFunction(moduleEntityCreate); - -/** - * Entity.dispose(entity) - disposes an entity and all its components. - * @param args[0] Entity JS object. - */ -moduleBaseFunction(moduleEntityDisposeEntity); - -/** - * entity.add(type) - adds a component of the given type to this entity. - * @param args[0] Component type constant (e.g. COMPONENT_TYPE_POSITION). - * @return Typed component JS object (Position, Camera, etc.). - * @throws If the type is invalid or no component slots are available. - */ -moduleBaseFunction(moduleEntityAdd); - -/** @return Entity ID as a string. */ -moduleBaseFunction(moduleEntityToString); - -/** - * Initializes the Entity module and registers the global Entity class with - * create/dispose static methods, add instance method, and INVALID constant. - * Also calls moduleComponentInit() and moduleComponentListInit(). - */ -void moduleEntityInit(void); - -/** - * Disposes the Entity module and calls moduleComponentListDispose() and - * moduleComponentDispose(). - */ -void moduleEntityDispose(void); diff --git a/src/dusk/script/module/event/CMakeLists.txt b/src/dusk/script/module/event/CMakeLists.txt deleted file mode 100644 index 5bb1fd1b..00000000 --- a/src/dusk/script/module/event/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - moduleevent.c -) diff --git a/src/dusk/script/module/event/moduleevent.c b/src/dusk/script/module/event/moduleevent.c deleted file mode 100644 index 058bdfe4..00000000 --- a/src/dusk/script/module/event/moduleevent.c +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleevent.h" -#include "util/memory.h" -#include "assert/assert.h" - -#define MODULE_EVENT_PENDING_MAX 32 - -scriptproto_t MODULE_EVENT_PROTO; - -static scriptpromisepend_t MODULE_EVENT_PENDING[MODULE_EVENT_PENDING_MAX]; -static uint32_t MODULE_EVENT_PENDING_COUNT = 0; - -void moduleEventTrampoline0(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline1(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline2(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -void moduleEventTrampoline3(void *params, void *user) { - jerry_value_t fn = (jerry_value_t)(uintptr_t)user; - jerry_value_t ret = jerry_call(fn, jerry_undefined(), NULL, 0); - jerry_value_free(ret); -} - -eventcallback_t MODULE_EVENT_TRAMPOLINES[MODULE_EVENT_MAX_SLOTS] = { - moduleEventTrampoline0, - moduleEventTrampoline1, - moduleEventTrampoline2, - moduleEventTrampoline3, -}; - -void moduleEventFree(void *ptr, jerry_object_native_info_t *info) { - jsevent_t *ev = (jsevent_t *)ptr; - if(ev) { - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(jerry_value_is_function(ev->fns[i])) { - if(ev->event) { - eventUnsubscribe(ev->event, MODULE_EVENT_TRAMPOLINES[i]); - } - jerry_value_free(ev->fns[i]); - } - } - } - memoryFree(ptr); -} - -moduleBaseFunction(moduleEventOn) { - moduleBaseRequireArgs(1); - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) { - return moduleBaseThrow("Event.on: invalid event"); - } - if(!jerry_value_is_function(args[0])) { - return moduleBaseThrow("Event.on: expected function"); - } - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(!jerry_value_is_function(ev->fns[i])) { - ev->fns[i] = jerry_value_copy(args[0]); - eventSubscribe( - ev->event, - MODULE_EVENT_TRAMPOLINES[i], - (void *)(uintptr_t)ev->fns[i] - ); - return jerry_value_copy(callInfo->this_value); - } - } - return moduleBaseThrow("Event.on: no available subscriber slots"); -} - -moduleBaseFunction(moduleEventOff) { - moduleBaseRequireArgs(1); - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) return jerry_value_copy(callInfo->this_value); - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - if(!jerry_value_is_function(ev->fns[i])) continue; - jerry_value_t eq = jerry_binary_op( - JERRY_BIN_OP_STRICT_EQUAL, ev->fns[i], args[0] - ); - bool_t match = jerry_value_is_true(eq); - jerry_value_free(eq); - if(match) { - eventUnsubscribe(ev->event, MODULE_EVENT_TRAMPOLINES[i]); - jerry_value_free(ev->fns[i]); - ev->fns[i] = jerry_undefined(); - return jerry_value_copy(callInfo->this_value); - } - } - return jerry_value_copy(callInfo->this_value); -} - -moduleBaseFunction(moduleEventWait) { - jsevent_t *ev = (jsevent_t *)scriptProtoGetValue( - &MODULE_EVENT_PROTO, callInfo->this_value - ); - if(!ev || !ev->event) { - return moduleBaseThrow("Event.wait: invalid event"); - } - if(MODULE_EVENT_PENDING_COUNT >= MODULE_EVENT_PENDING_MAX) { - return moduleBaseThrow("Event.wait: too many pending awaits"); - } - if(!scriptPromisePendHas( - MODULE_EVENT_PENDING, MODULE_EVENT_PENDING_COUNT, ev->event - )) { - if(ev->event->count >= ev->event->size) { - return moduleBaseThrow( - "Event.wait: event subscriber capacity exceeded" - ); - } - eventSubscribe(ev->event, moduleEventWaitFire, ev->event); - } - jerry_value_t promise = jerry_promise(); - scriptPromisePendAdd( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT, - MODULE_EVENT_PENDING_MAX, ev->event, promise - ); - return promise; -} - -jerry_value_t moduleEventCreate(event_t *event) { - assertNotNull(event, "moduleEventCreate: event must not be NULL"); - jsevent_t ev; - ev.event = event; - for(uint32_t i = 0; i < MODULE_EVENT_MAX_SLOTS; i++) { - ev.fns[i] = jerry_undefined(); - } - return scriptProtoCreateValue(&MODULE_EVENT_PROTO, &ev); -} - -jerry_value_t moduleEventGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -) { - jerry_value_t keyStr = jerry_string_sz(pinKey); - jerry_value_t existing = jerry_object_get( - callInfo->this_value, keyStr - ); - if(!jerry_value_is_undefined(existing)) { - jerry_value_free(keyStr); - return existing; - } - jerry_value_free(existing); - jerry_value_t ev = moduleEventCreate(event); - jerry_object_set(callInfo->this_value, keyStr, ev); - jerry_value_free(keyStr); - return ev; -} - -void moduleEventWaitFire(void *params, void *user) { - event_t *event = (event_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT, event, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe(event, moduleEventWaitFire); -} - -void moduleEventInit(void) { - MODULE_EVENT_PENDING_COUNT = 0; - scriptProtoInit( - &MODULE_EVENT_PROTO, NULL, - sizeof(jsevent_t), NULL - ); - MODULE_EVENT_PROTO.info.free_cb = moduleEventFree; - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "on", moduleEventOn); - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "off", moduleEventOff); - scriptProtoDefineFunc(&MODULE_EVENT_PROTO, "wait", moduleEventWait); -} - -void moduleEventDispose(void) { - for(uint32_t i = 0; i < MODULE_EVENT_PENDING_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_EVENT_PENDING[j].key == MODULE_EVENT_PENDING[i].key) { - seen = true; break; - } - } - if(!seen) { - eventUnsubscribe( - (event_t *)MODULE_EVENT_PENDING[i].key, moduleEventWaitFire - ); - } - } - scriptPromisePendFreeAll( - MODULE_EVENT_PENDING, &MODULE_EVENT_PENDING_COUNT - ); - scriptProtoDispose(&MODULE_EVENT_PROTO); -} diff --git a/src/dusk/script/module/event/moduleevent.h b/src/dusk/script/module/event/moduleevent.h deleted file mode 100644 index c8012f6c..00000000 --- a/src/dusk/script/module/event/moduleevent.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "script/scriptpromisepend.h" -#include "event/event.h" - -/** Maximum number of on() subscriber slots per Event. */ -#define MODULE_EVENT_MAX_SLOTS 4 - -extern scriptproto_t MODULE_EVENT_PROTO; - -/** - * Array of C trampolines for on() subscriptions. - * Slot i uses MODULE_EVENT_TRAMPOLINES[i]. - */ -extern eventcallback_t MODULE_EVENT_TRAMPOLINES[MODULE_EVENT_MAX_SLOTS]; - -/** Native data stored on each Event JS object. */ -typedef struct { - event_t *event; - jerry_value_t fns[MODULE_EVENT_MAX_SLOTS]; -} jsevent_t; - -/** - * Event trampoline used by wait(). Resolves all pending wait() promises - * for the fired event and unsubscribes itself. - * - * @param params Unused. - * @param user The event_t pointer that fired. - */ -void moduleEventWaitFire(void *params, void *user); - -/** - * GC free callback - unsubscribes all on() slots and releases JS function - * refs when the Event object is garbage collected. - * - * @param ptr Native jsevent_t pointer. - * @param info Native info (unused). - */ -void moduleEventFree(void *ptr, jerry_object_native_info_t *info); - -/** - * on(fn) - subscribes fn to the event. Returns this for chaining. - * @param args[0] Function to call when the event fires. - */ -moduleBaseFunction(moduleEventOn); - -/** - * off(fn) - removes a previously subscribed fn. Returns this for chaining. - * @param args[0] The same function reference passed to on(). - */ -moduleBaseFunction(moduleEventOff); - -/** - * wait() - returns a Promise that resolves the next time the event fires. - */ -moduleBaseFunction(moduleEventWait); - -/** - * Wraps a C event_t pointer in a new JS Event object. - * - * @param event The event to wrap. Must outlive the returned JS value. - * @return A new JS Event instance. - */ -jerry_value_t moduleEventCreate(event_t *event); - -/** - * Returns the Event object pinned at pinKey on the parent JS object, - * creating and pinning a new one on first access. - * - * @param callInfo The call info - this_value is the parent object. - * @param event The C event_t to wrap. Must outlive the parent object. - * @param pinKey Property name used to cache the Event on the parent. - * @return A JS Event value (caller must free). - */ -jerry_value_t moduleEventGetOrCreate( - const jerry_call_info_t *callInfo, - event_t *event, - const char_t *pinKey -); - -/** Initializes the Event module and registers the global Event prototype. */ -void moduleEventInit(void); - -/** - * Disposes the Event module, rejecting any pending wait() promises and - * cleaning up. - */ -void moduleEventDispose(void); diff --git a/src/dusk/script/module/input/CMakeLists.txt b/src/dusk/script/module/input/CMakeLists.txt deleted file mode 100644 index fde1ea90..00000000 --- a/src/dusk/script/module/input/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - moduleinput.c -) diff --git a/src/dusk/script/module/input/moduleinput.c b/src/dusk/script/module/input/moduleinput.c deleted file mode 100644 index f927e330..00000000 --- a/src/dusk/script/module/input/moduleinput.c +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleinput.h" -#include - -scriptproto_t MODULE_INPUT_PROTO; - -moduleBaseFunction(moduleInputIsDown) { - moduleInputRequireAction(0, "Input.isDown"); - return jerry_boolean(inputIsDown((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputWasDown) { - moduleInputRequireAction(0, "Input.wasDown"); - return jerry_boolean(inputWasDown((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputPressed) { - moduleInputRequireAction(0, "Input.pressed"); - return jerry_boolean(inputPressed((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputReleased) { - moduleInputRequireAction(0, "Input.released"); - return jerry_boolean(inputReleased((inputaction_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleInputGetValue) { - moduleInputRequireAction(0, "Input.getValue"); - return jerry_number( - inputGetCurrentValue((inputaction_t)moduleBaseArgInt(0)) - ); -} - -moduleBaseFunction(moduleInputAxis) { - moduleInputRequireAction(0, "Input.axis"); - moduleInputRequireAction(1, "Input.axis"); - return jerry_number(inputAxis( - (inputaction_t)moduleBaseArgInt(0), - (inputaction_t)moduleBaseArgInt(1) - )); -} - -moduleBaseFunction(moduleInputBind) { - moduleBaseRequireArgs(2); - moduleBaseRequireString(0); - if(!jerry_value_is_number(args[1])) { - return moduleBaseThrow("Input.bind: action must be a number"); - } - const inputaction_t action = (inputaction_t)moduleBaseArgInt(1); - if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - return moduleBaseThrow("Input.bind: invalid action"); - } - - char_t name[128]; - moduleBaseToString(args[0], name, sizeof(name)); - if(name[0] == '\0') { - return moduleBaseThrow("Input.bind: button name cannot be empty"); - } - - inputbutton_t btn = inputButtonGetByName(name); - if(btn.type == INPUT_BUTTON_TYPE_NONE) { - return moduleBaseThrow("Input.bind: unknown button name"); - } - - inputBind(btn, action); - return jerry_undefined(); -} - -void moduleInputInit(void) { - scriptProtoInit(&MODULE_INPUT_PROTO, "Input", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "isDown", moduleInputIsDown - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "wasDown", moduleInputWasDown - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "pressed", moduleInputPressed - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "released", moduleInputReleased - ); - scriptProtoDefineStaticFunc( - &MODULE_INPUT_PROTO, "getValue", moduleInputGetValue - ); - scriptProtoDefineStaticFunc(&MODULE_INPUT_PROTO, "axis", moduleInputAxis); - scriptProtoDefineStaticFunc(&MODULE_INPUT_PROTO, "bind", moduleInputBind); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)INPUT_ACTION_SCRIPT, - strlen(INPUT_ACTION_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleInputDispose(void) { - scriptProtoDispose(&MODULE_INPUT_PROTO); -} diff --git a/src/dusk/script/module/input/moduleinput.h b/src/dusk/script/module/input/moduleinput.h deleted file mode 100644 index d649ee2d..00000000 --- a/src/dusk/script/module/input/moduleinput.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "input/input.h" -#include "input/inputbutton.h" - -extern scriptproto_t MODULE_INPUT_PROTO; - -/** - * Validates an inputaction_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleInputRequireAction(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": action must be a number"); \ - } \ - const inputaction_t _act = (inputaction_t)moduleBaseArgInt(i); \ - if(_act <= INPUT_ACTION_NULL || _act >= INPUT_ACTION_COUNT) { \ - return moduleBaseThrow(ctx ": invalid action"); \ - } \ -} while(0) - -/** - * Input.isDown(action) - true while the action is held this frame. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputIsDown); - -/** - * Input.wasDown(action) - true if the action was held last frame. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputWasDown); - -/** - * Input.pressed(action) - true on the first frame the action is held. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputPressed); - -/** - * Input.released(action) - true on the first frame the action is released. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputReleased); - -/** - * Input.getValue(action) - returns the analog value (0-1) of the action. - * @param args[0] Input action constant. - */ -moduleBaseFunction(moduleInputGetValue); - -/** - * Input.axis(negative, positive) - returns a signed axis value in [-1, 1]. - * @param args[0] Negative action constant. - * @param args[1] Positive action constant. - */ -moduleBaseFunction(moduleInputAxis); - -/** - * Input.bind(buttonName, action) - binds a named button to an action. - * @param args[0] Button name string. - * @param args[1] Input action constant. - */ -moduleBaseFunction(moduleInputBind); - -/** - * Initializes the Input module, registers Input.isDown/wasDown/pressed/ - * released/getValue/axis/bind, and evaluates INPUT_ACTION_SCRIPT to populate - * action constants in the global scope. - */ -void moduleInputInit(void); - -/** - * Disposes the Input module. - */ -void moduleInputDispose(void); diff --git a/src/dusk/script/module/item/CMakeLists.txt b/src/dusk/script/module/item/CMakeLists.txt deleted file mode 100644 index 1e292daf..00000000 --- a/src/dusk/script/module/item/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - moduleitem.c - modulebackpack.c -) diff --git a/src/dusk/script/module/item/modulebackpack.c b/src/dusk/script/module/item/modulebackpack.c deleted file mode 100644 index c0b0702a..00000000 --- a/src/dusk/script/module/item/modulebackpack.c +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulebackpack.h" -#include "moduleitem.h" -#include "item/backpack.h" - -scriptproto_t MODULE_BACKPACK_PROTO; - -moduleBaseFunction(moduleBackpackGetIsFull) { - return jerry_boolean(inventoryIsFull(&BACKPACK)); -} - -moduleBaseFunction(moduleBackpackGetCount) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.getCount"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_number((double)inventoryGetCount(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackHas) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.has"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_boolean(inventoryItemExists(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackIsItemFull) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.isItemFull"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_boolean(inventoryItemFull(&BACKPACK, id)); -} - -moduleBaseFunction(moduleBackpackSet) { - moduleBaseRequireArgs(2); - moduleItemRequireId(0, "Backpack.set"); - moduleBaseRequireNumber(1); - const int32_t qty = moduleBaseArgInt(1); - if(qty < 0 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) { - return moduleBaseThrow("Backpack.set: quantity out of range (0-255)"); - } - inventorySet(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackAdd) { - moduleBaseRequireArgs(2); - moduleItemRequireId(0, "Backpack.add"); - moduleBaseRequireNumber(1); - const int32_t qty = moduleBaseArgInt(1); - if(qty < 1 || qty > (int32_t)ITEM_STACK_QUANTITY_MAX) { - return moduleBaseThrow("Backpack.add: quantity out of range (1-255)"); - } - inventoryAdd(&BACKPACK, (itemid_t)moduleBaseArgInt(0), (uint8_t)qty); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackRemove) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Backpack.remove"); - inventoryRemove(&BACKPACK, (itemid_t)moduleBaseArgInt(0)); - return jerry_undefined(); -} - -moduleBaseFunction(moduleBackpackSort) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - const inventorysort_t sortBy = (inventorysort_t)moduleBaseArgInt(0); - if((int32_t)sortBy < 0 || sortBy >= INVENTORY_SORT_COUNT) { - return moduleBaseThrow("Backpack.sort: invalid sort type"); - } - const bool_t reverse = (argc > 1 && jerry_value_is_true(args[1])); - inventorySort(&BACKPACK, sortBy, reverse); - return jerry_undefined(); -} - -void moduleBackpackInit(void) { - scriptProtoInit( - &MODULE_BACKPACK_PROTO, "Backpack", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticProp( - &MODULE_BACKPACK_PROTO, "isFull", moduleBackpackGetIsFull, NULL - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "getCount", moduleBackpackGetCount - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "has", moduleBackpackHas - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "isItemFull", moduleBackpackIsItemFull - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "set", moduleBackpackSet - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "add", moduleBackpackAdd - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "remove", moduleBackpackRemove - ); - scriptProtoDefineStaticFunc( - &MODULE_BACKPACK_PROTO, "sort", moduleBackpackSort - ); - - moduleBaseSetInt( - "INVENTORY_SORT_BY_ID", (int32_t)INVENTORY_SORT_BY_ID - ); - moduleBaseSetInt( - "INVENTORY_SORT_BY_TYPE", (int32_t)INVENTORY_SORT_BY_TYPE - ); -} - -void moduleBackpackDispose(void) { - scriptProtoDispose(&MODULE_BACKPACK_PROTO); -} diff --git a/src/dusk/script/module/item/modulebackpack.h b/src/dusk/script/module/item/modulebackpack.h deleted file mode 100644 index 5bbcd715..00000000 --- a/src/dusk/script/module/item/modulebackpack.h +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_BACKPACK_PROTO; - -/** - * Backpack.isFull - true when all storage slots are occupied. - */ -moduleBaseFunction(moduleBackpackGetIsFull); - -/** - * Backpack.getCount(itemId) - quantity of itemId (0 if absent). - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackGetCount); - -/** - * Backpack.has(itemId) - true if itemId is present with quantity > 0. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackHas); - -/** - * Backpack.isItemFull(itemId) - true if the stack is at max quantity. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackIsItemFull); - -/** - * Backpack.set(itemId, quantity) - set quantity; removes when 0. - * - * @param args[0] Item ID constant (ITEM_ID_*). - * @param args[1] Quantity 0-255. - */ -moduleBaseFunction(moduleBackpackSet); - -/** - * Backpack.add(itemId, quantity) - add quantity units of itemId. - * - * @param args[0] Item ID constant (ITEM_ID_*). - * @param args[1] Quantity to add (1-255). - */ -moduleBaseFunction(moduleBackpackAdd); - -/** - * Backpack.remove(itemId) - removes itemId entirely from the backpack. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleBackpackRemove); - -/** - * Backpack.sort(sortBy[, reverse]) - sort the backpack contents. - * - * @param args[0] Sort type (INVENTORY_SORT_BY_ID or - * INVENTORY_SORT_BY_TYPE). - * @param args[1] Optional boolean; true to reverse order. - */ -moduleBaseFunction(moduleBackpackSort); - -/** - * Initializes the Backpack module, registers all Backpack methods, and - * sets INVENTORY_SORT_BY_ID / INVENTORY_SORT_BY_TYPE as globals. - */ -void moduleBackpackInit(void); - -/** - * Disposes the Backpack module. - */ -void moduleBackpackDispose(void); diff --git a/src/dusk/script/module/item/moduleitem.c b/src/dusk/script/module/item/moduleitem.c deleted file mode 100644 index 05512814..00000000 --- a/src/dusk/script/module/item/moduleitem.c +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleitem.h" -#include - -scriptproto_t MODULE_ITEM_PROTO; - -moduleBaseFunction(moduleItemGetName) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Item.getName"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_string_sz(ITEMS[id].name); -} - -moduleBaseFunction(moduleItemGetType) { - moduleBaseRequireArgs(1); - moduleItemRequireId(0, "Item.getType"); - const itemid_t id = (itemid_t)moduleBaseArgInt(0); - return jerry_number((double)ITEMS[id].type); -} - -void moduleItemInit(void) { - scriptProtoInit(&MODULE_ITEM_PROTO, "Item", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_ITEM_PROTO, "getName", moduleItemGetName - ); - scriptProtoDefineStaticFunc( - &MODULE_ITEM_PROTO, "getType", moduleItemGetType - ); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)ITEM_SCRIPT, - strlen(ITEM_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleItemDispose(void) { - scriptProtoDispose(&MODULE_ITEM_PROTO); -} diff --git a/src/dusk/script/module/item/moduleitem.h b/src/dusk/script/module/item/moduleitem.h deleted file mode 100644 index 752b550c..00000000 --- a/src/dusk/script/module/item/moduleitem.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "item/item.h" - -extern scriptproto_t MODULE_ITEM_PROTO; - -/** - * Validates an itemid_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleItemRequireId(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": itemId must be a number"); \ - } \ - const itemid_t _id = (itemid_t)moduleBaseArgInt(i); \ - if(_id <= ITEM_ID_NULL || _id >= ITEM_ID_COUNT) { \ - return moduleBaseThrow(ctx ": invalid item ID"); \ - } \ -} while(0) - -/** - * Item.getName(itemId) - returns the item's string name. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleItemGetName); - -/** - * Item.getType(itemId) - returns the item's type constant. - * - * @param args[0] Item ID constant (ITEM_ID_*). - */ -moduleBaseFunction(moduleItemGetType); - -/** - * Initializes the Item module, registers Item.getName/getType, and - * evaluates ITEM_SCRIPT to populate ITEM_ID_* and ITEM_TYPE_* constants - * as globals in the script scope. - */ -void moduleItemInit(void); - -/** - * Disposes the Item module. - */ -void moduleItemDispose(void); diff --git a/src/dusk/script/module/locale/CMakeLists.txt b/src/dusk/script/module/locale/CMakeLists.txt deleted file mode 100644 index 7eb305af..00000000 --- a/src/dusk/script/module/locale/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulelocale.c -) diff --git a/src/dusk/script/module/locale/modulelocale.c b/src/dusk/script/module/locale/modulelocale.c deleted file mode 100644 index f824b9d2..00000000 --- a/src/dusk/script/module/locale/modulelocale.c +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulelocale.h" -#include "asset/loader/locale/assetlocaleloader.h" -#include "util/string.h" -#include - -#define MODULE_LOCALE_SUBSTITUTION_MAX 4 -#define MODULE_LOCALE_BUFFER_SIZE 512 -#define MODULE_LOCALE_ID_SIZE 256 - -scriptproto_t MODULE_LOCALE_PROTO; - -moduleBaseFunction(moduleLocaleGetText) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - - if(LOCALE.entry == NULL) { - return moduleBaseThrow("Locale.getText: no locale loaded"); - } - - char_t id[MODULE_LOCALE_ID_SIZE]; - moduleBaseToString(args[0], id, sizeof(id)); - - // Second arg is optional plural count. - int32_t plural = 1; - jerry_length_t subStart = 1; - if(argc > 1 && jerry_value_is_number(args[1])) { - plural = moduleBaseArgInt(1); - subStart = 2; - } - - // Collect up to MODULE_LOCALE_SUBSTITUTION_MAX substitution args. - assetlocalearg_t subArgs[MODULE_LOCALE_SUBSTITUTION_MAX]; - char_t subStrings[MODULE_LOCALE_SUBSTITUTION_MAX][128]; - size_t subCount = 0; - - for(jerry_length_t i = subStart; i < argc; i++) { - if(subCount >= MODULE_LOCALE_SUBSTITUTION_MAX) break; - - if(jerry_value_is_string(args[i])) { - moduleBaseToString( - args[i], subStrings[subCount], sizeof(subStrings[subCount]) - ); - subArgs[subCount].type = ASSET_LOCALE_ARG_STRING; - subArgs[subCount].stringValue = subStrings[subCount]; - } else if(jerry_value_is_number(args[i])) { - double n = jerry_value_as_number(args[i]); - if(floor(n) == n) { - subArgs[subCount].type = ASSET_LOCALE_ARG_INT; - subArgs[subCount].intValue = (int32_t)n; - } else { - subArgs[subCount].type = ASSET_LOCALE_ARG_FLOAT; - subArgs[subCount].floatValue = (float_t)n; - } - } else { - continue; - } - subCount++; - } - - char_t buf[MODULE_LOCALE_BUFFER_SIZE]; - assetLocaleGetStringWithArgs( - &LOCALE.entry->data.locale, - id, - plural, - buf, - sizeof(buf), - subCount > 0 ? subArgs : NULL, - subCount - ); - - return jerry_string_sz(buf); -} - -moduleBaseFunction(moduleLocaleSetLocale) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - - char_t name[64]; - moduleBaseToString(args[0], name, sizeof(name)); - - const localeinfo_t *locale = NULL; - if(stringEquals(name, LOCALE_EN_US.name)) locale = &LOCALE_EN_US; - - if(locale == NULL) { - return moduleBaseThrow("Locale.setLocale: unknown locale name"); - } - - localeManagerSetLocale(locale); - return jerry_undefined(); -} - -void moduleLocaleInit(void) { - scriptProtoInit( - &MODULE_LOCALE_PROTO, "Locale", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticFunc( - &MODULE_LOCALE_PROTO, "getText", moduleLocaleGetText - ); - scriptProtoDefineStaticFunc( - &MODULE_LOCALE_PROTO, "setLocale", moduleLocaleSetLocale - ); -} - -void moduleLocaleDispose(void) { - scriptProtoDispose(&MODULE_LOCALE_PROTO); -} diff --git a/src/dusk/script/module/locale/modulelocale.h b/src/dusk/script/module/locale/modulelocale.h deleted file mode 100644 index 1504dcf2..00000000 --- a/src/dusk/script/module/locale/modulelocale.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "locale/localemanager.h" - -extern scriptproto_t MODULE_LOCALE_PROTO; - -/** - * Locale.getText(id[, pluralCount[, arg1, ...]]) - returns the - * translated string for the given message ID. - * - * @param args[0] Message ID string. - * @param args[1] Optional plural count (number). Defaults to 1. - * @param args[2..5] Optional substitution args (string or number). - */ -moduleBaseFunction(moduleLocaleGetText); - -/** - * Locale.setLocale(name) - switches the active locale by name. - * - * @param args[0] Locale name string, e.g. "en-US". - */ -moduleBaseFunction(moduleLocaleSetLocale); - -/** - * Initializes the Locale module and registers Locale.getText/setLocale. - */ -void moduleLocaleInit(void); - -/** - * Disposes the Locale module. - */ -void moduleLocaleDispose(void); diff --git a/src/dusk/script/module/math/CMakeLists.txt b/src/dusk/script/module/math/CMakeLists.txt deleted file mode 100644 index da510807..00000000 --- a/src/dusk/script/module/math/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulevec3.c -) diff --git a/src/dusk/script/module/math/modulevec3.c b/src/dusk/script/module/math/modulevec3.c deleted file mode 100644 index 1e0090b1..00000000 --- a/src/dusk/script/module/math/modulevec3.c +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulevec3.h" - -scriptproto_t MODULE_VEC3_PROTO; - -float_t *moduleVec3Get(const jerry_call_info_t *callInfo) { - return (float_t *)scriptProtoGetValue( - &MODULE_VEC3_PROTO, callInfo->this_value - ); -} - -float_t *moduleVec3From(const jerry_value_t val) { - return (float_t *)scriptProtoGetValue(&MODULE_VEC3_PROTO, val); -} - -jerry_value_t moduleVec3Push(const vec3 v) { - return scriptProtoCreateValue(&MODULE_VEC3_PROTO, v); -} - -moduleBaseFunction(moduleVec3Constructor) { - float_t *ptr = (float_t *)memoryAllocate(sizeof(vec3)); - ptr[0] = moduleBaseOptFloat(0, 0.0f); - ptr[1] = moduleBaseOptFloat(1, 0.0f); - ptr[2] = moduleBaseOptFloat(2, 0.0f); - jerry_object_set_native_ptr( - callInfo->this_value, &MODULE_VEC3_PROTO.info, ptr - ); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetX) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[0]); -} - -moduleBaseFunction(moduleVec3SetX) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[0] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetY) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[1]); -} - -moduleBaseFunction(moduleVec3SetY) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[1] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3GetZ) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - return jerry_number((double)v[2]); -} - -moduleBaseFunction(moduleVec3SetZ) { - moduleBaseRequireArgs(1); - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_undefined(); - v[2] = moduleBaseArgFloat(0); - return jerry_undefined(); -} - -moduleBaseFunction(moduleVec3ToString) { - float_t *v = moduleVec3Get(callInfo); - if(!v) return jerry_string_sz("Vec3:invalid"); - char_t buf[64]; - snprintf(buf, sizeof(buf), "Vec3(%g, %g, %g)", - (double)v[0], (double)v[1], (double)v[2] - ); - return jerry_string_sz(buf); -} - -void moduleVec3Init(void) { - scriptProtoInit( - &MODULE_VEC3_PROTO, "Vec3", - sizeof(vec3), moduleVec3Constructor - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "x", moduleVec3GetX, moduleVec3SetX - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "y", moduleVec3GetY, moduleVec3SetY - ); - scriptProtoDefineProp( - &MODULE_VEC3_PROTO, "z", moduleVec3GetZ, moduleVec3SetZ - ); - scriptProtoDefineToString(&MODULE_VEC3_PROTO, moduleVec3ToString); -} - -void moduleVec3Dispose(void) { - scriptProtoDispose(&MODULE_VEC3_PROTO); -} diff --git a/src/dusk/script/module/math/modulevec3.h b/src/dusk/script/module/math/modulevec3.h deleted file mode 100644 index cc267bfb..00000000 --- a/src/dusk/script/module/math/modulevec3.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "util/memory.h" -#include "cglm/cglm.h" - -extern scriptproto_t MODULE_VEC3_PROTO; - -/** - * Returns the raw float[3] pointer from a Vec3 JS instance. - * - * @param callInfo Call info whose this_value is the Vec3 instance. - * @return Pointer to the vec3 data, or NULL if not a Vec3. - */ -float_t *moduleVec3Get(const jerry_call_info_t *callInfo); - -/** - * Returns the raw float[3] pointer from an arbitrary Vec3 JS value. - * - * @param val The JS value to extract from. - * @return Pointer to the vec3 data, or NULL if not a Vec3. - */ -float_t *moduleVec3From(const jerry_value_t val); - -/** - * Creates a Vec3 JS object wrapping a copy of a C vec3. - * - * @param v The source vec3. - * @return A new Vec3 JS object. - */ -jerry_value_t moduleVec3Push(const vec3 v); - -/** Vec3(x?, y?, z?) constructor. */ -moduleBaseFunction(moduleVec3Constructor); - -/** @return The x component as a number. */ -moduleBaseFunction(moduleVec3GetX); -/** Sets the x component. @param args[0] New x value. */ -moduleBaseFunction(moduleVec3SetX); - -/** @return The y component as a number. */ -moduleBaseFunction(moduleVec3GetY); -/** Sets the y component. @param args[0] New y value. */ -moduleBaseFunction(moduleVec3SetY); - -/** @return The z component as a number. */ -moduleBaseFunction(moduleVec3GetZ); -/** Sets the z component. @param args[0] New z value. */ -moduleBaseFunction(moduleVec3SetZ); - -/** @return "Vec3(x, y, z)" string. */ -moduleBaseFunction(moduleVec3ToString); - -/** - * Initializes the Vec3 module and registers the global Vec3 class. - */ -void moduleVec3Init(void); - -/** - * Disposes the Vec3 module. - */ -void moduleVec3Dispose(void); diff --git a/src/dusk/script/module/modulebase.c b/src/dusk/script/module/modulebase.c deleted file mode 100644 index c3797f6d..00000000 --- a/src/dusk/script/module/modulebase.c +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulebase.h" -#include "util/memory.h" - -jerry_object_native_info_t MODULE_BASE_NATIVE_INFO = { - .free_cb = moduleBaseFreeProto, - .number_of_references = 0, - .offset_of_references = 0 -}; - -void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info) { - assertNotNull(ptr, "Pointer must not be null"); - assertNotNull(info, "Native info must not be null"); - memoryFree(ptr); -} - -jerry_value_t moduleBaseThrow(const char_t *message) { - assertStrLenMin(message, 1, "Error message must not be empty"); - return jerry_throw_sz(JERRY_ERROR_TYPE, message); -} - -jerry_value_t moduleBaseThrowError(const errorret_t err) { - assertNotNull(err.state, "Error state must not be NULL"); - assertNotNull(err.state->message, "Error message must not be NULL"); - jerry_value_t jsErr = jerry_throw_sz(JERRY_ERROR_TYPE, err.state->message); - errorCatch(err); - return jsErr; -} - -void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen) { - jerry_size_t len = jerry_string_to_buffer( - val, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, buflen - 1 - ); - buf[len] = '\0'; -} - -void moduleBaseExceptionMessage( - jerry_value_t exception, - char_t *buf, - size_t buflen -) { - jerry_value_t errVal = jerry_exception_value(exception, false); - jerry_value_t errStr = jerry_value_to_string(errVal); - jerry_size_t len = jerry_string_to_buffer( - errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, (jerry_size_t)(buflen - 1) - ); - buf[len] = '\0'; - jerry_value_free(errStr); - jerry_value_free(errVal); -} - -void moduleBaseDefineMethod( - jerry_value_t obj, - const char_t *name, - jerry_external_handler_t fn -) { - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(obj, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -void moduleBaseDefineGlobalMethod( - const char_t *name, - jerry_external_handler_t fn -) { - jerry_value_t global = jerry_current_realm(); - moduleBaseDefineMethod(global, name, fn); - jerry_value_free(global); -} - -void moduleBaseSetValue(const char_t *name, jerry_value_t value) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_object_set(global, key, value); - jerry_value_free(key); - jerry_value_free(global); -} - -jerry_value_t moduleBaseWrapPointer(void *ptr) { - jerry_value_t obj = jerry_object(); - jerry_object_set_native_ptr(obj, &MODULE_BASE_NATIVE_INFO, ptr); - return obj; -} - -void *moduleBaseUnwrapPointer(jerry_value_t val) { - if(!jerry_value_is_object(val)) return NULL; - return jerry_object_get_native_ptr(val, &MODULE_BASE_NATIVE_INFO); -} - -jerry_value_t moduleBaseGetProp( - jerry_value_t obj, const char_t *name -) { - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = jerry_object_get(obj, key); - jerry_value_free(key); - return val; -} - -float_t moduleBaseValueFloat(jerry_value_t val) { - return (float_t)jerry_value_as_number(val); -} - -int32_t moduleBaseValueInt(jerry_value_t val) { - return (int32_t)jerry_value_as_number(val); -} - -void moduleBaseSetNumber(const char_t *name, double value) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = jerry_number(value); - jerry_object_set(global, key, val); - jerry_value_free(val); - jerry_value_free(key); - jerry_value_free(global); -} - -void moduleBaseSetInt(const char_t *name, int32_t value) { - moduleBaseSetNumber(name, (double)value); -} \ No newline at end of file diff --git a/src/dusk/script/module/modulebase.h b/src/dusk/script/module/modulebase.h deleted file mode 100644 index 71e36f11..00000000 --- a/src/dusk/script/module/modulebase.h +++ /dev/null @@ -1,190 +0,0 @@ -/** - * 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 "assert/assert.h" -#include "util/string.h" -#include -#include - -extern jerry_object_native_info_t MODULE_BASE_NATIVE_INFO; - -/** - * Frees a native pointer associated with a JavaScript object, uses the internal - * memory functions. - * - * @param ptr The native pointer to free. Must not be NULL. - * @param info The native info associated with the pointer. Must not be NULL. - */ -void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info); - -/** - * Throws a JavaScript error with the given message. - * - * @param message The error message to throw. Must not be empty. - * @return A jerry_value_t representing the thrown error. - */ -jerry_value_t moduleBaseThrow(const char_t *message); - -/** - * "Converts" a dusk error to a jerry type error. - * - * @param err Native dusk error to convert. - * @return A jerry_value_t representing the thrown error. - */ -jerry_value_t moduleBaseThrowError(const errorret_t err); - -/** - * Converts a jerry value to a string and writes it to the provided buffer. - * - * @param val The jerry value to convert. - * @param buf The buffer to write the string to. - * @param buflen The length of the buffer. - */ -void moduleBaseToString(jerry_value_t val, char_t *buf, jerry_size_t buflen); - -/** - * Converts a jerry exception value to a string and writes it to the provided - * buffer. - * - * @param exception The jerry exception value to convert. - * @param buf The buffer to write the string to. - * @param buflen The length of the buffer. - */ -void moduleBaseExceptionMessage( - jerry_value_t exception, - char_t *buf, - size_t buflen -); - -/** - * Defines a method on a given object. - * - * @param obj The object to define the method on. - * @param name The name of the method. - * @param fn The function pointer to the method implementation. - */ -void moduleBaseDefineMethod( - jerry_value_t obj, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Defines a global method. - * - * @param name The name of the method. - * @param fn The function pointer to the method implementation. - */ -void moduleBaseDefineGlobalMethod( - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Sets a global variable to a given jerry value. - * - * @param name The name of the global variable. - * @param value The jerry value to set the variable to. - */ -void moduleBaseSetValue(const char_t *name, jerry_value_t value); - -/** - * Wraps a native pointer in a jerry object. - * - * @param ptr The native pointer to wrap. - * @return A jerry_value_t representing the wrapped pointer. - */ -jerry_value_t moduleBaseWrapPointer(void *ptr); - -/** - * Unwraps a native pointer from a jerry object. - * - * @param val The jerry value to unwrap the pointer from. - * @return The unwrapped native pointer. - */ -void *moduleBaseUnwrapPointer(jerry_value_t val); - -/** - * Gets a property from a jerry object as a jerry value. - * - * @param obj The object to get the property from. - * @param name The name of the property to get. - * @return The value of the property. - */ -jerry_value_t moduleBaseGetProp( - jerry_value_t obj, const char_t *name -); - -/** - * Converts a jerry value to a float. - * - * @param val The jerry value to convert. - * @return The converted float value. - */ -float_t moduleBaseValueFloat(jerry_value_t val); - -/** - * Converts a jerry value to an int. - * - * @param val The jerry value to convert. - * @return The converted int value. - */ -int32_t moduleBaseValueInt(jerry_value_t val); - -/** - * Sets a global variable to a given number value. - * - * @param name The name of the global variable. - * @param value The number value to set the variable to. - */ -void moduleBaseSetNumber(const char_t *name, double value); - -/** - * Sets a global variable to a given int value. - * - * @param name The name of the global variable. - * @param value The int value to set the variable to. - */ -void moduleBaseSetInt(const char_t *name, int32_t value); - -#define moduleBaseFunction(name) \ - jerry_value_t name( \ - const jerry_call_info_t *callInfo, \ - const jerry_value_t args[], \ - const jerry_length_t argc) - -#define moduleBaseRequireArgs(n) do { \ - if(argc < (jerry_length_t)(n)) { \ - return moduleBaseThrow("Expected at least " #n " argument(s)"); \ - } \ -} while(0) - -#define moduleBaseRequireNumber(i) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow("Expected number argument"); \ - } \ -} while(0) - -#define moduleBaseRequireString(i) do { \ - if(!jerry_value_is_string(args[(i)])) { \ - return moduleBaseThrow("Expected string argument"); \ - } \ -} while(0) - -#define moduleBaseArgFloat(i) ((float_t)jerry_value_as_number(args[(i)])) -#define moduleBaseArgInt(i) ((int32_t)jerry_value_as_number(args[(i)])) -#define moduleBaseArgBool(i) (jerry_value_is_true(args[(i)])) - -#define moduleBaseOptFloat(i, def) \ - ((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \ - ? (float_t)jerry_value_as_number(args[(i)]) : (def)) - -#define moduleBaseOptInt(i, def) \ - ((jerry_length_t)(i) < argc && jerry_value_is_number(args[(i)]) \ - ? (int32_t)jerry_value_as_number(args[(i)]) : (def)) diff --git a/src/dusk/script/module/modulelist.c b/src/dusk/script/module/modulelist.c deleted file mode 100644 index 1a686172..00000000 --- a/src/dusk/script/module/modulelist.c +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulelist.h" -#include "script/module/animation/moduleanimation.h" -#include "script/module/animation/moduleeasing.h" -#include "script/module/asset/moduleasset.h" -#include "script/module/console/moduleconsole.h" -#include "script/module/display/modulecolor.h" -#include "script/module/display/modulescreen.h" -#include "script/module/engine/moduleengine.h" -#include "script/module/engine/moduleframe.h" -#include "script/module/event/moduleevent.h" -#include "script/module/engine/moduletimeout.h" -#include "script/module/entity/moduleentity.h" -#include "script/module/input/moduleinput.h" -#include "script/module/item/moduleitem.h" -#include "script/module/item/modulebackpack.h" -#include "script/module/locale/modulelocale.h" -#include "script/module/save/modulesave.h" -#include "script/module/math/modulevec3.h" -#include "script/module/overworld/moduleoverworld.h" -#include "script/module/require/modulerequire.h" -#include "script/module/scene/modulescene.h" -#include "script/module/story/modulestory.h" -#include "script/module/system/modulesystem.h" -#include "script/module/ui/modulefullbox.h" -#include "script/module/ui/moduletextbox.h" - - -void moduleListInit(void) { - moduleAnimationInit(); - moduleEasingInit(); - moduleEventInit(); - moduleTextureInit(); - moduleColorInit(); - moduleAssetInit(); - moduleConsoleInit(); - moduleScreenInit(); - moduleEngineInit(); - moduleFrameInit(); - moduleTimeoutInit(); - moduleVec3Init(); - moduleEntityInit(); - moduleInputInit(); - moduleItemInit(); - moduleBackpackInit(); - moduleLocaleInit(); - moduleSaveInit(); - moduleOverworldInit(); - moduleRequireInit(); - moduleSceneInit(); - moduleStoryInit(); - moduleSystemInit(); - moduleFullboxInit(); - moduleTextboxInit(); -} - -void moduleListUpdate(void) { - moduleFrameFlush(); - moduleTimeoutFlush(); -} - -void moduleListDispose(void) { - moduleFullboxDispose(); - moduleTextboxDispose(); - moduleSystemDispose(); - moduleOverworldDispose(); - moduleEasingDispose(); - moduleAnimationDispose(); - moduleStoryDispose(); - moduleSceneDispose(); - moduleRequireDispose(); - moduleSaveDispose(); - moduleLocaleDispose(); - moduleBackpackDispose(); - moduleItemDispose(); - moduleInputDispose(); - moduleEntityDispose(); - moduleVec3Dispose(); - moduleTimeoutDispose(); - moduleFrameDispose(); - moduleEngineDispose(); - moduleScreenDispose(); - moduleConsoleDispose(); - moduleAssetDispose(); - moduleColorDispose(); - moduleTextureDispose(); - moduleEventDispose(); -} diff --git a/src/dusk/script/module/modulelist.h b/src/dusk/script/module/modulelist.h deleted file mode 100644 index 71fd3b9c..00000000 --- a/src/dusk/script/module/modulelist.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" - -/** - * Initializes all of the internal (C) script modules. - */ -void moduleListInit(void); - -/** - * Flushes per-frame module state. Must be called once per frame before - * jerry_run_jobs() so that frame() promises resolve in the same tick. - */ -void moduleListUpdate(void); - -/** - * Disposes all of the internal (C) script modules. - */ -void moduleListDispose(void); \ No newline at end of file diff --git a/src/dusk/script/module/overworld/CMakeLists.txt b/src/dusk/script/module/overworld/CMakeLists.txt deleted file mode 100644 index 10af18ad..00000000 --- a/src/dusk/script/module/overworld/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - moduleoverworld.c -) diff --git a/src/dusk/script/module/overworld/moduleoverworld.c b/src/dusk/script/module/overworld/moduleoverworld.c deleted file mode 100644 index 8bf23d2f..00000000 --- a/src/dusk/script/module/overworld/moduleoverworld.c +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduleoverworld.h" -#include "overworld/map.h" - -scriptproto_t MODULE_OVERWORLD_PROTO; - -moduleBaseFunction(moduleOverworldLoadMap) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t handle[MAP_HANDLE_MAX]; - moduleBaseToString(args[0], handle, (jerry_size_t)MAP_HANDLE_MAX); - errorret_t err = mapLoad(handle); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldIsLoaded) { - return jerry_boolean(mapIsLoaded()); -} - -moduleBaseFunction(moduleOverworldSetPosition) { - moduleBaseRequireArgs(3); - moduleBaseRequireNumber(0); - moduleBaseRequireNumber(1); - moduleBaseRequireNumber(2); - tilepos_t pos; - pos.x = (tileunit_t)moduleBaseArgInt(0); - pos.y = (tileunit_t)moduleBaseArgInt(1); - pos.z = (tileunit_t)moduleBaseArgInt(2); - errorret_t err = mapPositionSet(pos); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldUpdate) { - errorret_t err = mapUpdate(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleOverworldJsDispose) { - mapDispose(); - return jerry_undefined(); -} - -void moduleOverworldInit(void) { - scriptProtoInit(&MODULE_OVERWORLD_PROTO, "Overworld", 0, NULL); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "loadMap", moduleOverworldLoadMap - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "isLoaded", moduleOverworldIsLoaded - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "setPosition", moduleOverworldSetPosition - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "update", moduleOverworldUpdate - ); - scriptProtoDefineStaticFunc( - &MODULE_OVERWORLD_PROTO, "dispose", moduleOverworldJsDispose - ); -} - -void moduleOverworldDispose(void) { - scriptProtoDispose(&MODULE_OVERWORLD_PROTO); -} diff --git a/src/dusk/script/module/overworld/moduleoverworld.h b/src/dusk/script/module/overworld/moduleoverworld.h deleted file mode 100644 index 19380c5a..00000000 --- a/src/dusk/script/module/overworld/moduleoverworld.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_OVERWORLD_PROTO; - -/** - * Overworld.loadMap(handle) - loads the map with the given handle string. - * Disposes any currently loaded map first. - * - * @param args[0] Map handle string (max 31 chars). - */ -moduleBaseFunction(moduleOverworldLoadMap); - -/** - * Overworld.isLoaded() - returns true when a map is currently loaded. - * - * @return boolean - */ -moduleBaseFunction(moduleOverworldIsLoaded); - -/** - * Overworld.setPosition(x, y, z) - slides the chunk window to the tile - * position (x, y, z). - * - * @param args[0] Tile X (integer). - * @param args[1] Tile Y (integer). - * @param args[2] Tile Z (integer). - */ -moduleBaseFunction(moduleOverworldSetPosition); - -/** - * Overworld.update() - advances the map one tick. - */ -moduleBaseFunction(moduleOverworldUpdate); - -/** - * Overworld.dispose() - unloads the current map. - */ -moduleBaseFunction(moduleOverworldJsDispose); - -/** - * Initializes the Overworld module and registers the Overworld global. - */ -void moduleOverworldInit(void); - -/** - * Disposes the Overworld module. - */ -void moduleOverworldDispose(void); diff --git a/src/dusk/script/module/require/CMakeLists.txt b/src/dusk/script/module/require/CMakeLists.txt deleted file mode 100644 index c063ce82..00000000 --- a/src/dusk/script/module/require/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulerequire.c -) diff --git a/src/dusk/script/module/require/modulerequire.c b/src/dusk/script/module/require/modulerequire.c deleted file mode 100644 index 36956cd5..00000000 --- a/src/dusk/script/module/require/modulerequire.c +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulerequire.h" -#include "script/scriptpromisepend.h" -#include "util/memory.h" -#include "util/string.h" -#include "assert/assert.h" - -#define MODULE_REQUIRE_ASYNC_MAX 16 - -static scriptpromisepend_t MODULE_REQUIRE_ASYNC_PEND[MODULE_REQUIRE_ASYNC_MAX]; -static uint32_t MODULE_REQUIRE_ASYNC_PEND_COUNT = 0; - -void moduleRequireAsyncOnLoaded(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - eventUnsubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&entry->onError, moduleRequireAsyncOnError); - - jerry_value_t exports = ( - jerry_value_is_undefined(entry->data.script.exports) ? - jerry_undefined() : - jerry_value_copy(entry->data.script.exports) - ); - uint32_t n = scriptPromisePendResolve( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - entry, exports - ); - jerry_value_free(exports); - for(uint32_t i = 0; i < n; i++) assetUnlockEntry(entry); -} - -void moduleRequireAsyncOnError(void *params, void *user) { - assetentry_t *entry = (assetentry_t *)user; - eventUnsubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&entry->onError, moduleRequireAsyncOnError); - - jerry_value_t errStr = jerry_string_sz("Module load failed"); - uint32_t n = scriptPromisePendReject( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - entry, errStr - ); - jerry_value_free(errStr); - for(uint32_t i = 0; i < n; i++) assetUnlockEntry(entry); -} - -jerry_value_t moduleRequireFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -) { - assertNotNull(callInfo, "callInfo must not be null."); - assertNotNull(args, "args must not be null."); - - if(argc < 1 || !jerry_value_is_string(args[0])) { - return moduleBaseThrow("Expected a string argument for module name."); - } - if(jerry_string_size(args[0], JERRY_ENCODING_UTF8) >= ASSET_FILE_NAME_MAX) { - return moduleBaseThrow("Module name too long."); - } - - char_t moduleName[ASSET_FILE_NAME_MAX]; - moduleBaseToString(args[0], moduleName, sizeof(moduleName)); - - assetloaderinput_t input; - input.script.nothing = NULL; - assetentry_t *entry = assetLock( - moduleName, ASSET_LOADER_TYPE_SCRIPT, &input - ); - - errorret_t err = assetRequireLoaded(entry); - if(errorIsNotOk(err)) { - assetUnlockEntry(entry); - return moduleBaseThrowError(err); - } - - if(jerry_value_is_undefined(entry->data.script.exports)) { - assetUnlockEntry(entry); - return jerry_undefined(); - } - - jerry_value_t exportsCopy = jerry_value_copy(entry->data.script.exports); - assetUnlockEntry(entry); - return exportsCopy; -} - -jerry_value_t moduleRequireAsyncFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -) { - assertNotNull(callInfo, "callInfo must not be null."); - assertNotNull(args, "args must not be null."); - - if(argc < 1 || !jerry_value_is_string(args[0])) { - return moduleBaseThrow("requireAsync expects a filename string."); - } - if( - jerry_string_size(args[0], JERRY_ENCODING_UTF8) >= ASSET_FILE_NAME_MAX - ) { - return moduleBaseThrow("Module name too long."); - } - if(MODULE_REQUIRE_ASYNC_PEND_COUNT >= MODULE_REQUIRE_ASYNC_MAX) { - return moduleBaseThrow("Too many pending requireAsync calls."); - } - - char_t moduleName[ASSET_FILE_NAME_MAX]; - moduleBaseToString(args[0], moduleName, sizeof(moduleName)); - - assetloaderinput_t input; - input.script.nothing = NULL; - assetentry_t *entry = assetLock( - moduleName, ASSET_LOADER_TYPE_SCRIPT, &input - ); - - jerry_value_t promise = jerry_promise(); - - if(entry->state == ASSET_ENTRY_STATE_LOADED) { - jerry_value_t exports = ( - jerry_value_is_undefined(entry->data.script.exports) ? - jerry_undefined() : - jerry_value_copy(entry->data.script.exports) - ); - assetUnlockEntry(entry); - jerry_value_t ret = jerry_promise_resolve(promise, exports); - jerry_value_free(ret); - jerry_value_free(exports); - return promise; - } - - if(entry->state == ASSET_ENTRY_STATE_ERROR) { - assetUnlockEntry(entry); - jerry_value_t errStr = jerry_string_sz("Module load failed"); - jerry_value_t ret = jerry_promise_reject(promise, errStr); - jerry_value_free(ret); - jerry_value_free(errStr); - return promise; - } - - if(!scriptPromisePendHas( - MODULE_REQUIRE_ASYNC_PEND, MODULE_REQUIRE_ASYNC_PEND_COUNT, entry - )) { - if(entry->onLoaded.count >= entry->onLoaded.size) { - assetUnlockEntry(entry); - jerry_value_free(promise); - return moduleBaseThrow( - "requireAsync: onLoaded event capacity exceeded." - ); - } - if(entry->onError.count >= entry->onError.size) { - assetUnlockEntry(entry); - jerry_value_free(promise); - return moduleBaseThrow( - "requireAsync: onError event capacity exceeded." - ); - } - eventSubscribe(&entry->onLoaded, moduleRequireAsyncOnLoaded, entry); - eventSubscribe(&entry->onError, moduleRequireAsyncOnError, entry); - } - - scriptPromisePendAdd( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT, - MODULE_REQUIRE_ASYNC_MAX, entry, promise - ); - return promise; -} - -void moduleRequireInit(void) { - MODULE_REQUIRE_ASYNC_PEND_COUNT = 0; - moduleBaseDefineGlobalMethod("require", moduleRequireFunc); - moduleBaseDefineGlobalMethod("requireAsync", moduleRequireAsyncFunc); -} - -void moduleRequireDispose(void) { - for(uint32_t i = 0; i < MODULE_REQUIRE_ASYNC_PEND_COUNT; i++) { - assetentry_t *e = (assetentry_t *)MODULE_REQUIRE_ASYNC_PEND[i].key; - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_REQUIRE_ASYNC_PEND[j].key == e) { - seen = true; break; - } - } - if(!seen) { - eventUnsubscribe(&e->onLoaded, moduleRequireAsyncOnLoaded); - eventUnsubscribe(&e->onError, moduleRequireAsyncOnError); - } - assetUnlockEntry(e); - } - scriptPromisePendFreeAll( - MODULE_REQUIRE_ASYNC_PEND, &MODULE_REQUIRE_ASYNC_PEND_COUNT - ); -} diff --git a/src/dusk/script/module/require/modulerequire.h b/src/dusk/script/module/require/modulerequire.h deleted file mode 100644 index 4bf5a576..00000000 --- a/src/dusk/script/module/require/modulerequire.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "asset/asset.h" - -/** - * Success loaded callback for async module loading. - * - * @param params The asset entry that finished loading. - * @param user Unused. - */ -void moduleRequireAsyncOnLoaded(void *params, void *user); - -/** - * Error callback for when an async loaded module fails. - * - * @param params The asset entry that failed to load. - * @param user Unused. - */ -void moduleRequireAsyncOnError(void *params, void *user); - -/** - * Synchronous require function exposed to scripts. - * - * @param callInfo JerryScript call info. - * @param args The arguments passed to the function. - * @param argc The number of arguments passed. - * @return The exports of the required module. - */ -jerry_value_t moduleRequireFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -); - -/** - * Asynchronous require function exposed to scripts. - * - * @param callInfo JerryScript call info. - * @param args The arguments passed to the function. - * @param argc The number of arguments passed. - * @return A promise that resolves with the exports of the required module. - */ -jerry_value_t moduleRequireAsyncFunc( - const jerry_call_info_t *callInfo, - const jerry_value_t args[], - const jerry_length_t argc -); - -/** - * Initializes the require module. - */ -void moduleRequireInit(void); - -/** - * Disposes the require module. - */ -void moduleRequireDispose(void); \ No newline at end of file diff --git a/src/dusk/script/module/save/CMakeLists.txt b/src/dusk/script/module/save/CMakeLists.txt deleted file mode 100644 index 9121bce6..00000000 --- a/src/dusk/script/module/save/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulesave.c -) diff --git a/src/dusk/script/module/save/modulesave.c b/src/dusk/script/module/save/modulesave.c deleted file mode 100644 index 4ab0e8ca..00000000 --- a/src/dusk/script/module/save/modulesave.c +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulesave.h" - -scriptproto_t MODULE_SAVE_PROTO; - -moduleBaseFunction(moduleSaveExists) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.exists"); - return jerry_boolean(saveExists((uint8_t)moduleBaseArgInt(0))); -} - -moduleBaseFunction(moduleSaveLoad) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.load"); - errorret_t err = saveLoad((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleSaveWrite) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.write"); - errorret_t err = saveWrite((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleSaveDelete) { - moduleBaseRequireArgs(1); - moduleSaveRequireSlot(0, "Save.delete"); - errorret_t err = saveDelete((uint8_t)moduleBaseArgInt(0)); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -void moduleSaveInit(void) { - scriptProtoInit(&MODULE_SAVE_PROTO, "Save", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "exists", moduleSaveExists - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "load", moduleSaveLoad - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "write", moduleSaveWrite - ); - scriptProtoDefineStaticFunc( - &MODULE_SAVE_PROTO, "delete", moduleSaveDelete - ); -} - -void moduleSaveDispose(void) { - scriptProtoDispose(&MODULE_SAVE_PROTO); -} diff --git a/src/dusk/script/module/save/modulesave.h b/src/dusk/script/module/save/modulesave.h deleted file mode 100644 index 56d913d3..00000000 --- a/src/dusk/script/module/save/modulesave.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "save/save.h" - -extern scriptproto_t MODULE_SAVE_PROTO; - -/** - * Validates a save-slot argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleSaveRequireSlot(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": slot must be a number"); \ - } \ - const int32_t _s = moduleBaseArgInt(i); \ - if(_s < 0 || _s >= (int32_t)SAVE_FILE_COUNT_MAX) { \ - return moduleBaseThrow(ctx ": invalid save slot"); \ - } \ -} while(0) - -/** - * Save.exists(slot) - true if a save file is present for the slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveExists); - -/** - * Save.load(slot) - loads the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveLoad); - -/** - * Save.write(slot) - writes the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveWrite); - -/** - * Save.delete(slot) - deletes the save file for the given slot. - * - * @param args[0] Save slot index (0 to SAVE_FILE_COUNT_MAX - 1). - */ -moduleBaseFunction(moduleSaveDelete); - -/** - * Initializes the Save module and registers Save.exists/load/write/delete. - */ -void moduleSaveInit(void); - -/** - * Disposes the Save module. - */ -void moduleSaveDispose(void); diff --git a/src/dusk/script/module/scene/CMakeLists.txt b/src/dusk/script/module/scene/CMakeLists.txt deleted file mode 100644 index 4ef416e3..00000000 --- a/src/dusk/script/module/scene/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulescene.c -) diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c deleted file mode 100644 index bfe70383..00000000 --- a/src/dusk/script/module/scene/modulescene.c +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulescene.h" - -scriptproto_t MODULE_SCENE_PROTO; - -void moduleSceneInit(void) { - scriptProtoInit(&MODULE_SCENE_PROTO, "Scene", sizeof(uint8_t), NULL); -} - -void moduleSceneDispose(void) { - scriptProtoDispose(&MODULE_SCENE_PROTO); -} diff --git a/src/dusk/script/module/scene/modulescene.h b/src/dusk/script/module/scene/modulescene.h deleted file mode 100644 index 898a3c20..00000000 --- a/src/dusk/script/module/scene/modulescene.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/module/asset/moduleassetbatch.h" -#include "script/scriptproto.h" -#include "scene/scene.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "util/memory.h" - -extern scriptproto_t MODULE_SCENE_PROTO; - -/** - * Initializes the Scene module and registers the global Scene object. - */ -void moduleSceneInit(void); - -/** - * Disposes the Scene module. - */ -void moduleSceneDispose(void); diff --git a/src/dusk/script/module/story/CMakeLists.txt b/src/dusk/script/module/story/CMakeLists.txt deleted file mode 100644 index 50da105d..00000000 --- a/src/dusk/script/module/story/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulestory.c -) diff --git a/src/dusk/script/module/story/modulestory.c b/src/dusk/script/module/story/modulestory.c deleted file mode 100644 index c87d1371..00000000 --- a/src/dusk/script/module/story/modulestory.c +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulestory.h" -#include - -scriptproto_t MODULE_STORY_PROTO; - -moduleBaseFunction(moduleStoryGet) { - moduleBaseRequireArgs(1); - moduleStoryRequireFlag(0, "Story.get"); - const storyflag_t flag = (storyflag_t)moduleBaseArgInt(0); - return jerry_number((double)storyFlagGet(flag)); -} - -moduleBaseFunction(moduleStorySet) { - moduleBaseRequireArgs(2); - moduleStoryRequireFlag(0, "Story.set"); - moduleBaseRequireNumber(1); - const int32_t val = moduleBaseArgInt(1); - if(val < 0 || val > 255) { - return moduleBaseThrow("Story.set: value out of range (0-255)"); - } - storyFlagSet( - (storyflag_t)moduleBaseArgInt(0), - (storyflagvalue_t)val - ); - return jerry_undefined(); -} - -void moduleStoryInit(void) { - scriptProtoInit(&MODULE_STORY_PROTO, "Story", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticFunc( - &MODULE_STORY_PROTO, "get", moduleStoryGet - ); - scriptProtoDefineStaticFunc( - &MODULE_STORY_PROTO, "set", moduleStorySet - ); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)STORY_FLAG_SCRIPT, - strlen(STORY_FLAG_SCRIPT), - JERRY_PARSE_NO_OPTS - ); - jerry_value_free(result); -} - -void moduleStoryDispose(void) { - scriptProtoDispose(&MODULE_STORY_PROTO); -} diff --git a/src/dusk/script/module/story/modulestory.h b/src/dusk/script/module/story/modulestory.h deleted file mode 100644 index d5ab6a5b..00000000 --- a/src/dusk/script/module/story/modulestory.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "story/storyflag.h" - -extern scriptproto_t MODULE_STORY_PROTO; - -/** - * Validates a storyflag_t argument and returns a type error if invalid. - * - * @param i Argument index. - * @param ctx Context string for error messages. - */ -#define moduleStoryRequireFlag(i, ctx) do { \ - if(!jerry_value_is_number(args[(i)])) { \ - return moduleBaseThrow(ctx ": flagId must be a number"); \ - } \ - const storyflag_t _f = (storyflag_t)moduleBaseArgInt(i); \ - if(_f <= STORY_FLAG_NULL || _f >= STORY_FLAG_COUNT) { \ - return moduleBaseThrow(ctx ": invalid story flag ID"); \ - } \ -} while(0) - -/** - * Story.get(flagId) - returns the current uint8 value of a story flag. - * - * @param args[0] Story flag constant (STORY_FLAG_*). - */ -moduleBaseFunction(moduleStoryGet); - -/** - * Story.set(flagId, value) - sets a story flag to a uint8 value. - * - * @param args[0] Story flag constant (STORY_FLAG_*). - * @param args[1] Value 0-255. - */ -moduleBaseFunction(moduleStorySet); - -/** - * Initializes the Story module, registers Story.get/set, and evaluates - * STORY_FLAG_SCRIPT to inject STORY_FLAG_* constants as globals. - */ -void moduleStoryInit(void); - -/** - * Disposes the Story module. - */ -void moduleStoryDispose(void); diff --git a/src/dusk/script/module/system/CMakeLists.txt b/src/dusk/script/module/system/CMakeLists.txt deleted file mode 100644 index b7f82eb8..00000000 --- a/src/dusk/script/module/system/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# 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 - modulesystem.c -) diff --git a/src/dusk/script/module/system/modulesystem.c b/src/dusk/script/module/system/modulesystem.c deleted file mode 100644 index 356c7aea..00000000 --- a/src/dusk/script/module/system/modulesystem.c +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulesystem.h" - -scriptproto_t MODULE_SYSTEM_PROTO; - -moduleBaseFunction(moduleSystemGetPlatform) { - return jerry_number((double)systemGetPlatform()); -} - -void moduleSystemInit(void) { - scriptProtoInit(&MODULE_SYSTEM_PROTO, "System", sizeof(uint8_t), NULL); - - scriptProtoDefineStaticProp( - &MODULE_SYSTEM_PROTO, "platform", - moduleSystemGetPlatform, NULL - ); - - jerry_value_t target = MODULE_SYSTEM_PROTO.prototype; -#define SYSTEM_PLATFORM(name, value) \ - do { \ - jerry_value_t key = jerry_string_sz("PLATFORM_" #name); \ - jerry_value_t val = jerry_number((double)(value)); \ - jerry_object_set(target, key, val); \ - jerry_value_free(val); \ - jerry_value_free(key); \ - } while(0); - SYSTEM_PLATFORM_LIST -#undef SYSTEM_PLATFORM -} - -void moduleSystemDispose(void) { - scriptProtoDispose(&MODULE_SYSTEM_PROTO); -} diff --git a/src/dusk/script/module/system/modulesystem.h b/src/dusk/script/module/system/modulesystem.h deleted file mode 100644 index 65d9da98..00000000 --- a/src/dusk/script/module/system/modulesystem.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "system/system.h" - -extern scriptproto_t MODULE_SYSTEM_PROTO; - -/** - * @return Current platform as a System.PLATFORM_* constant. - */ -moduleBaseFunction(moduleSystemGetPlatform); - -/** - * Initializes the System module, registers the global System object with the - * platform property and all PLATFORM_* constants. - */ -void moduleSystemInit(void); - -/** - * Disposes the System module. - */ -void moduleSystemDispose(void); diff --git a/src/dusk/script/module/ui/CMakeLists.txt b/src/dusk/script/module/ui/CMakeLists.txt deleted file mode 100644 index d2884254..00000000 --- a/src/dusk/script/module/ui/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# 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 - modulefullbox.c - moduletextbox.c -) diff --git a/src/dusk/script/module/ui/modulefullbox.c b/src/dusk/script/module/ui/modulefullbox.c deleted file mode 100644 index 85d15bd1..00000000 --- a/src/dusk/script/module/ui/modulefullbox.c +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "modulefullbox.h" -#include "script/scriptpromisepend.h" -#include "script/module/display/modulecolor.h" -#include "animation/easing.h" - -#define MODULE_FULLBOX_PEND_MAX 8 - -typedef struct { - uifullbox_t *fullbox; -} jsuifullbox_t; - -scriptproto_t MODULE_FULLBOX_PROTO; - -static scriptpromisepend_t MODULE_FULLBOX_PEND[MODULE_FULLBOX_PEND_MAX]; -static uint32_t MODULE_FULLBOX_PEND_COUNT = 0; - -moduleBaseFunction(moduleFullboxSetColor) { - moduleBaseRequireArgs(1); - jsuifullbox_t *d = (jsuifullbox_t *)scriptProtoGetValue( - &MODULE_FULLBOX_PROTO, callInfo->this_value - ); - if(!d || !d->fullbox) return jerry_undefined(); - color_t *c = moduleColorFrom(args[0]); - if(!c) return jerry_undefined(); - moduleFullboxTransitionFire(NULL, d->fullbox); - uiFullboxTransition(d->fullbox, *c, *c, 0.0f, EASING_LINEAR); - jerry_value_t promise = jerry_promise(); - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; -} - -moduleBaseFunction(moduleFullboxTransition) { - moduleBaseRequireArgs(3); - jsuifullbox_t *d = (jsuifullbox_t *)scriptProtoGetValue( - &MODULE_FULLBOX_PROTO, callInfo->this_value - ); - if(!d || !d->fullbox) return jerry_undefined(); - color_t *from = moduleColorFrom(args[0]); - color_t *to = moduleColorFrom(args[1]); - if(!from || !to) return jerry_undefined(); - float_t duration = moduleBaseArgFloat(2); - easingtype_t easing = (easingtype_t)moduleBaseOptInt( - 3, (int32_t)EASING_LINEAR - ); - moduleFullboxTransitionFire(NULL, d->fullbox); - uiFullboxTransition(d->fullbox, *from, *to, duration, easing); - jerry_value_t promise = jerry_promise(); - if(duration <= 0.0f) { - jerry_value_t undef = jerry_undefined(); - jerry_value_t r = jerry_promise_resolve(promise, undef); - jerry_value_free(undef); - jerry_value_free(r); - return promise; - } - if(MODULE_FULLBOX_PEND_COUNT >= MODULE_FULLBOX_PEND_MAX) { - jerry_value_t errStr = jerry_string_sz( - "UIFullbox.transition: too many pending" - ); - jerry_value_t r = jerry_promise_reject(promise, errStr); - jerry_value_free(errStr); - jerry_value_free(r); - return promise; - } - if(!scriptPromisePendHas( - MODULE_FULLBOX_PEND, MODULE_FULLBOX_PEND_COUNT, d->fullbox - )) { - eventSubscribe( - &d->fullbox->onTransitionEnd, - moduleFullboxTransitionFire, - d->fullbox - ); - } - scriptPromisePendAdd( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT, - MODULE_FULLBOX_PEND_MAX, d->fullbox, promise - ); - return promise; -} - -void moduleFullboxTransitionFire(void *params, void *user) { - uifullbox_t *fullbox = (uifullbox_t *)user; - jerry_value_t undef = jerry_undefined(); - uint32_t n = scriptPromisePendResolve( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT, fullbox, undef - ); - jerry_value_free(undef); - if(!n) return; - eventUnsubscribe( - &fullbox->onTransitionEnd, moduleFullboxTransitionFire - ); -} - -void moduleFullboxInit(void) { - MODULE_FULLBOX_PEND_COUNT = 0; - scriptProtoInit( - &MODULE_FULLBOX_PROTO, NULL, sizeof(jsuifullbox_t), NULL - ); - - scriptProtoDefineFunc( - &MODULE_FULLBOX_PROTO, "setColor", moduleFullboxSetColor - ); - scriptProtoDefineFunc( - &MODULE_FULLBOX_PROTO, "transition", moduleFullboxTransition - ); - - jsuifullbox_t over = { .fullbox = &UI_FULLBOX_OVER }; - jerry_value_t overVal = scriptProtoCreateValue( - &MODULE_FULLBOX_PROTO, &over - ); - moduleBaseSetValue("UIFullboxOver", overVal); - jerry_value_free(overVal); - - jsuifullbox_t under = { .fullbox = &UI_FULLBOX_UNDER }; - jerry_value_t underVal = scriptProtoCreateValue( - &MODULE_FULLBOX_PROTO, &under - ); - moduleBaseSetValue("UIFullboxUnder", underVal); - jerry_value_free(underVal); -} - -void moduleFullboxDispose(void) { - for(uint32_t i = 0; i < MODULE_FULLBOX_PEND_COUNT; i++) { - bool_t seen = false; - for(uint32_t j = 0; j < i; j++) { - if(MODULE_FULLBOX_PEND[j].key == MODULE_FULLBOX_PEND[i].key) { - seen = true; break; - } - } - if(!seen) { - uifullbox_t *fb = (uifullbox_t *)MODULE_FULLBOX_PEND[i].key; - eventUnsubscribe( - &fb->onTransitionEnd, moduleFullboxTransitionFire - ); - } - } - scriptPromisePendFreeAll( - MODULE_FULLBOX_PEND, &MODULE_FULLBOX_PEND_COUNT - ); - scriptProtoDispose(&MODULE_FULLBOX_PROTO); -} diff --git a/src/dusk/script/module/ui/modulefullbox.h b/src/dusk/script/module/ui/modulefullbox.h deleted file mode 100644 index dbd320c9..00000000 --- a/src/dusk/script/module/ui/modulefullbox.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" -#include "ui/uifullbox.h" - -extern scriptproto_t MODULE_FULLBOX_PROTO; - -/** - * Event trampoline that resolves all pending transition promises for a - * fullbox and unsubscribes from onTransitionEnd. Also called directly - * to flush promises when a new transition interrupts a running one. - * - * @param params Unused (receives the uifullbox_t from eventInvoke). - * @param user The uifullbox_t pointer whose transition completed. - */ -void moduleFullboxTransitionFire(void *params, void *user); - -/** - * setColor(color) - instantly sets the fullbox to a solid color. - * Returns a Promise that resolves immediately. - * - * @param args[0] Color instance. - */ -moduleBaseFunction(moduleFullboxSetColor); - -/** - * transition(from, to, duration, easing?) - animates the fullbox from - * one color to another. Returns a Promise that resolves when the - * transition completes. - * - * @param args[0] Starting Color. - * @param args[1] Ending Color. - * @param args[2] Duration in seconds. - * @param args[3] Optional EASING_* constant (default: EASING_LINEAR). - */ -moduleBaseFunction(moduleFullboxTransition); - -/** - * Initializes the UIFullbox module, registering UIFullboxOver and - * UIFullboxUnder as global singleton objects. - */ -void moduleFullboxInit(void); - -/** - * Disposes the UIFullbox module. - */ -void moduleFullboxDispose(void); diff --git a/src/dusk/script/module/ui/moduletextbox.c b/src/dusk/script/module/ui/moduletextbox.c deleted file mode 100644 index 459dd862..00000000 --- a/src/dusk/script/module/ui/moduletextbox.c +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "moduletextbox.h" -#include "ui/uitextbox.h" -#include "input/inputaction.h" - -scriptproto_t MODULE_TEXTBOX_PROTO; - -moduleBaseFunction(moduleTextboxSetText) { - moduleBaseRequireArgs(1); - moduleBaseRequireString(0); - char_t buf[UI_TEXTBOX_TEXT_MAX]; - moduleBaseToString(args[0], buf, sizeof(buf)); - uiTextboxSetText(buf); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxNextPage) { - uiTextboxNextPage(); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxUpdate) { - errorret_t err = uiTextboxUpdate(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxDraw) { - errorret_t err = uiTextboxDraw(); - if(errorIsNotOk(err)) return moduleBaseThrowError(err); - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxSetAdvanceAction) { - moduleBaseRequireArgs(1); - moduleBaseRequireNumber(0); - const inputaction_t action = (inputaction_t)moduleBaseArgInt(0); - if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { - return moduleBaseThrow( - "UITextbox.setAdvanceAction: invalid action" - ); - } - UI_TEXTBOX.advanceAction = action; - return jerry_undefined(); -} - -moduleBaseFunction(moduleTextboxGetIsPageComplete) { - return jerry_boolean(uiTextboxPageIsComplete()); -} - -moduleBaseFunction(moduleTextboxGetHasNextPage) { - return jerry_boolean(uiTextboxHasNextPage()); -} - -moduleBaseFunction(moduleTextboxGetCurrentPage) { - return jerry_number((double)UI_TEXTBOX.currentPage); -} - -moduleBaseFunction(moduleTextboxGetPageCount) { - return jerry_number((double)UI_TEXTBOX.pageCount); -} - -void moduleTextboxInit(void) { - scriptProtoInit( - &MODULE_TEXTBOX_PROTO, "UITextbox", sizeof(uint8_t), NULL - ); - - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "setText", moduleTextboxSetText - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "nextPage", moduleTextboxNextPage - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "update", moduleTextboxUpdate - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "draw", moduleTextboxDraw - ); - scriptProtoDefineStaticFunc( - &MODULE_TEXTBOX_PROTO, "setAdvanceAction", - moduleTextboxSetAdvanceAction - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "isPageComplete", - moduleTextboxGetIsPageComplete, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "hasNextPage", - moduleTextboxGetHasNextPage, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "currentPage", - moduleTextboxGetCurrentPage, NULL - ); - scriptProtoDefineStaticProp( - &MODULE_TEXTBOX_PROTO, "pageCount", - moduleTextboxGetPageCount, NULL - ); -} - -void moduleTextboxDispose(void) { - scriptProtoDispose(&MODULE_TEXTBOX_PROTO); -} diff --git a/src/dusk/script/module/ui/moduletextbox.h b/src/dusk/script/module/ui/moduletextbox.h deleted file mode 100644 index ace7e0ab..00000000 --- a/src/dusk/script/module/ui/moduletextbox.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" -#include "script/scriptproto.h" - -extern scriptproto_t MODULE_TEXTBOX_PROTO; - -/** - * UITextbox.setText(text) - sets the textbox content and rebuilds layout. - * - * @param args[0] Null-terminated text string. - */ -moduleBaseFunction(moduleTextboxSetText); - -/** - * UITextbox.nextPage() - advances to the next page; no-op on last page. - */ -moduleBaseFunction(moduleTextboxNextPage); - -/** - * UITextbox.update() - advances the typewriter scroll by one tick. - */ -moduleBaseFunction(moduleTextboxUpdate); - -/** - * UITextbox.draw() - draws the frame and visible text for this frame. - */ -moduleBaseFunction(moduleTextboxDraw); - -/** - * UITextbox.setAdvanceAction(action) - sets the input action that - * advances the textbox when held. - * - * @param args[0] Input action constant (INPUT_ACTION_*). - */ -moduleBaseFunction(moduleTextboxSetAdvanceAction); - -/** @return true when the typewriter has fully revealed the current page. */ -moduleBaseFunction(moduleTextboxGetIsPageComplete); - -/** @return true when at least one more page follows the current one. */ -moduleBaseFunction(moduleTextboxGetHasNextPage); - -/** @return Zero-based index of the current page. */ -moduleBaseFunction(moduleTextboxGetCurrentPage); - -/** @return Total number of pages for the current text. */ -moduleBaseFunction(moduleTextboxGetPageCount); - -/** - * Initializes the UITextbox module and registers all methods and - * properties on the UITextbox global. - */ -void moduleTextboxInit(void); - -/** - * Disposes the UITextbox module. - */ -void moduleTextboxDispose(void); diff --git a/src/dusk/script/script.c b/src/dusk/script/script.c deleted file mode 100644 index 043c538e..00000000 --- a/src/dusk/script/script.c +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "script.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "script/module/modulelist.h" -#include - -script_t SCRIPT; - -static void scriptExceptionInfo( - jerry_value_t exception, - char_t *msgBuf, size_t msgSize, - char_t *stackBuf, size_t stackSize -) { - jerry_value_t errVal = jerry_exception_value(exception, false); - - jerry_value_t errStr = jerry_value_to_string(errVal); - jerry_size_t len = jerry_string_to_buffer( - errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)msgBuf, msgSize - 1 - ); - msgBuf[len] = '\0'; - jerry_value_free(errStr); - - stackBuf[0] = '\0'; - jerry_value_t stackKey = jerry_string_sz("stack"); - jerry_value_t stackVal = jerry_object_get(errVal, stackKey); - jerry_value_free(stackKey); - if( - !jerry_value_is_exception(stackVal) && - !jerry_value_is_undefined(stackVal) - ) { - jerry_value_t stackStr = jerry_value_to_string(stackVal); - jerry_size_t stackLen = jerry_string_to_buffer( - stackStr, JERRY_ENCODING_UTF8, (jerry_char_t *)stackBuf, stackSize - 1 - ); - stackBuf[stackLen] = '\0'; - jerry_value_free(stackStr); - } - jerry_value_free(stackVal); - jerry_value_free(errVal); -} - -errorret_t scriptInit(void) { - memoryZero(&SCRIPT, sizeof(script_t)); - - jerry_init(JERRY_INIT_EMPTY); - SCRIPT.initialized = true; - - moduleListInit(); - - errorOk(); -} - -errorret_t scriptUpdate() { - moduleListUpdate(); - jerry_value_t ret = jerry_run_jobs(); - - if(jerry_value_is_exception(ret)) { - char_t buf[256]; - char_t stack[512]; - scriptExceptionInfo(ret, buf, sizeof(buf), stack, sizeof(stack)); - jerry_value_free(ret); - if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack); - errorThrow("Script error: %s", buf); - } - - jerry_value_free(ret); - errorOk(); -} - -errorret_t scriptExecString(const char_t *source) { - assertNotNull(source, "Source cannot be NULL"); - assertTrue(SCRIPT.initialized, "Script system not initialized"); - - jerry_value_t result = jerry_eval( - (const jerry_char_t *)source, - strlen(source), - JERRY_PARSE_NO_OPTS - ); - - if(jerry_value_is_exception(result)) { - char_t buf[256]; - char_t stack[512]; - scriptExceptionInfo(result, buf, sizeof(buf), stack, sizeof(stack)); - jerry_value_free(result); - if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack); - errorThrow("Script error: %s", buf); - } - - jerry_value_free(result); - errorOk(); -} - -errorret_t scriptExecFile(const char_t *path) { - assertNotNull(path, "Path cannot be NULL"); - assertTrue(SCRIPT.initialized, "Script system not initialized"); - - assetentry_t *entry = assetLock(path, ASSET_LOADER_TYPE_SCRIPT, NULL); - assertNotNull(entry, "Failed to get asset entry for script"); - errorChain(assetRequireLoaded(entry)); - assetUnlockEntry(entry); - - errorOk(); -} - -errorret_t scriptDispose(void) { - if(!SCRIPT.initialized) errorOk(); - - // Make a long story short we need to dispose script assets here, because the - // asset reaper isn't called until later. - assetentry_t *entries[ASSET_ENTRY_COUNT_MAX]; - uint32_t count = assetGetEntriesOfType(entries, ASSET_LOADER_TYPE_SCRIPT); - - // Release the locks - for(size_t i = 0; i < count; i++) { - assetUnlockEntry(entries[i]); - assetRequireDisposed(entries[i]); - } - - assertTrue( - assetGetEntriesOfType(entries, ASSET_LOADER_TYPE_SCRIPT) == 0, - "All script assets should be disposed by now." - ); - - // Script modules are now freed, so any JS objects that were only reachable - // through module-scope globals (texEntry, renderable._tex, etc.) are now - // orphaned. A GC pass here fires their finalizers and releases asset entry - // locks before assetDispose() checks ref counts. - jerry_heap_gc(JERRY_GC_PRESSURE_HIGH); - - moduleListDispose(); - jerry_cleanup(); - SCRIPT.initialized = false; - - errorOk(); -} diff --git a/src/dusk/script/script.h b/src/dusk/script/script.h deleted file mode 100644 index 99a8e5df..00000000 --- a/src/dusk/script/script.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 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 struct { - bool_t initialized; -} script_t; - -extern script_t SCRIPT; - -/** - * Initializes the script system, starting up JerryScript and registering all - * built-in modules. - * - * @return Any error that occurred. - */ -errorret_t scriptInit(void); - -/** - * Updates the script system, running any pending jobs. This should be called - * once per frame. - * - * @return Any error that occurred. - */ -errorret_t scriptUpdate(void); - -/** - * Evaluates a JS source string in the global scope. - * - * @param source Null-terminated JS source to evaluate. - * @return Any error that occurred. - */ -errorret_t scriptExecString(const char_t *source); - -/** - * Loads and evaluates a script asset from the archive. The result is cached - * by the asset system; repeated calls with the same path do not re-execute. - * - * @param path Path of the script inside the asset archive. - * @return Any error that occurred. - */ -errorret_t scriptExecFile(const char_t *path); - -/** - * Disposes of the script system and shuts down JerryScript. - * - * @return Any error that occurred. - */ -errorret_t scriptDispose(void); diff --git a/src/dusk/script/scriptmodule.h b/src/dusk/script/scriptmodule.h deleted file mode 100644 index fef0f58f..00000000 --- a/src/dusk/script/scriptmodule.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "scriptproto.h" - -typedef struct { - jerry_value_t exports; -} scriptmodule_t; \ No newline at end of file diff --git a/src/dusk/script/scriptpromisepend.c b/src/dusk/script/scriptpromisepend.c deleted file mode 100644 index 0559ca51..00000000 --- a/src/dusk/script/scriptpromisepend.c +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scriptpromisepend.h" -#include "assert/assert.h" - -uint32_t scriptPromisePendResolve( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t value -) { - uint32_t resolved = 0; - uint32_t i = 0; - while(i < *count) { - if(pend[i].key != key) { i++; continue; } - jerry_value_t r = jerry_promise_resolve(pend[i].promise, value); - jerry_value_free(r); - jerry_value_free(pend[i].promise); - (*count)--; - if(i < *count) pend[i] = pend[*count]; - resolved++; - } - return resolved; -} - -uint32_t scriptPromisePendReject( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t error -) { - uint32_t rejected = 0; - uint32_t i = 0; - while(i < *count) { - if(pend[i].key != key) { i++; continue; } - jerry_value_t r = jerry_promise_reject(pend[i].promise, error); - jerry_value_free(r); - jerry_value_free(pend[i].promise); - (*count)--; - if(i < *count) pend[i] = pend[*count]; - rejected++; - } - return rejected; -} - -bool_t scriptPromisePendHas( - const scriptpromisepend_t *pend, - uint32_t count, - const void *key -) { - for(uint32_t i = 0; i < count; i++) { - if(pend[i].key == key) return true; - } - return false; -} - -void scriptPromisePendAdd( - scriptpromisepend_t *pend, - uint32_t *count, - uint32_t max, - const void *key, - jerry_value_t promise -) { - assertTrue(*count < max, "scriptPromisePendAdd: capacity exceeded"); - pend[*count].key = (void *)key; - pend[*count].promise = jerry_value_copy(promise); - (*count)++; -} - -void scriptPromisePendFreeAll( - scriptpromisepend_t *pend, - uint32_t *count -) { - for(uint32_t i = 0; i < *count; i++) { - jerry_value_free(pend[i].promise); - } - *count = 0; -} diff --git a/src/dusk/script/scriptpromisepend.h b/src/dusk/script/scriptpromisepend.h deleted file mode 100644 index c6df046f..00000000 --- a/src/dusk/script/scriptpromisepend.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include - -/** - * One pending promise keyed to a pointer. - * Declare a static array of these in each script module that needs to - * wait on a C event before resolving a JS promise. - */ -typedef struct { - void *key; - jerry_value_t promise; -} scriptpromisepend_t; - -/** - * Resolves all entries whose key matches and swap-removes them. - * Returns the number of promises resolved. - * - * @param pend The pending array. - * @param count Current count (updated in place). - * @param key Key to match. - * @param value Value passed to jerry_promise_resolve. - * @return Number of promises resolved. - */ -uint32_t scriptPromisePendResolve( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t value -); - -/** - * Rejects all entries whose key matches and swap-removes them. - * Returns the number of promises rejected. - * - * @param pend The pending array. - * @param count Current count (updated in place). - * @param key Key to match. - * @param error Reason passed to jerry_promise_reject. - * @return Number of promises rejected. - */ -uint32_t scriptPromisePendReject( - scriptpromisepend_t *pend, - uint32_t *count, - const void *key, - jerry_value_t error -); - -/** - * Returns true if any entry has key equal to key. - * - * @param pend The pending array. - * @param count Current count. - * @param key Key to search for. - * @return true if found. - */ -bool_t scriptPromisePendHas( - const scriptpromisepend_t *pend, - uint32_t count, - const void *key -); - -/** - * Appends a new entry. Stores a copy of the promise; the caller keeps - * the original to return to script. Asserts that count < max. - * - * @param pend The pending array. - * @param count Current count (incremented in place). - * @param max Capacity of the array. - * @param key Key to associate with the promise. - * @param promise Promise to copy into the array. - */ -void scriptPromisePendAdd( - scriptpromisepend_t *pend, - uint32_t *count, - uint32_t max, - const void *key, - jerry_value_t promise -); - -/** - * Frees all stored promise references without resolving them, then - * zeros count. Call during module dispose before cleaning up events. - * - * @param pend The pending array. - * @param count Current count (set to 0). - */ -void scriptPromisePendFreeAll( - scriptpromisepend_t *pend, - uint32_t *count -); diff --git a/src/dusk/script/scriptproto.c b/src/dusk/script/scriptproto.c deleted file mode 100644 index c88f0a57..00000000 --- a/src/dusk/script/scriptproto.c +++ /dev/null @@ -1,190 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "scriptproto.h" -#include "assert/assert.h" -#include "util/memory.h" -#include "script/module/modulebase.h" - -void scriptProtoInit( - scriptproto_t *proto, - const char_t *name, - const size_t size, - jerry_external_handler_t constructor -) { - assertNotNull(proto, "Script prototype struct must not be null"); - memoryZero(proto, sizeof(scriptproto_t)); - - proto->info = (jerry_object_native_info_t){ - .free_cb = moduleBaseFreeProto, - .number_of_references = 0, - .offset_of_references = 0 - }; - proto->prototype = jerry_object(); - proto->size = size; - - if(constructor != NULL) { - proto->constructor = jerry_function_external(constructor); - jerry_value_t protoKey = jerry_string_sz("prototype"); - jerry_object_set(proto->constructor, protoKey, proto->prototype); - jerry_value_free(protoKey); - jerry_value_t ctorKey = jerry_string_sz("constructor"); - jerry_object_set(proto->prototype, ctorKey, proto->constructor); - jerry_value_free(ctorKey); - } - - if(name != NULL) { - jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t val = proto->constructor - ? proto->constructor - : proto->prototype; - jerry_object_set(global, key, val); - jerry_value_free(key); - jerry_value_free(global); - } -} - -void scriptProtoDefineProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Property name must not be empty"); - assertNotNull(getter, "Getter must not be null"); - - jerry_property_descriptor_t desc; - memoryZero(&desc, sizeof(desc)); - desc.flags = (uint16_t)( - JERRY_PROP_IS_GET_DEFINED | - JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | - JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE - ); - desc.getter = jerry_function_external(getter); - if(setter != NULL) { - desc.flags |= JERRY_PROP_IS_SET_DEFINED; - desc.setter = jerry_function_external(setter); - } - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t result = jerry_object_define_own_prop( - proto->prototype, key, &desc - ); - jerry_value_free(result); - jerry_value_free(key); - jerry_value_free(desc.getter); - if(setter != NULL) jerry_value_free(desc.setter); -} - -void scriptProtoDefineFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Method name must not be empty"); - assertNotNull(fn, "Function handler must not be null"); - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(proto->prototype, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -void scriptProtoDefineStaticProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Property name must not be empty"); - assertNotNull(getter, "Getter must not be null"); - - jerry_value_t target = ( - proto->constructor ? proto->constructor : proto->prototype - ); - - jerry_property_descriptor_t desc; - memoryZero(&desc, sizeof(desc)); - desc.flags = (uint16_t)( - JERRY_PROP_IS_GET_DEFINED | - JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE | - JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE - ); - desc.getter = jerry_function_external(getter); - if(setter != NULL) { - desc.flags |= JERRY_PROP_IS_SET_DEFINED; - desc.setter = jerry_function_external(setter); - } - - jerry_value_t key = jerry_string_sz(name); - jerry_value_t result = jerry_object_define_own_prop(target, key, &desc); - jerry_value_free(result); - jerry_value_free(key); - jerry_value_free(desc.getter); - if(setter != NULL) jerry_value_free(desc.setter); -} - -void scriptProtoDefineStaticFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertStrLenMin(name, 1, "Method name must not be empty"); - assertNotNull(fn, "Function handler must not be null"); - - jerry_value_t target = ( - proto->constructor ? proto->constructor : proto->prototype - ); - jerry_value_t key = jerry_string_sz(name); - jerry_value_t func = jerry_function_external(fn); - jerry_object_set(target, key, func); - jerry_value_free(func); - jerry_value_free(key); -} - -jerry_value_t scriptProtoCreateValue( - const scriptproto_t *proto, - const void *value -) { - assertNotNull(proto, "Script prototype struct must not be null"); - assertNotNull(value, "Value pointer must not be null"); - - void *ptr = memoryAllocate(proto->size); - memoryCopy(ptr, value, proto->size); - jerry_value_t obj = jerry_object(); - jerry_object_set_native_ptr(obj, &proto->info, ptr); - jerry_object_set_proto(obj, proto->prototype); - return obj; -} - -void *scriptProtoGetValue( - const scriptproto_t *proto, - const jerry_value_t obj -) { - assertNotNull(proto, "Script prototype struct must not be null"); - if(!jerry_value_is_object(obj)) return NULL; - return jerry_object_get_native_ptr(obj, &proto->info); -} - -void scriptProtoDefineToString( - scriptproto_t *proto, - jerry_external_handler_t fn -) { - scriptProtoDefineFunc(proto, "toString", fn); -} - -void scriptProtoDispose(scriptproto_t *proto) { - assertNotNull(proto, "Script prototype struct must not be null"); - jerry_value_free(proto->prototype); - if(proto->constructor) jerry_value_free(proto->constructor); -} diff --git a/src/dusk/script/scriptproto.h b/src/dusk/script/scriptproto.h deleted file mode 100644 index eb11b24a..00000000 --- a/src/dusk/script/scriptproto.h +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "dusk.h" -#include - -/** - * Represents a JavaScript class prototype backed by a C struct. - * Use scriptProtoInit() to populate, scriptProtoDispose() to clean up. - */ -typedef struct { - /** Native-info tag used to identify instances of this class. */ - jerry_object_native_info_t info; - /** The JS prototype object shared by all instances. */ - jerry_value_t prototype; - /** The JS constructor function, or 0 if no constructor was given. */ - jerry_value_t constructor; - /** sizeof the C struct wrapped by each instance. */ - size_t size; -} scriptproto_t; - -/** - * Initializes a JS class prototype. - * - * If name is non-NULL the class is registered as a global. When ctor is - * also non-NULL the global is the constructor function (enabling - * `new Name(...)`); otherwise the prototype object itself becomes the - * global. - * - * @param proto The struct to initialize. - * @param name JS global name, or NULL to skip global registration. - * @param size sizeof the C struct this class wraps. - * @param ctor Constructor handler, or NULL for no constructor. - */ -void scriptProtoInit( - scriptproto_t *proto, - const char_t *name, - const size_t size, - jerry_external_handler_t ctor -); - -/** - * Defines an instance property with a getter and optional setter. - * - * @param proto The class prototype. - * @param name Property name. - * @param getter Getter handler (must not be NULL). - * @param setter Setter handler, or NULL for a read-only property. - */ -void scriptProtoDefineProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -); - -/** - * Defines an instance method on the class prototype. - * - * @param proto The class prototype. - * @param name Method name. - * @param fn C handler called when the method is invoked. - */ -void scriptProtoDefineFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Defines a static property on the class (e.g. Console.visible). - * - * Attaches to the constructor when one exists, otherwise attaches - * directly to the prototype object (which is the global in that case). - * - * @param proto The class prototype. - * @param name Property name. - * @param getter Getter handler (must not be NULL). - * @param setter Setter handler, or NULL for a read-only property. - */ -void scriptProtoDefineStaticProp( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t getter, - jerry_external_handler_t setter -); - -/** - * Defines a static method on the class (e.g. Console.print). - * - * Attaches to the constructor when one exists, otherwise attaches - * directly to the prototype object. - * - * @param proto The class prototype. - * @param name Method name. - * @param fn C handler called when the static method is invoked. - */ -void scriptProtoDefineStaticFunc( - scriptproto_t *proto, - const char_t *name, - jerry_external_handler_t fn -); - -/** - * Creates a JS instance wrapping a copy of a C value. - * - * Allocates a copy of the value, sets it as native data on a new JS - * object, and applies the class prototype. - * - * @param proto The class prototype. - * @param value Pointer to the C value to copy into the new JS object. - * @return A new JS object with the class prototype and native data set. - */ -jerry_value_t scriptProtoCreateValue( - const scriptproto_t *proto, - const void *value -); - -/** - * Unwraps the native C pointer from a JS object instance. - * - * @param proto The class prototype. - * @param obj The JS object to inspect. - * @return Pointer to the wrapped C value, or NULL if not an instance. - */ -void *scriptProtoGetValue( - const scriptproto_t *proto, - const jerry_value_t obj -); - -/** - * Defines a toString() method on the class prototype. - * - * @param proto The class prototype. - * @param fn C handler called when toString() is invoked on an - * instance. - */ -void scriptProtoDefineToString( - scriptproto_t *proto, - jerry_external_handler_t fn -); - -/** - * Releases all JerryScript resources held by the prototype. - * - * Must be called before jerry_cleanup() to avoid heap finalizer - * crashes from live jerry_value_t GC roots. - * - * @param proto The class prototype to dispose. - */ -void scriptProtoDispose(scriptproto_t *proto); diff --git a/src/dusk/ui/uitextbox.c b/src/dusk/ui/uitextbox.c index aa032803..25f42986 100644 --- a/src/dusk/ui/uitextbox.c +++ b/src/dusk/ui/uitextbox.c @@ -25,26 +25,28 @@ errorret_t uiTextboxInit(void) { UI_TEXTBOX.font = &FONT_DEFAULT; - float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth; - float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; - float_t tbHeight = ( - (float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH + - (float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING + - 2.0f * fontH - ); - UI_TEXTBOX.x = 10.0f; - UI_TEXTBOX.y = (float_t)SCREEN.height - tbHeight - 10.0f; - UI_TEXTBOX.width = (float_t)SCREEN.width - 20.0f; - UI_TEXTBOX.height = tbHeight; + if(FONT_DEFAULT.tileset != NULL) { + float_t fontW = (float_t)FONT_DEFAULT.tileset->tileWidth; + float_t fontH = (float_t)FONT_DEFAULT.tileset->tileHeight; + float_t tbHeight = ( + (float_t)UI_TEXTBOX_LINES_PER_PAGE_MAX * fontH + + (float_t)(UI_TEXTBOX_LINES_PER_PAGE_MAX - 1) * UI_TEXTBOX_LINE_SPACING + + 2.0f * fontH + ); + UI_TEXTBOX.x = 10.0f; + UI_TEXTBOX.y = (float_t)SCREEN.height - tbHeight - 10.0f; + UI_TEXTBOX.width = (float_t)SCREEN.width - 20.0f; + UI_TEXTBOX.height = tbHeight; - UI_TEXTBOX.frame.tileset.columns = 3; - UI_TEXTBOX.frame.tileset.rows = 3; - UI_TEXTBOX.frame.tileset.tileCount = 9; - UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth; - UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight; - UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f; - UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f; - UI_TEXTBOX.frame.texture = &TEXTURE_WHITE; + UI_TEXTBOX.frame.tileset.columns = 3; + UI_TEXTBOX.frame.tileset.rows = 3; + UI_TEXTBOX.frame.tileset.tileCount = 9; + UI_TEXTBOX.frame.tileset.tileWidth = FONT_DEFAULT.tileset->tileWidth; + UI_TEXTBOX.frame.tileset.tileHeight = FONT_DEFAULT.tileset->tileHeight; + UI_TEXTBOX.frame.tileset.uv[0] = 1.0f / 3.0f; + UI_TEXTBOX.frame.tileset.uv[1] = 1.0f / 3.0f; + UI_TEXTBOX.frame.texture = &TEXTURE_WHITE; + } eventInit( &UI_TEXTBOX.onPageComplete, diff --git a/src/duskdolphin/CMakeLists.txt b/src/duskdolphin/CMakeLists.txt index 818635ee..e2ee2ba6 100644 --- a/src/duskdolphin/CMakeLists.txt +++ b/src/duskdolphin/CMakeLists.txt @@ -15,7 +15,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME} ) # Subdirs -add_subdirectory(asset) add_subdirectory(log) add_subdirectory(display) add_subdirectory(input) diff --git a/src/duskdolphin/asset/CMakeLists.txt b/src/duskdolphin/asset/CMakeLists.txt deleted file mode 100644 index 32883cf8..00000000 --- a/src/duskdolphin/asset/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -if(DUSK_DOLPHIN_BUILD_TYPE STREQUAL "ISO") - target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetdolphindvd.c - ) -else() - target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetdolphinfat.c - ) -endif() diff --git a/src/duskdolphin/asset/assetdolphindvd.c b/src/duskdolphin/asset/assetdolphindvd.c deleted file mode 100644 index 2670cf79..00000000 --- a/src/duskdolphin/asset/assetdolphindvd.c +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetdolphindvd.h" -#include "asset/asset.h" -#include "util/string.h" -#include "util/memory.h" - -#define ASSET_DOLPHIN_DVD_SECTOR_SIZE 2048u -#define ASSET_DOLPHIN_DVD_PVD_SECTOR 16u - -errorret_t assetInitDolphinDVD(void) { - DVD_Init(); - DVD_Mount(); - - // ISO 9660 Primary Volume Descriptor is at sector 16. - u8 *pvd = (u8 *)assetDolphinDVDRead( - (s64)(ASSET_DOLPHIN_DVD_PVD_SECTOR * ASSET_DOLPHIN_DVD_SECTOR_SIZE), - ASSET_DOLPHIN_DVD_SECTOR_SIZE - ); - if(!pvd) errorThrow("Failed to read ISO 9660 PVD."); - - // Sanity-check: type=1, identifier="CD001" - if(pvd[0] != 1 || pvd[1] != 'C' || pvd[2] != 'D' || - pvd[3] != '0' || pvd[4] != '0' || pvd[5] != '1') { - memoryFree(pvd); - errorThrow("Not a valid ISO 9660 disc."); - } - - // Root Directory Record starts at PVD+156. - // ISO 9660 stores multi-byte fields in both byte orders; use the BE copies - // (offset +6 for LBA, +14 for size) since the GameCube is big-endian. - u32 rootLBA = assetDolphinDVDReadBigEndian32(pvd + 156 + 6); - u32 rootSize = assetDolphinDVDReadBigEndian32(pvd + 156 + 14); - memoryFree(pvd); - - u8 *dir = (u8 *)assetDolphinDVDRead( - (s64)rootLBA * ASSET_DOLPHIN_DVD_SECTOR_SIZE, rootSize - ); - if(!dir) errorThrow("Failed to read ISO 9660 root directory."); - - // Scan directory records for dusk.dsk. - // ISO 9660 level-1 names are uppercase with a ";1" version suffix, e.g. - // "DUSK.DSK;1". We strip the suffix before comparing case-insensitively. - u32 fileLBA = 0, fileSize = 0; - u32 pos = 0; - while(pos < rootSize) { - u8 recLen = dir[pos]; - if(recLen == 0) { - // Sector padding - skip to the start of the next sector. - pos = ( - pos + (ASSET_DOLPHIN_DVD_SECTOR_SIZE - 1u) - ) & ~(ASSET_DOLPHIN_DVD_SECTOR_SIZE - 1u); - continue; - } - - u8 flags = dir[pos + 25]; - u8 nameLen = dir[pos + 32]; - - if(!(flags & 0x02) && nameLen > 1) { // skip directories and "." / ".." - const char_t *isoName = (const char_t *)(dir + pos + 33); - - // Build a null-terminated copy of the base name (strip ";N" version). - char_t baseName[32]; - u8 baseLen = 0; - while(baseLen < nameLen && isoName[baseLen] != ';' && baseLen < 31) { - baseName[baseLen] = isoName[baseLen]; - baseLen++; - } - baseName[baseLen] = '\0'; - - if(stringCompareInsensitive(baseName, ASSET_FILE_NAME) == 0) { - fileLBA = assetDolphinDVDReadBigEndian32(dir + pos + 6); - fileSize = assetDolphinDVDReadBigEndian32(dir + pos + 14); - break; - } - } - pos += recLen; - } - memoryFree(dir); - - if(!fileLBA) errorThrow("Failed to find asset file on ISO."); - - u8 *data = (u8 *)assetDolphinDVDRead( - (s64)fileLBA * ASSET_DOLPHIN_DVD_SECTOR_SIZE, fileSize - ); - if(!data) errorThrow("Failed to read asset file from ISO."); - - zip_error_t zerr; - zip_source_t *src = zip_source_buffer_create(data, fileSize, 1, &zerr); - if(!src) { - memoryFree(data); - errorThrow("Failed to create zip source from DVD buffer."); - } - - ASSET.zip = zip_open_from_source(src, ZIP_RDONLY, &zerr); - if(!ASSET.zip) { - zip_source_free(src); - errorThrow("Failed to open asset zip from DVD."); - } - - errorOk(); -} - -void *assetDolphinDVDRead(const s64 offset, const u32 size) { - u32 padded = ASSET_DOLPHIN_DVD_ALIGN_UP(size); - void *buf = memoryAlign(ASSET_DOLPHIN_DVD_ALIGN, padded); - if(!buf) return NULL; - DCInvalidateRange(buf, padded); - static dvdcmdblk block; - if(DVD_ReadAbs(&block, buf, padded, offset) <= 0) { - memoryFree(buf); - return NULL; - } - return buf; -} - -u32 assetDolphinDVDReadBigEndian32(const u8 *p) { - return ((u32)p[0] << 24) | ((u32)p[1] << 16) | ((u32)p[2] << 8) | (u32)p[3]; -} - -errorret_t assetDisposeDolphinDVD(void) { - errorOk(); -} diff --git a/src/duskdolphin/asset/assetdolphindvd.h b/src/duskdolphin/asset/assetdolphindvd.h deleted file mode 100644 index 883a290b..00000000 --- a/src/duskdolphin/asset/assetdolphindvd.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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 -#include - -/** Alignment for Dolphin DVD asset reads. */ -#define ASSET_DOLPHIN_DVD_ALIGN 32u - -/** - * Gets the next multiple of ASSET_DOLPHIN_DVD_ALIGN greater than or equal to n. - * This is used to ensure that DVD reads are properly aligned and padded. - * - * @param n The number to align. - * @return The aligned number. - */ -#define ASSET_DOLPHIN_DVD_ALIGN_UP(n) \ - (((u32)(n) + ASSET_DOLPHIN_DVD_ALIGN - 1u) & ~(ASSET_DOLPHIN_DVD_ALIGN - 1u)) - -typedef struct { - uint8_t nothing; -} assetdolphindvd_t; - -/** - * Initializes the Dolphin DVD asset system. - * - * This will; - * - Initialize the DVD subsystem and mount the disc. - * - Read the disc header to find the FST location. - * - Read the FST and find the offset and length of the "data" file. - * - Store the offset and length for later use when loading assets. - * - * @return An errorret_t indicating success or failure. - */ -errorret_t assetInitDolphinDVD(void); - -/** - * Reads a block of data from the DVD at the specified offset and size. - * The returned buffer is aligned to 32 bytes and padded. The caller is - * responsible for freeing the buffer. - * - * @param offset The offset on the DVD to read from. - * @param size The number of bytes to read. - * @return A pointer to the buffer, or NULL on failure. - */ -void * assetDolphinDVDRead(const s64 offset, const u32 size); - -/** - * Reads a big-endian 32-bit integer from the given data. - * - * @param data The data to read from. - * @return The big-endian 32-bit integer as a host-endian uint32_t. - */ -u32 assetDolphinDVDReadBigEndian32(const u8 *data); - -/** - * Disposes of the Dolphin DVD asset system. - * - * @return An errorret_t indicating success or failure. - */ -errorret_t assetDisposeDolphinDVD(void); \ No newline at end of file diff --git a/src/duskdolphin/asset/assetdolphinfat.c b/src/duskdolphin/asset/assetdolphinfat.c deleted file mode 100644 index f41002fd..00000000 --- a/src/duskdolphin/asset/assetdolphinfat.c +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetdolphinfat.h" -#include "asset/asset.h" -#include "util/string.h" -#include -#include -#include -#include -#include -#include -#include -#include - -errorret_t assetInitDolphinFAT(void) { - if(!fatInitDefault()) errorThrow("Failed to initialize FAT filesystem."); - - char_t **dolphinSearchPath = (char_t **)ASSET_DOLPHIN_PATHS; - char_t foundPath[ASSET_DOLPHIN_FAT_PATH_MAX]; - foundPath[0] = '\0'; - do { - DIR *pdir = opendir(*dolphinSearchPath); - if(pdir == NULL) continue; - - while(true) { - struct dirent* pent = readdir(pdir); - if(pent == NULL) break; - - if(stringCompareInsensitive(pent->d_name, ASSET_FILE_NAME) != 0) continue; - - snprintf( - foundPath, - ASSET_DOLPHIN_FAT_PATH_MAX, - "%s/%s", - *dolphinSearchPath, - ASSET_FILE_NAME - ); - break; - } - - closedir(pdir); - if(foundPath[0] != '\0') break; - } while(*(++dolphinSearchPath) != NULL); - - if(foundPath[0] == '\0') - errorThrow("Failed to find asset file on FAT filesystem."); - - ASSET.zip = zip_open(foundPath, ZIP_RDONLY, NULL); - if(ASSET.zip == NULL) - errorThrow("Failed to open asset file on FAT filesystem."); - - errorOk(); -} - -errorret_t assetDisposeDolphinFAT(void) { - errorOk(); -} diff --git a/src/duskdolphin/asset/assetdolphinfat.h b/src/duskdolphin/asset/assetdolphinfat.h deleted file mode 100644 index ded5d023..00000000 --- a/src/duskdolphin/asset/assetdolphinfat.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -#define ASSET_DOLPHIN_FAT_PATH_MAX FILENAME_MAX - -static const char_t *ASSET_DOLPHIN_PATHS[] = { - "/", - "/Dusk", - "/dusk", - "/DUSK", - "/apps", - "/apps/Dusk", - "/apps/dusk", - "/apps/DUSK", - ".", - "./", - "./Dusk", - "./dusk", - "./DUSK", - "./apps", - "./apps/Dusk", - "./apps/dusk", - "./apps/DUSK", - NULL -}; - -typedef struct { - uint8_t nothing; -} assetdolphinfat_t; - -/** - * Initializes the Dolphin FAT asset system. - * - * FAT being the general term for the FAT filesystem, on Wii this would likely - * be the SD Card, on Gamecube it can be something like the SD Gecko or the - * Slot2 SD Card. - * - * As far as I know this doesn't (currently) work on the CUBEODE? - * - * @return An errorret_t indicating success or failure. - */ -errorret_t assetInitDolphinFAT(void); - -/** - * Disposes of the Dolphin FAT asset system. - * - * @return An errorret_t indicating success or failure. - */ -errorret_t assetDisposeDolphinFAT(void); \ No newline at end of file diff --git a/src/duskdolphin/asset/assetplatform.h b/src/duskdolphin/asset/assetplatform.h deleted file mode 100644 index 5ef73420..00000000 --- a/src/duskdolphin/asset/assetplatform.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once - -#ifdef DUSK_DOLPHIN_BUILD_ISO - #include "assetdolphindvd.h" - #define assetInitPlatform assetInitDolphinDVD - #define assetDisposePlatform assetDisposeDolphinDVD - typedef assetdolphindvd_t assetplatform_t; -#else - #include "assetdolphinfat.h" - #define assetInitPlatform assetInitDolphinFAT - #define assetDisposePlatform assetDisposeDolphinFAT - typedef assetdolphinfat_t assetplatform_t; -#endif - diff --git a/src/duskdolphin/script/module/moduleplatformdolphin.h b/src/duskdolphin/script/module/moduleplatformdolphin.h deleted file mode 100644 index 6a19105c..00000000 --- a/src/duskdolphin/script/module/moduleplatformdolphin.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" - -static void modulePlatformDolphin(void) { - moduleBaseEval("var DOLPHIN = true;\n"); -} diff --git a/src/duskdolphin/script/module/moduleplatformplatform.h b/src/duskdolphin/script/module/moduleplatformplatform.h deleted file mode 100644 index b02e99e4..00000000 --- a/src/duskdolphin/script/module/moduleplatformplatform.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "moduleplatformdolphin.h" - -#define modulePlatformPlatform modulePlatformDolphin \ No newline at end of file diff --git a/src/dusklinux/CMakeLists.txt b/src/dusklinux/CMakeLists.txt index af0e8acb..ba71ec2b 100644 --- a/src/dusklinux/CMakeLists.txt +++ b/src/dusklinux/CMakeLists.txt @@ -10,7 +10,6 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME} ) # Subdirs -add_subdirectory(asset) add_subdirectory(log) add_subdirectory(input) add_subdirectory(network) diff --git a/src/dusklinux/asset/CMakeLists.txt b/src/dusklinux/asset/CMakeLists.txt deleted file mode 100644 index 3910a3b8..00000000 --- a/src/dusklinux/asset/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetlinux.c -) \ No newline at end of file diff --git a/src/dusklinux/asset/assetlinux.c b/src/dusklinux/asset/assetlinux.c deleted file mode 100644 index 57b790e7..00000000 --- a/src/dusklinux/asset/assetlinux.c +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset/asset.h" -#include "engine/engine.h" -#include "util/string.h" -#include "assert/assert.h" - -errorret_t assetInitLinux(void) { - // Engine may have been provided the launch path - if(ENGINE.argc > 0) { - // Get the directory of the executable - char_t buffer[ASSET_SYSTEM_PATH_MAX]; - stringCopy(buffer, ENGINE.argv[0], ASSET_SYSTEM_PATH_MAX); - size_t len = strlen(buffer); - - // Normalize slashes - for(size_t i = 0; i < ASSET_SYSTEM_PATH_MAX; i++) { - if(buffer[i] == '\0') break; - if(buffer[i] == '\\') buffer[i] = '/'; - } - - // Now find the last slash - char_t *end = buffer + len - 1; - do { - end--; - if(*end == '/') { - *end = '\0'; - break; - } - } while(end != buffer); - - - // Did we find a slash? - if(end != buffer) { - // We found the directory, set as system path - stringCopy(ASSET.platform.systemPath, buffer, ASSET_SYSTEM_PATH_MAX); - } - } else { - // Default system path, intended to be overridden by the platform - stringCopy(ASSET.platform.systemPath, ".", ASSET_SYSTEM_PATH_MAX); - } - - // Open zip file - char_t searchPath[ASSET_SYSTEM_PATH_MAX]; - const char_t **path = ASSET_LINUX_SEARCH_PATHS; - int32_t error; - do { - char_t temp[ASSET_SYSTEM_PATH_MAX]; - snprintf( - temp, - ASSET_SYSTEM_PATH_MAX, - *path, - ASSET_FILE_NAME - ); - - // Ensure combined length does not exceed ASSET_SYSTEM_PATH_MAX - size_t syslen = strlen(ASSET.platform.systemPath); - size_t slashlen = 1; // for '/' - size_t max_temp = ASSET_SYSTEM_PATH_MAX - syslen - slashlen - 1; - if(strlen(temp) > max_temp) { - temp[max_temp] = '\0'; - } - snprintf( - searchPath, - ASSET_SYSTEM_PATH_MAX, - "%s/%s", - ASSET.platform.systemPath, - temp - ); - printf("Try open asset file: %s\n", searchPath); - - // Try open - error = 0; - ASSET.zip = zip_open(searchPath, ZIP_RDONLY, &error); - if(ASSET.zip == NULL) { - printf("Opened asset file with non-zero error code: %d\n", error); - continue; - } - break;// Found! - } while(*(++path) != NULL); - - // Did we open the asset? - if(ASSET.zip == NULL) { - errorThrow("Failed to open asset file."); - } - - errorOk(); -} - -errorret_t assetDisposeLinux(void) { - errorOk(); -} \ No newline at end of file diff --git a/src/dusklinux/asset/assetlinux.h b/src/dusklinux/asset/assetlinux.h deleted file mode 100644 index d45457d7..00000000 --- a/src/dusklinux/asset/assetlinux.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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 "asset/assetfile.h" - -#define ASSET_SYSTEM_PATH_MAX FILENAME_MAX - -static const char_t *ASSET_LINUX_SEARCH_PATHS[] = { - "%s", - "../%s", - "../../%s", - "data/%s", - "../data/%s", - NULL -}; - -typedef struct { - char_t systemPath[ASSET_SYSTEM_PATH_MAX]; -} assetlinux_t; - -/** - * Initializes the asset system on Linux. - * - * @return Error state if failed, otherwise returns success. - */ -errorret_t assetInitLinux(void); - -/** - * Disposes the asset system on Linux. - * - * @return Error state if failed, otherwise returns success. - */ -errorret_t assetDisposeLinux(void); \ No newline at end of file diff --git a/src/dusklinux/asset/assetplatform.h b/src/dusklinux/asset/assetplatform.h deleted file mode 100644 index 669b8701..00000000 --- a/src/dusklinux/asset/assetplatform.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset/assetlinux.h" - -typedef assetlinux_t assetplatform_t; -#define assetInitPlatform assetInitLinux -#define assetDisposePlatform assetDisposeLinux \ No newline at end of file diff --git a/src/dusklinux/script/module/moduleplatformlinux.h b/src/dusklinux/script/module/moduleplatformlinux.h deleted file mode 100644 index 9b05c65f..00000000 --- a/src/dusklinux/script/module/moduleplatformlinux.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" - -static void modulePlatformLinux(void) { - moduleBaseEval("var LINUX = true;\n"); -} diff --git a/src/dusklinux/script/module/moduleplatformplatform.h b/src/dusklinux/script/module/moduleplatformplatform.h deleted file mode 100644 index 4fb3c6eb..00000000 --- a/src/dusklinux/script/module/moduleplatformplatform.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "moduleplatformlinux.h" - -#define modulePlatformPlatform modulePlatformLinux \ No newline at end of file diff --git a/src/duskpsp/CMakeLists.txt b/src/duskpsp/CMakeLists.txt index ca661aa5..d55f9ca8 100644 --- a/src/duskpsp/CMakeLists.txt +++ b/src/duskpsp/CMakeLists.txt @@ -15,7 +15,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME} ) # Subdirs -add_subdirectory(asset) add_subdirectory(input) add_subdirectory(log) add_subdirectory(network) diff --git a/src/duskpsp/asset/CMakeLists.txt b/src/duskpsp/asset/CMakeLists.txt deleted file mode 100644 index 412f7a82..00000000 --- a/src/duskpsp/asset/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetpbp.c - assetpsp.c -) \ No newline at end of file diff --git a/src/duskpsp/asset/assetpbp.c b/src/duskpsp/asset/assetpbp.c deleted file mode 100644 index 4092bfda..00000000 --- a/src/duskpsp/asset/assetpbp.c +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset/asset.h" -#include "assert/assert.h" -#include "util/memory.h" - -errorret_t assetInitPBP(const char_t *pbpPath) { - assertNotNull(pbpPath, "PBP path cannot be null."); - assertStrLenMin(pbpPath, 1, "PBP path cannot be empty."); - assertStrLenMax(pbpPath, ASSET_PBP_PATH_MAX, "PBP path is too long."); - - ASSET.platform.pbpFile = fopen(pbpPath, "rb"); - if(ASSET.platform.pbpFile == NULL) { - errorThrow("Failed to open PBP file: %s", pbpPath); - } - - // Get size of PBP file. - if(fseek(ASSET.platform.pbpFile, 0, SEEK_END) != 0) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to seek to end of PBP file : %s", pbpPath); - } - - size_t pbpSize = ftell(ASSET.platform.pbpFile); - if(pbpSize == -1L) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to get size of PBP file : %s", pbpPath); - } - - if(pbpSize < sizeof(assetpbpheader_t)) { - fclose(ASSET.platform.pbpFile); - errorThrow("PBP file is too small to be valid: %s", pbpPath); - } - - // Rewind to start - if(fseek(ASSET.platform.pbpFile, 0, SEEK_SET) != 0) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to seek to start of PBP file : %s", pbpPath); - } - - // Read the PBP header - size_t read = fread( - &ASSET.platform.pbpHeader, - 1, - sizeof(assetpbpheader_t), - ASSET.platform.pbpFile - ); - if(read != sizeof(assetpbpheader_t)) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to read PBP header", pbpPath); - } - - if(memoryCompare( - ASSET.platform.pbpHeader.signature, - ASSET_PBP_SIGNATURE, - sizeof(ASSET_PBP_SIGNATURE) - ) != 0) { - fclose(ASSET.platform.pbpFile); - errorThrow("Invalid PBP signature in file: %s", pbpPath); - } - - // If we seek to the PSAR offset, we can read the WAD file from there. - // I'm not sure what PSAR was intended for, but it holds any user data we - // want, so I shoved the entire dusk wad there. - if(fseek( - ASSET.platform.pbpFile, ASSET.platform.pbpHeader.psarOffset, SEEK_SET - ) != 0) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to seek to PSAR offset in PBP file: %s", pbpPath); - } - - zip_uint64_t zipPsarOffset = (zip_uint64_t)( - ASSET.platform.pbpHeader.psarOffset - ); - zip_int64_t zipPsarSize = (zip_int64_t)( - pbpSize - ASSET.platform.pbpHeader.psarOffset - ); - - zip_source_t *psarSource = zip_source_filep_create( - ASSET.platform.pbpFile, - zipPsarOffset, - zipPsarSize, - NULL - ); - if(psarSource == NULL) { - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to create zip source in PBP file: %s", pbpPath); - } - - ASSET.zip = zip_open_from_source( - psarSource, - ZIP_RDONLY, - NULL - ); - if(ASSET.zip == NULL) { - zip_source_free(psarSource); - fclose(ASSET.platform.pbpFile); - errorThrow("Failed to open zip from PBP file: %s", pbpPath); - } - - errorOk(); -} - -errorret_t assetDisposePBP(void) { - if(ASSET.platform.pbpFile != NULL) { - fclose(ASSET.platform.pbpFile); - ASSET.platform.pbpFile = NULL; - } - - errorOk(); -} \ No newline at end of file diff --git a/src/duskpsp/asset/assetpbp.h b/src/duskpsp/asset/assetpbp.h deleted file mode 100644 index f9ac848d..00000000 --- a/src/duskpsp/asset/assetpbp.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * 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 "asset/assetfile.h" - -#define ASSET_PBP_SIGNATURE_SIZE 4 -#define ASSET_PBP_SIGNATURE "\0PBP" - -#define ASSET_PBP_PATH_MAX FILENAME_MAX - -typedef struct { - char_t signature[ASSET_PBP_SIGNATURE_SIZE]; - uint32_t version; - uint32_t sfoOffset; - uint32_t icon0Offset; - uint32_t icon1Offset; - uint32_t pic0Offset; - uint32_t pic1Offset; - uint32_t snd0Offset; - uint32_t pspOffset; - uint32_t psarOffset; -} assetpbpheader_t; - -typedef struct { - FILE *pbpFile; - assetpbpheader_t pbpHeader; -} assetpbp_t; - -/** - * Initializes the PBP style asset system. - * - * @param pbpPath The file path to the PBP file to read. - * @returns An errorret_t indicating success or failure of the operation. - */ -errorret_t assetInitPBP(const char_t *pbpPath); - -/** - * Disposes the PBP style asset system. - * - * @returns An errorret_t indicating success or failure of the operation. - */ -errorret_t assetDisposePBP(void); \ No newline at end of file diff --git a/src/duskpsp/asset/assetplatform.h b/src/duskpsp/asset/assetplatform.h deleted file mode 100644 index b7440a8c..00000000 --- a/src/duskpsp/asset/assetplatform.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetpsp.h" - -typedef assetpsp_t assetplatform_t; -#define assetInitPlatform assetInitPSP -#define assetDisposePlatform assetDisposePSP \ No newline at end of file diff --git a/src/duskpsp/asset/assetpsp.c b/src/duskpsp/asset/assetpsp.c deleted file mode 100644 index 0d983bfb..00000000 --- a/src/duskpsp/asset/assetpsp.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset/asset.h" -#include "engine/engine.h" -#include "assert/assert.h" -#include "util/string.h" - -errorret_t assetInitPSP(void) { - assertTrue(ENGINE.argc >= 1, "PSP requires launch argument."); - - // PSP is given either the prx OR the PBP file. - // In the format of "ms0:/PSP/GAME/DUSK/EBOOT.PBP" or "host0:/Dusk.prx" - // IF the file is the PBP file, we are loading directly on the PSP itself. - // IF the file is the .prx then we are debugging and fopen will return - // relative filepaths correctly, e.g. host0:/dusk.dsk will be on host. - - // Originally I was going to allow host to read the .dsk directly but then - // I'd have to maintain two file implementations. - - const char_t *pbpPath; - if(stringEndsWithCaseInsensitive(ENGINE.argv[0], ASSET_PSP_EXTENSION_PBP)) { - pbpPath = ENGINE.argv[0]; - } else { - // In Debugging this would be next to host0:/Dusk.prx - pbpPath = "./EBOOT.PBP"; - } - - errorChain(assetInitPBP(pbpPath)); - errorOk(); -} - -errorret_t assetDisposePSP(void) { - errorChain(assetDisposePBP()); - errorOk(); -} \ No newline at end of file diff --git a/src/duskpsp/asset/assetpsp.h b/src/duskpsp/asset/assetpsp.h deleted file mode 100644 index badcd314..00000000 --- a/src/duskpsp/asset/assetpsp.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetpbp.h" - -#define ASSET_PSP_EXTENSION_PBP ".pbp" - -typedef assetpbp_t assetpsp_t; - -/** - * Initializes the PSP style asset system. - * - * @returns An errorret_t indicating success or failure of the operation. - */ -errorret_t assetInitPSP(void); - -/** - * Disposes the PSP style asset system. - * - * @returns An errorret_t indicating success or failure of the operation. - */ -errorret_t assetDisposePSP(void); \ No newline at end of file diff --git a/src/duskpsp/script/module/moduleplatformplatform.h b/src/duskpsp/script/module/moduleplatformplatform.h deleted file mode 100644 index d718a08b..00000000 --- a/src/duskpsp/script/module/moduleplatformplatform.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "moduleplatformpsp.h" - -#define modulePlatformPlatform modulePlatformPSP \ No newline at end of file diff --git a/src/duskpsp/script/module/moduleplatformpsp.h b/src/duskpsp/script/module/moduleplatformpsp.h deleted file mode 100644 index d733251e..00000000 --- a/src/duskpsp/script/module/moduleplatformpsp.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/module/modulebase.h" - -static void modulePlatformPSP(void) { - moduleBaseEval("var PSP = true;\n"); -} diff --git a/src/duskvita/CMakeLists.txt b/src/duskvita/CMakeLists.txt index 8e31820d..0277eeca 100644 --- a/src/duskvita/CMakeLists.txt +++ b/src/duskvita/CMakeLists.txt @@ -15,7 +15,6 @@ target_sources(${DUSK_BINARY_TARGET_NAME} ) # Subdirs -add_subdirectory(asset) add_subdirectory(input) add_subdirectory(log) add_subdirectory(save) diff --git a/src/duskvita/asset/CMakeLists.txt b/src/duskvita/asset/CMakeLists.txt deleted file mode 100644 index d3531331..00000000 --- a/src/duskvita/asset/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -# Sources -target_sources(${DUSK_LIBRARY_TARGET_NAME} - PUBLIC - assetvita.c -) diff --git a/src/duskvita/asset/assetplatform.h b/src/duskvita/asset/assetplatform.h deleted file mode 100644 index 725e5122..00000000 --- a/src/duskvita/asset/assetplatform.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "assetvita.h" - -typedef assetvita_t assetplatform_t; -#define assetInitPlatform assetInitVita -#define assetDisposePlatform assetDisposeVita diff --git a/src/duskvita/asset/assetvita.c b/src/duskvita/asset/assetvita.c deleted file mode 100644 index 667b2445..00000000 --- a/src/duskvita/asset/assetvita.c +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "asset/asset.h" -#include "assert/assert.h" - -errorret_t assetInitVita(void) { - int32_t error; - ASSET.zip = zip_open(ASSET_VITA_DSK_PATH, ZIP_RDONLY, &error); - if(ASSET.zip == NULL) { - errorThrow("Failed to open asset file: " ASSET_VITA_DSK_PATH); - } - errorOk(); -} - -errorret_t assetDisposeVita(void) { - errorOk(); -} diff --git a/src/duskvita/asset/assetvita.h b/src/duskvita/asset/assetvita.h deleted file mode 100644 index 82d2bbbf..00000000 --- a/src/duskvita/asset/assetvita.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "error/error.h" - -// dusk.dsk is packaged at the root of the VPK and accessible via app0:/ -#define ASSET_VITA_DSK_PATH "app0:/" ASSET_FILE_NAME - -typedef struct { - uint8_t _unused; -} assetvita_t; - -/** - * Initializes the Vita asset system, opening dusk.dsk from the VPK mount. - * - * @returns An errorret_t indicating success or failure. - */ -errorret_t assetInitVita(void); - -/** - * Disposes the Vita asset system. - * - * @returns An errorret_t indicating success or failure. - */ -errorret_t assetDisposeVita(void); diff --git a/src/duskvita/script/module/moduleplatformplatform.h b/src/duskvita/script/module/moduleplatformplatform.h deleted file mode 100644 index b4c40956..00000000 --- a/src/duskvita/script/module/moduleplatformplatform.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "moduleplatformvita.h" - -#define modulePlatformPlatform modulePlatformVita diff --git a/src/duskvita/script/module/moduleplatformvita.h b/src/duskvita/script/module/moduleplatformvita.h deleted file mode 100644 index 4d58a148..00000000 --- a/src/duskvita/script/module/moduleplatformvita.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "script/scriptmanager.h" - -void modulePlatformVita(void) { - scriptManagerExec("VITA = true\n", NULL); -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 226d9346..c172972d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,6 @@ # https://opensource.org/licenses/MIT add_subdirectory(assert) -add_subdirectory(asset) add_subdirectory(error) add_subdirectory(thread) add_subdirectory(display) diff --git a/test/asset/CMakeLists.txt b/test/asset/CMakeLists.txt deleted file mode 100644 index f0f5c424..00000000 --- a/test/asset/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2026 Dominic Masters -# -# This software is released under the MIT License. -# https://opensource.org/licenses/MIT - -include(dusktest) - -dusktest(test_assetlocale.c) -dusktest(test_asset.c) -dusktest(test_assetjsonloader.c) -dusktest(test_assettilesetloader.c) diff --git a/test/asset/test_asset.c b/test/asset/test_asset.c deleted file mode 100644 index a64c3197..00000000 --- a/test/asset/test_asset.c +++ /dev/null @@ -1,514 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "dusktest.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "util/memory.h" -#include "util/string.h" - -// ============================================================ -// Stub loader callbacks -// ============================================================ - -static errorret_t stub_load_success(assetloading_t *loading) { - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -static errorret_t stub_load_fail(assetloading_t *loading) { - loading->entry->state = ASSET_ENTRY_STATE_ERROR; - errorThrow("Stub loader failed"); -} - -static errorret_t stub_dispose(assetentry_t *entry) { - errorOk(); -} - -// ============================================================ -// Per-test setup / teardown -// ============================================================ - -static assetloadercallbacks_t saved_callbacks[ASSET_LOADER_TYPE_COUNT]; - -static int asset_setup(void **state) { - // Save real callbacks so we can restore them in teardown. - memoryCopy(saved_callbacks, ASSET_LOADER_CALLBACKS, sizeof(saved_callbacks)); - - // Manually init ASSET — no thread, no ZIP. - memoryZero(&ASSET, sizeof(ASSET)); - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - threadMutexInit(&ASSET.loading[i].mutex); - } - - // Replace all loader callbacks with stubs. - for(int i = 0; i < ASSET_LOADER_TYPE_COUNT; i++) { - ASSET_LOADER_CALLBACKS[i].loadSync = stub_load_success; - ASSET_LOADER_CALLBACKS[i].dispose = stub_dispose; - } - - return 0; -} - -static int asset_teardown(void **state) { - // Dispose any entries that tests left behind. - for(int i = 0; i < ASSET_ENTRY_COUNT_MAX; i++) { - if(ASSET.entries[i].type != ASSET_LOADER_TYPE_NULL) { - errorret_t ret = assetEntryDispose(&ASSET.entries[i]); - if(errorIsNotOk(ret)) errorCatch(ret); - } - } - - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - threadMutexDispose(&ASSET.loading[i].mutex); - } - - // Restore real callbacks before zeroing state. - memoryCopy(ASSET_LOADER_CALLBACKS, saved_callbacks, sizeof(saved_callbacks)); - memoryZero(&ASSET, sizeof(ASSET)); - return 0; -} - -// ============================================================ -// Helper: find which loading slot owns a given entry -// ============================================================ - -static bool_t loading_slot_has_entry(const assetentry_t *entry) { - for(size_t i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - if(ASSET.loading[i].entry == entry) return true; - } - return false; -} - -// ============================================================ -// assetGetEntry tests -// ============================================================ - -static void test_getEntry_creates_new(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assert_non_null(entry); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED); - assert_int_equal(entry->type, ASSET_LOADER_TYPE_LOCALE); - assert_true(stringEquals(entry->name, "test.locale")); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getEntry_dedup(void **state) { - assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assetentry_t *b = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assert_ptr_equal(a, b); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getEntry_distinct_names(void **state) { - assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assetentry_t *b = assetGetEntry("b.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assert_ptr_not_equal(a, b); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetUpdate — state machine tests -// ============================================================ - -static void test_update_entry_reaches_loaded(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED); - - assetEntryLock(entry); - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(entry); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_slot_occupied_after_first_update(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(entry); - assetUpdate(); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - - // Slot not cleared until the second update processes the LOADED case. - assert_true(loading_slot_has_entry(entry)); - assetEntryUnlock(entry); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_slot_cleared_after_second_update(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(entry); - assetUpdate(); - assetUpdate(); - - assert_false(loading_slot_has_entry(entry)); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(entry); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_four_slots_fill_independently(void **state) { - // ASSET_LOADING_COUNT_MAX concurrent entries should all load in one pass. - assetentry_t *entries[ASSET_LOADING_COUNT_MAX]; - for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - char_t name[ASSET_FILE_NAME_MAX]; - snprintf(name, sizeof(name), "asset%d.locale", i); - entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL); - assetEntryLock(entries[i]); - } - - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - - for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(entries[i]); - } - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_error_state(void **state) { - ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = stub_load_fail; - - assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - // First update: dispatches and calls the failing stub. - // assetUpdate itself returns OK here; the error from loadSync is caught internally. - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR); - - // Second update: sees ERROR state and propagates it. - ret = assetUpdate(); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_noop_on_empty_table(void **state) { - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_loaded_entry_not_redispatched(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(entry); - assetUpdate(); - assetUpdate(); // slot freed - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - - // Further updates must not re-dispatch or modify a LOADED entry. - assetUpdate(); - assetUpdate(); - assetUpdate(); - - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - assert_false(loading_slot_has_entry(entry)); - assetEntryUnlock(entry); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_overflow_queues_entries(void **state) { - // Create one more entry than there are loading slots. - const int TOTAL = ASSET_LOADING_COUNT_MAX + 1; - assetentry_t *entries[ASSET_LOADING_COUNT_MAX + 1]; - - for(int i = 0; i < TOTAL; i++) { - char_t name[ASSET_FILE_NAME_MAX]; - snprintf(name, sizeof(name), "asset%d.locale", i); - entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL); - } - - // Lock first batch so the reaper won't collect them before we check. - for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assetEntryLock(entries[i]); - } - - // Update 1: fills all slots, first ASSET_LOADING_COUNT_MAX entries reach LOADED. - // The overflow entry has no slot yet and stays NOT_STARTED. - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED); - } - assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED); - - // Unlock first batch — the reaper can collect them in update 2. - for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) { - assetEntryUnlock(entries[i]); - } - - // Update 2: LOADED slots are freed. Overflow entry still NOT_STARTED because - // the dispatch phase ran before the slots were cleared this turn. - ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED); - - // Lock overflow entry so it stays LOADED after update 3. - assetEntryLock(entries[ASSET_LOADING_COUNT_MAX]); - - // Update 3: now a slot is available; the overflow entry is dispatched and loaded. - ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_LOADED); - - assetEntryUnlock(entries[ASSET_LOADING_COUNT_MAX]); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_update_error_slot_stays_occupied(void **state) { - ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = stub_load_fail; - - assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetUpdate(); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR); - - // Unlike LOADED, the ERROR case does NOT clear the slot — it throws instead. - assert_true(loading_slot_has_entry(entry)); - - errorret_t ret = assetUpdate(); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetEntryInit — input copy -// ============================================================ - -static void test_getEntry_null_input_stays_null(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assert_null(entry->input); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getEntry_input_copied_into_entry(void **state) { - assetloaderinput_t input; - memoryZero(&input, sizeof(input)); - input.texture = (textureformat_t)42; - - assetentry_t *entry = assetGetEntry("test.texture", ASSET_LOADER_TYPE_TEXTURE, &input); - - // input must have been copied — entry->input must point inside the entry. - assert_non_null(entry->input); - assert_ptr_equal(entry->input, &entry->inputData); - assert_int_equal((int)entry->input->texture, 42); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetUpdate — re-entrant sync loader -// ============================================================ - -static assetentry_t *reentrant_inner_entry = NULL; -static bool_t reentrant_inner_loaded = false; - -static errorret_t reentrant_stub_load(assetloading_t *loading) { - if(reentrant_inner_entry == NULL) { - // Simulate a script loading another asset from within its own sync step. - reentrant_inner_entry = assetGetEntry( - "inner.json", ASSET_LOADER_TYPE_JSON, NULL - ); - errorret_t inner_ret = assetRequireLoaded(reentrant_inner_entry); - reentrant_inner_loaded = errorIsOk(inner_ret); - if(errorIsNotOk(inner_ret)) errorChain(inner_ret); - } - loading->entry->state = ASSET_ENTRY_STATE_LOADED; - errorOk(); -} - -static void test_update_reentrant_sync_loader(void **state) { - reentrant_inner_entry = NULL; - reentrant_inner_loaded = false; - // LOCALE uses the re-entrant loader; JSON keeps the default stub_load_success. - ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = reentrant_stub_load; - - assetentry_t *outer = assetGetEntry("outer.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - errorret_t ret = assetRequireLoaded(outer); - assert_true(errorIsOk(ret)); - assert_int_equal(outer->state, ASSET_ENTRY_STATE_LOADED); - // Verify the re-entrant load ran and completed successfully. - // (The inner entry may have been reaped by the time we check here, - // so we capture the result inside the callback rather than reading state.) - assert_non_null(reentrant_inner_entry); - assert_true(reentrant_inner_loaded); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetGetEntry — dedup against non-NOT_STARTED entries -// ============================================================ - -static void test_getEntry_returns_loaded_entry(void **state) { - assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(a); - assetUpdate(); - assert_int_equal(a->state, ASSET_ENTRY_STATE_LOADED); - - // A second request for the same name must return the same entry even though - // it is already LOADED rather than NOT_STARTED. - assetentry_t *b = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assert_ptr_equal(a, b); - assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(a); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetEntryDispose tests -// ============================================================ - -static void test_entry_dispose_clears_entry(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(entry); - assetUpdate(); - assetUpdate(); // ensure loading slot is freed before disposing - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(entry); - - errorret_t ret = assetEntryDispose(entry); - assert_true(errorIsOk(ret)); - assert_int_equal(entry->type, ASSET_LOADER_TYPE_NULL); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_entry_dispose_slot_reusable(void **state) { - assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(a); - assetUpdate(); - assetUpdate(); - assetEntryUnlock(a); - assert_false(loading_slot_has_entry(a)); - - assetEntryDispose(a); - assert_int_equal(a->type, ASSET_LOADER_TYPE_NULL); - - // The freed slot should now accept a new entry. - assetentry_t *b = assetGetEntry("b.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assert_non_null(b); - assert_int_equal(b->state, ASSET_ENTRY_STATE_NOT_STARTED); - - assetEntryLock(b); - errorret_t ret = assetUpdate(); - assert_true(errorIsOk(ret)); - assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(b); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetRequireLoaded tests -// ============================================================ - -static void test_requireLoaded_already_loaded(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - assetEntryLock(entry); - assetUpdate(); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - assetEntryUnlock(entry); - - // Should return immediately without calling assetUpdate again. - errorret_t ret = assetRequireLoaded(entry); - assert_true(errorIsOk(ret)); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_requireLoaded_spins_to_loaded(void **state) { - assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED); - - // requireLoaded calls assetUpdate internally until LOADED. - errorret_t ret = assetRequireLoaded(entry); - assert_true(errorIsOk(ret)); - assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_requireLoaded_propagates_error(void **state) { - ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_LOCALE].loadSync = stub_load_fail; - - assetentry_t *entry = assetGetEntry("fail.locale", ASSET_LOADER_TYPE_LOCALE, NULL); - - // requireLoaded spins assetUpdate until LOADED — but the loader always fails, - // so the second assetUpdate sees ERROR and throws, which errorChain propagates. - errorret_t ret = assetRequireLoaded(entry); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(entry->state, ASSET_ENTRY_STATE_ERROR); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// main -// ============================================================ - -int main(void) { - assertInit(); - const struct CMUnitTest tests[] = { - // getEntry - cmocka_unit_test_setup_teardown(test_getEntry_creates_new, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_getEntry_dedup, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_getEntry_distinct_names, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_getEntry_returns_loaded_entry, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_getEntry_null_input_stays_null, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_getEntry_input_copied_into_entry, asset_setup, asset_teardown), - - // assetUpdate — state machine - cmocka_unit_test_setup_teardown(test_update_noop_on_empty_table, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_entry_reaches_loaded, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_slot_occupied_after_first_update, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_slot_cleared_after_second_update, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_four_slots_fill_independently, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_loaded_entry_not_redispatched, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_overflow_queues_entries, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_error_state, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_error_slot_stays_occupied, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_update_reentrant_sync_loader, asset_setup, asset_teardown), - - // assetEntryDispose - cmocka_unit_test_setup_teardown(test_entry_dispose_clears_entry, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_entry_dispose_slot_reusable, asset_setup, asset_teardown), - - // assetRequireLoaded - cmocka_unit_test_setup_teardown(test_requireLoaded_already_loaded, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_requireLoaded_spins_to_loaded, asset_setup, asset_teardown), - cmocka_unit_test_setup_teardown(test_requireLoaded_propagates_error, asset_setup, asset_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/asset/test_assetjsonloader.c b/test/asset/test_assetjsonloader.c deleted file mode 100644 index 52756b2a..00000000 --- a/test/asset/test_assetjsonloader.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "dusktest.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/json/assetjsonloader.h" -#include "thread/thread.h" -#include "util/memory.h" -#include - -// ============================================================ -// Fixtures -// ============================================================ - -static const char_t *JSON_VALID = "{\"hello\":\"world\",\"count\":42}"; -static const char_t *JSON_INVALID = "{ this is definitely not valid json !!!"; - -// ============================================================ -// Async thread helper -// ============================================================ - -typedef struct { - assetloading_t *loading; - bool_t ok; -} json_async_run_t; - -static void json_async_thread_cb(thread_t *thread) { - json_async_run_t *run = (json_async_run_t *)thread->data; - errorret_t ret = assetJsonLoaderAsync(run->loading); - run->ok = errorIsOk(ret); - if(errorIsNotOk(ret)) errorCatch(ret); -} - -static bool_t run_json_async(assetloading_t *loading) { - json_async_run_t run = { .loading = loading, .ok = false }; - thread_t thread; - threadInit(&thread, json_async_thread_cb); - thread.data = &run; - threadStart(&thread); - threadStop(&thread); - return run.ok; -} - -// ============================================================ -// In-memory ZIP -// ============================================================ - -static zip_t *g_zip = NULL; - -static int zip_setup(void **state) { - zip_error_t err; - zip_error_init(&err); - - zip_source_t *write_src = zip_source_buffer_create(NULL, 0, 1, &err); - if(!write_src) return -1; - - zip_t *za = zip_open_from_source(write_src, ZIP_TRUNCATE, &err); - if(!za) { zip_source_free(write_src); return -1; } - - // valid.json - zip_source_t *s; - s = zip_source_buffer(za, JSON_VALID, strlen(JSON_VALID), 0); - if(zip_file_add(za, "valid.json", s, ZIP_FL_OVERWRITE) < 0) { - zip_close(za); return -1; - } - - // invalid.json - s = zip_source_buffer(za, JSON_INVALID, strlen(JSON_INVALID), 0); - if(zip_file_add(za, "invalid.json", s, ZIP_FL_OVERWRITE) < 0) { - zip_close(za); return -1; - } - - zip_source_keep(write_src); - if(zip_close(za) != 0) { zip_source_free(write_src); return -1; } - - zip_stat_t zs; - memset(&zs, 0, sizeof(zs)); - if(zip_source_stat(write_src, &zs) != 0 || !(zs.valid & ZIP_STAT_SIZE)) { - zip_source_free(write_src); return -1; - } - - void *zipbuf = malloc((size_t)zs.size); - if(!zipbuf) { zip_source_free(write_src); return -1; } - - if(zip_source_open(write_src) != 0) { - free(zipbuf); zip_source_free(write_src); return -1; - } - zip_source_read(write_src, zipbuf, (zip_uint64_t)zs.size); - zip_source_close(write_src); - zip_source_free(write_src); - - zip_error_init(&err); - zip_source_t *read_src = zip_source_buffer_create( - zipbuf, (zip_uint64_t)zs.size, 1, &err - ); - if(!read_src) { free(zipbuf); return -1; } - - g_zip = zip_open_from_source(read_src, 0, &err); - if(!g_zip) { zip_source_free(read_src); return -1; } - - ASSET.zip = g_zip; - return 0; -} - -static int zip_teardown(void **state) { - if(g_zip) { zip_close(g_zip); g_zip = NULL; } - ASSET.zip = NULL; - return 0; -} - -// ============================================================ -// Loader pipeline helper -// ============================================================ - -typedef struct { - assetentry_t entry; - assetloading_t loading; -} loader_ctx_t; - -static void loader_ctx_init(loader_ctx_t *ctx, const char_t *name) { - assetEntryInit(&ctx->entry, name, ASSET_LOADER_TYPE_JSON, NULL); - threadMutexInit(&ctx->loading.mutex); - memoryZero(&ctx->loading.loading, sizeof(ctx->loading.loading)); - ctx->loading.type = ASSET_LOADER_TYPE_JSON; - ctx->loading.entry = &ctx->entry; - ctx->entry.state = ASSET_ENTRY_STATE_PENDING_SYNC; -} - -// Drives sync(INITIAL) -> async(READ) -> sync(PARSE). -static errorret_t loader_ctx_run(loader_ctx_t *ctx) { - errorret_t ret = assetJsonLoaderSync(&ctx->loading); - if(errorIsNotOk(ret)) return ret; - - if(!run_json_async(&ctx->loading)) { - ctx->entry.state = ASSET_ENTRY_STATE_ERROR; - errorThrow("Async JSON load failed"); - } - - return assetJsonLoaderSync(&ctx->loading); -} - -static void loader_ctx_dispose(loader_ctx_t *ctx) { - if(ctx->entry.type != ASSET_LOADER_TYPE_NULL) { - errorret_t ret = assetEntryDispose(&ctx->entry); - if(errorIsNotOk(ret)) errorCatch(ret); - } - threadMutexDispose(&ctx->loading.mutex); -} - -// ============================================================ -// Tests -// ============================================================ - -static void test_json_valid_loads(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "valid.json"); - - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsOk(ret)); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_LOADED); - assert_non_null(ctx.entry.data.json); - - // Verify content is accessible - yyjson_val *root = yyjson_doc_get_root(ctx.entry.data.json); - assert_non_null(root); - assert_true(yyjson_is_obj(root)); - - yyjson_val *hello = yyjson_obj_get(root, "hello"); - assert_non_null(hello); - assert_string_equal(yyjson_get_str(hello), "world"); - - yyjson_val *count = yyjson_obj_get(root, "count"); - assert_non_null(count); - assert_int_equal((int)yyjson_get_int(count), 42); - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_json_parse_error(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "invalid.json"); - - // Async read succeeds; sync parse fails because yyjson rejects the content. - errorret_t ret = assetJsonLoaderSync(&ctx.loading); - assert_true(errorIsOk(ret)); - - assert_true(run_json_async(&ctx.loading)); - - ret = assetJsonLoaderSync(&ctx.loading); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - assert_null(ctx.entry.data.json); - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_json_missing_file(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "nonexistent.json"); - - errorret_t ret = assetJsonLoaderSync(&ctx.loading); - assert_true(errorIsOk(ret)); - - // Async phase stat-fails because the file isn't in the ZIP. - assert_false(run_json_async(&ctx.loading)); - - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_json_buffer_cleared_after_load(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "valid.json"); - - loader_ctx_run(&ctx); - - // The scratch buffer must be freed by the time sync returns LOADED. - assert_null(ctx.loading.loading.json.buffer); - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// main -// ============================================================ - -int main(void) { - assertInit(); - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(test_json_valid_loads, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_json_parse_error, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_json_missing_file, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_json_buffer_cleared_after_load, zip_setup, zip_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/asset/test_assetlocale.c b/test/asset/test_assetlocale.c deleted file mode 100644 index fd17d9fc..00000000 --- a/test/asset/test_assetlocale.c +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "dusktest.h" -#include "asset/loader/locale/assetlocaleloader.h" -#include "asset/asset.h" -#include "util/memory.h" -#include - -// ============================================================ -// Test locale file (gettext PO format) -// ============================================================ - -static const char_t *LOCALE_EN = - "msgid \"\"\n" - "msgstr \"\"\n" - "\"Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\\n\"\n" - "\n" - "msgid \"greeting\"\n" - "msgstr \"Hello, World!\"\n" - "\n" - "msgid \"item\"\n" - "msgid_plural \"items\"\n" - "msgstr[0] \"one item\"\n" - "msgstr[1] \"many items\"\n" - "\n" - "msgid \"score\"\n" - "msgstr \"Score: %d\"\n" - "\n" - "msgid \"player\"\n" - "msgstr \"Player: %s\"\n"; - -// ============================================================ -// In-memory ZIP fixture (shared across all ZIP-based tests) -// ============================================================ - -static zip_t *g_zip = NULL; -static assetlocalefile_t g_locale; - -static int locale_setup(void **state) { - zip_error_t err; - zip_error_init(&err); - - // Phase 1: write the zip to a growable buffer source - zip_source_t *write_src = zip_source_buffer_create(NULL, 0, 1, &err); - if(!write_src) return -1; - - zip_t *za = zip_open_from_source(write_src, ZIP_TRUNCATE, &err); - if(!za) { zip_source_free(write_src); return -1; } - - size_t flen = strlen(LOCALE_EN); - zip_source_t *fs = zip_source_buffer(za, LOCALE_EN, flen, 0); - if(zip_file_add(za, "en.locale", fs, ZIP_FL_OVERWRITE) < 0) { - zip_close(za); return -1; - } - - // Keep write_src alive after zip_close so we can read the bytes back out - zip_source_keep(write_src); - if(zip_close(za) != 0) { zip_source_free(write_src); return -1; } - - // Phase 2: extract the raw zip bytes from the write buffer. - // zip_source_stat must be called before zip_source_open on a written source. - zip_stat_t zs; - memset(&zs, 0, sizeof(zs)); - if(zip_source_stat(write_src, &zs) != 0 || !(zs.valid & ZIP_STAT_SIZE)) { - zip_source_free(write_src); return -1; - } - - void *zipbuf = malloc((size_t)zs.size); - if(!zipbuf) { zip_source_free(write_src); return -1; } - - if(zip_source_open(write_src) != 0) { - free(zipbuf); zip_source_free(write_src); return -1; - } - zip_source_read(write_src, zipbuf, (zip_uint64_t)zs.size); - zip_source_close(write_src); - zip_source_free(write_src); - - // Phase 3: open a fresh read-only archive from the extracted bytes. - // The archive takes ownership of the source (and thus zipbuf via freep=1). - zip_error_init(&err); - zip_source_t *read_src = zip_source_buffer_create( - zipbuf, (zip_uint64_t)zs.size, 1, &err - ); - if(!read_src) { free(zipbuf); return -1; } - - g_zip = zip_open_from_source(read_src, 0, &err); - if(!g_zip) { zip_source_free(read_src); return -1; } - - ASSET.zip = g_zip; - - // Init locale file and parse the header - memoryZero(&g_locale, sizeof(g_locale)); - errorret_t ret = assetFileInit(&g_locale.file, "en.locale", NULL, NULL); - if(errorIsNotOk(ret)) { errorCatch(ret); goto fail; } - - ret = assetFileOpen(&g_locale.file); - if(errorIsNotOk(ret)) { errorCatch(ret); goto fail; } - - char_t header[512]; - ret = assetLocaleGetString(&g_locale, "", 0, header, sizeof(header)); - if(errorIsNotOk(ret)) { errorCatch(ret); assetFileClose(&g_locale.file); goto fail; } - - ret = assetLocaleParseHeader(&g_locale, header, sizeof(header)); - if(errorIsNotOk(ret)) { errorCatch(ret); assetFileClose(&g_locale.file); goto fail; } - - return 0; - -fail: - zip_close(g_zip); g_zip = NULL; - ASSET.zip = NULL; - return -1; -} - -static int locale_teardown(void **state) { - if(g_locale.file.zipFile != NULL) { - errorret_t ret = assetFileClose(&g_locale.file); - if(errorIsNotOk(ret)) errorCatch(ret); - } - - if(g_zip != NULL) { - zip_close(g_zip); // also frees the read_src and zipbuf - g_zip = NULL; - } - - ASSET.zip = NULL; - memoryZero(&g_locale, sizeof(g_locale)); - return 0; -} - -// ============================================================ -// assetLocaleParseHeader — pure tests (no ZIP required) -// ============================================================ - -static void test_parseHeader_english(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsOk(ret)); - assert_int_equal(locale.pluralStateCount, 2); - assert_int_equal(locale.pluralDefaultIndex, 0); - assert_int_equal(locale.pluralOps[0], ASSET_LOCALE_PLURAL_OP_NOT_EQUAL); - assert_int_equal(locale.pluralValues[0], 1); - assert_int_equal(locale.pluralIndices[0], 1); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_singular(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=1; plural=(0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsOk(ret)); - assert_int_equal(locale.pluralStateCount, 1); - assert_int_equal(locale.pluralDefaultIndex, 0); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_less_than(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n < 2 ? 0 : 1);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsOk(ret)); - assert_int_equal(locale.pluralStateCount, 2); - assert_int_equal(locale.pluralOps[0], ASSET_LOCALE_PLURAL_OP_LESS); - assert_int_equal(locale.pluralValues[0], 2); - assert_int_equal(locale.pluralIndices[0], 0); - assert_int_equal(locale.pluralDefaultIndex, 1); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_greater_equal(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n >= 2 ? 1 : 0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsOk(ret)); - assert_int_equal(locale.pluralStateCount, 2); - assert_int_equal(locale.pluralOps[0], ASSET_LOCALE_PLURAL_OP_GREATER_EQUAL); - assert_int_equal(locale.pluralValues[0], 2); - assert_int_equal(locale.pluralIndices[0], 1); - assert_int_equal(locale.pluralDefaultIndex, 0); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_no_plural_forms(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Content-Type: text/plain; charset=UTF-8\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsOk(ret)); - assert_int_equal(locale.pluralStateCount, 0); - - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_error_nplurals_zero(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=0; plural=(0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_error_nplurals_too_large(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: nplurals=7; plural=(0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_parseHeader_error_missing_nplurals(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - - char_t hdr[] = "Plural-Forms: plural=(0);\n"; - errorret_t ret = assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetLocaleEvaluatePlural — pure tests -// ============================================================ - -static void test_evaluatePlural_english_singular(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"; - assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_int_equal(assetLocaleEvaluatePlural(&locale, 1), 0); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_evaluatePlural_english_plural(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n != 1 ? 1 : 0);\n"; - assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_int_equal(assetLocaleEvaluatePlural(&locale, 0), 1); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 2), 1); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 100), 1); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_evaluatePlural_singular_only(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - char_t hdr[] = "Plural-Forms: nplurals=1; plural=(0);\n"; - assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_int_equal(assetLocaleEvaluatePlural(&locale, 0), 0); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 1), 0); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 99), 0); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_evaluatePlural_less_than_boundary(void **state) { - assetlocalefile_t locale; - memoryZero(&locale, sizeof(locale)); - char_t hdr[] = "Plural-Forms: nplurals=2; plural=(n < 2 ? 0 : 1);\n"; - assetLocaleParseHeader(&locale, hdr, sizeof(hdr)); - - assert_int_equal(assetLocaleEvaluatePlural(&locale, 0), 0); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 1), 0); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 2), 1); - assert_int_equal(assetLocaleEvaluatePlural(&locale, 10), 1); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetLocaleGetString — ZIP-based tests -// ============================================================ - -static void test_getString_simple(void **state) { - char_t result[256]; - errorret_t ret = assetLocaleGetString(&g_locale, "greeting", 0, result, sizeof(result)); - - assert_true(errorIsOk(ret)); - assert_string_equal(result, "Hello, World!"); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getString_plural_singular(void **state) { - char_t result[256]; - errorret_t ret = assetLocaleGetString(&g_locale, "item", 1, result, sizeof(result)); - - assert_true(errorIsOk(ret)); - assert_string_equal(result, "one item"); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getString_plural_many(void **state) { - char_t result[256]; - errorret_t ret = assetLocaleGetString(&g_locale, "item", 5, result, sizeof(result)); - - assert_true(errorIsOk(ret)); - assert_string_equal(result, "many items"); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getString_multiple_calls(void **state) { - char_t a[256], b[256]; - errorret_t ret = assetLocaleGetString(&g_locale, "greeting", 0, a, sizeof(a)); - assert_true(errorIsOk(ret)); - - // Second call rewinds the file and re-reads from scratch. - ret = assetLocaleGetString(&g_locale, "greeting", 0, b, sizeof(b)); - assert_true(errorIsOk(ret)); - - assert_string_equal(a, b); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getString_missing_id(void **state) { - char_t result[256]; - errorret_t ret = assetLocaleGetString(&g_locale, "nonexistent", 0, result, sizeof(result)); - - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// assetLocaleGetStringWithArgs — ZIP-based tests -// ============================================================ - -static void test_getStringWithArgs_int(void **state) { - assetlocalearg_t args[] = { - { .type = ASSET_LOCALE_ARG_INT, .intValue = 42 } - }; - char_t result[256]; - errorret_t ret = assetLocaleGetStringWithArgs( - &g_locale, "score", 0, result, sizeof(result), args, 1 - ); - - assert_true(errorIsOk(ret)); - assert_string_equal(result, "Score: 42"); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_getStringWithArgs_string(void **state) { - assetlocalearg_t args[] = { - { .type = ASSET_LOCALE_ARG_STRING, .stringValue = "Alice" } - }; - char_t result[256]; - errorret_t ret = assetLocaleGetStringWithArgs( - &g_locale, "player", 0, result, sizeof(result), args, 1 - ); - - assert_true(errorIsOk(ret)); - assert_string_equal(result, "Player: Alice"); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// main -// ============================================================ - -int main(void) { - const struct CMUnitTest tests[] = { - // parseHeader — pure - cmocka_unit_test(test_parseHeader_english), - cmocka_unit_test(test_parseHeader_singular), - cmocka_unit_test(test_parseHeader_less_than), - cmocka_unit_test(test_parseHeader_greater_equal), - cmocka_unit_test(test_parseHeader_no_plural_forms), - cmocka_unit_test(test_parseHeader_error_nplurals_zero), - cmocka_unit_test(test_parseHeader_error_nplurals_too_large), - cmocka_unit_test(test_parseHeader_error_missing_nplurals), - - // evaluatePlural — pure - cmocka_unit_test(test_evaluatePlural_english_singular), - cmocka_unit_test(test_evaluatePlural_english_plural), - cmocka_unit_test(test_evaluatePlural_singular_only), - cmocka_unit_test(test_evaluatePlural_less_than_boundary), - - // getString — in-memory ZIP - cmocka_unit_test_setup_teardown(test_getString_simple, locale_setup, locale_teardown), - cmocka_unit_test_setup_teardown(test_getString_plural_singular, locale_setup, locale_teardown), - cmocka_unit_test_setup_teardown(test_getString_plural_many, locale_setup, locale_teardown), - cmocka_unit_test_setup_teardown(test_getString_multiple_calls, locale_setup, locale_teardown), - cmocka_unit_test_setup_teardown(test_getString_missing_id, locale_setup, locale_teardown), - - // getStringWithArgs — in-memory ZIP - cmocka_unit_test_setup_teardown(test_getStringWithArgs_int, locale_setup, locale_teardown), - cmocka_unit_test_setup_teardown(test_getStringWithArgs_string, locale_setup, locale_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/test/asset/test_assettilesetloader.c b/test/asset/test_assettilesetloader.c deleted file mode 100644 index 2b7f9f4f..00000000 --- a/test/asset/test_assettilesetloader.c +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "dusktest.h" -#include "asset/asset.h" -#include "asset/loader/assetloader.h" -#include "asset/loader/assetentry.h" -#include "asset/loader/display/assettilesetloader.h" -#include "thread/thread.h" -#include "util/memory.h" -#include - -// ============================================================ -// DTF binary fixtures -// DTF layout: -// [0-2] magic "DTF" -// [3] version 0x00 -// [4-5] tileWidth (uint16 LE) -// [6-7] tileHeight (uint16 LE) -// [8-9] columns (uint16 LE) -// [10-11] rows (uint16 LE) -// [12-15] reserved -// [16-19] uv[0] (float LE) -// [20-23] uv[1] (float LE, must be 0.0–1.0) -// ============================================================ - -// uv[1] = 0.5f = 0x3F000000 LE = {0x00,0x00,0x00,0x3F} -// uv[1] = 2.0f = 0x40000000 LE = {0x00,0x00,0x00,0x40} (out-of-range) - -static const uint8_t DTF_VALID[] = { - 'D','T','F', 0x00, - 16, 0, - 16, 0, - 4, 0, - 4, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_BAD_MAGIC[] = { - 'X','Y','Z', 0x00, - 16, 0, 16, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_BAD_VERSION[] = { - 'D','T','F', 0xFF, - 16, 0, 16, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_ZERO_WIDTH[] = { - 'D','T','F', 0x00, - 0, 0, - 16, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_ZERO_HEIGHT[] = { - 'D','T','F', 0x00, - 16, 0, - 0, 0, - 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_ZERO_COLUMNS[] = { - 'D','T','F', 0x00, - 16, 0, 16, 0, - 0, 0, - 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -static const uint8_t DTF_ZERO_ROWS[] = { - 'D','T','F', 0x00, - 16, 0, 16, 0, 4, 0, - 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, 0x00, 0x3F -}; - -// uv[1] = 2.0f, which fails the 0.0–1.0 range check -static const uint8_t DTF_INVALID_UV[] = { - 'D','T','F', 0x00, - 16, 0, 16, 0, 4, 0, 4, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0x00, 0x00, 0x00, 0x40 -}; - -// ============================================================ -// Async thread helper -// ============================================================ - -typedef struct { - assetloading_t *loading; - bool_t ok; -} tileset_async_run_t; - -static void tileset_async_thread_cb(thread_t *thread) { - tileset_async_run_t *run = (tileset_async_run_t *)thread->data; - errorret_t ret = assetTilesetLoaderAsync(run->loading); - run->ok = errorIsOk(ret); - if(errorIsNotOk(ret)) errorCatch(ret); -} - -static bool_t run_tileset_async(assetloading_t *loading) { - tileset_async_run_t run = { .loading = loading, .ok = false }; - thread_t thread; - threadInit(&thread, tileset_async_thread_cb); - thread.data = &run; - threadStart(&thread); - threadStop(&thread); - return run.ok; -} - -// ============================================================ -// In-memory ZIP -// ============================================================ - -static zip_t *g_zip = NULL; - -static int tileset_zip_add(zip_t *za, const char_t *name, const void *data, size_t len) { - zip_source_t *s = zip_source_buffer(za, data, len, 0); - return (int)zip_file_add(za, name, s, ZIP_FL_OVERWRITE); -} - -static int zip_setup(void **state) { - zip_error_t err; - zip_error_init(&err); - - zip_source_t *write_src = zip_source_buffer_create(NULL, 0, 1, &err); - if(!write_src) return -1; - - zip_t *za = zip_open_from_source(write_src, ZIP_TRUNCATE, &err); - if(!za) { zip_source_free(write_src); return -1; } - - if(tileset_zip_add(za, "valid.tileset", DTF_VALID, sizeof(DTF_VALID)) < 0 || - tileset_zip_add(za, "badmagic.tileset", DTF_BAD_MAGIC, sizeof(DTF_BAD_MAGIC)) < 0 || - tileset_zip_add(za, "badversion.tileset", DTF_BAD_VERSION, sizeof(DTF_BAD_VERSION)) < 0 || - tileset_zip_add(za, "zerowidth.tileset", DTF_ZERO_WIDTH, sizeof(DTF_ZERO_WIDTH)) < 0 || - tileset_zip_add(za, "zeroheight.tileset", DTF_ZERO_HEIGHT, sizeof(DTF_ZERO_HEIGHT)) < 0 || - tileset_zip_add(za, "zerocolumns.tileset", DTF_ZERO_COLUMNS, sizeof(DTF_ZERO_COLUMNS)) < 0 || - tileset_zip_add(za, "zerorows.tileset", DTF_ZERO_ROWS, sizeof(DTF_ZERO_ROWS)) < 0 || - tileset_zip_add(za, "invaliduv.tileset", DTF_INVALID_UV, sizeof(DTF_INVALID_UV)) < 0) { - zip_close(za); return -1; - } - - zip_source_keep(write_src); - if(zip_close(za) != 0) { zip_source_free(write_src); return -1; } - - zip_stat_t zs; - memset(&zs, 0, sizeof(zs)); - if(zip_source_stat(write_src, &zs) != 0 || !(zs.valid & ZIP_STAT_SIZE)) { - zip_source_free(write_src); return -1; - } - - void *zipbuf = malloc((size_t)zs.size); - if(!zipbuf) { zip_source_free(write_src); return -1; } - - if(zip_source_open(write_src) != 0) { - free(zipbuf); zip_source_free(write_src); return -1; - } - zip_source_read(write_src, zipbuf, (zip_uint64_t)zs.size); - zip_source_close(write_src); - zip_source_free(write_src); - - zip_error_init(&err); - zip_source_t *read_src = zip_source_buffer_create( - zipbuf, (zip_uint64_t)zs.size, 1, &err - ); - if(!read_src) { free(zipbuf); return -1; } - - g_zip = zip_open_from_source(read_src, 0, &err); - if(!g_zip) { zip_source_free(read_src); return -1; } - - ASSET.zip = g_zip; - return 0; -} - -static int zip_teardown(void **state) { - if(g_zip) { zip_close(g_zip); g_zip = NULL; } - ASSET.zip = NULL; - return 0; -} - -// ============================================================ -// Loader pipeline helper -// ============================================================ - -typedef struct { - assetentry_t entry; - assetloading_t loading; -} loader_ctx_t; - -static void loader_ctx_init(loader_ctx_t *ctx, const char_t *name) { - assetEntryInit(&ctx->entry, name, ASSET_LOADER_TYPE_TILESET, NULL); - threadMutexInit(&ctx->loading.mutex); - memoryZero(&ctx->loading.loading, sizeof(ctx->loading.loading)); - ctx->loading.type = ASSET_LOADER_TYPE_TILESET; - ctx->loading.entry = &ctx->entry; - ctx->entry.state = ASSET_ENTRY_STATE_PENDING_SYNC; -} - -static errorret_t loader_ctx_run(loader_ctx_t *ctx) { - errorret_t ret = assetTilesetLoaderSync(&ctx->loading); - if(errorIsNotOk(ret)) return ret; - - if(!run_tileset_async(&ctx->loading)) { - ctx->entry.state = ASSET_ENTRY_STATE_ERROR; - errorThrow("Async tileset load failed"); - } - - return assetTilesetLoaderSync(&ctx->loading); -} - -static void loader_ctx_dispose(loader_ctx_t *ctx) { - if(ctx->entry.type != ASSET_LOADER_TYPE_NULL) { - errorret_t ret = assetEntryDispose(&ctx->entry); - if(errorIsNotOk(ret)) errorCatch(ret); - } - threadMutexDispose(&ctx->loading.mutex); -} - -// ============================================================ -// Tests -// ============================================================ - -static void test_tileset_valid_loads(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "valid.tileset"); - - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsOk(ret)); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_LOADED); - - tileset_t *ts = &ctx.entry.data.tileset; - assert_int_equal(ts->tileWidth, 16); - assert_int_equal(ts->tileHeight, 16); - assert_int_equal(ts->columns, 4); - assert_int_equal(ts->rows, 4); - assert_int_equal(ts->tileCount, 16); // columns * rows - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_data_cleared_after_load(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "valid.tileset"); - loader_ctx_run(&ctx); - - // The async scratch buffer must be freed by the time sync returns LOADED. - assert_null(ctx.loading.loading.tileset.data); - - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_bad_magic(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "badmagic.tileset"); - - errorret_t ret = assetTilesetLoaderSync(&ctx.loading); - assert_true(errorIsOk(ret)); - assert_true(run_tileset_async(&ctx.loading)); - ret = assetTilesetLoaderSync(&ctx.loading); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_bad_version(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "badversion.tileset"); - - errorret_t ret = assetTilesetLoaderSync(&ctx.loading); - assert_true(errorIsOk(ret)); - assert_true(run_tileset_async(&ctx.loading)); - ret = assetTilesetLoaderSync(&ctx.loading); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_zero_tile_width(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "zerowidth.tileset"); - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_zero_tile_height(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "zeroheight.tileset"); - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_zero_columns(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "zerocolumns.tileset"); - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_zero_rows(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "zerorows.tileset"); - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_invalid_uv(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "invaliduv.tileset"); - errorret_t ret = loader_ctx_run(&ctx); - assert_true(errorIsNotOk(ret)); - errorCatch(ret); - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -static void test_tileset_missing_file(void **state) { - loader_ctx_t ctx; - loader_ctx_init(&ctx, "nonexistent.tileset"); - - errorret_t ret = assetTilesetLoaderSync(&ctx.loading); - assert_true(errorIsOk(ret)); - - assert_false(run_tileset_async(&ctx.loading)); - - assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR); - loader_ctx_dispose(&ctx); - assert_int_equal(memoryGetAllocatedCount(), 0); -} - -// ============================================================ -// main -// ============================================================ - -int main(void) { - assertInit(); - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(test_tileset_valid_loads, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_data_cleared_after_load, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_bad_magic, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_bad_version, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_zero_tile_width, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_zero_tile_height, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_zero_columns, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_zero_rows, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_invalid_uv, zip_setup, zip_teardown), - cmocka_unit_test_setup_teardown(test_tileset_missing_file, zip_setup, zip_teardown), - }; - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/types/animation/animation.d.ts b/types/animation/animation.d.ts deleted file mode 100644 index 6edd0aad..00000000 --- a/types/animation/animation.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** A single keyframe descriptor passed to the Animation constructor. */ -interface AnimationKeyframe { - /** Time offset in seconds. */ - time: number; - /** Value at this keyframe. */ - value: number; - /** Easing type (EASING_* constant). Defaults to EASING_LINEAR. */ - easing?: number; -} - -/** Keyframe-based float animation. */ -interface AnimationInstance { - /** - * Interpolates the animation at the given time. - * @param time Time in seconds. - * @returns Interpolated value. - */ - getValue(time: number): number; -} - -/** Constructs a new Animation from an array of keyframe descriptors. */ -declare var Animation: { - new (keyframes: AnimationKeyframe[]): AnimationInstance; -}; - -/** Easing function utilities. */ -interface EasingNamespace { - /** - * Applies the given easing function to normalized time t. - * @param type An EASING_* constant. - * @param t Normalized time in [0, 1]. - * @returns Eased value in [0, 1]. - */ - apply(type: number, t: number): number; -} - -declare var Easing: EasingNamespace; - -declare var EASING_LINEAR: number; -declare var EASING_IN_SINE: number; -declare var EASING_OUT_SINE: number; -declare var EASING_IN_OUT_SINE: number; -declare var EASING_IN_QUAD: number; -declare var EASING_OUT_QUAD: number; -declare var EASING_IN_OUT_QUAD: number; -declare var EASING_IN_CUBIC: number; -declare var EASING_OUT_CUBIC: number; -declare var EASING_IN_OUT_CUBIC: number; -declare var EASING_IN_QUART: number; -declare var EASING_OUT_QUART: number; -declare var EASING_IN_OUT_QUART: number; -declare var EASING_IN_BACK: number; -declare var EASING_OUT_BACK: number; -declare var EASING_IN_OUT_BACK: number; diff --git a/types/asset/asset.d.ts b/types/asset/asset.d.ts deleted file mode 100644 index 1389b06a..00000000 --- a/types/asset/asset.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Asset archive queries and cache management. */ -interface AssetNamespace { - // Loader type constants - readonly TYPE_MESH: number; - readonly TYPE_TEXTURE: number; - readonly TYPE_TILESET: number; - readonly TYPE_LOCALE: number; - readonly TYPE_JSON: number; - readonly TYPE_SCRIPT: number; - - // Mesh axis input constants (pass as `input` to lock with TYPE_MESH) - readonly MESH_AXIS_Y_UP: number; - readonly MESH_AXIS_Z_UP: number; - readonly MESH_AXIS_X_UP: number; - readonly MESH_AXIS_Y_DOWN: number; - readonly MESH_AXIS_Z_DOWN: number; - readonly MESH_AXIS_X_DOWN: number; - - /** - * Returns `true` if the given path exists in the asset archive (`dusk.dsk`). - * - * @param path - Archive-relative path, e.g. `"init.js"` or `"ui/hud.png"`. - */ - exists(path: string): boolean; - - /** - * Locks an entry in the asset cache and returns an `AssetEntry`. - * The entry begins loading in the background. Call `entry.requireLoaded()` - * to block until it is ready. - * - * The lock is released when the `AssetEntry` is GC'd or `entry.unlock()` - * is called explicitly. - * - * @param path - Archive-relative path. - * @param type - Loader type constant (`Asset.TYPE_*`). - * @param input - Optional loader-specific input constant. - * `TYPE_TEXTURE` → `Texture.FORMAT_*` - * `TYPE_MESH` → `Asset.MESH_AXIS_*` - * - * @example - * const entry = Asset.lock('data/map.json'); - * entry.requireLoaded(); - */ - lock(path: string, type: number, input?: number): AssetEntry; - - /** - * Blocks until the given entry is fully loaded. - * Returns the entry for chaining. - * @throws If the load fails. - * - * @example - * const entry = Asset.requireLoaded(Asset.lock('map.json', Asset.TYPE_JSON)); - */ - requireLoaded(entry: AssetEntry): AssetEntry; - - /** - * Releases the lock on an asset by path. - * Prefer calling `entry.unlock()` on the `AssetEntry` object directly. - * - * @param path - The path originally passed to `lock`. - */ - unlock(path: string): void; -} - -declare var Asset: AssetNamespace; diff --git a/types/asset/assetbatch.d.ts b/types/asset/assetbatch.d.ts deleted file mode 100644 index 09b23a7b..00000000 --- a/types/asset/assetbatch.d.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Descriptor for one entry in an `AssetBatch`. - * - * `format` — texture format constant (`Texture.FORMAT_*`). Alias for `input` - * when the type is `Asset.TYPE_TEXTURE`. - * `axis` — mesh axis constant (`Asset.MESH_AXIS_*`). Alias for `input` - * when the type is `Asset.TYPE_MESH`. - * `input` — generic numeric input for all other loader types. - */ -interface AssetBatchDescriptor { - path: string; - type: number; - format?: number; - axis?: number; - input?: number; -} - -/** A group of asset entries locked and queued for loading together. */ -interface AssetBatch { - /** Number of entries in the batch. */ - readonly count: number; - /** `true` when every entry has reached `LOADED`. */ - readonly isLoaded: boolean; - /** `true` if any entry is in an `ERROR` state. */ - readonly hasError: boolean; - /** - * Returns a Promise that resolves when all entries have loaded, or rejects - * if any entry errors. Use with `await`. - */ - loaded(): Promise; - /** - * Blocks (spin-waits) until every entry is loaded. - * Returns `this` for chaining. - * @throws If any entry fails to load. - */ - requireLoaded(): this; - /** - * Acquires one additional lock on every entry. - * Returns `this` for chaining. - */ - lock(): this; - /** - * Releases all locks and clears the batch. - * After this call the object is invalid — do not use it again. - */ - unlock(): void; - /** - * Returns the `AssetEntry` at `index`, adding an independent lock. - * The returned entry must be unlocked separately when no longer needed. - * Returns `undefined` if `index` is out of range or the batch is disposed. - */ - entry(index: number): AssetEntry | undefined; - /** - * Returns the first `AssetEntry` whose path matches, adding an - * independent lock. Returns `undefined` if no entry matches. - * The returned entry must be unlocked separately when no longer needed. - */ - getAssetByPath(path: string): AssetEntry | undefined; - - /** - * Fires once when every entry has loaded successfully. - * Subscribe with `onLoaded[0] = () => { ... }`. - */ - readonly onLoaded: AssetEventProxy; - /** - * Fires each time a single entry finishes loading. - * Subscribe with `onEntryLoaded[0] = () => { ... }`. - */ - readonly onEntryLoaded: AssetEventProxy; - /** - * Fires once when all entries have finished but at least one errored. - * Subscribe with `onError[0] = () => { ... }`. - */ - readonly onError: AssetEventProxy; - /** - * Fires each time a single entry transitions to an error state. - * Subscribe with `onEntryError[0] = () => { ... }`. - */ - readonly onEntryError: AssetEventProxy; - - toString(): string; -} - -interface AssetBatchConstructor { - /** Creates a batch from an array of descriptors. Works with or without `new`. */ - (descriptors: AssetBatchDescriptor[]): AssetBatch; - new(descriptors: AssetBatchDescriptor[]): AssetBatch; -} - -declare var AssetBatch: AssetBatchConstructor; diff --git a/types/asset/assetentry.d.ts b/types/asset/assetentry.d.ts deleted file mode 100644 index f20b8d64..00000000 --- a/types/asset/assetentry.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * A live reference to an entry in the asset cache. - * Holds a lock that keeps the entry alive; the lock is released automatically - * when the object is garbage collected, or immediately via `unlock()`. - */ -interface AssetEntry { - /** Archive-relative path used as the cache key. */ - readonly name: string; - /** Current loading state — compare against `AssetEntry.*` state constants. */ - readonly state: number; - /** Loader type — one of the `AssetEntry.TYPE_*` constants. */ - readonly type: number; - /** `true` when the entry has fully loaded (`state === AssetEntry.LOADED`). */ - readonly isLoaded: boolean; - /** - * Returns a `Texture` for this entry when it is a loaded texture asset. - * The `Texture` holds its own asset lock — independent of this `AssetEntry`. - * Returns `undefined` if the entry is not of type `Asset.TYPE_TEXTURE` or - * is not yet loaded. - */ - readonly texture: Texture | undefined; - /** Event proxy — subscribe up to 4 callbacks for when loading completes. */ - readonly onLoaded: AssetEventProxy; - /** Event proxy — subscribe up to 4 callbacks for when the entry is disposed. */ - readonly onUnloaded: AssetEventProxy; - /** Event proxy — subscribe up to 4 callbacks for when loading fails. */ - readonly onError: AssetEventProxy; - /** - * Returns a Promise that resolves when the entry is loaded, or rejects on - * error. Use with `await`. - */ - loaded(): Promise; - /** - * Blocks (spin-waits) until the entry reaches `LOADED` (or `ERROR`). - * Returns `this` for chaining. - * @throws If the load fails. - */ - requireLoaded(): this; - /** - * Releases the lock immediately. - * After this call the object is invalid — do not use it again. - */ - unlock(): void; - toString(): string; -} - -interface AssetEntryConstructor { - // Loading state constants - readonly NOT_STARTED: number; - readonly PENDING: number; - readonly LOADING: number; - readonly LOADED: number; - readonly ERROR: number; - - new(): never; -} - -declare var AssetEntry: AssetEntryConstructor; diff --git a/types/asset/asseteventproxy.d.ts b/types/asset/asseteventproxy.d.ts deleted file mode 100644 index 32a73501..00000000 --- a/types/asset/asseteventproxy.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * An event proxy with up to 4 subscribable callback slots (indices 0–3). - * Assign a function to subscribe; assign `null` to unsubscribe. - * - * @example - * assets.onLoaded[0] = () => { Console.print('all loaded'); }; - * assets.onLoaded[0] = null; // unsubscribe - */ -interface AssetEventProxy { - 0: (() => void) | null; - 1: (() => void) | null; - 2: (() => void) | null; - 3: (() => void) | null; - /** Number of available slots (always 4). */ - readonly length: number; -} diff --git a/types/console/console.d.ts b/types/console/console.d.ts deleted file mode 100644 index dc5e1cc7..00000000 --- a/types/console/console.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Interface for the in-game developer console. */ -interface ConsoleNamespace { - /** - * Prints one or more values to the in-game console, separated by tabs. - * Each argument is coerced to a string before printing. - * - * @example - * Console.print("x =", player.x, "y =", player.y); - */ - print(...args: unknown[]): void; - - /** - * Whether the in-game console overlay is currently visible. - * Set to `true` to show the console, `false` to hide it. - */ - visible: boolean; -} - -/** In-game developer console. */ -declare var Console: ConsoleNamespace; diff --git a/types/display/color.d.ts b/types/display/color.d.ts deleted file mode 100644 index f6a0807e..00000000 --- a/types/display/color.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** An RGBA color with channels in the range 0–255. */ -declare class Color { - /** @param r Red 0–255 (default 0) */ - /** @param g Green 0–255 (default 0) */ - /** @param b Blue 0–255 (default 0) */ - /** @param a Alpha 0–255 (default 255) */ - constructor(r?: number, g?: number, b?: number, a?: number); - - r: number; - g: number; - b: number; - a: number; - - toString(): string; - - // Named color constants - static readonly WHITE: Color; - static readonly BLACK: Color; - static readonly RED: Color; - static readonly GREEN: Color; - static readonly BLUE: Color; - static readonly YELLOW: Color; - static readonly CYAN: Color; - static readonly MAGENTA: Color; - static readonly TRANSPARENT: Color; - static readonly GRAY: Color; - static readonly LIGHT_GRAY: Color; - static readonly DARK_GRAY: Color; - static readonly ORANGE: Color; - static readonly PURPLE: Color; - static readonly PINK: Color; - static readonly TEAL: Color; - static readonly CORNFLOWER_BLUE: Color; -} diff --git a/types/display/screen.d.ts b/types/display/screen.d.ts deleted file mode 100644 index 1bbd8696..00000000 --- a/types/display/screen.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Read-only information about the current display surface. */ -interface ScreenNamespace { - /** Current render-target width in pixels. */ - readonly width: number; - /** Current render-target height in pixels. */ - readonly height: number; - /** Aspect ratio: `width / height`. */ - readonly aspect: number; -} - -/** Current display / render-target dimensions. */ -declare var Screen: ScreenNamespace; diff --git a/types/display/texture.d.ts b/types/display/texture.d.ts deleted file mode 100644 index 665ec7ad..00000000 --- a/types/display/texture.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * A loaded texture asset. Holds a lock on the underlying asset entry — - * the lock is released automatically when the object is garbage collected. - */ -interface Texture { - /** Pixel width of the texture. */ - readonly width: number; - /** Pixel height of the texture. */ - readonly height: number; - toString(): string; -} - -interface TextureConstructor { - /** RGBA 32-bit format (4 channels × 8 bits). */ - readonly FORMAT_RGBA: number; - /** Paletted format. */ - readonly FORMAT_PALETTE: number; - new(): never; -} - -declare var Texture: TextureConstructor; diff --git a/types/engine/engine.d.ts b/types/engine/engine.d.ts deleted file mode 100644 index 504ad743..00000000 --- a/types/engine/engine.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Controls over the engine main loop. */ -interface EngineNamespace { - /** - * Whether the engine main loop is still running. - * Becomes `false` after `Engine.exit()` is called. - */ - readonly running: boolean; - - /** - * Requests an orderly shutdown. Sets `running` to `false`; the main loop - * exits at the end of the current tick. - */ - exit(): void; -} - -/** Engine lifecycle controls. */ -declare var Engine: EngineNamespace; diff --git a/types/engine/globals.d.ts b/types/engine/globals.d.ts deleted file mode 100644 index 3fc31def..00000000 --- a/types/engine/globals.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Returns a Promise that resolves on the next engine frame. - * - * @example - * await frame(); - * Console.print('one frame later'); - */ -declare function frame(): Promise; - -/** - * Returns a Promise that resolves after `ms` milliseconds of engine time. - * - * @example - * await timeout(500); - * Console.print('half a second later'); - */ -declare function timeout(ms: number): Promise; diff --git a/types/entity/component.d.ts b/types/entity/component.d.ts deleted file mode 100644 index 5f71005b..00000000 --- a/types/entity/component.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Base type for all components. `entity.add()` returns a subtype when the - * component type has a dedicated module; cast with `as Position` etc. when - * you need the specific API. - */ -interface Component { - /** Entity ID this component belongs to. */ - readonly entity: number; - /** Component slot index. */ - readonly id: number; - toString(): string; -} - -interface ComponentConstructor { - readonly POSITION: number; - readonly CAMERA: number; - readonly RENDERABLE: number; - readonly PHYSICS: number; - readonly TRIGGER: number; - - new(): never; -} - -declare var Component: ComponentConstructor; diff --git a/types/entity/component/camera.d.ts b/types/entity/component/camera.d.ts deleted file mode 100644 index d7771796..00000000 --- a/types/entity/component/camera.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Camera projection component. Only one camera is active at a time. */ -interface Camera extends Component { - /** Field of view in radians (perspective only). */ - fov: number; - /** Near clip plane distance. */ - nearClip: number; - /** Far clip plane distance. */ - farClip: number; - /** One of `Camera.PERSPECTIVE`, `Camera.PERSPECTIVE_FLIPPED`, or `Camera.ORTHOGRAPHIC`. */ - projType: number; - toString(): string; -} - -interface CameraConstructor { - readonly PERSPECTIVE: number; - readonly PERSPECTIVE_FLIPPED: number; - readonly ORTHOGRAPHIC: number; - new(): never; -} - -declare var Camera: CameraConstructor; diff --git a/types/entity/component/interactable.d.ts b/types/entity/component/interactable.d.ts deleted file mode 100644 index 0e7154bf..00000000 --- a/types/entity/component/interactable.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Interactable component. Fires a callback when the player activates it. - * - * Assign a JS function to `onInteract` to handle the event from script. - * Call `trigger()` to fire the callback imperatively from C or JS. - */ -interface Interactable extends Component { - /** Called when the player interacts with this entity. Set to null to clear. */ - onInteract: (() => void) | null; - /** Fires the registered callback immediately (no-op if none is set). */ - trigger(): void; - toString(): string; -} - -interface InteractableConstructor { - new(): never; -} - -declare var Interactable: InteractableConstructor; diff --git a/types/entity/component/overworld.d.ts b/types/entity/component/overworld.d.ts deleted file mode 100644 index 91696332..00000000 --- a/types/entity/component/overworld.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Overworld character component (player or NPC). */ -interface Overworld extends Component { - /** - * Entity type — `Overworld.PLAYER` or `Overworld.NPC`. - */ - type: number; - /** - * Facing direction — one of the `Overworld.FACING_*` constants. - */ - facing: number; - /** Component ID of the linked Renderable, or INVALID if none. */ - readonly renderComponentId: number; - /** Component ID of the linked Physics body, or INVALID if none. */ - readonly physicsComponentId: number; - toString(): string; -} - -interface OverworldConstructor { - readonly PLAYER: number; - readonly NPC: number; - - readonly FACING_DOWN: number; - readonly FACING_UP: number; - readonly FACING_LEFT: number; - readonly FACING_RIGHT: number; - readonly FACING_SOUTH: number; - readonly FACING_NORTH: number; - readonly FACING_WEST: number; - readonly FACING_EAST: number; - - new(): never; -} - -declare var Overworld: OverworldConstructor; diff --git a/types/entity/component/overworldcamera.d.ts b/types/entity/component/overworldcamera.d.ts deleted file mode 100644 index ecfa243f..00000000 --- a/types/entity/component/overworldcamera.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Overworld follow-camera component. - * Smoothly tracks a target entity's Position each frame. - */ -interface OverworldCamera extends Component { - /** Entity ID to follow. */ - targetEntity: number; - /** Position component ID on the target entity. */ - targetPositionComponent: number; - /** World-space offset added to the target's position before placing the camera. */ - targetOffset: Vec3; - /** Eye-space offset applied on top of targetOffset. */ - eyeOffset: Vec3; - /** Orthographic scale factor (larger = wider view). */ - scale: number; - /** - * Convenience setter — equivalent to assigning `targetEntity` and - * `targetPositionComponent` individually. - */ - setTarget(targetEntityId: number, targetPositionComponentId: number): void; - toString(): string; -} - -interface OverworldCameraConstructor { - new(): never; -} - -declare var OverworldCamera: OverworldCameraConstructor; diff --git a/types/entity/component/overworldtrigger.d.ts b/types/entity/component/overworldtrigger.d.ts deleted file mode 100644 index 461fb5ea..00000000 --- a/types/entity/component/overworldtrigger.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * Overworld AABB trigger. Fires callbacks as the player enters, stays inside, - * exits, or remains outside the defined bounding box each frame. - */ -interface OverworldTrigger extends Component { - /** Minimum corner of the trigger AABB. */ - min: Vec3; - /** Maximum corner of the trigger AABB. */ - max: Vec3; - /** `true` while the player is inside the trigger bounds. */ - readonly playerInside: boolean; - - /** Fired once when the player first enters the bounds. */ - onEnter: (() => void) | null; - /** Fired once when the player leaves the bounds. */ - onExit: (() => void) | null; - /** Fired every frame while the player remains inside the bounds. */ - onStay: (() => void) | null; - /** Fired every frame while the player remains outside the bounds. */ - onOutside: (() => void) | null; - - /** Convenience setter for both corners at once. */ - setBounds(min: Vec3, max: Vec3): void; - toString(): string; -} - -interface OverworldTriggerConstructor { - new(): never; -} - -declare var OverworldTrigger: OverworldTriggerConstructor; diff --git a/types/entity/component/physics.d.ts b/types/entity/component/physics.d.ts deleted file mode 100644 index c4616a24..00000000 --- a/types/entity/component/physics.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Physics body component. */ -interface Physics extends Component { - /** Body simulation type — `Physics.STATIC`, `DYNAMIC`, or `KINEMATIC`. */ - bodyType: number; - /** - * Collision shape type — one of the `Physics.SHAPE_*` constants. - * Changing the type preserves existing velocity and ground state. - */ - shape: number; - /** Current linear velocity (Vec3). */ - velocity: Vec3; - /** Gravity multiplier. 0 = no gravity, 1 = full, negative = inverted. */ - gravityScale: number; - /** `true` if the body rested on a surface during the last simulation step. */ - readonly onGround: boolean; - /** Applies an instantaneous velocity change. No-op on STATIC bodies. */ - applyImpulse(impulse: Vec3): void; - toString(): string; -} - -interface PhysicsConstructor { - readonly STATIC: number; - readonly DYNAMIC: number; - readonly KINEMATIC: number; - readonly SHAPE_CUBE: number; - readonly SHAPE_SPHERE: number; - readonly SHAPE_CAPSULE: number; - readonly SHAPE_PLANE: number; - new(): never; -} - -declare var Physics: PhysicsConstructor; diff --git a/types/entity/component/player.d.ts b/types/entity/component/player.d.ts deleted file mode 100644 index 1b79a987..00000000 --- a/types/entity/component/player.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Player movement component. Controls walk and run speeds. */ -interface Player extends Component { - /** Walk speed in world units per second. */ - speed: number; - /** Run speed in world units per second. */ - runSpeed: number; - toString(): string; -} - -interface PlayerConstructor { - new(): never; -} - -declare var Player: PlayerConstructor; diff --git a/types/entity/component/position.d.ts b/types/entity/component/position.d.ts deleted file mode 100644 index 5f7addfe..00000000 --- a/types/entity/component/position.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Transform component — local/world PRS with an optional parent hierarchy. */ -interface Position extends Component { - /** - * Local-space position. Reading returns a Vec3 copy; assigning a Vec3 - * writes through to the C transform and marks descendants dirty. - */ - localPosition: Vec3; - /** World-space position (full parent-chain applied). */ - worldPosition: Vec3; - /** Local euler rotation in radians (XYZ order). */ - localRotation: Vec3; - /** World euler rotation in radians. */ - worldRotation: Vec3; - /** Local scale. */ - localScale: Vec3; - /** World scale (extracted from parent-chain matrix). */ - worldScale: Vec3; - /** - * Orients the transform so it faces `target`. Uses the current local - * position as the eye. `up` defaults to world Y-up. - */ - lookAt(target: Vec3, up?: Vec3): void; - /** - * Attaches this transform to a parent. Pass `null` / `undefined` to detach. - */ - setParent(parent: Position | null | undefined): void; - toString(): string; -} - -interface PositionConstructor { - new(): never; -} - -declare var Position: PositionConstructor; diff --git a/types/entity/component/renderable.d.ts b/types/entity/component/renderable.d.ts deleted file mode 100644 index 11a0d77a..00000000 --- a/types/entity/component/renderable.d.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** A Renderable component. Returned by `entity.add(Component.RENDERABLE)`. */ -interface Renderable extends Component { - /** Current render type — one of the `Renderable.*` type constants. */ - type: number; - /** Render priority. 0 = auto. Higher = drawn later. */ - priority: number; - /** - * Unlit material color. Reading returns a fresh `Color` copy; assigning - * a `Color` instance writes through to the C material. - * - * @example - * r.color = Color.RED; - * r.color = new Color(255, 128, 0); - */ - color: Color; - /** - * The bound texture. Assigning a `Texture` switches the renderable to - * `SPRITEBATCH` mode and pins the object against GC. Reading returns the - * same `Texture` instance that was assigned, or `undefined` if none. - */ - texture: Texture | undefined; - /** - * Sprite list. Reading returns a JS array of 10-element sub-arrays - * `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` — one per sprite. - * - * Assigning an array replaces all sprites. Each element may be: - * - 10 numbers (3D): `[x1,y1,z1, x2,y2,z2, u1,v1, u2,v2]` - * - 8 numbers (2D, z defaults to 0): `[x1,y1, x2,y2, u1,v1, u2,v2]` - * - * @example - * r.sprites = [[-0.5, 0, 0.5, 1, 0, 0, 1, 1]]; - * r.sprites = []; // clear - */ - sprites: number[][]; - toString(): string; -} - -interface RenderableConstructor { - readonly SHADER_MATERIAL: number; - readonly SPRITEBATCH: number; - readonly CUSTOM: number; - new(): never; -} - -declare var Renderable: RenderableConstructor; diff --git a/types/entity/component/trigger.d.ts b/types/entity/component/trigger.d.ts deleted file mode 100644 index ac86c20b..00000000 --- a/types/entity/component/trigger.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** AABB trigger volume. `contains` tests whether a world point is inside. */ -interface Trigger extends Component { - /** Minimum corner of the AABB (Vec3). */ - min: Vec3; - /** Maximum corner of the AABB (Vec3). */ - max: Vec3; - /** Sets both corners at once. */ - setBounds(min: Vec3, max: Vec3): void; - /** Returns `true` if `point` is inside `[min, max]`. */ - contains(point: Vec3): boolean; - toString(): string; -} - -interface TriggerConstructor { - new(): never; -} - -declare var Trigger: TriggerConstructor; diff --git a/types/entity/entity.d.ts b/types/entity/entity.d.ts deleted file mode 100644 index 46d0808c..00000000 --- a/types/entity/entity.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -interface Entity { - /** - * Adds a component of the given type and returns it. - * Returns a typed subclass when the component has a dedicated module - * (`Position`, `Camera`, `Renderable`, `Trigger`, `Physics`); otherwise - * returns the base `Component`. Cast with `as Position` etc. when needed. - */ - add(type: number): Component; - toString(): string; -} - -interface EntityConstructor { - /** Sentinel for an invalid entity ID. */ - readonly INVALID: number; - /** Allocates a new entity from the fixed pool (max 64). */ - create(): Entity; - /** Disposes the entity and all of its components. */ - dispose(entity: Entity): void; - new(): never; -} - -declare var Entity: EntityConstructor; diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index e2d0edf4..00000000 --- a/types/index.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - * - * Root type declarations for the Dusk engine built-in JavaScript modules. - * These globals are injected by the native JerryScript runtime — they are not - * ES modules and cannot be imported. - * - * Reference this file from a jsconfig.json or tsconfig.json to get - * IntelliSense across all game scripts: - * - * { "compilerOptions": { "typeRoots": ["./types"] } } - */ - -// module system -/// - -// math -/// - -// display -/// -/// -/// - -// asset -/// -/// -/// -/// - -// animation -/// - -// overworld -/// - -// item system -/// -/// - -// story system -/// - -// locale -/// - -// save -/// - -// ui -/// -/// - -// engine systems -/// -/// -/// -/// -/// -/// - -// entity / components -/// -/// -/// -/// -/// -/// -/// diff --git a/types/input/input.d.ts b/types/input/input.d.ts deleted file mode 100644 index e39a2f24..00000000 --- a/types/input/input.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Opaque type alias for input action identifiers. */ -type InputAction = number; - -/** Polling-based input queries and button rebinding. */ -interface InputNamespace { - /** Returns `true` while the given action is held down this frame. */ - isDown(action: InputAction): boolean; - /** Returns `true` if the given action was held down last frame. */ - wasDown(action: InputAction): boolean; - /** Returns `true` on the first frame the action transitions up → down. */ - pressed(action: InputAction): boolean; - /** Returns `true` on the first frame the action transitions down → up. */ - released(action: InputAction): boolean; - /** Continuous (analog) value in `0.0–1.0`. Digital buttons return 0 or 1. */ - getValue(action: InputAction): number; - /** - * Signed axis value `-1.0–1.0`: `getValue(pos) - getValue(neg)`. - * @param neg - Action mapped to the negative direction. - * @param pos - Action mapped to the positive direction. - */ - axis(neg: InputAction, pos: InputAction): number; - /** - * Rebinds a physical button to a logical action at runtime. - * @param buttonName - Platform-specific button name, e.g. `"A"`, `"START"`. - * @param action - Target `INPUT_ACTION_*` constant. - */ - bind(buttonName: string, action: InputAction): void; -} - -/** Polling-based input system. */ -declare var Input: InputNamespace; - -// Input action constants — injected as globals by the engine at startup. -declare var INPUT_ACTION_UP: InputAction; -declare var INPUT_ACTION_DOWN: InputAction; -declare var INPUT_ACTION_LEFT: InputAction; -declare var INPUT_ACTION_RIGHT: InputAction; -declare var INPUT_ACTION_ACCEPT: InputAction; -declare var INPUT_ACTION_CANCEL: InputAction; -declare var INPUT_ACTION_RAGEQUIT: InputAction; -declare var INPUT_ACTION_CONSOLE: InputAction; -declare var INPUT_ACTION_POINTERX: InputAction; -declare var INPUT_ACTION_POINTERY: InputAction; diff --git a/types/item/backpack.d.ts b/types/item/backpack.d.ts deleted file mode 100644 index d4eb9fd1..00000000 --- a/types/item/backpack.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** The player's item backpack (a fixed 20-slot inventory). */ -interface BackpackNamespace { - /** `true` when all 20 storage slots are occupied by distinct items. */ - readonly isFull: boolean; - - /** - * Returns the quantity of `itemId` in the backpack (0 if absent). - * @param itemId - An `ITEM_ID_*` constant. - */ - getCount(itemId: ItemId): number; - - /** - * Returns `true` if `itemId` is present with quantity greater than 0. - * @param itemId - An `ITEM_ID_*` constant. - */ - has(itemId: ItemId): boolean; - - /** - * Returns `true` if the stack for `itemId` is at maximum quantity (255). - * @param itemId - An `ITEM_ID_*` constant. - */ - isItemFull(itemId: ItemId): boolean; - - /** - * Sets the quantity of `itemId`; passing 0 removes the stack entirely. - * @param itemId - An `ITEM_ID_*` constant. - * @param quantity - New quantity, 0–255. - */ - set(itemId: ItemId, quantity: number): void; - - /** - * Adds `quantity` units of `itemId` to the backpack. - * @param itemId - An `ITEM_ID_*` constant. - * @param quantity - Amount to add, 1–255. - */ - add(itemId: ItemId, quantity: number): void; - - /** - * Removes `itemId` entirely from the backpack. - * @param itemId - An `ITEM_ID_*` constant. - */ - remove(itemId: ItemId): void; - - /** - * Sorts the backpack contents. - * @param sortBy - `INVENTORY_SORT_BY_ID` or `INVENTORY_SORT_BY_TYPE`. - * @param reverse - Optional; pass `true` to reverse the sort order. - */ - sort(sortBy: number, reverse?: boolean): void; -} - -declare var Backpack: BackpackNamespace; - -// Inventory sort constants — injected as globals by the engine at startup. -declare var INVENTORY_SORT_BY_ID: number; -declare var INVENTORY_SORT_BY_TYPE: number; diff --git a/types/item/item.d.ts b/types/item/item.d.ts deleted file mode 100644 index 00566669..00000000 --- a/types/item/item.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Opaque type alias for item identifier constants. */ -type ItemId = number; - -/** Opaque type alias for item type constants. */ -type ItemType = number; - -/** Item data lookup. */ -interface ItemNamespace { - /** - * Returns the string name for the given item ID. - * @param itemId - An `ITEM_ID_*` constant. - */ - getName(itemId: ItemId): string; - - /** - * Returns the type constant for the given item ID. - * @param itemId - An `ITEM_ID_*` constant. - * @returns An `ITEM_TYPE_*` constant. - */ - getType(itemId: ItemId): ItemType; -} - -declare var Item: ItemNamespace; - -// Item ID constants — injected as globals by the engine at startup. -declare var ITEM_ID_POTION: ItemId; -declare var ITEM_ID_POTATO: ItemId; -declare var ITEM_ID_APPLE: ItemId; - -// Item type constants — injected as globals by the engine at startup. -declare var ITEM_TYPE_MEDICINE: ItemType; -declare var ITEM_TYPE_FOOD: ItemType; diff --git a/types/locale/locale.d.ts b/types/locale/locale.d.ts deleted file mode 100644 index 61000da4..00000000 --- a/types/locale/locale.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Locale string lookup and language switching. */ -interface LocaleNamespace { - /** - * Returns the translated string for the given message ID. - * - * @param id - PO message ID, e.g. `"ITEM_POTION_NAME"`. - * @param pluralCount - Optional count used to select the plural form. - * Defaults to 1 (singular). - * @param args - Optional substitution values (`%s`, `%d`, `%f`). - */ - getText( - id: string, - pluralCount?: number, - ...args: Array - ): string; - - /** - * Switches the active locale. - * @param name - Locale name string, e.g. `"en-US"`. - */ - setLocale(name: string): void; -} - -declare var Locale: LocaleNamespace; diff --git a/types/math/vec3.d.ts b/types/math/vec3.d.ts deleted file mode 100644 index bf8b6811..00000000 --- a/types/math/vec3.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** A three-component float vector (x, y, z). */ -declare class Vec3 { - constructor(x?: number, y?: number, z?: number); - x: number; - y: number; - z: number; - toString(): string; -} diff --git a/types/overworld/overworld.d.ts b/types/overworld/overworld.d.ts deleted file mode 100644 index c3070bb2..00000000 --- a/types/overworld/overworld.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Map loading and world-position management. */ -interface OverworldNamespace { - /** - * Loads the map with the given handle, disposing any currently loaded map. - * @param handle Map handle string (max 31 chars). - */ - loadMap(handle: string): void; - - /** - * Returns `true` when a map is currently loaded. - */ - isLoaded(): boolean; - - /** - * Slides the loaded chunk window to the given tile-space position. - * @param x Tile X. - * @param y Tile Y. - * @param z Tile Z. - */ - setPosition(x: number, y: number, z: number): void; - - /** - * Advances the map one tick. Call once per frame. - */ - update(): void; - - /** - * Unloads the current map. - */ - dispose(): void; -} - -declare var Overworld: OverworldNamespace; diff --git a/types/require.d.ts b/types/require.d.ts deleted file mode 100644 index 26632987..00000000 --- a/types/require.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * CommonJS-style module loader. - * - * Modules are cached after their first load. Subsequent calls with the same - * resolved path return the cached exports without re-executing the file. - * - * Path rules: the string is passed verbatim to the asset loader and resolved - * from the archive root. The `.js` extension must be included explicitly. - * - * @example - * const NPC = require('./entities/NPC'); - */ -declare function require(path: string): any; - -/** - * Asynchronous module loader. Loads the module at `path` in the background - * and returns a Promise that resolves to the module's `exports`. - * - * If the module is already cached it resolves immediately. On load failure - * the Promise is rejected. - * - * Path rules are identical to `require`. - * - * @example - * const NPC = await requireAsync('./entities/NPC'); - */ -declare function requireAsync(path: string): Promise; - -/** - * The module object for the currently executing script. - * Assign `module.exports` to control what `require()` returns to callers. - */ -declare var module: { exports: any }; - -/** - * Shorthand for `module.exports`. Direct assignment (`exports = ...`) does - * NOT update `module.exports`; use `module.exports = ...` for that. - */ -declare var exports: any; diff --git a/types/save/save.d.ts b/types/save/save.d.ts deleted file mode 100644 index cfcd14f4..00000000 --- a/types/save/save.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Slot-based save file management. */ -interface SaveNamespace { - /** - * Returns `true` if a save file is present for the given slot. - * @param slot - Save slot index (0–2). - */ - exists(slot: number): boolean; - - /** - * Loads the save file for the given slot from persistent storage. - * Throws if the load fails. - * @param slot - Save slot index (0–2). - */ - load(slot: number): void; - - /** - * Writes the save file for the given slot to persistent storage. - * Throws if the write fails. - * @param slot - Save slot index (0–2). - */ - write(slot: number): void; - - /** - * Deletes the save file for the given slot from persistent storage. - * Throws if the delete fails. - * @param slot - Save slot index (0–2). - */ - delete(slot: number): void; -} - -declare var Save: SaveNamespace; diff --git a/types/scene/scene.d.ts b/types/scene/scene.d.ts deleted file mode 100644 index b64e77ed..00000000 --- a/types/scene/scene.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** - * The object a JS scene module must export. - * All methods are optional. - * - * @example - * // assets/scenes/game.js - * var scene = {}; - * - * scene.init = function() { Console.print('scene started'); }; - * scene.update = function() { /* per-frame logic *\/ }; - * scene.dispose = function() { }; - * - * module.exports = scene; - */ -interface SceneObject { - /** Called when this scene becomes active. Optional. */ - init?(): void; - /** Called every frame while this scene is active. Optional. */ - update?(): void; - /** Called when the scene is replaced or the engine shuts down. Optional. */ - dispose?(): void; -} - -/** Scene management. */ -interface SceneNamespace { - /** The currently active scene object, or `null` if none. */ - current: SceneObject | null; - - /** - * Replaces the active scene. Calls `dispose()` on the previous scene (if - * any), then calls `init()` on the new one. Pass `null` to clear. - * - * @example - * const myScene = require('./scenes/game'); - * Scene.set(myScene); - */ - set(newScene: SceneObject | null): void; - - /** Called each frame by the engine — drives `current.update()`. */ - update(): void; - - /** Called each frame by the engine for dynamic/physics updates. */ - dynamicUpdate(): void; - - /** Disposes the current scene and sets `current` to `null`. */ - dispose(): void; -} - -declare var Scene: SceneNamespace; diff --git a/types/story/story.d.ts b/types/story/story.d.ts deleted file mode 100644 index 9b555afe..00000000 --- a/types/story/story.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Opaque type alias for story flag identifier constants. */ -type StoryFlag = number; - -/** Story flag read/write access. */ -interface StoryNamespace { - /** - * Returns the current value of a story flag (0–255). - * @param flagId - A `STORY_FLAG_*` constant. - */ - get(flagId: StoryFlag): number; - - /** - * Sets a story flag to a new value. - * @param flagId - A `STORY_FLAG_*` constant. - * @param value - New value, 0–255. - */ - set(flagId: StoryFlag, value: number): void; -} - -declare var Story: StoryNamespace; - -// Story flag constants — injected as globals by the engine at startup. -declare var STORY_FLAG_TEST: StoryFlag; diff --git a/types/system/system.d.ts b/types/system/system.d.ts deleted file mode 100644 index d2d77649..00000000 --- a/types/system/system.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** Runtime platform detection. */ -interface SystemNamespace { - /** - * Numeric identifier for the current platform. - * Compare against the `System.PLATFORM_*` constants. - * - * @example - * if(System.platform === System.PLATFORM_PSP) { useLowResAssets(); } - */ - readonly platform: number; - - readonly PLATFORM_LINUX: number; - readonly PLATFORM_KNULLI: number; - readonly PLATFORM_PSP: number; - readonly PLATFORM_GAMECUBE: number; - readonly PLATFORM_WII: number; -} - -/** Platform detection and system-level information. */ -declare var System: SystemNamespace; diff --git a/types/ui/fullbox.d.ts b/types/ui/fullbox.d.ts deleted file mode 100644 index 2082f9d4..00000000 --- a/types/ui/fullbox.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** A full-screen color overlay. */ -interface UIFullboxInstance { - /** - * Sets the overlay to a solid color with no animation. - * Returns a Promise that resolves immediately. - * @param color - The color to apply. - */ - setColor(color: Color): Promise; - - /** - * Animates the overlay from one color to another. - * Returns a Promise that resolves when the transition completes. - * Starting a new transition while one is in progress resolves the - * old Promise immediately before beginning the new one. - * @param from - Starting color. - * @param to - Ending color. - * @param duration - Duration in seconds. - * @param easing - Optional EASING_* constant (default: EASING_LINEAR). - */ - transition( - from: Color, - to: Color, - duration: number, - easing?: number - ): Promise; -} - -/** Full-screen overlay drawn on top of all UI elements. */ -declare var UIFullboxOver: UIFullboxInstance; - -/** Full-screen overlay drawn below all UI elements. */ -declare var UIFullboxUnder: UIFullboxInstance; diff --git a/types/ui/textbox.d.ts b/types/ui/textbox.d.ts deleted file mode 100644 index 1d255be0..00000000 --- a/types/ui/textbox.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2026 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -/** VN-style typewriter textbox singleton. */ -interface UITextboxNamespace { - /** `true` when the typewriter has fully revealed the current page. */ - readonly isPageComplete: boolean; - /** `true` when at least one more page follows the current one. */ - readonly hasNextPage: boolean; - /** Zero-based index of the current page. */ - readonly currentPage: number; - /** Total number of pages for the current text. */ - readonly pageCount: number; - - /** - * Sets the textbox content and rebuilds the word-wrap layout. - * Resets to page 0 and scroll 0. - * @param text - Text to display (max 1024 chars). - */ - setText(text: string): void; - - /** - * Advances to the next page; no-op on the last page. - */ - nextPage(): void; - - /** - * Advances the typewriter scroll by one tick. - * Call once per frame before `draw()`. - */ - update(): void; - - /** - * Draws the frame and currently visible text. - * Call once per frame after `update()`. - */ - draw(): void; - - /** - * Sets the input action that auto-advances the textbox when held. - * @param action - An `INPUT_ACTION_*` constant. - */ - setAdvanceAction(action: InputAction): void; -} - -declare var UITextbox: UITextboxNamespace;