/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "asset/asset.h" #include "assert/assert.h" #include "rpg/entity/entity.h" #pragma pack(push, 1) typedef struct { uint32_t tileCount; uint8_t modelCount; uint8_t entityCount; } assetchunkheader_t; #pragma pack(pop) #pragma pack(push, 1) typedef struct { tile_t tile; } assetchunktiledata_t; #pragma pack(pop) #pragma pack(push, 1) typedef struct { uint32_t vertexCount; } assetchunkmodelheader_t; #pragma pack(pop) #pragma pack(push, 1) typedef struct { entitytype_t entityType; uint8_t localX; uint8_t localY; uint8_t localZ; } assetchunkentityheader_t; #pragma pack(pop) errorret_t assetChunkLoad(assetcustom_t custom) { assertNotNull(custom.output, "Output pointer cannot be NULL"); assertNotNull(custom.zipFile, "Zip file pointer cannot be NULL"); chunk_t *chunk = (chunk_t *)custom.output; assertTrue(chunk->meshCount == 0, "Chunk is not in a good state"); // Read header assetchunkheader_t header; size_t bytesRead = zip_fread( custom.zipFile, &header, sizeof(assetchunkheader_t) ); if(bytesRead != sizeof(assetchunkheader_t)) { zip_fclose(custom.zipFile); errorThrow("Failed to read chunk asset header."); } if(header.tileCount != CHUNK_TILE_COUNT) { zip_fclose(custom.zipFile); errorThrow( "Chunk asset has invalid tile count: %d (expected %d).", header.tileCount, CHUNK_TILE_COUNT ); } if(header.modelCount > CHUNK_MESH_COUNT_MAX) { zip_fclose(custom.zipFile); errorThrow( "Chunk asset has too many models: %d (max %d).", header.modelCount, CHUNK_MESH_COUNT_MAX ); } if(header.entityCount > CHUNK_ENTITY_COUNT_MAX) { zip_fclose(custom.zipFile); errorThrow( "Chunk asset has too many entities: %d (max %d).", header.entityCount, CHUNK_ENTITY_COUNT_MAX ); } chunk->meshCount = header.modelCount; // Read tile data bytesRead = zip_fread( custom.zipFile, chunk->tiles, sizeof(assetchunktiledata_t) * header.tileCount ); if(bytesRead != sizeof(assetchunktiledata_t) * header.tileCount) { zip_fclose(custom.zipFile); errorThrow("Failed to read chunk tile data."); } // For each model... uint32_t vertexIndex = 0; for(uint8_t i = 0; i < header.modelCount; i++) { assetchunkmodelheader_t modelHeader; bytesRead = zip_fread( custom.zipFile, &modelHeader, sizeof(assetchunkmodelheader_t) ); if(bytesRead != sizeof(assetchunkmodelheader_t)) { zip_fclose(custom.zipFile); errorThrow("Failed to read chunk model header."); } if( vertexIndex + modelHeader.vertexCount > CHUNK_VERTEX_COUNT_MAX ) { zip_fclose(custom.zipFile); errorThrow("Chunk model vertex count exceeds maximum."); } // Read vertex data. bytesRead = zip_fread( custom.zipFile, &chunk->vertices[vertexIndex], sizeof(meshvertex_t) * modelHeader.vertexCount ); if(bytesRead != sizeof(meshvertex_t) * modelHeader.vertexCount) { zip_fclose(custom.zipFile); errorThrow("Failed to read chunk model vertex data."); } // Init the mesh if(modelHeader.vertexCount > 0) { mesh_t *mesh = &chunk->meshes[i]; meshInit( mesh, MESH_PRIMITIVE_TRIANGLES, modelHeader.vertexCount, &chunk->vertices[vertexIndex] ); vertexIndex += modelHeader.vertexCount; } else { chunk->meshes[i].vertexCount = 0; } } // Read entity data for(uint8_t i = 0; i < header.entityCount; i++) { assetchunkentityheader_t entityHeader; bytesRead = zip_fread( custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t) ); if(bytesRead != sizeof(assetchunkentityheader_t)) { zip_fclose(custom.zipFile); errorThrow("Failed to read chunk entity header."); } uint8_t entityIndex = entityGetAvailable(); if(entityIndex == 0xFF) { zip_fclose(custom.zipFile); errorThrow("No available entity slots."); } entity_t *entity = &ENTITIES[entityIndex]; entityInit(entity, (entitytype_t)entityHeader.entityType); entity->position.x = ( (chunk->position.x * CHUNK_WIDTH) + entityHeader.localX ); entity->position.y = ( (chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY ); entity->position.z = ( (chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ ); chunk->entities[i] = entityIndex; } errorOk(); }