/** * 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; } }