scummvm/backends/morphos/morphos.cpp
Eugene Sandulenko 14ec3f45fa - Heavily modified patch #1214784: "Disable overlay scaling"
- Eriktorbjorn's patch from same tracker item for scaling sword1/2 cutscenes
  is applied as is. It lacks resolution switch on-the-fly.
- GUI widgets are repositioned on the fly and use most space, even aspect
  ratio corrected screen is used without scaling
- Heavy tesing is required, but works for me in all cases except for bug
  #1483272: "GUI: SCUMM pause dialog breaks upon scaler switch" which needs more
  work.
- I probavly broke some backend or two

svn-id: r22505
2006-05-17 23:52:45 +00:00

1646 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* MorphOS interface
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "base/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);
}
}