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.
303 static struct _imagedata *imagedata = NULL;
304 static int image_n = 0;
305 static int image_alloc = 0;
307 #define IMAGE_REALLOC 32
309 static void free_imagedata()
315 for (i = 0; i < image_n; i++)
316 free(imagedata[i].s);
320 image_n = image_alloc = 0;
323 static int size_load(const char *file, int *w, int *h)
327 if ((S = IMG_Load(file)))
339 static void size_image(const char *name, int *w, int *h)
347 for (i = 0; i < image_n; i++)
348 if (strncmp(imagedata[i].s, name, MAXSTR) == 0)
359 strcpy(jpg, name); strcat(jpg, ".jpg");
360 strcpy(tga, name); strcat(tga, ".tga");
361 strcpy(png, name); strcat(png, ".png");
363 if (size_load(config_data(png), w, h) ||
364 size_load(config_data(tga), w, h) ||
365 size_load(config_data(jpg), w, h))
368 if (image_n + 1 >= image_alloc)
370 struct _imagedata *tmp =
371 (struct _imagedata *) malloc(sizeof(struct _imagedata) * (image_alloc + IMAGE_REALLOC));
374 printf("malloc error\n");
379 (void) memcpy(tmp, imagedata, sizeof(struct _imagedata) * image_alloc);
383 image_alloc += IMAGE_REALLOC;
386 imagedata[image_n].s = (char *) calloc(strlen(name) + 1, 1);
387 imagedata[image_n].w = *w;
388 imagedata[image_n].h = *h;
389 strcpy(imagedata[image_n].s, name);
395 /*---------------------------------------------------------------------------*/
397 /* Read the given material file, adding a new material to the solid. */
399 static int read_mtrl(struct s_file *fp, const char *name)
405 for (mi = 0; mi < fp->mc; mi++)
406 if (strncmp(name, fp->mv[mi].f, MAXSTR) == 0)
409 mp = fp->mv + incm(fp);
411 strncpy(mp->f, name, PATHMAX - 1);
413 mp->a[0] = mp->a[1] = mp->a[2] = mp->a[3] = 1.0f;
414 mp->d[0] = mp->d[1] = mp->d[2] = mp->d[3] = 1.0f;
415 mp->s[0] = mp->s[1] = mp->s[2] = mp->s[3] = 1.0f;
416 mp->e[0] = mp->e[1] = mp->e[2] = mp->e[3] = 1.0f;
420 if ((fin = fopen(config_data(name), "r")))
428 mp->d, mp->d + 1, mp->d + 2, mp->d + 3,
429 mp->a, mp->a + 1, mp->a + 2, mp->a + 3,
430 mp->s, mp->s + 1, mp->s + 2, mp->s + 3,
431 mp->e, mp->e + 1, mp->e + 2, mp->e + 3,
439 /*---------------------------------------------------------------------------*/
442 * All bodies with an associated path are assumed to be positioned at
443 * the beginning of that path. These bodies must be moved to the
444 * origin in order for their path transforms to behave correctly.
445 * This is how we get away with defining func_trains with no origin
449 static void move_side(struct s_side *sp, const float p[3])
451 sp->d -= v_dot(sp->n, p);
454 static void move_vert(struct s_vert *vp, const float p[3])
456 v_sub(vp->p, vp->p, p);
459 static void move_lump(struct s_file *fp,
460 struct s_lump *lp, const float p[3])
464 for (i = 0; i < lp->sc; i++)
465 move_side(fp->sv + fp->iv[lp->s0 + i], p);
466 for (i = 0; i < lp->vc; i++)
467 move_vert(fp->vv + fp->iv[lp->v0 + i], p);
470 static void move_body(struct s_file *fp,
475 for (i = 0; i < bp->lc; i++)
476 move_lump(fp, fp->lv + bp->l0 + i, fp->pv[bp->pi].p);
479 static void move_file(struct s_file *fp)
483 for (i = 0; i < fp->bc; i++)
484 if (fp->bv[i].pi >= 0)
485 move_body(fp, fp->bv + i);
488 /*---------------------------------------------------------------------------*/
491 * This is a basic OBJ loader. It is by no means fully compliant with
492 * the OBJ specification, but it works well with the output of
493 * Wings3D. All faces must be triangles and all vertices must include
494 * normals and texture coordinates. Material names are taken to be
495 * references to Neverball materials, rather than MTL definitions.
498 static void read_vt(struct s_file *fp, const char *line)
500 struct s_texc *tp = fp->tv + inct(fp);
502 sscanf(line, "%f %f", tp->u, tp->u + 1);
505 static void read_vn(struct s_file *fp, const char *line)
507 struct s_side *sp = fp->sv + incs(fp);
509 sscanf(line, "%f %f %f", sp->n, sp->n + 1, sp->n + 2);
512 static void read_v(struct s_file *fp, const char *line)
514 struct s_vert *vp = fp->vv + incv(fp);
516 sscanf(line, "%f %f %f", vp->p, vp->p + 1, vp->p + 2);
519 static void read_f(struct s_file *fp, const char *line,
520 int v0, int t0, int s0, int mi)
522 struct s_geom *gp = fp->gv + incg(fp);
527 sscanf(line, "%d%c%d%c%d %d%c%d%c%d %d%c%d%c%d",
528 &gp->vi, &c1, &gp->ti, &c2, &gp->si,
529 &gp->vj, &c1, &gp->tj, &c2, &gp->sj,
530 &gp->vk, &c1, &gp->tk, &c2, &gp->sk);
545 static void read_obj(struct s_file *fp, const char *name)
556 if ((fin = fopen(config_data(name), "r")))
558 while (fgets(line, MAXSTR, fin))
560 if (strncmp(line, "usemtl", 6) == 0)
562 sscanf(line + 6, "%s", mtrl);
563 mi = read_mtrl(fp, mtrl);
566 else if (strncmp(line, "f", 1) == 0)
568 if (fp->mv[mi].d[3] > 0)
569 read_f(fp, line + 1, v0, t0, s0, mi);
572 else if (strncmp(line, "vt", 2) == 0) read_vt(fp, line + 2);
573 else if (strncmp(line, "vn", 2) == 0) read_vn(fp, line + 2);
574 else if (strncmp(line, "v", 1) == 0) read_v (fp, line + 1);
580 /*---------------------------------------------------------------------------*/
582 static float plane_d[MAXS];
583 static float plane_n[MAXS][3];
584 static float plane_p[MAXS][3];
585 static float plane_u[MAXS][3];
586 static float plane_v[MAXS][3];
587 static int plane_f[MAXS];
588 static int plane_m[MAXS];
590 static void make_plane(int pi, int x0, int y0, int z0,
591 int x1, int y1, int z1,
592 int x2, int y2, int z2,
593 int tu, int tv, int r,
594 float su, float sv, int fl, const char *s)
596 static const float base[6][3][3] = {
597 {{ 0, 0, 1 }, { 1, 0, 0 }, { 0, -1, 0 }},
598 {{ 0, 0, -1 }, { 1, 0, 0 }, { 0, -1, 0 }},
599 {{ 1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
600 {{ -1, 0, 0 }, { 0, 0, -1 }, { 0, -1, 0 }},
601 {{ 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
602 {{ 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, 1 }},
606 float p0[3], p1[3], p2[3];
607 float u[3], v[3], p[3];
612 size_image(s, &w, &h);
614 plane_f[pi] = fl ? L_DETAIL : 0;
616 p0[0] = +(float) x0 / SCALE;
617 p0[1] = +(float) z0 / SCALE;
618 p0[2] = -(float) y0 / SCALE;
620 p1[0] = +(float) x1 / SCALE;
621 p1[1] = +(float) z1 / SCALE;
622 p1[2] = -(float) y1 / SCALE;
624 p2[0] = +(float) x2 / SCALE;
625 p2[1] = +(float) z2 / SCALE;
626 p2[2] = -(float) y2 / SCALE;
631 v_crs(plane_n[pi], u, v);
632 v_nrm(plane_n[pi], plane_n[pi]);
634 plane_d[pi] = v_dot(plane_n[pi], p1);
636 for (i = 0; i < 6; i++)
637 if ((k = v_dot(plane_n[pi], base[i][0])) >= d)
647 m_rot(R, base[n][0], V_RAD(r));
649 v_mad(p, p, base[n][1], su * tu / SCALE);
650 v_mad(p, p, base[n][2], sv * tv / SCALE);
652 m_vxfm(plane_u[pi], R, base[n][1]);
653 m_vxfm(plane_v[pi], R, base[n][2]);
654 m_vxfm(plane_p[pi], R, p);
656 v_scl(plane_u[pi], plane_u[pi], 64.f / w);
657 v_scl(plane_v[pi], plane_v[pi], 64.f / h);
659 v_scl(plane_u[pi], plane_u[pi], 1.f / su);
660 v_scl(plane_v[pi], plane_v[pi], 1.f / sv);
663 /*---------------------------------------------------------------------------*/
672 static int map_token(FILE *fin, int pi, char key[MAXSTR], char val[MAXSTR])
676 if (fgets(buf, MAXSTR, fin))
686 /* Scan the beginning or end of a block. */
688 if (buf[0] == '{') return T_BEG;
689 if (buf[0] == '}') return T_END;
691 /* Scan a key-value pair. */
695 strcpy(key, strtok(buf, "\""));
696 (void) strtok(NULL, "\"");
697 strcpy(val, strtok(NULL, "\""));
708 "%s %d %d %d %f %f %d",
709 &c, &x0, &y0, &z0, &c,
710 &c, &x1, &y1, &z1, &c,
711 &c, &x2, &y2, &z2, &c,
712 key, &tu, &tv, &r, &su, &sv, &fl) == 22)
714 make_plane(pi, x0, y0, z0,
717 tu, tv, r, su, sv, fl, key);
721 /* If it's not recognized, it must be uninteresting. */
728 /*---------------------------------------------------------------------------*/
730 /* Parse a lump from the given file and add it to the solid. */
732 static void read_lump(struct s_file *fp, FILE *fin)
738 struct s_lump *lp = fp->lv + incl(fp);
742 while ((t = map_token(fin, fp->sc, k, v)))
746 fp->sv[fp->sc].n[0] = plane_n[fp->sc][0];
747 fp->sv[fp->sc].n[1] = plane_n[fp->sc][1];
748 fp->sv[fp->sc].n[2] = plane_n[fp->sc][2];
749 fp->sv[fp->sc].d = plane_d[fp->sc];
751 plane_m[fp->sc] = read_mtrl(fp, k);
753 fp->iv[fp->ic] = fp->sc;
763 /*---------------------------------------------------------------------------*/
765 static void make_path(struct s_file *fp,
767 char v[][MAXSTR], int c)
769 int i, pi = incp(fp);
771 struct s_path *pp = fp->pv + pi;
780 for (i = 0; i < c; i++)
782 if (strcmp(k[i], "targetname") == 0)
785 if (strcmp(k[i], "target") == 0)
786 make_ref(v[i], &pp->pi);
788 if (strcmp(k[i], "state") == 0)
791 if (strcmp(k[i], "speed") == 0)
792 sscanf(v[i], "%f", &pp->t);
794 if (strcmp(k[i], "origin") == 0)
796 int x = 0, y = 0, z = 0;
798 sscanf(v[i], "%d %d %d", &x, &y, &z);
800 pp->p[0] = +(float) x / SCALE;
801 pp->p[1] = +(float) z / SCALE;
802 pp->p[2] = -(float) y / SCALE;
807 static void make_body(struct s_file *fp,
809 char v[][MAXSTR], int c, int l0)
811 int i, bi = incb(fp);
822 struct s_body *bp = fp->bv + bi;
828 for (i = 0; i < c; i++)
830 if (strcmp(k[i], "targetname") == 0)
833 else if (strcmp(k[i], "target") == 0)
834 make_ref(v[i], &bp->pi);
836 else if (strcmp(k[i], "model") == 0)
839 else if (strcmp(k[i], "origin") == 0)
840 sscanf(v[i], "%d %d %d", &x, &y, &z);
842 else if (strcmp(k[i], "classname") != 0)
844 /* Considers other strings as metadata */
845 strcat(fp->av, k[i]);
847 strcat(fp->av, v[i]);
848 strcat(fp->av, "\n");
849 fp->ac += (int) (strlen(v[i]) + (strlen(k[i])) + 2);
854 bp->lc = fp->lc - l0;
856 bp->gc = fp->gc - g0;
858 for (i = 0; i < bp->gc; i++)
859 fp->iv[inci(fp)] = g0++;
861 p[0] = +(float) x / SCALE;
862 p[1] = +(float) z / SCALE;
863 p[2] = -(float) y / SCALE;
865 for (i = v0; i < fp->vc; i++)
866 v_add(fp->vv[i].p, fp->vv[i].p, p);
869 static void make_coin(struct s_file *fp,
871 char v[][MAXSTR], int c)
873 int i, ci = incc(fp);
875 struct s_coin *cp = fp->cv + ci;
882 for (i = 0; i < c; i++)
884 if (strcmp(k[i], "light") == 0)
885 sscanf(v[i], "%d", &cp->n);
887 if (strcmp(k[i], "origin") == 0)
889 int x = 0, y = 0, z = 0;
891 sscanf(v[i], "%d %d %d", &x, &y, &z);
893 cp->p[0] = +(float) x / SCALE;
894 cp->p[1] = +(float) z / SCALE;
895 cp->p[2] = -(float) y / SCALE;
900 static void make_bill(struct s_file *fp,
902 char v[][MAXSTR], int c)
904 int i, ri = incr(fp);
906 struct s_bill *rp = fp->rv + ri;
908 memset(rp, 0, sizeof (struct s_bill));
911 for (i = 0; i < c; i++)
913 if (strcmp(k[i], "width") == 0)
914 sscanf(v[i], "%f %f %f", rp->w, rp->w + 1, rp->w + 2);
915 if (strcmp(k[i], "height") == 0)
916 sscanf(v[i], "%f %f %f", rp->h, rp->h + 1, rp->h + 2);
918 if (strcmp(k[i], "xrot") == 0)
919 sscanf(v[i], "%f %f %f", rp->rx, rp->rx + 1, rp->rx + 2);
920 if (strcmp(k[i], "yrot") == 0)
921 sscanf(v[i], "%f %f %f", rp->ry, rp->ry + 1, rp->ry + 2);
922 if (strcmp(k[i], "zrot") == 0)
923 sscanf(v[i], "%f %f %f", rp->rz, rp->rz + 1, rp->rz + 2);
925 if (strcmp(k[i], "time") == 0)
926 sscanf(v[i], "%f", &rp->t);
927 if (strcmp(k[i], "dist") == 0)
928 sscanf(v[i], "%f", &rp->d);
929 if (strcmp(k[i], "flag") == 0)
930 sscanf(v[i], "%d", &rp->fl);
932 if (strcmp(k[i], "image") == 0)
934 rp->mi = read_mtrl(fp, v[i]);
935 fp->mv[rp->mi].fl |= M_CLAMPED;
938 if (strcmp(k[i], "origin") == 0)
940 int x = 0, y = 0, z = 0;
943 sscanf(v[i], "%d %d %d", &x, &y, &z);
945 p[0] = +(float) x / SCALE;
946 p[1] = +(float) z / SCALE;
947 p[2] = -(float) y / SCALE;
950 rp->rx[0] = V_DEG(fatan2f(+p[1], rp->d));
951 rp->ry[0] = V_DEG(fatan2f(+p[0], -p[2]));
955 if (rp->fl & B_ADDITIVE)
956 fp->mv[rp->mi].fl |= M_ADDITIVE;
959 static void make_goal(struct s_file *fp,
961 char v[][MAXSTR], int c)
963 int i, zi = incz(fp);
965 struct s_goal *zp = fp->zv + zi;
974 for (i = 0; i < c; i++)
976 if (strcmp(k[i], "radius") == 0)
977 sscanf(v[i], "%f", &zp->r);
978 if (strcmp(k[i], "skip") == 0)
979 sscanf(v[i], "%d", &zp->s);
980 if (strcmp(k[i], "special") == 0)
981 sscanf(v[i], "%d", &zp->c);
983 if (strcmp(k[i], "origin") == 0)
985 int x = 0, y = 0, z = 0;
987 sscanf(v[i], "%d %d %d", &x, &y, &z);
989 zp->p[0] = +(float) (x) / SCALE;
990 zp->p[1] = +(float) (z - 24) / SCALE;
991 zp->p[2] = -(float) (y) / SCALE;
996 static void make_view(struct s_file *fp,
998 char v[][MAXSTR], int c)
1000 int i, wi = incw(fp);
1002 struct s_view *wp = fp->wv + wi;
1011 for (i = 0; i < c; i++)
1013 if (strcmp(k[i], "target") == 0)
1014 make_ref(v[i], targ_wi + wi);
1016 if (strcmp(k[i], "origin") == 0)
1018 int x = 0, y = 0, z = 0;
1020 sscanf(v[i], "%d %d %d", &x, &y, &z);
1022 wp->p[0] = +(float) x / SCALE;
1023 wp->p[1] = +(float) z / SCALE;
1024 wp->p[2] = -(float) y / SCALE;
1029 static void make_jump(struct s_file *fp,
1031 char v[][MAXSTR], int c)
1033 int i, ji = incj(fp);
1035 struct s_jump *jp = fp->jv + ji;
1045 for (i = 0; i < c; i++)
1047 if (strcmp(k[i], "radius") == 0)
1048 sscanf(v[i], "%f", &jp->r);
1050 if (strcmp(k[i], "target") == 0)
1051 make_ref(v[i], targ_ji + ji);
1053 if (strcmp(k[i], "origin") == 0)
1055 int x = 0, y = 0, z = 0;
1057 sscanf(v[i], "%d %d %d", &x, &y, &z);
1059 jp->p[0] = +(float) x / SCALE;
1060 jp->p[1] = +(float) z / SCALE;
1061 jp->p[2] = -(float) y / SCALE;
1066 static void make_swch(struct s_file *fp,
1068 char v[][MAXSTR], int c)
1070 int i, xi = incx(fp);
1072 struct s_swch *xp = fp->xv + xi;
1085 for (i = 0; i < c; i++)
1087 if (strcmp(k[i], "radius") == 0)
1088 sscanf(v[i], "%f", &xp->r);
1090 if (strcmp(k[i], "target") == 0)
1091 make_ref(v[i], &xp->pi);
1093 if (strcmp(k[i], "timer") == 0)
1094 sscanf(v[i], "%f", &xp->t0);
1096 if (strcmp(k[i], "state") == 0)
1099 if (strcmp(k[i], "invisible") == 0)
1102 if (strcmp(k[i], "origin") == 0)
1104 int x = 0, y = 0, z = 0;
1106 sscanf(v[i], "%d %d %d", &x, &y, &z);
1108 xp->p[0] = +(float) x / SCALE;
1109 xp->p[1] = +(float) z / SCALE;
1110 xp->p[2] = -(float) y / SCALE;
1115 static void make_targ(struct s_file *fp,
1117 char v[][MAXSTR], int c)
1121 targ_p[targ_n][0] = 0.f;
1122 targ_p[targ_n][1] = 0.f;
1123 targ_p[targ_n][3] = 0.f;
1125 for (i = 0; i < c; i++)
1127 if (strcmp(k[i], "targetname") == 0)
1128 make_sym(v[i], targ_n);
1130 if (strcmp(k[i], "origin") == 0)
1132 int x = 0, y = 0, z = 0;
1134 sscanf(v[i], "%d %d %d", &x, &y, &z);
1136 targ_p[targ_n][0] = +(float) x / SCALE;
1137 targ_p[targ_n][1] = +(float) z / SCALE;
1138 targ_p[targ_n][2] = -(float) y / SCALE;
1145 static void make_ball(struct s_file *fp,
1147 char v[][MAXSTR], int c)
1149 int i, ui = incu(fp);
1151 struct s_ball *up = fp->uv + ui;
1175 for (i = 0; i < c; i++)
1177 if (strcmp(k[i], "radius") == 0)
1178 sscanf(v[i], "%f", &up->r);
1180 if (strcmp(k[i], "origin") == 0)
1182 int x = 0, y = 0, z = 0;
1184 sscanf(v[i], "%d %d %d", &x, &y, &z);
1186 up->p[0] = +(float) (x) / SCALE;
1187 up->p[1] = +(float) (z - 24) / SCALE;
1188 up->p[2] = -(float) (y) / SCALE;
1192 up->p[1] += up->r + SMALL;
1195 /*---------------------------------------------------------------------------*/
1197 static void read_ent(struct s_file *fp, FILE *fin)
1199 char k[MAXKEY][MAXSTR];
1200 char v[MAXKEY][MAXSTR];
1201 int t, i = 0, c = 0;
1205 while ((t = map_token(fin, -1, k[c], v[c])))
1209 if (strcmp(k[c], "classname") == 0)
1213 if (t == T_BEG) read_lump(fp, fin);
1214 if (t == T_END) break;
1217 if (!strcmp(v[i], "light")) make_coin(fp, k, v, c);
1218 if (!strcmp(v[i], "info_camp")) make_swch(fp, k, v, c);
1219 if (!strcmp(v[i], "info_null")) make_bill(fp, k, v, c);
1220 if (!strcmp(v[i], "path_corner")) make_path(fp, k, v, c);
1221 if (!strcmp(v[i], "info_player_start")) make_ball(fp, k, v, c);
1222 if (!strcmp(v[i], "info_player_intermission")) make_view(fp, k, v, c);
1223 if (!strcmp(v[i], "info_player_deathmatch")) make_goal(fp, k, v, c);
1224 if (!strcmp(v[i], "target_teleporter")) make_jump(fp, k, v, c);
1225 if (!strcmp(v[i], "target_position")) make_targ(fp, k, v, c);
1226 if (!strcmp(v[i], "worldspawn")) make_body(fp, k, v, c, l0);
1227 if (!strcmp(v[i], "func_train")) make_body(fp, k, v, c, l0);
1228 if (!strcmp(v[i], "misc_model")) make_body(fp, k, v, c, l0);
1231 static void read_map(struct s_file *fp, FILE *fin)
1237 while ((t = map_token(fin, -1, k, v)))
1242 /*---------------------------------------------------------------------------*/
1244 /* Test the location of a point with respect to a side plane. */
1246 static int fore_side(const float p[3], const struct s_side *sp)
1248 return (v_dot(p, sp->n) - sp->d > +SMALL) ? 1 : 0;
1251 static int on_side(const float p[3], const struct s_side *sp)
1253 float d = v_dot(p, sp->n) - sp->d;
1255 return (-SMALL < d && d < +SMALL) ? 1 : 0;
1258 /*---------------------------------------------------------------------------*/
1260 * Confirm that the addition of a vert would not result in degenerate
1264 static int ok_vert(const struct s_file *fp,
1265 const struct s_lump *lp, const float p[3])
1270 for (i = 0; i < lp->vc; i++)
1272 float *q = fp->vv[fp->iv[lp->v0 + i]].p;
1276 if (v_len(r) < SMALL)
1282 /*---------------------------------------------------------------------------*/
1285 * The following functions take the set of planes defining a lump and
1286 * find the verts, edges, and geoms that describe its boundaries. To
1287 * do this, they first find the verts, and then search these verts for
1288 * valid edges and geoms. It may be more efficient to compute edges
1289 * and geoms directly by clipping down infinite line segments and
1290 * planes, but this would be more complex and prone to numerical
1295 * Given 3 side planes, compute the point of intersection, if any.
1296 * Confirm that this point falls within the current lump, and that it
1297 * is unique. Add it as a vert of the solid.
1299 static void clip_vert(struct s_file *fp,
1300 struct s_lump *lp, int si, int sj, int sk)
1302 float M[16], X[16], I[16];
1306 d[0] = fp->sv[si].d;
1307 d[1] = fp->sv[sj].d;
1308 d[2] = fp->sv[sk].d;
1310 m_basis(M, fp->sv[si].n, fp->sv[sj].n, fp->sv[sk].n);
1317 for (i = 0; i < lp->sc; i++)
1319 int si = fp->iv[lp->s0 + i];
1321 if (fore_side(p, fp->sv + si))
1325 if (ok_vert(fp, lp, p))
1327 v_cpy(fp->vv[fp->vc].p, p);
1329 fp->iv[fp->ic] = fp->vc;
1338 * Given two side planes, find an edge along their intersection by
1339 * finding a pair of vertices that fall on both planes. Add it to the
1342 static void clip_edge(struct s_file *fp,
1343 struct s_lump *lp, int si, int sj)
1347 for (i = 1; i < lp->vc; i++)
1348 for (j = 0; j < i; j++)
1350 int vi = fp->iv[lp->v0 + i];
1351 int vj = fp->iv[lp->v0 + j];
1353 if (on_side(fp->vv[vi].p, fp->sv + si) &&
1354 on_side(fp->vv[vj].p, fp->sv + si) &&
1355 on_side(fp->vv[vi].p, fp->sv + sj) &&
1356 on_side(fp->vv[vj].p, fp->sv + sj))
1358 fp->ev[fp->ec].vi = vi;
1359 fp->ev[fp->ec].vj = vj;
1361 fp->iv[fp->ic] = fp->ec;
1371 * Find all verts that lie on the given side of the lump. Sort these
1372 * verts to have a counter-clockwise winding about the plane normal.
1373 * Create geoms to tessalate the resulting convex polygon.
1375 static void clip_geom(struct s_file *fp,
1376 struct s_lump *lp, int si)
1378 int m[256], t[256], d, i, j, n = 0;
1383 struct s_side *sp = fp->sv + si;
1387 for (i = 0; i < lp->vc; i++)
1389 int vi = fp->iv[lp->v0 + i];
1391 if (on_side(fp->vv[vi].p, sp))
1396 v_add(v, fp->vv[vi].p, plane_p[si]);
1398 fp->tv[t[n]].u[0] = v_dot(v, plane_u[si]);
1399 fp->tv[t[n]].u[1] = v_dot(v, plane_v[si]);
1407 for (i = 1; i < n; i++)
1408 for (j = i + 1; j < n; j++)
1410 v_sub(u, fp->vv[m[i]].p, fp->vv[m[0]].p);
1411 v_sub(v, fp->vv[m[j]].p, fp->vv[m[0]].p);
1414 if (v_dot(w, sp->n) < 0.f)
1428 for (i = 0; i < n - 2; i++)
1430 fp->gv[fp->gc].mi = plane_m[si];
1432 fp->gv[fp->gc].ti = t[0];
1433 fp->gv[fp->gc].tj = t[i + 1];
1434 fp->gv[fp->gc].tk = t[i + 2];
1436 fp->gv[fp->gc].si = si;
1437 fp->gv[fp->gc].sj = si;
1438 fp->gv[fp->gc].sk = si;
1440 fp->gv[fp->gc].vi = m[0];
1441 fp->gv[fp->gc].vj = m[i + 1];
1442 fp->gv[fp->gc].vk = m[i + 2];
1444 fp->iv[fp->ic] = fp->gc;
1452 * Iterate the sides of the lump, attemping to generate a new vert for
1453 * each trio of planes, a new edge for each pair of planes, and a new
1454 * set of geom for each visible plane.
1456 static void clip_lump(struct s_file *fp, struct s_lump *lp)
1463 for (i = 2; i < lp->sc; i++)
1464 for (j = 1; j < i; j++)
1465 for (k = 0; k < j; k++)
1469 fp->iv[lp->s0 + k]);
1474 for (i = 1; i < lp->sc; i++)
1475 for (j = 0; j < i; j++)
1478 fp->iv[lp->s0 + j]);
1483 for (i = 0; i < lp->sc; i++)
1484 if (fp->mv[plane_m[fp->iv[lp->s0 + i]]].d[3] > 0)
1486 fp->iv[lp->s0 + i]);
1488 for (i = 0; i < lp->sc; i++)
1489 if (plane_f[fp->iv[lp->s0 + i]])
1493 static void clip_file(struct s_file *fp)
1497 for (i = 0; i < fp->lc; i++)
1498 clip_lump(fp, fp->lv + i);
1501 /*---------------------------------------------------------------------------*/
1504 * For each body element type, determine if element 'p' is equivalent
1505 * to element 'q'. This is more than a simple memory compare. It
1506 * effectively snaps mtrls and verts togather, and may reverse the
1507 * winding of an edge or a geom. This is done in order to maximize
1508 * the number of elements that can be eliminated.
1511 static int comp_mtrl(const struct s_mtrl *mp, const struct s_mtrl *mq)
1513 if (fabs(mp->d[0] - mq->d[0]) > SMALL) return 0;
1514 if (fabs(mp->d[1] - mq->d[1]) > SMALL) return 0;
1515 if (fabs(mp->d[2] - mq->d[2]) > SMALL) return 0;
1516 if (fabs(mp->d[3] - mq->d[3]) > SMALL) return 0;
1518 if (fabs(mp->a[0] - mq->a[0]) > SMALL) return 0;
1519 if (fabs(mp->a[1] - mq->a[1]) > SMALL) return 0;
1520 if (fabs(mp->a[2] - mq->a[2]) > SMALL) return 0;
1521 if (fabs(mp->a[3] - mq->a[3]) > SMALL) return 0;
1523 if (fabs(mp->s[0] - mq->s[0]) > SMALL) return 0;
1524 if (fabs(mp->s[1] - mq->s[1]) > SMALL) return 0;
1525 if (fabs(mp->s[2] - mq->s[2]) > SMALL) return 0;
1526 if (fabs(mp->s[3] - mq->s[3]) > SMALL) return 0;
1528 if (fabs(mp->e[0] - mq->e[0]) > SMALL) return 0;
1529 if (fabs(mp->e[1] - mq->e[1]) > SMALL) return 0;
1530 if (fabs(mp->e[2] - mq->e[2]) > SMALL) return 0;
1531 if (fabs(mp->e[3] - mq->e[3]) > SMALL) return 0;
1533 if (fabs(mp->h[0] - mq->h[0]) > SMALL) return 0;
1535 if (strncmp(mp->f, mq->f, PATHMAX)) return 0;
1540 static int comp_vert(const struct s_vert *vp, const struct s_vert *vq)
1542 if (fabs(vp->p[0] - vq->p[0]) > SMALL) return 0;
1543 if (fabs(vp->p[1] - vq->p[1]) > SMALL) return 0;
1544 if (fabs(vp->p[2] - vq->p[2]) > SMALL) return 0;
1549 static int comp_edge(const struct s_edge *ep, const struct s_edge *eq)
1551 if (ep->vi != eq->vi && ep->vi != eq->vj) return 0;
1552 if (ep->vj != eq->vi && ep->vj != eq->vj) return 0;
1557 static int comp_side(const struct s_side *sp, const struct s_side *sq)
1559 if (fabs(sp->d - sq->d) > SMALL) return 0;
1560 if (v_dot(sp->n, sq->n) < 0.9999) return 0;
1565 static int comp_texc(const struct s_texc *tp, const struct s_texc *tq)
1567 if (fabs(tp->u[0] - tq->u[0]) > SMALL) return 0;
1568 if (fabs(tp->u[1] - tq->u[1]) > SMALL) return 0;
1573 static int comp_geom(const struct s_geom *gp, const struct s_geom *gq)
1575 if (gp->mi != gq->mi) return 0;
1577 if (gp->ti != gq->ti) return 0;
1578 if (gp->si != gq->si) return 0;
1579 if (gp->vi != gq->vi) return 0;
1581 if (gp->tj != gq->tj) return 0;
1582 if (gp->sj != gq->sj) return 0;
1583 if (gp->vj != gq->vj) return 0;
1585 if (gp->tk != gq->tk) return 0;
1586 if (gp->sk != gq->sk) return 0;
1587 if (gp->vk != gq->vk) return 0;
1592 /*---------------------------------------------------------------------------*/
1595 * For each file element type, replace all references to element 'i'
1596 * with a reference to element 'j'. These are used when optimizing
1597 * and sorting the file.
1600 static void swap_mtrl(struct s_file *fp, int mi, int mj)
1604 for (i = 0; i < fp->gc; i++)
1605 if (fp->gv[i].mi == mi) fp->gv[i].mi = mj;
1606 for (i = 0; i < fp->rc; i++)
1607 if (fp->rv[i].mi == mi) fp->rv[i].mi = mj;
1610 static void swap_vert(struct s_file *fp, int vi, int vj)
1614 for (i = 0; i < fp->ec; i++)
1616 if (fp->ev[i].vi == vi) fp->ev[i].vi = vj;
1617 if (fp->ev[i].vj == vi) fp->ev[i].vj = vj;
1620 for (i = 0; i < fp->gc; i++)
1622 if (fp->gv[i].vi == vi) fp->gv[i].vi = vj;
1623 if (fp->gv[i].vj == vi) fp->gv[i].vj = vj;
1624 if (fp->gv[i].vk == vi) fp->gv[i].vk = vj;
1627 for (i = 0; i < fp->lc; i++)
1628 for (j = 0; j < fp->lv[i].vc; j++)
1629 if (fp->iv[fp->lv[i].v0 + j] == vi)
1630 fp->iv[fp->lv[i].v0 + j] = vj;
1633 static void swap_edge(struct s_file *fp, int ei, int ej)
1637 for (i = 0; i < fp->lc; i++)
1638 for (j = 0; j < fp->lv[i].ec; j++)
1639 if (fp->iv[fp->lv[i].e0 + j] == ei)
1640 fp->iv[fp->lv[i].e0 + j] = ej;
1643 static void swap_side(struct s_file *fp, int si, int sj)
1647 for (i = 0; i < fp->gc; i++)
1649 if (fp->gv[i].si == si) fp->gv[i].si = sj;
1650 if (fp->gv[i].sj == si) fp->gv[i].sj = sj;
1651 if (fp->gv[i].sk == si) fp->gv[i].sk = sj;
1653 for (i = 0; i < fp->nc; i++)
1654 if (fp->nv[i].si == si) fp->nv[i].si = sj;
1656 for (i = 0; i < fp->lc; i++)
1657 for (j = 0; j < fp->lv[i].sc; j++)
1658 if (fp->iv[fp->lv[i].s0 + j] == si)
1659 fp->iv[fp->lv[i].s0 + j] = sj;
1662 static void swap_texc(struct s_file *fp, int ti, int tj)
1666 for (i = 0; i < fp->gc; i++)
1668 if (fp->gv[i].ti == ti) fp->gv[i].ti = tj;
1669 if (fp->gv[i].tj == ti) fp->gv[i].tj = tj;
1670 if (fp->gv[i].tk == ti) fp->gv[i].tk = tj;
1675 static void swap_geom(struct s_file *fp, int gi, int gj)
1679 for (i = 0; i < fp->lc; i++)
1680 for (j = 0; j < fp->lv[i].gc; j++)
1681 if (fp->iv[fp->lv[i].g0 + j] == gi)
1682 fp->iv[fp->lv[i].g0 + j] = gj;
1684 for (i = 0; i < fp->bc; i++)
1685 for (j = 0; j < fp->bv[i].gc; j++)
1686 if (fp->iv[fp->bv[i].g0 + j] == gi)
1687 fp->iv[fp->bv[i].g0 + j] = gj;
1690 /*---------------------------------------------------------------------------*/
1692 static void uniq_mtrl(struct s_file *fp)
1696 for (i = 0; i < fp->mc; i++)
1698 for (j = 0; j < k; j++)
1699 if (comp_mtrl(fp->mv + i, fp->mv + j))
1701 swap_mtrl(fp, i, j);
1709 fp->mv[k] = fp->mv[i];
1710 swap_mtrl(fp, i, k);
1719 static void uniq_vert(struct s_file *fp)
1723 for (i = 0; i < fp->vc; i++)
1725 for (j = 0; j < k; j++)
1726 if (comp_vert(fp->vv + i, fp->vv + j))
1728 swap_vert(fp, i, j);
1736 fp->vv[k] = fp->vv[i];
1737 swap_vert(fp, i, k);
1746 static void uniq_edge(struct s_file *fp)
1750 for (i = 0; i < fp->ec; i++)
1752 for (j = 0; j < k; j++)
1753 if (comp_edge(fp->ev + i, fp->ev + j))
1755 swap_edge(fp, i, j);
1763 fp->ev[k] = fp->ev[i];
1764 swap_edge(fp, i, k);
1773 static void uniq_geom(struct s_file *fp)
1777 for (i = 0; i < fp->gc; i++)
1779 for (j = 0; j < k; j++)
1780 if (comp_geom(fp->gv + i, fp->gv + j))
1782 swap_geom(fp, i, j);
1790 fp->gv[k] = fp->gv[i];
1791 swap_geom(fp, i, k);
1800 static void uniq_texc(struct s_file *fp)
1804 for (i = 0; i < fp->tc; i++)
1806 for (j = 0; j < k; j++)
1807 if (comp_texc(fp->tv + i, fp->tv + j))
1809 swap_texc(fp, i, j);
1817 fp->tv[k] = fp->tv[i];
1818 swap_texc(fp, i, k);
1827 static void uniq_side(struct s_file *fp)
1831 for (i = 0; i < fp->sc; i++)
1833 for (j = 0; j < k; j++)
1834 if (comp_side(fp->sv + i, fp->sv + j))
1836 swap_side(fp, i, j);
1844 fp->sv[k] = fp->sv[i];
1845 swap_side(fp, i, k);
1854 static void uniq_file(struct s_file *fp)
1864 /*---------------------------------------------------------------------------*/
1866 static void sort_file(struct s_file *fp)
1870 /* Sort billboards farthest to nearest. */
1872 for (i = 0; i < fp->rc; i++)
1873 for (j = i + 1; j < fp->rc; j++)
1874 if (fp->rv[j].d > fp->rv[i].d)
1879 fp->rv[i] = fp->rv[j];
1883 /* Ensure the first vertex is the lowest. */
1885 for (i = 0; i < fp->vc; i++)
1886 if (fp->vv[0].p[1] > fp->vv[i].p[1])
1891 fp->vv[0] = fp->vv[i];
1894 swap_vert(fp, 0, -1);
1895 swap_vert(fp, i, 0);
1896 swap_vert(fp, -1, i);
1900 /*---------------------------------------------------------------------------*/
1902 static int test_lump_side(const struct s_file *fp,
1903 const struct s_lump *lp,
1904 const struct s_side *sp)
1912 /* If the given side is part of the given lump, then the lump is behind. */
1914 for (si = 0; si < lp->sc; si++)
1915 if (fp->sv + fp->iv[lp->s0 + si] == sp)
1918 /* Check if each lump vertex is in front of, behind, on the side. */
1920 for (vi = 0; vi < lp->vc; vi++)
1922 float d = v_dot(fp->vv[fp->iv[lp->v0 + vi]].p, sp->n) - sp->d;
1928 /* If no verts are behind, the lump is in front, and vice versa. */
1930 if (f > 0 && b == 0) return +1;
1931 if (b > 0 && f == 0) return -1;
1933 /* Else, the lump crosses the side. */
1938 static int node_node(struct s_file *fp, int l0, int lc)
1942 /* Base case. Dump all given lumps into a leaf node. */
1944 fp->nv[fp->nc].si = -1;
1945 fp->nv[fp->nc].ni = -1;
1946 fp->nv[fp->nc].nj = -1;
1947 fp->nv[fp->nc].l0 = l0;
1948 fp->nv[fp->nc].lc = lc;
1958 int li = 0, lic = 0;
1959 int lj = 0, ljc = 0;
1960 int lk = 0, lkc = 0;
1963 /* Find the side that most evenly splits the given lumps. */
1965 for (si = 0; si < fp->sc; si++)
1971 for (li = 0; li < lc; li++)
1972 if ((k = test_lump_side(fp, fp->lv + l0 + li, fp->sv + si)))
1979 if ((d < sjd) || (d == sjd && o < sjo))
1987 /* Flag each lump with its position WRT the side. */
1989 for (li = 0; li < lc; li++)
1990 switch (test_lump_side(fp, fp->lv + l0 + li, fp->sv + sj))
1992 case +1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x10; break;
1993 case 0: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x20; break;
1994 case -1: fp->lv[l0+li].fl = (fp->lv[l0+li].fl & 1) | 0x40; break;
1997 /* Sort all lumps in the range by their flag values. */
1999 for (li = 1; li < lc; li++)
2000 for (lj = 0; lj < li; lj++)
2001 if (fp->lv[l0 + li].fl < fp->lv[l0 + lj].fl)
2005 l = fp->lv[l0 + li];
2006 fp->lv[l0 + li] = fp->lv[l0 + lj];
2007 fp->lv[l0 + lj] = l;
2010 /* Establish the in-front, on, and behind lump ranges. */
2016 for (i = lc - 1; i >= 0; i--)
2017 switch (fp->lv[l0 + i].fl & 0xf0)
2019 case 0x10: li = l0 + i; lic++; break;
2020 case 0x20: lj = l0 + i; ljc++; break;
2021 case 0x40: lk = l0 + i; lkc++; break;
2024 /* Add the lumps on the side to the node. */
2029 fp->nv[i].ni = node_node(fp, li, lic);
2031 fp->nv[i].nj = node_node(fp, lk, lkc);
2039 static void node_file(struct s_file *fp)
2043 /* Sort the lumps of each body into BSP nodes. */
2045 for (bi = 0; bi < fp->bc; bi++)
2046 fp->bv[bi].ni = node_node(fp, fp->bv[bi].l0, fp->bv[bi].lc);
2049 /*---------------------------------------------------------------------------*/
2051 static void dump_file(struct s_file *p, const char *name)
2056 int m = p->rc + p->cc * 128 + (p->zc * p->jc + p->xc) * 32;
2058 /* Count the number of solid lumps. */
2060 for (i = 0; i < p->lc; i++)
2061 if ((p->lv[i].fl & 1) == 0)
2064 /* Count the number of visible geoms. */
2066 for (i = 0; i < p->bc; i++)
2068 for (j = 0; j < p->bv[i].lc; j++)
2069 m += p->lv[p->bv[i].l0 + j].gc;
2073 /* Count the total value of all coins. */
2075 for (i = 0; i < p->cc; i++)
2078 printf("%s (%d/%d/$%d)\n"
2079 " mtrl vert edge side texc"
2080 " geom lump path node body\n"
2081 "%6d%6d%6d%6d%6d%6d%6d%6d%6d%6d\n"
2082 " coin goal view jump swch"
2083 " bill ball char indx\n"
2084 "%6d%6d%6d%6d%6d%6d%6d%6d%6d\n",
2086 p->mc, p->vc, p->ec, p->sc, p->tc,
2087 p->gc, p->lc, p->pc, p->nc, p->bc,
2088 p->cc, p->zc, p->wc, p->jc, p->xc,
2089 p->rc, p->uc, p->ac, p->ic);
2092 /* Skip the ugly SDL main substitution since we only need sdl_image. */
2097 int main(int argc, char *argv[])
2106 if (config_data_path(argv[2], NULL))
2108 strncpy(src, argv[1], MAXSTR);
2109 strncpy(dst, argv[1], MAXSTR);
2111 if (strcmp(dst + strlen(dst) - 4, ".map") == 0)
2112 strcpy(dst + strlen(dst) - 4, ".sol");
2114 strcat(dst, ".sol");
2116 if ((fin = fopen(src, "r")))
2138 else fprintf(stderr, "Failure to establish data directory\n");
2140 else fprintf(stderr, "Usage: %s <map> [data]\n", argv[0]);