Logo Search packages:      
Sourcecode: scummvm version File versions

morphos.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2002 Rüdiger Hanke
 *
 * 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.
 *
 * MorphOS interface
 *
 * $Header: /cvsroot/scummvm/scummvm/backends/morphos/morphos.cpp,v 1.43 2004/12/05 17:42:16 fingolfin Exp $
 *
 */

#include "stdafx.h"
#include "engine.h"
#include "common/util.h"
#include "scumm/scumm.h"

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <exec/semaphores.h>
#include <devices/ahi.h>
#include <devices/rawkeycodes.h>
#include <dos/dostags.h>
#include <intuition/screens.h>
#include <cybergraphics/cybergraphics.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>

#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/keymap.h>
#include <proto/timer.h>
#include <proto/cdda.h>
#include <proto/cybergraphics.h>

#include <time.h>

#include "morphos.h"
#include "morphos_sound.h"
#include "morphos_scaler.h"

static TagItem PlayTags[] =   { { CDPA_StartTrack, 1 },
                                                              { CDPA_StartFrame, 0 },
                                                              { CDPA_EndTrack,   1 },
                                                              { CDPA_EndFrame,   0 },
                                                              { CDPA_Loops,      1 },
                                                              { TAG_DONE,              0 }
                                                            };

static CONST_STRPTR MonkeyCDIDs[] = { "ID2500496F035CBC", "ID250040360345DB", NULL };
static CONST_STRPTR LoomCDIDs[]   = { NULL };
static CONST_STRPTR MonkeyNames[] = { "Monkey1CD", "Madness", NULL };
static CONST_STRPTR LoomNames[]   = { "LoomCD", NULL };

#define BLOCKSIZE_X     32
#define BLOCKSIZE_Y     8

#define BLOCKS_X              (ScummBufferWidth/BLOCKSIZE_X)
#define BLOCKS_Y              (ScummBufferHeight/BLOCKSIZE_Y)
#define BLOCK_ID(x, y)  ((y/BLOCKSIZE_Y)*BLOCKS_X+(x/BLOCKSIZE_X))

OSystem_MorphOS *OSystem_MorphOS::create(SCALERTYPE gfx_scaler, bool full_screen)
{
      OSystem_MorphOS *syst = new OSystem_MorphOS(gfx_scaler, full_screen);

      if (!syst || !syst->Initialise())
      {
            delete syst;
            error("Failed to create system object. Exiting.");
      }

      return syst;
}

OSystem_MorphOS::OSystem_MorphOS(SCALERTYPE gfx_mode, bool full_screen)
{
      ScummScreen = NULL;
      ScummWindow = NULL;
      ScummBuffer = NULL;
      ScummScreenBuffer[0] = NULL;
      ScummScreenBuffer[1] = NULL;
      ScummRenderTo = NULL;
      ScummNoCursor = NULL;
      ScummSoundThread = NULL;
      ScummWinX = -1;
      ScummWinY = -1;
      ScummDefaultMouse = false;
      ScummOrigMouse = false;
      ScummShakePos = 0;
      ScummScaler = gfx_mode;
      ScummScale = (gfx_mode == ST_NONE) ? 0 : 1;
      ScummDepth = 0;
      Scumm16ColFmt16 = false;
      ScummScrWidth = 0;
      ScummScrHeight = 0;
      ScreenChanged = false;
      DirtyBlocks = NULL;
      BlockColors = NULL;
      UpdateRects = 0;
      Scaler = NULL;
      FullScreenMode = full_screen;
      CDrive = NULL;
      CDDATrackOffset = 0;
      strcpy(ScummWndTitle, "ScummVM MorphOS");
      TimerMsgPort = NULL;
      TimerIORequest = NULL;
      InputMsgPort = NULL;
      InputIORequest = NULL;
      ThreadPort = NULL;
      OvlCMap = NULL;
      OvlBitMap = NULL;
      OvlSavedBuffer = NULL;
      TimerBase = NULL;
      ScummNoCursor = NULL;
      UpdateRegion = NULL;
      NewUpdateRegion = NULL;
      MouseImage = NULL;
}

bool OSystem_MorphOS::Initialise()
{
      OpenATimer(&TimerMsgPort, (IORequest **) &TimerIORequest, UNIT_MICROHZ);

      if ((InputMsgPort = CreateMsgPort()))
      {
            if ((InputIORequest = (IOStdReq*) CreateIORequest(InputMsgPort, sizeof (IOStdReq))))
            {
                  if ((OpenDevice("input.device", NULL, (IORequest *) InputIORequest, NULL)))
                  {
                        DeleteIORequest(InputIORequest);
                        DeleteMsgPort(InputMsgPort);
                        InputIORequest = NULL;
                        InputMsgPort = NULL;
                  }
            }
            else
            {
                  DeleteMsgPort(InputMsgPort);
                  InputMsgPort = NULL;
            }
      }

      if (!InputIORequest)
      {
            warning("input.device could not be opened");
            return false;
      }

      ThreadPort = CreateMsgPort();
      if (!ThreadPort)
      {
            warning("Unable to create a message port");
            return false;
      }

      OvlCMap = GetColorMap(256);

      InitSemaphore(&CritSec);

      TimerBase = (Library*) TimerIORequest->tr_node.io_Device;
      ScummNoCursor = (UWORD *) AllocVec(16, MEMF_CLEAR);
      UpdateRegion = NewRegion();
      NewUpdateRegion = NewRegion();
      if (!UpdateRegion || !NewUpdateRegion)
      {
            warning("Could not create region for screen update");
            return false;
      }
      if (!OvlCMap)
      {
            warning("Could not allocate overlay color map");
            return false;
      }
      if (!ScummNoCursor)
      {
            warning("Could not allocate empty cursor image");
            return false;
      }

      return true;
}

OSystem_MorphOS::~OSystem_MorphOS()
{
      if (DirtyBlocks)
      {
            FreeVec(DirtyBlocks);

            for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++)
                  FreeVec(BlockColors[b]);
            FreeVec(BlockColors);
      }

      if (OvlCMap)
            FreeColorMap(OvlCMap);

      delete Scaler;

      if (UpdateRegion)
            DisposeRegion(UpdateRegion);

      if (NewUpdateRegion)
            DisposeRegion(NewUpdateRegion);

      if (ThreadPort)
            DeleteMsgPort(ThreadPort);

      if (CDrive && CDDABase)
      {
            CDDA_Stop(CDrive);
            CDDA_ReleaseDrive(CDrive);
      }

      if (InputIORequest)
      {
            CloseDevice((IORequest *) InputIORequest);
            DeleteIORequest((IORequest *) InputIORequest);
      }

      if (InputMsgPort)
            DeleteMsgPort(InputMsgPort);

      if (TimerIORequest)
      {
            CloseDevice((IORequest *) TimerIORequest);
            DeleteIORequest((IORequest *) TimerIORequest);
      }

      if (TimerMsgPort)
            DeleteMsgPort(TimerMsgPort);

      if (ScummNoCursor)
            FreeVec(ScummNoCursor);

      if (ScummBuffer)
            FreeVec(ScummBuffer);

      if (OvlSavedBuffer)
            FreeVec(OvlSavedBuffer);

      if (ScummRenderTo && !ScummScreen)
            FreeBitMap(ScummRenderTo);

      if (OvlBitMap)
            FreeVec(OvlBitMap);

      if (ScummWindow)
            CloseWindow(ScummWindow);

      if (ScummScreen)
      {
            if (ScummScreenBuffer[0])
                  FreeScreenBuffer(ScummScreen, ScummScreenBuffer[0]);
            if( ScummScreenBuffer[1] )
                  FreeScreenBuffer(ScummScreen, ScummScreenBuffer[1]);
            CloseScreen(ScummScreen);
      }
}

bool OSystem_MorphOS::OpenATimer(MsgPort **port, IORequest **req, ULONG unit, bool required)
{
      *req = NULL;
      const char *err_msg = NULL;
      
      *port = CreateMsgPort();
      if (*port)
      {
            *req = (IORequest *) CreateIORequest(*port, sizeof (timerequest));
            if (*req)
            {
                  if (OpenDevice(TIMERNAME, unit, *req, 0))
                  {
                        DeleteIORequest(*req);
                        *req = NULL;
                        err_msg = "Failed to open timer device";
                  }
            }
            else
                  err_msg = "Failed to create IO request";
      }
      else
            err_msg = "Failed to create message port";

      if (err_msg)
      {
            if (required)
                  error(err_msg);
            warning(err_msg);
      }

      return *req != NULL;
}

uint32 OSystem_MorphOS::getMillis()
{
      int ticks = clock();
      ticks *= (1000/CLOCKS_PER_SEC);
      return ticks;
}

void OSystem_MorphOS::delayMillis(uint msecs)
{
/*      TimerIORequest->tr_node.io_Command = TR_ADDREQUEST;
      TimerIORequest->tr_time.tv_secs = 0;
      TimerIORequest->tr_time.tv_micro = msecs*1000;
      DoIO((IORequest *) TimerIORequest);*/
   TimeDelay(UNIT_MICROHZ, 0, msecs*1000);
}

void OSystem_MorphOS::setTimerCallback(TimerProc callback, int timer)
{
      warning("setTimerCallback() unexpectedly called");
}

OSystem::MutexRef OSystem_MorphOS::createMutex()
{
      SignalSemaphore *sem = (SignalSemaphore *) AllocVec(sizeof (SignalSemaphore), MEMF_PUBLIC);

      if (sem)
            InitSemaphore(sem);

      return (MutexRef)sem;
}

void OSystem_MorphOS::lockMutex(MutexRef mutex)
{
      ObtainSemaphore((SignalSemaphore *) mutex);
}

void OSystem_MorphOS::unlockMutex(MutexRef mutex)
{
      ReleaseSemaphore((SignalSemaphore *)mutex);
}

void OSystem_MorphOS::deleteMutex(MutexRef mutex)
{
      FreeVec(mutex);
}

uint32 OSystem_MorphOS::property(int param, Property *value)
{
      AUTO_LOCK

      switch (param)
      {
            case PROP_GET_FULLSCREEN:
                  return ScummScreen != NULL;

            case PROP_TOGGLE_FULLSCREEN:
                  CreateScreen(CSDSPTYPE_TOGGLE);
                  return 1;

            case PROP_SET_WINDOW_CAPTION:
                  sprintf(ScummWndTitle, "ScummVM MorphOS - %s", value->caption);
                  if (ScummWindow)
                        SetWindowTitles(ScummWindow, ScummWndTitle, ScummWndTitle);
                  return 1;

            case PROP_OPEN_CD:
            {
                  CONST_STRPTR *ids = NULL, *names = NULL;

                  if (g_scumm)
                        GameID = g_scumm->_gameId;

                  switch (GameID)
                  {
                        case GID_MONKEY:
                        case GID_MONKEY_SEGA:
                              ids = MonkeyCDIDs;
                              names = MonkeyNames;
                              break;

                        case GID_LOOM256:
                              ids = LoomCDIDs;
                              names = LoomNames;
                              break;
                  }

                  if (!CDDABase) CDDABase = OpenLibrary("cdda.library", 2);
                  if (CDDABase)
                  {
                        CDrive = NULL;
                        if (ids)
                        {
                              int i = 0;

                              while (ids[i] && !CDrive)
                              {
                                    TagItem FindCDTags[] =  { { CDFA_CDID, (ULONG) ids[i] },
                                                                                      { TAG_DONE,  0      }
                                                                                    };
                                    CDrive = CDDA_FindNextDriveA(NULL, FindCDTags);
                                    i++;
                              }
                        }

                        if (!CDrive && names)
                        {
                              int i = 0;

                              while (names[i] && !CDrive)
                              {
                                    TagItem FindCDTags[] =  { { CDFA_VolumeName, (ULONG) names[i] },
                                                                                      { TAG_DONE,        0        }
                                                                                    };
                                    CDrive = CDDA_FindNextDriveA(NULL, FindCDTags);
                                    i++;
                              }
                        }

                        if (CDrive)
                        {
                              if (!CDDA_ObtainDriveA(CDrive, CDDA_SHARED_ACCESS, NULL))
                              {
                                    CDrive = NULL;
                                    warning("Failed to obtain CD drive - music will not play");
                              }
                              else if (GameID == GID_LOOM256)
                              {
                                    // Offset correction *may* be required
                                    CDS_TrackInfo ti = { sizeof (CDS_TrackInfo) };

                                    if (CDDA_GetTrackInfo(CDrive, 1, 0, &ti))
                                          CDDATrackOffset = ti.ti_TrackStart.tm_Format.tm_Frame-22650;
                              }
                        }
                        else
                              warning( "Could not find game CD inserted in CD-ROM drive - cd audio will not play" );
                  }
                  else
                        warning( "Failed to open cdda.library - cd audio will not play" );
                  break;
            }

            case PROP_GET_SAMPLE_RATE:
                  return SAMPLES_PER_SEC;
      }

      return 0;
}

void OSystem_MorphOS::playCD(int track, int num_loops, int start_frame, int duration)
{
      if (CDrive && start_frame >= 0)
      {
            if (start_frame > 0)
                  start_frame -= CDDATrackOffset;

            PlayTags[0].ti_Data = track;
            PlayTags[1].ti_Data = start_frame;
            PlayTags[2].ti_Data = (duration == 0) ? track+1 : track;
            PlayTags[3].ti_Data = duration ? start_frame+duration : 0;
            PlayTags[4].ti_Data = (num_loops == 0) ? 1 : num_loops;
            CDDA_PlayA(CDrive, PlayTags);
      }
}

void OSystem_MorphOS::stopCD()
{
      if (CDrive)
            CDDA_Stop(CDrive);
}

bool OSystem_MorphOS::pollCD()
{
      ULONG status;

      if (CDrive == NULL)
            return false;

      CDDA_GetAttr(CDDA_Status, CDrive, &status);
      return status == CDDA_Status_Busy;
}

void OSystem_MorphOS::updateCD()
{
}

void OSystem_MorphOS::quit()
{
      int num_threads = 0;

      if (ScummSoundThread)
      {
            num_threads++;
            Signal((Task *) ScummSoundThread, SIGBREAKF_CTRL_C);
            ScummSoundThread = NULL;
      }

      // TODO: this code could probably greatly simplified now that there is
      // only one thread left...
      while (num_threads > 0)
      {
            Message* msg;

            WaitPort(ThreadPort);
            while (msg = GetMsg(ThreadPort))
                  num_threads--;
      }

      exit(0);
}

#define CVT8TO32(col)   ((col<<24) | (col<<16) | (col<<8) | col)

void OSystem_MorphOS::setPalette(const byte *colors, uint start, uint num)
{
      const byte *data = colors;
      UWORD changed_colors[256];
      UWORD num_changed = 0;

      for (uint i = start; i != start+num; i++)
      {
            ULONG color32 = (data[0] << 16) | (data[1] << 8) | data[2];
            if (color32 != ScummColors[i])
            {
                  if (ScummDepth == 8)
                        SetRGB32(&ScummScreen->ViewPort, i, CVT8TO32(data[0]), CVT8TO32(data[1]), CVT8TO32(data[2]));
                  ScummColors16[i] = Scumm16ColFmt16 ? (((data[0]*31)/255) << 11) | (((data[1]*63)/255) << 5) | ((data[ 2 ]*31)/255) : (((data[0]*31)/255) << 10) | (((data[1]*31)/255) << 5) | ((data[2]*31)/255);
                  ScummColors[i] = color32;
                  changed_colors[num_changed++] = i;
            }
            data += 4;
      }

      if (ScummScale || ScummDepth != 8)
      {
            if (DirtyBlocks && num_changed < 200)
            {
                  for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++)
                  {
                        UWORD *block_colors = BlockColors[b];
                        UWORD *color_ptr = changed_colors;
                        for (int c = 0; c < num_changed; c++)
                        {
                              if (block_colors[*color_ptr++])
                              {
                                    UWORD x, y;
                                    x = b % BLOCKS_X;
                                    y = b / BLOCKS_X;
                                    DirtyBlocks[b] = true;
                                    AddUpdateRect(x*BLOCKSIZE_X, y*BLOCKSIZE_Y, BLOCKSIZE_X, BLOCKSIZE_Y);
                                    break;
                              }
                        }
                  }
            }
            else
                  AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight);
      }
}

void OSystem_MorphOS::CreateScreen(CS_DSPTYPE dspType)
{
      LONG  mode = INVALID_ID;
      int   depths[] = { 8, 32, 16, 15, 0 };
      int   i;
      Screen *wb = NULL;

      if (dspType != CSDSPTYPE_KEEP)
            FullScreenMode = (dspType == CSDSPTYPE_FULLSCREEN) || (dspType == CSDSPTYPE_TOGGLE && !FullScreenMode);

      if (ScummRenderTo && !ScummScreen)
            FreeBitMap(ScummRenderTo);
      ScummRenderTo = NULL;

      if (ScummWindow)
      {
            if (ScummScreen == NULL)
            {
                  ScummWinX = ScummWindow->LeftEdge;
                  ScummWinY = ScummWindow->TopEdge;
            }
            CloseWindow (ScummWindow);
            ScummWindow = NULL;
      }

      if (ScummScreen)
      {
            if (ScummScreenBuffer[0])
                  FreeScreenBuffer(ScummScreen, ScummScreenBuffer[0]);
            if (ScummScreenBuffer[1])
                  FreeScreenBuffer(ScummScreen, ScummScreenBuffer[1]);
            CloseScreen(ScummScreen);
            ScummScreen = NULL;
      }
      
      ScummScrWidth  = ScummBufferWidth << ScummScale;
      ScummScrHeight = ScummBufferHeight << ScummScale;

      if (FullScreenMode)
      {
            for (i = ScummScale; mode == INVALID_ID && depths[i]; i++)
                  mode = BestCModeIDTags(CYBRBIDTG_NominalWidth,    ScummScrWidth,
                                                              CYBRBIDTG_NominalHeight,   ScummScrHeight,
                                                              CYBRBIDTG_Depth,              depths[i],
                                                              TAG_DONE
                                                             );
            ScummDepth = depths[i-1];

            if (mode == INVALID_ID)
                  error("Could not find suitable screenmode");

            ScummScreen = OpenScreenTags(NULL,  SA_AutoScroll, TRUE,
                                                                                    SA_Depth,         ScummDepth,
                                                                                    SA_Width,         STDSCREENWIDTH,
                                                                                    SA_Height,        STDSCREENHEIGHT,
                                                                                    SA_DisplayID,     mode,
                                                                                    SA_ShowTitle,     FALSE,
                                                                                    SA_Type,                CUSTOMSCREEN,
                                                                                    SA_Title,         "ScummVM MorphOS",
                                                                                    TAG_DONE
                                                                   );

            if (ScummScreen == NULL)
                  error("Failed to open screen");

            LONG RealDepth = GetBitMapAttr(&ScummScreen->BitMap, BMA_DEPTH);
            if (RealDepth != ScummDepth)
            {
                  warning("Screen did not open in expected depth");
                  ScummDepth = RealDepth;
            }
            ScummScreenBuffer[0] = AllocScreenBuffer(ScummScreen, NULL, SB_SCREEN_BITMAP);
            ScummScreenBuffer[1] = AllocScreenBuffer(ScummScreen, NULL, 0);
            ScummRenderTo = ScummScreenBuffer[1]->sb_BitMap;
            ScummPaintBuffer = 1;

            if (ScummScreenBuffer[0] == NULL || ScummScreenBuffer[1] == NULL)
                  error("Failed to allocate screen buffer");

            // Make both buffers black to avoid grey strip on bottom of screen
            RastPort rp;
            InitRastPort(&rp);
            SetRGB32(&ScummScreen->ViewPort, 0, 0, 0, 0);
            rp.BitMap = ScummScreenBuffer[0]->sb_BitMap;
            FillPixelArray(&ScummScreen->RastPort, 0, 0, ScummScreen->Width, ScummScreen->Height, 0);
            rp.BitMap = ScummRenderTo;
            FillPixelArray(&rp, 0, 0, ScummScreen->Width, ScummScreen->Height, 0);

            if (ScummDepth == 8)
            {
                  for (int color = 0; color < 256; color++)
                  {
                        ULONG r, g, b;

                        r = (ScummColors[color] >> 16) & 0xff;
                        g = (ScummColors[color] >> 8) & 0xff;
                        b = (ScummColors[color] >> 0) & 0xff;
                        SetRGB32(&ScummScreen->ViewPort, color, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b));
                  }
            }
      }
      else
      {
            wb = LockPubScreen(NULL);
            if (wb == NULL)
                  error("Could not lock default public screen");

            ScreenToFront(wb);
      }

      ScummWindow = OpenWindowTags(NULL,  WA_Left,                      (wb && ScummWinX >= 0) ? ScummWinX : 0,
                                                                              WA_Top,                       wb ? ((ScummWinY >= 0) ? ScummWinY : wb->BarHeight+1) : 0,
                                                                              WA_InnerWidth,       FullScreenMode ? ScummScreen->Width : ScummScrWidth,
                                                                              WA_InnerHeight,   FullScreenMode ? ScummScreen->Height : ScummScrHeight,
                                                                              WA_Activate,            TRUE,
                                                                              WA_Title,            wb ? ScummWndTitle : NULL,
                                                                              WA_ScreenTitle,   wb ? ScummWndTitle : NULL,
                                                                              WA_Borderless,          FullScreenMode,
                                                                              WA_CloseGadget,   !FullScreenMode,
                                                                              WA_DepthGadget,   !FullScreenMode,
                                                                              WA_DragBar,             !FullScreenMode,
                                                                              WA_ReportMouse,   TRUE,
                                                                              WA_RMBTrap,             TRUE,
                                                                              WA_IDCMP,               IDCMP_RAWKEY            |
                                                                                                                  IDCMP_MOUSEMOVE   |
                                                                                                                  IDCMP_CLOSEWINDOW |
                                                                                                                  IDCMP_MOUSEBUTTONS,
                                                                              WA_CustomScreen,  FullScreenMode ? (ULONG)ScummScreen : (ULONG)wb,
                                                                              TAG_DONE
                                                             );

      if (wb)
            UnlockPubScreen(NULL, wb);

      if (ScummWindow == NULL)
            error("Failed to open window");

      if (!ScummDefaultMouse)
      {
            SetPointer(ScummWindow, ScummNoCursor, 1, 1, 0, 0);
            ScummOrigMouse = false;
      }

      if (ScummScreen == NULL)
      {
            ScummDepth = GetCyberMapAttr(ScummWindow->RPort->BitMap, CYBRMATTR_DEPTH);
            if (ScummDepth == 8)
                  error("Default public screen must be 15 bit or higher if you want to play in window mode");

            ScummRenderTo = AllocBitMap(ScummScrWidth, ScummScrHeight, ScummDepth, BMF_MINPLANES, ScummWindow->RPort->BitMap);
            if (ScummRenderTo == NULL)
                  error("Failed to allocate bitmap");
      }

      if ((ScummDepth == 15 && Scumm16ColFmt16) || (ScummDepth == 16 && !Scumm16ColFmt16))
      {
            for (int col = 0; col < 256; col++)
            {
                  int r = (ScummColors[col] >> 16) & 0xff;
                  int g = (ScummColors[col] >> 8) & 0xff;
                  int b = ScummColors[col] & 0xff;
                  ScummColors16[col] = (Scumm16ColFmt16 == false) ? (((r*31)/255) << 11) | (((g*63)/255) << 5) | ((b*31)/255) : (((r*31)/255) << 10) | (((g*31)/255) << 5) | ((b*31)/255);
            }

            Scumm16ColFmt16 = (ScummDepth == 16);
      }

      if (OvlBitMap)
            FreeVec(OvlBitMap);

      OvlBitMap = AllocVec(ScummBufferWidth*ScummBufferHeight*3, MEMF_PUBLIC | MEMF_CLEAR);
      if (OvlBitMap == NULL)
            error("Failed to allocated bitmap for overlay");

      if (Scaler)
      {
            delete Scaler;
            Scaler = NULL;
      }

      if (ScummScale)
      {
            Scaler = MorphOSScaler::Create(ScummScaler, ScummBuffer, ScummBufferWidth, ScummBufferHeight, ScummColors, ScummColors16, ScummRenderTo);
            if (Scaler == NULL)
            {
                  warning("Failed to create scaler - scaling will be disabled");
                  SwitchScalerTo(ST_NONE);
            }
      }

      AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight);
}

void OSystem_MorphOS::SwitchScalerTo(SCALERTYPE newScaler)
{
      if (newScaler == ST_NONE && ScummScale != 0)
      {
            if (Scaler)
            {
                  delete Scaler;
                  Scaler = NULL;
            }
            ScummScale = 0;
            ScummScaler = ST_NONE;
            CreateScreen(ScummScreen ? CSDSPTYPE_FULLSCREEN : CSDSPTYPE_WINDOWED);
      }
      else
      {
            if (ScummScale == 0)
            {
                  ScummScale = 1;
                  ScummScaler = newScaler;
                  CreateScreen(ScummScreen ? CSDSPTYPE_FULLSCREEN : CSDSPTYPE_WINDOWED);
            }
            else if (ScummScaler != newScaler)
            {
                  ScummScaler = newScaler;
                  if (Scaler)
                        delete Scaler;
                  Scaler = MorphOSScaler::Create(ScummScaler, ScummBuffer, ScummBufferWidth, ScummBufferHeight, ScummColors, ScummColors16, ScummRenderTo);
                  if (Scaler == NULL)
                  {
                        warning("Failed to create scaler - scaling will be disabled");
                        SwitchScalerTo(ST_NONE);
                  }
                  else
                        AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight);
            }
      }
}

bool OSystem_MorphOS::pollEvent(Event &event)
{
      IntuiMessage *ScummMsg;

      ScummMsg = (IntuiMessage *) GetMsg(ScummWindow->UserPort);
      if (ScummMsg)
      {
            switch (ScummMsg->Class)
            {
                  case IDCMP_RAWKEY:
                  {
                        InputEvent FakedIEvent;
                        char charbuf;
            int  qual = 0;

                        memset(&FakedIEvent, 0, sizeof (InputEvent));
                        FakedIEvent.ie_Class = IECLASS_RAWKEY;
                        FakedIEvent.ie_Code = ScummMsg->Code;

                        if (ScummMsg->Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
                              qual |= KBD_ALT;
                        if (ScummMsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
                              qual |= KBD_SHIFT;
                        if (ScummMsg->Qualifier & IEQUALIFIER_CONTROL)
                              qual |= KBD_CTRL;
                        event.kbd.flags = qual;

                        event.type = (ScummMsg->Code & IECODE_UP_PREFIX) ? EVENT_KEYUP : EVENT_KEYDOWN;
                        ScummMsg->Code &= ~IECODE_UP_PREFIX;

                        if (ScummMsg->Code >= RAWKEY_F1 && ScummMsg->Code <= RAWKEY_F10)
                        {
                              /*
                               * Function key
                               */
                              event.kbd.ascii = (ScummMsg->Code-RAWKEY_F1)+315;
                              event.kbd.keycode = 0;
                        }
                        else if (ScummMsg->Code == RAWKEY_F11 || ScummMsg->Code == RAWKEY_F12)
                        {
                              /*
                               * Function key on PC keyboard
                               */
                              event.kbd.ascii = (ScummMsg->Code == RAWKEY_F11) ? 325 : 326;
                              event.kbd.keycode = 0;
                        }
                        else if (ScummMsg->Code == NM_WHEEL_UP || ScummMsg->Code == NM_WHEEL_DOWN)
                        {
                              /*
                               * Wheelmouse event
                               */
                              event.type = (ScummMsg->Code == NM_WHEEL_UP) ? EVENT_WHEELUP : EVENT_WHEELDOWN;
                        }
                        else if (MapRawKey(&FakedIEvent, &charbuf, 1, NULL) == 1)
                        {
                              if (qual == KBD_CTRL && charbuf == 'z')
                              {
                                    event.type = EVENT_QUIT;
                                    break;
                              }
                              else if (qual == KBD_ALT)
                              {
                                    if (charbuf >= '0' && charbuf <= '9')
                                    {
                                          SCALERTYPE new_scaler = MorphOSScaler::FindByIndex(charbuf-'0');
                                          ReplyMsg((Message *) ScummMsg);
                                          if (new_scaler != ST_INVALID)
                                                SwitchScalerTo(new_scaler);
                                          return false;
                                    }
                                    else if (charbuf == 'x')
                                    {
                                          event.type = EVENT_QUIT;
                                          break;
                                    }
                                    else if (charbuf == 0x0d)
                                    {
                                          ReplyMsg((Message *) ScummMsg);
                                          CreateScreen(CSDSPTYPE_TOGGLE);
                     return false;
                                    }
                              }

                              event.kbd.ascii = charbuf;
                              event.kbd.keycode = charbuf;
                        }
                        break;
                  }

                  case IDCMP_MOUSEMOVE:
                  {
                        LONG newx, newy;

                        newx = (ScummMsg->MouseX-ScummWindow->BorderLeft) >> ScummScale;
                        newy = (ScummMsg->MouseY-ScummWindow->BorderTop) >> ScummScale;

                        if (!FullScreenMode && !ScummDefaultMouse)
                        {
                              if (newx < 0 || newx > (LONG) ScummBufferWidth ||
                                     newy < 0 || newy > (LONG) ScummBufferHeight
                                    )
                              {
                                    if (!ScummOrigMouse)
                                    {
                                          ScummOrigMouse = true;
                                          ClearPointer(ScummWindow);
                                    }
                              }
                              else if (ScummOrigMouse)
                              {
                                    ScummOrigMouse = false;
                                    SetPointer(ScummWindow, ScummNoCursor, 1, 1, 0, 0);
                              }
                        }
                        else if (FullScreenMode)
                              newy = newy <? (ScummScrHeight >> ScummScale)-2;

                        event.type = EVENT_MOUSEMOVE;
                        event.mouse.x = newx;
                        event.mouse.y = newy;
                        set_mouse_pos(event.mouse.x, event.mouse.y);
                        break;
                  }

                  case IDCMP_MOUSEBUTTONS:
                  {
                        int newx, newy;

                        newx = (ScummMsg->MouseX-ScummWindow->BorderLeft) >> ScummScale;
                        newy = (ScummMsg->MouseY-ScummWindow->BorderTop) >> ScummScale;

                        switch (ScummMsg->Code)
                        {
                              case SELECTDOWN:
                                    event.type = EVENT_LBUTTONDOWN;
                                    break;

                              case SELECTUP:
                                    event.type = EVENT_LBUTTONUP;
                                    break;

                              case MENUDOWN:
                                    event.type = EVENT_RBUTTONDOWN;
                                    break;

                              case MENUUP:
                                    event.type = EVENT_RBUTTONUP;
                                    break;

                              default:
                                    ReplyMsg((Message *)ScummMsg);
                                    return false;
                        }
                        event.mouse.x = newx;
                        event.mouse.y = newy;
                        break;
                  }

                  case IDCMP_CLOSEWINDOW:
                        event.type = EVENT_QUIT;
                        break;
            }

            if (ScummMsg)
                  ReplyMsg((Message *) ScummMsg);

            return true;
      }

      return false;
}

void OSystem_MorphOS::warpMouse(int x, int y)
{
      if (InputIORequest)
      {
            InputEvent*        FakeIE;
            IEPointerPixel* NewPixel;
      
            /*
             * Fake a mousemove input event
             */
            if ((FakeIE = (InputEvent*) AllocVec(sizeof (InputEvent), MEMF_PUBLIC)))
            {
                  if ((NewPixel = (IEPointerPixel*) AllocVec(sizeof (IEPointerPixel), MEMF_PUBLIC)))
                  {
                        NewPixel->iepp_Screen = ScummWindow->WScreen;
                        NewPixel->iepp_Position.X = (x << ScummScale) + ScummWindow->LeftEdge + ScummWindow->BorderLeft;
                        NewPixel->iepp_Position.Y = (y << ScummScale) + ScummWindow->TopEdge + ScummWindow->BorderTop;

                        FakeIE->ie_EventAddress = NewPixel;
                        FakeIE->ie_NextEvent = NULL;
                        FakeIE->ie_Class = IECLASS_NEWPOINTERPOS;
                        FakeIE->ie_SubClass = IESUBCLASS_PIXEL;
                        FakeIE->ie_Code = IECODE_NOBUTTON;
                        FakeIE->ie_Qualifier = NULL;

                        InputIORequest->io_Data = FakeIE;
                        InputIORequest->io_Length = sizeof (InputEvent);
                        InputIORequest->io_Command = IND_WRITEEVENT;
                        DoIO((IORequest *) InputIORequest);

                        FreeVec(NewPixel);
                  }
                  FreeVec(FakeIE);
            }
      }
}

void OSystem_MorphOS::setShakePos(int shake_pos)
{
      ScummShakePos = shake_pos;
      AddUpdateRect(0, 0, ScummBufferWidth, ScummBufferHeight);
}

#define MOUSE_INTERSECTS(x, y, w, h) \
      (!((MouseOldX+MouseOldWidth <= x ) || (MouseOldX >= x+w) || \
            (MouseOldY+MouseOldHeight <= y) || (MouseOldY >= y+h)))

/* Copy part of bitmap */
void OSystem_MorphOS::copyRectToScreen(const byte *src, int pitch, int x, int y, int w, int h)
{
      byte *dst;

      if (x < 0) { w+=x; src-=x; x = 0; }
      if (y < 0) { h+=y; src-=y*pitch; y = 0; }
      if (w >= ScummBufferWidth-x) { w = ScummBufferWidth - x; }
      if (h >= ScummBufferHeight-y) { h = ScummBufferHeight - y; }

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

      AUTO_LOCK

      if (MouseDrawn)
      {
            if (MOUSE_INTERSECTS(x, y, w, h))
                  UndrawMouse();
      }

      AddUpdateRect(x, y, w, h);

      dst = (byte *)ScummBuffer+y*ScummBufferWidth + x;
      if (DirtyBlocks)
      {
            int cx, cy;
            int block = BLOCK_ID(x, y);
            int line_block = block;
            int start_block = BLOCKSIZE_X-(x % BLOCKSIZE_X);
            int start_y_block = BLOCKSIZE_Y-(y % BLOCKSIZE_Y);
            int next_block;
            int next_y_block;
            UWORD *block_cols = BlockColors[block];

            if (start_block == 0)
                  start_block = BLOCKSIZE_X;
            if (start_y_block == 0)
                  start_y_block = BLOCKSIZE_Y;

            next_block = start_block;
            next_y_block = start_y_block;
            for (cy = 0; cy < h; cy++)
            {
                  for (cx = 0; cx < w; cx++)
                  {
                        UWORD old_pixel = *dst;
                        UWORD src_pixel = *src++;
                        if (old_pixel != src_pixel)
                        {
                              *dst++ = src_pixel;
                              block_cols[old_pixel]--;
                              block_cols[src_pixel]++;
                        }
                        else
                              dst++;
                        if (--next_block == 0)
                        {
                              block++;
                              block_cols = BlockColors[block];
                              next_block = BLOCKSIZE_X;
                        }
                  }
                  if (--next_y_block == 0)
                  {
                        line_block += BLOCKS_X;
                        next_y_block = BLOCKSIZE_Y;
                  }
                  block = line_block;
                  block_cols = BlockColors[block];
                  next_block = start_block;
                  dst += ScummBufferWidth-w;
                  src += pitch-w;
            }
      }
      else
      {
            do
            {
                  memcpy(dst, src, w);
                  dst += ScummBufferWidth;
                  src += pitch;
            } while (--h);
      }
}

bool OSystem_MorphOS::AddUpdateRect(WORD x, WORD y, WORD w, WORD h)
{
      if (UpdateRects > 25)
            return false;

      if (x < 0) { w+=x; x = 0; }
      if (y < 0) { h+=y; y = 0; }
      if (w >= ScummBufferWidth-x) { w = ScummBufferWidth - x; }
      if (h >= ScummBufferHeight-y) { h = ScummBufferHeight - y; }

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

      if (++UpdateRects > 25)
      {
            x = 0; y = 0;
            w = ScummBufferWidth; h = ScummBufferHeight;
      }

      Rectangle update_rect = { x, y, x+w, y+h };
      OrRectRegion(NewUpdateRegion, &update_rect);
      ScreenChanged = true;

      return true;
}

void OSystem_MorphOS::updateScreen()
{
      AUTO_LOCK

      DrawMouse();

      if (!ScreenChanged)
            return;

      OrRegionRegion(NewUpdateRegion, UpdateRegion);

      if (ScummShakePos)
      {
            RastPort rp;

            InitRastPort(&rp);
            rp.BitMap = ScummRenderTo;

            uint32 src_y = 0;
            uint32 dest_y = 0;
            if (ScummShakePos < 0)
                  src_y = -ScummShakePos;
            else
                  dest_y = ScummShakePos;

            if (!ScummScale)
            {
                  if (ScummDepth == 8)
                        WritePixelArray(ScummBuffer, 0, src_y, ScummBufferWidth, &rp, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y, RECTFMT_LUT8);
                  else
                        WriteLUTPixelArray(ScummBuffer, 0, src_y, ScummBufferWidth, &rp, ScummColors, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y, CTABFMT_XRGB8);
            }
            else if (Scaler->Prepare(ScummRenderTo))
            {
                  Scaler->Scale(0, src_y, 0, dest_y, ScummBufferWidth, ScummBufferHeight-src_y-dest_y);
                  Scaler->Finish();
            }

            if (ScummShakePos < 0)
                  FillPixelArray(&rp, 0, (ScummBufferHeight-1) << ScummScale, ScummScrWidth, -ScummShakePos << ScummScale, 0);
            else
                  FillPixelArray(&rp, 0, 0, ScummScrWidth, ScummShakePos << ScummScale, 0);
      }
      else if (!ScummScale)
      {
            RastPort rp;

            InitRastPort(&rp);
            rp.BitMap = ScummRenderTo;

            int32 src_x, src_y;
            int32 src_w, src_h;
            int32 reg_x, reg_y;
            RegionRectangle *update_rect = UpdateRegion->RegionRectangle;

            reg_x = UpdateRegion->bounds.MinX;
            reg_y = UpdateRegion->bounds.MinY;
            while (update_rect)
            {
                  src_x = update_rect->bounds.MinX;
                  src_y = update_rect->bounds.MinY;
                  src_w = update_rect->bounds.MaxX-src_x;
                  src_h = update_rect->bounds.MaxY-src_y;
                  src_x += reg_x;
                  src_y += reg_y;

                  if (src_x) src_x--;
                  if (src_y) src_y--;
                  src_w += 2;
                  if (src_x+src_w >= ScummBufferWidth)
                        src_w = ScummBufferWidth-src_x;
                  src_h += 2;
                  if (src_y+src_h >= ScummBufferHeight)
                        src_h = ScummBufferHeight-src_y;

                  if (ScummDepth == 8)
                        WritePixelArray(ScummBuffer, src_x, src_y, ScummBufferWidth, &rp, src_x, src_y, src_w, src_h, RECTFMT_LUT8);
                  else
                        WriteLUTPixelArray(ScummBuffer, src_x, src_y, ScummBufferWidth, &rp, ScummColors, src_x, src_y, src_w, src_h, CTABFMT_XRGB8);

                  update_rect = update_rect->Next;
            }
      }
      else
      {
            int32 src_x, src_y;
            int32 src_w, src_h;
            int32 reg_x, reg_y;
            RegionRectangle *update_rect = UpdateRegion->RegionRectangle;

            reg_x = UpdateRegion->bounds.MinX;
            reg_y = UpdateRegion->bounds.MinY;

            if (!Scaler->Prepare(ScummRenderTo))
                  update_rect = NULL;

            while (update_rect)
            {
                  src_x = update_rect->bounds.MinX;
                  src_y = update_rect->bounds.MinY;
                  src_w = update_rect->bounds.MaxX-src_x;
                  src_h = update_rect->bounds.MaxY-src_y;
                  src_x += reg_x;
                  src_y += reg_y;

                  if (src_x) src_x--;
                  if (src_y) src_y--;
                  src_w += 2;
                  if (src_x+src_w >= ScummBufferWidth)
                        src_w = ScummBufferWidth-src_x;
                  src_h += 2;
                  if (src_y+src_h >= ScummBufferHeight)
                        src_h = ScummBufferHeight-src_y;

                  Scaler->Scale(src_x, src_y, src_x, src_y, src_w, src_h);
                  update_rect = update_rect->Next;
            }
            Scaler->Finish();
      }

      if (ScummScreen)
      {
            while (!ChangeScreenBuffer(ScummScreen, ScummScreenBuffer[ScummPaintBuffer]));
            ScummPaintBuffer = !ScummPaintBuffer;
             ScummRenderTo = ScummScreenBuffer[ScummPaintBuffer]->sb_BitMap;
      }
      else
      {
            int32 x = (UpdateRegion->bounds.MinX-1) << ScummScale;
            int32 y = (UpdateRegion->bounds.MinY-1) << ScummScale;
            if (x < 0) x = 0;
            if (y < 0) y = 0;
            int32 w = (UpdateRegion->bounds.MaxX << ScummScale)-x+(1 << ScummScale);
            int32 h = (UpdateRegion->bounds.MaxY << ScummScale)-y+(1 << ScummScale);
            if (x+w > ScummScrWidth)  w = ScummScrWidth-x;
            if (y+h > ScummScrHeight) h = ScummScrHeight-y;
            BltBitMapRastPort(ScummRenderTo, x, y, ScummWindow->RPort, ScummWindow->BorderLeft+x, ScummWindow->BorderTop+y, w, h, ABNC | ABC);
            WaitBlit();
      }

      Region *new_region_part = NewUpdateRegion;
      NewUpdateRegion = UpdateRegion;
      ClearRegion(NewUpdateRegion);
      UpdateRegion = new_region_part;

      ScreenChanged = false;
      memset(DirtyBlocks, 0, BLOCKS_X*BLOCKS_Y*sizeof (bool));
      UpdateRects = 0;
}

void OSystem_MorphOS::DrawMouse()
{
      int x,y;
      byte *dst,*bak;
      byte color;

      if (MouseDrawn || !MouseVisible)
            return;
      MouseDrawn = true;

      int ydraw = MouseY - MouseHotspotY;
      int xdraw = MouseX - MouseHotspotX;
      int w = MouseWidth;
      int h = MouseHeight;
      int x_mouseimg_offs = 0;
      int y_mouseimg_offs = 0;
      byte *buf;

      if (xdraw < 0) { x_mouseimg_offs = -xdraw; w += xdraw; xdraw = 0; }
      if (ydraw < 0) { y_mouseimg_offs = -ydraw; h += ydraw; ydraw = 0; }

      MouseOldX = xdraw;
      MouseOldY = ydraw;
      MouseOldWidth = w;
      MouseOldHeight = h;

      AddUpdateRect(xdraw, ydraw, w, h);
      dst = (byte*)ScummBuffer + ydraw*ScummBufferWidth + xdraw;
      bak = MouseBackup;
      buf = MouseImage + y_mouseimg_offs*MAX_MOUSE_W + x_mouseimg_offs;

      for (y = 0; y < h; y++, dst += ScummBufferWidth, bak += MAX_MOUSE_W, buf += MouseWidth)
      {
            if (ydraw+y < ScummBufferHeight)
            {
                  for (x = 0; x<w; x++)
                  {
                        if (xdraw+x < ScummBufferWidth)
                        {
                              bak[x] = dst[x];
                              if ((color=buf[x])!=MouseKeycolor)
                                    dst[x] = color;
                        }
                  }
            }
            else
                  break;
      }
}

void OSystem_MorphOS::UndrawMouse()
{
      int x,y;
      byte *dst,*bak;

      if (!MouseDrawn)
            return;
      MouseDrawn = false;

      dst = (byte*)ScummBuffer + MouseOldY*ScummBufferWidth + MouseOldX;
      bak = MouseBackup;

      AddUpdateRect(MouseOldX, MouseOldY, MouseOldWidth, MouseOldHeight);

      for (y = 0; y < MouseOldHeight; y++, bak += MAX_MOUSE_W, dst += ScummBufferWidth)
      {
            if (MouseOldY + y < ScummBufferHeight)
            {
                  for (x = 0; x < MouseOldWidth; x++)
                  {
                        if (MouseOldX + x < ScummBufferWidth)
                              dst[x] = bak[x];
                  }
            }
            else
                  break;
      }
}

bool OSystem_MorphOS::showMouse(bool visible)
{
      if (MouseVisible == visible)
            return visible;

      bool last = MouseVisible;
      MouseVisible = visible;

      if (!visible)
            UndrawMouse();

      return last;
}

void OSystem_MorphOS::set_mouse_pos(int x, int y)
{
      if (x != MouseX || y != MouseY)
      {
            MouseX = x;
            MouseY = y;
            UndrawMouse();
      }
}

void OSystem_MorphOS::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor)
{
      MouseWidth = w;
      MouseHeight = h;

      MouseHotspotX = hotspot_x;
      MouseHotspotY = hotspot_y;

      MouseKeycolor = keycolor;

      if (MouseImage)
            free(MouseImage);

      MouseImage = (byte *)malloc(w * h);
      memcpy(mouseImage, buf, w * h);

      UndrawMouse();
}

bool OSystem_MorphOS::setSoundCallback(OSystem::SoundProc proc, void *param)
{
      if (ScummSoundThread)
      {
            if (SoundProc == proc)
                  return true;
            clearSoundCallback();
      }

      SoundProc = proc;
      SoundParam = param;

      /*
       * Create Sound Thread
       */
      SoundStartup.mn_Node.ln_Type = NT_MESSAGE;
      SoundStartup.mn_ReplyPort = ThreadPort;
      SoundStartup.mn_Length = sizeof(SoundStartup);

      ScummSoundThread = CreateNewProcTags(NP_Entry, (ULONG) &morphos_sound_thread,
                                                                               NP_CodeType, CODETYPE_PPC,
                                                                               NP_Name, (ULONG) "ScummVM Sound Thread",
                                                                               NP_StartupMsg, &SoundStartup,
                                                                               NP_PPC_Arg1, (ULONG) this,
                                                                               NP_PPC_Arg2, AHIST_S16S, TAG_DONE);
      if (!ScummSoundThread)
      {
            puts("Failed to create sound thread");
            exit(1);
      }

      return true;
}


void OSystem_MorphOS::fill_sound(byte *stream, int len)
{
      if (SoundProc)
            SoundProc(SoundParam, stream, len);
      else
            memset(stream, 0x0, len);
}

void OSystem_MorphOS::clearSoundCallback()
{
      if (ScummSoundThread)
      {
            Signal((Task *) ScummSoundThread, SIGBREAKF_CTRL_C);
            ScummSoundThread = NULL;
            /* Wait for thread to finish */
            WaitPort(ThreadPort);
      }
}

void OSystem_MorphOS::initSize(uint w, uint h)
{
      if (ScummBuffer)
      {
            FreeVec(ScummBuffer);
            ScummBuffer = NULL;
      }
      if (DirtyBlocks)
      {
            FreeVec(DirtyBlocks);

            for (int b = 0; b < BLOCKS_X*BLOCKS_Y; b++)
                  FreeVec(BlockColors[b]);
            FreeVec(BlockColors);
            DirtyBlocks = NULL;
      }

      /*
       * Allocate image buffer
       */
      ScummBuffer = AllocVec(w*h, MEMF_CLEAR);
      
      if (ScummBuffer == NULL)
      {
            puts("Couldn't allocate image buffer");
            exit(1);
      }

      OvlSavedBuffer = AllocVec(w*h, MEMF_CLEAR);
      
      if (OvlSavedBuffer == NULL)
      {
            FreeVec(ScummBuffer);
            puts("Couldn't allocate overlay backup image buffer");
            exit(1);
      }

      memset(ScummColors, 0, 256*sizeof (ULONG));

      ScummBufferWidth = w;
      ScummBufferHeight = h;

      DirtyBlocks = (bool *) AllocVec(BLOCKS_X*BLOCKS_Y*sizeof (bool), MEMF_CLEAR);
      if (DirtyBlocks)
      {
            BlockColors = (UWORD **) AllocVec(BLOCKS_X*BLOCKS_Y*sizeof (UWORD *), MEMF_CLEAR);
            if (BlockColors)
            {
                  int b;

                  for (b = 0; b < BLOCKS_X*BLOCKS_Y; b++)
                  {
                        BlockColors[b] = (UWORD *) AllocVec(256*sizeof (UWORD), MEMF_CLEAR);
                        if (BlockColors[b] == NULL)
                              break;
                        BlockColors[b][0] = BLOCKSIZE_X*BLOCKSIZE_Y;
                  }

                  if (b < BLOCKS_X*BLOCKS_Y)
                  {
                        for (--b; b >= 0; --b)
                              FreeVec(BlockColors[b]);
                        FreeVec(BlockColors);
                        BlockColors = NULL;
                  }
            }

            if (!BlockColors)
            {
                  FreeVec(DirtyBlocks);
                  DirtyBlocks = NULL;
            }
      }

      CreateScreen(CSDSPTYPE_KEEP);
}

int16 OSystem_MorphOS::getWidth()
{
      return ScummScrWidth;
}

int16 OSystem_MorphOS::getHeight()
{
      return ScummScrHeight;
}

void OSystem_MorphOS::showOverlay()
{
      UndrawMouse();
      memcpy(OvlSavedBuffer, ScummBuffer, ScummBufferWidth*ScummBufferHeight);
      clearOverlay();
      for (int c = 0; c < 256; c++)
      {
            ULONG r, g, b;
            r = ScummColors[c] >> 16;
            g = (ScummColors[c] >> 8) & 0xff;
            b = ScummColors[c] & 0xff;
            SetRGB32CM(OvlCMap, c, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b));
      }
}

void OSystem_MorphOS::hideOverlay()
{
      copyRectToScreen((byte *) OvlSavedBuffer, ScummBufferWidth, 0, 0, ScummBufferWidth, ScummBufferHeight);
}

void OSystem_MorphOS::clearOverlay()
{
      AUTO_LOCK

      UBYTE *src = (UBYTE *) ScummBuffer;
      UBYTE *dest = (UBYTE *) OvlBitMap;
      copyRectToScreen((byte *) OvlSavedBuffer, ScummBufferWidth, 0, 0, ScummBufferWidth, ScummBufferHeight);
      for (int y = 0; y < ScummBufferHeight; y++)
            for (int x = 0; x < ScummBufferWidth; x++)
            {
                  *dest++ = ScummColors[*src] >> 16;
                  *dest++ = (ScummColors[*src] >> 8) & 0xff;
                  *dest++ = ScummColors[*src++] & 0xff;
            }
}

void OSystem_MorphOS::grabOverlay(int16 *buf, int pitch)
{
      int h = ScummBufferHeight;
      int x;
      UBYTE *src = (UBYTE *) OvlBitMap;

      do
      {
            for (x = 0; x < pitch; x++)
            {
                  *buf++ = (src[0]*31/255 << 11) | (src[1]*63/255 << 5) | src[2]*31/255;
                  src += 3;
            }
            src += (ScummBufferWidth-pitch)*3;
      } while (--h);
}

void OSystem_MorphOS::copyRectToOverlay(const int16 *ovl, int pitch, int x, int y, int w, int h)
{
      int x1, y1;
      UBYTE *dest;
      UBYTE *bmap, *bmap_dest;
      LONG last_col[2] = { -1, -1 };
      LONG last_pen[2] = { -1, -1 };

      if (w > pitch) w = pitch;
      bmap = (UBYTE*) AllocVec(w*h, MEMF_ANY);
      if (bmap)
      {
            bmap_dest = bmap;
            dest = ((UBYTE *) OvlBitMap)+y*ScummBufferWidth*3+x*3;
            for (y1 = 0; y1 < h; y1++)
            {
                  for (x1 = 0; x1 < w; x1++)
                  {
                        uint8 r, g, b;
                        int16 col;

                        col = *ovl++;
                        colorToRGB(col, r, g, b);
                        *dest++ = r;
                        *dest++ = g;
                        *dest++ = b;
                        if (col == last_col[0])
                              *bmap_dest++ = last_pen[0];
                        else if (col == last_col[1])
                              *bmap_dest++ = last_pen[1];
                        else
                        {
                              last_col[1] = last_col[0];
                              last_pen[1] = last_pen[0];
                              last_col[0] = col;
                              last_pen[0] = FindColor(OvlCMap, CVT8TO32(r), CVT8TO32(g), CVT8TO32(b), -1);
                              *bmap_dest++ = last_pen[0];
                        }
                  }
                  dest += (ScummBufferWidth-w)*3;
                  ovl += pitch-w;
            }
            copyRectToScreen(bmap, w, x, y, w, h);
            FreeVec(bmap);
      }
}


Generated by  Doxygen 1.6.0   Back to index