summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidovski <david@davidovski.xyz>2023-07-21 01:53:48 +0200
committerdavidovski <david@davidovski.xyz>2023-07-21 01:53:48 +0200
commit2dcefdb4565f96b808ed01ce1d96a802b8f73931 (patch)
tree4776446d3040fbf97b09ef4abda6be816d3c7c67
parent1aceb70c5659677929bfbbd3f6480bacd8d75e35 (diff)
add chunked tiledmap io
-rw-r--r--src/chunkedtiledmap.c173
-rw-r--r--src/chunkedtiledmap.h16
-rw-r--r--src/kdtree.c27
-rw-r--r--src/kdtree.h12
4 files changed, 221 insertions, 7 deletions
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 <raylib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <wchar.h>
+
+#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 <raylib.h>
+#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 <stdlib.h>
-
#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 <stdio.h>
+#include <stdlib.h>
+
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);
+