#include "stlfile.h" #include "util.h" static const char wsset[] = " \r\n\t"; static const struct { int code; const char *str; } kwmap[] = { { 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" }, }; 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) return -1; stack->count--; return stack->data[stack->count]; } void stack_free(struct stack *stack) { free(stack->data); } int stack_ind(struct stack *stack, int search) { int i; for (i = 0; i < stack->count; i++) if (stack->data[i] == search) return i; return -1; } 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) { char *bp; int i, len; bp = *start + skip_set(*start, wsset); for (i = 0; i < ARRSIZE(kwmap); i++) { len = strlen(kwmap[i].str); if (!strncmp(kwmap[i].str, bp, len) && strchr(wsset, *(bp + len))) { printf("GOT: %s\n", kwmap[i].str); *start = bp + len + (bp[len] ? 1 : 0); return kwmap[i].code; } } return KW_UNKNOWN; } #define PARSE_FAIL(...) \ do { fprintf(stderr, "FORMAT ERR: " __VA_ARGS__); goto fail; } while (0) int parse_file_ascii(struct parseinfo *info, char *buf, size_t len) { char *bp, *arg, *prev, *tmp; struct stack states; float farg; int i, kw; stack_init(&states); info->type = TYPE_ASCII; info->loopcount = 0; bp = prev = buf; while ((kw = consume_keyword(&bp))) { switch (kw) { case KW_SOLID_BEGIN: stack_push(&states, STATE_SOLID); if (stack_ind(&states, KW_SOLID_BEGIN) != -1) PARSE_FAIL("multiple nested solids!\n"); tmp = bp; if (!consume_keyword(&bp) && (arg = consume_arg(&bp))) { memcpy(info->extra, arg, MIN(strlen(arg), 80)); } else { bp = tmp; } info->namehash = checkp(strdup(mhash(info->extra, 80))); break; case KW_SOLID_END: if ((kw = stack_pop(&states)) != STATE_SOLID) PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); tmp = bp; if (*info->extra && !consume_keyword(&bp) && (arg = consume_arg(&bp))) { if (strncmp(info->extra, arg, 80)) PARSE_FAIL("solid end/begin names do not match!\n"); } else { bp = tmp; } break; case KW_LOOP_BEGIN: stack_push(&states, STATE_LOOP); if (stack_ind(&states, KW_LOOP_BEGIN) != -1) PARSE_FAIL("multiple nested loops!\n"); break; case KW_LOOP_END: if ((kw = stack_pop(&states)) != STATE_LOOP) PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); info->loopcount++; break; case KW_FACET_BEGIN: stack_push(&states, STATE_FACET); if (stack_ind(&states, KW_LOOP_BEGIN) != -1) PARSE_FAIL("multiple nested facets!\n"); for (i = 0; i < 3; i++) { if (!(arg = consume_arg(&bp))) PARSE_FAIL("Facet with less than 3 args!\n"); farg = strtof(arg, &tmp); if (tmp && *tmp) PARSE_FAIL("Facet with invalid arg '%s'!\n", arg); } break; case KW_FACET_END: if ((kw = stack_pop(&states)) != STATE_FACET) PARSE_FAIL("Improper nesting, parent: %s\n", kwmap[kw].str); break; case KW_VERTEX: /* TODO: calc bounding box */ for (i = 0; i < 3; i++) { if (!(arg = consume_arg(&bp))) PARSE_FAIL("Vertex with less than 3 args!\n"); farg = strtof(arg, &tmp); if (tmp && *tmp) PARSE_FAIL("Vertex with invalid arg '%s'!\n", arg); } break; case KW_UNKNOWN: prev += skip_set(prev, wsset); PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, prev); } prev = bp; } if (states.count) PARSE_FAIL("Expected keyword, got:\n%.*s...\n", 30, bp); stack_free(&states); return OK; fail: stack_free(&states); return FAIL; } int parse_file_bin(struct parseinfo *info, char *buf, size_t len) { char *bp, *end = buf + len; unsigned int i; info->type = TYPE_BIN; if (len < 84) PARSE_FAIL("Truncated data! (header missing)\n"); /* 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) PARSE_FAIL("Truncated data! (loops missing)\n"); /* TODO: load floats and calc bounding box */ bp += 50; } return OK; fail: return FAIL; } int parse_file(struct parseinfo *info, char *buf, size_t len) { int status; char *bp; if (info->valid) free_info(info); /* check bin vs ascii */ for (bp = buf; strchr(wsset, *bp); bp++); status = !strncmp("solid ", bp, 6) ? parse_file_ascii(info, buf, len) : parse_file_bin(info, buf, len); if (status == FAIL) return FAIL; printf("LOOPS: %i\n", info->loopcount); /* TODO: create new dir and write parseinfo to files */ return OK; } void free_info(struct parseinfo *info) { NULLFREE(info->namehash); NULLFREE(info->stlpath); NULLFREE(info->infopath); }