#include "stlfile.h" #include "util.h" static const char wsset[] = " \r\n\t"; 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); } long skip_set(char *start, const char *set) { char *p; for (p = start; *p && strchr(set, *p); p++); return p - start; } char* consume_arg(char **start) { char *p, *tmp; p = *start + skip_set(*start, wsset); for (; !strchr(wsset, *p); p++); *p = '\0'; tmp = *start; *start = p + 1; return tmp; } int consume_keyword(char **start) { 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, *nsep; int i, len; bp = *start + skip_set(*start, wsset); for (i = 0; i < ARRSIZE(mapping); i++) { len = strlen(mapping[i].str); if (!strncmp(mapping[i].str, bp, len) && strchr(wsset, *(bp + len))) { printf("GOT: %s\n", mapping[i].str); *start = bp + len + (bp[len] ? 1 : 0); return mapping[i].code; } } return KW_UNKNOWN; } int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) { char *bp, *nsep, *prev; struct stack states; int i, kw; stack_init(&states); info->type = TYPE_ASCII; bp = prev = buf; while ((kw = consume_keyword(&bp))) { switch (kw) { case KW_SOLID_BEGIN: /* TODO: save solid name */ stack_push(&states, STATE_SOLID); consume_arg(&bp); break; case KW_SOLID_END: /* TODO: check that name matches */ if (stack_pop(&states) != STATE_SOLID) goto unknown; consume_arg(&bp); break; case KW_LOOP_BEGIN: /* TODO: ensure only one loop */ stack_push(&states, STATE_LOOP); break; case KW_LOOP_END: if (stack_pop(&states) != STATE_LOOP) goto unknown; break; case KW_FACET_BEGIN: /* TODO: check if args are integers */ stack_push(&states, STATE_FACET); for (i = 0; i < 3 && consume_arg(&bp); i++); break; case KW_FACET_END: if (stack_pop(&states) != STATE_FACET) goto unknown; break; case KW_VERTEX: /* TODO: check if args are integers */ for (i = 0; i < 3 && consume_arg(&bp); i++); break; unknown: case KW_UNKNOWN: prev += skip_set(prev, wsset); fprintf(stderr, "Expected keyword, got:\n%.*s...\n", 30, prev); return FAIL; } prev = bp; } if (states.count) { fprintf(stderr, "Expected keyword, got:\n%.*s...\n", 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(wsset, *bp); bp++); return !strncmp("solid ", bp, 6) ? parse_file_ascii(info, buf, len) : parse_file_bin(info, buf, len); }