1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
#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);
}
|