// utils.c
#include "stdafx.h"
#include "common.h"


extern			"C" void 	newtimer();
void (*_prev_int)();
int prev_int[2];


int cval;

UBYTE linecol=64;
SLONG midastime=0;
SLONG lasttime;
SLONG fprocrate=60;
UBYTE *tex[8];
UBYTE *texptr;
UBYTE *fadeptr;
UBYTE *ghostptr;
UBYTE *multab;
UBYTE *texram;
UBYTE mypal[256][3];

UBYTE *screenbuf;
UBYTE *zbuf;

SLONG edgebuf[200][2];
SLONG squaretab[256];
SLONG reciptab[2049];

unsigned char noisetab[NOISETABSIZE+16];
unsigned char noisetab2[NOISETABSIZE+16];

void error(unsigned char *s)
{
	vidmode(3);
	printf("Error: %s\n",s);
	beep();
	exit(-1);
}




//#ifdef DEBUG
SLONG curalloc=0,maxalloc=0;
void *MYCALLOC( size_t num, size_t size )
{	
#undef calloc	
	void *x=calloc(num,size);
	if (x) curalloc+=_msize(x);
	if (!x) {error((unsigned char*)"calloc returned 0");return(NULL);}
	if (curalloc>maxalloc) maxalloc=curalloc;
	return(x);
#define calloc MYCALLOC
}
void *MYMALLOC( size_t size )
{
#undef malloc
	void *x=malloc(size);
	if (x) curalloc+=_msize(x);
	if (!x) {error((unsigned char*)"malloc returned 0");return(NULL);}
	if (curalloc>maxalloc) maxalloc=curalloc;
	return(x);
#define malloc MYMALLOC
}
void *testmalloc( size_t size )
{
#undef malloc
	void *x=malloc(size);
	if (x) curalloc+=_msize(x);
	//if (!x) {error("malloc returned 0");return(NULL);}
	if (curalloc>maxalloc) maxalloc=curalloc;
	return(x);
#define malloc MYMALLOC
}

void *MYREALLOC( void *memblock, size_t size )
{
	if (!memblock) {error((unsigned char*)"attempt to resize a null pointer");return(NULL);}
	curalloc-=_msize(memblock);
#undef realloc
	void *x=realloc(memblock,size);
	if (x) curalloc+=_msize(x);
	if (!x) {error((unsigned char*)"realloc returned 0");return(NULL);}
	if (curalloc>maxalloc) maxalloc=curalloc;
	return(x);
#define realloc MYREALLOC
}
void MYFREE( void *memblock )
{
#undef free
	if (!memblock) {error((unsigned char*)"attempt to free a null pointer");return;}
	if (memblock) curalloc-=_msize(memblock);
	free(memblock);
#define free MYFREE
}
//#endif


void portdelay()
{
	static int moo=0;
	moo++;
}

#define NUMFPROC 8
TIMERFN fprocl[NUMFPROC];
int fproct[NUMFPROC];
int fprocp[NUMFPROC];

void resetfproc()
{
	//lasttime=mcpGet(-1,mcpGTimer);
	lasttime=midastime;
	memset(fprocl,0,sizeof(fprocl));
	memset(fproct,0,sizeof(fproct));
}

UBYTE zmaptab1[256];
UBYTE zmaptab2[256];
UBYTE zmaptab3[256];

int FrameTime;

void initdemo()
{
	SLONG c1,c2,c3;
	lasttime=0;
	// alloc texture ram
	texram=new UBYTE[65536*10];
	texptr=tex[0]=(UBYTE*)((SLONG)(texram+65535)&0xffff0000);
	for (c1=1;c1<8;c1++) tex[c1]=tex[0]+c1*65536;
	fadeptr=tex[7];
	ghostptr=tex[6];
	multab=tex[0]+8*65536;
	UBYTE *b=multab;
	for (c1=0;c1<256;c1++)
	for (c2=0;c2<256;c2++)
	{
		c3=c1*c2/128;if (c3>255) c3=255;
		*b++=c3;
	}

	// noise tab
	for (c1=0;c1<sizeof(noisetab);c1++) noisetab[c1]=(rand()>>2)&3;
	for (c1=0;c1<sizeof(noisetab2);c1++) noisetab2[c1]=(rand()>>2)&127;

	// square tab
	for (c1=0;c1<256;c1++) squaretab[c1]=c1*c1;

	// work out recip tab
	for (c1=0;c1<2049;c1++) if (c1!=1024) reciptab[c1]=65536/(c1-1024);

	calcfocus(128,100);

	// allocate the video buffer
	screenbuf=new UBYTE[76800*4+320*16];
	memset(screenbuf,0,4*76800+320*16);
	screenbuf+=320*8;
	zbuf=new UBYTE[64000+320*16];
	memset(zbuf,0,1*64000+320*16);
	zbuf+=320*8;

	// font
	loadfont();

	// 3d engine
	init3d();

#ifdef NOMUS
	//W32TODO 
	/*
	// own timer
	outp(0x43,0x36);portdelay();
	FrameTime=(1193180/fprocrate);
	outp(0x40,FrameTime&255);portdelay();
	outp(0x40,FrameTime>>8);portdelay();
	outp(0x20,0x20);
	_prev_int = _dos_getvect( 8);
	memcpy(&prev_int,&_prev_int,8);
	_dos_setvect( 8, newtimer);
	*/
#endif

}

void calcfocus(float f, float blurfactor)
{
	int c0,c1,c2;

	float f2;
	f=1.f/f;
	blurfactor*=20000;
	// work out the focusing tables
	for (c0=0;c0<256;c0++)
	{

		f2=(1.f/(0.5f+c0))-f;
		if (f2<0) f2*=6;
		c1=(SQR(f2))*blurfactor;
		if (c1>255) c1=255;
		c2=(128-c1);if (c2<0) c2=0;zmaptab1[c0]=c2;
		c2=(c1);if (c2>128) c2=256-c2;zmaptab2[c0]=c2;
		c2=128-zmaptab2[c0]-zmaptab1[c0];if (c2<0) c2=0;zmaptab3[c0]=c2;
	}
}

void addfproc(TIMERFN p, int pal)
{
	int c1;
	for (c1=0;c1<NUMFPROC;c1++)
	{
		if (fprocl[c1]==NULL || fprocl[c1]==p)
		{
			fprocl[c1]=p;
			fproct[c1]=lasttime;
			fprocp[c1]=pal;
			p();
			return;
		}
	}
	static int c2=0;
	c2++;
	int bt=0x7fffffff;
	for (c1=0;c1<NUMFPROC;c1++)
	{
		if (fproct[c1]<bt)
		{
			bt=fproct[c1];
			c2=c1;
		}
	}
	fprocl[c2]=p;
	fproct[c2]=lasttime;
	fprocp[c2]=pal;
	p();
}

void removefproc(TIMERFN p)
{
	int c1;
	for (c1=0;c1<NUMFPROC;c1++)
	{
		if (fprocl[c1]==p)
		{
			fprocl[c1]=NULL;
			fproct[c1]=0;
		}
	}
}

int dofproc(int pal)
{
	SLONG t,dt,c1;

	if (pal)
	{
		for (c1=0;c1<NUMFPROC;c1++) if (fprocl[c1] && fprocp[c1]==pal) fprocl[c1]();
		return 0;
	}
	UpdateInfo();
	t=midastime;
	dt=t-lasttime;
	int r=dt;
	lasttime=t;
	while (dt>0)
	{
		for (c1=0;c1<NUMFPROC;c1++) if (fprocl[c1] && fprocp[c1]==pal) fprocl[c1]();
		dt--;
	}
	UpdateInfo();
	return r;
}




void closedemo()
{
#ifdef NOMUS
	// restore timer
	//WIN32TODO 
	/*
	outp(0x43,0x36);portdelay();
	outp(0x40,0);portdelay();
	outp(0x40,0);portdelay();
	outp(0x20,0x20);
	_dos_setvect( 8, _prev_int);
	*/

#endif

#ifndef DEBUG
	delete [] texram;
	closelibf();
#endif
}

int tweaked,page,lines400;

void vidmode(int x)
{
	//W32TODO 
	/*
	union REGS regs;
	regs.w.ax=x;
	int386(0x10,&regs,&regs);
	tweaked=page=lines400=0;
	*/
}

void putpixel(int x,int y,int c)
{
	if (x>=0 && y>=0 && x<320 && y<200) screenbuf[y*320+x]=c;
}



void tweak(int _tweak, int _lines400)
{
	// force 240 lines mode!
	_tweak=2;

	tweaked=_tweak;
	lines400=_lines400;
	page=0;
	//WIN32TODO 
	/*
	outpw(0x3c4,0x0f02);
	memset((void*)0xa0000,0,65536);
	if (tweaked)
	{
		// tweakvga
		outpw(0x3c4,0x0604);
		outpw(0x3c4,0x0f02);
		outpw(0x3d4,0x0014);
		outpw(0x3d4,0xe317);
		outpw(0x3d4,0x000c);
		outpw(0x3d4,0x000d);
		if (tweaked>1)
		{
			// 240 line mode
			LONG c1;
			LONG modedata[11]={0x0D06,0x3E07,0x4109,0xEA10,0xAC11,0xDF12,0x0014,0xE715,0x0616,0xE317,0xff18};

			outp (0x3c4,1);
			outp (0x3c4,(inp(0x3c5) || 0x20) && 0xdf);

			outpw(0x3c4,0x0604);
			outpw(0x3c4,0x0100);
			outp (0x3c2,0x0e3);
			outpw(0x3c4,0x0300);
			outp (0x3d4,0x011);
			outp (0x3d5,inp(0x3d5) && 0x7f);
			for (c1=0;c1<11;c1++) outpw(0x3d4,modedata[c1]);
			outpw(0x3c4,0x0f02);
		}
	}
	else
	{
		// untweakvga
		outpw(0x3c4,0x0e04);
		outpw(0x3c4,0x0f02);
		outpw(0x3d4,0x4014);
		outpw(0x3d4,0xa317);
		outpw(0x3d4,0x000c);
		outpw(0x3d4,0x000d);
	}
	if (tweaked && lines400) outpw(0x3d4,0x4009); else outpw(0x3d4,0x4109);
	outpw(0x3c4,0x0f02);
	memset((void*)0xa0000,0,65536);
	*/

	memset((void*)wina000,0,sizeof(wina000));
}

unsigned char bmphdr[54]={
0x42,0x4D,0x38,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x04,0x00,0x00,0x28,0x00,
0x00,0x00,0x40,0x01,0x00,0x00,0xC8,0x00,0x00,0x00,0x01,0x00,0x08,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x0B,0x00,0x00,0x12,0x0B,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00};



void screenshot(int y,void *src1, void *src2=NULL)
{
	return;
	/*
	if ((*((unsigned char*)0x417)&3)!=3) return;
	int c1,c2;
	unsigned char thepal[768];
//WIN32TODO 	outp(0x3c7,0);for (c1=0;c1<768;c1++) thepal[c1]=inp(0x3c9);
//WIN32TODO 	outp(0x3c8,0);for (c1=0;c1<768;c1++) outp(0x3c9,(c1/3)&63);
	beep();
	unsigned char *s1=(unsigned char*)src1;
	unsigned char *s2=(unsigned char*)src2;
	unsigned char *s3;
	if (src2)
	{
		s3=new unsigned char[320*y];
		for (c1=0;c1<y;c1+=2)
		{
			memcpy(s3+320*c1,s1+160*c1,320);
			memcpy(s3+320*c1+320,s2+160*c1,320);
		}
		s1=s3;
	}
	static int scr=1;
	unsigned char fname[32];
	while ((((volatile)(*((unsigned char*)0x417))&3)==3)) ;
	beep();
	FILE *f;
	do
	{
		sprintf(fname,"shot%04d.bmp",scr);
		f=fopen(fname,"rb");
		if (f) fclose(f);
		scr++;
	} while (f);
	f=fopen(fname,"wb");
	if (f)
	{
		beep();
		bmphdr[22]=y&255;
		bmphdr[23]=y/256;
		fwrite(bmphdr,1,54,f);
		for (c1=0;c1<256;c1++)
		{
			c2=4*thepal[c1*3+2];fwrite(&c2,1,1,f);
			c2=4*thepal[c1*3+1];fwrite(&c2,1,1,f);
			c2=4*thepal[c1*3+0];fwrite(&c2,1,1,f);
			c2=0;fwrite(&c2,1,1,f);
		}
		for (c1=y-1;c1>=0;c1--) fwrite(s1+c1*320,1,320,f);
		fclose(f);
		beep();
	}
	if (src2) delete [] s3;
	outp(0x3c8,0);
	for (c1=0;c1<768;c1++) outp(0x3c9,thepal[c1]);
	palupdate++;
	*/
}

void swapscreens(void *src, void *src2)
{
	if (tweaked)
	{

		if (lines400)
		{
			page=0;
			if (src2)
			{
				//WIN32TODO
//				xcopy2((unsigned char*)0xa0000+1600+page*19200,src,src2);
				screenshot(400,src,src2);
			} else
			{
				//WIN32DONE
				xcopy(40,(unsigned char*)src,400);
				screenshot(400,src);
			}
		}
		else
		{
			//WIN32DONE
			xcopy(20,(unsigned char*)src,200);
			screenshot(200,src);
		}
//WIN32TODO 		outpw(0x3d4,0xc+page*19200);
		page=(page+1)%3;
	}
	else
	{
		xcopy(20,(unsigned char*)src,200);
	}
}

void swapscreens2(void *src)
{
	//WIN32DONE
//	xcopy((unsigned char*)0xa0000+page*19200,src,240);
	xcopy(0,(unsigned char*)src,240);
	screenshot(240,src);
//WIN32TODO 	outpw(0x3d4,0xc+page*19200);
	page=(page+1)%3;
}

void swapscreens3(void *src)
{
	//WIN32DONE
	xcopy(0,(unsigned char*)src,480);
	screenshot(480,src);
	page=0;
//WIN32TODO 	outpw(0x3d4,0xc+page*19200);

}


BYTE getvidmode()
{
	//W32TODO 
	/*
	union REGS regs;
	regs.w.ax=0x0f00;
	int386(0x10,&regs,&regs);
	return(regs.h.al);
	*/
	return 0;
}




extern LONG findcol(SLONG r,SLONG g,SLONG b);
LONG findcol(SLONG r,SLONG g,SLONG b)
{
	SLONG md,d,c1,c2,o;
	if (r<0) r=0;
	if (g<0) g=0;
	if (b<0) b=0;
	r=r/32;
	g=g/32;
	b=b/32;
	if (r>63) r=63;
	if (g>63) g=63;
	if (b>63) b=63;
	//o=0;
	//if (r>63) o+=r-63;
	//if (g>63) o+=g-63;
	//if (b>63) o+=b-63;
	//r+=o;g+=o;b+=o;
	c2=0;md=0x7fffffff;
	for (c1=0;c1<256;c1++)
	{
		d=squaretab[abs((SLONG)mypal[c1][0]-r)]+
		  squaretab[abs((SLONG)mypal[c1][1]-g)]+
		  squaretab[abs((SLONG)mypal[c1][2]-b)];
		if (d<md)
		{
			c2=c1;
			md=d;
			if (md==0) return(c1);
		}
	}
	return(c2);
}



void computefade(unsigned char *fname)
{
	SLONG c2,c1;
	FILE *fi=fopen((char*)fname,"wb");
	// compute fade table
	for (c2=0;c2<32;c2++)
	for (c1=0;c1<256;c1++)
		(fadeptr)[c2*256+c1]=findcol(mypal[c1][0]*c2,mypal[c1][1]*c2,mypal[c1][2]*c2);
	  for (c2=0;c2<32;c2++)
	for (c1=0;c1<256;c1++)
		(fadeptr)[(63-c2)*256+c1]=findcol(32*63-c2*63+(LONG)mypal[c1][0]*c2,32*63-c2*63+(LONG)mypal[c1][1]*c2,32*63-c2*63+(LONG)mypal[c1][2]*c2);
	// write fade table to disk
	fwrite(fadeptr,1,64*256,fi);
	fclose(fi);
	readfade(fname);
}

void readfade(unsigned char *fname)
{
	LFILE *f=openf(fname);
	readf(f,fadeptr+256*16,256*64);
	int c1;
	for (c1=0;c1<16;c1++) memcpy(fadeptr+c1*256,fadeptr+16*256,256);
	for (c1=0;c1<64;c1++) memcpy(fadeptr+(80+c1)*256,fadeptr+79*256,256);
	closef(f);
}

void computeghost(unsigned char *fname, SLONG alpha, SLONG beta)
{
	SLONG c2,c1;
	FILE *fi=fopen((char*)fname,"wb");
	// compute fade table
	for (c2=0;c2<256;c2++)
	for (c1=0;c1<256;c1++)
		(ghostptr)[c2*256+c1]=
			findcol(mypal[c1][0]*alpha+mypal[c2][0]*beta,
					mypal[c1][1]*alpha+mypal[c2][1]*beta,
					mypal[c1][2]*alpha+mypal[c2][2]*beta);
	// write fade table to disk
	fwrite(ghostptr,1,256*256,fi);
	fclose(fi);
}

void readghost(unsigned char *fname)
{
	LFILE *f=openf(fname);
	readf(f,ghostptr,256*256);
	closef(f);
}

int mxv,myv,mx,my,mb;
void	GetMouseV()
{
	return;

}

void	GetMouse()
{
	return;
}


//**********************************************************
//**********************************************************
// file library stuff
//**********************************************************
//**********************************************************


DICTIONARY *dict = NULL;
#define DICT( i ) dict[ i ]

FILE *libfi;

typedef struct
{
	unsigned char name[12];
	SLONG posn,clen,len;
} LIBREC;

LIBREC *libdata=NULL;
SLONG numlibfiles=0;
#define LIBMODE numlibfiles		// oh nasty coding! 2 variables in one...


void	closelibf()
{
	if (!LIBMODE) return;
	LIBMODE=0;
	fclose(libfi);
	if (libdata) free(libdata);
	libdata=NULL;
}



SLONG	openlibf(unsigned char *fname)
{
	SLONG c1;
	if (LIBMODE) closelibf();
	libfi=NULL;
	if ((libfi=fopen((char*)fname,"rb"))==NULL) return(-1);
	LIBMODE=1;
	fread(&c1,1,4,libfi);
	if (c1!='XELA')
	{
		closelibf();
		return(-1);
	}
	fread(&numlibfiles,1,4,libfi);
	libdata=(LIBREC*)calloc(numlibfiles,sizeof(LIBREC));
	fread(&c1,1,4,libfi);
	fseek(libfi,c1,SEEK_SET);
	fread(libdata,numlibfiles,sizeof(LIBREC),libfi);
	return(0);
}

SLONG	findlibname(unsigned char *fname)
{
	LIBREC *r=libdata;
	SLONG c1;
	for (c1=0;c1<numlibfiles;c1++)
	{
		if (strnicmp((char*)fname,(char*)r->name,12)==0) return(c1);
		r++;
	}
	return(-1);
}

LFILE*	openf(unsigned char *fname)
{
	if (LIBMODE && strnicmp((char*)fname,"data\\",5)==0) fname+=5;

	//printf("%s\n",fname);
	LFILE *f=(LFILE*)malloc(sizeof(LFILE));
	if (LIBMODE)
	{
		//printf("library opening %s...\n",fname);
		SLONG idx=findlibname(fname);
		//printf("idx %d\n",idx);
		if (idx<0)
		{
						//printf("could not find file %s in library.\n",fname);
						//getch();
			goto ohdear;
		}
		f->len=libdata[idx].len;
		f->buf=(UBYTE*)malloc(libdata[idx].clen);
		fseek(libfi,libdata[idx].posn,SEEK_SET);
		fread(f->buf,1,libdata[idx].clen,libfi);
		resetlzw(f);
		f->fi=libfi;
		//printf("lzwok\n");
	}
	else
	{
ohdear:
		//printf("\n*** open file %s\n",fname);
		f->fi=fopen((char*)fname,"rb");
		if (!f->fi)
		{
			free(f);
			return(NULL);
		}
		fseek(f->fi,0,SEEK_END);
		f->buf=0;
		f->len=ftell(f->fi);
		fseek(f->fi,0,SEEK_SET);
		//printf("ok\n");
	}
	return(f);
}


LFILE*	openflzw(unsigned char *fname)
{
	if (LIBMODE && strnicmp((char*)fname,"data\\",5)==0) fname+=5;
	/*
	if (LIBMODE)
	{
		return(openf(fname));
	}

	else
	*/
	{
		LFILE *f=openf(fname);
		f->buf=(UBYTE*)malloc(f->len);
		fread(f->buf,1,f->len,f->fi);
		resetlzw(f);
		return(f);
	}
	return(NULL);
}

void	closef(LFILE *f)
{
	//printf("close\n");
	if (f->fi!=libfi && f->fi!=NULL) fclose(f->fi);
	if (f->buf) free(f->buf);

	free(f);
}

SLONG	seeklzw(LFILE *f, SLONG pos)
{
	if (pos==0) return(0);
	if (f->fi==libfi)
	{
		resetlzw(f);
		UBYTE *b=(UBYTE*)malloc(pos);
		asmexpand(b,pos);
		free(b);
	}
	else
	{
		resetlzw(f);
		UBYTE *b=(UBYTE*)malloc(pos);
		asmexpand(b,pos);
		free(b);
	}
	return(pos);
}

void	resetlzw(LFILE *f)
{
	RAMRack=0;
	RAMMask=0x80;
	RAMPtr=f->buf;
	InitialiseStorage();
	InitializeDictionary();
	old_code = (ULONG) RAMReadBits(current_code_bits );
	decode_stack[0] = character = old_code;
	numdecode=1;
}

SLONG	readf(LFILE *f, void *buf, SLONG count)
{
	if (count<=0) return(0);
	if (f->fi==libfi || f->buf)
	{
		return(asmexpand(buf,count));
	}
	return(fread(buf,1,count,f->fi));
}

SLONG	seekf(LFILE *f, SLONG pos)
{
	if (f->fi==libfi) return(-1);
	fseek(f->fi,pos,SEEK_SET);
	return(pos);
}

SLONG	readflzw(LFILE *fi, void *buf, SLONG count)
{
	return(asmexpand(buf,count));
}

LONG RAMReadBits(LONG n)
{
	LONG mask=1L<<(n-1),val=0;
	while (mask)
	{
	  if (RAMMask==0x80) RAMRack=*RAMPtr++;
	  if (RAMMask & RAMRack) val|=mask;
	  if (!(RAMMask>>=1)) RAMMask=0x80;
	  mask >>=1;
	}
	return(val);
}







//**********************************************************
//**********************************************************
// LZW stuff
//**********************************************************
//**********************************************************

LONG RAMReadBits(LONG n);

unsigned char decode_stack[ TABLE_SIZE ];
ULONG next_code;
SLONG current_code_bits;
ULONG next_bump_code;
ULONG new_code;
ULONG old_code;
LONG character;
BYTE *RAMPtr,RAMRack,RAMMask;
LONG numdecode;


void InitializeDictionary()
{
    ULONG i;

    for ( i = 0 ; i < TABLE_SIZE ; i++ )
	  DICT( i ).code_value = UNUSED;
    next_code = FIRST_CODE;
    current_code_bits = 9;
    next_bump_code = 511;
}

void InitialiseStorage()
{
	if (dict) return;
	  dict = (DICTIONARY *)
			   malloc( (TABLE_SIZE+1) * sizeof (DICTIONARY) );
}

void FreeStorage()
{
	free(dict);
	dict=NULL;
}


