libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
nbt.c
1#include <alloca.h>
2#include <assert.h>
3#include <string.h>
4#include <clod/nbt.h>
5
6constexpr size_t payload_zero_sizes[] = {
7 [CLOD_NBT_INT8] = 1,
8 [CLOD_NBT_INT16] = 2,
9 [CLOD_NBT_INT32] = 4,
10 [CLOD_NBT_INT64] = 8,
11 [CLOD_NBT_FLOAT32] = 4,
12 [CLOD_NBT_FLOAT64] = 8,
13 [CLOD_NBT_INT8_ARRAY] = 4,
14 [CLOD_NBT_INT32_ARRAY] = 4,
15 [CLOD_NBT_INT64_ARRAY] = 4,
16 [CLOD_NBT_STRING] = 2,
17 [CLOD_NBT_LIST] = 5,
18 [CLOD_NBT_COMPOUND] = 1
19};
20constexpr char payload_zero_sizes_len = sizeof(payload_zero_sizes) / sizeof(payload_zero_sizes[0]);
21
22#define type_zero_size(type) (0 <= (type) && (type) < payload_zero_sizes_len ? payload_zero_sizes[(unsigned)type] : 0)
23#define type_valid(type) (type_zero_size(type))
24#define available(ptr, end) ((ptr) <= (char*)(end) ? (size_t)((char*)(end) - (ptr)) : 0)
25
27 const char *restrict const payload,
28 const void *const end,
29 const char payload_type
30) {
31 switch (payload_type) {
32 default: return 0;
33 case CLOD_NBT_INT8: return 1;
34 case CLOD_NBT_INT16: return 2;
35 case CLOD_NBT_INT32: return 4;
36 case CLOD_NBT_INT64: return 8;
37 case CLOD_NBT_FLOAT32: return 4;
38 case CLOD_NBT_FLOAT64: return 8;
39 case CLOD_NBT_INT8_ARRAY: {
40 if (available(payload, end) < 4) return 0;
41 const size_t size = (size_t)bei32_dec(payload);
42 if (available(payload, end) < 4 + size) return 0;
43 return 4 + size;
44 }
45 case CLOD_NBT_INT32_ARRAY: {
46 if (available(payload, end) < 4) return 0;
47 const size_t size = (size_t)bei32_dec(payload) * 4;
48 if (available(payload, end) < 4 + size) return 0;
49 return 4 + size;
50 }
51 case CLOD_NBT_INT64_ARRAY: {
52 if (available(payload, end) < 4) return 0;
53 const size_t size = (size_t)bei32_dec(payload) * 8;
54 if (available(payload, end) < 4 + size) return 0;
55 return 4 + size;
56 }
57 case CLOD_NBT_STRING: {
58 if (available(payload, end) < 2) return 0;
59 const size_t size = (size_t)beu16_dec(payload);
60 if (available(payload, end) < 2 + size) return 0;
61 return 2 + size;
62 }
63 case CLOD_NBT_LIST: {
64 if (available(payload, end) < 5) return 0;
65 if (payload[0] == CLOD_NBT_ZERO) return 5;
66 if (!type_valid(payload[0])) return 0;
67 const size_t length = (size_t)bei32_dec(payload + 1);
68 size_t size = 5;
69 for (size_t i = 0; i < length; i++) {
70 const size_t elem_size = clod_nbt_payload_size(payload + size, end, payload[0]);
71 if (elem_size == 0) return 0;
72 size += elem_size;
73 }
74 return size;
75 }
76 case CLOD_NBT_COMPOUND: {
77 size_t size = 0;
78 for (;;) {
79 while (available(payload, end) >= size + 3) {
80 if (!type_valid(payload[size])) break;
81 const size_t name_size = beu16_dec(payload + size + 1);
82 if (available(payload, end) < size + 3 + name_size) return 0;
83 size += 3 + name_size + clod_nbt_payload_size(payload + size + 3 + name_size, end, payload[size]);
84 }
85
86 if (available(payload, end) < size + 1) return 0;
87 if (payload[size] == CLOD_NBT_ZERO) return size + 1;
88 return 0;
89 }
90 }
91 }
92}
93
94size_t clod_nbt_tag_size(const char *restrict tag, const void *end) {
95 if (available(tag, end) < 3) return 0;
96 if (!type_valid(tag[0])) return 0;
97 const size_t name_size = beu16_dec(tag + 1);
98 if (available(tag, end) < 3 + name_size) return 0;
99 return 3 + name_size + clod_nbt_payload_size(tag + 3 + name_size, end, tag[0]);
100}
101
102char *clod_nbt_tag_payload(const char *restrict tag, const void *end) {
103 if (available(tag, end) < 3) return nullptr;
104 if (!type_valid(tag[0])) return nullptr;
105 const size_t name_size = beu16_dec(tag + 1);
106 if (available(tag, end) < 3 + name_size) return nullptr;
107 return (char*)tag + 3 + name_size;
108}
109
110clod_sstr clod_nbt_tag_name(const char *restrict tag, const void *end) {
111 if (available(tag, end) < 3) return CLOD_SSTR_NULL;
112 if (!type_valid(tag[0])) return CLOD_SSTR_NULL;
113 const size_t name_size = beu16_dec(tag + 1);
114 if (available(tag, end) < 3 + name_size) return CLOD_SSTR_NULL;
115 return clod_sstr(tag + 3, name_size);
116}
117
119 const char *const restrict payload,
120 const void *const end,
121 const char payload_type,
122 struct clod_nbt_iter *iter
123) {
124 switch (payload_type) {
125 case CLOD_NBT_COMPOUND: {
126 if (iter->payload == nullptr) {
127 memset(iter, 0, sizeof(*iter));
128 iter->tag = (char*)payload;
129 } else {
130 iter->tag += iter->size;
131 iter->index++;
132 }
133
134 if (available(iter->tag, end) < 1) goto iter_fail;
135 if (iter->tag[0] == CLOD_NBT_ZERO) {
136 iter->tag++;
137 iter->payload = nullptr;
138 iter->size = 0;
139 iter->type = CLOD_NBT_ZERO;
140 return false;
141 }
142
143 char *tag_payload = clod_nbt_tag_payload(iter->tag, end);
144 if (!tag_payload) goto iter_fail;
145 const size_t payload_size = clod_nbt_payload_size(tag_payload, end, iter->tag[0]);
146 if (payload_size == 0) goto iter_fail;
147
148 iter->payload = tag_payload;
149 iter->size = (size_t)(tag_payload - iter->tag) + payload_size;
150 iter->type = iter->tag[0];
151 return true;
152 }
153 case CLOD_NBT_LIST: {
154 if (iter->payload == nullptr) {
155 if (available(payload, end) < 5) goto iter_fail;
156 memset(iter, 0, sizeof(*iter));
157 iter->payload = (char*)payload;
158 iter->type = payload[0];
159 } else {
160 iter->payload += iter->size;
161 iter->index++;
162 }
163
164 if (iter->index >= (uint32_t)bei32_dec(payload + 1)) {
165 iter->tag = iter->payload;
166 iter->payload = nullptr;
167 iter->size = 0;
168 iter->type = CLOD_NBT_ZERO;
169 return false;
170 }
171
172 const size_t payload_size = clod_nbt_payload_size(iter->payload, end, payload[0]);
173 if (payload_size == 0) goto iter_fail;
174 iter->size = payload_size;
175 return true;
176 }
177 case CLOD_NBT_STRING: {
178 if (iter->payload == nullptr) {
179 if (available(payload, end) < 2) goto iter_fail;
180 memset(iter, 0, sizeof(*iter));
181 iter->payload = (char*)payload;
182 iter->size = 1;
183 iter->type = CLOD_NBT_INT8;
184 } else {
185 iter->payload++;
186 iter->index++;
187 }
188
189 if (iter->index >= beu16_dec(payload)) {
190 iter->tag = iter->payload;
191 iter->payload = nullptr;
192 iter->size = 0;
193 iter->type = CLOD_NBT_ZERO;
194 return false;
195 }
196 return true;
197 }
198 case CLOD_NBT_INT8_ARRAY: {
199 if (iter->payload == nullptr) {
200 if (available(payload, end) < 4) goto iter_fail;
201 memset(iter, 0, sizeof(*iter));
202 iter->payload = (char*)payload;
203 iter->size = 1;
204 iter->type = CLOD_NBT_INT8;
205 } else {
206 iter->payload++;
207 iter->index++;
208 }
209
210 if (iter->index >= (uint32_t)bei32_dec(payload)) {
211 iter->tag = iter->payload;
212 iter->payload = nullptr;
213 iter->size = 0;
214 iter->type = CLOD_NBT_ZERO;
215 return false;
216 }
217 return true;
218 }
219 case CLOD_NBT_INT32_ARRAY: {
220 if (iter->payload == nullptr) {
221 if (available(payload, end) < 4) goto iter_fail;
222 memset(iter, 0, sizeof(*iter));
223 iter->payload = (char*)payload;
224 iter->size = 4;
225 iter->type = CLOD_NBT_INT32;
226 } else {
227 iter->payload += 4;
228 iter->index++;
229 }
230
231 if (iter->index >= (uint32_t)bei32_dec(payload)) {
232 iter->tag = iter->payload;
233 iter->payload = nullptr;
234 iter->size = 0;
235 iter->type = CLOD_NBT_ZERO;
236 return false;
237 }
238 return true;
239 }
240 case CLOD_NBT_INT64_ARRAY: {
241 if (iter->payload == nullptr) {
242 if (available(payload, end) < 4) goto iter_fail;
243 memset(iter, 0, sizeof(*iter));
244 iter->payload = (char*)payload;
245 iter->size = 8;
246 iter->type = CLOD_NBT_INT64;
247 } else {
248 iter->payload += 8;
249 iter->index++;
250 }
251
252 if (iter->index >= (uint32_t)bei32_dec(payload)) {
253 iter->tag = iter->payload;
254 iter->payload = nullptr;
255 iter->size = 0;
256 iter->type = CLOD_NBT_ZERO;
257 return false;
258 }
259 return true;
260 }
261 default: return false;
262 }
263
264iter_fail:
265 memset(iter, 0, sizeof(*iter));
266 return false;
267}
268
270 const char *restrict compound,
271 const void *end,
272 const clod_sstr name
273) {
274 struct clod_nbt_iter iter = CLOD_NBT_ITER_ZERO;
275 while (clod_nbt_iter_next(compound, end, CLOD_NBT_COMPOUND, &iter)) {
276 if (clod_sstr_eq(clod_nbt_tag_name(iter.tag, end), name)) return iter.tag;
277 }
278 return nullptr;
279}
280
282 char *restrict compound,
283 const void **end,
284 ptrdiff_t *free,
285 const clod_sstr name,
286 const char type
287) {
288 if (!type_valid(type)) return nullptr;
289 const size_t elem_size = 3 + name.size + type_zero_size(type);
290 if (!compound) {
291 *free -= (ptrdiff_t)elem_size;
292 return nullptr;
293 }
294
295 struct clod_nbt_iter iter = CLOD_NBT_ITER_ZERO;
296 while (clod_nbt_iter_next(compound, *end, CLOD_NBT_COMPOUND, &iter)) {
297 if (clod_sstr_eq(clod_nbt_tag_name(iter.tag, *end), name)) return iter.tag;
298 }
299
300 if (!iter.tag) return nullptr;
301 *free -= (ptrdiff_t)elem_size;
302 if (*free < 0) {
303 // Out of space.
304 return nullptr;
305 }
306
307 memmove(iter.tag + elem_size, iter.tag, available(iter.tag, *end) - elem_size);
308
309 iter.tag[0] = type;
310 beu16_enc(iter.tag + 1, (uint16_t)name.size);
311 memcpy(iter.tag + 3, name.ptr, name.size);
312 memset(iter.tag + 3 + name.size, 0, type_zero_size(type));
313
314 *end = *(char**)end + elem_size;
315 return iter.tag;
316}
317
319 char *restrict compound,
320 const void **end,
321 ptrdiff_t *free,
322 const clod_sstr name
323) {
324 struct clod_nbt_iter iter = CLOD_NBT_ITER_ZERO;
325 while (clod_nbt_iter_next(compound, *end, CLOD_NBT_COMPOUND, &iter)) {
326 if (clod_sstr_eq(clod_nbt_tag_name(iter.tag, *end), name)) {
327 memmove(iter.tag, iter.tag + iter.size, available(iter.tag, *end) - iter.size);
328 *end = *(char**)end - iter.size;
329 *free += (ptrdiff_t)iter.size;
330 return true;
331 }
332 }
333 return false;
334}
335
337 char *restrict list,
338 const char **end,
339 ptrdiff_t *free,
340 char type,
341 const uint32_t length
342) {
343 if (!list) {
344 *free -= (ptrdiff_t)(length * type_zero_size(type));
345 return false;
346 }
347 if (available(list, *end) < 5) return false;
348
349 if (list[0] != type) {
350 const size_t old_size = clod_nbt_payload_size(list, *end, CLOD_NBT_LIST);
351 const size_t new_size = 5 + length * type_zero_size(type);
352 if (old_size == 0) return false;
353
354 const ptrdiff_t delta = (ptrdiff_t)new_size - (ptrdiff_t)old_size;
355 *free -= delta;
356 if (*free < 0) return false;
357
358 memmove(list + new_size, list + old_size, available(list, *end) - old_size);
359 memset(list, 0, new_size);
360 list[0] = type;
361 bei32_enc(list + 1, (int32_t)length);
362 *end = *(char**)end + delta;
363 return true;
364 }
365
366 const uint32_t old_length = (uint32_t)bei32_dec(list + 1);
367
368 if (old_length < length) {
369 const size_t old_size = clod_nbt_payload_size(list, *end, CLOD_NBT_LIST);
370 const size_t append_size = type_zero_size(list[0]) * (length - old_length);
371
372 *free -= (ptrdiff_t)append_size;
373 if (*free < 0) return false;
374
375 memmove(list + old_size + append_size, list + old_size, available(list, *end) - old_size - append_size);
376 memset(list + old_size, 0, append_size);
377 bei32_enc(list + 1, (int32_t)length);
378
379 *end = *(char**)end + append_size;
380 return true;
381 }
382
383 if (old_length > length) {
384 struct clod_nbt_iter iter = CLOD_NBT_ITER_ZERO;
385 char *truncate = nullptr;
386 while (clod_nbt_iter_next(list, *end, CLOD_NBT_LIST, &iter))
387 if (iter.index == length) truncate = iter.payload;
388
389 if (!iter.tag || !truncate) return false;
390
391 memmove(truncate, iter.tag, available(iter.tag, *end));
392 bei32_enc(list + 1, (int32_t)length);
393 *free += iter.tag - truncate;
394 *end = *(char**)end - (iter.tag - truncate);
395 return true;
396 }
397
398 return true;
399}
static int32_t bei32_dec(const char ptr[4])
Decode a 32-bit signed integer in big-endian format.
Definition big_endian.h:127
static void bei32_enc(char ptr[4], const int32_t val)
Encode a 32-bit signed integer into big-endian format.
Definition big_endian.h:110
static uint16_t beu16_dec(const char ptr[2])
Decode a 16-bit unsigned integer in big-endian format.
Definition big_endian.h:55
static void beu16_enc(char ptr[2], const uint16_t val)
Encode a 16-bit unsigned integer into big-endian format.
Definition big_endian.h:38
char * clod_nbt_compound_get(const char *restrict compound, const void *end, const clod_sstr name)
Definition nbt.c:269
bool clod_nbt_iter_next(const char *const restrict payload, const void *const end, const char payload_type, struct clod_nbt_iter *iter)
Definition nbt.c:118
bool clod_nbt_list_resize(char *restrict list, const char **end, ptrdiff_t *free, char type, const uint32_t length)
Definition nbt.c:336
bool clod_nbt_compound_del(char *restrict compound, const void **end, ptrdiff_t *free, const clod_sstr name)
Definition nbt.c:318
char * clod_nbt_compound_add(char *restrict compound, const void **end, ptrdiff_t *free, const clod_sstr name, const char type)
Definition nbt.c:281
clod_sstr clod_nbt_tag_name(const char *tag, const void *end)
size_t clod_nbt_payload_size(const char *restrict const payload, const void *const end, const char payload_type)
Definition nbt.c:26
char * clod_nbt_tag_payload(const char *restrict tag, const void *end)
Definition nbt.c:102
size_t clod_nbt_tag_size(const char *restrict tag, const void *end)
Definition nbt.c:94
static bool clod_sstr_eq(const clod_sstr str1, const clod_sstr str2)
Definition sstr.h:44
char * tag
Definition nbt.h:99
uint32_t index
Definition nbt.h:107
size_t size
Definition nbt.h:103
char type
Definition nbt.h:105
char * payload
Definition nbt.h:101
char * ptr
Definition sstr.h:22
size_t size
Definition sstr.h:24