/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001  Ludvig Strigeus
 * Copyright (C) 2001/2004 The ScummVM project
 * Copyright (C) 2002 Ph0x - GP32 Backend
 * Copyright (C) 2003/2004 DJWillis - GP32 Backend
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

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

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header$
 *
 */

/*
 *
 * Main Source for ScummVM for the GP32
 *
 */

//      TODO:
//      Clean up GP leftovers and strip backend to only bits the GP32 needs.
//      Finish restructure.
//      Sort all remaining GCC 3.4 warnings.
//      setTimerCallback: function call doesnt use * ?

//#define REAL_MAIN

#ifdef GP32_GDB
#include <gdb-stub.h>
#endif				/*GP32_GDB */

#include "backends/gp32/setup.h"

#include "backends/gp32/gp32.h"

#include "backends/gp32/gfx_splash.h"
//#include "backends/gp32/resources/gfx_splash_alt.h"

#define gpRGB16(r,g,b)		(((((r)>>3)&0x1F) << 11) | ((((g)>>3)&0x1F) << 6) | (((b)>>3)&0x1F)<<1)
#define RGB_TO_16(r,g,b)	(((((r)>>3)&0x1F) << 11) | ((((g)>>3)&0x1F) << 6) | (((b)>>3)&0x1F)<<1)

int nflip, keydata;		// Flip Index
GP_HPALETTE PAL;		//palette
GPDRAWSURFACE LCDbuffer[BUFFERCOUNT + 1];	//buffers

void GpSetPaletteEntry(u8 i, u8 r, u8 g, u8 b);

//      FIXME: No global init!

float gammatab[256], gammatab2[256];	// fixme: one table
const float scrGamma[] = { 1.0, 1.001, 1.002, 1.003, 1.004, 1.005 };
char gindex = 3;

int mx = 1, my = 1;
int scrofsy = 239;
char currentsurf;

//      FIXME!!
//      crashes if here and not buildgammatab() not called as very first line! check
//      check if sav file handling overwrites something!
//      float gammatab[256];

FILE *fstderr, *fstdout, *fstdin;

/****************************************************************
    GP32 Input mappings - Returns Button Pressed.
****************************************************************/
int gpTrapKey(void)
{
	int value = 0;

#define rKEY_A          0x4000
#define rKEY_B          0x2000
#define rKEY_L          0x1000
#define rKEY_R          0x8000
#define rKEY_UP         0x0800
#define rKEY_DOWN       0x0200
#define rKEY_LEFT       0x0100
#define rKEY_RIGHT      0x0400
#define rKEY_START      0x0040
#define rKEY_SELECT     0x0080
#define rPBDAT          (*(volatile unsigned *)0x1560000c)
#define rPEDAT          (*(volatile unsigned *)0x15600030)

	unsigned long gpb = rPBDAT;	// 0x156
	unsigned long gpe = rPEDAT;

	if ((gpb & rKEY_LEFT) == 0)
		value |= GPC_VK_LEFT;
	if ((gpb & rKEY_RIGHT) == 0)
		value |= GPC_VK_RIGHT;
	if ((gpb & rKEY_UP) == 0)
		value |= GPC_VK_UP;
	if ((gpb & rKEY_DOWN) == 0)
		value |= GPC_VK_DOWN;
	if ((gpb & rKEY_A) == 0)
		value |= GPC_VK_FA;
	if ((gpb & rKEY_B) == 0)
		value |= GPC_VK_FB;
	if ((gpb & rKEY_L) == 0)
		value |= GPC_VK_FL;
	if ((gpb & rKEY_R) == 0)
		value |= GPC_VK_FR;
	if ((gpe & rKEY_SELECT) == 0)
		value |= GPC_VK_SELECT;
	if ((gpe & rKEY_START) == 0)
		value |= GPC_VK_START;

	return value;
}

/****************************************************************
    GP32 ScummVM OSystem Implementation.
****************************************************************/

//OSystem *OSystem_GP32::create()
//{
//      //OSystem_GP32 *syst = new OSystem_GP32();
//      //return syst;
//      return new OSystem_GP32();
//}

OSystem *OSystem_GP32_create()
{
//
//      OSystem_GP32 *syst = new OSystem_GP32();
////
////    //syst->_mode = gfx_mode;
////    //syst->_full_screen = full_screen;
//
//      // allocate palette storage
//      syst->_currentPalette = (gpColor*)calloc(sizeof(gpColor), 256);
//
//      // allocate the dirty rect storage
//      syst->_mouseBackup = (byte*)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2);
//      return syst;
//      return new OSystem_GP32();

	//return OSystem_GP32::create();
	return new OSystem_GP32();
}

OSystem_GP32::OSystem_GP32() :
//#ifdef USE_OSD
//      _osdSurface(0), _osdAlpha(SDL_ALPHA_TRANSPARENT), _osdFadeStartTime(0),
//#endif
	_hwscreen(0), _screen(0), _screenWidth(0), _screenHeight(0),
	_tmpscreen(0), _overlayVisible(false),
//      _cdrom(0),
//      _scaler_proc(0),
    _modeChanged(false), _dirty_checksums(0),
	_mouseVisible(false), _mouseDrawn(false), _mouseData(0),
	_mouseHotspotX(0), _mouseHotspotY(0),
	_currentShakePos(0), _newShakePos(0),
	_paletteDirtyStart(0), _paletteDirtyEnd(0), _graphicsMutex(0) {

	// allocate palette storage
	_currentPalette = (gpColor *) calloc(sizeof(gpColor), 256);

	// allocate the dirty rect storage
	_mouseBackup =
	    (byte *) malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2);

	// reset mouse state
	memset(&km, 0, sizeof(km));

	_scaleFactor = 1;
	_scaler_proc = Normal1x;

	_mode = GFX_NORMAL;
	_full_screen = true;
	_adjustAspectRatio = false;
	_mode_flags = 0;

	//init_intern();
}

OSystem_GP32::~OSystem_GP32() {
	if (_dirty_checksums)
		free(_dirty_checksums);
	free(_currentPalette);
	free(_mouseBackup);
	deleteMutex(_graphicsMutex);
	quit();
}

//      Set colors of the palette
void OSystem_GP32::setPalette(const byte * colors, uint start, uint num) {
	const byte *b = colors;
	uint i;
	gpColor *base = _currentPalette + start;
	for (i = 0; i < num; i++) {
		base[i].r = b[0];
		base[i].g = b[1];
		base[i].b = b[2];
		b += 4;
	}

	if (start < _paletteDirtyStart)
		_paletteDirtyStart = start;

	if (start + num > _paletteDirtyEnd)
		_paletteDirtyEnd = start + num;
}

//      Set the size of the video bitmap.
//      Typically, 320x200 (x240 for FMTowns)

//      FIXME: Is there a clean way to get the Game_ID in the backend, I still like the virual keymap feature below.
//#include "base/gameDetector.h"
//#include "scumm/scumm.h"
//extern ScummEngine *g_scumm;

const char shortkey0[] = "";
const char shortkey1[] = "gpuolscty";	// give, pick up, use, open, look at, push, close, talk to, pull
const char shortkey2[] = "qwerasdfzxcv";	//push, open, walk to, use, pull, close, pick up, turn on, give, look, what is, turn off
const char shortkey3[] = "wlptuo";	// samnmax
const char *shortkey;
int skindex = 0;

void OSystem_GP32::initSize(uint w, uint h, int overlayScale) {
	/*switch (menu[MENU_SCREENPOS].index) {
	 * case 0 : scrofsy = 239 - (240 - h)/2; break;
	 * case 1 : scrofsy = 239; break;
	 * }
	 */
	//switch (g_scumm->_gameId) { //fixme: add all
	//      case GID_TENTACLE : case GID_MONKEY2 : case GID_INDY4 : shortkey=shortkey1; break;
	//      case GID_INDY3 : case GID_ZAK256 : case GID_MONKEY : shortkey=shortkey2; break;
	//      case GID_SAMNMAX : shortkey=shortkey3; break;
	//      default : shortkey=shortkey0; break;
	//}

	shortkey = shortkey0;

	//      Avoid redundant res changes
	if ((int)w == _screenWidth && (int)h == _screenHeight)
		return;

	_screenWidth = w;
	_screenHeight = h;
	CKSUM_NUM = (_screenWidth * _screenHeight / (8 * 8));
	if (_dirty_checksums)
		free(_dirty_checksums);
	_dirty_checksums = (uint32 *) calloc(CKSUM_NUM * 2, sizeof(uint32));

	_mouseData = NULL;
	unload_gfx_mode();
	load_gfx_mode();
	return;
}

void OSystem_GP32::add_dirty_rect(int x, int y, int w, int h) {
	if (_forceFull)
		return;

	if (_num_dirty_rects == NUM_DIRTY_RECT)
		_forceFull = true;
	else {
		gpRect *r = &_dirty_rect_list[_num_dirty_rects++];

		//      Extend the dirty region by 1 pixel for scalers
		//      that "smear" the screen, e.g. 2xSAI
		if (_mode_flags & DF_UPDATE_EXPAND_1_PIXEL) {
			x--;
			y--;
			w += 2;
			h += 2;
		}
		// clip
		if (x < 0) {
			w += x;
			x = 0;
		}
		if (y < 0) {
			h += y;
			y = 0;
		}
		if (w > _screenWidth - x) {
			w = _screenWidth - x;
		}
		if (h > _screenHeight - y) {
			h = _screenHeight - y;
		}

		r->x = x;
		r->y = y;
		r->w = w;
		r->h = h;
	}
}

void OSystem_GP32::mk_checksums(const byte *buf) {
	uint32 *sums = _dirty_checksums;
	uint x, y;
	const uint last_x = (uint) _screenWidth / 8;
	const uint last_y = (uint) _screenHeight / 8;

	const uint BASE = 65521;	/* largest prime smaller than 65536 */

	/* the 8x8 blocks in buf are enumerated starting in the top left corner and
	 * reading each line at a time from left to right */
	for (y = 0; y != last_y; y++, buf += _screenWidth * (8 - 1))
		for (x = 0; x != last_x; x++, buf += 8) {
			// Adler32 checksum algorithm (from RFC1950, used by gzip and zlib).
			// This computes the Adler32 checksum of a 8x8 pixel block. Note
			// that we can do the modulo operation (which is the slowest part)
			// of the algorithm) at the end, instead of doing each iteration,
			// since we only have 64 iterations in total - and thus s1 and
			// s2 can't overflow anyway.
			uint32 s1 = 1;
			uint32 s2 = 0;
			const byte *ptr = buf;
			for (int subY = 0; subY < 8; subY++) {
				for (int subX = 0; subX < 8; subX++) {
					s1 += ptr[subX];
					s2 += s1;
				}
				ptr += _screenWidth;
			}

			s1 %= BASE;
			s2 %= BASE;

			/* output the checksum for this block */
			*sums++ = (s2 << 16) + s1;
		}
}

void OSystem_GP32::add_dirty_rgn_auto(const byte *buf) {
	assert(((uint32) buf & 3) == 0);

	/* generate a table of the checksums */
	mk_checksums(buf);

	if (!cksum_valid) {
		_forceFull = true;
		cksum_valid = true;
	}

	/* go through the checksum list, compare it with the previous checksums,
	 * and add all dirty rectangles to a list. try to combine small rectangles
	 * into bigger ones in a simple way */
	if (!_forceFull) {
		int x, y, w;
		uint32 *ck = _dirty_checksums;

		for (y = 0; y != _screenHeight / 8; y++) {
			for (x = 0; x != _screenWidth / 8; x++, ck++) {
				if (ck[0] != ck[CKSUM_NUM]) {
					/* found a dirty 8x8 block, now go as far to the right as possible,
					 * and at the same time, unmark the dirty status by setting old to new. */
					w = 0;
					do {
						ck[w + CKSUM_NUM] = ck[w];
						w++;
					} while (x + w != _screenWidth / 8
					    && ck[w] != ck[w + CKSUM_NUM]);

					add_dirty_rect(x * 8, y * 8, w * 8, 8);

					if (_forceFull)
						goto get_out;
				}
			}
		}
	} else {
	      get_out:;
		/* Copy old checksums to new */
		memcpy(_dirty_checksums + CKSUM_NUM, _dirty_checksums,
		    CKSUM_NUM * sizeof(uint32));
	}
}

// Draw a bitmap to screen.
// The screen will not be updated to reflect the new bitmap
void OSystem_GP32::copyRectToScreen(const byte *buf, int pitch, int x, int y,
    int w, int h) {
	if (_screen == NULL)
		return;

	if (pitch == _screenWidth && x == 0 && y == 0 && w == _screenWidth
	    && h == _screenHeight && _mode_flags & DF_WANT_RECT_OPTIM) {
		/* Special, optimized case for full screen updates.
		 * It tries to determine what areas were actually changed,
		 * and just updates those, on the actual display. */
		add_dirty_rgn_auto(buf);
	} else {
		/* Clip the coordinates */
		if (x < 0) {
			w += x;
			buf -= x;
			x = 0;
		}
		if (y < 0) {
			h += y;
			buf -= y * pitch;
			y = 0;
		}
		if (w > _screenWidth - x) {
			w = _screenWidth - x;
		}
		if (h > _screenHeight - y) {
			h = _screenHeight - y;
		}

		if (w <= 0 || h <= 0)
			return;

		cksum_valid = false;
		add_dirty_rect(x, y, w, h);
	}

	/* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */
	if (_mouseDrawn)
		undraw_mouse();

	byte *dst = (byte *) _screen->pixels + y * _screenWidth + x;
	do {
		memcpy(dst, buf, w);
		dst += _screenWidth;
		buf += pitch;
	} while (--h);
}

gpSurface *gpCreateRGBSurface(Uint32 flags, int width, int height, int depth,
    Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) {

	gpSurface *surf = (gpSurface *) malloc(sizeof(gpSurface));
	surf->format = (gpPixelFormat *) malloc(sizeof(gpPixelFormat));

	if ((flags & gpHWSurface) == gpHWSurface) {
		error(">HW surface (w=%d, h=%d)", width, height);
	} else if ((flags & gpSWSurface) == gpSWSurface) {
		int size = width * height * (depth / 8);
		printf(">SW surface (w=%d, h=%d, size=%d, depth=%d)", width,
		    height, size, depth);
		surf->pixels = malloc(size);
	} else {
		error(">unknown surface", width, height);
		return NULL;
	}
	surf->w = width;
	surf->h = height;
	surf->pitch = width * (depth / 8);
	surf->format->BitsPerPixel = depth;
	surf->format->BytesPerPixel = depth / 8;
	return surf;
}

gpSurface *gpSetVideoMode(int width, int height, int bpp, Uint32 flags) {
	return gpCreateRGBSurface(flags, width, height, bpp, 0, 0, 0, 0);
}

void gpFreeSurface(gpSurface *surface) {
	// implement
}

gpSurface *gpCreateRGBSurfaceFrom(void *pixels,
    int width, int height, int depth, int pitch,
    Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) {

// FIXME dont reuse code

	gpSurface *surf = (gpSurface *) malloc(sizeof(gpSurface));
	surf->format = (gpPixelFormat *) malloc(sizeof(gpPixelFormat));

	surf->w = width;
	surf->h = height;
	surf->pitch = pitch;
	surf->pixels = pixels;
	surf->format->BitsPerPixel = depth;
	surf->format->BytesPerPixel = depth / 8;
	return surf;
}

int gpFillRect(gpSurface *dst, gpRect *dstrect, Uint32 color) {
	// FIXME: implement
	return 0;
}

int mcshake = 0;

void gpUpdateRects(gpSurface *screen, int numrects, gpRect *rects) {
	// FIXME dont duplicate code :)
	// CHECK: shake causes crash? mcshake can get negative?

	if (screen->format->BitsPerPixel == 8)
		while (numrects--) {
			//if (mcshake && rects->h == LCD_HEIGHT) { //fixme?
			if (mcshake && rects->h == /*_screenHeight*/ 200) {	//fixme?
				//printf("shaking %d", mcshake);
				rects->h -= mcshake;
				GpRectFill(NULL, &LCDbuffer[GAME_SURFACE], 0, rects->h + scrofsy, 320, mcshake, 0);	//black border
			}

			u8 *s =
			    (u8 *) ((u8 *) screen->pixels + (rects->y +
				mcshake) * 320 + rects->x);
			u8 *d =
			    (u8 *) ((u8 *) LCDbuffer[GAME_SURFACE].ptbuffer +
			    rects->x * 240 + scrofsy - rects->y);
			u8 *s2 = s, *d2 = d;

			for (int x = rects->w; x; x--) {
				for (int y = rects->h; y; y--) {
					*d-- = *s;
					s += 320;	// FIXME? screen->pitch;
				}
				d2 += 240;
				d = d2;
				s2++;
				s = s2;
			}
			rects++;
	} else if (screen->format->BitsPerPixel == 16)
		while (numrects--) {
			u16 *s =
			    (u16 *) ((u16 *) screen->pixels + rects->y * 320 +
			    rects->x);
			u16 *d =
			    (u16 *) ((u16 *) LCDbuffer[GAME_SURFACE].ptbuffer +
			    rects->x * 240 + scrofsy - rects->y);
			u16 *s2 = s, *d2 = d;

			for (int x = rects->w; x; x--) {
				for (int y = rects->h; y; y--) {
					*d-- = *s;
					s += 320;	// FIXME? screen->pitch;
				}
				d2 += 240;
				d = d2;
				s2++;
				s = s2;
			}
			rects++;
	} else
		error("blitting surface with wrong depth (%d)",
		    screen->format->BitsPerPixel);
	// eh? works also when rects++ is here??
}

//#define gpBlitSurface gpUpperBlit
int gpBlitSurface(gpSurface *screen, gpRect *rects, gpSurface *dst, gpRect *dstrect) {
	// FIXME? role??
	//gpUpdateRects(screen, 1, rects); //ph0x! _hwscreen
	return 0;
}

int gpSetColors(gpSurface *surface, gpColor *colors, int firstcolor, int ncolors) {
	float rr, gg, bb;
	gpColor colors2[256];

	if (currentsurf == DEBUG_SURFACE)
		return 1;

	for (int i = firstcolor; i < firstcolor + ncolors; i++) {
		rr = colors[i].r * gammatab[colors[i].r];
		gg = colors[i].g * gammatab[colors[i].g];
		bb = colors[i].b * gammatab[colors[i].b];

		if (rr > 255)
			rr = 255;
		if (gg > 255)
			gg = 255;
		if (bb > 255)
			bb = 255;

		colors2[i].r = (u8) rr;
		colors2[i].g = (u8) gg;
		colors2[i].b = (u8) bb;
	}

	GpPaletteEntryChangeEx(firstcolor, ncolors, (GP_LOGPALENTRY *) colors2,
	    0);
	return 1;
}

// Moves the screen content around by the given amount of pixels
// but only the top height pixel rows, the rest stays untouched
//void OSystem_GP32::move_screen(int dx, int dy, int height)
//{
//      if ((dx == 0) && (dy == 0))
//              return;
//
//      if (dx == 0) {
//              // vertical movement
//              if (dy > 0) {
//                      // move down
//                      // copy from bottom to top
//                      for (int y = height - 1; y >= dy; y--)
//                              copyRectToScreen((byte *)_screen->pixels + _screenWidth * (y - dy), _screenWidth, 0, y, _screenWidth, 1);
//              } else {
//                      // move up
//                      // copy from top to bottom
//                      for (int y = 0; y < height + dx; y++)
//                              copyRectToScreen((byte *)_screen->pixels + _screenWidth * (y - dy), _screenWidth, 0, y, _screenWidth, 1);
//              }
//      } else if (dy == 0) {
//              // horizontal movement
//              if (dx > 0) {
//                      // move right
//                      // copy from right to left
//                      for (int x = _screenWidth - 1; x >= dx; x--)
//                              copyRectToScreen((byte *)_screen->pixels + x - dx, _screenWidth, x, 0, 1, height);
//              } else {
//                      // move left
//                      // copy from left to right
//                      for (int x = 0; x < _screenWidth; x++)
//                              copyRectToScreen((byte *)_screen->pixels + x - dx, _screenWidth, x, 0, 1, height);
//              }
//      } else {
//              // free movement
//              // not necessary for now
//      }
//}

int16 OSystem_GP32::get_height() {
	return _screenHeight;
}

int16 OSystem_GP32::get_width() {
	return _screenWidth;
}

//void OSystem_GP32::warpMouse(int, int)
//{
//}

void OSystem_GP32::warpMouse(int x, int y) {
//              set_mouse_pos(x, y);
}

void OSystem_GP32::load_gfx_mode() {

	GpRectFill(NULL, &LCDbuffer[GAME_SURFACE], 0, 0, 320, 240, 0);	//black border

	_forceFull = true;
	_mode_flags = DF_WANT_RECT_OPTIM | DF_UPDATE_EXPAND_1_PIXEL;

	_tmpscreen = NULL;
	TMP_SCREEN_WIDTH = (_screenWidth + 3);

	switch (_mode) {

	case GFX_NORMAL:
		//???????
		//normal_mode:;
		_scaleFactor = 1;
		_scaler_proc = Normal1x;

		break;
	default:
		error("Unknown graphics mode");
		_scaleFactor = 1;
		_scaler_proc = NULL;
	}

	//
	// Create the surface that contains the 8 bit game data
	//
	_screen =
	    gpCreateRGBSurface(gpSWSurface, _screenWidth, _screenHeight, 8, 0,
	    0, 0, 0);
	if (_screen == NULL)
		error("_screen failed");

	//
	// Create the surface that contains the scaled graphics in 16 bit mode
	//
	_hwscreen =
	    gpSetVideoMode(_screenWidth * _scaleFactor,
	    _screenHeight * _scaleFactor, 16,
	    _full_screen ? (gpFullScreen | gpSWSurface) : gpSWSurface);
	if (_hwscreen == NULL)
		error("_hwscreen failed");

	//
	// Create the surface used for the graphics in 16 bit before scaling, and also the overlay
	//

/*
	// Distinguish 555 and 565 mode
	if (_hwscreen->format->Rmask == 0x7C00)
		InitScalers(555);
	else
		InitScalers(565);
*/
	//InitScalers(555); // ph0x fixme?

	//ph0x fixme - tmpscreen needed?

	// Need some extra bytes around when using 2xSaI
	uint16 *tmp_screen =
	    (uint16 *) calloc(TMP_SCREEN_WIDTH * (_screenHeight + 3),
	    sizeof(uint16));
	_tmpscreen =
	    gpCreateRGBSurfaceFrom(tmp_screen, TMP_SCREEN_WIDTH,
	    _screenHeight + 3, 16, TMP_SCREEN_WIDTH * 2,
	    _hwscreen->format->Rmask, _hwscreen->format->Gmask,
	    _hwscreen->format->Bmask, _hwscreen->format->Amask);

	if (_tmpscreen == NULL)
		error("_tmpscreen failed");

	// keyboard cursor control, some other better place for it?
	km.x_max = _screenWidth * _scaleFactor - 1;
	km.y_max = _screenHeight * _scaleFactor - 1;
	//km.delay_time = 25;
	km.delay_time = 15;
	km.last_time = 0;

}

void OSystem_GP32::unload_gfx_mode() {
	if (_screen) {
		gpFreeSurface(_screen);
		_screen = NULL;
	}

	if (_hwscreen) {
		gpFreeSurface(_hwscreen);
		_hwscreen = NULL;
	}

	if (_tmpscreen) {
		free((uint16 *) _tmpscreen->pixels);
		gpFreeSurface(_tmpscreen);
		_tmpscreen = NULL;
	}
}

#include "common/util.h"

void OSystem_GP32::draw_mouse() {
	if (!_overlayVisible) {

		if (_mouseDrawn || !_mouseVisible)
			return;

		int x = _mouseCurState.x - _mouseHotspotX;
		int y = _mouseCurState.y - _mouseHotspotY;
		int w = _mouseCurState.w;
		int h = _mouseCurState.h;
		byte color;
		byte *src = _mouseData;	// Image representing the mouse
		byte *bak = _mouseBackup;	// Surface used to backup the area obscured by the mouse
		byte *dst;	// Surface we are drawing into

		// clip the mouse rect, and addjust the src pointer accordingly
		if (x < 0) {
			w += x;
			src -= x;
			x = 0;
		}
		if (y < 0) {
			h += y;
			src -= y * _mouseCurState.w;
			y = 0;
		}
		if (w > _screenWidth - x)
			w = _screenWidth - x;
		if (h > _screenHeight - y)
			h = _screenHeight - y;

		// Quick check to see if anything has to be drawn at all
		if (w <= 0 || h <= 0)
			return;

		// Store the bounding box so that undraw mouse can restore the area the
		// mouse currently covers to its original content.
		_mouseOldState.x = x;
		_mouseOldState.y = y;
		_mouseOldState.w = w;
		_mouseOldState.h = h;

		// Draw the mouse cursor; backup the covered area in "bak"

		///if (gpLockSurface(_screen) == -1)
		///     error("gpLockSurface failed: %s.\n", gpGetError());

		//  as dirty
		add_dirty_rect(x, y, w, h);

		dst = (byte *) _screen->pixels + y * _screenWidth + x;
		while (h > 0) {
			int width = w;
			while (width > 0) {
				*bak++ = *dst;
				color = *src++;
				if (color != _mouseKeycolor)	// Transparent, don't draw
					*dst = color;
				dst++;
				width--;
			}
			src += _mouseCurState.w - w;
			bak += MAX_MOUSE_W - w;
			dst += _screenWidth - w;
			h--;
		}

		///gpUnlockSurface(_screen);

		// Finally, set the flag to indicate the mouse has been drawn
		_mouseDrawn = true;
	}

	if (_mouseDrawn || !_mouseVisible)
		return;

	int x = _mouseCurState.x - _mouseHotspotX;
	int y = _mouseCurState.y - _mouseHotspotY;
	int w = _mouseCurState.w;
	int h = _mouseCurState.h;
	byte color;
	//byte *src = _mouseData;               // Image representing the mouse
	//uint16 *src = _mouseData;             // Image representing the mouse
	const byte *src = _mouseData;	// Image representing the mouse
	//byte *bak = _mouseBackup;     // Surface used to backup the area obscured by the mouse
	//byte *dst;                                    // Surface we are drawing into

	//uint16 *bak = (uint16*)_mouseBackup;  // Surface used to backup the area obscured by the mouse
	//uint16 *dst;                                  // Surface we are drawing into

	// clip the mouse rect, and addjust the src pointer accordingly
	if (x < 0) {
		w += x;
		src -= x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		src -= y * _mouseCurState.w;
		y = 0;
	}
	// Quick check to see if anything has to be drawn at all
	if (w <= 0 || h <= 0)
		return;

	if (w > _screenWidth - x)
		w = _screenWidth - x;
	if (h > _screenHeight - y)
		h = _screenHeight - y;

	// Store the bounding box so that undraw mouse can restore the area the
	// mouse currently covers to its original content.
	_mouseOldState.x = x;
	_mouseOldState.y = y;
	_mouseOldState.w = w;
	_mouseOldState.h = h;

	// Draw the mouse cursor; backup the covered area in "bak"

	///if (gpLockSurface(_tmpscreen) == -1)
	///     error("gpLockSurface failed: %s.\n", gpGetError());

	//  as dirty
	add_dirty_rect(x, y, w, h);

	uint16 *bak = (uint16 *) _mouseBackup;	// Surface used to backup the area obscured by the mouse
	uint16 *dst;		// Surface we are drawing into

	dst =
	    (uint16 *) _tmpscreen->pixels + (y + 1) * TMP_SCREEN_WIDTH + (x +
	    1);
	while (h > 0) {
		int width = w;
		while (width > 0) {
			*bak++ = *dst;
			color = *src++;
			if (color != 0xFF)	// 0xFF = transparent, don't draw
				*dst =
				    RGB_TO_16(_currentPalette[color].r,
				    _currentPalette[color].g,
				    _currentPalette[color].b);
			dst++;
			width--;
		}
		src += _mouseCurState.w - w;
		bak += MAX_MOUSE_W - w;
		dst += TMP_SCREEN_WIDTH - w;
		h--;
	}

	///gpUnlockSurface(_tmpscreen);

	// Finally, set the flag to indicate the mouse has been drawn
	_mouseDrawn = true;
}

void OSystem_GP32::undraw_mouse() {
	//return; //fixme!
	if (!_overlayVisible) {

		if (!_mouseDrawn)
			return;
		_mouseDrawn = false;

		byte *dst, *bak = _mouseBackup;
		const int old_mouse_x = _mouseOldState.x;
		const int old_mouse_y = _mouseOldState.y;
		const int old_mouse_w = _mouseOldState.w;
		const int old_mouse_h = _mouseOldState.h;
		int x, y;

		// No need to do clipping here, since draw_mouse() did that already

		dst =
		    (byte *) _screen->pixels + old_mouse_y * _screenWidth +
		    old_mouse_x;
		for (y = 0; y < old_mouse_h;
		    ++y, bak += MAX_MOUSE_W, dst += _screenWidth) {
			for (x = 0; x < old_mouse_w; ++x) {
				dst[x] = bak[x];
			}
		}

		add_dirty_rect(old_mouse_x, old_mouse_y, old_mouse_w,
		    old_mouse_h);
	}

	if (!_mouseDrawn)
		return;
	_mouseDrawn = false;

	uint16 *dst, *bak = (uint16 *) _mouseBackup;
	const int old_mouse_x = _mouseOldState.x;
	const int old_mouse_y = _mouseOldState.y;
	const int old_mouse_w = _mouseOldState.w;
	const int old_mouse_h = _mouseOldState.h;
	int x, y;

	// No need to do clipping here, since draw_mouse() did that already

	dst =
	    (uint16 *) _tmpscreen->pixels + (old_mouse_y +
	    1) * TMP_SCREEN_WIDTH + (old_mouse_x + 1);
	for (y = 0; y < old_mouse_h;
	    ++y, bak += MAX_MOUSE_W, dst += TMP_SCREEN_WIDTH) {
		for (x = 0; x < old_mouse_w; ++x) {
			dst[x] = bak[x];
		}
	}

	add_dirty_rect(old_mouse_x, old_mouse_y, old_mouse_w, old_mouse_h);
}

char *gpGetError(void) {
	// FIXME: implement
	return NULL;
}

/////////////////////////////////////////////////////////////////////////////
//
// GP32 Screen Update Stuff - Mostly 'borrowed' from GP but using gpSDK
//
/////////////////////////////////////////////////////////////////////////////

//// Update the dirty areas of the screen
void OSystem_GP32::updateScreen() {
	//Common::StackLock lock(_graphicsMutex);       // Lock the mutex until this function ends
	internUpdateScreen();
}

//      assert(_hwscreen != NULL);
//
//      // If the shake position changed, fill the dirty area with blackness
//      if (_currentShakePos != _newShakePos) {
//              gpRect blackrect = {0, 0, _screenWidth*_scaleFactor, _newShakePos*_scaleFactor};
//
//              if (_adjustAspectRatio)
//                      blackrect.h = real2Aspect(blackrect.h - 1) + 1;
//
//              gpFillRect(_hwscreen, &blackrect, 0);
//
//              _currentShakePos = _newShakePos;
//
//              _forceFull = true;
//      }
//
//      // Make sure the mouse is drawn, if it should be drawn.
//      draw_mouse();
//
//      // Check whether the palette was changed in the meantime and update the
//      // screen surface accordingly.
//      if (_paletteDirtyEnd != 0) {
//              gpSetColors(_screen, _currentPalette + _paletteDirtyStart,
//                      _paletteDirtyStart,
//                      _paletteDirtyEnd - _paletteDirtyStart);
//
//              _paletteDirtyEnd = 0;
//
//              _forceFull = true;
//      }
//
//      // Force a full redraw if requested
//      if (_forceFull) {
//              _num_dirty_rects = 1;
//
//              _dirty_rect_list[0].x = 0;
//              _dirty_rect_list[0].y = 0;
//              _dirty_rect_list[0].w = _screenWidth;
//              _dirty_rect_list[0].h = _screenHeight;
//      }
//
//      // Only draw anything if necessary
//      if (_num_dirty_rects > 0) {
//
//              gpRect *r;
//              uint32 srcPitch, dstPitch;
//              gpRect *last_rect = _dirty_rect_list + _num_dirty_rects;
//
//              // Convert appropriate parts of the 8bpp image into 16bpp
//              if (!_overlayVisible) {
//                      gpRect dst;
//                      for(r = _dirty_rect_list; r != last_rect; ++r) {
//                              dst = *r;
//                              dst.x++;        // FIXME? Shift rect by one since 2xSai needs to acces the data around
//                              dst.y++;        // FIXME? any pixel to scale it, and we want to avoid mem access crashes.
//
//                              if (gpBlitSurface(_screen, r, _hwscreen, &dst) != 0) //ph0x! _tmpscreen
//                                      error("gpBlitSurface failed: %s", gpGetError());
//                      }
//              }
//
//// ph0x! (no scaling) cannot skip intro if commented?
//
//              srcPitch = _tmpscreen->pitch;
//              dstPitch = _hwscreen->pitch;
//              for(r = _dirty_rect_list; r != last_rect; ++r) {
//                      register int dst_y = r->y + _currentShakePos;
//                      register int dst_h = 0;
//                      if (dst_y < _screenHeight) {
//                              dst_h = r->h;
//                              if (dst_h > _screenHeight - dst_y)
//                                      dst_h = _screenHeight - dst_y;
//
//                              dst_y *= _scaleFactor;
//
//                              if (_overlayVisible) //ph0x fixme?
//                              _scaler_proc((byte*)_tmpscreen->pixels + (r->x*2+2) + (r->y+1)*srcPitch, srcPitch,
//                                      (byte*)_hwscreen->pixels + r->x*2*_scaleFactor + dst_y*dstPitch, dstPitch, r->w, dst_h);
//                      }
//                      r->x *= _scaleFactor;
//                      r->y = dst_y;
//                      r->w *= _scaleFactor;
//                      r->h = dst_h * _scaleFactor;
//              }
//
//              // Readjust the dirty rect list in case we are doing a full update.
//              // This is necessary if shaking is active.
//              if (_forceFull) {
//                      _dirty_rect_list[0].y = 0;
//                      _dirty_rect_list[0].h = _screenHeight * _scaleFactor;
//              }
//
//              // Finally, blit all our changes to the screen
//
//              // FIXME (dont use condition)
//              if (_overlayVisible)
//                      gpUpdateRects(_hwscreen, _num_dirty_rects, _dirty_rect_list); //ph0x! _hwscreen
//              else
//                      gpUpdateRects(_screen, _num_dirty_rects, _dirty_rect_list);
//      }
//
//      _num_dirty_rects = 0;
//      _forceFull = false;
//}

// Either show or hide the mouse cursor
bool OSystem_GP32::showMouse(bool visible) {
	if (_mouseVisible == visible)
		return visible;

	bool last = _mouseVisible;
	_mouseVisible = visible;

	if (visible)
		draw_mouse();
	else
		undraw_mouse();

	return last;
}

// Set the position of the mouse cursor
void OSystem_GP32::set_mouse_pos(int x, int y) {
	if (x != _mouseCurState.x || y != _mouseCurState.y) {
		_mouseCurState.x = x;
		_mouseCurState.y = y;

		mx = x;		//ph0x fixme
		my = y;		//ph0x fixme
		undraw_mouse();
	}
}

// Set the bitmap that's used when drawing the cursor.
void OSystem_GP32::setMouseCursor(const byte *buf, uint w, uint h,
    int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) {

//      assert(w <= MAX_MOUSE_W);
//      assert(h <= MAX_MOUSE_H);

	_mouseCurState.w = w;
	_mouseCurState.h = h;

	_mouseHotspotX = hotspot_x;
	_mouseHotspotY = hotspot_y;

	_mouseKeycolor = keycolor;

//??????????
	_mouseData = (byte *) buf;

	undraw_mouse();

	if (_mouseData)
		free(_mouseData);

	_mouseData = (byte *) malloc(w * h);

	memcpy(_mouseData, buf, w * h);
}

// Shaking is used in SCUMM. Set current shake position.
void OSystem_GP32::setShakePos(int shake_pos) {
	_newShakePos = shake_pos;
	mcshake = shake_pos;
}

// Get the number of milliseconds since the program was started.
uint32 OSystem_GP32::getMillis() {
	return GpTickCountGet();
}

// Delay for a specified amount of milliseconds
void OSystem_GP32::delayMillis(uint msecs) {
	int n = GpTickCountGet();
	while ((GpTickCountGet() - n) < msecs);
}

// Get the next event.
// Returns true if an event was retrieved.

const signed char abc[] = "0123456789abcdefghijklmnopqrstuvwxyz";
signed int abcindex = -1;

void switchsurf(int surf);
void buildgammatab(int val);

/////////////////////////////////////////////////////////////////////////////
//
// GP32 Event Handlers.
//
/////////////////////////////////////////////////////////////////////////////

bool OSystem_GP32::pollEvent(Event & event) {

#define EVENT_COUNT	2	// >=1
	//#define MOUSE_MIPS    2 // bg updates wrong if >1 ??
#define MOUSE_MIPS 1		// bg updates wrong if >1 ??

	static int lastkey, eventcount = EVENT_COUNT, lastevent = 0;
	static int simulate;
	static bool backspace = true;
	static uint32 t;
	int key;

	key = gpTrapKey();

	if (simulate)
		simulate--;
	switch (simulate) {
	case 5:
		lastevent = event.type = EVENT_KEYDOWN;
		event.kbd.keycode = event.kbd.ascii = 8;
		return true;
		break;
	case 3:
		lastevent = event.type = EVENT_KEYDOWN;
		event.kbd.keycode = event.kbd.ascii = abc[abcindex];
		return true;
		break;
	case 4:
	case 2:
		lastevent = event.type = EVENT_KEYUP;
		//event.kbd.keycode = event.kbd.ascii =
		return true;
		break;
	case 1:
		lastkey = key = 0;
		lastevent = 0;
		event.type = (EventType) 0;
		break;
	}

	if (lastevent == EVENT_KEYDOWN) {
		lastevent = event.type = EVENT_KEYUP;
		//event.kbd.keycode = event.kbd.ascii;
		return true;
	}

	if (key == GPC_VK_NONE) {
		lastevent = lastkey = 0;
		return false;
	}

	if (key == lastkey) {
		eventcount--;
		if (eventcount)
			return false;
	}

	eventcount = EVENT_COUNT;
	event.type = EVENT_KEYDOWN;

	if (key & GPC_VK_FL) {	// L
		if (_overlayVisible)
			return false;

		if (key & GPC_VK_UP) {
			if (key == lastkey)
				return false;

			if (gindex < ARRAYSIZE(scrGamma) - 1)
				gindex++;
			buildgammatab(gindex);
			_paletteDirtyStart = 0;
			_paletteDirtyEnd = 255;	//fixme?
			lastevent = event.type;
			lastkey = key;
			return true;
		} else
		 if (key & GPC_VK_DOWN) {
			if (key == lastkey)
				return false;

			if (gindex > 0)
				gindex--;
			buildgammatab(gindex);
			_paletteDirtyStart = 0;
			_paletteDirtyEnd = 255;	//fixme?
			lastevent = event.type;
			lastkey = key;
			return true;
		}

		if (key == lastkey)
			return false;
		if (skindex > 0)
			skindex--;
		event.kbd.keycode = event.kbd.ascii = shortkey[skindex];
		lastevent = event.type;
		lastkey = key;
		return true;
	}

	lastkey = key;

	if (key & GPC_VK_FR) {	// R
		if (key & GPC_VK_UP) {
			if (getMillis() < t)
				return false;
			//do key=GpKeyGet(); while (key & GPC_VK_UP);
			//fixme -2/-1
			t = getMillis() + 200;
			if (abcindex == -1)
				abcindex = 0;
			else {
				if (abcindex < sizeof(abc) - 2)
					abcindex++;
				else
					abcindex = 0;
			}
			if (backspace)
				simulate = 6;
			else {
				backspace = true;
				simulate = 4;
			}
			return false;
		}

		if (key & GPC_VK_DOWN) {
			if (getMillis() < t)
				return false;
			//do key=GpKeyGet(); while (key & GPC_VK_DOWN);
			//fixme -2/-1
			t = getMillis() + 200;
			if (abcindex == -1)
				abcindex = abcindex = sizeof(abc) - 2;
			else {
				if (abcindex > 0)
					abcindex--;
				else
					abcindex = sizeof(abc) - 2;
			}
			if (backspace)
				simulate = 6;
			else {
				backspace = true;
				simulate = 4;
			}
			return false;
		}

		if (key & GPC_VK_LEFT) {
			abcindex = -1;
			event.kbd.keycode = event.kbd.ascii = 8;
			lastevent = event.type;
			do
				key = gpTrapKey();
			while (key & GPC_VK_LEFT);
			return true;
		} else if (key & GPC_VK_RIGHT) {
			abcindex = -1;
			backspace = false;
			return false;
		}
		if (!_overlayVisible) {
			if (lastevent == EVENT_KEYUP)
				return false;
			if (shortkey[skindex + 1])
				skindex++;
			event.kbd.keycode = event.kbd.ascii =
			    shortkey[skindex];
			lastevent = event.type;
			return true;
		}
	} else if (key & GPC_VK_START) {	// START = menu/enter
		if (_overlayVisible)
			event.kbd.keycode = event.kbd.ascii = 13;
		else {
			event.kbd.keycode = event.kbd.ascii = 319;
			//buildgammatab(ARRAYSIZE(scrGamma)-1); // moved to colortoRBG
		}
		lastevent = event.type;
		return true;
	}

	if (key & GPC_VK_SELECT) {	// SELECT == escape/skip
		if (_overlayVisible) {
			do
				key = gpTrapKey();
			while (key != GPC_VK_NONE);	// prevent 2xESC
			buildgammatab(gindex);
			_paletteDirtyStart = 0;
			_paletteDirtyEnd = 255;	//fixme?
		}
		event.kbd.keycode = event.kbd.ascii = 27;
		lastevent = event.type;
		return true;
	}

	if (key & GPC_VK_FA) {
		if (lastevent == EVENT_LBUTTONUP)
			return false;

		if (lastevent == EVENT_LBUTTONDOWN) {
			lastevent = EVENT_LBUTTONUP;
			event.type = EVENT_LBUTTONUP;
		} else {
			lastevent = EVENT_LBUTTONDOWN;
			event.type = EVENT_LBUTTONDOWN;
		}
		return true;
	}

	if (key & GPC_VK_FB) {
		if (lastevent == EVENT_RBUTTONUP)
			return false;

		if (lastevent == EVENT_RBUTTONDOWN) {
			lastevent = EVENT_RBUTTONUP;
			event.type = EVENT_RBUTTONUP;
		} else {
			lastevent = EVENT_RBUTTONDOWN;
			event.type = EVENT_RBUTTONDOWN;
		}
		return true;
	}

	if (key & GPC_VK_LEFT) {
		mx -= MOUSE_MIPS;
		if (mx < 1)
			mx = 1;	// wrong if 0?
	}

	if (key & GPC_VK_RIGHT) {
		mx += MOUSE_MIPS;
		if (mx > 319)
			mx = 319;
	}

	if (key & GPC_VK_UP) {
		my -= MOUSE_MIPS;
		if (my < 1)
			my = 1;	// wrong if 0?
	}

	if (key & GPC_VK_DOWN) {
		my += MOUSE_MIPS;
		if (my > _screenHeight - 1)
			my = _screenHeight - 1;
	}

	event.type = EVENT_MOUSEMOVE;
	km.x = event.mouse.x = mx;
	km.y = event.mouse.y = my;
	event.mouse.x /= _scaleFactor;
	event.mouse.y /= _scaleFactor;
	set_mouse_pos(event.mouse.x, event.mouse.y);
}

/////////////////////////////////////////////////////////////////////////////
//
// GP32 Graphics Stuff -
//
/////////////////////////////////////////////////////////////////////////////

int16 OSystem_GP32::RBGToColor(uint8 r, uint8 g, uint8 b) {
	float rr, gg, bb;

	rr = r * gammatab2[r];
	gg = g * gammatab2[g];
	bb = b * gammatab2[b];

	if (rr > 255)
		rr = 255;
	if (gg > 255)
		gg = 255;
	if (bb > 255)
		bb = 255;

	r = (u8) rr;
	g = (u8) gg;
	b = (u8) bb;

	//return ((((r>>3)&0x1F) << 11) | (((g>>2)&0x3F) << 5) | ((b>>3)&0x1F)); //ph0x
	return (((((r) >> 3) & 0x1F) << 11) | ((((g) >> 3) & 0x1F) << 6) |
	    (((b) >> 3) & 0x1F) << 1);
}

void OSystem_GP32::colorToRBG(int16 color, uint8 &r, uint8 &g, uint8 &b) {
	float rr, gg, bb;
	r = ((((color) >> 11) & 0x1F) << 3);	//(((color>>11)&0x1F) << 3);
	g = ((((color) >> 6) & 0x1F) << 3);	//(((color>>5)&0x3F) << 2);
	b = ((((color) >> 1) & 0x1F) << 3);	//((color&0x1F) << 3);

	rr = r * gammatab2[r];
	gg = g * gammatab2[g];
	bb = b * gammatab2[b];

	if (rr > 255)
		rr = 255;
	if (gg > 255)
		gg = 255;
	if (bb > 255)
		bb = 255;

	r = (u8) rr;
	g = (u8) gg;
	b = (u8) bb;
}

void switchsurf(int surf) {
	GPLCDINFO lcd;
	GpLcdInfoGet(&lcd);

	if (surf == DEBUG_SURFACE) {
		if (lcd.lcd_global.U8_lcd.bpp == 16)
			GpGraphicModeSet(8, NULL);

		currentsurf = DEBUG_SURFACE;
		GpSurfaceFlip(&LCDbuffer[(int)currentsurf]);

		//GpSetPaletteEntry ( 0, 0,0,0 );
		//GpSetPaletteEntry ( 1, 0,0,0 );
		//GpSetPaletteEntry ( 2, 255,255,255 );

	} else if (surf == GAME_SURFACE) {
		//if (lcd.lcd_global.U8_lcd.bpp == 8) GpGraphicModeSet(16, NULL);

		currentsurf = GAME_SURFACE;
		GpSurfaceFlip(&LCDbuffer[(int)currentsurf]);
		//GpSetPaletteEntry ( 2, 0,0,0 );
		//GpSetPaletteEntry ( 1, 0,107,84 );
		//GpSetPaletteEntry ( 0, 255,255,255 );
	} else
		error("Switching to false stuface");
}

/////////////////////////////////////////////////////////////////////////////
//
// GP32 Sound Stuff -
//
/////////////////////////////////////////////////////////////////////////////

void OSystem_GP32::clearSoundCallback() {
//  _sound_proc = NULL;
//  _sound_proc_param = NULL;
}

typedef void SoundProc(void *param, byte *buf, int len);

typedef struct GPSOUNDBUF {
	PCM_SR freq;		/* Taken from gpmm.h */
	PCM_BIT format;		/* Taken from gpmm.h */
	unsigned int samples;	/* Buffer length (in samples) */
	void *userdata;		/* Userdata which gets passed to the callback function */
	SoundProc *callback;
	unsigned int pollfreq;	/* Frequency of the timer interrupt which polls the playing position
				 * recommended value: 2*(playingfreq in Hz/GPSOUNDBUF.samples) */
	unsigned int samplesize;	/* Size of one sample (8bit mono->1, 16bit stereo->4) - don't touch this */
} GPSOUNDBUF;

GPSOUNDBUF gpsndbuf;		// for scumm

/* Global variables */
unsigned int frame = 0;
unsigned int *soundPos = 0;
volatile int idx_buf;
unsigned int shiftVal = 0;
void *buffer;
GPSOUNDBUF soundBuf;

/* This routine gets called by the timer interrupt and
 * polls the current playing position within the buffer.
 */

//void *blah; // holds "this" for mixer.cpp

void soundtimer(void) {
	unsigned int t =
	    (((unsigned int)(*soundPos) - (unsigned int)buffer) >> shiftVal) >=
	    soundBuf.samples ? 1 : 0;
	if (t != frame) {
		unsigned int offs =
		    ((frame == 1) ? (soundBuf.samples << shiftVal) : 0);
		soundBuf.callback(soundBuf.userdata /*blah */ , (u8 *) ((unsigned int)buffer + offs), soundBuf.samples << shiftVal);	//FIXME (*callback)(param) ?
		frame = t;
	}
}

int GpSoundBufStart(GPSOUNDBUF *sb) {
	frame = 0;

	/* Copy the structure */
	memcpy(&soundBuf, sb, sizeof(GPSOUNDBUF));

	/* Calculate size of a single sample in bytes
	 * and a corresponding shift value
	 */
	shiftVal = 0;
	switch (soundBuf.freq) {
	case PCM_S11:
		break;
	case PCM_S22:
		break;
	case PCM_S44:
		shiftVal++;
		break;
	case PCM_M11:
		break;
	case PCM_M22:
		break;
	case PCM_M44:
		shiftVal++;
		break;
	}
	if (soundBuf.format == PCM_16BIT)
		shiftVal++;
	soundBuf.samplesize = 1 << shiftVal;

	/* Allocate memory for the playing buffer */
	buffer = malloc(soundBuf.samplesize * soundBuf.samples * 2);
	memset(buffer, 0, soundBuf.samplesize * soundBuf.samples * 2);

	/* Set timer interrupt #0 */
	if (GpTimerOptSet(0, soundBuf.pollfreq, 0,
		soundtimer) == GPOS_ERR_ALREADY_USED)
		error("timer slot used");
	GpTimerSet(0);
	/* Start playing */
	GpPcmPlay((unsigned short *)buffer,
	    soundBuf.samples * soundBuf.samplesize * 2, 1);
	GpPcmLock((unsigned short *)buffer, (int *)&idx_buf,
	    (unsigned int *)&soundPos);

	return 0;
}

void GpSoundBufStop(void) {
	GpPcmStop();
	GpPcmRemove((unsigned short *)buffer);
	GpTimerKill(0);
	free(buffer);
}

int OSystem_GP32::getOutputSampleRate() const {
	return SAMPLES_PER_SEC;
}

// Set the function to be invoked whenever samples need to be generated
// Buffer Length and Poll Frequency changed. DJWillis
bool OSystem_GP32::setSoundCallback(SoundProc proc, void *param) {
	gpsndbuf.freq = PCM_S22;	// Taken from gpmm.h
	gpsndbuf.format = PCM_16BIT;	// Taken from gpmm.h
	gpsndbuf.samples = 2048;	//128; //fixme?    // Buffer length (in samples)
	//FIXME? crashes if not commented?!
	//gpsndbuf.userdata=g_scumm; //param; //fixme?          // Userdata which gets passed to the callback function
	gpsndbuf.callback = proc;	//mycallback;         // Callback function (just like in SDL)
	//2*((float)22025/(float)s.samples);
	gpsndbuf.pollfreq = 8 * (SAMPLES_PER_SEC / gpsndbuf.samples);	//fixme
	// Frequency of the timer interrupt which polls the playing position
	// recommended value: 2*(playingfreq in Hz/GPSOUNDBUF.samples)
	//s.samplesize;  // Size of one sample (8bit mono->1, 16bit stereo->4) - don't touch this

	GpPcmInit(PCM_S22, PCM_16BIT);
	GpSoundBufStart(&gpsndbuf);
	return true;
}

/////////////////////////////////////////////////////////////////////////////
//
// GP32 Graphics Stuff
//
/////////////////////////////////////////////////////////////////////////////

void OSystem_GP32::get_screen_image(byte *buf) {
	/* make sure the mouse is gone */
	undraw_mouse();

	///if (gpLockSurface(_screen) == -1)
	///     error("gpLockSurface failed: %s.\n", gpGetError());

	memcpy(buf, _screen->pixels, _screenWidth * _screenHeight);

	///gpUnlockSurface(_screen);
}

void OSystem_GP32::hotswap_gfx_mode() {
	/* We allocate a screen sized bitmap which contains a "backup"
	 * of the screen data during the change. Then we draw that to
	 * the new screen right after it's setup.
	 */

	byte *bak_mem = (byte *) malloc(_screenWidth * _screenHeight);

	get_screen_image(bak_mem);

	unload_gfx_mode();
	load_gfx_mode();

	// reset palette
	gpSetColors(_screen, _currentPalette, 0, 256);

	// blit image
	copyRectToScreen(bak_mem, _screenWidth, 0, 0, _screenWidth,
	    _screenHeight);
	free(bak_mem);

	updateScreen();
}

// Get or set a property
//uint32 OSystem_GP32::property(int param, Property *value)
//{
//      switch(param) {
//
//      case PROP_GET_FULLSCREEN:
//              return _full_screen;
//}

void OSystem_GP32::setWindowCaption(const char *caption) {
	//gGameName = caption; // Would like to return game here like DC port. - DJWillis
}

// CDROM Code - All returns false as the GP32 has no CDROM ;-)

bool OSystem_GP32::openCD(int drive) {
	return false;
}

bool OSystem_GP32::pollCD() {
	return false;
}

void OSystem_GP32::playCD(int track, int num_loops, int start_frame, int duration) {
}

void OSystem_GP32::stopCD() {
}

void OSystem_GP32::updateCD() {
}

// End CDROM Code.

// Add a new callback timer

// ph0x FIXME: make members
int _timerinterval;
int (*_timercallback)(int);

void voidcallback() {
	//printf("timer running");
	_timercallback(_timerinterval);	//FIXME ?? (*_timercallback)(_timerinterval);
}

void OSystem_GP32::setTimerCallback(TimerProc callback, int timer) {
	int timerno = 1;	//0 used by sound proc

	if (!callback) {
		GpTimerKill(timerno);
		return;
	}

	if (GpTimerOptSet(timerno, timer, 0,
		voidcallback) == GPOS_ERR_ALREADY_USED)
		error("timer slot used");

	_timerinterval = timer;
	_timercallback = callback;
	GpTimerSet(timerno);
}

// Mutex handling - DJWillis Hack
OSystem::MutexRef OSystem_GP32::createMutex(void) {
	return NULL;
}

void OSystem_GP32::lockMutex(MutexRef) {
}

void OSystem_GP32::unlockMutex(MutexRef) {
}

void OSystem_GP32::deleteMutex(MutexRef) {
}

// Quit
void OSystem_GP32::quit() {
	printf("Quitting...");
	exit(0);
}

// Overlay
void OSystem_GP32::showOverlay() {
	// hide the mouse
	undraw_mouse();

	u8 *s = (u8 *) _screen->pixels;
	u16 *d = (u16 *) _tmpscreen->pixels;
	u8 c;
	// convert to 16 bit
	for (int y = 0; y < _screenHeight; y++) {
		for (int x = 0; x < 320; x++) {
			c = *s;
			*d++ =
			    (u16) gpRGB16(_currentPalette[c].r,
			    _currentPalette[c].g, _currentPalette[c].b);
			s++;
		}
		d += 3;		// tmpscreen width is screen+3
	}
	GpGraphicModeSet(16, NULL);	//ph0x
	// Test code.
	//???????????
	//GpRectFill(NULL,&LCDbuffer[GAME_SURFACE], 0, 0, 320, 240, 0); //black border
	_overlayVisible = true;
	clearOverlay();
}

void OSystem_GP32::hideOverlay() {
	// hide the mouse
	undraw_mouse();

	GpGraphicModeSet(8, NULL);	//ph0x
	GpRectFill(NULL, &LCDbuffer[GAME_SURFACE], 0, 0, 320, 240, 0);	//black border

	_overlayVisible = false;
	_forceFull = true;
}

void OSystem_GP32::clearOverlay() {
	if (!_overlayVisible)
		return;

	// hide the mouse
	undraw_mouse();

	// Clear the overlay by making the game screen "look through" everywhere.
	gpRect src, dst;
	src.x = src.y = 0;
	dst.x = dst.y = 1;
	src.w = dst.w = _screenWidth;
	src.h = dst.h = _screenHeight;
	if (gpBlitSurface(_screen, &src, _tmpscreen, &dst) != 0)	//FIXME
		error("gpBlitSurface failed: %s", gpGetError());
	_forceFull = true;
}

void OSystem_GP32::grabOverlay(int16 *buf, int pitch) {
	if (!_overlayVisible)
		return;

	if (_tmpscreen == NULL)
		return;

	// hide the mouse
	undraw_mouse();

	///if (gpLockSurface(_tmpscreen) == -1)
	///     error("gpLockSurface failed: %s.\n", gpGetError());

	int16 *src = (int16 *) _tmpscreen->pixels + TMP_SCREEN_WIDTH + 1;
	int h = _screenHeight;
	do {
		memcpy(buf, src, _screenWidth * 2);
		src += TMP_SCREEN_WIDTH;
		buf += pitch;
	} while (--h);

	///gpUnlockSurface(_tmpscreen);
}

void OSystem_GP32::copyRectToOverlay(const int16 * buf, int pitch, int x,
    int y, int w, int h) {
	if (!_overlayVisible)
		return;

	if (_tmpscreen == NULL)
		return;

	// Clip the coordinates
	if (x < 0) {
		w += x;
		buf -= x;
		x = 0;
	}
	if (y < 0) {
		h += y;
		buf -= y * pitch;
		y = 0;
	}
	if (w > _screenWidth - x) {
		w = _screenWidth - x;
	}
	if (h > _screenHeight - y) {
		h = _screenHeight - y;
	}
	if (w <= 0 || h <= 0)
		return;

	// Mark the modified region as dirty
	cksum_valid = false;
	add_dirty_rect(x, y, w, h);

	/* FIXME: undraw mouse only if the draw rect intersects with the mouse rect */
	undraw_mouse();

	///if (gpLockSurface(_tmpscreen) == -1)
	///     error("gpLockSurface failed: %s.\n", gpGetError());

	int16 *dst =
	    (int16 *) _tmpscreen->pixels + (y + 1) * TMP_SCREEN_WIDTH + (x +
	    1);
	do {
		memcpy(dst, buf, w * 2);
		dst += TMP_SCREEN_WIDTH;
		buf += pitch;
	} while (--h);

	///gpUnlockSurface(_tmpscreen);
}

void OSystem_GP32::internUpdateScreen() {
	assert(_hwscreen != NULL);

	// If the shake position changed, fill the dirty area with blackness

	if (_currentShakePos != _newShakePos) {

		gpRect blackrect =
		    { 0, 0, _screenWidth * _scaleFactor,
			    _newShakePos * _scaleFactor };

		//if (_adjustAspectRatio)
		//      blackrect.h = real2Aspect(blackrect.h - 1) + 1;

		gpFillRect(_hwscreen, &blackrect, 0);

		_currentShakePos = _newShakePos;

		_forceFull = true;
	}
	// Make sure the mouse is drawn, if it should be drawn.
	draw_mouse();

	// Check whether the palette was changed in the meantime and update the
	// screen surface accordingly.
	if (_paletteDirtyEnd != 0) {

		gpSetColors(_screen, _currentPalette + _paletteDirtyStart,
		    _paletteDirtyStart, _paletteDirtyEnd - _paletteDirtyStart);

		_paletteDirtyEnd = 0;

		_forceFull = true;
	}
//#ifdef USE_OSD
//      // OSD visible (i.e. non-transparent)?
//      if (_osdAlpha != gpALPHA_TRANSPARENT) {
//              // Updated alpha value
//              const int diff = gpGetTicks() - _osdFadeStartTime;
//              if (diff > 0) {
//                      if (diff >= kOSDFadeOutDuration) {
//                              // Back to full transparency
//                              _osdAlpha = gpALPHA_TRANSPARENT;
//                      } else {
//                              // Do a linear fade out...
//                              const int startAlpha = gpALPHA_TRANSPARENT + kOSDInitialAlpha * (gpALPHA_OPAQUE - gpALPHA_TRANSPARENT) / 100;
//                              _osdAlpha = startAlpha + diff * (gpALPHA_TRANSPARENT - startAlpha) / kOSDFadeOutDuration;
//                      }
//                      gpSetAlpha(_osdSurface, gpRLEACCEL | gpSRCCOLORKEY | gpSRCALPHA, _osdAlpha);
//                      _forceFull = true;
//              }
//      }
//#endif

	// Force a full redraw if requested
	if (_forceFull) {
		_num_dirty_rects = 1;

		_dirty_rect_list[0].x = 0;
		_dirty_rect_list[0].y = 0;
		_dirty_rect_list[0].w = _screenWidth;
		_dirty_rect_list[0].h = _screenHeight;
	}
	// Only draw anything if necessary
	if (_num_dirty_rects > 0) {

		gpRect *r;
		gpRect dst;
		uint32 srcPitch, dstPitch;
		gpRect *last_rect = _dirty_rect_list + _num_dirty_rects;

		if (_scaler_proc == Normal1x && !_adjustAspectRatio) {

			gpSurface *target =
			    _overlayVisible ? _tmpscreen : _screen;
			for (r = _dirty_rect_list; r != last_rect; ++r) {
				dst = *r;

				if (_overlayVisible) {
					// FIXME: I don't understand why this is necessary...
					dst.x--;
					dst.y--;
				}
				dst.y += _currentShakePos;
				if (gpBlitSurface(target, r, _hwscreen,
					&dst) != 0)
					error("gpBlitSurface failed: %s",
					    gpGetError());
			}
		} else {
			if (!_overlayVisible) {

				for (r = _dirty_rect_list; r != last_rect; ++r) {
					dst = *r;
					dst.x++;	// Shift rect by one since 2xSai needs to acces the data around
					dst.y++;	// any pixel to scale it, and we want to avoid mem access crashes.
					if (gpBlitSurface(_screen, r,
						_tmpscreen, &dst) != 0)
						error
						    ("gpBlitSurface failed: %s",
						    gpGetError());
				}
			}
			//gpLockSurface(_tmpscreen);
			//gpLockSurface(_hwscreen);

			srcPitch = _tmpscreen->pitch;
			dstPitch = _hwscreen->pitch;

			for (r = _dirty_rect_list; r != last_rect; ++r) {

				register int dst_y = r->y + _currentShakePos;
				register int dst_h = 0;
				register int orig_dst_y = 0;

				if (dst_y < _screenHeight) {
					dst_h = r->h;
					if (dst_h > _screenHeight - dst_y)
						dst_h = _screenHeight - dst_y;

					dst_y *= _scaleFactor;

					if (_adjustAspectRatio) {
						orig_dst_y = dst_y;
						dst_y = real2Aspect(dst_y);

					}

					_scaler_proc((byte *) _tmpscreen->
					    pixels + (r->x * 2 + 2) + (r->y +
						1) * srcPitch, srcPitch,
					    (byte *) _hwscreen->pixels +
					    r->x * 2 * _scaleFactor +
					    dst_y * dstPitch, dstPitch, r->w,
					    dst_h);
				}

				r->x *= _scaleFactor;
				r->y = dst_y;
				r->w *= _scaleFactor;
				r->h = dst_h * _scaleFactor;

				if (_adjustAspectRatio
				    && orig_dst_y / _scaleFactor <
				    _screenHeight)
					r->h =
					    stretch200To240((uint8 *)
					    _hwscreen->pixels, dstPitch, r->w,
					    r->h, r->x, r->y, orig_dst_y);
			}
			//gpUnlockSurface(_tmpscreen);
			//gpUnlockSurface(_hwscreen);

		}

		// Readjust the dirty rect list in case we are doing a full update.
		// This is necessary if shaking is active.
		if (_forceFull) {
			_dirty_rect_list[0].y = 0;
			_dirty_rect_list[0].h = 240;

		}
#ifdef USE_OSD
		if (_osdAlpha != gpALPHA_TRANSPARENT) {
			gpBlitSurface(_osdSurface, 0, _hwscreen, 0);
		}
#endif

		// Finally, blit all our changes to the screen
		gpUpdateRects(_hwscreen, _num_dirty_rects, _dirty_rect_list);

	}

	_num_dirty_rects = 0;
	_forceFull = false;
}

//      assert(_hwscreen != NULL);
//
//      // If the shake position changed, fill the dirty area with blackness
//      if (_currentShakePos != _newShakePos) {
//              gpRect blackrect = {0, 0, _screenWidth*_scaleFactor, _newShakePos*_scaleFactor};
//              gpFillRect(_hwscreen, &blackrect, 0);
//
//              _currentShakePos = _newShakePos;
//
//              _forceFull = true;
//      }
//
//      // Make sure the mouse is drawn, if it should be drawn.
//      draw_mouse(); //ph0x
//
//      // Check whether the palette was changed in the meantime and update the
//      // screen surface accordingly.
//      if (_paletteDirtyEnd != 0) {
//              gpSetColors(_screen, _currentPalette + _paletteDirtyStart,
//                      _paletteDirtyStart,
//                      _paletteDirtyEnd - _paletteDirtyStart);
//
//              _paletteDirtyEnd = 0;
//
//              _forceFull = true;
//      }
//
//      // Force a full redraw if requested
//      if (_forceFull) {
//              _num_dirty_rects = 1;
//
//              _dirty_rect_list[0].x = 0;
//              _dirty_rect_list[0].y = 0;
//              _dirty_rect_list[0].w = _screenWidth;
//              _dirty_rect_list[0].h = _screenHeight;
//      }
//
//      // Only draw anything if necessary
//      if (_num_dirty_rects > 0) {
//
//              gpRect *r;
//              uint32 srcPitch, dstPitch;
//              gpRect *last_rect = _dirty_rect_list + _num_dirty_rects;
//
//              // Convert appropriate parts of the 8bpp image into 16bpp
//              if (!_overlayVisible) {
//                      gpRect dst;
//                      for(r = _dirty_rect_list; r != last_rect; ++r) {
//                              dst = *r;
//                              dst.x++;        // FIXME? Shift rect by one since 2xSai needs to acces the data around
//                              dst.y++;        // FIXME? any pixel to scale it, and we want to avoid mem access crashes.
//
//                              if (gpBlitSurface(_screen, r, _hwscreen, &dst) != 0) //ph0x! gp_tmpscreen
//                                      error("gpBlitSurface failed: %s", gpGetError());
//                      }
//              }
//
//              ///gp_LockSurface(gp_tmpscreen);
//              ///gp_LockSurface(gp_hwscreen);
//
//// ph0x! (no scaling) cannot skip intro if commented?
//
//              srcPitch = _tmpscreen->pitch;
//              dstPitch = _hwscreen->pitch;
//              for(r = _dirty_rect_list; r != last_rect; ++r) {
//                      register int dst_y = r->y + _currentShakePos;
//                      register int dst_h = 0;
//                      if (dst_y < _screenHeight) {
//                              dst_h = r->h;
//                              if (dst_h > _screenHeight - dst_y)
//                                      dst_h = _screenHeight - dst_y;
//
//                              dst_y *= _scaleFactor;
//
//                              if (_overlayVisible) //ph0x fixme?
//                              _scaler_proc((byte*)_tmpscreen->pixels + (r->x*2+2) + (r->y+1)*srcPitch, srcPitch,
//                                      (byte*)_hwscreen->pixels + r->x*2*_scaleFactor + dst_y*dstPitch, dstPitch, r->w, dst_h);
//                      }
//                      r->x *= _scaleFactor;
//                      r->y = dst_y;
//                      r->w *= _scaleFactor;
//                      r->h = dst_h * _scaleFactor;
//              }
//
//              ///gp_UnlockSurface(gp_tmpscreen);
//              ///gp_UnlockSurface(gp_hwscreen);
//
//              // Readjust the dirty rect list in case we are doing a full update.
//              // This is necessary if shaking is active.
//              if (_forceFull) {
//                      _dirty_rect_list[0].y = 0;
//                      _dirty_rect_list[0].h = _screenHeight * _scaleFactor;
//              }
//
//              // Finally, blit all our changes to the screen
//
//              // FIXME (dont use condition)
//              if (_overlayVisible)
//                      gpUpdateRects(_hwscreen, _num_dirty_rects, _dirty_rect_list); //ph0x! gp_hwscreen
//              else
//                      gpUpdateRects(_screen, _num_dirty_rects, _dirty_rect_list);
//      }
//
//      _num_dirty_rects = 0;
//      _forceFull = false;
//}

void OSystem_GP32::setFeatureState(Feature f, bool enable) {

	switch (f) {
	case kFeatureFullscreenMode:
		setFullscreenMode(enable);
		break;
	case kFeatureAspectRatioCorrection:
		if (_screenHeight == 200 && _adjustAspectRatio != enable) {
			Common::StackLock lock(_graphicsMutex);

			//assert(_hwscreen != 0);
			_adjustAspectRatio ^= true;
			hotswap_gfx_mode();

#ifdef USE_OSD
			char buffer[128];
			if (_adjustAspectRatio)
				sprintf(buffer,
				    "Enabled aspect ratio correction\n%d x %d -> %d x %d",
				    _screenWidth, _screenHeight, _hwscreen->w,
				    _hwscreen->h);
			else
				sprintf(buffer,
				    "Disabled aspect ratio correction\n%d x %d -> %d x %d",
				    _screenWidth, _screenHeight, _hwscreen->w,
				    _hwscreen->h);
			displayMessageOnOSD(buffer);
#endif

			// Blit everything to the screen
			internUpdateScreen();

			// Make sure that an EVENT_SCREEN_CHANGED gets sent later
			_modeChanged = true;
		}
		break;
	case kFeatureAutoComputeDirtyRects:
		if (enable)
			_mode_flags |= DF_WANT_RECT_OPTIM;
		else
			_mode_flags &= ~DF_WANT_RECT_OPTIM;
		break;
	default:
		break;
	}
}

bool OSystem_GP32::hasFeature(Feature f) {
	return false;
	(f == kFeatureFullscreenMode) || (f == kFeatureAspectRatioCorrection);
	// ||
//              (f == kFeatureAutoComputeDirtyRects);
}

bool OSystem_GP32::getFeatureState(Feature f) {

	switch (f) {
	case kFeatureFullscreenMode:
		return _full_screen;
	case kFeatureAspectRatioCorrection:
		return _adjustAspectRatio;
	case kFeatureAutoComputeDirtyRects:
		return _mode_flags & DF_WANT_RECT_OPTIM;
	default:
		return false;
	}
}

void OSystem_GP32::setFullscreenMode(bool enable) {
	Common::StackLock lock(_graphicsMutex);

	if (_full_screen != enable) {
		assert(_hwscreen != 0);
		_full_screen ^= true;
		undraw_mouse();

		//if (!gpWM_ToggleFullScreen(_hwscreen)) {
		// if ToggleFullScreen fails, achieve the same effect with hotswap gfx mode
		//      hotswap_gfx_mode();
		//}

#ifdef USE_OSD
		if (_full_screen)
			displayMessageOnOSD("Fullscreen mode");
		else
			displayMessageOnOSD("Windowed mode");
#endif

		// Blit everything to the screen
		internUpdateScreen();

		// Make sure that an EVENT_SCREEN_CHANGED gets sent later
		_modeChanged = true;
	}
}

static const OSystem::GraphicsMode supportedGraphicsModes[] = {
	{"1x", "320x240 16bpp", GFX_NORMAL},
	{0, 0, 0}
};

const OSystem::GraphicsMode *OSystem_GP32::getSupportedGraphicsModes() const {
	return supportedGraphicsModes;
}

int OSystem_GP32::getDefaultGraphicsMode() const {
	return GFX_NORMAL;
}

bool OSystem_GP32::setGraphicsMode(int mode) {

	Common::StackLock lock(_graphicsMutex);

	int newScaleFactor = 1;
	ScalerProc *newScalerProc;

	switch (mode) {
	case GFX_NORMAL:
		newScaleFactor = 1;
		newScalerProc = Normal1x;
		break;
		//case GFX_DOUBLESIZE:
		//      newScaleFactor = 2;
		//      newScalerProc = Normal2x;
		//      break;
		//case GFX_TRIPLESIZE:
		//      newScaleFactor = 3;
		//      newScalerProc = Normal3x;
		//      break;
		//case GFX_2XSAI:
		//      newScaleFactor = 2;
		//      newScalerProc = _2xSaI;
		//      break;
		//case GFX_SUPER2XSAI:
		//      newScaleFactor = 2;
		//      newScalerProc = Super2xSaI;
		//      break;
		//case GFX_SUPEREAGLE:
		//      newScaleFactor = 2;
		//      newScalerProc = SuperEagle;
		//      break;
		//case GFX_ADVMAME2X:
		//      newScaleFactor = 2;
		//      newScalerProc = AdvMame2x;
		//      break;
		//case GFX_ADVMAME3X:
		//      newScaleFactor = 3;
		//      newScalerProc = AdvMame3x;
		//      break;
		//case GFX_HQ2X:
		//      newScaleFactor = 2;
		//      newScalerProc = HQ2x;
		//      break;
		//case GFX_HQ3X:
		//      newScaleFactor = 3;
		//      newScalerProc = HQ3x;
		//      break;
		//case GFX_TV2X:
		//      newScaleFactor = 2;
		//      newScalerProc = TV2x;
		//      break;
		//case GFX_DOTMATRIX:
		//      newScaleFactor = 2;
		//      newScalerProc = DotMatrix;
		//      break;

	default:
		warning("unknown gfx mode %d", mode);
		return false;
	}

	_mode = mode;
	_scaler_proc = newScalerProc;
	if (newScaleFactor != _scaleFactor) {
		_scaleFactor = newScaleFactor;
		hotswap_gfx_mode();
	}

	if (!_screen)
		return true;

#ifdef USE_OSD
	if (_osdSurface) {
		const char *newScalerName = 0;
		const GraphicsMode *g = s_supportedGraphicsModes;
		while (g->name) {
			if (g->id == mode) {
				newScalerName = g->description;
				break;
			}
			g++;
		}
		if (newScalerName) {
			char buffer[128];
			sprintf(buffer,
			    "Active graphics filter: %s\n%d x %d -> %d x %d",
			    newScalerName, _screenWidth, _screenHeight,
			    _hwscreen->w, _hwscreen->h);
			displayMessageOnOSD(buffer);
		}
	}
#endif

	// Blit everything to the screen
	_forceFull = true;
	internUpdateScreen();

	// Make sure that an EVENT_SCREEN_CHANGED gets sent later
	_modeChanged = true;

	return true;
}

int OSystem_GP32::getGraphicsMode() const {
	return _mode;
}

////OSystem *OSystem_GP32::create(int gfx_mode, bool full_screen)
//OSystem *OSystem_GP32::create()
//{
//      //OSystem_GP32 *syst = new OSystem_GP32();
//
//      //syst->_mode = gfx_mode;
//      //syst->_full_screen = full_screen;
//      //
//      //// allocate palette storage
//      //syst->_currentPalette = (gpColor*)calloc(sizeof(gpColor), 256);
//
//      //// allocate the dirty rect storage
//      //syst->_mouseBackup = (byte*)malloc(MAX_MOUSE_W * MAX_MOUSE_H * MAX_SCALING * 2);
//      /*return syst;*/
//      return new OSystem_GP32();
//}

//////////////////////////////////////////////////
// GP32 stuff
//////////////////////////////////////////////////

extern "C" int write(int fd, void *p, size_t n);
int write(int fd, void *p, size_t n) {	//ph0x hack!
	return 0;
}

// Converts 8bit rgb values to a GP32 palette value
void GpSetPaletteEntry(u8 i, u8 r, u8 g, u8 b) {
	GP_PALETTEENTRY entry = gpRGB16(r, g, b);
	GpPaletteEntryChange(i, 1, &entry, 0);
}

int gpprintf(const char *fmt, ...) {
	static bool busy;
	static int y;
	char s[1024];		// ?
	va_list marker;

	if (busy)
		return 0;
	busy = true;
	va_start(marker, fmt);
	vsnprintf(s, 1024, fmt, marker);
	va_end(marker);

#ifdef GPDEBUG
	//dprintf("mem: %d  ", gm_availablesize());
	dprintf(s);
	if (s[strlen(s) - 1] != '\n')
		dprintf("\n");
	//if (s[0]!='>') return r;
#endif

	fprintf(stdout, s);

	// print to lcd
	GpTextOut(NULL, &LCDbuffer[DEBUG_SURFACE], 0, y, s, 1);
	y += (ENGFONT_H - FONT_LINEGAP);
	if (y >
	    (240 / (ENGFONT_H - FONT_LINEGAP)) * (ENGFONT_H - FONT_LINEGAP)) {
		y = 0;
		GpRectFill(NULL, &LCDbuffer[DEBUG_SURFACE], 0, 0, 320, 240, 2);
	}
	busy = false;
	return 0;
}

int gpfprintf(FILE *stream, const char *fmt, ...) {
	char s[256];
	va_list marker;

	va_start(marker, fmt);
	vsnprintf(s, 256, fmt, marker);
	va_end(marker);

	return fwrite(s, 1, strlen(s), stream);
}

typedef struct {
	FILE f;
	ulong size;
	ulong p;		//cache position
} xfile;

#define XFILE(f) (*(xfile*)f)
#define FCACHE_SIZE 8*1024	// speed up writes

FILE *gpfopen(const char *filename, const char *mode) {
	//FIXME: allocation, mode, malloc -> new
	ulong m;
	FILE *f;
	ERR_CODE err;
	char s[256];

	if (!strchr(filename, '.')) {
		sprintf(s, "%s.", filename);
		filename = s;
	}
	//printf(">open %s as %s", filename, mode);

	// FIXME add binary/text support
	if (tolower(mode[0]) == 'r') {
		f = (FILE *) malloc(sizeof(xfile));
		m = OPEN_R;
		GpFileGetSize(filename, &XFILE(f).size);
		err = GpFileOpen(filename, m, f);
	} else if (tolower(mode[0]) == 'w') {
		//printf("open if as W");
		f = (FILE *) malloc(sizeof(xfile) + FCACHE_SIZE);
		XFILE(f).size = 0;	// FIXME? new file has no size?
		XFILE(f).p = 0;
		m = OPEN_W;
		err = GpFileCreate(filename, ALWAYS_CREATE, f);
	} else
		error("wrong file mode");

	if (!f)
		error("%s: cannot crate F_HANDLE", __FUNCTION__);
	if (err) {
		//printf("IOerr %d", err);
		return NULL;
	} else
		return f;
}

int gpfclose(FILE *f) {
	if (!f) {
		//warning("closing null file");
		return 1;
	}

	if (*(u32 *)((char *)f - sizeof(u32)) == 0x4321) {
		debug(0, "Double closing", __FUNCTION__);
		return 1;
	}			// return 1 ??

	if (XFILE(f).p) {
		GpFileWrite(*f, (char *)f + sizeof(xfile), XFILE(f).p);	// flush cache
		XFILE(f).p = 0;
	}

	ERR_CODE err = GpFileClose(*f);
	free(f);

	return err;
}

int gpfseek(FILE *stream, long offset, int whence) {
	ulong dummy;

	switch (whence) {
	case SEEK_SET:
		whence = FROM_BEGIN;
		break;
	case SEEK_CUR:
		whence = FROM_CURRENT;
		break;
	case SEEK_END:
		whence = FROM_END;
		break;
	}
	return GpFileSeek(*stream, whence, offset, (long *)&dummy);
}

long gpftell(FILE *stream)	{ // fixme? use standard func
	ulong pos = 0;
	//ERR_CODE err = GpFileSeek(*stream, FROM_CURRENT, 0, (long*)&pos);
	return pos;
}

size_t gpfread(void *ptr, size_t size, size_t n, FILE *stream) {
	ulong readcount = 0;
	//ERR_CODE err = GpFileRead(*stream, ptr, size*n, &readcount); //fixme? size*n
	return readcount / size;	//FIXME?
}

size_t gpfwrite(const void *ptr, size_t size, size_t n, FILE *f) {
	int len = size * n;

	if (!f) {
		//warning("writing to null file");
		return 0;
	}

	if (XFILE(f).p + len < FCACHE_SIZE) {
		memcpy((char *)f + sizeof(xfile) + XFILE(f).p, ptr, len);
		XFILE(f).p += len;
	} else {
		if (XFILE(f).p) {
			GpFileWrite(*f, (char *)f + sizeof(xfile), XFILE(f).p);	// flush cache
			XFILE(f).p = 0;
		}

		ERR_CODE err = GpFileWrite(*f, ptr, len);
		if (!err)
			return n;
		else
			return -err;
	}
	return 0;
}

void gpclearerr(FILE *stream) {
	//warning("fixme: %s", __FUNCTION__);
}

int gpfeof(FILE *f)	{	//fixme!
	return ftell(f) >= XFILE(f).size;
}

char *gpfgets(char *s, int n, FILE *f) {
	int i = 0;

	while (!feof(f) && i < n) {
		fread(&s[i], 1, 1, f);
		if (s[i] == '\n') {
			s[i + 1] = 0;
			return s;
		}
		i++;
	}
	if (feof(f))
		return NULL;
	else
		return s;
}

char gpfgetc(FILE *f) {
	char c[1];

	fread(&c[0], 1, 1, f);
	return c[0];
}

int gpfflush(FILE * stream) {
	return 0;
}

/*
 * GP32 Memory managment.
 */

void *gpmalloc(size_t size) {
	u32 np;
	u32 *up;

	np = (u32) gm_malloc(size + sizeof(u32));

	if (np) {
		up = (u32 *) np;
		*up = 0x1234;
		return (void *)(np + sizeof(u32));
	}

	return NULL;
}

void *gpcalloc(size_t nitems, size_t size) {
	void *p = gpmalloc(nitems * size);	//gpcalloc doesnt clear?

	memset(p, 0, nitems * size);
	if (*(u8 *) p)
		warning("%s: calloc doesn't clear!", __FUNCTION__);	//fixme: was error
	//printf("callocing");
	return p;
}

void gpfree(void *block) {
	u32 np;
	u32 *up;

	if (!block) {
		debug(0, "freeing null pointer");
		return;
	}

	np = ((u32) block) - sizeof(u32);
	up = (u32 *) np;
	if (*up == 0x4321)
		error("%s: double deallocation!", __FUNCTION__);

	if (*up != 0x1234)
		error("%s: corrupt block!", __FUNCTION__);
	*up = 0x4321;

	gm_free(up);
}

/*
char *gpstrdup(const char *s) {
	char *p=(char*)malloc(strlen(s)+1);
	memcpy(p, s, strlen(s)+1);
	return p;
}
*/

char *gpstrdup(const char *strSource) {
	char *strBuffer;
	strBuffer = (char *)malloc(strlen(strSource) + 1);
	if (strBuffer)
		strcpy(strBuffer, strSource);
	return strBuffer;
}

time_t gptime(time_t *timer) {
	time_t t = GpTickCountGet() / 1000;
	if (timer)
		*timer = t;
	return t;
}

void gpdeinit() {
	fclose(fstdin);
	fclose(fstdout);
	fclose(fstderr);
}

void gpexit(int code) {
	switchsurf(DEBUG_SURFACE);

	if (!code) {
		printf("----------------------------------------");
		printf("     Your GP32 is now restarting...     ");
		printf("----------------------------------------");

		gpdeinit();

		// FIXME: use function :)
		int n = GpTickCountGet();
		while ((GpTickCountGet() - n) < 3000);

		GpAppExit();
	} else {
		printf("Exit Code %d", code);
		while (1);
	}
}

/****************************************************************
    Setup CPU Speed - Calls to CPUSPEED.S
****************************************************************/
void gpCPUSpeed(int freq) {
	// To extend use: cpu_speed(CLK_SPEED, DIV_FACTOR, CLK_MODE);
	if (freq == 166)
		cpu_speed(165000000, 0x2f001, 3);	// 40 Bus?
	if (freq == 156)
		cpu_speed(156000000, 0x2c001, 3);	// 36 Bus
	if (freq == 133)
		cpu_speed(133500000, (81 << 12) | (2 << 4) | 1, 2);	// 66 Bus?
	if (freq == 132)
		cpu_speed(132000000, 0x3a011, 3);	// 33 Bus
	if (freq == 120)
		cpu_speed(120000000, 0x24001, 2);
	if (freq == 100)
		cpu_speed(102000000, (43 << 12) | (1 << 4) | 1, 2);
	if (freq == 66)
		cpu_speed(67500000, (37 << 12) | (0 << 4) | 2, 2);
	if (freq == 40)
		cpu_speed(40000000, 0x48013, 1);	// Default
	if (freq == 33)
		cpu_speed(33750000, (37 << 12) | (0 << 4) | 3, 2);	// Ultra slow
}

/****************************************************************
    Pre-ScummVM Configuration Menu.
****************************************************************/
int ConfigMenu() {
	//#ifndef GP32_GDB
	//      gpCPUSpeed(40);
	//#endif /*GP32_GDB*/

	GpSetPaletteEntry(2, 0, 0, 0);
	GpSetPaletteEntry(1, 0, 0, 0);
	GpSetPaletteEntry(0, 255, 255, 255);

	int i, key, fg, bg, choice = 0, y = ENGFONT_H * 7;
	int n = ARRAYSIZE(menu);

	GpTextOut(NULL, &LCDbuffer[(int)currentsurf], 0, y,
	    "Configuration Menu", 1);
	y += ENGFONT_H;
	GpTextOut(NULL, &LCDbuffer[(int)currentsurf], 0, y,
	    "----------------------------------------", 1);
	y += ENGFONT_H;

	do {
		for (i = 0; i < n; i++) {
			if (i == choice) {
				fg = 2;
				bg = 1;
			} else {
				fg = 1;
				bg = 2;
			}
			GpRectFill(NULL, &LCDbuffer[(int)currentsurf], 0,
			    y + i * ENGFONT_H, 320, ENGFONT_H, bg);
			char s[256];
			sprintf(s, "%s [%s]", menu[i].option,
			    menu[i].submenu[menu[i].index]);
			GpTextOut(NULL, &LCDbuffer[(int)currentsurf], 0,
			    y + i * ENGFONT_H, s, fg);
		}

		do
			key = gpTrapKey();
		while (key == GPC_VK_NONE);

		if (key & GPC_VK_DOWN) {
			if (choice < n - 1)
				choice++;
		}
		if (key & GPC_VK_UP) {
			if (choice > 0)
				choice--;
		}
		if (key & GPC_VK_LEFT) {
			if (menu[choice].index > 0)
				menu[choice].index--;
		}
		if (key & GPC_VK_RIGHT) {
			if (menu[choice].submenu[menu[choice].index + 1])
				menu[choice].index++;
		}
		//if (key & GPC_VK_START || key  & GPC_VK_FA) return 1;
		if (key & GPC_VK_FA)
			return 1;
		//if (key & GPC_VK_SELECT) return 0;

		do
			key = gpTrapKey();
		while (key != GPC_VK_NONE);

	} while (1);
}

/****************************************************************
    Delay (very simple delay)
****************************************************************/
void Delay(unsigned int ms) {
	unsigned int delay_by;
	delay_by = GpTickCountGet();
	while (GpTickCountGet() - delay_by < ms);
}

/****************************************************************
    Triple buffering code
****************************************************************/
void FlipScreen() {
	if (nflip == 0) {
		GpSurfaceFlip(&LCDbuffer[0]);
		nflip = 1;
	} else if (nflip == 1) {
		GpSurfaceFlip(&LCDbuffer[1]);
		nflip = 2;
	} else if (nflip == 2) {
		GpSurfaceFlip(&LCDbuffer[2]);
		nflip = 0;
	}
}

/****************************************************************
    Clear all the screen buffers
****************************************************************/
void ClearScreen() {
	int i;
	for (i = 0; i <= BUFFERCOUNT; i++) {
		GpRectFill(NULL, &LCDbuffer[i], 0, 0, LCDbuffer[i].buf_w,
		    LCDbuffer[i].buf_h, 0x00);
	}
}

/****************************************************************
    Fade to black
****************************************************************/
void FadeToBlack(int delay_time) {
	//Fade to black
	int x;
	for (x = 0; x < 30; x++) {
		//Fade it further
		GpLcdFade(-1, NULL);

		//refresh screen
		GpSurfaceFlip(&LCDbuffer[nflip]);

		//wait a littel bit
		Delay(delay_time);
	}

	//Now Clear all the buffers
	ClearScreen();

	//Turn the fading off
	GpLcdNoFade(NULL);

	//Now flip to end it all and leave it black
	FlipScreen();
}

/****************************************************************
    Fade to White
****************************************************************/
void FadeToWhite(int delay_time) {
	//Fade to black
	int x;
	for (x = 0; x < 30; x++) {
		//Fade it further
		GpLcdFade(1, NULL);

		//refresh screen
		GpSurfaceFlip(&LCDbuffer[nflip]);

		//wait a littel bit
		Delay(delay_time);
	}

	//Now Clear all the buffers
	ClearScreen();

	//Turn the fading off
	GpLcdNoFade(NULL);

	//Now flip to end it all and leave it black
	FlipScreen();
}

/****************************************************************
    Initialise the File System
****************************************************************/
void InitFileSystem() {
	//Initialises GP32 file system
	GpFatInit();
	GpRelativePathSet("gp:\\gpmm");

	// Create folders on SMC if there not there
	// For storing games, saves, config and scummvm.ini.
	GpDirCreate("gp:\\data", NOT_IF_EXIST);
	GpDirCreate("gp:\\data\\scummvm", NOT_IF_EXIST);
	GpDirCreate("gp:\\data\\scummvm\\games", NOT_IF_EXIST);
	GpDirCreate("gp:\\data\\scummvm\\config", NOT_IF_EXIST);
}

/****************************************************************
    Splash Screen - show splash screen
****************************************************************/

void InitSplashPal() {
	static GP_HPALETTE h_splash_pal = NULL;
	if (h_splash_pal)
		GpPaletteDelete(h_splash_pal);
	h_splash_pal = GpPaletteCreate(gfx_splash_palnb, gfx_splash_Pal);
	GpPaletteDelete(GpPaletteSelect(h_splash_pal));
	GpPaletteRealize();
}

int SplashScreen() {
	int key;

	ClearScreen();
	InitSplashPal();

	// Show the screen (load into all buffers - ready for screen transition)
	GpBitBlt(NULL, &LCDbuffer[0], 0, 0, gfx_splash_width,
	    gfx_splash_height, (unsigned char *)gfx_splash, 0, 0,
	    gfx_splash_width, gfx_splash_height);
	GpBitBlt(NULL, &LCDbuffer[1], 0, 0, gfx_splash_width,
	    gfx_splash_height, (unsigned char *)gfx_splash, 0, 0,
	    gfx_splash_width, gfx_splash_height);
	GpBitBlt(NULL, &LCDbuffer[2], 0, 0, gfx_splash_width,
	    gfx_splash_height, (unsigned char *)gfx_splash, 0, 0,
	    gfx_splash_width, gfx_splash_height);

	// Refresh screen (show the logo)
	FlipScreen();

	// Initialise the File System
	// Done during SpashScreen to hide folder create (if needed) from users.
	InitFileSystem();

	//TODO: Put branchs for Start and Select and act accordingly.

	do {
		do
			key = gpTrapKey();
		while (key == GPC_VK_NONE);
		if (key & GPC_VK_START) {
			FadeToBlack(20);
			return 0;
		}
		if (key & GPC_VK_SELECT) {
			FadeToWhite(20);
			ConfigMenu();
			return 0;
		}
		do
			key = gpTrapKey();
		while (key != GPC_VK_NONE);
	} while (1);

	//Fade the screen into GP32 setup or ScummVM.
	//FadeToWhite(100);
}

/****************************************************************
    Read and write the GP32 config file to the SMC
****************************************************************/
void ConfigRead() {
	FILE *f;

	f = fopen("gp:\\data\\scummvm\\config\\config.dat", "r");

	if (f) {
		for (unsigned int i = 0; i < ARRAYSIZE(menu); i++)
			fread(&menu[i].index, 1, sizeof(menu[i].index), f);
		fclose(f);
	}
}

void ConfigWrite() {
	FILE *f;

	f = fopen("gp:\\data\\scummvm\\config\\config.dat", "w");

	if (f) {
		for (unsigned int i = 0; i < ARRAYSIZE(menu); i++)
			fwrite(&menu[i].index, 1, sizeof(menu[i].index), f);
		fclose(f);
	}
}

/****************************************************************
    Prepare GP32
****************************************************************/
void InitLCD() {
	// Initialize graphics
	GpGraphicModeSet(COLOUR_8BIT_MODE, NULL);

	// Set the current buffer
	nflip = 0;

	short i;
	for (i = 0; i <= BUFFERCOUNT; i++) {
		GpLcdSurfaceGet(&LCDbuffer[i], i);
	}
}

void Init() {
	// Setup the LCD.
	InitLCD();

	// Load the Splash Screen and give the option of config or ScummVM.
	// also sets up file system.
	SplashScreen();

	//GpSetPaletteEntry ( 2, 0,0,0 );
	//GpSetPaletteEntry ( 1, 0,0,0 );
	//GpSetPaletteEntry ( 0, 255,255,255 );

	//// fixme - use get function
	////currentsurf=DEBUG_SURFACE;
	////GpSurfaceSet(&LCDbuffer[(int)currentsurf]);
	//GpSurfaceSet(&LCDbuffer[nflip]);
	//GpLcdEnable();

	//stderr = fstdout = fopen("gp:\\data\\scummvm\\config\\debug.out", "w");
	//stdin = NULL; //fixme?
	////fstdin = fopen("stdin", "w");
	////fstderr = fopen("stderr", "w");

	//printf("          ScummVM for the GP32");
	//printf("----------------------------------------");
	//printf("PRIVATE BUILD - DO NOT PASS ON!");
	//printf("ScummVM (c) 2001-4 The ScummVM Team");
	//printf("GP32 Backend (c) 2004 by DJWillis");
	//printf("Compiled %s, %s", __DATE__, __TIME__);
	//printf("----------------------------------------");
	//printf("       Press 'A' to Start ScummVM");
	//printf("----------------------------------------");

	///*
	//ERR_CODE err;
	//
	//unsigned long bad;
	//err = GpFormat("gp:", FORMAT_RESCUE, &bad);
	//char s[256];
	//GpRelativePathGet(s);
	//*/
}

//void *gpmemset (void *s, int c, size_t n) {
//      for (int i=n-1; i>=0; i--)
//              ((char*)s)[i]=(char)c;
//}
//
//void *gpmemcpy (void *dest, const void *src, size_t n) {
//      for (int i=n-1; i>=0; i--)
//              ((char*)dest)[i]=((char*)src)[i];
//}

void buildgammatab(int val) {
	float g = 1;
	for (int i = 0; i < 256; i++) {
		gammatab[255 - i] = g;
		g *= scrGamma[val];
	}
}

void buildgammatab2(int val) {
	float g = 1;
	for (int i = 0; i < 256; i++) {
		gammatab2[255 - i] = g;
		g *= scrGamma[val];
	}
}

int stricmp(const char *string1, const char *string2) {
	char src[4096];
	char dest[4096];
	int i;

	for (i = 0; i < strlen(string1); i++)
		if (string1[i] >= 'A' && string1[i] <= 'Z')
			src[i] = string1[i] + 32;
		else
			src[i] = string1[i];
	src[i] = 0;

	for (i = 0; i < strlen(string2); i++)
		if (string2[i] >= 'A' && string2[i] <= 'Z')
			dest[i] = string2[i] + 32;
		else
			dest[i] = string2[i];
	dest[i] = 0;

	return strcmp(src, dest);
}

int strnicmp(const char *string1, const char *string2, int len) {
	char src[4096];
	char dest[4096];
	int i;

	for (i = 0; i < strlen(string1) && i < len; i++)
		if (string1[i] >= 'A' && string1[i] <= 'Z')
			src[i] = string1[i] + 32;
		else
			src[i] = string1[i];
	src[i] = 0;

	for (i = 0; i < strlen(string2) && i < len; i++)
		if (string2[i] >= 'A' && string2[i] <= 'Z')
			dest[i] = string2[i] + 32;
		else
			dest[i] = string2[i];
	dest[i] = 0;

	return strncmp(src, dest, len);
}

extern "C" void GpMain(void *arg);
extern "C" int scummvm_main(int argc, char *argv[]);

void GpMain(void *arg) {

#ifdef GP32_GDB
	OpenUSB();
	InstallISR();
#endif				/*GP32_GDB */

	// Wank up the GP32 good and propper ;-)
	//      asm volatile(" \n"
	//" mov         r0, #0x01 \n"
	//" ldr         r1, [r0] \n"

	//" \n"
	//:
	//:
	//:"r0", "r1");

	// FIXME: causes crash?! (if not at first line of gpmain())
	buildgammatab(gindex);
	buildgammatab2(ARRAYSIZE(scrGamma) - 1);

	Init();

	// ConfigRead();

	//if ()
	//{
	//      ConfigWrite();
	//}

	//ConfigMenu();
	//ConfigWrite();

	// fixme - use get function

	//currentsurf=GAME_SURFACE;
	//GpSurfaceFlip(&LCDbuffer[(int)currentsurf]);

#ifndef GP32_GDB
	int CPUSpeed =
	    atoi((const char *)menu[MENU_CPUSPEED].submenu[menu[MENU_CPUSPEED].
		index]);
#endif				/*GP32_GDB */

	//static char *argv[] = { "scummvm", NULL, NULL, NULL };
//      char *argv[] = { "scummvm", (char*)menu[MENU_MUSICDRV].submenu[menu[MENU_MUSICDRV].index]};
//      static int argc = 4;

	// Game Testing...
	//int argc = 2;
	//int argc = 4; char *argv[] = { "scummvm", "-enull", "-pgp:\\gpmm\\scummvm\\sky\\", "sky" };
	int argc = 4;
	char *argv[] = { "scummvm", "-enull", "", "" };

	while (1) {
		// Only set the CPU speed if the GDB Stub is NOT needed.
		// No point calling ClearScreen(); as we want to see any odd stuff.
#ifndef GP32_GDB
		gpCPUSpeed(CPUSpeed);
		ClearScreen();
#endif				/*GP32_GDB */

		//FadeToWhite(200);
		//exit(scummvm_main(argc, argv));
		////////////////cast_argv = f(const_cast<double&>(d));
		//////////////char* argv_;
		//////////////  argv_ = const_cast<*char*>(argv);
		scummvm_main(argc, argv);
	}
}