Basic window.

This commit is contained in:
2025-02-20 22:09:21 -06:00
commit 11806510c0
47 changed files with 7696 additions and 0 deletions

8
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,8 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(dusk)
add_subdirectory(duskgl)
add_subdirectory(duskglfw)

21
src/dusk/CMakeLists.txt 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
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
game.c
input.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(display)

View File

@ -0,0 +1,10 @@
# 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
assert.c
)

79
src/dusk/assert/assert.c Normal file
View File

@ -0,0 +1,79 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert.h"
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
) {
if(x != true) {
fprintf(
stderr,
"Assertion Failed in %s:%i\n\n%s\n",
file,
line,
message
);
abort();
}
}
void assertFalseImpl(
const char *file,
const int32_t line,
bool x,
const char *message
) {
assertTrueImpl(file, line, !x, message);
}
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
) {
assertTrueImpl(file, line, false, message);
}
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer != NULL,
message
);
}
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer == NULL,
message
);
}
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
) {
assertUnreachableImpl(file, line, message);
}

122
src/dusk/assert/assert.h Normal file
View File

@ -0,0 +1,122 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Assert a given value to be true.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as true.
* @param message Message to throw against assertion failure.
*/
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
);
/**
* Asserts a given statement to be false.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as false.
* @param message Message to throw against assertion failure.
*/
void assertFalseImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
);
/**
* Asserts that a given line of code is unreachable. Essentially a forced
* assertion failure, good for "edge cases"
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
);
/**
* Assert a given pointer to not point to a null pointer.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is not a null pointer.
* @param message Message to throw against assertion failure.
*/
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a given pointer to be a nullptr.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is nullptr.
* @param message Message to throw against assertion failure.
*/
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a function as being deprecated.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
);
#define assertTrue(x, message) \
assertTrueImpl(__FILE__, __LINE__, x, message)
#define assertFalse(x, message) \
assertFalseImpl(__FILE__, __LINE__, x, message)
#define assertUnreachable(message) \
assertUnreachableImpl(__FILE__, __LINE__, message)
#define assertNotNull(pointer, message) \
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNull(pointer, message) \
assertNullImpl(__FILE__, __LINE__, pointer, message)
#define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertStrLen(str, len, message) \
assertTrue(strlen(str) <= len, message)
#define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message)
// EOF

View File

@ -0,0 +1,11 @@
# 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
)
# Subdirs

18
src/dusk/dusk.h Normal file
View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
typedef bool bool_t;
typedef char char_t;

17
src/dusk/game.c Normal file
View File

@ -0,0 +1,17 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game.h"
#include "input.h"
void gameInit() {
inputInit();
}
void gameUpdate() {
inputUpdate();
}

19
src/dusk/game.h Normal file
View File

@ -0,0 +1,19 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Initializes the game.
*/
void gameInit();
/**
* Updates the game.
*/
void gameUpdate();

46
src/dusk/input.c Normal file
View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
inputstate_t INPUT;
void inputInit() {
memset(&INPUT, 0, sizeof(inputstate_t));
}
void inputUpdate() {
memcpy(INPUT.previous, INPUT.current, sizeof(inputvalue_t) * INPUT_BIND_COUNT);
for(int i = 0; i < INPUT_BIND_COUNT; i++) {
INPUT.current[i] = inputStateGet(i);
}
}
inputvalue_t inputGet(inputbind_t bind) {
return INPUT.current[bind];
}
inputvalue_t inputGetPrevious(inputbind_t bind) {
return INPUT.previous[bind];
}
bool_t inputWasPressed(inputbind_t bind) {
return inputGet(bind) != 0.0f && inputGetPrevious(bind) == 0.0f;
}
bool_t inputWasReleased(inputbind_t bind) {
return inputGet(bind) == 0.0f && inputGetPrevious(bind) != 0.0f;
}
bool_t inputIsDown(inputbind_t bind) {
return inputGet(bind) != 0.0f;
}
bool_t inputIsUp(inputbind_t bind) {
return inputGet(bind) == 0.0f;
}

96
src/dusk/input.h Normal file
View File

@ -0,0 +1,96 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef float_t inputvalue_t;
typedef enum {
INPUT_UP,
INPUT_DOWN,
INPUT_LEFT,
INPUT_RIGHT,
INPUT_ACCEPT,
INPUT_BACK,
INPUT_QUIT
} inputbind_t;
#define INPUT_BIND_COUNT INPUT_QUIT + 1
typedef struct {
inputvalue_t current[INPUT_BIND_COUNT];
inputvalue_t previous[INPUT_BIND_COUNT];
} inputstate_t;
extern inputstate_t INPUT;
/**
* Initializes the input system.
*/
void inputInit();
/**
* Updates the input system.
*/
void inputUpdate();
/**
* Calls on the platform to get the current state of a given input bind.
*
* @param input Input to check.
* @return Current state of the input.
*/
inputvalue_t inputStateGet(const inputbind_t input);
/**
* Returns the current value of the input bind.
*
* @param input Input to get.
* @return Current value of the input.
*/
inputvalue_t inputGet(const inputbind_t input);
/**
* Returns the previous value of the input bind.
*
* @param input Input to get.
* @return Previous value of the input.
*/
inputvalue_t inputGetPrevious(const inputbind_t input);
/**
* Returns whether the input was pressed this frame.
*
* @param input Input to check.
* @return Whether the input was pressed this frame.
*/
bool_t inputWasPressed(inputbind_t input);
/**
* Returns whether the input was released this frame.
*
* @param input Input to check.
* @return Whether the input was released this frame.
*/
bool_t inputWasReleased(inputbind_t input);
/**
* Returns whether the input is currently down.
*
* @param input Input to check.
* @return Whether the input is currently down.
*/
bool_t inputIsDown(inputbind_t input);
/**
* Returns whether the input is currently up.
*
* @param input Input to check.
* @return Whether the input is currently up.
*/
bool_t inputIsUp(inputbind_t input);

View File

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

24
src/dusk/util/math.c Normal file
View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "math.h"
#include "assert/assert.h"
int32_t mathClampi32(
const int32_t value,
const int32_t min,
const int32_t max
) {
assertTrue(max >= min, "mathClampi32: Max must be >= Min");
if(value < min) return min;
if(value > max) return max;
return value;
}
int32_t mathModi32(int32_t value, int32_t modulo) {
return ((value % modulo) + modulo) % modulo;
}

48
src/dusk/util/math.h Normal file
View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Returns the minimum of two values.
* @param a First value.
* @param b Second value.
* @return The minimum of the two values.
*/
#define mathMin(a, b) ((a) < (b) ? (a) : (b))
/**
* Returns the maximum of two values.
* @param a First value.
* @param b Second value.
* @return The maximum of the two values.
*/
#define mathMax(a, b) ((a) > (b) ? (a) : (b))
/**
* Clamps a value between a min and max.
*
* @param value Value to clamp.
* @param min Minimum value to clamp value to.
* @param max Maximum value to clamp value to.
* @return The clamped value.
*/
int32_t mathClampi32(
const int32_t value,
const int32_t min,
const int32_t max
);
/**
* Returns the modulo of a value. For 32 bit integers.
*
* @param value Value to modulo.
* @param modulo Modulo value.
* @return The modulo of the value.
*/
int32_t mathModi32(int32_t value, int32_t modulo);

26
src/dusk/util/random.c Normal file
View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "random.h"
#include "assert/assert.h"
bool_t RANDOM_IS_INITIALIZED = false;
void randomInit() {
assertFalse(RANDOM_IS_INITIALIZED, "Random already initialized.");
srand(1234567890);
RANDOM_IS_INITIALIZED = true;
}
int32_t randomInt32() {
assertTrue(RANDOM_IS_INITIALIZED, "Random not initialized.");
return rand();
}
int32_t randomInt32Range(int32_t min, int32_t max) {
return min + (rand() % (max - min));
}

32
src/dusk/util/random.h Normal file
View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
extern bool_t RANDOM_IS_INITIALIZED;
/**
* Initializes the random number generator, and seeds it.
*/
void randomInit();
/**
* Returns a random 32-bit integer.
*
* @return Random 32-bit integer.
*/
int32_t randomInt32();
/**
* Returns a random 32-bit integer within a given range.
*
* @param min Minimum value.
* @param max Maximum value (exclusive).
* @return Random 32-bit integer within the given range.
*/
int32_t randomInt32Range(int32_t min, int32_t max);

29
src/duskgl/CMakeLists.txt Normal file
View File

@ -0,0 +1,29 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
cglm
archive_static
m
spng_static
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
asset.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(display)

View File

@ -0,0 +1,10 @@
# 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
assertgl.c
)

View File

@ -0,0 +1,68 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assertgl.h"
#include "assert/assert.h"
void assertNotGLErrorCheck(const char_t *file, const int32_t line) {
assertNotNull(file, "File is an invlaid string");
assertTrue(line > 0, "assertNotGLErrorCheck: line is invalid");
char_t *buffer;
GLenum errorCode;
int32_t errorCount = 0;
while((errorCode = glGetError()) != GL_NO_ERROR) {
if(errorCount == 0) {
buffer = malloc(sizeof(char_t) * 4096);
sprintf(buffer, "assertNotGLErrorCheck: %s:%d\n", file, line);
}
errorCount++;
switch (errorCode) {
case GL_INVALID_ENUM:
sprintf(buffer, "%s\nINVALID_ENUM", buffer);
break;
case GL_INVALID_VALUE:
sprintf(buffer, "%s\nINVALID_ENUM", buffer);
break;
case GL_INVALID_OPERATION:
sprintf(buffer, "%s\nINVALID_OPERATION", buffer);
break;
// case GL_INVALID_FRAMEBUFFER_OPERATION:
// sprintf(buffer, "%s\nINVALID_FRAMEBUFFER_OPERATION", buffer);
// break;
case GL_OUT_OF_MEMORY:
sprintf(buffer, "%s\nOUT_OF_MEMORY", buffer);
break;
case GL_STACK_UNDERFLOW:
sprintf(buffer, "%s\nSTACK_UNDERFLOW", buffer);
break;
case GL_STACK_OVERFLOW:
sprintf(buffer, "%s\nSTACK_OVERFLOW", buffer);
break;
default:
sprintf(buffer, "%s\nUNKNOWN_ERROR", buffer);
break;
}
sprintf(buffer, "%s (%i)\n", buffer, errorCode);
}
if(errorCount > 0) {
assertUnreachable(buffer);
free(buffer);
}
}

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
/**
* Asserts that there are no OpenGL errors.
*
* @param file The file the assertion is being made in.
* @param line The line the assertion is being made in.
*/
void assertNotGLErrorCheck(const char_t *file, const int32_t line);
/**
* Asserts that there are no OpenGL errors.
*/
#define assertNoGLError() assertNotGLErrorCheck(__FILE__, __LINE__)

222
src/duskgl/asset.c Normal file
View File

@ -0,0 +1,222 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset.h"
#include "assert/assert.h"
#include "util/math.h"
asset_t ASSET;
void assetInit() {
const char_t *assetFilename = "dusk.tar";
char_t assetPath[FILENAME_MAX];
const char_t* scanLocations[] = {
EXECUTABLE_DIRECTORY
};
memset(&ASSET, 0, sizeof(asset_t));
// Try and find the asset file.
for(int32_t i = 0; i < sizeof(scanLocations) / sizeof(char_t*); i++) {
sprintf(assetPath, "%s/%s", scanLocations[i], assetFilename);
FILE *file = fopen(assetPath, "rb");
if(file == NULL) continue;
// File found.
ASSET.file = file;
}
// Ensure we found it.
assertNotNull(ASSET.file, "Failed to find asset file!");
}
void assetOpen(const char_t *path) {
assertNotNull(path, "Path is not valid!");
assertStrLen(path, FILENAME_MAX, "Path is too long!");
assertStrLenMin(path, 1, "Path is empty!");
// Make sure things are clean
assertNull(ASSET.archive, "Archive is not NULL!");
assertNull(ASSET.entry, "Entry is not NULL!");
assertNotNull(ASSET.file, "File is NULL!");
// Store path
strcpy(ASSET.path, path);
// Prepare data
ASSET.archive = archive_read_new();
assertNotNull(ASSET.archive, "Failed to init archive reader");
// Set up the reader
// archive_read_support_filter_bzip2(ASSET_ARCHIVE);
archive_read_support_format_tar(ASSET.archive);
// Set the archive reader callbacks
archive_read_set_open_callback(ASSET.archive, &assetArchiveOpen);
archive_read_set_read_callback(ASSET.archive, &assetArchiveRead);
archive_read_set_seek_callback(ASSET.archive, &assetArchiveSeek);
archive_read_set_close_callback(ASSET.archive, &assetArchiveClose);
archive_read_set_callback_data(ASSET.archive, ASSET.buffer);// TODO: Not needed?
// Open the archive
int32_t ret = archive_read_open1(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to open archive!");
// Iterate over each file.
while(archive_read_next_header(ASSET.archive, &ASSET.entry) == ARCHIVE_OK) {
// What file is at this position?
const char_t *headerFile = (char_t*)archive_entry_pathname(ASSET.entry);
// Compare if this is the file we are looking for, if it is just exit the
// function
if(strcmp(headerFile, ASSET.path) == 0) return;
// It is not, skip it.
int32_t ret = archive_read_data_skip(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to skip data!");
}
// If we get here we did not find the find in the archive.
assertUnreachable("Failed to find file!");
}
size_t assetGetSize() {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
assertTrue(archive_entry_size_is_set(ASSET.entry), "Entry size is not set!");
return archive_entry_size(ASSET.entry);
}
size_t assetRead(
uint8_t *buffer,
const size_t bufferSize
) {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
assertNotNull(buffer, "Buffer is NULL!");
assertTrue(bufferSize > 0, "Buffer size must be greater than 0!");
ssize_t read = archive_read_data(ASSET.archive, buffer, bufferSize);
if(read == ARCHIVE_FATAL) {
assertUnreachable(archive_error_string(ASSET.archive));
return -1;
}
assertTrue(read != ARCHIVE_RETRY, "Failed to read data (RETRY)!");
assertTrue(read != ARCHIVE_WARN, "Failed to read data (WARN)!");
return read;
}
size_t assetReadUntil(
uint8_t *buffer,
const char_t c,
const size_t maxLength
) {
if(buffer == NULL) {
assertTrue(
maxLength == -1, "If no buffer is provided, maxLength must be -1."
);
uint8_t tBuffer[1];
size_t read = 0;
while(assetRead(tBuffer, 1) == 1 && (char_t)tBuffer[0] != c) read++;
return read;
}
size_t read = 0;
while(read < maxLength) {
// TODO: Read more than 1 char at a time.
read += assetRead(buffer + read, 1);
if((char_t)buffer[read-1] == c) return read - 1;
}
return -1;
}
void assetSkip(const size_t length) {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
// Asset archive does not support skipping, so we have to read and discard.
uint8_t buffer[ASSET_BUFFER_SIZE];
size_t remaining = length;
do {
size_t toRead = mathMin(remaining, ASSET_BUFFER_SIZE);
size_t read = assetRead(buffer, toRead);
assertTrue(read == toRead, "Failed to skip data! (overskip?)");
remaining -= read;
} while(remaining > 0);
}
void assetClose() {
if(ASSET.archive == NULL) return;
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
int32_t ret = archive_read_free(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to close archive!");
assertNull(ASSET.archive, "Archive is not NULL? Cleanup incorrect.");
}
void assetDispose() {
assertNull(ASSET.archive, "Asset disposing but asset is currently open?");
assertNull(ASSET.entry, "Asset disposing but entry is currently open?");
assertNotNull(ASSET.file, "Asset disposing but file is NULL?");
fclose(ASSET.file);
memset(&ASSET, 0, sizeof(asset_t));
}
// Libarchive callbacks
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(data, "Data is NULL!");
assertNotNull(buffer, "Buffer is NULL!");
*buffer = data;
size_t read = fread(data, 1, ASSET_BUFFER_SIZE, ASSET.file);
if(ferror(ASSET.file)) return ARCHIVE_FATAL;
return read;
}
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(data, "Data is NULL!");
assertTrue(offset > 0, "Offset must be greater than 0!");
assertNotNull(ASSET.file, "File is NULL!");
int32_t ret = fseek(ASSET.file, offset, whence);
assertTrue(ret == 0, "Failed to seek!");
return ftell(ASSET.file);
}
int32_t assetArchiveOpen(struct archive *a, void *data) {
int32_t ret = fseek(ASSET.file, 0, SEEK_SET);
assertTrue(ret == 0, "Failed to seek to start of file!");
return ARCHIVE_OK;
}
int32_t assetArchiveClose(struct archive *a, void *data) {
assertNotNull(ASSET.file, "File is NULL!");
ASSET.archive = NULL;
ASSET.entry = NULL;
return ARCHIVE_OK;
}

137
src/duskgl/asset.h Normal file
View File

@ -0,0 +1,137 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
#include <archive.h>
#include <archive_entry.h>
#define ASSET_BUFFER_SIZE 32768
typedef struct {
FILE *file;
struct archive *archive;
struct archive_entry *entry;
uint8_t buffer[ASSET_BUFFER_SIZE];
// ?
char_t path[FILENAME_MAX];
} asset_t;
extern asset_t ASSET;
/**
* Initializes the asset manager.
*/
void assetInit();
/**
* Opens an asset by its filename (within the asset archive). Asset paths should
* always use the unix forward slash '/' as a path separator.
*
* @param path The path to the asset within the archive.
*/
void assetOpen(const char *path);
/**
* Returns the size of the asset.
*
* @return The size of the asset.
*/
size_t assetGetSize();
/**
* Reads the asset into the buffer.
*
* @param buffer The buffer to read the asset into.
* @param bufferSize The size of the buffer.
* @return The amount of data read.
*/
size_t assetRead(uint8_t *buffer, const size_t bufferSize);
/**
* Reads ahead in the buffer until either the end of the buffer, or the
* specified character is found. Return value will be -1 if the character was
* not found.
*
* Buffer can be NULL if you just want to skip ahead.
*
* Returned value will be either the amount of data read into the buffer, which
* excludes the extra 1 character that was read from the asset. If the character
* was not found, -1 will be returned.
*
* @param buffer Buffer to read into.
* @param c Character to read until.
* @param maxLength Maximum length to read.
* @return -1 if the character was not found, otherwise the amount of data read.
*/
size_t assetReadUntil(uint8_t *buffer, const char c, const size_t maxLength);
/**
* Skips ahead in the buffer by the specified length.
*
* @param length The length to skip ahead by.
*/
void assetSkip(const size_t length);
/**
* Closes the asset.
*/
void assetClose();
/**
* Destroys and cleans up the asset manager.
*/
void assetDispose();
/**
* Internal read method provided to libarchive api.
*
* @param archive The archive to read from.
* @param data The data to read into.
* @param buffer The buffer to read from.
* @return The amount of data read.
*/
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
);
/**
* Internal seek method provided to libarchive api.
*
* @param archive The archive to seek in.
* @param data The data to seek in.
* @param offset Offset bytes to seek.
* @param whence Relative to whence to seek.
* @return The new position.
*/
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
);
/**
* Internal open method provided to libarchive api.
*
* @param archive The archive to open.
* @param data The data to open.
* @return The result of the open.
*/
int32_t assetArchiveOpen(struct archive *a, void *data);
/**
* Internal close method provided to libarchive api.
*
* @param archive The archive to close.
* @param data The data to close.
* @return The result of the close.
*/
int32_t assetArchiveClose(struct archive *a, void *data);

View File

@ -0,0 +1,12 @@
# 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
render.c
)
# Subdirs

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assertgl.h"
#include "render.h"
render_t RENDER;
void renderInit() {
memset(&RENDER, 0, sizeof(render_t));
}
void renderUpdate() {
}
void renderOverworld() {
}
void renderDispose() {
}

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
typedef struct {
int32_t nothing;
} render_t;
extern render_t RENDER;
/**
* Initializes the render system.
*/
void renderInit();
/**
* Updates the render system.
*/
void renderUpdate();
/**
* Renders the overworld scene.
*/
void renderOverworld();
/**
* Disposes of the render system.
*/
void renderDispose();

20
src/duskgl/duskgl.h Normal file
View File

@ -0,0 +1,20 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "duskglimpl.h"
#include <cglm/cglm.h>
#include <libgen.h>
#include <float.h>
typedef float float_t;
typedef double double_t;
extern char_t EXECUTABLE_PATH[];
extern char_t EXECUTABLE_DIRECTORY[];

View File

@ -0,0 +1,27 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
glfw
glad
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
input.c
)
# Subdirs
add_subdirectory(display)

View File

@ -0,0 +1,10 @@
# 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
window.c
)

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "window.h"
GLFWwindow* window;
int32_t WINDOW_WIDTH;
int32_t WINDOW_HEIGHT;
int32_t windowInit() {
// Initialize GLFW
if(!glfwInit()) {
const char* description;
int code = glfwGetError(&description);
if (description) {
printf("GLFW Error %d: %s\n", code, description);
} else {
printf("GLFW Error: Unknown error\n");
}
return -1;
}
// Create a windowed mode window and its OpenGL contex
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_FALSE);
window = glfwCreateWindow(
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT,
"Dusk",
NULL, NULL
);
if(!window) {
glfwTerminate();
return -2;
}
// Make the window's context current
glfwMakeContextCurrent(window);
// Get initial framebuffer size
glfwGetFramebufferSize(window, &WINDOW_WIDTH, &WINDOW_HEIGHT);
// Load OpenGL functions using glad or another loader here if necessary
if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) return -3;
return 0;
}
bool_t windowShouldClose() {
return glfwWindowShouldClose(window);
}
void windowUpdate() {
glfwSwapBuffers(window);
glfwPollEvents();
glfwGetFramebufferSize(window, &WINDOW_WIDTH, &WINDOW_HEIGHT);
}
void windowDispose() {
glfwDestroyWindow(window);
glfwTerminate();
}

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskglfw.h"
#define WINDOW_WIDTH_DEFAULT 800
#define WINDOW_HEIGHT_DEFAULT 600
extern GLFWwindow* window;
extern int32_t WINDOW_WIDTH;
extern int32_t WINDOW_HEIGHT;
/**
* Initializes the game render window.
*
* @return 0 on success, any other value on failure.
*/
int32_t windowInit();
/**
* Returns whether the window should close.
*
* @return true if the window should close, false otherwise.
*/
bool_t windowShouldClose();
/**
* Updates the window subsystem.
*/
void windowUpdate();
/**
* Disposes of the window.
*/
void windowDispose();

9
src/duskglfw/duskglfw.h Normal file
View File

@ -0,0 +1,9 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"

11
src/duskglfw/duskglimpl.h Normal file
View File

@ -0,0 +1,11 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>

52
src/duskglfw/input.c Normal file
View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "display/window.h"
typedef struct {
uint16_t glfw;
inputbind_t bind;
} glfwkeybindmap_t;
glfwkeybindmap_t GLFW_KEY_BIND_MAP[] = {
{GLFW_KEY_W, INPUT_UP},
{GLFW_KEY_UP, INPUT_UP},
{GLFW_KEY_S, INPUT_DOWN},
{GLFW_KEY_DOWN, INPUT_DOWN},
{GLFW_KEY_A, INPUT_LEFT},
{GLFW_KEY_LEFT, INPUT_LEFT},
{GLFW_KEY_D, INPUT_RIGHT},
{GLFW_KEY_RIGHT, INPUT_RIGHT},
{GLFW_KEY_ENTER, INPUT_ACCEPT},
{GLFW_KEY_E, INPUT_ACCEPT},
{GLFW_KEY_SPACE, INPUT_ACCEPT},
{GLFW_KEY_ESCAPE, INPUT_BACK},
{GLFW_KEY_BACKSPACE, INPUT_BACK},
{GLFW_KEY_Q, INPUT_BACK},
{0, 0}
};
inputvalue_t inputStateGet(const inputbind_t input) {
// Handle keybinds
glfwkeybindmap_t *map = GLFW_KEY_BIND_MAP;
do {
if(map->bind == input && glfwGetKey(window, map->glfw) == GLFW_PRESS) {
return 1;
}
map++;
} while(map->glfw != 0);
return 0;
}

72
src/duskglfw/main.c Normal file
View File

@ -0,0 +1,72 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "display/window.h"
#include "display/render.h"
#include "game.h"
#include "asset.h"
#define TIME_BETWEEN_UPDATES (1.0f / 60.0f)
float_t timeUntilNextUpdate;
char_t EXECUTABLE_PATH[FILENAME_MAX];
char_t EXECUTABLE_DIRECTORY[FILENAME_MAX];
int32_t main(int32_t argc, char_t **argv) {
// Get launch args
assertTrue(argc > 0, "argc is not valid!");
assertNotNull(argv, "argv is not valid!");
assertNotNull(argv[0], "argv[0] is not valid!");
assertStrLen(argv[0], FILENAME_MAX, "argv[0] is too long!");
assertStrLenMin(argv[0], 1, "argv[0] is empty!");
// Store the executable path and directory.
strcpy(EXECUTABLE_PATH, argv[0]);
strcpy(EXECUTABLE_DIRECTORY, dirname(EXECUTABLE_PATH));
// Init systems
int32_t ret;
if((ret = windowInit()) != 0) {
return -1;
}
assetInit();
renderInit();
gameInit();
// Prepare for time tracking
double_t time, newTime;
float_t fDelta;
int32_t updateResult;
timeUntilNextUpdate = 0.0f;
// Main loop
while(!windowShouldClose()) {
// Determine the delta.
newTime = glfwGetTime();
fDelta = (float_t)(newTime - time);
time = newTime;
timeUntilNextUpdate -= fDelta;
// Should game tick?
if(timeUntilNextUpdate <= 0) {
gameUpdate();
timeUntilNextUpdate = TIME_BETWEEN_UPDATES;
}
// Draw
renderUpdate();
windowUpdate();
}
renderDispose();
windowDispose();
assetDispose();
return 0;
}