#include "stlfile.h" #include "util.h" void stack_init(struct stack *stack) { stack->cap = 10; stack->data = checkp(malloc(sizeof(int) * stack->cap)); stack->count = 0; } void stack_push(struct stack *stack, int v) { if (stack->count == stack->cap) { stack->cap *= 2; stack->data = checkp(realloc(stack->data, sizeof(int) * stack->cap)); } stack->data[stack->count] = v; stack->count++; } int stack_pop(struct stack *stack) { if (stack->count == 0) die("popping empty stack!\n"); stack->count--; return stack->data[stack->count]; } void stack_free(struct stack *stack) { free(stack->data); } int consume_keyword(char **start, char *end) { static const struct { int code; const char *str; } mapping[] = { { KW_SOLID_BEGIN, "solid" }, { KW_SOLID_END, "endsolid" }, { KW_LOOP_BEGIN, "outer loop" }, { KW_LOOP_END, "endloop" }, { KW_FACET_BEGIN, "facet normal" }, { KW_FACET_END, "endfacet" }, { KW_VERTEX, "vertex" }, }; char *bp; int i; for (bp = *start; strchr(" \r\n\t", *bp); bp++); for (i = 0; i < ARRSIZE(mapping); i++) if (!strcmp(mapping[i].str, bp)) return mapping[i].code; return KW_UNKNOWN; } int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) { char *bp, *pbp, *end = buf + len; struct stack states; int type, kw; stack_init(&states); #define PARSER_ASSERT_NESTING(x, y) \ do { if ((x) != (y)) { \ fprintf(stderr, "STL Format error: invalid nesting!"); \ return FAIL; \ } } while (0) info = checkp(malloc(sizeof(struct parseinfo))); info->type = TYPE_ASCII; while ((kw = consume_keyword(&bp, end))) { switch (kw) { case KW_SOLID_BEGIN: stack_push(&states, STATE_SOLID); break; case KW_SOLID_END: if (stack_pop(&states) == STATE_SOLID) break; goto unknown; case KW_LOOP_BEGIN: stack_push(&states, STATE_LOOP); break; case KW_LOOP_END: if (stack_pop(&states) == STATE_LOOP) break; goto unknown; case KW_FACET_BEGIN: stack_push(&states, STATE_FACET); break; case KW_FACET_END: if (stack_pop(&states) == STATE_FACET) break; goto unknown; unknown: case KW_UNKNOWN: fprintf(stderr, "Encountered unexpected keyword: '%.*s..'", 30, bp); return FAIL; } } stack_free(&states); return OK; fail: stack_free(&states); free(info); return FAIL; } int parse_file_bin(struct parseinfo *info, char *buf, size_t len) { char *bp, *end = buf + len; unsigned int i; if (len < 84) { fprintf(stderr, "Truncated data! (header missing)\n"); return FAIL; } /* treat header as model name */ info->namehash = strdup(mhash(buf, 80)); bp = buf + 80; info->loopcount = le32toh(*(uint32_t*)bp); for (i = 0; i < info->loopcount; i++) { if (bp + 50 > end) { fprintf(stderr, "Truncated data! (loops missing)\n"); return FAIL; } bp += 50; } return OK; } int parse_file(struct parseinfo *info, char *buf, size_t len) { char *bp; if (info->valid) { NULLFREE(info->infopath); NULLFREE(info->namehash); NULLFREE(info->stlpath); } /* check bin vs ascii */ for (bp = buf; strchr(" \n\r\t", *bp); bp++); return !strncmp("solid ", bp, 6) ? parse_file_ascii(info, buf, len) : parse_file_bin(info, buf, len); }