libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
dsf.c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license in the file COPYING
10 * or http://www.opensource.org/licenses/CDDL-1.0.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file COPYING.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2023 Saso Kiselkov. All rights reserved.
24 */
25
26#include <string.h>
27#include <errno.h>
28#include <stddef.h>
29#include <stdarg.h>
30
31#include <acfutils/assert.h>
32#include <acfutils/compress.h>
33#include <acfutils/dsf.h>
34#include <acfutils/helpers.h>
35#include <acfutils/safe_alloc.h>
36
37#define DSF_MAX_VERSION 1
38#define INDENT_DEPTH 4
39#define IDX_UNSET ((uint64_t)-1)
40
41static dsf_atom_t *parse_atom(const uint8_t *buf, size_t bufsz,
42 char reason[DSF_REASON_SZ], uint64_t abs_off);
43static void free_atom(dsf_atom_t *atom);
44static bool_t parse_atom_list(const uint8_t *buf, uint64_t bufsz,
45 list_t *atoms, char reason[DSF_REASON_SZ], uint64_t abs_off);
46static bool_t parse_prop_atom(dsf_atom_t *atom, char reason[DSF_REASON_SZ]);
47static void destroy_prop_atom(dsf_atom_t *atom);
48static void destroy_planar_numeric_atom(dsf_atom_t *atom);
49
50typedef int (*cmd_parser_cb_t)(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
51 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
52
53typedef struct {
54 dsf_cmd_t cmd;
55 cmd_parser_cb_t cb;
57
58static int pool_sel_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
59 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
60static int junct_off_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
61 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
62static int set_defn_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
63 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
64static int road_subt_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
65 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
66static int obj_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
67 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
68static int net_chain_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
69 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
70static int poly_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
71 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
72static int terr_patch_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
73 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
74static int patch_tria_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
75 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
76static int comment_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
77 const uint8_t *data, size_t len, dsf_cmd_cb_t cb);
78
79#define DSF_CMD_ID_MAX 34
80static cmd_parser_info_t cmd_parser_info[DSF_CMD_ID_MAX + 1] = {
81 { 0, NULL },
82 { DSF_POOL_SEL, pool_sel_cb }, /* 1 */
83 { DSF_JUNCT_OFFSET_SEL, junct_off_cb }, /* 2 */
84 { DSF_SET_DEFN8, set_defn_cb }, /* 3 */
85 { DSF_SET_DEFN16, set_defn_cb }, /* 4 */
86 { DSF_SET_DEFN32, set_defn_cb }, /* 5 */
87 { DSF_ROAD_SUBTYPE, road_subt_cb }, /* 6 */
88 { DSF_OBJ, obj_cb }, /* 7 */
89 { DSF_OBJ_RNG, obj_cb }, /* 8 */
90 { DSF_NET_CHAIN, net_chain_cb }, /* 9 */
91 { DSF_NET_CHAIN_RNG, net_chain_cb }, /* 10 */
92 { DSF_NET_CHAIN32, net_chain_cb }, /* 11 */
93 { DSF_POLY, poly_cb }, /* 12 */
94 { DSF_POLY_RNG, poly_cb }, /* 13 */
95 { DSF_NEST_POLY, poly_cb }, /* 14 */
96 { DSF_NEST_POLY_RNG, poly_cb }, /* 15 */
97 { DSF_TERR_PATCH, terr_patch_cb }, /* 16 */
98 { DSF_TERR_PATCH_FLAGS, terr_patch_cb }, /* 17 */
99 { DSF_TERR_PATCH_FLAGS_N_LOD, terr_patch_cb }, /* 18 */
100 { 0, NULL }, /* 19 = unused */
101 { 0, NULL }, /* 20 = unused */
102 { 0, NULL }, /* 21 = unused */
103 { 0, NULL }, /* 22 = unused */
104 { DSF_PATCH_TRIA, patch_tria_cb }, /* 23 */
105 { DSF_PATCH_TRIA_XPOOL, patch_tria_cb }, /* 24 */
106 { DSF_PATCH_TRIA_RNG, patch_tria_cb }, /* 25 */
107 { DSF_PATCH_TRIA_STRIP, patch_tria_cb }, /* 26 */
108 { DSF_PATCH_TRIA_STRIP_XPOOL, patch_tria_cb }, /* 27 */
109 { DSF_PATCH_TRIA_STRIP_RNG, patch_tria_cb }, /* 28 */
110 { DSF_PATCH_TRIA_FAN, patch_tria_cb }, /* 29 */
111 { DSF_PATCH_TRIA_FAN_XPOOL, patch_tria_cb }, /* 30 */
112 { DSF_PATCH_TRIA_FAN_RNG, patch_tria_cb }, /* 31 */
113 { DSF_COMMENT8, comment_cb }, /* 32 */
114 { DSF_COMMENT16, comment_cb }, /* 33 */
115 { DSF_COMMENT32, comment_cb } /* 34 */
116};
117
118static const char *
119data_type2str(dsf_data_type_t data_type)
120{
121 switch (data_type) {
122 case DSF_DATA_SINT16:
123 return ("s16");
124 case DSF_DATA_UINT16:
125 return ("u16");
126 case DSF_DATA_SINT32:
127 return ("s32");
128 case DSF_DATA_UINT32:
129 return ("u32");
130 case DSF_DATA_SINT64:
131 return ("s64");
132 case DSF_DATA_UINT64:
133 return ("u64");
134 case DSF_DATA_FP32:
135 return ("fp32");
136 case DSF_DATA_FP64:
137 return ("fp64");
138 default:
139 VERIFY(0);
140 }
141}
142
143static inline uint16_t
144read_u16(const uint8_t *buf)
145{
146#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
147 return (BSWAP16(*(uint16_t *)buf));
148#else
149 return (*(uint16_t *)buf);
150#endif
151}
152
153static inline uint32_t
154read_u32(const uint8_t *buf)
155{
156#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
157 return (BSWAP32(*(uint32_t *)buf));
158#else
159 return (*(uint32_t *)buf);
160#endif
161}
162
172dsf_t *
173dsf_init(const char *filename)
174{
175 dsf_t *dsf = NULL;
176 uint8_t *buf = NULL;
177 ssize_t bufsz = filesz(filename);
178 ssize_t readsz = 0;
179 FILE *fp = fopen(filename, "rb");
180 char reason[DSF_REASON_SZ];
181 static const uint8_t magic[8] = {
182 'X', 'P', 'L', 'N', 'E', 'D', 'S', 'F'
183 };
184
185 if (bufsz < 12 + 16 || fp == NULL)
186 goto errout;
187 buf = safe_malloc(bufsz);
188
189 if (fread(buf, 1, sizeof (magic), fp) != sizeof (magic))
190 goto errout;
191 readsz = sizeof (magic);
192
193 if (memcmp(buf, magic, sizeof (magic)) == 0) {
194 /* raw DSF, just read its contents */
195 while (readsz < bufsz) {
196 size_t n = fread(&buf[readsz], 1, bufsz - readsz, fp);
197 if (n == 0)
198 break;
199 readsz += n;
200 }
201 if (!feof(fp) && readsz < bufsz) {
202 logMsg("Error reading DSF %s: %s", filename,
203 strerror(errno));
204 goto errout;
205 }
206 fclose(fp);
207 fp = NULL;
208 } else if (test_7z(buf, sizeof (magic))) {
209 fclose(fp);
210 fp = NULL;
211 free(buf);
212 buf = decompress_7z(filename, (size_t *)&bufsz);
213 if (buf == NULL)
214 goto errout;
215 } else {
216 goto errout;
217 }
218 dsf = dsf_parse(buf, bufsz, reason);
219 if (dsf == NULL) {
220 logMsg("Error parsing DSF %s: %s", filename, reason);
221 goto errout;
222 }
223 /* No need to release `buf', the dsf_t has taken ownership of it. */
224
225 return (dsf);
226errout:
227 free(buf);
228 if (fp != NULL)
229 fclose(fp);
230 return (NULL);
231}
232
233static bool_t
234parse_prop_atom(dsf_atom_t *atom, char reason[DSF_REASON_SZ])
235{
236 const char *payload_end =
237 (const char *)(atom->payload + atom->payload_sz);
238
239 ASSERT3U(atom->id, ==, DSF_ATOM_PROP);
240
241 list_create(&atom->prop_atom.props, sizeof (dsf_prop_t),
242 offsetof(dsf_prop_t, prop_node));
243 atom->subtype_inited = B_TRUE;
244
245 if (atom->payload_sz < 2) {
246 snprintf(reason, DSF_REASON_SZ, "PROP atom too short");
247 return (B_FALSE);
248 }
249 for (const char *name = (const char *)atom->payload;
250 name < payload_end;) {
251 size_t name_len = strnlen(name, payload_end - name);
252 size_t value_len;
253 const char *value;
254 dsf_prop_t *prop;
255
256 if (name_len == (size_t)(payload_end - name)) {
257 snprintf(reason, DSF_REASON_SZ, "PROP atom contains "
258 "an unterminated name string");
259 return (B_FALSE);
260 }
261 value = name + name_len + 1;
262 if (value >= payload_end) {
263 snprintf(reason, DSF_REASON_SZ, "Last name-value pair "
264 "in PROP atom is missing the value");
265 return (B_FALSE);
266 }
267 value_len = strnlen(value, payload_end - value);
268 if (value_len == (size_t)(payload_end - value)) {
269 snprintf(reason, DSF_REASON_SZ, "PROP atom contains "
270 "an unterminated value string");
271 return (B_FALSE);
272 }
273 prop = safe_calloc(1, sizeof (*prop));
274 prop->name = name;
275 prop->value = value;
276 list_insert_tail(&atom->prop_atom.props, prop);
277
278 name = value + value_len + 1;
279 }
280
281 return (B_TRUE);
282}
283
284#define CHECK_LEN(x) \
285 do { \
286 if (start + (x) > end) { \
287 snprintf(reason, DSF_REASON_SZ, "planar numeric atom " \
288 "%c%c%c%c at %lx, plane %u contains too little " \
289 "data", DSF_ATOM_ID_PRINTF(atom), \
290 (unsigned long)atom->file_off, plane); \
291 goto errout; \
292 } \
293 } while (0)
294#define REALLOC_INCR (1 << 14) /* items */
295#define RESIZE(buf, fill, cap, itemsz, add) \
296 do { \
297 if (fill + add > cap) { \
298 cap += REALLOC_INCR; \
299 buf = realloc(buf, cap * (itemsz)); \
300 } \
301 } while (0)
302
303static unsigned
304type2datalen(dsf_data_type_t data_type)
305{
306 switch (data_type) {
307 case DSF_DATA_SINT16:
308 case DSF_DATA_UINT16:
309 return (2);
310 case DSF_DATA_SINT32:
311 case DSF_DATA_UINT32:
312 case DSF_DATA_FP32:
313 return (4);
314 case DSF_DATA_SINT64:
315 case DSF_DATA_UINT64:
316 case DSF_DATA_FP64:
317 return (8);
318 default:
319 VERIFY(0);
320 }
321}
322
323static void *
324rle_decode(dsf_atom_t *atom, unsigned plane, const uint8_t *start,
325 const uint8_t *end, dsf_data_type_t data_type, uint32_t num_values,
326 size_t *bytes_consumed, char reason[DSF_REASON_SZ])
327{
328 unsigned datalen = type2datalen(data_type);
329 uint8_t *out = NULL;
330 size_t out_cap = 0;
331 size_t i = 0;
332
333 for (uint32_t num = 0; num < num_values;) {
334 unsigned repeat, cnt;
335
336 CHECK_LEN(1);
337 repeat = start[i];
338 cnt = repeat & 0x7f;
339 i++;
340
341 RESIZE(out, num, out_cap, datalen, cnt);
342 if (repeat & 0x80) {
343 CHECK_LEN(datalen);
344 /* repeating case */
345 for (unsigned k = 0; k < cnt; k++) {
346 memcpy(&out[num * datalen], &start[i], datalen);
347 num++;
348 }
349 i += datalen;
350 } else {
351 CHECK_LEN(cnt * datalen);
352 for (unsigned k = 0; k < cnt; k++) {
353 memcpy(&out[num * datalen], &start[i], datalen);
354 i += datalen;
355 num++;
356 }
357 }
358 }
359
360 if (bytes_consumed != NULL)
361 *bytes_consumed = i;
362 return (out);
363errout:
364 free(out);
365 return (NULL);
366}
367
368static void *
369diff_decode(dsf_atom_t *atom, unsigned plane, const uint8_t *start,
370 const uint8_t *end, dsf_data_type_t data_type, uint32_t num_values,
371 size_t *bytes_consumed, char reason[DSF_REASON_SZ])
372{
373 unsigned datalen = type2datalen(data_type);
374 void *out;
375
376 CHECK_LEN(num_values * datalen);
377
378 out = safe_calloc(datalen, num_values);
379
380#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
381#error "TODO: implement big-endian"
382#endif
383
384#define DIFF_DEC(ctype) \
385 do { \
386 ctype prev = 0; \
387 ctype *out_type = out; \
388 for (uint32_t i = 0; i < num_values; i++) { \
389 ctype val = ((ctype *)start)[i]; \
390 prev += val; \
391 out_type[i] = prev; \
392 } \
393 } while (0)
394 switch (data_type) {
395 case DSF_DATA_SINT16:
396 DIFF_DEC(int16_t);
397 break;
398 case DSF_DATA_UINT16:
399 DIFF_DEC(uint16_t);
400 break;
401 case DSF_DATA_SINT32:
402 DIFF_DEC(int32_t);
403 break;
404 case DSF_DATA_UINT32:
405 DIFF_DEC(uint32_t);
406 break;
407 case DSF_DATA_SINT64:
408 DIFF_DEC(int64_t);
409 break;
410 case DSF_DATA_UINT64:
411 DIFF_DEC(uint64_t);
412 break;
413 case DSF_DATA_FP32:
414 DIFF_DEC(float);
415 break;
416 case DSF_DATA_FP64:
417 DIFF_DEC(double);
418 break;
419 default:
420 VERIFY(0);
421 }
422#undef DIFF_DEC
423
424 if (bytes_consumed != NULL)
425 *bytes_consumed = num_values * datalen;
426 return (out);
427errout:
428 return (NULL);
429}
430
431static void *
432raw_decode(dsf_atom_t *atom, unsigned plane, const uint8_t *start,
433 const uint8_t *end, dsf_data_type_t data_type, uint32_t num_values,
434 size_t *bytes_consumed, char reason[DSF_REASON_SZ])
435{
436 unsigned datalen = type2datalen(data_type);
437 void *out;
438
439 CHECK_LEN(num_values * datalen);
440
441 out = safe_calloc(datalen, num_values);
442#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
443#error "TODO: implement big-endian"
444#else /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
445 memcpy(out, start, datalen * num_values);
446#endif /* __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ */
447
448 if (bytes_consumed != NULL)
449 *bytes_consumed = num_values * datalen;
450 return (out);
451errout:
452 return (NULL);
453}
454
455static ssize_t
456parse_plane(dsf_atom_t *atom, unsigned plane, dsf_data_type_t data_type,
457 const uint8_t *start, const uint8_t *end, char reason[DSF_REASON_SZ])
458{
459 void *out = NULL;
460 size_t consumed = 0;
461 uint32_t datacnt = atom->planar_atom.data_count;
462 unsigned datalen = type2datalen(data_type);
463 unsigned enc;
464
465 CHECK_LEN(1);
466 enc = start[0];
467 start++;
468 if (enc > 3) {
469 snprintf(reason, DSF_REASON_SZ, "invalid encoding type %u for "
470 "planar numeric atom %c%c%c%c at %lx", enc,
471 DSF_ATOM_ID_PRINTF(atom), (unsigned long)atom->file_off);
472 return (-1);
473 }
474
475 if (datacnt == 0)
476 return (1);
477
478 switch (enc) {
479 case DSF_ENC_RAW:
480 out = raw_decode(atom, plane, start, end, data_type, datacnt,
481 &consumed, reason);
482 break;
483 case DSF_ENC_DIFF:
484 out = diff_decode(atom, plane, start, end, data_type,
485 datacnt, &consumed, reason);
486 break;
487 case DSF_ENC_DIFF | DSF_ENC_RLE: {
488 void *tmp = rle_decode(atom, plane, start, end, data_type,
489 datacnt, &consumed, reason);
490 if (tmp == NULL)
491 return (-1);
492 out = diff_decode(atom, plane, tmp, tmp + datacnt * datalen,
493 data_type, datacnt, NULL, reason);
494 free(tmp);
495 break;
496 }
497 default: {
498 void *tmp;
499 ASSERT3U(enc, ==, DSF_ENC_RLE);
500 tmp = rle_decode(atom, plane, start, end, data_type, datacnt,
501 &consumed, reason);
502 if (tmp == NULL)
503 return (-1);
504 out = raw_decode(atom, plane, start, end, data_type, datacnt,
505 &consumed, reason);
506 free(tmp);
507 break;
508 }
509 }
510
511 if (out == NULL)
512 return (-1);
513
514 atom->planar_atom.data[plane] = out;
515
516 return (consumed + 1);
517errout:
518 return (-1);
519}
520
521#undef REALLOC_INCR
522#undef RESIZE
523#undef CHECK_LEN
524
525static bool_t
526parse_planar_numeric_atom(dsf_atom_t *atom, dsf_data_type_t data_type,
527 char reason[DSF_REASON_SZ])
528{
529 dsf_planar_atom_t *pa = &atom->planar_atom;
530 const uint8_t *plane_p = &atom->payload[5];
531 const uint8_t *end = atom->payload + atom->payload_sz;
532
533 if (atom->payload_sz < 5) {
534 snprintf(reason, DSF_REASON_SZ, "invalid planar numeric atom "
535 "%c%c%c%c at %lx: not enough payload",
536 DSF_ATOM_ID_PRINTF(atom), (unsigned long)atom->file_off);
537 return (B_FALSE);
538 }
539
540 pa->data_type = data_type;
541 pa->data_count = read_u32(atom->payload);
542 pa->plane_count = atom->payload[4];
543 pa->data = safe_calloc(pa->plane_count, sizeof (*pa->data));
544 atom->subtype_inited = B_TRUE;
545
546 for (unsigned i = 0; i < pa->plane_count; i++) {
547 ssize_t len = parse_plane(atom, i, data_type,
548 plane_p, end, reason);
549 if (len < 0) {
550 printf("plane parse error\n");
551 return (B_FALSE);
552 }
553 plane_p += len;
554 }
555
556 if (plane_p != end) {
557 snprintf(reason, DSF_REASON_SZ, "planar numeric atom %c%c%c%c "
558 "at %lx contained trailing garbage",
559 DSF_ATOM_ID_PRINTF(atom), (unsigned long)atom->file_off);
560 return (B_FALSE);
561 }
562
563 return (B_TRUE);
564}
565
566static bool_t
567parse_demi_atom(dsf_atom_t *atom, char reason[DSF_REASON_SZ])
568{
569 const uint8_t *payload = atom->payload;
570
571 if (atom->payload_sz != 20) {
572 snprintf(reason, DSF_REASON_SZ, "Invalid payload size of "
573 "DEMI atom (%x, wanted 20)", atom->payload_sz);
574 return (B_FALSE);
575 }
576 atom->demi_atom.version = *payload++;
577 if (atom->demi_atom.version != 1) {
578 snprintf(reason, DSF_REASON_SZ, "Unsupported DEMI atom "
579 "version %d", atom->demi_atom.version);
580 return (B_FALSE);
581 }
582 atom->demi_atom.bpp = *payload++;
583 atom->demi_atom.flags = read_u16(payload);
584 payload += 2;
585 atom->demi_atom.width = read_u32(payload);
586 payload += 4;
587 atom->demi_atom.height = read_u32(payload);
588 payload += 4;
589 atom->demi_atom.scale = *(float *)payload;
590 payload += 4;
591 atom->demi_atom.offset = *(float *)payload;
592
593 atom->subtype_inited = B_TRUE;
594
595 return (B_TRUE);
596}
597
598static void
599destroy_planar_numeric_atom(dsf_atom_t *atom)
600{
601 ASSERT(atom->subtype_inited);
602 for (unsigned i = 0; i < atom->planar_atom.plane_count; i++)
603 free(atom->planar_atom.data[i]);
604 free(atom->planar_atom.data);
605 atom->planar_atom.data = NULL;
606}
607
608static void
609destroy_prop_atom(dsf_atom_t *atom)
610{
611 dsf_prop_t *prop;
612
613 ASSERT3U(atom->id, ==, DSF_ATOM_PROP);
614 ASSERT(atom->subtype_inited);
615
616 while ((prop = list_remove_head(&atom->prop_atom.props)) != NULL)
617 free(prop);
618 list_destroy(&atom->prop_atom.props);
619}
620
621static dsf_atom_t *
622parse_atom(const uint8_t *buf, size_t bufsz, char reason[DSF_REASON_SZ],
623 uint64_t abs_off)
624{
625 dsf_atom_t *atom = safe_calloc(1, sizeof (*atom));
626
627 ASSERT(reason != NULL);
628 list_create(&atom->subatoms, sizeof (dsf_atom_t),
629 offsetof(dsf_atom_t, atom_list));
630
631 if (bufsz < 8)
632 goto errout;
633#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
634 atom->id = BSWAP32(read_u32(buf));
635#else
636 atom->id = read_u32(buf);
637#endif
638 atom->payload_sz = read_u32(&buf[4]) - 8;
639 atom->payload = &buf[8];
640 if (atom->payload_sz + 8 > bufsz) {
641 snprintf(reason, DSF_REASON_SZ, "invalid atom at %lx, "
642 "size (%lx) is too large",
643 (unsigned long)abs_off, (unsigned long)atom->payload_sz);
644 goto errout;
645 }
646 atom->file_off = abs_off;
647
648 if (atom->id == DSF_ATOM_HEAD || atom->id == DSF_ATOM_DEFN ||
649 atom->id == DSF_ATOM_GEOD || atom->id == DSF_ATOM_DEMS) {
650 if (!parse_atom_list(atom->payload, atom->payload_sz,
651 &atom->subatoms, reason, abs_off + 8))
652 goto errout;
653 } else if (atom->id == DSF_ATOM_PROP) {
654 if (!parse_prop_atom(atom, reason))
655 goto errout;
656 } else if (atom->id == DSF_ATOM_POOL) {
657 if (!parse_planar_numeric_atom(atom, DSF_DATA_UINT16, reason))
658 goto errout;
659 } else if (atom->id == DSF_ATOM_PO32) {
660 if (!parse_planar_numeric_atom(atom, DSF_DATA_UINT32, reason))
661 goto errout;
662 } else if (atom->id == DSF_ATOM_DEMI) {
663 if (!parse_demi_atom(atom, reason))
664 goto errout;
665 }
666
667 return (atom);
668errout:
669 free_atom(atom);
670 return (NULL);
671}
672
673static bool_t
674parse_atom_list(const uint8_t *buf, uint64_t bufsz, list_t *atoms,
675 char reason[DSF_REASON_SZ], uint64_t abs_off)
676{
677 ASSERT(reason != NULL);
678
679 for (const uint8_t *atom_buf = buf; atom_buf < buf + bufsz;) {
680 dsf_atom_t *atom = parse_atom(atom_buf,
681 (buf + bufsz) - atom_buf, reason,
682 abs_off + (atom_buf - buf));
683
684 if (atom == NULL)
685 return (B_FALSE);
686 list_insert_tail(atoms, atom);
687 atom_buf += atom->payload_sz + 8;
688 }
689
690 return (B_TRUE);
691}
692
706dsf_t *
707dsf_parse(uint8_t *buf, size_t bufsz, char reason[DSF_REASON_SZ])
708{
709 dsf_t *dsf = safe_calloc(1, sizeof (*dsf));
710 static const uint8_t magic[8] = {
711 'X', 'P', 'L', 'N', 'E', 'D', 'S', 'F'
712 };
713
714 VERIFY(reason != NULL);
715
716 list_create(&dsf->atoms, sizeof (dsf_atom_t),
717 offsetof(dsf_atom_t, atom_list));
718
719 if (bufsz < 12 + 16) {
720 snprintf(reason, DSF_REASON_SZ,
721 "file is too short (%d) to be a valid DSF", (int)bufsz);
722 goto errout;
723 }
724 if (memcmp(buf, magic, sizeof (magic)) != 0) {
725 snprintf(reason, DSF_REASON_SZ,
726 "file premable missing DSF magic number");
727 goto errout;
728 }
729 dsf->version = read_u32(&buf[8]);
730 if (dsf->version > DSF_MAX_VERSION) {
731 snprintf(reason, DSF_REASON_SZ,
732 "file version (%d) exceeds our max supported version (%d)",
733 dsf->version, DSF_MAX_VERSION);
734 goto errout;
735 }
736 memcpy(dsf->md5sum, &buf[bufsz - 16], 16);
737 if (!parse_atom_list(&buf[12], bufsz - (12 + 16), &dsf->atoms, reason,
738 12))
739 goto errout;
740
741 /* Set this last, this confirms our ownership of the data buffer. */
742 dsf->data = buf;
743 dsf->size = bufsz;
744
745 return (dsf);
746errout:
747 dsf_fini(dsf);
748 return (NULL);
749}
750
751static void
752free_atom(dsf_atom_t *atom)
753{
754 dsf_atom_t *subatom;
755
756 while ((subatom = list_remove_head(&atom->subatoms)) != NULL)
757 free_atom(subatom);
758 list_destroy(&atom->subatoms);
759
760 if (atom->subtype_inited) {
761 switch (atom->id) {
762 case DSF_ATOM_PROP:
763 destroy_prop_atom(atom);
764 break;
765 case DSF_ATOM_POOL:
766 case DSF_ATOM_PO32:
767 destroy_planar_numeric_atom(atom);
768 break;
769 case DSF_ATOM_DEMI:
770 /* no-op */
771 break;
772 default:
773 VERIFY(0);
774 }
775 atom->subtype_inited = B_FALSE;
776 }
777
778 free(atom);
779}
780
785void
787{
788 dsf_atom_t *atom;
789
790 while ((atom = list_remove_head(&dsf->atoms)) != NULL)
791 free_atom(atom);
792 list_destroy(&dsf->atoms);
793 free(dsf->data);
794 free(dsf);
795}
796
797static void
798dump_prop_atom(const dsf_atom_t *atom, char **str, size_t *len, int depth)
799{
800 char *indent = safe_calloc((depth * INDENT_DEPTH) + 1,
801 sizeof (*indent));
802
803 VERIFY3U(atom->id, ==, DSF_ATOM_PROP);
804
805 memset(indent, ' ', depth * INDENT_DEPTH);
806 indent[depth * INDENT_DEPTH] = '\0';
807
808 for (dsf_prop_t *prop = list_head(&atom->prop_atom.props); prop != NULL;
809 prop = list_next(&atom->prop_atom.props, prop)) {
810 append_format(str, len, "%s\"%s\" = \"%s\"\n",
811 indent, prop->name, prop->value);
812 }
813 free(indent);
814}
815
816static void
817dump_planar_atom(const dsf_atom_t *atom, char **str, size_t *len, int depth)
818{
819 char *indent = safe_calloc((depth * INDENT_DEPTH) + 1,
820 sizeof (*indent));
821
822 memset(indent, ' ', depth * INDENT_DEPTH);
823 indent[depth * INDENT_DEPTH] = '\0';
824
825 append_format(str, len, "%sdata type: %s\n"
826 "%snum items: %d\n"
827 "%snum planes: %d\n",
828 indent, data_type2str(atom->planar_atom.data_type),
829 indent, atom->planar_atom.data_count,
830 indent, atom->planar_atom.plane_count);
831 free(indent);
832}
833
834static void
835dump_demi_atom(const dsf_atom_t *atom, char **str, size_t *len, int depth)
836{
837 char *indent = safe_calloc((depth * INDENT_DEPTH) + 1,
838 sizeof (*indent));
839 const char *data_type;
840
841 memset(indent, ' ', depth * INDENT_DEPTH);
842 indent[depth * INDENT_DEPTH] = '\0';
843
844 switch (atom->demi_atom.flags & DEMI_DATA_MASK) {
845 case DEMI_DATA_FP32:
846 data_type = "float";
847 break;
848 case DEMI_DATA_SINT:
849 data_type = "sint";
850 break;
851 case DEMI_DATA_UINT:
852 data_type = "uint";
853 break;
854 default:
855 data_type = "<unknown>";
856 break;
857 }
858
859 append_format(str, len,
860 "%sversion: %d\n"
861 "%sbpp: %d\n"
862 "%stype: %s\n"
863 "%spostctr: %d\n"
864 "%sflags: %x\n"
865 "%swidth: %u\n"
866 "%sheight: %u\n"
867 "%sscale: %f\n"
868 "%soffset: %f\n",
869 indent, atom->demi_atom.version,
870 indent, atom->demi_atom.bpp,
871 indent, data_type,
872 indent, (atom->demi_atom.flags >> 2) & 1,
873 indent, atom->demi_atom.flags,
874 indent, atom->demi_atom.width,
875 indent, atom->demi_atom.height,
876 indent, atom->demi_atom.scale,
877 indent, atom->demi_atom.offset);
878 free(indent);
879}
880
881static void
882dump_atom(const dsf_atom_t *atom, char **str, size_t *len, int depth)
883{
884 char *indent = safe_calloc((depth * INDENT_DEPTH) + 1,
885 sizeof (*indent));
886
887 memset(indent, ' ', depth * INDENT_DEPTH);
888 indent[depth * INDENT_DEPTH] = '\0';
889
890 append_format(str, len,
891 "%s%c%c%c%c\t%lx\n",
892 indent,
893 (atom->id & 0xff000000) >> 24,
894 (atom->id & 0xff0000) >> 16,
895 (atom->id & 0xff00) >> 8,
896 (atom->id & 0xff),
897 (unsigned long)atom->payload_sz);
898
899 if (atom->id == DSF_ATOM_PROP)
900 dump_prop_atom(atom, str, len, depth + 1);
901 else if (atom->id == DSF_ATOM_POOL)
902 dump_planar_atom(atom, str, len, depth + 1);
903 else if (atom->id == DSF_ATOM_PO32)
904 dump_planar_atom(atom, str, len, depth + 1);
905 else if (atom->id == DSF_ATOM_DEMI)
906 dump_demi_atom(atom, str, len, depth + 1);
907
908 for (dsf_atom_t *subatom = list_head(&atom->subatoms); subatom != NULL;
909 subatom = list_next(&atom->subatoms, subatom)) {
910 dump_atom(subatom, str, len, depth + 1);
911 }
912 free(indent);
913}
914
921char *
922dsf_dump(const dsf_t *dsf)
923{
924 char *str = NULL;
925 size_t len = 0;
926
927 append_format(&str, &len,
928 "Version: %d\n"
929 "Size: %lx\n"
930 "MD5: %02x%02x%02x%02x%02x%02x%02x%02x"
931 "%02x%02x%02x%02x%02x%02x%02x%02x\n"
932 "Atoms:\n",
933 dsf->version, (unsigned long)dsf->size,
934 dsf->md5sum[0], dsf->md5sum[1], dsf->md5sum[2], dsf->md5sum[3],
935 dsf->md5sum[4], dsf->md5sum[5], dsf->md5sum[6], dsf->md5sum[7],
936 dsf->md5sum[8], dsf->md5sum[9], dsf->md5sum[10], dsf->md5sum[11],
937 dsf->md5sum[12], dsf->md5sum[13], dsf->md5sum[14], dsf->md5sum[15]);
938 for (const dsf_atom_t *atom = list_head(&dsf->atoms); atom != NULL;
939 atom = list_next(&dsf->atoms, atom)) {
940 dump_atom(atom, &str, &len, 1);
941 }
942
943 /* Cut off the last newline character */
944 str[len - 1] = '\0';
945
946 return (str);
947}
948
962const dsf_atom_t *
963dsf_lookup(const dsf_t *dsf, ...)
964{
965 va_list ap;
966 unsigned num_args = 0;
967 uint32_t atom_id;
968 dsf_lookup_t *lookup;
969 const dsf_atom_t *atom;
970
971 va_start(ap, dsf);
972 while ((atom_id = va_arg(ap, uint32_t)) != 0) {
973 (void) va_arg(ap, unsigned);
974 num_args++;
975 }
976 va_end(ap);
977
978 lookup = safe_calloc(num_args + 1, sizeof (*lookup));
979
980 va_start(ap, dsf);
981 for (unsigned i = 0; i < num_args; i++) {
982 lookup[i].atom_id = va_arg(ap, uint32_t);
983 lookup[i].idx = va_arg(ap, unsigned);
984 }
985 va_end(ap);
986
987 atom = dsf_lookup_v(dsf, lookup);
988 free(lookup);
989
990 return (atom);
991}
992
1005const dsf_atom_t *
1006dsf_lookup_v(const dsf_t *dsf, const dsf_lookup_t *lookup)
1007{
1008 const list_t *list = &dsf->atoms;
1009 dsf_atom_t *atom = NULL;
1010
1011 for (int i = 0; lookup[i].atom_id != 0; i++) {
1012 unsigned seen = 0;
1013 const dsf_lookup_t *l = &lookup[i];
1014
1015 for (atom = list_head(list); atom != NULL;
1016 atom = list_next(list, atom)) {
1017 if (atom->id == l->atom_id) {
1018 if (l->idx == seen)
1019 break;
1020 seen++;
1021 }
1022 }
1023 /* Required atom not found. */
1024 if (atom == NULL)
1025 return (NULL);
1026 list = &atom->subatoms;
1027 }
1028
1029 return (atom);
1030}
1031
1051const dsf_atom_t *
1052dsf_iter(const dsf_atom_t *parent, uint32_t atom_id, const dsf_atom_t *prev)
1053{
1054 for (const dsf_atom_t *atom = (prev != NULL ?
1055 list_next(&parent->subatoms, prev) :
1056 list_head(&parent->subatoms)); atom != NULL;
1057 atom = list_next(&parent->subatoms, atom)) {
1058 if (atom->id == atom_id)
1059 return (atom);
1060 }
1061 return (NULL);
1062}
1063
1080bool_t
1081dsf_parse_cmds(const dsf_t *dsf, dsf_cmd_cb_t user_cbs[NUM_DSF_CMDS],
1082 void *userinfo, char reason[DSF_REASON_SZ])
1083{
1084 dsf_cmd_parser_t parser;
1085 const dsf_atom_t *cmds_atom;
1086 const uint8_t *payload, *payload_end;
1087 char subreason[DSF_REASON_SZ] = { 0 };
1088
1089 memset(&parser, 0, sizeof (parser));
1090
1091 parser.dsf = dsf;
1092 parser.junct_off = IDX_UNSET;
1093 parser.defn_idx = IDX_UNSET;
1094 parser.road_subt = IDX_UNSET;
1095 parser.userinfo = userinfo;
1096 parser.reason = subreason;
1097
1098 cmds_atom = dsf_lookup(dsf, DSF_ATOM_CMDS, 0);
1099 if (cmds_atom == NULL) {
1100 if (reason != NULL)
1101 snprintf(reason, DSF_REASON_SZ, "CMDS atom not found");
1102 return (B_FALSE);
1103 }
1104
1105 payload = cmds_atom->payload;
1106 payload_end = cmds_atom->payload + cmds_atom->payload_sz;
1107 while (payload < payload_end) {
1108 int cmd_id = *payload;
1109 int n;
1110 dsf_cmd_t cmd;
1111
1112 parser.cmd_file_off = (payload - cmds_atom->payload) +
1113 cmds_atom->file_off + 8;
1114
1115 ASSERT(cmd_id <= DSF_CMD_ID_MAX &&
1116 cmd_parser_info[cmd_id].cb != NULL);
1117
1118 if (cmd_id > DSF_CMD_ID_MAX ||
1119 cmd_parser_info[cmd_id].cb == NULL) {
1120 if (reason != NULL) {
1121 snprintf(reason, DSF_REASON_SZ,
1122 "invalid command ID %x at offset %lx",
1123 cmd_id, (long)(payload -
1124 cmds_atom->payload + cmds_atom->file_off));
1125 }
1126 return (B_FALSE);
1127 }
1128 cmd = cmd_parser_info[cmd_id].cmd;
1129 n = cmd_parser_info[cmd_id].cb(&parser, cmd, payload + 1,
1130 payload_end - payload - 1, user_cbs[cmd]);
1131 if (n < 0) {
1132 if (reason != NULL) {
1133 snprintf(reason, DSF_REASON_SZ,
1134 "malformed command %s at offset %lx: %s",
1135 dsf_cmd2str(cmd_parser_info[cmd_id].cmd),
1136 (long)parser.cmd_file_off, subreason);
1137 }
1138 return (B_FALSE);
1139 }
1140 payload += 1 + n;
1141 ASSERT3P(payload, <=, payload_end);
1142 }
1143
1144 return (B_TRUE);
1145}
1146
1151const char *
1152dsf_cmd2str(dsf_cmd_t cmd)
1153{
1154 VERIFY3U(cmd, <, NUM_DSF_CMDS);
1155
1156 switch (cmd) {
1157 case DSF_POOL_SEL:
1158 return ("POOL_SEL");
1159 case DSF_JUNCT_OFFSET_SEL:
1160 return ("JUNCT_OFFSET_SEL");
1161 case DSF_SET_DEFN8:
1162 return ("SET_DEFN8");
1163 case DSF_SET_DEFN16:
1164 return ("SET_DEFN16");
1165 case DSF_SET_DEFN32:
1166 return ("SET_DEFN32");
1167 case DSF_ROAD_SUBTYPE:
1168 return ("ROAD_SUBTYPE");
1169 case DSF_OBJ:
1170 return ("OBJ");
1171 case DSF_OBJ_RNG:
1172 return ("OBJ_RNG");
1173 case DSF_NET_CHAIN:
1174 return ("NET_CHAIN");
1175 case DSF_NET_CHAIN_RNG:
1176 return ("NET_CHAIN_RNG");
1177 case DSF_NET_CHAIN32:
1178 return ("NET_CHAIN32");
1179 case DSF_POLY:
1180 return ("POLY");
1181 case DSF_POLY_RNG:
1182 return ("POLY_RNG");
1183 case DSF_NEST_POLY:
1184 return ("NEST_POLY");
1185 case DSF_NEST_POLY_RNG:
1186 return ("NEST_POLY_RNG");
1187 case DSF_TERR_PATCH:
1188 return ("TERR_PATCH");
1189 case DSF_TERR_PATCH_FLAGS:
1190 return ("TERR_PATCH_FLAGS");
1191 case DSF_TERR_PATCH_FLAGS_N_LOD:
1192 return ("TERR_PATCH_FLAGS_N_LOD");
1193 case DSF_PATCH_TRIA:
1194 return ("PATCH_TRIA");
1195 case DSF_PATCH_TRIA_XPOOL:
1196 return ("PATCH_TRIA_XPOOL");
1197 case DSF_PATCH_TRIA_RNG:
1198 return ("PATCH_TRIA_RNG");
1199 case DSF_PATCH_TRIA_STRIP:
1200 return ("PATCH_TRIA_STRIP");
1201 case DSF_PATCH_TRIA_STRIP_XPOOL:
1202 return ("PATCH_TRIA_STRIP_XPOOL");
1203 case DSF_PATCH_TRIA_STRIP_RNG:
1204 return ("PATCH_TRIA_STRIP_RNG");
1205 case DSF_PATCH_TRIA_FAN:
1206 return ("PATCH_TRIA_FAN");
1207 case DSF_PATCH_TRIA_FAN_XPOOL:
1208 return ("PATCH_TRIA_FAN_XPOOL");
1209 case DSF_PATCH_TRIA_FAN_RNG:
1210 return ("PATCH_TRIA_FAN_RNG");
1211 case DSF_COMMENT8:
1212 return ("COMMENT8");
1213 case DSF_COMMENT16:
1214 return ("COMMENT16");
1215 case DSF_COMMENT32:
1216 return ("COMMENT32");
1217 default:
1218 VERIFY_FAIL();
1219 }
1220}
1221
1222#define CHECK_LEN(b) \
1223 do { \
1224 if ((b) > (int)len) { \
1225 snprintf(parser->reason, DSF_REASON_SZ, \
1226 "not enough data in command (wanted %d bytes, " \
1227 "have %d bytes)", (b), (int)len); \
1228 return (-1); \
1229 } \
1230 } while (0)
1231
1232static int
1233parse_idx_rng_common(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1234 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1235{
1236 if (parser->pool == NULL) {
1237 snprintf(parser->reason, DSF_REASON_SZ,
1238 "no current POOL/SCAL selected");
1239 return (-1);
1240 }
1241
1242 CHECK_LEN(4);
1243 if (cb != NULL) {
1244 dsf_idx_rng_arg_t arg = { read_u16(data), read_u16(data + 2) };
1245 if (arg.first >= parser->pool->planar_atom.data_count) {
1246 snprintf(parser->reason, DSF_REASON_SZ,
1247 "range start index (%x) out of bounds for "
1248 "corresponding POOL atom (max: %x)", arg.first,
1249 parser->pool->planar_atom.data_count);
1250 return (-1);
1251 }
1252 if (arg.last_plus_one > parser->pool->planar_atom.data_count) {
1253 snprintf(parser->reason, DSF_REASON_SZ,
1254 "range end index (%x) out of bounds for "
1255 "corresponding POOL atom (max: %x)",
1256 arg.last_plus_one,
1257 parser->pool->planar_atom.data_count);
1258 return (-1);
1259 }
1260 if (arg.first >= arg.last_plus_one) {
1261 snprintf(parser->reason, DSF_REASON_SZ, "range start "
1262 "index (%x) not lower than end index (%x)",
1263 arg.first, arg.last_plus_one);
1264 return (-1);
1265 }
1266 cb(cmd, &arg, parser);
1267 }
1268 return (4);
1269}
1270
1271static int
1272parse_idx_list_common(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1273 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1274{
1276
1277 if (parser->pool == NULL) {
1278 snprintf(parser->reason, DSF_REASON_SZ,
1279 "no current POOL/SCAL selected");
1280 return (-1);
1281 }
1282
1283 CHECK_LEN(1);
1284 arg.num_coords = *data;
1285 CHECK_LEN(1 + arg.num_coords * 2);
1286 if (cb != NULL) {
1287 for (int i = 0; i < arg.num_coords; i++) {
1288 arg.indices[i] = read_u16(&data[1 + i * 2]);
1289 if (arg.indices[i] >=
1290 parser->pool->planar_atom.data_count) {
1291 snprintf(parser->reason, DSF_REASON_SZ,
1292 "index %d (%x) out of bounds of "
1293 "corresponding POOL atom", i,
1294 arg.indices[i]);
1295 return (-1);
1296 }
1297 }
1298 cb(cmd, &arg, parser);
1299 }
1300 return (1 + arg.num_coords * 2);
1301}
1302
1303static int
1304parse_idx_list_xpool_common(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1305 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1306{
1308
1309 if (parser->pool == NULL) {
1310 snprintf(parser->reason, DSF_REASON_SZ,
1311 "no current POOL/SCAL selected");
1312 return (-1);
1313 }
1314
1315 CHECK_LEN(1);
1316 arg.num_coords = *data;
1317 CHECK_LEN(1 + 2 * arg.num_coords * 2);
1318 if (cb != NULL) {
1319 for (int i = 0; i < arg.num_coords; i++) {
1320 int seq = read_u16(&data[1 + i * 4]);
1321 arg.indices[i].pool = dsf_lookup(parser->dsf,
1322 DSF_ATOM_GEOD, 0, DSF_ATOM_POOL, seq, 0);
1323 arg.indices[i].scal = dsf_lookup(parser->dsf,
1324 DSF_ATOM_GEOD, 0, DSF_ATOM_SCAL, seq, 0);
1325 if (arg.indices[i].pool == NULL ||
1326 arg.indices[i].scal == NULL) {
1327 snprintf(parser->reason, DSF_REASON_SZ,
1328 "index %d POOL or SCAL atom %d not found",
1329 i, seq);
1330 return (-1);
1331 }
1332 arg.indices[i].idx = read_u16(&data[1 + i * 4 + 2]);
1333 if (arg.indices[i].idx >=
1334 arg.indices[i].pool->planar_atom.data_count) {
1335 snprintf(parser->reason, DSF_REASON_SZ,
1336 "index %d (%x) out of bounds of "
1337 "corresponding POOL atom", i,
1338 arg.indices[i].idx);
1339 return (-1);
1340 }
1341 }
1342 cb(cmd, &arg, parser);
1343 }
1344 return (1 + 2 * arg.num_coords * 2);
1345}
1346
1347static int
1348pool_sel_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1349 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1350{
1351 int pool_idx;
1352
1353 CHECK_LEN(2);
1354 pool_idx = read_u16(data);
1355
1356 parser->pool = dsf_lookup(parser->dsf, DSF_ATOM_GEOD, 0,
1357 DSF_ATOM_POOL, pool_idx, 0);
1358 parser->scal = dsf_lookup(parser->dsf, DSF_ATOM_GEOD, 0,
1359 DSF_ATOM_SCAL, pool_idx, 0);
1360 if (parser->pool == NULL || parser->scal == NULL) {
1361 snprintf(parser->reason, DSF_REASON_SZ,
1362 "POOL or SCAL atom with index %d not found", pool_idx);
1363 return (-1);
1364 }
1365
1366 if (cb != NULL)
1367 cb(cmd, NULL, parser);
1368
1369 return (2);
1370}
1371
1372static int
1373junct_off_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1374 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1375{
1376 CHECK_LEN(4);
1377 parser->junct_off = read_u32(data);
1378 if (cb != NULL)
1379 cb(cmd, NULL, parser);
1380 return (4);
1381}
1382
1383static int
1384set_defn_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1385 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1386{
1387 int cmdlen;
1388 if (cmd == DSF_SET_DEFN8) {
1389 CHECK_LEN(1);
1390 parser->defn_idx = *data;
1391 cmdlen = 1;
1392 } else if (cmd == DSF_SET_DEFN16) {
1393 CHECK_LEN(2);
1394 parser->defn_idx = read_u16(data);
1395 cmdlen = 2;
1396 } else {
1397 ASSERT3U(cmd, ==, DSF_SET_DEFN32);
1398 CHECK_LEN(4);
1399 parser->defn_idx = read_u32(data);
1400 cmdlen = 4;
1401 }
1402 if (cb != NULL)
1403 cb(cmd, NULL, parser);
1404 return (cmdlen);
1405}
1406
1407static int
1408road_subt_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1409 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1410{
1411 CHECK_LEN(1);
1412 parser->road_subt = *data;
1413 if (cb != NULL)
1414 cb(cmd, NULL, parser);
1415 return (1);
1416}
1417
1418static int
1419obj_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1420 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1421{
1422 if (cmd == DSF_OBJ) {
1423 uint16_t idx;
1424 CHECK_LEN(2);
1425 idx = read_u16(data);
1426 if (cb != NULL)
1427 cb(cmd, (void *)(uintptr_t)idx, parser);
1428 return (2);
1429 } else {
1430 ASSERT3U(cmd, ==, DSF_OBJ_RNG);
1431 return (parse_idx_rng_common(parser, cmd, data, len, cb));
1432 }
1433}
1434
1435static int
1436net_chain_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1437 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1438{
1439 if (cmd == DSF_NET_CHAIN) {
1440 return (parse_idx_list_common(parser, cmd, data, len, cb));
1441 } else if (cmd == DSF_NET_CHAIN_RNG) {
1442 return (parse_idx_rng_common(parser, cmd, data, len, cb));
1443 } else {
1445
1446 ASSERT3U(cmd, ==, DSF_NET_CHAIN32);
1447 CHECK_LEN(1);
1448 arg.num_coords = *data;
1449 CHECK_LEN(1 + arg.num_coords * 4);
1450 if (cb != NULL) {
1451 for (int i = 0; i < arg.num_coords; i++)
1452 arg.indices[i] = read_u32(&data[1 + i * 4]);
1453 cb(cmd, &arg, parser);
1454 }
1455 return (1 + arg.num_coords * 4);
1456 }
1457}
1458
1459static int
1460parse_nest_poly_wind(dsf_cmd_parser_t *parser, const uint8_t *data,
1461 size_t len, dsf_cmd_cb_t cb, dsf_poly_arg_t *arg)
1462{
1463 CHECK_LEN(1);
1464 arg->num_coords = *data;
1465 data++;
1466 len--;
1467
1468 CHECK_LEN(arg->num_coords * 2);
1469 if (cb == NULL)
1470 return (1 + arg->num_coords * 2);
1471
1472 for (int i = 0; i < arg->num_coords; i++) {
1473 arg->indices[i] = read_u16(&data[i * 2]);
1474 if (arg->indices[i] >= parser->pool->planar_atom.data_count) {
1475 snprintf(parser->reason, DSF_REASON_SZ,
1476 "index %d (%x) out of bounds of corresponding "
1477 "POOL atom", i, arg->indices[i]);
1478 return (-1);
1479 }
1480 }
1481 cb(DSF_NEST_POLY, &arg, parser);
1482
1483 return (1 + arg->num_coords * 2);
1484}
1485
1486static int poly_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1487 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1488{
1489 if (cmd == DSF_POLY || cmd == DSF_NEST_POLY_RNG) {
1490 dsf_poly_arg_t arg;
1491
1492 CHECK_LEN(3);
1493 arg.param = read_u16(data);
1494 arg.num_coords = data[2];
1495 if (cmd == DSF_NEST_POLY_RNG)
1496 /* The spec lies, there's an extra uint16 here */
1497 arg.num_coords++;
1498 CHECK_LEN(3 + arg.num_coords * 2);
1499 if (cb != NULL) {
1500 for (int i = 0; i < arg.num_coords; i++)
1501 arg.indices[i] = read_u16(3 + &data[i * 2]);
1502 cb(cmd, &arg, parser);
1503 }
1504 return (3 + arg.num_coords * 2);
1505 } else if (cmd == DSF_POLY_RNG) {
1506 CHECK_LEN(6);
1507 if (cb != NULL) {
1508 dsf_poly_rng_arg_t arg = {
1509 read_u16(data),
1510 read_u16(data + 2),
1511 read_u16(data + 4)
1512 };
1513 cb(cmd, &arg, parser);
1514 }
1515 return (6);
1516 } else {
1517 dsf_poly_arg_t arg;
1518 int n_wind;
1519 size_t total = 0;
1520
1521 ASSERT3U(cmd, ==, DSF_NEST_POLY);
1522 CHECK_LEN(3);
1523 arg.param = read_u16(data);
1524 n_wind = data[2];
1525 data += 3;
1526 len -= 3;
1527 total += 3;
1528 for (int i = 0; i < n_wind; i++) {
1529 int l = parse_nest_poly_wind(parser, data, len, cb,
1530 &arg);
1531 if (l < 0)
1532 return (-1);
1533 total += l;
1534 }
1535 return (total);
1536 }
1537}
1538
1539static int
1540terr_patch_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1541 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1542{
1543 if (cmd == DSF_TERR_PATCH) {
1544 if (cb != NULL)
1545 cb(cmd, NULL, parser);
1546 return (0);
1547 } else if (cmd == DSF_TERR_PATCH_FLAGS) {
1548 CHECK_LEN(1);
1549 if (cb != NULL)
1550 cb(cmd, (void *)(uintptr_t)*data, parser);
1551 return (1);
1552 } else {
1553 ASSERT3U(cmd, ==, DSF_TERR_PATCH_FLAGS_N_LOD);
1554 CHECK_LEN(9);
1555 if (cb != NULL) {
1556 dsf_flags_n_lod_arg_t arg = {
1557 *data, *(float *)(data + 1), *(float *)(data + 5)
1558 };
1559 cb(cmd, &arg, parser);
1560 }
1561 return (9);
1562 }
1563}
1564
1565static int
1566patch_tria_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1567 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1568{
1569 if (cmd == DSF_PATCH_TRIA) {
1570 return (parse_idx_list_common(parser, cmd, data, len, cb));
1571 } else if (cmd == DSF_PATCH_TRIA_XPOOL) {
1572 return (parse_idx_list_xpool_common(parser, cmd, data, len,
1573 cb));
1574 } else if (cmd == DSF_PATCH_TRIA_RNG) {
1575 return (parse_idx_rng_common(parser, cmd, data, len, cb));
1576 } else if (cmd == DSF_PATCH_TRIA_STRIP) {
1577 return (parse_idx_list_common(parser, cmd, data, len, cb));
1578 } else if (cmd == DSF_PATCH_TRIA_STRIP_XPOOL) {
1579 return (parse_idx_list_xpool_common(parser, cmd, data, len,
1580 cb));
1581 } else if (cmd == DSF_PATCH_TRIA_STRIP_RNG) {
1582 return (parse_idx_rng_common(parser, cmd, data, len, cb));
1583 } else if (cmd == DSF_PATCH_TRIA_FAN) {
1584 return (parse_idx_list_common(parser, cmd, data, len, cb));
1585 } else if (cmd == DSF_PATCH_TRIA_FAN_XPOOL) {
1586 return (parse_idx_list_xpool_common(parser, cmd, data, len,
1587 cb));
1588 } else {
1589 ASSERT3U(cmd, ==, DSF_PATCH_TRIA_FAN_RNG);
1590 return (parse_idx_rng_common(parser, cmd, data, len, cb));
1591 }
1592}
1593
1594static int
1595comment_cb(dsf_cmd_parser_t *parser, dsf_cmd_t cmd,
1596 const uint8_t *data, size_t len, dsf_cmd_cb_t cb)
1597{
1599 size_t hdrlen = 0;
1600
1601 if (cmd == DSF_COMMENT8) {
1602 CHECK_LEN(1);
1603 hdrlen = 1;
1604 arg.len = *data;
1605 } else if (cmd == DSF_COMMENT16) {
1606 CHECK_LEN(2);
1607 hdrlen = 2;
1608 arg.len = read_u16(data);
1609 } else if (cmd == DSF_COMMENT32) {
1610 CHECK_LEN(4);
1611 hdrlen = 4;
1612 arg.len = read_u32(data);
1613 }
1614 if (cb != NULL) {
1615 arg.data = &data[1];
1616 cb(cmd, &arg, parser);
1617 }
1618
1619 return (hdrlen + arg.len);
1620}
1621
1622#undef CHECK_LEN
#define ASSERT3P(x, op, y)
Definition assert.h:212
#define VERIFY_FAIL()
Definition assert.h:160
#define VERIFY(x)
Definition assert.h:78
#define ASSERT3U(x, op, y)
Definition assert.h:210
#define ASSERT(x)
Definition assert.h:208
#define VERIFY3U(x, op, y)
Definition assert.h:136
void * decompress_7z(const char *filename, size_t *out_len)
Definition compress_7z.c:61
bool_t test_7z(const void *in_buf, size_t len)
Definition compress_7z.c:41
const dsf_atom_t * dsf_iter(const dsf_atom_t *parent, uint32_t atom_id, const dsf_atom_t *prev)
Definition dsf.c:1052
const dsf_atom_t * dsf_lookup_v(const dsf_t *dsf, const dsf_lookup_t *lookup)
Definition dsf.c:1006
bool_t dsf_parse_cmds(const dsf_t *dsf, dsf_cmd_cb_t user_cbs[NUM_DSF_CMDS], void *userinfo, char reason[DSF_REASON_SZ])
Definition dsf.c:1081
dsf_t * dsf_parse(uint8_t *buf, size_t bufsz, char reason[DSF_REASON_SZ])
Definition dsf.c:707
const dsf_atom_t * dsf_lookup(const dsf_t *dsf,...)
Definition dsf.c:963
const char * dsf_cmd2str(dsf_cmd_t cmd)
Definition dsf.c:1152
dsf_t * dsf_init(const char *filename)
Definition dsf.c:173
void dsf_fini(dsf_t *dsf)
Definition dsf.c:786
char * dsf_dump(const dsf_t *dsf)
Definition dsf.c:922
ssize_t filesz(const char *filename)
Definition helpers.c:1174
void append_format(char **str, size_t *sz, const char *format,...)
Definition helpers.c:731
void list_destroy(list_t *)
Definition list.c:136
void * list_head(const list_t *)
Definition list.c:292
void list_create(list_t *, size_t, size_t)
Definition list.c:113
void * list_next(const list_t *, const void *)
Definition list.c:344
void * list_remove_head(list_t *)
Definition list.c:251
void list_insert_tail(list_t *, void *)
Definition list.c:213
#define logMsg(...)
Definition log.h:112
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
Definition dsf.h:156