archivemuh

This commit is contained in:
2025-08-20 19:18:38 -05:00
parent 1411c2e96b
commit fbfcbe9578
173 changed files with 2802 additions and 13 deletions

View File

@@ -0,0 +1,23 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
console.c
consolecmd.c
consolevar.c
)
# Subdirectories
add_subdirectory(cmd)
# Compiler flags.
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
target_compile_definitions(${DUSK_TARGET_NAME}
PRIVATE
DUSK_CONSOLE_TERMIOS=1
)
endif()

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
)

18
src/console/cmd/cmdecho.h Normal file
View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdEcho(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 1,
"echo command requires 1 argument."
);
consolePrint("%s", exec->argv[0]);
}

25
src/console/cmd/cmdget.h Normal file
View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdGet(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 1,
"Get command requires 1 argument."
);
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
consolePrint("%s", var->value);
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
}

15
src/console/cmd/cmdquit.h Normal file
View File

@@ -0,0 +1,15 @@
/**
* 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 "engine/engine.h"
void cmdQuit(const consolecmdexec_t *exec) {
consolePrint("Quitting...");
ENGINE.running = false;
}

27
src/console/cmd/cmdset.h Normal file
View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdSet(const consolecmdexec_t *exec) {
assertTrue(exec->argc >= 2, "set command requires 2 arguments.");
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
consoleVarSetValue(var, exec->argv[1]);
consolePrint("%s %s", var->name, var->value);
for(i = 0; i < var->eventCount; i++) {
assertNotNull(var->events[i], "Event is NULL");
var->events[i](var);
}
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
}

344
src/console/console.c Normal file
View File

@@ -0,0 +1,344 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "console/cmd/cmdquit.h"
#include "console/cmd/cmdecho.h"
#include "console/cmd/cmdset.h"
#include "console/cmd/cmdget.h"
console_t CONSOLE;
void consoleInit() {
memoryZero(&CONSOLE, sizeof(console_t));
// Register the get and set command.
CONSOLE.cmdGet = consoleRegCmd("get", cmdGet);
CONSOLE.cmdSet = consoleRegCmd("set", cmdSet);
consoleRegCmd("quit", cmdQuit);
consoleRegCmd("echo", cmdEcho);
consolePrint(" = Dawn Console = ");
#if DUSK_CONSOLE_TERMIOS
// Create termios session.
struct termios newTermios;
tcgetattr(STDIN_FILENO, &CONSOLE.originalTermios);
// Disable canonical mode & echo
newTermios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);// Set stdin to non-blocking
#endif
}
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
consoleCmdInit(cmd, name, function);
return cmd;
}
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consolevar_t *var = &CONSOLE.variables[CONSOLE.variableCount++];
consoleVarInitListener(var, name, value, event);
return var;
}
void consolePrint(const char_t *message, ...) {
char_t buffer[CONSOLE_LINE_MAX];
va_list args;
va_start(args, message);
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
// Move all lines back
memoryMove(
CONSOLE.line[0],
CONSOLE.line[1],
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
);
// Copy the new line
memoryCopy(
CONSOLE.line[CONSOLE_HISTORY_MAX - 1],
buffer,
len + 1
);
printf("%s\n", buffer);
}
void consoleExec(const char_t *line) {
assertNotNull(line, "line must not be NULL");
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Too many commands in the buffer."
);
char_t buffer[CONSOLE_LINE_MAX];
size_t i = 0, j = 0;
char_t c;
consoleexecstate_t state = CONSOLE_EXEC_STATE_INITIAL;
consolecmdexec_t *exec = NULL;
while(state != CONSOLE_EXEC_STATE_FULLY_PARSED) {
c = line[i];
switch(state) {
case CONSOLE_EXEC_STATE_INITIAL:
assertTrue(j == 0, "Buffer not empty?");
if(c == '\0') {
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
break;
}
if(stringIsWhitespace(c) || c == ';') {
i++;
continue;
}
state = CONSOLE_EXEC_STATE_PARSE_CMD;
break;
case CONSOLE_EXEC_STATE_PARSE_CMD:
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_CMD_PARSED;
continue;
}
if(c == '"') {
// Can't handle quotes within the command.
consolePrint("Invalid command");
while(c != '\0' && c != ';') c = line[++i];
continue;
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Command too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_CMD_PARSED:
if(j == 0) {
state = CONSOLE_EXEC_STATE_INITIAL;
continue;
}
// Create exec
assertNull(exec, "Existing command parsing?");
exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount];
memoryZero(exec, sizeof(consolecmdexec_t));
buffer[j] = '\0';
stringCopy(exec->command, buffer, CONSOLE_LINE_MAX);
state = CONSOLE_EXEC_STATE_FIND_ARG;
j = 0;// Free up buffer
break;
case CONSOLE_EXEC_STATE_FIND_ARG:
if(c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_CMD_FINISHED;
continue;
}
if(stringIsWhitespace(c)) {
i++;
continue;
}
if(c == '"') {
state = CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED;
i++;
} else {
state = CONSOLE_EXEC_STATE_PARSE_ARG;
}
break;
case CONSOLE_EXEC_STATE_PARSE_ARG:
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_ARG_PARSED;
continue;
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED:
if(c == '"') {
state = CONSOLE_EXEC_STATE_ARG_PARSED;
i++;
continue;
}
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
if(c == '\\') {
c = line[++i];
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_ARG_PARSED:
buffer[j] = '\0';
stringCopy(exec->argv[exec->argc++], buffer, CONSOLE_LINE_MAX);
state = CONSOLE_EXEC_STATE_FIND_ARG;
j = 0;// Free up buffer
break;
case CONSOLE_EXEC_STATE_CMD_FINISHED:
assertNotNull(exec, "No command found?");
// Now, is there a command that matches?
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
consolecmd_t *cmd = &CONSOLE.commands[k];
if(stringCompare(cmd->name, exec->command) != 0) continue;
exec->cmd = cmd;
break;
}
if(exec->cmd == NULL) {
// Command wasn't found, is there a variable that matches?
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
consolevar_t *var = &CONSOLE.variables[k];
if(stringCompare(var->name, exec->command) != 0) continue;
// Matching variable found, is this a GET or a SET?
if(exec->argc == 0) {
exec->cmd = CONSOLE.cmdGet;
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 1;
} else {
exec->cmd = CONSOLE.cmdSet;
stringCopy(exec->argv[1], exec->argv[0], CONSOLE_LINE_MAX);
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 2;
}
break;
}
if(exec->cmd == NULL) {
consolePrint("Command not found", exec->command);
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
break;
}
}
// Prep for next command.
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
CONSOLE.execBufferCount++;
break;
default:
assertUnreachable("Invalid state.");
break;
}
}
}
// May move these later
void consoleUpdate() {
#if DUSK_CONSOLE_TERMIOS
char_t c;
for(;;) {
if(!read(STDIN_FILENO, &c, 1)) break;
putchar(c);
}
#endif
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);
}
// #if DUSK_KEYBOARD_SUPPORT == 1
// uint8_t key;
// while((key = inputKeyboardPop()) != 0) {
// printf("Key pressed: %c\n", key);
// switch(key) {
// case 0:
// break;
// case INPUT_KEY_ENTER:
// consoleExec(CONSOLE.inputBuffer);
// CONSOLE.inputIndex = 0;
// CONSOLE.inputBuffer[0] = '\0';
// break;
// case INPUT_KEY_BACKSPACE:
// if(CONSOLE.inputIndex > 0) {
// CONSOLE.inputIndex--;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// default:
// if(
// key >= INPUT_KEY_ASCII_START && key <= INPUT_KEY_ASCII_END &&
// CONSOLE.inputIndex < CONSOLE_LINE_MAX - 1
// ) {
// CONSOLE.inputBuffer[CONSOLE.inputIndex++] = key;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// }
// }
// #endif
// Clear the exec buffer
CONSOLE.execBufferCount = 0;
}
void consoleDispose(void) {
// Reset termios
#if DUSK_CONSOLE_TERMIOS
tcsetattr(STDIN_FILENO, TCSANOW, &CONSOLE.originalTermios);
#endif
}

112
src/console/console.h Normal file
View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "consolevar.h"
#include "consolecmd.h"
#if DUSK_CONSOLE_TERMIOS
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
#endif
typedef enum {
CONSOLE_EXEC_STATE_INITIAL,
CONSOLE_EXEC_STATE_PARSE_CMD,
CONSOLE_EXEC_STATE_CMD_PARSED,
CONSOLE_EXEC_STATE_FIND_ARG,
CONSOLE_EXEC_STATE_PARSE_ARG,
CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED,
CONSOLE_EXEC_STATE_ARG_PARSED,
CONSOLE_EXEC_STATE_CMD_FINISHED,
CONSOLE_EXEC_STATE_FULLY_PARSED
} consoleexecstate_t;
typedef struct {
consolecmd_t commands[CONSOLE_COMMANDS_MAX];
uint32_t commandCount;
consolevar_t variables[CONSOLE_VARIABLES_MAX];
uint32_t variableCount;
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX];
uint32_t execBufferCount;
consolecmd_t *cmdGet;
consolecmd_t *cmdSet;
bool_t visible;
#if DUSK_CONSOLE_TERMIOS
struct termios originalTermios;
char_t inputBuffer[CONSOLE_LINE_MAX];
int32_t inputBufferLength;
#endif
} console_t;
extern console_t CONSOLE;
/**
* Initializes the console.
*/
void consoleInit();
/**
* Registers a console command.
*
* @param name The name of the command.
* @param function The function to execute when the command is called.
* @return The registered command.
*/
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function);
/**
* Registers a console variable.
*
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
* @return The registered variable.
*/
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param name The name of the variable.
* @param value The new value of the variable.
*/
void consolePrint(
const char_t *message,
...
);
/**
* Executes a console command.
*
* @param line The line to execute.
*/
void consoleExec(const char_t *line);
/**
* Processes the console's pending commands.
*/
void consoleUpdate();
/**
* Disposes of the console.
*/
void consoleDispose(void);

27
src/console/consolecmd.c Normal file
View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolecmd.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
) {
assertNotNull(cmd, "Command is NULL.");
assertNotNull(name, "Name is NULL.");
assertNotNull(function, "Function is NULL.");
assertStrLenMin(name, 1, "Name is empty.");
assertStrLenMax(name, CONSOLE_CMD_NAME_MAX, "Name is too long.");
memoryZero(cmd, sizeof(consolecmd_t));
stringCopy(cmd->name, name, CONSOLE_CMD_NAME_MAX);
cmd->function = function;
}

39
src/console/consolecmd.h Normal file
View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolecmd_s consolecmd_t;
typedef struct {
consolecmd_t *cmd;
char_t command[CONSOLE_LINE_MAX];
char_t argv[CONSOLE_CMD_ARGC_MAX][CONSOLE_LINE_MAX];
uint32_t argc;
} consolecmdexec_t;
typedef void (*consolecmdfunc_t)(const consolecmdexec_t *exec);
typedef struct consolecmd_s {
char_t name[CONSOLE_CMD_NAME_MAX];
consolecmdfunc_t function;
} consolecmd_t;
/**
* Initializes a console command.
*
* @param cmd Pointer to the console command.
* @param name The name of the command.
* @param function The function to execute when the command is called.
*/
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
);

21
src/console/consoledefs.h Normal file
View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#define CONSOLE_CMD_NAME_MAX 32
#define CONSOLE_CMD_ARGC_MAX 16
#define CONSOLE_COMMANDS_MAX 128
#define CONSOLE_VARIABLES_MAX 128
#define CONSOLE_LINE_MAX 256
#define CONSOLE_HISTORY_MAX 32
#define CONSOLE_EXEC_BUFFER_MAX 16
#define CONSOLE_VAR_NAME_MAX 32
#define CONSOLE_VAR_VALUE_MAX 128
#define CONSOLE_VAR_EVENTS_MAX 8

64
src/console/consolevar.c Normal file
View File

@@ -0,0 +1,64 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolevar.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
) {
assertNotNull(var, "var must not be NULL");
assertNotNull(name, "name must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMin(name, 1, "name must not be empty");
assertStrLenMax(name, CONSOLE_VAR_NAME_MAX, "name is too long");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
memoryZero(var, sizeof(consolevar_t));
stringCopy(var->name, name, CONSOLE_VAR_NAME_MAX);
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
}
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consoleVarInit(var, name, value);
if(event) consoleVarListen(var, event);
}
void consoleVarSetValue(consolevar_t *var, const char_t *value) {
assertNotNull(var, "var must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
uint8_t i = 0;
while (i < var->eventCount) {
var->events[i](var);
i++;
}
}
void consoleVarListen(consolevar_t *var, consolevarchanged_t event) {
assertNotNull(var, "var must not be NULL");
assertNotNull(event, "event must not be NULL");
assertTrue(
var->eventCount < CONSOLE_VAR_EVENTS_MAX,
"Event count is too high"
);
var->events[var->eventCount++] = event;
}

65
src/console/consolevar.h Normal file
View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolevar_s consolevar_t;
typedef void (*consolevarchanged_t)(const consolevar_t *var);
typedef struct consolevar_s {
char_t name[CONSOLE_VAR_NAME_MAX];
char_t value[CONSOLE_VAR_VALUE_MAX];
consolevarchanged_t events[CONSOLE_VAR_EVENTS_MAX];
uint8_t eventCount;
} consolevar_t;
/**
* Initializes a console variable.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
*/
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
);
/**
* Initializes a console variable with a listener.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
*/
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param var Pointer to the console variable.
* @param value The new value of the variable.
*/
void consoleVarSetValue(consolevar_t *var, const char_t *value);
/**
* Registers an event to be called when the value of a console variable changes.
*
* @param var Pointer to the console variable.
* @param event The event to register.
*/
void consoleVarListen(consolevar_t *var, consolevarchanged_t event);