stlfile.c (9093B)
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 for (bp = buf; isws(*bp); bp++); 340 status = !strncmp("solid", bp, 5) && isws(bp[5]) 341 ? parse_file_ascii(info, buf, len) 342 : parse_file_bin(info, buf, len); 343 if (status == FAIL) return FAIL; 344 345 info->modelname = *modelname; 346 *modelname = NULL; 347 348 info->hash = checkp(strdup(mhash(info->modelname, -1))); 349 350 return OK; 351} 352 353int 354save_info(struct parseinfo *info, FILE *f) 355{ 356 size_t nwrote = 0; 357 int i; 358 359 nwrote += fwrite(&info->type, sizeof(int), 1, f); 360 nwrote += fwrite(&info->loopcount, sizeof(int), 1, f); 361 nwrote += fwrite(&info->filesize, sizeof(unsigned), 1, f); 362 363 for (i = 0; i < 3; i++) { 364 nwrote += fwrite(&info->bbmin[i], sizeof(float), 1, f); 365 nwrote += fwrite(&info->bbmax[i], sizeof(float), 1, f); 366 } 367 368 nwrote += fwrite(info->header, 80, 1, f); 369 370 if (nwrote != 10) return FAIL; 371 372 fputstr(f, info->solidname); 373 fputstr(f, info->hash); 374 fputstr(f, info->modelname); 375 376 return OK; 377} 378 379int 380load_info(struct parseinfo *info, FILE *f) 381{ 382 size_t nread = 0; 383 int i; 384 385 nread += fread(&info->type, sizeof(int), 1, f); 386 nread += fread(&info->loopcount, sizeof(int), 1, f); 387 nread += fread(&info->filesize, sizeof(unsigned), 1, f); 388 389 for (i = 0; i < 3; i++) { 390 nread += fread(&info->bbmin[i], sizeof(float), 1, f); 391 nread += fread(&info->bbmax[i], sizeof(float), 1, f); 392 } 393 394 nread += fread(info->header, 80, 1, f); 395 396 if (nread != 10) return FAIL; 397 398 freadstr(f, &info->solidname); 399 freadstr(f, &info->hash); 400 freadstr(f, &info->modelname); 401 402 info->valid = 1; 403 404 return OK; 405} 406 407void 408print_info(struct parseinfo *info) 409{ 410 int i, k; 411 412 printf(" === Model info === \n"); 413 414 printf(" File Size: %u\n", info->filesize); 415 if (info->type == TYPE_BIN) { 416 printf(" Header:\n"); 417 for (i = 0; i < 80; i += k) { 418 printf(" "); 419 for (k = 0; k < MIN(80 - i, 20); k++) 420 printf(" %02x", (uint8_t) info->header[i+k]); 421 printf(" | "); 422 for (k = 0; k < MIN(80 - i, 20); k++) 423 putchar(PRINTABLE(info->header[i+k])); 424 printf("\n"); 425 } 426 } 427 printf(" Model ID: %s\n", info->hash); 428 printf(" Model Name: %s\n", info->modelname); 429 printf(" Solid Name: %s\n", info->solidname); 430 printf(" Triangle Count: %i\n", info->loopcount); 431 printf(" Bounding Box Size: %.2f x %.2f x %.2f\n", 432 info->bbmax[0] - info->bbmin[0], 433 info->bbmax[1] - info->bbmin[1], 434 info->bbmax[2] - info->bbmin[2]); 435 printf(" Bounding Box Origin: %.2f x %.2f x %.2f\n", 436 info->bbmin[0], info->bbmin[1], info->bbmin[2]); 437 438 printf(" ================== \n"); 439} 440 441void 442free_info(struct parseinfo *info) 443{ 444 FREE(info->hash); 445 FREE(info->modelname); 446 FREE(info->solidname); 447 info->valid = 0; 448} 449 450float fle32toh(float v) 451{ 452 union { 453 uint32_t u; 454 float f; 455 } conv; 456 457 conv.f = v; 458 conv.u = le32toh(conv.u); 459 return conv.f; 460}