diff --git a/src/asset/CMakeLists.txt b/src/asset/CMakeLists.txt index 364c0b2..4236828 100644 --- a/src/asset/CMakeLists.txt +++ b/src/asset/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE asset.c - assetsystem.c ) # Compile definitions diff --git a/src/asset/asset.c b/src/asset/asset.c index e016193..6ff2737 100644 --- a/src/asset/asset.c +++ b/src/asset/asset.c @@ -6,7 +6,127 @@ */ #include "asset.h" +#include "util/memory.h" +#include "console/console.h" +#include "util/string.h" +#include "assert/assert.h" -void assetInit() { +#define ASSET_ASSET_FILE "dusk.dsk" +asset_t ASSET; + +errorret_t assetInit(void) { + memoryZero(&ASSET, sizeof(asset_t)); + + // Open zip file + char_t searchPath[FILENAME_MAX]; + consolevar_t *var = consoleVarGet("sys_path"); + const char_t *sysPath = var ? var->value : "."; + for(int32_t i = 0; i < ASSET_SEARCH_PATHS_COUNT; i++) { + sprintf( + searchPath, + ASSET_SEARCH_PATHS[i], + sysPath, + ASSET_ASSET_FILE + ); + + // Try open + ASSET.zip = zip_open(searchPath, ZIP_RDONLY, NULL); + if(ASSET.zip == NULL) continue; + consolePrint("Opened asset file: %s", searchPath); + break; + } + + // Did we open the asset? + if(ASSET.zip == NULL) errorThrow("Failed to open asset file."); + + // Get "test.palette.dpf" file. + zip_file_t *file = zip_fopen(ASSET.zip, "test.palette.dpf", 0); + if(file == NULL) errorThrow("Failed to open test.palette.dpf in asset file."); + + // Read it + char_t buffer[256]; + zip_int64_t n = zip_fread(file, buffer, 256); + if(n < 0) { + zip_fclose(file); + errorThrow("Failed to read test.palette.dpf in asset file."); + } + + errorOk(); +} + +void assetLoad(const char_t *filename) { + assertNotNull(filename, "Filename cannot be NULL."); + assertTrue(strlen(filename) < ASSET_FILENAME_MAX, "Filename too long."); + assertTrue(strlen(filename) > 0, "Filename cannot be empty."); + + assertTrue( + ASSET.state != ASSET_STATE_LOADING, + "Asset system is already loading an asset." + ); + + // Pass off to the thread to begin loading. + ASSET.errorState = ERROR_STATE_INIT; + ASSET.state = ASSET_STATE_LOADING; + stringCopy(ASSET.filename, filename, ASSET_FILENAME_MAX); + memoryZero(&ASSET.data, sizeof(ASSET.data)); + + consolePrint("Loading asset: %s", filename); + + // For the sake of testing I'm going to do blocking load, fun stuff. + zip_file_t *file = zip_fopen(ASSET.zip, filename, 0); + if(file == NULL) { + ASSET.state = ASSET_STATE_ERROR; + ASSET.error = errorCreate( + &ASSET.errorState, + "Failed to open asset: %s", + filename + ); + return; + } + + consolePrint("Opened asset: %s", filename); + + // Determine length of the file (uncompressed) + struct zip_stat st; + if(zip_stat(ASSET.zip, filename, 0, &st) != 0) { + zip_fclose(file); + ASSET.state = ASSET_STATE_ERROR; + ASSET.error = errorCreate( + &ASSET.errorState, + "Failed to stat asset: %s", + filename + ); + return; + } + + printf("Asset size: %d\n", (int)st.size); + if(st.size > sizeof(ASSET.data.palette)) { + zip_fclose(file); + ASSET.error = errorCreate( + &ASSET.errorState, + "Asset size mismatch: %s (got %d, expected %d)", + filename, + (int)st.size, + (int)sizeof(ASSET.data.palette) + ); + return; + } + + // Read entire file into the asset data. + zip_fread(file, &ASSET.data, st.size); + zip_fclose(file); + ASSET.state = ASSET_STATE_LOADED; + consolePrint("First 3 bytes: %c%c%c\n", + ASSET.data.header[0], + ASSET.data.header[1], + ASSET.data.header[2] + ); +} + +void assetDispose(void) { + if(ASSET.zip != NULL) { + zip_close(ASSET.zip); + ASSET.zip = NULL; + } } \ No newline at end of file diff --git a/src/asset/asset.h b/src/asset/asset.h index 9e82d59..b6d654f 100644 --- a/src/asset/asset.h +++ b/src/asset/asset.h @@ -7,19 +7,75 @@ #pragma once #include "dusk.h" +#include "assetpalette.h" +#include "assettileset.h" +#include "error/error.h" #include -#define ASSET_FILENAME_MAX 256 +#define ASSET_COUNT_MAX 128 +#define ASSET_FILENAME_MAX 128 -typedef struct asset_s { +#if ASSET_TYPE == wad +#else + #error "Unsupported ASSET_TYPE" +#endif + +typedef struct { + char_t header[3]; + union { + assetpalette_t palette; + assettileset_t tileset; + }; +} assetdata_t; + +typedef enum { + ASSET_STATE_NONE, + ASSET_STATE_LOADING, + ASSET_STATE_LOADED, + ASSET_STATE_ERROR +} assetstate_t; + +typedef struct { int32_t nothing; + zip_t *zip; + // thread_t thread; - const char_t *filename; - zip_file_t *fileHandle; + errorstate_t errorState; + errorret_t error; + assetstate_t state; + char_t filename[ASSET_FILENAME_MAX]; + assetdata_t data; } asset_t; -void assetInit( - const char_t *filename, -); +static const char_t ASSET_SEARCH_PATHS[][FILENAME_MAX] = { + "%s/%s", + "./%s", + "../%s", + "../../%s", + "data/%s", + "../data/%s", +}; +#define ASSET_SEARCH_PATHS_COUNT (\ + sizeof(ASSET_SEARCH_PATHS) / FILENAME_MAX\ +) + +extern asset_t ASSET; + +/** + * Initializes the asset system. + */ +errorret_t assetInit(void); + +/** + * Gets an asset by filename, does not load it. + * + * @param filename The filename of the asset to get. + * @return The asset. + */ +void assetLoad(const char_t *filename); + +/** + * Disposes/cleans up the asset system. + */ void assetDispose(void); \ No newline at end of file diff --git a/src/asset/assetpalette.h b/src/asset/assetpalette.h index 88f0ddc..2fe8e5c 100644 --- a/src/asset/assetpalette.h +++ b/src/asset/assetpalette.h @@ -12,7 +12,6 @@ #define ASSET_PALETTE_COLOR_COUNT_MAX 256 typedef struct { - char_t header[3]; int32_t colorCount; color4b_t colors[ASSET_PALETTE_COLOR_COUNT_MAX]; } assetpalette_t; \ No newline at end of file diff --git a/src/asset/assetsystem.c b/src/asset/assetsystem.c deleted file mode 100644 index 32900ae..0000000 --- a/src/asset/assetsystem.c +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "assetsystem.h" -#include "util/memory.h" -#include "console/console.h" - -#define ASSET_SYSTEM_ASSET_FILE "dusk.dsk" - -const char_t ASSET_SYSTEM_SEARCH_PATHS[][FILENAME_MAX] = { - "%s/%s", - "./%s", - "../%s", - "../../%s", - "data/%s", - "../data/%s", -}; -#define ASSET_SYSTEM_SEARCH_PATHS_COUNT (\ - sizeof(ASSET_SYSTEM_SEARCH_PATHS) / FILENAME_MAX\ -) - -assetsystem_t ASSET_SYSTEM; - -errorret_t assetSystemInit(void) { - memoryZero(&ASSET_SYSTEM, sizeof(assetsystem_t)); - - // Open zip file - char_t searchPath[FILENAME_MAX]; - consolevar_t *var = consoleVarGet("sys_path"); - const char_t *sysPath = var ? var->value : "."; - for(int32_t i = 0; i < ASSET_SYSTEM_SEARCH_PATHS_COUNT; i++) { - sprintf( - searchPath, - ASSET_SYSTEM_SEARCH_PATHS[i], - sysPath, - ASSET_SYSTEM_ASSET_FILE - ); - - // Try open - ASSET_SYSTEM.zip = zip_open(searchPath, ZIP_RDONLY, NULL); - if(ASSET_SYSTEM.zip == NULL) continue; - consolePrint("Opened asset file: %s", searchPath); - break; - } - - // Did we open the asset? - if(ASSET_SYSTEM.zip == NULL) errorThrow("Failed to open asset file."); - - // Get "test.palette.dpf" file. - zip_file_t *file = zip_fopen(ASSET_SYSTEM.zip, "test.palette.dpf", 0); - if(file == NULL) errorThrow("Failed to open test.palette.dpf in asset file."); - - // Read it - char_t buffer[256]; - zip_int64_t n = zip_fread(file, buffer, 256); - if(n < 0) { - zip_fclose(file); - errorThrow("Failed to read test.palette.dpf in asset file."); - } - - - errorOk(); -} - -void assetSystemDispose(void) { - if(ASSET_SYSTEM.zip != NULL) { - zip_close(ASSET_SYSTEM.zip); - ASSET_SYSTEM.zip = NULL; - } -} \ No newline at end of file diff --git a/src/asset/assetsystem.h b/src/asset/assetsystem.h deleted file mode 100644 index aee3566..0000000 --- a/src/asset/assetsystem.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2025 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "asset.h" -#include "error/error.h" - -#if ASSET_TYPE == wad -#else - #error "Unsupported ASSET_TYPE" -#endif - -typedef struct { - int32_t nothing; - zip_t *zip; -} assetsystem_t; - -extern assetsystem_t ASSET_SYSTEM; - -/** - * Initializes the asset system. - */ -errorret_t assetSystemInit(void); - -/** - * Disposes/cleans up the asset system. - */ -void assetSystemDispose(void); \ No newline at end of file diff --git a/src/asset/assettileset.h b/src/asset/assettileset.h index 8d51439..848d38a 100644 --- a/src/asset/assettileset.h +++ b/src/asset/assettileset.h @@ -11,7 +11,6 @@ typedef struct assetimage_s assetimage_t; typedef struct { - char_t header[3]; int32_t tileWidth; int32_t tileHeight; int32_t tileCount; diff --git a/src/engine/engine.c b/src/engine/engine.c index 887857c..c36f6c1 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -12,7 +12,7 @@ #include "display/display.h" #include "ecs/ecssystem.h" #include "scene/node.h" -#include "asset/assetsystem.h" +#include "asset/asset.h" #include "scene/test/scenetest.h" @@ -26,8 +26,12 @@ errorret_t engineInit(void) { timeInit(); consoleInit(); ecsSystemInit(); - errorChain(assetSystemInit()); + errorChain(assetInit()); errorChain(displayInit()); + + assetLoad("test.palette.dp123123"); + // assetLoad("test.palette.dpf"); + if(ASSET.state == ASSET_STATE_ERROR) errorChain(ASSET.error); sceneTestAdd(); @@ -45,7 +49,7 @@ errorret_t engineUpdate(void) { errorret_t engineDispose(void) { ecsSystemDispose(); errorChain(displayDispose()); - assetSystemDispose(); + assetDispose(); consoleDispose(); errorOk(); diff --git a/src/error/error.h b/src/error/error.h index 65c202d..0fd3f7f 100644 --- a/src/error/error.h +++ b/src/error/error.h @@ -21,15 +21,15 @@ typedef struct { errorstate_t *state; } errorret_t; + static const errorcode_t ERROR_OK = 0; static const errorcode_t ERROR_NOT_OK = 1; + +#define ERROR_STATE_INIT ((errorstate_t){ ERROR_OK, NULL, NULL }) + static const char_t *ERROR_PRINT_FORMAT = "Error (%d): %s\n%s"; static const char_t *ERROR_LINE_FORMAT = " at %s:%d in function %s\n"; -static errorstate_t ERROR_STATE = { - .code = ERROR_OK, - .message = NULL, - .lines = NULL -}; +static errorstate_t ERROR_STATE = ERROR_STATE_INIT; /** * Sets the error state with the provided code and message. @@ -91,6 +91,34 @@ void errorCatch(const errorret_t retval); */ errorret_t errorPrint(const errorret_t retval); +/** + * Creates an error with a specific error state. + * + * @param state The error state to set. + * @param message The format string for the error message. + * @param ... Additional arguments for the format string. + * @return The error code. + */ +#define errorCreate(state, message, ... ) \ + errorThrowImpl(\ + (state), ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \ + ##__VA_ARGS__ \ + ) + +/** + * Throws an error with a specific error state. + * + * @param state The error state to set. + * @param message The format string for the error message. + * @param ... Additional arguments for the format string. + * @return The error code. + */ +#define errorThrowState(state, message, ... ) \ + return errorThrowImpl(\ + (state), ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \ + ##__VA_ARGS__ \ + ) + /** * Throws an error with a formatted message. * diff --git a/src/util/string.c b/src/util/string.c index 264a158..ccc5ecc 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -125,4 +125,14 @@ bool_t stringToU16(const char_t *str, uint16_t *out) { } *out = (uint16_t)result; return true; +} + +bool_t stringEndsWith(const char_t *str, const char_t *suffix) { + assertNotNull(str, "str must not be NULL"); + assertNotNull(suffix, "suffix must not be NULL"); + + size_t strLen = strlen(str); + size_t suffixLen = strlen(suffix); + if(suffixLen > strLen) return false; + return strcmp(str + strLen - suffixLen, suffix) == 0; } \ No newline at end of file diff --git a/src/util/string.h b/src/util/string.h index 77c5fb0..94757cd 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -115,4 +115,13 @@ bool_t stringToI16(const char_t *str, int16_t *out); * @param out The output unsigned integer. * @return TRUE if the conversion was successful, FALSE otherwise. */ -bool_t stringToU16(const char_t *str, uint16_t *out); \ No newline at end of file +bool_t stringToU16(const char_t *str, uint16_t *out); + +/** + * Determines if a string ends with a specified suffix. + * + * @param str The string to check. + * @param suffix The suffix to check for. + * @return TRUE if the string ends with the suffix, FALSE otherwise. + */ +bool_t stringEndsWith(const char_t *str, const char_t *suffix); \ No newline at end of file