/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#define RONIN_TIMER_ACCESS

#include "common/scummsys.h"
#include "graphics/blit.h"
#include "graphics/surface.h"
#include "dc.h"

#define SCREEN_W 640
#define SCREEN_H 480
#define MOUSE_W 128
#define MOUSE_H 128

#define OVL_W 320
#define OVL_H 200
#define OVL_TXSTRIDE 512

#define LEFT_OFFSET (_xscale*_current_shake_x_pos)
#define TOP_OFFSET (_top_offset+_yscale*_current_shake_y_pos)

static const struct {
  Graphics::PixelFormat pixelFormat;
  unsigned int textureFormat;
  operator const Graphics::PixelFormat&() const { return pixelFormat; }
} screenFormats[] = {
  /* Note: These are ordered by _increasing_ preference, so that
	 CLUT8 appears at index 0.  getSupportedFormats() will return
	 them in reversed order. */
  { Graphics::PixelFormat::createFormatCLUT8(), TA_TEXTUREMODE_ARGB1555 },
  { Graphics::PixelFormat(2,4,4,4,4,8,4,0,12), TA_TEXTUREMODE_ARGB4444 },
  { Graphics::PixelFormat(2,5,5,5,1,10,5,0,15), TA_TEXTUREMODE_ARGB1555 },
  { Graphics::PixelFormat(2,5,6,5,0,11,5,0,0), TA_TEXTUREMODE_RGB565 },
};
#define NUM_FORMATS (sizeof(screenFormats)/sizeof(screenFormats[0]))


#define QACR0 (*(volatile unsigned int *)(void *)0xff000038)
#define QACR1 (*(volatile unsigned int *)(void *)0xff00003c)


#define COPYPIXEL(n) do {			\
  unsigned short _tmp = pal[*s++];		\
  d[n] = _tmp|(pal[*s++]<<16);			\
} while (0)

static void texture_memcpy64_pal(void *dest, void *src, int cnt, unsigned short *pal)
{
  unsigned char *s = (unsigned char *)src;
  unsigned int *d = (unsigned int *)(void *)
	(0xe0000000 | (((unsigned long)dest) & 0x03ffffc0));
  QACR0 = ((0xa4000000>>26)<<2)&0x1c;
  QACR1 = ((0xa4000000>>26)<<2)&0x1c;
  while (cnt--) {
	COPYPIXEL(0);
	COPYPIXEL(1);
	COPYPIXEL(2);
	COPYPIXEL(3);
	asm("pref @%0" : : "r" (s+4*16));
	COPYPIXEL(4);
	COPYPIXEL(5);
	COPYPIXEL(6);
	COPYPIXEL(7);
	asm("pref @%0" : : "r" (d));
	d += 8;
	COPYPIXEL(0);
	COPYPIXEL(1);
	COPYPIXEL(2);
	COPYPIXEL(3);
	asm("pref @%0" : : "r" (s+4*16));
	COPYPIXEL(4);
	COPYPIXEL(5);
	COPYPIXEL(6);
	COPYPIXEL(7);
	asm("pref @%0" : : "r" (d));
	d += 8;
  }
}

static void texture_memcpy64(void *dest, void *src, int cnt)
{
  unsigned int *s = (unsigned int *)src;
  unsigned int *d = (unsigned int *)(void *)
	(0xe0000000 | (((unsigned long)dest) & 0x03ffffc0));
  QACR0 = ((0xa4000000>>26)<<2)&0x1c;
  QACR1 = ((0xa4000000>>26)<<2)&0x1c;
  while (cnt--) {
	d[0] = *s++;
	d[1] = *s++;
	d[2] = *s++;
	d[3] = *s++;
	asm("pref @%0" : : "r" (s+16));
	d[4] = *s++;
	d[5] = *s++;
	d[6] = *s++;
	d[7] = *s++;
	asm("pref @%0" : : "r" (d));
	d += 8;
	d[0] = *s++;
	d[1] = *s++;
	d[2] = *s++;
	d[3] = *s++;
	asm("pref @%0" : : "r" (s+16));
	d[4] = *s++;
	d[5] = *s++;
	d[6] = *s++;
	d[7] = *s++;
	asm("pref @%0" : : "r" (d));
	d += 8;
  }
}

void commit_dummy_transpoly()
{
  struct polygon_list mypoly;

  mypoly.cmd =
	TA_CMD_POLYGON|TA_CMD_POLYGON_TYPE_TRANSPARENT|TA_CMD_POLYGON_SUBLIST|
	TA_CMD_POLYGON_STRIPLENGTH_2|TA_CMD_POLYGON_PACKED_COLOUR;
  mypoly.mode1 = TA_POLYMODE1_Z_ALWAYS|TA_POLYMODE1_NO_Z_UPDATE;
  mypoly.mode2 =
	TA_POLYMODE2_BLEND_SRC_ALPHA|TA_POLYMODE2_BLEND_DST_INVALPHA|
	TA_POLYMODE2_FOG_DISABLED|TA_POLYMODE2_ENABLE_ALPHA;
  mypoly.texture = 0;
  mypoly.red = mypoly.green = mypoly.blue = mypoly.alpha = 0;
  ta_commit_list(&mypoly);
}


void OSystem_Dreamcast::setPalette(const byte *colors, uint start, uint num)
{
  unsigned short *dst = palette + start;
  if (num>0)
	while ( num-- ) {
	  *dst++ = ((colors[0]<<7)&0x7c00)|
	((colors[1]<<2)&0x03e0)|
	((colors[2]>>3)&0x001f);
	  colors += 3;
	}
  _screen_dirty = true;
}

void OSystem_Dreamcast::setCursorPalette(const byte *colors, uint start, uint num)
{
  unsigned short *dst = cursor_palette + start;
  if (num>0)
	while ( num-- ) {
	  *dst++ = ((colors[0]<<7)&0x7c00)|
	((colors[1]<<2)&0x03e0)|
	((colors[2]>>3)&0x001f);
	  colors += 3;
	}
  _enable_cursor_palette = true;
}

void OSystem_Dreamcast::grabPalette(byte *colors, uint start, uint num) const
{
  const unsigned short *src = palette + start;
  if (num>0)
	while ( num-- ) {
	  unsigned short p = *src++;
	  colors[0] = ((p&0x7c00)>>7)|((p&0x7000)>>12);
	  colors[1] = ((p&0x03e0)>>2)|((p&0x0380)>>7);
	  colors[2] = ((p&0x001f)<<3)|((p&0x001c)>>2);
	  colors += 3;
	}
}

Graphics::PixelFormat OSystem_Dreamcast::getScreenFormat() const
{
  return screenFormats[_screenFormat];
}

Common::List<Graphics::PixelFormat> OSystem_Dreamcast::getSupportedFormats() const
{
  Common::List<Graphics::PixelFormat> list;
  unsigned i;
  for (i=0; i<NUM_FORMATS; i++)
	list.push_front(screenFormats[i]);
  return list;
}

void OSystem_Dreamcast::setScaling()
{
  if (_screen_w > 400) {
	_xscale = _yscale = 1.0;
	_top_offset = (SCREEN_H-_screen_h)>>1;
  } else if (_aspect_stretch && _screen_w == 320 && _screen_h == 200) {
	_xscale = SCREEN_W/320.0;
	_yscale = SCREEN_H/200.0;
	_top_offset = 0;
  } else {
	_xscale = _yscale = 2.0;
	_top_offset = (SCREEN_H>>1)-_screen_h;
  }
}

void OSystem_Dreamcast::initSize(uint w, uint h, const Graphics::PixelFormat *format)
{
  assert(w <= SCREEN_W && h <= SCREEN_H);

  int i = 0;
  if (format != NULL)
	for (i=NUM_FORMATS-1; i>0; --i)
	  if (*format == screenFormats[i])
	break;
  _screenFormat = i;

  _overlay_visible = false;
  _overlay_fade = 0.0;
  _screen_w = w;
  _screen_h = h;
  _overlay_x = (w-OVL_W)/2;
  _overlay_y = (h-OVL_H)/2;
  if (_overlay_x<0) _overlay_x = 0;
  if (_overlay_y<0) _overlay_y = 0;
  setScaling();
  ta_sync();
  if (!screen)
	screen = new unsigned char[SCREEN_W*SCREEN_H*2];
  if (!overlay)
	overlay = new unsigned short[OVL_W*OVL_H];
  for (i=0; i<NUM_BUFFERS; i++)
	if (!screen_tx[i])
	  screen_tx[i] = ta_txalloc(SCREEN_W*SCREEN_H*2);
  for (i=0; i<NUM_BUFFERS; i++)
	if (!mouse_tx[i])
	  mouse_tx[i] = ta_txalloc(MOUSE_W*MOUSE_H*2);
  for (i=0; i<NUM_BUFFERS; i++)
	if (!ovl_tx[i])
	  ovl_tx[i] = ta_txalloc(OVL_TXSTRIDE*OVL_H*2);
  _screen_buffer = 0;
  _mouse_buffer = 0;
  _overlay_buffer = 0;
  _screen_dirty = true;
  _overlay_dirty = true;
  *(volatile unsigned int *)(0xa05f80e4) = SCREEN_W/32; //stride
  //  dc_reset_screen(0, 0);
  memset(screen, 0, SCREEN_W*SCREEN_H*2);
  memset(overlay, 0, OVL_W*OVL_H*sizeof(unsigned short));

  _devpoll = Timer();
}

void OSystem_Dreamcast::copyRectToScreen(const void *buf, int pitch, int x, int y,
					 int w, int h)
{
  if (w<1 || h<1)
	return;
  if (_screenFormat != 0) {
	x<<=1; w<<=1;
  }
  unsigned char *dst = screen + y*SCREEN_W*2 + x;
  const byte *src = (const byte *)buf;
  do {
	memcpy(dst, src, w);
	dst += SCREEN_W*2;
	src += pitch;
  } while (--h);
  _screen_dirty = true;
}

bool OSystem_Dreamcast::showMouse(bool visible)
{
  bool last = _ms_visible;
  _ms_visible = visible;

  return last;
}

void OSystem_Dreamcast::warpMouse(int x, int y)
{
  _ms_cur_x = x;
  _ms_cur_y = y;
}

void OSystem_Dreamcast::setMouseCursor(const void *buf, uint w, uint h,
				       int hotspot_x, int hotspot_y,
				       uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format)
{
  _ms_cur_w = w;
  _ms_cur_h = h;

  _ms_hotspot_x = hotspot_x;
  _ms_hotspot_y = hotspot_y;

  _ms_keycolor = keycolor;

  int i = 0;
  if (format != NULL)
	for (i=NUM_FORMATS-1; i>0; --i)
	  if (*format == screenFormats[i])
	break;
  _mouseFormat = i;

  free(_ms_buf);

  if (_mouseFormat != 0)
	w <<= 1;

  _ms_buf = (byte *)malloc(w * h);
  memcpy(_ms_buf, buf, w * h);
}

void OSystem_Dreamcast::setShakePos(int shake_x_pos, int shake_y_pos)
{
  _current_shake_x_pos = shake_x_pos;
  _current_shake_y_pos = shake_y_pos;
}

void OSystem_Dreamcast::updateScreenTextures(void)
{
  if (_screen_dirty) {

	_screen_buffer++;
	_screen_buffer &= NUM_BUFFERS-1;

	unsigned short *dst = (unsigned short *)screen_tx[_screen_buffer];
	unsigned char *src = screen;

	// while ((*((volatile unsigned int *)(void *)0xa05f810c) & 0x3ff) != 200);
	// *((volatile unsigned int *)(void *)0xa05f8040) = 0xff0000;

	if (_screenFormat == 0)
	  for ( int y = 0; y<_screen_h; y++ )
	  {
	texture_memcpy64_pal( dst, src, _screen_w>>5, palette );
	src += SCREEN_W*2;
	dst += SCREEN_W;
	  }
	else
	  for ( int y = 0; y<_screen_h; y++ )
	  {
	texture_memcpy64( dst, src, _screen_w>>5 );
	src += SCREEN_W*2;
	dst += SCREEN_W;
	  }

	_screen_dirty = false;
  }

  if ( _overlay_visible && _overlay_dirty ) {

	_overlay_buffer++;
	_overlay_buffer &= NUM_BUFFERS-1;

	unsigned short *dst = (unsigned short *)ovl_tx[_overlay_buffer];
	unsigned short *src = overlay;

	for ( int y = 0; y<OVL_H; y++ )
	{
	  texture_memcpy64( dst, src, OVL_W>>5 );
	  src += OVL_W;
	  dst += OVL_TXSTRIDE;
	}

	_overlay_dirty = false;
  }
}

void OSystem_Dreamcast::updateScreenPolygons(void)
{
  struct polygon_list mypoly;
  struct packed_colour_vertex_list myvertex;

  // *((volatile unsigned int *)(void *)0xa05f8040) = 0x00ff00;

  mypoly.cmd =
	TA_CMD_POLYGON|TA_CMD_POLYGON_TYPE_OPAQUE|TA_CMD_POLYGON_SUBLIST|
	TA_CMD_POLYGON_STRIPLENGTH_2|TA_CMD_POLYGON_PACKED_COLOUR|TA_CMD_POLYGON_TEXTURED;
  mypoly.mode1 = TA_POLYMODE1_Z_ALWAYS|TA_POLYMODE1_NO_Z_UPDATE;
  mypoly.mode2 =
	TA_POLYMODE2_BLEND_SRC|TA_POLYMODE2_FOG_DISABLED|TA_POLYMODE2_TEXTURE_REPLACE|
	TA_POLYMODE2_U_SIZE_1024|TA_POLYMODE2_V_SIZE_1024;
  mypoly.texture = screenFormats[_screenFormat].textureFormat|
	TA_TEXTUREMODE_NON_TWIDDLED|TA_TEXTUREMODE_STRIDE|
	TA_TEXTUREMODE_ADDRESS(screen_tx[_screen_buffer]);

  mypoly.red = mypoly.green = mypoly.blue = mypoly.alpha = 0;

  ta_begin_frame();
  // *((volatile unsigned int *)(void *)0xa05f8040) = 0x0000ff;
  ta_commit_list(&mypoly);

  myvertex.cmd = TA_CMD_VERTEX;
  myvertex.ocolour = 0;
  myvertex.colour = 0;
  myvertex.z = 0.5;
  myvertex.u = 0.0;
  myvertex.v = 0.0;

  myvertex.x = LEFT_OFFSET;
  myvertex.y = TOP_OFFSET;
  ta_commit_list(&myvertex);

  myvertex.x = _screen_w*_xscale;
  myvertex.u = _screen_w*(1/1024.0);
  ta_commit_list(&myvertex);

  myvertex.x = 0.0;
  myvertex.y += _screen_h*_yscale;
  myvertex.u = 0.0;
  myvertex.v = _screen_h*(1/1024.0);
  ta_commit_list(&myvertex);

  myvertex.x = _screen_w*_xscale;
  myvertex.u = _screen_w*(1/1024.0);
  myvertex.cmd |= TA_CMD_VERTEX_EOS;
  ta_commit_list(&myvertex);

  ta_commit_end();

  if (_overlay_visible) {
	if (_overlay_fade < 1.0)
	  _overlay_fade += 0.125;
  } else {
	if (_overlay_fade > 0)
	  _overlay_fade -= 0.125;
  }

  if (_overlay_fade > 0.0) {

	mypoly.cmd =
	  TA_CMD_POLYGON|TA_CMD_POLYGON_TYPE_TRANSPARENT|TA_CMD_POLYGON_SUBLIST|
	  TA_CMD_POLYGON_STRIPLENGTH_2|TA_CMD_POLYGON_PACKED_COLOUR|TA_CMD_POLYGON_TEXTURED;
	mypoly.mode1 = TA_POLYMODE1_Z_ALWAYS|TA_POLYMODE1_NO_Z_UPDATE;
	mypoly.mode2 =
	  TA_POLYMODE2_BLEND_SRC/*_ALPHA*/|TA_POLYMODE2_BLEND_DST_INVALPHA|
	  TA_POLYMODE2_ENABLE_ALPHA|
	  TA_POLYMODE2_FOG_DISABLED|TA_POLYMODE2_TEXTURE_MODULATE_ALPHA|
	  TA_POLYMODE2_U_SIZE_512|TA_POLYMODE2_V_SIZE_512;
	mypoly.texture = TA_TEXTUREMODE_ARGB4444|TA_TEXTUREMODE_NON_TWIDDLED|
	  TA_TEXTUREMODE_ADDRESS(ovl_tx[_overlay_buffer]);

	mypoly.red = mypoly.green = mypoly.blue = mypoly.alpha = 0.0;

	ta_commit_list(&mypoly);

	myvertex.cmd = TA_CMD_VERTEX;
	myvertex.ocolour = 0;
	myvertex.colour = 0xffffff|(((int)(255*_overlay_fade))<<24);

	myvertex.z = 0.5;
	myvertex.u = 0.0;
	myvertex.v = 0.0;

	myvertex.x = _overlay_x*_xscale+LEFT_OFFSET;
	myvertex.y = _overlay_y*_yscale+TOP_OFFSET;
	ta_commit_list(&myvertex);

	myvertex.x += OVL_W*_xscale;
	myvertex.u = OVL_W*(1.0/512.0);
	ta_commit_list(&myvertex);

	myvertex.x = _overlay_x*_xscale;
	myvertex.y += OVL_H*_yscale;
	myvertex.u = 0.0;
	myvertex.v = OVL_H*(1.0/512.0);
	ta_commit_list(&myvertex);

	myvertex.x += OVL_W*_xscale;
	myvertex.u = OVL_W*(1.0/512.0);
	myvertex.cmd |= TA_CMD_VERTEX_EOS;
	ta_commit_list(&myvertex);
  }

  if (_softkbd_on)
	if (_softkbd_motion < 120)
	  _softkbd_motion += 10;
	else
	  ;
  else
	if (_softkbd_motion > 0)
	  _softkbd_motion -= 10;

  if (_softkbd_motion)
	_softkbd.draw(330.0*sin(0.013*_softkbd_motion) - 320.0, 200.0,
		  120-_softkbd_motion);

  // *((volatile unsigned int *)(void *)0xa05f8040) = 0xffff00;
  drawMouse(_ms_cur_x, _ms_cur_y, _ms_cur_w, _ms_cur_h, _ms_buf, _ms_visible);
  // *((volatile unsigned int *)(void *)0xa05f8040) = 0xff00ff;
  ta_commit_frame();

  // *((volatile unsigned int *)(void *)0xa05f8040) = 0x0;

  _last_screen_refresh = Timer();
}

void OSystem_Dreamcast::updateScreen(void)
{
  updateScreenTextures();
  updateScreenPolygons();
}

void OSystem_Dreamcast::maybeRefreshScreen(void)
{
  unsigned int t = Timer();
  if((int)(t-_last_screen_refresh) > USEC_TO_TIMER(30000))
	updateScreenPolygons();
}

void OSystem_Dreamcast::drawMouse(int xdraw, int ydraw, int w, int h,
				  unsigned char *buf, bool visible)
{
  if (!visible || buf == NULL || !w || !h || w>MOUSE_W || h>MOUSE_H) {
	commit_dummy_transpoly();
	return;
  }

  struct polygon_list mypoly;
  struct packed_colour_vertex_list myvertex;

  _mouse_buffer++;
  _mouse_buffer &= NUM_BUFFERS-1;

  unsigned short *dst = (unsigned short *)mouse_tx[_mouse_buffer];
  unsigned int texturemode = screenFormats[_mouseFormat].textureFormat;

  if (_mouseFormat == 0) {
	unsigned short *pal = _enable_cursor_palette? cursor_palette : palette;
	for (int y=0; y<h; y++) {
	  int x;
	  for (x=0; x<w; x++)
	if (*buf == _ms_keycolor) {
	  *dst++ = 0;
	  buf++;
	} else
	  *dst++ = pal[*buf++]|0x8000;
	  dst += MOUSE_W-x;
	}
  } else if(texturemode == TA_TEXTUREMODE_RGB565 &&
	    _ms_keycolor<=0xffff) {
	/* Special handling when doing colorkey on RGB565; we need
	   to convert to ARGB1555 to get an alpha channel... */
	texturemode = TA_TEXTUREMODE_ARGB1555;
	unsigned short *bufs = (unsigned short *)buf;
	for (int y=0; y<h; y++) {
	  int x;
	  for (x=0; x<w; x++)
	if (*bufs == _ms_keycolor) {
	  *dst++ = 0;
	  bufs++;
	} else {
	  unsigned short p = *bufs++;
	  *dst++ = (p&0x1f)|((p&0xffc0)>>1)|0x8000;
	}
	  dst += MOUSE_W-x;
	}
  } else {
	unsigned short *bufs = (unsigned short *)buf;
	for (int y=0; y<h; y++) {
	  int x;
	  for (x=0; x<w; x++)
	if (*bufs == _ms_keycolor) {
	  *dst++ = 0;
	  bufs++;
	} else
	  *dst++ = *bufs++;
	  dst += MOUSE_W-x;
	}
  }

  mypoly.cmd =
	TA_CMD_POLYGON|TA_CMD_POLYGON_TYPE_TRANSPARENT|TA_CMD_POLYGON_SUBLIST|
	TA_CMD_POLYGON_STRIPLENGTH_2|TA_CMD_POLYGON_PACKED_COLOUR|TA_CMD_POLYGON_TEXTURED;
  mypoly.mode1 = TA_POLYMODE1_Z_ALWAYS|TA_POLYMODE1_NO_Z_UPDATE;
  mypoly.mode2 =
	TA_POLYMODE2_BLEND_SRC_ALPHA|TA_POLYMODE2_BLEND_DST_INVALPHA|
	TA_POLYMODE2_FOG_DISABLED|TA_POLYMODE2_TEXTURE_REPLACE|
	TA_POLYMODE2_U_SIZE_128|TA_POLYMODE2_V_SIZE_128;
  mypoly.texture = texturemode|TA_TEXTUREMODE_NON_TWIDDLED|
	TA_TEXTUREMODE_ADDRESS(mouse_tx[_mouse_buffer]);

  mypoly.red = mypoly.green = mypoly.blue = mypoly.alpha = 0;

  ta_commit_list(&mypoly);

  myvertex.cmd = TA_CMD_VERTEX;
  myvertex.ocolour = 0;
  myvertex.colour = 0xffff00;
  myvertex.z = 0.25;
  myvertex.u = 0.0;
  myvertex.v = 0.0;

  myvertex.x = (xdraw-_ms_hotspot_x)*_xscale + LEFT_OFFSET;
  myvertex.y = (ydraw-_ms_hotspot_y)*_yscale + TOP_OFFSET;
  ta_commit_list(&myvertex);

  myvertex.x += w*_xscale;
  myvertex.u = w*(1.0/MOUSE_W);
  ta_commit_list(&myvertex);

  myvertex.x -= w*_xscale;
  myvertex.y += h*_yscale;
  myvertex.u = 0.0;
  myvertex.v = h*(1.0/MOUSE_H);
  ta_commit_list(&myvertex);

  myvertex.x += w*_xscale;
  myvertex.u = w*(1.0/MOUSE_W);
  myvertex.cmd |= TA_CMD_VERTEX_EOS;
  ta_commit_list(&myvertex);
}

void OSystem_Dreamcast::mouseToSoftKbd(int x, int y, int &rx, int &ry) const
{
  if (_softkbd_motion) {
	rx = (int)(x*_xscale - (330.0*sin(0.013*_softkbd_motion) + LEFT_OFFSET - 320.0));
	ry = (int)(y*_yscale + TOP_OFFSET - 200.0);
  } else {
	rx = -1;
	ry = -1;
  }
}


void OSystem_Dreamcast::showOverlay(bool inGUI)
{
  _overlay_in_gui = inGUI;
  _overlay_visible = true;
  clearOverlay();
}

void OSystem_Dreamcast::hideOverlay()
{
  _overlay_in_gui = false;
  _overlay_visible = false;
}

void OSystem_Dreamcast::clearOverlay()
{
  if (!_overlay_visible)
	return;

  memset(overlay, 0, OVL_W*OVL_H*sizeof(unsigned short));

  _overlay_dirty = true;
}

void OSystem_Dreamcast::grabOverlay(Graphics::Surface &surface)
{
  assert(surface.w >= OVL_W);
  assert(surface.h >= OVL_H);
  assert(surface.format.bytesPerPixel == sizeof(unsigned short));

  byte *src = (byte *)overlay;
  byte *dst = (byte *)surface.getPixels();
  Graphics::copyBlit(dst, src, surface.pitch, OVL_W * sizeof(unsigned short),
	OVL_W, OVL_H, sizeof(unsigned short));
}

void OSystem_Dreamcast::copyRectToOverlay(const void *buf, int pitch,
					  int x, int y, int w, int h)
{
  if (w<1 || h<1)
	return;
  unsigned short *dst = overlay + y*OVL_W + x;
  const unsigned char *src = (const unsigned char *)buf;
  do {
	memcpy(dst, src, w*sizeof(int16));
	dst += OVL_W;
	src += pitch;
  } while (--h);
  _overlay_dirty = true;
}

Graphics::Surface *OSystem_Dreamcast::lockScreen()
{
  if (!screen)
	return 0;

  _framebuffer.init(_screen_w, _screen_h, SCREEN_W*2, screen, screenFormats[_screenFormat]);

  return &_framebuffer;
}

void OSystem_Dreamcast::unlockScreen()
{
  // Force screen update
  _screen_dirty = true;
}

int16 OSystem_Dreamcast::getOverlayHeight()
{
  return OVL_H;
}

int16 OSystem_Dreamcast::getOverlayWidth()
{
  return OVL_W;
}
