#define	LIBQTOOLS_CORE
#include "../include/libqtools.h"
#include "../include/libqbuild.h"
#include "TDDD.h"

#define	CLUSTER_POINTS		(CLUSTER_FACES / 3)
#define	CLUSTER_GROUPS		16
#define	CLUSTER_SUBGROUP	(CLUSTER_FACES / 3)

int TDDDmax_pcount = 0;
int TDDDmax_ecount = 0;
int TDDDmax_tcount = 0;
int TDDDmax_gcount = 0;
struct points *TDDDpoints = 0;
struct edges *TDDDedges = 0;
struct faces *TDDDfaces = 0;
int TDDDgroups = 0;
int *TDDDgroupsizes = 0;
struct facesubgroup **TDDDgroup = 0;
struct brush5 *TDDDbrushes = 0;

int TDDDnumpoints = 0;
int TDDDnumedges = 0;
int TDDDnumfaces = 0;
int TDDDnumgroups = 0;
int TDDDnumobjects = 0;

int nummapbrushes;						/* 4 */

void AllocTDDDSubGroup(register unsigned short int faceNum, register char *texname)
{
  unsigned short int i, j;

#if	(WORDS_BIGENDIAN == 0)
  faceNum = BigShort(faceNum);
#endif

  if (!TDDDgroup) {
    if (!(TDDDgroup = (struct facesubgroup **)tmalloc(CLUSTER_GROUPS * sizeof(struct facesubgroup *))))
        Error(failed_memoryunsize, "group");
    if (!(TDDDgroupsizes = (int *)tmalloc(CLUSTER_GROUPS * sizeof(int))))
        Error(failed_memoryunsize, "group");
    if (!(TDDDbrushes = (struct brush5 *)tmalloc(CLUSTER_GROUPS * sizeof(struct brush5))))
        Error(failed_memoryunsize, "brush");
  }

  if (TDDDgroups >= TDDDmax_gcount) {
    if (!(TDDDgroup = (struct facesubgroup **)trealloc(TDDDgroup, ((TDDDmax_gcount += CLUSTER_GROUPS) * sizeof(struct facesubgroup *)))))
        Error(failed_memoryunsize, "group");
    if (!(TDDDgroupsizes = (int *)trealloc(TDDDgroupsizes, (TDDDmax_gcount * sizeof(int)))))
        Error(failed_memoryunsize, "group");
    if (!(TDDDbrushes = (struct brush5 *)trealloc(TDDDbrushes, (TDDDmax_gcount * sizeof(struct brush5)))))
        Error(failed_memoryunsize, "brush");
  }

  for (i = 0; i < TDDDgroups; i++)
    if (!__strncmp(TDDDgroup[i]->name, texname, 18))
      break;

  if (!TDDDgroup[i])
    if (!(TDDDgroup[i] = (struct facesubgroup *)tmalloc((CLUSTER_SUBGROUP * sizeof(unsigned short int)) + sizeof(struct facesubgroup))))
        Error(failed_memoryunsize, "subgroup");

  if (TDDDgroup[i]->count >= TDDDgroupsizes[i])
    if (!(TDDDgroup[i] = (struct facesubgroup *)trealloc(TDDDgroup[i], ((TDDDgroupsizes[i] += CLUSTER_SUBGROUP) * sizeof(unsigned short int)) + sizeof(struct facesubgroup))))
        Error(failed_memoryunsize, "subgroup");

  if (i == TDDDgroups) {
    __strncpy(TDDDgroup[i]->name, texname, 18);
    TDDDbrushes[i].fullscale = BigShort(1);
    TDDDbrushes[i].flags = BigShort(BRS_COLOR);
    TDDDbrushes[i].wflags = BigShort(BRW_REPEA);
    TDDDgroups++;
    TDDDnumgroups++;
  }

  for (j = 0; j < TDDDgroup[i]->count; j++)
    if (faceNum == TDDDgroup[i]->facelist[j])
      return;

  TDDDgroup[i]->facelist[j] = faceNum;
  TDDDgroup[i]->count++;
}

unsigned short int AllocTDDDPoint(register vector * point)
{
  unsigned short int i;

#if	(WORDS_BIGENDIAN == 0)
  point->x = BigLong(point->x);
  point->y = BigLong(point->y);
  point->z = BigLong(point->z);
#endif

  if (!TDDDpoints)
    if (!(TDDDpoints = (struct points *)tmalloc((CLUSTER_POINTS * sizeof(vector)) + sizeof(struct points))))
        Error(failed_memoryunsize, "point");

  if (TDDDpoints->pcount >= TDDDmax_pcount)
    if (!(TDDDpoints = (struct points *)trealloc(TDDDpoints, ((TDDDmax_pcount += CLUSTER_POINTS) * sizeof(vector)) + sizeof(struct points))))
        Error(failed_memoryunsize, "point");

  for (i = 0; i < TDDDpoints->pcount; i++) {
    if (point->x == TDDDpoints->points[i].x)
      if (point->y == TDDDpoints->points[i].y)
	if (point->z == TDDDpoints->points[i].z)
	  return i;
  }
  TDDDpoints->points[i].x = point->x;
  TDDDpoints->points[i].y = point->y;
  TDDDpoints->points[i].z = point->z;
  TDDDpoints->pcount++;
  TDDDnumpoints++;

  return i;
}

unsigned short int AllocTDDDEdge(register unsigned short int point0, register unsigned short int point1)
{
  unsigned short int i;

#if	(WORDS_BIGENDIAN == 0)
  point0 = BigShort(point0);
  point1 = BigShort(point1);
#endif

  if (!TDDDedges)
    if (!(TDDDedges = (struct edges *)tmalloc((CLUSTER_EDGES * sizeof(unsigned short int) * 2) + sizeof(struct edges))))
        Error(failed_memoryunsize, "edge");

  if (TDDDedges->ecount >= TDDDmax_ecount)
    if (!(TDDDedges = (struct edges *)trealloc(TDDDedges, ((TDDDmax_ecount += CLUSTER_EDGES) * sizeof(unsigned short int) * 2) + sizeof(struct edges))))
        Error(failed_memoryunsize, "edge");

  for (i = 0; i < TDDDedges->ecount; i++) {
    if (point0 == TDDDedges->edges[i][0])
      if (point1 == TDDDedges->edges[i][1])
	return i;
  }
  TDDDedges->edges[i][0] = point0;
  TDDDedges->edges[i][1] = point1;
  TDDDedges->ecount++;
  TDDDnumedges++;
  return i;
}

unsigned short int AllocTDDDFace(register unsigned short int connect0, register unsigned short int connect1, register unsigned short int connect2)
{
  unsigned short int i;

#if	(WORDS_BIGENDIAN == 0)
  connect0 = BigShort(connect0);
  connect1 = BigShort(connect1);
  connect2 = BigShort(connect2);
#endif

  if (!TDDDfaces)
    if (!(TDDDfaces = (struct faces *)tmalloc((CLUSTER_FACES * sizeof(unsigned short int) * 3) + sizeof(struct faces))))
        Error(failed_memoryunsize, "face");

  if (TDDDfaces->tcount >= TDDDmax_tcount)
    if (!(TDDDfaces = (struct faces *)trealloc(TDDDfaces, ((TDDDmax_tcount += CLUSTER_FACES) * sizeof(unsigned short int) * 3) + sizeof(struct faces))))
        Error(failed_memoryunsize, "face");

  for (i = 0; i < TDDDfaces->tcount; i++) {
    if (connect0 == TDDDfaces->connects[i][0])
      if (connect1 == TDDDfaces->connects[i][1])
	if (connect2 == TDDDfaces->connects[i][2])
	  return i;
  }
  TDDDfaces->connects[i][0] = connect0;
  TDDDfaces->connects[i][1] = connect1;
  TDDDfaces->connects[i][2] = connect2;
  TDDDfaces->tcount++;
  TDDDnumfaces++;
  return i;
}

bool SaveFace(vec3_t point0, vec3_t point1, vec3_t point2, register char *texname)
{
  unsigned short int face, p0, p1, p2;
  vector p;

  p.x = float2fract(point0[0]);
  p.y = float2fract(point0[1]);
  p.z = float2fract(point0[2]);
  p0 = AllocTDDDPoint(&p);
  p.x = float2fract(point1[0]);
  p.y = float2fract(point1[1]);
  p.z = float2fract(point1[2]);
  p1 = AllocTDDDPoint(&p);
  p.x = float2fract(point2[0]);
  p.y = float2fract(point2[1]);
  p.z = float2fract(point2[2]);
  p2 = AllocTDDDPoint(&p);

  face = AllocTDDDFace(AllocTDDDEdge(p0, p1),
		       AllocTDDDEdge(p1, p2),
		       AllocTDDDEdge(p2, p0));
  AllocTDDDSubGroup(face, texname);
  return TRUE;
}

bool SetForm(register HANDLE outFile, register int *last, register int ID)
{
  int Form[3];
  
  Form[0] = BigLong(ID_FORM);
  Form[1] = BigLong(*last);
  Form[2] = BigLong(ID);

  *last = __ltell(outFile) + 8;
  __write(outFile, Form, 12);

  return TRUE;
}

bool SetRoot(register HANDLE outFile, register int *last, register int ID)
{
  int Root[2];
  
  Root[0] = BigLong(ID);
  Root[1] = BigLong(*last);
  
  *last = __ltell(outFile) + 8;
  __write(outFile, Root, 8);

  return TRUE;
}

bool SetEndM(register HANDLE outFile, register int ID)
{
  int EndM[2];
  
  EndM[0] = BigLong(ID);
  EndM[1] = BigLong(0);

  __write(outFile, EndM, 8);
  TDDDnumobjects++;

  return TRUE;
}

bool VerRoot(register HANDLE outFile, register int *last, register int ID)
{
  int this = __ltell(outFile);
  int Root[2];

  __lseek(outFile, *last - 8, SEEK_SET);
  *last = this - *last;
  
  Root[0] = BigLong(ID);
  Root[1] = BigLong(*last);

  __write(outFile, Root, 8);
  __lseek(outFile, this, SEEK_SET);

  return TRUE;
}

bool SetClassName(register HANDLE outFile, register char *className)
{
  int Class[2];
  
  Class[0] = BigLong(ID_NAME);
  Class[1] = BigLong(18);

  __write(outFile, Class, 8);
  __write(outFile, className, __strlen(className));

  return TRUE;
}

bool SetEntity(register HANDLE outFile, register char *variable, register char *content)
{
  int nameLen = __strlen(content);
  struct {
    int Entity[2];
    struct texture4 brushTex;
  } ent;

  nameLen = ((nameLen + 1) & ~1);
  nameLen++;
  nameLen = ((nameLen + 1) & ~1);
  __bzero(&ent.brushTex, sizeof(struct texture4));
  __strncpy(ent.brushTex.label, variable, 18 + 1);

  ent.Entity[0] = BigLong(ID_TXT4);
  ent.Entity[1] = BigLong(sizeof(struct texture4) + nameLen);
  ent.brushTex.flags = BigShort(TXT_DISAB);
  ent.brushTex.length = (char)nameLen;

  __write(outFile, &ent, 8 + sizeof(struct texture4));
  __write(outFile, content, nameLen - 1);
  __write(outFile, "\0", 1);

  return TRUE;
}

bool SetBrushes(register HANDLE outFile)
{
  int lastBrush, i;

  for (i = 0; i < TDDDgroups; i++) {
    /*
     * write only a subgroup if there are more than one
     * and if the subgroup contains members
     */
    if ((TDDDgroups > 1) && (TDDDgroup[i]->count > 0)) {
      SetRoot(outFile, &lastBrush, ID_FGRP);
      __write(outFile, TDDDgroup[i], 20 + (sizeof(unsigned short int) * TDDDgroup[i]->count));

      VerRoot(outFile, &lastBrush, ID_FGRP);
      __strncpy(TDDDbrushes[i].subgrp, TDDDgroup[i]->name, 18);
    }
    SetRoot(outFile, &lastBrush, ID_BRS5);
    TDDDbrushes[i].length = ((__strlen(TDDDgroup[i]->name + 1) + 1) & ~1);
    __write(outFile, &TDDDbrushes[i], sizeof(struct brush5));

    __write(outFile, TDDDgroup[i]->name, TDDDbrushes[i].length);
    VerRoot(outFile, &lastBrush, ID_BRS5);
    TDDDgroup[i]->count = 0;
  }
  TDDDgroups = 0;

  return TRUE;
}

bool SetPoints(register HANDLE outFile)
{
  if (TDDDpoints) {
    if (TDDDpoints->pcount) {
      int len = (sizeof(vector) * TDDDpoints->pcount) + sizeof(unsigned short int);
      int Points[2];

      Points[0] = BigLong(ID_PNTS);
      Points[1] = BigLong(len);

      __write(outFile, Points, 8);
      __write(outFile, TDDDpoints, len);

      TDDDpoints->pcount = 0;
    }
  }
  return TRUE;
}

bool SetEdges(register HANDLE outFile)
{
  if (TDDDedges) {
    if (TDDDedges->ecount) {
      int len = sizeof(unsigned short int) * ((TDDDedges->ecount * 2) + 1);
      int Edges[2];
      
      Edges[0] = BigLong(ID_EDGE);
      Edges[1] = BigLong(len);

      __write(outFile, Edges, 8);
      __write(outFile, TDDDedges, len);

      TDDDedges->ecount = 0;
    }
  }
  return TRUE;
}

bool SetFaces(register HANDLE outFile)
{
  if (TDDDfaces) {
    if (TDDDfaces->tcount) {
      int len = sizeof(unsigned short int) * ((TDDDfaces->tcount * 3) + 1);
      int Faces[2];
      
      Faces[0] = BigLong(ID_FACE);
      Faces[1] = BigLong(len);

      __write(outFile, Faces, 8);
      __write(outFile, TDDDfaces, len);

      TDDDfaces->tcount = 0;
    }
  }
  return TRUE;
}

bool SetOtherDefaults(register HANDLE outFile, register struct entity * ent)
{
  double posx = 0;
  double posy = 0;
  double posz = 0;
  int points, len, i;
  struct axis axis =
  {
    {0x00010000, 0x00000000, 0x00000000},
    {0x00000000, 0x00010000, 0x00000000},
    {0x00000000, 0x00000000, 0x00010000}};
  struct posi position =
  {
    {0, 0, 0}};
  struct shap shape;
  struct colr colour;
  int Defs[2];

  axis.xaxis.x = axis.yaxis.y = axis.zaxis.z = BigLong(0x00010000);
  shape.shape = BigShort(SH_AXIS);
  shape.lamp = BigShort(LP2_NOLAM);

  if (ent) {
    position.position.x = float2fract(ent->origin[0]);
    position.position.y = float2fract(ent->origin[1]);
    position.position.z = float2fract(ent->origin[2]);
    if (ent->style) {
      struct {
        int Light[2];
        struct int1 light;
      } lit;

      shape.lamp = BigShort(LP2_POINT);

      lit.Light[0] = BigLong(ID_INT1);
      lit.Light[1] = BigLong(sizeof(struct int1));
      lit.light.intensity.x = lit.light.intensity.y = lit.light.intensity.z = BigFloat(((unsigned int)ent->light << 22) / 75);

      __write(outFile, &lit, 8 + sizeof(struct int1));
    }
  }

  if (TDDDpoints)
    if (TDDDpoints->pcount) {
      struct {
        int Attr[2];
        struct bbox bound;
      } att;
      struct faceattr *attr = (struct faceattr *)tmalloc((TDDDfaces->tcount * sizeof(rgb)) + sizeof(struct faceattr));

      att.bound.maxs.x = att.bound.maxs.y = att.bound.maxs.z = 0x80000000;
      att.bound.mins.x = att.bound.mins.y = att.bound.mins.z = 0x7FFFFFFF;

      for (points = 0; points < TDDDpoints->pcount; points++) {
	if (TDDDpoints->points[points].x > att.bound.maxs.x)
	  att.bound.maxs.x = TDDDpoints->points[points].x;
	else if (TDDDpoints->points[points].x < att.bound.mins.x)
	  att.bound.mins.x = TDDDpoints->points[points].x;
	if (TDDDpoints->points[points].y > att.bound.maxs.y)
	  att.bound.maxs.y = TDDDpoints->points[points].y;
	else if (TDDDpoints->points[points].y < att.bound.mins.y)
	  att.bound.mins.y = TDDDpoints->points[points].y;
	if (TDDDpoints->points[points].z > att.bound.maxs.z)
	  att.bound.maxs.z = TDDDpoints->points[points].z;
	else if (TDDDpoints->points[points].z < att.bound.mins.z)
	  att.bound.mins.z = TDDDpoints->points[points].z;
	posx += fract2float(TDDDpoints->points[points].x);
	posy += fract2float(TDDDpoints->points[points].y);
	posz += fract2float(TDDDpoints->points[points].z);
      }
      position.position.x = BigLong(float2fract(posx / TDDDpoints->pcount));
      position.position.y = BigLong(float2fract(posy / TDDDpoints->pcount));
      position.position.z = BigLong(float2fract(posz / TDDDpoints->pcount));

      att.Attr[0] = BigLong(ID_BBOX);
      att.Attr[1] = BigLong(sizeof(struct bbox));
      att.bound.maxs.x = BigLong(att.bound.maxs.x - BigLong(position.position.x));
      att.bound.maxs.y = BigLong(att.bound.maxs.y - BigLong(position.position.y));
      att.bound.maxs.z = BigLong(att.bound.maxs.z - BigLong(position.position.z));
      att.bound.mins.x = BigLong(att.bound.mins.x - BigLong(position.position.x));
      att.bound.mins.y = BigLong(att.bound.mins.y - BigLong(position.position.y));
      att.bound.mins.z = BigLong(att.bound.mins.z - BigLong(position.position.z));

      __write(outFile, &att, 8 + sizeof(struct bbox));

      attr->count = BigShort(TDDDfaces->tcount);
      for (i = 0; i < TDDDfaces->tcount; i++) {
	attr->attr[i].r = 0xFF;
	attr->attr[i].g = 0xFF;
	attr->attr[i].b = 0xFF;
      }
      len = (TDDDfaces->tcount * sizeof(rgb)) + sizeof(unsigned short int);
      len = ((len + 1) & ~1);
      att.Attr[1] = BigLong(len);
      
      att.Attr[0] = BigLong(ID_CLST);
      __write(outFile, att.Attr, 8);
      __write(outFile, &attr, len);
      att.Attr[0] = BigLong(ID_RLST);
      __write(outFile, att.Attr, 8);
      __write(outFile, &attr, len);
      att.Attr[0] = BigLong(ID_TLST);
      __write(outFile, att.Attr, 8);
      __write(outFile, &attr, len);

      tfree(attr);
    }

  Defs[0] = BigLong(ID_AXIS);
  Defs[1] = BigLong(sizeof(struct axis));
  __write(outFile, Defs, 8);
  __write(outFile, &axis, sizeof(struct axis));

  Defs[0] = BigLong(ID_POSI);
  Defs[1] = BigLong(sizeof(struct posi));
  __write(outFile, Defs, 8);
  __write(outFile, &position, sizeof(struct posi));

  Defs[0] = BigLong(ID_SHP2);
  Defs[1] = BigLong(sizeof(struct shap));
  __write(outFile, Defs, 8);
  __write(outFile, &shape, sizeof(struct shap));

  colour.color = 0x00FFFFFF;
  Defs[1] = BigLong(sizeof(struct colr));

  Defs[0] = BigLong(ID_COLR);
  __write(outFile, Defs, 8);
  __write(outFile, &colour, sizeof(struct colr));
  Defs[0] = BigLong(ID_REFL);
  __write(outFile, Defs, 8);
  __write(outFile, &colour, sizeof(struct colr));
  Defs[0] = BigLong(ID_TRAN);
  __write(outFile, Defs, 8);
  __write(outFile, &colour, sizeof(struct colr));
  Defs[0] = BigLong(ID_SPC1);
  __write(outFile, Defs, 8);
  __write(outFile, &colour, sizeof(struct colr));

  return TRUE;
}

/*
 * convert a Imagine-TDDD to a Quake-Map
 */

/* IFF to pseudo IFF */
int fileIFFtopIFF(HANDLE iobFile, struct IFFchunk *IFFroot);
int memIFFtopIFF(unsigned char *iobMem, struct IFFchunk *IFFroot);

/* pseudo IFF to pseudo Brushes */
void pIFFtopBrushes(__memBase, struct IFFchunk *IFFroot, int iterVal, struct entity *fillEntity);

/* pseudo IFF to pseudo Map */
void pIFFtopMap(__memBase, struct IFFchunk *IFFroot, int iterVal);

void strlwrcpy(register char *dst, register char *src)
{
  int i, len = __strlen(src);

  for (i = 0; i < len; i++)
    dst[i] = (char)tolower((int)src[i]);
  dst[i] = 0;
}

int fileIFFtopIFF(register HANDLE iobFile, register struct IFFchunk *IFFroot)
{
  int processed = 0;

  /*
   * small hack -> IFFroot->iter is IFFpart->next
   */
  struct IFFchunk *IFFpart = (struct IFFchunk *)&IFFroot->size;

  while (IFFpart->type != BigLong(ID_TOBJ)) {
    int oldOffs;

    /*
     * allocate IFFchunk
     */
    IFFpart->next = (struct IFFchunk *)tmalloc(sizeof(struct IFFchunk));

    IFFpart = IFFpart->next;
    IFFpart->next = 0;
    IFFpart->iter = 0;
    IFFpart->data = 0;

    /*
     * read and parse IFFchunk
     */
    __read(iobFile, IFFpart, 8);
    IFFpart->size = ((BigLong(IFFpart->size) + 1) & ~1);
    processed += 8;
    oldOffs = __ltell(iobFile);

    if (IFFpart->type == BigLong(ID_OBJ)) {
      processed += fileIFFtopIFF(iobFile, IFFpart);
      if ((IFFroot->type == BigLong(ID_TDDD)) && (IFFroot->size == processed))
	break;
    }
    else if (IFFpart->type == BigLong(ID_DESC)) {
      processed += fileIFFtopIFF(iobFile, IFFpart);
      if ((IFFroot->type == BigLong(ID_OBJ)) && (IFFroot->size == processed))
	break;
    }
    else if (IFFpart->type != BigLong(ID_TOBJ)) {
      processed += IFFpart->size;
      IFFpart->data = (void *)tmalloc(IFFpart->size + 1);
      __read(iobFile, IFFpart->data, IFFpart->size);
    }
  }

  return processed;
}

int memIFFtopIFF(register unsigned char *iobMem, register struct IFFchunk *IFFroot)
{
  int processed = 0;

  /*
   * small hack -> IFFroot->iter is IFFpart->next
   */
  struct IFFchunk *IFFpart = (struct IFFchunk *)&IFFroot->size;

  while (IFFpart->type != BigLong(ID_TOBJ)) {
    /*
     * allocate IFFchunk
     */
    IFFpart->next = (struct IFFchunk *)tmalloc(sizeof(struct IFFchunk));

    IFFpart = IFFpart->next;
    IFFpart->next = 0;
    IFFpart->iter = 0;
    IFFpart->data = 0;

    /*
     * read and parse IFFchunk
     */
    __memcpy(IFFpart, iobMem, 8);
    iobMem += 8;
    IFFpart->size = ((BigLong(IFFpart->size) + 1) & ~1);
    processed += 8;

    if (IFFpart->type == BigLong(ID_OBJ)) {
      processed += memIFFtopIFF(iobMem, IFFpart);
      if ((IFFroot->type == BigLong(ID_TDDD)) && (IFFroot->size == processed))
	break;
    }
    else if (IFFpart->type == BigLong(ID_DESC)) {
      processed += memIFFtopIFF(iobMem, IFFpart);
      if ((IFFroot->type == BigLong(ID_OBJ)) && (IFFroot->size == processed))
	break;
    }
    else if (IFFpart->type != BigLong(ID_TOBJ)) {
      processed += IFFpart->size;
      IFFpart->data = (void *)tmalloc(IFFpart->size + 1);
      __memcpy(IFFpart->data, iobMem, IFFpart->size);
      iobMem += IFFpart->size;
    }
  }

  return processed;
}

void pIFFtopBrushes(__memBase, register struct IFFchunk *IFFroot, register int iterVal, register struct entity *fillEntity)
{
  struct IFFchunk *IFFpart = IFFroot;

  struct faces *facelist = 0;
  struct posi *origin = 0;
  struct edges *edgelist = 0;
  struct points *pointlist = 0;
  struct brush5 **brushtex = (struct brush5 **)tmalloc(15 * sizeof(struct brush5 *));
  int brushtexs = 0;
  struct facesubgroup **facegroup = (struct facesubgroup **)tmalloc(15 * sizeof(struct facesubgroup *));
  int facegroups = 0;

  /*
   * look for other iterated brushes
   */
  while (IFFpart) {
    struct IFFchunk *actIFFpart = IFFpart;
    int indentSpace = iterVal;

    while (indentSpace-- > 0) {
      oprintf("  ");
    }
    oprintf("%4s %d bytes\n", (char *)&IFFpart->type, IFFpart->size);

    /*
     * all types that are not DESC are for the actBrush
     * after first appeareance of DESC there are only DESCs
     */
    switch (BigLong(IFFpart->type)) {
      case ID_POSI:
	origin = (struct posi *)actIFFpart->data;
	break;
      case ID_FACE:
	facelist = (struct faces *)actIFFpart->data;
	break;
      case ID_EDGE:
	edgelist = (struct edges *)actIFFpart->data;
	break;
      case ID_PNTS:
	pointlist = (struct points *)actIFFpart->data;
	break;
      case ID_FGRP:
      case ID_FGR2:
      case ID_FGR3:
      case ID_FGR4:
	facegroup[facegroups] = (struct facesubgroup *)actIFFpart->data;
	facegroups++;
	break;
      case ID_BRS5:
	brushtex[brushtexs] = (struct brush5 *)actIFFpart->data;
	brushtexs++;
	break;
      case ID_DESC:
	pIFFtopBrushes(bspMem, actIFFpart->iter, iterVal + 1, fillEntity);
	break;
      default:
	tfree(actIFFpart->data);
	break;
    }
    IFFpart = IFFpart->next;
    tfree(actIFFpart);
  }

  if (facelist && edgelist && pointlist && brushtexs) {
    int i;

    unsigned short int facecnt;
    struct mbrush *actBrush = (struct mbrush *)tmalloc(sizeof(struct mbrush));
    struct mface *checkFace = 0;
    vec3_t middle;
    unsigned short int p0, p1, p2;
    vector *point;

    for (facecnt = 0; facecnt < BigShort(facelist->tcount); facecnt++) {
      p0 = edgelist->edges[BigShort(facelist->connects[facecnt][0])][0];
      p1 = edgelist->edges[BigShort(facelist->connects[facecnt][0])][1];
      if ((edgelist->edges[BigShort(facelist->connects[facecnt][1])][0] != p0) &&
	  (edgelist->edges[BigShort(facelist->connects[facecnt][1])][0] != p1))
	p2 = edgelist->edges[BigShort(facelist->connects[facecnt][1])][0];
      else
	p2 = edgelist->edges[BigShort(facelist->connects[facecnt][1])][1];

      point = &pointlist->points[BigShort(p0)];
      middle[0] += fract2float(BigLong(point->x));
      middle[1] += fract2float(BigLong(point->y));
      middle[2] += fract2float(BigLong(point->z));
      point = &pointlist->points[BigShort(p1)];
      middle[0] += fract2float(BigLong(point->x));
      middle[1] += fract2float(BigLong(point->y));
      middle[2] += fract2float(BigLong(point->z));
      point = &pointlist->points[BigShort(p2)];
      middle[0] += fract2float(BigLong(point->x));
      middle[1] += fract2float(BigLong(point->y));
      middle[2] += fract2float(BigLong(point->z));
    }
    middle[0] /= (facecnt * 3);
    middle[1] /= (facecnt * 3);
    middle[2] /= (facecnt * 3);

    for (facecnt = 0; facecnt < BigShort(facelist->tcount); facecnt++) {
      int j;
      struct mface *actFace = (struct mface *)tmalloc(sizeof(struct mface));
      vec3_t t1, t2, t3;
      float distance;

      p0 = edgelist->edges[BigShort(facelist->connects[facecnt][0])][0];
      p1 = edgelist->edges[BigShort(facelist->connects[facecnt][0])][1];
      if ((edgelist->edges[BigShort(facelist->connects[facecnt][1])][0] != p0) &&
	  (edgelist->edges[BigShort(facelist->connects[facecnt][1])][0] != p1))
	p2 = edgelist->edges[BigShort(facelist->connects[facecnt][1])][0];
      else
	p2 = edgelist->edges[BigShort(facelist->connects[facecnt][1])][1];

      point = &pointlist->points[BigShort(p0)];
      actFace->p0[0] = fract2float(BigLong(point->x));
      actFace->p0[1] = fract2float(BigLong(point->y));
      actFace->p0[2] = fract2float(BigLong(point->z));
      point = &pointlist->points[BigShort(p1)];
      actFace->p1[0] = fract2float(BigLong(point->x));
      actFace->p1[1] = fract2float(BigLong(point->y));
      actFace->p1[2] = fract2float(BigLong(point->z));
      point = &pointlist->points[BigShort(p2)];
      actFace->p2[0] = fract2float(BigLong(point->x));
      actFace->p2[1] = fract2float(BigLong(point->y));
      actFace->p2[2] = fract2float(BigLong(point->z));

      /*
       * correct to clockwise order
       * planenormal must direct to other side of middle
       * or: the middle must be in the negative side of the room splitted by the plane
       * positive distance between plane and point mean it is on the positive side
       *
       * Erzeugen der Hessischen Normalenform
       * building of hesse-normalform ? to calculate distane between plane and point
       */
      VectorSubtract(actFace->p0, actFace->p1, t1);
      VectorSubtract(actFace->p2, actFace->p1, t2);
      VectorCopy(actFace->p1, t3);
      CrossProduct(t1, t2, actFace->plane.normal);
      VectorNormalize(actFace->plane.normal);
      actFace->plane.dist = DotProduct(t3, actFace->plane.normal);

      if ((distance = DotProduct(middle, actFace->plane.normal) - actFace->plane.dist) > 0) {
	vec3_t temp;

	VectorCopy(actFace->p0, temp);
	VectorCopy(actFace->p2, actFace->p0);
	VectorCopy(temp, actFace->p2);

	VectorNegate(actFace->plane.normal);
	actFace->plane.dist = -actFace->plane.dist;
      }

      /*
       * elimination doubled faces in resulting only valid brushes
       * equal is: if normal and distance to origin are equal
       * the distance to origin is equal then if the angle between normal and normal of ?-normal is equal
       *
       * in theory in quakeMode, there are no eleminations possible
       */
      checkFace = actBrush->faces;
      while (checkFace) {
	float a, b, c;

	/* Abstand Punkt->Ebene / distances point->plane */
	a = fabs(DotProduct(actFace->p0, checkFace->plane.normal) - checkFace->plane.dist);
	b = fabs(DotProduct(actFace->p1, checkFace->plane.normal) - checkFace->plane.dist);
	c = fabs(DotProduct(actFace->p2, checkFace->plane.normal) - checkFace->plane.dist);

	/* if point->plane less than minimum, eleminate them */
	if ((a < ON_EPSILON) && (b < ON_EPSILON) && (c < ON_EPSILON))
	  break;

	checkFace = checkFace->next;
      }

      if (!checkFace) {
	/*
	 * get the textures
	 */
	for (i = 0; i < facegroups; i++) {
	  for (j = 0; j < BigShort(facegroup[i]->count); j++) {
	    if (BigShort(facegroup[i]->facelist[j]) == facecnt)
	      break;
	  }
	  if (j < BigShort(facegroup[i]->count))
	    break;
	}
	for (j = 0; j < brushtexs; j++) {
	  if (!__strncmp(facegroup[i]->name, brushtex[j]->subgrp, 18))
	    break;
	}
	if (j < brushtexs) {
	  float zero[2] =
	  {0, 0};
	  float one[2] =
	  {1, 1};

	  actFace->texinfo = MakeTexinfo(bspMem, brushtex[j]->name, actFace, one, 0, zero);
	}

	actFace->next = actBrush->faces;
	actBrush->faces = actFace;
      }
      else
	tfree(actFace);
    }

    tfree(facelist);
    tfree(edgelist);
    tfree(pointlist);
    for (i = 0; i < facegroups; i++)
      tfree(facegroup[i]);
    tfree(facegroup);
    for (i = 0; i < brushtexs; i++)
      tfree(brushtex[i]);
    tfree(brushtex);

    /*
     * put in brush
     */
    actBrush->next = fillEntity->brushes;
    fillEntity->brushes = actBrush;
    nummapbrushes++;
  }
  else
    eprintf("not enough data to convert brush!\n");
}

void pIFFtopMap(__memBase, register struct IFFchunk *IFFroot, register int iterVal)
{
  struct IFFchunk *IFFpart = IFFroot;

  while (IFFpart) {
    struct IFFchunk *actIFFpart = IFFpart;
    int indentSpace = iterVal;

    while (indentSpace-- > 0) {
      oprintf("  ");
    }
    oprintf("%4s %d bytes\n", (char *)&actIFFpart->type, actIFFpart->size);

    switch (BigLong(actIFFpart->type)) {
	/*
	 * only toplevel-processing (iter 0)
	 */
      case ID_TDDD:
	/*
	 * only toplevel-processing (iter 1)
	 */
      case ID_OBJ:
	pIFFtopMap(bspMem, actIFFpart->iter, iterVal + 1);
	break;
	/*
	 * only toplevel-processing (iter 2)
	 */
      case ID_DESC:
	if (iterVal == 2)
	  /* the dummy-axis to save the hierarchie to disk */
	  pIFFtopMap(bspMem, actIFFpart->iter, iterVal + 1);
	else {
	  if (actIFFpart->iter) {
	    struct IFFchunk *Brushes = actIFFpart->iter;
	    struct entity *thisEntity;

	    if (bspMem->nummapentities == bspMem->max_nummapentities)
	      ExpandClusters(bspMem, MAP_ENTITIES);
	    thisEntity = &bspMem->mapentities[bspMem->nummapentities];
	    bspMem->nummapentities++;

	    while (Brushes) {
	      if (Brushes->type == BigLong(ID_NAME)) {
		thisEntity->classname = (char *)tmalloc(__strlen(Brushes->data) + 1);
		strlwrcpy(thisEntity->classname, Brushes->data);
	      }
	      else if (Brushes->type == BigLong(ID_INT1)) {
		struct int1 *inten = (struct int1 *)Brushes->data;

		thisEntity->light = (unsigned char)rint((fract2float(BigLong(inten->intensity.x)) +
							 fract2float(BigLong(inten->intensity.y)) +
							 fract2float(BigLong(inten->intensity.z))) / 3);
	      }
	      else if (Brushes->type == BigLong(ID_SHP2)) {
		struct shap *shape = (struct shap *)Brushes->data;

		if ((shape->lamp & BigLong(LP2_TYPE)) != BigLong(LP2_NOLAM))
		  thisEntity->style = 1;
	      }
	      else if (Brushes->type == BigLong(ID_POSI)) {
		struct posi *origin = (struct posi *)Brushes->data;

		thisEntity->origin[0] = fract2float(BigLong(origin->position.x));
		thisEntity->origin[1] = fract2float(BigLong(origin->position.y));
		thisEntity->origin[2] = fract2float(BigLong(origin->position.z));
	      }
	      else if (Brushes->type == BigLong(ID_TXT4)) {
		struct texture4 *brushtex = (struct texture4 *)Brushes->data;
		struct epair *lastString;
		struct epair *actString = (struct epair *)tmalloc(sizeof(struct epair));

		actString->next = 0;
		actString->key = (char *)tmalloc(brushtex->length + 1);
		__strncpy(actString->key, brushtex->name, brushtex->length);
		actString->key[brushtex->length] = '\0';
		actString->value = (char *)tmalloc(__strlen(brushtex->label) + 1);
		__strcpy(actString->value, brushtex->label);

		if ((lastString = thisEntity->epairs)) {
		  while (lastString->next)
		    lastString = lastString->next;
		  lastString->next = actString;
		}
		else
		  thisEntity->epairs = actString;
	      }
	      else if (Brushes->type == BigLong(ID_DESC)) {
		/*
		 * after this we get no datas any more
		 */
		if (Brushes->iter)
		  pIFFtopBrushes(bspMem, Brushes->iter, iterVal + 1, thisEntity);
	      }
	      Brushes = Brushes->next;
	    }

	    /*
	     * for all 
	     */
	    if (VectorZero(thisEntity->origin))
	      GetVectorForKey(thisEntity, "origin", thisEntity->origin);
	    if (!thisEntity->classname)
	      thisEntity->classname = ValueForKey(thisEntity, "classname");
	    thisEntity->target = ValueForKey(thisEntity, "target");
	    thisEntity->targetname = ValueForKey(thisEntity, "targetname");

	    /*
	     * special for qbsp+light+vis in one part 
	     */
	    if (bspMem->mapOptions & MAP_LOADLIGHTS) {
	      if (!(thisEntity->light = FloatForKeyN(thisEntity, "light")))
		if (!(thisEntity->light = FloatForKey(thisEntity, "_light")))
		  if (!thisEntity->light)
		    thisEntity->light = MAX_MAPLIGHTLEVEL;
	      if (!(thisEntity->style = FloatForKey(thisEntity, "style")))
		if (!(thisEntity->style = FloatForKey(thisEntity, "_style")))
		  if (!thisEntity->style)
		    thisEntity->style = 0;
	      if (!thisEntity->angle)
		thisEntity->angle = FloatForKey(thisEntity, "angle");

	      if (__strcmp(thisEntity->classname, "light")) {
		if (!thisEntity->light)
		  thisEntity->light = DEFAULTLIGHTLEVEL;

		if (thisEntity->targetname[0] && !thisEntity->style) {
		  char s[256];

		  thisEntity->style = LightStyleForTargetname(thisEntity->targetname, TRUE);
		  sprintf(s, "%i", thisEntity->style);
		  SetKeyValue(thisEntity, "style", s);
		}
	      }
	    }
	  }
	}
	break;
      default:
	break;
    }
    IFFpart = IFFpart->next;
    tfree(actIFFpart->data);
    tfree(actIFFpart);
  }
}

/*
 * the rules:
 * 
 * -the hierarchy:
 * 
 * DESC "world/axis/root" (axis)
 * -> acts as global group-manager to save all subhierarchies
 * DESC "worldspawn" (axis)
 * -> all subdesc's desribes the brushes (objects grouped to axis "worldspawn")
 * -> all subdesc's texture info must contain the name of the texture to use and
 * the alignment/positioning must be valid
 * DESC "info_player_start" (axis)
 * -> the axis position describes the players origin
 * DESC "standard quake"
 * -> will be searched for information
 * (eg. DESC "light" (axis with light) is the standard entity "light", parameters
 * are parsed out of the axis-informations)
 * -> axis that are groupt to a non-worldspawn-entity and that have no brushes
 * will beinterpreted as movement-points (?)
 * -> the texture-list of an entity will be parsed for key-values (standard-quake
 * like "target")
 * 
 * restriction: 
 * 
 * dont make objects, that are not convex
 * calculation of clockwise point-order goes via middlepoint of object
 * 
 */

/*
 * ================
 * SaveTDDDFile
 * ================
 */
bool SaveTDDDFile(__memBase, HANDLE outFile)
{
  struct entity *ent;
  struct epair *ep;
  struct mbrush *b;
  struct mface *f;
  int i;
  struct dmiptexlump_t *head_miptex = (struct dmiptexlump_t *)bspMem->shared.quake1.dtexdata;
  int lastTDDD = 0;
  int lastObj = 0;
  int lastWorld = 0;
  int lastRoot = 0;
  int lastDesc = 0;

  /*
   * hierarchy:
   * 
   * TDDD
   * OBJ
   * DESC         worldaxis
   * | DESC        worldspawn
   * | | DESC   model1
   * | | +-TOBJ
   * | | DESC   model2
   * | | +-TOBJ
   * | | ...
   * | +-TOBJ
   * | DESC        light
   * | ...
   * +-TOBJ
   */

  SetForm(outFile, &lastTDDD, ID_TDDD);
  SetRoot(outFile, &lastObj, ID_OBJ);

  SetRoot(outFile, &lastWorld, ID_DESC);
  SetClassName(outFile, "worldaxis");
  SetOtherDefaults(outFile, 0);
  VerRoot(outFile, &lastWorld, ID_DESC);

  /* set worldspawn as root of all the other models */
  SetRoot(outFile, &lastRoot, ID_DESC);
  for (i = 0, ent = bspMem->mapentities; i < bspMem->nummapentities; i++, ent++)
    if (!__strcmp(ent->classname, "worldspawn"))
      break;

  if (i == bspMem->nummapentities) {
    eprintf("SaveTDDDFile: worldspawn not found!\n");
    return FALSE;
  }

  SetClassName(outFile, ent->classname);
  for (ep = ent->epairs; ep; ep = ep->next)
    if (__strcmp(ep->key, "model"))
      SetEntity(outFile, ep->key, ep->value);
  for (b = ent->brushes; b; b = b->next) {
    for (f = b->faces; f; f = f->next) {
      struct texinfo *texinfo = &bspMem->shared.quake1.texinfo[f->texinfo];
      char *miptexname;

      if (head_miptex)						/* for an allready compiled map */
	miptexname = ((struct mipmap *)(bspMem->shared.quake1.dtexdata + head_miptex->dataofs[texinfo->miptex]))->name;
      else if (bspMem->maptexstrings)				/* for an uncompiled map */
	miptexname = bspMem->maptexstrings[texinfo->miptex];
      else							/* for unknown states */
	miptexname = "unknown\0";

      SaveFace(f->p0, f->p1, f->p2, miptexname);
    }
  }
  SetPoints(outFile);
  SetEdges(outFile);
  SetFaces(outFile);
  SetBrushes(outFile);
  SetOtherDefaults(outFile, ent);
  VerRoot(outFile, &lastRoot, ID_DESC);

  for (i = 0, ent = bspMem->mapentities; i < bspMem->nummapentities; i++, ent++) {
    bool model = FALSE;

    for (ep = ent->epairs; ep; ep = ep->next) {
      if (!__strcmp(ep->key, "model")) {
	model = TRUE;
	break;
      }
    }

    if (model && __strcmp(ent->classname, "worldspawn")) {
      SetRoot(outFile, &lastDesc, ID_DESC);
      SetClassName(outFile, ent->classname);
      for (ep = ent->epairs; ep; ep = ep->next)
	if (__strcmp(ep->key, "model"))
	  SetEntity(outFile, ep->key, ep->value);
      for (b = ent->brushes; b; b = b->next) {
	for (f = b->faces; f; f = f->next) {
	  struct texinfo *texinfo = &bspMem->shared.quake1.texinfo[f->texinfo];
	  char *miptexname;

	  if (head_miptex)					/* for an allready compiled map */
	    miptexname = ((struct mipmap *)(bspMem->shared.quake1.dtexdata + head_miptex->dataofs[texinfo->miptex]))->name;
	  else if (bspMem->maptexstrings)			/* for an uncompiled map */
	    miptexname = bspMem->maptexstrings[texinfo->miptex];
	  else							/* for unknown states */
	    miptexname = "unknown\0";

	  SaveFace(f->p0, f->p1, f->p2, miptexname);
	}
      }
      SetPoints(outFile);
      SetEdges(outFile);
      SetFaces(outFile);
      SetBrushes(outFile);
      SetOtherDefaults(outFile, ent);
      VerRoot(outFile, &lastDesc, ID_DESC);
      SetEndM(outFile, ID_TOBJ);
    }
  }
  SetEndM(outFile, ID_TOBJ);
  VerRoot(outFile, &lastObj, ID_OBJ);
  VerRoot(outFile, &lastTDDD, ID_FORM);

  mprintf("----- SaveTDDDFile -------\n");
  mprintf("%5i points\n", TDDDnumpoints);
  mprintf("%5i edges\n", TDDDnumedges);
  mprintf("%5i faces\n", TDDDnumfaces);
  mprintf("%5i groups\n", TDDDnumgroups);
  mprintf("%5i objects\n", TDDDnumobjects);

  return TRUE;
}

/*
 * ================
 * LoadTDDDFile
 * ================
 */
bool LoadTDDDFile(__memBase, unsigned char *tdddBuf)
{
  struct IFFheader IFFfile =
  {0, 0, 0};
  struct IFFchunk *IFFroot = (struct IFFchunk *)tmalloc(sizeof(struct IFFchunk));

  __memcpy(&IFFfile, tdddBuf, 12);
  tdddBuf += 12;
  IFFroot->type = IFFfile.type;
  IFFroot->size = IFFfile.size - 4;
  IFFroot->next = 0;
  IFFroot->iter = 0;
  IFFroot->data = 0;
  memIFFtopIFF(tdddBuf, IFFroot);
  pIFFtopMap(bspMem, IFFroot, 0);
  MatchTargets(bspMem);

  mprintf("----- LoadTDDDFile -------\n");
  mprintf("%5i brushes\n", nummapbrushes);
  mprintf("%5i entities\n", bspMem->nummapentities);
  mprintf("%5i miptex\n", bspMem->nummaptexstrings);
  mprintf("%5i texinfo\n", bspMem->shared.quake1.numtexinfo);

  return TRUE;
}
