/* ScummVM - Scumm Interpreter * Copyright (C) 2001 Ludvig Strigeus * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ #include "stdafx.h" #include "scumm.h" void Scumm::getGraphicsPerformance() { int i; for (i=10; i!=0; i--) { initScreens(0, 0, 320, 200); } _vars[VAR_PERFORMANCE_1] = 0;//_scummTimer; for (i=10; i!=0; i--) { setDirtyRange(0, 0, 200); drawDirtyScreenParts(); } _vars[VAR_PERFORMANCE_2] = 0;//_scummTimer; initScreens(0, 16, 320, 144); } void Scumm::initScreens(int a, int b, int w, int h) { int i; for (i=0; i<3; i++) { nukeResource(rtBuffer, i+1); nukeResource(rtBuffer, i+5); } if (!getResourceAddress(rtBuffer,4)) { initVirtScreen(3, 80, 13, false, false); } initVirtScreen(0, b, h-b, true, true); initVirtScreen(1, 0, b, false, false); initVirtScreen(2, h, 200-h, false, false); _screenB = b; _screenH = h; } void Scumm::initVirtScreen(int slot, int top, int height, bool twobufs, bool fourextra) { VirtScreen *vs = &virtscr[slot]; int size; assert(height>=0); assert(slot>=0 && slot<4); vs->number = slot; vs->unk1 = 0; vs->width = 320; vs->topline = top; vs->height = height; vs->alloctwobuffers = twobufs; vs->scrollable = fourextra; vs->xstart = 0; size = vs->width * vs->height; vs->size = size; if (vs->scrollable) size += 320*4; createResource(rtBuffer, slot+1, size); if (twobufs) { createResource(rtBuffer, slot+5, size); } if (slot != 3) { setDirtyRange(slot, 0, height); } } void Scumm::setDirtyRange(int slot, int top, int bottom) { int i; VirtScreen *vs = &virtscr[slot]; for (i=0; i<40; i++) { vs->tdirty[i] = top; vs->bdirty[i] = bottom; } } /* power of 2 */ #define NUM_SHAKE_POSITIONS 8 static const int8 shake_positions[NUM_SHAKE_POSITIONS] = { 0,1*2,2*2,1*2,0*2,2*2,3*2,1*2 }; void Scumm::drawDirtyScreenParts() { int i; VirtScreen *vs; updateDirtyScreen(2); if (_features & GF_OLD256) updateDirtyScreen(1); #if defined(FULL_THROTTLE) if (camera._last.x==camera._cur.x && camera._last.y==camera._cur.y) { #else if (camera._last.x == camera._cur.x) { #endif updateDirtyScreen(0); } else { vs = &virtscr[0]; blitToScreen(this, getResourceAddress(rtBuffer, 1) + _screenStartStrip*8, 0, vs->topline, 320, vs->height); for (i = 0; i<40; i++) { vs->tdirty[i] = (byte)vs->height; vs->bdirty[i] = 0; } } /* Handle shaking */ if (_shakeEnabled) { _shakeFrame = (_shakeFrame + 1) & (NUM_SHAKE_POSITIONS-1); setShakePos(this,shake_positions[_shakeFrame]); } } void Scumm::redrawLines(int from, int to) { VirtScreen *vs = virtscr; int i,j; if (to<=from) return; for(i=0; i!=ARRAYSIZE(virtscr); i++,vs++) { if (to > vs->topline && from < vs->topline + vs->height) { int min = from - vs->topline; int max = to - vs->topline; if (min < 0) min = 0; if (max > vs->height) max = vs->height; for (j=0; j!=40; j++) { vs->tdirty[j] = min; vs->bdirty[j] = max; } gdi.updateDirtyScreen(vs); } } } void Scumm::updateDirtyScreen(int slot) { gdi.updateDirtyScreen(&virtscr[slot]); } void Gdi::updateDirtyScreen(VirtScreen *vs) { int i; int start,w,top,bottom; if (vs->height==0) return; _readOffs = 0; if (vs->scrollable) _readOffs = vs->xstart; w = 8; start = 0; for (i=0; i<40; i++) { bottom = vs->bdirty[i]; if (bottom) { top = vs->tdirty[i]; vs->tdirty[i] = (byte)vs->height; vs->bdirty[i] = 0; if (i!=39 && vs->bdirty[i+1] == (byte)bottom && vs->tdirty[i+1] == (byte)top) { w += 8; continue; } drawStripToScreen(vs, start, w, top, bottom); w = 8; } start = i+1; } } void Gdi::drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b) { byte *ptr; if (b <= t) return; if (t > vs->height) t = 0; if (b > vs->height) b = vs->height; ptr = _vm->getResourceAddress(rtBuffer, vs->number+1) + (t*40+x)*8 + _readOffs; blitToScreen(_vm, ptr, x*8, vs->topline+t, w, b-t); } void blit(byte *dst, byte *src, int w, int h) { assert(h>0); do { memcpy(dst, src, w); dst += 320; src += 320; } while (--h); } void Scumm::setCursor(int cursor) { warning("setCursor(%d)", cursor); } #if defined(FULL_THROTTLE) void Scumm::setCameraAt(int pos_x, int pos_y) { CameraData *cd = &camera; Point old; old = cd->_cur; cd->_cur.x = pos_x; cd->_cur.y = pos_y; clampCameraPos(&cd->_cur); cd->_dest = cd->_cur; assert(cd->_cur.x>=160 && cd->_cur.y>=100); if ((cd->_cur.x != old.x || cd->_cur.y != old.y) && _vars[VAR_SCROLL_SCRIPT]) { _vars[VAR_CAMERA_POS_X] = cd->_cur.x; _vars[VAR_CAMERA_POS_Y] = cd->_cur.y; runScript(_vars[VAR_SCROLL_SCRIPT], 0, 0, 0); } } #else void Scumm::setCameraAt(int pos_x, int pos_y) { int t; CameraData *cd = &camera; if (cd->_mode!=CM_FOLLOW_ACTOR || abs(pos_x - cd->_cur.x) > 160) { cd->_cur.x = pos_x; } cd->_dest.x = pos_x; t = _vars[VAR_CAMERA_MIN_X]; if (cd->_cur.x < t) cd->_cur.x = t; t = _vars[VAR_CAMERA_MAX_X]; if (cd->_cur.x > t) cd->_cur.x = t; if (_vars[VAR_SCROLL_SCRIPT]) { _vars[VAR_CAMERA_POS_X] = cd->_cur.x; runScript(_vars[VAR_SCROLL_SCRIPT], 0, 0, 0); } if (cd->_cur.x != cd->_last.x && charset._hasMask) stopTalk(); } #endif #if defined(FULL_THROTTLE) void Scumm::setCameraFollows(Actor *a) { CameraData *cd = &camera; byte oldfollow = cd->_follows; int ax,ay; cd->_follows = a->number; if (a->room != _currentRoom) { startScene(a->room, 0, 0); } ax = abs(a->x - cd->_cur.x); ay = abs(a->y - cd->_cur.y); if ( ax > _vars[VAR_CAMERA_THRESHOLD_X] || ay > _vars[VAR_CAMERA_THRESHOLD_Y] || ax > 160 || ay > 100) { setCameraAt(a->x, a->y); } if (a->number != oldfollow) runHook(0); } #else void Scumm::setCameraFollows(Actor *a) { int t,i; CameraData *cd = &camera; cd->_mode = CM_FOLLOW_ACTOR; cd->_follows = a->number; if (a->room != _currentRoom) { startScene(a->room, 0, 0); cd->_mode = CM_FOLLOW_ACTOR; cd->_cur.x = a->x; setCameraAt(cd->_cur.x, 0); } t = (a->x >> 3); if (t-_screenStartStrip < cd->_leftTrigger || t-_screenStartStrip > cd->_rightTrigger) setCameraAt(a->x, 0); for (i=1,a=getFirstActor(); ++a,iroom==_currentRoom) a->needRedraw = true; } runHook(0); } #endif void Scumm::initBGBuffers() { byte *ptr; int size, itemsize, i; byte *room; room = getResourceAddress(rtRoom, _roomResource); if(_features & GF_SMALL_HEADER) { gdi._numZBuffer = 1; } else { ptr = findResource(MKID('RMIH'), findResource(MKID('RMIM'), room)); gdi._numZBuffer = READ_LE_UINT16(ptr+8) + 1; } assert(gdi._numZBuffer>=1 && gdi._numZBuffer<=4); itemsize = (_scrHeight + 4) * 40; size = itemsize * gdi._numZBuffer; createResource(rtBuffer, 9, size); for (i=0; i<4; i++) gdi._imgBufOffs[i] = i*itemsize; } void Scumm::setPaletteFromPtr(byte *ptr) { int i, r, g, b; byte *dest; int numcolor; if( _features & GF_SMALL_HEADER ) { if (_features & GF_OLD256) numcolor = 256; else numcolor = READ_LE_UINT16(ptr+6) / 3; ptr+=8; } else { numcolor = getResourceDataSize(ptr) / 3; } checkRange(256, 0, numcolor, "Too many colors (%d) in Palette"); dest = _currentPalette; for (i=0; i min) _palDirtyMin = min; if (_palDirtyMax < max) _palDirtyMax = max; } void Scumm::initCycl(byte *ptr) { int j; ColorCycle *cycl; memset(_colorCycle, 0, sizeof(_colorCycle)); while ((j=*ptr++) != 0) { if (j<1 || j>16) { error("Invalid color cycle index %d", j); } cycl = &_colorCycle[j-1]; ptr += 2; cycl->counter = 0; cycl->delay = 16384 / READ_BE_UINT16_UNALIGNED(ptr); ptr += 2; cycl->flags = READ_BE_UINT16_UNALIGNED(ptr); ptr += 2; cycl->start = *ptr++; cycl->end = *ptr++; } } void Scumm::stopCycle(int i) { ColorCycle *cycl; checkRange(16, 0, i, "Stop Cycle %d Out Of Range"); if (i!=0) { _colorCycle[i-1].delay = 0; return; } for (i=0,cycl=_colorCycle; i<16; i++,cycl++) cycl->delay = 0; } void Scumm::cyclePalette() { ColorCycle *cycl; int valueToAdd; int i, num; byte *start, *end; byte tmp[3]; valueToAdd = _vars[VAR_TIMER]; if (valueToAdd < _vars[VAR_TIMER_NEXT]) valueToAdd = _vars[VAR_TIMER_NEXT]; for (i=0,cycl=_colorCycle; i<16; i++,cycl++) { if (cycl->delay && (cycl->counter+=valueToAdd) >= cycl->delay) { do { cycl->counter -= cycl->delay; } while (cycl->delay <= cycl->counter); setDirtyColors(cycl->start, cycl->end); moveMemInPalRes(cycl->start, cycl->end, cycl->flags&2); start = &_currentPalette[cycl->start*3]; end = &_currentPalette[cycl->end*3]; num = cycl->end - cycl->start; if (!(cycl->flags&2)) { memmove(tmp, end, 3); memmove(start+3, start, num*3); memmove(start, tmp, 3); } else { memmove(tmp, start, 3); memmove(start, start+3, num*3); memmove(end, tmp, 3); } } } } void Scumm::moveMemInPalRes(int start, int end, byte direction) { byte *startptr, *endptr; byte *startptr2, *endptr2; int num; byte tmp[6]; byte tmp2[6]; if (!_palManipCounter) return; startptr = getResourceAddress(rtTemp, 4) + start * 6; endptr = getResourceAddress(rtTemp, 4) + end * 6; startptr2 = getResourceAddress(rtTemp, 5) + start * 6; endptr2 = getResourceAddress(rtTemp, 5) + end * 6; num = end - start; if (!direction) { memmove(tmp, endptr, 6); memmove(startptr+6, startptr, num*6); memmove(startptr, tmp, 6); memmove(tmp2, endptr2, 6); memmove(startptr2+6, startptr2, num*6); memmove(startptr2, tmp2, 6); } else { memmove(tmp, startptr, 6); memmove(startptr, startptr+6, num*6); memmove(endptr, tmp, 6); memmove(tmp2, startptr2, 6); memmove(startptr2, startptr2+6, num*6); memmove(endptr2, tmp2, 6); } } void Scumm::unkVirtScreen4(int a) { VirtScreen *vs; setDirtyRange(0, 0, 0); #if !defined(FULL_THROTTLE) camera._last.x = camera._cur.x; #endif if (!_screenEffectFlag) return; _screenEffectFlag = false; if (a==0) return; vs = &virtscr[0]; gdi._backbuff_ptr = getResourceAddress(rtBuffer, 1) + vs->xstart; memset(gdi._backbuff_ptr, 0, vs->size); switch(a) { case 1: case 2: case 3: unkScreenEffect7(a-1); break; case 128: unkScreenEffect6(); break; case 129: // setDirtyRange(0, 0, vs->height); // updateDirtyScreen(0); /* XXX: EGA_proc4(0); */ warning("EGA_proc4"); /* FIXME */ break; case 134: unkScreenEffect5(0); break; case 135: unkScreenEffect5(1); break; default: error("unkVirtScreen4: default case %d", a); } } void Scumm::redrawBGAreas() { int i; int val; CameraData *cd = &camera; int diff; // Full throttle hack #if !defined(FULL_THROTTLE) if (cd->_cur.x!=cd->_last.x && charset._hasMask) stopTalk(); #endif val = 0; if (!_fullRedraw && _BgNeedsRedraw) { for (i=0; i!=40; i++) { if (gfxUsageBits[_screenStartStrip + i]&0x80000000) { redrawBGStrip(i, 1); } } } #if defined(FULL_THROTTLE) diff = (cd->_cur.x>>3) - (cd->_last.x>>3); if (_fullRedraw==0 && diff==1) { val = 2; redrawBGStrip(39, 1); } else if (_fullRedraw==0 && diff==-1) { val = 1; redrawBGStrip(0, 1); } else if (_fullRedraw!=0 || diff!=0) { _BgNeedsRedraw = false; _fullRedraw = false; redrawBGStrip(0, 40); } #else if (_fullRedraw==0 && cd->_cur.x - cd->_last.x == 8) { val = 2; redrawBGStrip(39, 1); } else if (_fullRedraw==0 && cd->_cur.x - cd->_last.x == -8) { val = 1; redrawBGStrip(0, 1); } else if (_fullRedraw!=0 || cd->_cur.x != cd->_last.x) { _BgNeedsRedraw = false; redrawBGStrip(0, 40); } #endif drawRoomObjects(val); _BgNeedsRedraw = false; } const uint32 zplane_tags[] = { MKID('ZP00'), MKID('ZP01'), MKID('ZP02'), MKID('ZP03'), MKID('ZP04') }; void Gdi::drawBitmap(byte *ptr, VirtScreen *vs, int x, int y, int h, int stripnr, int numstrip, byte flag) { byte *smap_ptr,*where_draw_ptr; int i; byte *zplane_list[4]; int bottom; byte twobufs; int numzbuf; int sx; CHECK_HEAP if(_vm->_features & GF_SMALL_HEADER) smap_ptr = _smap_ptr = ptr; else smap_ptr = findResource(MKID('SMAP'), ptr); assert(smap_ptr); numzbuf = _disable_zbuffer ? 0 : _numZBuffer; for(i=1; i vs->height) { error("Gdi::drawBitmap, strip drawn to %d below window bottom %d", bottom, vs->height); } twobufs = vs->alloctwobuffers; _vertStripNextInc = h * 320 - 1; _numLinesToProcess = h; do { if(_vm->_features & GF_SMALL_HEADER) _smap_ptr = smap_ptr + READ_LE_UINT32(smap_ptr + stripnr*4 + 4); else _smap_ptr = smap_ptr + READ_LE_UINT32(smap_ptr + stripnr*4 + 8); CHECK_HEAP sx = x; if (vs->scrollable) sx -= vs->xstart>>3; if ((uint)sx >= 40) return; if (y < vs->tdirty[sx]) vs->tdirty[sx]=y; if (bottom > vs->bdirty[sx]) vs->bdirty[sx] = bottom; _backbuff_ptr = _vm->getResourceAddress(rtBuffer, vs->number+1) + (y*40+x)*8; _bgbak_ptr = _vm->getResourceAddress(rtBuffer, vs->number+5) + (y*40+x)*8; if (!twobufs) { _bgbak_ptr = _backbuff_ptr; } _mask_ptr = _vm->getResourceAddress(rtBuffer, 9) + (y*40+x); where_draw_ptr = _bgbak_ptr; decompressBitmap(); CHECK_HEAP if (twobufs) { _bgbak_ptr = where_draw_ptr; if (_vm->hasCharsetMask(sx<<3, y, (sx+1)<<3, bottom)) { if (flag&dbClear) clear8ColWithMasking(); else draw8ColWithMasking(); } else { if (flag&dbClear) clear8Col(); else blit(_backbuff_ptr, _bgbak_ptr, 8, h); } } CHECK_HEAP if (flag & dbDrawMaskOnBoth) { _z_plane_ptr = zplane_list[1] + READ_LE_UINT16(zplane_list[1] + stripnr*2 + 8); _mask_ptr_dest = _vm->getResourceAddress(rtBuffer, 9) + y*40 + x; if (_useOrDecompress && flag&dbAllowMaskOr) decompressMaskImgOr(); else decompressMaskImg(); } for (i=1; igetResourceAddress(rtBuffer, 9) + y*40 + x + _imgBufOffs[i]; if (_useOrDecompress && flag&dbAllowMaskOr) decompressMaskImgOr(); else decompressMaskImg(); } CHECK_HEAP x++; stripnr++; } while (--numstrip); } void Gdi::decompressBitmap() { const byte decompress_table[] = { 0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x0, }; _useOrDecompress = false; byte code = *_smap_ptr++; assert(_numLinesToProcess); switch(code) { case 1: unkDecode7(); break; case 2: unkDecode8(); /* Ender - Zak256/Indy256 */ break; case 3: unkDecode9(); /* Ender - Zak256/Indy256 */ break; case 4: unkDecode10(); /* Ender - Zak256/Indy256 */ break; case 7: unkDecode11(); /* Ender - Zak256/Indy256 */ break; case 14: case 15: case 16: case 17: case 18: _decomp_shr = code - 10; _decomp_mask = decompress_table[code - 10]; unkDecode6(); break; case 24: case 25: case 26: case 27: case 28: _decomp_shr = code - 20; _decomp_mask = decompress_table[code - 20]; unkDecode5(); break; case 34: case 35: case 36: case 37: case 38: _useOrDecompress = true; _decomp_shr = code - 30; _decomp_mask = decompress_table[code - 30 ]; unkDecode4(); break; case 44: case 45: case 46: case 47: case 48: _useOrDecompress = true; _decomp_shr = code - 40; _decomp_mask = decompress_table[code - 40]; unkDecode2(); break; case 64: case 65: case 66: case 67: case 68: _decomp_shr = code - 60; _decomp_mask = decompress_table[code - 60]; unkDecode1(); break; case 84: case 85: case 86: case 87: case 88: _useOrDecompress = true; _decomp_shr = code - 80; _decomp_mask = decompress_table[code - 80]; unkDecode3(); break; /* New since version 6 */ case 104: case 105: case 106: case 107: case 108: _decomp_shr = code - 100; _decomp_mask = decompress_table[code - 100]; unkDecode1(); break; /* New since version 6 */ case 124: case 125: case 126: case 127: case 128: _useOrDecompress = true; _decomp_shr = code - 120; _decomp_mask = decompress_table[code - 120]; unkDecode3(); break; default: error("Gdi::decompressBitmap: default case %d", code); } } int Scumm::hasCharsetMask(int x, int y, int x2, int y2) { if (!charset._hasMask || y > gdi._mask_bottom || x > gdi._mask_right || y2 < gdi._mask_top || x2 < gdi._mask_left ) return 0; return 1; } void Gdi::draw8ColWithMasking() { int height = _numLinesToProcess; byte *mask = _mask_ptr; byte *dst = _backbuff_ptr; byte *src = _bgbak_ptr; byte maskbits; do { maskbits = *mask; if (maskbits) { if (!(maskbits&0x80)) dst[0] = src[0]; if (!(maskbits&0x40)) dst[1] = src[1]; if (!(maskbits&0x20)) dst[2] = src[2]; if (!(maskbits&0x10)) dst[3] = src[3]; if (!(maskbits&0x08)) dst[4] = src[4]; if (!(maskbits&0x04)) dst[5] = src[5]; if (!(maskbits&0x02)) dst[6] = src[6]; if (!(maskbits&0x01)) dst[7] = src[7]; } else { /* alignment safe */ ((uint32*)dst)[0] = ((uint32*)src)[0]; ((uint32*)dst)[1] = ((uint32*)src)[1]; } src += 320; dst += 320; mask += 40; } while (--height); } void Gdi::clear8ColWithMasking() { int height = _numLinesToProcess; byte *mask = _mask_ptr; byte *dst = _backbuff_ptr; byte maskbits; do { maskbits = *mask; if (!maskbits) { ((uint32*)dst)[1] = ((uint32*)dst)[0] = 0; } else { if (!(maskbits&0x80)) dst[0] = 0; if (!(maskbits&0x40)) dst[1] = 0; if (!(maskbits&0x20)) dst[2] = 0; if (!(maskbits&0x10)) dst[3] = 0; if (!(maskbits&0x08)) dst[4] = 0; if (!(maskbits&0x04)) dst[5] = 0; if (!(maskbits&0x02)) dst[6] = 0; if (!(maskbits&0x01)) dst[7] = 0; } dst += 320; mask += 40; } while (--height); } void Gdi::clear8Col() { int height = _numLinesToProcess; byte *dst = _backbuff_ptr; do { ((uint32*)dst)[1] = ((uint32*)dst)[0] = 0; dst += 320; } while (--height); } void Gdi::decompressMaskImg() { byte *src = _z_plane_ptr; byte *dst = _mask_ptr_dest; int height = _numLinesToProcess; byte b, c; while(1) { b = *src++; if (b&0x80) { b&=0x7F; c = *src++; do { *dst = c; dst += 40; if (!--height) return; } while (--b); } else { do { *dst = *src++; dst += 40; if (!--height) return; } while (--b); } } } void Gdi::decompressMaskImgOr() { byte *src = _z_plane_ptr; byte *dst = _mask_ptr_dest; int height = _numLinesToProcess; byte b, c; while(1) { b = *src++; if (b&0x80) { b&=0x7F; c = *src++; do { *dst |= c; dst += 40; if (!--height) return; } while (--b); } else { do { *dst |= *src++; dst += 40; if (!--height) return; } while (--b); } } } void Scumm::redrawBGStrip(int start, int num) { int s = _screenStartStrip + start; assert(s>=0 && sheight > _scrHeight) { error("Screen Y size %d < Room height %d", _curVirtScreen->height, _scrHeight); } gdi.drawBitmap(getResourceAddress(rtRoom, _roomResource)+_IM00_offs, _curVirtScreen, s, 0, _curVirtScreen->height, s, num, 0); } #define READ_BIT (cl--,bit = bits&1, bits>>=1,bit) #define FILL_BITS if (cl <= 8) { bits |= (*src++ << cl); cl += 8;} void Gdi::unkDecode1() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; uint bits = *src++; byte cl = 8; byte bit; byte incm,reps; _tempNumLines = _numLinesToProcess; do { _currentX = 8; do { FILL_BITS *dst++=color; againPos:; if (!READ_BIT) {} else if (READ_BIT) { incm = (bits&7)-4; cl-=3; bits>>=3; if (!incm) { FILL_BITS reps = bits&0xFF; do { if (!--_currentX) { _currentX = 8; dst += 312; if (!--_tempNumLines) return; } *dst++=color; } while (--reps); bits>>=8; bits |= (*src++)<<(cl-8); goto againPos; } else { color += incm; } } else { FILL_BITS color = bits&_decomp_mask; cl -= _decomp_shr; bits >>= _decomp_shr; } } while (--_currentX); dst += 312; } while (--_tempNumLines); } void Gdi::unkDecode2() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; int8 inc = -1; uint bits = *src++; byte cl = 8; byte bit; _tempNumLines = _numLinesToProcess; do { _currentX = 8; do { FILL_BITS if (color!=_transparency) *dst=color; dst++; if (!READ_BIT) {} else if (!READ_BIT) { FILL_BITS color = bits&_decomp_mask; bits >>= _decomp_shr; cl -= _decomp_shr; inc = -1; } else if (!READ_BIT) { color += inc; } else { inc = -inc; color += inc; } } while (--_currentX); dst += 312; } while (--_tempNumLines); } void Gdi::unkDecode3() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; uint bits = *src++; byte cl = 8; byte bit; byte incm,reps; _tempNumLines = _numLinesToProcess; do { _currentX = 8; do { FILL_BITS if (color!=_transparency) *dst=color; dst++; againPos:; if (!READ_BIT) {} else if (READ_BIT) { incm = (bits&7)-4; cl-=3; bits>>=3; if (incm) { color += incm; } else { FILL_BITS reps = bits&0xFF; if (color==_transparency) { do { if (!--_currentX) { _currentX = 8; dst += 312; if (!--_tempNumLines) return; } dst++; } while (--reps); } else { do { if (!--_currentX) { _currentX = 8; dst += 312; if (!--_tempNumLines) return; } *dst++=color; } while (--reps); } bits>>=8; bits |= (*src++)<<(cl-8); goto againPos; } } else { FILL_BITS color = bits&_decomp_mask; cl -= _decomp_shr; bits >>= _decomp_shr; } } while (--_currentX); dst += 312; } while (--_tempNumLines); } void Gdi::unkDecode4() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; int8 inc = -1; uint bits = *src++; byte cl = 8; byte bit; _currentX = 8; do { _tempNumLines = _numLinesToProcess; do { FILL_BITS if (color!=_transparency) *dst=color; dst+=320; if (!READ_BIT) {} else if (!READ_BIT) { FILL_BITS color = bits&_decomp_mask; bits >>= _decomp_shr; cl -= _decomp_shr; inc = -1; } else if (!READ_BIT) { color += inc; } else { inc = -inc; color += inc; } } while (--_tempNumLines); dst -= _vertStripNextInc; } while (--_currentX); } void Gdi::unkDecode5() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; int8 inc = -1; uint bits = *src++; byte cl = 8; byte bit; _tempNumLines = _numLinesToProcess; do { _currentX = 8; do { FILL_BITS *dst++=color; if (!READ_BIT) {} else if (!READ_BIT) { FILL_BITS color = bits&_decomp_mask; bits >>= _decomp_shr; cl -= _decomp_shr; inc = -1; } else if (!READ_BIT) { color += inc; } else { inc = -inc; color += inc; } } while (--_currentX); dst += 312; } while (--_tempNumLines); } void Gdi::unkDecode6() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; byte color = *src++; int8 inc = -1; uint bits = *src++; byte cl = 8; byte bit; _currentX = 8; do { _tempNumLines = _numLinesToProcess; do { FILL_BITS *dst=color; dst+=320; if (!READ_BIT) {} else if (!READ_BIT) { FILL_BITS color = bits&_decomp_mask; bits >>= _decomp_shr; cl -= _decomp_shr; inc = -1; } else if (!READ_BIT) { color += inc; } else { inc = -inc; color += inc; } } while (--_tempNumLines); dst -= _vertStripNextInc; } while (--_currentX); } void Gdi::unkDecode7() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; int height = _numLinesToProcess; do { /* Endian safe */ #if defined(SCUMM_NEED_ALIGNMENT) memcpy(dst, src, 8); #else ((uint32*)dst)[0] = ((uint32*)src)[0]; ((uint32*)dst)[1] = ((uint32*)src)[1]; #endif dst += 320; src += 8; } while (--height); } /* Ender - Zak256/Indy256 decoders */ #define READ_256BIT \ if ((mask <<= 1) == 256) {buffer = *src++; mask = 1;} \ bits = ((buffer & mask) != 0); #define NEXT_ROW \ dst += 320; \ if (--h == 0) { \ if (!--_currentX) \ return; \ dst -= _vertStripNextInc; \ h = _numLinesToProcess; \ } void Gdi::unkDecode8() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; uint h = _numLinesToProcess; _currentX = 8; for(;;) { uint run = (*src++)+1; byte color = *src++; do { *dst = color; NEXT_ROW } while (--run); } } void Gdi::unkDecode9() { byte *src = _smap_ptr; byte *dst = _bgbak_ptr; unsigned char c, bits, color, run; int x, y, i, z; uint buffer, mask = 128; int h = _numLinesToProcess; x = y = i = z = run = 0; _currentX = 8; for(;;) { c = 0; for (i = 0; i < 4; i++) {READ_256BIT; c+=(bits<>2)) { case 0: color=0; for (i=0; i<4; i++) {READ_256BIT; color+=bits<topline; height = topline + vs->height; if (vs->number==0) { left += _lastXstart - vs->xstart; right += _lastXstart - vs->xstart; } right++; if (left<0) left=0; if (right<0)right=0; if (left>320) return; if (right>320) right=320; if (bottom>=height) bottom=height; updateDirtyRect(vs->number, left, right, top-topline,bottom-topline, 0x40000000); height = (top-topline) * 320 + vs->xstart + left; backbuff = getResourceAddress(rtBuffer, vs->number+1) + height; bgbak = getResourceAddress(rtBuffer, vs->number+5) + height; mask = getResourceAddress(rtBuffer, 9) + top * 40 + (left>>3) + _screenStartStrip; if (vs->number==0) { mask += vs->topline * 216; } height = bottom - top; width = right - left; widthmod = (width >> 2) + 2; if (vs->alloctwobuffers && _currentRoom!=0 /*&& _vars[VAR_V5_DRAWFLAGS]&2*/) { blit(backbuff, bgbak, width, height); if (vs->number==0 && charset._hasMask && height) { do { memset(mask, 0, widthmod); mask += 40; } while (--height); } } else { if (height) { do { memset(backbuff, _bkColor, width); backbuff+=320; } while (--height); } } } void Scumm::updateDirtyRect(int virt, int left, int right, int top, int bottom, uint32 dirtybits) { VirtScreen *vs = &virtscr[virt]; int lp,rp; uint32 *sp; int num; if (top > vs->height || left > vs->width || right < 0 || bottom < 0) return; if (top<0) top=0; if (left<0) left=0; if (bottom > vs->height) bottom = vs->height; if (right > vs->width) right = vs->width; if (virt==0 && dirtybits) { rp = (right >> 3) + _screenStartStrip; lp = (left >> 3) + _screenStartStrip; if (lp<0) lp=0; if (rp >= 200) rp = 200; if (lp <= rp) { num = rp - lp + 1; sp = &gfxUsageBits[lp]; do { *sp++ |= dirtybits; } while (--num); } } setVirtscreenDirty(vs, left, top, right, bottom); } void Scumm::setVirtscreenDirty(VirtScreen *vs, int left, int top, int right, int bottom) { int lp = left >> 3; int rp = right >> 3; if (lp>=40 || rp<0) return; if (lp<0) lp=0; if (rp>=40) rp=39; while (lp<=rp) { if (top < vs->tdirty[lp]) vs->tdirty[lp] = top; if (bottom > vs->bdirty[lp]) vs->bdirty[lp] = bottom; lp++; } } VirtScreen *Scumm::findVirtScreen(int y) { VirtScreen *vs = virtscr; int i; for(i=0; i<3; i++,vs++) { if (y >= vs->topline && y < vs->topline+vs->height) { return _curVirtScreen=vs; } } return _curVirtScreen=NULL; } void Scumm::unkScreenEffect1() { /* XXX: not implemented */ warning("stub unkScreenEffect1()"); } void Scumm::unkScreenEffect2() { /* XXX: not implemented */ warning("stub unkScreenEffect2()"); } void Scumm::unkScreenEffect3() { /* XXX: not implemented */ warning("stub unkScreenEffect3()"); } void Scumm::unkScreenEffect4() { /* XXX: not implemented */ warning("stub unkScreenEffect4()"); } static const int8 screen_eff7_table1[4][16] = { {1,1,-1,1,-1,1,-1,-1, 1,-1,-1,-1,1,1,1,-1}, {0,1,2,1,2,0,2,1, 2,0,2,1,0,0,0,0}, {-2,-1,0,-1,-2,-1,-2,0 -2,-1,-2,0,0,0,0,0}, {0,-1,-2,-1,-2,0,-2,-1 -2,0,-2,-1,0,0,0,0} }; static const byte screen_eff7_table2[4][16] = { {0,0,39,0,39,0,39,24, 0,24,39,24,0,0,0,24}, {0,0,0,0,0,0,0,0, 1,0,1,0,255,0,0,0}, {39,24,39,24,39,24,39,24, 38,24,38,24,255,0,0,0}, {0,24,39,24,39,0,39,24, 38,0,38,24,255,0,0,0} }; static const byte screen_eff7_table3[4] = { 13,25,25,25 }; void Scumm::unkScreenEffect7(int a) { int tab_1[16]; int tab_2[16]; int i,j; int bottom; int l,t,r,b; for (i=0; i<16; i++) { tab_1[i] = screen_eff7_table1[a][i]; j = screen_eff7_table2[a][i]; if (j==24) j = (virtscr[0].height>>3)-1; tab_2[i] = j; } bottom = virtscr[0].height >> 3; for (j=0; j < screen_eff7_table3[a]; j++) { for (i=0; i<4; i++) { l = tab_2[i*4]; t = tab_2[i*4+1]; r = tab_2[i*4+2]; b = tab_2[i*4+3]; if (t==b) { while (l <= r) { if (l>=0 && l<40 && (uint)t<(uint)bottom) { virtscr[0].tdirty[l] = t<<3; virtscr[0].bdirty[l] = (t+1)<<3; } l++; } } else { if (l<0 || l>=40 || b<=t) continue; if (b>bottom) b=bottom; virtscr[0].tdirty[l] = t<<3; virtscr[0].bdirty[l] = (b+1)<<3; } updateDirtyScreen(0); } for (i=0; i<16; i++) tab_2[i] += tab_1[i]; updateScreen(this); waitForTimer(this,30); } } void Scumm::unkScreenEffect6() { /* XXX: not implemented */ warning("stub unkScreenEffect6"); } void Scumm::unkScreenEffect5(int a) { /* XXX: not implemented */ warning("stub unkScreenEffect5(%d)",a); } void Scumm::setShake(int mode) { _shakeEnabled = mode != 0; _shakeFrame = 0; setShakePos(this,0); } void Gdi::clearUpperMask() { memset( _vm->getResourceAddress(rtBuffer, 9), 0, _imgBufOffs[1] - _imgBufOffs[0] ); } #if defined(FULL_THROTTLE) void Scumm::clampCameraPos(Point *pt) { if (pt->x < _vars[VAR_CAMERA_MIN_X]) pt->x = _vars[VAR_CAMERA_MIN_X]; if (pt->x > _vars[VAR_CAMERA_MAX_X]) pt->x = _vars[VAR_CAMERA_MAX_X]; if (pt->y < _vars[VAR_CAMERA_MIN_Y]) pt->y = _vars[VAR_CAMERA_MIN_Y]; if (pt->y > _vars[VAR_CAMERA_MAX_Y]) pt->y = _vars[VAR_CAMERA_MAX_Y]; } void Scumm::moveCamera() { CameraData *cd = &camera; Point old = cd->_cur; Actor *a; if (cd->_follows) { a = derefActorSafe(cd->_follows, "moveCamera"); if (abs(cd->_cur.x - a->x) > _vars[VAR_CAMERA_THRESHOLD_X] || abs(cd->_cur.y - a->y) > _vars[VAR_CAMERA_THRESHOLD_Y]) { cd->_movingToActor = true; if (_vars[VAR_CAMERA_THRESHOLD_X] == 0) cd->_cur.x = a->x; if (_vars[VAR_CAMERA_THRESHOLD_Y] == 0) cd->_cur.y = a->y; clampCameraPos(&cd->_cur); } } else { cd->_movingToActor = false; } if (cd->_movingToActor) { cd->_dest.x = a->x; cd->_dest.y = a->y; } assert(cd->_cur.x>=160 && cd->_cur.y>=100); clampCameraPos(&cd->_dest); if (cd->_cur.x < cd->_dest.x) { cd->_cur.x += _vars[VAR_CAMERA_SPEED_X]; if (cd->_cur.x > cd->_dest.x) cd->_cur.x = cd->_dest.x; } if (cd->_cur.x > cd->_dest.x) { cd->_cur.x -= _vars[VAR_CAMERA_SPEED_X]; if (cd->_cur.x < cd->_dest.x) cd->_cur.x = cd->_dest.x; } if (cd->_cur.y < cd->_dest.y) { cd->_cur.y += _vars[VAR_CAMERA_SPEED_Y]; if (cd->_cur.y > cd->_dest.y) cd->_cur.y = cd->_dest.y; } if (cd->_cur.y > cd->_dest.y) { cd->_cur.y -= _vars[VAR_CAMERA_SPEED_Y]; if (cd->_cur.y < cd->_dest.y) cd->_cur.y = cd->_dest.y; } if (cd->_cur.x == cd->_dest.x && cd->_cur.y == cd->_dest.y) { cd->_movingToActor = false; cd->_accel.x = cd->_accel.y = 0; _vars[VAR_CAMERA_SPEED_X] = _vars[VAR_CAMERA_SPEED_Y] = 0; } else { cd->_accel.x += _vars[VAR_CAMERA_ACCEL_X]; cd->_accel.y += _vars[VAR_CAMERA_ACCEL_Y]; _vars[VAR_CAMERA_SPEED_X] += cd->_accel.x / 100; _vars[VAR_CAMERA_SPEED_Y] += cd->_accel.y / 100; if (_vars[VAR_CAMERA_SPEED_X] < 8) _vars[VAR_CAMERA_SPEED_X] = 8; if (_vars[VAR_CAMERA_SPEED_Y] < 8) _vars[VAR_CAMERA_SPEED_Y] = 8; } cameraMoved(); if (cd->_cur.x != old.x || cd->_cur.y != old.y) { _vars[VAR_CAMERA_POS_X] = cd->_cur.x; _vars[VAR_CAMERA_POS_Y] = cd->_cur.y; runScript(_vars[VAR_SCROLL_SCRIPT], 0, 0, 0); } } #else void Scumm::moveCamera() { CameraData *cd = &camera; int pos = cd->_cur.x; int actorx, t; Actor *a; cd->_cur.x &= 0xFFF8; if (cd->_cur.x < _vars[VAR_CAMERA_MIN_X]) { if (_vars[VAR_CAMERA_FAST_X]) cd->_cur.x = _vars[VAR_CAMERA_MIN_X]; else cd->_cur.x += 8; cameraMoved(); return; } if (cd->_cur.x > _vars[VAR_CAMERA_MAX_X]) { if (_vars[VAR_CAMERA_FAST_X]) cd->_cur.x = _vars[VAR_CAMERA_MAX_X]; else cd->_cur.x-=8; cameraMoved(); return; } if (cd->_mode==CM_FOLLOW_ACTOR) { a = derefActorSafe(cd->_follows, "moveCamera"); actorx = a->x; t = (actorx>>3) - _screenStartStrip; if (t < cd->_leftTrigger || t > cd->_rightTrigger) { if (_vars[VAR_CAMERA_FAST_X]) { if (t > 35) cd->_dest.x = actorx + 80; if (t < 5) cd->_dest.x = actorx - 80; } else cd->_movingToActor = 1; } } if (cd->_movingToActor) { a = derefActorSafe(cd->_follows, "moveCamera(2)"); cd->_dest.x = a->x; } if (cd->_dest.x < _vars[VAR_CAMERA_MIN_X]) cd->_dest.x = _vars[VAR_CAMERA_MIN_X]; if (cd->_dest.x > _vars[VAR_CAMERA_MAX_X]) cd->_dest.x = _vars[VAR_CAMERA_MAX_X]; if (_vars[VAR_CAMERA_FAST_X]) { cd->_cur.x = cd->_dest.x; } else { if (cd->_cur.x < cd->_dest.x) cd->_cur.x+=8; if (cd->_cur.x > cd->_dest.x) cd->_cur.x-=8; } /* a is set a bit above */ if (cd->_movingToActor && cd->_cur.x>>3 == a->x>>3) { cd->_movingToActor = 0; } cameraMoved(); if (pos != cd->_cur.x && _vars[VAR_SCROLL_SCRIPT]) { _vars[VAR_CAMERA_POS_X] = cd->_cur.x; runScript(_vars[VAR_SCROLL_SCRIPT], 0, 0, 0); } } #endif #if defined(FULL_THROTTLE) void Scumm::cameraMoved() { CameraData *cd = &camera; assert(cd->_cur.x>=160 && cd->_cur.y>=100); _screenStartStrip = (cd->_cur.x-160) >> 3; _screenEndStrip = _screenStartStrip + 39; virtscr[0].xstart = _screenStartStrip << 3; _screenLeft = cd->_cur.x - 160; _screenTop = cd->_cur.y - 100; } #else void Scumm::cameraMoved() { CameraData *cd = &camera; if (cd->_cur.x < 160) { cd->_cur.x = 160; } else if (cd->_cur.x + 160 >= _scrWidth) { cd->_cur.x = _scrWidth-160; } _screenStartStrip = (cd->_cur.x >> 3) - 20; _screenEndStrip = _screenStartStrip + 39; virtscr[0].xstart = _screenStartStrip << 3; } #endif #if defined(FULL_THROTTLE) void Scumm::panCameraTo(int x, int y) { CameraData *cd = &camera; cd->_follows = 0; cd->_dest.x = x; cd->_dest.y = y; } #else void Scumm::panCameraTo(int x, int y) { CameraData *cd = &camera; cd->_dest.x = x; cd->_mode = CM_PANNING; cd->_movingToActor = 0; } #endif void Scumm::actorFollowCamera(int act) { #if !defined(FULL_THROTTLE) int old; CameraData *cd = &camera; /* mi1 compatibilty */ if (act==0) { cd->_mode = CM_NORMAL; cd->_follows = 0; cd->_movingToActor = 0; return; } old = cd->_follows; setCameraFollows(derefActorSafe(act, "actorFollowCamera")); if (cd->_follows != old) runHook(0); cd->_movingToActor = 0; #endif } void Scumm::setCameraAtEx(int at) { #if !defined(FULL_THROTTLE) CameraData *cd = &camera; cd->_mode = CM_NORMAL; cd->_cur.x = at; setCameraAt(at, 0); cd->_movingToActor = 0; #endif } void Scumm::palManipulate() { byte *srcptr, *destptr; byte *pal; int i,j; if (!_palManipCounter) return; srcptr = getResourceAddress(rtTemp, 4) + _palManipStart*6; destptr = getResourceAddress(rtTemp, 5) + _palManipStart*6; pal = _currentPalette + _palManipStart * 3; i = _palManipStart; while (i < _palManipEnd) { j = (*((uint16*)srcptr) += *(uint16*)destptr ); *pal++ = j>>8; srcptr += 2; destptr += 2; j = (*((uint16*)srcptr) += *(uint16*)destptr ); *pal++ = j>>8; srcptr += 2; destptr += 2; j = (*((uint16*)srcptr) += *(uint16*)destptr ); *pal++ = j>>8; srcptr += 2; destptr += 2; i++; } setDirtyColors(_palManipStart, _palManipEnd); if (!--_palManipCounter) { nukeResource(rtTemp, 4); nukeResource(rtTemp, 5); } } void Scumm::swapPalColors(int a, int b) { byte *ap,*bp; byte t; if ((uint)a>=256 || (uint)b>=256) error("swapPalColors: invalid values, %d, %d", a, b); ap = &_currentPalette[a*3]; bp = &_currentPalette[b*3]; t=ap[0]; ap[0]=bp[0]; bp[0]=t; t=ap[1]; ap[1]=bp[1]; bp[1]=t; t=ap[2]; ap[2]=bp[2]; bp[2]=t; setDirtyColors(a,b); } void Scumm::screenEffect(int effect) { switch(effect) { case 1: case 2: case 3: unkScreenEffect7(effect-1); break; case 128: unkScreenEffect6(); break; case 130: unkScreenEffect1(); break; case 131: unkScreenEffect2(); break; case 132: unkScreenEffect3(); break; case 133: unkScreenEffect4(); break; case 134: unkScreenEffect5(0); break; case 135: unkScreenEffect5(1); break; case 129: break; default: warning("Unknown screen effect, %d", effect); } _screenEffectFlag = true; } void Scumm::resetActorBgs() { Actor *a; int i; uint32 onlyActorFlags,bitpos; for(i=0; i<40; i++) { onlyActorFlags = (gfxUsageBits[_screenStartStrip + i]&=0x3FFFFFFF); a = getFirstActor(); bitpos = 1; while (onlyActorFlags) { if(onlyActorFlags&1 && a->top!=0xFF && a->needBgReset) { gfxUsageBits[_screenStartStrip + i] ^= bitpos; gdi.resetBackground(a->top, a->bottom, i); } bitpos<<=1; onlyActorFlags>>=1; a++; } } for(i=1,a=getFirstActor(); ++a,ineedBgReset = false; } } void Gdi::resetBackground(byte top, byte bottom, int strip) { VirtScreen *vs = &_vm->virtscr[0]; int offs; if (top < vs->tdirty[strip]) vs->tdirty[strip] = top; if (bottom > vs->bdirty[strip]) vs->bdirty[strip] = bottom; offs = (top * 40 + _vm->_screenStartStrip + strip); _mask_ptr = _vm->getResourceAddress(rtBuffer, 9) + offs; _bgbak_ptr = _vm->getResourceAddress(rtBuffer, 5) + (offs<<3); _backbuff_ptr = _vm->getResourceAddress(rtBuffer, 1) + (offs<<3); _numLinesToProcess = bottom - top; if (_numLinesToProcess) { if (1/*_vm->_vars[VAR_V5_DRAWFLAGS]&2*/) { if(_vm->hasCharsetMask(strip<<3, top, (strip+1)<<3, bottom)) draw8ColWithMasking(); else blit(_backbuff_ptr, _bgbak_ptr, 8, _numLinesToProcess); } else { clear8Col(); } } } void Scumm::setPalColor(int index, int r, int g, int b) { _currentPalette[index*3+0] = r; _currentPalette[index*3+1] = g; _currentPalette[index*3+2] = b; setDirtyColors(index,index); } void Scumm::drawMouse() { /* TODO: handle shake here */ if (_cursorAnimate) { if (!(_cursorAnimateIndex&0x3)) decompressDefaultCursor((_cursorAnimateIndex>>2)&3); _cursorAnimateIndex++; } ::drawMouse(this, mouse.x - _cursorHotspotX, mouse.y - _cursorHotspotY, _cursorWidth, _cursorHeight, _grabbedCursor, gdi._cursorActive>0 ); } void Scumm::setCursorHotspot2(int x,int y) { _cursorHotspotX = x; _cursorHotspotY = y; } byte Scumm::isMaskActiveAt(int l, int t, int r, int b, byte *mem) { int w,h,i; if(_features & GF_SMALL_HEADER) /* FIXME */ return false; l>>=3; if (l<0) l = 0; if (t<0) t = 0; r>>=3; if (r>39) r=39; mem += l + t*40; w = r-l; h = b-t+1; do { for(i=0; i<=w; i++) if (mem[i]) return true; mem += 40; } while (--h); return false; } void Scumm::setPalette(int palindex) { byte *pals; _curPalIndex = palindex; pals = getPalettePtr(); if (pals==NULL) error("invalid palette %d", palindex); setPaletteFromPtr(pals); } byte *Scumm::findPalInPals(byte *pal, int index) { byte *offs; uint32 size; pal = findResource(MKID('WRAP'), pal); if (pal==NULL) return NULL; offs = findResourceData(MKID('OFFS'),pal); if (offs==NULL) return NULL; size = getResourceDataSize(offs) >> 2; if ((uint32)index >= (uint32)size) return NULL; return offs + READ_LE_UINT32(offs + index * sizeof(uint32)); } byte *Scumm::getPalettePtr() { byte *cptr; cptr = getResourceAddress(rtRoom, _roomResource); if (_CLUT_offs) { cptr += _CLUT_offs; } else { cptr = findPalInPals(cptr + _PALS_offs, _curPalIndex); } return cptr; } void Scumm::darkenPalette(int a, int b, int c, int d, int e) { byte *cptr, *cur; int num; int color; cptr = getPalettePtr() + a*3; cur = _currentPalette + a*3; if (a <= b) { num = b - a + 1; do { color = *cptr++; if (c != 0xFF) color = color * c / 0xFF; if(color>255) color = 255; *cur++=color; color = *cptr++; if (d != 0xFF) color = color * d / 0xFF; if(color>255) color = 255; *cur++=color; color = *cptr++; if (e != 0xFF) color = color * e / 0xFF; if(color>255) color = 255; *cur++=color; } while (--num); } setDirtyColors(a,b); } void Scumm::grabCursor(int x, int y, int w, int h) { VirtScreen *vs = findVirtScreen(y); if (vs==NULL) { warning("grabCursor: invalid Y %d", y); return; } grabCursor( getResourceAddress(rtBuffer, vs->number+1) + (y-vs->topline)*320 + x, w,h); } void Scumm::decompressBomp(byte *dst, byte *src, int w, int h) { int len,num; byte code,color; src += 8; do { len = w; src += 2; while (len) { code = *src++; num = (code>>1)+1; if (num>len) num=len; len -= num; if (code&1) { color = *src++; do *dst++ = color; while (--num); } else { do *dst++ = *src++; while (--num); } } } while (--h); } void Scumm::grabCursor(byte *ptr, int width, int height) { uint size; byte *dst; size = width * height; if (size > sizeof(_grabbedCursor)) error("grabCursor: grabbed cursor too big"); _cursorWidth = width; _cursorHeight = height; _cursorAnimate = 0; dst = _grabbedCursor; for(;height;height--) { memcpy(dst, ptr, width); dst += width; ptr += 320; } } void Scumm::useIm01Cursor(byte *im, int w, int h) { VirtScreen *vs = &virtscr[0]; w<<=3; h<<=3; drawBox(0,0,w-1,h-1,0xFF); vs->alloctwobuffers = false; gdi._disable_zbuffer = true; gdi.drawBitmap(im, vs, _screenStartStrip, 0, h, 0, w>>3, 0); vs->alloctwobuffers = true; gdi._disable_zbuffer = false; grabCursor(getResourceAddress(rtBuffer, 1) + vs->xstart, w, h); blit(getResourceAddress(rtBuffer, 1) + vs->xstart, getResourceAddress(rtBuffer, 5) + vs->xstart, w, h); } void Scumm::useBompCursor(byte *im, int width, int height) { uint size; width<<=3; height<<=3; size = width * height; if (size > sizeof(_grabbedCursor)) error("useBompCursor: cursor too big"); _cursorWidth = width; _cursorHeight = height; _cursorAnimate = 0; decompressBomp(_grabbedCursor, im+10, width, height); } static const byte default_cursor_colors[4] = { 15,15,7,8 }; void Scumm::decompressDefaultCursor(int index) { int i; byte color; memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); _cursorWidth = 16; _cursorHeight = 16; _cursorHotspotX = 8; _cursorHotspotY = 7; color = default_cursor_colors[index]; for(i=0; i<16; i++) { _grabbedCursor[16*8+i] = color; _grabbedCursor[16*i+8] = color; } } int Scumm::remapPaletteColor(int r, int g, int b, uint threshold) { int i; int ar,ag,ab; uint sum,j,bestsum,bestitem; byte *pal = _currentPalette; if (r>255) r=255; if (g>255) g=255; if (b>255) b=255; bestsum = (uint)-1; r &= ~3; g &= ~3; b &= ~3; for(i=0; i<256; i++,pal+=3) { ar = pal[0]&~3; ag = pal[1]&~3; ab = pal[2]&~3; if (ar==r && ag==g && ab==b) return i; j=abs(ar-r); sum = j*j*3; j=abs(ag-g); sum += j*j*6; j=abs(ab-b); sum += j*j*2; if (sum < bestsum) { bestsum = sum; bestitem = i; } } if (threshold != (uint)-1 && bestsum > threshold*threshold*(2+3+6)) { pal = _currentPalette + (256-2)*3; for(i=254; i>48; i--,pal-=3) { if (pal[0]>=252 && pal[1]>=252 && pal[2]>=252) { setPalColor(i, r, g, b); return i; } } } return bestitem; } void Scumm::drawBomp(BompDrawData *bd) { byte *dest = bd->out + bd->y * bd->outwidth, *src; int h = bd->srcheight; bool inside; if (h==0 || bd->srcwidth==0) return; inside = (bd->x>=0) && (bd->y>=0) && (bd->x <= bd->outwidth - bd->srcwidth) && (bd->y <= bd->outheight - bd->srcheight); if (1 || bd->scale_x==255 && bd->scale_y==255) { /* Routine used when no scaling is needed */ if (inside) { dest += bd->x; src = bd->dataptr; do { byte code,color; uint len = bd->srcwidth, num, i; byte *d = dest; src += 2; do { code = *src++; num = (code>>1)+1; if (num>len) num=len; len -= num; if (code&1) { color = *src++; if (color!=255) { do *d++ = color; while (--num); } else { d += num; } } else { for(i=0;ioutwidth; } while (--h); } else { uint y = bd->y; src = bd->dataptr; do { byte color; uint len, num; uint x; if ((uint)y >= (uint)bd->outheight) { src += READ_LE_UINT16(src) + 2; continue; } len = bd->srcwidth; x = bd->x; src += 2; do { byte code = *src++; num = (code>>1)+1; if (num>len) num=len; len -= num; if (code&1) { if ((color = *src++)!=255) { do { if ((uint)x < (uint)bd->outwidth) dest[x] = color; } while (++x,--num); } else { x += num; } } else { do { if ((color=*src++) != 255 && (uint)x < (uint)bd->outwidth) dest[x] = color; } while (++x,--num); } } while (len); } while (dest += bd->outwidth,y++,--h); } } else { /* scaling of bomp images not supported yet */ } CHECK_HEAP }