+1
include/keraforge.h
+1
include/keraforge.h
+472
include/keraforge/bini.h
+472
include/keraforge/bini.h
···
1
+
#ifndef __kf_bini__
2
+
#define __kf_bini__
3
+
4
+
/*
5
+
bini.h: endian-independent binary IO.
6
+
License: BSD 3-Clause. See EOF for license text.
7
+
8
+
Version: 1.0
9
+
10
+
Changelog:
11
+
1.0 (Dec 28, 2025): Initial version.
12
+
*/
13
+
14
+
/* Used to add functions for version-specific types. */
15
+
#define bini_c 1989
16
+
#ifdef __STDC_VERSION__
17
+
# undef bini_c
18
+
# if __STDC_VERSION__ == 199901
19
+
# define bini_c 1999
20
+
# elif __STDC_VERSION__ == 201112
21
+
# define bini_c 2011
22
+
# elif __STDC_VERSION__ == 201710
23
+
# define bini_c 2017
24
+
# elif __STDC_VERSION__ == 202000 || __STDC_VERSION__ == 202311
25
+
# define bini_c 2023
26
+
# else
27
+
# warning "bini.h: unknown C version. Falling back to C89."
28
+
# define bini_c 1989
29
+
# endif
30
+
#endif
31
+
32
+
33
+
#include <stdint.h>
34
+
#include <stddef.h>
35
+
#include <stdarg.h>
36
+
#include <stdio.h>
37
+
#if bini_c >= 1999
38
+
# include <stdbool.h>
39
+
#endif
40
+
41
+
42
+
#ifndef BINI_BUFSIZ
43
+
/* Starting size for bini_stream buffers. */
44
+
# define BINI_BUFSIZ 8192
45
+
#endif
46
+
47
+
#ifndef BINI_STRBUFSIZ
48
+
/* Starting size for bini_*print buffers. */
49
+
# define BINI_STRBUFSIZ 1024
50
+
#endif
51
+
52
+
53
+
/* Execute the `x` macro for each type that bini uses.
54
+
Excludes string types (char *). */
55
+
#ifndef BINI_X
56
+
# if bini_c >= 1999
57
+
# define BINI_X(x) \
58
+
x(char, c) \
59
+
x(short, s) \
60
+
x(int, i) \
61
+
x(long, l) \
62
+
x(long long, ll) \
63
+
x(unsigned char, cu) \
64
+
x(unsigned short, su) \
65
+
x(unsigned int, iu) \
66
+
x(unsigned long, lu) \
67
+
x(unsigned long long, llu) \
68
+
x(float, f) \
69
+
x(double, d) \
70
+
x(bool, b)
71
+
# else
72
+
# define BINI_X(x) \
73
+
x(char, c) \
74
+
x(short, s) \
75
+
x(int, i) \
76
+
x(long, l) \
77
+
x(unsigned char, cu) \
78
+
x(unsigned short, su) \
79
+
x(unsigned int, iu) \
80
+
x(unsigned long, lu) \
81
+
x(float, f) \
82
+
x(double, d)
83
+
# endif
84
+
#endif
85
+
86
+
87
+
enum { BINI_ERR, BINI_OK };
88
+
enum { BINI_BIGENDIAN, BINI_LITTLEENDIAN };
89
+
enum
90
+
{
91
+
/* Read values from the end of the buffer. */
92
+
BINI_STACK,
93
+
/* Read values from the start of the buffer. */
94
+
BINI_STREAM
95
+
};
96
+
97
+
98
+
/* Represents a binary IO stream. */
99
+
struct bini_stream
100
+
{
101
+
/* Read mode for the stream. Should be either BINI_STACK or BINI_STREAM. */
102
+
int mode;
103
+
size_t cap;
104
+
size_t len;
105
+
uint8_t *buffer;
106
+
};
107
+
108
+
109
+
int bini_getendian(void);
110
+
111
+
112
+
/* Initialize a bini_stream, allocating a buffer and
113
+
setting each field to its respective defaults. Also
114
+
see bini_new(). Free data using bini_end(). */
115
+
int bini_init(struct bini_stream *);
116
+
/* Create a new bini_stream, allocating a starting buffer
117
+
of BINI_BUFSIZ bytes. To use your own buffer, initialize
118
+
your own bini_stream and call bini_init() to fill in the
119
+
necessary fields. Free data using bini_close(). */
120
+
struct bini_stream *bini_new(void);
121
+
/* End a bini_stream, flushing it if need be and freeing
122
+
its buffer. */
123
+
void bini_end(struct bini_stream *);
124
+
/* End a bini_stream, flushing it if need be, freeing its
125
+
buffer, and frees the provided variable as well. */
126
+
void bini_close(struct bini_stream *bs);
127
+
128
+
/* Flush a binary stream, clearing its buffer. */
129
+
int bini_flush(struct bini_stream *);
130
+
/* Dump the binary stream's buffer to fp. */
131
+
void bini_dump(FILE *fp, struct bini_stream *bs);
132
+
133
+
/* Read data from a FILE into a binary stream. */
134
+
size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp);
135
+
/* Write data to a FILE into a binary stream. This will
136
+
NOT consume data from the stream! Stack/stream mode does
137
+
not affect this function.
138
+
To consume data, use `bs->len -= bini_fwrite(bs, ...);` */
139
+
size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp);
140
+
141
+
142
+
#define _bini_rwx(_type, _name) \
143
+
/* Write a `typeof(v)` to the stream. */ \
144
+
void bini_w##_name(struct bini_stream *bs, _type v); \
145
+
/* Read a `typeof(v)` from the stream. */ \
146
+
_type bini_r##_name(struct bini_stream *bs);
147
+
BINI_X(_bini_rwx)
148
+
#undef _bini_rwx
149
+
150
+
void bini_wstrn(struct bini_stream *, const char *, size_t);
151
+
void bini_wstr(struct bini_stream *, const char *);
152
+
153
+
size_t bini_rstr(struct bini_stream *, char *);
154
+
155
+
#if 0 /* unimplemented */
156
+
/* Write to a binary stream. */
157
+
int bini_vprint(struct bini_stream *bs, char *fmt, va_list va);
158
+
/* Write to a binary stream. */
159
+
int bini_print(struct bini_stream *bs, char *fmt, ...);
160
+
161
+
/* Read from a binary stream. */
162
+
int bini_vscan(struct bini_stream *bs, char *fmt, va_list va);
163
+
/* Read from a binary stream. */
164
+
int bini_scan(struct bini_stream *bs, char *fmt, ...);
165
+
#endif
166
+
167
+
168
+
/* Aliased names that are more akin to libc names. */
169
+
#if defined(bini_libc_names)
170
+
171
+
typedef struct bini_stream bstream;
172
+
173
+
# define bnew bini_new
174
+
# define binit bini_init
175
+
# define bend bini_end
176
+
# define bclose bini_close
177
+
178
+
# define bflush bini_flush
179
+
# define bdump bini_dump
180
+
181
+
# define bputc bini_wc
182
+
# define bputs bini_ws
183
+
# define bputi bini_wi
184
+
# define bputl bini_wl
185
+
# define bputll bini_wll
186
+
# define bputcu bini_wcu
187
+
# define bputsu bini_wsu
188
+
# define bputiu bini_wiu
189
+
# define bputlu bini_wlu
190
+
# define bputllu bini_wllu
191
+
# define bputf bini_wf
192
+
# define bputd bini_wd
193
+
# define bputb bini_wb
194
+
# define bputstr bini_wstr
195
+
# define bputstrn bini_wstrn
196
+
197
+
# define breadc bini_rc
198
+
# define breads bini_rs
199
+
# define breadi bini_ri
200
+
# define breadl bini_rl
201
+
# define breadll bini_rll
202
+
# define breadcu bini_rcu
203
+
# define breadsu bini_rsu
204
+
# define breadiu bini_riu
205
+
# define breadlu bini_rlu
206
+
# define breadllu bini_rllu
207
+
# define breadf bini_rf
208
+
# define breadd bini_rd
209
+
# define breadb bini_rb
210
+
# define breadstr bini_rstr
211
+
# define breadstrn bini_rstrn
212
+
213
+
#if 0
214
+
# define bfprint bini_vprint
215
+
# define bprint bini_print
216
+
# define bvscan bini_vscan
217
+
# define bscan bini_scan
218
+
#endif
219
+
220
+
#endif
221
+
222
+
223
+
#if defined(bini_impl)
224
+
225
+
226
+
#include <stdlib.h>
227
+
#include <string.h>
228
+
229
+
230
+
static
231
+
void _bini_dbg(char *fmt, ...)
232
+
{
233
+
# if bini_dbg
234
+
va_list ap;
235
+
va_start(ap, fmt);
236
+
vfprintf(stderr, fmt, ap);
237
+
va_end(ap);
238
+
# else
239
+
(void)fmt;
240
+
# endif
241
+
}
242
+
243
+
244
+
int bini_getendian(void)
245
+
{
246
+
const int n = 1;
247
+
if (*(char *)&n == 1)
248
+
return BINI_LITTLEENDIAN;
249
+
return BINI_BIGENDIAN;
250
+
}
251
+
252
+
253
+
int bini_init(struct bini_stream *bs)
254
+
{
255
+
bs->buffer = calloc(BINI_BUFSIZ, 1);
256
+
if (!bs->buffer)
257
+
return BINI_ERR;
258
+
bs->len = 0;
259
+
bs->cap = BINI_BUFSIZ;
260
+
bs->mode = BINI_STREAM;
261
+
return BINI_OK;
262
+
}
263
+
264
+
struct bini_stream *bini_new(void)
265
+
{
266
+
struct bini_stream *bs = malloc(sizeof(struct bini_stream));
267
+
bini_init(bs);
268
+
return bs;
269
+
}
270
+
271
+
void bini_end(struct bini_stream *bs)
272
+
{
273
+
bini_flush(bs);
274
+
free(bs->buffer);
275
+
}
276
+
277
+
void bini_close(struct bini_stream *bs)
278
+
{
279
+
bini_end(bs);
280
+
free(bs);
281
+
}
282
+
283
+
int bini_flush(struct bini_stream *bs)
284
+
{
285
+
size_t i;
286
+
for (i = 0 ; i < bs->len ; i++)
287
+
bs->buffer[i] = 0;
288
+
bs->len = 0;
289
+
return BINI_OK;
290
+
}
291
+
292
+
void bini_dump(FILE *fp, struct bini_stream *bs)
293
+
{
294
+
size_t i;
295
+
fprintf(fp, "bini buffer dump:\n");
296
+
fprintf(fp, "buffer: %lu/%lu bytes\n", bs->len, bs->cap);
297
+
fprintf(fp, "---\n");
298
+
for (i = 0 ; i < bs->len ; i++)
299
+
fprintf(fp, ". %02x '%c'\n", bs->buffer[i], bs->buffer[i]);
300
+
fprintf(fp, "---\n");
301
+
}
302
+
303
+
static
304
+
#if bini_c >= 1999
305
+
inline
306
+
#endif
307
+
int _bini_grow(struct bini_stream *bs, size_t n)
308
+
{
309
+
uint8_t *newbuf;
310
+
if (bs->len + n < bs->cap)
311
+
return BINI_OK;
312
+
while (bs->len + n >= bs->cap)
313
+
{
314
+
newbuf = realloc(bs->buffer, bs->cap * 2);
315
+
if (!newbuf)
316
+
return BINI_ERR;
317
+
bs->buffer = newbuf;
318
+
bs->cap *= 2;
319
+
}
320
+
return BINI_OK;
321
+
}
322
+
323
+
size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp)
324
+
{
325
+
size_t nread;
326
+
_bini_grow(bs, size*n);
327
+
nread = fread(bs->buffer, size, n, fp);
328
+
bs->len += nread;
329
+
return nread;
330
+
}
331
+
332
+
size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp)
333
+
{
334
+
return fwrite(bs->buffer, size, n, fp);
335
+
}
336
+
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
+
#define _bini_rwx(_type, _name) \
364
+
void bini_w##_name(struct bini_stream *bs, _type v) \
365
+
{ \
366
+
size_t i; \
367
+
uint8_t *bytes; \
368
+
int l = bini_getendian() == BINI_LITTLEENDIAN; \
369
+
_bini_dbg("%s: %d (%lu bytes)\n", __FUNCTION__, *(int *)&v, sizeof(v)); \
370
+
_bini_grow(bs, sizeof(v)); \
371
+
bytes = (uint8_t *)&v; \
372
+
for (i = 0 ; i < sizeof(v) ; i++) \
373
+
{ \
374
+
_bini_dbg("%s: byte index [%lu] (= %02x '%c') to buffer index %lu\n", \
375
+
__FUNCTION__, \
376
+
l ? sizeof(v)-i-1 : i, \
377
+
bytes[l ? sizeof(v)-i-1 : i], \
378
+
bytes[l ? sizeof(v)-i-1 : i], \
379
+
bs->len+i); \
380
+
bs->buffer[bs->len+i] = bytes[l ? sizeof(v)-i-1 : i]; \
381
+
} \
382
+
bs->len += i; \
383
+
_bini_dbg("-> %s: %d (%lu bytes)\n", __FUNCTION__, *(int *)bytes, sizeof(v)); \
384
+
} \
385
+
_type bini_r##_name(struct bini_stream *bs) \
386
+
{ \
387
+
size_t i; \
388
+
_type v; \
389
+
uint8_t *bytes = (uint8_t *)&v; \
390
+
int l = bini_getendian() == BINI_LITTLEENDIAN; \
391
+
int m = bs->mode == BINI_STREAM; \
392
+
for (i = 0 ; i < sizeof(v) ; i++) \
393
+
{ \
394
+
_bini_dbg("%s: byte index [%lu] from buffer index %lu (= %02x '%c')\n", \
395
+
__FUNCTION__, \
396
+
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)]; \
401
+
} \
402
+
if (m) \
403
+
_bini_shiftleft(bs, i); \
404
+
bs->len -= i; \
405
+
return v; \
406
+
}
407
+
BINI_X(_bini_rwx)
408
+
#undef _bini_rwx
409
+
410
+
411
+
void bini_wstrn(struct bini_stream *bs, const char *str, size_t n)
412
+
{
413
+
bini_wlu(bs, n);
414
+
_bini_grow(bs, n);
415
+
while (str)
416
+
{
417
+
bini_wc(bs, *str);
418
+
str++;
419
+
}
420
+
}
421
+
422
+
void bini_wstr(struct bini_stream *bs, const char *str)
423
+
{
424
+
size_t i, n = strlen(str);
425
+
char *s = (char *)str;
426
+
bini_wlu(bs, n);
427
+
_bini_grow(bs, n);
428
+
for (i = 0 ; i < n ; i++)
429
+
bini_wc(bs, s[i]);
430
+
}
431
+
432
+
size_t bini_rstr(struct bini_stream *bs, char *str)
433
+
{
434
+
size_t i, n = bini_rlu(bs);
435
+
for (i = 0 ; i < n ; i++)
436
+
str[i] = bini_rc(bs);
437
+
return n;
438
+
}
439
+
440
+
/*
441
+
Bini license: BSD 3-Clause.
442
+
443
+
Copyright 2025 Emmeline Coats
444
+
445
+
Redistribution and use in source and binary forms, with or without
446
+
modification, are permitted provided that the following conditions are met:
447
+
448
+
1. Redistributions of source code must retain the above copyright notice, this
449
+
list of conditions and the following disclaimer.
450
+
451
+
2. Redistributions in binary form must reproduce the above copyright notice,
452
+
this list of conditions and the following disclaimer in the documentation
453
+
and/or other materials provided with the distribution.
454
+
455
+
3. Neither the name of the copyright holder nor the names of its contributors
456
+
may be used to endorse or promote products derived from this software
457
+
without specific prior written permission.
458
+
459
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
460
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
461
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
462
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
463
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
464
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
465
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
466
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
467
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
468
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
469
+
*/
470
+
471
+
#endif /* bini_impl */
472
+
#endif /* __bini__ */
+46
-6
src/world.c
+46
-6
src/world.c
···
1
+
#include "keraforge/bini.h"
1
2
#include "keraforge/fs.h"
2
3
#include <keraforge.h>
3
4
#include <raylib.h>
···
224
225
#define _KF_MAPFILE_XZ "data/map.bin.xz"
225
226
#define _KF_MAPFILE "data/map.bin"
226
227
227
-
int kf_world_save(struct kf_world *world, bool compress)
228
+
static
229
+
void _kf_world_save_bs(struct kf_world *world, struct bini_stream *bs)
228
230
{
229
-
char *outfile = compress ? _KF_MAPFILE_TMP : _KF_MAPFILE;
230
-
if (!kf_writebin(outfile, (u8 *)world, kf_world_getsize(world)))
231
+
bini_wiu(bs, world->revision);
232
+
bini_wiu(bs, world->width);
233
+
bini_wiu(bs, world->height);
234
+
struct kf_tile *t = &world->map[0];
235
+
for (size_t i = 0 ; i < world->width*world->height ; i++)
231
236
{
232
-
KF_THROW("failed to write to %s", outfile);
233
-
return 0; /* unreachable */
237
+
bini_wsu(bs, t->subid);
238
+
bini_wsu(bs, t->id);
239
+
bini_wcu(bs, t->data);
240
+
t++;
234
241
}
242
+
}
243
+
244
+
int kf_world_save(struct kf_world *world, bool compress)
245
+
{
246
+
char *outfile = compress ? _KF_MAPFILE_TMP : _KF_MAPFILE;
247
+
struct bini_stream *bs = bini_new();
248
+
_kf_world_save_bs(world, bs);
249
+
if (kf_writebin(outfile, bs->buffer, bs->len))
250
+
KF_THROW("failed to open %s", outfile);
251
+
bini_close(bs);
235
252
236
253
if (compress)
237
254
{
···
247
264
return 1;
248
265
}
249
266
267
+
static
268
+
void _kf_world_load_bs(struct kf_world *world, struct bini_stream *bs)
269
+
{
270
+
world->revision = bini_riu(bs);
271
+
world->width = bini_riu(bs);
272
+
world->height = bini_riu(bs);
273
+
struct kf_tile *t = &world->map[0];
274
+
for (size_t i = 0 ; i < world->width*world->height ; i++)
275
+
{
276
+
t->subid = bini_rsu(bs);
277
+
t->id = bini_rsu(bs);
278
+
t->data = bini_rcu(bs);
279
+
t++;
280
+
}
281
+
}
282
+
250
283
int kf_world_load(struct kf_world **pworld, bool compressed)
251
284
{
252
285
if (compressed) /* decompress before loading */
···
269
302
kf_logdbg("loading world: %s", infile);
270
303
271
304
size_t len = 0;
272
-
*pworld = (struct kf_world *)kf_readbin(infile, &len);
305
+
struct bini_stream *bs = bini_new();
306
+
free(bs->buffer);
307
+
bs->buffer = kf_readbin(infile, &len);
308
+
if (!bs->buffer)
309
+
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);
273
313
kf_logdbg("loaded world (%p): r=%d, wh=%dx%d, len=%lu", *pworld, (*pworld)->revision, (*pworld)->width, (*pworld)->height, len);
274
314
275
315
if (compressed)