#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include "kernel.h"
#include "swis.h"

#define Font_EnumerateCharacters 0x400A9
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define SGN(a) ((a)>0?1:((a)<0?-1:0))
#define SHIFTUP(a,s) (((a)+(1<<(s))-1)>>(s))

/* TODO:
   - Reorder arguments to be more unixy, or remove unnecesary - from options
   - Option to pad sprites to be 4 pixels, etc. wide?
   - Fix background sprite alignment to be same for all vertical alignments (i.e. align around 0,0 font coords)
   - Fix colours for <256 colour sprites - it seems to do it around palette entries, instead of colour tables? Need to supply ColourTrans with a colour table to use, or find closest GCOL? etc. Background sprite colours are dodgy too. Also need to fix anti-aliasing for 256 colour sprites.
     - use ReadPalette to get the palette of the sprite, then WritePalette to set the desktop palette to it?
   - Phrase support - simplifies the task of rendering a phrase in a certain style (i.e. using one large background image for the whole lot, ala DeathDawn logo)
   - Independent control over x/y pt size
   - Choice of background colour, auto-removal of mask if no transparent bits used
     - Will require a bit of a rework for the background sprite gubbins - maybe create a masked sprite how it currently does it, then paste that sprite onto a sprite containing just the background colour? Or write own code to go through and replace the transparent pixels with coloured ones (but won't be a catch-all solution - i.e. won't work if the user wants transparent text)
   - Change to spool sprites to disc one at a time, to allow for really big sprites
   - Crazy scripting support - ability to specify complex combinations of fonts, draw paths, sprites using a textfile to produce more complex output?
   - Fix mask for 256 colour sprites - should be 1 byte per pixel, not 1 bit per pixel?
   - Fix eigen values - they are related to desktop eigens?
*/

typedef struct {
	float width;
	int col;
} outline;

/* Args */
char *fontname=0; /* Font name */
char *outname=0; /* Output file */
float pt=20; /* Point size of font */
#define OUTLINE_MAX 16
outline outlines[OUTLINE_MAX]; /* Outlines */
int num_outlines=0; /* Number used */
int fill=-1; /* Fill colour */
char *back=0; /* Background sprite name */
int fixedw=0,fixedh=0; /* Fixed width, height flags */
int padt=0,padl=0,padb=0,padr=0; /* Padding */
int xeig=1,yeig=1; /* Eigen values */
int cols=5; /* Output colour depth (log2) */
int aal_depth=0; /* Anti-aliasing depth (palette entries) (for non-outline, non-backspr fonts) */
int aal_col=-1; /* Anti-aliasing colour (for non-outline, non-backspr fonts) */
int max=-1; /* Max char to export (inclusive, -1=none) */
int spacew=0; /* Space width */
int findbbox=0; /* Examine sprites to find bbox */
int append=0; /* Append to output file */
char *range=0; /* Range of codes to output */

/* Working vars */
_kernel_swi_regs regs;
_kernel_oserror *err=0;
int font_handle=0;
int font_w,font_h; /* Width, height of largest character, including padding */
int font_ofs_x,font_ofs_y; /* Offset for rendering, if fixed width */
int sprite_size; /* size of each sprite (bytes) */
int font_chars; /* Count of how many characters there are */
int *sprite_file; /* Sprite file */
#define DRAWBUFFER_SIZE 16384
int draw_buffer[DRAWBUFFER_SIZE]; /* Drawfile buffer */
int *back_file=0; /* Background sprite file */
int *back_sprite=0; /* Sprite within the file */
_kernel_swi_regs screen_regs; /* For redirecting screen output to sprite */
int screen_redirected=0; /* Flag for tracking */
int font_redirected=0; /* Flag for tracking */
int seigx,seigy; /* Screen eigen values for current screen mode - since we (currently) need them for the initial size calculation */
char errorbuf[8192]; /* For non-fatal errors */

void error(char *msg,...)
{
	va_list a;
	if(font_redirected)
	{
		regs.r[0] = 0;
		regs.r[1] = 0;
		_kernel_swi(Font_SwitchOutputToBuffer,&regs,&regs);
	}
	if(screen_redirected)
		_kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs);
	va_start(a,msg);
	fprintf(stderr,"\nError: ");
	vfprintf(stderr,msg,a);
	if(err)
		fprintf(stderr," (error %d, %s)\n",err->errnum,err->errmess);
	else
		fprintf(stderr,"\n");
	if(font_handle != 0)
	{
		regs.r[0] = font_handle;
		_kernel_swi(Font_LoseFont,&regs,&regs);
	}
	exit(EXIT_FAILURE);
}

void errorlog(char *msg,...)
{
	va_list a;
	if(strlen(errorbuf) > sizeof(errorbuf)-1024)
		return;
	va_start(a,msg);
	sprintf(errorbuf+strlen(errorbuf),"\nWarning: ");
	vsprintf(errorbuf+strlen(errorbuf),msg,a);
	if(err)
		sprintf(errorbuf+strlen(errorbuf)," (error %d, %s)",err->errnum,err->errmess);
}

int inrange(int c)
{
	if(!range)
		return 1;
	char *r = range;
	int min=0,max=-1;
	int i;
	while((i = *r++))
	{
		if((i >= '0') && (i <= '9'))
		{
			if(max == -1)
				min = min*10+i-'0';
			else
				max = max*10+i-'0';
		}
		else if(i == ',')
		{
			if((max > 0) && (c >= min) && (c < max))
				return 1;
			if(c == min)
				return 1;
			min = 0;
			max = -1;
		}
		else if(i == '-')
			max = 0;
		else
			error("Bad range string");
	}
	if((max > 0) && (c >= min) && (c < max))
		return 1;
	if(c == min)
		return 1;
	return 0;
}

void fontinate(int c)
{
	int char_w,char_h; /* Character width, height */
	int char_ofs_x,char_ofs_y; /* Render offsets */
	int *sprite_ptr; /* Current sprite ptr */
	int str[2]; /* String (UCS4 support) */
	str[0] = c;
	str[1] = 0;
	/* Fontinate the given character */
	/* Operation:
	   1. Get character size
	   2. Create sprite
	   3. Clear to transparent colour
	   4. Redirect screen output to sprite
	   5. If not using background, paint font in correct colour, else paint background sprite everywhere
	   6. Redirect output to mask
	   7. Paint font in white
	   8. If outlining:
	   8.1 Convert letter to Draw path
	   8.2 Manipulate path to get correct thickness, no fill, etc.
	   8.3 Render draw path to sprite in white
	   8.4 Redirect output to sprite again
	   8.5 Render draw path to sprite in correct colour
	*/
	regs.r[0] = font_handle;
	regs.r[1] = c;
	regs.r[2] = 1<<4; /* Get size in OS units */
	if((fixedw>=0)||(fixedh>=0))
	{
		if(!findbbox)
		{
			if((err = _kernel_swi(Font_CharBBox,&regs,&regs)))
				error("Couldn't get size of character %d",c);
		}
		else
		{
			/* Find the bbox ourselves */
			int *temp_spr;
			temp_spr = (int *) malloc(16+44+font_w*font_h*4);
			if(!temp_spr)
				error("Couldn't allocate %d bytes for bbox sprite file",16+44+font_w*font_h*4);
			memset(temp_spr,0,16+44+font_w*font_h*4);
			temp_spr[0] = 16+44+font_w*font_h*4;
			temp_spr[1] = 1;
			temp_spr[2] = 16;
			temp_spr[3] = temp_spr[0];
			temp_spr[4] = 44+font_w*font_h*4;
			sprintf((char *) (&temp_spr[5]),"%d",c);
			temp_spr[8] = font_w-1;
			temp_spr[9] = font_h-1;
			temp_spr[10] = 0;
			temp_spr[11] = 31;
			temp_spr[12] = 44;
			temp_spr[13] = 44;
			temp_spr[14] = 1+((180>>xeig)<<1)+((180>>yeig)<<14)+(6<<27);
			/* Redirect screen output to sprite */
			screen_regs.r[0] = 60+512;
			screen_regs.r[1] = (int) temp_spr;
			screen_regs.r[2] = (int) (&temp_spr[4]);
			screen_regs.r[3] = 0;
			if((err = _kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs)))
				error("Couldn't redirect screen output to sprite for character %d bbox",c);
			screen_redirected = 1;
			/* Paint the char */
			regs.r[0] = font_handle;
			regs.r[1] = 0xFFFFFF00;
			regs.r[2] = 0xFFFFFF00;
			regs.r[3] = 0;
			_kernel_swi(ColourTrans_SetFontColours,&regs,&regs);
			/* Paint letter */
			regs.r[0] = font_handle;
			regs.r[1] = (int) str;
			regs.r[2] = (1<<4)+(1<<8);
			if(c > 255)
				regs.r[2] |= 1<<13; /* UCS4 */
			regs.r[3] = -font_ofs_x;
			regs.r[4] = -font_ofs_y;
			if((err = _kernel_swi(Font_Paint,&regs,&regs)))
				errorlog("Couldn't paint character %d to sprite for bbox",c);
			/* Switch output back to screen */
			_kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs);
			screen_redirected = 0;
			/* Examine to get bbox */
			regs.r[1] = font_w;
			regs.r[2] = font_h;
			regs.r[3] = 0;
			regs.r[4] = 0;
			for(char_h=0;char_h<font_h;char_h++)
				for(char_w=0;char_w<font_w;char_w++)
					if(temp_spr[15+char_w+(font_h-(char_h+1))*font_w])
					{
						if(char_w < regs.r[1])
							regs.r[1] = char_w;
						if(char_w > regs.r[3])
							regs.r[3] = char_w;
						if(char_h < regs.r[2])
							regs.r[2] = char_h;
						if(char_h > regs.r[4])
							regs.r[4] = char_h;
					}
			if(regs.r[3] < regs.r[1])
				regs.r[3] = (regs.r[1]+1) << xeig;
			else
				regs.r[3] = (regs.r[3]+1) << xeig;
			if(regs.r[4] < regs.r[2])
				regs.r[4] = (regs.r[2]+1) << yeig;
			else
				regs.r[4] = (regs.r[4]+1) << yeig;
			regs.r[1] = regs.r[1] << xeig;
			regs.r[2] = regs.r[2] << yeig;
			regs.r[1] += font_ofs_x;
			regs.r[2] += font_ofs_y;
			regs.r[3] += font_ofs_x;
			regs.r[4] += font_ofs_y;
			free(temp_spr);
		}
	}
	if(fixedw)
	{
		char_w = font_w;
		if(fixedw<0)
			char_ofs_x = font_ofs_x;
		else
			char_ofs_x = (-padl << xeig)+regs.r[1];
	}
	else
	{
		char_w = padl+padr+SHIFTUP(regs.r[3],xeig)-SHIFTUP(regs.r[1],xeig);
		char_ofs_x = (-padl << xeig)+regs.r[1];
	}
	if((c == 32) && (spacew))
		char_w = spacew;
	if(fixedh)
	{
		char_h = font_h;
		if(fixedh<0)
			char_ofs_y = font_ofs_y;
		else
			char_ofs_y = (-padb << yeig)+regs.r[2];
		if(fixedh==-2)
		{
			char_h -= SHIFTUP(((-padb << yeig)+regs.r[2])-char_ofs_y,yeig);
			char_ofs_y = (-padb << yeig)+regs.r[2];
		}
	}
	else
	{
		char_h = padt+padb+SHIFTUP(regs.r[4],yeig)-SHIFTUP(regs.r[2],yeig);
		char_ofs_y = (-padb << yeig)+regs.r[2];
	}
	char_ofs_x = -char_ofs_x;
	char_ofs_y = -char_ofs_y;
	if((char_w > font_w) || (char_h > font_h))
		fprintf(stderr,"Warning: Char %d is bigger than expected! Got size %dx%d\n",c,char_w,char_h);
	if(char_w > font_w)
		char_w = font_w;
	if(char_h > font_h)
		char_h = font_h;
	if(char_w < 1)
		char_w = 1;
	if(char_h < 1)
		char_h = 1;
	/* Create and clear sprite */
	sprite_ptr = (int *) (((int) sprite_file)+sprite_file[3]);
	regs.r[0] = 15+512;
	regs.r[1] = 
	sprite_file[1]++;
	sprite_ptr[0] = 44+SHIFTUP(char_w,5-cols)*char_h*4;
	sprite_ptr[0] += SHIFTUP(char_w,5)*char_h*4;
	sprite_file[3] += sprite_ptr[0];
	if(sprite_file[3] > sprite_file[0])
		error("Sprite file got bigger than expected while converting character %d",c);
	sprite_ptr[1] = sprite_ptr[2] = sprite_ptr[3] = 0;
	sprintf((char *) (&sprite_ptr[1]),"%d",c);
	sprite_ptr[4] = SHIFTUP(char_w,5-cols)-1;
	sprite_ptr[5] = char_h-1;
	sprite_ptr[6] = 0;
	sprite_ptr[7] = ((char_w<<cols)-1)&31;
	sprite_ptr[8] = 44;
	sprite_ptr[9] = 44+(sprite_ptr[4]+1)*(sprite_ptr[5]+1)*4;
	sprite_ptr[10] = 1+((180>>xeig)<<1)+((180>>yeig)<<14)+((cols+1)<<27);
	memset(&sprite_ptr[11],0,sprite_ptr[0]-44);
	/* Redirect screen output to sprite */
	screen_regs.r[0] = 60+512;
	screen_regs.r[1] = (int) sprite_file;
	screen_regs.r[2] = (int) sprite_ptr;
	screen_regs.r[3] = 0;
	if((err = _kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs)))
		error("Couldn't redirect screen output to sprite for character %d",c);
	screen_redirected = 1;
	/* If using background, fill with background sprite */
	if(back_sprite)
	{
		int w,h,x,y;
		regs.r[0] = 40+512;
		regs.r[1] = (int) back_file;
		regs.r[2] = (int) back_sprite;
		if((err = _kernel_swi(OS_SpriteOp,&regs,&regs)))
			error("Couldn't read size of background sprite");
		w = regs.r[3];
		h = regs.r[4];
		/* Just fill it from top-left for now */
		for(y=0;y<(char_h << yeig);y+=(h << yeig))
			for(x=0;x<(char_w << xeig);x+=(w << xeig))
			{
				regs.r[0] = 52+512;
				regs.r[1] = (int) back_file;
				regs.r[2] = (int) back_sprite;
				regs.r[3] = x;
				regs.r[4] = y;
				regs.r[5] = 16;
				regs.r[6] = 0;
				regs.r[7] = 0;
				if((err = _kernel_swi(OS_SpriteOp,&regs,&regs)))
					error("Couldn't plot background sprite into char %d",c);
			}
	}
	else if(fill != -1)
	{
		/* Else paint font, if we have a fill colour */
		/* set colour */
		regs.r[0] = font_handle;
		if(aal_depth != 0)
			regs.r[1] = aal_col<<8; /* enable anti-aliasing */
		else
			regs.r[1] = fill<<8;
		regs.r[2] = fill<<8; /* Fill colour if we're outlining */
		regs.r[3] = aal_depth;
		_kernel_swi(ColourTrans_SetFontColours,&regs,&regs);
		/* Paint letter */
		regs.r[0] = font_handle;
		regs.r[1] = (int) str;
		regs.r[2] = (1<<4)+(1<<8);
		if(c > 255)
			regs.r[2] |= 1<<13; /* UCS4 */
		regs.r[3] = char_ofs_x;
		regs.r[4] = char_ofs_y;
		if((err = _kernel_swi(Font_Paint,&regs,&regs)))
			errorlog("Couldn't paint character %d to sprite",c);
	}
	/* Redirect to mask */
	_kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs);
	screen_redirected = 0;
	screen_regs.r[0] = 61+512;
	screen_regs.r[1] = (int) sprite_file;
	screen_regs.r[2] = (int) sprite_ptr;
	screen_regs.r[3] = 0;
	if((err = _kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs)))
		error("Couldn't redirect screen output to mask for character %d",c);
	screen_redirected = 1;
	/* Plot font if required */
	if(back_sprite || (fill != -1))
	{
		/* set colour */
		regs.r[0] = font_handle;
		regs.r[1] = 0xFFFFFF00;
		regs.r[2] = 0xFFFFFF00;
		regs.r[3] = 0;
		_kernel_swi(ColourTrans_SetFontColours,&regs,&regs);
		/* Paint letter */
		regs.r[0] = font_handle;
		regs.r[1] = (int) str;
		regs.r[2] = (1<<4)+(1<<8);
		if(c > 255)
			regs.r[2] |= 1<<13; /* UCS4 */
		regs.r[3] = char_ofs_x;
		regs.r[4] = char_ofs_y;
		if((err = _kernel_swi(Font_Paint,&regs,&regs)))
			errorlog("Couldn't paint character %d to mask",c);
	}
	/* If outlining... */
	if (num_outlines)
	{
		/* Convert to draw path */
		/* First, we insert a drawfile header into the draw_buffer */
		draw_buffer[0] = 0x77617244; /* "Draw" */
		draw_buffer[1] = 201;
		draw_buffer[2] = 0;
		sprintf((char *) (&draw_buffer[3]),"fontinator  ");
		draw_buffer[6] = draw_buffer[7] = 0x7FFFFFFF;
		draw_buffer[8] = draw_buffer[9] = 0x80000000;
		/* Now the header for the font manager */
		draw_buffer[10] = 0;
		draw_buffer[11] = DRAWBUFFER_SIZE-40;
		regs.r[0] = 1<<4;
		regs.r[1] = (int) (&draw_buffer[10]);
		if((err = _kernel_swi(Font_SwitchOutputToBuffer,&regs,&regs)))
			error("Couldn't switch font output to buffer for character %d outline",c);
		font_redirected = 1;
		/* set colour (irrelevant, but it complains if you don't) */
		regs.r[0] = font_handle;
		regs.r[1] = regs.r[2] = 0xFFFFFF00;
		regs.r[3] = 0;
		_kernel_swi(ColourTrans_SetFontColours,&regs,&regs);
		/* Paint letter */
		regs.r[0] = font_handle;
		regs.r[1] = (int) str;
		regs.r[2] = (1<<4)+(1<<8);
		if(c > 255)
			regs.r[2] |= 1<<13; /* UCS4 */
		regs.r[3] = char_ofs_x;
		regs.r[4] = char_ofs_y;
		if((err = _kernel_swi(Font_Paint,&regs,&regs)))
			errorlog("Couldn't paint character %d to draw buffer",c);
		/* Switch font output back */
		regs.r[0] = 0;
		regs.r[1] = 0;
		_kernel_swi(Font_SwitchOutputToBuffer,&regs,&regs);
		font_redirected = 0;
		if(!err)
		{
			/* Now process the buffer, changing it to a white outline, ready for mask painting
			   According to the stronghelp manuals, we should only get given filled path objects - so only process those
			   Additionally, regs.r[1] should be the terminator of the list */
			int i,size,j;
			size = regs.r[1]-((int) draw_buffer);
			i = 10;
			while (draw_buffer[i] && draw_buffer[i+1] && (4*i < size))
			{
				if(draw_buffer[i] == 2)
				{
					draw_buffer[i+6] = -1; /* No fill */
					draw_buffer[i+7] = 0xFFFFFF00; /* White line */
					draw_buffer[i+8] = outlines[0].width*0x280; /* Outline width */
					draw_buffer[6] = MIN(draw_buffer[6],draw_buffer[i+2]); /* Update file bbox */
					draw_buffer[7] = MIN(draw_buffer[7],draw_buffer[i+3]);
					draw_buffer[8] = MAX(draw_buffer[8],draw_buffer[i+4]);
					draw_buffer[9] = MAX(draw_buffer[9],draw_buffer[i+5]);
				}
				i += draw_buffer[i+1];
			}
			/* Now render the buffer to the sprite mask */
			regs.r[0] = 0;
			regs.r[1] = (int) draw_buffer;
			regs.r[2] = size;
			regs.r[3] = 0;
			regs.r[4] = 0;
			if((err = _kernel_swi(DrawFile_Render,&regs,&regs)))
				errorlog("Couldn't render outline to mask for character %d",c);
			/* Now switch output to the sprite again */
			_kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs);
			screen_redirected = 0;
			screen_regs.r[0] = 60+512;
			screen_regs.r[1] = (int) sprite_file;
			screen_regs.r[2] = (int) sprite_ptr;
			screen_regs.r[3] = 0;
			if((err = _kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs)))
				error("Couldn't redirect screen output to sprite for character %d",c);
			screen_redirected = 1;
			/* Now for each outline, plot the colour into the sprite */
			for(j=0;j<num_outlines;j++)
			{
				/* Process draw_buffer again, setting correct outline colour */
				i = 10;
				while (draw_buffer[i] && draw_buffer[i+1] && (4*i < size))
				{
					if(draw_buffer[i] == 2)
					{
						draw_buffer[i+7] = outlines[j].col << 8;
						draw_buffer[i+8] = outlines[j].width*0x280;
					}
					i += draw_buffer[i+1];
				}
				/* Now render the buffer to the sprite */
				regs.r[0] = 0;
				regs.r[1] = (int) draw_buffer;
				regs.r[2] = size;
				regs.r[3] = 0;
				regs.r[4] = 0;
				if((err = _kernel_swi(DrawFile_Render,&regs,&regs)))
					errorlog("Couldn't render outline %d to sprite for character %d",j,c);
			}
		}
	}
	/* Ye gods, are we done?
	   Switch output back to screen and return */
	_kernel_swi(OS_SpriteOp,&screen_regs,&screen_regs);
	screen_redirected = 0;
}   

int main(int argc,char **argv)
{
	int a,c;
	if (argc < 3)
	{
		printf("fontinator 0.10\n\
Usage:\n\
fontinator <fontname> <outname> [options]\n\
Converts the RISC OS font fontname to a set of masked sprites in file outname.\n\
Options are as follows:\n\
-pt=<n>		Size of font, in points (default 20)\n\
-fill=<bbggrr>	Fill colour for font (default black if no fill sprite or\n\
		outline; transparent if there is a fill sprite/outline)\n\
-aal=<n>,<bbggrr>	Anti-aliasing depth (0-14) and colour. (default is no\n\
			anti-aliasing; option has no effect if an outline or\n\
			background sprite is used)\n\
-back=<sprfile>	Spritefile to use as the background (interior) of outline fonts\n\
-ol=<width>,<bbggrr>	Add an outline to the list of outlines to render.\n\
-fixedw[=<n>]	Produce fixed-width font (default off)\n\
-fixedh[=<n>]	Produce fixed-height font (default off)\n\
-pad[tlbr]=<n>	Pad top/left/bottom/right of each letter with <n> pixels\n\
		(default 0)\n\
-pad=<n>	Pad all sides with <n> pixels (default 0)\n\
-max=<n>	Max character code to export (inclusive; default no limit)\n\
-xeig=<n>	X eigen value of output sprite (default=1, i.e. 90dpi)\n\
-yeig=<n>	Y eigen value of output sprite (default=1, i.e. 90dpi)\n\
-bpp=<n>	Colour depth of sprites, in bpp (1,2,4,8,16,24) (default 24)\n\
-spacew=<n>	Width of space character\n\
-findbbox	Find character bounding boxes manually (for broken font files)\n\
-alignv		Produce variable-height font, but with chars aligned vertically\n\
-append		Append sprites to the <outname> spritefile\n\
-range=<x>	Range of character codes to export - e.g. -range=48-58,64-92\n\
		for just numbers and upper-case letters\n");
		return EXIT_SUCCESS;
	}
	/* Taskwindow detection */
	regs.r[0] = 0;
	_kernel_swi(TaskWindow_TaskInfo,&regs,&regs);
	if(regs.r[0] != 0)
	{
		fprintf(stderr,"Warning: Running from a taskwindow is not supported. Will attempt to launch as a new task instead...\n");
		/* Glue our args together */
		char argbuf[4096];
		char *outfile,*errfile;
		FILE *f;
		argbuf[0] = 0;
		for(a=0;a<argc;a++)
		{
			strcat(argbuf,argv[a]);
			strcat(argbuf," ");
		}
		/* Add stdout/stderr redirection */
		strcat(argbuf,"> ");
		outfile = argbuf+strlen(argbuf);
		tmpnam(outfile);
		strcat(argbuf," 2> ");
		errfile = argbuf+strlen(argbuf);
		tmpnam(errfile);
	/*	fprintf(stderr,"Launching with args: %s\n",argbuf); */
		regs.r[0] = (int) argbuf;
		_kernel_swi(Wimp_StartTask,&regs,&regs);
	/*	if(regs.r[0] == 0)
			fprintf(stderr,"Execution complete!\n");
		else
			fprintf(stderr,"Hmm, we seem to have launched a new WIMP task. This wasn't part of the plan...\n");
		fprintf(stderr,"Child stdout and stderr spool follows\n"); */
		f = fopen(outfile,"r");
		while((f) && (!feof(f)))
		{
			c = fgetc(f);
			if(c != -1)
				putc(c,stdout);
		}
		if(f)
		{
			fclose(f);
			remove(outfile);
		}
		f = fopen(errfile,"r");
		while((f) && (!feof(f)))
		{
			c = fgetc(f);
			if(c != -1)
				putc(c,stderr);
		}
		if(f)
		{
			fclose(f);
			remove(errfile);
		}
		return EXIT_SUCCESS;
	}
	setvbuf(stdout,0,_IONBF,0);
	setvbuf(stderr,0,_IONBF,0);
	fontname = argv[1];
	outname = argv[2];
	for(a=3;a<argc;a++)
	{
		if(!strncmp(argv[a],"-pt=",4))
			pt = atof(argv[a]+4);
		else if(!strncmp(argv[a],"-ol=",4))
		{
			if(num_outlines == OUTLINE_MAX)
				fprintf(stderr,"Warning: Too many outlines defined to add any more\n");
			else if(sscanf(argv[a]+4,"%f,%06x",&outlines[num_outlines].width,&outlines[num_outlines].col) != 2)
				fprintf(stderr,"Warning: Unrecognised option '%s'\n",argv[a]);
			else
				num_outlines++;
		}
		else if(!strncmp(argv[a],"-fill=",6))
			sscanf(argv[a]+6,"%06x",&fill);
		else if(!strncmp(argv[a],"-aal=",5))
		{
			if(sscanf(argv[a]+5,"%d,%06x",&aal_depth,&aal_col) != 2)
				fprintf(stderr,"Warning: Unrecognised option '%s'\n",argv[a]);
		}
		else if(!strncmp(argv[a],"-back=",6))
			back = argv[a]+6;
		else if(!strcmp(argv[a],"-fixedw"))
			fixedw = -1;
		else if(!strcmp(argv[a],"-fixedh"))
			fixedh = -1;
		else if(!strncmp(argv[a],"-fixedw=",8))
			fixedw = atoi(argv[a]+8);
		else if(!strncmp(argv[a],"-fixedh=",8))
			fixedh = atoi(argv[a]+8);
		else if(!strncmp(argv[a],"-padt=",6))
			padt = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-padl=",6))
			padl = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-padb=",6))
			padb = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-padr=",6))
			padr = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-pad=",5))
			padt = padl = padb = padr = atoi(argv[a]+5);
		else if(!strncmp(argv[a],"-max=",5))
			max = atoi(argv[a]+5);
		else if(!strncmp(argv[a],"-xeig=",6))
			xeig = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-yeig=",6))
			yeig = atoi(argv[a]+6);
		else if(!strncmp(argv[a],"-bpp=",5))
			cols = atoi(argv[a]+5);
		else if(!strncmp(argv[a],"-spacew=",8))
			spacew = atoi(argv[a]+8);
		else if(!strncmp(argv[a],"-findbbox",9))
			findbbox = 1;
		else if(!strncmp(argv[a],"-alignv",7))
			fixedh = -2;
		else if(!strncmp(argv[a],"-append",7))
			append = 1;
		else if(!strncmp(argv[a],"-range=",7))
			range = argv[a]+7;
		else
			fprintf(stderr,"Warning: Unrecognised option '%s'\n",argv[a]);
	}
	if(cols == 24)
		cols = 5;
	if(cols != 5)
		cols = log(cols)/log(2); /* Should work well enough! */
	if((num_outlines == 0) && (back == 0) && (fill == -1))
		fill = 0; /* Black fill if none specified */
	/* If we're outlining, add the line width onto the pad amounts */
	if(num_outlines > 0)
	{
		a = ceil(outlines[0].width);
		padt += a;
		padl += a;
		padb += a;
		padr += a;
	}
	/* Need to:
	   1. Get font handle
	   2. Calculate size of sprite file, allocate memory (assuming fixed width)
	   3. Load background image if needed
	   4. Process each character
	   5. Save output
	*/
	/* get font handle */
	regs.r[1] = (int) fontname;
	regs.r[2] = regs.r[3] = pt*16;
	regs.r[4] = 180 >> xeig;
	regs.r[5] = 180 >> yeig;
	if((err = _kernel_swi(Font_FindFont,&regs,&regs)))
		error("Couldn't find font");
	font_handle = regs.r[0];
	/* Calculate size of spritefile needed (assuming fixed width, and ignoring outline) */
	/* First we need to get the current screen eigen values - because those are the ones that Font_ReadInfo will have scaled the font for. Not an ideal situation, but it shouldn't result in any serious problems */
	regs.r[0] = -1;
	regs.r[1] = 4;
	_kernel_swi(OS_ReadModeVariable,&regs,&regs);
	seigx = regs.r[2];
	regs.r[1] = 5;
	_kernel_swi(OS_ReadModeVariable,&regs,&regs);
	seigy = regs.r[2];
	/* Now we can get the font size, and translate that to pixels */
	regs.r[0] = font_handle;
	_kernel_swi(Font_ReadInfo,&regs,&regs);
	if(fixedw > 0)
		font_w = fixedw;
	else
		font_w = padl+padr+SHIFTUP(regs.r[3],seigx)-SHIFTUP(regs.r[1],seigx);
	if(fixedh > 0)
		font_h = fixedh;
	else
		font_h = padt+padb+SHIFTUP(regs.r[4],seigy)-SHIFTUP(regs.r[2],seigy);
	font_ofs_x = (-padl << seigx)+regs.r[1];
	font_ofs_y = (-padb << seigy)+regs.r[2];
	/* Calculate size of each sprite */
	sprite_size = 44+SHIFTUP(font_w,5-cols)*font_h*4;
	sprite_size += SHIFTUP(font_w,5)*font_h*4;
	/* Count how many letters there are */
	font_chars = 0;
	regs.r[1] = 32;
	while (regs.r[1] != -1)
	{
		regs.r[0] = font_handle;
		c = regs.r[1];
		if((err = _kernel_swi(Font_EnumerateCharacters,&regs,&regs)))
			error("Problem counting characters!");
		if((regs.r[2] != -1) && ((max == -1) || (c <= max)) && inrange(c))
			font_chars++;
	}
	printf("Font is %dx%d, %d chars\n",font_w,font_h,font_chars);
	/* Are we appending? */
	int appendsize = 16;
	FILE *f=0;
	if(append)
	{
		f = fopen(outname,"rb");
		if(f)
		{
			fseek(f,0,SEEK_END);
			appendsize = 4+ftell(f);
			if(appendsize < 16)
				error("File to append to doesn't seem to be valid");
		}
		else
			printf("Can't open file to append to - will just write instead");
	}
	/* Alloc sprite file */
	sprite_file = (int *) malloc(appendsize+sprite_size*font_chars);
	if(!sprite_file)
		error("Couldn't allocate %d bytes for sprite file",appendsize+sprite_size*font_chars);
	sprite_file[0] = appendsize+sprite_size*font_chars;
	sprite_file[1] = 0;
	sprite_file[2] = 16;
	sprite_file[3] = 16;
	/* Load append file if needed */
	if(appendsize > 16)
	{
		fseek(f,0,SEEK_SET);
		fread(&(sprite_file[1]),appendsize-4,1,f);
	}
	if(f)
		fclose(f);
	/* Load background image if needed */
	if(back)
	{
		regs.r[0] = 17;
		regs.r[1] = (int) back;
		if((err = _kernel_swi(OS_File,&regs,&regs)) || (regs.r[0] != 1))
			error("Couldn't load background sprite");
		back_file = (int *) malloc(regs.r[4]+4);
		if(!back_file)
			error("Couldn't allocate %d bytes for background sprite",regs.r[4]+4);
		back_file[0] = regs.r[4]+4;
		regs.r[0] = 10+256;
		regs.r[1] = (int) back_file;
		regs.r[2] = (int) back;
		if((err = _kernel_swi(OS_SpriteOp,&regs,&regs)))
			error("Couldn't load background sprite");
		if(back_file[1] == 0)
			error("Background sprite file empty!");
		back_sprite = (int *) (((int) back_file)+back_file[2]);
	}
	/* Process font! */
	regs.r[1] = 32;
	while (regs.r[1] != -1)
	{
		regs.r[0] = font_handle;
		c = regs.r[1];
		_kernel_swi(Font_EnumerateCharacters,&regs,&regs);
		a = regs.r[1]; /* Allow reuse of regs */
		if((regs.r[2] != -1) && ((max == -1) || (c <= max)) && inrange(c))
		{
			errorbuf[0] = 0;
			fontinate(c);
			printf("%s.",errorbuf);
		}
		regs.r[1] = a;
	}
	printf("\n");
	/* Save sprite file */
	regs.r[0] = 12+256;
	regs.r[1] = (int) sprite_file;
	regs.r[2] = (int) outname;
	if((err = _kernel_swi(OS_SpriteOp,&regs,&regs)))
		error("Couldn't save sprites");
	/* Free font handle */
	regs.r[0] = font_handle;
	_kernel_swi(Font_LoseFont,&regs,&regs);
	/* Done! */
	return EXIT_SUCCESS;
}
