enowars5-service-stldoctor

STL-Analyzing A/D Service for ENOWARS5 in 2021
git clone https://git.sinitax.com/sinitax/enowars5-service-stldoctor
Log | Files | Refs | README | LICENSE | sfeed.txt

stlfile.c (9164B)


      1#include "stlfile.h"
      2
      3static const char wsset[] = " \r\n\t";
      4static const struct {
      5	int code;
      6	const char *str;
      7} kwmap[] = {
      8	{ KW_SOLID_BEGIN, "solid"        },
      9	{ KW_SOLID_END,   "endsolid"     },
     10	{ KW_LOOP_BEGIN,  "outer loop"   },
     11	{ KW_LOOP_END,    "endloop"      },
     12	{ KW_FACET_BEGIN, "facet normal" },
     13	{ KW_FACET_END,   "endfacet"     },
     14	{ KW_VERTEX,      "vertex"       },
     15};
     16
     17void
     18stack_init(struct stack *stack)
     19{
     20	stack->cap = 10;
     21	stack->data = checkp(malloc(sizeof(int) * stack->cap));
     22	stack->count = 0;
     23}
     24
     25void
     26stack_push(struct stack *stack, int v)
     27{
     28	if (stack->count == stack->cap) {
     29		stack->cap *= 2;
     30		stack->data = realloc(stack->data, sizeof(int) * stack->cap);
     31		checkp(stack->data);
     32	}
     33
     34	stack->data[stack->count] = v;
     35	stack->count++;
     36}
     37
     38int
     39stack_pop(struct stack *stack)
     40{
     41	if (stack->count == 0)
     42		return -1;
     43
     44	stack->count--;
     45	return stack->data[stack->count];
     46}
     47
     48void
     49stack_free(struct stack *stack)
     50{
     51	free(stack->data);
     52}
     53
     54int
     55stack_ind(struct stack *stack, int search)
     56{
     57	int i;
     58	for (i = 0; i < stack->count; i++)
     59		if (stack->data[i] == search)
     60			return i;
     61	return -1;
     62}
     63
     64int
     65stack_top_eq(struct stack *stack, int cmp)
     66{
     67	if (!stack->count) return 0;
     68	return stack->data[stack->count-1] == cmp;
     69}
     70
     71int
     72isws(char c)
     73{
     74	return c && strchr(wsset, c);
     75}
     76
     77char*
     78skipws(char *p)
     79{
     80	for (; isws(*p); p++);
     81	return p;
     82}
     83
     84char*
     85consume_arg(char **start, char **end)
     86{
     87	char *c, *tmp;
     88
     89	*start = skipws(*start);
     90	if (!*start) return NULL;
     91	for (c = *start; *c && !isws(*c); c++);
     92	tmp = *start;
     93	*start = c + (*c ? 1 : 0);
     94	*end = c;
     95	return tmp;
     96}
     97
     98int
     99consume_keyword(char **start)
    100{
    101	char *bp;
    102	int i, len;
    103
    104	bp = skipws(*start);
    105
    106	for (i = 0; i < ARRSIZE(kwmap); i++) {
    107		len = strlen(kwmap[i].str);
    108		if (!strncmp(kwmap[i].str, bp, len) && (!bp[len] || isws(bp[len]))) {
    109			*start = bp + len + (bp[len] ? 1 : 0);
    110			return kwmap[i].code;
    111		}
    112	}
    113
    114	return KW_UNKNOWN;
    115}
    116
    117int
    118parse_file_ascii(struct parseinfo *info, char *buf, size_t len)
    119{
    120	char *bp, *arg, *prev, *tmp, *end;
    121	struct stack states;
    122	float farg;
    123	int i, kw;
    124
    125	stack_init(&states);
    126
    127	info->type = TYPE_ASCII;
    128	info->loopcount = 0;
    129
    130	memset(info->header, 0, 80);
    131
    132	for (i = 0; i < 3; i++) {
    133		info->bbmin[i] = INFINITY;
    134		info->bbmax[i] = -INFINITY;
    135	}
    136
    137	bp = prev = buf;
    138	while ((kw = consume_keyword(&bp))) {
    139		switch (kw) {
    140		case KW_SOLID_BEGIN:
    141			if (states.count) {
    142				FMT_ERR("Improper nesting, solid not top-level in STL\n");
    143				goto fail;
    144			}
    145			stack_push(&states, STATE_SOLID);
    146			tmp = bp;
    147			if (!consume_keyword(&bp)
    148					&& (arg = consume_arg(&bp, &end))) {
    149				info->solidname = strndup(arg, end - arg);
    150			} else {
    151				bp = tmp;
    152			}
    153			break;
    154		case KW_SOLID_END:
    155			if ((kw = stack_pop(&states)) != STATE_SOLID) {
    156				FMT_ERR("Improper nesting, parent: %s\n",
    157					kwmap[kw].str);
    158				goto fail;
    159			}
    160			tmp = bp;
    161			if (info->solidname && !consume_keyword(&bp)
    162					&& (arg = consume_arg(&bp, &end))) {
    163				if (strncmp(info->solidname, arg, end - arg)) {
    164					FMT_ERR("Solid names do not match!\n");
    165					goto fail;
    166				}
    167			} else {
    168				bp = tmp;
    169			}
    170			break;
    171		case KW_LOOP_BEGIN:
    172			if (!stack_top_eq(&states, STATE_FACET)) {
    173				FMT_ERR("Loop parent is not a facet!\n");
    174				goto fail;
    175			}
    176			stack_push(&states, STATE_LOOP);
    177			break;
    178		case KW_LOOP_END:
    179			if ((kw = stack_pop(&states)) != STATE_LOOP) {
    180				FMT_ERR("Improper nesting, parent: %s\n",
    181					kwmap[kw].str);
    182				goto fail;
    183			}
    184			info->loopcount++;
    185			break;
    186		case KW_FACET_BEGIN:
    187			if (!stack_top_eq(&states, STATE_SOLID)) {
    188				FMT_ERR("Facet parent is not a solid!\n");
    189				goto fail;
    190			}
    191			stack_push(&states, STATE_FACET);
    192			for (i = 0; i < 3; i++) {
    193				if (!(arg = consume_arg(&bp, &end))) {
    194					FMT_ERR("Facet with < 3 args!\n");
    195					goto fail;
    196				}
    197				farg = strtof(arg, &tmp);
    198				if (!isws(*tmp)) {
    199					FMT_ERR("Facet arg '%s'\n", arg);
    200					goto fail;
    201				}
    202			}
    203			break;
    204		case KW_FACET_END:
    205			if ((kw = stack_pop(&states)) != STATE_FACET) {
    206				FMT_ERR("Improper nesting, parent: %s\n",
    207					kwmap[kw].str);
    208				goto fail;
    209			}
    210			break;
    211		case KW_VERTEX:
    212			if (!stack_top_eq(&states, STATE_LOOP)) {
    213				FMT_ERR("Vertex parent is not a loop!\n");
    214				goto fail;
    215			}
    216			for (i = 0; i < 3; i++) {
    217				if (!(arg = consume_arg(&bp, &end))) {
    218					FMT_ERR("Vertex with < 3 args\n");
    219					goto fail;
    220				}
    221				farg = strtof(arg, &tmp);
    222				if (!isws(*tmp)) {
    223					FMT_ERR("Vertex arg '%s'\n", arg);
    224					goto fail;
    225				}
    226				info->bbmin[i] = MIN(info->bbmin[i], farg);
    227				info->bbmax[i] = MAX(info->bbmax[i], farg);
    228			}
    229			break;
    230		case KW_UNKNOWN:
    231			prev = skipws(prev);
    232			FMT_ERR("Expected keyword, got:\n%.*s...\n", 30, prev);
    233			goto fail;
    234		}
    235		prev = bp;
    236	}
    237
    238	if (states.count) {
    239		FMT_ERR("Expected keyword, got:\n%.*s...\n", 30, bp);
    240		goto fail;
    241	}
    242
    243	bp = skipws(bp);
    244	if (*bp) {
    245		FMT_ERR("Extraneous data at end of file\n");
    246		goto fail;
    247	}
    248
    249	stack_free(&states);
    250	return OK;
    251
    252fail:
    253	stack_free(&states);
    254	return FAIL;
    255}
    256
    257int
    258parse_file_bin(struct parseinfo *info, char *buf, size_t len)
    259{
    260	char *bp, *end = buf + len;
    261	int i, k;
    262	float v;
    263
    264	info->type = TYPE_BIN;
    265
    266	if (len < 84) {
    267		FMT_ERR("Truncated data! (header missing)\n");
    268		goto fail;
    269	}
    270
    271	memcpy(info->header, buf, 80);
    272
    273	info->solidname = checkp(strndup(buf + (*buf == '#'), 80));
    274
    275	bp = buf + 80;
    276
    277	info->loopcount = le32toh(*(uint32_t*)bp);
    278	bp += 4;
    279
    280	if (!info->loopcount) {
    281		memset(info->bbmax, 0, sizeof(float) * 3);
    282		memset(info->bbmin, 0, sizeof(float) * 3);
    283		return OK;
    284	}
    285
    286	for (i = 0; i < 3; i++) {
    287		info->bbmin[i] = INFINITY;
    288		info->bbmax[i] = -INFINITY;
    289	}
    290
    291	for (i = 0; i < info->loopcount; i++) {
    292		if (bp + 50 > end) {
    293			FMT_ERR("Truncated data! (loops missing)\n");
    294			goto fail;
    295		}
    296		for (k = 0; k < 12; k++, bp += 4) {
    297			v = fle32toh(*(float*)bp);
    298			if (v == INFINITY || v == NAN) {
    299				FMT_ERR("Encountered invalid float\n");
    300				goto fail;
    301			}
    302			if (k >= 3) {
    303				info->bbmin[k % 3] = MIN(info->bbmin[k % 3], v);
    304				info->bbmax[k % 3] = MAX(info->bbmax[k % 3], v);
    305			}
    306		}
    307		bp += 2;
    308	}
    309
    310	if (bp != end) {
    311		FMT_ERR("Extraneous data at end of file\n");
    312		goto fail;
    313	}
    314
    315	return OK;
    316
    317fail:
    318	FREE(info->solidname);
    319	return FAIL;
    320}
    321
    322int
    323parse_file(struct parseinfo *info, char *buf, size_t len, char **modelname)
    324{
    325	int status;
    326	const char *resp;
    327	char *bp;
    328
    329	if (info->valid)
    330		free_info(info);
    331
    332	if (len < 10) {
    333		ERR("File too small!\n");
    334		return FAIL;
    335	}
    336
    337	info->filesize = len;
    338
    339	/* check bin vs ascii with first keyword */
    340	for (bp = buf; isws(*bp); bp++);
    341	status = !strncmp("solid", bp, 5) && isws(bp[5])
    342		? parse_file_ascii(info, buf, len)
    343		: parse_file_bin(info, buf, len);
    344	if (status == FAIL) return FAIL;
    345
    346	/* transfer ownership */
    347	info->modelname = *modelname;
    348	*modelname = NULL;
    349
    350	info->hash = checkp(strdup(mhash(info->modelname, -1)));
    351
    352	return OK;
    353}
    354
    355int
    356save_info(struct parseinfo *info, FILE *f)
    357{
    358	size_t nwrote = 0;
    359	int i;
    360
    361	nwrote += fwrite(&info->type, sizeof(int), 1, f);
    362	nwrote += fwrite(&info->loopcount, sizeof(int), 1, f);
    363	nwrote += fwrite(&info->filesize, sizeof(unsigned), 1, f);
    364
    365	for (i = 0; i < 3; i++) {
    366		nwrote += fwrite(&info->bbmin[i], sizeof(float), 1, f);
    367		nwrote += fwrite(&info->bbmax[i], sizeof(float), 1, f);
    368	}
    369
    370	nwrote += fwrite(info->header, 80, 1, f);
    371
    372	if (nwrote != 10) return FAIL;
    373
    374	fputstr(f, info->solidname);
    375	fputstr(f, info->hash);
    376	fputstr(f, info->modelname);
    377
    378	return OK;
    379}
    380
    381int
    382load_info(struct parseinfo *info, FILE *f)
    383{
    384	size_t nread = 0;
    385	int i;
    386
    387	nread += fread(&info->type, sizeof(int), 1, f);
    388	nread += fread(&info->loopcount, sizeof(int), 1, f);
    389	nread += fread(&info->filesize, sizeof(unsigned), 1, f);
    390
    391	for (i = 0; i < 3; i++) {
    392		nread += fread(&info->bbmin[i], sizeof(float), 1, f);
    393		nread += fread(&info->bbmax[i], sizeof(float), 1, f);
    394	}
    395
    396	nread += fread(info->header, 80, 1, f);
    397
    398	if (nread != 10) return FAIL;
    399
    400	freadstr(f, &info->solidname);
    401	freadstr(f, &info->hash);
    402	freadstr(f, &info->modelname);
    403
    404	info->valid = 1;
    405
    406	return OK;
    407}
    408
    409void
    410print_info(struct parseinfo *info)
    411{
    412	int i, k;
    413
    414	printf(" === Model info === \n");
    415
    416	printf("  File Size: %u\n", info->filesize);
    417	if (info->type == TYPE_BIN) {
    418		printf("  Header:\n");
    419		for (i = 0; i < 80; i += k) {
    420			printf("  ");
    421			for (k = 0; k < MIN(80 - i, 20); k++)
    422				printf(" %02x", (uint8_t) info->header[i+k]);
    423			printf(" | ");
    424			for (k = 0; k < MIN(80 - i, 20); k++)
    425				putchar(PRINTABLE(info->header[i+k]));
    426			printf("\n");
    427		}
    428	}
    429	printf("  Model ID: %s\n", info->hash);
    430	printf("  Model Name: %s\n", info->modelname);
    431	printf("  Solid Name: %s\n", info->solidname);
    432	printf("  Triangle Count: %i\n", info->loopcount);
    433	printf("  Bounding Box Size: %.2f x %.2f x %.2f\n",
    434			info->bbmax[0] - info->bbmin[0],
    435			info->bbmax[1] - info->bbmin[1],
    436			info->bbmax[2] - info->bbmin[2]);
    437	printf("  Bounding Box Origin: %.2f x %.2f x %.2f\n",
    438			info->bbmin[0], info->bbmin[1], info->bbmin[2]);
    439
    440	printf(" ================== \n");
    441}
    442
    443void
    444free_info(struct parseinfo *info)
    445{
    446	FREE(info->hash);
    447	FREE(info->modelname);
    448	FREE(info->solidname);
    449	info->valid = 0;
    450}
    451
    452float fle32toh(float v)
    453{
    454	union {
    455		uint32_t u;
    456		float f;
    457	} conv;
    458
    459	conv.f = v;
    460	conv.u = le32toh(conv.u);
    461	return conv.f;
    462}