+3
include/keraforge.h
+3
include/keraforge.h
-32
include/keraforge/_header.h
-32
include/keraforge/_header.h
···
16
16
#endif
17
17
18
18
19
-
#ifdef KF_SANITY_CHECKS
20
-
# include <stdio.h> /* fprintf, stderr */
21
-
# include <stdlib.h> /* exit */
22
-
# define KF_SANITY_CHECK(EXPR, ...) \
23
-
do \
24
-
{ \
25
-
if (!(EXPR)) \
26
-
{ \
27
-
fprintf(stderr, "\x1b[1;31msanity check failed: \x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s: \x1b[0m", __FILE__, __LINE__, __FUNCTION_NAME__); \
28
-
fprintf(stderr, __VA_ARGS__); \
29
-
fprintf(stderr, "\n"); \
30
-
kf_printbacktrace(stderr); \
31
-
exit(1); \
32
-
} \
33
-
} \
34
-
while (0)
35
-
# define KF_UNREACHABLE(...) \
36
-
do \
37
-
{ \
38
-
fprintf(stderr, "\x1b[1;31munreachable: \x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s: \x1b[0m", __FILE__, __LINE__, __FUNCTION_NAME__); \
39
-
fprintf(stderr, __VA_ARGS__); \
40
-
fprintf(stderr, "\n"); \
41
-
kf_printbacktrace(stderr); \
42
-
exit(1); \
43
-
} \
44
-
while (0)
45
-
#else
46
-
# define KF_SANITY_CHECK(EXPR, ...) do { ; } while (0)
47
-
# define KF_UNREACHABLE(...) do { ; } while (0)
48
-
#endif
49
-
50
-
51
19
typedef int8_t i8;
52
20
typedef int16_t i16;
53
21
typedef int32_t i32;
+5
include/keraforge/fs.h
+5
include/keraforge/fs.h
···
13
13
/* Write binary file contents. */
14
14
int kf_writebin(char *filename, u8 *data, size_t len);
15
15
16
+
/* Compress a file using LZMA (XZ). */
17
+
int kf_compress(char *infile, char *outfile);
18
+
/* Decompress a file using LZMA (XZ). */
19
+
int kf_decompress(char *infile, char *outfile);
20
+
16
21
17
22
#endif
+65
include/keraforge/log.h
+65
include/keraforge/log.h
···
1
+
#ifndef __kf_log__
2
+
#define __kf_log__
3
+
4
+
#include <stdio.h> /* fprintf, stderr */
5
+
#include <stdlib.h> /* exit */
6
+
#include <stdarg.h>
7
+
8
+
void kf_vlog(char *level, char *fmt, va_list va);
9
+
void kf_log(char *level, char *fmt, ...);
10
+
void kf_logdbg(char *fmt, ...);
11
+
void kf_loginfo(char *fmt, ...);
12
+
void kf_logerr(char *fmt, ...);
13
+
14
+
/* Errors */
15
+
16
+
/* Throw a formatted error message without printing a traceback or exiting. */
17
+
#define KF_THROWSOFTER(MSG, ...) \
18
+
kf_logerr("\x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s: \x1b[0m" MSG, __FILE__, __LINE__, __FUNCTION_NAME__, __VA_ARGS__)
19
+
20
+
/* Throw a formatted error message and print a traceback if available. */
21
+
#define KF_THROWSOFT(MSG, ...) \
22
+
do \
23
+
{ \
24
+
kf_logerr("\x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s: \x1b[0m" MSG, __FILE__, __LINE__, __FUNCTION_NAME__, __VA_ARGS__); \
25
+
kf_printbacktrace(stderr); \
26
+
} \
27
+
while (0)
28
+
29
+
/* Throw a formatted error message, print a traceback if available, and aborts. */
30
+
#define KF_THROW(MSG, ...) \
31
+
do \
32
+
{ \
33
+
kf_logerr("\x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s: \x1b[0m" MSG, __FILE__, __LINE__, __FUNCTION_NAME__ __VA_OPT__(,) __VA_ARGS__); \
34
+
kf_printbacktrace(stderr); \
35
+
abort(); \
36
+
} \
37
+
while (0)
38
+
39
+
/* Sanity Checking */
40
+
41
+
#ifdef KF_SANITY_CHECKS
42
+
/* Indicate that the given expression should never resolve to false and throw an error if it manages to. */
43
+
# define KF_SANITY_CHECK(EXPR, MSG, ...) \
44
+
do \
45
+
{ \
46
+
if (!(EXPR)) \
47
+
{ \
48
+
KF_THROW("sanity check failed: \x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s:\x1b[0m " MSG, __FILE__, __LINE__, __FUNCTION_NAME__ __VA_OPT__(,) __VA_ARGS__); \
49
+
} \
50
+
} \
51
+
while (0)
52
+
53
+
/* Indicate that this branch of code should never be reached. Throws an error if it ever manages to. */
54
+
# define KF_UNREACHABLE(MSG, ...) \
55
+
do \
56
+
{ \
57
+
KF_THROW("unreachable: \x1b[0;34m%s:%d\x1b[0m in \x1b[34m%s:\x1b[0m " MSG, __FILE__, __LINE__, __FUNCTION_NAME__ __VA_OPT__(,) __VA_ARGS__); \
58
+
} \
59
+
while (0)
60
+
#else
61
+
# define KF_SANITY_CHECK(EXPR, MSG, ...) do { ; } while (0)
62
+
# define KF_UNREACHABLE(MSG, ...) do { ; } while (0)
63
+
#endif
64
+
65
+
#endif
+5
include/keraforge/world.h
+5
include/keraforge/world.h
···
104
104
/* Draw the part of the world visible to the given camera. */
105
105
void kf_world_draw(struct kf_world *world, Camera2D camera);
106
106
107
+
/* Save a world to map.bin(.xz). */
108
+
int kf_world_save(struct kf_world *world, bool compress);
109
+
/* Load a world from a map.bin(.xz). */
110
+
int kf_world_load(struct kf_world **world, bool compressed);
111
+
107
112
#endif
+1
-1
scripts/_config.sh
+1
-1
scripts/_config.sh
···
11
11
export KF_DEBUG_LFLAGS="-g -rdynamic"
12
12
13
13
export CFLAGS="-Wall -Wextra -Werror -std=c99 -Iinclude/ -c -DKF_GNU $KF_DEBUG_CFLAGS"
14
-
export LFLAGS="-lraylib -lm -lGL -lpthread -ldl -lrt -lX11 $KF_DEBUG_LFLAGS"
14
+
export LFLAGS="-lraylib -lm -lGL -lpthread -ldl -lrt -lX11 -llzma $KF_DEBUG_LFLAGS"
+27
scripts/build-tools.sh
+27
scripts/build-tools.sh
···
1
+
#!/usr/bin/env sh
2
+
set -e
3
+
4
+
. scripts/_config.sh
5
+
6
+
mkdir -p build/tools/
7
+
8
+
echo ": compiling keraforge"
9
+
for f in `find src/ -name '*.c'`
10
+
do
11
+
ff=$(echo "$f" | tr '/' '_')
12
+
gcc $CFLAGS $f -o build/${ff%.c}.o
13
+
done
14
+
15
+
echo ": compiling tools"
16
+
for f in `find tools/ -name '*.c'`
17
+
do
18
+
ff=$(echo "$f" | tr '/' '_')
19
+
o=build/tools/${ff%.c}.o
20
+
echo ": tool: $f->build/tools/${ff%.c}"
21
+
gcc $CFLAGS $f -o $o
22
+
gcc \
23
+
-o build/tools/${ff%.c} \
24
+
`find build -maxdepth 1 -name '*.o' ! -name '*src_main.o'` \
25
+
$o \
26
+
$LFLAGS
27
+
done
+127
src/fs.c
+127
src/fs.c
···
1
1
#include <keraforge.h>
2
2
#include <stdio.h>
3
3
#include <stdlib.h>
4
+
#include <string.h>
5
+
#include <errno.h>
6
+
#include <lzma.h>
4
7
5
8
6
9
int kf_exists(char *filename)
···
49
52
50
53
return 1;
51
54
}
55
+
56
+
static
57
+
int _kf_compress(lzma_stream *s, FILE *ifp, FILE *ofp)
58
+
{
59
+
lzma_action a = LZMA_RUN;
60
+
u8 inbuf[BUFSIZ], outbuf[BUFSIZ];
61
+
62
+
s->next_in = NULL;
63
+
s->avail_in = 0;
64
+
s->next_out = outbuf;
65
+
s->avail_out = sizeof(outbuf);
66
+
67
+
for (;;)
68
+
{
69
+
if (s->avail_in == 0 && !feof(ifp))
70
+
{
71
+
s->next_in = inbuf;
72
+
s->avail_in = fread(inbuf, 1, sizeof(inbuf), ifp);
73
+
74
+
if (ferror(ifp))
75
+
{
76
+
KF_THROW("read error: %s", strerror(errno));
77
+
return 0; /* unreachable */
78
+
}
79
+
80
+
if (feof(ifp))
81
+
a = LZMA_FINISH;
82
+
}
83
+
84
+
lzma_ret r = lzma_code(s, a);
85
+
86
+
if (s->avail_out == 0 || r == LZMA_STREAM_END)
87
+
{
88
+
size_t nwrote = sizeof(outbuf) - s->avail_out;
89
+
90
+
if (fwrite(outbuf, 1, nwrote, ofp) != nwrote)
91
+
{
92
+
KF_THROW("write error: %s", strerror(errno));
93
+
return 0; /* unreachable */
94
+
}
95
+
96
+
s->next_out = outbuf;
97
+
s->avail_out = sizeof(outbuf);
98
+
}
99
+
100
+
if (r != LZMA_OK)
101
+
{
102
+
if (r == LZMA_STREAM_END)
103
+
return 1;
104
+
105
+
KF_THROW("lzma compression error: code=%d", r);
106
+
return 0; /* unreachable */
107
+
}
108
+
}
109
+
110
+
KF_UNREACHABLE("broke out of for(;;) loop");
111
+
return 0; /* unreachable */
112
+
}
113
+
114
+
int kf_compress(char *infile, char *outfile)
115
+
{
116
+
FILE *ifp = fopen(infile, "r");
117
+
if (!ifp)
118
+
{
119
+
KF_THROW("failed to open input file: %s", infile);
120
+
return 0; /* unreachable */
121
+
}
122
+
FILE *ofp = fopen(outfile, "w");
123
+
if (!ofp)
124
+
{
125
+
fclose(ifp);
126
+
KF_THROW("failed to open output file: %s", outfile);
127
+
return 0; /* unreachable */
128
+
}
129
+
130
+
lzma_stream s = LZMA_STREAM_INIT;
131
+
lzma_ret r = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
132
+
if (r != LZMA_OK)
133
+
{
134
+
KF_THROW("failed to initialize lzma encoder: code=%d\n", r);
135
+
return 0; /* unreachable */
136
+
}
137
+
138
+
int res = _kf_compress(&s, ifp, ofp);
139
+
140
+
lzma_end(&s);
141
+
fclose(ifp);
142
+
fclose(ofp);
143
+
144
+
return res;
145
+
}
146
+
147
+
int kf_decompress(char *infile, char *outfile)
148
+
{
149
+
FILE *ifp = fopen(infile, "r");
150
+
if (!ifp)
151
+
{
152
+
KF_THROW("failed to open input file: %s", infile);
153
+
return 0; /* unreachable */
154
+
}
155
+
FILE *ofp = fopen(outfile, "w");
156
+
if (!ofp)
157
+
{
158
+
fclose(ifp);
159
+
KF_THROW("failed to open output file: %s", outfile);
160
+
return 0; /* unreachable */
161
+
}
162
+
163
+
lzma_stream s = LZMA_STREAM_INIT;
164
+
lzma_ret r = lzma_stream_decoder(&s, UINT64_MAX, LZMA_CONCATENATED);
165
+
if (r != LZMA_OK)
166
+
{
167
+
KF_THROW("failed to initialize lzma decoder: code=%d\n", r);
168
+
return 0; /* unreachable */
169
+
}
170
+
171
+
int res = _kf_compress(&s, ifp, ofp);
172
+
173
+
lzma_end(&s);
174
+
fclose(ifp);
175
+
fclose(ofp);
176
+
177
+
return res;
178
+
}
+41
src/log.c
+41
src/log.c
···
1
+
#include <keraforge.h>
2
+
3
+
void kf_vlog(char *level, char *fmt, va_list va)
4
+
{
5
+
fprintf(stderr, "\x1b[0;1m-> %s\x1b[0;1m:\x1b[0m ", level);
6
+
vfprintf(stderr, fmt, va);
7
+
fprintf(stderr, "\n");
8
+
}
9
+
10
+
void kf_log(char *level, char *fmt, ...)
11
+
{
12
+
va_list va;
13
+
va_start(va, fmt);
14
+
kf_vlog(level, fmt, va);
15
+
va_end(va);
16
+
}
17
+
18
+
void kf_logdbg(char *fmt, ...)
19
+
{
20
+
va_list va;
21
+
va_start(va, fmt);
22
+
kf_vlog("\x1b[1;33mdbg", fmt, va);
23
+
va_end(va);
24
+
}
25
+
26
+
void kf_loginfo(char *fmt, ...)
27
+
{
28
+
va_list va;
29
+
va_start(va, fmt);
30
+
kf_vlog("\x1b[1;34minfo", fmt, va);
31
+
va_end(va);
32
+
}
33
+
34
+
void kf_logerr(char *fmt, ...)
35
+
{
36
+
va_list va;
37
+
va_start(va, fmt);
38
+
kf_vlog("\x1b[1;31merr", fmt, va);
39
+
va_end(va);
40
+
}
41
+
+15
-33
src/main.c
+15
-33
src/main.c
···
1
-
#include "keraforge/input.h"
2
-
#include "keraforge/sprites.h"
3
-
#include "keraforge/world.h"
4
1
#include <keraforge.h>
5
2
#include <raylib.h>
6
3
#include <raymath.h>
···
22
19
} modal;
23
20
static char *modals[] = { "play", "edit" };
24
21
static bool dirty = false;
22
+
static bool preserve_mapdotbin = false;
25
23
26
24
static kf_inputbind_t
27
25
inputbind_move_up,
···
96
94
{
97
95
case 0: self->pointing = kf_east; break;
98
96
case 1: self->pointing = kf_north; break;
99
-
case -2:
97
+
case -2: /* fallthrough */
100
98
case 2: self->pointing = kf_west; break;
101
99
case -1: self->pointing = kf_south; break;
102
100
}
···
186
184
(void)argc;
187
185
(void)argv;
188
186
189
-
// SetTraceLogLevel(LOG_WARNING);
187
+
SetTraceLogLevel(LOG_WARNING);
190
188
InitWindow(800, 600, "Keraforge");
191
189
SetTargetFPS(target_fps);
192
190
SetExitKey(KEY_NULL);
···
225
223
.sprite = {8, 0},
226
224
.collide = true,
227
225
);
228
-
printf("loaded %d tiles\n", kf_tiles.count);
226
+
kf_logdbg("loaded %d tiles", kf_tiles.count);
229
227
230
228
struct kf_uiconfig *uiconfig = kf_ui_getconfig();
231
229
uiconfig->select = inputbind_select;
···
237
235
MakeDirectory("data");
238
236
239
237
struct kf_world *world = NULL;
240
-
if (!kf_exists("data/map.bin"))
241
-
{
242
-
printf("-> creating world\n");
243
-
world = kf_world_new(4096, 4096, 2);
244
-
printf("-> saving world\n");
245
-
size_t len = kf_world_getsize(world);
246
-
printf("-> writing of %lu bytes\n", len);
247
-
if (!kf_writebin("data/map.bin", (u8 *)world, len))
248
-
{
249
-
fprintf(stderr, "error creating map: failed to save map.bin\n");
250
-
free(world);
251
-
exit(1);
252
-
}
253
-
}
254
-
else
255
-
{
256
-
printf("-> loading world\n");
257
-
size_t len = 0;
258
-
world = (struct kf_world *)kf_readbin("data/map.bin", &len);
259
-
printf("-> world is %lu bytes\n", len);
260
-
}
238
+
kf_world_load(&world, true);
261
239
if (!world)
262
-
{
263
-
fprintf(stderr, "error: failed to load world\n");
264
-
exit(1);
265
-
}
240
+
KF_THROW("failed to load world: %p", world);
266
241
267
242
struct kf_actor *player = kf_actor_new(kf_actor_loadspritesheet("data/res/img/char/whom.png"), 10, 10, true);
268
243
player->sizeoffset.y = 6;
···
368
343
369
344
if (world)
370
345
{
371
-
if (dirty && !kf_writebin("data/map.bin", (u8 *)world, kf_world_getsize(world)))
372
-
fprintf(stderr, "error: failed to save map.bin\n");
346
+
if (dirty)
347
+
{
348
+
if (!kf_writebin("data/tmp/map.bin", (u8 *)world, kf_world_getsize(world)))
349
+
KF_THROW("failed to save map.bin");
350
+
if (!kf_compress("data/tmp/map.bin", "data/map.bin.xz"))
351
+
KF_THROW("failed to compress map.bin into map.bin.xz");
352
+
if (!preserve_mapdotbin)
353
+
remove("data/tmp/map.bin");
354
+
}
373
355
free(world);
374
356
}
375
357
CloseWindow();
+69
src/world.c
+69
src/world.c
···
1
+
#include "keraforge/fs.h"
1
2
#include <keraforge.h>
2
3
#include <raylib.h>
3
4
#include <stdio.h>
···
213
214
tile += down; /* shift tile pointer down */
214
215
}
215
216
}
217
+
218
+
#define _KF_MAPFILE_TMP "data/tmp/map.bin"
219
+
#define _KF_MAPFILE_XZ "data/map.bin.xz"
220
+
#define _KF_MAPFILE "data/map.bin"
221
+
222
+
int kf_world_save(struct kf_world *world, bool compress)
223
+
{
224
+
char *outfile = compress ? _KF_MAPFILE_TMP : _KF_MAPFILE;
225
+
if (!kf_writebin(outfile, (u8 *)world, kf_world_getsize(world)))
226
+
{
227
+
KF_THROW("failed to write to %s", outfile);
228
+
return 0; /* unreachable */
229
+
}
230
+
231
+
if (compress)
232
+
{
233
+
if (!kf_compress(_KF_MAPFILE_TMP, _KF_MAPFILE_XZ))
234
+
{
235
+
KF_THROW("failed to compress %s", _KF_MAPFILE_XZ);
236
+
return 0; /* unreachable */
237
+
}
238
+
/* we don't need this anymore, might as well toss it to save file storage. */
239
+
remove(_KF_MAPFILE_TMP);
240
+
}
241
+
242
+
return 1;
243
+
}
244
+
245
+
int kf_world_load(struct kf_world **pworld, bool compressed)
246
+
{
247
+
if (compressed) /* decompress before loading */
248
+
{
249
+
if (!kf_exists(_KF_MAPFILE_XZ))
250
+
{
251
+
KF_THROW("no such file: %s", _KF_MAPFILE_XZ);
252
+
return 0; /* unreachable */
253
+
}
254
+
255
+
kf_logdbg("decompressing %s to %s", _KF_MAPFILE_XZ, _KF_MAPFILE_TMP);
256
+
if (!kf_decompress(_KF_MAPFILE_XZ, _KF_MAPFILE_TMP))
257
+
{
258
+
KF_THROW("failed to decompress %s", _KF_MAPFILE_XZ);
259
+
return 0; /* unreachable */
260
+
}
261
+
}
262
+
263
+
char *infile = compressed ? _KF_MAPFILE_TMP : _KF_MAPFILE;
264
+
kf_logdbg("loading world: %s", infile);
265
+
266
+
size_t len = 0;
267
+
*pworld = (struct kf_world *)kf_readbin(infile, &len);
268
+
kf_logdbg("loaded world (%p): r=%d, wh=%dx%d, len=%lu", pworld, (*pworld)->revision, (*pworld)->width, (*pworld)->height, len);
269
+
270
+
if (compressed)
271
+
{
272
+
/* we don't need this anymore, might as well toss it to save file storage. */
273
+
remove(_KF_MAPFILE_TMP);
274
+
}
275
+
276
+
if (!pworld)
277
+
{
278
+
KF_THROW("failed to load world");
279
+
return 0; /* unreachable */
280
+
}
281
+
282
+
return 1;
283
+
}
284
+
+3
-1
todo
+3
-1
todo
···
11
11
. World
12
12
/ Tiles
13
13
. Actors
14
-
. Compression (perhaps https://github.com/google/brotli/)
14
+
x Compression
15
+
. Compress without saving the world binary as an intermediate step.
16
+
All I need to do for this is adapt the compression functions to have an in-memory (`u8 *`) compression equivalent instead of just `FILE *` compression.
15
17
. Dialogue
16
18
. Quests
17
19
. Combat
+100
tools/newgame.c
+100
tools/newgame.c
···
1
+
#include <keraforge.h>
2
+
#include <raylib.h>
3
+
#include <stdio.h>
4
+
#include <stdlib.h>
5
+
#include <string.h>
6
+
7
+
8
+
static const char *HELP =
9
+
"usage: newgame [options...]\n\n"
10
+
"options:\n"
11
+
"\t-w --width <int> specify width for the world (default: 1024)\n"
12
+
"\t-h --height <int> specify height for the world (default: 1024)\n"
13
+
"\t-s --size <int> specify width and height for the world\n"
14
+
"\t-p --path <str> specify path to save the game in (default: path)\n"
15
+
"\t-f --force create the new game even if the directory exists, this will delete data\n"
16
+
"\t --no-force opposite of -f (default)\n"
17
+
"\t-c --compress compress the world after creating it (recommended)\n"
18
+
"\t --no-compress don't compress the world after creating it (default)\n"
19
+
"\t --help display this message\n"
20
+
;
21
+
22
+
23
+
int main(int argc, char *argv[])
24
+
{
25
+
char *path = "data";
26
+
int width = 1024, height = 1024;
27
+
bool compress = false;
28
+
bool force = false;
29
+
30
+
for (int i = 1 ; i < argc ; i++)
31
+
{
32
+
char *arg = argv[i];
33
+
34
+
# define _checkshort(SHORT) (strncmp(arg, "-" SHORT, strlen("-" SHORT)) == 0)
35
+
# define _checklong(LONG) (strncmp(arg, "--" LONG, strlen("--" LONG)) == 0)
36
+
# define _check(SHORT, LONG) (_checkshort(SHORT) || _checklong(LONG))
37
+
38
+
if (_check("w", "width"))
39
+
width = atoi(argv[++i]);
40
+
else if (_check("h", "height"))
41
+
height = atoi(argv[++i]);
42
+
else if (_check("s", "size"))
43
+
width = height = atoi(argv[++i]);
44
+
else if (_check("p", "path"))
45
+
path = argv[++i];
46
+
else if (_check("c", "compress"))
47
+
compress = true;
48
+
else if (_checklong("no-compress"))
49
+
compress = false;
50
+
else if (_check("f", "force"))
51
+
force = true;
52
+
else if (_checklong("no-force"))
53
+
force = false;
54
+
else if (_checklong("help"))
55
+
{
56
+
kf_loginfo("%s", HELP);
57
+
exit(0);
58
+
}
59
+
else
60
+
{
61
+
kf_logerr("invalid argument: %s", arg);
62
+
exit(1);
63
+
}
64
+
65
+
# undef _checkshort
66
+
# undef _checklong
67
+
# undef _check
68
+
}
69
+
70
+
if (!force && DirectoryExists(path))
71
+
KF_THROW("path exists: %s", path);
72
+
73
+
struct kf_world *world = NULL;
74
+
75
+
kf_loginfo("creating world");
76
+
world = kf_world_new(width, height, 2);
77
+
78
+
/* path for our map.bin */
79
+
char worldpath[4096] = {0};
80
+
strcpy(worldpath, path);
81
+
strcpy(&worldpath[0] + strlen(path), compress ? "/tmp/map.bin" : "/map.bin");
82
+
MakeDirectory(GetDirectoryPath(worldpath));
83
+
84
+
size_t len = kf_world_getsize(world);
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);
88
+
89
+
if (compress)
90
+
{
91
+
char worldxzpath[4096] = {0};
92
+
strcpy(worldxzpath, path);
93
+
strcpy(&worldxzpath[0] + strlen(path), "/map.bin.xz");
94
+
if (!kf_compress(worldpath, worldxzpath))
95
+
KF_THROW("failed to compress %s", worldpath);
96
+
remove(worldpath); /* no longer needed */
97
+
}
98
+
99
+
free(world);
100
+
}