text: /***
  *
  *	Copyright (c) 1996-2002, Valve LLC. All rights reserved.
  *	
  *	This product contains software technology licensed from Id 
  *	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
  *	All Rights Reserved.
  *
  ****/
  
  //
  // spritegen.c: generates a .spr file from a series of .lbm frame files.
  // Result is stored in /raid/quake/id1/sprites/<scriptname>.spr.
  //
  #pragma warning(disable : 4244)     // type conversion warning.
  #define INCLUDELIBS
  
  
  #ifdef NeXT
  #include <libc.h>
  #endif
  
  #include "spritegn.h"
  
  #define MAX_BUFFER_SIZE		0x100000
  #define MAX_FRAMES			1000
  
  dsprite_t		sprite;
  byte			*byteimage, *lbmpalette;
  int				byteimagewidth, byteimageheight;
  byte			*lumpbuffer = NULL, *plump;
  char			spritedir[1024];
  char			spriteoutname[1024];
  int				framesmaxs[2];
  int				framecount;
  
  qboolean do16bit = true;
  
  typedef struct {
  	spriteframetype_t	type;		// single frame or group of frames
  	void				*pdata;		// either a dspriteframe_t or group info
  	float				interval;	// only used for frames in groups
  	int					numgroupframes;	// only used by group headers
  } spritepackage_t;
  
  spritepackage_t	frames[MAX_FRAMES];
  
  void FinishSprite (void);
  void Cmd_Spritename (void);
  
  
  /*
  ============
  WriteFrame
  ============
  */
  void WriteFrame (FILE *spriteouthandle, int framenum)
  {
  	dspriteframe_t	*pframe;
  	dspriteframe_t	frametemp;
  
  	pframe = (dspriteframe_t *)frames[framenum].pdata;
  	frametemp.origin[0] = LittleLong (pframe->origin[0]);
  	frametemp.origin[1] = LittleLong (pframe->origin[1]);
  	frametemp.width = LittleLong (pframe->width);
  	frametemp.height = LittleLong (pframe->height);
  
  	SafeWrite (spriteouthandle, &frametemp, sizeof (frametemp));
  	SafeWrite (spriteouthandle,
  			   (byte *)(pframe + 1),
  			   pframe->height * pframe->width);
  }
  
  
  /*
  ============
  WriteSprite
  ============
  */
  void WriteSprite (FILE *spriteouthandle)
  {
  	int			i, groupframe, curframe;
  	dsprite_t	spritetemp;
  
  	sprite.boundingradius = sqrt (((framesmaxs[0] >> 1) *
  								   (framesmaxs[0] >> 1)) +
  								  ((framesmaxs[1] >> 1) *
  								   (framesmaxs[1] >> 1)));
  
  //
  // write out the sprite header
  //
  	spritetemp.type = LittleLong (sprite.type);
  	spritetemp.texFormat = LittleLong (sprite.texFormat);
  	spritetemp.boundingradius = LittleFloat (sprite.boundingradius);
  	spritetemp.width = LittleLong (framesmaxs[0]);
  	spritetemp.height = LittleLong (framesmaxs[1]);
  	spritetemp.numframes = LittleLong (sprite.numframes);
  	spritetemp.beamlength = LittleFloat (sprite.beamlength);
  	spritetemp.synctype = LittleFloat (sprite.synctype);
  	spritetemp.version = LittleLong (SPRITE_VERSION);
  	spritetemp.ident = LittleLong (IDSPRITEHEADER);
  
  	SafeWrite (spriteouthandle, &spritetemp, sizeof(spritetemp));
  
  	if( do16bit )
  	{
  		// Write out palette in 16bit mode
  		short cnt = 256;
  		SafeWrite( spriteouthandle, (void *) &cnt, sizeof(cnt) );
  		SafeWrite( spriteouthandle, lbmpalette, cnt * 3 );
  	}
  
  //
  // write out the frames
  //
  	curframe = 0;
  
  	for (i=0 ; i<sprite.numframes ; i++)
  	{
  		SafeWrite (spriteouthandle, &frames[curframe].type,
  				   sizeof(frames[curframe].type));
  
  		if (frames[curframe].type == SPR_SINGLE)
  		{
  		//
  		// single (non-grouped) frame
  		//
  			WriteFrame (spriteouthandle, curframe);
  			curframe++;
  		}
  		else
  		{
  			int					j, numframes;
  			dspritegroup_t		dsgroup;
  			float				totinterval;
  
  			groupframe = curframe;
  			curframe++;
  			numframes = frames[groupframe].numgroupframes;
  
  		//
  		// set and write the group header
  		//
  			dsgroup.numframes = LittleLong (numframes);
  
  			SafeWrite (spriteouthandle, &dsgroup, sizeof(dsgroup));
  
  		//
  		// write the interval array
  		//
  			totinterval = 0.0;
  
  			for (j=0 ; j<numframes ; j++)
  			{
  				dspriteinterval_t	temp;
  
  				totinterval += frames[groupframe+1+j].interval;
  				temp.interval = LittleFloat (totinterval);
  
  				SafeWrite (spriteouthandle, &temp, sizeof(temp));
  			}
  
  			for (j=0 ; j<numframes ; j++)
  			{
  				WriteFrame (spriteouthandle, curframe);
  				curframe++;
  			}
  		}
  	}
  
  }
  
  
  /*
  ============
  ExecCommand
  ============
  */
  int	cmdsrun;
  
  void ExecCommand (char *cmd, ...)
  {
  	int		ret;
  	char	cmdline[1024];
  	va_list	argptr;
  	
  	cmdsrun++;
  	
  	va_start (argptr, cmd);
  	vsprintf (cmdline,cmd,argptr);
  	va_end (argptr);
  	
  //	printf ("=============================================================\n");
  //	printf ("spritegen: %s\n",cmdline);
  	fflush (stdout);
  	ret = system (cmdline);
  //	printf ("=============================================================\n");
  	
  	if (ret)
  		Error ("spritegen: exiting due to error");
  }
  
  /*
  ==============
  LoadScreen
  ==============
  */
  void LoadScreen (char *name)
  {
  	static byte origpalette[256*3];
  	int iError;
  
  	printf ("grabbing from %s...\n",name);
  	iError = LoadBMP( name, &byteimage, &lbmpalette );
  	if (iError)
  		Error( "unable to load file \"%s\"\n", name );
  
  	byteimagewidth = bmhd.w;
  	byteimageheight = bmhd.h;
  
  	if (sprite.numframes == 0)
  		memcpy( origpalette, lbmpalette, sizeof( origpalette ));
  	else if (memcmp( origpalette, lbmpalette, sizeof( origpalette )) != 0)
  	{
  		Error( "bitmap \"%s\" doesn't share a pallette with the previous bitmap\n", name );
  	}
  }
  
  
  /*
  ===============
  Cmd_Type
  ===============
  */
  void Cmd_Type (void)
  {
  	GetToken (false);
  	if (!strcmp (token, "vp_parallel_upright"))
  		sprite.type = SPR_VP_PARALLEL_UPRIGHT;
  	else if (!strcmp (token, "facing_upright"))
  		sprite.type = SPR_FACING_UPRIGHT;
  	else if (!strcmp (token, "vp_parallel"))
  		sprite.type = SPR_VP_PARALLEL;
  	else if (!strcmp (token, "oriented"))
  		sprite.type = SPR_ORIENTED;
  	else if (!strcmp (token, "vp_parallel_oriented"))
  		sprite.type = SPR_VP_PARALLEL_ORIENTED;
  	else
  		Error ("Bad sprite type\n");
  }
  
  
  /*
  ===============
  Cmd_Texture
  ===============
  */
  void Cmd_Texture (void)
  {
  	GetToken (false);
  
  	if (!strcmp (token, "additive"))
  		sprite.texFormat = SPR_ADDITIVE;
  	else if (!strcmp (token, "normal"))
  		sprite.texFormat = SPR_NORMAL;
  	else if (!strcmp (token, "indexalpha"))
  		sprite.texFormat = SPR_INDEXALPHA;
  	else if (!strcmp (token, "alphatest"))
  		sprite.texFormat = SPR_ALPHTEST;
  	else
  		Error ("Bad sprite texture type\n");
  }
  
  
  /*
  ===============
  Cmd_Beamlength
  ===============
  */
  void Cmd_Beamlength ()
  {
  	GetToken (false);
  	sprite.beamlength = atof (token);
  }
  
  
  /*
  ===============
  Cmd_Load
  ===============
  */
  void Cmd_Load (void)
  {
  	GetToken (false);
  	LoadScreen (ExpandPathAndArchive(token));
  }
  
  
  /*
  ===============
  Cmd_Frame
  ===============
  */
  void Cmd_Frame ()
  {
  	int             x,y,xl,yl,xh,yh,w,h;
  	byte            *screen_p, *source;
  	int             linedelta;
  	dspriteframe_t	*pframe;
  	int				pix;
  	
  	GetToken (false);
  	xl = atoi (token);
  	GetToken (false);
  	yl = atoi (token);
  	GetToken (false);
  	w = atoi (token);
  	GetToken (false);
  	h = atoi (token);
  
  	if ((xl & 0x07) || (yl & 0x07) || (w & 0x07) || (h & 0x07))
  		Error ("Sprite dimensions not multiples of 8\n");
  
  	if ((w > 256) || (h > 256))
  		Error ("Sprite has a dimension longer than 256");
  
  	xh = xl+w;
  	yh = yl+h;
  
  	pframe = (dspriteframe_t *)plump;
  	frames[framecount].pdata = pframe;
  	frames[framecount].type = SPR_SINGLE;
  
  	if (TokenAvailable ())
  	{
  		GetToken (false);
  		frames[framecount].interval = atof (token);
  		if (frames[framecount].interval <= 0.0)
  			Error ("Non-positive interval");
  	}
  	else
  	{
  		frames[framecount].interval = (float)0.1;
  	}
  	
  	if (TokenAvailable ())
  	{
  		GetToken (false);
  		pframe->origin[0] = -atoi (token);
  		GetToken (false);
  		pframe->origin[1] = atoi (token);
  	}
  	else
  	{
  		pframe->origin[0] = -(w >> 1);
  		pframe->origin[1] = h >> 1;
  	}
  
  	pframe->width = w;
  	pframe->height = h;
  
  	if (w > framesmaxs[0])
  		framesmaxs[0] = w;
  	
  	if (h > framesmaxs[1])
  		framesmaxs[1] = h;
  	
  	plump = (byte *)(pframe + 1);
  
  	screen_p = byteimage + yl*byteimagewidth + xl;
  	linedelta = byteimagewidth - w;
  
  	source = plump;
  
  	for (y=yl ; y<yh ; y++)
  	{
  		for (x=xl ; x<xh ; x++)
  		{
  			pix = *screen_p;
  			*screen_p++ = 0;
  //			if (pix == 255)
  //				pix = 0;
  			*plump++ = pix;
  		}
  		screen_p += linedelta;
  	}
  
  	framecount++;
  	if (framecount >= MAX_FRAMES)
  		Error ("Too many frames; increase MAX_FRAMES\n");
  }
  
  
  /*
  ===============
  Cmd_GroupStart	
  ===============
  */
  void Cmd_GroupStart (void)
  {
  	int			groupframe;
  
  	groupframe = framecount++;
  
  	frames[groupframe].type = SPR_GROUP;
  	frames[groupframe].numgroupframes = 0;
  
  	while (1)
  	{
  		GetToken (true);
  		if (endofscript)
  			Error ("End of file during group");
  
  		if (!strcmp (token, "$frame"))
  		{
  			Cmd_Frame ();
  			frames[groupframe].numgroupframes++;
  		}
  		else if (!strcmp (token, "$load"))
  		{
  			Cmd_Load ();
  		}
  		else if (!strcmp (token, "$groupend"))
  		{
  			break;
  		}
  		else
  		{
  			Error ("$frame, $load, or $groupend expected\n");
  		}
  
  	}
  
  	if (frames[groupframe].numgroupframes == 0)
  		Error ("Empty group\n");
  }
  
  
  /*
  ===============
  ParseScript	
  ===============
  */
  void ParseScript (void)
  {
  	while (1)
  	{
  		GetToken (true);
  		if (endofscript)
  			break;
  	
  		if (!strcmp (token, "$load"))
  		{
  			Cmd_Load ();
  		}
  		if (!strcmp (token, "$spritename"))
  		{
  			Cmd_Spritename ();
  		}
  		else if (!strcmp (token, "$type"))
  		{
  			Cmd_Type ();
  		}
  		else if (!strcmp (token, "$texture"))
  		{
  			Cmd_Texture ();
  		}
  		else if (!strcmp (token, "$beamlength"))
  		{
  			Cmd_Beamlength ();
  		}
  		else if (!strcmp (token, "$sync"))
  		{
  			sprite.synctype = ST_SYNC;
  		}
  		else if (!strcmp (token, "$frame"))
  		{
  			Cmd_Frame ();
  			sprite.numframes++;
  		}		
  		else if (!strcmp (token, "$load"))
  		{
  			Cmd_Load ();
  		}
  		else if (!strcmp (token, "$groupstart"))
  		{
  			Cmd_GroupStart ();
  			sprite.numframes++;
  		}
  	}
  }
  
  /*
  ==============
  Cmd_Spritename
  ==============
  */
  void Cmd_Spritename (void)
  {
  	if (sprite.numframes)
  		FinishSprite ();
  
  	GetToken (false);
  	sprintf (spriteoutname, "%s%s.spr", spritedir, token);
  	memset (&sprite, 0, sizeof(sprite));
  	framecount = 0;
  
  	framesmaxs[0] = -9999999;
  	framesmaxs[1] = -9999999;
  
  	if ( !lumpbuffer )
  		lumpbuffer = malloc (MAX_BUFFER_SIZE * 2);	// *2 for padding
  
  	if (!lumpbuffer)
  		Error ("Couldn't get buffer memory");
  
  	plump = lumpbuffer;
  	sprite.synctype = ST_RAND;	// default
  }
  
  /*
  ==============
  FinishSprite	
  ==============
  */
  void FinishSprite (void)
  {
  	FILE	*spriteouthandle;
  
  	if (sprite.numframes == 0)
  		Error ("no frames\n");
  
  	if (!strlen(spriteoutname))
  		Error ("Didn't name sprite file");
  		
  	if ((plump - lumpbuffer) > MAX_BUFFER_SIZE)
  		Error ("Sprite package too big; increase MAX_BUFFER_SIZE");
  
  	spriteouthandle = SafeOpenWrite (spriteoutname);
  	printf ("saving in %s\n", spriteoutname);
  	WriteSprite (spriteouthandle);
  	fclose (spriteouthandle);
  	
  	printf ("spritegen: successful\n");
  	printf ("%d frame(s)\n", sprite.numframes);
  	printf ("%d ungrouped frame(s), including group headers\n", framecount);
  	
  	spriteoutname[0] = 0;		// clear for a new sprite
  }
  
  /*
  ==============
  main
  	
  ==============
  */
  extern char qproject[];
  
  int main (int argc, char **argv)
  {
  	int		i;
  
  	printf( "sprgen.exe v 1.1 (%s)\n", __DATE__ );
  	printf ("----- Sprite Gen ----\n");
  
  	if (argc < 2 || argc > 7 )
  		Error ("usage: sprgen [-archive directory] [-no16bit] [-proj <project>] file.qc");
  		
  	for( i=1; i<argc; i++ )
  	{
  		if( *argv[ i ] == '-' )
  		{
  			if( !strcmp( argv[ i ], "-archive" ) )
  			{
  				archive = true;
  				strcpy (archivedir, argv[i+1]);
  				printf ("Archiving source to: %s\n", archivedir);
  				i++;
  			}
  			else if( !strcmp( argv[ i ], "-proj" ) )
  			{
  				strcpy( qproject, argv[i+1] );
  				i++;
  			}
  			else if( !strcmp( argv[ i ], "-16bit" ) )
  			{
  				do16bit = true;
  			}
  			else if( !strcmp( argv[ i ], "-no16bit" ) )
  			{
  				do16bit = false;
  			} 
  			else
  			{
  				printf( "Unsupported command line flag: '%s'", argv[i] );
  			}
  
  		}
  		else
  			break;
  	}
  
  	// SetQdirFromPath (argv[i]);
  	ExtractFilePath (argv[i], spritedir);	// chop the filename
  
  //
  // load the script
  //
  	LoadScriptFile (argv[i]);
  	
  	ParseScript ();
  	FinishSprite ();
  
  	return 0;
  }
  
  