/*
PMP Mod
Copyright (C) 2006 Raphael

E-mail:   raphael@fx-world.org

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
font rendering system
*/

#include <pspkernel.h>
#include <pspiofilemgr.h>
#include <pspgu.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#include "valloc.h"
#include "gu_font.h"



unsigned short __attribute__((aligned(16))) gu_font_clut[16];

struct gu_font_struct* gu_cur_font = 0;					// Current set font
static struct gu_font_manager_struct gu_font_manager;


static struct gu_glyph_cache_manager_struct gu_glyph_cache_manager;

static struct gu_glyph_cache_struct gu_glyph_temp_cache;		// temporary texture for bypassing glyph cache system


static int gu_font_initialized = 0;
static int gu_font_border = 1;
static int gu_font_color = 0xffffff;
static int gu_font_border_color = 0;


#define IsSet(val,flag) (val&flag)==flag

#define FONTHEIGHT (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)'I'].byte_height+3)
#define CHARWIDTH(c) (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)c].pixel_width)
#define CHARHEIGHT(c) (gu_cur_font==0?0:gu_cur_font->chars[(unsigned char)c].byte_height)


#ifdef DEBUG
void gu_debug_print_glyph_cache()
	{
	FILE* f = fopen("debug.txt","a+");
	if (!f) return;
	
	fprintf(f, "\nDEBUGPRINT GLYPH CACHE:\n");
	fprintf(f, "--------------------------------------------\n\n");
	
	struct gu_glyph_cache_list_struct* list = gu_glyph_cache_manager.root;
	if (list == 0) return;
	int i = 1;
	while (list!=0)
		{
			{
			fprintf(f, "----------------\n");
			fprintf(f, "GLYPH CACHE NO. %i\n", i);
			fprintf(f, "list->\n");
			fprintf(f, "----------------\n");
			fprintf(f, "lru_index      : %i\n", list->lru_index);
			fprintf(f, "cache->cacheptr: 0x%x\n", list->cache->cacheptr);
			fprintf(f, "cache->size    : 0x%x\n", list->cache->size);
			fprintf(f, "cache->width   : %i\n", list->cache->width);
			fprintf(f, "cache->height  : %i\n", list->cache->height);
			fprintf(f, "cache->flags   : %i\n", list->cache->flags);
			fprintf(f, "cache->font_id : %i\n", list->cache->font_id);
			fprintf(f, "cache->dirty   : %i\n", list->cache->dirty);
			fprintf(f, "cache->string  : %s\n", list->cache->string);
			fprintf(f, "----------------\n\n");
			}
		list = list->next;
		i++;
		}
	fprintf(f,"\n\n--------------------------------------------\nFINISHED\n");
	fclose(f);
	}
	

void gu_debug_print_charset()
{
	if (gu_cur_font == 0) return;
	FILE* f = fopen("debug.txt","a+");
	if (!f) return;
	
	fprintf(f, "\nDEBUGPRINT CHARSET:\n");
	fprintf(f, "--------------------------------------------\n\n");
	
	int i;
	char letter[2];
	letter[1] = '\0';
	for (i=0;i<256;i++)
		{
			{
			letter[0] = i;
			fprintf(f, "----------------\n");
			fprintf(f, "CHARACTER: %s(%i)\n", letter, i);
			fprintf(f, "----------------\n");
			fprintf(f, "byte_width     : %i\n", gu_cur_font->chars[i].byte_width);
			fprintf(f, "byte_height    : %i\n", gu_cur_font->chars[i].byte_height);
			fprintf(f, "x_offset       : %i\n", gu_cur_font->chars[i].x_offset);
			fprintf(f, "y_offset       : %i\n", gu_cur_font->chars[i].y_offset);
			fprintf(f, "pixel_width    : %i\n", gu_cur_font->chars[i].pixel_width);
			fprintf(f, "pixel_height   : %i\n", gu_cur_font->chars[i].pixel_height);
			}
		}
	fprintf(f,"\n\n--------------------------------------------\nFINISHED\n");
	fclose(f);
}
#endif

void gu_glyph_cache_safe_constructor(struct gu_glyph_cache_struct *p)
	{
	if (p==0) return;
	p->cacheptr = 0;
	p->width = 0;
	p->height = 0;
	p->flags = 0;
	p->string[0] = '\0';
	}


void gu_glyph_cache_safe_destructor(struct gu_glyph_cache_struct *p)
	{
	if (p==0) return;
	if (p->cacheptr!=0)
		vfree( p->cacheptr );
	p->cacheptr = 0;
	p->width = 0;
	p->height = 0;
	p->flags = 0;
	p->string[0] = '\0';
	p->dirty = 0;
	}

	
void gu_glyph_cache_list_safe_constructor(struct gu_glyph_cache_list_struct *p)
	{
	if (p==0) return;
	p->cache = 0;
	p->next = 0;
	p->prev = 0;
	p->lru_index = 0;
	}


void gu_glyph_cache_list_safe_destructor(struct gu_glyph_cache_list_struct *p)
	{
	if (p==0) return;
	gu_glyph_cache_list_safe_destructor(p->next);
	gu_glyph_cache_safe_destructor(p->cache);
	p->cache = 0;
	p->next = 0;
	p->prev = 0;
	p->lru_index = 0;
	}


struct gu_glyph_cache_struct* gu_glyph_cache_create( unsigned int size )
	{
	struct gu_glyph_cache_struct* p = malloc(sizeof(struct gu_glyph_cache_struct));
	if (p==0) return(0);
	
	gu_glyph_cache_safe_constructor(p);
	
	p->cacheptr = valloc( size );
	if (p->cacheptr==0)
		{
		free(p);
		return(0);
		}
	p->flags = GLYPH_CACHE_VRAM;
	p->size = size;
	p->dirty = 1;
	
	return(p);
	}


struct gu_glyph_cache_list_struct* gu_glyph_cache_list_create( unsigned int size )
	{
	struct gu_glyph_cache_list_struct *p = malloc(sizeof(struct gu_glyph_cache_list_struct));
	if (p==0) return(0);
	
	gu_glyph_cache_list_safe_constructor(p);
	
	p->cache = gu_glyph_cache_create( size );
	if (p->cache==0)
		{
		free(p);
		return(0);
		}
	
	return(p);
	}


char* gu_glyph_cache_init()
	{
	if (gu_font_initialized==1) return(0);
	
	gu_glyph_cache_manager.lru_counter = 0;
	gu_glyph_cache_manager.num_caches = 0;
	
	if (gu_glyph_cache_manager.root==0)
		gu_glyph_cache_manager.root = gu_glyph_cache_list_create( 256*MAX_CACHE_LINES*16 );
	
	if (gu_glyph_cache_manager.root==0)
		return("gu_glyph_cache_init: failed to create glyph cache root");
	
	gu_glyph_cache_manager.num_caches++;
	
	struct gu_glyph_cache_list_struct* new_cache;
	while(gu_glyph_cache_manager.num_caches<MAX_GLYPH_CACHES)
		{
		new_cache = gu_glyph_cache_list_create( 256*MAX_CACHE_LINES*16 );
		if (new_cache==0)
			break;
		new_cache->next = gu_glyph_cache_manager.root;
		gu_glyph_cache_manager.root->prev = new_cache;
		gu_glyph_cache_manager.root = new_cache;
		gu_glyph_cache_manager.num_caches++;
		}
	
	// Try to allocate temporary cache in VRAM first
	gu_glyph_cache_safe_constructor(&gu_glyph_temp_cache);
	//gu_glyph_temp_cache.cacheptr = valloc( 256*MAX_CACHE_LINES*16 );
	gu_glyph_temp_cache.flags = GLYPH_CACHE_VRAM;
	if (gu_glyph_temp_cache.cacheptr==0)
		{
		gu_glyph_temp_cache.cacheptr = memalign(16, 256*MAX_CACHE_LINES*16 );
		if (gu_glyph_temp_cache.cacheptr==0)
			return("gu_glyph_cache_init: memalign failed on gu_glyph_temp_cache");
		gu_glyph_temp_cache.flags = GLYPH_CACHE_SYSMEM;
		}
	gu_glyph_temp_cache.dirty = 1;		// temporary cache is always dirty
		
	return(0);
	}


void gu_glyph_cache_free()
	{
	if (IsSet(gu_glyph_temp_cache.flags,GLYPH_CACHE_VRAM))
		vfree( gu_glyph_temp_cache.cacheptr );
	else if (IsSet(gu_glyph_temp_cache.flags,GLYPH_CACHE_SYSMEM))
		free( gu_glyph_temp_cache.cacheptr );
		
	gu_glyph_temp_cache.flags = 0;
	gu_glyph_cache_list_safe_destructor( gu_glyph_cache_manager.root );
	gu_glyph_cache_manager.num_caches = 0;
	gu_glyph_cache_manager.lru_counter = 0;
	}


// return a pointer to a cache texture useable by the GU
struct gu_glyph_cache_struct* gu_glyph_cache_manager_get( char* s, int width, int height, int flags, int font_id )
	{
	if (IsSet(flags,FLAG_NOCACHE) || gu_glyph_cache_manager.num_caches==0)
		{
		if (gu_glyph_temp_cache.cacheptr==0) return(0);
		
		memset(gu_glyph_temp_cache.cacheptr, 0, 256*height);
		sceKernelDcacheWritebackAll();
		return(&gu_glyph_temp_cache);
		}
	
	// Check if a cache with s, flags and font_id already exists
	struct gu_glyph_cache_list_struct* list = gu_glyph_cache_manager.root;
	if (list == 0) return(0);
	while (list!=0)
		{
		if (list->cache->flags==(flags&FLAG_ALIGN_MASK) &&
			list->cache->font_id==font_id &&
			strcmp(list->cache->string,s)==0)
			{
			// cache-hit
			list->lru_index = gu_glyph_cache_manager.lru_counter++;
			list->cache->width = width;
			list->cache->height = height;
			list->cache->dirty = 0;			// cache doesn't need to be regenerated
			
			return(list->cache);
			}
		list = list->next;
		}
	
	list = gu_glyph_cache_manager.root;
	struct gu_glyph_cache_list_struct* lru = list;
	
	// cache-miss
	while (list->next!=0)
		{
		list = list->next;
		if (lru->lru_index>list->lru_index)
			lru = list;
		}
	
	lru->lru_index = gu_glyph_cache_manager.lru_counter++;
	if (lru->cache==0) return(0);
	lru->cache->width = width;
	lru->cache->height = height;
	lru->cache->dirty = 1;
	lru->cache->font_id = font_id;
	lru->cache->flags = (flags&FLAG_ALIGN_MASK);
	strncpy((char*)lru->cache->string,s,MAX_CACHE_STRING-1);
	memset(lru->cache->cacheptr, 0, 256*height );
	sceKernelDcacheWritebackAll();
	
	return(lru->cache);
	}




/* sw always 16 so there's always 64bit to copy for each line
		s1			s2
  s = |xxxxxxxx|yyyyyyyy|				sx = 0
  s1 << (3*4) = |000xxxxx|
  s1 >> (5*4) = |xxx00000|
  s2 << (3*4) = |000yyyyy|
  s2 >> (5*4) = |yyy00000|
  d = |00000000|00000000|00000000|		dx = 3
  => d' = |000xxxxx|xxxyyyyy|yyy00000|	
*/
inline void gu_font_copy_glyph( int sx, int sy, int sh, char *s, int dx, int dy, char *d )
{
	int i;
	if ((dx&0x7)==0) {
		unsigned int* u32s = (unsigned int*)((unsigned int)s+((sx+(sy<<8)) >> 1));
		unsigned int* u32d = (unsigned int*)((unsigned int)d+((dx+(dy<<9)) >> 1));

		// can do fast copy
		for (i=0;i<sh;i++) {
		  // 32bit copy part
		  *u32d++ = *u32s++;
		  *u32d++ = *u32s++;
		  u32s += (128-8)>>2;
		  u32d += (256-8)>>2;
		}
	} else {

	unsigned int* u32s = (unsigned int*)((unsigned int)s+((sx+(sy<<8)) >> 1));
	unsigned int* u32d = (unsigned int*)((unsigned int)d+(((dx>>3)<<2)+(dy<<8)));

	unsigned int mask = 0;
	unsigned int shift = (dx&0x7)<<2;
	for (i=0;i<(dx&0x7);i++) {
		mask <<= 4;	
		mask |= 0xf;
	}

	for (i=0;i<sh;i++) {
	  unsigned int s1 = *u32s++;
	  unsigned int s2 = *u32s++;
	  
	  // copy first halfbytes
	  *u32d++ = ((*u32d) & mask)|(s1 << shift);	// dst = |000xxxxx|
	  
	  *u32d++ = (s1 >> (32-shift)) | (s2 << shift);	// dst = |000xxxxx|xxxyyyyy|
	  
	  // copy last halfbytes
	  *u32d++ = ((*u32d) & ~mask)|(s2 >> (32-shift));		// dst = |000xxxxx|xxxyyyyy|yyy00000|
	  
	  u32s += (128-8)>>2;
	  u32d += (256-12)>>2;
	}

	}
}


inline void gu_font_cache_glyph( int x, int y, char c, char* cache )
	{
	int sx = (int)((unsigned char)c % 16)*16;
	int sy = (int)((unsigned char)c / 16)*16;
	int sw = gu_cur_font->chars[(unsigned char)c].pixel_width;
	int sh = gu_cur_font->chars[(unsigned char)c].byte_height;
	int dx = x+gu_cur_font->chars[(unsigned char)c].x_offset;
	int dy = y+gu_cur_font->chars[(unsigned char)c].y_offset;
	if (dy<0) { sy-=dy; sh+=dy; dy=0; }
	if (dx<0) { sx-=dx; sw+=dx; dx=0; }
	if (dy+sh>=272) { sh=271-dy; }
	//if (dx+sw>=480) { sw=479-dx; }
	//sw = (sw+3)>>2;
	if (sh==0 || sw==0) return;
	/*if ((dx&0x3)==0)
	  sceGuCopyImage(GU_PSM_4444,sx>>2,sy,sw,sh,256>>2,g_font_tex[g_cur_font],dx>>2,dy,512>>2,(void*)((unsigned int)g_glyph_cache | 0x40000000));
	else*/
	  gu_font_copy_glyph( sx, sy, sh, (char*)gu_cur_font->data, dx, dy, (char*)((unsigned int)cache | 0x40000000) );
	}


inline void blit_fast( int x, int y, int w, int h, int x2, int y2 )
{
		int start;
		for (start = 0; start < w; start += 64, x2 += 64)
		{
			struct VertexInt* vertices = (struct VertexInt*)sceGuGetMemory(2 * sizeof(struct VertexInt));
			int width = (start + 64) < w ? 64 : w-start;

			vertices[0].u = x + start; vertices[0].v = y;
			vertices[0].color = 0x3000;
			vertices[0].x = x2; vertices[0].y = y2; vertices[0].z = 0;

			vertices[1].u = x + start + width; vertices[1].v = y + h;
			vertices[1].color = 0x3000;
			vertices[1].x = x2 + width; vertices[1].y = y2 + h; vertices[1].z = 0;

			sceGuDrawArray(GU_SPRITES,GU_TEXTURE_16BIT|GU_COLOR_4444|GU_VERTEX_16BIT|GU_TRANSFORM_2D,2,0,vertices);
		}
}


int strpos( const char* s, const char c )
	{
	if (s==0) return -1;
	char* t = s;
	int i = 0;
	while (*t!='\0' && *t!=c)
		{
		t++;
		i++;
		}
	
	if (*t!=c) return -1;
	return i;
	}


void gu_font_cache_string( char* s, int maxwidth, int flags, char* cache )
	{
	char* c = s;
	int i = 0;
	int x = 0;
	int y = 10;	// don't know why this needs to be, with 0 it will draw the first line off-screen
	int linesize;
	int numlines = 0;
		
	if (IsSet(flags,FLAG_ALIGN_CENTER))
		{
			x = ((maxwidth-gu_font_line_width_get( s))/2);
		}
	else if (IsSet(flags,FLAG_ALIGN_RIGHT))
		{
		//linesize = strpos( s, '\n' );
		//if (linesize>0)
			x = (maxwidth-gu_font_line_width_get( s ));
		}


	int maxlines = (MAX_CACHE_LINES*16)/FONTHEIGHT;
	while (*c!='\0' && i<MAX_CACHE_STRING && numlines<=maxlines) {
			if (*c==' ')
				{
				x += CHARWIDTH('t');
				c++;
				}
			else if (*c=='\n')
				{
				numlines++;
				c++;
				x = 0;
				if (IsSet(flags,FLAG_ALIGN_CENTER))
					{
					//linesize = strpos( c, '\n' );
						x = ((maxwidth-gu_font_line_width_get( c))/2);
					}
				else if (IsSet(flags,FLAG_ALIGN_RIGHT))
					{
					//linesize = strpos( c, '\n' );
						x = (maxwidth-gu_font_line_width_get( c));
					}
				y += FONTHEIGHT;
				}
			else if ((unsigned char)*c>=32)
				{
				gu_font_cache_glyph( x, y, *c, cache );
				x += CHARWIDTH(*c);
				c++;
				}
			
			if (IsSet(flags,FLAG_CLIP_WRAP))
				if (x>=472)
					{
					numlines++;
					x = 0;
					y += FONTHEIGHT;
					}

			i++;
		}

	}




void gu_font_safe_constructor(struct gu_font_struct *f)
	{
	if (f==0) return;
	f->name[0] = '\0';
	f->id = -1;
	f->data = 0;
	f->d_width = 0;
	f->d_height = 0;
	memset( f->chars, 0, 256*sizeof(struct gu_char_struct) );
	}
	

void gu_font_safe_destructor(struct gu_font_struct *f)
	{
	if (f==0) return;
	if (f->data!=0) free(f->data);
	f->data = 0;
	}
	


void gu_font_list_safe_constructor(struct gu_font_list_struct *f)
	{
	if (f==0) return;
	f->font = 0;
	f->next = 0;
	}
	

void gu_font_list_safe_destructor(struct gu_font_list_struct *f)
	{
	if (f==0) return;
	gu_font_safe_destructor(f->font);
	f->next = 0;
	}


char* gu_font_init()
	{
	if (gu_font_initialized==1) return(0);
	char* result = gu_glyph_cache_init();
	if (result!=0)
		return(result);
	
	// Create a white color CLUT usable by GU
	gu_font_color_set( 0xFFFFFF );
	gu_font_border_color_set( 0x000000 );	// Black border
	gu_font_initialized = 1;
	
	return(0);
	}
	

void gu_font_close()
	{
	if (gu_font_initialized==0) return;
	gu_glyph_cache_free();
	gu_font_free_all();
	gu_font_initialized=0;
	}
	

struct gu_font_struct* gu_font_find( char* name )
	{
	struct gu_font_list_struct *f = gu_font_manager.list;

	while (f!=0)
		{
		if (f->font!=0)
			{
			if (strcmp( f->font->name, name)==0)
				return(f->font);
			}
		f = f->next;
		}
	
	return(0);
	}


struct gu_font_list_struct* gu_font_list_remove( char* name )
	{
	struct gu_font_list_struct *f = gu_font_manager.list;
	if (f==0) return(0);
	
	if (f->font!=0)
		if (strcmp( f->font->name, name)==0)
			{
			gu_font_manager.list = f->next;
			gu_font_manager.num_fonts--;
			return(f);
			}

	while (f->next!=0)
		{
		if (f->next->font!=0)
			{
			if (strcmp( f->next->font->name, name)==0)
				{
				f->next = f->next->next;
				gu_font_manager.num_fonts--;
				return(f->next);
				}
			}
		f = f->next;
		}
	
	return(0);
	}


char* gu_font_load( char* name )
	{
	struct gu_font_struct* f = gu_font_find( name );
	if (f!=0)
		{
		gu_cur_font = f;
		return(0);
		}
	
		
	SceUID	fd;
	if (!(fd = sceIoOpen( name, PSP_O_RDONLY, 0777)))
		return("gu_font_load: Could not open file");
	
	f = malloc(sizeof(struct gu_font_struct));
	if (f==0)
		{
		sceIoClose(fd);
		return("gu_font_load: malloc failed on f");
		}
	
	gu_font_safe_constructor( f );
	strncpy(f->name,name,MAX_NAME_SIZE-1);
	f->id = gu_font_manager.id_counter++;
	f->d_width = 128;
	f->d_height = 256;
	
	struct gu_font_list_struct* new_list = malloc(sizeof(struct gu_font_list_struct));
	if (new_list==0)
		{
		free(f);
		sceIoClose(fd);
		return("gu_font_load: malloc failed on new_list");
		}
	
	gu_font_list_safe_constructor(new_list);
	
	new_list->font = f;
	new_list->next = gu_font_manager.list;
	
	f->data = memalign( 16, 256*128 );
	if (f->data==0)
		{
		free(f);
		free(new_list);
		sceIoClose(fd);
		return("gu_font_load: memalign failed on f->data");
		}
	
	struct gu_font_header_struct header;
	sceIoRead( fd,&header,sizeof(struct gu_font_header_struct) );
	/*if (header.ID != FOURCC('G','U','F','1'))
		{
		free(f->data);
		free(f);
		free(new_list);
		sceIoClose(fd);
		return("gu_font_load: wrong filetype");
		}
	*/

	sceIoRead( fd,f->chars,sizeof(struct gu_char_struct)*256 );
	sceIoRead( fd,f->data,128*256 );
	sceIoClose( fd );
	
	sceKernelDcacheWritebackAll();
	gu_font_manager.num_fonts++;
	gu_font_manager.list = new_list;
	gu_cur_font = f;

	return(0);
	}


void gu_font_free( char* name )
	{
	struct gu_font_list_struct* f = gu_font_list_remove( name );
	gu_font_list_safe_destructor( f );
	}
	

void gu_font_free_all()
	{
	struct gu_font_list_struct* f = gu_font_manager.list;
	struct gu_font_list_struct* t = 0;
	
	while (f!=0)
		{
		t = f->next;
		gu_font_list_safe_destructor(f);
		f = t;
		}
	gu_font_manager.list = 0;
	gu_font_manager.num_fonts = 0;
	gu_font_manager.id_counter = 0;
	}


void gu_font_set( char* name )
	{
	struct gu_font_struct* f = gu_font_find( name );
	if (f!=0)
		gu_cur_font = f;
	}


void* gu_font_texture_get( char* name )
	{
	struct gu_font_struct* f = gu_font_find( name );
	if (f==0) return(0);
	return(f->data);
	}


void gu_font_border_enable( int enable )
	{
	if (gu_font_border==enable) return;
	gu_font_border = enable;
	if (enable)
		{
		gu_font_border_color_set( gu_font_border_color );
		gu_font_color_set( gu_font_color );
		}
	else
		{
		unsigned short* clut = (unsigned short*)(((unsigned int)gu_font_clut)|0x40000000);
		int i;
		for (i=0;i<8;i++)
			*(clut++) = 0x0;
		for (i=8;i<16;i++)
			*(clut++) = (i << 12) | BGR444(gu_font_color);
		}
	}
	

void gu_font_border_color_set( int color )
	{
	gu_font_border_color = color;
	if (gu_font_border==0) return;

	unsigned short* clut = (unsigned short*)(((unsigned int)gu_font_clut)|0x40000000);
	int i;
	*(clut++) = 0x0;		// Transparent parts always black
	for (i=1;i<8;i++)
		*(clut++) = (((i << 1)+1) << 12) | BGR444(color);
	}
	

void gu_font_color_set( int color )
	{
	gu_font_color = color;
	
	unsigned short* clut = (unsigned short*)(((unsigned int)gu_font_clut)|0x40000000);
	int i;
	clut+=8;
	if (gu_font_border==0)
		{
		for (i=8;i<16;i++)
			*(clut++) = (i << 12) | BGR444(color);
		}
	else
		{
		for (i=8;i<16;i++)
			*(clut++) = (15 << 12) | BGR444_blend(color,i);
		}
	}


int gu_font_line_width_get( char* s )
	{
	if (s==0 || gu_cur_font==0) return 0;
	char* c = s;
	int x = 0;

	while (*c!='\0' && *c!='\n')
		{
		if (*c==' ')
			{
			x += CHARWIDTH('t');
			}
		else if ((unsigned char)*c>32)
			{
			x += CHARWIDTH(*c);
			}
		c++;
		}
	return (x);
	}
	

int gu_font_width_get( char* s, int flag )
	{
	if (s==0 || gu_cur_font==0) return 0;
	char* c = s;
	int x = 0;
	int width = 0;
	if (flag == 0) flag--;
	
	while (*c!='\0' && flag!=0)
		{
		if (*c==' ')
			{
			flag--;
			x += CHARWIDTH('t');
			}
		else if (*c=='\n')
			{
			if (x>width) width=x;
			x = 0;
			}
		else if ((unsigned char)*c>32)
			{
			flag--;
			x += CHARWIDTH(*c);
			}
		c++;
		}
	return (x>width?x:width);
	}
	

int gu_font_height_get( char* s )
	{
	if (s == 0 || gu_cur_font==0) return 0;
	char* c = s;
	int height = FONTHEIGHT;
	
	while (*c!='\0')
		{
		if (*c=='\n')
			{
			height += FONTHEIGHT;
			}
		c++;
		}
	return (height);
	}
	

inline int gu_font_height()
	{
	return FONTHEIGHT;
	}
	

inline int gu_char_width_get( char c )
	{
	return CHARWIDTH(c);
	}
	

inline int gu_char_height_get( char c )
	{
	return CHARHEIGHT(c);
	}


void gu_font_printf( int x, int y, int flags, char* fmt, ... )
	{
	if (gu_cur_font==0) return;
	
	va_list         ap;
	char   p[512];

	va_start( ap,fmt );
	vsnprintf( p,512,fmt,ap );
	va_end( ap );

	if (p[0]=='\0') return;
	
	int width = gu_font_width_get( p, 0 );
	int height = gu_font_height_get( p );

	struct gu_glyph_cache_struct* gu_drawbuffer = gu_glyph_cache_manager_get( p, width, height, flags, gu_cur_font->id );
	if (gu_drawbuffer==0) return;

	if (gu_drawbuffer->cacheptr==0) return;
	
	if (gu_drawbuffer->dirty==1)
		{
		gu_font_cache_string( p, width, flags, gu_drawbuffer->cacheptr );
		}
		

	// string is now in cache, so just draw
	sceGuClutMode(GU_PSM_4444,0,0xff,0); // 16-bit palette
	sceGuClutLoad((16/8),gu_font_clut); // upload 2*8 entries (16)
	sceGuTexMode(GU_PSM_T4,0,0,0); // 4-bit image, but unswizzled
	sceGuTexImage(0,512,MAX_CACHE_LINES*16,512,gu_drawbuffer->cacheptr);
	sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
	sceGuTexFilter(GU_NEAREST,GU_NEAREST);
	sceGuEnable( GU_BLEND );

	if (IsSet(flags,FLAG_SHADOW))
		{
		sceGuTexFunc(GU_TFX_MODULATE,GU_TCC_RGBA);
		blit_fast( 0, 0, width, height, x+2, y+2 );
		}
	
	sceGuTexFunc(GU_TFX_REPLACE,GU_TCC_RGBA);
	blit_fast( 0, 0, width, height, x, y );
	}
	
