From 2dcefdb4565f96b808ed01ce1d96a802b8f73931 Mon Sep 17 00:00:00 2001 From: davidovski Date: Fri, 21 Jul 2023 01:53:48 +0200 Subject: add chunked tiledmap io --- src/chunkedtiledmap.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/chunkedtiledmap.h | 16 +++++ src/kdtree.c | 27 ++++++-- src/kdtree.h | 12 ++++ 4 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 src/chunkedtiledmap.c create mode 100644 src/chunkedtiledmap.h diff --git a/src/chunkedtiledmap.c b/src/chunkedtiledmap.c new file mode 100644 index 0000000..ce6be57 --- /dev/null +++ b/src/chunkedtiledmap.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include + +#include "chunkedtiledmap.h" + +//! 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; +} + +int writeb(char * in, size_t noBytes, FILE * file) { + if (!is_bigendian()) { + int tmp; + // reverse byte order + for(int i = 0; i < noBytes/2; i++) { + tmp = in[i]; + in[i] = in[noBytes-i-1]; + in[noBytes-i-1] = tmp; + } + + } + + return fwrite(in, (size_t)1, (size_t) noBytes, file); +} + +void buildChunkTree(ChunkedTiledMap *tiledMap) { + int noChunks; + + // 4 bytes for number of chunks + readb((char *)&noChunks, 4, tiledMap->file); + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; + for (int n = 0; n < noChunks; n++) { + int x, y; + readb((char *)&x, 4, tiledMap->file); + readb((char *)&y, 4, tiledMap->file); + long pointer = ftell(tiledMap->file); + // TODO casting to pointer here is maybe unsafe + kdtree_insert(&tiledMap->chunkTree, x, y, (char *) pointer); + fseek(tiledMap->file, chunkSizeBytes, SEEK_CUR); + } +} + +ChunkedTiledMap openTiledMap(char * filename) { + ChunkedTiledMap tiledMap; + + if (!(tiledMap.file = fopen(filename, "r+b"))) { + fprintf(stderr, "Failed to load %s\n", filename); + } + + FILE * file = tiledMap.file; + + // skip header + fseek(file, 10, SEEK_CUR); + // 4 bytes for int width + readb((char *)&tiledMap.chunkWidth, 4, file); + // 4 bytes for int height + readb((char *)&tiledMap.chunkHeight, 4, file); + + // read the pixel size of each tile + readb((char *)&tiledMap.tileSize, 4, file); + + // read the atlas size + readb((char *)&tiledMap.atlasSize[0], 4, file); + readb((char *)&tiledMap.atlasSize[1], 4, file); + + // read the atlas itself + 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); + + buildChunkTree(&tiledMap); + return tiledMap; +} + +char* loadChunk(ChunkedTiledMap tiledMap, int x, int y) { + // TODO add caching for this + size_t chunkSizeBytes = tiledMap.chunkWidth * tiledMap.chunkHeight; + char * chunk = malloc(chunkSizeBytes); + + long pos = (long) kdtree_search(tiledMap.chunkTree, x, y); + fseek(tiledMap.file, pos, SEEK_SET); + fread(chunk, 1, chunkSizeBytes, tiledMap.file); + return chunk; +} + +void appendChunk(ChunkedTiledMap *tiledMap, int x, int y, char * chunk) { + // TODO does this actually need tiledMap as a pointer + size_t chunkSizeBytes = tiledMap->chunkWidth * tiledMap->chunkHeight; + + fseek(tiledMap->file, 0, SEEK_END); + + // calculate position before writing + long pos = ftell(tiledMap->file) + 8; + kdtree_insert(&tiledMap->chunkTree, x, y, chunk); + + writeb((char *) &x, 4, tiledMap->file); + writeb((char *) &y, 4, tiledMap->file); + fwrite(chunk, 1, chunkSizeBytes, tiledMap->file); +} + + +void writeTiledMapHeader(ChunkedTiledMap tiledMap) { + FILE * file = tiledMap.file; + size_t atlasSizeBytes = tiledMap.atlasSize[0]*tiledMap.tileSize*tiledMap.atlasSize[1]*tiledMap.tileSize*4; + + // write header information from the start of the file + fseek(file, 0, SEEK_SET); + fwrite("TILEFILEv3", 10, 1, 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 +} + +ChunkedTiledMap newTiledMap(char * filename, Image atlas, int tileSize, int chunkWidth, int chunkHeight, int width, int height) { + ChunkedTiledMap 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); + + if (!(tiledMap.file = fopen(filename, "r+b"))) { + 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); + appendChunk(&tiledMap, x, y, chunk); + } + } + + return tiledMap; +} + +void closeTiledMap(ChunkedTiledMap tiledMap) { + kdtree_free(&tiledMap.chunkTree); + UnloadImageColors(tiledMap.atlasData); + fclose(tiledMap.file); +} + + + diff --git a/src/chunkedtiledmap.h b/src/chunkedtiledmap.h new file mode 100644 index 0000000..0d5c5d0 --- /dev/null +++ b/src/chunkedtiledmap.h @@ -0,0 +1,16 @@ +#include +#include "kdtree.h" + +const int i = 1; +#define is_bigendian() ( (*(char*)&i) == 0 ) + +typedef struct ChunkedTiledMap { + FILE * file; + int chunkWidth; + int chunkHeight; + int tileSize; + int atlasSize[2]; + int tileCount; + Color * atlasData; + kdtree_t * chunkTree; +} ChunkedTiledMap; diff --git a/src/kdtree.c b/src/kdtree.c index 6e9323b..ab5aa98 100644 --- a/src/kdtree.c +++ b/src/kdtree.c @@ -1,5 +1,3 @@ -#include - #include "kdtree.h" kdtree_t * kdtree_create(int x, int y, char * value) { @@ -75,11 +73,26 @@ void kdtree_walk(kdtree_t *root, void (* consume)(kdtree_t*)) { if (root == NULL) return; - if (root->left != NULL) - kdtree_walk(root->left, consume); - + kdtree_walk(root->left, consume); consume(root); + kdtree_walk(root->right, consume); +} + +int kdtree_size(kdtree_t *root) { + if (root == NULL) + return 0; - if (root->right != NULL) - kdtree_walk(root->right, consume); + return 1 + kdtree_size(root->left) + kdtree_size(root->right); +} + +int kdtree_fwrite(kdtree_t *root, FILE *file) { + return fwrite(root, 1, sizeof(kdtree_t), file) + | kdtree_fwrite(root->left, file) + | kdtree_fwrite(root->right, file); +} + +int kdtree_fread(kdtree_t **root, FILE *file) { + return fread(*root, 1, sizeof(kdtree_t), file) + | kdtree_fread(&(*root)->left, file) + | kdtree_fread(&(*root)->right, file); } diff --git a/src/kdtree.h b/src/kdtree.h index 8969533..e121442 100644 --- a/src/kdtree.h +++ b/src/kdtree.h @@ -1,3 +1,6 @@ +#include +#include + typedef struct KDTree { unsigned int x; unsigned int y; @@ -19,3 +22,12 @@ void kdtree_free(kdtree_t **root); //! in order walk of nodes in kdtree void kdtree_walk(kdtree_t *root, void (* consume)(kdtree_t*)); +//! get number of nodes in a kdtree +int kdtree_size(kdtree_t *root); + +//! write tree to a file +int kdtree_fwrite(kdtree_t *root, FILE *file); + +//! read a tree from file +int kdtree_fread(kdtree_t **root, FILE *file); + -- cgit v1.2.1