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

@@ -34,7 +34,7 @@ add_subdirectory(display)
add_subdirectory(engine) add_subdirectory(engine)
add_subdirectory(error) add_subdirectory(error)
add_subdirectory(input) add_subdirectory(input)
# add_subdirectory(locale) add_subdirectory(locale)
add_subdirectory(rpg) add_subdirectory(rpg)
add_subdirectory(scene) add_subdirectory(scene)
add_subdirectory(thread) add_subdirectory(thread)

View File

@@ -6,9 +6,9 @@
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
asset.c # asset.c
assetmanager.c # assetmanager.c
) )
# Subdirs # Subdirs
add_subdirectory(type) # add_subdirectory(type)

View File

@@ -6,134 +6,46 @@
*/ */
#include "asset.h" #include "asset.h"
#include "assetmanager.h"
#include "util/memory.h" #include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h" #include "assert/assert.h"
assetdef_t ASSET_DEFINITIONS[ASSET_TYPE_COUNT] = { errorret_t assetInit(void) {
[ASSET_TYPE_PALETTE_IMAGE] = { memoryZero(&ASSET_MANAGER, sizeof(assetmanager_t));
"DPI", assetPaletteImageLoad, assetPaletteImageDispose
},
[ASSET_TYPE_ALPHA_IMAGE] = {
"DAI", assetAlphaImageLoad, assetAlphaImageDispose
},
};
errorret_t assetInit(asset_t *asset, const char_t *filename) { // Default system path, intended to be overridden by the paltform
assertNotNull(asset, "Asset cannot be NULL."); stringCopy(ASSET_MANAGER.systemPath, ".", FILENAME_MAX);
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)); // Open zip file
memoryCopy(asset->filename, filename, strlen(filename) + 1); char_t searchPath[FILENAME_MAX];
char_t **path = ASSET_SEARCH_PATHS;
// Initialze the reference list. do {
refListInit( sprintf(
&asset->refList, searchPath,
asset->refListArray, *path,
ASSET_REFERENCE_COUNT_MAX ASSET_MANAGER.systemPath,
ASSET_ASSET_FILE
); );
// Test the file can be opened. In future I may make the handle stay open for // Try open
// a while to see if it increases performance and stops disk thrashing. ASSET.zip = zip_open(searchPath, ZIP_RDONLY, NULL);
asset->file = zip_fopen(ASSET_MANAGER.zip, filename, 0); if(ASSET.zip == NULL) continue;
if(asset->file == NULL) errorThrow("Failed to open asset file: %s", filename); break;// Found!
zip_fclose(asset->file); } while(*(++path) != NULL);
asset->file = NULL;
// Did we open the asset?
if(ASSET.zip == NULL) errorThrow("Failed to open asset file.");
errorOk(); errorOk();
} }
ref_t assetLock(asset_t *asset) { erroret_t assetLoad(const char_t *filename, void *output) {
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(); errorOk();
} }
errorret_t assetDispose(asset_t *asset) { void assetDispose(void) {
assertNotNull(asset, "Asset cannot be NULL."); if(ASSET_MANAGER.zip != NULL) {
zip_close(ASSET_MANAGER.zip);
if(asset->state == ASSET_STATE_LOADED) { ASSET_MANAGER.zip = NULL;
// 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;
} }
} }

View File

@@ -7,98 +7,48 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "util/reflist.h"
#include <zip.h> #include <zip.h>
#include "asset/type/assetpaletteimage.h" #if ASSET_TYPE == wad
#include "asset/type/assetalphaimage.h" #else
#error "Unsupported ASSET_TYPE"
#endif
#define ASSET_HEADER_SIZE 3 #define ASSET_FILE "dusk.dsk"
#define ASSET_REFERENCE_COUNT_MAX 8
static const char_t *ASSET_SEARCH_PATHS[] = {
"%s/%s",
"%s",
"../%s",
"../../%s",
"data/%s",
"../data/%s",
NULL
};
typedef struct { typedef struct {
void (*load)(); zip_t *zip;
} assetcallback_t; char_t systemPath[FILENAME_MAX];
uint8_t assetCount;
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; } asset_t;
typedef struct { static asset_t ASSET;
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 * Initializes the asset system.
* 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); errorret_t assetInit(void);
/** /**
* Requests a lock on the given asset. This will increase the reference count * Loads an asset by its filename, the output type depends on the asset type.
* of the asset, and prevent it from being unloaded until all locks are
* released.
* *
* @param asset The asset to lock. * @param filename The filename of the asset to retrieve.
* @return A unique reference ID for the lock. * @param output The output pointer to store the loaded asset data.
* @return An error code if the asset could not be loaded.
*/ */
ref_t assetLock(asset_t *asset); errorret_t assetLoad(const char_t *filename, void *output);
/** /**
* Releases a lock on the given asset. This will decrease the reference count * Disposes/cleans up the asset system.
* 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); void assetDispose(void);
/**
* 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

@@ -9,6 +9,7 @@
#include "util/memory.h" #include "util/memory.h"
#include "time/time.h" #include "time/time.h"
#include "input/input.h" #include "input/input.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/assetmanager.h"
@@ -27,6 +28,7 @@ errorret_t engineInit(void) {
// Init systems. Order is important. // Init systems. Order is important.
timeInit(); timeInit();
inputInit(); inputInit();
localeManagerInit();
errorChain(assetManagerInit()); errorChain(assetManagerInit());
errorChain(displayInit()); errorChain(displayInit());
errorChain(uiInit()); errorChain(uiInit());

View File

@@ -21,7 +21,6 @@ typedef struct {
errorstate_t *state; errorstate_t *state;
} errorret_t; } errorret_t;
static const errorcode_t ERROR_OK = 0; static const errorcode_t ERROR_OK = 0;
static const errorcode_t ERROR_NOT_OK = 1; static const errorcode_t ERROR_NOT_OK = 1;

View File

@@ -6,5 +6,5 @@
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
language.c localemanager.c
) )

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "localemanager.h"
#include "util/memory.h"
localemanager_t LOCALE;
void localeManagerInit() {
memoryZero(&LOCALE, sizeof(localemanager_t));
}

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"
typedef struct {
void *nothing;
} localemanager_t;
extern localemanager_t LOCALE;
/**
* Initialize the locale system.
*/
void localeManagerInit();

View File

@@ -6,7 +6,7 @@
*/ */
#include "engine/engine.h" #include "engine/engine.h"
#include "asset/assetmanager.h" #include "asset/asset.h"
#include "util/string.h" #include "util/string.h"
#include "input/input.h" #include "input/input.h"
@@ -23,8 +23,8 @@ int main(int argc, char **argv) {
// Setup system path on asset manager // Setup system path on asset manager
if(argc > 0) { if(argc > 0) {
stringCopy( stringCopy(
ASSET_MANAGER.systemPath, argv[0], ASSET.systemPath, argv[0],
sizeof(ASSET_MANAGER.systemPath) / sizeof(char_t) sizeof(ASSET.systemPath) / sizeof(char_t)
); );
} }

View File

@@ -7,7 +7,7 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include "asset/assetmanager.h" #include "asset/asset.h"
#include "display/tileset/tileset.h" #include "display/tileset/tileset.h"
#include "display/camera.h" #include "display/camera.h"

View File

@@ -6,7 +6,7 @@
*/ */
#pragma once #pragma once
#include "asset/assetmanager.h" #include "asset/asset.h"
#include "display/tileset/tileset_minogram.h" #include "display/tileset/tileset_minogram.h"
#define UI_TEXT_CHAR_START '!' #define UI_TEXT_CHAR_START '!'

View File

@@ -36,7 +36,6 @@ def processPalette(asset):
# PSP requires that the palette size be a power of two, so we will pad the # PSP requires that the palette size be a power of two, so we will pad the
# palette with transparent colors if needed. # palette with transparent colors if needed.
# if args.platform == "psp":
def mathNextPowTwo(x): def mathNextPowTwo(x):
return 1 << (x - 1).bit_length() return 1 << (x - 1).bit_length()
@@ -53,7 +52,7 @@ def processPalette(asset):
data += f"static const color_t PALETTE_{paletteIndex}_COLORS[PALETTE_{paletteIndex}_COLOR_COUNT] = {{\n" data += f"static const color_t PALETTE_{paletteIndex}_COLORS[PALETTE_{paletteIndex}_COLOR_COUNT] = {{\n"
for pixel in pixels: for pixel in pixels:
data += f" {{ 0x{pixel[0]:02X}, 0x{pixel[1]:02X}, 0x{pixel[2]:02X}, 0x{pixel[3]:02X} }},\n" data += f" {{ 0x{pixel[0]:02X}, 0x{pixel[1]:02X}, 0x{pixel[2]:02X}, 0x{pixel[3]:02X} }},\n"
data += f"}};\n\n" data += f"}};\n"
data += f"#pragma pack(pop)\n\n" data += f"#pragma pack(pop)\n\n"
data += f"static const palette_t PALETTE_{paletteIndex} = {{\n" data += f"static const palette_t PALETTE_{paletteIndex} = {{\n"
data += f" .colorCount = PALETTE_{paletteIndex}_COLOR_COUNT,\n" data += f" .colorCount = PALETTE_{paletteIndex}_COLOR_COUNT,\n"