/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "asset.h" #include "util/memory.h" #include "console/console.h" #include "util/string.h" #include "assert/assert.h" #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."); errorOk(); } void assetLoad( const char_t *filename, void (*callback)(void* data), void *data ) { 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." ); // Determine the asset map type we are expecting based on the extension. const assetmap_t *expected = &ASSET_MAP[0]; do { if(stringEndsWith(filename, expected->extension)) break; expected++; } while(expected->extension); assertNotNull(expected->extension, "Unknown asset type."); // Pass off to the thread to begin loading. ASSET.callback = callback; ASSET.callbackData = data; ASSET.errorState = ERROR_STATE_INIT; ASSET.state = ASSET_STATE_LOADING; stringCopy(ASSET.filename, filename, ASSET_FILENAME_MAX); memoryZero(&ASSET.data, sizeof(ASSET.data)); // 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 ); if(ASSET.callback) ASSET.callback(ASSET.callbackData); return; } printf("Loading asset: %s\n", 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 ); if(ASSET.callback) ASSET.callback(ASSET.callbackData); return; } if(st.size > sizeof(ASSET.data.palette)) { zip_fclose(file); ASSET.state = ASSET_STATE_ERROR; ASSET.error = errorCreate( &ASSET.errorState, "Asset size mismatch: %s (got %d, expected %d)", filename, (int)st.size, (int)sizeof(ASSET.data.palette) ); if(ASSET.callback) ASSET.callback(ASSET.callbackData); return; } // Read entire file into the asset data. zip_fread(file, &ASSET.data, st.size); zip_fclose(file); ASSET.state = ASSET_STATE_LOADED; if(memoryCompare( ASSET.data.header, expected->header, ASSET_HEADER_LENGTH ) != 0) { ASSET.state = ASSET_STATE_ERROR; ASSET.error = errorCreate( &ASSET.errorState, "Asset header mismatch: %s (got %c%c%c, expected %c%c%c)", filename, ASSET.data.header[0], ASSET.data.header[1], ASSET.data.header[2], expected->header[0], expected->header[1], expected->header[2] ); if(ASSET.callback) ASSET.callback(ASSET.callbackData); return; } // Parse the asset. assertNotNull(expected->parser, "Asset parser cannot be NULL."); expected->parser(); printf("Loaded asset: %s\n", filename); if(ASSET.callback) ASSET.callback(ASSET.callbackData); } void assetDispose(void) { if(ASSET.zip != NULL) { zip_close(ASSET.zip); ASSET.zip = NULL; } }