A game engine for top-down 2D RPG games.
rpg game-engine raylib c99

Use bini for map (de)serialization

Changed files
+109 -55
include
keraforge
scripts
src
tools
+2
compile_flags.txt
··· 8 8 9 9 -DKF_SANITY_CHECKS 10 10 -DKF_GNU 11 + -Dbini_impl 12 + -Dbini_dbg
+62 -34
include/keraforge/bini.h
··· 5 5 bini.h: endian-independent binary IO. 6 6 License: BSD 3-Clause. See EOF for license text. 7 7 8 - Version: 1.0 8 + Version: 1.1 9 9 10 10 Changelog: 11 11 1.0 (Dec 28, 2025): Initial version. 12 + 1.1 (Dec 30, 2025): Use an offset instead of shifting the 13 + byte buffer so carelessly. _bini_grow will shift the 14 + buffer on-demand instead so as to conserve CPU time. 12 15 */ 13 16 14 17 /* Used to add functions for version-specific types. */ ··· 100 103 { 101 104 /* Read mode for the stream. Should be either BINI_STACK or BINI_STREAM. */ 102 105 int mode; 106 + /* Offset from the start of the buffer in bytes. Only used for BINI_STREAM. */ 107 + size_t off; 103 108 size_t cap; 104 109 size_t len; 105 110 uint8_t *buffer; ··· 125 130 buffer, and frees the provided variable as well. */ 126 131 void bini_close(struct bini_stream *bs); 127 132 133 + /* Shift a binary stream, resetting the offset and shifting the array. */ 134 + void bini_shift(struct bini_stream *); 128 135 /* Flush a binary stream, clearing its buffer. */ 129 136 int bini_flush(struct bini_stream *); 130 137 /* Dump the binary stream's buffer to fp. */ ··· 255 262 bs->buffer = calloc(BINI_BUFSIZ, 1); 256 263 if (!bs->buffer) 257 264 return BINI_ERR; 265 + bs->off = 0; 258 266 bs->len = 0; 259 267 bs->cap = BINI_BUFSIZ; 260 268 bs->mode = BINI_STREAM; ··· 280 288 free(bs); 281 289 } 282 290 291 + static 292 + #if bini_c >= 1999 293 + inline 294 + #endif 295 + void _bini_shiftleft(struct bini_stream *bs, size_t n) 296 + { 297 + size_t i; 298 + _bini_dbg("shift %d: (len=%lu)\n", n, bs->len); 299 + for (i = 0 ; i < bs->len ; i++) 300 + { 301 + if (i+n >= bs->len) 302 + { 303 + _bini_dbg(" %d: [%d] (out of bounds) to [%d]\n", n, i+n, i); 304 + bs->buffer[i] = 0; 305 + continue; 306 + } 307 + _bini_dbg(" %d: [%d] (%02x '%c') to [%d]\n", 308 + n, 309 + i+n, 310 + bs->buffer[i+n], 311 + bs->buffer[i+n], 312 + i); 313 + bs->buffer[i] = bs->buffer[i+n]; 314 + } 315 + } 316 + 317 + void bini_shift(struct bini_stream *bs) 318 + { 319 + _bini_shiftleft(bs, bs->off); 320 + bs->off = 0; 321 + } 322 + 283 323 int bini_flush(struct bini_stream *bs) 284 324 { 285 325 size_t i; ··· 307 347 int _bini_grow(struct bini_stream *bs, size_t n) 308 348 { 309 349 uint8_t *newbuf; 350 + 351 + if (bs->off + bs->len + n < bs->cap) 352 + return BINI_OK; 353 + 354 + /* Shifting the array could give enough space to 355 + fit `n`, avoiding the need to realloc. */ 356 + bini_shift(bs); 357 + 310 358 if (bs->len + n < bs->cap) 311 359 return BINI_OK; 360 + 312 361 while (bs->len + n >= bs->cap) 313 362 { 314 363 newbuf = realloc(bs->buffer, bs->cap * 2); ··· 317 366 bs->buffer = newbuf; 318 367 bs->cap *= 2; 319 368 } 369 + 320 370 return BINI_OK; 321 371 } 322 372 ··· 334 384 return fwrite(bs->buffer, size, n, fp); 335 385 } 336 386 337 - static 338 - #if bini_c >= 1999 339 - inline 340 - #endif 341 - void _bini_shiftleft(struct bini_stream *bs, size_t n) 342 - { 343 - size_t i; 344 - _bini_dbg("shift %d: (len=%lu)\n", n, bs->len); 345 - for (i = 0 ; i < bs->len ; i++) 346 - { 347 - if (i+n >= bs->len) 348 - { 349 - _bini_dbg(" %d: [%d] (out of bounds) to [%d]\n", n, i+n, i); 350 - bs->buffer[i] = 0; 351 - continue; 352 - } 353 - _bini_dbg(" %d: [%d] (%02x '%c') to [%d]\n", 354 - n, 355 - i+n, 356 - bs->buffer[i+n], 357 - bs->buffer[i+n], 358 - i); 359 - bs->buffer[i] = bs->buffer[i+n]; 360 - } 361 - } 362 - 363 387 #define _bini_rwx(_type, _name) \ 364 388 void bini_w##_name(struct bini_stream *bs, _type v) \ 365 389 { \ ··· 376 400 l ? sizeof(v)-i-1 : i, \ 377 401 bytes[l ? sizeof(v)-i-1 : i], \ 378 402 bytes[l ? sizeof(v)-i-1 : i], \ 379 - bs->len+i); \ 380 - bs->buffer[bs->len+i] = bytes[l ? sizeof(v)-i-1 : i]; \ 403 + bs->off+bs->len+i); \ 404 + bs->buffer[bs->off+bs->len+i] = bytes[l ? sizeof(v)-i-1 : i]; \ 381 405 } \ 382 406 bs->len += i; \ 383 407 _bini_dbg("-> %s: %d (%lu bytes)\n", __FUNCTION__, *(int *)bytes, sizeof(v)); \ ··· 391 415 int m = bs->mode == BINI_STREAM; \ 392 416 for (i = 0 ; i < sizeof(v) ; i++) \ 393 417 { \ 418 + const size_t p = bs->off + (m ? i : (bs->len-sizeof(v)+i)); \ 394 419 _bini_dbg("%s: byte index [%lu] from buffer index %lu (= %02x '%c')\n", \ 395 420 __FUNCTION__, \ 396 421 l ? sizeof(v)-i-1 : i, \ 397 - m ? i : (bs->len-sizeof(v)+i), \ 398 - bs->buffer[m ? i : (bs->len-sizeof(v)+i)], \ 399 - bs->buffer[m ? i : (bs->len-sizeof(v)+i)]); \ 400 - bytes[l ? sizeof(v)-i-1 : i] = bs->buffer[m ? i : (bs->len-sizeof(v)+i)]; \ 422 + p, \ 423 + bs->buffer[p], \ 424 + bs->buffer[p]); \ 425 + bytes[l ? sizeof(v)-i-1 : i] = bs->buffer[p]; \ 401 426 } \ 402 427 if (m) \ 403 - _bini_shiftleft(bs, i); \ 428 + { \ 429 + bs->off += i; \ 430 + /* _bini_shiftleft(bs, i); */ \ 431 + } \ 404 432 bs->len -= i; \ 405 433 return v; \ 406 434 }
+10 -4
include/keraforge/world.h
··· 31 31 32 32 33 33 /* Represents a singular tile in the world. */ 34 + // struct kf_tile 35 + // { 36 + // kf_tileid_t subid : KF_TILE_IDWIDTH; 37 + // kf_tileid_t id : KF_TILE_IDWIDTH; 38 + // kf_tiledatum_t data : KF_TILE_DATAWIDTH; 39 + // } __attribute__((packed)); 34 40 struct kf_tile 35 41 { 36 - kf_tileid_t subid : KF_TILE_IDWIDTH; 37 - kf_tileid_t id : KF_TILE_IDWIDTH; 38 - kf_tiledatum_t data : KF_TILE_DATAWIDTH; 39 - } __attribute__((packed)); 42 + kf_tileid_t subid ;//: KF_TILE_IDWIDTH; 43 + kf_tileid_t id ;//: KF_TILE_IDWIDTH; 44 + kf_tiledatum_t data ;//: KF_TILE_DATAWIDTH; 45 + };// __attribute__((packed)); 40 46 41 47 /* Represents a world (or a subworld, often called "rooms"). */ 42 48 struct kf_world
+10
scripts/run-valgrind.sh
··· 1 + #!/usr/bin/env sh 2 + set -e 3 + 4 + if [ -e "./build/keraforge" ] 5 + then 6 + valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all -s ./build/keraforge 7 + else 8 + echo ": error: no build available" 9 + exit 1 10 + fi
+1 -1
src/editor.c
··· 21 21 kf_logdbg("exiting editor, world is %s", d ? "dirty" : "not dirty"); 22 22 23 23 if (d) 24 - kf_world_save(kf_window.room, true); 24 + kf_world_save(kf_window.room, false); 25 25 26 26 free(kf_window.modal->data); 27 27 kf_window.modal->data = NULL;
+1 -1
src/graphics.c
··· 47 47 kf_window.state = state; 48 48 49 49 struct kf_world *world = NULL; 50 - kf_world_load(&world, true); 50 + kf_world_load(&world, false); 51 51 kf_window.room = world; 52 52 53 53 kf_actorregistry.id[0] = "player";
+20 -13
src/world.c
··· 1 1 #include "keraforge/bini.h" 2 - #include "keraforge/fs.h" 3 2 #include <keraforge.h> 4 3 #include <raylib.h> 5 4 #include <stdio.h> ··· 246 245 char *outfile = compress ? _KF_MAPFILE_TMP : _KF_MAPFILE; 247 246 struct bini_stream *bs = bini_new(); 248 247 _kf_world_save_bs(world, bs); 249 - if (kf_writebin(outfile, bs->buffer, bs->len)) 248 + if (!kf_writebin(outfile, bs->buffer, bs->len)) 250 249 KF_THROW("failed to open %s", outfile); 251 250 bini_close(bs); 252 251 ··· 265 264 } 266 265 267 266 static 268 - void _kf_world_load_bs(struct kf_world *world, struct bini_stream *bs) 267 + void _kf_world_load_bs(struct kf_world **pworld, struct bini_stream *bs) 269 268 { 270 - world->revision = bini_riu(bs); 271 - world->width = bini_riu(bs); 272 - world->height = bini_riu(bs); 269 + u32 r = bini_riu(bs); 270 + u32 w = bini_riu(bs); 271 + u32 h = bini_riu(bs); 272 + struct kf_world *world = malloc(sizeof(*world) + sizeof(struct kf_tile)*w*h); 273 + world->revision = r; 274 + world->width = w; 275 + world->height = h; 273 276 struct kf_tile *t = &world->map[0]; 274 277 for (size_t i = 0 ; i < world->width*world->height ; i++) 275 278 { ··· 278 281 t->data = bini_rcu(bs); 279 282 t++; 280 283 } 284 + *pworld = world; 281 285 } 282 286 283 287 int kf_world_load(struct kf_world **pworld, bool compressed) ··· 302 306 kf_logdbg("loading world: %s", infile); 303 307 304 308 size_t len = 0; 305 - struct bini_stream *bs = bini_new(); 306 - free(bs->buffer); 307 - bs->buffer = kf_readbin(infile, &len); 308 - if (!bs->buffer) 309 + struct bini_stream bs = { 310 + .mode = BINI_STREAM, 311 + .buffer = kf_readbin(infile, &len), 312 + }; 313 + if (!bs.buffer) 309 314 KF_THROW("failed to read/open %s", infile); 310 - bs->len = bs->cap = len; 311 - _kf_world_load_bs(*pworld, bs); 312 - bini_close(bs); 315 + bs.cap = len; 316 + bs.len = len; 317 + kf_logdbg("loaded world into binary stream: len=%lu", len); 318 + _kf_world_load_bs(pworld, &bs); 319 + free(bs.buffer); 313 320 kf_logdbg("loaded world (%p): r=%d, wh=%dx%d, len=%lu", *pworld, (*pworld)->revision, (*pworld)->width, (*pworld)->height, len); 314 321 315 322 if (compressed)
+3 -2
tools/newgame.c
··· 83 83 84 84 size_t len = kf_world_getsize(world); 85 85 kf_loginfo("saving world (%lu bytes uncompressed)", len); 86 - if (!kf_writebin(worldpath, (u8 *)world, len)) 87 - KF_THROW("failed to save %s", worldpath); 86 + kf_world_save(world, compress); 87 + // if (!kf_writebin(worldpath, (u8 *)world, len)) 88 + // KF_THROW("failed to save %s", worldpath); 88 89 89 90 if (compress) 90 91 {