/*
Copyright (C) 2007, 2010 - Bit-Blot

This file is part of Aquaria.

Aquaria 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "Core.h"
#include "Texture.h"
#include "AfterEffect.h"
#include "Particles.h"

#include <time.h>
#include <iostream>

#ifdef BBGE_BUILD_UNIX
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include <assert.h>

#if __APPLE__
#include <Carbon/Carbon.h>
#endif

#if BBGE_BUILD_WINDOWS
#include <shlobj.h>
#include <direct.h>
#endif

#ifdef BBGE_BUILD_SDL
	#include "SDL_syswm.h"
	#ifdef BBGE_BUILD_SDL2
	static SDL_Window *gScreen=0;
	static SDL_GLContext gGLctx=0;
	#else
	static SDL_Surface *gScreen=0;
	#endif

	bool ignoreNextMouse=false;
	Vector unchange;
#endif

Core *core = 0;

#ifdef BBGE_BUILD_WINDOWS
	HICON icon_windows = 0;
#endif

void Core::initIcon()
{
#ifdef BBGE_BUILD_WINDOWS
	HINSTANCE handle = ::GetModuleHandle(NULL);
	//if (icon_windows)
	//	::DestroyIcon(icon_windows);

	icon_windows = ::LoadIcon(handle, "icon");

	SDL_SysWMinfo wminfo;
	SDL_VERSION(&wminfo.version)
	if (SDL_GetWindowWMInfo(gScreen, &wminfo) != 1)
	{
		//errorLog("wrong SDL version");
		// error: wrong SDL version
	}

	HWND hwnd = wminfo.info.win.window;

	::SetClassLong(hwnd, GCL_HICON, (LONG) icon_windows);
#endif
}

void Core::resetCamera()
{
	cameraPos = Vector(0,0);
}

ParticleEffect* Core::createParticleEffect(const std::string &name, const Vector &position, int layer, float rotz)
{
	ParticleEffect *e = new ParticleEffect();
	e->load(name);
	e->position = position;
	e->start();
	e->setDie(true);
	e->rotation.z = rotz;
	core->getTopStateData()->addRenderObject(e, layer);
	return e;
}

void Core::unloadDevice()
{
	for (int i = 0; i < renderObjectLayers.size(); i++)
	{
		RenderObjectLayer *r = &renderObjectLayers[i];
		RenderObject *robj = r->getFirst();
		while (robj)
		{
			robj->unloadDevice();
			robj = r->getNext();
		}
	}
	frameBuffer.unloadDevice();

	if (afterEffectManager)
		afterEffectManager->unloadDevice();
}

void Core::reloadDevice()
{
	for (int i = 0; i < renderObjectLayers.size(); i++)
	{
		RenderObjectLayer *r = &renderObjectLayers[i];
		r->reloadDevice();
		RenderObject *robj = r->getFirst();
		while (robj)
		{
			robj->reloadDevice();
			robj = r->getNext();
		}
	}
	frameBuffer.reloadDevice();

	if (afterEffectManager)
		afterEffectManager->reloadDevice();
}

void Core::resetGraphics(int w, int h, int fullscreen, int vsync, int bpp)
{
	if (fullscreen == -1)
		fullscreen = _fullscreen;

	if (vsync == -1)
		vsync = _vsync;

	if (w == -1)
		w = width;

	if (h == -1)
		h = height;

	if (bpp == -1)
		bpp = _bpp;

	unloadDevice();
	unloadResources();

	shutdownGraphicsLibrary();

	initGraphicsLibrary(w, h, fullscreen, vsync, bpp);
	
	enable2DWide(w, h);

	reloadResources();
	reloadDevice();


	resetTimer();
}

void Core::toggleScreenMode(int t)
{
#ifdef BBGE_BUILD_GLFW
/*
		glfwCloseWindow();

		createWindow(800,600,32,false,"");
		initGraphicsLibrary(false, true);
		enable2D(800);
		//reloadResources();
		*/
#endif
#ifdef BBGE_BUILD_SDL
	sound->pause();
	resetGraphics(-1, -1, t);
	cacheRender();
	resetTimer();
	sound->resume();
#endif
}

void Core::updateCursorFromJoystick(float dt, int spd)
{
	//debugLog("updating mouse from joystick");

	core->mouse.position += joystick.position*dt*spd;

/*
	if (!joystick.position.isZero())
		setMousePosition(core->mouse.position);
	*/

	doMouseConstraint();
}

void Core::setWindowCaption(const std::string &caption, const std::string &icon)
{
#ifdef BBGE_BUILD_SDL
#ifndef BBGE_BUILD_SDL2
	SDL_WM_SetCaption(caption.c_str(), icon.c_str());
#endif
#endif
}

RenderObjectLayer *Core::getRenderObjectLayer(int i)
{
	if (i == LR_NONE)
		return 0;
	return &renderObjectLayers[i];
}

#if defined(BBGE_BUILD_WINDOWS) && !defined(BBGE_BUILD_SDL)
	LPDIRECTINPUT8			g_pDI       = NULL; // The DirectInput object
	LPDIRECTINPUTDEVICE8	g_pKeyboard = NULL; // The keyboard device
	LPDIRECTINPUTDEVICE8	g_pMouse	= NULL;

	D3DCOLOR				d3dColor	=0xFFFFFFFF;
#endif

#ifdef BBGE_BUILD_DIRECTX

	__int64 timerStart=0, timerEnd=0, timerFreq=0;
	//Direct3D 9 interface
	IDirect3D9* d3d						= NULL;
	//Capabilities of graphics adapter
	D3DCAPS9 d3dCaps;

	//Direct3D present parameters
	D3DPRESENT_PARAMETERS d3dPresent;
	LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device
	LPD3DXSPRITE			d3dSprite	= NULL;
	LPD3DXMATRIXSTACK		d3dMatrixStack = NULL;
	IDirect3DVertexBuffer9* vertexBuffer	= NULL;
	IDirect3DVertexBuffer9* preTransVertexBuffer = NULL;

	//Custom vertex
	struct TLVERTEX
	{
	    float x;
	    float y;
	    float z;
	    //float rhw;
	    D3DCOLOR colour;
	    float u;
	    float v;
	};
	const DWORD D3DFVF_TLVERTEX = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;
	struct PTLVERTEX
	{
	    float x;
	    float y;
	    float z;
	    float rhw;
	    D3DCOLOR colour;
	    float u;
	    float v;
	};

	const DWORD D3DFVF_PTLVERTEX = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1;
#endif



	#ifdef BBGE_BUILD_DIRECTX
	/*
	LPDIRECT3DVERTEXBUFFER9 g_pVB        = NULL; // Buffer to hold vertices
	LPDIRECT3DTEXTURE9      g_pTexture   = NULL; // Our texture
	*/
	// A structure for our custom vertex type
	struct CUSTOMVERTEX
	{
	    FLOAT x, y, z, rhw; // The transformed position for the vertex
	    DWORD color;        // The vertex color
	};

	// Our custom FVF, which describes our custom vertex structure
	#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

	LPD3DXMATRIXSTACK Core::getD3DMatrixStack()
	{
		return d3dMatrixStack;
	}

	LPDIRECT3DDEVICE9 Core::getD3DDevice()
	{
		return g_pd3dDevice;
	}

	LPD3DXSPRITE Core::getD3DSprite()
	{
		return d3dSprite;
	}

	LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
	{
		switch( msg )
		{
	        case WM_DESTROY:
				//Cleanup();
				PostQuitMessage( 0 );
				return 0;
		}

	    return DefWindowProc( hWnd, msg, wParam, lParam );
}
	void Core::blitD3DVerts(IDirect3DTexture9 *texture, float v1x, float v1y, float v2x, float v2y, float v3x, float v3y, float v4x, float v4y)
	{
		TLVERTEX* vertices;

		//Lock the vertex buffer
		vertexBuffer->Lock(0, 0, (void**)&vertices, NULL);

		vertices[0].colour = d3dColor;
		vertices[0].x = v1x;
		vertices[0].y = v1y;
		vertices[0].z = 1.0f;
		vertices[0].u = 0.0f;
		vertices[0].v = 1.0f-1.0f;

		vertices[1].colour = d3dColor;
		vertices[1].x = v2x;
		vertices[1].y = v2y;
		vertices[1].z = 1.0f;
		vertices[1].u = 1.0f;
		vertices[1].v = 1.0f-1.0f;

		vertices[2].colour = d3dColor;
		vertices[2].x = v3x;
		vertices[2].y = v3y;
		vertices[2].z = 1.0f;
		vertices[2].u = 1.0f;
		vertices[2].v = 1.0f-0.0f;

		vertices[3].colour = d3dColor;
		vertices[3].x = v4x;
		vertices[3].y = v4y;
		vertices[3].z = 1.0f;
		vertices[3].u = 0.0f;
		vertices[3].v = 1.0f-0.0f;
		//Unlock the vertex buffer
		vertexBuffer->Unlock();

		//Set texture
		g_pd3dDevice->SetTexture (0, texture);

		//Draw image
		g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
	}

	void Core::blitD3DEx (IDirect3DTexture9 *texture, int w2, int h2, float u1, float v1, float u2, float v2)
	{
		TLVERTEX* vertices;

		/*
		int w2=width/2;
		int h2=height/2;
		*/
		//Lock the vertex buffer
		vertexBuffer->Lock(0, 0, (void**)&vertices, NULL);

		//Setup vertices
		//A -0.5f modifier is applied to vertex coordinates to match texture
		//and screen coords. Some drivers may compensate for this
		//automatically, but on others texture alignment errors are introduced
		//More information on this can be found in the Direct3D 9 documentation
		vertices[0].colour = d3dColor;
		vertices[0].x = -0.5f*w2;
		vertices[0].y = -0.5f*h2;
		vertices[0].z = 1.0f;
		//vertices[0].rhw = 1.0f;
		vertices[0].u = u1;
		vertices[0].v = 1.0f-v2;

		vertices[1].colour = d3dColor;
		vertices[1].x = 0.5f*w2;
		vertices[1].y = -0.5f*h2;
		vertices[1].z = 1.0f;
		//vertices[1].rhw = 1.0f;
		vertices[1].u = u2;
		vertices[1].v = 1.0f-v2;

		vertices[2].colour = d3dColor;
		vertices[2].x = 0.5f*w2;
		vertices[2].y = 0.5f*h2;
		vertices[2].z = 1.0f;
		//vertices[2].rhw = 1.0f;
		vertices[2].u = u2;
		vertices[2].v = 1.0f-v1;

		vertices[3].colour = d3dColor;
		vertices[3].x = -0.5f*w2;
		vertices[3].y = 0.5f*h2;
		vertices[3].z = 1.0f;
		//vertices[3].rhw = 1.0f;
		vertices[3].u = u1;
		vertices[3].v = 1.0f-v1;
		//Unlock the vertex buffer
		vertexBuffer->Unlock();

		//Set texture
		g_pd3dDevice->SetTexture (0, texture);

		//Draw image
		g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
	}

	void Core::blitD3DGradient(D3DCOLOR ulc0, D3DCOLOR ulc1, D3DCOLOR ulc2, D3DCOLOR ulc3)
	{
		TLVERTEX* vertices;

		//Lock the vertex buffer
		vertexBuffer->Lock(0, 0, (void**)&vertices, NULL);
		vertices[0].colour = ulc0;
		vertices[0].x = -0.5f;
		vertices[0].y = -0.5f;
		vertices[0].z = 1.0f;
		//vertices[0].rhw = 1.0f;
		vertices[0].u = 0.0f;
		vertices[0].v = 1.0f-1.0f;

		vertices[1].colour = ulc1;
		vertices[1].x = 0.5f;
		vertices[1].y = -0.5f;
		vertices[1].z = 1.0f;
		//vertices[1].rhw = 1.0f;
		vertices[1].u = 1.0f;
		vertices[1].v = 1.0f-1.0f;

		vertices[2].colour = ulc2;
		vertices[2].x = 0.5f;
		vertices[2].y = 0.5f;
		vertices[2].z = 1.0f;
		//vertices[2].rhw = 1.0f;
		vertices[2].u = 1.0f;
		vertices[2].v = 1.0f-0.0f;

		vertices[3].colour = ulc3;
		vertices[3].x = -0.5f;
		vertices[3].y = 0.5f;
		vertices[3].z = 1.0f;
		//vertices[3].rhw = 1.0f;
		vertices[3].u = 0.0f;
		vertices[3].v = 1.0f-0.0f;
		//Unlock the vertex buffer
		vertexBuffer->Unlock();

		//Set texture
		//g_pd3dDevice->SetTexture (0, texture);
		g_pd3dDevice->SetTexture (0, 0);

		//Draw image
		g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
	}

	void Core::blitD3DPreTrans(IDirect3DTexture9 *texture, float x, float y, int w2, int h2)
	{
		/*
		PTLVERTEX* vertices;
		//Lock the vertex buffer
		preTransVertexBuffer->Lock(0, 0, (void**)&vertices, NULL);
		*/
		TLVERTEX* vertices;
		//Lock the vertex buffer
		vertexBuffer->Lock(0, 0, (void**)&vertices, NULL);


		//Setup vertices
		//A -0.5f modifier is applied to vertex coordinates to match texture
		//and screen coords. Some drivers may compensate for this
		//automatically, but on others texture alignment errors are introduced
		//More information on this can be found in the Direct3D 9 documentation
		vertices[0].colour = d3dColor;
		vertices[0].x = x-0.5f*w2;
		vertices[0].y = y-0.5f*h2;
		vertices[0].z = 1.0f;
		//vertices[0].rhw = 1.0f;
		vertices[0].u = 0.0f;
		vertices[0].v = 1.0f-1.0f;

		vertices[1].colour = d3dColor;
		vertices[1].x = x+0.5f*w2;
		vertices[1].y = y-0.5f*h2;
		vertices[1].z = 1.0f;
		//vertices[1].rhw = 1.0f;
		vertices[1].u = 1.0f;
		vertices[1].v = 1.0f-1.0f;

		vertices[2].colour = d3dColor;
		vertices[2].x = x+0.5f*w2;
		vertices[2].y = y+0.5f*h2;
		vertices[2].z = 1.0f;
		//vertices[2].rhw = 1.0f;
		vertices[2].u = 1.0f;
		vertices[2].v = 1.0f-0.0f;

		vertices[3].colour = d3dColor;
		vertices[3].x = x-0.5f*w2;
		vertices[3].y = y+0.5f*h2;
		vertices[3].z = 1.0f;
		//vertices[3].rhw = 1.0f;
		vertices[3].u = 0.0f;
		vertices[3].v = 1.0f-0.0f;
		/*
		//Unlock the vertex buffer
		preTransVertexBuffer->Unlock();
		*/
		vertexBuffer->Unlock();


		//Set texture
		g_pd3dDevice->SetTexture (0, texture);

		//Draw image
		g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
	}
	void Core::blitD3D (IDirect3DTexture9 *texture, int w2, int h2)
	{
		TLVERTEX* vertices;
		//D3DCOLOR d3dColor = 0xFFFFFFFF;

		/*
		int w2=width/2;
		int h2=height/2;
		*/
		//Lock the vertex buffer
		vertexBuffer->Lock(0, 0, (void**)&vertices, NULL);

		//Setup verticeserr
		//A -0.5f modifier is applied to vertex coordinates to match texture
		//and screen coords. Some drivers may compensate for this
		//automatically, but on others texture alignment ors are introduced
		//More information on this can be found in the Direct3D 9 documentation
		vertices[0].colour = d3dColor;
		vertices[0].x = -0.5f*w2;
		vertices[0].y = -0.5f*h2;
		vertices[0].z = 1.0f;
		//vertices[0].rhw = 1.0f;
		vertices[0].u = 0.0f;
		vertices[0].v = 1.0f-1.0f;

		vertices[1].colour = d3dColor;
		vertices[1].x = 0.5f*w2;
		vertices[1].y = -0.5f*h2;
		vertices[1].z = 1.0f;
		//vertices[1].rhw = 1.0f;
		vertices[1].u = 1.0f;
		vertices[1].v = 1.0f-1.0f;

		vertices[2].colour = d3dColor;
		vertices[2].x = 0.5f*w2;
		vertices[2].y = 0.5f*h2;
		vertices[2].z = 1.0f;
		//vertices[2].rhw = 1.0f;
		vertices[2].u = 1.0f;
		vertices[2].v = 1.0f-0.0f;

		vertices[3].colour = d3dColor;
		vertices[3].x = -0.5f*w2;
		vertices[3].y = 0.5f*h2;
		vertices[3].z = 1.0f;
		//vertices[3].rhw = 1.0f;
		vertices[3].u = 0.0f;
		vertices[3].v = 1.0f-0.0f;
		//Unlock the vertex buffer
		vertexBuffer->Unlock();

		//Set texture
		g_pd3dDevice->SetTexture (0, texture);

		//Draw image
		g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
	}

	HRESULT InitD3D( HWND hWnd, bool fullscreen, int vsync)
	{
		// Create the D3D object.
		HRESULT hr;

		//Make Direct3D object
		d3d = Direct3DCreate9(D3D_SDK_VERSION);

		//Make sure NULL pointer was not returned
		if (!d3d)
			return FALSE;

		//Get device capabilities
		ZeroMemory (&d3dCaps, sizeof(d3dCaps));
		if (FAILED(d3d->GetDeviceCaps (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps)))
			return FALSE;

		//Setup present parameters
		ZeroMemory(&d3dPresent,sizeof(d3dPresent));
		d3dPresent.hDeviceWindow = hWnd;

		//Check if windowed
		if (!fullscreen)
		{
			D3DDISPLAYMODE d3ddm;
			RECT rWindow;

			//Get display mode
			d3d->GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &d3ddm);

			//Get window bounds
			GetClientRect (hWnd, &rWindow);

			//Setup screen dimensions
			core->width = rWindow.right - rWindow.left;
			core->height = rWindow.bottom - rWindow.top;

			//Setup backbuffer
			d3dPresent.Windowed = true;
			d3dPresent.BackBufferWidth = rWindow.right - rWindow.left;
			d3dPresent.BackBufferHeight = rWindow.bottom - rWindow.top;
		}
		else
		{
			d3dPresent.Windowed = false;
			d3dPresent.BackBufferWidth = core->width;
			d3dPresent.BackBufferHeight = core->height;
		}
		d3dPresent.BackBufferFormat = D3DFMT_A8R8G8B8;
		d3dPresent.BackBufferCount = 1;
		d3dPresent.SwapEffect = D3DSWAPEFFECT_DISCARD;

		if (vsync>0)
			d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
		else
			d3dPresent.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;


		//Check if hardware vertex processing is available
		if (d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		{
			debugLog("hardware T&L!");
			//Create device with hardware vertex processing
			hr = d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hWnd,
				D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dPresent, &g_pd3dDevice);
		}
		else
		{
			debugLog("no hardware T&L.");
			//Create device with software vertex processing
			hr = d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hWnd,
				D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dPresent, &g_pd3dDevice);
		}

		//Make sure device was created
		if (FAILED(hr))
		{
			errorLog ("directx init failed");
			return false;
		}

		g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		// Turn off culling
		g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
		/*
			D3DCULL_NONE = 1,
			D3DCULL_CW = 2,
			D3DCULL_CCW = 3,
		*/
		// Turn off D3D lighting
		g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
		// Turn on the zbuffer
		g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE);

		D3DXCreateSprite(core->getD3DDevice(), &d3dSprite);
		D3DXCreateMatrixStack(0, &d3dMatrixStack);

		//Set vertex shader
		g_pd3dDevice->SetVertexShader(NULL);
		g_pd3dDevice->SetFVF(D3DFVF_TLVERTEX);

		//Create vertex buffer
		g_pd3dDevice->CreateVertexBuffer(sizeof(TLVERTEX) * 4, NULL, D3DFVF_TLVERTEX, D3DPOOL_MANAGED, &vertexBuffer, NULL);
		g_pd3dDevice->SetStreamSource(0, vertexBuffer, 0, sizeof(TLVERTEX));

		/*
		g_pd3dDevice->CreateVertexBuffer(sizeof(PTLVERTEX) * 4, NULL, D3DFVF_TLVERTEX, D3DPOOL_MANAGED, &preTransVertexBuffer, NULL);
		g_pd3dDevice->SetStreamSource(0, preTransVertexBuffer, 0, sizeof(PTLVERTEX));
		*/

		g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

		g_pd3dDevice->SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
		g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
		g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
		g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);

		return S_OK;
	}
#endif


void Core::setColor(float r, float g, float b, float a)
{
#ifdef BBGE_BUILD_OPENGL
	glColor4f(r, g, b, a);
#endif
#ifdef BBGE_BUILD_DIRECTX
	d3dColor = D3DCOLOR_RGBA(int(r*255), int(g*255), int(b*255), int(a*255));
#endif
}

void Core::bindTexture(int stage, unsigned int handle)
{
#ifdef BBGE_BUILD_DIRECTX
	getD3DDevice()->SetTexture(stage, (IDirect3DBaseTexture9*)handle);
#endif
#ifdef BBGE_BUILD_OPENGL
	//glBindTexture(GL_TEXTURE_2D, handle);
#endif
}

void Core::translateMatrixStack(float x, float y, float z)
{
#ifdef BBGE_BUILD_OPENGL
	glTranslatef(x, y, z);
#endif
#ifdef BBGE_BUILD_DIRECTX
	/*
	D3DXMATRIX matTranslation;
	D3DXMatrixTranslation (&matTranslation, x, y, 0);
	*/
	/*
	float usex, usey;
    usex = x - (float)core->getWindowWidth() / 2;
    usey = -y + (float)core->getWindowHeight() / 2;
	*/
	//core->getD3DMatrixStack()->MultMatrixLocal(&matTranslation);
	core->getD3DMatrixStack()->TranslateLocal(x, y, z);
#endif
}

void Core::scaleMatrixStack(float x, float y, float z)
{
#ifdef BBGE_BUILD_OPENGL
	glScalef(x, y, z);
#endif
#ifdef BBGE_BUILD_DIRECTX
	if (x != 1 || y != 1)
		core->getD3DMatrixStack()->ScaleLocal(x, y, 1);
#endif
}

void Core::rotateMatrixStack(float x, float y, float z)
{
#ifdef BBGE_BUILD_OPENGL
	glRotatef(0, 0, 1, z);
#endif
#ifdef BBGE_BUILD_DIRECTX
	if (z != 0)
	{
		D3DXVECTOR3 axis(0,0,1);
		core->getD3DMatrixStack()->RotateAxisLocal(&axis,D3DXToRadian(z));
	}
#endif
}

void Core::applyMatrixStackToWorld()
{
#ifdef BBGE_BUILD_DIRECTX
	g_pd3dDevice->SetTransform(D3DTS_WORLD, core->getD3DMatrixStack()->GetTop());
#endif
}

void Core::rotateMatrixStack(float z)
{
#ifdef BBGE_BUILD_OPENGL
	glRotatef(0, 0, 1, z);
#endif
#ifdef BBGE_BUILD_DIRECTX
	//core->getD3DMatrixStack()->RotateAxis(0, 0, z);
	/*
	D3DXVECTOR3 axis(0,0,1);
	float angle = D3DXToRadian(z);
	if (angle == D3DX_PI)
		angle += 0.001f;
	core->getD3DMatrixStack()->RotateAxisLocal(&axis,angle);
	*/
	if (z != 0)
	{
		D3DXMATRIX mat;
		D3DXMatrixRotationZ(&mat,D3DXToRadian(z));
		core->getD3DMatrixStack()->MultMatrixLocal(&mat);
	}
#endif
}

bool Core::getShiftState()
{
	return getKeyState(KEY_LSHIFT) || getKeyState(KEY_RSHIFT);
}

bool Core::getAltState()
{
	return getKeyState(KEY_LALT) || getKeyState(KEY_RALT);
}

bool Core::getCtrlState()
{
	return getKeyState(KEY_LCONTROL) || getKeyState(KEY_RCONTROL);
}

bool Core::getMetaState()
{
	return getKeyState(KEY_LMETA) || getKeyState(KEY_RMETA);
}

void Core::errorLog(const std::string &s)
{
	messageBox("Error!", s);
	debugLog(s);
}

void Core::messageBox(const std::string &title, const std::string &msg)
{
	::messageBox(title, msg);
}

void Core::debugLog(const std::string &s)
{
	if (debugLogActive)
	{
		_logOut << s << std::endl;
	}
#ifdef _DEBUG
	std::cout << s << std::endl;
#endif
}

#ifdef BBGE_BUILD_WINDOWS
static bool checkWritable(const std::string& path, bool warn, bool critical)
{
	bool writeable = false;
	std::string f = path + "/~chk_wrt.tmp";
	FILE *fh = fopen(f.c_str(), "w");
	if(fh)
	{
		writeable = fwrite("abcdef", 5, 1, fh) == 1;
		fclose(fh);
		unlink(f.c_str());
	}
	if(!writeable)
	{
		if(warn)
		{
			std::ostringstream os;
			os << "Trying to use \"" << path << "\" as user data path, but it is not writeable.\n"
				<< "Please make sure the game is allowed to write to that directory.\n"
				<< "You can move the game to another location and run it there,\n"
				<< "or try running it as administrator, that may help as well.";
			if(critical)
				os << "\n\nWill now exit.";
			MessageBoxA(NULL, os.str().c_str(), "Need to write but can't!", MB_OK | MB_ICONERROR);
		}
		if(critical)
			exit(1);
	}
	return writeable;
}
#endif


const float SORT_DELAY = 10;
Core::Core(const std::string &filesystem, const std::string& extraDataDir, int numRenderLayers, const std::string &appName, int particleSize, std::string userDataSubFolder)
: ActionMapper(), StateManager(), appName(appName)
{
	sound = NULL;
	screenCapScale = Vector(1,1,1);
	timeUpdateType = TIMEUPDATE_DYNAMIC;
	_extraDataDir = extraDataDir;

	fixedFPS = 60;

	if (userDataSubFolder.empty())
		userDataSubFolder = appName;
		
#if defined(BBGE_BUILD_UNIX)
	const char *envr = getenv("HOME");
	if (envr == NULL)
        envr = ".";  // oh well.
	const std::string home(envr);

	mkdir(home.c_str(), 0700);  // just in case.

	// "/home/icculus/.Aquaria" or something. Spaces are okay.
	#ifdef BBGE_BUILD_MACOSX
	const std::string prefix("Library/Application Support/");
	#else
	const std::string prefix(".");
	#endif

	userDataFolder = home + "/" + prefix + userDataSubFolder;
	mkdir(userDataFolder.c_str(), 0700);
	debugLogPath = userDataFolder + "/";
	mkdir((userDataFolder + "/screenshots").c_str(), 0700);
	std::string prefpath(getPreferencesFolder());
	mkdir(prefpath.c_str(), 0700);
#else
	debugLogPath = "";
	userDataFolder = ".";

	#ifdef BBGE_BUILD_WINDOWS
	{
		if(checkWritable(userDataFolder, true, true)) // working dir?
		{
			puts("Using working directory as user directory.");
		}
		// TODO: we may want to use a user-specific path under windows as well
		// if the code below gets actually used, pass 2x false to checkWritable() above.
		// not sure about this right now -- FG
		/*else
		{
			puts("Working directory is not writeable...");
			char pathbuf[MAX_PATH];
			if(SHGetSpecialFolderPathA(NULL, &pathbuf[0], CSIDL_APPDATA, 0))
			{
				userDataFolder = pathbuf;
				userDataFolder += '/';
				userDataFolder += userDataSubFolder;
				for(uint32 i = 0; i < userDataFolder.length(); ++i)
					if(userDataFolder[i] == '\\')
						userDataFolder[i] = '/';
				debugLogPath = userDataFolder + "/";
				puts(("Using \"" + userDataFolder + "\" as user directory.").c_str());
				CreateDirectoryA(userDataFolder.c_str(), NULL);
				checkWritable(userDataFolder, true, true);
			}
			else
				puts("Failed to retrieve appdata path, using working dir."); // too bad, but can't do anything about it
		}
		*/
	}
	#endif
#endif

	_logOut.open((debugLogPath + "debug.log").c_str());
	debugLogActive = true;

	debugLogTextures = true;
	
	grabInputOnReentry = -1;

	srand(time(NULL));
	old_dt = 0;
	current_dt = 0;

	aspectX = 4;
	aspectY = 3;

	virtualOffX = virtualOffY = 0;
	vw2 = 0;
	vh2 = 0;

	viewOffX = viewOffY = 0;

	/*
	aspectX = 1440;  //4.0f;
	aspectY = 900;   //3.0f;
	*/

	particleManager = new ParticleManager(particleSize);
#ifdef BBGE_BUILD_SDL
	nowTicks = thenTicks = 0;
#endif
	_hasFocus = false;
	lib_graphics = lib_sound = lib_input = false;
	clearColor = Vector(0,0,0);
	updateCursorFromMouse = true;
	mouseConstraint = false;
	mouseCircle = 0;
	overrideStartLayer = 0;
	overrideEndLayer = 0;
	coreVerboseDebug = false;
	frameOutputMode = false;
	updateMouse = true;
	particlesPaused = false;
	joystickAsMouse = false;
	currentLayerPass = 0;
	flipMouseButtons = 0;
	joystickOverrideMouse = false;
	joystickEnabled = false;
	doScreenshot = false;
	baseCullRadius = 1;
	width = height = 0;
	afterEffectManagerLayer = 0;
	renderObjectLayers.resize(1);
	invGlobalScale = 1.0;
	invGlobalScaleSqr = 1.0;
	renderObjectCount = 0;
	avgFPS.resize(1);
	minimized = false;
	sortFlag = true;
	sortTimer = SORT_DELAY;
	numSavedScreenshots = 0;
	shuttingDown = false;
	clearedGarbageFlag = false;
	nestedMains = 0;
	afterEffectManager = 0;
	loopDone = false;
	core = this;

	#ifdef BBGE_BUILD_WINDOWS
		hRC = 0;
		hDC = 0;
		hWnd = 0;
	#endif

	for (int i = 0; i < KEY_MAXARRAY; i++)
	{
		keys[i] = 0;
	}

	aspect = (aspectX/aspectY);//320.0f/240.0f;
	//1.3333334f;

	globalResolutionScale = globalScale = Vector(1,1,1);

	initRenderObjectLayers(numRenderLayers);

	initPlatform(filesystem);
}

void Core::initPlatform(const std::string &filesystem)
{
#if defined(BBGE_BUILD_MACOSX) && !defined(BBGE_BUILD_MACOSX_NOBUNDLEPATH)
	// FIXME: filesystem not handled
	CFBundleRef mainBundle = CFBundleGetMainBundle();
	//CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
	CFURLRef resourcesURL = CFBundleCopyBundleURL(mainBundle);
	char path[PATH_MAX];
	if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
	{
		// error!
		debugLog("CFURLGetFileSystemRepresentation");
	}
	CFRelease(resourcesURL);
	debugLog(path);
	chdir(path);
#elif defined(BBGE_BUILD_UNIX)
	if (!filesystem.empty())
	{
		if (chdir(filesystem.c_str()) == 0)
			return;
		else
			debugLog("Failed to chdir to filesystem path " + filesystem);
	}
#ifdef BBGE_DATA_PREFIX
	if (chdir(BBGE_DATA_PREFIX) == 0 && chdir(appName.c_str()) == 0)
		return;
	else
		debugLog("Failed to chdir to filesystem path " BBGE_DATA_PREFIX + appName);
#endif
	char path[PATH_MAX];
	// always a symlink to this process's binary, on modern Linux systems.
	const ssize_t rc = readlink("/proc/self/exe", path, sizeof (path));
	if ( (rc == -1) || (rc >= sizeof (path)) )
	{
		// error!
		debugLog("readlink");
	}
	else
	{
		path[rc] = '\0';
		char *ptr = strrchr(path, '/');
		if (ptr != NULL)
		{
			*ptr = '\0';
			debugLog(path);
			if (chdir(path) != 0)
				debugLog("Failed to chdir to executable path" + std::string(path));
		}
	}
#endif
#ifdef BBGE_BUILD_WINDOWS
	if(filesystem.length())
	{
		if(_chdir(filesystem.c_str()) != 0)
		{
			debugLog("chdir failed: " + filesystem);
		}
	}
	// FIXME: filesystem not handled
#endif
}

std::string Core::getPreferencesFolder()
{
#ifdef BBGE_BUILD_UNIX
	return userDataFolder + "/preferences";
#endif
#ifdef BBGE_BUILD_WINDOWS
	return "";
#endif
}

std::string Core::getUserDataFolder()
{
	return userDataFolder;
}

#if BBGE_BUILD_UNIX
#include <sys/types.h>
#include <pwd.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

// based on code I wrote for PhysicsFS: http://icculus.org/physfs/
//  the zlib license on physfs allows this cut-and-pasting.
static int locateOneElement(char *buf)
{
	char *ptr;
	DIR *dirp;

	if (access(buf, F_OK) == 0)
		return(1);  // quick rejection: exists in current case.

	ptr = strrchr(buf, '/');  // find entry at end of path.
	if (ptr == NULL)
	{
		dirp = opendir(".");
		ptr = buf;
	}
	else
	{
		*ptr = '\0';
		dirp = opendir(buf);
		*ptr = '/';
		ptr++;  // point past dirsep to entry itself.
	}

	struct dirent *dent;
	while ((dent = readdir(dirp)) != NULL)
	{
		if (strcasecmp(dent->d_name, ptr) == 0)
		{
			strcpy(ptr, dent->d_name); // found a match. Overwrite with this case.
			closedir(dirp);
			return(1);
		}
	}

	// no match at all...
	closedir(dirp);
	return(0);
}
#endif


std::string Core::adjustFilenameCase(const char *_buf)
{
#ifdef BBGE_BUILD_UNIX  // any case is fine if not Linux.
	int rc = 1;
	char *buf = (char *) alloca(strlen(_buf) + 1);
	strcpy(buf, _buf);

	char *ptr = buf;
	while ((ptr = strchr(ptr + 1, '/')) != 0)
	{
		*ptr = '\0';  // block this path section off
		rc = locateOneElement(buf);
		*ptr = '/'; // restore path separator
		if (!rc)
			break;  // missing element in path.
	}

	// check final element...
	if (rc)
		rc = locateOneElement(buf);

	#if 0
	if (strcmp(_buf, buf) != 0)
	{
		fprintf(stderr, "Corrected filename case: '%s' => '%s (%s)'\n",
		        _buf, buf, rc ? "found" : "not found");
	}
	#endif

	return std::string(buf);
#else
	return std::string(_buf);
#endif
}


Core::~Core()
{
	if (particleManager)
	{
		delete particleManager;
	}
	if (sound)
	{
		delete sound;
		sound = 0;
	}
	debugLog("~Core()");
	_logOut.close();
	core = 0;
}

bool Core::hasFocus()
{
	return _hasFocus;
}

void Core::setInputGrab(bool on)
{
	if (isWindowFocus())
	{
#ifdef BBGE_BUILD_SDL
		#ifdef BBGE_BUILD_SDL2
		SDL_SetWindowGrab(gScreen, on ? SDL_TRUE : SDL_FALSE);
		#else
		SDL_WM_GrabInput(on?SDL_GRAB_ON:SDL_GRAB_OFF);
		#endif
#endif
	}
}

void Core::setReentryInputGrab(int on)
{
	if (grabInputOnReentry == -1)
	{
		setInputGrab(on);
	}
	else
	{
		setInputGrab(grabInputOnReentry);
	}
}

bool Core::isFullscreen()
{
	return _fullscreen;
}

bool Core::isShuttingDown()
{
	return shuttingDown;
}

void Core::init()
{
	setupFileAccess();

	flags.set(CF_CLEARBUFFERS);
	quitNestedMainFlag = false;
#ifdef BBGE_BUILD_GLFW
	if (!glfwInit())
		exit(0);
#endif
#ifdef BBGE_BUILD_SDL
#ifndef BBGE_BUILD_SDL2
	// Disable relative mouse motion at the edges of the screen, which breaks
	// mouse control for absolute input devices like Wacom tablets and touchscreens.
	SDL_putenv((char *) "SDL_MOUSE_RELATIVE=0");
#endif

	if((SDL_Init(0))==-1)
	{
		exit_error("Failed to init SDL");
	}
	
#endif
	/*
#ifdef BBGE_BUILD_DIRECTX
	if (!glfwInit())
		exit(0);
#endif
		*/
	loopDone = false;
	clearedGarbageFlag = false;

	initInputCodeMap();

	//glfwSetWindowSizeCallback(lockWindowSize);
}

void Core::initRenderObjectLayers(int num)
{
	renderObjectLayers.resize(num);
	renderObjectLayerOrder.resize(num);
	for (int i = 0; i < num; i++)
	{
		renderObjectLayerOrder[i] = i;
	}
}

bool Core::initSoundLibrary(const std::string &defaultDevice)
{
	debugLog("Creating SoundManager");
	sound = new SoundManager(defaultDevice);
	debugLog("Done");
	return sound != 0;
}

Vector Core::getGameCursorPosition()
{
	return core->cameraPos + mouse.position * Vector(1/core->globalScale.x, 1/core->globalScale.y, 1);
}

Vector Core::getGamePosition(const Vector &v)
{
	return core->cameraPos + (v * Vector(1/core->globalScale.x, 1/core->globalScale.y, 1));
}

bool Core::getMouseButtonState(int m)
{
#ifdef BBGE_BUILD_SDL
	int mcode=m;

	switch(m)
	{
	case 0: mcode=1; break;
	case 1: mcode=3; break;
	case 2: mcode=2; break;
	}

	Uint8 mousestate = SDL_GetMouseState(0,0);

	return mousestate & SDL_BUTTON(mcode);
#endif
	return false;
}

bool Core::getKeyState(int k)
{
#ifdef BBGE_BUILD_GLFW
	return glfwGetKey(k)==GLFW_PRESS;
#endif

#ifdef BBGE_BUILD_SDL
	if (k >= KEY_MAXARRAY || k < 0)
	{
		return 0;
	}
	return keys[k];
#endif

#ifdef BBGE_BUILD_WINDOWS
	if (k >= KEY_MAXARRAY || k < 0)
	{
		return 0;
	}
	return keys[k];
#endif

	return 0;
}

//#ifdef BBGE_BUILD_DIRECTX

//float sensitivity = 1.0;

Vector joychange;
Vector lastjoy;
void readJoystickData()
{
	/*
	if (core->joystickEnabled && core->numJoysticks > 0)
	{
		//DIJOYSTATE2 js;
		core->joysticks[0]->poll(&core->joystate);

		Vector joy = Vector(core->joystate.lX, core->joystate.lY);
		joychange = joy-lastjoy;
		lastjoy = joy;
		core->joystickPosition = Vector(core->joystate.lX - (65536/2), core->joystate.lY - (65536/2));
		core->joystickPosition /= (65536/2);

		// HACK: super hacky!!
		core->mouse.buttons.left = (core->joystate.rgbButtons[0] & 0x80) ? Buttons::DOWN : Buttons::UP;
		core->mouse.buttons.right = (core->joystate.rgbButtons[1] & 0x80) ? Buttons::DOWN : Buttons::UP;
	}
	*/
}

void readMouseData()
{
#if defined(BBGE_BUILD_WINDOWS) && !defined(BBGE_BUILD_SDL)
	if (!core->updateMouse) return;
    HRESULT       hr;
    DIMOUSESTATE2 dims2;      // DirectInput Mouse state structure

    if( NULL == g_pMouse )
        return;

    // Get the input's device state, and put the state in dims
    ZeroMemory( &dims2, sizeof(dims2) );
    hr = g_pMouse->GetDeviceState( sizeof(DIMOUSESTATE2), &dims2 );
    if( FAILED(hr) )
    {
        // DirectInput may be telling us that the input stream has been
        // interrupted.  We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done.
        // We just re-acquire and try again.

        // If input is lost then acquire and keep trying
        hr = g_pMouse->Acquire();
        while( hr == DIERR_INPUTLOST )
            hr = g_pMouse->Acquire();

        // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
        // may occur when the app is minimized or in the process of
        // switching, so just try again later
        return;
    }

	//float sensitivity = float(core->width) / float(core->getVirtualWidth());
	float sensitivity = 1;
	core->mouse.position.x += dims2.lX*sensitivity;
	core->mouse.position.y += dims2.lY*sensitivity;
	core->mouse.position.z += dims2.lZ;
	core->mouse.change.x = dims2.lX*sensitivity;
	core->mouse.change.y = dims2.lY*sensitivity;
	core->mouse.change.z = dims2.lZ;
	core->mouse.scrollWheelChange = dims2.lZ;
	if (!core->flipMouseButtons)
	{
		core->mouse.buttons.left = (dims2.rgbButtons[0] & 0x80) ? DOWN : UP;
		core->mouse.buttons.right = (dims2.rgbButtons[1] & 0x80) ? DOWN : UP;
	}
	else
	{
		core->mouse.buttons.left = (dims2.rgbButtons[1] & 0x80) ? DOWN : UP;
		core->mouse.buttons.right = (dims2.rgbButtons[0] & 0x80) ? DOWN : UP;
	}
	core->mouse.buttons.middle = (dims2.rgbButtons[2] & 0x80) ? DOWN : UP;

#elif defined(BBGE_BUILD_SDL)
	//core->mouse.position += dMouse;
#elif defined(BBGE_BUILD_GLFW)
	//HACK: may not always want 800x600 virtual
	/*
	static int lastx=400, lasty=300;
	int x, y;
	glfwGetMousePos(&x,&y);
	int mickeyx,mickeyy;
	mickeyx = x - lastx;
	mickeyy = y - lasty;
	lastx = x;
	lasty = y;
	core->mouse.position.x += mickeyx;
	core->mouse.position.y += mickeyy;
	*/

	int x,y;
	glfwGetMousePos(&x,&y);
	core->mouse.position = Vector(x, y);


/*
	int mid_x = core->width / 2;
	int mid_y = core->height / 2;
	int dx=0,dy=0;
	int x,y;
	glfwGetMousePos(&x, &y);
	// Don't do anything if mouse hasn't moved
	if (x == mid_x && y == mid_y)
	{
	}
	else
	{
		dx = x - mid_x;
		dy = y - mid_y;
	}

	std::ostringstream os;
	os << "d(" << dx << ", " << dy <<")";
	debugLog(os.str());

	core->mouse.position += Vector(dx, dy);


	// Now move the mouse back to the middle, because
	// we don't care where it really is, just how much
	// it moves.
	glfwSetMousePos(mid_x, mid_y);
	*/



	core->mouse.buttons.left = glfwGetMouseButton(GLFW_MOUSE_BUTTON_LEFT) ? DOWN : UP;
	core->mouse.buttons.right = glfwGetMouseButton(GLFW_MOUSE_BUTTON_RIGHT) ? DOWN : UP;
	core->mouse.buttons.middle = glfwGetMouseButton(GLFW_MOUSE_BUTTON_MIDDLE) ? DOWN : UP;
	core->mouse.scrollWheel = glfwGetMouseWheel();
#endif
}

void readKeyData()
{

#if defined(BBGE_BUILD_WINDOWS) && !defined(BBGE_BUILD_SDL)
	if( NULL == g_pKeyboard )
		return;
	HRESULT hr;
	BYTE    diks[256];
    // Get the input's device state, and put the state in dims
    ZeroMemory( diks, sizeof(diks) );
    hr = g_pKeyboard->GetDeviceState( sizeof(diks), diks );
    if( FAILED(hr) )
    {
        // DirectInput may be telling us that the input stream has been
        // interrupted.  We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done.
        // We just re-acquire and try again.

        // If input is lost then acquire and keep trying
        hr = g_pKeyboard->Acquire();
        while( hr == DIERR_INPUTLOST )
            hr = g_pKeyboard->Acquire();

        // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
        // may occur when the app is minimized or in the process of
        // switching, so just try again later
        return;
    }

    // Make a string of the index values of the keys that are down
    for(int i = 0; i < 256; i++ )
    {
        core->keys[i] = ( diks[i] & 0x80 );
    }
#endif
}
//#endif


bool Core::initJoystickLibrary(int numSticks)
{
	//joystickEnabled = false;
#ifdef BBGE_BUILD_SDL
#ifdef BBGE_BUILD_SDL2
	SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER);
#else
	SDL_InitSubSystem(SDL_INIT_JOYSTICK);
#endif
#endif

	if (numSticks > 0)
		joystick.init(0);

	joystickEnabled = true;
	/*
	numJoysticks = Joystick::deviceCount();
	std::ostringstream os;
	os << "Found " << numJoysticks << " joysticks";
	debugLog(os.str());
	if (numJoysticks > 0)
	{
		if (numJoysticks > 4)
			numJoysticks = 4;

		// HACK: memory leak... add code to clean this up!
		for (int i = 0; i < numJoysticks; i++) {
			joysticks[i] = new Joystick(i);
			joysticks[i]->open();

			// Print the name of the joystick.
			char name[MAX_PATH];
			joysticks[i]->deviceName(name);
			std::ostringstream os;
			os << "   Joystick " << i << ": " << name;
			debugLog(os.str());
		}
		joystickEnabled = true;
		return true;
	}
	*/

	return true;
}

bool Core::initInputLibrary()
{
	core->mouse.position = Vector(getWindowWidth()/2, getWindowHeight()/2);

#ifdef BBGE_BUILD_GFLW
	glfwDisable(GLFW_MOUSE_CURSOR);
	//glfwEnable( GLFW_SYSTEM_KEYS );
#endif
	for (int i = 0; i < KEY_MAXARRAY; i++)
	{
		keys[i] = 0;
	}
#if defined(BBGE_BUILD_WINDOWS) && !defined(BBGE_BUILD_SDL)

	HRESULT hr;
    BOOL    bExclusive = true;
    BOOL    bForeground = true;
    //BOOL    bImmediate = true;
    BOOL    bDisableWindowsKey = false;
    DWORD   dwCoopFlags;

    if( bExclusive )
        dwCoopFlags = DISCL_EXCLUSIVE;
    else
        dwCoopFlags = DISCL_NONEXCLUSIVE;

    if( bForeground )
        dwCoopFlags |= DISCL_FOREGROUND;
    else
        dwCoopFlags |= DISCL_BACKGROUND;

    // Disabling the windows key is only allowed only if we are in foreground nonexclusive
    if( bDisableWindowsKey && !bExclusive && bForeground )
        dwCoopFlags |= DISCL_NOWINKEY;

    // Create a DInput object
    if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
                                         IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
        return false;

    // Obtain an interface to the system keyboard device.
    if( FAILED( hr = g_pDI->CreateDevice( GUID_SysKeyboard, &g_pKeyboard, NULL ) ) )
        return false;

    // Set the data format to "Keyboard format" - a predefined data format
    //
    // A data format specifies which controls on a device we
    // are interested in, and how they should be reported.
    //
    // This tells DirectInput that we will be passing an array
    // of 256 bytes to IDirectInputDevice::GetDeviceState.
    if( FAILED( hr = g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
        return false;

    // Set the cooperativity level to let DirectInput know how
    // this device should interact with the system and with other
    // DirectInput applications.
    hr = g_pKeyboard->SetCooperativeLevel( this->hWnd, dwCoopFlags );
    if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive )
    {
		debugLog("could not set cooperative level");
        //FreeDirectInput();
		//errorLog ("failed to init input");
		/*
        MessageBox( hDlg, _T("SetCooperativeLevel() returned DIERR_UNSUPPORTED.\n")
                          _T("For security reasons, background exclusive Keyboard\n")
                          _T("access is not allowed."), _T("Keyboard"), MB_OK );
		*/
        //return false;;
    }

	/*
    if( FAILED(hr) )
	{
		errorLog("failed to init input");
		return false;
	}
	*/


    // Acquire the newly created device
    g_pKeyboard->Acquire();


//#ifdef BBGE_BUILD_DIRECTX

	if( FAILED( hr = g_pDI->CreateDevice( GUID_SysMouse, &g_pMouse, NULL ) ) )
        return false;

    // Set the data format to "Mouse format" - a predefined data format
    //
    // A data format specifies which controls on a device we
    // are interested in, and how they should be reported.
    //
    // This tells DirectInput that we will be passing a
    // DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
    if( FAILED( hr = g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) )
        return false;

    // Set the cooperativity level to let DirectInput know how
    // this device should interact with the system and with other
    // DirectInput applications.
    hr = g_pMouse->SetCooperativeLevel( this->hWnd, dwCoopFlags );
    if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive )
    {
        //FreeDirectInput();
		//errorLog ("mouse failed");
		debugLog("could not set cooperative level");
        //return false;
    }

	/*
    if( FAILED(hr) )
		return false;
	*/

    // Acquire the newly created device
    g_pMouse->Acquire();

#endif



	// joystick init
//#endif
	return true;
}

void Core::onUpdate(float dt)
{
	if (minimized) return;
	ActionMapper::onUpdate(dt);
	StateManager::onUpdate(dt);


	core->mouse.lastPosition = core->mouse.position;
	core->mouse.lastScrollWheel = core->mouse.scrollWheel;

	readKeyData();
	readMouseData();
	readJoystickData();
	pollEvents();
	joystick.update(dt);






	/*
	std::ostringstream os;
	os << "x: " << joystate.lX << " y: " << joystate.lY;
	os << " frx: " << joystate.lFRx << " fry: " << joystate.lFRy;
	debugLog(os.str());
	*/

	/*
	if (joystickOverrideMouse && !joychange.isZero())
	{
		Vector joy(joystate.lX, joystate.lY);
		//core->mouse.position += joychange * 0.001f;
		core->mouse.position = Vector(400,300) + ((joy * 600) / (65536/2))-300;
	}
	*/


	/*

	*/


	/*
	if (mouse.position.x < 0)
		mouse.position.x = 0;
	if (mouse.position.x > core->getVirtualWidth())
		mouse.position.x = core->getVirtualWidth();
	if (mouse.position.y < 0)
		mouse.position.y = 0;
	if (mouse.position.y > core->getVirtualHeight())
		mouse.position.y = core->getVirtualHeight();
	*/

	onMouseInput();

	/*
#ifdef BBGE_BUILD_GLFW
	glfwSetMousePos(mouse.position.x, mouse.position.y);
#endif
	*/
#ifdef BBGE_BUILD_DIRECTX
#endif
	//core->mouse.change = core->mouse.position - core->mouse.lastPosition;

	//core->mouse.scrollWheelChange = core->mouse.scrollWheel - core->mouse.lastScrollWheel;





	//script.update(dt);

	cameraPos.update(dt);
	globalScale.update(dt);

	if (afterEffectManager)
	{
		afterEffectManager->update(dt);
	}

	if (!sortFlag)
	{
		if (sortTimer>0)
		{
			sortTimer -= dt;
			if (sortTimer <= 0)
			{
				sortTimer = SORT_DELAY;
				sort();
			}
		}
	}
}

Vector Core::getClearColor()
{
	return clearColor;
}

void Core::setClearColor(const Vector &c)
{
	clearColor = c;

#ifdef BBGE_BUILD_OPENGL
	glClearColor(c.x, c.y, c.z, 0.0);
#endif

#ifdef BBGE_BUILD_DIRECTX

#endif
}

void Core::setSDLGLAttributes()
{
	std::ostringstream os;
	os << "setting vsync: " << _vsync;
	debugLog(os.str());

#ifdef BBGE_BUILD_SDL
#ifndef BBGE_BUILD_SDL2
	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync);
#endif

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#endif
}


#ifdef GLAPIENTRY
#undef GLAPIENTRY
#endif

#ifdef BBGE_BUILD_WINDOWS
#define GLAPIENTRY APIENTRY
#else
#define GLAPIENTRY
#endif

unsigned int Core::dbg_numRenderCalls = 0;

#ifdef BBGE_BUILD_OPENGL_DYNAMIC
#define GL_FUNC(ret,fn,params,call,rt) \
    extern "C" { \
        static ret (GLAPIENTRY *p##fn) params = NULL; \
        ret GLAPIENTRY fn params { ++Core::dbg_numRenderCalls; rt p##fn call; } \
    }
#include "OpenGLStubs.h"
#undef GL_FUNC

static bool lookup_glsym(const char *funcname, void **func)
{
	*func = SDL_GL_GetProcAddress(funcname);
	if (*func == NULL)
	{
		std::ostringstream os;
		os << "Failed to find OpenGL symbol \"" << funcname << "\"\n";
		errorLog(os.str());
		return false;
	}
	return true;
}

static bool lookup_all_glsyms(void)
{
	bool retval = true;
	#define GL_FUNC(ret,fn,params,call,rt) \
		if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
	#include "OpenGLStubs.h"
	#undef GL_FUNC
	return retval;
}
#endif


bool Core::initGraphicsLibrary(int width, int height, bool fullscreen, int vsync, int bpp, bool recreate)
{	
	static bool didOnce = false;
	
	aspectX = width;
	aspectY = height;

	aspect = (aspectX/aspectY);

	

	this->width = width;
	this->height = height;
	_vsync = vsync;
	_fullscreen = fullscreen;
	_bpp = bpp;

	_hasFocus = false;

#if defined(BBGE_BUILD_SDL)

	//setenv("SDL_VIDEO_CENTERED", "1", 1);
	//SDL_putenv("SDL_VIDEO_WINDOW_POS=400,300");

#ifndef BBGE_BUILD_SDL2
#if !defined(BBGE_BUILD_MACOSX)
	// have to cast away constness, since SDL_putenv() might be #defined to
	//  putenv(), which takes a (char *), and freaks out newer GCC releases
	//  when you try to pass a (const!) string literal here...  --ryan.
	SDL_putenv((char *) "SDL_VIDEO_CENTERED=1");
#endif
#endif
	//SDL_putenv((char *) "LIBGL_DEBUG=verbose"); // temp, to track errors on linux with nouveau drivers.

	if (recreate)
	{
		if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
		{
			exit_error(std::string("SDL Error: ") + std::string(SDL_GetError()));
		}

#if BBGE_BUILD_OPENGL_DYNAMIC
		if (SDL_GL_LoadLibrary(NULL) == -1)
		{
			std::string err = std::string("SDL_GL_LoadLibrary Error: ") + std::string(SDL_GetError());
			SDL_Quit();
			exit_error(err);
		}
#endif
	}

	setWindowCaption(appName, appName);

	initIcon();
    // Create window

	setSDLGLAttributes();

	//if (!didOnce)
	{
#ifdef BBGE_BUILD_SDL2
		Uint32 flags = 0;
		flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
		if (fullscreen)
			flags |= SDL_WINDOW_FULLSCREEN;
		gScreen = SDL_CreateWindow(appName.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
		if (gScreen == NULL)
		{
			std::ostringstream os;
			os << "Couldn't set resolution [" << width << "x" << height << "]\n" << SDL_GetError();
			errorLog(os.str());
			SDL_Quit();
			exit(0);
		}
		gGLctx = SDL_GL_CreateContext(gScreen);
		if (gGLctx == NULL)
		{
			std::ostringstream os;
			os << "Couldn't create OpenGL context!\n" << SDL_GetError();
			errorLog(os.str());
			SDL_Quit();
			exit(0);
		}
#else
		Uint32 flags = 0;
		flags = SDL_OPENGL;
		if (fullscreen)
			flags |= SDL_FULLSCREEN;

		gScreen = SDL_SetVideoMode(width, height, bpp, flags);
		if (gScreen == NULL)
		{
			std::ostringstream os;
			os << "Couldn't set resolution [" << width << "x" << height << "]\n" << SDL_GetError();
			SDL_Quit();
			exit_error(os.str());
		}
#endif

#if BBGE_BUILD_OPENGL_DYNAMIC
		if (!lookup_all_glsyms())
		{
			std::ostringstream os;
			os << "Couldn't load OpenGL symbols we need\n";
			SDL_Quit();
			exit_error(os.str());
		}
#endif
	}

	setWindowCaption(appName, appName);

#ifdef BBGE_BUILD_SDL2
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	SDL_GL_SwapWindow(gScreen);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	SDL_GL_SwapWindow(gScreen);
	if ((_vsync != 1) || (SDL_GL_SetSwapInterval(-1) == -1))
		SDL_GL_SetSwapInterval(_vsync);
	const char *name = SDL_GetCurrentVideoDriver();
	SDL_SetWindowGrab(gScreen, SDL_TRUE);
#else
	SDL_WM_GrabInput(grabInputOnReentry==0 ? SDL_GRAB_OFF : SDL_GRAB_ON);
	char name[256];
	SDL_VideoDriverName((char*)name, 256);
#endif

	glViewport(0, 0, width, height);
	glScissor(0, 0, width, height);

	std::ostringstream os2;
	os2 << "Video Driver Name [" << name << "]";
	debugLog(os2.str());

	SDL_ShowCursor(SDL_DISABLE);
	SDL_PumpEvents();

	for (int i = 0; i < KEY_MAXARRAY; i++)
	{
		keys[i] = 0;
	}

/*
#ifdef BBGE_BUILD_WINDOWS
	SDL_SysWMinfo wmInfo;
	SDL_GetWMInfo(&wmInfo);
	hWnd = wmInfo.window;
#endif
*/

#endif

#if defined(BBGE_BUILD_OPENGL)
	glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);				// Black Background
	glClearDepth(1.0);								// Depth Buffer Setup
	glDisable(GL_CULL_FACE);
	
	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glLoadIdentity();
	
	glFinish();

#ifdef BBGE_BUILD_GLFW
	glfwSwapInterval(vsync);
#endif

#endif



#if defined(BBGE_BUILD_DIRECTX)

	// Initialize Direct3D
	if( SUCCEEDED( InitD3D( this->hWnd, fullscreen, vsync ) ) )
	{
		// Show the window
		ShowWindow( this->hWnd, SW_SHOWDEFAULT );
		UpdateWindow( this->hWnd );
		//initPipeline(PT_NORMAL);
	}
	else
	{
		errorLog("Could not init D3D");
		exit(-1);
	}

#endif

	setClearColor(clearColor);
	
	clearBuffers();
	showBuffer();

	lib_graphics = true;

	_hasFocus = true;

	enumerateScreenModes();

	if (!didOnce)
		didOnce = true;

	// init success
	return true;
}

void Core::enumerateScreenModes()
{
	screenModes.clear();

#ifdef BBGE_BUILD_SDL
#ifdef BBGE_BUILD_SDL2
	SDL_DisplayMode mode;
	const int modecount = SDL_GetNumDisplayModes(0);
	if(modecount == 0){
		debugLog("No modes available!");
		return;
	}
	
	for (int i = 0; i < modecount; i++) {
		SDL_GetDisplayMode(0, i, &mode);
		if (mode.w && mode.h && (mode.w > mode.h))
		{
			screenModes.push_back(ScreenMode(i, mode.w, mode.h));
		}
	}

#else
	SDL_Rect **modes;
	int i;

	modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);

	if(modes == (SDL_Rect **)0){
		debugLog("No modes available!");
		return;
	}

	if(modes == (SDL_Rect **)-1){
		debugLog("All resolutions available.");
	}
	else{
		int c=0;
		for(i=0;modes[i];++i){
			c++;
		}
		for (i=c-1;i>=0;i--)
		{
			if (modes[i]->w > modes[i]->h)
			{
				screenModes.push_back(ScreenMode(i, modes[i]->w, modes[i]->h));
			}
		}
	}
#endif
#endif
}

void Core::shutdownSoundLibrary()
{
}

void Core::shutdownGraphicsLibrary(bool killVideo)
{
#ifdef BBGE_BUILD_SDL
	glFinish();
	if (killVideo) {
		#ifdef BBGE_BUILD_SDL2
		SDL_SetWindowGrab(gScreen, SDL_FALSE);
		SDL_GL_MakeCurrent(gScreen, NULL);
		SDL_GL_DeleteContext(gGLctx);
		SDL_DestroyWindow(gScreen);
		gGLctx = 0;
		SDL_QuitSubSystem(SDL_INIT_VIDEO);
		#else
		SDL_QuitSubSystem(SDL_INIT_VIDEO);
		SDL_WM_GrabInput(SDL_GRAB_OFF);
		#endif

		FrameBuffer::resetOpenGL();

		gScreen = 0;

#if BBGE_BUILD_OPENGL_DYNAMIC
		// reset all the entry points to NULL, so we know exactly what happened
		//  if we call a GL function after shutdown.
		#define GL_FUNC(ret,fn,params,call,rt) \
			p##fn = NULL;
		#include "OpenGLStubs.h"
		#undef GL_FUNC
#endif
	}
#endif

	_hasFocus = false;

	lib_graphics = false;

#ifdef BBGE_BUILD_WINDOWS
	if (icon_windows)
	{
		::DestroyIcon(icon_windows);
		icon_windows = 0;
	}
#endif

}

void Core::quit()
{
	enqueueJumpState("STATE_QUIT");
	//loopDone = true;
	//popAllStates();
}

void Core::applyState(const std::string &state)
{
	if (nocasecmp(state, "state_quit")==0)
	{
		loopDone = true;
	}
	StateManager::applyState(state);
}

#ifdef BBGE_BUILD_GLFW
void GLFWCALL windowResize(int w, int h)
{
	// this gets called on minimize + restore?
	if (w == 0 && h == 0)
	{
		core->minimized = true;
		return;
	}
	else
		core->minimized = false;
	if (w != core->width || h != core->height)
		glfwSetWindowSize(core->width,core->height);
}
#endif


#ifdef BBGE_BUILD_WINDOWS
void centerWindow(HWND hwnd)
{
    int x, y;
    HWND hwndDeskTop;
    RECT rcWnd, rcDeskTop;
    // Get a handle to the desktop window
    hwndDeskTop = ::GetDesktopWindow();
    // Get dimension of desktop in a rect
    ::GetWindowRect(hwndDeskTop, &rcDeskTop);
    // Get dimension of main window in a rect
    ::GetWindowRect(hwnd, &rcWnd);
    // Find center of desktop
	x = (rcDeskTop.right - rcDeskTop.left)/2;
	y = (rcDeskTop.bottom - rcDeskTop.top)/2;
    x -= (rcWnd.right - rcWnd.left)/2;
	y -= (rcWnd.bottom - rcWnd.top)/2;
    // Set top and left to center main window on desktop
    ::SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE);
//	::ShowWindow(hwnd, 1);
}
#endif

/*
void Core::adjustWindowPosition(int x, int y)
{
#ifdef BBGE_BUILD_WINDOWS
	RECT rcWnd;
	::GetWindowRect(hWnd, &rcWnd);
	rcWnd.left += x;
	rcWnd.top += y;
	::SetWindowPos(hWnd, HWND_TOP, rcWnd.left, rcWnd.top, 0, 0, SWP_NOSIZE);
#endif
}
*/

bool Core::createWindow(int width, int height, int bits, bool fullscreen, std::string windowTitle)
{
	this->width = width;
	this->height = height;

	redBits = greenBits = blueBits = alphaBits = 0;
#ifdef BBGE_BUILD_SDL
	return true;
#endif

#ifdef BBGE_BUILD_GLFW
	int redbits, greenbits, bluebits, alphabits;
	redbits = greenbits = bluebits = 8;
	alphabits = 0;
	switch(bits)
	{
	case 16:
		redbits = 5;
		greenbits = 6;
		bluebits = 5;
	break;
	case 24:
		redbits = 8;
		bluebits = 8;
		greenbits = 8;
		alphabits = 0;
	break;
	case 32:
		redbits = 8;
		greenbits = 8;
		bluebits = 8;
		alphabits = 8;
	break;
	case 8:
		redbits = 2;
		greenbits = 2;
		bluebits = 2;
	break;
	}
	if (glfwOpenWindow(width, height, redbits, greenbits, bluebits, 0, 0, 0, fullscreen ? GLFW_FULLSCREEN : GLFW_WINDOW) == GL_TRUE)
	{
		glfwSetWindowTitle(windowTitle.c_str());
		resize(width,height);


#ifdef BBGE_BUILD_WINDOWS
		this->hWnd = (HWND)glfwGetWindowHandle();

		if (!fullscreen)	centerWindow(hWnd);
#endif

		glfwSetWindowSizeCallback(windowResize);

		redBits = glfwGetWindowParam(GLFW_RED_BITS);
		blueBits = glfwGetWindowParam(GLFW_BLUE_BITS);
		greenBits = glfwGetWindowParam(GLFW_GREEN_BITS);
		alphaBits = glfwGetWindowParam(GLFW_ALPHA_BITS);

		if (redBits < 8 && (bits == 32 || bits == 24))
		{
			int sayBits = 32;
			std::ostringstream os;
			os << "(" << width << ", " << height << ") " << sayBits << "-bit mode could not be enabled. Please try setting your desktop to " << sayBits << "-bit color depth";
			if (!fullscreen)
				os << ", or try running in fullscreen.";
			else
				os << ".";
			os << " This resolution may not be supported on your machine.";
			errorLog(os.str());
			exit(0); return false;
		}
		return true;
	}
	else
		return false;
#endif

#ifdef BBGE_BUILD_DIRECTX
	// Register the window class
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      windowTitle.c_str(), NULL };
    RegisterClassEx( &wc );

	this->hWnd = CreateWindow( windowTitle.c_str(), windowTitle.c_str(),
							WS_OVERLAPPEDWINDOW, 100, 100, width, height+10,
							GetDesktopWindow(), NULL, wc.hInstance, NULL );
	return true;
#endif
}

// No longer part of C/C++ standard
#ifndef M_PI
#define M_PI           3.14159265358979323846
#endif

static void
bbgePerspective(float fovy, float aspect, float zNear, float zFar)
{
    float sine, cotangent, deltaZ;
    float radians = fovy / 2.0f * M_PI / 180.0f;

    deltaZ = zFar - zNear;
    sine = sinf(radians);
    if ((deltaZ == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) {
        return;
    }
    cotangent = cosf(radians) / sine;

    GLfloat m[4][4] = {
        { 1.0f, 0.0f, 0.0f, 0.0f },
        { 0.0f, 1.0f, 0.0f, 0.0f },
        { 0.0f, 0.0f, 1.0f, 0.0f },
        { 0.0f, 0.0f, 0.0f, 1.0f }
    };
    m[0][0] = (GLfloat) (cotangent / aspect);
    m[1][1] = (GLfloat) cotangent;
    m[2][2] = (GLfloat) (-(zFar + zNear) / deltaZ);
    m[2][3] = -1.0f;
    m[3][2] = (GLfloat) (-2.0f * zNear * zFar / deltaZ);
    m[3][3] = 0.0f;

    glMultMatrixf(&m[0][0]);
}

void Core::setPixelScale(int pixelScaleX, int pixelScaleY)
{
	/*
	piScaleX = pixelScaleX;
	piScaleY = pixelScaleY;
	*/
	virtualWidth = pixelScaleX;
	//MAX(virtualWidth, 800);
	virtualHeight = pixelScaleY;//int((pixelScale*aspectY)/aspectX);					//assumes 4:3 aspect ratio
	this->baseCullRadius = sqrtf(sqr(getVirtualWidth()/2) + sqr(getVirtualHeight()/2));

	std::ostringstream os;
	os << "virtual(" << virtualWidth << ", " << virtualHeight << ")";
	debugLog(os.str());
	
	vw2 = virtualWidth/2;
	vh2 = virtualHeight/2;

	center = Vector(baseVirtualWidth/2, baseVirtualHeight/2);


	virtualOffX = 0;
	virtualOffY = 0;

	int diff = 0;

	diff = virtualWidth-baseVirtualWidth;
	if (diff > 0)
		virtualOffX = ((virtualWidth-baseVirtualWidth)/2);
	else
		virtualOffX = 0;


	diff = virtualHeight-baseVirtualHeight;
	if (diff > 0)
		virtualOffY = ((virtualHeight-baseVirtualHeight)/2);
	else
		virtualOffY = 0;
}

// forcePixelScale used by Celu

void Core::enable2DWide(int rx, int ry)
{
	float aspect = float(rx) / float(ry);
	if (aspect >= 1.3f)
	{
		int vw = int(float(baseVirtualHeight) * (float(rx)/float(ry)));
		//vw = MAX(vw, baseVirtualWidth);
		core->enable2D(vw, baseVirtualHeight, 1);
	}
	else
	{
		int vh = int(float(baseVirtualWidth) * (float(ry)/float(rx)));
		//vh = MAX(vh, baseVirtualHeight);
		core->enable2D(baseVirtualWidth, vh, 1);
	}
}

static void bbgeOrtho2D(float left, float right, float bottom, float top)
{
    glOrtho(left, right, bottom, top, -1.0, 1.0);
}

void Core::enable2D(int pixelScaleX, int pixelScaleY, bool forcePixelScale)
{
	// why do this again? don't really get it
	/*
	if (mode == MODE_2D)
	{
		if (forcePixelScale || (pixelScaleX!=0 && core->width!=pixelScaleX) || (pixelScaleY!=0 && core->height!=pixelScaleY))
		{
			float widthFactor = core->width/float(pixelScaleX);
			float heightFactor = core->height/float(pixelScaleY);
			core->globalResolutionScale = Vector(widthFactor,heightFactor,1.0f);
			setPixelScale(pixelScaleX, pixelScaleY);

			std::ostringstream os;
			os << "top of call: ";
			os << "widthFactor: " << widthFactor;
			os << "heightFactor: " << heightFactor;
			debugLog(os.str());
		}
		return;
	}
	*/
	
#ifdef BBGE_BUILD_OPENGL

    GLint viewPort[4];
    glGetIntegerv(GL_VIEWPORT, viewPort);

    glMatrixMode(GL_PROJECTION);
    //glPushMatrix();
    glLoadIdentity();

	float vw=0,vh=0;

	viewOffX = viewOffY = 0;

	float aspect = float(width)/float(height);

	if (aspect >= 1.4f)
	{
		vw = float(baseVirtualWidth * viewPort[3]) / float(baseVirtualHeight);

		viewOffX = (viewPort[2] - vw) * 0.5f;
	}
	else if (aspect < 1.3f)
	{
		vh = float(baseVirtualHeight * viewPort[2]) / float(baseVirtualWidth);

		viewOffY = (viewPort[3] - vh) * 0.5f;
	}



	/*
	vh = float(baseVirtualHeight * viewPort[2]) / float(baseVirtualWidth);

	viewOffY = (viewPort[3] - vh) * 0.5f;
	*/
	

	/*
	std::ostringstream os;
	os << "vw: " << vw << " OFFX: " << viewOffX << " ";
	os << "vh: " << vh << " OFFY: " << viewOffY;
	debugLog(os.str());
	*/


	/*
	float aspect = float(width) / float (height);

	if (aspect < 1.3f)
	{
		viewOffX *= 0.5f;
	}
	*/

	
//#else
//	int offx=0,offy=0;
//#endif

	//+offx
	//-offx
	//glOrtho(0.0f,viewPort[2],viewPort[3],0.0f,-1000.0f,1000.0f);
	//glOrtho(0.0f+offx,viewPort[2]+offx,viewPort[3]+offy,0.0f+offy,-1.0f,1.0f);
	bbgeOrtho2D(0.0f-viewOffX,viewPort[2]-viewOffX,viewPort[3]-viewOffY,0.0f-viewOffY);
	/*
	static bool doOnce = false;
	if (!doOnce)
	{
		glOrtho(0.0f,viewPort[2],viewPort[3],0.0f,-10.0f,10.0f);
		doOnce = true;
	}
	*/
	//glOrtho(-viewPort[2]/2,viewPort[2]/2,viewPort[3]/2,-viewPort[3]/2,-10.0f,10.0f);
    //glOrtho(0, viewPort[2], 0, viewPort[3], -100, 100);

    glMatrixMode(GL_MODELVIEW);
    //glPushMatrix();
    glLoadIdentity();

	setupRenderPositionAndScale();
#endif

#ifdef BBGE_BUILD_DIRECTX
	D3DXMATRIX matOrtho;
	D3DXMATRIX matIdentity;

	//Setup orthographic projection matrix

	D3DXMatrixOrthoOffCenterLH(&matOrtho, 0, getWindowWidth(), getWindowHeight(), 0, 1, 10);
	//D3DXMatrixOrthoLH (&matOrtho, getWindowWidth(), getWindowHeight(), 1.0f, 10.0f);
	D3DXMatrixIdentity (&matIdentity);
	g_pd3dDevice->SetTransform (D3DTS_PROJECTION, &matOrtho);
	g_pd3dDevice->SetTransform (D3DTS_WORLD, &matIdentity);
	g_pd3dDevice->SetTransform (D3DTS_VIEW, &matIdentity);
	// For our world matrix, we will just leave it as the identity
	/*
    D3DXMATRIXA16 matWorld;
    D3DXMatrixIdentity( &matWorld );
    //D3DXMatrixRotationX( &matWorld, 0/1000.0f );
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

    // Set up our view matrix. A view matrix can be defined given an eye point,
    // a point to lookat, and a direction for which way is up. Here, we set the
    // eye five units back along the z-axis and up three units, look at the
    // origin, and define "up" to be in the y-direction.
    D3DXVECTOR3 vEyePt( 0.0f, 0.0f,0.0f );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    // For the projection matrix, we set up a perspective transform (which
    // transforms geometry from 3D view space to 2D viewport space, with
    // a perspective divide making objects smaller in the distance). To build
    // a perpsective transform, we need the field of view (1/4 pi is common),
    // the aspect ratio, and the near and far clipping planes (which define at
    // what distances geometry should be no longer be rendered).
	///LPDIRECT3DVIEWPORT3 Viewport;

	D3DVIEWPORT9 viewport;
	viewport.Width = core->getWindowWidth();
	viewport.Height = core->getWindowHeight();
	viewport.MaxZ = 5;
	viewport.MinZ = -5;
	viewport.X = 0;
	viewport.Y = 0;

	g_pd3dDevice->SetViewport( &viewport );

	D3DXMATRIXA16 matProj;
	D3DXMatrixOrthoLH(&matProj, 800, 600, -5, 5);
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
	*/

	// Create the viewport
	/*
	if (FAILED(g_pd3dDevice->CreateViewport(&Viewport,NULL)))
	{ errorLog("Failed to create a viewport"); };
	if (FAILED(g_pd3dDevice->AddViewport(Viewport)))
	{ errorLog("Failed to add a viewport"); };
	if (FAILED(g_pd3dDevice->SetViewport2(&Viewdata)))
	{ errorLog("Failed to set Viewport data"); };
	g_pd3dDevice->SetCurrentViewport(Viewport);
	*/

	/*
	D3DXMATRIXA16 matProj;
	D3DXMatrixOrthoLH(&matProj, 4, 3, -5, 5);
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
	*/

	/*
   D3DVIEWPORT9 viewport;
   g_pd3dDevice->GetViewport(&viewport);
   D3DXMATRIX matProj;
   D3DXMatrixOrthoLH(&matProj, viewport.Width, viewport.Height, -10, 10);
   g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
   */


#endif

	if (forcePixelScale || (pixelScaleX!=0 && core->width!=pixelScaleX) || (pixelScaleY!=0 && core->height!=pixelScaleY))
	{
		/*
		float f = core->width/float(pixelScale);
		core->globalResolutionScale = Vector(f,f,1.0f);
		*/
		//debugLog("HEEEREEE");
		float widthFactor = core->width/float(pixelScaleX);
		float heightFactor = core->height/float(pixelScaleY);
		//float heightFactor = 
		core->globalResolutionScale = Vector(widthFactor,heightFactor,1.0f);
		setPixelScale(pixelScaleX, pixelScaleY);

		//core->globalResolutionScale = Vector(1.5,1.5,1);
		/*
		std::ostringstream os;
		os << "bottom of call: ";
		os << "widthFactor: " << widthFactor;
		os << " heightFactor: " << heightFactor;
		debugLog(os.str());
		*/
	}
	setPixelScale(pixelScaleX, pixelScaleY);

	//core->globalResolutionScale.x = 1.6;

	//setupRenderPositionAndScale();

	
}

void Core::quitNestedMain()
{
	if (getNestedMains() > 1)
	{
		quitNestedMainFlag = true;
	}
}

void Core::resetTimer()
{
#ifdef BBGE_BUILD_GLFW
	glfwSetTime(0);
#endif
#ifdef BBGE_BUILD_SDL
	nowTicks = thenTicks = SDL_GetTicks();
#endif
#ifdef BBGE_BUILD_DIRECTX
	QueryPerformanceCounter((LARGE_INTEGER*)&timerEnd);
	timerStart = timerEnd;
#endif

	for (int i = 0; i < avgFPS.size(); i++)
	{
		avgFPS[i] = 0;
	}
}

void Core::setDockIcon(const std::string &ident)
{
}

void Core::setMousePosition(const Vector &p)
{
	Vector lp = core->mouse.position;

	core->mouse.position = p;
#if !defined(BBGE_BUILD_WINDOWS) && defined(BBGE_BUILD_GLFW)
	glfwSetMousePos(p.x,p.y);
#endif
#ifdef BBGE_BUILD_SDL
	float px = p.x + virtualOffX;
	float py = p.y;// + virtualOffY;

	#ifdef BBGE_BUILD_SDL2
	SDL_WarpMouseInWindow(gScreen, px * (float(width)/float(virtualWidth)), py * (float(height)/float(virtualHeight)));
	#else
	SDL_WarpMouse( px * (float(width)/float(virtualWidth)), py * (float(height)/float(virtualHeight)));
	#endif

	/*
	ignoreNextMouse = true;
	unchange = core->mouse.position - lp;
	*/
#endif

	/*
	std::ostringstream os;
	os << "setting position (" << p.x << ", " << p.y << ")";
	debugLog(os.str());
	*/
}

// used to update all render objects either uniformly or as part of a time sliced update process
void Core::updateRenderObjects(float dt)
{
	for (int c = 0; c < renderObjectLayers.size(); c++)
	{

		RenderObjectLayer *rl = &renderObjectLayers[c];

		if (!rl->update)
			continue;

		for (RenderObject *r = rl->getFirst(); r; r = rl->getNext())
		{
			r->update(dt);
		}
	}

	if (loopDone)
		return;

	if (clearedGarbageFlag)
	{
		clearedGarbageFlag = false;
	}
}

std::string Core::getEnqueuedJumpState()
{
	return this->enqueuedJumpState;
}

int screenshotNum = 0;
std::string getScreenshotFilename()
{
	while (true)
	{
		std::ostringstream os;
		os << core->getUserDataFolder() << "/screenshots/screen" << screenshotNum << ".tga";
		screenshotNum ++;
        std::string str(os.str());
		if (!core->exists(str))  // keep going until we hit an unused filename.
			return str;
	}
}

uint32 Core::getTicks()
{
#ifdef BBGE_BUILD_SDL
	return SDL_GetTicks();
#endif
	return 0;
}

float Core::stopWatch(int d)
{
	if (d)
	{
		stopWatchStartTime = getTicks()/1000.0f;
		return stopWatchStartTime;
	}
	else
	{
		return (getTicks()/1000.0f) - stopWatchStartTime;
	}

	return 0;
}

bool Core::isWindowFocus()
{
#ifdef BBGE_BUILD_SDL
	#ifdef BBGE_BUILD_SDL2
	return ((SDL_GetWindowFlags(gScreen) & SDL_WINDOW_INPUT_FOCUS) != 0);
	#else
	return ((SDL_GetAppState() & SDL_APPINPUTFOCUS) != 0);
	#endif
#endif
	return true;
}

void Core::onBackgroundUpdate()
{
#if BBGE_BUILD_SDL
	SDL_Delay(200);
#endif
}

void Core::main(float runTime)
{
	bool verbose = coreVerboseDebug;
	if (verbose) debugLog("entered Core::main");
	// cannot nest loops when the game is over
	if (loopDone) return;

	//QueryPerformanceCounter((LARGE_INTEGER*)&lastTime);
	//QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
	float dt;
	float counter = 0;
	int frames = 0;
	float real_dt = 0;
	//std::ofstream out("debug.log");

#if (!defined(_DEBUG) || defined(BBGE_BUILD_UNIX)) && defined(BBGE_BUILD_SDL)
	bool wasInactive = false;
#endif

#ifdef BBGE_BUILD_GLFW
	if (runTime == -1)
		glfwSetTime(0);
#endif
#ifdef BBGE_BUILD_DIRECTX
	// HACK: find out how to use performance counter again Query


	if (verbose) debugLog("Performance Counter");

	if (!QueryPerformanceFrequency((LARGE_INTEGER*)&freq))
	{
		errorLog ("could not get clock freq");
		return;
	}
	QueryPerformanceCounter((LARGE_INTEGER*)&timerStart);
	/*
	DWORD ticks = GetTickCount();
	DWORD newTicks;
	*/
#endif

#ifdef BBGE_BUILD_SDL
	nowTicks = thenTicks = SDL_GetTicks();
#endif

	//int i;

	nestedMains++;
	// HACK: Why block this?
	/*
	if (nestedMains > 1 && runTime <= 0)
		return;
	*/

#ifdef BBGE_BUILD_DIRECTX
	MSG msg;
	ZeroMemory( &msg, sizeof(msg) );
#endif

	while((runTime == -1 && !loopDone) || (runTime >0))									// Loop That Runs While done=FALSE
	{
		BBGE_PROF(Core_main);
#ifdef BBGE_BUILD_DIRECTX
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
#endif


#ifdef BBGE_BUILD_GLFW
		if (verbose) debugLog("glfwSetTime");
		dt = glfwGetTime();
		glfwSetTime(0);
#endif

#ifdef BBGE_BUILD_DIRECTX
		/*
		newTicks = GetTickCount();
		*/
		QueryPerformanceCounter((LARGE_INTEGER*)&timerEnd);
		dt = (float(timerEnd-timerStart)/float(freq));
		timerStart = timerEnd;
//		dt = float(newTicks)/1000.0f;
		//dt = float(newTicks - ticks)/1000.0f;
		//ticks = newTicks;
#endif

#ifdef BBGE_BUILD_SDL
		if (timeUpdateType == TIMEUPDATE_DYNAMIC)
		{
			nowTicks = SDL_GetTicks();
		}
		/*
		else
		{
			if (nowTicks == 0)
			{
				nowTicks = SDL_GetTicks();
			}
		}
		*/
		dt = (nowTicks-thenTicks)/1000.0;
		thenTicks = nowTicks;
		//thenTicks = SDL_GetTicks();
#endif

		if (verbose) debugLog("avgFPS");
		if (!avgFPS.empty())
		{
			/*
			if (avgFPS[0] <= 0)
			{
				for (int i = 0; i < avgFPS.size(); i++)
					avgFPS[i] = dt;
			}
			*/
			int i = 0;
			for (i = avgFPS.size()-1; i > 0; i--)
			{
				avgFPS[i] = avgFPS[i-1];
			}
			avgFPS[0] = dt;

			float c=0;
			int n = 0;
			for (i = 0; i < avgFPS.size(); i++)
			{
				if (avgFPS[i] > 0)
				{
					c += avgFPS[i];
					n ++;
				}
			}
			if (n > 0) // && n == avgFPS.size() ??
			{
				c /= n;
				dt = c;
			}
			/*
			std::ostringstream os;
			os << dt;
			debugLog(os.str());
			*/
		}

#if !defined(_DEBUG) && defined(BBGE_BUILD_SDL)
		if (verbose) debugLog("checking window active");

		if (lib_graphics && (wasInactive || !settings.runInBackground))
		{
			if (isWindowFocus())
			{
				_hasFocus = true;
				if (wasInactive)
				{
					debugLog("WINDOW ACTIVE");
					
					setReentryInputGrab(1);

					wasInactive = false;
				}
			}
			else
			{
				if (_hasFocus)
				{
					if (!wasInactive)
						debugLog("WINDOW INACTIVE");

					wasInactive = true;
					_hasFocus = false;

					setReentryInputGrab(0);

					sound->pause();

					core->joystick.rumble(0,0,0);

					while (!isWindowFocus())
					{
						pollEvents();
						//debugLog("app not in input focus");
						onBackgroundUpdate();

						resetTimer();
					}

					debugLog("app back in focus, reset");

					// Don't do this on Linux, it's not necessary and causes big stalls.
					//  We don't actually _lose_ the device like Direct3D anyhow.
					#ifndef BBGE_BUILD_UNIX
					if (_fullscreen)
					{
						// calls reload device - reloadDevice()
						resetGraphics(width, height);
					}
					#endif

					resetTimer();

					sound->resume();

					resetTimer();
					
					SDL_ShowCursor(SDL_DISABLE);

					continue;
				}
			}
		}
#endif

		if (timeUpdateType == TIMEUPDATE_FIXED)
		{
			real_dt = dt;
			dt = 1.0f/float(fixedFPS);
		}

		old_dt = dt;

		if (verbose) debugLog("modify dt");
		modifyDt(dt);

		current_dt = dt;

		if (verbose) debugLog("check runtime/quit");

		if (quitNestedMainFlag)
		{
			quitNestedMainFlag = false;
			break;
		}
		if (runTime>0)
		{
			runTime -= dt;
			if (runTime < 0)
				runTime = 0;
		}

		// UPDATE
		if (verbose) debugLog("post processing fx update");
		postProcessingFx.update(dt);

		if (verbose) debugLog("update eventQueue");
		eventQueue.update(dt);

		if (verbose) debugLog("Update render objects");

		updateRenderObjects(dt);

		if (verbose) debugLog("Update particle manager");

		if (particleManager)
			particleManager->update(dt);

		if (verbose) debugLog("sound update");
		sound->update(dt);

		if (verbose) debugLog("onUpdate");
		onUpdate(dt);

		if (nestedMains == 1)
			clearGarbage();

		if (loopDone)
			break;

		updateCullData();

		dbg_numRenderCalls = 0;

		if (settings.renderOn)
		{
			if (verbose) debugLog("dark layer prerender");
			if (darkLayer.isUsed())
			{
				darkLayer.preRender();
			}

			if (verbose) debugLog("render");
			render();

			if (verbose) debugLog("showBuffer");
			showBuffer();

			BBGE_PROF(STOP);

			if (verbose) debugLog("clearGarbage");
			if (nestedMains == 1)
				clearGarbage();


			if (verbose) debugLog("frame counter");
			frames++;

			counter += dt;
			if (counter > 1)
			{
				fps = frames;
				frames = counter = 0;
			}
		}

		sound->setListenerPos(screenCenter.x, screenCenter.y);

		if (doScreenshot)
		{
			if (verbose) debugLog("screenshot");

			doScreenshot = false;

			saveScreenshotTGA(getScreenshotFilename());
			prepScreen(0);
		}
		
		// wait
		if (timeUpdateType == TIMEUPDATE_FIXED)
		{
			static float avg_diff=0;
			static int avg_diff_count=0;

			float diff = (1.0f/float(fixedFPS)) - real_dt;

			avg_diff_count++;
			avg_diff += diff;
			
			char buf[256];
			sprintf(buf, "real_dt: %5.4f \n realFPS: %5.4f \n fixedFPS: %5.4f \n diff: %5.4f \n delay: %5.4f \n avgdiff: %5.8f", float(real_dt), float(real_dt>0?(1.0f/real_dt):0.0f), float(fixedFPS), float(diff), float(diff*1000), float(avg_diff/(float)avg_diff_count));
			fpsDebugString = buf;

			/*
			std::ostringstream os;
			os << "real_dt: " << real_dt << "\n realFPS: " << (1.0/real_dt) << "\n fixedFPS: " << fixedFPS << "\n diff: " << diff << "\n delay: " << diff*1000;
			fpsDebugString = os.str();
			*/

#ifdef BBGE_BUILD_SDL
			nowTicks = SDL_GetTicks();
			
			if (diff > 0)
			{
				//Sleep(diff*1000);
				//SDL_Delay(diff*1000);
				while ((SDL_GetTicks() - nowTicks) < (diff*1000))
				{
					//wend, bitch
				}
			}

			//nowTicks = SDL_GetTicks();
#endif

		}	
	}
	if (verbose) debugLog("bottom of function");
	quitNestedMainFlag = false;
	if (nestedMains==1)
		clearGarbage();
	nestedMains--;
	if (verbose) debugLog("exit Core::main");
}

// less than through pointer
bool RenderObject_lt(RenderObject* x, RenderObject* y)
{
	return x->getSortDepth() < y->getSortDepth();
}

// greater than through pointer
bool RenderObject_gt(RenderObject* x, RenderObject* y)
{
	return x->getSortDepth() > y->getSortDepth();
}

void Core::sortLayer(int layer)
{
	if (layer >= 0 && layer < renderObjectLayers.size())
		renderObjectLayers[layer].sort();
}

void Core::sort()
{
	/*
	if (sortEnabled)
		renderObjects.sort(RenderObject_lt);
	*/
	// sort layeres independantly

	/*
	for (int i = renderObjects.size()-1; i >= 0; i--)
	{
		bool flipped = false;
		for (int j = 0; j < i; j++)
		{
			//position.z
			//position.z
			//!renderObjects[j]->parent && !renderObjects[j+1]->parent &&
			if (renderObjects[j]->getSortDepth() > renderObjects[j+1]->getSortDepth())
			{
				RenderObject *temp;
				temp = renderObjects[j];
				renderObjects[j] = renderObjects[j+1];
				renderObjects[j+1] = temp;
				flipped = true;
			}
		}
		if (!flipped) break;
	}
	*/

}

void Core::clearBuffers()
{
	if (flags.get(CF_CLEARBUFFERS))
	{
#ifdef BBGE_BUILD_OPENGL
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
#endif
#ifdef BBGE_BUILD_DIRECTX
		g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(int(clearColor.x*255),int(clearColor.y*255),int(clearColor.z*255)), 1.0f, 0 );
#endif
	}
}

void Core::setupRenderPositionAndScale()
{
#ifdef BBGE_BUILD_OPENGL
	glScalef(globalScale.x*globalResolutionScale.x*screenCapScale.x, globalScale.y*globalResolutionScale.y*screenCapScale.y, globalScale.z*globalResolutionScale.z);
	glTranslatef(-(cameraPos.x+cameraOffset.x), -(cameraPos.y+cameraOffset.y), -(cameraPos.z+cameraOffset.z));
#endif
}

void Core::setupGlobalResolutionScale()
{
	glScalef(globalResolutionScale.x, globalResolutionScale.y, globalResolutionScale.z);
}

void Core::initFrameBuffer()
{
	frameBuffer.init(-1, -1, true);
}

void Core::setMouseConstraint(bool on)
{
/*
	if (mouseConstraint && !on)
	{
		setMousePosition(mouse.position);
	}
	*/
	mouseConstraint = on;
}

void Core::setMouseConstraintCircle(int circle)
{
	mouseConstraint = true;
	mouseCircle = circle;
}

/*
void Core::clearKeys()
{
	for (int i = 0; i < KEY_MAXARRAY; i++)
	{
		keys[i] = 0;
	}
}
*/

int Core::getVirtualOffX()
{
	return virtualOffX;
}

int Core::getVirtualOffY()
{
	return virtualOffY;
}

void Core::centerMouse()
{
	setMousePosition(Vector((virtualWidth/2) - core->getVirtualOffX(), virtualHeight/2));
}

bool Core::doMouseConstraint()
{
	if (mouseConstraint)
	{
		//- core->getVirtualOffX()
		//- virtualOffX
		Vector h = Vector(core->center.x , core->center.y);
		Vector d = mouse.position - h;
		if (!d.isLength2DIn(mouseCircle))
		{
			d.setLength2D(mouseCircle);
			mouse.position = h+d;
			//warpMouse = true;
			return true;
		}
	}
	return false;
}

#if defined(BBGE_BUILD_SDL)

#if defined(BBGE_BUILD_SDL2)
typedef std::map<SDL_Keycode,int> sdlKeyMap;
#else
typedef std::map<SDLKey,int> sdlKeyMap;
#endif

static sdlKeyMap *initSDLKeymap(void)
{
	sdlKeyMap *_retval = new sdlKeyMap;
	sdlKeyMap &retval = *_retval;

	#define SETKEYMAP(gamekey,sdlkey) retval[sdlkey] = gamekey

#ifdef BBGE_BUILD_SDL2
	SETKEYMAP(KEY_LSUPER, SDLK_LGUI);
	SETKEYMAP(KEY_RSUPER, SDLK_RGUI);
	SETKEYMAP(KEY_LMETA, SDLK_LGUI);
	SETKEYMAP(KEY_RMETA, SDLK_RGUI);
	SETKEYMAP(KEY_PRINTSCREEN, SDLK_PRINTSCREEN);
	SETKEYMAP(KEY_NUMPAD1, SDLK_KP_1);
	SETKEYMAP(KEY_NUMPAD2, SDLK_KP_2);
	SETKEYMAP(KEY_NUMPAD3, SDLK_KP_3);
	SETKEYMAP(KEY_NUMPAD4, SDLK_KP_4);
	SETKEYMAP(KEY_NUMPAD5, SDLK_KP_5);
	SETKEYMAP(KEY_NUMPAD6, SDLK_KP_6);
	SETKEYMAP(KEY_NUMPAD7, SDLK_KP_7);
	SETKEYMAP(KEY_NUMPAD8, SDLK_KP_8);
	SETKEYMAP(KEY_NUMPAD9, SDLK_KP_9);
	SETKEYMAP(KEY_NUMPAD0, SDLK_KP_0);
#else
	SETKEYMAP(KEY_LSUPER, SDLK_LSUPER);
	SETKEYMAP(KEY_RSUPER, SDLK_RSUPER);
	SETKEYMAP(KEY_LMETA, SDLK_LMETA);
	SETKEYMAP(KEY_RMETA, SDLK_RMETA);
	SETKEYMAP(KEY_PRINTSCREEN, SDLK_PRINT);
	SETKEYMAP(KEY_NUMPAD1, SDLK_KP1);
	SETKEYMAP(KEY_NUMPAD2, SDLK_KP2);
	SETKEYMAP(KEY_NUMPAD3, SDLK_KP3);
	SETKEYMAP(KEY_NUMPAD4, SDLK_KP4);
	SETKEYMAP(KEY_NUMPAD5, SDLK_KP5);
	SETKEYMAP(KEY_NUMPAD6, SDLK_KP6);
	SETKEYMAP(KEY_NUMPAD7, SDLK_KP7);
	SETKEYMAP(KEY_NUMPAD8, SDLK_KP8);
	SETKEYMAP(KEY_NUMPAD9, SDLK_KP9);
	SETKEYMAP(KEY_NUMPAD0, SDLK_KP0);
#endif

	SETKEYMAP(KEY_BACKSPACE, SDLK_BACKSPACE);

	//SETKEYMAP(KEY_CAPSLOCK, DIK_CAPSLOCK);
	//SETKEYMAP(KEY_CIRCUMFLEX, DIK_CIRCUMFLEX);
	SETKEYMAP(KEY_LALT, SDLK_LALT);
	SETKEYMAP(KEY_RALT, SDLK_RALT);
	SETKEYMAP(KEY_LSHIFT, SDLK_LSHIFT);
	SETKEYMAP(KEY_RSHIFT, SDLK_RSHIFT);
	SETKEYMAP(KEY_LCONTROL, SDLK_LCTRL);
	SETKEYMAP(KEY_RCONTROL, SDLK_RCTRL);
	SETKEYMAP(KEY_NUMPADMINUS, SDLK_KP_MINUS);
	SETKEYMAP(KEY_NUMPADPERIOD, SDLK_KP_PERIOD);
	SETKEYMAP(KEY_NUMPADPLUS, SDLK_KP_PLUS);
	SETKEYMAP(KEY_NUMPADSLASH, SDLK_KP_DIVIDE);
	SETKEYMAP(KEY_NUMPADSTAR, SDLK_KP_MULTIPLY);
	SETKEYMAP(KEY_PGDN, SDLK_PAGEDOWN);
	SETKEYMAP(KEY_PGUP, SDLK_PAGEUP);
	SETKEYMAP(KEY_APOSTROPHE, SDLK_QUOTE);
	SETKEYMAP(KEY_EQUALS, SDLK_EQUALS);
	SETKEYMAP(KEY_SEMICOLON, SDLK_SEMICOLON);
	SETKEYMAP(KEY_LBRACKET, SDLK_LEFTBRACKET);
	SETKEYMAP(KEY_RBRACKET, SDLK_RIGHTBRACKET);
	//SETKEYMAP(KEY_RALT, GLFW_SETKEYMAP(KEY_RALT);
	SETKEYMAP(KEY_TILDE, SDLK_BACKQUOTE);
	SETKEYMAP(KEY_0, SDLK_0);
	SETKEYMAP(KEY_1, SDLK_1);
	SETKEYMAP(KEY_2, SDLK_2);
	SETKEYMAP(KEY_3, SDLK_3);
	SETKEYMAP(KEY_4, SDLK_4);
	SETKEYMAP(KEY_5, SDLK_5);
	SETKEYMAP(KEY_6, SDLK_6);
	SETKEYMAP(KEY_7, SDLK_7);
	SETKEYMAP(KEY_8, SDLK_8);
	SETKEYMAP(KEY_9, SDLK_9);
	SETKEYMAP(KEY_A, SDLK_a);
	SETKEYMAP(KEY_B, SDLK_b);
	SETKEYMAP(KEY_C, SDLK_c);
	SETKEYMAP(KEY_D, SDLK_d);
	SETKEYMAP(KEY_E, SDLK_e);
	SETKEYMAP(KEY_F, SDLK_f);
	SETKEYMAP(KEY_G, SDLK_g);
	SETKEYMAP(KEY_H, SDLK_h);
	SETKEYMAP(KEY_I, SDLK_i);
	SETKEYMAP(KEY_J, SDLK_j);
	SETKEYMAP(KEY_K, SDLK_k);
	SETKEYMAP(KEY_L, SDLK_l);
	SETKEYMAP(KEY_M, SDLK_m);
	SETKEYMAP(KEY_N, SDLK_n);
	SETKEYMAP(KEY_O, SDLK_o);
	SETKEYMAP(KEY_P, SDLK_p);
	SETKEYMAP(KEY_Q, SDLK_q);
	SETKEYMAP(KEY_R, SDLK_r);
	SETKEYMAP(KEY_S, SDLK_s);
	SETKEYMAP(KEY_T, SDLK_t);
	SETKEYMAP(KEY_U, SDLK_u);
	SETKEYMAP(KEY_V, SDLK_v);
	SETKEYMAP(KEY_W, SDLK_w);
	SETKEYMAP(KEY_X, SDLK_x);
	SETKEYMAP(KEY_Y, SDLK_y);
	SETKEYMAP(KEY_Z, SDLK_z);

	SETKEYMAP(KEY_LEFT, SDLK_LEFT);
	SETKEYMAP(KEY_RIGHT, SDLK_RIGHT);
	SETKEYMAP(KEY_UP, SDLK_UP);
	SETKEYMAP(KEY_DOWN, SDLK_DOWN);

	SETKEYMAP(KEY_DELETE, SDLK_DELETE);
	SETKEYMAP(KEY_SPACE, SDLK_SPACE);
	SETKEYMAP(KEY_RETURN, SDLK_RETURN);
	SETKEYMAP(KEY_PERIOD, SDLK_PERIOD);
	SETKEYMAP(KEY_MINUS, SDLK_MINUS);
	SETKEYMAP(KEY_CAPSLOCK, SDLK_CAPSLOCK);
	SETKEYMAP(KEY_SYSRQ, SDLK_SYSREQ);
	SETKEYMAP(KEY_TAB, SDLK_TAB);
	SETKEYMAP(KEY_HOME, SDLK_HOME);
	SETKEYMAP(KEY_END, SDLK_END);
	SETKEYMAP(KEY_COMMA, SDLK_COMMA);
	SETKEYMAP(KEY_SLASH, SDLK_SLASH);

	SETKEYMAP(KEY_F1, SDLK_F1);
	SETKEYMAP(KEY_F2, SDLK_F2);
	SETKEYMAP(KEY_F3, SDLK_F3);
	SETKEYMAP(KEY_F4, SDLK_F4);
	SETKEYMAP(KEY_F5, SDLK_F5);
	SETKEYMAP(KEY_F6, SDLK_F6);
	SETKEYMAP(KEY_F7, SDLK_F7);
	SETKEYMAP(KEY_F8, SDLK_F8);
	SETKEYMAP(KEY_F9, SDLK_F9);
	SETKEYMAP(KEY_F10, SDLK_F10);
	SETKEYMAP(KEY_F11, SDLK_F11);
	SETKEYMAP(KEY_F12, SDLK_F12);
	SETKEYMAP(KEY_F13, SDLK_F13);
	SETKEYMAP(KEY_F14, SDLK_F14);
	SETKEYMAP(KEY_F15, SDLK_F15);

	SETKEYMAP(KEY_ESCAPE, SDLK_ESCAPE);
	//SETKEYMAP(KEY_ANYKEY, 4059);
	//SETKEYMAP(KEY_MAXARRAY, SDLK_LAST+1

	#undef SETKEYMAP

	return _retval;
}

#if defined(BBGE_BUILD_SDL2)
static int mapSDLKeyToGameKey(const SDL_Keycode val)
#else
static int mapSDLKeyToGameKey(const SDLKey val)
#endif
{
	static sdlKeyMap *keymap = NULL;
	if (keymap == NULL)
		keymap = initSDLKeymap();

	return (*keymap)[val];
}
#endif


void Core::pollEvents()
{
#if defined(BBGE_BUILD_SDL)
	bool warpMouse=false;

	/*
	Uint8 *keystate = SDL_GetKeyState(NULL);
	for (int i = 0; i < KEY_MAXARRAY; i++)
	{
		keys[i] = keystate[i];
	}
	*/

	if (updateMouse)
	{
		int x, y;
		Uint8 mousestate = SDL_GetMouseState(&x,&y);

		if (mouse.buttonsEnabled)
		{
			mouse.buttons.left		= mousestate & SDL_BUTTON(1)?DOWN:UP;
			mouse.buttons.right		= mousestate & SDL_BUTTON(3)?DOWN:UP;
			mouse.buttons.middle	= mousestate & SDL_BUTTON(2)?DOWN:UP;

			mouse.pure_buttons = mouse.buttons;

			if (flipMouseButtons)
			{
				std::swap(mouse.buttons.left, mouse.buttons.right);
			}
		}
		else
		{
			mouse.buttons.left = mouse.buttons.right = mouse.buttons.middle = UP;
		}

		mouse.scrollWheelChange = 0;
		mouse.change = Vector(0,0);
	}





	SDL_Event event;

	

	while ( SDL_PollEvent (&event) ) {
		switch (event.type) {
			case SDL_KEYDOWN:
			{
				#if __APPLE__
				if ((event.key.keysym.sym == SDLK_q) && (event.key.keysym.mod & KMOD_META))
				#else
				if ((event.key.keysym.sym == SDLK_F4) && (event.key.keysym.mod & KMOD_ALT))
				#endif
				{
					quitNestedMain();
					quit();
				}

				if ((event.key.keysym.sym == SDLK_g) && (event.key.keysym.mod & KMOD_CTRL))
				{
					// toggle mouse grab with the magic hotkey.
					grabInputOnReentry = (grabInputOnReentry)?0:-1;
					setReentryInputGrab(1);
				}
				else if (_hasFocus)
				{
					keys[mapSDLKeyToGameKey(event.key.keysym.sym)] = 1;
				}
			}
			break;

			case SDL_KEYUP:
			{
				if (_hasFocus)
				{
					keys[mapSDLKeyToGameKey(event.key.keysym.sym)] = 0;
				}
			}
			break;

			case SDL_MOUSEMOTION:
			{
				if (_hasFocus && updateMouse)
				{
					mouse.lastPosition = mouse.position;

					mouse.position.x = ((event.motion.x) * (float(virtualWidth)/float(getWindowWidth()))) - getVirtualOffX();
					mouse.position.y = event.motion.y * (float(virtualHeight)/float(getWindowHeight()));

					mouse.change = mouse.position - mouse.lastPosition;

					if (doMouseConstraint()) warpMouse = true;
				}
			}
			break;

			#ifdef BBGE_BUILD_SDL2
			case SDL_WINDOWEVENT:
			{
				if (event.window.event == SDL_WINDOWEVENT_CLOSE)
				{
					SDL_Quit();
					_exit(0);
					//loopDone = true;
					//quit();
				}
			}
			break;

			case SDL_MOUSEWHEEL:
			{
				if (_hasFocus && updateMouse)
				{
					if (event.wheel.y > 0)
						mouse.scrollWheelChange = 1;
					else if (event.wheel.y < 0)
						mouse.scrollWheelChange = -1;
				}
			}
			break;
			#else
			case SDL_MOUSEBUTTONDOWN:
			{
				if (_hasFocus && updateMouse)
				{
					switch(event.button.button)
					{
					case 4:
						mouse.scrollWheelChange = 1;
					break;
					case 5:
						mouse.scrollWheelChange = -1;
					break;
					}
				}
			}
			break;

			case SDL_MOUSEBUTTONUP:
			{
				if (_hasFocus && updateMouse)
				{
					switch(event.button.button)
					{
					case 4:
						mouse.scrollWheelChange = 1;
					break;
					case 5:
						mouse.scrollWheelChange = -1;
					break;
					}
				}
			}
			break;
			#endif

			case SDL_QUIT:
				SDL_Quit();
				_exit(0);
				//loopDone = true;
				//quit();
			break;

			case SDL_SYSWMEVENT:
			{
				/*
				debugLog("SYSWM!");
				if (event.syswm.type == WM_ACTIVATE)
				{
					debugLog("ACTIVE");
					this->unloadDevice();
					this->reloadDevice();
				}
				else
				{
					debugLog("NOT ACTIVE");
					this->unloadDevice();
				}
				*/
			}
			break;

			default:
			break;
		}
	}

	if (updateMouse)
	{
		mouse.scrollWheel += mouse.scrollWheelChange;

		if (warpMouse)
		{
			setMousePosition(mouse.position);
		}
	}

#endif
}

#define _VLN(x, y, x2, y2) glVertex2f(x, y); glVertex2f(x2, y2);

void Core::print(int x, int y, const char *str, float sz)
{
	//Prof(Core_print);
	/*
	glLoadIdentity();
	core->setupRenderPositionAndScale();
	*/
	///glPushAttrib(GL_ALL_ATTRIB_BITS);

#ifdef BBGE_BUILD_OPENGL
	glBindTexture(GL_TEXTURE_2D, 0);

	glPushMatrix();
	//sz *= 8;
	//float osz = sz;
	float xx = x;
	float yy = y;
	glTranslatef(x, y-0.5f*sz, 0);
	x = y = 0;
	xx = 0; yy = 0;
	bool isLower = false, wasLower = false;
	int c=0;

	/*
	if (a == 1)
		glDisable(GL_BLEND);
	else
		glEnable(GL_BLEND);
	glColor4f(r,g,b,a);
	*/
	glLineWidth(1);
	glScalef(sz*0.75f, sz, 1);

	glBegin(GL_LINES);

	while (str[c] != '\0')
	{
		if (str[c] <= 'z' && str[c] >= 'a')
			isLower = true;
		else
			isLower = false;

		/*
		if (isLower)
			glScalef(sz*0.5f, sz*0.5f, 1);
		else if (wasLower)
		{
			glScalef(sz, sz, 1);
			wasLower = false;
		}
		*/

		switch(toupper(str[c]))
		{
		case '_':
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case '-':
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
		break;
		case '~':
			_VLN(xx, y+0.5f, xx+0.25f, y+0.4f)
			_VLN(xx+0.25f, y+0.4f, xx+0.75f, y+0.6f)
			_VLN(xx+0.75f, y+0.6f, xx+1, y+0.5f)
		break;
		case 'A':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
		break;
		case 'B':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'C':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'D':
			_VLN(xx, y, xx+1, y+0.2f)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx+1, y+0.2f, xx+1, y+1)
		break;
		case 'E':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'F':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
		break;
		case 'G':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx+1, y+0.5f, xx+1, y+1)
		break;
		case 'H':
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx+1, y, xx+1, y+1)
		break;
		case 'I':
			_VLN(xx+0.5f, y, xx+0.5f, y+1)
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'J':
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y+1, xx, y+0.75f)
		break;
		case 'K':
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.25f, xx+1, y)
			_VLN(xx, y+0.25f, xx+1, y+1)
		break;
		case 'L':
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'M':
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx+0.5f, y+0.5f)
			_VLN(xx+1, y, xx+0.5f, y+0.5f)
		break;
		case 'N':
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx+1, y+1)
		break;
		case 'O':
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y, xx+1, y)
		break;
		case 'P':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx+1, y+0.5f, xx+1, y)
		break;
		case 'Q':
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y+0.5f, xx+1.25f, y+1.25f)
		break;
		case 'R':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx+1, y+0.5f, xx+1, y)
			_VLN(xx, y+0.5f, xx+1, y+1)
		break;
		case 'S':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+0.5f)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx+1, y+0.5f, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case 'T':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+0.5f, y, xx+0.5f, y+1)
		break;
		case 'U':
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
		break;
		case 'V':
			_VLN(xx, y, xx+0.5f, y+1)
			_VLN(xx+1, y, xx+0.5f, y+1)
		break;
		case 'W':
			_VLN(xx, y, xx+0.25f, y+1)
			_VLN(xx+0.25f, y+1, xx+0.5f, y+0.5f)
			_VLN(xx+0.5f, y+0.5f, xx+0.75f, y+1)
			_VLN(xx+1, y, xx+0.75f, y+1)
		break;
		case 'X':
			_VLN(xx, y, xx+1, y+1)
			_VLN(xx+1, y, xx, y+1)
		break;
		case 'Y':
			_VLN(xx, y, xx+0.5f, y+0.5f)
			_VLN(xx+1, y, xx+0.5f, y+0.5f)
			_VLN(xx+0.5f, y+0.5f, xx+0.5f, y+1)
		break;
		case 'Z':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y+1, xx+1, y)
			_VLN(xx, y+1, xx+1, y+1)
		break;

		case '1':
			_VLN(xx+0.5f, y, xx+0.5f, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx+0.5f, y, xx+0.25f, y+0.25f)
		break;
		case '2':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+1, y, xx+1, y+0.5f)
			_VLN(xx+1, y+0.5f, xx, y+0.5f)
			_VLN(xx, y+0.5f, xx, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case '3':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx+1, y, xx+1, y+1)
		break;
		case '4':
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx+1, y, xx, y+0.5f)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
		break;
		case '5':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+0.5f)
			_VLN(xx+1, y+0.5f, xx, y+0.5f)
			_VLN(xx+1, y+0.5f, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case '6':
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y+0.5f, xx, y+0.5f)
			_VLN(xx+1, y+0.5f, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case '7':
			_VLN(xx+1, y, xx+0.5f, y+1)
			_VLN(xx, y, xx+1, y)
		break;
		case '8':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y, xx, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx, y+1, xx+1, y+1)
		break;
		case '9':
			_VLN(xx, y, xx+1, y)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y+0.5f, xx+1, y+0.5f)
			_VLN(xx, y+0.5f, xx, y)
		break;
		case '0':
			_VLN(xx, y, xx, y+1)
			_VLN(xx+1, y, xx+1, y+1)
			_VLN(xx, y+1, xx+1, y+1)
			_VLN(xx, y, xx+1, y)
			_VLN(xx, y, xx+1, y+1)
		break;
		case '.':
			_VLN(xx+0.4f, y+1, xx+0.6f, y+1)
		break;
		case ',':
			_VLN(xx+0.5f, y+0.75f, xx+0.5f, y+1.0f);
			_VLN(xx+0.5f, y+1.0f, xx+0.2f, y+1.25f);
		break;
		case ' ':
		break;
		case '(':
		case '[':
			_VLN(xx, y, xx, y+1);
			_VLN(xx, y, xx+0.25f, y);
			_VLN(xx, y+1, xx+0.25f, y+1);
		break;
		case ')':
		case ']':
			_VLN(xx+1, y, xx+1, y+1);
			_VLN(xx+1, y, xx+0.75f, y);
			_VLN(xx+1, y+1, xx+0.75f, y+1);
		break;
		case ':':
			_VLN(xx+0.5f, y, xx+0.5f, y+0.25f);
			_VLN(xx+0.5f, y+0.75f, xx+0.5f, y+1);
		break;
		case '/':
			_VLN(xx, y+1, xx+1, y);
		break;
		default:
			/*
			std::ostringstream os;
			os << "Core::print doesn't know char: " << str[c];
			debugLog(os.str());
			*/
		break;
		}
		if (isLower)
		{
			wasLower = true;

		}
		c++;
		xx += 1.4f;
	}
	glEnd();

	glPopMatrix();
	//glPopAttrib();

#endif
}

void Core::cacheRender()
{
	render();
	// what if the screen was full white? then you wouldn't want to clear buffers
	//clearBuffers();
	showBuffer();
	resetTimer();
}

void Core::updateCullData()
{
	// update cull data
	//this->cullRadius = int((getVirtualWidth())*invGlobalScale);
	this->cullRadius = baseCullRadius * invGlobalScale;
	this->cullRadiusSqr = (float)this->cullRadius * (float)this->cullRadius;
	this->cullCenter = cameraPos + Vector(400.0f*invGlobalScale,300.0f*invGlobalScale);
	screenCullX1 = cameraPos.x;
	screenCullX2 = cameraPos.x + 800*invGlobalScale;
	screenCullY1 = cameraPos.y;
	screenCullY2 = cameraPos.y + 600*invGlobalScale;

	
	int cx = core->cameraPos.x + 400*invGlobalScale;
	int cy = core->cameraPos.y + 300*invGlobalScale;
	screenCenter = Vector(cx, cy);
}

void Core::render(int startLayer, int endLayer, bool useFrameBufferIfAvail)
{

	BBGE_PROF(Core_render);
	//HWND hwnd = _glfwWin.Wnd;

	if (startLayer == -1 && endLayer == -1 && overrideStartLayer != 0)
	{
		startLayer = overrideStartLayer;
		endLayer = overrideEndLayer;
	}

	if (core->minimized) return;
	onRender();

	invGlobalScale = 1.0f/globalScale.x;
	invGlobalScaleSqr = invGlobalScale * invGlobalScale;

	RenderObject::lastTextureApplied = 0;

	updateCullData();



	renderObjectCount = 0;
	processedRenderObjectCount = 0;
	totalRenderObjectCount = 0;


#ifdef BBGE_BUILD_OPENGL
	glBindTexture(GL_TEXTURE_2D, 0);
	glLoadIdentity();									// Reset The View
	clearBuffers();

	if (afterEffectManager && frameBuffer.isInited() && useFrameBufferIfAvail)
	{
		frameBuffer.startCapture();
	}

	setupRenderPositionAndScale();
#endif

#ifdef BBGE_BUILD_DIRECTX
	bool doRender = false;

	core->getD3DMatrixStack()->LoadIdentity();


	core->scaleMatrixStack(globalScale.x*globalResolutionScale.x, globalScale.y*globalResolutionScale.y);
	core->translateMatrixStack(-(cameraPos.x+cameraOffset.x), -(cameraPos.y+cameraOffset.y));

	clearBuffers();
	if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
		doRender = true;
		//d3dSprite->Begin(D3DXSPRITE_BILLBOARD | D3DXSPRITE_ALPHABLEND);
    }

#endif


	/*
	//default
	if (renderObjectLayerOrder.empty())
	{
		renderObjectLayerOrder.resize(renderObjectLayers.size());
		for (int i = 0; i < renderObjectLayerOrder.size(); i++)
		{
			renderObjectLayerOrder[i] = i;
		}
	}
	*/
	RenderObject::rlayer = 0;

	for (int c = 0; c < renderObjectLayerOrder.size(); c++)
	//for (int i = 0; i < renderObjectLayers.size(); i++)
	{
		int i = renderObjectLayerOrder[c];
		if (i == -1) continue;
		if ((startLayer != -1 && endLayer != -1) && (i < startLayer || i > endLayer)) continue;

		if (i == postProcessingFx.layer)
		{
			postProcessingFx.preRender();
		}
		if (i == postProcessingFx.renderLayer)
		{
			postProcessingFx.render();
		}

		if (darkLayer.isUsed() )
		{
			/*
			if (i == darkLayer.getLayer())
			{
				darkLayer.preRender();
			}
			*/
			if (i == darkLayer.getRenderLayer())
			{
				darkLayer.render();
			}

			if (i == darkLayer.getLayer() && startLayer != i)
			{
				continue;
			}
		}

		if (afterEffectManager && afterEffectManager->active && i == afterEffectManagerLayer)
		{
			afterEffectManager->render();
		}

		RenderObjectLayer *r = &renderObjectLayers[i];
		RenderObject::rlayer = r;
		if (r->visible)
		{
			if (r->startPass == r->endPass)
			{
				r->renderPass(RenderObject::RENDER_ALL);
			}
			else
			{
				for (int pass = r->startPass; pass <= r->endPass; pass++)
				{
					r->renderPass(pass);
				}
			}
		}
	}

#ifdef BBGE_BUILD_DIRECTX
	if (doRender)
	{
		// End the scene
		//d3dSprite->End();
		//core->getD3DMatrixStack()->Pop();
		g_pd3dDevice->EndScene();
	}
#endif

}

void Core::showBuffer()
{
	BBGE_PROF(Core_showBuffer);
#ifdef BBGE_BUILD_SDL2
	SDL_GL_SwapWindow(gScreen);
#elif BBGE_BUILD_SDL
	SDL_GL_SwapBuffers();
	//glFlush();
#endif

#ifdef BBGE_BUILD_GLFW
	glfwSwapBuffers();
	//_glfwPlatSwapBuffers();
#endif
#ifdef BBGE_BUILD_DIRECTX
	// Present the backbuffer contents to the display
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
#endif
}

// WARNING: only for use during shutdown
// otherwise, textures will try to remove themselves
// when destroy is called on them
void Core::clearResources()
{
	std::vector<Resource*> deletedResources;
	int i;
	for (i = 0; i < resources.size(); i++)
	{
		int j = 0;
		for (j = 0; j < deletedResources.size(); j++)
		{
			if (deletedResources[j] == resources[i])
				break;
		}
		if (j == deletedResources.size())
		{
			deletedResources.push_back (resources[i]);
			Resource *r = resources[i];
			r->destroy();
			delete r;
		}
	}
	resourceNames.clear();
	resources.clear();
}

void Core::shutdownInputLibrary()
{
#if defined(BBGE_BUILD_WINDOWS) && !defined(BBGE_BUILD_SDL)
	g_pKeyboard->Unacquire();
	g_pKeyboard->Release();
	g_pKeyboard = 0;
	g_pMouse->Unacquire();
	g_pMouse->Release();
	g_pMouse = 0;
#endif
}

void Core::shutdownJoystickLibrary()
{
	if (joystickEnabled) {
		joystick.shutdown();
#ifdef BBGE_BUIDL_SDL
		SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
#endif
		joystickEnabled = false;
	}
}

void Core::clearRenderObjects()
{
	for (int i = 0; i < renderObjectLayers.size(); i++)
	{
		/*
		for (int j = 0; j < renderObjectLayers[i].renderObjects.size(); j++)
		{
			RenderObject *r = renderObjectLayers[i].renderObjects[j];
		*/
		RenderObject *r = renderObjectLayers[i].getFirst();
		while (r)
		{
			if (r)
			{
				removeRenderObject(r, DESTROY_RENDER_OBJECT);
			}
			r = renderObjectLayers[i].getNext();
		}
	}
}

void Core::shutdown()
{
	// pop all the states


	debugLog("Core::shutdown");
	shuttingDown = true;

	debugLog("Shutdown Joystick Library...");
		shutdownJoystickLibrary();
	debugLog("OK");

	debugLog("Shutdown Input Library...");
		shutdownInputLibrary();
	debugLog("OK");

	debugLog("Shutdown All States...");
		popAllStates();
	debugLog("OK");

	debugLog("Clear State Instances...");
		clearStateInstances();
	debugLog("OK");

	debugLog("Clear All Remaining RenderObjects...");
		clearRenderObjects();
	debugLog("OK");

	debugLog("Clear All Resources...");
		clearResources();
	debugLog("OK");


	debugLog("Clear State Objects...");
		clearStateObjects();
	debugLog("OK");

	if (afterEffectManager)
	{
		debugLog("Delete AEManager...");
			delete afterEffectManager;
			afterEffectManager = 0;
		debugLog("OK");
	}


	if (sound)
	{
		debugLog("Shutdown Sound Library...");
			sound->stopAll();
			delete sound;
			sound = 0;
		debugLog("OK");
	}

	debugLog("Core's framebuffer...");
		frameBuffer.unloadDevice();
	debugLog("OK");

	debugLog("Shutdown Graphics Library...");
		shutdownGraphicsLibrary();
	debugLog("OK");



#ifdef BBGE_BUILD_GLFW
	debugLog("Terminate GLFW...");
		//killGlWindow();
		glfwTerminate();
	debugLog("OK");
#endif

#ifdef BBGE_BUILD_VFS
	debugLog("Unload VFS...");
		vfs.Clear();
	debugLog("OK");
#endif


#ifdef BBGE_BUILD_SDL
	debugLog("SDL Quit...");
		SDL_Quit();
	debugLog("OK");
#endif
}

//util funcs

void Core::instantQuit()
{
#ifdef BBGE_BUILD_SDL
    SDL_Event event;
    event.type = SDL_QUIT;
    SDL_PushEvent(&event);
#endif
}

bool Core::exists(const std::string &filename)
{
	return ::exists(filename, false); // defined in Base.cpp
}

Resource* Core::findResource(const std::string &name)
{
	for (int i = 0; i < resources.size(); i++)
	{
		if (resources[i]->name == name)
		{
			return resources[i];
		}
	}
	return 0;
}


Texture* Core::findTexture(const std::string &name)
{
	//stringToUpper(name);
	//std::ofstream out("texturefind.log");
	int sz = resources.size();
	for (int i = 0; i < sz; i++)
	{
		//out << resources[i]->name << " is " << name << " ?" << std::endl;
		//NOTE: ensure all names are lowercase before this point
		if (resources[i]->name == name)
		{
			return (Texture*)resources[i];
		}
	}
	return 0;
}

std::string Core::getInternalTextureName(const std::string &name)
{
	std::string n = name;
	stringToUpper(n);
	return n;
}

// This handles unix/win32 relative paths: ./rel/path
// Unix abs paths: /home/user/...
// Win32 abs paths: C:/Stuff/.. and also C:\Stuff\...
#define ISPATHROOT(x) (x[0] == '.' || x[0] == '/' || ((x).length() > 1 && x[1] == ':'))

std::string Core::getTextureLoadName(const std::string &texture)
{
	std::string loadName = texture;

	if (texture.empty() || !ISPATHROOT(texture))
	{
		if (texture.find(baseTextureDirectory) == std::string::npos)
			loadName = baseTextureDirectory + texture;
	}
	return loadName;
}

Texture *Core::doTextureAdd(const std::string &texture, const std::string &loadName, std::string internalTextureName)
{
	if (texture.empty() || !ISPATHROOT(texture))
	{
		if (texture.find(baseTextureDirectory) != std::string::npos)
			internalTextureName = internalTextureName.substr(baseTextureDirectory.size(), internalTextureName.size());
	}

	if (internalTextureName.size() > 4)
	{
		if (internalTextureName[internalTextureName.size()-4] == '.')
		{
			internalTextureName = internalTextureName.substr(0, internalTextureName.size()-4);
		}
	}

	stringToLowerUserData(internalTextureName);
	Texture *t = core->findTexture(internalTextureName);
	if (t)
	{
		t->addRef();

		Texture::textureError = TEXERR_OK;

		/*
		std::ostringstream os;
		os << "reference texture: " << internalTextureName << " ref: " << t->getRef();
		debugLog(os.str());
		*/

		//msg ("found texture " + internalTextureName);
		return t;
	}

	t = new Texture;
	t->name = internalTextureName;
	t->load(loadName);
	t->addRef();
	//resources.push_back (t);
	addResource(t);

	if (debugLogTextures)
	{
		std::ostringstream os;
		os << "LOADED TEXTURE FROM DISK: [" << internalTextureName << "] ref: " << t->getRef() << " idx: " << resources.size()-1;
		debugLog(os.str());
	}

	return t;
}

Texture* Core::addTexture(const std::string &textureName)
{
	if (textureName.empty()) return 0;

	BBGE_PROF(Core_addTexture);

	Texture *texPointer = 0;

	std::string texture = textureName;
	stringToLowerUserData(texture);
	std::string internalTextureName = texture;
	std::string loadName = getTextureLoadName(texture);

	if (!texture.empty() && texture[0] == '@')
	{
		texture = secondaryTexturePath + texture.substr(1, texture.size());
		loadName = texture;
	}
	else if (!secondaryTexturePath.empty() && texture[0] != '.' && texture[0] != '/')
	{
		std::string t = texture;
		std::string ln = loadName;
		texture = secondaryTexturePath + texture;
		loadName = texture;
		texPointer = doTextureAdd(texture, loadName, internalTextureName);
		if (Texture::textureError != TEXERR_OK)
		{
			if (texPointer)
			{
				texPointer->destroy();
				texPointer = 0;
			}
			texPointer = doTextureAdd(t, ln, internalTextureName);
		}
	}
	else
		texPointer = doTextureAdd(texture, loadName, internalTextureName);

	return texPointer;
}

void Core::removeTexture(std::string texture)
{
	//std::string internalName = baseTextureDirectory + texture;
	removeResource(texture, DESTROY);
}


void Core::addRenderObject(RenderObject *o, int layer)
{
	if (!o) return;
	o->layer = layer;
	if (layer < 0 || layer >= renderObjectLayers.size())
	{
		std::ostringstream os;
		os << "attempted to add render object to invalid layer [" << layer << "]";
		errorLog(os.str());
	}
	renderObjectLayers[layer].add(o);
}

void Core::switchRenderObjectLayer(RenderObject *o, int toLayer)
{
	if (!o) return;
	renderObjectLayers[o->layer].remove(o);
	renderObjectLayers[toLayer].add(o);
	o->layer = toLayer;
}

void Core::unloadResources()
{
	for (int i = 0; i < resources.size(); i++)
	{
		resources[i]->unload();
	}
}

void Core::onReloadResources()
{
}

void Core::reloadResources()
{
	for (int i = 0; i < resources.size(); i++)
	{
		resources[i]->reload();
	}
	onReloadResources();
}

void Core::addResource(Resource *r)
{
	resources.push_back(r);
	resourceNames.push_back(r->name);
	if (r->name.empty())
	{
		debugLog("Empty name resource added");
	}
}

void Core::removeResource(std::string name, RemoveResource removeFlag)
{
	//Resource *r = findResource(name);
	//int idx = 0;
	int i = 0;
	std::vector<Resource*>copy;
	copy = resources;
	resources.clear();


	std::vector <std::string> copyNames;
	copyNames = resourceNames;
	resourceNames.clear();

	bool isDestroyed = false;


	for (i = 0; i < copy.size(); i++)
	{
#ifdef _DEBUG
			std::string s = copy[i]->name;
#endif
		if (!isDestroyed && copy[i]->name == name)
		{
			if (removeFlag == DESTROY)
			{
				copy[i]->destroy();
				delete copy[i];
				isDestroyed = true;
			}
			continue;
		}
		// also remove other entries of the same resource
		else if (isDestroyed && copyNames[i] == name)
		{
			continue;
		}
		else
		{
			resources.push_back(copy[i]);
			resourceNames.push_back(copy[i]->name);
		}
	}
}

void Core::deleteRenderObjectMemory(RenderObject *r)
{
	//if (!r->allocStatic)
	delete r;
}

void Core::removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag)
{
	if (r)
	{
		if (r->layer != LR_NONE && !renderObjectLayers[r->layer].empty())
		{
			renderObjectLayers[r->layer].remove(r);
		}
		if (flag != DO_NOT_DESTROY_RENDER_OBJECT )
		{
			r->destroy();

			deleteRenderObjectMemory(r);
		}
	}
}


void Core::enqueueRenderObjectDeletion(RenderObject *object)
{
	if (!object->_dead) // && !object->staticallyAllocated)
	{
		garbage.push_back (object);
		object->_dead = true;
	}
}

void Core::clearGarbage()
{
	BBGE_PROF(Core_clearGarbage);
	// HACK: optimize this (use a list instead of a queue)

	for (RenderObjectList::iterator i = garbage.begin(); i != garbage.end(); i++)
	{
		removeRenderObject(*i, DO_NOT_DESTROY_RENDER_OBJECT);

		(*i)->destroy();
	}

	for (RenderObjectList::iterator i = garbage.begin(); i != garbage.end(); i++)
	{
		deleteRenderObjectMemory(*i);
	}

	garbage.clear();

	// to clear resources
	for (std::vector<Resource*>::iterator i = resources.begin(); i != resources.end(); )
	{
		if ((*i)->getRef() == 0)
		{
			clearedGarbageFlag = true;
			delete (*i);
			i = resources.erase(i);
			continue;
		}

		if ((*i)->getRef() < 0)
		{
			errorLog("Texture ref < 0");
		}

		i++;
	}
}

bool Core::canChangeState()
{
	return (nestedMains<=1);
}

/*
int Core::getVirtualWidth()
{
	return virtualWidth;
}

int Core::getVirtualHeight()
{
	return virtualHeight;
}
*/

// Take a screenshot of the specified region of the screen and store it
// in a 32bpp pixel buffer.  delete[] the returned buffer when it's no
// longer needed.
unsigned char *Core::grabScreenshot(int x, int y, int w, int h)
{
#ifdef BBGE_BUILD_OPENGL

	unsigned char *imageData;

	unsigned int size = sizeof(unsigned char) * w * h * 4;
	imageData = new unsigned char[size];

	glPushAttrib(GL_ALL_ATTRIB_BITS);
	glDisable(GL_BLEND);
	glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST); glDisable(GL_DITHER); glDisable(GL_FOG);
	glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP);
	glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_1D);
	glDisable(GL_TEXTURE_2D); glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
	glPixelTransferi(GL_RED_SCALE, 1); glPixelTransferi(GL_RED_BIAS, 0);
	glPixelTransferi(GL_GREEN_SCALE, 1); glPixelTransferi(GL_GREEN_BIAS, 0);
	glPixelTransferi(GL_BLUE_SCALE, 1); glPixelTransferi(GL_BLUE_BIAS, 0);
	glPixelTransferi(GL_ALPHA_SCALE, 1); glPixelTransferi(GL_ALPHA_BIAS, 0);
	glRasterPos2i(0, 0);
	glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)imageData);
	glPopAttrib();

	// Force all alpha values to 255.
	unsigned char *c = imageData;
	for (int x = 0; x < w; x++)
	{
		for (int y = 0; y < h; y++, c += 4)
		{
			c[3] = 255;
		}
	}

	return imageData;

#else

	#warning FIXME: Need to implement non-GL grabScreenshot().
	// Avoid crashing, at least.
	return new unsigned char[sizeof(unsigned char) * w * h * 4];

#endif
}

// Like grabScreenshot(), but grab from the center of the screen.
unsigned char *Core::grabCenteredScreenshot(int w, int h)
{
	return grabScreenshot(core->width/2 - w/2, core->height/2 - h/2, w, h);
}

// takes a screen shot and saves it to a TGA image
int Core::saveScreenshotTGA(const std::string &filename)
{
	int w = getWindowWidth(), h = getWindowHeight();
	unsigned char *imageData = grabCenteredScreenshot(w, h);
	return tgaSave(filename.c_str(),w,h,32,imageData);
}

void Core::saveCenteredScreenshotTGA(const std::string &filename, int sz)
{
	int w=sz, h=sz;
	int hsm = (w * 3.0f) / 4.0f;
	unsigned char *imageData = grabCenteredScreenshot(w, hsm);

	int imageDataSize = sizeof(unsigned char) * w * hsm * 4;
	int tgaImageSize = sizeof(unsigned char) * w * h * 4;
	unsigned char *tgaImage = new unsigned char[tgaImageSize];
	memcpy(tgaImage, imageData, imageDataSize);
	memset(tgaImage + imageDataSize, 0, tgaImageSize - imageDataSize);
	delete[] imageData;

	int savebits = 32;
	tgaSave(filename.c_str(),w,h,savebits,tgaImage);
}

void Core::saveSizedScreenshotTGA(const std::string &filename, int sz, int crop34)
{
	debugLog("saveSizedScreenshot");

	int w, h;
	unsigned char *imageData;
	w = sz;
	h = sz;
	float fsz = (float)sz;

	unsigned int size = sizeof(unsigned char) * w * h * 3;
	imageData = (unsigned char *)malloc(size);

	float wbit = fsz;//+1;
	float hbit = ((fsz)*(3.0f/4.0f));

	int width = core->width-1;
	int height = core->height-1;
	int diff = 0;

	if (crop34)
	{
		width = int((core->height*4.0f)/3.0f);
		diff = (core->width - width)/2;
		width--;
	}

	float zx = wbit/(float)width;
	float zy = hbit/(float)height;

	float copyw = w*(1/zx);
	float copyh = h*(1/zy);



	std::ostringstream os;
	os << "wbit: " << wbit << " hbit: " << hbit << std::endl;
	os << "zx: " << zx << " zy: " << zy << std::endl;
	os << "w: " << w << " h: " << h << std::endl;
	os << "width: " << width << " height: " << height << std::endl;
	os << "copyw: " << copyw << " copyh: " << copyh << std::endl;
	debugLog(os.str());

	glRasterPos2i(0, 0);
	
	/*
	glPushAttrib(GL_ALL_ATTRIB_BITS);

	glDisable(GL_BLEND);

	glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND);
	glDisable(GL_DEPTH_TEST); glDisable(GL_DITHER); glDisable(GL_FOG);
	glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP);
	glDisable(GL_STENCIL_TEST); glDisable(GL_TEXTURE_1D);
	glDisable(GL_TEXTURE_2D); glPixelTransferi(GL_MAP_COLOR,
		GL_FALSE); glPixelTransferi(GL_RED_SCALE, 1);
	glPixelTransferi(GL_RED_BIAS, 0); glPixelTransferi(GL_GREEN_SCALE, 1);
	glPixelTransferi(GL_GREEN_BIAS, 0); glPixelTransferi(GL_BLUE_SCALE, 1);
	glPixelTransferi(GL_BLUE_BIAS, 0); glPixelTransferi(GL_ALPHA_SCALE, 1);
	glPixelTransferi(GL_ALPHA_BIAS, 0);
	*/

	//glPixelStorei(GL_PACK_ALIGNMENT, 1);

	debugLog("pixel zoom");
	glPixelZoom(zx,zy);
	glFlush();

	glPixelZoom(1,1);
	debugLog("copy pixels");
	glCopyPixels(diff, 0, width, height, GL_COLOR);
	glFlush();

	debugLog("read pixels");
	glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)imageData);
	glFlush();

	int savebits = 24;
	debugLog("saving bpp");
	tgaSave(filename.c_str(),w,h,savebits,imageData);

	debugLog("pop");
	//glPopAttrib();

	debugLog("done");
}

void Core::save64x64ScreenshotTGA(const std::string &filename)
{
#ifdef BBGE_BUILD_OPENGL
	int w, h;
	unsigned char *imageData;

// compute width and heidth of the image
	//w = xmax - xmin;
	//h = ymax - ymin;
	w = 64;
	h = 64;

// allocate memory for the pixels
	imageData = (unsigned char *)malloc(sizeof(unsigned char) * w * h * 4);

// read the pixels from the frame buffer

	//glReadPixels(xmin,ymin,xmax,ymax,GL_RGBA,GL_UNSIGNED_BYTE, (GLvoid *)imageData);
	glPixelZoom(64.0f/(float)getVirtualWidth(), 48.0f/(float)getVirtualHeight());
	glCopyPixels(0, 0, getVirtualWidth(), getVirtualHeight(), GL_COLOR);

	glReadPixels(0,0,64,64,GL_RGBA,GL_UNSIGNED_BYTE, (GLvoid *)imageData);


	unsigned char *c = imageData;
	for (int x=0; x < w; x++)
	{
		for (int y=0; y< h; y++)
		{
			c += 3;
			(*c) = 255;
			c ++;
		}
	}


// save the image
	tgaSave(filename.c_str(),64,64,32,imageData);
	glPixelZoom(1,1);
#endif

	// do NOT free imageData here
	// it IS freed in tgaSave
	//free(imageData);
}




// saves an array of pixels as a TGA image (frees the image data passed in)
int Core::tgaSave(	const char	*filename,
		short int	width,
		short int	height,
		unsigned char	pixelDepth,
		unsigned char	*imageData) {

	unsigned char cGarbage = 0, type,mode,aux;
	short int iGarbage = 0;
	int i;
	FILE *file;

// open file and check for errors
	file = fopen(adjustFilenameCase(filename).c_str(), "wb");
	if (file == NULL) {
		delete [] imageData;
		return (int)false;
	}

// compute image type: 2 for RGB(A), 3 for greyscale
	mode = pixelDepth / 8;
	if ((pixelDepth == 24) || (pixelDepth == 32))
		type = 2;
	else
		type = 3;

// write the header
	if (fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
		|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
		|| fwrite(&type, sizeof(unsigned char), 1, file) != 1
		|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
		|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
		|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1
		|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
		|| fwrite(&iGarbage, sizeof(short int), 1, file) != 1
		|| fwrite(&width, sizeof(short int), 1, file) != 1
		|| fwrite(&height, sizeof(short int), 1, file) != 1
		|| fwrite(&pixelDepth, sizeof(unsigned char), 1, file) != 1
		|| fwrite(&cGarbage, sizeof(unsigned char), 1, file) != 1)
	{
		fclose(file);
		delete [] imageData;
		return (int)false;
	}

// convert the image data from RGB(A) to BGR(A)
	if (mode >= 3)
	for (i=0; i < width * height * mode ; i+= mode) {
		aux = imageData[i];
		imageData[i] = imageData[i+2];
		imageData[i+2] = aux;
	}

// save the image data
	if (fwrite(imageData, sizeof(unsigned char),
			width * height * mode, file) != width * height * mode)
	{
		fclose(file);
		delete [] imageData;
		return (int)false;
	}

	fclose(file);
	delete [] imageData;

	return (int)true;
}

// saves a series of files with names "filenameX"
int Core::tgaSaveSeries(char		*filename,
			 short int		width,
			 short int		height,
			 unsigned char	pixelDepth,
			 unsigned char	*imageData) {

	char *newFilename;
	int status;

// compute the new filename by adding the
// series number and the extension
	newFilename = (char *)malloc(sizeof(char) * strlen(filename)+8);

	sprintf(newFilename,"%s%d",filename,numSavedScreenshots);

// save the image
	status = tgaSave(newFilename,width,height,pixelDepth,imageData);

//increase the counter
	if (status == (int)true)
		numSavedScreenshots++;
	free(newFilename);
	return(status);
}

 void Core::screenshot()
 {
	 doScreenshot = true;
//	ilutGLScreenie();
 }


 #include "DeflateCompressor.h"

 // saves an array of pixels as a TGA image (frees the image data passed in)
int Core::zgaSave(	const char	*filename,
		short int	w,
		short int	h,
		unsigned char	depth,
		unsigned char	*imageData) {

	ByteBuffer::uint8 type,mode,aux, pixelDepth = depth;
	ByteBuffer::uint8 cGarbage = 0;
	ByteBuffer::uint16 iGarbage = 0;
	ByteBuffer::uint16 width = w, height = h;

// open file and check for errors
	FILE *file = fopen(adjustFilenameCase(filename).c_str(), "wb");
	if (file == NULL) {
		delete [] imageData;
		return (int)false;
	}

// compute image type: 2 for RGB(A), 3 for greyscale
	mode = pixelDepth / 8;
	if ((pixelDepth == 24) || (pixelDepth == 32))
		type = 2;
	else
		type = 3;

// convert the image data from RGB(A) to BGR(A)
	if (mode >= 3)
	for (int i=0; i < width * height * mode ; i+= mode) {
		aux = imageData[i];
		imageData[i] = imageData[i+2];
		imageData[i+2] = aux;
	}

	ZlibCompressor z;
	z.SetForceCompression(true);
	z.reserve(width * height * mode + 30);
	z	<< cGarbage
		<< cGarbage
		<< type
		<< iGarbage
		<< iGarbage
		<< cGarbage
		<< iGarbage
		<< iGarbage
		<< width
		<< height
		<< pixelDepth
		<< cGarbage;

	z.append(imageData, width * height * mode);
	z.Compress(3);

// save the image data
	if (fwrite(z.contents(), 1, z.size(), file) != z.size())
	{
		fclose(file);
		delete [] imageData;
		return (int)false;
	}

	fclose(file);
	delete [] imageData;

	return (int)true;
}



#include "ttvfs_zip/VFSZipArchiveLoader.h"

void Core::setupFileAccess()
{
#ifdef BBGE_BUILD_VFS
	debugLog("Init VFS...");

	if(!ttvfs::checkCompat())
		exit_error("ttvfs not compatible");

	vfs.AddArchiveLoader(new ttvfs::VFSZipArchiveLoader);

	if(!vfs.LoadFileSysRoot(false))
	{
		exit_error("Failed to setup file access");
	}
	
	vfs.Prepare();


	ttvfs::VFSDir *override = vfs.GetDir("override");
	if(override)
	{
		debugLog("Mounting override dir...");
		override->load(true);
		vfs.Mount("override", "", true);
	}

	// If we ever want to read from a container...
	//vfs.AddArchive("aqfiles.zip", false, "");

	if(_extraDataDir.length())
	{
		debugLog("Mounting extra data dir: " + _extraDataDir);
		vfs.MountExternalPath(_extraDataDir.c_str(), "", true, true);
	}


	debugLog("Done");
#endif
}
