[comp.sources.amiga] v02i002: surf - produce bezier surfaces of revolution, Part01/03

page@swan.ulowell.edu (Bob Page) (10/20/88)

Submitted-by: edavies@uvvm.bitnet (Eric Davies)
Posting-number: Volume 2, Issue 2
Archive-name: graphics/surf.1of3

Surf is a program for producing bezier surfaces of revolution.  It
produces awesome pictures of wine glasses and doorknobs, and other
objects one could turn on a lathe.  Surf includes the capacity to map
iff image files onto any surface that it can draw.

# This is a shell archive.  Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
#	./menudef.c
#	./menudef.h
#	./menuexp.h
#	./mergergb.c
#	./mergergb.lnk
#	./mouse.c
#	./mytypes.h
#	./packer.c
#	./poly.c
#	./poly.h
#	./readilbm.c
#	./readilbm.h
#	./revolve.c
#	./revolve.h
#	./scrndef.c
#	./scrndef.h
#	./scrnio.c
#	./scrnio.h
#	./scrnio.ih
#	./scrnops.c
#	./surf.lnk
#	./writeilbm.c
#
if `test ! -s ./menudef.c`
then
echo "writing ./menudef.c"
cat > ./menudef.c << '\Rogue\Monster\'
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#ifdef MANX
#include <functions.h>
#endif
#include "scrnio.h"
#include "menudef.h"

#include "scrndef.h"
#include "poly.h"
#include "readilbm.h"

#define Selected(ax) ((ax).Flags & CHECKED)

#include "menu_color.c"
#include "menu_scrn.c"
#include "menu_image.c"
#include "menu_files.c"

struct Menu menu[] ={
    { &menu[1], /* next menu */
    8, 0, /* x, y */
    6*8, 10, /* w,h */
    MENUENABLED,
    (BYTE *)"File",
    fileitems,
    0,0,0,0
    },
    { &menu[2], /* next menu */
    7*8, 0, /* x, y */
    6*8, 10, /* w,h */
    MENUENABLED,
    (BYTE *)"Color",
    coloritems,
    0,0,0,0
    },
    { &menu[3], /* next menu */
    14*8,0, /* x, y */
    7*8, 10, /* w,h */
    MENUENABLED,
    (BYTE *)"Screen",
    scrnitems,
    0,0,0,0
    },
    { NULL, /* next menu */
    22*8,0, /* x, y */
    6*8, 10, /* w,h */
    MENUENABLED,
    (BYTE *)"Image",
    imageitems,
    0,0,0,0
    }
};



void MenuHandler(code)
USHORT code;
{
    if( code == 0xffff ) { /* invalid menu pick */
        return;
    }

    switch( MENUNUM(code)) {
        case 0: /* write ilbm */
            MenuDoFile(ITEMNUM(code));
            break;
        case 1: /* set screen color */
            MenuSetColMap();
            break;
        case 2: /* set screen type */
            MenuSetScrn();
            break;
        case 3: /* set image stuff */
            MenuSetImage();
            break;
        default:
            break;
    }
}
\Rogue\Monster\
else
  echo "will not over write ./menudef.c"
fi
if `test ! -s ./menudef.h`
then
echo "writing ./menudef.h"
cat > ./menudef.h << '\Rogue\Monster\'
extern struct Menu menu[];
extern void MenuHandler(/* USHORT */);

\Rogue\Monster\
else
  echo "will not over write ./menudef.h"
fi
if `test ! -s ./menuexp.h`
then
echo "writing ./menuexp.h"
cat > ./menuexp.h << '\Rogue\Monster\'
#ifndef MENUEXP_H_FILE
#define MENUEXP_H_FILE

#ifndef INTUITION_INTUITION_H
#include <intuition/intuition.h>
#endif  INTUITION_INTUITION_H


extern USHORT *AbortDrawPtr;
extern USHORT *DebugOnPtr;

#define AbortDraw (*AbortDrawPtr & CHECKED)
#define ClrAbort() { *AbortDrawPtr &= ~CHECKED; }
#define DebugOn (*DebugOnPtr & CHECKED)

#endif MENUEXP_H_FILE
\Rogue\Monster\
else
  echo "will not over write ./menuexp.h"
fi
if `test ! -s ./mergergb.c`
then
echo "writing ./mergergb.c"
cat > ./mergergb.c << '\Rogue\Monster\'
/*
 * This program, when called with arg "smith", tries to read smith.r, smith.g
 * and smith.b, and write a combined raw data file acceptable to RAY2, called
 * smith.tmp
 */
#include <stdio.h>
#include "mytypes.h"

#define RED 0
#define GRN 1
#define BLU 2
#define NumPrims 3 /* number of primary colors */

#define MakeID(a,b,c,d) ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )

#define ID_FORM MakeID('F','O','R','M')
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_CAMG MakeID('C','A','M','G')
#define ID_BODY MakeID('B','O','D','Y')

#define ROUNDUP(x) ( ((x)+1) & (~1L) )

struct CHUNK {
    unsigned long Id, Size;
} Chunk;

struct BMHD {
    short w,h,x,y;
    char npl,mask,compress,pad1;
    short transparentColor;
    char xAspect,yAspect;
    short pageWidth,pageHeight;
};

/*
 * compare width and height of two bit maps
 * return false if equal
 */
bool CmpBMHD(a, b)
    struct BMHD *a, *b;
{
    return( a->w != b->w ||
            a->h != b->h ||
          a->npl != b->npl
          );
}


struct BMHD bmhd[NumPrims];
static FILE *ftrip[NumPrims], *fout;
char *nametrip[NumPrims];
char *oname;
bool FileError;
short depth;

unsigned char colmap[NumPrims][32][3];


/*
 * read len bytes from selected file
 */
void FRead(buffer, len, filenum)
    char *buffer;
    int len, filenum;
{
    if( fread(buffer, len, 1, ftrip[filenum]) == 0 ) {
        fprintf(stderr, "read error in %s\n", nametrip[filenum] );
        exit(-1);
    }
}



#define ABORT(str) { fprintf(stderr,str); exit(-1); }

#define ReadChunk()  FRead(&Chunk, sizeof(Chunk), filenum)

void ReadIffHeaders(filenum)
    int filenum;
{
    long    ILBMid;
    bool cmapFlag = false,
         bmhdFlag = false;
    short i;


    ReadChunk ();
    if (Chunk.Id != ID_FORM)
        ABORT ("Not an IFF File");

    FRead (&ILBMid, 4, filenum);
    if (ILBMid != ID_ILBM)
        ABORT ("Not an ILBM File");

    while (1) {
        long camgdata;

        ReadChunk ();

        if (Chunk.Id == ID_BODY) {
            if (!cmapFlag) {
                printf("no cmap before body in %s\n", nametrip[filenum]);
                FileError = true;
            }
            if (!bmhdFlag) {
                printf("no bmhd before the body in %s\n", nametrip[filenum]);
                FileError = true;
            }
            return;
        }

        if( feof( ftrip[filenum] ) ) {
            printf("reached end of file without seeing body in %s\n", nametrip[filenum]);
            FileError = true;
            return;
        }

        switch (Chunk.Id) {
            case ID_CMAP:
                FRead (colmap[filenum], Chunk.Size, filenum);
                for (i = 0; i < 32; i++) {
                    colmap[filenum][i][filenum] >>= 4;
                }
                cmapFlag = true;
                break;
            case ID_BMHD:
                FRead(&bmhd[filenum], Chunk.Size, filenum);
                bmhdFlag = true;
                break;
            case ID_CAMG:
                FRead( &camgdata, sizeof(camgdata),filenum );
                break;
            default:            /* unknown identifier */
                fseek( ftrip[filenum], Chunk.Size, 1);
                break;
        }
    }
}


/*
 * leftmost pixel of byte is in most significant bit of byte
 */
static char GetIlbmVal( rastlist, h)
    char *rastlist[6];
    int h;
{
    int i;
    char value = 0;
    short mask, bytep;

    mask = 0x80 >> ( h & 7);
    bytep = h >> 3;

    for( i = depth-1; i >= 0; i-- ) {
        value <<= 1;
        value |= (*(rastlist[i]+bytep) & mask) ? 1: 0;
    }
    return( value );
}

/*
 * decompress a runlength row of bytes
 */
static void ReadDecomLine(linebytes, rp, filenum)
    int linebytes, filenum;
    char *rp;
{
    int runlen;
    char pixel;

    while (linebytes) {
        runlen = getc (ftrip[filenum]);
        if (runlen > 127)
            runlen -= 256;
        if (runlen >= 0) {
            runlen++;
            FRead (rp, runlen, filenum);
            rp += runlen;
            linebytes -= runlen;
        }
        else {
            runlen = -runlen + 1;
            linebytes -= runlen;
            pixel = getc (ftrip[filenum]);
            do
                *(rp++) = pixel;
            while (--runlen);
        }
    }
}
/*
 * read raster line from one of the color files
 */
void LoadRaster(raster, ByteWidth, filenum)
    char *raster[6];
    int ByteWidth, filenum;
{
    if(bmhd[filenum].compress = 0) {
        FRead (raster, ByteWidth * depth, filenum);
    }
    else {
        short i;
        for( i = 0; i < depth; i++) {
            ReadDecomLine( ByteWidth, raster[i], filenum);
        }
    }
}

/*
 * write intensity values for a given gun
 * note: I've made dirty assumption about byte ordering in a short word
 * if you run this on an ibm (ha ha ha) you'll have to change it
 */
#define ChipSize 4
void SaveRaster( raster, width, prim)
    char *raster[];
    int width, prim;
{
    short i, j;
    unsigned short val;

    for( i = 0; i < width; i += ChipSize ) {
        val = colmap[prim][GetIlbmVal(raster, i+3)][prim] << ChipSize;
        val |= colmap[prim][GetIlbmVal(raster, i+2)][prim];
        fputc( val&0xff, fout);
        val = colmap[prim][GetIlbmVal(raster, i+1)][prim] << ChipSize;
        val |= colmap[prim][GetIlbmVal(raster, i+0)][prim];
        fputc( val&0xff, fout);
    }
}

/*
 * translate body of file to format RAY2 likes
 */
void TranslateRay2Body()
{
    int BytesWidth;
    int row, height, width, plane, prim;
    char *raster[6];

    depth = bmhd[0].npl;
    width = bmhd[0].w;
    height = bmhd[0].h;
    BytesWidth = (width + 7)/8 ;

    for( plane = 0; plane < bmhd[0].npl; plane++ ) {
        raster[plane] = (char *)malloc(BytesWidth);
    }

    for( row = 0; !FileError && row < height; row++ ) {
        fputc((unsigned char)(row>>8),fout);      /* write line number */
        fputc((unsigned char)(row&0xff),fout);

        for( prim = RED; prim <= BLU; prim++ ) {
            LoadRaster( raster, BytesWidth, prim);
            SaveRaster( raster, width, prim);
        }
    }
}



main(argc, argv)
    int argc;
    char *argv[];
{
    char buff[4][80];
    char *extensions = "rgb";
    int i;

    if( argc == 1 || *argv[1] == '?' ) {
        fprintf(stderr,"usage:  merge filename\n");
        exit(-1);
    }
    FileError = false;

    for( i = 0; i < NumPrims; i++ ) {
        sprintf(buff[i], "%s.%c", argv[1], extensions[i]);
        nametrip[i] = buff[i];
        ftrip[i] = fopen(nametrip[i],"r");
        if(!ftrip[i]) {
            fprintf(stderr,"unable to read %s\n", nametrip[i]);
            FileError = true;
        }
    }

    sprintf(buff[3], "%s.tmp", argv[1]);
    oname = (argc>2)? argv[2]: buff[3];
    fout = fopen( oname,"w");
    if(!fout) {
        fprintf(stderr,"unable to write to %s\n", oname);
        FileError = true;
    }

    if( FileError ) goto ErrorExit;

    for( i = 0; i < NumPrims; i++ ) {
        printf("reading header %d\n", i);
        ReadIffHeaders(i);
    }

    for( i = 1; i < NumPrims; i++ ) {
        if( CmpBMHD(&bmhd[0], &bmhd[i])) {
            fprintf(stderr,"image sizes differ(r != %d)\n", i);
            FileError = true;
        }
    }

    if( FileError ) goto ErrorExit;
    printf("about to translate body\n");
    TranslateRay2Body();

    ErrorExit:
        for( i = 0; i < NumPrims; i++) {
            if(ftrip[i]) fclose( ftrip[i] );
        }
        if(fout) fclose(fout);
}


\Rogue\Monster\
else
  echo "will not over write ./mergergb.c"
fi
if `test ! -s ./mergergb.lnk`
then
echo "writing ./mergergb.lnk"
cat > ./mergergb.lnk << '\Rogue\Monster\'
ROOT 	lib:c.o
	MergeRGB.o

LIBRARY	LIB:lc.lib
	Lib:lcm.lib
	LIB:amiga.lib

TO MergeRGB

verbose
nodebug
\Rogue\Monster\
else
  echo "will not over write ./mergergb.lnk"
fi
if `test ! -s ./mouse.c`
then
echo "writing ./mouse.c"
cat > ./mouse.c << '\Rogue\Monster\'
#include "scrnio.ih"
#ifdef MANX
#include <functions.h>
#endif
#include "scrnio.h"
#include "mytypes.h"

#include "bezpt.h"
#include "control.h"


static bool buttondown;  /* is left button down */
static short mousex, mousey;
static bool isleftbut;  /* left button or pseudo middle button */

void HandleTicks(mesg)
struct IntuiMessage *mesg;
{
    int x, y;

    x = CntrX(mesg->MouseX);
    y = CntrY(mesg->MouseY);

    if(!buttondown || (mousex == x && mousey == y )){
        return;
    }

    mousex = x;
    mousey = y;

    switch( CurMode ) {

    case DRAWPOLY:
         EditBezPt( mousex, mousey);
         break;

    case FITBEZIER:
        /*
         * need to select between the two
         */
        if( isleftbut ) {
            EditControl0(mousex, mousey);
        }
        else {
            EditControl1(mousex, mousey);
        }
        break;

    default:
        break;
    }
}



void HandleMButtons(mesg)
struct IntuiMessage *mesg;
{

    long leftdist, rightdist;
    long tx, ty;

    mousex = CntrX(mesg->MouseX);
    mousey = CntrY(mesg->MouseY);

    switch( mesg->Code) {
    case SELECTDOWN:
        buttondown = true;  /* down */

        switch( CurMode ) {
        case DRAWPOLY:
            InitBezPt( mousex, mousey);
            if( GetNumSegs() == 0 ) {
                InitBezPt( mousex, mousey );
            }
            break;

        case FITBEZIER:
            tx = mousex - Cntrl1X(GetCurSeg());
            ty = mousey - Cntrl1Y(GetCurSeg());
            leftdist = tx *tx + ty * ty;

            tx = mousex - Cntrl2X(GetCurSeg());
            ty = mousey - Cntrl2Y(GetCurSeg());
            rightdist = tx *tx + ty * ty;

            if( isleftbut = (leftdist <= rightdist) ) {
                EditControl0( mousex, mousey );
            }
            else {
                EditControl1( mousex, mousey );
            }
            break;

        default:
            break;
        }
        break;


    case SELECTUP:
        buttondown = false; /* up */
        break;

    case MENUUP:
        if( CurMode == FITBEZIER ) {
            DrawControl0();
            DrawControl1();
            NextSeg();
            DrawControl0();
            DrawControl1();
        }
        break;

    default:
        break;
    }
}
\Rogue\Monster\
else
  echo "will not over write ./mouse.c"
fi
if `test ! -s ./mytypes.h`
then
echo "writing ./mytypes.h"
cat > ./mytypes.h << '\Rogue\Monster\'
#ifndef MYTYPES_H_FILE
#define MYTYPES_H_FILE
#define true 1
#define false 0

typedef char bool;
extern char *calloc(), *malloc();

#define null 0L

#ifndef MANX
#include "exec/types.h"
#include "libraries/mathffp.h"
#define ceil	SPCeil
#define floor	SPFloor
#define fabs	SPAbs
#endif


#endif MYTYPES_H_FILE
\Rogue\Monster\
else
  echo "will not over write ./mytypes.h"
fi
if `test ! -s ./packer.c`
then
echo "writing ./packer.c"
cat > ./packer.c << '\Rogue\Monster\'
/*
 * this file is a hacked up version of the original, who blurb is below
 * changes made: internal buffer no longer static, but is now on stack
 */
/*----------------------------------------------------------------------*
 * packer.c Convert data to "cmpByteRun1" run compression.     11/15/85
 *
 * By Jerry Morrison and Steve Shaw, Electronic Arts.
 * This software is in the public domain.
 *
 *      control bytes:
 *       [0..127]   : followed by n+1 bytes of data.
 *       [-1..-127] : followed by byte to be repeated (-n)+1 times.
 *       -128       : NOOP.
 *
 * This version for the Commodore-Amiga computer.
 *----------------------------------------------------------------------*/
#include <exec/types.h>

#define DUMP    0
#define RUN     1

#define MinRun 3
#define MaxRun 128
#define MaxDat 128

static short putSize;
#define GetByte()       (*source++)
#define PutByte(c)      { *dest++ = c;   ++putSize; }

static BYTE *buf;  /* [TBD] should be 128?  on stack?*/

static BYTE *PutDump(dest, nn)
    BYTE *dest;
    int nn;
{
        int i;

    PutByte(nn-1);
    for(i = 0;  i < nn;  i++)   PutByte(buf[i]);
    return(dest);
}

static BYTE *PutRun(dest, nn, cc)
    BYTE *dest;
    int nn, cc;
{
    PutByte(-(nn-1));
    PutByte(cc);
    return(dest);
}

#define OutDump(nn)   dest = PutDump(dest, nn)
#define OutRun(nn,cc) dest = PutRun(dest, nn, cc)

/*----------- PackRow --------------------------------------------------*/
/* Given POINTERS TO POINTERS, packs one row, updating the source and
   destination pointers.  RETURNs count of packed bytes.*/
int PackRow(source, dest, rowSize)
    BYTE *source, *dest;
    int rowSize;
{
    char innerbuf[256];
    char c,lastc = '\0';
    BOOL mode = DUMP;
    short nbuf = 0;             /* number of chars in buffer */
    short rstart = 0;           /* buffer index current run starts */

    /*
     * cute way to make local buffer known to external procedures
     * since static variables increase executable size in Manx
     */
    buf = innerbuf; /* way to put make local buffer known to external procs */
    putSize = 0;
    buf[0] = lastc = c = GetByte();  /* so have valid lastc */
    nbuf = 1;   rowSize--;      /* since one byte eaten.*/


    for (;  rowSize;  --rowSize) {
        buf[nbuf++] = c = GetByte();
        switch (mode) {
                case DUMP:
                        /* If the buffer is full, write the length byte,
                           then the data */
                        if (nbuf>MaxDat) {
                                OutDump(nbuf-1);
                                buf[0] = c;
                                nbuf = 1;   rstart = 0;
                                break;
                                }

                        if (c == lastc) {
                            if (nbuf-rstart >= MinRun) {
                                if (rstart > 0) OutDump(rstart);
                                mode = RUN;
                                }
                            else if (rstart == 0)
                                mode = RUN;     /* no dump in progress,
                                so can't lose by making these 2 a run.*/
                            }
                        else  rstart = nbuf-1;          /* first of run */
                        break;

                case RUN: if ( (c != lastc)|| ( nbuf-rstart > MaxRun)) {
                        /* output run */
                        OutRun(nbuf-1-rstart,lastc);
                        buf[0] = c;
                        nbuf = 1; rstart = 0;
                        mode = DUMP;
                        }
                        break;
                }

        lastc = c;
        }

    switch (mode) {
        case DUMP: OutDump(nbuf); break;
        case RUN: OutRun(nbuf-rstart,lastc); break;
        }
    return(putSize);
}

\Rogue\Monster\
else
  echo "will not over write ./packer.c"
fi
if `test ! -s ./poly.c`
then
echo "writing ./poly.c"
cat > ./poly.c << '\Rogue\Monster\'
#include <math.h>
#include "mytypes.h"
#include "scrnio.h"
#include "bezpt.h"
#include "revolve.h"
#include "control.h"
#include "poly.h"
#include "readilbm.h"
#include "menuexp.h"

bool  SpecOn = false; /* specular lighting enable - default off */

float Ambience = DefAmbience;
float PtIntensity = (float)DefIntensity;
float Kd = DefKd,
      Ks = DefKs;

Pt3 LightSrc = {
        DefLightSrcX,
        DefLightSrcY,
        DefLightSrcZ
        };

static
Rhomboid *polylist = null,
         *nextpoly;

MapRhomboid *mpolylist = null,
        *mnextpoly;

static
int polyspace = 0;


static bool shadeflag;

long CalcMaxPolyNum() {
    return( GetNumSegs() * BezMesh * RevMesh );
}

static
bool PrepPoly()
{
        if( polylist ) free( (char *)polylist );
        polylist = (Rhomboid *)malloc( CalcMaxPolyNum() * sizeof(Rhomboid));
        if( !polylist ) {
            OutErr("PrepPoly: not enough memory");
            return( true );
        }
        nextpoly = polylist;
        return(false); /* success = 0 */
}


static bool
PrepMPoly()
{
        if( mpolylist ) free( (char *)mpolylist );
        mpolylist = (MapRhomboid *)
                    malloc( CalcMaxPolyNum() * sizeof(MapRhomboid));
        if( !mpolylist ) {
            OutErr("PrepPoly: not enough memory");
            return( true );
        }
        mnextpoly = mpolylist;
        return(false); /* success = 0 */
}


/*
 * Multiply a vector by scalar quantity
 */
void ScaleVec( fact, src, dst )
        float fact;
        Pt3 *src, *dst;
{
        dst->x = src->x * fact;
        dst->y = src->y * fact;
        dst->z = src->z * fact;
}
/*
 * convert a vector to a unitized vector
 * if possible
 */
void Unitize( vec )
        Pt3 *vec;
{
        float len;

        len = vec->x*vec->x + vec->y*vec->y + vec->z*vec->z;
        len = sqrt( len );
        if( len != 0.0 ) {
                vec->x /= len;
                vec->y /= len;
                vec->z /= len;
        }
}

/*
 * calculate a vector from two points
 */
void CalcVector( src1, src2, dest )
Pt3 *src1, *src2, *dest ;
{
        dest->x = src1->x - src2->x;
        dest->y = src1->y - src2->y;
        dest->z = src1->z - src2->z;
}




/*
 * calculate a normal from a list of polygons. This routine does the
 * logical trick of trying to exclude each point in turn if the
 * normal can not be calculated, or something of the sort.
 * a value of true is returned if a normal with a nonzero z component
 * could not be calculated
 */

bool CalcNormal( vxlist, normal)
        PtGen *vxlist[];
        register Pt3 *normal;
{
        int i, k, m;
        Pt3 *j[3];
        Pt3 v1, v2;

        for( i = 0; i < RhomVxNum; i++ ) {
                for( k = 0, m = 3; m--; k++ ) {
                        if( k == i ) {
                                k++;
                        }
                        j[m] = &vxlist[k]->d3;
                }
                CalcVector( j[1], j[0], &v1 );
                CalcVector( j[2], j[1], &v2 );

                normal->z = v1.x*v2.y - v1.y*v2.x;
                if( normal->z == 0 ) {
                        continue;
                }
                normal->x = v1.y*v2.z - v1.z*v2.y;
                normal->y = v1.z*v2.x - v1.x*v2.z;
                if( normal->z < 0 ) {
                        normal->x = -normal->x;
                        normal->y = -normal->y;
                        normal->z = -normal->z;
                }
                Unitize(normal);
                return( false );
        }
        return(true);
}


/*
 * Euclidean dot product.
 * I wonder what the minkowski dot product would look like
 */
float DotProd( v1, v2 )
        Pt3 *v1, *v2;
{
        return( v1->x*v2->x + v1->y*v2->y + v1->z*v2->z );
}





/*
 * define a polygon as a set of four points
 * returns true if polygon created
 */
static bool CreatePoly(curpoly, p0, p1, p2, p3)
    register Rhomboid *curpoly;
    PtGen *p0, *p1, *p2, *p3;
{

    Pt3 normal;
    PtGen *list[RhomVxNum];

    list[0] = p0; list[1] = p1; list[2] = p2; list[3] = p3;
    /*
     * compute stuff needed only if producing shaded image
     */
    if( shadeflag ) {
            Pt3 lvec;
            Pt3 center;
            float ptintens;
            float ldotn; /* light vector dot normal */
            /*
            * if cant compute normal, then junk polygon
            */
            if( CalcNormal( list, &normal )) {
                return(false);
            }

            curpoly->intensity = Ambience;
            center.x = ( list[0]->d3.x
                    + list[1]->d3.x
                    + list[2]->d3.x
                    + list[3]->d3.x)/4.0;
            center.y = ( list[0]->d3.y
                    + list[1]->d3.y
                    + list[2]->d3.y
                    + list[3]->d3.y)/4.0;
            center.z = ( list[0]->d3.z
                    + list[1]->d3.z
                    + list[2]->d3.z
                    + list[3]->d3.z)/4.0;

            curpoly->depth = center.z;

            CalcVector( &center, &LightSrc, &lvec );
            Unitize( &lvec );
            ldotn = DotProd( &lvec, &normal );
            if( ldotn < 0 ) {
                ptintens = PtIntensity * Kd * -ldotn;
                curpoly->intensity += ptintens;
            }
            /*
             * calculate specular component
             */
            if( SpecOn && ldotn < 0 ) {
                float Kspec, Is;
                Pt3 rvec; /* lvec reflected through poly */

                ScaleVec( 2*ldotn, &normal, &rvec );
                CalcVector(&lvec, &rvec, &rvec );
                Unitize( &center );
                Kspec = DotProd( &rvec, &center);

                if( Kspec <= 0.0 ) {
                    Is = Ks * Kspec * Kspec* PtIntensity;
                    curpoly->intensity += Is;
                }
            }

            if( curpoly->intensity > 1.0 ) {
                curpoly->intensity = 1.0;
            }
    }
    else {
        /*
         * calculate depth of polygon
         * for now, try an average of the vertex depths
         */
        curpoly->depth =( list[0]->d3.z
                + list[1]->d3.z
                + list[2]->d3.z
                + list[3]->d3.z)/4.0;
    }
    /*
     * store index to screen coordinates
     */
    curpoly->pt[0] = p0->d2;
    curpoly->pt[1] = p1->d2;
    curpoly->pt[2] = p2->d2;
    curpoly->pt[3] = p3->d2;

    return(true);
}
/*
 * passable procedure for creating polygons without mapping
 */
static
void AcceptPoly(p0, p1, p2, p3)
    PtGen *p0, *p1, *p2, *p3;
{
    if( CreatePoly(nextpoly, p0, p1, p2,p3)) {
        nextpoly++;
    }
}

static
void AcceptMPoly( p0, p1, p2, p3)
    PtGen *p0, *p1, *p2, *p3;
{
    if( CreatePoly(&mnextpoly->rhom, p0, p1, p2,p3)) {
        mnextpoly->bezindex = RevImageB;
        mnextpoly->revindex = RevImageR;
        mnextpoly++;
    }
}





/*
 * compare the depth of two polygons for SortPoly
 */
static int CmpDepth( a, b )
        Rhomboid *a, *b;
{
        if( a->depth < b->depth ) return(-1);
        else if( a->depth > b->depth ) return(1);
        else return(0);
}

static int CmpMDepth( a, b )
        MapRhomboid *a, *b;
{
        if( a->rhom.depth < b->rhom.depth ) return(-1);
        else if( a->rhom.depth > b->rhom.depth ) return(1);
        else return(0);
}





void RevNoShade() {
        Rhomboid *i;
        if( GetNumSegs() < 1 ) {
                return;
        }
        CurMode = NOTACTIVE;

        ClrAbort();
        shadeflag = false;
        if( PrepPoly() ) return;
        if( Revolve(AcceptPoly) ) return;

        qsort( (char *)polylist, nextpoly - polylist,
                sizeof(Rhomboid), CmpDepth);

        ClrWindow(false);

        for( i = polylist; i< nextpoly; i++ ) {
            if( AbortDraw ) return;
                DrawRhomFrame( i->pt );
        }
}




void RevShade() {
        register Rhomboid *i;

        if( GetNumSegs() < 1 ) {
                return;
        }
        CurMode = NOTACTIVE;
        ClrAbort();
        shadeflag = true;
        if( PrepPoly() || Revolve(AcceptPoly) ) {
            return;
        }
        qsort( (char *)polylist, nextpoly-polylist,
                sizeof(Rhomboid), CmpDepth);

        ClrWindow(false);

        for( i = polylist; i< nextpoly; i++ ) {
                if( AbortDraw ) return;
                DrawRhomShade( i );
        }
}



void RevMap() {
    register MapRhomboid *i;

    if( GetNumSegs() < 1 ) {
            return;
    }
    if( InitMapping() ) {
        return;
    }
    ClrAbort();
    CurMode = NOTACTIVE;
    shadeflag = true;


    if( PrepMPoly() || Revolve(AcceptMPoly) ) {
        return;
    }
    qsort( (char *)mpolylist, mnextpoly-mpolylist,
        sizeof(MapRhomboid), CmpMDepth);
    ClrWindow(false);

    for( i = mpolylist; i< mnextpoly; i++ ) {
            if( AbortDraw ) return;
            DrawRhomMap(i);
    }
}
\Rogue\Monster\
else
  echo "will not over write ./poly.c"
fi
if `test ! -s ./poly.h`
then
echo "writing ./poly.h"
cat > ./poly.h << '\Rogue\Monster\'
#ifndef POLY_H_FILE
#define POLY_H_FILE
#include <exec/types.h>
#include "mytypes.h"
#include "revolve.h"


#define RhomVxNum 4

typedef struct {
    ScrnPair    pt[RhomVxNum];
    float       depth;
    float       intensity;
} Rhomboid;

typedef struct {
    Rhomboid   rhom;
    short   bezindex,
            revindex;
} MapRhomboid;

extern bool SpecOn;

#define DefLightSrcX 0.0
#define DefLightSrcY 0.0
#define DefLightSrcZ 0.0
#define DefIntensity 0.8
#define DefAmbience 0.15
#define DefKd   0.8
#define DefKs   0.2


extern float Ambience,
             PtIntensity,
             Kd, Ks;

extern Pt3 LightSrc;

extern void RevNoShade();
extern void RevShade();
extern void RevMap();

#endif !POLY_H_FILE
\Rogue\Monster\
else
  echo "will not over write ./poly.h"
fi
if `test ! -s ./readilbm.c`
then
echo "writing ./readilbm.c"
cat > ./readilbm.c << '\Rogue\Monster\'
#include <stdio.h>
#include <ctype.h>
#include "readilbm.h"

#define FALSE 0
#define TRUE 1

#define RED 0
#define GRN 1
#define BLU 2

 /* color to grey conversion methods */

#define AVERAGE 0
#define LUMIN    1
#define DIST     2
#define REDONLY  3
#define GREENONLY 4
#define BLUEONLY 5

#define MakeID(a,b,c,d) ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )

#define ID_FORM MakeID('F','O','R','M')
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_CAMG MakeID('C','A','M','G')
#define ID_BODY MakeID('B','O','D','Y')

#define ROUNDUP(x) ( ((x)+1) & (~1L) )



static struct CHUNK {
    unsigned long Id, Size;
} Chunk;

static unsigned char   cmap[32][3];

struct BMHD {
    short w,h,x,y;
    char npl,mask,compress,pad1;
    short transparentColor;
    char xAspect,yAspect;
    short pageWidth,pageHeight;
};


static short    Color[32],              /* output colors        */
                Method = AVERAGE;       /* color conversion     */
static short     numcolors;

static
FILE            *fin;                   /* input file           */

extern char     *malloc();

static void FRead();
static void ReadBMHD();
static void ReadChunk();
static void RastOut();
static void HamOut();
static char GetIlbmVal();
static void InitColorMappings();
static short Convert();
static void ReadDecomLine();
static void ProcessRows();

#define ABORT(str) { OutErr(str); goto ErrorExit; }


void SetGreyModel( model )
    int model;
{
    Method = model;
}

 /*
  * main routine for reading in an iff file
  */

void ReadIlbm(filename)
    char *filename;
{
    struct BMHD bmhd;
    long    ILBMid;
    unsigned char cmapFlag = FALSE,
                  bmhdFlag = FALSE;


    if ((fin = fopen (filename, "r")) == NULL) {
        OutErr ("ERROR: cannot open input file");
        return;
    }
    /* read in iff file */

    ReadChunk ();
    if (Chunk.Id != ID_FORM)
        ABORT ("Not an IFF File");

    FRead (&ILBMid, 4);
    if (ILBMid != ID_ILBM)
        ABORT ("Not an ILBM File");

    while (1) {
        long camgdata;

        ReadChunk ();

        if (Chunk.Id == ID_BODY)
            break;

        if( feof( fin ) ) {
            ABORT("reached end of file without seeing body\n");
        }

        switch (Chunk.Id) {
            case ID_CMAP:
                FRead (cmap, Chunk.Size);
                numcolors = Chunk.Size/3;
                cmapFlag = TRUE;
                break;
            case ID_BMHD:
                ReadBMHD (&bmhd);
                bmhdFlag = TRUE;
                break;
            case ID_CAMG:
                FRead( &camgdata, sizeof(camgdata) );
                break;
            default:            /* unknown identifier */
                fseek( fin, Chunk.Size, 1);
                break;
        }
    }
    if (!cmapFlag) {
        ABORT("IFF file does not contain a CMAP chunk before the BODY\n");
    }

    if (!bmhdFlag) {
        ABORT("IFF file does not contain a BMHD chunk before the BODY\n");
    }

    InitColorMappings();
    if( OpenImgPix( bmhd.w, bmhd.h, Convert(0xf, 0xf, 0xf)) ) {
        ProcessRows(&bmhd);
    }

    ErrorExit:
        fclose( fin);
}

static void ProcessRows(bmhd)
    struct BMHD *bmhd;
{
    char *rastlist[6];
    char *Raster;
    int depth, i, v, pixwidth;
    int BytePerLine;

    depth = bmhd->npl;
    pixwidth = bmhd->w;
    BytePerLine =(pixwidth+7) / 8;


    Raster = (char *) malloc (BytePerLine * depth);

    if(!Raster ) {
        OutErr("ProcessRows:could not allocate Raster");
        return;
    }

    for( i = 0; i < depth; i++ ) {
        rastlist[i] = Raster + BytePerLine*i;
    }

    for( v = 0; v < bmhd->h; v++) {
        switch (bmhd->compress) {
            case 0:
                FRead (Raster, BytePerLine * depth);
                break;
            case 1:
                for( i = 0; i < depth; i++) {
                    ReadDecomLine( BytePerLine, rastlist[i]);
                }
                break;
            default:
                ABORT ("Unknown Compression type in BODY");

        }
        if(depth == 6 ) {
            HamOut( rastlist, pixwidth, v);
        }
        else {
            RastOut( rastlist, pixwidth, v, depth);
        }
    }

    ErrorExit:
        if( Raster ) free(Raster);
}


static void ReadDecomLine(linebytes, rp)
    int linebytes;
    char *rp;
{
    int runlen;
    char pixel;

    while (linebytes) {
        runlen = getc (fin);
        if (runlen > 127)
            runlen -= 256;
        if (runlen >= 0) {
            runlen++;
            FRead (rp, runlen);
            rp += runlen;
            linebytes -= runlen;
        }
        else {
            runlen = -runlen + 1;
            linebytes -= runlen;
            pixel = getc (fin);
            do
                *(rp++) = pixel;
            while (--runlen);
        }
    }
}

 /*
  * Convert - convert (r,g,b) to hex greyscale.
  */

static short Convert(r,g,b)
    unsigned char   r,g,b;
{
    short   i,
            rd, gd, bd,
            min,
            dist,
            best;

 /* convert color according to 'Method' */
    switch (Method) {
        case AVERAGE:           /* average r,g,b to obtain grey level */
            return ((r + g + b) / 3);
        case LUMIN:             /* use NTSC luminescence as grey level */
            return ((r * 30 + g * 59 + b * 11) / 100);
        case DIST:          /* use grey with minimum distance in color */
            min = 15*15 * 3;
            for( i = 0; i < numcolors; i++ ) {
                rd = r -i;
                gd = g - i;
                bd = b - i;
                dist = rd * rd + gd * gd + bd * bd;
                if( dist < min ) {
                    min = dist; best = i;
                }
            }
            return( best );
        case REDONLY:
            return(r);
        case GREENONLY:
            return(g);
        case BLUEONLY:
            return(b);

    }
}                               /* Convert */

static void InitColorMappings()
{
    int     i;

 /* put colors in 4-bit range and Convert */
    for (i = 0; i < 32; i++) {
        cmap[i][RED] >>= 4;
        cmap[i][GRN] >>= 4;
        cmap[i][BLU] >>= 4;
        Color[i] = Convert (cmap[i][RED], cmap[i][GRN], cmap[i][BLU]);
    }
}


/*
 * leftmost pixel of byte is in most significant bit of byte
 */
static char GetIlbmVal( rastlist, h, bpp )
    char *rastlist[6];
    int h, bpp;
{
    int i;
    char value = 0;
    short mask, bytep;

    mask = 0x80 >> ( h & 7);
    bytep = h >> 3;

    for( i = bpp-1; i >= 0; i-- ) {
        value <<= 1;
        value |= (*(rastlist[i]+bytep) & mask) ? 1: 0;
    }
    return( value );
}

 /*
  * HamOut - output ham image in hex.
  */
static void HamOut(rastlist, pixwidth, v)
    char *rastlist[6];
    int pixwidth, v;
{
    unsigned char   lastred = 0,
                    lastgreen = 0,
                    lastblue = 0;
    int     h;
    char pixval;
    for( h = 0; h <pixwidth; h++ ) {
        short shade;

        shade = GetIlbmVal(rastlist, h, 6);
        pixval = shade & 0x0F;
        switch (shade & 0x30) {
        case 0x00:
            lastred = cmap[pixval][RED];
            lastgreen = cmap[pixval][GRN];
            lastblue = cmap[pixval][BLU];
            shade = Color[pixval];
            break;
        case 0x10:
            lastblue = pixval;
            shade = Convert(lastred,lastgreen,lastblue);
            break;
        case 0x20:
            lastred = pixval;
            shade = Convert(lastred,lastgreen,lastblue);
            break;
        case 0x30:
            lastgreen = pixval;
            shade = Convert(lastred,lastgreen,lastblue);
        }
        SetImgPix(h, v, shade);
    }
}

 /*
  * RastOut - handle normal bit mapped images
  */
static void RastOut(rastlist, pixwidth, v, depth)
    char *rastlist[6];
    int pixwidth, v, depth;
{
    int h, bit;
    for( h = 0; h < pixwidth; h++ ) {
        short shade;
        shade = Color[GetIlbmVal( rastlist, h, depth)];
        SetImgPix( h, v, shade);
    }
}

 /*
  * ReadChunk - read in an IFF Chunk.
  */

static void ReadChunk()
{
    FRead (&Chunk, sizeof (Chunk));

}                               /* ReadChunk */

 /*
  * ReadBMHD - read the BMHD structure.
  */

static void ReadBMHD(bmhd)
struct BMHD *bmhd;
{
    FRead (bmhd, Chunk.Size);
}                               /* ReadBMHD */


 /*
  * FRead - read 'len' bytes to 'pointer' while checking for an error.
  */

static void FRead(pointer,len)
char    *pointer;
int     len;
{
    if (fread (pointer, len, 1, fin) == 0)  {
        char outbuff[90];
        sprintf(outbuff,"Fread Error in reading input file at %d ", ftell(fin));
        OutErr(outbuff);
    }
}                               /* FRead */
\Rogue\Monster\
else
  echo "will not over write ./readilbm.c"
fi
if `test ! -s ./readilbm.h`
then
echo "writing ./readilbm.h"
cat > ./readilbm.h << '\Rogue\Monster\'
#ifndef READILBM_H_FILE

#define READILBM_H_FILE

#ifndef MYTYPES_H_FILE
#include "mytypes.h"
#endif MYTYPES_H_FILE

#define DefRepV 1
#define DefRepH 1

/*
 * how many times the image should be replicated on screen
 */
extern short MapRepV, MapRepH;

/*
 * Size of image read in
 */
extern int MapImageV, MapImageH;
/*
 * ReadPixel returns an intensity between 0-255
 */
extern short GetImgPix(/* short vert, hori */);
extern void CloseImgPix();
bool OpenImgPix( /* int sizex, sizey; short maxshade */);
void SetImgPix( /* int x, y; short val */ );
short GetImgPix( /* int x, y */);
extern void PrepImgPix(/* void */);
#define DefXYFlip false
extern void FlipImgPix( /* bool flip */);
extern void SetGreyModel( /* int */);

#endif READILBM_H_FILE

\Rogue\Monster\
else
  echo "will not over write ./readilbm.h"
fi
if `test ! -s ./revolve.c`
then
echo "writing ./revolve.c"
cat > ./revolve.c << '\Rogue\Monster\'
#include <math.h>
#include "fasttrig.h"
#include "bezpt.h"
#include "revolve.h"
#include "mytypes.h"


RevAxisType RevAxis;


short RevMesh = DefRevMeshVal;
short RevImageR, /* revolution index */
      RevImageB; /* bezier index */

static int RotRange = DefRotRange;
static int RotStart = DefRotStart;
static int SecAngle = DefTilt;
static float SurfDist = DefSurfDist;
static float ViewDist = DefViewDist;
static bool Perspective = DefPersp;

void SetPerspective(  value)
    int value;
{
    Perspective = value;
}



void SetRevAxis( value)
    int value;
{
    RevAxis = (value)? RevY : RevX;
}


void SetRotStart( value )
    int value;
{
    RotStart = value;
}

void SetRotRange(  value )
    int value;
{
    RotRange = value;
}

void SetSecAng( value )
    int value;
{
    SecAngle = value;
}

void SetRevMesh( value )
    int value;
{
    RevMesh = value;
}


void SetSurfDist( value )
    int value;
{
    SurfDist = (float )value;
}


void SetViewDist( value )
    int value;
{
    ViewDist = (float )value;
}



static
float secsin, seccos; /* trig values of secondary angle */

static
int sizeptlist = 0;

static
PtGen *ptlist1 = 0,
      *ptlist2 = 0;

static
int NumEnts; /* number of angle slices */


static
bool PrepRev()
{
    NumEnts = RevMesh+1;

    /*
     * allocate space 3d descriptions of a point revolved x degrees
     */
    if( NumEnts > sizeptlist ) {
        if( ptlist1 ) free(ptlist1);
        if( ptlist2 ) free(ptlist2);

        ptlist1 =(PtGen *) malloc( NumEnts * sizeof(PtGen)  );
        ptlist2 =(PtGen *) malloc( NumEnts * sizeof(PtGen)  );
        if( !ptlist1 || !ptlist2 ) {
            OutErr("PrepRev:not enough memory");
            return(true);
        }
    }


    if( InitFastTrig( RotStart, RotRange, NumEnts)) return(true);
    secsin = sin((float)SecAngle*PI/180);
    seccos = cos((float)SecAngle*PI/180);
    return (false);
}


static
void CalcRing(ptlist, xpos, ypos)
register PtGen *ptlist;
float xpos, ypos;
{
    int angle;

    for( angle = 0; angle < NumEnts; angle++, ptlist++) {
        float temp;
        /*
         * calculate 3d coordinate of point revolved
         */
        if( RevAxis == RevX) {
            ptlist->d3.y = ypos * fcos(angle);
            temp = ypos * fsin(angle);
            ptlist->d3.x = xpos* seccos - temp *secsin;
            ptlist->d3.z = xpos * secsin + temp * seccos;
        }
        else {
            ptlist->d3.x = xpos * fcos(angle);
            temp = xpos * fsin( angle);
            ptlist->d3.y = ypos * seccos + temp * secsin;
            ptlist->d3.z = secsin * ypos - temp * seccos;
        }
        ptlist->d3.z -= SurfDist;

        if( Perspective ) {
            float PerspScale;

            PerspScale = fabs(ViewDist / ptlist->d3.z);
            ptlist->d3.x *= PerspScale;
            ptlist->d3.y *= PerspScale;
        }
        /*
         * calculate the 2d screen coordinate equvalent
         */
      /*
        ptlist->d2.x = (short) ptlist->d3.x;
        ptlist->d2.y = (short) ptlist->d3.y;
       */
        ptlist->d2.x = (short) (ptlist->d3.x + 0.5);
        ptlist->d2.y = (short) (ptlist->d3.y + 0.5);
    }
}






/*
 * return true on failure
 */
bool Revolve(acceptfunc)
void (*acceptfunc)();
{
    int segno;
    float tparm, deltat;
    int angle;
    int subseg;

    if( PrepRev() ) {
        return(true);
    }

    deltat = 1.0/BezMesh;
    RevImageB = 0;
    ResetActSeg();
    do {
        float xpos, ypos;


        InitCalcBez();
        xpos = StartPtX(GetCurSeg());
        ypos = StartPtY(GetCurSeg());
        CalcRing(ptlist1, xpos, ypos );
        for( subseg = 1; subseg <= BezMesh; subseg++ ) {
            register PtGen *ptlista, *ptlistb;
            register int numpoly;

            tparm = subseg * deltat;
            if( subseg & 1 ) {
                ptlista = ptlist2; ptlistb = ptlist1;
            }
            else {
                ptlista = ptlist1; ptlistb = ptlist2;
            }

            CalcBezPt(tparm, &xpos, &ypos );
            CalcRing( ptlista, xpos, ypos );
            RevImageR = 0;
            for( numpoly = NumEnts -1; numpoly--; ) {
     /*         (* acceptfunc)(ptlista, ptlista+1, ptlistb+1, ptlistb); */
                (* acceptfunc)(ptlistb, ptlista, ptlista+1, ptlistb+1);
                ptlista++;
                ptlistb++;
                RevImageR++;
            }
            RevImageB++;
        }
        NextSeg();

    } while( ActSeg );
    return( false );
}
\Rogue\Monster\
else
  echo "will not over write ./revolve.c"
fi
if `test ! -s ./revolve.h`
then
echo "writing ./revolve.h"
cat > ./revolve.h << '\Rogue\Monster\'
#ifndef REVOLVE_H_FILE

#define REVOLVE_H_FILE
#include "mytypes.h"

typedef enum { RevX, RevY } RevAxisType;
extern RevAxisType RevAxis;

#define DefRevMeshVal 30
#define DefSurfDist 3000
#define DefTilt 15
#define DefRotRange 360
#define DefRotStart 0
#define DefPersp false
#define DefViewDist 3000

typedef struct {
    short x, y;
} ScrnPair;




typedef struct {
        float x, y, z;
} Pt3;

typedef struct {
        Pt3 d3;
        ScrnPair d2;
} PtGen;

extern short RevMesh;
extern short RevImageR,
             RevImageB;

extern void SetRotStart();
extern void SetRotRange(/* Panel_item, int, struct input_event */);
extern void SetSecAng(/* Panel_item, int, struct input_event */);
extern void SetPolyMode( /* Panel_item, int, struct input_event */);
extern void SetFitBez( /* Panel_item, int, struct input_event */);
extern bool Revolve( /* Panel_item, int, struct input_event */);
extern void SetRevAxis( /* Panel_item, int, struct input_event */);
extern void SetRevMesh( /* Panel_item, int, struct input_event */);
extern void SetSurfDist(/* Panel_item, int, struct input_event */);
extern void SetViewDist();
extern void SetPerspective();
#endif !REVOLVE_H_FILE
\Rogue\Monster\
else
  echo "will not over write ./revolve.h"
fi
if `test ! -s ./scrndef.c`
then
echo "writing ./scrndef.c"
cat > ./scrndef.c << '\Rogue\Monster\'
/* this file contains definition for the screen */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#ifdef MANX
#include <functions.h>
#endif

#include "scrndef.h"

struct TextAttr myfont1 = {
    (UBYTE *)"topaz.font", 8, 0, 0
};



struct NewScreen SurfScrnDef = {
    0,0,   /* left and top edge */
    SurfInitW, SurfInitH+ButHeight,  /* width and height */
    SurfInitD,   /* num bitplanes bit planes */
    0,1, /* detail, block pen */
    SurfInitType, /* lores non interlaced */
    CUSTOMSCREEN,
    &myfont1,
    NULL /*(UBYTE *) "Screen" */,
    NULL,
    NULL
};


struct NewWindow SurfWinDef = {
    0, ButHeight, /* left, top */
    SurfInitW, SurfInitH, /* width, height */
    -1, -1, /* default detail and block pen */
    MOUSEBUTTONS | MOUSEMOVE| INTUITICKS /* | CLOSEWINDOW */,
    RMBTRAP | SIMPLE_REFRESH | GIMMEZEROZERO | BORDERLESS
     | BACKDROP,
    NULL, NULL, /* gadget, checkmark */
    NULL /* (UBYTE *) "BezSurf - By Eric Davies" */,
    NULL, /* pointer to screen */
    NULL, /* pointer to super bitmap */
    10, 10, 640, 200, /* min and max dimensions */
    CUSTOMSCREEN
};

struct NewWindow GadWinDef = {
    0, 0, /* left, top */
    SurfInitW, ButHeight, /* width, height */
    -1, 0, /* default detail and block pen */
    GADGETUP,
    SIMPLE_REFRESH | BORDERLESS | BACKDROP,
    NULL, NULL, /* gadget, checkmark */
    NULL /*(UBYTE *) "Gadwin" */,
    NULL, /* pointer to screen */
    NULL, /* pointer to super bitmap */
    10, 10, 10, 10, /* min and max dimensions */
    CUSTOMSCREEN
};



struct NewWindow CntrlWinDef = {
    0, 0, /* left, top */
    640, 180, /* width, height */
    -1, -1, /* default detail and block pen */
    CLOSEWINDOW| GADGETUP| MENUPICK,
    SIMPLE_REFRESH | WINDOWCLOSE | WINDOWDEPTH
    | WINDOWDRAG | WINDOWSIZING,
    NULL, NULL, /* gadget(set by program), checkmark */
    (UBYTE *)  "BezSurf - Control Panel - By Eric Davies",
    NULL, /* pointer to screen */
    NULL, /* pointer to super bitmap */
    10, 10, 640, 200, /* min and max dimensions */
    WBENCHSCREEN
};
\Rogue\Monster\
else
  echo "will not over write ./scrndef.c"
fi
if `test ! -s ./scrndef.h`
then
echo "writing ./scrndef.h"
cat > ./scrndef.h << '\Rogue\Monster\'
#ifndef SCRNDEF_H_FILE
#define SCRNDEF_H_FILE

#define ButHeight 10   /* height of GadWin */
#define SurfInitW 320  /* initial width */
#define SurfInitH 200  /* initial height */
#define SurfInitD 4    /* 16 color */
#define SurfInitType 0 /* Lores */

extern struct NewWindow SurfWinDef;
extern struct NewWindow CntrlWinDef;
extern struct NewScreen SurfScrnDef;
extern struct NewWindow GadWinDef;

#endif !SCRNDEF_H_FILE

\Rogue\Monster\
else
  echo "will not over write ./scrndef.h"
fi
if `test ! -s ./scrnio.c`
then
echo "writing ./scrnio.c"
cat > ./scrnio.c << '\Rogue\Monster\'
/* main program */

#include "scrnio.ih"
/*#include <functions.h>*/

#include "scrndef.h"
#include "scrnio.h"
#include "gadgetdef.h"
#include "menudef.h"

struct Screen *SurfScrn = NULL;
struct Window *SurfWin = NULL;
struct Window *CntrlWin = NULL;
struct Window *GadWin = NULL;
/*
 * bit masks for waiting for signals
 */
short CntrlSigBit, SurfSigBit, GadSigBit;
long SignalMask = 0;

struct RastPort *rp;
struct ViewPort *vp;

struct Library *GfxBase = 0,
               *IntuitionBase = 0;

int XOR = COMPLEMENT, /* so my other modules don't need to */
    WRITE = JAM1;     /* include so many amiga includes */

int WinHOrig, WinVOrig;
int WinFgCol;
int ColorMax;
int NumColors;

/*
 * data structures needed to use amiga polygons routines
 */
static WORD PolyArea[5*5];
static struct AreaInfo PolyInfo;
static PLANEPTR PolyWorkSpace;
static struct TmpRas PolyTmpRas;



static void ResetWinDat() {

    WinHOrig = SurfWinDef.Width >>1;
    WinVOrig = SurfWinDef.Height >>1;


    rp = SurfWin->RPort;
    SetDrMd( rp,  JAM1 );
}



/*
 * open surface window/screen
 */
OpenSurf() {
    NumColors = 1 << SurfScrnDef.Depth;
    ColorMax = ( NumColors -1) * 8 + 1;
    WinFgCol = (NumColors - 1) & 0x1f;

    SurfScrnDef.BlockPen = ( WinFgCol *3) /4;
    SurfScrnDef.DetailPen = WinFgCol>>2;

    SurfScrn = OpenScreen( &SurfScrnDef );
    MenuSetColMap();
    SurfWinDef.Screen = GadWinDef.Screen = SurfScrn;
    SurfWinDef.Width = GadWinDef.Width = SurfScrnDef.Width;
    SurfWinDef.Height = SurfScrnDef.Height - ButHeight;

    SurfWin = OpenWindow( &SurfWinDef );
    GadWin = OpenWindow( &GadWinDef );
    SurfSigBit = SurfWin->UserPort->mp_SigBit;
    GadSigBit = GadWin->UserPort->mp_SigBit;
    SignalMask = (1<<CntrlSigBit) | (1<<SurfSigBit)| (1<<GadSigBit);

    ResetWinDat();
    ShowTitle( SurfScrn, 0L ); /* hide screen title behind backdrop */

    InitArea( &PolyInfo, PolyArea, 5);
    rp->AreaInfo = &PolyInfo;

    PolyWorkSpace = AllocRaster( SurfWinDef.Width, SurfWinDef.Height);

    if( !PolyWorkSpace ) {
        CloseDisplay();
        perror("no space for temporary rastern");
        exit(0);
    }
    else {
        InitTmpRas( &PolyTmpRas, PolyWorkSpace,
                    RASSIZE( SurfWinDef.Width, SurfWinDef.Height ));
        rp->TmpRas = &PolyTmpRas;
    }
}


void InitWindow()
{
    GfxBase = OpenLibrary("graphics.library",0);
    if( GfxBase == 0 ) {
        OutErr("graphics library won't open");
        exit(10);
    }

    IntuitionBase = OpenLibrary("intuition.library",0);
    if( IntuitionBase == 0 ) {
        OutErr("intuition library won't open");
        exit(10);
    }

    InitGadgets();
    CntrlWin = OpenWindow( &CntrlWinDef );
    CntrlSigBit = CntrlWin->UserPort->mp_SigBit;

    MenuSetScrn();

    if( !SurfScrn || !SurfWin || !CntrlWin ) {
        OutErr("couldn't open at least one window or screen");
        CloseDisplay();
        exit( 0 );
    }

    SetMenuStrip(CntrlWin, menu );
}


/*
 * remove surface window/screen
 */
CloseSurf() {

    if( PolyWorkSpace)
        FreeRaster( PolyWorkSpace, SurfWinDef.Width, SurfWinDef.Height );

    if( SurfWin )
        CloseWindow( SurfWin );

    if( GadWin )
        CloseWindow( GadWin );

    if( SurfScrn )
        CloseScreen( SurfScrn );
}


void CloseDisplay()
{

    CloseSurf();


    if( CntrlWin ) {
        ClearMenuStrip( CntrlWin );
        CloseWindow( CntrlWin );
    }

    EndGadgets();

    if ( IntuitionBase )
        CloseLibrary(IntuitionBase);

    if ( GfxBase )
        CloseLibrary(GfxBase);
}


\Rogue\Monster\
else
  echo "will not over write ./scrnio.c"
fi
if `test ! -s ./scrnio.h`
then
echo "writing ./scrnio.h"
cat > ./scrnio.h << '\Rogue\Monster\'
extern void InitWindow();
extern void CloseDisplay();
extern void SetMono();
extern void ClearWindow();
extern void DrawPoly();

extern int WinHOrig, WinVOrig;
extern int XOR, WRITE;
extern short DitherMask;

#define CntrX(XVAL) ((XVAL) - WinHOrig)
#define CntrY(YVAL) (WinVOrig - (YVAL)) /* fudge for the mouse */

#define UCntrX(XVAL) ((XVAL) + WinHOrig)
#define UCntrY(YVAL) (WinVOrig - (YVAL))

\Rogue\Monster\
else
  echo "will not over write ./scrnio.h"
fi
if `test ! -s ./scrnio.ih`
then
echo "writing ./scrnio.ih"
cat > ./scrnio.ih << '\Rogue\Monster\'
/* main program */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>


extern struct Screen *SurfScrn;
extern struct Window *SurfWin;
extern struct Window *CntrlWin;
extern struct Window *GadWin;
extern long BackColor;
/*
 * bit masks for waiting for signals
 */
extern short CntrlSigBit, SurfSigBit, GadSigBit;
extern long SignalMask;

extern struct RastPort *rp;
extern struct ViewPort *vp;



extern int WinFgCol;
extern int ColorMax;
extern int NumColors;

extern UWORD GrayPat[9][4];

#define DefBkPlane 0

\Rogue\Monster\
else
  echo "will not over write ./scrnio.ih"
fi
if `test ! -s ./scrnops.c`
then
echo "writing ./scrnops.c"
cat > ./scrnops.c << '\Rogue\Monster\'
#include "scrnio.ih"
#ifdef MANX
#include <functions.h>
#endif

#include "scrndef.h"
#include "scrnio.h"
#include "gadgetdef.h"
#include "menudef.h"

#include "bezpt.h"
#include "revolve.h"
#include "control.h"
#include "poly.h"


long BackColor = DefBkPlane;
short DitherMask = 7;

void SetMono( maxrval, maxgval, maxbval )
    short maxrval,
          maxgval,
          maxbval;
{
    long i;
    short range;
    long rval, gval, bval;

    range = (NumColors -1) & 0x1f;  /* max 32 colours */
    for( i = 0; i <= range; i++ ) {
        rval = (maxrval * i )/range;
        gval = (maxgval * i )/range;
        bval = (maxbval * i )/range;

        SetRGB4( &(SurfScrn->ViewPort),  i, rval, gval, bval );
    }
}


void SetRainbow()
{
    long i;
    short range;
    long rval, gval, bval;

    range = NumColors>> 1;
    /*
     * can't do a rainbow with only 2 colors
     */
    if( range < 2) {
        return;
    }

    for( i = 0; i < range; i++ ) {
        long diff;

        diff = (0xf * i )/(range-1);
        rval = 0xf - diff;
        bval = diff;
        gval = 0xf;
        SetRGB4( &(SurfScrn->ViewPort), i, rval, gval, bval);
    }
    for( i = 0; i < range; i++ ) {
        long diff;

        diff = (0xf * i )/(range-1);
        rval = diff;
        bval = 0xf;
        gval = 0xf - diff;
        SetRGB4( &(SurfScrn->ViewPort), i+range, rval, gval, bval);
    }
    SetRGB4( &(SurfScrn->ViewPort), 0L, 0L, 0L, 0L);
}

/*
 * set colours for hourglass pointer
 */
SetHourGlassCol()
{
    SetRGB4( &(SurfScrn->ViewPort),17L, 6L, 2L, 3L );
    SetRGB4( &(SurfScrn->ViewPort),18L, 0L, 0L, 0L );
    SetRGB4( &(SurfScrn->ViewPort),19L, 9L, 7L, 6L );
}



void ClrWindow(drawaxis)
bool drawaxis;
{
    long BkColAdj; /* background color adjusted for number of bit planes */

    BkColAdj = (BackColor * NumColors) / 32;
    SetRast(rp, BackColor);  /* clear the window to colour 0 */
    SetAPen(rp, WinFgCol );
    /*
     * Draw axis on screen
     */
    if( drawaxis) {
        Move( rp, 0, WinVOrig);    /* x axis */
        Draw( rp, (long)SurfWinDef.Width, (long)WinVOrig );

        Move( rp, WinHOrig, 0);     /* y axis */
        Draw( rp, (long)WinHOrig, (long)SurfWinDef.Height );
    }
}





void DrawLine( x1, y1, x2, y2, mode )
int x1, y1, x2, y2;
int mode;
{
    SetDrMd( rp, mode );
    Move( rp, (long)UCntrX(x1), (long)UCntrY(y1));
    Draw(rp, (long)UCntrX(x2), (long)UCntrY(y2) );
}



void PaintPoint(x,y,forecolor)
    short x, y;
    float forecolor;
{
    long shade;
    shade = forecolor * (NumColors-1);

    if( shade >= NumColors) {
        shade = NumColors-1;
    }
    else if ( shade < 0 ) {
        shade = 0;
    }

    SetAPen( rp, shade );
    WritePixel( rp, (long)UCntrX(x), (long)UCntrY(y));
}



void DrawPnt( x, y, op )
int x, y, op;
{
    x = UCntrX(x);
    y = UCntrY(y);
    SetDrMd(rp, op );
    RectFill( rp, (long)x, (long)y, (long)x, (long)y);
}


void DrawSqr(x, y, op )
int x, y, op;
{
    x = UCntrX(x);
    y = UCntrY(y);
    SetDrMd(rp, op );
    RectFill( rp, x - 2L, y -2L, x+ 2L, y+2L );
}


void DrawRhomShade( poly)
Rhomboid *poly;
{
    int i;
    int shade;
    long backcolor, forecolor;


    shade = (int)((poly->intensity) * ColorMax);
    if( shade > ColorMax) {
        shade = ColorMax;
    }

    backcolor = shade >> 3;
    forecolor = backcolor +1;
    if( forecolor >= NumColors ) {
        forecolor = backcolor;
    }

    SetDrMd( rp, JAM2);
    SetAPen( rp, forecolor );
    SetBPen( rp, backcolor );
    SetAfPt( rp, GrayPat[ shade & DitherMask ], 2);

    AreaMove( rp, UCntrX(poly->pt[0].x), UCntrY(poly->pt[0].y));

    for( i = 1; i < 4; i++ ) {
        AreaDraw( rp, UCntrX(poly->pt[i].x), UCntrY(poly->pt[i].y) );
    }
    AreaEnd(rp);

    SetAfPt( rp, GrayPat[8], 2);  /* reset back to solid */

}

void DrawRhomFrame( inlist )
ScrnPair inlist[];
{
    int i;

    SetDrMd( rp, JAM1);
    SetAPen( rp, 0L );
    SetOPen( rp, WinFgCol );

    AreaMove( rp, UCntrX(inlist[0].x), UCntrY(inlist[0].y));

    for( i = 1; i < 4; i++ ) {
        AreaDraw( rp, UCntrX(inlist[i].x), UCntrY(inlist[i].y) );
    }
    AreaEnd(rp);
    BNDRYOFF( rp ); /* turn off outlining */
}




SwitchBox()
{
    struct IntuiMessage mycopy,
                        *orig;

    RefreshGadgets(SurfWinDef.FirstGadget, SurfWin, NULL );

    while(1) {
        long wakeupmask;

        wakeupmask = Wait( SignalMask );
        /*
         * for now, we ignore the wakeupmask,
         * just read messages from each. if I notice a performance problem,
         * I'll fix it then
         */

        /*
         * handle messages for the control window
         */
        while( orig =(struct IntuiMessage *) GetMsg( CntrlWin->UserPort ) ) {

            mycopy = *orig;
            ReplyMsg( orig );

            switch( mycopy.Class ) {
                case MENUPICK:
                    MenuHandler( mycopy.Code );
                    break;

                case GADGETUP:
                    GadgetHandler( (struct Gadget*)mycopy.IAddress );
                    break;

                case CLOSEWINDOW:
                    return;

                default:
                    break;
            }
        }
        /*
         * handle the button window
         */
        while( orig =(struct IntuiMessage *) GetMsg( GadWin->UserPort ) ) {

            mycopy = *orig;
            ReplyMsg( orig );

            switch( mycopy.Class ) {
                case GADGETUP:
                    GadgetHandler( (struct Gadget*)mycopy.IAddress );
                    RefreshGadgets(SurfWinDef.FirstGadget, SurfWin, NULL );
                    break;

                default:
                    break;
            }
        }

        /*
         * handle messages for the other window
         */
        while( orig =(struct IntuiMessage *) GetMsg( SurfWin->UserPort ) ) {

            mycopy = *orig;
            ReplyMsg( orig );

            switch( mycopy.Class ) {
                case MOUSEBUTTONS:
                    HandleMButtons(&mycopy);
                    break;

                case INTUITICKS:
                    HandleTicks(&mycopy);
                    break;

                case MOUSEMOVE:
                    break;

                default:
                    break;
            }
        }
    }
}

/*
 * display error messages inside a requestor
 */
OutErr(errstr)
    char *errstr;
{
    static struct IntuiText errtext =
        { -1, -1, JAM1, 10, 10, NULL, NULL, NULL };
    static struct IntuiText negtext =
        { -1, -1, JAM1, 80, 20, NULL,(UBYTE *)"Onwards", NULL };

    errtext.IText = (UBYTE *)errstr;

    WBenchToFront();
    AutoRequest(CntrlWin, &errtext, NULL, &negtext, NULL, NULL,
        8*strlen(errstr)+ 40, 60 );
    WindowToFront( CntrlWin );

}

\Rogue\Monster\
else
  echo "will not over write ./scrnops.c"
fi
if `test ! -s ./surf.lnk`
then
echo "writing ./surf.lnk"
cat > ./surf.lnk << '\Rogue\Monster\'
ROOT 	lib:c.o
	scrnio.o
	scrnops.o
	scrndef.o
	main.o
	gadgetdef.o
	menudef.o
	graypat.o
	mouse.o
	gadgetuse.o
	bezpt.o
	revolve.o
	control.o
	poly.o
	fasttrig.o
	readilbm.o
	writeilbm.o
	packer.o
	mapstuff.o
	mapcalc.o
	getfilenames.o
	mapimgpix.o


LIBRARY	LIB:lcmffp.lib
	Lib:lc.lib
	LIB:amiga.lib

TO Surf

verbose
nodebug
\Rogue\Monster\
else
  echo "will not over write ./surf.lnk"
fi
if `test ! -s ./writeilbm.c`
then
echo "writing ./writeilbm.c"
cat > ./writeilbm.c << '\Rogue\Monster\'
#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#ifdef MANX
#include <functions.h>
#endif
#include "mytypes.h"

extern int PackRow();
/*
 * following definitions cut from ilbm.h file
 */
typedef UBYTE Masking;          /* Choice of masking technique.*/
#define mskNone                 0
#define mskHasMask              1
#define mskHasTransparentColor  2
#define mskLasso                3

typedef UBYTE Compression;      /* Choice of compression algorithm applied to
     * each row of the source and mask planes. "cmpByteRun1" is the byte run
     * encoding generated by Mac's PackBits. See Packer.h . */
#define cmpNone      0
#define cmpByteRun1  1

/* Aspect ratios: The proper fraction xAspect/yAspect represents the pixel
 * aspect ratio pixel_width/pixel_height.
 *
 * For the 4 Amiga display modes:
 *   320 x 200: 10/11  (these pixels are taller than they are wide)
 *   320 x 400: 20/11
 *   640 x 200:  5/11
 *   640 x 400: 10/11           */
#define x320x200Aspect 10
#define y320x200Aspect 11
#define x320x400Aspect 20
#define y320x400Aspect 11
#define x640x200Aspect  5
#define y640x200Aspect 11
#define x640x400Aspect 10
#define y640x400Aspect 11

/* A BitMapHeader is stored in a BMHD chunk. */
typedef struct {
    UWORD w, h;                 /* raster width & height in pixels */
    WORD  x, y;                 /* position for this image */
    UBYTE nPlanes;              /* # source bitplanes */
    Masking masking;            /* masking technique */
    Compression compression;    /* compression algoithm */
    UBYTE pad1;                 /* UNUSED.  For consistency, put 0 here.*/
    UWORD transparentColor;     /* transparent "color number" */
    UBYTE xAspect, yAspect;     /* aspect ratio, a rational number x/y */
    WORD  pageWidth, pageHeight;  /* source "page" size in pixels */
    } BitMapHeader;

/* RowBytes computes the number of bytes in a row, from the width in pixels.*/
#define RowBytes(w)   (((w) + 15) >> 4 << 1)



#define IDSIZE 4

WriteIlbm( filename, win, scrn,packflag )
    char *filename;
    struct NewWindow *win;
    struct NewScreen *scrn;
    bool packflag;
{
    FILE *ofile;
    long formpos; /* position of length following 'FORM' */
    long formsize;
    struct ViewPort *vp;

    ofile = fopen(filename,"w");
    if( !ofile ) {
        return;
    }

    fwrite("FORM", IDSIZE, 1, ofile);
    formpos = ftell( ofile );
    fwrite( &formsize, sizeof(formsize), 1, ofile); /* will be rewritten */
    fwrite( "ILBMBMHD", IDSIZE*2, 1, ofile );

    {
        BitMapHeader bmhdr;
        long bmhdrsize;
        static UBYTE xaspect[2][2]= { { x320x200Aspect, x320x400Aspect },
                                      { x640x200Aspect, x640x400Aspect }};
        static UBYTE yaspect[2][2]= { { y320x200Aspect, y320x400Aspect },
                                      { y640x200Aspect, y640x400Aspect }};
        int wx, wy;

        bmhdrsize = 20;
        fwrite( &bmhdrsize, sizeof(bmhdrsize), 1, ofile);
        bmhdr.x = bmhdr.y = 0;
        bmhdr.w = win->Width;
        bmhdr.h = win->Height;
        bmhdr.nPlanes = scrn->Depth;
        bmhdr.masking = mskNone;
        bmhdr.compression = packflag ?cmpByteRun1: cmpNone;
        bmhdr.pad1 = 0;

        wx = (scrn->Width == 320)? 0: 1;
        wy = (scrn->Height == 200)? 0: 1;
        bmhdr.xAspect = xaspect[wx][wy];
        bmhdr.yAspect = yaspect[wx][wy];
        bmhdr.pageHeight = win->Height;
        bmhdr.pageWidth = scrn->Width;
        bmhdr.transparentColor = 0;
        fwrite(&bmhdr, bmhdrsize, 1, ofile );
    }

    fwrite("CMAP",IDSIZE, 1, ofile);
    vp = &win->Screen->ViewPort;
    {
        long cmapsize;
        long i;
        UWORD value;
        UBYTE col[3];
        int numentries;

        numentries = (1<< scrn->Depth );
        cmapsize = numentries*3;
        fwrite( &cmapsize, sizeof(cmapsize), 1,ofile);
        for( i = 0; i < numentries; i++ ) {
              value = GetRGB4(vp->ColorMap, i);
              col[2] = (value & 0xf) << 4;        /* blue */
              col[1] = value & 0xf0; /* green */
              col[0] = (value & 0xf00) >> 4; /* red */
              fwrite(col, 3, 1, ofile);
        }
    }

    fwrite("CAMG", IDSIZE, 1, ofile);
    {
        long viewmode;
        long viewmodesize;

        viewmodesize = sizeof(viewmode);
        viewmode = scrn->ViewModes;
        fwrite(&viewmodesize, sizeof(viewmodesize), 1, ofile);
        fwrite(&viewmode, sizeof(viewmode), 1, ofile );
    }

    fwrite("BODY", IDSIZE,1, ofile);
    {
        struct BitMap *bm;
        long bodypos,
             bodysize;
        UBYTE *bmd[16]; /* assume as many as 16 bit planes :-) */
        int row;
        int plane;
        int rowlength; /* in bytes */
        char outbuff[200]; /* largest enough for a row 1600 bits wide */

        bodypos = ftell(ofile);
        fwrite( &bodysize, sizeof(bodysize), 1, ofile);
        bm = vp->RasInfo->BitMap;
        rowlength = RowBytes(scrn->Width);

        for( plane = 0; plane < scrn->Depth; plane++ ) {
            bmd[plane] = bm->Planes[plane] + rowlength * win->TopEdge;
        }

        /*
         * write actual bitplanes
         */
        for( row = 0; row < win->Height; row++ ) {

            for( plane = 0; plane < scrn->Depth; plane++ ) {
                if( packflag ) {
                    int packedsize;

                    packedsize = PackRow(bmd[plane],outbuff, rowlength );
                    fwrite(outbuff, packedsize, 1, ofile);
                }
                else {
                    fwrite(bmd[plane], rowlength, 1, ofile );
                }
                bmd[plane] += rowlength;
            }
        }
        bodysize = ftell(ofile) -( bodypos + 4);
        /*
         * fill out body to make even
         */
        if( bodysize & 1 ) {
            fputc(0,ofile);
            bodysize++;
        }
        formsize = ftell(ofile) -( formpos + 4);

        fseek( ofile, bodypos, 0L );
        fwrite( &bodysize, sizeof(bodysize), 1, ofile);
    }

    fseek( ofile, formpos, 0L );
    fwrite( &formsize, sizeof(formsize), 1, ofile);

    fclose(ofile);
}
\Rogue\Monster\
else
  echo "will not over write ./writeilbm.c"
fi
echo "Finished archive 1 of 3"
# if you want to concatenate archives, remove anything after this line
exit
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
"I can't tell the difference between ABC News and Hill Street Blues" -Bono