Refactor asset loading
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,79 +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"
|
|
||||||
|
|
||||||
#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);
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "language.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
language_t LANGUAGE;
|
|
||||||
|
|
||||||
void languageInit(void) {
|
|
||||||
LANGUAGE.current = LANGUAGE_EN;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char_t * languageGet(const char_t *key) {
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
|
||||||
keyIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(keyIndex != -1, "Key not found in language");
|
|
||||||
return LANGUAGE_VALUES[LANGUAGE.current][keyIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t langaugeGetLength(const char_t *key) {
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
|
||||||
keyIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(keyIndex != -1, "Key not found in language");
|
|
||||||
return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t languageFormat(
|
|
||||||
const char_t *key,
|
|
||||||
char_t *buffer,
|
|
||||||
uint16_t buffSize,
|
|
||||||
const char_t **keys,
|
|
||||||
const char_t **values,
|
|
||||||
const uint16_t valueCount
|
|
||||||
) {
|
|
||||||
if(buffer != NULL) {
|
|
||||||
assertTrue(buffSize > 0, "Buffer size must be greater than 0");
|
|
||||||
} else {
|
|
||||||
assertTrue(buffSize == 0, "Buffer size must be 0 if buffer is NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNotNull(key, "Key cannot be NULL");
|
|
||||||
assertNotNull(keys, "Keys cannot be NULL");
|
|
||||||
assertNotNull(values, "Values cannot be NULL");
|
|
||||||
|
|
||||||
const char_t *val = languageGet(key);
|
|
||||||
assertNotNull(val, "Value for key cannot be NULL");
|
|
||||||
|
|
||||||
char_t c;
|
|
||||||
uint16_t i = 0;
|
|
||||||
uint16_t j = 0;
|
|
||||||
uint8_t k = 0;
|
|
||||||
bool_t inBraces = false;
|
|
||||||
char_t braceBuffer[64] = {0};
|
|
||||||
|
|
||||||
#define bufferChar(c) ( \
|
|
||||||
(buffer ? (buffer[j++] = c) : (j++)), \
|
|
||||||
assertTrue(buffer ? j < buffSize : true, "Buffer overflow") \
|
|
||||||
)
|
|
||||||
|
|
||||||
while((c = val[i++]) != '\0') {
|
|
||||||
if(c == '{' && val[i] == '{') {
|
|
||||||
goto startBraces;
|
|
||||||
} else if(c == '}' && val[i] == '}') {
|
|
||||||
goto endBraces;
|
|
||||||
} else if(inBraces) {
|
|
||||||
goto braceBuffering;
|
|
||||||
} else {
|
|
||||||
goto character;
|
|
||||||
}
|
|
||||||
|
|
||||||
character: {
|
|
||||||
bufferChar(c);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
braceBuffering: {
|
|
||||||
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
|
||||||
braceBuffer[k++] = c;
|
|
||||||
assertTrue(k < sizeof(braceBuffer), "Brace buffer overflow");
|
|
||||||
if(val[i] == ' ') i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
startBraces: {
|
|
||||||
assertFalse(inBraces, "Nested braces are not allowed");
|
|
||||||
|
|
||||||
inBraces = true;
|
|
||||||
i++;
|
|
||||||
k = 0;
|
|
||||||
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
|
||||||
if(val[i] == ' ') i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
endBraces: {
|
|
||||||
assertTrue(inBraces, "Unmatched closing brace found");
|
|
||||||
|
|
||||||
inBraces = false;
|
|
||||||
i++;
|
|
||||||
braceBuffer[k] = '\0';
|
|
||||||
|
|
||||||
uint16_t l;
|
|
||||||
for(l = 0; l < valueCount; l++) {
|
|
||||||
if(strcmp(braceBuffer, keys[l]) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const char_t *replacement = values[l];
|
|
||||||
|
|
||||||
uint16_t r = 0;
|
|
||||||
while((c = replacement[r++]) != '\0') {
|
|
||||||
bufferChar(c);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assertTrue(l < valueCount, "No string replacement found!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(buffer){
|
|
||||||
assertTrue(j < buffSize, "Buffer overflow");
|
|
||||||
buffer[j] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
assertFalse(inBraces, "Unmatched opening brace found");
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "locale/language/languages.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t current;
|
|
||||||
} language_t;
|
|
||||||
|
|
||||||
extern language_t LANGUAGE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the language system.
|
|
||||||
*
|
|
||||||
* This function sets up the language system, loading the default language
|
|
||||||
* and preparing any necessary resources for language handling.
|
|
||||||
*/
|
|
||||||
void languageInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a language string by its key.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string.
|
|
||||||
* @return The language string associated with the key.
|
|
||||||
*/
|
|
||||||
const char_t * languageGet(const char_t *key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the length of a language string by its key.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string.
|
|
||||||
* @return The length of the language string associated with the key.
|
|
||||||
*/
|
|
||||||
uint16_t langaugeGetLength(const char_t *key);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a language string with given keys and values.
|
|
||||||
*
|
|
||||||
* This function replaces placeholders in the language string with the provided
|
|
||||||
* values based on the keys.
|
|
||||||
*
|
|
||||||
* If buffer is NULL, the function will instead calculate the length of the
|
|
||||||
* formatted string.
|
|
||||||
*
|
|
||||||
* @param key The key for the language string to format.
|
|
||||||
* @param buffer The buffer to store the formatted string.
|
|
||||||
* @param buffSize The size of the buffer.
|
|
||||||
* @param keys An array of keys to replace in the language string.
|
|
||||||
* @param values An array of values corresponding to the keys.
|
|
||||||
* @param valueCount The number of key-value pairs.
|
|
||||||
* @return The number of characters written to the buffer.
|
|
||||||
*/
|
|
||||||
uint16_t languageFormat(
|
|
||||||
const char_t *key,
|
|
||||||
char_t *buffer,
|
|
||||||
uint16_t buffSize,
|
|
||||||
const char_t **keys,
|
|
||||||
const char_t **values,
|
|
||||||
const uint16_t valueCount
|
|
||||||
);
|
|
||||||
@@ -6,9 +6,8 @@
|
|||||||
# Sources
|
# Sources
|
||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# asset.c
|
asset.c
|
||||||
# assetmanager.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirs
|
# Subdirs
|
||||||
# add_subdirectory(type)
|
add_subdirectory(type)
|
||||||
@@ -9,22 +9,23 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include "asset/assettype.h"
|
||||||
|
|
||||||
errorret_t assetInit(void) {
|
errorret_t assetInit(void) {
|
||||||
memoryZero(&ASSET_MANAGER, sizeof(assetmanager_t));
|
memoryZero(&ASSET, sizeof(asset_t));
|
||||||
|
|
||||||
// Default system path, intended to be overridden by the paltform
|
// Default system path, intended to be overridden by the platform
|
||||||
stringCopy(ASSET_MANAGER.systemPath, ".", FILENAME_MAX);
|
stringCopy(ASSET.systemPath, ".", FILENAME_MAX);
|
||||||
|
|
||||||
// Open zip file
|
// Open zip file
|
||||||
char_t searchPath[FILENAME_MAX];
|
char_t searchPath[FILENAME_MAX];
|
||||||
char_t **path = ASSET_SEARCH_PATHS;
|
const char_t **path = ASSET_SEARCH_PATHS;
|
||||||
do {
|
do {
|
||||||
sprintf(
|
sprintf(
|
||||||
searchPath,
|
searchPath,
|
||||||
*path,
|
*path,
|
||||||
ASSET_MANAGER.systemPath,
|
ASSET.systemPath,
|
||||||
ASSET_ASSET_FILE
|
ASSET_FILE
|
||||||
);
|
);
|
||||||
|
|
||||||
// Try open
|
// Try open
|
||||||
@@ -39,13 +40,61 @@ errorret_t assetInit(void) {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
erroret_t assetLoad(const char_t *filename, void *output) {
|
errorret_t assetLoad(const char_t *filename, void *output) {
|
||||||
|
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
||||||
|
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||||
|
|
||||||
|
// Try to open the file
|
||||||
|
zip_file_t *file = zip_fopen(ASSET.zip, filename, 0);
|
||||||
|
if(file == NULL) {
|
||||||
|
errorThrow("Failed to open asset file: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the header.
|
||||||
|
assetheader_t header;
|
||||||
|
memoryZero(&header, sizeof(assetheader_t));
|
||||||
|
zip_int64_t bytesRead = zip_fread(file, &header, sizeof(assetheader_t));
|
||||||
|
if(bytesRead != sizeof(assetheader_t)) {
|
||||||
|
zip_fclose(file);
|
||||||
|
errorThrow("Failed to read asset header for: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the asset type based on the header
|
||||||
|
const assettypedef_t *def = NULL;
|
||||||
|
for(uint_fast8_t i = 0; i < ASSET_TYPE_COUNT; i++) {
|
||||||
|
if(ASSET_TYPE_DEFINITIONS[i].header == NULL) continue;
|
||||||
|
if(strcmp(header.header, ASSET_TYPE_DEFINITIONS[i].header) != 0) continue;
|
||||||
|
def = &ASSET_TYPE_DEFINITIONS[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(def == NULL) {
|
||||||
|
zip_fclose(file);
|
||||||
|
errorThrow("Unknown asset type for file: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found the asset type, now load the asset data
|
||||||
|
assertNotNull(def->load, "Asset load function cannot be NULL.");
|
||||||
|
void *data = memoryAllocate(def->dataSize);
|
||||||
|
bytesRead = zip_fread(file, data, def->dataSize);
|
||||||
|
if(bytesRead == 0 || bytesRead > def->dataSize) {
|
||||||
|
memoryFree(data);
|
||||||
|
zip_fclose(file);
|
||||||
|
errorThrow("Failed to read asset data for file: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file now we have the data
|
||||||
|
zip_fclose(file);
|
||||||
|
|
||||||
|
// Pass to the asset type loader
|
||||||
|
errorret_t ret = def->load(data, output);
|
||||||
|
memoryFree(data);
|
||||||
|
errorChain(ret);
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void assetDispose(void) {
|
void assetDispose(void) {
|
||||||
if(ASSET_MANAGER.zip != NULL) {
|
if(ASSET.zip != NULL) {
|
||||||
zip_close(ASSET_MANAGER.zip);
|
zip_close(ASSET.zip);
|
||||||
ASSET_MANAGER.zip = NULL;
|
ASSET.zip = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,12 @@ static const char_t *ASSET_SEARCH_PATHS[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
char_t header[3];
|
||||||
|
} assetheader_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
zip_t *zip;
|
zip_t *zip;
|
||||||
char_t systemPath[FILENAME_MAX];
|
char_t systemPath[FILENAME_MAX];
|
||||||
|
|||||||
41
src/asset/assettype.h
Normal file
41
src/asset/assettype.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "type/assetpaletteimage.h"
|
||||||
|
#include "type/assetalphaimage.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASSET_TYPE_NULL,
|
||||||
|
ASSET_TYPE_PALETTE_IMAGE,
|
||||||
|
ASSET_TYPE_ALPHA_IMAGE,
|
||||||
|
ASSET_TYPE_COUNT,
|
||||||
|
} assettype_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char_t *header;
|
||||||
|
const size_t dataSize;
|
||||||
|
errorret_t (*load)(void *data, void *output);
|
||||||
|
} assettypedef_t;
|
||||||
|
|
||||||
|
static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
|
||||||
|
[ASSET_TYPE_NULL] = {
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
[ASSET_TYPE_PALETTE_IMAGE] = {
|
||||||
|
.header = "DPI",
|
||||||
|
.dataSize = sizeof(assetpaletteimage_t),
|
||||||
|
.load = assetPaletteImageLoad
|
||||||
|
},
|
||||||
|
|
||||||
|
[ASSET_TYPE_ALPHA_IMAGE] = {
|
||||||
|
.header = "DAI",
|
||||||
|
.dataSize = sizeof(assetalphaimage_t),
|
||||||
|
.load = assetAlphaImageLoad
|
||||||
|
},
|
||||||
|
};
|
||||||
28
src/asset/type/assetalphaimage.c
Normal file
28
src/asset/type/assetalphaimage.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "assetalphaimage.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "display/texture.h"
|
||||||
|
|
||||||
|
errorret_t assetAlphaImageLoad(void *data, void *output) {
|
||||||
|
assertNotNull(data, "Data pointer cannot be NULL.");
|
||||||
|
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||||
|
|
||||||
|
assetalphaimage_t *dataPtr = (assetalphaimage_t *)data;
|
||||||
|
texture_t *outputPtr = (texture_t *)output;
|
||||||
|
|
||||||
|
textureInit(
|
||||||
|
outputPtr,
|
||||||
|
dataPtr->width,
|
||||||
|
dataPtr->height,
|
||||||
|
TEXTURE_FORMAT_ALPHA,
|
||||||
|
(texturedata_t){ .alpha = { .data = dataPtr->pixels } }
|
||||||
|
);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "display/texture.h"
|
|
||||||
|
|
||||||
#define ASSET_ALPHA_IMAGE_WIDTH_MAX 256
|
#define ASSET_ALPHA_IMAGE_WIDTH_MAX 256
|
||||||
#define ASSET_ALPHA_IMAGE_HEIGHT_MAX 256
|
#define ASSET_ALPHA_IMAGE_HEIGHT_MAX 256
|
||||||
@@ -15,19 +14,13 @@
|
|||||||
ASSET_ALPHA_IMAGE_WIDTH_MAX * ASSET_ALPHA_IMAGE_HEIGHT_MAX \
|
ASSET_ALPHA_IMAGE_WIDTH_MAX * ASSET_ALPHA_IMAGE_HEIGHT_MAX \
|
||||||
)
|
)
|
||||||
|
|
||||||
typedef struct asset_s asset_t;
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint8_t pixels[ASSET_ALPHA_IMAGE_SIZE_MAX];
|
uint8_t pixels[ASSET_ALPHA_IMAGE_SIZE_MAX];
|
||||||
} assetalphaimageraw_t;
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
texture_t texture;
|
|
||||||
} assetalphaimage_t;
|
} assetalphaimage_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an alpha image asset from the given asset structure. The asset must
|
* Loads an alpha image asset from the given asset structure. The asset must
|
||||||
@@ -36,12 +29,4 @@ typedef struct {
|
|||||||
* @param asset The asset to load the alpha image from.
|
* @param asset The asset to load the alpha image from.
|
||||||
* @return An error code.
|
* @return An error code.
|
||||||
*/
|
*/
|
||||||
errorret_t assetAlphaImageLoad(asset_t *asset);
|
errorret_t assetAlphaImageLoad(void *data, void *output);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
33
src/asset/type/assetpaletteimage.c
Normal file
33
src/asset/type/assetpaletteimage.c
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "assetpaletteimage.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "display/texture.h"
|
||||||
|
|
||||||
|
errorret_t assetPaletteImageLoad(void *data, void *output) {
|
||||||
|
assertNotNull(data, "Data pointer cannot be NULL.");
|
||||||
|
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||||
|
|
||||||
|
assetpaletteimage_t *assetData = (assetpaletteimage_t *)data;
|
||||||
|
texture_t *texture = (texture_t *)output;
|
||||||
|
|
||||||
|
textureInit(
|
||||||
|
texture,
|
||||||
|
assetData->width,
|
||||||
|
assetData->height,
|
||||||
|
TEXTURE_FORMAT_PALETTE,
|
||||||
|
(texturedata_t){
|
||||||
|
.palette = {
|
||||||
|
.palette = assetData->paletteIndex,
|
||||||
|
.data = assetData->palette
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "display/texture.h"
|
|
||||||
|
|
||||||
#define ASSET_PALETTE_IMAGE_WIDTH_MAX 128
|
#define ASSET_PALETTE_IMAGE_WIDTH_MAX 128
|
||||||
#define ASSET_PALETTE_IMAGE_HEIGHT_MAX 128
|
#define ASSET_PALETTE_IMAGE_HEIGHT_MAX 128
|
||||||
@@ -15,34 +14,21 @@
|
|||||||
ASSET_PALETTE_IMAGE_WIDTH_MAX * ASSET_PALETTE_IMAGE_HEIGHT_MAX \
|
ASSET_PALETTE_IMAGE_WIDTH_MAX * ASSET_PALETTE_IMAGE_HEIGHT_MAX \
|
||||||
)
|
)
|
||||||
|
|
||||||
typedef struct asset_s asset_t;
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
uint8_t paletteIndex;
|
uint8_t paletteIndex;
|
||||||
uint8_t palette[ASSET_PALETTE_IMAGE_SIZE_MAX];
|
uint8_t palette[ASSET_PALETTE_IMAGE_SIZE_MAX];
|
||||||
} assetpaletteimageraw_t;
|
} assetpaletteimage_t;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
texture_t texture;
|
|
||||||
} assetpaletteimage_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a palette image asset from the given asset structure. The asset must
|
* Loads a palette image asset from the given data pointer into the output
|
||||||
* be of type ASSET_TYPE_PALETTE_IMAGE and must be loaded.
|
* texture.
|
||||||
*
|
*
|
||||||
* @param asset The asset to load the palette image from.
|
* @param data Pointer to the raw assetpaletteimage_t data.
|
||||||
|
* @param output Pointer to the texture_t to load the image into.
|
||||||
* @return An error code.
|
* @return An error code.
|
||||||
*/
|
*/
|
||||||
errorret_t assetPaletteImageLoad(asset_t *asset);
|
errorret_t assetPaletteImageLoad(void *data, void *output);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
@@ -12,15 +12,12 @@
|
|||||||
#include "locale/localemanager.h"
|
#include "locale/localemanager.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "scene/scenemanager.h"
|
#include "scene/scenemanager.h"
|
||||||
#include "asset/assetmanager.h"
|
#include "asset/asset.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "rpg/rpg.h"
|
#include "rpg/rpg.h"
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
|
|
||||||
asset_t *outAsset;
|
|
||||||
ref_t outRef;
|
|
||||||
|
|
||||||
errorret_t engineInit(void) {
|
errorret_t engineInit(void) {
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
ENGINE.running = true;
|
ENGINE.running = true;
|
||||||
@@ -29,7 +26,7 @@ errorret_t engineInit(void) {
|
|||||||
timeInit();
|
timeInit();
|
||||||
inputInit();
|
inputInit();
|
||||||
localeManagerInit();
|
localeManagerInit();
|
||||||
errorChain(assetManagerInit());
|
errorChain(assetInit());
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
errorChain(uiInit());
|
errorChain(uiInit());
|
||||||
errorChain(rpgInit());
|
errorChain(rpgInit());
|
||||||
@@ -41,7 +38,6 @@ errorret_t engineInit(void) {
|
|||||||
errorret_t engineUpdate(void) {
|
errorret_t engineUpdate(void) {
|
||||||
timeUpdate();
|
timeUpdate();
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
assetManagerUpdate();
|
|
||||||
|
|
||||||
rpgUpdate();
|
rpgUpdate();
|
||||||
uiUpdate();
|
uiUpdate();
|
||||||
@@ -62,6 +58,6 @@ errorret_t engineDispose(void) {
|
|||||||
rpgDispose();
|
rpgDispose();
|
||||||
uiDispose();
|
uiDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
assetManagerDispose();
|
assetDispose();
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
22
src/ui/ui.c
22
src/ui/ui.c
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
#include "ui/uifps.h"
|
#include "ui/uifps.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "display/tileset/tileset_minogram.h"
|
#include "display/tileset/tileset_minogram.h"
|
||||||
@@ -36,26 +37,29 @@ void uiRender(void) {
|
|||||||
cameraPushMatrix(&UI.camera);
|
cameraPushMatrix(&UI.camera);
|
||||||
|
|
||||||
// Render UI elements here
|
// Render UI elements here
|
||||||
if(UI.font) {
|
if(UI.fontTexture.width > 0) {
|
||||||
uiFPSRender(UI.fontTileset, &UI.font->alphaImage.texture);
|
uiFPSRender(UI.fontTileset, &UI.fontTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
cameraPopMatrix();
|
cameraPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiSetFont(const tileset_t *fontTileset) {
|
errorret_t uiSetFont(const tileset_t *fontTileset) {
|
||||||
if(UI.font) {
|
if(UI.fontTexture.width > 0) {
|
||||||
assetUnlock(UI.font, UI.fontRef);
|
textureDispose(&UI.fontTexture);
|
||||||
UI.font = NULL;
|
UI.fontTexture.width = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertNotNull(fontTileset, "Font tileset cannot be NULL.");
|
||||||
|
|
||||||
UI.fontTileset = fontTileset;
|
UI.fontTileset = fontTileset;
|
||||||
assetManagerLoadAsset(fontTileset->image, &UI.font, &UI.fontRef);
|
errorChain(assetLoad(UI.fontTileset->image, &UI.fontTexture));
|
||||||
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDispose(void) {
|
void uiDispose(void) {
|
||||||
if(UI.font) {
|
if(UI.fontTexture.width > 0) {
|
||||||
assetUnlock(UI.font, UI.fontRef);
|
textureDispose(&UI.fontTexture);
|
||||||
UI.font = NULL;
|
UI.fontTexture.width = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,16 +6,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
|
#include "display/texture.h"
|
||||||
#include "display/tileset/tileset.h"
|
#include "display/tileset/tileset.h"
|
||||||
#include "display/camera.h"
|
#include "display/camera.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
camera_t camera;
|
camera_t camera;
|
||||||
|
texture_t fontTexture;
|
||||||
ref_t fontRef;
|
|
||||||
asset_t *font;
|
|
||||||
const tileset_t *fontTileset;
|
const tileset_t *fontTileset;
|
||||||
} ui_t;
|
} ui_t;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
// */
|
// */
|
||||||
|
|
||||||
#include "uitext.h"
|
#include "uitext.h"
|
||||||
#include "asset/assetmanager.h"
|
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "display/spritebatch.h"
|
#include "display/spritebatch.h"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
|
#include "display/texture.h"
|
||||||
#include "display/tileset/tileset_minogram.h"
|
#include "display/tileset/tileset_minogram.h"
|
||||||
|
|
||||||
#define UI_TEXT_CHAR_START '!'
|
#define UI_TEXT_CHAR_START '!'
|
||||||
|
|||||||
@@ -9,5 +9,4 @@ target_sources(${DUSK_TARGET_NAME}
|
|||||||
memory.c
|
memory.c
|
||||||
string.c
|
string.c
|
||||||
math.c
|
math.c
|
||||||
reflist.c
|
|
||||||
)
|
)
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "reflist.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void refListInit(
|
|
||||||
reflist_t *list,
|
|
||||||
ref_t *array,
|
|
||||||
const uint_fast16_t max
|
|
||||||
) {
|
|
||||||
assertNotNull(list, "Reference list cannot be NULL");
|
|
||||||
assertNotNull(array, "Reference array cannot be NULL");
|
|
||||||
assertTrue(max > 0, "Reference list max must be greater than 0");
|
|
||||||
assertTrue(max <= UINT16_MAX, "Reference list too large");
|
|
||||||
|
|
||||||
memoryZero(list, sizeof(reflist_t));
|
|
||||||
memoryZero(array, sizeof(ref_t) * max);
|
|
||||||
|
|
||||||
list->array = array;
|
|
||||||
list->max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref_t refListLock(reflist_t *list) {
|
|
||||||
assertFalse(refListIsFull(list), "Reference list is full");
|
|
||||||
|
|
||||||
ref_t ref = list->refNext++;
|
|
||||||
assertTrue(ref >= 0, "Reference ID overflow");
|
|
||||||
assertTrue(ref < UINT16_MAX, "Reference ID too large.");
|
|
||||||
|
|
||||||
bool_t empty = list->onNotEmpty && refListIsEmpty(list);
|
|
||||||
|
|
||||||
list->array[list->count++] = ref;
|
|
||||||
|
|
||||||
if(empty) list->onNotEmpty(list);
|
|
||||||
if(list->onAdd) list->onAdd(list, ref);
|
|
||||||
if(list->onFull && refListIsFull(list)) list->onFull(list);
|
|
||||||
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
void refListUnlock(reflist_t *list, const ref_t ref) {
|
|
||||||
assertFalse(refListIsEmpty(list), "Reference list is empty");
|
|
||||||
|
|
||||||
ref_t *slot = list->array;
|
|
||||||
ref_t *end = list->array + list->count;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if(*slot == ref) break;
|
|
||||||
++slot;
|
|
||||||
} while(slot < end);
|
|
||||||
|
|
||||||
assertTrue(slot < end, "Reference not found in list");
|
|
||||||
|
|
||||||
// Can't move if list only has one ref
|
|
||||||
if(list->count > 1) {
|
|
||||||
memoryMove(slot, slot + 1, (end - slot - 1) * sizeof(ref_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
list->count--;
|
|
||||||
if(list->onRemove) list->onRemove(list, ref);
|
|
||||||
if(list->onEmpty && refListIsEmpty(list)) list->onEmpty(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t refListIsFull(const reflist_t *list) {
|
|
||||||
assertNotNull(list, "Reference list cannot be NULL");
|
|
||||||
assertNotNull(list->array, "Reference list array cannot be NULL");
|
|
||||||
assertTrue(list->max > 0, "Reference list max must be greater than 0");
|
|
||||||
assertTrue(list->max <= UINT16_MAX, "Reference list too large");
|
|
||||||
|
|
||||||
return (list->count >= list->max);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t refListIsEmpty(const reflist_t *list) {
|
|
||||||
assertNotNull(list, "Reference list cannot be NULL");
|
|
||||||
assertNotNull(list->array, "Reference list array cannot be NULL");
|
|
||||||
assertTrue(list->max > 0, "Reference list max must be greater than 0");
|
|
||||||
assertTrue(list->max <= UINT16_MAX, "Reference list too large");
|
|
||||||
|
|
||||||
return (list->count == 0);
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef uint_fast16_t ref_t;
|
|
||||||
|
|
||||||
typedef struct reflist_s reflist_t;
|
|
||||||
|
|
||||||
typedef struct reflist_s {
|
|
||||||
ref_t *array;
|
|
||||||
uint_fast16_t max;
|
|
||||||
uint_fast16_t count;
|
|
||||||
ref_t refNext;
|
|
||||||
|
|
||||||
void *user;
|
|
||||||
|
|
||||||
void (*onNotEmpty)(reflist_t *list);
|
|
||||||
void (*onFull)(reflist_t *list);
|
|
||||||
void (*onAdd)(reflist_t *list, const ref_t ref);
|
|
||||||
void (*onRemove)(reflist_t *list, const ref_t ref);
|
|
||||||
void (*onEmpty)(reflist_t *list);
|
|
||||||
} reflist_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize a reference list. Reference lists just hold a list of references
|
|
||||||
* that are "holding on to" a given object/resource.
|
|
||||||
*
|
|
||||||
* @param list The reference list to initialize.
|
|
||||||
* @param array The array to use as backing storage for the list.
|
|
||||||
* @param max The maximum number of references the list can hold.
|
|
||||||
*/
|
|
||||||
void refListInit(
|
|
||||||
reflist_t *list,
|
|
||||||
ref_t *array,
|
|
||||||
const uint_fast16_t max
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock a reference in the list. This will return a new reference ID that can be
|
|
||||||
* used to access the locked reference.
|
|
||||||
*
|
|
||||||
* @param list The reference list to lock a reference in.
|
|
||||||
* @return The locked reference ID, or 0 if the list is full.
|
|
||||||
*/
|
|
||||||
ref_t refListLock(reflist_t *list);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlock a reference in the list. This will free up the reference ID for
|
|
||||||
* reuse.
|
|
||||||
*
|
|
||||||
* @param list The reference list to unlock a reference in.
|
|
||||||
* @param ref The reference ID to unlock.
|
|
||||||
*/
|
|
||||||
void refListUnlock(reflist_t *list, const ref_t ref);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the reference list is full.
|
|
||||||
*
|
|
||||||
* @param list The reference list to check.
|
|
||||||
* @return true if the list is full, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t refListIsFull(const reflist_t *list);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the reference list is empty.
|
|
||||||
*
|
|
||||||
* @param list The reference list to check.
|
|
||||||
* @return true if the list is empty, false otherwise.
|
|
||||||
*/
|
|
||||||
bool_t refListIsEmpty(const reflist_t *list);
|
|
||||||
Reference in New Issue
Block a user