From e32d1f09002900a485bfa3f562106b9608c7b637 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sun, 7 Sep 2025 23:24:21 -0500 Subject: [PATCH] Add exec command --- assets/CMakeLists.txt | 2 + assets/init.cfg | 1 - assets/init.dcf | 3 ++ assets/test.dcf | 1 + src/asset/asset.c | 7 ++-- src/asset/asset.h | 3 ++ src/asset/assetmanager.c | 2 - src/asset/type/CMakeLists.txt | 1 + src/asset/type/assetconfig.c | 61 +++++++++++++++++++++++++++++++ src/asset/type/assetconfig.h | 42 +++++++++++++++++++++ src/console/cmd/cmdecho.h | 8 ++-- src/console/cmd/cmdexec.h | 50 +++++++++++++++++++++++++ src/console/console.c | 33 +++++++++++++---- src/engine/engine.c | 6 ++- tools/assetstool/processasset.py | 3 ++ tools/assetstool/processconfig.py | 44 ++++++++++++++++++++++ 16 files changed, 249 insertions(+), 18 deletions(-) delete mode 100644 assets/init.cfg create mode 100644 assets/init.dcf create mode 100644 assets/test.dcf create mode 100644 src/asset/type/assetconfig.c create mode 100644 src/asset/type/assetconfig.h create mode 100644 src/console/cmd/cmdexec.h create mode 100644 tools/assetstool/processconfig.py diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt index e4c872e..f9894eb 100644 --- a/assets/CMakeLists.txt +++ b/assets/CMakeLists.txt @@ -6,5 +6,7 @@ add_asset(PALETTE first.palette.png) add_asset(IMAGE font_minogram.png type=ALPHA) add_asset(IMAGE entities.png type=PALETTIZED) +add_asset(CONFIG init.dcf) +add_asset(CONFIG test.dcf) # add_asset(TILESET entities.tsx) \ No newline at end of file diff --git a/assets/init.cfg b/assets/init.cfg deleted file mode 100644 index d0866c4..0000000 --- a/assets/init.cfg +++ /dev/null @@ -1 +0,0 @@ -bind w up; \ No newline at end of file diff --git a/assets/init.dcf b/assets/init.dcf new file mode 100644 index 0000000..38cda2b --- /dev/null +++ b/assets/init.dcf @@ -0,0 +1,3 @@ +echo "test1"; +echo "test2"; +exec "test"; \ No newline at end of file diff --git a/assets/test.dcf b/assets/test.dcf new file mode 100644 index 0000000..c86b479 --- /dev/null +++ b/assets/test.dcf @@ -0,0 +1 @@ +echo "test file" \ No newline at end of file diff --git a/src/asset/asset.c b/src/asset/asset.c index cc33b70..9768349 100644 --- a/src/asset/asset.c +++ b/src/asset/asset.c @@ -15,9 +15,12 @@ assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT] = { [ASSET_TYPE_PALETTE_IMAGE] = { "DPI", assetPaletteImageLoad, assetPaletteImageDispose }, - [ASSET_TYPE_ALPHA_IMAGE] = { + [ASSET_TYPE_ALPHA_IMAGE] = { "DAI", assetAlphaImageLoad, assetAlphaImageDispose }, + [ASSET_TYPE_CONFIG] = { + "DCF", assetConfigLoad, assetConfigDispose + } }; errorret_t assetInit(asset_t *asset, const char_t *filename) { @@ -42,8 +45,6 @@ errorret_t assetInit(asset_t *asset, const char_t *filename) { if(asset->file == NULL) errorThrow("Failed to open asset file: %s", filename); zip_fclose(asset->file); asset->file = NULL; - - consolePrint("Initialized asset: %s", filename); errorOk(); } diff --git a/src/asset/asset.h b/src/asset/asset.h index 630ce6c..5a3e97c 100644 --- a/src/asset/asset.h +++ b/src/asset/asset.h @@ -12,6 +12,7 @@ #include "asset/type/assetpaletteimage.h" #include "asset/type/assetalphaimage.h" +#include "asset/type/assetconfig.h" #define ASSET_HEADER_SIZE 3 #define ASSET_REFERENCE_COUNT_MAX 8 @@ -31,6 +32,7 @@ typedef enum { ASSET_TYPE_UNKNOWN, ASSET_TYPE_PALETTE_IMAGE, ASSET_TYPE_ALPHA_IMAGE, + ASSET_TYPE_CONFIG, ASSET_TYPE_COUNT } assettype_t; @@ -46,6 +48,7 @@ typedef struct asset_s { union { assetpaletteimage_t paletteImage; assetalphaimager_t alphaImage; + assetconfig_t config; }; } asset_t; diff --git a/src/asset/assetmanager.c b/src/asset/assetmanager.c index f82e5a8..10f96a9 100644 --- a/src/asset/assetmanager.c +++ b/src/asset/assetmanager.c @@ -33,7 +33,6 @@ errorret_t assetManagerInit(void) { // Try open ASSET_MANAGER.zip = zip_open(searchPath, ZIP_RDONLY, NULL); if(ASSET_MANAGER.zip == NULL) continue; - consolePrint("Opened asset file: %s", searchPath); break; } @@ -51,7 +50,6 @@ void assetManagerUpdate(void) { // Check if the asset is loaded if(asset->file != NULL) { asset->state = ASSET_STATE_LOADED; - consolePrint("Asset loaded: %s", asset->filename); } } ++asset; diff --git a/src/asset/type/CMakeLists.txt b/src/asset/type/CMakeLists.txt index ba07c92..7c2e9ea 100644 --- a/src/asset/type/CMakeLists.txt +++ b/src/asset/type/CMakeLists.txt @@ -7,5 +7,6 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE assetalphaimage.c + assetconfig.c assetpaletteimage.c ) \ No newline at end of file diff --git a/src/asset/type/assetconfig.c b/src/asset/type/assetconfig.c new file mode 100644 index 0000000..58e7a4b --- /dev/null +++ b/src/asset/type/assetconfig.c @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "asset/asset.h" +#include "console/console.h" +#include "assert/assert.h" + +errorret_t assetConfigLoad(asset_t *asset) { + char_t buffer[CONSOLE_LINE_MAX + 1]; + + // Read byte by byte. + zip_int64_t bytesRead = 0; + zip_int64_t totalBytesRead = 0; + char_t c; + while(1) { + bytesRead = zip_fread( + asset->file, + &c, + 1 + ); + + if(bytesRead < 0) errorThrow("Failed to read from config asset file"); + + // Is byte execute? + if(c != ';' && c != '\n' && c != '\r' && bytesRead == 1) { + // Not execute, add to buffer. + buffer[totalBytesRead] = c; + if(++totalBytesRead >= CONSOLE_LINE_MAX) { + errorThrow("Config line too long!"); + } + continue; + } + + // Execute buffer. + buffer[totalBytesRead] = '\0'; + consoleExec(buffer); + totalBytesRead = 0; + + if(bytesRead == 0) break; + } + + if(totalBytesRead > 0) { + // Execute remaining buffer. + buffer[totalBytesRead] = '\0'; + consoleExec(buffer); + } + + errorOk(); +} + +errorret_t assetConfigExecute(asset_t *asset) { + errorOk(); +} + +errorret_t assetConfigDispose(asset_t *asset) { + errorOk(); +} \ No newline at end of file diff --git a/src/asset/type/assetconfig.h b/src/asset/type/assetconfig.h new file mode 100644 index 0000000..849855e --- /dev/null +++ b/src/asset/type/assetconfig.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "error/error.h" + +typedef struct asset_s asset_t; + +typedef struct { + zip_int64_t pos; + zip_int64_t size; +} assetconfig_t; + +/** + * Loads a config asset from the given asset structure. The asset must be of + * type ASSET_TYPE_CONFIG and must be loaded. + * + * @param asset The asset to load the config from. + * @return An error code. + */ +errorret_t assetConfigLoad(asset_t *asset); + +/** + * Executes the config commands in the given asset. The asset must be of type + * ASSET_TYPE_CONFIG and must be loaded. + * + * @param asset The asset to execute the config from. + * @return An error code. + */ +errorret_t assetConfigExecute(asset_t *asset); + +/** + * Disposes of a config asset, freeing any allocated resources. + * + * @param asset The asset to dispose of. + * @return An error code. + */ +errorret_t assetConfigDispose(asset_t *asset); \ No newline at end of file diff --git a/src/console/cmd/cmdecho.h b/src/console/cmd/cmdecho.h index d9cc4a0..6bcd159 100644 --- a/src/console/cmd/cmdecho.h +++ b/src/console/cmd/cmdecho.h @@ -9,10 +9,10 @@ #include "console/console.h" void cmdEcho(const consolecmdexec_t *exec) { - assertTrue( - exec->argc >= 1, - "echo command requires 1 argument." - ); + if(exec->argc < 1) { + consolePrint("Expected 1 argument: "); + return; + } consolePrint("%s", exec->argv[0]); } \ No newline at end of file diff --git a/src/console/cmd/cmdexec.h b/src/console/cmd/cmdexec.h new file mode 100644 index 0000000..3d5d905 --- /dev/null +++ b/src/console/cmd/cmdexec.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "console/console.h" +#include "asset/assetmanager.h" + +void cmdExec(const consolecmdexec_t *exec) { + if(exec->argc < 1) { + consolePrint("Expected 1 argument: "); + return; + } + + char_t file[FILENAME_MAX]; + stringCopy(file, exec->argv[0], FILENAME_MAX); + if(!stringEndsWith(file, ".dcf")) { + sprintf( + file, + "%s.dcf", + exec->argv[0] + ); + } + + ref_t ref; + asset_t asset; + errorret_t ret = assetInit(&asset, file); + if(ret.code != ERROR_OK) { + errorPrint(ret); + consolePrint("Failed to load asset %s", file); + return; + } + + ret = assetLoad(&asset); + if(asset.type != ASSET_TYPE_CONFIG) { + consolePrint("Asset is not a config: %s", file); + assetDispose(&asset); + return; + } + + assetDispose(&asset); + if(ret.code != ERROR_OK) { + errorPrint(ret); + consolePrint("Failed to load asset %s", file); + return; + } +} \ No newline at end of file diff --git a/src/console/console.c b/src/console/console.c index 7abc349..915fa75 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -13,6 +13,7 @@ #include "console/cmd/cmdecho.h" #include "console/cmd/cmdset.h" #include "console/cmd/cmdget.h" +#include "console/cmd/cmdexec.h" #include "input/input.h" console_t CONSOLE; @@ -25,6 +26,7 @@ void consoleInit() { CONSOLE.cmdSet = consoleRegCmd("set", cmdSet); consoleRegCmd("quit", cmdQuit); consoleRegCmd("echo", cmdEcho); + consoleRegCmd("exec", cmdExec); consolePrint(" = Dawn Console = "); @@ -316,19 +318,36 @@ void consoleUpdate() { CONSOLE.visible = !CONSOLE.visible; } - // Exec pending buffer. - for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) { - consolecmdexec_t *exec = &CONSOLE.execBuffer[i]; - assertNotNull(exec->cmd, "Command execution has no command."); - exec->cmd->function(exec); + // Anything to exec? + if(CONSOLE.execBufferCount == 0) { + #if CONSOLE_POSIX + threadMutexUnlock(&CONSOLE.execMutex); + #endif + return; } - // Clear the exec buffer - CONSOLE.execBufferCount = 0; + // Copy the exec buffer, this allows exec command to work + consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX]; + uint32_t execBufferCount = CONSOLE.execBufferCount; + memoryCopy( + execBuffer, + CONSOLE.execBuffer, + sizeof(consolecmdexec_t) * execBufferCount + ); + // Clear the exec buffer and unlock so new commands can be added while we + // process the current ones. + CONSOLE.execBufferCount = 0; #if CONSOLE_POSIX threadMutexUnlock(&CONSOLE.execMutex); #endif + + // Exec pending buffer. + for(uint32_t i = 0; i < execBufferCount; i++) { + consolecmdexec_t *exec = &execBuffer[i]; + assertNotNull(exec->cmd, "Command execution has no command."); + exec->cmd->function(exec); + } } void consoleDispose(void) { diff --git a/src/engine/engine.c b/src/engine/engine.c index 9134db7..e44da0c 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -16,6 +16,8 @@ engine_t ENGINE; +asset_t *outAsset; +ref_t outRef; errorret_t engineInit(void) { memoryZero(&ENGINE, sizeof(engine_t)); @@ -23,12 +25,14 @@ errorret_t engineInit(void) { // Init systems. Order is important. timeInit(); - inputInit(); consoleInit(); + inputInit(); errorChain(assetManagerInit()); errorChain(displayInit()); rpgInit(); + consoleExec("exec init.dcf"); + errorOk(); } diff --git a/tools/assetstool/processasset.py b/tools/assetstool/processasset.py index cd182e5..aecc0c2 100644 --- a/tools/assetstool/processasset.py +++ b/tools/assetstool/processasset.py @@ -2,6 +2,7 @@ import sys # from processtileset import processTileset from processimage import processImage from processpalette import processPalette +from processconfig import processConfig processedAssets = [] @@ -17,6 +18,8 @@ def processAsset(asset): return processPalette(asset) elif t == 'image': return processImage(asset) + elif t == 'config': + return processConfig(asset) # elif t == 'tileset': # return processTileset(asset) else: diff --git a/tools/assetstool/processconfig.py b/tools/assetstool/processconfig.py new file mode 100644 index 0000000..40df4eb --- /dev/null +++ b/tools/assetstool/processconfig.py @@ -0,0 +1,44 @@ +import os +import sys +from args import args +from assethelpers import getAssetRelativePath + +def processConfig(asset): + assetPath = asset['path'] + print(f"Processing config: {assetPath}") + + # Takes each line, seperates it by either semicolon or newline, + # trims whitespace. Then outputs it to a file. + with open(assetPath, "r") as f: + lines = f.read().replace('\r', '').split('\n') + + commands = [] + for line in lines: + lineCommands = line.split(';') + for command in lineCommands: + command = command.strip() + if command != '': + commands.append(command) + + data = bytearray() + data.extend(b"DCF") # Dusk Config File + for command in commands: + # Convert to string and seperate each command with semicolon + data.extend(command.encode('utf-8')) + data.append(0x3B) # Semicolon + + if len(commands) > 0: + data.pop() # Remove last semicolon + + relative = getAssetRelativePath(assetPath) + fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0] + outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf") + outputFilePath = os.path.join(args.output_assets, outputFileRelative) + os.makedirs(os.path.dirname(outputFilePath), exist_ok=True) + with open(outputFilePath, "wb") as f: + f.write(data) + + outConfig = { + "files": [ outputFilePath ], + } + return outConfig \ No newline at end of file