summaryrefslogtreecommitdiff
path: root/src/tiledmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tiledmap.c')
-rw-r--r--src/tiledmap.c272
1 files changed, 205 insertions, 67 deletions
diff --git a/src/tiledmap.c b/src/tiledmap.c
index e274a6e..1c6964b 100644
--- a/src/tiledmap.c
+++ b/src/tiledmap.c
@@ -1,12 +1,12 @@
#include <raylib.h>
#include <stdlib.h>
#include <stdio.h>
-#include <string.h>
+#include <unistd.h>
#include "tiledmap.h"
-const int i = 1;
-#define is_bigendian() ( (*(char*)&i) == 0 )
+const int endian = 1;
+#define is_bigendian() ( (*(char*)&endian) == 0 )
void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height) {
Image checkedIm = {
@@ -20,16 +20,26 @@ void textureFromPixels(Texture2D *texOut, Color *pixels, int width, int height)
*texOut = LoadTextureFromImage(checkedIm);
}
-//! read rgba image from file
-void readrgba(Texture2D *loc, int width, int height, FILE *file) {
- Color *pixels = malloc(width*height*4);
- fread(pixels, (size_t) width*height*4, (size_t) 1, file);
- textureFromPixels(loc, pixels, width, height);
-}
+//! read a big endian bytes from file
+int readb(char * out, size_t noBytes, FILE * file) {
+ if (!fread(out, (size_t)1, (size_t) noBytes, file))
+ return 1;
+ if (is_bigendian())
+ return 0;
+
+ int tmp;
+ // reverse byte order
+ for(int i = 0; i < noBytes/2; i++) {
+ tmp = out[i];
+ out[i] = out[noBytes-i-1];
+ out[noBytes-i-1] = tmp;
+ }
+
+ return 0;
+}
-//! write a big endian bytes from file
int writeb(char * in, size_t noBytes, FILE * file) {
if (!is_bigendian()) {
int tmp;
@@ -45,53 +55,50 @@ int writeb(char * in, size_t noBytes, FILE * file) {
return fwrite(in, (size_t)1, (size_t) noBytes, file);
}
-//! read a big endian bytes from file
-int readb(char * out, size_t noBytes, FILE * file) {
- if (!fread(out, (size_t)1, (size_t) noBytes, file))
- return 1;
+void buildChunkTree(TiledMap *tiledMap) {
+ // initialise chunk tree
+ tiledMap->chunkTree = NULL;
- if (is_bigendian())
- return 0;
+ size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight;
+ // calculate how much space there is until the end of the file
+ // TODO save the number of chunks total to avoid having to jump around to find referenceso
+ // or just keep reading chunks until eof or other descriptor
+ long chunksStart = ftell(tiledMap->file);
+ fseek(tiledMap->file, 0, SEEK_END);
+ long totalChunksSize = ftell(tiledMap->file) - chunksStart;
- int tmp;
- // reverse byte order
- for(int i = 0; i < noBytes/2; i++) {
- tmp = out[i];
- out[i] = out[noBytes-i-1];
- out[noBytes-i-1] = tmp;
- }
+ fseek(tiledMap->file, chunksStart, SEEK_SET);
- return 0;
-}
+ for (int n = 0; n < totalChunksSize; n += chunkSizeBytes + 8) {
+ int x, y;
+ readb((char *)&x, 4, tiledMap->file);
+ readb((char *)&y, 4, tiledMap->file);
+ long pointer = ftell(tiledMap->file);
-char getTiledMapTile(TiledMap tiledMap, int pos[2]) {
- return tiledMap.tilelayout[pos[1]*tiledMap.width + pos[0]];
-}
+ CachedChunk * cached = malloc(sizeof(CachedChunk));
+ cached->filePos = pointer;
+ cached->chunk = NULL;
-void setTiledMapTile(TiledMap tiledMap, int pos[2], char tile) {
- tiledMap.tilelayout[pos[1]*tiledMap.width + pos[0]] = tile;
+ kdtree_insert(&tiledMap->chunkTree, x, y, (char *) cached);
+ fseek(tiledMap->file, chunkSizeBytes, SEEK_CUR);
+ }
}
-//! load tilemap data from file
-TiledMap loadTiledMap(char * filename) {
+TiledMap openTiledMap(char * filename) {
TiledMap tiledMap;
- FILE * file;
- if (!(file = fopen(filename, "rb"))) {
+ if (!(tiledMap.file = fopen(filename, "r+b"))) {
fprintf(stderr, "Failed to load %s\n", filename);
- return tiledMap;
}
+ FILE * file = tiledMap.file;
+
// skip header
fseek(file, 10, SEEK_CUR);
// 4 bytes for int width
- readb((char *)&tiledMap.width, 4, file);
+ readb((char *)&tiledMap.chunkWidth, 4, file);
// 4 bytes for int height
- readb((char *)&tiledMap.height, 4, file);
-
- size_t layoutSize = tiledMap.width*tiledMap.height;
- tiledMap.tilelayout = malloc(layoutSize);
- fread(tiledMap.tilelayout, layoutSize, 1, file);
+ readb((char *)&tiledMap.chunkHeight, 4, file);
// read the pixel size of each tile
readb((char *)&tiledMap.tileSize, 4, file);
@@ -104,52 +111,183 @@ TiledMap loadTiledMap(char * filename) {
size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4;
tiledMap.atlasData = malloc(atlasSizeBytes);
fread(tiledMap.atlasData, atlasSizeBytes, (size_t) 1, file);
-
+
tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1;
- fclose(file);
+ buildChunkTree(&tiledMap);
return tiledMap;
}
-TiledMap openNewTiledMap(Image atlas, int tileSize, int width, int height) {
- TiledMap tiledMap;
- tiledMap.width = width;
- tiledMap.height = height;
- tiledMap.tilelayout = malloc(width * height);
+//! save chunk to file
+void commitChunk(TiledMap * tiledMap, CachedChunk * cached) {
+ size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight;
- tiledMap.tileSize = tileSize;
+ fseek(tiledMap->file, cached->filePos, SEEK_SET);
+ fwrite(cached->chunk, 1, chunkSizeBytes, tiledMap->file);
+}
- tiledMap.atlasSize[0] = atlas.width / tileSize;
- tiledMap.atlasSize[1] = atlas.height / tileSize;
+void unloadChunk(TiledMap * tiledMap, CachedChunk * cached) {
+ // commit a chunk before unloading
+ commitChunk(tiledMap, cached);
- tiledMap.atlasData = LoadImageColors(atlas);
-
- return tiledMap;
+ // free memory
+ free(cached->chunk);
+ cached->chunk = NULL;
}
-void saveTiledMap(char * filename, TiledMap tiledMap) {
- FILE * file;
+//! load a chunk into the cache and return it
+CachedChunk * loadChunk(TiledMap * tiledMap, int x, int y) {
+ // TODO add caching for this
+ size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight;
- if (!(file = fopen(filename, "wb"))) {
- fprintf(stderr, "Failed to load %s\n", filename);
- return;
+
+ CachedChunk *cached = (CachedChunk * ) kdtree_search(tiledMap->chunkTree, x, y);
+ // if this chunk is not indexed, return NULL
+ if (cached == NULL)
+ return NULL;
+
+ // if this chunk is not loaded into memory, load it
+ if (cached->chunk == NULL) {
+ cached->chunk = malloc(chunkSizeBytes);
+ fseek(tiledMap->file, cached->filePos, SEEK_SET);
+ fread(cached->chunk, 1, chunkSizeBytes, tiledMap->file);
}
- size_t layoutSize = tiledMap.width*tiledMap.height;
+
+ return cached;
+}
+
+CachedChunk * createChunk(TiledMap *tiledMap, int x, int y, Chunk chunk) {
+ size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight;
+
+ fseek(tiledMap->file, 0, SEEK_END);
+
+ // calculate position before writing
+ long pos = ftell(tiledMap->file) + 8;
+ CachedChunk *cached = malloc(sizeof(CachedChunk));
+ cached->filePos = pos;
+ cached->chunk = chunk;
+
+ kdtree_insert(&tiledMap->chunkTree, x, y, (char *) cached);
+
+ int chunkx, chunky;
+ writeb((char *) &x, 4, tiledMap->file);
+ writeb((char *) &y, 4, tiledMap->file);
+ fwrite(chunk, 1, chunkSizeBytes, tiledMap->file);
+
+ return cached;
+}
+
+CachedChunk * createEmptyChunk(TiledMap * tiledMap, int x, int y) {
+ Chunk chunk = calloc(tiledMap->chunkWidth*tiledMap->chunkHeight, 1);
+ return createChunk(tiledMap, x, y, chunk);
+}
+
+char getChunkedTile(TiledMap *tiledMap, int x, int y) {
+ // TODO put this calculation in function
+ int inChunkX = x % tiledMap->chunkWidth;
+ int inChunkY = y % tiledMap->chunkHeight;
+ int chunkX = (x - inChunkX) / tiledMap->chunkWidth;
+ int chunkY = (y - inChunkY) / tiledMap->chunkHeight;
+
+ CachedChunk * cached = loadChunk(tiledMap, chunkX, chunkY);
+ if (cached == NULL)
+ return 0;
+
+ if (cached->chunk == NULL)
+ return 0;
+
+ char v = cached->chunk[inChunkY * tiledMap->chunkWidth + inChunkX];
+ return v;
+}
+
+char setChunkedTile(TiledMap * tiledMap, int x, int y, char value) {
+ int inChunkX = x % tiledMap->chunkWidth;
+ int inChunkY = y % tiledMap->chunkHeight;
+ int chunkX = (x - inChunkX) / tiledMap->chunkWidth;
+ int chunkY = (y - inChunkY) / tiledMap->chunkHeight;
+
+ CachedChunk * cached = loadChunk(tiledMap, chunkX, chunkY);
+ if (cached == NULL)
+ cached = createEmptyChunk(tiledMap, chunkX, chunkY);
+
+ cached->chunk[inChunkY * tiledMap->chunkWidth + inChunkX] = value;
+
+ // TODO do this when unloading
+ //commitChunk(tiledMap, cached);
+ return value;
+}
+
+
+void writeTiledMapHeader(TiledMap tiledMap) {
+ FILE * file = tiledMap.file;
size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4;
- fwrite("TILEFILEv2", 10, 1, file);
+ // write header information from the start of the file
+ fseek(file, 0, SEEK_SET);
+ fwrite("TILEFILEv3", 10, 1, file);
- writeb((char *) &tiledMap.width, 4, file);
- writeb((char *) &tiledMap.height, 4, file);
-
- fwrite(tiledMap.tilelayout, 1, layoutSize, file);
+ writeb((char *) &tiledMap.chunkWidth, 4, file);
+ writeb((char *) &tiledMap.chunkHeight, 4, file);
writeb((char *) &tiledMap.tileSize, 4, file);
writeb((char *) &tiledMap.atlasSize[0], 4, file);
writeb((char *) &tiledMap.atlasSize[1], 4, file);
fwrite(tiledMap.atlasData, 1, atlasSizeBytes, file);
+ // since chunks are already directly written here, do not write anything else
+ // TODO when caching, commit everything left in cache here
+}
- fclose(file);
- fprintf(stderr, "Written tiledfiled to %s\n", filename);
+TiledMap openNewTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height) {
+ TiledMap tiledMap;
+ tiledMap.chunkWidth = chunkWidth;
+ tiledMap.chunkHeight = chunkHeight;
+
+ tiledMap.tileSize = tileSize;
+
+ tiledMap.atlasSize[0] = atlas.width / tileSize;
+ tiledMap.atlasSize[1] = atlas.height / tileSize;
+
+ tiledMap.atlasData = LoadImageColors(atlas);
+ tiledMap.tileCount = tiledMap.atlasSize[0]*tiledMap.atlasSize[1] + 1;
+
+ tiledMap.chunkTree = NULL;
+
+ if (!(tiledMap.file = fopen(filename, "wb"))) {
+ fprintf(stderr, "Failed to load %s\n", filename);
+ }
+
+ writeTiledMapHeader(tiledMap);
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ char * chunk = calloc(chunkWidth*chunkHeight, 1);
+ createChunk(&tiledMap, x, y, chunk);
+ }
+ }
+
+ // reopen the file in read+write mode
+ fclose(tiledMap.file);
+
+ if (!(tiledMap.file = fopen(filename, "r+b"))) {
+ fprintf(stderr, "Failed to load %s\n", filename);
+ }
+
+ return tiledMap;
}
+
+void unloadChunks(TiledMap *tiledMap, kdtree_t * root) {
+ if (root == NULL)
+ return;
+
+ unloadChunks(tiledMap, root->left);
+ unloadChunks(tiledMap, root->right);
+ unloadChunk(tiledMap, (CachedChunk *)root->value);
+}
+
+void closeTiledMap(TiledMap *tiledMap) {
+ unloadChunks(tiledMap, tiledMap->chunkTree);
+ kdtree_free(&tiledMap->chunkTree);
+ UnloadImageColors(tiledMap->atlasData);
+ fclose(tiledMap->file);
+}
+