packages feed

tcod-haskell-0.1.0.0: libtcod/src/console_c.c

/*
* libtcod 1.6.3
* Copyright (c) 2008,2009,2010,2012,2013,2016,2017 Jice & Mingos & rmtew
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * The name of Jice or Mingos may not be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JICE, MINGOS AND RMTEW ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JICE, MINGOS OR RMTEW BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <console.h>

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifndef NO_UNICODE
#include <wchar.h>
#include <wctype.h>
#endif

#include <noise.h>
#include <mersenne.h>
#include <libtcod_int.h>
#include <libtcod_utility.h>
#include <libtcod_version.h>

#ifdef TCOD_CONSOLE_SUPPORT

#if defined( TCOD_VISUAL_STUDIO )
static const char *version_string ="libtcod "TCOD_STRVERSION;
#else
static const char *version_string __attribute__((unused)) ="libtcod "TCOD_STRVERSION;
#endif

TCOD_internal_context_t TCOD_ctx={
	/* number of characters in the bitmap font */
	16,16,
	/* font type and layout */
	false,false,false,
	/* character size in font */
	8,8,
	"terminal.png","",
	NULL,NULL,NULL,0,false,0,0,0,0,0,0,
#ifdef TCOD_SDL2
	/* default renderer to use */
	TCOD_RENDERER_GLSL,
	NULL,
#endif
	/* fading data */
	{0,0,0},255,
	/*key state*/
	{0},
	/* window closed ? */
	false,
	/* mouse focus ? */ 
	false,
	/* application active ? */
	true,
};

static TCOD_color_t color_control_fore[TCOD_COLCTRL_NUMBER];
static TCOD_color_t color_control_back[TCOD_COLCTRL_NUMBER];

void TCOD_console_set_color_control(TCOD_colctrl_t con, TCOD_color_t fore, TCOD_color_t back) {
	TCOD_IFNOT(con >= TCOD_COLCTRL_1 && con <= TCOD_COLCTRL_NUMBER ) return;
	color_control_fore[con-1]=fore;
	color_control_back[con-1]=back;
}

TCOD_console_t TCOD_console_new(int w, int h)  {
	TCOD_IFNOT(w > 0 && h > 0 ) {
		return NULL;
	} else {
		TCOD_console_data_t *con=(TCOD_console_data_t *)calloc(sizeof(TCOD_console_data_t),1);
		con->w=w;
		con->h=h;
		TCOD_console_init(con,NULL,false);
		if(TCOD_ctx.root) {
			con->alignment=TCOD_ctx.root->alignment;
			con->bkgnd_flag=TCOD_ctx.root->bkgnd_flag;
		}
		return (TCOD_console_t)con;
	}
}

TCOD_key_t TCOD_console_check_for_keypress(int flags) {
	return TCOD_sys_check_for_keypress(flags);
}

TCOD_key_t TCOD_console_wait_for_keypress(bool flush) {
	return TCOD_sys_wait_for_keypress(flush);
}

bool TCOD_console_is_window_closed(void) {
	return TCOD_ctx.is_window_closed;
}

bool TCOD_console_has_mouse_focus(void) {
	return TCOD_ctx.app_has_mouse_focus;
}

#ifdef TCOD_SDL2
bool TCOD_console_is_active(void) {
	return TCOD_ctx.app_is_active;
}
#endif

void TCOD_console_set_window_title(const char *title) {
	TCOD_sys_set_window_title(title);
}

void TCOD_console_set_fullscreen(bool fullscreen) {
	TCOD_IFNOT(TCOD_ctx.root != NULL) return;
	TCOD_sys_set_fullscreen(fullscreen);
	TCOD_ctx.fullscreen=fullscreen;
}

bool TCOD_console_is_fullscreen(void) {
	return TCOD_ctx.fullscreen;
}

void TCOD_console_set_background_flag(TCOD_console_t con,TCOD_bkgnd_flag_t flag) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	dat->bkgnd_flag=flag;
}

TCOD_bkgnd_flag_t TCOD_console_get_background_flag(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return TCOD_BKGND_NONE;
	return dat->bkgnd_flag;
}

void TCOD_console_set_alignment(TCOD_console_t con,TCOD_alignment_t alignment) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	dat->alignment=alignment;
}

TCOD_alignment_t TCOD_console_get_alignment(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return TCOD_LEFT;
	return dat->alignment;
}

static void TCOD_console_data_free(TCOD_console_data_t *dat) {
	if (dat->fg_colors) TCOD_image_delete(dat->fg_colors);
	if (dat->bg_colors) TCOD_image_delete(dat->bg_colors);
	free(dat->ch_array);
}

void TCOD_console_delete(TCOD_console_t con) {
    TCOD_console_data_t *dat=(TCOD_console_data_t *)(con);
	if (! dat ) {
		dat=TCOD_ctx.root;
		TCOD_sys_uninit();
		TCOD_ctx.root=NULL;
	}
	TCOD_console_data_free(dat);
	free(dat);
}

void TCOD_console_blit(TCOD_console_t srcCon, int xSrc, int ySrc, int wSrc, int hSrc,
	TCOD_console_t dstCon, int xDst, int yDst, float foreground_alpha, float background_alpha) {
	TCOD_console_data_t *src = srcCon ? (TCOD_console_data_t *)srcCon : TCOD_ctx.root;
	TCOD_console_data_t *dst = dstCon ? (TCOD_console_data_t *)dstCon : TCOD_ctx.root;
	TCOD_color_t *srcFgColors, *srcBgColors, *dstFgColors, *dstBgColors;
	bool srcHasKeyColor;
	TCOD_color_t srcKeyColor;
	int cx, cy;
	if (wSrc == 0) wSrc = src->w;
	if (hSrc == 0) hSrc = src->h;
	TCOD_IFNOT(wSrc > 0 && hSrc > 0) return;
	TCOD_IFNOT(xDst + wSrc >= 0 && yDst + hSrc >= 0 && xDst < dst->w && yDst < dst->h) return;
	TCOD_image_get_key_data(src->bg_colors, &srcHasKeyColor, &srcKeyColor);
	srcFgColors = TCOD_image_get_colors(src->fg_colors);
	srcBgColors = TCOD_image_get_colors(src->bg_colors);
	dstFgColors = TCOD_image_get_colors(dst->fg_colors);
	dstBgColors = TCOD_image_get_colors(dst->bg_colors);
	for (cx = xSrc; cx < xSrc + wSrc; cx++) {
		for (cy = ySrc; cy < ySrc + hSrc; cy++) {
			/* check if we're outside the dest console */
			int dx = cx - xSrc + xDst;
			int dy = cy - ySrc + yDst;
			int dst_idx = dy * dst->w + dx;
			int src_idx = cy * src->w + cx;
			int srcChar, dstChar;
			TCOD_color_t srcFgColor, srcBgColor, dstFgColor, dstBgColor;
			if ((unsigned)cx >= (unsigned)src->w || (unsigned)cy >= (unsigned)src->h) continue;
			if ((unsigned)dx >= (unsigned)dst->w || (unsigned)dy >= (unsigned)dst->h) continue;
			srcChar = src->ch_array[src_idx];
			srcFgColor = srcFgColors[src_idx];
			srcBgColor = srcBgColors[src_idx];
			/* check if source pixel is transparent */
			if (srcHasKeyColor &&
				srcBgColor.r == srcKeyColor.r && srcBgColor.g == srcKeyColor.g && srcBgColor.b == srcKeyColor.b)
				continue;

			if (foreground_alpha == 1.0f && background_alpha == 1.0f) {
				dstChar = srcChar;
				dstFgColor = srcFgColor;
				dstBgColor = srcBgColor;
			}
			else {
				dstChar = dst->ch_array[dst_idx];
				dstFgColor = dstFgColors[dst_idx];
				dstBgColor = dstBgColors[dst_idx];

				dstBgColor = TCOD_color_lerp(dstBgColor, srcBgColor, background_alpha);
				if (srcChar == ' ') {
					dstFgColor = TCOD_color_lerp(dstFgColor, srcBgColor, background_alpha);
				}
				else if (dstChar == ' ') {
					dstChar = srcChar;
					dstFgColor = TCOD_color_lerp(dstBgColor, srcFgColor, foreground_alpha);
				}
				else if (dstChar == srcChar) {
					dstFgColor = TCOD_color_lerp(dstFgColor, srcFgColor, foreground_alpha);
				}
				else {
					if (foreground_alpha < 0.5f) {
						dstFgColor = TCOD_color_lerp(dstFgColor, dstBgColor,
							foreground_alpha * 2);
					}
					else {
						dstChar = srcChar;
						dstFgColor = TCOD_color_lerp(dstBgColor, srcFgColor,
							(foreground_alpha - 0.5f) * 2);
					}
				}
			}
			dstFgColors[dst_idx] = dstFgColor;
			dstBgColors[dst_idx] = dstBgColor;
			dst->ch_array[dst_idx] = dstChar;
		}
	}
	TCOD_image_invalidate_mipmaps(dst->fg_colors);
	TCOD_image_invalidate_mipmaps(dst->bg_colors);
}

void TCOD_console_flush(void) {
	TCOD_IFNOT(TCOD_ctx.root != NULL) return;
	TCOD_sys_flush(true);
}

void TCOD_console_set_fade(uint8_t val, TCOD_color_t fadecol) {
	TCOD_ctx.fade=val;
	TCOD_ctx.fading_color=fadecol;
}

uint8_t TCOD_console_get_fade(void) {
	return TCOD_ctx.fade;
}

TCOD_color_t TCOD_console_get_fading_color(void) {
	return TCOD_ctx.fading_color;
}

void TCOD_console_put_char(TCOD_console_t con, int x, int y, int c, TCOD_bkgnd_flag_t flag) {
	int offset;
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL && (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h) return;
	TCOD_IFNOT(c >= 0 && c < TCOD_ctx.max_font_chars) return;
	offset = y * dat->w + x;
	dat->ch_array[offset] = c;
	TCOD_image_put_pixel(dat->fg_colors, x, y, dat->fore);
	TCOD_console_set_char_background(con, x, y, dat->back, (TCOD_bkgnd_flag_t)flag);
}

void TCOD_console_put_char_ex(TCOD_console_t con, int x, int y, int c, TCOD_color_t fore, TCOD_color_t back) {
	int offset;
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL && (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h) return;
	TCOD_IFNOT(c >= 0 && c < TCOD_ctx.max_font_chars) return;
	offset = y * dat->w + x;
	dat->ch_array[offset] = c;
	TCOD_image_put_pixel(dat->fg_colors, x, y, fore);
	TCOD_image_put_pixel(dat->bg_colors, x, y, back);
}

void TCOD_console_set_dirty(int dx, int dy, int dw, int dh) {
	TCOD_sys_set_dirty(dx, dy, dw, dh);
}

void TCOD_console_clear(TCOD_console_t con) {
	int i;
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	for (i = 0; i < dat->w * dat->h; i++) {
		dat->ch_array[i] = ' ';
	}
	TCOD_image_clear(dat->fg_colors, dat->fore);
	TCOD_image_clear(dat->bg_colors, dat->back);
	/* clear the sdl renderer cache */
	TCOD_sys_set_dirty(0, 0, dat->w, dat->h);
}

TCOD_color_t TCOD_console_get_char_background(TCOD_console_t con, int x, int y) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h)
		return TCOD_black;
	return TCOD_image_get_pixel(dat->bg_colors, x, y);
}

void TCOD_console_set_char_foreground(TCOD_console_t con, int x, int y, TCOD_color_t col) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	if ((unsigned)(x) >= (unsigned)dat->w || (unsigned)(y) >= (unsigned)dat->h) return;
	TCOD_IFNOT(dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h)
		return;
	TCOD_image_put_pixel(dat->fg_colors, x, y, col);
}

TCOD_color_t TCOD_console_get_char_foreground(TCOD_console_t con, int x, int y) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h)
		return TCOD_white;
	return TCOD_image_get_pixel(dat->fg_colors, x, y);
}

int TCOD_console_get_char(TCOD_console_t con, int x, int y) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h)
		return 0;
	return dat->ch_array[y * dat->w + x];
}

void TCOD_console_set_char_background(TCOD_console_t con, int x, int y, TCOD_color_t col, TCOD_bkgnd_flag_t flag) {
	TCOD_color_t *back;
	int newr, newg, newb;
	int alpha;
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h)
		return;
	back = &(TCOD_image_get_colors(dat->bg_colors)[y*dat->w + x]);
	if (flag == TCOD_BKGND_DEFAULT) flag = dat->bkgnd_flag;
	switch (flag & 0xff) {
	case TCOD_BKGND_SET: *back = col; break;
	case TCOD_BKGND_MULTIPLY: *back = TCOD_color_multiply(*back, col); break;
	case TCOD_BKGND_LIGHTEN:
		back->r = MAX(back->r, col.r);
		back->g = MAX(back->g, col.g);
		back->b = MAX(back->b, col.b);
		break;
	case TCOD_BKGND_DARKEN:
		back->r = MIN(back->r, col.r);
		back->g = MIN(back->g, col.g);
		back->b = MIN(back->b, col.b);
		break;
	case TCOD_BKGND_SCREEN:
		/* newbk = white - (white - oldbk) * (white - curbk) */
		back->r = (uint8_t)(255 - (int)(255 - back->r)*(255 - col.r) / 255);
		back->g = (uint8_t)(255 - (int)(255 - back->g)*(255 - col.g) / 255);
		back->b = (uint8_t)(255 - (int)(255 - back->b)*(255 - col.b) / 255);
		break;
	case TCOD_BKGND_COLOR_DODGE:
		/* newbk = curbk / (white - oldbk) */
		if (back->r != 255) newr = (int)(255 * col.r) / (255 - back->r);
		else newr = 255;
		if (back->g != 255) newg = (int)(255 * col.g) / (255 - back->g);
		else newg = 255;
		if (back->b != 255) newb = (int)(255 * col.b) / (255 - back->b);
		else newb = 255;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_COLOR_BURN:
		/* newbk = white - (white - oldbk) / curbk */
		if (col.r > 0) newr = 255 - (int)(255 * (255 - back->r)) / col.r;
		else newr = 0;
		if (col.g > 0) newg = 255 - (int)(255 * (255 - back->g)) / col.g;
		else newg = 0;
		if (col.b > 0) newb = 255 - (int)(255 * (255 - back->b)) / col.b;
		else newb = 0;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_ADD:
		/* newbk = oldbk + curbk */
		newr = (int)(back->r) + col.r;
		newg = (int)(back->g) + col.g;
		newb = (int)(back->b) + col.b;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_ADDA:
		alpha = (flag >> 8);
		/* newbk = oldbk + alpha * curbk */
		newr = (int)(back->r) + alpha * col.r / 255;
		newg = (int)(back->g) + alpha * col.g / 255;
		newb = (int)(back->b) + alpha * col.b / 255;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_BURN:
		/* newbk = oldbk + curbk - white */
		newr = (int)(back->r) + col.r - 255;
		newg = (int)(back->g) + col.g - 255;
		newb = (int)(back->b) + col.b - 255;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_OVERLAY:
		/* newbk = curbk.x <= 0.5 ? 2*curbk*oldbk : white - 2*(white-curbk)*(white-oldbk) */
		newr = col.r <= 128 ? 2 * (int)(col.r) * back->r / 255 : 255 - 2 * (int)(255 - col.r)*(255 - back->r) / 255;
		newg = col.g <= 128 ? 2 * (int)(col.g) * back->g / 255 : 255 - 2 * (int)(255 - col.g)*(255 - back->g) / 255;
		newb = col.b <= 128 ? 2 * (int)(col.b) * back->b / 255 : 255 - 2 * (int)(255 - col.b)*(255 - back->b) / 255;
		back->r = (uint8_t)CLAMP(0, 255, newr);
		back->g = (uint8_t)CLAMP(0, 255, newg);
		back->b = (uint8_t)CLAMP(0, 255, newb);
		break;
	case TCOD_BKGND_ALPH:
		/* newbk = (1.0f-alpha)*oldbk + alpha*(curbk-oldbk) */
		alpha = (flag >> 8);
		*back = TCOD_color_lerp(*back, col, (float)(alpha / 255.0f));
		break;
	default: break;
	}
}

void TCOD_console_set_char(TCOD_console_t con, int x, int y, int c) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	if ((unsigned)(x) >= (unsigned)dat->w || (unsigned)(y) >= (unsigned)dat->h) return;
	dat->ch_array[y * dat->w + x] = c;
}

static void TCOD_console_clamp(int cx, int cy, int cw, int ch, int *x, int *y, int *w, int *h) {
	if (*x + *w > cw) *w = cw - *x;
	if (*y + *h > ch) *h = ch - *y;
	if (*x < cx) {
		*w -= cx - *x;
		*x = cx;
	}
	if (*y < cy) {
		*h -= cy - *y;
		*y = cy;
	}
}

void TCOD_console_rect(TCOD_console_t con, int x, int y, int rw, int rh, bool clear, TCOD_bkgnd_flag_t flag) {
	int cx, cy;
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	TCOD_ASSERT((unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h);
	TCOD_ASSERT(x + rw <= dat->w && y + rh <= dat->h);

	TCOD_console_clamp(0, 0, dat->w, dat->h, &x, &y, &rw, &rh);
	TCOD_IFNOT(rw > 0 && rh > 0) return;
	for (cx = x; cx < x + rw; cx++) {
		for (cy = y; cy<y + rh; cy++) {
			TCOD_console_set_char_background(con, cx, cy, dat->back, flag);
			if (clear) {
				dat->ch_array[cx + cy*dat->w] = ' ';
			}
		}
	}
}

void TCOD_console_hline(TCOD_console_t con,int x,int y, int l, TCOD_bkgnd_flag_t flag) {
	int i;
	for (i=x; i< x+l; i++) TCOD_console_put_char(con,i,y,TCOD_CHAR_HLINE,flag);
}

void TCOD_console_vline(TCOD_console_t con,int x,int y, int l, TCOD_bkgnd_flag_t flag) {
	int i;
	for (i=y; i< y+l; i++) TCOD_console_put_char(con,x,i,TCOD_CHAR_VLINE,flag);
}

char *TCOD_console_vsprint(const char *fmt, va_list ap) {
	#define NB_BUFFERS 10
	#define INITIAL_SIZE 512
	/* several static buffers in case the function is used more than once in a single function call */
	static char *msg[NB_BUFFERS]={NULL};
	static int buflen[NB_BUFFERS]={0};
	static int curbuf=0;
	char *ret;
	bool ok=false;
	if (!msg[0]) {
		int i;
		for (i=0; i < NB_BUFFERS; i++) {
			buflen[i]=INITIAL_SIZE;
			msg[i]=(char *)calloc(sizeof(char),INITIAL_SIZE);
		}
	}
	do {
		/* warning ! depending on the compiler, vsnprintf return -1 or
		 the expected string length if the buffer is not big enough */
		int len = vsnprintf(msg[curbuf],buflen[curbuf],fmt,ap);
		ok=true;
		if (len < 0 || len >= buflen[curbuf]) {
			/* buffer too small. */
			if ( len > 0 ) {
				while ( buflen[curbuf] < len+1 ) buflen[curbuf]*=2;
			} else {
				buflen[curbuf]*=2;
			}
			free( msg[curbuf] );
			msg[curbuf]=(char *)calloc(sizeof(char),buflen[curbuf]);
			ok=false;
		}
	} while (! ok);
	ret=msg[curbuf];
	curbuf = (curbuf+1)%NB_BUFFERS;
	return ret;
}

void TCOD_console_print_frame(TCOD_console_t con,int x,int y,int w,int h, bool empty, TCOD_bkgnd_flag_t flag, const char *fmt, ...) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_console_put_char(con,x,y,TCOD_CHAR_NW,flag);
	TCOD_console_put_char(con,x+w-1,y,TCOD_CHAR_NE,flag);
	TCOD_console_put_char(con,x,y+h-1,TCOD_CHAR_SW,flag);
	TCOD_console_put_char(con,x+w-1,y+h-1,TCOD_CHAR_SE,flag);
	TCOD_console_hline(con,x+1,y,w-2,flag);
	TCOD_console_hline(con,x+1,y+h-1,w-2,flag);
	if ( h > 2 ) {
		TCOD_console_vline(con,x,y+1,h-2,flag);
		TCOD_console_vline(con,x+w-1,y+1,h-2,flag);
		if ( empty ) {
			TCOD_console_rect(con,x+1,y+1,w-2,h-2,true,flag);
		}
	}
	if (fmt) {
		va_list ap;
		int xs;
		TCOD_color_t tmp;
		char *title;
		va_start(ap,fmt);
		title = TCOD_console_vsprint(fmt,ap);
		va_end(ap);
		title[w-3]=0; /* truncate if needed */
		xs = x + (w-(int)strlen(title)-2)/2;
		tmp=dat->back; /* swap colors */
		dat->back=dat->fore;
		dat->fore=tmp;
		TCOD_console_print_ex(con,xs,y,TCOD_BKGND_SET,TCOD_LEFT," %s ",title);
		tmp=dat->back; /* swap colors */
		dat->back=dat->fore;
		dat->fore=tmp;
	}
}

void TCOD_console_print(TCOD_console_t con,int x, int y, const char *fmt, ...) {
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	va_start(ap,fmt);
	TCOD_console_print_internal(con,x,y,0,0,dat->bkgnd_flag,
		dat->alignment,TCOD_console_vsprint(fmt,ap), false, false);
	va_end(ap);
}

void TCOD_console_print_ex(TCOD_console_t con,int x, int y,
	TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const char *fmt, ...) {
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	va_start(ap,fmt);
	TCOD_console_print_internal(con,x,y,0,0,flag,alignment,
		TCOD_console_vsprint(fmt,ap), false, false);
	va_end(ap);
}

int TCOD_console_print_rect(TCOD_console_t con,int x, int y, int w, int h, const char *fmt, ...) {
	int ret;
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return 0;
	va_start(ap,fmt);
	ret = TCOD_console_print_internal(con,x,y,w,h,dat->bkgnd_flag,dat->alignment,
		TCOD_console_vsprint(fmt,ap), true, false);
	va_end(ap);
	return ret;
}

int TCOD_console_print_rect_ex(TCOD_console_t con,int x, int y, int w, int h,
	TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment,const char *fmt, ...) {
	int ret;
	va_list ap;
	va_start(ap,fmt);
	ret = TCOD_console_print_internal(con,x,y,w,h,flag,alignment,TCOD_console_vsprint(fmt,ap), true, false);
	va_end(ap);
	return ret;
}

int TCOD_console_get_height_rect(TCOD_console_t con,int x, int y, int w, int h, const char *fmt, ...) {
	int ret;
	va_list ap;
	va_start(ap,fmt);
	ret = TCOD_console_print_internal(con,x,y,w,h,TCOD_BKGND_NONE,TCOD_LEFT,TCOD_console_vsprint(fmt,ap), true, true);
	va_end(ap);
	return ret;
}

/* non public methods */
int TCOD_console_stringLength(const unsigned char *s) {
	int l=0;
	while (*s) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		else if ( (unsigned)(*s) > (unsigned)TCOD_COLCTRL_STOP ) l++;
		s++;
	}
	return l;
}

unsigned char * TCOD_console_forward(unsigned char *s,int l) {
	while ( *s && l > 0 ) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		else if ( *s > (int)TCOD_COLCTRL_STOP ) l--;
		s++;
	}
	return s;
}

unsigned char *TCOD_console_strchr(unsigned char *s, unsigned char c) {
	while ( *s && *s != c ) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		s++;
	}
	return (*s ? s : NULL);
}

int TCOD_console_print_internal(TCOD_console_t con,int x,int y, int rw, int rh, TCOD_bkgnd_flag_t flag,
	TCOD_alignment_t align, char *msg, bool can_split, bool count_only) {
	unsigned char *c=(unsigned char *)msg;
	int cx=0,cy=y;
	int minx,maxx,miny,maxy;
	TCOD_color_t oldFore;
	TCOD_color_t oldBack;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h )
		return 0;
	TCOD_IFNOT(msg != NULL) return 0;
	if ( rh == 0 ) rh = dat->h-y;
	if ( rw == 0 ) switch(align) {
		case TCOD_LEFT : rw = dat->w-x; break;
		case TCOD_RIGHT : rw=x+1; break;
		case TCOD_CENTER : default : rw=dat->w; break;
	}
	oldFore=dat->fore;
	oldBack=dat->back;
	miny=y;
	maxy=dat->h-1;
	if (rh > 0) maxy=MIN(maxy,y+rh-1);
	switch (align) {
		case TCOD_LEFT : minx=MAX(0,x); maxx=MIN(dat->w-1,x+rw-1); break;
		case TCOD_RIGHT : minx=MAX(0,x-rw+1); maxx=MIN(dat->w-1,x); break;
		case TCOD_CENTER : default : minx=MAX(0,x-rw/2); maxx=MIN(dat->w-1,x+rw/2); break;
	}

	do {
		/* get \n delimited sub-message */
		unsigned char *end=TCOD_console_strchr(c,'\n');
		char bak=0;
		int cl;
		unsigned char *split=NULL;
		if ( end ) *end=0;
		cl= TCOD_console_stringLength(c);
		/* find starting x */
		switch (align) {
			case TCOD_LEFT : cx=x; break;
			case TCOD_RIGHT : cx=x-cl+1; break;
			case TCOD_CENTER : cx= x-cl/2;break;
		}
		/* check if the string is completely out of the minx,miny,maxx,maxy frame */
		if ( cy >= miny && cy <= maxy && cx <= maxx && cx+cl -1 >= minx ) {
			if ( can_split && cy <= maxy ) {
				/* if partially out of screen, try to split the sub-message */
				if ( cx < minx ) split = TCOD_console_forward(c, align == TCOD_CENTER ? cl-2*(minx-cx) : cl-(minx-cx));
				else if ( align == TCOD_CENTER ) {
					if ( cx + cl/2 > maxx+1 ) split = TCOD_console_forward(c, maxx+1 - cx);
				} else {
					if ( cx + cl > maxx+1 ) split = TCOD_console_forward(c, maxx+1 - cx);
				}
			}
			if ( split ) {
				unsigned char *oldsplit=split;
				while ( ! isspace(*split) && split > c ) split --;
				if (end) *end='\n';
				if (!isspace(*split) ) {
					split=oldsplit;
				}
				end=split;
				bak=*split;
				*split=0;
				cl=TCOD_console_stringLength(c);
				switch (align) {
					case TCOD_LEFT : cx=x; break;
					case TCOD_RIGHT : cx=x-cl+1; break;
					case TCOD_CENTER : cx= x-cl/2;break;
				}
			}
			if ( cx < minx ) {
				/* truncate left part */
				c += minx-cx;
				cl -= minx-cx;
				cx=minx;
			}
			if ( cx + cl > maxx+1 ) {
				/* truncate right part */
				split = TCOD_console_forward(c, maxx+1 - cx);
				*split=0;
			}
			/* render the sub-message */
			if ( cy >= 0 && cy < dat->h )
			while (*c) {
				if ( *c >= TCOD_COLCTRL_1 && *c <= TCOD_COLCTRL_NUMBER ) {
					dat->fore=color_control_fore[(int)(*c)-1];
					dat->back=color_control_back[(int)(*c)-1];
				} else if ( *c == TCOD_COLCTRL_FORE_RGB ) {
					c++;
					dat->fore.r=*c++;
					dat->fore.g=*c++;
					dat->fore.b=*c;
				} else if ( *c == TCOD_COLCTRL_BACK_RGB ) {
					c++;
					dat->back.r=*c++;
					dat->back.g=*c++;
					dat->back.b=*c;
				} else if ( *c == TCOD_COLCTRL_STOP ) {
					dat->fore=oldFore;
					dat->back=oldBack;
				} else {
					if (! count_only) TCOD_console_put_char(con,cx,cy,(int)(*c),flag);
					cx++;
				}
				c++;
			}
		}
		if ( end ) {
			/* next line */
			if ( split && ! isspace(bak) ) {
				*end=bak;
				c=end;
			} else {
				c=end+1;
			}
			cy++;
		} else c=NULL;
	} while ( c && cy < dat->h && (rh == 0 || cy < y+rh) );
	return cy-y+1;
}

#ifndef NO_UNICODE

wchar_t *TCOD_console_strchr_utf(wchar_t *s, char c) {
	while ( *s && *s != c ) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		s++;
	}
	return (*s ? s : NULL);
}

void TCOD_console_map_string_to_font_utf(const wchar_t *s, int fontCharX, int fontCharY) {
	TCOD_IFNOT(s != NULL) return;
	while (*s) {
		TCOD_sys_map_ascii_to_font(*s, fontCharX, fontCharY);
		fontCharX++;
		if ( fontCharX == TCOD_ctx.fontNbCharHoriz ) {
			fontCharX=0;
			fontCharY++;
		}
		s++;
	}
}

wchar_t *TCOD_console_vsprint_utf(const wchar_t *fmt, va_list ap) {
	#define NB_BUFFERS 10
	#define INITIAL_SIZE 512
	/* several static buffers in case the function is used more than once in a single function call */
	static wchar_t *msg[NB_BUFFERS]={NULL};
	static int buflen[NB_BUFFERS]={0};
	static int curbuf=0;
	wchar_t *ret;
	bool ok=false;
	if (!msg[0]) {
		int i;
		for (i=0; i < NB_BUFFERS; i++) {
			buflen[i]=INITIAL_SIZE;
			msg[i]=(wchar_t *)calloc(sizeof(wchar_t),INITIAL_SIZE);
		}
	}
	do {
		/* warning ! depending on the compiler, vsnprintf return -1 or
		 the expected string length if the buffer is not big enough */
		int len = vsnwprintf(msg[curbuf],buflen[curbuf],fmt,ap);
		ok=true;
		if (len < 0 || len >= buflen[curbuf]) {
			/* buffer too small. */
			if ( len > 0 ) {
				while ( buflen[curbuf] < len+1 ) buflen[curbuf]*=2;
			} else {
				buflen[curbuf]*=2;
			}
			free( msg[curbuf] );
			msg[curbuf]=(wchar_t *)calloc(sizeof(wchar_t),buflen[curbuf]);
			ok=false;
		}
	} while (! ok);
	ret=msg[curbuf];
	curbuf = (curbuf+1)%NB_BUFFERS;
	return ret;
}


int TCOD_console_stringLength_utf(const wchar_t *s) {
	int l=0;
	while (*s) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		else if ( *s > (int)TCOD_COLCTRL_STOP ) l++;
		s++;
	}
	return l;
}

wchar_t * TCOD_console_forward_utf(wchar_t *s,int l) {
	while ( *s && l > 0 ) {
		if ( *s == (int)TCOD_COLCTRL_FORE_RGB || *s == (int)TCOD_COLCTRL_BACK_RGB ) s+=3;
		else if ( *s > (int)TCOD_COLCTRL_STOP ) l--;
		s++;
	}
	return s;
}

int TCOD_console_print_internal_utf(TCOD_console_t con,int x,int y, int rw, int rh, TCOD_bkgnd_flag_t flag,
	TCOD_alignment_t align, wchar_t *msg, bool can_split, bool count_only) {
	wchar_t *c=msg;
	int cx=0,cy=y;
	int minx,maxx,miny,maxy;
	TCOD_color_t oldFore;
	TCOD_color_t oldBack;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL
		&& (unsigned)(x) < (unsigned)dat->w && (unsigned)(y) < (unsigned)dat->h )
		return 0;
	TCOD_IFNOT(msg != NULL) return 0;
	if ( rh == 0 ) rh = dat->h-y;
	if ( rw == 0 ) switch(align) {
		case TCOD_LEFT : rw = dat->w-x; break;
		case TCOD_RIGHT : rw=x+1; break;
		case TCOD_CENTER : default : rw=dat->w; break;
	}
	oldFore=dat->fore;
	oldBack=dat->back;
	miny=y;
	maxy=dat->h-1;
	if (rh > 0) maxy=MIN(maxy,y+rh-1);
	switch (align) {
		case TCOD_LEFT : minx=MAX(0,x); maxx=MIN(dat->w-1,x+rw-1); break;
		case TCOD_RIGHT : minx=MAX(0,x-rw+1); maxx=MIN(dat->w-1,x); break;
		case TCOD_CENTER : default : minx=MAX(0,x-rw/2); maxx=MIN(dat->w-1,x+rw/2); break;
	}

	do {
		/* get \n delimited sub-message */
		wchar_t *end=TCOD_console_strchr_utf(c,'\n');
		wchar_t bak=0;
		int cl;
		wchar_t *split=NULL;
		if ( end ) *end=0;
		cl= TCOD_console_stringLength_utf(c);
		/* find starting x */
		switch (align) {
			case TCOD_LEFT : cx=x; break;
			case TCOD_RIGHT : cx=x-cl+1; break;
			case TCOD_CENTER : cx= x-cl/2;break;
		}
		/* check if the string is completely out of the minx,miny,maxx,maxy frame */
		if ( cy >= miny && cy <= maxy && cx <= maxx && cx+cl -1 >= minx ) {
			if ( can_split && cy < maxy ) {
				/* if partially out of screen, try to split the sub-message */
				if ( cx < minx ) split = TCOD_console_forward_utf(c, align == TCOD_CENTER ? cl-2*(minx-cx) : cl-(minx-cx));
				else if ( align==TCOD_CENTER ) {
					if ( cx + cl/2 > maxx+1 ) split = TCOD_console_forward_utf(c, maxx+1 - cx);
				} else {
					if ( cx + cl > maxx+1 ) split = TCOD_console_forward_utf(c, maxx+1 - cx);
				}
			}
			if ( split ) {
				wchar_t *oldsplit=split;
				while ( ! iswspace(*split) && split > c ) split --;
				if (end) *end='\n';
				if (!iswspace(*split) ) {
					split=oldsplit;
				}
				end=split;
				bak=*split;
				*split=0;
				cl=TCOD_console_stringLength_utf(c);
				switch (align) {
					case TCOD_LEFT : cx=x; break;
					case TCOD_RIGHT : cx=x-cl+1; break;
					case TCOD_CENTER : cx= x-cl/2;break;
				}
			}
			if ( cx < minx ) {
				/* truncate left part */
				c += minx-cx;
				cl -= minx-cx;
				cx=minx;
			}
			if ( cx + cl > maxx+1 ) {
				/* truncate right part */
				split = TCOD_console_forward_utf(c, maxx+1 - cx);
				*split=0;
			}
			/* render the sub-message */
			if ( cy >= 0 && cy < dat->h )
			while (*c) {
				if ( *c >= TCOD_COLCTRL_1 && *c <= TCOD_COLCTRL_NUMBER ) {
					dat->fore=color_control_fore[(int)(*c)-1];
					dat->back=color_control_back[(int)(*c)-1];
				} else if ( *c == TCOD_COLCTRL_FORE_RGB ) {
					c++;
					dat->fore.r=(uint8_t)(*c++);
					dat->fore.g=(uint8_t)(*c++);
					dat->fore.b=(uint8_t)(*c);
				} else if ( *c == TCOD_COLCTRL_BACK_RGB ) {
					c++;
					dat->back.r=(uint8_t)(*c++);
					dat->back.g=(uint8_t)(*c++);
					dat->back.b=(uint8_t)(*c);
				} else if ( *c == TCOD_COLCTRL_STOP ) {
					dat->fore=oldFore;
					dat->back=oldBack;
				} else {
					if (! count_only) TCOD_console_put_char(con,cx,cy,(int)(*c),flag);
					cx++;
				}
				c++;
			}
		}
		if ( end ) {
			/* next line */
			if ( split && ! iswspace(bak) ) {
				*end=bak;
				c=end;
			} else {
				c=end+1;
			}
			cy++;
		} else c=NULL;
	} while ( c && cy < dat->h && (rh == 0 || cy < y+rh) );
	return cy-y+1;
}

void TCOD_console_print_utf(TCOD_console_t con,int x, int y, const wchar_t *fmt, ...) {
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	va_start(ap,fmt);
	TCOD_console_print_internal_utf(con,x,y,0,0,dat->bkgnd_flag,dat->alignment,
		TCOD_console_vsprint_utf(fmt,ap), false, false);
	va_end(ap);
}

void TCOD_console_print_ex_utf(TCOD_console_t con,int x, int y,
	TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const wchar_t *fmt, ...) {
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return;
	va_start(ap,fmt);
	TCOD_console_print_internal_utf(con,x,y,0,0,flag,alignment,TCOD_console_vsprint_utf(fmt,ap), false, false);
	va_end(ap);
}

int TCOD_console_print_rect_utf(TCOD_console_t con,int x, int y, int w, int h,
	const wchar_t *fmt, ...) {
	int ret;
	va_list ap;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT ( dat != NULL ) return 0;
	va_start(ap,fmt);
	ret = TCOD_console_print_internal_utf(con,x,y,w,h,dat->bkgnd_flag,dat->alignment,
		TCOD_console_vsprint_utf(fmt,ap), true, false);
	va_end(ap);
	return ret;
}

int TCOD_console_print_rect_ex_utf(TCOD_console_t con,int x, int y, int w, int h,
	TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const wchar_t *fmt, ...) {
	int ret;
	va_list ap;
	va_start(ap,fmt);
	ret=TCOD_console_print_internal_utf(con,x,y,w,h,flag,alignment,TCOD_console_vsprint_utf(fmt,ap), true, false);
	va_end(ap);
	return ret;
}

int TCOD_console_get_height_rect_utf(TCOD_console_t con,int x, int y, int w, int h, const wchar_t *fmt, ...) {
	int ret;
	va_list ap;
	va_start(ap,fmt);
	ret = TCOD_console_print_internal_utf(con,x,y,w,h,TCOD_BKGND_NONE,TCOD_LEFT,TCOD_console_vsprint_utf(fmt,ap), true, true);
	va_end(ap);
	return ret;
}

#endif

void TCOD_console_init_root(int w, int h, const char*title, bool fullscreen, TCOD_renderer_t renderer) {
	TCOD_IF(w > 0 && h > 0) {
		TCOD_console_data_t *con=(TCOD_console_data_t *)calloc(sizeof(TCOD_console_data_t),1);
		int i;
		con->w=w;
		con->h=h;
		TCOD_ctx.root=con;
#ifdef TCOD_SDL2
		TCOD_ctx.renderer=renderer;
#endif
		for (i=0; i < TCOD_COLCTRL_NUMBER; i++) {
			color_control_fore[i]=TCOD_white;
			color_control_back[i]=TCOD_black;
		}
		TCOD_console_init((TCOD_console_t)con,title,fullscreen);
	}
}

static void TCOD_console_data_alloc(TCOD_console_data_t *dat) {
	dat->ch_array = (int *)calloc(sizeof(int), dat->w*dat->h);
	dat->fg_colors = TCOD_image_new(dat->w, dat->h);
	dat->bg_colors = TCOD_image_new(dat->w, dat->h);
}

bool TCOD_console_init(TCOD_console_t con,const char *title, bool fullscreen) {
	int i;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return false;
	dat->fore=TCOD_white;
	dat->back=TCOD_black;

	TCOD_console_data_alloc(dat);

	dat->bkgnd_flag=TCOD_BKGND_NONE;
	dat->alignment=TCOD_LEFT;
	for (i=0; i< dat->w*dat->h; i++) {
		dat->ch_array[i] = ' ';
	}
	if ( title ) {
		if (! TCOD_sys_init(dat, fullscreen) ) return false;
		TCOD_sys_set_window_title(title);
	}
	return true;
}

void TCOD_console_set_default_foreground(TCOD_console_t con,TCOD_color_t col) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	dat->fore=col;
}

void TCOD_console_set_default_background(TCOD_console_t con,TCOD_color_t col) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	dat->back=col;
}

TCOD_color_t TCOD_console_get_default_foreground(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return TCOD_white;
	return dat->fore;
}

TCOD_color_t TCOD_console_get_default_background(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return TCOD_black;
	return dat->back;
}

int TCOD_console_get_width(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return 0;
	return dat->w;
}

int TCOD_console_get_height(TCOD_console_t con) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return 0;
	return dat->h;
}

TCOD_image_t TCOD_console_get_foreground_color_image(TCOD_console_t con) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return NULL;
	return dat->fg_colors;
}

TCOD_image_t TCOD_console_get_background_color_image(TCOD_console_t con) {
	TCOD_console_data_t *dat = con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return NULL;
	return dat->bg_colors;
}

void TCOD_console_set_custom_font(const char *fontFile, int flags,int nb_char_horiz, int nb_char_vertic) {
	TCOD_sys_set_custom_font(fontFile, nb_char_horiz, nb_char_vertic, flags);
}

void TCOD_console_map_ascii_code_to_font(int asciiCode, int fontCharX, int fontCharY) {
	/* cannot change mapping before initRoot is called */
	TCOD_IFNOT(TCOD_ctx.root != NULL) return;
	TCOD_sys_map_ascii_to_font(asciiCode, fontCharX, fontCharY);
}

void TCOD_console_map_ascii_codes_to_font(int asciiCode, int nbCodes, int fontCharX, int fontCharY) {
	int c;
	/* cannot change mapping before initRoot is called */
	TCOD_IFNOT(TCOD_ctx.root != NULL) return;
	TCOD_IFNOT(asciiCode >= 0 && asciiCode+nbCodes <= TCOD_ctx.max_font_chars) return;
	for (c=asciiCode; c < asciiCode+nbCodes; c++ ) {
		TCOD_sys_map_ascii_to_font(c, fontCharX, fontCharY);
		fontCharX++;
		if ( fontCharX == TCOD_ctx.fontNbCharHoriz ) {
			fontCharX=0;
			fontCharY++;
		}
	}
}

void TCOD_console_map_string_to_font(const char *s, int fontCharX, int fontCharY) {
	TCOD_IFNOT(s != NULL) return;
	/* cannot change mapping before initRoot is called */
	TCOD_IFNOT(TCOD_ctx.root != NULL) return;
	while (*s) {
		TCOD_console_map_ascii_code_to_font(*s, fontCharX, fontCharY);
		fontCharX++;
		if ( fontCharX == TCOD_ctx.fontNbCharHoriz ) {
			fontCharX=0;
			fontCharY++;
		}
		s++;
	}
}

bool TCOD_console_is_key_pressed(TCOD_keycode_t key) {
	return TCOD_sys_is_key_pressed(key);
}
void TCOD_console_set_key_color(TCOD_console_t con,TCOD_color_t col) {
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	TCOD_image_set_key_color(dat->bg_colors, col);
}

void TCOD_console_credits(void) {
	bool end=false;
	int x=TCOD_console_get_width(NULL)/2-6;
	int y=TCOD_console_get_height(NULL)/2;
	int fade=260;
	TCOD_sys_save_fps();
	TCOD_sys_set_fps(25);
	while (!end ) {
		TCOD_key_t k;
		end=TCOD_console_credits_render(x,y,false);
		TCOD_sys_check_for_event(TCOD_EVENT_KEY_PRESS,&k,NULL);
		if ( fade == 260 && k.vk != TCODK_NONE ) {
			fade -= 10;
		}
		TCOD_console_flush();
		if ( fade < 260 ) {
			fade -= 10;
			TCOD_console_set_fade(fade,TCOD_black);
			if ( fade == 0 ) end=true;
		}
	}
	TCOD_console_set_fade(255,TCOD_black);
	TCOD_sys_restore_fps();
}

static bool init2=false;

void TCOD_console_credits_reset(void) {
	init2=false;
}

bool TCOD_console_credits_render(int x, int y, bool alpha) {
	static char poweredby[128];
	static float char_heat[128];
	static int char_x[128];
	static int char_y[128];
	static bool init1=false;
	static int len,len1,cw=-1,ch=-1;
	static float xstr;
	static TCOD_color_t colmap[64];
	static TCOD_color_t colmap_light[64];
	static TCOD_noise_t noise;
	static TCOD_color_t colkeys[4] = {
		{255,255,204},
		{255,204,0},
		{255,102,0},
		{102,153,255},
	};
	static TCOD_color_t colkeys_light[4] = {
		{255,255,204},
		{128,128,77},
		{51,51,31},
		{0,0,0},
	};
	static int colpos[4]={
		0,21,42,63
	};
	static TCOD_image_t img=NULL;
	int i,xc,yc,xi,yi,j;
	static int left,right,top,bottom;
	float sparklex,sparkley,sparklerad,sparklerad2,noisex;
	/* mini particule system */
#define MAX_PARTICULES 50
	static float pheat[MAX_PARTICULES];
	static float px[MAX_PARTICULES],py[MAX_PARTICULES], pvx[MAX_PARTICULES],pvy[MAX_PARTICULES];
	static int nbpart=0, firstpart=0;
	static float partDelay=0.1f;
	float elapsed=TCOD_sys_get_last_frame_length();
	TCOD_color_t fbackup; /* backup fg color */

	if (!init1) {
		/* initialize all static data, colormaps, ... */
		TCOD_color_t col;
		TCOD_color_gen_map(colmap,4,colkeys,colpos);
		TCOD_color_gen_map(colmap_light,4,colkeys_light,colpos);
		sprintf(poweredby,"Powered by\n%s",version_string);
		noise=TCOD_noise_new(1,TCOD_NOISE_DEFAULT_HURST,TCOD_NOISE_DEFAULT_LACUNARITY,NULL);
		len=(int)strlen(poweredby);
		len1=11; /* sizeof "Powered by\n" */
		left=MAX(x-4,0);
		top=MAX(y-4,0);
		col= TCOD_console_get_default_background(NULL);
		TCOD_console_set_default_background(NULL,TCOD_black);
		TCOD_console_set_default_background(NULL,col);
		init1=true;
	}
	if (!init2) {
		/* reset the credits vars ... */
		int curx,cury;
		xstr=-4.0f;
		curx=x;
		cury=y;
		for (i=0; i < len ;i++) {
			char_heat[i]=-1;
			char_x[i]=curx;
			char_y[i]=cury;
			curx++;
			if ( poweredby[i] == '\n' ) {
				curx=x;
				cury++;
			}
		}
		nbpart=firstpart=0;
		init2=true;
	}
	if (TCOD_console_get_width(NULL) != cw || TCOD_console_get_height(NULL)!=ch) {
		/* console size has changed */
		int width,height;
		cw=TCOD_console_get_width(NULL);
		ch=TCOD_console_get_height(NULL);
		right=MIN(x+len,cw-1);
		bottom=MIN(y+6,ch-1);
		width=right - left + 1;
		height=bottom - top + 1;
		if ( img ) TCOD_image_delete(img);
		img = TCOD_image_new(width*2,height*2);
	}
	fbackup=TCOD_console_get_default_foreground(NULL);
	if ( xstr < (float)len1 ) {
		sparklex=x+xstr;
		sparkley=(float)y;
	} else {
		sparklex=x-len1+xstr;
		sparkley=(float)y+1;
	}
	noisex=xstr*6;
	sparklerad=3.0f+2*TCOD_noise_get(noise,&noisex);
	if ( xstr >= len-1 ) sparklerad -= (xstr-len+1)*4.0f;
	else if ( xstr < 0.0f ) sparklerad += xstr*4.0f;
	else if ( poweredby[ (int)(xstr+0.5f) ] == ' ' || poweredby[ (int)(xstr+0.5f) ] == '\n' ) sparklerad/=2;
	sparklerad2=sparklerad*sparklerad*4;

	/* draw the light */
	for (xc=left*2,xi=0; xc < (right+1)*2; xc++,xi++) {
		for (yc=top*2,yi=0; yc < (bottom+1)*2; yc++,yi++) {
			float dist=((xc-2*sparklex)*(xc-2*sparklex)+(yc-2*sparkley)*(yc-2*sparkley));
			TCOD_color_t pixcol;
			if ( sparklerad >= 0.0f && dist < sparklerad2 ) {
				int colidx=63-(int)(63*(sparklerad2-dist)/sparklerad2) + TCOD_random_get_int(NULL,-10,10);
				colidx=CLAMP(0,63,colidx);
				pixcol=colmap_light[colidx];
			} else {
				pixcol=TCOD_black;
			}
			if ( alpha ) {
				/*	console cells have following flag values :
				  		1 2
				  		4 8
				  	flag indicates which subcell uses foreground color */
				static int asciiToFlag[] = {
					1, /* TCOD_CHAR_SUBP_NW */
					2, /* TCOD_CHAR_SUBP_NE */
					3, /* TCOD_CHAR_SUBP_N */
					8, /* TCOD_CHAR_SUBP_SE */
					9, /* TCOD_CHAR_SUBP_DIAG */
					10, /* TCOD_CHAR_SUBP_E */
					4, /* TCOD_CHAR_SUBP_SW */
				};
				int conc= TCOD_console_get_char(NULL,xc/2,yc/2);
				TCOD_color_t bk=TCOD_console_get_char_background(NULL,xc/2,yc/2);
				if ( conc >= TCOD_CHAR_SUBP_NW && conc <= TCOD_CHAR_SUBP_SW ) {
					/* merge two subcell chars...
					   get the flag for the existing cell on root console */
					int bkflag=asciiToFlag[conc - TCOD_CHAR_SUBP_NW ];
					int xflag = (xc & 1);
					int yflag = (yc & 1);
					/* get the flag for the current subcell */
					int credflag = (1+3*yflag) * (xflag+1);
					if ( (credflag & bkflag) != 0 ) {
						/* the color for this subcell on root console
						   is foreground, not background */
						bk = TCOD_console_get_char_foreground(NULL,xc/2,yc/2);
					}
				}
				pixcol.r = MIN(255,(int)(bk.r)+pixcol.r);
				pixcol.g = MIN(255,(int)(bk.g)+pixcol.g);
				pixcol.b = MIN(255,(int)(bk.b)+pixcol.b);
			}
			TCOD_image_put_pixel(img,xi,yi,pixcol);
		}
	}

	/* draw and update the particules */
	j=nbpart;i=firstpart;
	while (j > 0) {
		int colidx=(int)(64*(1.0f-pheat[i]));
		TCOD_color_t col;
		colidx=MIN(63,colidx);
		col=colmap[colidx];
		if ( (int)py[i]< (bottom-top+1)*2 ) {
			int ipx = (int)px[i];
			int ipy = (int)py[i];
			float fpx = px[i]-ipx;
			float fpy = py[i]-ipy;
			TCOD_color_t col2=TCOD_image_get_pixel(img,ipx,ipy);
			col2=TCOD_color_lerp(col,col2,0.5f*(fpx+fpy));
			TCOD_image_put_pixel(img,ipx,ipy,col2);
			col2=TCOD_image_get_pixel(img,ipx+1,ipy);
			col2=TCOD_color_lerp(col2,col,fpx);
			TCOD_image_put_pixel(img,ipx+1,ipy,col2);
			col2=TCOD_image_get_pixel(img,ipx,ipy+1);
			col2=TCOD_color_lerp(col2,col,fpy);
			TCOD_image_put_pixel(img,ipx,ipy+1,col2);
		} else pvy[i]=-pvy[i] * 0.5f;
		pvx[i] *= (1.0f-elapsed);
		pvy[i] += (1.0f-pheat[i])*elapsed*300.0f;
		px[i] += pvx[i]*elapsed;
		py[i] += pvy[i]*elapsed;
		pheat[i] -= elapsed*0.3f;
		if ( pheat[i] < 0.0f ) {
			firstpart = (firstpart+1)%MAX_PARTICULES;
			nbpart--;
		}
		i = (i+1)%MAX_PARTICULES;
		j--;
	}
	partDelay -= elapsed;
	if ( partDelay < 0.0f && nbpart < MAX_PARTICULES && sparklerad > 2.0f ) {
		/* fire a new particule */
		int lastpart = firstpart;
		int nb=nbpart;
		while (nb > 0 ) {
			lastpart = ( lastpart + 1 )%MAX_PARTICULES;
			nb--;
		}
		nbpart++;
		px[lastpart] = 2*(sparklex-left);
		py[lastpart] = 2*(sparkley-top)+2;
		pvx[lastpart] = TCOD_random_get_float(NULL,-5.0f,5.0f);
		pvy[lastpart] = TCOD_random_get_float(NULL,-0.5f, -15.0f);
		pheat[lastpart] = 1.0f;
		partDelay += 0.1f;
	}
	TCOD_image_blit_2x(img,NULL,left,top,0,0,-1,-1);
	/* draw the text */
	for (i=0; i < len ;i++) {
		if ( char_heat[i] >= 0.0f && poweredby[i]!='\n') {
			int colidx=(int)(64*char_heat[i]);
			TCOD_color_t col;
			colidx=MIN(63,colidx);
			col=colmap[colidx];
			if ( xstr >= len  ) {
				float coef=(xstr-len)/len;
				if ( alpha ) {
					TCOD_color_t fore=TCOD_console_get_char_background(NULL,char_x[i],char_y[i]);
					int r=(int)(coef*fore.r + (1.0f-coef)*col.r);
					int g=(int)(coef*fore.g + (1.0f-coef)*col.g);
					int b=(int)(coef*fore.b + (1.0f-coef)*col.b);
					col.r = CLAMP(0,255,r);
					col.g = CLAMP(0,255,g);
					col.b = CLAMP(0,255,b);
					TCOD_console_set_char_foreground(NULL,char_x[i],char_y[i],col);
				} else {
					col=TCOD_color_lerp(col,TCOD_black,coef);
				}
			}
			TCOD_console_set_char(NULL,char_x[i],char_y[i],poweredby[i]);
			TCOD_console_set_char_foreground(NULL,char_x[i],char_y[i],col);
		}
	}
	/* update letters heat */
	xstr += elapsed * 4;
	for (i=0; i < (int)(xstr+0.5f); i++) {
		char_heat[i]=(xstr-i)/(len/2);
	}
	/* restore fg color */
	TCOD_console_set_default_foreground(NULL,fbackup);
	if ( xstr <= 2*len ) return false;
	init2=false;
	return true;
}

static void TCOD_console_read_asc(TCOD_console_t con,FILE *f,int width, int height, float version) {
	int x,y;
	TCOD_console_data_t *dat=con ? (TCOD_console_data_t *)con : TCOD_ctx.root;
	TCOD_IFNOT(dat != NULL) return;
	while(fgetc(f) != '#');
	for(x = 0; x < width; x++) {
	    for(y = 0; y < height; y++) {
	    	TCOD_color_t fore,back;
		    int c = fgetc(f);
		    fore.r = fgetc(f);
		    fore.g = fgetc(f);
		    fore.b = fgetc(f);
		    back.r = fgetc(f);
		    back.g = fgetc(f);
		    back.b = fgetc(f);
		    /* skip solid/walkable info */
		    if ( version >= 0.3f ) {
		    	fgetc(f); 
		    	fgetc(f);
		    }
		    TCOD_console_put_char_ex(con,x,y,c,fore,back);
	    }
    }
    fclose(f);
}

static void TCOD_console_read_apf(TCOD_console_t con,FILE *f,int width, int height, float version) {
}

static int string_ends_with(const char *str, const char *suffix) {
	size_t str_len = strlen(str);
	size_t suffix_len = strlen(suffix);
	return
		(str_len >= suffix_len) &&
		(0 == strcmp(str + (str_len-suffix_len), suffix));
}

TCOD_console_t TCOD_console_from_file(const char *filename) {
	float version;
	int width,height;
	TCOD_console_t con;
	FILE *f;
	TCOD_IFNOT( filename != NULL ) {
		return NULL;
	}
	if (string_ends_with(filename, ".xp")) {
		return TCOD_console_from_xp(filename);
	}
	f=fopen(filename,"rb");
	TCOD_IFNOT( f!=NULL ) {
		return NULL;
	}
	if (fscanf(f, "ASCII-Paint v%g", &version) != 1 ) {
		fclose(f);
		return NULL;
	}
	if (fscanf(f, "%i %i", &width, &height) != 2 ) {
		fclose(f);
		return NULL;
	}
	TCOD_IFNOT ( width > 0 && height > 0) {
		fclose(f);
		return NULL;
	}
	con=TCOD_console_new(width,height);
	if (string_ends_with(filename, ".asc")) {
		TCOD_console_read_asc(con,f,width,height,version);
	} else {
		TCOD_console_read_apf(con,f,width,height,version);
	}
	return con;
}

bool TCOD_console_load_asc(TCOD_console_t pcon, const char *filename) {
	float version;
	int width,height;
	FILE *f;
	TCOD_console_data_t *con=pcon ? (TCOD_console_data_t *)pcon : TCOD_ctx.root;
	TCOD_IFNOT(con != NULL) return false;
	TCOD_IFNOT( filename != NULL ) {
		return false;
	}
	f=fopen(filename,"rb");
	TCOD_IFNOT( f!=NULL ) {
		return false;
	}
	if (fscanf(f, "ASCII-Paint v%g", &version) != 1 ) {
		fclose(f);
		return false;
	}
	if (fscanf(f, "%i %i", &width, &height) != 2 ) {
		fclose(f);
		return false;
	}
	TCOD_IFNOT ( width > 0 && height > 0) {
		fclose(f);
		return false;
	}
	if ( con->w != width || con->h != height ) {
		/* resize console */
		TCOD_console_data_free(con);
		con->w = width;
		con->h = height;
		TCOD_console_data_alloc(con);
	}
	TCOD_console_read_asc(con,f,width,height,version);
	return true;
}

bool TCOD_console_save_asc(TCOD_console_t pcon, const char *filename) {
	static float version = 0.3f;
	FILE *f;
	int x,y;
	TCOD_console_data_t *con=pcon ? (TCOD_console_data_t *)pcon : TCOD_ctx.root;
	TCOD_IFNOT(con != NULL) return false;
	TCOD_IFNOT( filename != NULL ) {
		return false;
	}
	TCOD_IFNOT(con->w > 0 && con->h > 0) return false;
	f=fopen(filename,"wb");
	TCOD_IFNOT( f != NULL ) return false;
	fprintf(f, "ASCII-Paint v%g\n", version);
	fprintf(f, "%i %i\n", con->w, con->h);
	fputc('#', f);
	for(x = 0; x < con->w; x++) {
		for(y = 0; y < con->h; y++) {
			TCOD_color_t fore,back;
			int c=TCOD_console_get_char(con,x,y);
			fore=TCOD_console_get_char_foreground(con,x,y);			
			back=TCOD_console_get_char_background(con,x,y);
			fputc(c, f);
			fputc(fore.r,f);			
			fputc(fore.g,f);			
			fputc(fore.b,f);			
			fputc(back.r,f);			
			fputc(back.g,f);			
			fputc(back.b,f);
			fputc(0,f); /* solid */
			fputc(1,f); /* walkable */			
		}
	}
	fclose(f);
	return true;
}

static bool hasDetectedBigEndianness = false;
static bool isBigEndian;
void detectBigEndianness(void) {
	if (!hasDetectedBigEndianness){
		uint32_t Value32;
		uint8_t *VPtr = (uint8_t *)&Value32;
		VPtr[0] = VPtr[1] = VPtr[2] = 0; VPtr[3] = 1;
		if(Value32 == 1) isBigEndian = true;
		else isBigEndian = false;
		hasDetectedBigEndianness = true;
	}
}

uint16_t bswap16(uint16_t s){
	uint8_t* ps = (uint8_t*)&s;
	uint16_t res;
	uint8_t* pres = (uint8_t*)&res;
	pres[0] = ps[1];
	pres[1] = ps[0];
	return res;
}

uint32_t bswap32(uint32_t s){
	uint8_t *ps=(uint8_t *)(&s);
	uint32_t res;
	uint8_t *pres=(uint8_t *)&res;
	pres[0]=ps[3];
	pres[1]=ps[2];
	pres[2]=ps[1];
	pres[3]=ps[0];
	return res;
}

uint16_t l16(uint16_t s){
	if (isBigEndian) return bswap16(s); else return s;
}

uint32_t l32(uint32_t s){
	if (isBigEndian) return bswap32(s); else return s;
}

/* fix the endianness */
void fix16(uint16_t* u){
	*u = l16(*u);
}

void fix32(uint32_t* u){
	*u = l32(*u);
}

/************ RIFF helpers  */

uint32_t fourCC(const char* c){
	return (*(uint32_t*)c);
}

/* checks if u equals str */
bool fourCCequals(uint32_t u, const char* str){
	return fourCC(str)==u;
}

void fromFourCC(uint32_t u, char*s){
	const char* c = (const char*)(&u);
	s[0]=c[0];
	s[1]=c[1];
	s[2]=c[2];
	s[3]=c[3];
	s[4]=0;
}

void put8(uint8_t d, FILE* fp){
	fwrite(&d,1,1,fp);
}

void put16(uint16_t d, FILE* fp){
	fwrite(&d,2,1,fp);
}

void put32(uint32_t d, FILE* fp){
	fwrite(&d,4,1,fp);
}

void putFourCC(const char* c, FILE* fp){
	put32(fourCC(c),fp);
}

void putData(void* what, int length, FILE* fp){
	fwrite(what,length,1,fp);
}

bool get8(uint8_t* u, FILE* fp){
	return 1==fread((void*)u, sizeof(uint8_t),1,fp);
}

bool get16(uint16_t* u, FILE* fp){
	return 1==fread((void*)u, sizeof(uint16_t),1,fp);
}

bool get32(uint32_t* u, FILE* fp){
	return 1==fread((void*)u, sizeof(uint32_t),1,fp);
}

bool getData(void* u, size_t sz, FILE* fp){
	return 1==fread(u, sz,1,fp);
}


/********* APF RIFF structures */

typedef struct {
	uint32_t show_grid;
	uint32_t grid_width;
	uint32_t grid_height;
} SettingsDataV1;

#define FILTER_TYPE_UNCOMPRESSED 0
#define FORMAT_TYPE_CRGBRGB 0

typedef struct {
	uint32_t width;
	uint32_t height;
	uint32_t filter;
	uint32_t format;
} ImageDetailsV1;

/* Layers */

typedef struct {
	uint32_t name;
	uint32_t mode;
	uint32_t index;
	uint32_t dataSize;
} LayerV1 ;

typedef struct {
	uint32_t name;
	uint32_t mode;
	uint32_t fgalpha;
	uint32_t bgalpha;
	uint32_t visible;
	uint32_t index;
	uint32_t dataSize;
} LayerV2;

/* fix the endianness */
void fixSettings(SettingsDataV1* s){
	fix32(&s->show_grid);
	fix32(&s->grid_width);
	fix32(&s->grid_height);
}

void fixImage(ImageDetailsV1* v){
	fix32(&v->width);
	fix32(&v->height);
	fix32(&v->filter);
	fix32(&v->format);
}

void fixLayerv1(LayerV1* l){
	fix32(&l->mode);
	fix32(&l->index);
	fix32(&l->dataSize);
}

void fixLayerv2(LayerV2* l){
	fix32(&l->mode);
	fix32(&l->fgalpha);
	fix32(&l->bgalpha);
	fix32(&l->visible);
	fix32(&l->index);
	fix32(&l->dataSize);
}


/*********** ApfFile */

bool TCOD_console_save_apf(TCOD_console_t pcon, const char *filename) {
	TCOD_console_data_t *con=pcon ? (TCOD_console_data_t *)pcon : TCOD_ctx.root;
	FILE* fp ;
	TCOD_IFNOT(con != NULL) return false;
	detectBigEndianness();

	fp = fopen(filename, "wb");
	if(fp == NULL) {
		return false;
	}
	else {
		int x,y;
		uint32_t riffSize = 0;
		uint32_t imgDetailsSize ;
		SettingsDataV1 settingsData;
		ImageDetailsV1 imgData;
		fpos_t posRiffSize;
		uint32_t settingsSz ;
		uint32_t layerImageSize ;
		uint32_t layerChunkSize ;
		/*  riff header*/
		putFourCC("RIFF",fp);
		fgetpos(fp,&posRiffSize);
		put32(0,fp); 

			/* APF_ header */
			putFourCC("apf ",fp);
			riffSize += 4;

				/* settings */
				settingsData.show_grid = 0;
				settingsData.grid_width = 8;
				settingsData.grid_height = 8;
				settingsSz = sizeof(uint32_t) + sizeof settingsData;
				putFourCC("sett",fp);
				put32(l32(settingsSz),fp);
				put32(l32(1),fp);
				putData((void*)&settingsData,sizeof settingsData,fp);
				if (settingsSz&1){
					put8(0,fp);
					riffSize++;
				}
				riffSize += 4+4+settingsSz;

				/* image details */
				imgData.width = con->w;
				imgData.height = con->h;
				imgData.filter = 0;
				imgData.format = 0;
				imgDetailsSize = sizeof(uint32_t) + sizeof imgData;
				putFourCC("imgd",fp);
				put32(l32(imgDetailsSize),fp);
				put32(l32(1),fp); 
				putData((void*)&imgData,sizeof imgData,fp);
				if (imgDetailsSize&1){
					put8(0,fp);
					riffSize++;
				}
				riffSize += 4+4+imgDetailsSize;

				/* now write the layers as a RIFF list
				   the first layer is the lowest layer
				   Assume imgData filter = uncompressed, and imgData format = CRGB */
				layerImageSize = imgData.width*imgData.height*7;
				layerChunkSize = sizeof(uint32_t) /* version */
						+ sizeof(LayerV2) /* header */
						+ layerImageSize; /* data */

				putFourCC("layr",fp); /* layer */
				put32(l32(layerChunkSize),fp);
					/* VERSION -> */
					put32(l32(2),fp);
					/* Data */
					putFourCC("LAY0",fp);
					put32(l32(0),fp);
					put32(l32(255),fp);
					put32(l32(255),fp);
					put32(l32(1),fp);
					put32(l32(0),fp);
					put32(l32(layerImageSize),fp);

					/* now write out the data */

					for(x = 0; x < con->w; x++) {
						for(y = 0; y < con->h; y++) {
							TCOD_color_t fore,back;
							int c=TCOD_console_get_char(con,x,y);
							fore=TCOD_console_get_char_foreground(con,x,y);			
							back=TCOD_console_get_char_background(con,x,y);
							put8(c, fp);
							put8(fore.r,fp);			
							put8(fore.g,fp);			
							put8(fore.b,fp);			
							put8(back.r,fp);			
							put8(back.g,fp);			
							put8(back.b,fp);
						}
					}

					if (layerChunkSize&1){
						put8(0,fp); /* padding bit */
						riffSize++;
					}

				riffSize += 2*sizeof(uint32_t)+layerChunkSize;

		fsetpos(fp,&posRiffSize);
		put32(l32(riffSize),fp);
	}

	fclose(fp);
	return true;
}

typedef struct {
	LayerV1 headerv1;
	LayerV2 headerv2;
	uint8_t* data; /* dynamically allocated */
}  LayerData;

typedef struct {
	ImageDetailsV1 details;
	SettingsDataV1 settings;
	LayerData layer;
} Data;

bool TCOD_console_load_apf(TCOD_console_t pcon, const char *filename) {
	uint32_t sett = fourCC("sett");
	uint32_t imgd = fourCC("imgd");
	/*
	uint32_t LIST = fourCC("LIST");
	uint32_t LAYR = fourCC("LAYR");
	*/
	uint32_t layr = fourCC("layr");
	FILE* fp ;
	Data data; 
	TCOD_console_data_t *con=pcon ? (TCOD_console_data_t *)pcon : TCOD_ctx.root;
	TCOD_IFNOT(con != NULL) return false;

	detectBigEndianness();
	data.details.width = 1;
	data.details.height = 1;
	data.details.filter = 0;
	data.details.format = 0;

	data.settings.show_grid = true;
	data.settings.grid_width = 10;
	data.settings.grid_height = 10;

	#define ERR(x) {printf("Error: %s\n. Aborting operation.",x); return false;}
	#define ERR_NEWER(x) {printf("Error: It looks like this file was made with a newer version of Ascii-Paint\n. In particular the %s field. Aborting operation.",x); return false;}

	fp = fopen(filename, "rb");
	if(fp == NULL) {
		printf("The file %s could not be loaded.\n", filename);
		return false;
	}
	else {
		/* read the header */
		uint32_t riff;
		uint32_t riffSize;
		int index = 0;
		int x,y;
		uint8_t *imgData;
		bool keepGoing = true;
		if (! get32(&riff,fp) || ! fourCCequals(riff,"RIFF")){
			ERR("File doesn't have a RIFF header");
		}
		if (!get32(&riffSize,fp)) ERR("No RIFF size field!");
		fix32(&riffSize);

		while(keepGoing && fp){ /* for each subfield, try to find the APF_ field */
			uint32_t apf;
			if (! get32(&apf,fp)) break;
			if (fourCCequals(apf,"apf ") || fourCCequals(apf,"APF ")){
				/* Process APF segment */
				while(keepGoing && fp){
					uint32_t seg;
					if (! get32(&seg,fp)){
						keepGoing = false;
						break;
					}
					else {
						if (seg==sett){
							/* size */
							uint32_t sz;
							uint32_t ver;
							SettingsDataV1 settingsData;

							get32(&sz,fp);
							fix32(&sz);
							/* version */
							get32(&ver,fp);
							fix32(&ver);
							if (ver!=1) ERR_NEWER("settings");
							/* ver must be 1 */
							if (! getData((void*)&settingsData,sizeof settingsData,fp)) ERR("Can't read settings.");
							data.settings = settingsData;
							fixSettings(&data.settings);

						}
						else if (seg==imgd){
							/* sz */
							uint32_t sz;
							uint32_t ver;
							ImageDetailsV1 dets;

							get32(&sz,fp);
							fix32(&sz);
							/* version */
							get32(&ver,fp);
							fix32(&ver);
							if (ver!=1) ERR_NEWER("image details");
							/* ver must be 1 */
							if (! getData((void*)&dets, sizeof dets, fp)) ERR("Can't read image details.");
							data.details = dets;
							fixImage(&data.details);

							/* get canvas ready */
							TCOD_IFNOT ( data.details.width > 0 && data.details.height > 0) {
								fclose(fp);
								return false;
							}
							if ( con->w != data.details.width || con->h != data.details.height ) {
								/* resize console */
								TCOD_console_data_free(con);
								con->w = data.details.width;
								con->h = data.details.height;
								TCOD_console_data_alloc(con);
							}

						}
						else if (seg==layr){
							uint32_t sz;
							uint32_t ver;

							get32(&sz,fp);
							fix32(&sz);
							/* version */
							get32(&ver,fp);
							fix32(&ver);
							if (ver>2) ERR_NEWER("layer spec");

							if (ver==1){
								if (! getData((void*)&data.layer.headerv1, sizeof( LayerV1 ), fp)) ERR("Can't read layer header.");
								fixLayerv1(&data.layer.headerv1);

								/* Read in the data chunk*/
								data.layer.data = (uint8_t*)malloc(sizeof(uint8_t)*data.layer.headerv1.dataSize);
								getData((void*) data.layer.data, data.layer.headerv1.dataSize, fp);
							}
							else if (ver==2){
								if (! getData((void*)&data.layer.headerv2, sizeof( LayerV2 ), fp)) ERR("Can't read layer header.");
								fixLayerv2(&data.layer.headerv2);

								/* Read in the data chunk */
								data.layer.data = (uint8_t*)malloc(sizeof(uint8_t)*data.layer.headerv2.dataSize);
								getData((void*) data.layer.data, data.layer.headerv2.dataSize, fp);

							}
						}
						else {
							/* skip unknown segment */
							uint32_t sz;
							get32(&sz,fp);
							fix32(&sz);
							fseek(fp,sz,SEEK_CUR);
						}
					}
				}

				/* we're done! */
				keepGoing = false;
			}
			else {
				/* skip this segment */
				uint32_t sz;
				get32(&sz,fp);
				fseek(fp,sz,SEEK_CUR);
			}
		}

		imgData = data.layer.data;
		for(x = 0; x < con->w; x++) {
			for(y = 0; y < con->h; y++) {
	    	TCOD_color_t fore,back;
		    int c = (unsigned char)(imgData[index++]);
		    fore.r = (uint8_t)(imgData[index++]);
		    fore.g = (uint8_t)(imgData[index++]);
		    fore.b = (uint8_t)(imgData[index++]);
		    back.r = (uint8_t)(imgData[index++]);
		    back.g = (uint8_t)(imgData[index++]);
		    back.b = (uint8_t)(imgData[index++]);
		    TCOD_console_put_char_ex(con,x,y,c,fore,back);
			}
		}

		free (data.layer.data);
	}
	fclose(fp);

	return true;
}
/*

bool ApfFile::Load(std::string filename){
	detectBigEndianness();

	uint32_t sett = fourCC("sett");
	uint32_t imgd = fourCC("imgd");
	uint32_t LIST = fourCC("LIST");
	uint32_t LAYR = fourCC("LAYR");
	uint32_t layr = fourCC("layr");

	Data data; // File data

	data.details.width = 1;
	data.details.height = 1;
	data.details.filter = FILTER_TYPE_UNCOMPRESSED;
	data.details.format = FORMAT_TYPE_CRGBRGB;

	data.settings.show_grid = true;
	data.settings.grid_width = 10;
	data.settings.grid_height = 10;

	data.currentLayer = NULL;

	#define ERR(x) {printf("Error: %s\n. Aborting operation.",x); return false;}
	#define ERR_NEWER(x) {printf("Error: It looks like this file was made with a newer version of Ascii-Paint\n. In particular the %s field. Aborting operation.",x); return false;}

	FILE* fp = fopen(filename.c_str(), "rb");
	if(fp == NULL) {
		printf("The file %s could not be loaded.\n", filename.c_str());
		return false;
	}
	else {
		// read the header
		uint32_t riff;
		if (not get32(&riff,fp)
			or
			not fourCCequals(riff,"RIFF")){
			ERR("File doesn't have a RIFF header");
		}
		// else
		uint32_t riffSize;
		if (!get32(&riffSize,fp)) ERR("No RIFF size field!");
		fix(&riffSize);

		bool keepGoing = true;
		while(keepGoing and fp){ // for each subfield, try to find the APF_ field
			uint32_t apf;
			if (not get32(&apf,fp)) break;
			if (fourCCequals(apf,"apf ") or fourCCequals(apf,"APF ")){
				// Process APF segment
				while(keepGoing and fp){
					uint32_t seg;
					if (not get32(&seg,fp)){
						keepGoing = false;
						break;
					}
					else {
						if (seg==sett){
							// size
							uint32_t sz;
							get32(&sz,fp);
							fix(&sz);
							// version
							uint32_t ver;
							get32(&ver,fp);
							fix(&ver);
							if (ver!=1) ERR_NEWER("settings");
							// ver must be 1
							SettingsDataV1 settingsData;
							if (not getData((void*)&settingsData,sizeof settingsData,fp)) ERR("Can't read settings.");
							data.settings = settingsData;
							fix(&data.settings);

							// Change app settings
							app->setGridDimensions(data.settings.grid_width,data.settings.grid_height);
							app->setShowGrid(data.settings.show_grid==1);
						}
						else if (seg==imgd){
							// sz
							uint32_t sz;
							get32(&sz,fp);
							fix(&sz);
							// version
							uint32_t ver;
							get32(&ver,fp);
							fix(&ver);
							if (ver!=1) ERR_NEWER("image details");
							// ver must be 1
							ImageDetailsV1 dets;
							if (not getData((void*)&dets, sizeof dets, fp)) ERR("Can't read image details.");
							data.details = dets;
							fix(&data.details);

							// get canvas ready
							app->canvasWidth = data.details.width;
							app->canvasHeight = data.details.height;
							app->initCanvas();

							// delete new layer
							app->deleteLayer(app->getCurrentLayer()->name);

						}
						else if (seg==layr){
							// printf("Found a layer\n");

							// sz
							uint32_t sz;
							get32(&sz,fp);
							fix(&sz);
							// version
							uint32_t ver;
							get32(&ver,fp);
							fix(&ver);
							if (ver>2) ERR_NEWER("layer spec");

							if (ver==1){
								LayerV1 layerHeader;
								if (not getData((void*)&layerHeader, sizeof layerHeader, fp)) ERR("Can't read layer header.");
								fix(&layerHeader);

								// creat new layer data
								LayerData* ld = new LayerData;
								ld->header = layerHeader; // already fix'd
								ld->data = new uint8[ld->header.dataSize];

								// Read in the data chunk
								getData((void*) ld->data, ld->header.dataSize, fp);

								// push layer onto the list
								data.currentLayer = ld;
								data.layers.push(ld);
							}
							else if (ver==2){
								LayerV2 layerHeader;
								if (not getData((void*)&layerHeader, sizeof layerHeader, fp)) ERR("Can't read layer header.");
								fix(&layerHeader);

								// creat new layer data
								LayerData* ld = new LayerData;
								ld->header = layerHeader; // already fix'd
								ld->data = new uint8[ld->header.dataSize];

								// Read in the data chunk
								getData((void*) ld->data, ld->header.dataSize, fp);

								// push layer onto the list
								data.currentLayer = ld;
								data.layers.push(ld);
							}
						}
						else {
							// skip unknown segment
							uint32_t sz;
							get32(&sz,fp);
							fix(&sz);
							fseek(fp,sz,SEEK_CUR);
						}
					}
				}

				// we're done!
				keepGoing = false;
			}
			else {
				// skip this segment
				uint32_t sz;
				get32(&sz,fp);
				fseek(fp,sz,SEEK_CUR);
			}
		}

		// finally, copy the layers into the current document
		for(int i=0;i<data.layers.size();i++){
			// for now, just load the first layer
			LayerData* ld = data.layers.get(i);

			// canvas width/height have already been set...
			Layer* l = app->addNewLayer();

			// Parse layer header
			l->name = fromFourCC(ld->header.name);
			l->fgalpha = ld->header.fgalpha;
			l->bgalpha = ld->header.bgalpha;
			l->visible = (ld->header.visible==1);
			// l->compositingMode =

			// Copy data into currently selected canvas
			uint8_t* imgData = ld->data;
			CanvasImage *img = new CanvasImage;
			// Write the brush data for every brush in the image
			int index = 0;
			for(int x = 0; x < app->canvasWidth; x++) {
				for(int y = 0; y < app->canvasHeight; y++) {
					Brush b;
					b.symbol = (unsigned char)(imgData[index++]);
					b.fore.r = (uint8_t)(imgData[index++]);
					b.fore.g = (uint8_t)(imgData[index++]);
					b.fore.b = (uint8_t)(imgData[index++]);
					b.back.r = (uint8_t)(imgData[index++]);
					b.back.g = (uint8_t)(imgData[index++]);
					b.back.b = (uint8_t)(imgData[index++]);
					b.solid = true; // deprecated
					b.walkable = true; // deprecated
					img->push_back(b);
				}
			}

			app->setCanvasImage(*img);
			delete img;
		}

		// then free all the temporary layer data
		for(int i=0;i<data.layers.size();i++){
			delete[]data.layers.get(i)->data;
			delete data.layers.get(i);
		}

		// and update the layer widget
		app->gui->layerWidget->regenerateLayerList();
	}
	fclose(fp);

	return true;
}
*/

#endif /* TCOD_CONSOLE_SUPPORT */