Started asset refact

This commit is contained in:
2025-11-04 10:15:19 -06:00
parent 7d46b98310
commit 7c11a7e5bc
25 changed files with 362 additions and 208 deletions

View File

@@ -0,0 +1,14 @@
# 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
asset.c
assetmanager.c
)
# Subdirs
add_subdirectory(type)

139
archive/asset/asset.c Normal file
View File

@@ -0,0 +1,139 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset.h"
#include "assetmanager.h"
#include "util/memory.h"
#include "assert/assert.h"
assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT] = {
[ASSET_TYPE_PALETTE_IMAGE] = {
"DPI", assetPaletteImageLoad, assetPaletteImageDispose
},
[ASSET_TYPE_ALPHA_IMAGE] = {
"DAI", assetAlphaImageLoad, assetAlphaImageDispose
},
};
errorret_t assetInit(asset_t *asset, const char_t *filename) {
assertNotNull(asset, "Asset cannot be NULL.");
assertNotNull(filename, "Filename cannot be NULL.");
assertTrue(strlen(filename) < FILENAME_MAX, "Filename too long.");
assertTrue(strlen(filename) > 0, "Filename cannot be empty.");
memoryZero(asset, sizeof(asset_t));
memoryCopy(asset->filename, filename, strlen(filename) + 1);
// Initialze the reference list.
refListInit(
&asset->refList,
asset->refListArray,
ASSET_REFERENCE_COUNT_MAX
);
// Test the file can be opened. In future I may make the handle stay open for
// a while to see if it increases performance and stops disk thrashing.
asset->file = zip_fopen(ASSET_MANAGER.zip, filename, 0);
if(asset->file == NULL) errorThrow("Failed to open asset file: %s", filename);
zip_fclose(asset->file);
asset->file = NULL;
errorOk();
}
ref_t assetLock(asset_t *asset) {
assertNotNull(asset, "Asset cannot be NULL.");
return refListLock(&asset->refList);
}
void assetUnlock(asset_t *asset, const ref_t ref) {
assertNotNull(asset, "Asset cannot be NULL.");
// Just unlock the reference in the reference list.
refListUnlock(&asset->refList, ref);
}
errorret_t assetLoad(asset_t *asset) {
assertNotNull(asset, "Asset cannot be NULL.");
assertTrue(
asset->state == ASSET_STATE_NOT_LOADED,
"Asset is already loaded or loading."
);
// Mark as loading.
asset->state = ASSET_STATE_LOADING;
// Open the file.
if(asset->file == NULL) {
asset->file = zip_fopen(ASSET_MANAGER.zip, asset->filename, 0);
if(asset->file == NULL) {
asset->state = ASSET_STATE_ERROR;
errorThrow("Failed to open asset file: %s", asset->filename);
}
}
// Read header.
char_t header[ASSET_HEADER_SIZE + 1];
memoryZero(header, ASSET_HEADER_SIZE + 1);
zip_int64_t bytesRead = zip_fread(asset->file, header, ASSET_HEADER_SIZE);
if(bytesRead != ASSET_HEADER_SIZE) {
asset->state = ASSET_STATE_ERROR;
zip_fclose(asset->file);
asset->file = NULL;
errorThrow("Failed to read asset header for: %s", asset->filename);
}
// Check header.
if(strlen(header) != ASSET_HEADER_SIZE) {
asset->state = ASSET_STATE_ERROR;
zip_fclose(asset->file);
asset->file = NULL;
errorThrow("Invalid asset header for: %s", asset->filename);
}
// Get the asset definition based on the header.
assetdef_t *def;
for(uint_fast8_t i = 0; i < ASSET_TYPE_COUNT; i++) {
if(strcmp(header, ASSET_DEFINITIONS[i].header) == 0) {
def = &ASSET_DEFINITIONS[i];
asset->type = i;
break;
}
}
assertNotNull(def, "Failed to find asset definition for header.");
// Load the asset
errorret_t ret = def->load(asset);
if(ret.code != ERROR_OK) {
asset->state = ASSET_STATE_ERROR;
zip_fclose(asset->file);
asset->file = NULL;
errorChain(ret);
}
// Finished loading.
asset->state = ASSET_STATE_LOADED;
zip_fclose(asset->file);
asset->file = NULL;
errorOk();
}
errorret_t assetDispose(asset_t *asset) {
assertNotNull(asset, "Asset cannot be NULL.");
if(asset->state == ASSET_STATE_LOADED) {
// Dispose of the asset based on its type.
assetdef_t *def = &ASSET_DEFINITIONS[asset->type];
if(def->dispose) errorChain(def->dispose(asset));
asset->state = ASSET_STATE_NOT_LOADED;
}
if(asset->file) {
zip_fclose(asset->file);
asset->file = NULL;
}
}

104
archive/asset/asset.h Normal file
View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "util/reflist.h"
#include <zip.h>
#include "asset/type/assetpaletteimage.h"
#include "asset/type/assetalphaimage.h"
#define ASSET_HEADER_SIZE 3
#define ASSET_REFERENCE_COUNT_MAX 8
typedef struct {
void (*load)();
} assetcallback_t;
typedef enum {
ASSET_STATE_NOT_LOADED,
ASSET_STATE_LOADING,
ASSET_STATE_LOADED,
ASSET_STATE_ERROR,
} assetstate_t;
typedef enum {
ASSET_TYPE_UNKNOWN,
ASSET_TYPE_PALETTE_IMAGE,
ASSET_TYPE_ALPHA_IMAGE,
ASSET_TYPE_COUNT
} assettype_t;
typedef struct asset_s {
char_t filename[FILENAME_MAX];
ref_t refListArray[ASSET_REFERENCE_COUNT_MAX];
reflist_t refList;
assetstate_t state;
assettype_t type;
zip_file_t *file;
union {
assetpaletteimage_t paletteImage;
assetalphaimage_t alphaImage;
};
} asset_t;
typedef struct {
const char_t header[ASSET_HEADER_SIZE + 1];
errorret_t (*load)(asset_t *asset);
errorret_t (*dispose)(asset_t *asset);
} assetdef_t;
extern assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT];
/**
* Initializes an asset structure. This should be called by the asset manager
* only.
*
* @param asset The asset structure to initialize.
* @param filename The filename of the asset.
* @return An error code.
*/
errorret_t assetInit(asset_t *asset, const char_t *filename);
/**
* Requests a lock on the given asset. This will increase the reference count
* of the asset, and prevent it from being unloaded until all locks are
* released.
*
* @param asset The asset to lock.
* @return A unique reference ID for the lock.
*/
ref_t assetLock(asset_t *asset);
/**
* Releases a lock on the given asset. This will decrease the reference count
* of the asset, and allow it to be unloaded if there are no more locks.
*
* @param asset The asset to unlock.
* @param ref The reference ID of the lock to release.
*/
void assetUnlock(asset_t *asset, const ref_t ref);
/**
* Permission has been granted to load the asset data from disk. This should
* only be called by the asset manager.
*
* @param asset The asset to load.
* @return An error code.
*/
errorret_t assetLoad(asset_t *asset);
/**
* Disposes of the asset, freeing any allocated memory and closing any open
* file handles. This should only be called by the asset manager.
*
* @param asset The asset to dispose of.
*/
errorret_t assetDispose(asset_t *asset);

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetmanager.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#define ASSET_ASSET_FILE "dusk.dsk"
assetmanager_t ASSET_MANAGER;
errorret_t assetManagerInit(void) {
memoryZero(&ASSET_MANAGER, sizeof(assetmanager_t));
// Default system path, intended to be overridden by the paltform
stringCopy(ASSET_MANAGER.systemPath, ".", FILENAME_MAX);
// Open zip file
char_t searchPath[FILENAME_MAX];
for(int32_t i = 0; i < ASSET_MANAGER_SEARCH_PATHS_COUNT; i++) {
sprintf(
searchPath,
ASSET_MANAGER_SEARCH_PATHS[i],
ASSET_MANAGER.systemPath,
ASSET_ASSET_FILE
);
// Try open
ASSET_MANAGER.zip = zip_open(searchPath, ZIP_RDONLY, NULL);
if(ASSET_MANAGER.zip == NULL) continue;
break;
}
// Did we open the asset?
if(ASSET_MANAGER.zip == NULL) errorThrow("Failed to open asset file.");
errorOk();
}
void assetManagerUpdate(void) {
// Update all assets
asset_t *asset = ASSET_MANAGER.assets;
while(asset < &ASSET_MANAGER.assets[ASSET_MANAGER.assetCount]) {
if(asset->state == ASSET_STATE_LOADING) {
// Check if the asset is loaded
if(asset->file != NULL) {
asset->state = ASSET_STATE_LOADED;
}
}
++asset;
}
}
errorret_t assetManagerGetAsset(const char_t *filename, asset_t **outAsset) {
assertNotNull(outAsset, "Output asset pointer cannot be null.");
assertNotNull(filename, "Filename cannot be null.");
assertStrLenMin(filename, 1, "Filename cannot be empty.");
assertStrLenMax(filename, FILENAME_MAX - 1, "Filename is too long.");
// Is this asset already in memory?
asset_t *asset = ASSET_MANAGER.assets;
while(asset < &ASSET_MANAGER.assets[ASSET_MANAGER.assetCount]) {
if(stringCompare(asset->filename, filename) == 0) {
*outAsset = asset;
errorOk();
}
++asset;
}
// Asset not in memory, can we load it?
if(ASSET_MANAGER.assetCount >= ASSET_MANAGER_ASSET_COUNT_MAX) {
*outAsset = NULL;
errorThrow("Asset limit reached.");
}
// Pop an asset off the struct
asset = &ASSET_MANAGER.assets[ASSET_MANAGER.assetCount++];
*outAsset = asset;
errorChain(assetInit(asset, filename));
errorOk();
}
errorret_t assetManagerLoadAsset(
const char_t *filename,
asset_t **outAsset,
ref_t *outRef
) {
assertNotNull(outRef, "Output reference pointer cannot be null.");
errorChain(assetManagerGetAsset(filename, outAsset));
ref_t ref = assetLock(*outAsset);
errorChain(assetLoad(*outAsset));
*outRef = ref;
errorOk();
}
void assetManagerDispose(void) {
asset_t *asset = ASSET_MANAGER.assets;
while(asset < &ASSET_MANAGER.assets[ASSET_MANAGER.assetCount]) {
assetDispose(asset);
++asset;
}
if(ASSET_MANAGER.zip != NULL) {
zip_close(ASSET_MANAGER.zip);
ASSET_MANAGER.zip = NULL;
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset.h"
#define ASSET_MANAGER_ASSET_COUNT_MAX 32
#if ASSET_TYPE == wad
#else
#error "Unsupported ASSET_TYPE"
#endif
static const char_t ASSET_MANAGER_SEARCH_PATHS[][FILENAME_MAX] = {
"%s/%s",
"%s",
"../%s",
"../../%s",
"data/%s",
"../data/%s",
};
#define ASSET_MANAGER_SEARCH_PATHS_COUNT (\
sizeof(ASSET_MANAGER_SEARCH_PATHS) / FILENAME_MAX\
)
typedef struct {
zip_t *zip;
asset_t assets[ASSET_MANAGER_ASSET_COUNT_MAX];
char_t systemPath[FILENAME_MAX];
uint8_t assetCount;
} assetmanager_t;
extern assetmanager_t ASSET_MANAGER;
/**
* Initializes the asset system.
*/
errorret_t assetManagerInit(void);
/**
* Update the asset manager. This should be called once per frame.
*/
void assetManagerUpdate(void);
/**
* Get an asset by filename. This will return NULL if the asset does not exist.
* This will not lock the asset, you must do that separately. If you do not
* lock the asset there is no guarantee the asset pointer will remain valid.
*
* @param filename The filename of the asset to get.
* @param outAsset The output asset pointer.
* @return An error code.
*/
errorret_t assetManagerGetAsset(const char_t *filename, asset_t **outAsset);
/**
* Gets, locks and loads an asset. This is all blocking so only use if you
* really need to.
*
* @param filename The filename of the asset to get.
* @param outAsset The output asset pointer.
* @param outRef The output asset reference pointer.
* @return An error code if something goes wrong.
*/
errorret_t assetManagerLoadAsset(
const char_t *filename,
asset_t **outAsset,
ref_t *outRef
);
/**
* Disposes/cleans up the asset system.
*/
void assetManagerDispose(void);

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
assetalphaimage.c
assetpaletteimage.c
)

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
errorret_t assetAlphaImageLoad(asset_t *asset) {
// Read entire alpha image.
assetalphaimageraw_t raw;
zip_int64_t bytesRead = zip_fread(asset->file, &raw, sizeof(raw));
if(bytesRead < sizeof(raw.width) + sizeof(raw.height)) {
errorThrow("Failed to read alpha image dimensions.");
}
if(raw.width <= 0 || raw.width > ASSET_ALPHA_IMAGE_WIDTH_MAX) {
errorThrow("Invalid alpha image width.");
}
if(raw.height <= 0 || raw.height > ASSET_ALPHA_IMAGE_HEIGHT_MAX) {
errorThrow("Invalid alpha image height.");
}
zip_int64_t expecting = (
sizeof(raw.width) +
sizeof(raw.height) +
(raw.width * raw.height * sizeof(uint8_t))
);
if(bytesRead != expecting) {
errorThrow("Incorrect alpha filesize.");
}
textureInit(
&asset->alphaImage.texture,
(int32_t)raw.width,
(int32_t)raw.height,
TEXTURE_FORMAT_ALPHA,
(texturedata_t){
.alpha = {
.data = raw.pixels
}
}
);
errorOk();
}
errorret_t assetAlphaImageDispose(asset_t *asset) {
textureDispose(&asset->alphaImage.texture);
errorOk();
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/texture.h"
#define ASSET_ALPHA_IMAGE_WIDTH_MAX 256
#define ASSET_ALPHA_IMAGE_HEIGHT_MAX 256
#define ASSET_ALPHA_IMAGE_SIZE_MAX ( \
ASSET_ALPHA_IMAGE_WIDTH_MAX * ASSET_ALPHA_IMAGE_HEIGHT_MAX \
)
typedef struct asset_s asset_t;
#pragma pack(push, 1)
typedef struct {
uint32_t width;
uint32_t height;
uint8_t pixels[ASSET_ALPHA_IMAGE_SIZE_MAX];
} assetalphaimageraw_t;
#pragma pack(pop)
typedef struct {
texture_t texture;
} assetalphaimage_t;
/**
* Loads an alpha image asset from the given asset structure. The asset must
* be of type ASSET_TYPE_ALPHA_IMAGE and must be loaded.
*
* @param asset The asset to load the alpha image from.
* @return An error code.
*/
errorret_t assetAlphaImageLoad(asset_t *asset);
/**
* Disposes of an alpha image asset, freeing any allocated resources.
*
* @param asset The asset to dispose of.
* @return An error code.
*/
errorret_t assetAlphaImageDispose(asset_t *asset);

View File

@@ -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 "display/palette/palettelist.h"
errorret_t assetPaletteImageLoad(asset_t *asset) {
// Read entire palettized image.
assetpaletteimageraw_t raw;
zip_int64_t bytesRead = zip_fread(asset->file, &raw, sizeof(raw));
if(bytesRead < sizeof(raw.width) + sizeof(raw.height)) {
errorThrow("Failed to read palette image dimensions.");
}
if(raw.width <= 0 || raw.width > ASSET_PALETTE_IMAGE_WIDTH_MAX) {
errorThrow("Invalid palette image width.");
}
if(raw.height <= 0 || raw.height > ASSET_PALETTE_IMAGE_HEIGHT_MAX) {
errorThrow("Invalid palette image height.");
}
if(raw.paletteIndex >= PALETTE_LIST_COUNT) {
errorThrow("Invalid palette index.");
}
zip_int64_t expecting = (
sizeof(raw.width) +
sizeof(raw.height) +
sizeof(raw.paletteIndex) +
(raw.width * raw.height * sizeof(uint8_t))
);
if(bytesRead != expecting) {
errorThrow("Incorrect palette filesize.");
}
textureInit(
&asset->paletteImage.texture,
(int32_t)raw.width,
(int32_t)raw.height,
TEXTURE_FORMAT_PALETTE,
(texturedata_t){
.palette = {
.palette = raw.paletteIndex,
.data = raw.palette
}
}
);
errorOk();
}
errorret_t assetPaletteImageDispose(asset_t *asset) {
textureDispose(&asset->paletteImage.texture);
errorOk();
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/texture.h"
#define ASSET_PALETTE_IMAGE_WIDTH_MAX 128
#define ASSET_PALETTE_IMAGE_HEIGHT_MAX 128
#define ASSET_PALETTE_IMAGE_SIZE_MAX ( \
ASSET_PALETTE_IMAGE_WIDTH_MAX * ASSET_PALETTE_IMAGE_HEIGHT_MAX \
)
typedef struct asset_s asset_t;
#pragma pack(push, 1)
typedef struct {
uint32_t width;
uint32_t height;
uint8_t paletteIndex;
uint8_t palette[ASSET_PALETTE_IMAGE_SIZE_MAX];
} assetpaletteimageraw_t;
#pragma pack(pop)
typedef struct {
texture_t texture;
} assetpaletteimage_t;
/**
* Loads a palette image asset from the given asset structure. The asset must
* be of type ASSET_TYPE_PALETTE_IMAGE and must be loaded.
*
* @param asset The asset to load the palette image from.
* @return An error code.
*/
errorret_t assetPaletteImageLoad(asset_t *asset);
/**
* Disposes of a palette image asset, freeing any allocated resources.
*
* @param asset The asset to dispose of.
* @return An error code.
*/
errorret_t assetPaletteImageDispose(asset_t *asset);