2D_Engine_SDL3_Core/src/asset_manager.c

332 lines
9.6 KiB
C

#include "asset_manager.h"
#include <SDL3/SDL_render.h>
#include <stdarg.h>
#include <string.h>
#include "memory_alloc.h"
#include "linked_list.h"
#include "display.h"
#include "errors.h"
int create_asset(asset_t *asset, va_list args)
{
if(!asset)
{
error_printf("Failed to allocate memory : %d", errno);
return EXIT_FAILURE;
}
asset->type = va_arg(args, asset_type_t);
asset->nb_refs = 0;
return EXIT_SUCCESS;
}
int destroy_asset(asset_t *asset)
{
free(asset->name);
switch(asset->type)
{
case ASSET_TEXTURE:
SDL_DestroyTexture(asset->data.texture);
break;
default:
error_printf("Asset type not valid : %d", asset->type);
break;
}
return EXIT_SUCCESS;
}
int destroy_asset_header_def(asset_header_def_t *asset_header_def)
{
free(asset_header_def->name);
return EXIT_SUCCESS;
}
int asset_manager_init(asset_manager_t *asset_manager)
{
assert("Asset manager cannot be NULL" && asset_manager);
linked_list_init(&asset_manager->assets, sizeof(asset_t), (allocator_t)create_asset, (deleter_t)destroy_asset);
linked_list_init(&asset_manager->assets_header_defs, sizeof(asset_header_def_t), NULL, (deleter_t)destroy_asset_header_def);
asset_manager->asset_file = fopen(ASSET_FILE_NAME, "rb");
if(!asset_manager->asset_file)
{
error_printf("Failed to open asset file : %d", errno);
return EXIT_FAILURE;
}
size_t nb_assets;
size_t ret = fread(&nb_assets, sizeof(nb_assets), 1, asset_manager->asset_file);
if(ret != 1)
{
error_printf("Failed to read asset file.");
return EXIT_FAILURE;
}
if(linked_list_reserve(&asset_manager->assets_header_defs, nb_assets + 1)) return EXIT_FAILURE;
for(size_t i = 0; i < nb_assets; i++)
{
elem_t *asset_header_def_elem = create_elem(&asset_manager->assets_header_defs);
if(!asset_header_def_elem) return EXIT_FAILURE;
asset_header_def_t *asset_header_def = asset_header_def_elem->data;
size_t cap = 0;
ret = getdelim(&asset_header_def->name, &cap, '\0', asset_manager->asset_file);
if(ret == SIZE_MAX)
{
error_printf("Failed to read asset file.");
return EXIT_FAILURE;
}
ret = fread(&asset_header_def->start, sizeof(asset_header_def->start), 1, asset_manager->asset_file);
if(ret != 1)
{
error_printf("Failed to read asset file.");
return EXIT_FAILURE;
}
if(linked_list_push_back(&asset_manager->assets_header_defs, asset_header_def_elem)) return EXIT_FAILURE;
}
debug_printf("Initialized asset manager.");
return EXIT_SUCCESS;
}
int asset_manager_exit(asset_manager_t *asset_manager)
{
assert("Asset manager cannot be NULL" && asset_manager);
if(linked_list_exit(&asset_manager->assets_header_defs)) return EXIT_FAILURE;
if(linked_list_exit(&asset_manager->assets)) return EXIT_FAILURE;
if(fclose(asset_manager->asset_file))
{
error_printf("Failed to close asset file : %d", errno);
return EXIT_FAILURE;
}
debug_printf("Exited asset manager.");
return EXIT_SUCCESS;
}
// Only for this file
// No args
static inline bool condition_is_asset_unused(elem_t *elem, va_list args __attribute__((unused)))
{
return !((asset_t *)elem->data)->nb_refs;
}
inline int asset_manager_collect_garbage(asset_manager_t *asset_manager)
{
assert("Asset manager cannot be NULL" && asset_manager);
if(linked_list_remove_if(&asset_manager->assets, condition_is_asset_unused)) return EXIT_FAILURE;
return EXIT_SUCCESS;
}
// Only for this file
// Arg : const char *asset_name
static inline bool condition_is_asset_name(elem_t *elem, va_list args)
{
return !strcmp(((asset_t *)elem->data)->name, va_arg(args, const char *));
}
//Only for this file
// Arg : const char *asset_name
static inline bool condition_is_asset_header_def_name(elem_t *elem, va_list args)
{
return !strcmp(((asset_header_def_t *)elem->data)->name, va_arg(args, const char *));
}
static inline int load_texture(asset_t *asset, asset_header_def_t *asset_header_def, FILE *asset_file)
{
if(fseek(asset_file, asset_header_def->start, SEEK_SET))
{
error_printf("Failed to seek in asset file : %d", errno);
return EXIT_FAILURE;
}
size_t width, height;
if(fread(&width, sizeof(width), 1, asset_file) != 1)
{
error_printf("Failed to read in asset file.");
return EXIT_FAILURE;
}
if(fread(&height, sizeof(height), 1, asset_file) != 1)
{
error_printf("Failed to read in asset file.");
return EXIT_FAILURE;
}
SDL_Surface *tmp_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGB565);
if(!tmp_surface)
{
error_printf("Failed to create temporary surface : %s", SDL_GetError());
return EXIT_FAILURE;
}
if(!SDL_LockSurface(tmp_surface))
{
error_printf("Failed to lock temporary surface : %s", SDL_GetError());
SDL_DestroySurface(tmp_surface);
return EXIT_FAILURE;
}
if(fread(tmp_surface->pixels, sizeof(uint16_t), width * height, asset_file) != width * height)
{
error_printf("Failed to read asset file.");
SDL_DestroySurface(tmp_surface);
return EXIT_FAILURE;
}
for(size_t i = 0; i < height; i++)
{
for(size_t j = 0; j < width; j += sizeof(uint16_t))
{
uint8_t r, g, b;
r = *(uint16_t *)&tmp_surface->pixels[i * sizeof(uint16_t) * width + j] & 0x001F;
g = *(uint16_t *)&tmp_surface->pixels[i * sizeof(uint16_t) * width + j + 1] >> 5;
b = *(uint16_t *)&tmp_surface->pixels[i * sizeof(uint16_t) * width + j + 2] >> 11;
printf("\033[48;2;%d;%d;%dm ", r, g, b);
}
puts("\033[m");
}
if(!SDL_SetSurfaceColorKey(tmp_surface, true, 0xF81F))
{
error_printf("Failed to set color key of temporary surface : %s", SDL_GetError());
SDL_DestroySurface(tmp_surface);
return EXIT_FAILURE;
}
asset->data.texture = SDL_CreateTextureFromSurface(renderer, tmp_surface);
if(!asset->data.texture)
{
error_printf("Failed to create texture from temporary surface : %s", SDL_GetError());
SDL_DestroySurface(tmp_surface);
return EXIT_FAILURE;
}
SDL_DestroySurface(tmp_surface);
return EXIT_SUCCESS;
}
int asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_name, asset_type_t asset_type)
{
assert("Asset manager cannot be NULL" && asset_manager);
// Check if asset has alread been loaded.
elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name);
if(asset_elem)
{
((asset_t *)asset_elem->data)->nb_refs++;
debug_printf("Asset \"%s\" has already been loaded, nb refs : %lu", ((asset_t *)asset_elem->data)->name, ((asset_t *)asset_elem->data)->nb_refs);
return EXIT_SUCCESS;
}
else
{
// If it didn't, load it
elem_t *asset_header_def_elem = linked_list_get_if(&asset_manager->assets_header_defs, condition_is_asset_header_def_name, asset_name);
if(!asset_header_def_elem)
{
error_printf("Asset doesn't exist in asset file : %s", asset_name);
return EXIT_FAILURE;
}
asset_header_def_t *asset_header_def = asset_header_def_elem->data;
asset_elem = create_elem(&asset_manager->assets, asset_type);
if(!asset_elem) return EXIT_FAILURE;
asset_t *asset = asset_elem->data;
asset->name = calloc(strlen(asset_name) + 1, sizeof(char));
if(!asset->name)
{
error_printf("Failed to allocate memory for asset name : %d", errno);
destroy_asset(asset);
return EXIT_FAILURE;
}
strcpy(asset->name, asset_name);
asset->type = asset_type;
switch(asset_type)
{
case ASSET_TEXTURE:
if(load_texture(asset, asset_header_def, asset_manager->asset_file)) return EXIT_FAILURE;
break;
default:
error_printf("Asset type doesn't exists : %d", asset_type);
destroy_asset(asset);
return EXIT_FAILURE;
}
asset->nb_refs++;
linked_list_push_back(&asset_manager->assets, asset_elem);
debug_printf("Asset \"%s\" has been loaded, nb refs : %lu", asset->name, asset->nb_refs);
return EXIT_SUCCESS;
}
}
int asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name)
{
assert("Asset manager cannot be NULL" && asset_manager);
elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name);
if(!asset_elem)
{
error_printf("Asset doesn't exists : %s", asset_name);
return EXIT_FAILURE;
}
asset_t *asset = asset_elem->data;
asset->nb_refs--;
debug_printf("Asset \"%s\" has been let go, nb refs : %lu", asset->name, asset->nb_refs);
return EXIT_SUCCESS;
}
texture_t *asset_manager_get_texture(asset_manager_t *asset_manager, const char *texture_name)
{
assert("Asset manager cannot be NULL" && asset_manager);
elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, texture_name);
if(!asset_elem)
{
error_printf("Asset \"%s\" hasn't been loaded yet.", texture_name);
return NULL;
}
asset_t *asset = asset_elem->data;
if(asset->type != ASSET_TEXTURE)
{
error_printf("Asset \"%s\" isn't of type ASSET_TEXTURE.", texture_name);
return NULL;
}
return asset->data.texture;
}