Example scene working
This commit is contained in:
+3
-1
@@ -6,4 +6,6 @@ const platformNames = {
|
|||||||
[System.PLATFORM_WII]: 'Wii',
|
[System.PLATFORM_WII]: 'Wii',
|
||||||
};
|
};
|
||||||
|
|
||||||
Console.print('Platform: ' + (platformNames[System.platform] || 'Unknown'));
|
Console.print('Platform: ' + (platformNames[System.platform] || 'Unknown'));
|
||||||
|
|
||||||
|
requireAsync('testscene.js').then(Scene.set).catch(Engine.exit);
|
||||||
+28
-41
@@ -1,14 +1,4 @@
|
|||||||
Console.print('testscene.js is loaded');
|
var scene = {};
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
test: function() {
|
|
||||||
return 'Hello string';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// var scene = {
|
|
||||||
// 'test': 'teststring'
|
|
||||||
// };
|
|
||||||
|
|
||||||
// var assets = AssetBatch([
|
// var assets = AssetBatch([
|
||||||
// { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA }
|
// { path: 'test.png', type: Asset.TYPE_TEXTURE, format: Texture.FORMAT_RGBA }
|
||||||
@@ -21,38 +11,35 @@ module.exports = {
|
|||||||
// var testRenderable;
|
// var testRenderable;
|
||||||
// var texEntry;
|
// var texEntry;
|
||||||
|
|
||||||
// scene.init = function() {
|
scene.init = function() {
|
||||||
// assets.lock();
|
Console.print('Scene Init');
|
||||||
// assets.onLoaded[0] = scene.loaded;
|
// texEntry = assets.entry(0);
|
||||||
// };
|
|
||||||
|
|
||||||
// scene.loaded = function() {
|
// Camera at (3, 3, 3) looking at origin
|
||||||
// texEntry = assets.entry(0);
|
cam = Entity.create();
|
||||||
|
camPos = cam.add(Component.POSITION);
|
||||||
|
cam.add(Component.CAMERA);
|
||||||
|
camPos.localPosition = new Vec3(3, 3, 3);
|
||||||
|
camPos.lookAt(new Vec3(0, 0, 0));
|
||||||
|
|
||||||
// // Camera at (3, 3, 3) looking at origin
|
// Test entity with textured quad at origin
|
||||||
// cam = Entity.create();
|
testEntity = Entity.create();
|
||||||
// camPos = cam.add(Component.POSITION);
|
testPos = testEntity.add(Component.POSITION);
|
||||||
// cam.add(Component.CAMERA);
|
testRenderable = testEntity.add(Component.RENDERABLE);
|
||||||
// camPos.localPosition = new Vec3(3, 3, 3);
|
|
||||||
// camPos.lookAt(new Vec3(0, 0, 0));
|
|
||||||
|
|
||||||
// // Test entity with textured quad at origin
|
// testRenderable.texture = texEntry.texture;
|
||||||
// testEntity = Entity.create();
|
testRenderable.type = Renderable.SPRITEBATCH;
|
||||||
// testPos = testEntity.add(Component.POSITION);
|
testRenderable.sprites = [
|
||||||
// testRenderable = testEntity.add(Component.RENDERABLE);
|
[0, 0, 1, 1, 0, 1, 1, 0]
|
||||||
|
];
|
||||||
|
testPos.localPosition = new Vec3(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// testRenderable.texture = texEntry.texture;
|
scene.dispose = function() {
|
||||||
// testRenderable.sprites = [
|
Console.print('Scene Dispose');
|
||||||
// [0, 0, 1, 1, 0, 1, 1, 0]
|
Entity.dispose(cam);
|
||||||
// ];
|
Entity.dispose(testEntity);
|
||||||
// testPos.localPosition = new Vec3(0, 0, 0);
|
// assets.unlock();
|
||||||
// }
|
};
|
||||||
|
|
||||||
// scene.dispose = function() {
|
module.exports = scene;
|
||||||
// Console.print('Scene Dispose');
|
|
||||||
// Entity.dispose(cam);
|
|
||||||
// Entity.dispose(testEntity);
|
|
||||||
// assets.unlock();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// module.exports = scene;
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# dusk_embed_js(TARGET JS_FILE [NAME identifier])
|
||||||
|
#
|
||||||
|
# Converts a JS file into a C string header in DUSK_GENERATED_HEADERS_DIR.
|
||||||
|
# The generated header defines:
|
||||||
|
# static const char <NAME>[] = "...";
|
||||||
|
# static const size_t <NAME>_SIZE = sizeof(<NAME>) - 1;
|
||||||
|
#
|
||||||
|
# NAME defaults to the uppercase stem + "_JS" (e.g. scene.js -> SCENE_JS).
|
||||||
|
function(dusk_embed_js TARGET JS_FILE)
|
||||||
|
cmake_parse_arguments(ARG "" "NAME" "" ${ARGN})
|
||||||
|
|
||||||
|
get_filename_component(JS_ABS "${JS_FILE}" ABSOLUTE)
|
||||||
|
get_filename_component(JS_STEM "${JS_FILE}" NAME_WE)
|
||||||
|
|
||||||
|
set(OUTPUT_HEADER "${DUSK_GENERATED_HEADERS_DIR}/${JS_STEM}_js.h")
|
||||||
|
|
||||||
|
set(NAME_ARG "")
|
||||||
|
if(ARG_NAME)
|
||||||
|
set(NAME_ARG "--name" "${ARG_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${OUTPUT_HEADER}"
|
||||||
|
COMMAND ${Python3_EXECUTABLE} -m tools.js2c
|
||||||
|
--input "${JS_ABS}"
|
||||||
|
--output "${OUTPUT_HEADER}"
|
||||||
|
${NAME_ARG}
|
||||||
|
WORKING_DIRECTORY "${DUSK_ROOT_DIR}"
|
||||||
|
DEPENDS "${JS_ABS}"
|
||||||
|
COMMENT "js2c: ${JS_STEM}.js -> ${JS_STEM}_js.h"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
file(RELATIVE_PATH JS_REL "${DUSK_ROOT_DIR}" "${JS_ABS}")
|
||||||
|
string(MAKE_C_IDENTIFIER "dusk_js2c_${JS_REL}" JS_TARGET)
|
||||||
|
add_custom_target(${JS_TARGET} DEPENDS "${OUTPUT_HEADER}")
|
||||||
|
add_dependencies(${TARGET} ${JS_TARGET})
|
||||||
|
endfunction()
|
||||||
@@ -49,15 +49,14 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(uiInit());
|
errorChain(uiInit());
|
||||||
errorChain(uiTextboxInit());
|
errorChain(uiTextboxInit());
|
||||||
errorChain(cutsceneInit());
|
errorChain(cutsceneInit());
|
||||||
errorChain(sceneInit());
|
|
||||||
entityManagerInit();
|
entityManagerInit();
|
||||||
backpackInit();
|
backpackInit();
|
||||||
physicsManagerInit();
|
physicsManagerInit();
|
||||||
errorChain(networkInit());
|
errorChain(networkInit());
|
||||||
errorChain(scriptInit());
|
errorChain(scriptInit());
|
||||||
|
errorChain(sceneInit());
|
||||||
|
|
||||||
consolePrint("Engine initialized");
|
consolePrint("Engine initialized");
|
||||||
|
|
||||||
errorChain(scriptExecFile("init.js"));
|
errorChain(scriptExecFile("init.js"));
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
@@ -75,7 +74,7 @@ errorret_t engineUpdate(void) {
|
|||||||
physicsManagerUpdate();
|
physicsManagerUpdate();
|
||||||
errorChain(displayUpdate());
|
errorChain(displayUpdate());
|
||||||
errorChain(cutsceneUpdate());
|
errorChain(cutsceneUpdate());
|
||||||
sceneUpdate();
|
errorChain(sceneUpdate());
|
||||||
errorChain(assetUpdate());
|
errorChain(assetUpdate());
|
||||||
errorChain(scriptUpdate());
|
errorChain(scriptUpdate());
|
||||||
|
|
||||||
@@ -90,7 +89,8 @@ void engineExit(void) {
|
|||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
uiTextboxDispose();
|
uiTextboxDispose();
|
||||||
cutsceneDispose();
|
cutsceneDispose();
|
||||||
sceneDispose();
|
errorChain(sceneDispose());
|
||||||
|
errorChain(scriptDispose());
|
||||||
errorChain(networkDispose());
|
errorChain(networkDispose());
|
||||||
entityManagerDispose();
|
entityManagerDispose();
|
||||||
localeManagerDispose();
|
localeManagerDispose();
|
||||||
@@ -98,7 +98,6 @@ errorret_t engineDispose(void) {
|
|||||||
consoleDispose();
|
consoleDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
// errorChain(saveDispose());
|
// errorChain(saveDispose());
|
||||||
errorChain(scriptDispose());
|
|
||||||
errorChain(assetDispose());
|
errorChain(assetDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# 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}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
scene.c
|
scene.c
|
||||||
|
|||||||
+17
-1
@@ -18,15 +18,30 @@
|
|||||||
#include "asset/loader/assetloader.h"
|
#include "asset/loader/assetloader.h"
|
||||||
#include "script/module/modulebase.h"
|
#include "script/module/modulebase.h"
|
||||||
#include "console/console.h"
|
#include "console/console.h"
|
||||||
|
#include "script/script.h"
|
||||||
|
#include "scene_js.h"
|
||||||
|
|
||||||
scene_t SCENE;
|
scene_t SCENE;
|
||||||
|
|
||||||
errorret_t sceneInit(void) {
|
errorret_t sceneInit(void) {
|
||||||
memoryZero(&SCENE, sizeof(scene_t));
|
memoryZero(&SCENE, sizeof(scene_t));
|
||||||
|
errorChain(scriptExecString(SCENE_JS));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneUpdate(void) {
|
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) {
|
errorret_t sceneRender(void) {
|
||||||
@@ -65,5 +80,6 @@ errorret_t sceneRender(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneDispose(void) {
|
errorret_t sceneDispose(void) {
|
||||||
|
errorChain(scriptExecString("Scene.dispose();"));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,21 +16,29 @@ extern scene_t SCENE;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the scene manager.
|
* Initialises the scene manager.
|
||||||
|
*
|
||||||
|
* @return An error if the init failed, or errorOk() if it succeeded.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneInit(void);
|
errorret_t sceneInit(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ticks the scene manager. Processes any pending scene transition, then
|
* Ticks the scene manager. Processes any pending scene transition, then
|
||||||
* calls scriptSceneUpdate on the active scene.
|
* calls scriptSceneUpdate on the active scene.
|
||||||
|
*
|
||||||
|
* @return An error if the update failed, or errorOk() if it succeeded.
|
||||||
*/
|
*/
|
||||||
void sceneUpdate(void);
|
errorret_t sceneUpdate(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the current scene (entities, render pipeline, UI).
|
* Renders the current scene (entities, render pipeline, UI).
|
||||||
|
*
|
||||||
|
* @return An error if the render failed, or errorOk() if it succeeded.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneRender(void);
|
errorret_t sceneRender(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes the active scene immediately.
|
* Disposes the active scene immediately.
|
||||||
|
*
|
||||||
|
* @return An error if the dispose failed, or errorOk() if it succeeded.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneDispose(void);
|
errorret_t sceneDispose(void);
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2026 Dominic Masters
|
||||||
|
//
|
||||||
|
// This software is released under the MIT License.
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
var Scene = {
|
||||||
|
};
|
||||||
|
|
||||||
|
Scene.update = () => {
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene.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;
|
||||||
|
}
|
||||||
+36
-16
@@ -16,6 +16,36 @@
|
|||||||
|
|
||||||
script_t SCRIPT;
|
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) {
|
errorret_t scriptInit(void) {
|
||||||
memoryZero(&SCRIPT, sizeof(script_t));
|
memoryZero(&SCRIPT, sizeof(script_t));
|
||||||
|
|
||||||
@@ -33,15 +63,10 @@ errorret_t scriptUpdate() {
|
|||||||
|
|
||||||
if(jerry_value_is_exception(ret)) {
|
if(jerry_value_is_exception(ret)) {
|
||||||
char_t buf[256];
|
char_t buf[256];
|
||||||
jerry_value_t errVal = jerry_exception_value(ret, false);
|
char_t stack[512];
|
||||||
jerry_value_t errStr = jerry_value_to_string(errVal);
|
scriptExceptionInfo(ret, buf, sizeof(buf), stack, sizeof(stack));
|
||||||
jerry_size_t len = jerry_string_to_buffer(
|
|
||||||
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
|
|
||||||
);
|
|
||||||
buf[len] = '\0';
|
|
||||||
jerry_value_free(errStr);
|
|
||||||
jerry_value_free(errVal);
|
|
||||||
jerry_value_free(ret);
|
jerry_value_free(ret);
|
||||||
|
if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack);
|
||||||
errorThrow("Script error: %s", buf);
|
errorThrow("Script error: %s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,15 +86,10 @@ errorret_t scriptExecString(const char_t *source) {
|
|||||||
|
|
||||||
if(jerry_value_is_exception(result)) {
|
if(jerry_value_is_exception(result)) {
|
||||||
char_t buf[256];
|
char_t buf[256];
|
||||||
jerry_value_t errVal = jerry_exception_value(result, false);
|
char_t stack[512];
|
||||||
jerry_value_t errStr = jerry_value_to_string(errVal);
|
scriptExceptionInfo(result, buf, sizeof(buf), stack, sizeof(stack));
|
||||||
jerry_size_t len = jerry_string_to_buffer(
|
|
||||||
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
|
|
||||||
);
|
|
||||||
buf[len] = '\0';
|
|
||||||
jerry_value_free(errStr);
|
|
||||||
jerry_value_free(errVal);
|
|
||||||
jerry_value_free(result);
|
jerry_value_free(result);
|
||||||
|
if(stack[0]) errorThrow("Script error: %s\n%s", buf, stack);
|
||||||
errorThrow("Script error: %s", buf);
|
errorThrow("Script error: %s", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,9 +124,11 @@ static void test_update_entry_reaches_loaded(void **state) {
|
|||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
errorret_t ret = assetUpdate();
|
errorret_t ret = assetUpdate();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
@@ -134,11 +136,13 @@ static void test_update_entry_reaches_loaded(void **state) {
|
|||||||
static void test_update_slot_occupied_after_first_update(void **state) {
|
static void test_update_slot_occupied_after_first_update(void **state) {
|
||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
|
||||||
// Slot not cleared until the second update processes the LOADED case.
|
// Slot not cleared until the second update processes the LOADED case.
|
||||||
assert_true(loading_slot_has_entry(entry));
|
assert_true(loading_slot_has_entry(entry));
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
@@ -146,11 +150,13 @@ static void test_update_slot_occupied_after_first_update(void **state) {
|
|||||||
static void test_update_slot_cleared_after_second_update(void **state) {
|
static void test_update_slot_cleared_after_second_update(void **state) {
|
||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
|
|
||||||
assert_false(loading_slot_has_entry(entry));
|
assert_false(loading_slot_has_entry(entry));
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
@@ -162,6 +168,7 @@ static void test_update_four_slots_fill_independently(void **state) {
|
|||||||
char_t name[ASSET_FILE_NAME_MAX];
|
char_t name[ASSET_FILE_NAME_MAX];
|
||||||
snprintf(name, sizeof(name), "asset%d.locale", i);
|
snprintf(name, sizeof(name), "asset%d.locale", i);
|
||||||
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
|
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
assetEntryLock(entries[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t ret = assetUpdate();
|
errorret_t ret = assetUpdate();
|
||||||
@@ -169,6 +176,7 @@ static void test_update_four_slots_fill_independently(void **state) {
|
|||||||
|
|
||||||
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
for(int i = 0; i < ASSET_LOADING_COUNT_MAX; i++) {
|
||||||
assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entries[i]->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(entries[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
@@ -201,6 +209,8 @@ static void test_update_noop_on_empty_table(void **state) {
|
|||||||
|
|
||||||
static void test_update_loaded_entry_not_redispatched(void **state) {
|
static void test_update_loaded_entry_not_redispatched(void **state) {
|
||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assetUpdate(); // slot freed
|
assetUpdate(); // slot freed
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
@@ -212,6 +222,8 @@ static void test_update_loaded_entry_not_redispatched(void **state) {
|
|||||||
|
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
assert_false(loading_slot_has_entry(entry));
|
assert_false(loading_slot_has_entry(entry));
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +238,11 @@ static void test_update_overflow_queues_entries(void **state) {
|
|||||||
entries[i] = assetGetEntry(name, ASSET_LOADER_TYPE_LOCALE, NULL);
|
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.
|
// Update 1: fills all slots, first ASSET_LOADING_COUNT_MAX entries reach LOADED.
|
||||||
// The overflow entry has no slot yet and stays NOT_STARTED.
|
// The overflow entry has no slot yet and stays NOT_STARTED.
|
||||||
errorret_t ret = assetUpdate();
|
errorret_t ret = assetUpdate();
|
||||||
@@ -235,17 +252,27 @@ static void test_update_overflow_queues_entries(void **state) {
|
|||||||
}
|
}
|
||||||
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
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
|
// Update 2: LOADED slots are freed. Overflow entry still NOT_STARTED because
|
||||||
// the dispatch phase ran before the slots were cleared this turn.
|
// the dispatch phase ran before the slots were cleared this turn.
|
||||||
ret = assetUpdate();
|
ret = assetUpdate();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
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.
|
// Update 3: now a slot is available; the overflow entry is dispatched and loaded.
|
||||||
ret = assetUpdate();
|
ret = assetUpdate();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entries[ASSET_LOADING_COUNT_MAX]->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
|
||||||
|
assetEntryUnlock(entries[ASSET_LOADING_COUNT_MAX]);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +367,8 @@ static void test_update_reentrant_sync_loader(void **state) {
|
|||||||
|
|
||||||
static void test_getEntry_returns_loaded_entry(void **state) {
|
static void test_getEntry_returns_loaded_entry(void **state) {
|
||||||
assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *a = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(a);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assert_int_equal(a->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(a->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
|
||||||
@@ -348,6 +377,7 @@ static void test_getEntry_returns_loaded_entry(void **state) {
|
|||||||
assetentry_t *b = 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_ptr_equal(a, b);
|
||||||
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(a);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
@@ -358,9 +388,12 @@ static void test_getEntry_returns_loaded_entry(void **state) {
|
|||||||
|
|
||||||
static void test_entry_dispose_clears_entry(void **state) {
|
static void test_entry_dispose_clears_entry(void **state) {
|
||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assetUpdate(); // ensure loading slot is freed before disposing
|
assetUpdate(); // ensure loading slot is freed before disposing
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
errorret_t ret = assetEntryDispose(entry);
|
errorret_t ret = assetEntryDispose(entry);
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
@@ -371,8 +404,11 @@ static void test_entry_dispose_clears_entry(void **state) {
|
|||||||
|
|
||||||
static void test_entry_dispose_slot_reusable(void **state) {
|
static void test_entry_dispose_slot_reusable(void **state) {
|
||||||
assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *a = assetGetEntry("a.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(a);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
|
assetEntryUnlock(a);
|
||||||
assert_false(loading_slot_has_entry(a));
|
assert_false(loading_slot_has_entry(a));
|
||||||
|
|
||||||
assetEntryDispose(a);
|
assetEntryDispose(a);
|
||||||
@@ -383,9 +419,11 @@ static void test_entry_dispose_slot_reusable(void **state) {
|
|||||||
assert_non_null(b);
|
assert_non_null(b);
|
||||||
assert_int_equal(b->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
assert_int_equal(b->state, ASSET_ENTRY_STATE_NOT_STARTED);
|
||||||
|
|
||||||
|
assetEntryLock(b);
|
||||||
errorret_t ret = assetUpdate();
|
errorret_t ret = assetUpdate();
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(b->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(b);
|
||||||
|
|
||||||
assert_int_equal(memoryGetAllocatedCount(), 0);
|
assert_int_equal(memoryGetAllocatedCount(), 0);
|
||||||
}
|
}
|
||||||
@@ -396,8 +434,11 @@ static void test_entry_dispose_slot_reusable(void **state) {
|
|||||||
|
|
||||||
static void test_requireLoaded_already_loaded(void **state) {
|
static void test_requireLoaded_already_loaded(void **state) {
|
||||||
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
assetentry_t *entry = assetGetEntry("test.locale", ASSET_LOADER_TYPE_LOCALE, NULL);
|
||||||
|
|
||||||
|
assetEntryLock(entry);
|
||||||
assetUpdate();
|
assetUpdate();
|
||||||
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
assert_int_equal(entry->state, ASSET_ENTRY_STATE_LOADED);
|
||||||
|
assetEntryUnlock(entry);
|
||||||
|
|
||||||
// Should return immediately without calling assetUpdate again.
|
// Should return immediately without calling assetUpdate again.
|
||||||
errorret_t ret = assetRequireLoaded(entry);
|
errorret_t ret = assetRequireLoaded(entry);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "asset/loader/assetloader.h"
|
#include "asset/loader/assetloader.h"
|
||||||
#include "asset/loader/assetentry.h"
|
#include "asset/loader/assetentry.h"
|
||||||
#include "asset/loader/json/assetjsonloader.h"
|
#include "asset/loader/json/assetjsonloader.h"
|
||||||
|
#include "thread/thread.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
|
|
||||||
@@ -20,6 +21,32 @@
|
|||||||
static const char_t *JSON_VALID = "{\"hello\":\"world\",\"count\":42}";
|
static const char_t *JSON_VALID = "{\"hello\":\"world\",\"count\":42}";
|
||||||
static const char_t *JSON_INVALID = "{ this is definitely not valid json !!!";
|
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
|
// In-memory ZIP
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -110,8 +137,10 @@ static errorret_t loader_ctx_run(loader_ctx_t *ctx) {
|
|||||||
errorret_t ret = assetJsonLoaderSync(&ctx->loading);
|
errorret_t ret = assetJsonLoaderSync(&ctx->loading);
|
||||||
if(errorIsNotOk(ret)) return ret;
|
if(errorIsNotOk(ret)) return ret;
|
||||||
|
|
||||||
ret = assetJsonLoaderAsync(&ctx->loading);
|
if(!run_json_async(&ctx->loading)) {
|
||||||
if(errorIsNotOk(ret)) return ret;
|
ctx->entry.state = ASSET_ENTRY_STATE_ERROR;
|
||||||
|
errorThrow("Async JSON load failed");
|
||||||
|
}
|
||||||
|
|
||||||
return assetJsonLoaderSync(&ctx->loading);
|
return assetJsonLoaderSync(&ctx->loading);
|
||||||
}
|
}
|
||||||
@@ -162,8 +191,7 @@ static void test_json_parse_error(void **state) {
|
|||||||
errorret_t ret = assetJsonLoaderSync(&ctx.loading);
|
errorret_t ret = assetJsonLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
|
|
||||||
ret = assetJsonLoaderAsync(&ctx.loading);
|
assert_true(run_json_async(&ctx.loading));
|
||||||
assert_true(errorIsOk(ret));
|
|
||||||
|
|
||||||
ret = assetJsonLoaderSync(&ctx.loading);
|
ret = assetJsonLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsNotOk(ret));
|
assert_true(errorIsNotOk(ret));
|
||||||
@@ -184,9 +212,7 @@ static void test_json_missing_file(void **state) {
|
|||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
|
|
||||||
// Async phase stat-fails because the file isn't in the ZIP.
|
// Async phase stat-fails because the file isn't in the ZIP.
|
||||||
ret = assetJsonLoaderAsync(&ctx.loading);
|
assert_false(run_json_async(&ctx.loading));
|
||||||
assert_true(errorIsNotOk(ret));
|
|
||||||
errorCatch(ret);
|
|
||||||
|
|
||||||
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
|
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "asset/loader/assetloader.h"
|
#include "asset/loader/assetloader.h"
|
||||||
#include "asset/loader/assetentry.h"
|
#include "asset/loader/assetentry.h"
|
||||||
#include "asset/loader/display/assettilesetloader.h"
|
#include "asset/loader/display/assettilesetloader.h"
|
||||||
|
#include "thread/thread.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
|
|
||||||
@@ -91,6 +92,32 @@ static const uint8_t DTF_INVALID_UV[] = {
|
|||||||
0x00, 0x00, 0x00, 0x40
|
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
|
// In-memory ZIP
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -183,8 +210,10 @@ static errorret_t loader_ctx_run(loader_ctx_t *ctx) {
|
|||||||
errorret_t ret = assetTilesetLoaderSync(&ctx->loading);
|
errorret_t ret = assetTilesetLoaderSync(&ctx->loading);
|
||||||
if(errorIsNotOk(ret)) return ret;
|
if(errorIsNotOk(ret)) return ret;
|
||||||
|
|
||||||
ret = assetTilesetLoaderAsync(&ctx->loading);
|
if(!run_tileset_async(&ctx->loading)) {
|
||||||
if(errorIsNotOk(ret)) return ret;
|
ctx->entry.state = ASSET_ENTRY_STATE_ERROR;
|
||||||
|
errorThrow("Async tileset load failed");
|
||||||
|
}
|
||||||
|
|
||||||
return assetTilesetLoaderSync(&ctx->loading);
|
return assetTilesetLoaderSync(&ctx->loading);
|
||||||
}
|
}
|
||||||
@@ -238,8 +267,7 @@ static void test_tileset_bad_magic(void **state) {
|
|||||||
|
|
||||||
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
ret = assetTilesetLoaderAsync(&ctx.loading);
|
assert_true(run_tileset_async(&ctx.loading));
|
||||||
assert_true(errorIsOk(ret));
|
|
||||||
ret = assetTilesetLoaderSync(&ctx.loading);
|
ret = assetTilesetLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsNotOk(ret));
|
assert_true(errorIsNotOk(ret));
|
||||||
errorCatch(ret);
|
errorCatch(ret);
|
||||||
@@ -255,8 +283,7 @@ static void test_tileset_bad_version(void **state) {
|
|||||||
|
|
||||||
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
ret = assetTilesetLoaderAsync(&ctx.loading);
|
assert_true(run_tileset_async(&ctx.loading));
|
||||||
assert_true(errorIsOk(ret));
|
|
||||||
ret = assetTilesetLoaderSync(&ctx.loading);
|
ret = assetTilesetLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsNotOk(ret));
|
assert_true(errorIsNotOk(ret));
|
||||||
errorCatch(ret);
|
errorCatch(ret);
|
||||||
@@ -328,9 +355,7 @@ static void test_tileset_missing_file(void **state) {
|
|||||||
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
errorret_t ret = assetTilesetLoaderSync(&ctx.loading);
|
||||||
assert_true(errorIsOk(ret));
|
assert_true(errorIsOk(ret));
|
||||||
|
|
||||||
ret = assetTilesetLoaderAsync(&ctx.loading);
|
assert_false(run_tileset_async(&ctx.loading));
|
||||||
assert_true(errorIsNotOk(ret));
|
|
||||||
errorCatch(ret);
|
|
||||||
|
|
||||||
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
|
assert_int_equal(ctx.entry.state, ASSET_ENTRY_STATE_ERROR);
|
||||||
loader_ctx_dispose(&ctx);
|
loader_ctx_dispose(&ctx);
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Embed a JS file as a C string header")
|
||||||
|
parser.add_argument("--input", required=True, help="Path to input JS file")
|
||||||
|
parser.add_argument("--output", required=True, help="Path to output .h file")
|
||||||
|
parser.add_argument("--name", help="C identifier name (default: derived from filename)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.name:
|
||||||
|
name = args.name
|
||||||
|
else:
|
||||||
|
stem = os.path.splitext(os.path.basename(args.input))[0]
|
||||||
|
name = re.sub(r"[^a-zA-Z0-9]", "_", stem).upper() + "_JS"
|
||||||
|
|
||||||
|
with open(args.input, "r", encoding="utf-8") as f:
|
||||||
|
source = f.read()
|
||||||
|
|
||||||
|
def escape_line(s):
|
||||||
|
s = s.replace("\\", "\\\\")
|
||||||
|
s = s.replace('"', '\\"')
|
||||||
|
s = s.replace("\r", "\\r")
|
||||||
|
s = s.replace("\t", "\\t")
|
||||||
|
return s
|
||||||
|
|
||||||
|
lines = source.split("\n")
|
||||||
|
if lines and lines[-1] == "":
|
||||||
|
lines = lines[:-1]
|
||||||
|
|
||||||
|
out = [
|
||||||
|
"#pragma once",
|
||||||
|
"#include <stddef.h>",
|
||||||
|
"",
|
||||||
|
f"static const char {name}[] =",
|
||||||
|
]
|
||||||
|
|
||||||
|
if not lines:
|
||||||
|
out[-1] += ' "";'
|
||||||
|
else:
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
suffix = ";" if i == len(lines) - 1 else ""
|
||||||
|
out.append(f' "{escape_line(line)}\\n"{suffix}')
|
||||||
|
|
||||||
|
out += [
|
||||||
|
"",
|
||||||
|
f"static const size_t {name}_SIZE = sizeof({name}) - 1;",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(args.output), exist_ok=True)
|
||||||
|
with open(args.output, "w", encoding="utf-8") as f:
|
||||||
|
f.write("\n".join(out))
|
||||||
Reference in New Issue
Block a user