2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
15 /*---------------------------------------------------------------------------*/
18 #pragma comment(lib, "SDL_ttf.lib")
19 #pragma comment(lib, "SDL_image.lib")
20 #pragma comment(lib, "SDL_mixer.lib")
21 #pragma comment(lib, "SDL.lib")
22 #pragma comment(lib, "SDLmain.lib")
23 #pragma comment(lib, "opengl32.lib")
26 /*---------------------------------------------------------------------------*/
29 #include <SDL_image.h>
37 #include "base_config.h"
45 * The overall design of this map converter is very stupid, but very
46 * simple. It begins by assuming that every mtrl, vert, edge, side,
47 * and texc in the map is unique. It then makes an optimizing pass
48 * that discards redundant information. The result is optimal, though
49 * the process is terribly inefficient.
52 /*---------------------------------------------------------------------------*/
54 /* Ohhhh... arbitrary! */
77 static int overflow(const char *s)
79 printf("%s overflow\n", s);
84 static int incm(struct s_file *fp)
86 return (fp->mc < MAXM) ? fp->mc++ : overflow("mtrl");
89 static int incv(struct s_file *fp)
91 return (fp->vc < MAXV) ? fp->vc++ : overflow("vert");
94 static int ince(struct s_file *fp)
96 return (fp->ec < MAXE) ? fp->ec++ : overflow("edge");
99 static int incs(struct s_file *fp)
101 return (fp->sc < MAXS) ? fp->sc++ : overflow("side");
104 static int inct(struct s_file *fp)
106 return (fp->tc < MAXT) ? fp->tc++ : overflow("texc");
109 static int incg(struct s_file *fp)
111 return (fp->gc < MAXG) ? fp->gc++ : overflow("geom");
114 static int incl(struct s_file *fp)
116 return (fp->lc < MAXL) ? fp->lc++ : overflow("lump");
119 static int incn(struct s_file *fp)
121 return (fp->nc < MAXN) ? fp->nc++ : overflow("node");
124 static int incp(struct s_file *fp)
126 return (fp->pc < MAXP) ? fp->pc++ : overflow("path");
129 static int incb(struct s_file *fp)
131 return (fp->bc < MAXB) ? fp->bc++ : overflow("body");
134 static int incc(struct s_file *fp)
136 return (fp->cc < MAXC) ? fp->cc++ : overflow("coin");
139 static int incz(struct s_file *fp)
141 return (fp->zc < MAXZ) ? fp->zc++ : overflow("geol");
144 static int incj(struct s_file *fp)
146 return (fp->jc < MAXJ) ? fp->jc++ : overflow("jump");
149 static int incx(struct s_file *fp)
151 return (fp->xc < MAXX) ? fp->xc++ : overflow("swch");
154 static int incr(struct s_file *fp)
156 return (fp->rc < MAXR) ? fp->rc++ : overflow("bill");
159 static int incu(struct s_file *fp)
161 return (fp->uc < MAXU) ? fp->uc++ : overflow("ball");
164 static int incw(struct s_file *fp)
166 return (fp->wc < MAXW) ? fp->wc++ : overflow("view");
169 static int inci(struct s_file *fp)
171 return (fp->ic < MAXI) ? fp->ic++ : overflow("indx");
174 static void init_file(struct s_file *fp)
196 fp->mv = (struct s_mtrl *) calloc(MAXM, sizeof (struct s_mtrl));
197 fp->vv = (struct s_vert *) calloc(MAXV, sizeof (struct s_vert));
198 fp->ev = (struct s_edge *) calloc(MAXE, sizeof (struct s_edge));
199 fp->sv = (struct s_side *) calloc(MAXS, sizeof (struct s_side));
200 fp->tv = (struct s_texc *) calloc(MAXT, sizeof (struct s_texc));
201 fp->gv = (struct s_geom *) calloc(MAXG, sizeof (struct s_geom));
202 fp->lv = (struct s_lump *) calloc(MAXL, sizeof (struct s_lump));
203 fp->nv = (struct s_node *) calloc(MAXN, sizeof (struct s_node));
204 fp->pv = (struct s_path *) calloc(MAXP, sizeof (struct s_path));
205 fp->bv = (struct s_body *) calloc(MAXB, sizeof (struct s_body));
206 fp->cv = (struct s_coin *) calloc(MAXC, sizeof (struct s_coin));
207 fp->zv = (struct s_goal *) calloc(MAXZ, sizeof (struct s_goal));
208 fp->jv = (struct s_jump *) calloc(MAXJ, sizeof (struct s_jump));
209 fp->xv = (struct s_swch *) calloc(MAXX, sizeof (struct s_swch));
210 fp->rv = (struct s_bill *) calloc(MAXR, sizeof (struct s_bill));
211 fp->uv = (struct s_ball *) calloc(MAXU, sizeof (struct s_ball));
212 fp->wv = (struct s_view *) calloc(MAXW, sizeof (struct s_view));
213 fp->av = (char *) calloc(MAXA, sizeof (char));
214 fp->iv = (int *) calloc(MAXI, sizeof (int));
217 /*---------------------------------------------------------------------------*/
220 * The following is a small symbol table data structure. Symbols and
221 * their integer values are collected in symv and valv. References
222 * and pointers to their unsatisfied integer values are collected in
223 * refv and pntv. The resolve procedure matches references to symbols
224 * and fills waiting ints with the proper values.
229 static char symv[MAXSYM][MAXSTR];
230 static int valv[MAXSYM];
232 static char refv[MAXSYM][MAXSTR];
233 static int *pntv[MAXSYM];
238 static void make_sym(const char *s, int v)
240 strncpy(symv[strc], s, MAXSTR - 1);
245 static void make_ref(const char *r, int *p)
247 strncpy(refv[refc], r, MAXSTR - 1);
252 static void resolve(void)
256 for (i = 0; i < refc; i++)
257 for (j = 0; j < strc; j++)
258 if (strncmp(refv[i], symv[j], MAXSTR) == 0)
260 *(pntv[i]) = valv[j];
265 /*---------------------------------------------------------------------------*/
268 * The following globals are used to cache target_positions. They are
269 * targeted by various entities and must be resolved in a second pass.
272 static float targ_p [MAXW][3];
273 static int targ_wi[MAXW];
274 static int targ_ji[MAXW];
277 static void targets(struct s_file *fp)
281 for (i = 0; i < fp->wc; i++)
282 v_cpy(fp->wv[i].q, targ_p[targ_wi[i]]);
284 for (i = 0; i < fp->jc; i++)
285 v_cpy(fp->jv[i].q, targ_p[targ_ji[i]]);
288 /*---------------------------------------------------------------------------*/
291 * The following code caches image sizes. Textures are referenced by
292 * name, but their sizes are necessary when computing texture
293 * coordinates. This code allows each file to be accessed only once
294 * regardless of the number of surfaces refering to it.
297 static char *image_s[MAXM];
298 static int image_w[MAXM];
299 static int image_h[MAXM];
302 static int size_load(const char *file, int *w, int *h)
306 if ((S = IMG_Load(file)))
318 static void size_image(const char *name, int *w, int *h)
325 for (i = 0; i < image_n; i++)
326 if (strncmp(image_s[i], name, MAXSTR) == 0)
337 strcpy(jpg, name); strcat(jpg, ".jpg");
338 strcpy(tga, name); strcat(tga, ".tga");
339 strcpy(png, name); strcat(png, ".png");
341 if (size_load(config_data(png), w, h) ||
342 size_load(config_data(tga), w, h) ||
343 size_load(config_data(jpg), w, h))
345 image_s[image_n] = (char *) calloc(strlen(name) + 1, 1);
346 image_w[image_n] = *w;
347 image_h[image_n] = *h;
349 strcpy(image_s[image_n], name);
354 /*---------------------------------------------------------------------------*/
356 /* Read the given material file, adding a new material to the solid. */
358 static int read_mtrl(struct s_file *fp, const char *name)
364 for (mi = 0; mi < fp->mc; mi++)
365 if (strncmp(name, fp->mv[mi].f, MAXSTR) == 0)
368 mp = fp->mv + incm(fp);
370 strncpy(mp->f, name, PATHMAX - 1);
372 mp->a[0] = mp->a[1] = mp->a[2] = mp->a[3] = 1.0f;
373 mp->d[0] = mp->d[1] = mp->d[2] = mp->d[3] = 1.0f;
374 mp->s[0] = mp->s[1] = mp->s[2] = mp->s[3] = 1.0f;
375 mp->e[0] = mp->e[1] = mp->e[2] = mp->e[3] = 1.0f;
379 if ((fin = fopen(config_data(name), "r")))
387 mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
388 mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
389 mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
390 mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
398 /*---------------------------------------------------------------------------*/
401 * All bodies with an associated path are assumed to be positioned at
402 * the beginning of that path. These bodies must be moved to the
403 * origin in order for their path transforms to behave correctly.
404 * This is how we get away with defining func_trains with no origin
408 static void move_side(struct s_side *sp, const float p[3])
410 sp->d -= v_dot(sp->n, p);
413 static void move_vert(struct s_vert *vp, const float p[3])
415 v_sub(vp->p, vp->p, p);
418 static void move_lump(struct s_file *fp,
419 struct s_lump *lp, const float p[3])
423 for (i = 0; i < lp->sc; i++)
424 move_side(fp->sv + fp->iv[lp->s0 + i], p);
425 for (i = 0; i < lp->vc; i++)
426 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
429 static void move_body(struct s_file *fp,
434 for (i = 0; i < bp->lc; i++)
435 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
438 static void move_file(struct s_file *fp)
442 for (i = 0; i < fp->bc; i++)
443 if (fp->bv[i].pi >= 0)
444 move_body(fp, fp->bv + i);
447 /*---------------------------------------------------------------------------*/
450 * This is a basic OBJ loader. It is by no means fully compliant with
451 * the OBJ specification, but it works well with the output of
452 * Wings3D. All faces must be triangles and all vertices must include
453 * normals and texture coordinates. Material names are taken to be
454 * references to Neverball materials, rather than MTL definitions.
457 static void read_vt(struct s_file *fp, const char *line)
459 struct s_texc *tp = fp->tv + inct(fp);
461 sscanf(line, "%f %f", tp->u, tp->u + 1);
464 static void read_vn(struct s_file *fp, const char *line)
466 struct s_side *sp = fp->sv + incs(fp);
468 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
471 static void read_v(struct s_file *fp, const char *line)
473 struct s_vert *vp = fp->vv + incv(fp);
475 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
478 static void read_f(struct s_file *fp, const char *line,
479 int v0, int t0, int s0, int mi)
481 struct s_geom *gp = fp->gv + incg(fp);
486 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
487 &gp->vi, &c1, &gp->ti, &c2, &gp->si,
488 &gp->vj, &c1, &gp->tj, &c2, &gp->sj,
489 &gp->vk, &c1, &gp->tk, &c2, &gp->sk);
504 static void read_obj(struct s_file *fp, const char *name)
515 if ((fin = fopen(config_data(name), "r")))
517 while (fgets(line, MAXSTR, fin))
519 if (strncmp(line, "usemtl", 6) == 0)
521 sscanf(line + 6, "%s", mtrl);
522 mi = read_mtrl(fp, mtrl);
525 else if (strncmp(line, "f", 1) == 0)
527 if (fp->mv[mi].d[3] > 0)
528 read_f(fp, line + 1, v0, t0, s0, mi);
531 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
532 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
533 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
539 /*---------------------------------------------------------------------------*/
541 static float plane_d[MAXS];
542 static float plane_n[MAXS][3];
543 static float plane_p[MAXS][3];
544 static float plane_u[MAXS][3];
545 static float plane_v[MAXS][3];
546 static int plane_f[MAXS];
547 static int plane_m[MAXS];
549 static void make_plane(int pi, int x0, int y0, int z0,
550 int x1, int y1, int z1,
551 int x2, int y2, int z2,
552 int tu, int tv, int r,
553 float su, float sv, int fl, const char *s)
555 static const float base[6][3][3] = {
556 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, -1, 0 }},
557 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, -1, 0 }},
558 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
559 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
560 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
561 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
565 float p0[3], p1[3], p2[3];
566 float u[3], v[3], p[3];
571 size_image(s, &w, &h);
573 plane_f[pi] = fl ? L_DETAIL : 0;
575 p0[0] = +(float) x0 / SCALE;
576 p0[1] = +(float) z0 / SCALE;
577 p0[2] = -(float) y0 / SCALE;
579 p1[0] = +(float) x1 / SCALE;
580 p1[1] = +(float) z1 / SCALE;
581 p1[2] = -(float) y1 / SCALE;
583 p2[0] = +(float) x2 / SCALE;
584 p2[1] = +(float) z2 / SCALE;
585 p2[2] = -(float) y2 / SCALE;
590 v_crs(plane_n[pi], u, v);
591 v_nrm(plane_n[pi], plane_n[pi]);
593 plane_d[pi] = v_dot(plane_n[pi], p1);
595 for (i = 0; i < 6; i++)
596 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
606 m_rot(R, base[n][0], V_RAD(r));
608 v_mad(p, p, base[n][1], su * tu / SCALE);
609 v_mad(p, p, base[n][2], sv * tv / SCALE);
611 m_vxfm(plane_u[pi], R, base[n][1]);
612 m_vxfm(plane_v[pi], R, base[n][2]);
613 m_vxfm(plane_p[pi], R, p);
615 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
616 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
618 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
619 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
622 /*---------------------------------------------------------------------------*/
631 static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
635 if (fgets(buf, MAXSTR, fin))
645 /* Scan the beginning or end of a block. */
647 if (buf[0] == '{') return T_BEG;
648 if (buf[0] == '}') return T_END;
650 /* Scan a key-value pair. */
654 strcpy(key, strtok(buf, "\""));
655 (void) strtok(NULL, "\"");
656 strcpy(val, strtok(NULL, "\""));
667 "%s %d %d %d %f %f %d",
668 &c, &x0, &y0, &z0, &c,
669 &c, &x1, &y1, &z1, &c,
670 &c, &x2, &y2, &z2, &c,
671 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
673 make_plane(pi, x0, y0, z0,
676 tu, tv, r, su, sv, fl, key);
680 /* If it's not recognized, it must be uninteresting. */
687 /*---------------------------------------------------------------------------*/
689 /* Parse a lump from the given file and add it to the solid. */
691 static void read_lump(struct s_file *fp, FILE *fin)
697 struct s_lump *lp = fp->lv + incl(fp);
701 while ((t = map_token(fin, fp->sc, k, v)))
705 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
706 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
707 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
708 fp->sv[fp->sc].d = plane_d[fp->sc];
710 plane_m[fp->sc] = read_mtrl(fp, k);
712 fp->iv[fp->ic] = fp->sc;
722 /*---------------------------------------------------------------------------*/
724 static void make_path(struct s_file *fp,
726 char v[][MAXSTR], int c)
728 int i, pi = incp(fp);
730 struct s_path *pp = fp->pv + pi;
739 for (i = 0; i < c; i++)
741 if (strcmp(k[i], "targetname") == 0)
744 if (strcmp(k[i], "target") == 0)
745 make_ref(v[i], &pp->pi);
747 if (strcmp(k[i], "state") == 0)
750 if (strcmp(k[i], "speed") == 0)
751 sscanf(v[i], "%f", &pp->t);
753 if (strcmp(k[i], "origin") == 0)
755 int x = 0, y = 0, z = 0;
757 sscanf(v[i], "%d %d %d", &x, &y, &z);
759 pp->p[0] = +(float) x / SCALE;
760 pp->p[1] = +(float) z / SCALE;
761 pp->p[2] = -(float) y / SCALE;
766 static void make_body(struct s_file *fp,
768 char v[][MAXSTR], int c, int l0)
770 int i, bi = incb(fp);
781 struct s_body *bp = fp->bv + bi;
787 for (i = 0; i < c; i++)
789 if (strcmp(k[i], "targetname") == 0)
792 else if (strcmp(k[i], "target") == 0)
793 make_ref(v[i], &bp->pi);
795 else if (strcmp(k[i], "model") == 0)
798 else if (strcmp(k[i], "origin") == 0)
799 sscanf(v[i], "%d %d %d", &x, &y, &z);
801 else if (strcmp(k[i], "classname") != 0)
803 /* Considers other strings as metadata */
804 strcat(fp->av, k[i]);
806 strcat(fp->av, v[i]);
807 strcat(fp->av, "\n");
808 fp->ac += (int) (strlen(v[i]) + (strlen(k[i])) + 2);
813 bp->lc = fp->lc - l0;
815 bp->gc = fp->gc - g0;
817 for (i = 0; i < bp->gc; i++)
818 fp->iv[inci(fp)] = g0++;
820 p[0] = +(float) x / SCALE;
821 p[1] = +(float) z / SCALE;
822 p[2] = -(float) y / SCALE;
824 for (i = v0; i < fp->vc; i++)
825 v_add(fp->vv[i].p, fp->vv[i].p, p);
828 static void make_coin(struct s_file *fp,
830 char v[][MAXSTR], int c)
832 int i, ci = incc(fp);
834 struct s_coin *cp = fp->cv + ci;
841 for (i = 0; i < c; i++)
843 if (strcmp(k[i], "light") == 0)
844 sscanf(v[i], "%d", &cp->n);
846 if (strcmp(k[i], "origin") == 0)
848 int x = 0, y = 0, z = 0;
850 sscanf(v[i], "%d %d %d", &x, &y, &z);
852 cp->p[0] = +(float) x / SCALE;
853 cp->p[1] = +(float) z / SCALE;
854 cp->p[2] = -(float) y / SCALE;
859 static void make_bill(struct s_file *fp,
861 char v[][MAXSTR], int c)
863 int i, ri = incr(fp);
865 struct s_bill *rp = fp->rv + ri;
867 memset(rp, 0, sizeof (struct s_bill));
870 for (i = 0; i < c; i++)
872 if (strcmp(k[i], "width") == 0)
873 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
874 if (strcmp(k[i], "height") == 0)
875 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
877 if (strcmp(k[i], "xrot") == 0)
878 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
879 if (strcmp(k[i], "yrot") == 0)
880 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
881 if (strcmp(k[i], "zrot") == 0)
882 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
884 if (strcmp(k[i], "time") == 0)
885 sscanf(v[i], "%f", &rp->t);
886 if (strcmp(k[i], "dist") == 0)
887 sscanf(v[i], "%f", &rp->d);
888 if (strcmp(k[i], "flag") == 0)
889 sscanf(v[i], "%d", &rp->fl);
891 if (strcmp(k[i], "image") == 0)
893 rp->mi = read_mtrl(fp, v[i]);
894 fp->mv[rp->mi].fl |= M_CLAMPED;
897 if (strcmp(k[i], "origin") == 0)
899 int x = 0, y = 0, z = 0;
902 sscanf(v[i], "%d %d %d", &x, &y, &z);
904 p[0] = +(float) x / SCALE;
905 p[1] = +(float) z / SCALE;
906 p[2] = -(float) y / SCALE;
909 rp->rx[0] = V_DEG(fatan2f(+p[1], rp->d));
910 rp->ry[0] = V_DEG(fatan2f(+p[0], -p[2]));
914 if (rp->fl & B_ADDITIVE)
915 fp->mv[rp->mi].fl |= M_ADDITIVE;
918 static void make_goal(struct s_file *fp,
920 char v[][MAXSTR], int c)
922 int i, zi = incz(fp);
924 struct s_goal *zp = fp->zv + zi;
933 for (i = 0; i < c; i++)
935 if (strcmp(k[i], "radius") == 0)
936 sscanf(v[i], "%f", &zp->r);
937 if (strcmp(k[i], "skip") == 0)
938 sscanf(v[i], "%d", &zp->s);
939 if (strcmp(k[i], "special") == 0)
940 sscanf(v[i], "%d", &zp->c);
942 if (strcmp(k[i], "origin") == 0)
944 int x = 0, y = 0, z = 0;
946 sscanf(v[i], "%d %d %d", &x, &y, &z);
948 zp->p[0] = +(float) (x) / SCALE;
949 zp->p[1] = +(float) (z - 24) / SCALE;
950 zp->p[2] = -(float) (y) / SCALE;
955 static void make_view(struct s_file *fp,
957 char v[][MAXSTR], int c)
959 int i, wi = incw(fp);
961 struct s_view *wp = fp->wv + wi;
970 for (i = 0; i < c; i++)
972 if (strcmp(k[i], "target") == 0)
973 make_ref(v[i], targ_wi + wi);
975 if (strcmp(k[i], "origin") == 0)
977 int x = 0, y = 0, z = 0;
979 sscanf(v[i], "%d %d %d", &x, &y, &z);
981 wp->p[0] = +(float) x / SCALE;
982 wp->p[1] = +(float) z / SCALE;
983 wp->p[2] = -(float) y / SCALE;
988 static void make_jump(struct s_file *fp,
990 char v[][MAXSTR], int c)
992 int i, ji = incj(fp);
994 struct s_jump *jp = fp->jv + ji;
1004 for (i = 0; i < c; i++)
1006 if (strcmp(k[i], "radius") == 0)
1007 sscanf(v[i], "%f", &jp->r);
1009 if (strcmp(k[i], "target") == 0)
1010 make_ref(v[i], targ_ji + ji);
1012 if (strcmp(k[i], "origin") == 0)
1014 int x = 0, y = 0, z = 0;
1016 sscanf(v[i], "%d %d %d", &x, &y, &z);
1018 jp->p[0] = +(float) x / SCALE;
1019 jp->p[1] = +(float) z / SCALE;
1020 jp->p[2] = -(float) y / SCALE;
1025 static void make_swch(struct s_file *fp,
1027 char v[][MAXSTR], int c)
1029 int i, xi = incx(fp);
1031 struct s_swch *xp = fp->xv + xi;
1044 for (i = 0; i < c; i++)
1046 if (strcmp(k[i], "radius") == 0)
1047 sscanf(v[i], "%f", &xp->r);
1049 if (strcmp(k[i], "target") == 0)
1050 make_ref(v[i], &xp->pi);
1052 if (strcmp(k[i], "timer") == 0)
1053 sscanf(v[i], "%f", &xp->t0);
1055 if (strcmp(k[i], "state") == 0)
1058 if (strcmp(k[i], "invisible") == 0)
1061 if (strcmp(k[i], "origin") == 0)
1063 int x = 0, y = 0, z = 0;
1065 sscanf(v[i], "%d %d %d", &x, &y, &z);
1067 xp->p[0] = +(float) x / SCALE;
1068 xp->p[1] = +(float) z / SCALE;
1069 xp->p[2] = -(float) y / SCALE;
1074 static void make_targ(struct s_file *fp,
1076 char v[][MAXSTR], int c)
1080 targ_p[targ_n][0] = 0.f;
1081 targ_p[targ_n][1] = 0.f;
1082 targ_p[targ_n][3] = 0.f;
1084 for (i = 0; i < c; i++)
1086 if (strcmp(k[i], "targetname") == 0)
1087 make_sym(v[i], targ_n);
1089 if (strcmp(k[i], "origin") == 0)
1091 int x = 0, y = 0, z = 0;
1093 sscanf(v[i], "%d %d %d", &x, &y, &z);
1095 targ_p[targ_n][0] = +(float) x / SCALE;
1096 targ_p[targ_n][1] = +(float) z / SCALE;
1097 targ_p[targ_n][2] = -(float) y / SCALE;
1104 static void make_ball(struct s_file *fp,
1106 char v[][MAXSTR], int c)
1108 int i, ui = incu(fp);
1110 struct s_ball *up = fp->uv + ui;
1134 for (i = 0; i < c; i++)
1136 if (strcmp(k[i], "radius") == 0)
1137 sscanf(v[i], "%f", &up->r);
1139 if (strcmp(k[i], "origin") == 0)
1141 int x = 0, y = 0, z = 0;
1143 sscanf(v[i], "%d %d %d", &x, &y, &z);
1145 up->p[0] = +(float) (x) / SCALE;
1146 up->p[1] = +(float) (z - 24) / SCALE;
1147 up->p[2] = -(float) (y) / SCALE;
1151 up->p[1] += up->r + SMALL;
1154 /*---------------------------------------------------------------------------*/
1156 static void read_ent(struct s_file *fp, FILE *fin)
1158 char k[MAXKEY][MAXSTR];
1159 char v[MAXKEY][MAXSTR];
1160 int t, i = 0, c = 0;
1164 while ((t = map_token(fin, -1, k[c], v[c])))
1168 if (strcmp(k[c], "classname") == 0)
1172 if (t == T_BEG) read_lump(fp, fin);
1173 if (t == T_END) break;
1176 if (!strcmp(v[i], "light")) make_coin(fp, k, v, c);
1177 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1178 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1179 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1180 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1181 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1182 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1183 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1184 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1185 if (!strcmp(v[i], "worldspawn")) make_body(fp, k, v, c, l0);
1186 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1187 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1190 static void read_map(struct s_file *fp, FILE *fin)
1196 while ((t = map_token(fin, -1, k, v)))
1201 /*---------------------------------------------------------------------------*/
1203 /* Test the location of a point with respect to a side plane. */
1205 static int fore_side(const float p[3], const struct s_side *sp)
1207 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1210 static int on_side(const float p[3], const struct s_side *sp)
1212 float d = v_dot(p, sp->n) - sp->d;
1214 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1217 /*---------------------------------------------------------------------------*/
1219 * Confirm that the addition of a vert would not result in degenerate
1223 static int ok_vert(const struct s_file *fp,
1224 const struct s_lump *lp, const float p[3])
1229 for (i = 0; i < lp->vc; i++)
1231 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1235 if (v_len(r) < SMALL)
1241 /*---------------------------------------------------------------------------*/
1244 * The following functions take the set of planes defining a lump and
1245 * find the verts, edges, and geoms that describe its boundaries. To
1246 * do this, they first find the verts, and then search these verts for
1247 * valid edges and geoms. It may be more efficient to compute edges
1248 * and geoms directly by clipping down infinite line segments and
1249 * planes, but this would be more complex and prone to numerical
1254 * Given 3 side planes, compute the point of intersection, if any.
1255 * Confirm that this point falls within the current lump, and that it
1256 * is unique. Add it as a vert of the solid.
1258 static void clip_vert(struct s_file *fp,
1259 struct s_lump *lp, int si, int sj, int sk)
1261 float M[16], X[16], I[16];
1265 d[0] = fp->sv[si].d;
1266 d[1] = fp->sv[sj].d;
1267 d[2] = fp->sv[sk].d;
1269 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1276 for (i = 0; i < lp->sc; i++)
1278 int si = fp->iv[lp->s0 + i];
1280 if (fore_side(p, fp->sv + si))
1284 if (ok_vert(fp, lp, p))
1286 v_cpy(fp->vv[fp->vc].p, p);
1288 fp->iv[fp->ic] = fp->vc;
1297 * Given two side planes, find an edge along their intersection by
1298 * finding a pair of vertices that fall on both planes. Add it to the
1301 static void clip_edge(struct s_file *fp,
1302 struct s_lump *lp, int si, int sj)
1306 for (i = 1; i < lp->vc; i++)
1307 for (j = 0; j < i; j++)
1309 int vi = fp->iv[lp->v0 + i];
1310 int vj = fp->iv[lp->v0 + j];
1312 if (on_side(fp->vv[vi].p, fp->sv + si) &&
1313 on_side(fp->vv[vj].p, fp->sv + si) &&
1314 on_side(fp->vv[vi].p, fp->sv + sj) &&
1315 on_side(fp->vv[vj].p, fp->sv + sj))
1317 fp->ev[fp->ec].vi = vi;
1318 fp->ev[fp->ec].vj = vj;
1320 fp->iv[fp->ic] = fp->ec;
1330 * Find all verts that lie on the given side of the lump. Sort these
1331 * verts to have a counter-clockwise winding about the plane normal.
1332 * Create geoms to tessalate the resulting convex polygon.
1334 static void clip_geom(struct s_file *fp,
1335 struct s_lump *lp, int si)
1337 int m[256], t[256], d, i, j, n = 0;
1342 struct s_side *sp = fp->sv + si;
1346 for (i = 0; i < lp->vc; i++)
1348 int vi = fp->iv[lp->v0 + i];
1350 if (on_side(fp->vv[vi].p, sp))
1355 v_add(v, fp->vv[vi].p, plane_p[si]);
1357 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1358 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1366 for (i = 1; i < n; i++)
1367 for (j = i + 1; j < n; j++)
1369 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1370 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1373 if (v_dot(w, sp->n) < 0.f)
1387 for (i = 0; i < n - 2; i++)
1389 fp->gv[fp->gc].mi = plane_m[si];
1391 fp->gv[fp->gc].ti = t[0];
1392 fp->gv[fp->gc].tj = t[i + 1];
1393 fp->gv[fp->gc].tk = t[i + 2];
1395 fp->gv[fp->gc].si = si;
1396 fp->gv[fp->gc].sj = si;
1397 fp->gv[fp->gc].sk = si;
1399 fp->gv[fp->gc].vi = m[0];
1400 fp->gv[fp->gc].vj = m[i + 1];
1401 fp->gv[fp->gc].vk = m[i + 2];
1403 fp->iv[fp->ic] = fp->gc;
1411 * Iterate the sides of the lump, attemping to generate a new vert for
1412 * each trio of planes, a new edge for each pair of planes, and a new
1413 * set of geom for each visible plane.
1415 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1422 for (i = 2; i < lp->sc; i++)
1423 for (j = 1; j < i; j++)
1424 for (k = 0; k < j; k++)
1428 fp->iv[lp->s0 + k]);
1433 for (i = 1; i < lp->sc; i++)
1434 for (j = 0; j < i; j++)
1437 fp->iv[lp->s0 + j]);
1442 for (i = 0; i < lp->sc; i++)
1443 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0)
1445 fp->iv[lp->s0 + i]);
1447 for (i = 0; i < lp->sc; i++)
1448 if (plane_f[fp->iv[lp->s0 + i]])
1452 static void clip_file(struct s_file *fp)
1456 for (i = 0; i < fp->lc; i++)
1457 clip_lump(fp, fp->lv + i);
1460 /*---------------------------------------------------------------------------*/
1463 * For each body element type, determine if element 'p' is equivalent
1464 * to element 'q'. This is more than a simple memory compare. It
1465 * effectively snaps mtrls and verts togather, and may reverse the
1466 * winding of an edge or a geom. This is done in order to maximize
1467 * the number of elements that can be eliminated.
1470 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1472 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1473 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1474 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1475 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1477 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1478 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1479 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1480 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1482 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1483 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1484 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1485 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1487 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1488 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1489 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1490 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1492 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1494 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1499 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1501 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1502 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1503 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1508 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1510 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1511 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1516 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1518 if (fabs(sp->d - sq->d) > SMALL) return 0;
1519 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1524 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1526 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1527 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1532 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1534 if (gp->mi != gq->mi) return 0;
1536 if (gp->ti != gq->ti) return 0;
1537 if (gp->si != gq->si) return 0;
1538 if (gp->vi != gq->vi) return 0;
1540 if (gp->tj != gq->tj) return 0;
1541 if (gp->sj != gq->sj) return 0;
1542 if (gp->vj != gq->vj) return 0;
1544 if (gp->tk != gq->tk) return 0;
1545 if (gp->sk != gq->sk) return 0;
1546 if (gp->vk != gq->vk) return 0;
1551 /*---------------------------------------------------------------------------*/
1554 * For each file element type, replace all references to element 'i'
1555 * with a reference to element 'j'. These are used when optimizing
1556 * and sorting the file.
1559 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1563 for (i = 0; i < fp->gc; i++)
1564 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1565 for (i = 0; i < fp->rc; i++)
1566 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1569 static void swap_vert(struct s_file *fp, int vi, int vj)
1573 for (i = 0; i < fp->ec; i++)
1575 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1576 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1579 for (i = 0; i < fp->gc; i++)
1581 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1582 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1583 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1586 for (i = 0; i < fp->lc; i++)
1587 for (j = 0; j < fp->lv[i].vc; j++)
1588 if (fp->iv[fp->lv[i].v0 + j] == vi)
1589 fp->iv[fp->lv[i].v0 + j] = vj;
1592 static void swap_edge(struct s_file *fp, int ei, int ej)
1596 for (i = 0; i < fp->lc; i++)
1597 for (j = 0; j < fp->lv[i].ec; j++)
1598 if (fp->iv[fp->lv[i].e0 + j] == ei)
1599 fp->iv[fp->lv[i].e0 + j] = ej;
1602 static void swap_side(struct s_file *fp, int si, int sj)
1606 for (i = 0; i < fp->gc; i++)
1608 if (fp->gv[i].si == si) fp->gv[i].si = sj;
1609 if (fp->gv[i].sj == si) fp->gv[i].sj = sj;
1610 if (fp->gv[i].sk == si) fp->gv[i].sk = sj;
1612 for (i = 0; i < fp->nc; i++)
1613 if (fp->nv[i].si == si) fp->nv[i].si = sj;
1615 for (i = 0; i < fp->lc; i++)
1616 for (j = 0; j < fp->lv[i].sc; j++)
1617 if (fp->iv[fp->lv[i].s0 + j] == si)
1618 fp->iv[fp->lv[i].s0 + j] = sj;
1621 static void swap_texc(struct s_file *fp, int ti, int tj)
1625 for (i = 0; i < fp->gc; i++)
1627 if (fp->gv[i].ti == ti) fp->gv[i].ti = tj;
1628 if (fp->gv[i].tj == ti) fp->gv[i].tj = tj;
1629 if (fp->gv[i].tk == ti) fp->gv[i].tk = tj;
1634 static void swap_geom(struct s_file *fp, int gi, int gj)
1638 for (i = 0; i < fp->lc; i++)
1639 for (j = 0; j < fp->lv[i].gc; j++)
1640 if (fp->iv[fp->lv[i].g0 + j] == gi)
1641 fp->iv[fp->lv[i].g0 + j] = gj;
1643 for (i = 0; i < fp->bc; i++)
1644 for (j = 0; j < fp->bv[i].gc; j++)
1645 if (fp->iv[fp->bv[i].g0 + j] == gi)
1646 fp->iv[fp->bv[i].g0 + j] = gj;
1649 /*---------------------------------------------------------------------------*/
1651 static void uniq_mtrl(struct s_file *fp)
1655 for (i = 0; i < fp->mc; i++)
1657 for (j = 0; j < k; j++)
1658 if (comp_mtrl(fp->mv + i, fp->mv + j))
1660 swap_mtrl(fp, i, j);
1668 fp->mv[k] = fp->mv[i];
1669 swap_mtrl(fp, i, k);
1678 static void uniq_vert(struct s_file *fp)
1682 for (i = 0; i < fp->vc; i++)
1684 for (j = 0; j < k; j++)
1685 if (comp_vert(fp->vv + i, fp->vv + j))
1687 swap_vert(fp, i, j);
1695 fp->vv[k] = fp->vv[i];
1696 swap_vert(fp, i, k);
1705 static void uniq_edge(struct s_file *fp)
1709 for (i = 0; i < fp->ec; i++)
1711 for (j = 0; j < k; j++)
1712 if (comp_edge(fp->ev + i, fp->ev + j))
1714 swap_edge(fp, i, j);
1722 fp->ev[k] = fp->ev[i];
1723 swap_edge(fp, i, k);
1732 static void uniq_geom(struct s_file *fp)
1736 for (i = 0; i < fp->gc; i++)
1738 for (j = 0; j < k; j++)
1739 if (comp_geom(fp->gv + i, fp->gv + j))
1741 swap_geom(fp, i, j);
1749 fp->gv[k] = fp->gv[i];
1750 swap_geom(fp, i, k);
1759 static void uniq_texc(struct s_file *fp)
1763 for (i = 0; i < fp->tc; i++)
1765 for (j = 0; j < k; j++)
1766 if (comp_texc(fp->tv + i, fp->tv + j))
1768 swap_texc(fp, i, j);
1776 fp->tv[k] = fp->tv[i];
1777 swap_texc(fp, i, k);
1786 static void uniq_side(struct s_file *fp)
1790 for (i = 0; i < fp->sc; i++)
1792 for (j = 0; j < k; j++)
1793 if (comp_side(fp->sv + i, fp->sv + j))
1795 swap_side(fp, i, j);
1803 fp->sv[k] = fp->sv[i];
1804 swap_side(fp, i, k);
1813 static void uniq_file(struct s_file *fp)
1823 /*---------------------------------------------------------------------------*/
1825 static void sort_file(struct s_file *fp)
1829 /* Sort billboards farthest to nearest. */
1831 for (i = 0; i < fp->rc; i++)
1832 for (j = i + 1; j < fp->rc; j++)
1833 if (fp->rv[j].d > fp->rv[i].d)
1838 fp->rv[i] = fp->rv[j];
1842 /* Ensure the first vertex is the lowest. */
1844 for (i = 0; i < fp->vc; i++)
1845 if (fp->vv[0].p[1] > fp->vv[i].p[1])
1850 fp->vv[0] = fp->vv[i];
1853 swap_vert(fp, 0, -1);
1854 swap_vert(fp, i, 0);
1855 swap_vert(fp, -1, i);
1859 /*---------------------------------------------------------------------------*/
1861 static int test_lump_side(const struct s_file *fp,
1862 const struct s_lump *lp,
1863 const struct s_side *sp)
1871 /* If the given side is part of the given lump, then the lump is behind. */
1873 for (si = 0; si < lp->sc; si++)
1874 if (fp->sv + fp->iv[lp->s0 + si] == sp)
1877 /* Check if each lump vertex is in front of, behind, on the side. */
1879 for (vi = 0; vi < lp->vc; vi++)
1881 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
1887 /* If no verts are behind, the lump is in front, and vice versa. */
1889 if (f > 0 && b == 0) return +1;
1890 if (b > 0 && f == 0) return -1;
1892 /* Else, the lump crosses the side. */
1897 static int node_node(struct s_file *fp, int l0, int lc)
1901 /* Base case. Dump all given lumps into a leaf node. */
1903 fp->nv[fp->nc].si = -1;
1904 fp->nv[fp->nc].ni = -1;
1905 fp->nv[fp->nc].nj = -1;
1906 fp->nv[fp->nc].l0 = l0;
1907 fp->nv[fp->nc].lc = lc;
1917 int li = 0, lic = 0;
1918 int lj = 0, ljc = 0;
1919 int lk = 0, lkc = 0;
1922 /* Find the side that most evenly splits the given lumps. */
1924 for (si = 0; si < fp->sc; si++)
1930 for (li = 0; li < lc; li++)
1931 if ((k = test_lump_side(fp, fp->lv + l0 + li, fp->sv + si)))
1938 if ((d < sjd) || (d == sjd && o < sjo))
1946 /* Flag each lump with its position WRT the side. */
1948 for (li = 0; li < lc; li++)
1949 switch (test_lump_side(fp, fp->lv + l0 + li, fp->sv + sj))
1951 case +1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10; break;
1952 case 0: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20; break;
1953 case -1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40; break;
1956 /* Sort all lumps in the range by their flag values. */
1958 for (li = 1; li < lc; li++)
1959 for (lj = 0; lj < li; lj++)
1960 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
1964 l = fp->lv[l0 + li];
1965 fp->lv[l0 + li] = fp->lv[l0 + lj];
1966 fp->lv[l0 + lj] = l;
1969 /* Establish the in-front, on, and behind lump ranges. */
1975 for (i = lc - 1; i >= 0; i--)
1976 switch (fp->lv[l0 + i].fl & 0xf0)
1978 case 0x10: li = l0 + i; lic++; break;
1979 case 0x20: lj = l0 + i; ljc++; break;
1980 case 0x40: lk = l0 + i; lkc++; break;
1983 /* Add the lumps on the side to the node. */
1988 fp->nv[i].ni = node_node(fp, li, lic);
1990 fp->nv[i].nj = node_node(fp, lk, lkc);
1998 static void node_file(struct s_file *fp)
2002 /* Sort the lumps of each body into BSP nodes. */
2004 for (bi = 0; bi < fp->bc; bi++)
2005 fp->bv[bi].ni = node_node(fp, fp->bv[bi].l0, fp->bv[bi].lc);
2008 /*---------------------------------------------------------------------------*/
2010 static void dump_file(struct s_file *p, const char *name)
2015 int m = p->rc + p->cc * 128 + (p->zc * p->jc + p->xc) * 32;
2017 /* Count the number of solid lumps. */
2019 for (i = 0; i < p->lc; i++)
2020 if ((p->lv[i].fl & 1) == 0)
2023 /* Count the number of visible geoms. */
2025 for (i = 0; i < p->bc; i++)
2027 for (j = 0; j < p->bv[i].lc; j++)
2028 m += p->lv[p->bv[i].l0 + j].gc;
2032 /* Count the total value of all coins. */
2034 for (i = 0; i < p->cc; i++)
2037 printf("%s (%d/%d/$%d)\n"
2038 " mtrl vert edge side texc"
2039 " geom lump path node body\n"
2040 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2041 " coin goal view jump swch"
2042 " bill ball char indx\n"
2043 "%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2045 p->mc, p->vc, p->ec, p->sc, p->tc,
2046 p->gc, p->lc, p->pc, p->nc, p->bc,
2047 p->cc, p->zc, p->wc, p->jc, p->xc,
2048 p->rc, p->uc, p->ac, p->ic);
2051 int main(int argc, char *argv[])
2060 if (config_data_path(argv[2], NULL))
2062 strncpy(src, argv[1], MAXSTR);
2063 strncpy(dst, argv[1], MAXSTR);
2065 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2066 strcpy(dst + strlen(dst) - 4, ".sol");
2068 strcat(dst, ".sol");
2070 if ((fin = fopen(src, "r")))
2090 else fprintf(stderr, "Failure to establish data directory\n");
2092 else fprintf(stderr, "Usage: %s <map> [data]\n", argv[0]);