scummvm/backends/morphos/morphos.cpp
Eugene Sandulenko 7bb53f3bf1 Patch #1013937 "OSystem layer with bigger resolution". Now you will always
get at least 640x400 window. And finally we have means of implementing
nice looking GUI.

Also updated all backends. If your backend has ability to run with 640x400 or
640x480 resolution then read patch tracker item to find out details. Other
port maintainers shouldn't worry, as this patch doesn't affect them, they
still get their 320x200.

svn-id: r17055
2005-03-09 23:07:32 +00:00

1644 lines
38 KiB
C++

/* 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$
*
*/
#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, int overlayScale)
{
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);
}
}