#include #include #include #include #include "loader.h" #include "main.h" #include "tilemap.h" #include "collision.h" struct tilemap *tilemap = NULL, *next_tilemap = NULL; #define PACKED __attribute__((__packed__)) struct PACKED sets { uint32_t tilesets_count; uint32_t wang_count; }; struct PACKED map { uint32_t width; uint32_t height; uint32_t layers; uint8_t tiles[]; }; void free_tilemap(struct tilemap *tilemap) { SDL_DestroyTexture(tilemap->tileset); SDL_DestroyTexture(tilemap->wang_tileset); free(tilemap->parallax); free(tilemap->collision); for (int i = 0; i < tilemap->layers; i++) { SDL_DestroyTexture(tilemap->tilemaps[i]); } free(tilemap->tilemaps); free(tilemap); } struct tilemap *load_tilemap(void *data, size_t size) { struct tilemap *tilemap = malloc(sizeof (struct tilemap)); if (tilemap == NULL) { return NULL; } tilemap->tileset = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, 128, 128); tilemap->wang_tileset = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, 128, 128); SDL_SetTextureBlendMode(tilemap->wang_tileset, SDL_BLENDMODE_BLEND); tilemap->layers = 0; tilemap->parallax = NULL; tilemap->collision = NULL; tilemap->tilemaps = NULL; struct sets *const sets = data; data = sets + 1; size -= sizeof (struct sets); char *str = data; int y = 0; SDL_SetRenderTarget(renderer, tilemap->tileset); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); collision_T collision[256] = {0}; for (uint32_t count = sets->tilesets_count; count > 0; count--) { int height = *(uint32_t *) str; str += sizeof (uint32_t); size_t len = strnlen(str, size); if (len == size) { goto fail; } void *tex = res_get_texture(str).data; struct blob col = res_get_collision(str); SDL_RenderCopy(renderer, tex, &(SDL_Rect) {0, 0, 128, height}, &(SDL_Rect) {0, y, 128, height}); if (col.size + y * 2 > 0xf0) { fprintf(stderr, "warn: '%s' overflows tile properties\n", str); col.size = 0xf0 - y * 2; } memcpy(collision + y * 2, col.data, col.size * sizeof (collision_T)); y += height; str += len + 1; size -= len + 4 + 1; } SDL_SetRenderTarget(renderer, tilemap->wang_tileset); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); SDL_RenderClear(renderer); int wangs = 0; if (sets->wang_count) { int height = *(uint32_t *) str; wangs = height / 8; str += sizeof (uint32_t); size_t len = strnlen(str, size); if (len == size) { free_tilemap(tilemap); return NULL; } void *tex = res_get_texture(str).data; struct blob col = res_get_collision(str); SDL_RenderCopy(renderer, tex, &(SDL_Rect) {0, 0, 128, height}, &(SDL_Rect) {0, 0, 128, height}); if (col.size > 16) { fprintf(stderr, "warn: '%s' overflows tile properties\n", str); col.size = 16; } memcpy(collision + 0xf0, col.data, col.size * sizeof (collision_T)); str += len + 1; size -= len + 4 + 1; } struct map const *const map = (void *) str; if (map->width * map->height * map->layers > size - sizeof (struct map)) { goto fail; } tilemap->width = map->width; tilemap->height = map->height; tilemap->layers = map->layers; tilemap->tilemaps = malloc(sizeof (void *) * tilemap->layers); for (int l = 0; l < tilemap->layers; l++) { SDL_Texture *layer = SDL_CreateTexture(renderer, SDL_PIXELTYPE_UNKNOWN, SDL_TEXTUREACCESS_TARGET, map->width * 8, map->height * 8); SDL_SetTextureBlendMode(layer, SDL_BLENDMODE_BLEND); SDL_SetRenderTarget(renderer, layer); size_t const ll = l * map->width * map->height; for (int y = 0; y < map->height; y++) { size_t const yy = y * map->width; for (int x = 0; x < map->width; x++) { unsigned tile = map->tiles[x + yy + ll]; int tileX = tile & 0x0f; int tileY = tile >> 4; SDL_RenderCopy(renderer, tilemap->tileset, &(SDL_Rect) {tileX * 8, tileY * 8, 8, 8}, &(SDL_Rect) {x * 8, y * 8, 8, 8}); } } for (int tile = 0xf0; tile < 0xf0 + wangs; tile++) { for (int y = 0; y < map->height - 1; y++) { size_t const yy = y * map->width; for (int x = 0; x < map->width - 1; x++) { int tl = map->tiles[x + yy + ll] == tile; int tr = map->tiles[x + 1 + yy + ll] == tile; int bl = map->tiles[x + yy + map->width + ll] == tile; int br = map->tiles[x + 1 + yy + map->width + ll] == tile; int tileX = tl << 0 | tr << 1 | bl << 2 | br << 3; int tileY = tile & 0x0f; SDL_RenderCopy(renderer, tilemap->wang_tileset, &(SDL_Rect) {tileX * 8, tileY * 8, 8, 8}, &(SDL_Rect) {x * 8 + 4, y * 8 + 4, 8, 8}); } } } tilemap->tilemaps[l] = layer; } float *parallax = (void *) (map->tiles + map->width * map->height * map->layers); size -= (char *) parallax - (char *) map; if (sizeof (struct parallax) * map->layers > size) { goto fail; } tilemap->parallax = malloc(sizeof (struct parallax) * map->layers); for (int i = 0; i < map->layers; i++) { tilemap->parallax[i].x = *parallax++; tilemap->parallax[i].y = *parallax++; } size -= sizeof (struct parallax) * map->layers; SDL_SetRenderTarget(renderer, NULL); return tilemap; fail: free_tilemap(tilemap); SDL_SetRenderTarget(renderer, NULL); return NULL; }