Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

vga.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2005 The ScummVM project
 *
 * 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.
 *
 * $Header: /cvsroot/scummvm/scummvm/simon/vga.cpp,v 1.139.2.1 2005/10/18 02:11:26 sev Exp $
 *
 */

// Video script opcodes for Simon1/Simon2
#include "common/stdafx.h"
#include "simon/simon.h"
#include "simon/intern.h"
#include "simon/vga.h"

#include "common/system.h"

namespace Simon {

typedef void (SimonEngine::*VgaOpcodeProc) ();

// Opcode tables
static const VgaOpcodeProc vga_opcode_table[] = {
      NULL,
      &SimonEngine::vc1_fadeOut,
      &SimonEngine::vc2_call,
      &SimonEngine::vc3_loadSprite,
      &SimonEngine::vc4_fadeIn,
      &SimonEngine::vc5_skip_if_neq,
      &SimonEngine::vc6_skip_ifn_sib_with_a,
      &SimonEngine::vc7_skip_if_sib_with_a,
      &SimonEngine::vc8_skip_if_parent_is,
      &SimonEngine::vc9_skip_if_unk3_is,
      &SimonEngine::vc10_draw,
      &SimonEngine::vc11_clearPathFinder,
      &SimonEngine::vc12_delay,
      &SimonEngine::vc13_addToSpriteX,
      &SimonEngine::vc14_addToSpriteY,
      &SimonEngine::vc15_wakeup_id,
      &SimonEngine::vc16_sleep_on_id,
      &SimonEngine::vc17_setPathfinderItem,
      &SimonEngine::vc18_jump,
      &SimonEngine::vc19_chain_to_script,
      &SimonEngine::vc20_setRepeat,
      &SimonEngine::vc21_endRepeat,
      &SimonEngine::vc22_setSpritePalette,
      &SimonEngine::vc23_setSpritePriority,
      &SimonEngine::vc24_setSpriteXY,
      &SimonEngine::vc25_halt_sprite,
      &SimonEngine::vc26_setSubWindow,
      &SimonEngine::vc27_resetSprite,
      &SimonEngine::vc28_dummy_op,
      &SimonEngine::vc29_stopAllSounds,
      &SimonEngine::vc30_setFrameRate,
      &SimonEngine::vc31_setWindow,
      &SimonEngine::vc32_copyVar,
      &SimonEngine::vc33_setMouseOn,
      &SimonEngine::vc34_setMouseOff,
      &SimonEngine::vc35_clearWindow,
      &SimonEngine::vc36_setWindowImage,
      &SimonEngine::vc37_addToSpriteY,
      &SimonEngine::vc38_skipIfVarZero,
      &SimonEngine::vc39_setVar,
      &SimonEngine::vc40,
      &SimonEngine::vc41,
      &SimonEngine::vc42_delayIfNotEQ,
      &SimonEngine::vc43_skipIfBitClear,
      &SimonEngine::vc44_skipIfBitSet,
      &SimonEngine::vc45_setSpriteX,
      &SimonEngine::vc46_setSpriteY,
      &SimonEngine::vc47_addToVar,
      &SimonEngine::vc48_setPathFinder,
      &SimonEngine::vc49_setBit,
      &SimonEngine::vc50_clearBit,
      &SimonEngine::vc51_clear_hitarea_bit_0x40,
      &SimonEngine::vc52_playSound,
      &SimonEngine::vc53_no_op,
      &SimonEngine::vc54_no_op,
      &SimonEngine::vc55_offset_hit_area,
      &SimonEngine::vc56_delay,
      &SimonEngine::vc57_no_op,
      &SimonEngine::vc58,
      &SimonEngine::vc59,
      &SimonEngine::vc60_killSprite,
      &SimonEngine::vc61_changeSprite,
      &SimonEngine::vc62_fastFadeOut,
      &SimonEngine::vc63_fastFadeIn,
      &SimonEngine::vc64_skipIfSpeechEnded,
      &SimonEngine::vc65_slowFadeIn,
      &SimonEngine::vc66_skipIfNotEqual,
      &SimonEngine::vc67_skipIfGE,
      &SimonEngine::vc68_skipIfLE,
      &SimonEngine::vc69_playTrack,
      &SimonEngine::vc70_queueMusic,
      &SimonEngine::vc71_checkMusicQueue,
      &SimonEngine::vc72_play_track_2,
      &SimonEngine::vc73_setMark,
      &SimonEngine::vc74_clearMark,
      &SimonEngine::vc75_setScale,
      &SimonEngine::vc76_setScaleXOffs,
      &SimonEngine::vc77_setScaleYOffs,
      &SimonEngine::vc78_pathUnk1,
      &SimonEngine::vc79_pathUnk2,
      &SimonEngine::vc80_setOverlayImage,
      &SimonEngine::vc81_setRandom,
      &SimonEngine::vc82_pathUnk3,
      &SimonEngine::vc83_playSoundLoop,
      &SimonEngine::vc84_stopSoundLoop,
};

// Script parser
void SimonEngine::run_vga_script() {
      for (;;) {
            uint opcode;

            if (_continousVgaScript) {
                  if (_vcPtr != (const byte *)&_vc_get_out_of_code) {
                        fprintf(_dumpFile, "%.5d %.5X: %5d %4d ", _vgaTickCounter, _vcPtr - _curVgaFile1, _vgaCurSpriteId, _vgaCurFileId);
                        dump_video_script(_vcPtr, true);
                  }
            }

            if (_game & GF_SIMON1) {
                  opcode = READ_BE_UINT16(_vcPtr);
                  _vcPtr += 2;
            } else {
                  opcode = *_vcPtr++;
            }

            if (opcode >= NUM_VIDEO_OP_CODES)
                  error("Invalid VGA opcode '%d' encountered", opcode);

            if (opcode == 0)
                  return;

            (this->*vga_opcode_table[opcode]) ();
      }
}

int SimonEngine::vc_read_var_or_word() {
      int16 var = vc_read_next_word();
      if (var < 0)
            var = vc_read_var(-var);
      return var;
}

uint SimonEngine::vc_read_next_word() {
      uint a;
      a = readUint16Wrapper(_vcPtr);
      _vcPtr += 2;
      return a;
}

uint SimonEngine::vc_read_next_byte() {
      return *_vcPtr++;
}

void SimonEngine::vc_skip_next_instruction() {
      static const byte opcode_param_len_simon1[] = {
            0, 6, 2, 10, 6, 4, 2, 2,
            4, 4, 10, 0, 2, 2, 2, 2,
            2, 0, 2, 0, 4, 2, 4, 2,
            8, 0, 10, 0, 8, 0, 2, 2,
            4, 0, 0, 4, 4, 2, 2, 4,
            4, 4, 4, 2, 2, 2, 2, 4,
            0, 2, 2, 2, 2, 4, 6, 6,
            0, 0, 0, 0, 2, 6, 0, 0,
      };

      static const byte opcode_param_len_simon2[] = {
            0, 6, 2, 12, 6, 4, 2, 2,
            4, 4, 9, 0, 1, 2, 2, 2,
            2, 0, 2, 0, 4, 2, 4, 2,
            7, 0, 10, 0, 8, 0, 2, 2,
            4, 0, 0, 4, 4, 2, 2, 4,
            4, 4, 4, 2, 2, 2, 2, 4,
            0, 2, 2, 2, 2, 4, 6, 6,
            2, 0, 6, 6, 4, 6, 0, 0,
            0, 0, 4, 4, 4, 4, 4, 0,
            4, 2, 2
      };

      static const byte opcode_param_len_feeblefiles[] = {
            0, 6, 2, 12, 6, 4, 2, 2,
            4, 4, 9, 0, 1, 2, 2, 2,
            2, 0, 2, 0, 4, 2, 4, 2,
            7, 0, 10, 0, 8, 0, 2, 2,
            4, 0, 0, 4, 4, 2, 2, 4,
            4, 4, 4, 2, 2, 2, 2, 4,
            0, 2, 2, 2, 6, 6, 6, 6,
            2, 0, 6, 6, 4, 6, 0, 0,
            0, 0, 4, 4, 4, 4, 4, 0,
            4, 2, 2, 4, 6, 6, 0, 0,
            6, 4, 2, 6, 0
      };

      if (_game == GAME_FEEBLEFILES) {
            uint opcode = vc_read_next_byte();
            _vcPtr += opcode_param_len_feeblefiles[opcode];
      } else if (_game & GF_SIMON2) {
            uint opcode = vc_read_next_byte();
            _vcPtr += opcode_param_len_simon2[opcode];
      } else {
            uint opcode = vc_read_next_word();
            _vcPtr += opcode_param_len_simon1[opcode];
      }

      if (_continousVgaScript)
            fprintf(_dumpFile, "; skipped\n");
}

void SimonEngine::o_read_vgares_23() {
      // Simon1 Only
      if (_vgaRes328Loaded == true) {
            _vgaRes328Loaded = false;
            _lockWord |= 0x4000;
            read_vga_from_datfile_1(23);
            _lockWord &= ~0x4000;
      }
}

void SimonEngine::o_read_vgares_328() {
      // Simon1 Only
      if (_vgaRes328Loaded == false) {
            _vgaRes328Loaded = true;
            _lockWord |= 0x4000;
            read_vga_from_datfile_1(328);
            _lockWord &= ~0x4000;
      }
}

// VGA Script commands
void SimonEngine::vc1_fadeOut() {
      /* dummy opcode */
      _vcPtr += 6;
}

void SimonEngine::vc2_call() {
      VgaPointersEntry *vpe;
      uint num;
      uint res;
      byte *old_file_1, *old_file_2;
      byte *b, *bb;
      const byte *vc_ptr_org;

      num = vc_read_var_or_word();

      old_file_1 = _curVgaFile1;
      old_file_2 = _curVgaFile2;

      for (;;) {
            res = num / 100;
            vpe = &_vgaBufferPointers[res];

            _curVgaFile1 = vpe->vgaFile1;
            _curVgaFile2 = vpe->vgaFile2;
            if (vpe->vgaFile1 != NULL)
                  break;
            if (_vgaCurFile2 != res)
                  _videoVar7 = _vgaCurFile2;

            ensureVgaResLoaded(res);
            _videoVar7 = 0xFFFF;
      }


      bb = _curVgaFile1;
      if (_game == GAME_FEEBLEFILES) {
            b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start);
            b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable);

            while (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) != num)
                  b += sizeof(ImageHeader_Feeble);
      } else {
            b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start);
            b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable);

            while (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) != num)
                  b += sizeof(ImageHeader_Simon);
      }

      vc_ptr_org = _vcPtr;

      if (_game == GAME_FEEBLEFILES) {
            _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs);
      } else {
            _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs);
      }

      //dump_vga_script(_vcPtr, res, num);
      run_vga_script();

      _curVgaFile1 = old_file_1;
      _curVgaFile2 = old_file_2;

      _vcPtr = vc_ptr_org;
}

void SimonEngine::vc3_loadSprite() {
      uint16 windowNum, fileId, palette, x, y, vgaSpriteId;
      uint16 res;
      VgaSprite *vsp;
      VgaPointersEntry *vpe;
      byte *p, *pp;
      byte *old_file_1;

      windowNum = vc_read_next_word();          /* 0 */

      if (_game & GF_SIMON2) {
            fileId = vc_read_next_word();       /* 0 */
            vgaSpriteId = vc_read_next_word();  /* 2 */
      } else {
            vgaSpriteId = vc_read_next_word();  /* 2 */
            fileId = vgaSpriteId / 100;
      }

      x = vc_read_next_word();                  /* 4 */
      y = vc_read_next_word();                  /* 6 */
      palette = vc_read_next_word();            /* 8 */

      /* 2nd param ignored with simon1 */
      if (isSpriteLoaded(vgaSpriteId, fileId))
            return;

      vsp = _vgaSprites;
      while (vsp->id)
            vsp++;

      vsp->palette = palette;
      vsp->windowNum = windowNum;
      vsp->priority = 0;
      vsp->flags = 0;
      vsp->image = 0;
      vsp->x = x;
      vsp->y = y;
      vsp->id = vgaSpriteId;
      vsp->fileId = res = fileId;

      old_file_1 = _curVgaFile1;
      for (;;) {
            vpe = &_vgaBufferPointers[res];
            _curVgaFile1 = vpe->vgaFile1;

            if (vpe->vgaFile1 != NULL)
                  break;
            if (_vgaCurFile2 != res)
                  _videoVar7 = _vgaCurFile2;

            ensureVgaResLoaded(res);
            _videoVar7 = 0xFFFF;
      }

      pp = _curVgaFile1;
      if (_game == GAME_FEEBLEFILES) {
            p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start);
            p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable);

            while (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) != vgaSpriteId)
                  p += sizeof(AnimationHeader_Feeble);
      } else {
            p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start);
            p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable);

            while (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) != vgaSpriteId)
                  p += sizeof(AnimationHeader_Simon);
      }

#ifdef DUMP_FILE_NR
      {
            static bool dumped = false;
            if (res == DUMP_FILE_NR && !dumped) {
                  dumped = true;
                  dump_vga_file(_curVgaFile1);
            }
      }
#endif

#ifdef DUMP_BITMAPS_FILE_NR
      {
            static bool dumped = false;
            if (res == DUMP_BITMAPS_FILE_NR && !dumped) {
                  dumped = true;
                  dump_vga_bitmaps(_curVgaFile2, _curVgaFile1, res);
            }
      }
#endif

      if (_startVgaScript) {
            if (_game == GAME_FEEBLEFILES) {
                  dump_vga_script(_curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), res, vgaSpriteId);
            } else {
                  dump_vga_script(_curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), res, vgaSpriteId);

            }
      }

      if (_game == GAME_FEEBLEFILES) {
            add_vga_timer(VGA_DELAY_BASE, _curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, res);
      } else {
            add_vga_timer(VGA_DELAY_BASE, _curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, res);
      }

      _curVgaFile1 = old_file_1;
}

void SimonEngine::vc4_fadeIn() {
      /* dummy opcode */
      _vcPtr += 6;
}

void SimonEngine::vc5_skip_if_neq() {
      uint var = vc_read_next_word();
      uint value = vc_read_next_word();
      if (vc_read_var(var) != value)
            vc_skip_next_instruction();
}

void SimonEngine::vc6_skip_ifn_sib_with_a() {
      if (!itemIsSiblingOf(vc_read_next_word()))
            vc_skip_next_instruction();
}

void SimonEngine::vc7_skip_if_sib_with_a() {
      if (itemIsSiblingOf(vc_read_next_word()))
            vc_skip_next_instruction();
}

void SimonEngine::vc8_skip_if_parent_is() {
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();
      if (!itemIsParentOf(a, b))
            vc_skip_next_instruction();
}

void SimonEngine::vc9_skip_if_unk3_is() {
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();
      if (!vc_maybe_skip_proc_1(a, b))
            vc_skip_next_instruction();
}

byte *vc10_depack_column(VC10_state * vs) {
      int8 a = vs->depack_cont;
      const byte *src = vs->depack_src;
      byte *dst = vs->depack_dest;
      byte dh = vs->dh;
      byte color;

      if (a == -0x80)
            a = *src++;

      for (;;) {
            if (a >= 0) {
                  color = *src++;
                  do {
                        *dst++ = color;
                        if (!--dh) {
                              if (--a < 0)
                                    a = -0x80;
                              else
                                    src--;
                              goto get_out;
                        }
                  } while (--a >= 0);
            } else {
                  do {
                        *dst++ = *src++;
                        if (!--dh) {
                              if (++a == 0)
                                    a = -0x80;
                              goto get_out;
                        }
                  } while (++a != 0);
            }
            a = *src++;
      }

get_out:;
      vs->depack_src = src;
      vs->depack_cont = a;
      return vs->depack_dest + vs->y_skip;
}

void vc10_skip_cols(VC10_state *vs) {
      vs->depack_cont = -0x80;
      while (vs->x_skip) {
            vc10_depack_column(vs);
            vs->x_skip--;
      }
}

byte *SimonEngine::vc10_depack_swap(const byte *src, uint w, uint h) {
      w <<= 3;

      {
            byte *dst_org = _videoBuf1 + w;
            byte color;
            int8 cur = -0x80;
            uint w_cur = w;

            do {
                  byte *dst = dst_org;
                  uint h_cur = h;

                  if (cur == -0x80)
                        cur = *src++;

                  for (;;) {
                        if (cur >= 0) {
                              /* rle_same */
                              color = *src++;
                              do {
                                    *dst = color;
                                    dst += w;
                                    if (!--h_cur) {
                                          if (--cur < 0)
                                                cur = -0x80;
                                          else
                                                src--;
                                          goto next_line;
                                    }
                              } while (--cur >= 0);
                        } else {
                              /* rle_diff */
                              do {
                                    *dst = *src++;
                                    dst += w;
                                    if (!--h_cur) {
                                          if (++cur == 0)
                                                cur = -0x80;
                                          goto next_line;
                                    }
                              } while (++cur != 0);
                        }
                        cur = *src++;
                  }
            next_line:
                  dst_org++;
            } while (--w_cur);
      }

      {
            byte *dst_org, *src_org;
            uint i;

            src_org = dst_org = _videoBuf1 + w;

            do {
                  byte *dst = dst_org;
                  for (i = 0; i != w; ++i) {
                        byte b = src_org[i];
                        b = (b >> 4) | (b << 4);
                        *--dst = b;
                  }

                  src_org += w;
                  dst_org += w;
            } while (--h);

      }

      return _videoBuf1;
}

byte *SimonEngine::vc10_no_depack_swap(const byte *src, uint w, uint h) {
      if (src == _vc10BasePtrOld)
            return _videoBuf1;

      _vc10BasePtrOld = src;
      h *= 8;
      byte *dst = _videoBuf1 + h - 1;

      uint h_cur = h;
      do {
            do {
                  *dst = *src << 4;
                  (*dst--) |= (*src++) >> 4;
            } while (--h_cur != 0);
            h_cur = h;
            dst += h * 2;
      } while (--w != 0);

      return _videoBuf1;
}

/* must not be const */
static uint16 _video_windows[128] = {
      0, 0, 20, 200,
      0, 0, 3, 136,
      17, 0, 3, 136,
      0, 0, 20, 200,
      0, 0, 20, 134
};

/* simon2 specific */
void SimonEngine::decodeStripA(byte *dst, const byte *src, int height) {
      const uint pitch = _dxSurfacePitch;
      int8 reps = (int8)0x80;
      byte color;
      byte *dst_org = dst;
      uint h = height, w = 8;

      for (;;) {
            reps = *src++;
            if (reps >= 0) {
                  color = *src++;

                  do {
                        *dst = color;
                        dst += pitch;

                        /* reached bottom? */
                        if (--h == 0) {
                              /* reached right edge? */
                              if (--w == 0)
                                    return;
                              dst = ++dst_org;
                              h = height;
                        }
                  } while (--reps >= 0);
            } else {

                  do {
                        *dst = *src++;
                        dst += pitch;

                        /* reached bottom? */
                        if (--h == 0) {
                              /* reached right edge? */
                              if (--w == 0)
                                    return;
                              dst = ++dst_org;
                              h = height;
                        }
                  } while (++reps != 0);
            }
      }
}

void SimonEngine::vc10_draw() {
      byte *p2;
      uint width, height;
      byte flags;
      const uint16 *vlut;
      VC10_state state;

      int cur;

      state.image = (int16)vc_read_next_word();
      if (state.image == 0)
            return;

      state.palette = (_vcPtr[1] << 4);
      _vcPtr += 2;
      state.x = (int16)vc_read_next_word();
      if (_game & GF_SIMON2) {
            state.x -= _scrollX;
      }
      state.y = (int16)vc_read_next_word();

      if (!(_game & GF_SIMON2)) {
            state.flags = vc_read_next_word();
      } else {
            state.flags = vc_read_next_byte();
      }

      if (state.image < 0)
            state.image = vc_read_var(-state.image);

      p2 = _curVgaFile2 + state.image * 8;
      state.depack_src = _curVgaFile2 + READ_BE_UINT32(p2);

      if (_game == GAME_FEEBLEFILES) {
            width = READ_LE_UINT16(p2 + 6);
      } else {
            width = READ_BE_UINT16(p2 + 6) >> 4;
      }

      height = p2[5];
      flags = p2[4];

      debug(1, "Width %d Height %d Flags 0x%x", width, height, flags);

      if (height == 0 || width == 0)
            return;

      if (_dumpImages)
            dump_single_bitmap(_vgaCurFileId, state.image, state.depack_src, width * 16, height,
                                                                   state.palette);
      // TODO::Add support for image scaling
      if (_game == GAME_FEEBLEFILES)
            return;

      if (flags & 0x80 && !(state.flags & 0x10)) {
            if (state.flags & 1) {
                  state.flags &= ~1;
                  state.flags |= 0x10;
            } else {
                  state.flags |= 0x8;
            }
      }

      uint maxWidth = (_game == GAME_FEEBLEFILES) ? 641 : 21;
      if (_game & GF_SIMON2 && width >= maxWidth) {
            const byte *src;
            byte *dst;
            uint w;

            _scrollXMax = width * 2 - 40;
            _scrollImage = state.depack_src;
            _scrollHeight = height;
            if (_variableArray[34] == -1)
                  state.x = _variableArray[251];

            _scrollX = state.x;

            vc_write_var(251, _scrollX);

            dst = dx_lock_attached();
            src = state.depack_src + _scrollX * 4;

            for (w = 0; w < 40; w++) {
                  decodeStripA(dst, src + READ_BE_UINT32(src), height);
                  dst += 8;
                  src += 4;
            }

            dx_unlock_attached();

            return;
      }

      if (state.flags & 0x10) {
            state.depack_src = vc10_depack_swap(state.depack_src, width, height);
      } else if (state.flags & 1) {
            state.depack_src = vc10_no_depack_swap(state.depack_src, width, height);
      }

      vlut = &_video_windows[_windowNum * 4];

      state.draw_width = width << 1;      /* cl */
      state.draw_height = height;   /* ch */

      state.x_skip = 0;                                     /* colums to skip = bh */
      state.y_skip = 0;                                     /* rows to skip   = bl */

      cur = state.x;
      if (cur < 0) {
            do {
                  if (!--state.draw_width)
                        return;
                  state.x_skip++;
            } while (++cur);
      }
      state.x = cur;

      cur += state.draw_width - (vlut[2] << 1);
      if (cur > 0) {
            do {
                  if (!--state.draw_width)
                        return;
            } while (--cur);
      }

      cur = state.y;
      if (cur < 0) {
            do {
                  if (!--state.draw_height)
                        return;
                  state.y_skip++;
            } while (++cur);
      }
      state.y = cur;

      cur += state.draw_height - vlut[3];
      if (cur > 0) {
            do {
                  if (!--state.draw_height)
                        return;
            } while (--cur);
      }

      assert(state.draw_width != 0 && state.draw_height != 0);

      state.draw_width <<= 2;

      state.surf2_addr = dx_lock_2();
      state.surf2_pitch = _dxSurfacePitch;

      state.surf_addr = dx_lock_attached();
      state.surf_pitch = _dxSurfacePitch;

      {
            uint offs, offs2;
            // Allow one section of Simon the Sorcerer 1 introduction to be displayed
            // in lower half of screen
            if ((_game & GF_SIMON1) && _subroutine == 2926) {
                  offs = ((vlut[0]) * 2 + state.x) * 8;
                  offs2 = (vlut[1] + state.y);
            } else {
                  offs = ((vlut[0] - _video_windows[16]) * 2 + state.x) * 8;
                  offs2 = (vlut[1] - _video_windows[17] + state.y);
            }

            state.surf2_addr += offs + offs2 * state.surf2_pitch;
            state.surf_addr += offs + offs2 * state.surf_pitch;
      }

      if (state.flags & 0x20) {
            byte *mask, *src, *dst;
            byte h;
            uint w;

            state.x_skip <<= 2;
            state.dl = width;
            state.dh = height;

            vc10_skip_cols(&state);

            w = 0;
            do {
                  mask = vc10_depack_column(&state);  /* esi */
                  src = state.surf2_addr + w * 2;     /* ebx */
                  dst = state.surf_addr + w * 2;      /* edi */

                  h = state.draw_height;
                  if ((_game & GF_SIMON1) && vc_get_bit(88)) {
                        /* transparency */
                        do {
                              if (mask[0] & 0xF0) {
                                    if ((dst[0] & 0x0F0) == 0x20)
                                          dst[0] = src[0];
                              }
                              if (mask[0] & 0x0F) {
                                    if ((dst[1] & 0x0F0) == 0x20)
                                          dst[1] = src[1];
                              }
                              mask++;
                              dst += state.surf_pitch;
                              src += state.surf2_pitch;
                        } while (--h);
                  } else {
                        /* no transparency */
                        do {
                              if (mask[0] & 0xF0)
                                    dst[0] = src[0];
                              if (mask[0] & 0x0F)
                                    dst[1] = src[1];
                              mask++;
                              dst += state.surf_pitch;
                              src += state.surf2_pitch;
                        } while (--h);
                  }
            } while (++w != state.draw_width);

            /* vc10_helper_5 */
      } else if (_lockWord & 0x20 && state.palette == 0 || state.palette == 0xC0) {
            const byte *src;
            byte *dst;
            uint h, i;

            if (!(state.flags & 8)) {
                  src = state.depack_src + (width * state.y_skip << 4) + (state.x_skip << 3);
                  dst = state.surf_addr;

                  state.draw_width *= 2;

                  if (state.flags & 2) {
                        /* no transparency */
                        h = state.draw_height;
                        do {
                              memcpy(dst, src, state.draw_width);
                              dst += _screenWidth;
                              src += width * 16;
                        } while (--h);
                  } else {
                        /* transparency */
                        h = state.draw_height;
                        do {
                              for (i = 0; i != state.draw_width; i++)
                                    if (src[i])
                                          dst[i] = src[i];
                              dst += _screenWidth;
                              src += width * 16;
                        } while (--h);
                  }

            } else {
                  byte *dst_org = state.surf_addr;
                  src = state.depack_src;
                  /* AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE
                   * aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh
                   */

                  if (state.flags & 2) {
                        /* no transparency */
                        do {
                              uint count = state.draw_width >> 2;

                              dst = dst_org;
                              do {
                                    uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]);

                                    dst[0] = (byte)((bits >> (32 - 5)) & 31);
                                    dst[1] = (byte)((bits >> (32 - 10)) & 31);
                                    dst[2] = (byte)((bits >> (32 - 15)) & 31);
                                    dst[3] = (byte)((bits >> (32 - 20)) & 31);
                                    dst[4] = (byte)((bits >> (32 - 25)) & 31);
                                    dst[5] = (byte)((bits >> (32 - 30)) & 31);

                                    bits = (bits << 8) | src[4];

                                    dst[6] = (byte)((bits >> (40 - 35)) & 31);
                                    dst[7] = (byte)((bits) & 31);

                                    dst += 8;
                                    src += 5;
                              } while (--count);
                              dst_org += _screenWidth;
                        } while (--state.draw_height);
                  } else {
                        /* transparency */
                        do {
                              uint count = state.draw_width >> 2;

                              dst = dst_org;
                              do {
                                    uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]);
                                    byte tmp;

                                    tmp = (byte)((bits >> (32 - 5)) & 31);
                                    if (tmp)
                                          dst[0] = tmp;
                                    tmp = (byte)((bits >> (32 - 10)) & 31);
                                    if (tmp)
                                          dst[1] = tmp;
                                    tmp = (byte)((bits >> (32 - 15)) & 31);
                                    if (tmp)
                                          dst[2] = tmp;
                                    tmp = (byte)((bits >> (32 - 20)) & 31);
                                    if (tmp)
                                          dst[3] = tmp;
                                    tmp = (byte)((bits >> (32 - 25)) & 31);
                                    if (tmp)
                                          dst[4] = tmp;
                                    tmp = (byte)((bits >> (32 - 30)) & 31);
                                    if (tmp)
                                          dst[5] = tmp;

                                    bits = (bits << 8) | src[4];

                                    tmp = (byte)((bits >> (40 - 35)) & 31);
                                    if (tmp)
                                          dst[6] = tmp;
                                    tmp = (byte)((bits) & 31);
                                    if (tmp)
                                          dst[7] = tmp;

                                    dst += 8;
                                    src += 5;
                              } while (--count);
                              dst_org += _screenWidth;
                        } while (--state.draw_height);
                  }
            }
            /* vc10_helper_4 */
      } else {
            if (_game & GF_SIMON2 && state.flags & 0x4 && _bitArray[10] & 0x800) {
                  state.surf_addr = state.surf2_addr;
                  state.surf_pitch = state.surf2_pitch;
            }

            if (state.flags & 0x8) {
                  uint w, h;
                  byte *src, *dst, *dst_org;

                  state.x_skip <<= 2;                       /* reached */
                  state.dl = width;
                  state.dh = height;

                  vc10_skip_cols(&state);

                  if (state.flags & 2) {
                        dst_org = state.surf_addr;
                        w = 0;
                        do {
                              src = vc10_depack_column(&state);
                              dst = dst_org;

                              h = 0;
                              do {
                                    dst[0] = (*src >> 4) | state.palette;
                                    dst[1] = (*src & 15) | state.palette;
                                    dst += _screenWidth;
                                    src++;
                              } while (++h != state.draw_height);
                              dst_org += 2;
                        } while (++w != state.draw_width);
                  } else {
                        dst_org = state.surf_addr;
                        if (state.flags & 0x40) {           /* reached */
                              dst_org += vc_read_var(252);
                        }
                        w = 0;
                        do {
                              byte color;

                              src = vc10_depack_column(&state);
                              dst = dst_org;

                              h = 0;
                              do {
                                    color = (*src >> 4);
                                    if (color)
                                          dst[0] = color | state.palette;
                                    color = (*src & 15);
                                    if (color)
                                          dst[1] = color | state.palette;
                                    dst += _screenWidth;
                                    src++;
                              } while (++h != state.draw_height);
                              dst_org += 2;
                        } while (++w != state.draw_width);
                  }
                  /* vc10_helper_6 */
            } else {
                  const byte *src;
                  byte *dst;
                  uint count;

                  src = state.depack_src + (width * state.y_skip) * 8;
                  dst = state.surf_addr;
                  state.x_skip <<= 2;
                  if (state.flags & 2) {
                        do {
                              for (count = 0; count != state.draw_width; count++) {
                                    dst[count * 2] = (src[count + state.x_skip] >> 4) | state.palette;
                                    dst[count * 2 + 1] = (src[count + state.x_skip] & 15) | state.palette;
                              }
                              dst += _screenWidth;
                              src += width * 8;
                        } while (--state.draw_height);
                  } else {
                        do {
                              for (count = 0; count != state.draw_width; count++) {
                                    byte color;
                                    color = (src[count + state.x_skip] >> 4);
                                    if (color)
                                          dst[count * 2] = color | state.palette;
                                    color = (src[count + state.x_skip] & 15);
                                    if (color)
                                          dst[count * 2 + 1] = color | state.palette;
                              }
                              dst += _screenWidth;
                              src += width * 8;
                        } while (--state.draw_height);

                  }

                  /* vc10_helper_7 */
            }
      }

      dx_unlock_2();
      dx_unlock_attached();

}

void SimonEngine::vc11_clearPathFinder() {
      memset(&_pathFindArray, 0, sizeof(_pathFindArray));
}

void SimonEngine::vc12_delay() {
      VgaSprite *vsp = find_cur_sprite();
      uint num;

      if (_game & GF_SIMON1) {
            num = vc_read_var_or_word();
      } else {
            num = vc_read_next_byte() * _frameRate;
      }

      // Work around to allow inventory arrows to be
      // shown in some versions of Simon the Sorcerer 1
      if ((_game & GF_SIMON1) && vsp->id == 0x80)
            num = 0;
      else
            num += VGA_DELAY_BASE;

      add_vga_timer(num, _vcPtr, _vgaCurSpriteId, _vgaCurFileId);
      _vcPtr = (byte *)&_vc_get_out_of_code;
}

void SimonEngine::vc13_addToSpriteX() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->x += (int16)vc_read_next_word();
      _vgaSpriteChanged++;
}

void SimonEngine::vc14_addToSpriteY() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->y += (int16)vc_read_next_word();
      _vgaSpriteChanged++;
}

void SimonEngine::vc15_wakeup_id() {
      VgaSleepStruct *vfs = _vgaSleepStructs, *vfs_tmp;
      uint16 id = vc_read_next_word();
      while (vfs->ident != 0) {
            if (vfs->ident == id) {
                  add_vga_timer(VGA_DELAY_BASE, vfs->code_ptr, vfs->sprite_id, vfs->cur_vga_file);
                  vfs_tmp = vfs;
                  do {
                        memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct));
                        vfs_tmp++;
                  } while (vfs_tmp->ident != 0);
            } else {
                  vfs++;
            }
      }

      /* clear a wait event */
      if (id == _vgaWaitFor)
            _vgaWaitFor = 0;
}

void SimonEngine::vc16_sleep_on_id() {
      VgaSleepStruct *vfs = _vgaSleepStructs;
      while (vfs->ident)
            vfs++;

      vfs->ident = vc_read_next_word();
      vfs->code_ptr = _vcPtr;
      vfs->sprite_id = _vgaCurSpriteId;
      vfs->cur_vga_file = _vgaCurFileId;

      _vcPtr = (byte *)&_vc_get_out_of_code;
}

void SimonEngine::vc17_setPathfinderItem() {
      uint a = vc_read_next_word();
      _pathFindArray[a - 1] = (const uint16 *)_vcPtr;

      int end = (_game == GAME_FEEBLEFILES) ? 9999 : 999;
      while (readUint16Wrapper(_vcPtr) != end)
            _vcPtr += 4;
      _vcPtr += 2;
}

void SimonEngine::vc18_jump() {
      int16 offs = vc_read_next_word();
      _vcPtr += offs;
}

/* chain to script? */
void SimonEngine::vc19_chain_to_script() {
      /* unused */
      error("vc19_chain_to_script: not implemented");
}

/* helper routines */

void SimonEngine::vc20_setRepeat() {
      /* FIXME: This opcode is somewhat strange: it first reads a BE word from
       * the script (advancing the script pointer in doing so); then it writes
       * back the same word, this time as LE, into the script.
       */
      uint16 a = vc_read_next_word();
      WRITE_LE_UINT16(const_cast<byte *>(_vcPtr), a);
      _vcPtr += 2;
}

void SimonEngine::vc21_endRepeat() {
      int16 a = vc_read_next_word();
      const byte *tmp = _vcPtr + a;
      if (_game & GF_SIMON2)
            tmp += 3;
      else
            tmp += 4;

      uint16 val = READ_LE_UINT16(tmp);
      if (val != 0) {
            // Decrement counter
            WRITE_LE_UINT16(const_cast<byte *>(tmp), val - 1);
            _vcPtr = tmp + 2;
      }
}

void SimonEngine::vc22_setSpritePalette() {
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();
      uint num = a == 0 ? 0x20 : 0x10;
      byte *palptr, *src;

      palptr = &_palette[(a << 6)];

      src = _curVgaFile1 + 6 + b * 96;

      do {
            palptr[0] = src[0] << 2;
            palptr[1] = src[1] << 2;
            palptr[2] = src[2] << 2;
            palptr[3] = 0;

            palptr += 4;
            src += 3;
      } while (--num);

      _videoVar9 = 2;
      _vgaSpriteChanged++;
}

void SimonEngine::vc23_setSpritePriority() {
      VgaSprite *vsp = find_cur_sprite(), *vus2;
      uint16 pri = vc_read_next_word();
      VgaSprite bak;

      if (vsp->id == 0)
            return;

      memcpy(&bak, vsp, sizeof(bak));
      bak.priority = pri;
      bak.windowNum |= 0x8000;

      vus2 = vsp;

      if (vsp != _vgaSprites && pri < vsp[-1].priority) {
            do {
                  vsp--;
            } while (vsp != _vgaSprites && pri < vsp[-1].priority);
            do {
                  memcpy(vus2, vus2 - 1, sizeof(VgaSprite));
            } while (--vus2 != vsp);
            memcpy(vus2, &bak, sizeof(VgaSprite));
      } else if (vsp[1].id != 0 && pri >= vsp[1].priority) {
            do {
                  vsp++;
            } while (vsp[1].id != 0 && pri >= vsp[1].priority);
            do {
                  memcpy(vus2, vus2 + 1, sizeof(VgaSprite));
            } while (++vus2 != vsp);
            memcpy(vus2, &bak, sizeof(VgaSprite));
      } else {
            vsp->priority = pri;
      }
      _vgaSpriteChanged++;
}

void SimonEngine::vc24_setSpriteXY() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->image = vc_read_var_or_word();

      vsp->x += (int16)vc_read_next_word();
      vsp->y += (int16)vc_read_next_word();
      if (_game & GF_SIMON1) {
            vsp->flags = vc_read_next_word();
      } else {
            vsp->flags = vc_read_next_byte();
      }

      _vgaSpriteChanged++;
}

void SimonEngine::vc25_halt_sprite() {
      VgaSprite *vsp = find_cur_sprite();
      while (vsp->id != 0) {
            memcpy(vsp, vsp + 1, sizeof(VgaSprite));
            vsp++;
      }
      _vcPtr = (byte *)&_vc_get_out_of_code;
      _vgaSpriteChanged++;
}

void SimonEngine::vc26_setSubWindow() {
      uint16 *as = &_video_windows[vc_read_next_word() * 4]; // number
      as[0] = vc_read_next_word(); // x
      as[1] = vc_read_next_word(); // y
      as[2] = vc_read_next_word(); // width
      as[3] = vc_read_next_word(); // height
}

void SimonEngine::vc27_resetSprite() {
      VgaSprite bak, *vsp;
      VgaSleepStruct *vfs;
      VgaTimerEntry *vte, *vte2;

      _lockWord |= 8;

      memset(&bak, 0, sizeof(bak));

      vsp = _vgaSprites;
      while (vsp->id) {
            if ((_game & GF_SIMON1) && vsp->id == 0x80) {
                  memcpy(&bak, vsp, sizeof(VgaSprite));
            }
            vsp->id = 0;
            vsp++;
      }

      if (bak.id != 0)
            memcpy(_vgaSprites, &bak, sizeof(VgaSprite));

      vfs = _vgaSleepStructs;
      while (vfs->ident) {
            vfs->ident = 0;
            vfs++;
      }

      vte = _vgaTimerList;
      while (vte->delay) {
            if ((_game & GF_SIMON1) && vsp->id == 0x80) {
                  vte++;
            } else {
                  vte2 = vte;
                  while (vte2->delay) {
                        memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry));
                        vte2++;
                  }
            }
      }

      vc_write_var(0xFE, 0);

      _lockWord &= ~8;
}

void SimonEngine::vc28_dummy_op() {
      /* unused */
      _vcPtr += 8;
}

void SimonEngine::vc29_stopAllSounds() {
      _sound->stopAll();
}

void SimonEngine::vc30_setFrameRate() {
      _frameRate = vc_read_next_word();
}

void SimonEngine::vc31_setWindow() {
      _windowNum = vc_read_next_word();
}

uint SimonEngine::vc_read_var(uint var) {
      assert(var < 255);
      return (uint16)_variableArray[var];
}

void SimonEngine::vc_write_var(uint var, int16 value) {
      _variableArray[var] = value;
}

void SimonEngine::vc32_copyVar() {
      uint16 a = vc_read_var(vc_read_next_word());
      vc_write_var(vc_read_next_word(), a);
}

void SimonEngine::vc33_setMouseOn() {
      if (_lockCounter != 0) {
            _lockCounter = 1;
            unlock();
      }
}

void SimonEngine::vc34_setMouseOff() {
      lock();
      _lockCounter = 200;
      _leftButtonDown = 0;
}

void SimonEngine::vc35_clearWindow() {
      /* unused */
      _vcPtr += 4;
      _vgaSpriteChanged++;
}

void SimonEngine::vc36_setWindowImage() {
      _videoVar8 = false;
      uint vga_res = vc_read_next_word();
      uint windowNum = vc_read_next_word();

      if (_game & GF_SIMON1) {
            if (windowNum == 16) {
                  _copyPartialMode = 2;
            } else {
                  set_video_mode_internal(windowNum, vga_res);
            }
      } else {
            set_video_mode_internal(windowNum, vga_res);
      }
}

void SimonEngine::vc37_addToSpriteY() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->y += vc_read_var(vc_read_next_word());
      _vgaSpriteChanged++;
}

void SimonEngine::vc38_skipIfVarZero() {
      uint var = vc_read_next_word();
      if (vc_read_var(var) == 0)
            vc_skip_next_instruction();
}

void SimonEngine::vc39_setVar() {
      uint var = vc_read_next_word();
      int16 value = vc_read_next_word();
      vc_write_var(var, value);
}

void SimonEngine::vc40() {
      uint var = vc_read_next_word();
      int16 value = vc_read_var(var) + vc_read_next_word();

      if ((_game & GF_SIMON2) && var == 0xF && !(_bitArray[5] & 1)) {
            int16 tmp;

            if (_scrollCount != 0) {
                  if (_scrollCount >= 0)
                        goto no_scroll;
                  _scrollCount = 0;
            } else {
                  if (_scrollFlag != 0)
                        goto no_scroll;
            }

            if (value - _scrollX >= 30) {
                  _scrollCount = 20;
                  tmp = _scrollXMax - _scrollX;
                  if (tmp < 20)
                        _scrollCount = tmp;
                  add_vga_timer(6, NULL, 0, 0); /* special timer */
            }
      }
no_scroll:;

      vc_write_var(var, value);
}

void SimonEngine::vc41() {
      uint var = vc_read_next_word();
      int16 value = vc_read_var(var) - vc_read_next_word();

      if ((_game & GF_SIMON2) && var == 0xF && !(_bitArray[5] & 1)) {
            int16 tmp;

            if (_scrollCount != 0) {
                  if (_scrollCount < 0)
                        goto no_scroll;
                  _scrollCount = 0;
            } else {
                  if (_scrollFlag != 0)
                        goto no_scroll;
            }

            if ((uint16)(value - _scrollX) < 11) {
                  _scrollCount = -20;
                  tmp = _scrollXMax - _scrollX;
                  if (_scrollX < 20)
                        _scrollCount = -_scrollX;
                  add_vga_timer(6, NULL, 0, 0); /* special timer */
            }
      }
no_scroll:;

      vc_write_var(var, value);
}

void SimonEngine::vc42_delayIfNotEQ() {
      uint val = vc_read_var(vc_read_next_word());
      if (val != vc_read_next_word()) {

            add_vga_timer(_frameRate + 1, _vcPtr - 4, _vgaCurSpriteId, _vgaCurFileId);
            _vcPtr = (byte *)&_vc_get_out_of_code;
      }
}

void SimonEngine::vc43_skipIfBitClear() {
      if (!vc_get_bit(vc_read_next_word())) {
            vc_skip_next_instruction();
      }
}

void SimonEngine::vc44_skipIfBitSet() {
      if (vc_get_bit(vc_read_next_word())) {
            vc_skip_next_instruction();
      }
}

void SimonEngine::vc45_setSpriteX() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->x = vc_read_var(vc_read_next_word());
      _vgaSpriteChanged++;
}

void SimonEngine::vc46_setSpriteY() {
      VgaSprite *vsp = find_cur_sprite();
      vsp->y = vc_read_var(vc_read_next_word());
      _vgaSpriteChanged++;
}

void SimonEngine::vc47_addToVar() {
      uint var = vc_read_next_word();
      vc_write_var(var, vc_read_var(var) + vc_read_var(vc_read_next_word()));
}

void SimonEngine::vc48_setPathFinder() {
      uint a = (uint16)_variableArray[12];
      uint b = (uint16)_variableArray[13];
      int c = _variableArray[14];
      const uint16 *p = _pathFindArray[a - 1];
      int step;
      int y1, y2;
      int16 *vp;

      p += b * 2 + 1;

      step = 2;
      if (c < 0) {
            c = -c;
            step = -2;
      }

      vp = &_variableArray[20];

      do {
            y2 = readUint16Wrapper(p);
            p += step;
            y1 = readUint16Wrapper(p) - y2;

            vp[0] = y1 >> 1;
            vp[1] = y1 - (y1 >> 1);

            vp += 2;
      } while (--c);
}

void SimonEngine::vc_set_bit_to(uint bit, bool value) {
      uint16 *bits = &_bitArray[bit >> 4];
      *bits = (*bits & ~(1 << (bit & 15))) | (value << (bit & 15));
}

bool SimonEngine::vc_get_bit(uint bit) {
      uint16 *bits = &_bitArray[bit >> 4];
      return (*bits & (1 << (bit & 15))) != 0;
}

void SimonEngine::vc49_setBit() {
      vc_set_bit_to(vc_read_next_word(), true);
}

void SimonEngine::vc50_clearBit() {
      vc_set_bit_to(vc_read_next_word(), false);
}

void SimonEngine::vc51_clear_hitarea_bit_0x40() {
      clear_hitarea_bit_0x40(vc_read_next_word());
}

void SimonEngine::vc52_playSound() {
      uint16 sound_id = vc_read_next_word();

      if (_game == GAME_FEEBLEFILES) {
            uint16 pan = vc_read_next_word();
            uint16 vol = vc_read_next_word();
            debug(0, "STUB: vc52_playSound: snd %d pan %d vol %d", sound_id, pan, vol);
      } else if (_game & GF_SIMON2) {
            if (sound_id >= 0x8000) {
                  sound_id = -sound_id;
                  _sound->playAmbient(sound_id);
            } else {
                  _sound->playEffects(sound_id);
            }
      } else if (_game & GF_TALKIE) {
            _sound->playEffects(sound_id);
      } else {
            playSting(sound_id);
      }
}

void SimonEngine::vc53_no_op() {
      // Start sound effect, panning it with the animation
      int snd = vc_read_next_word();
      int xoffs = vc_read_next_word();
      int vol = vc_read_next_word();
      debug(0, "STUB: vc53_no_op: snd %d xoffs %d vol %d", snd, xoffs, vol);
}

void SimonEngine::vc54_no_op() {
      /* unused */
      _vcPtr += 6;
}

void SimonEngine::vc55_offset_hit_area() {
      HitArea *ha = _hitAreas;
      uint count = ARRAYSIZE(_hitAreas);
      uint16 id = vc_read_next_word();
      int16 x = vc_read_next_word();
      int16 y = vc_read_next_word();

      for (;;) {
            if (ha->id == id) {
                  ha->x += x;
                  ha->y += y;
                  break;
            }
            ha++;
            if (!--count)
                  break;
      }

      _needHitAreaRecalc++;
}

void SimonEngine::vc56_delay() {
      if (_game & GF_SIMON2) {
            uint num = vc_read_var_or_word() * _frameRate;

            add_vga_timer(num + VGA_DELAY_BASE, _vcPtr, _vgaCurSpriteId, _vgaCurFileId);
            _vcPtr = (byte *)&_vc_get_out_of_code;
      }
}

void SimonEngine::vc59() {
      if (_game & GF_SIMON2) {
            uint file = vc_read_next_word();
            uint start = vc_read_next_word();
            uint end = vc_read_next_word() + 1;

            do {
                  vc_kill_sprite(file, start);
            } while (++start != end);
      } else {
            if (!_sound->isVoiceActive())
                  vc_skip_next_instruction();
      }
}

void SimonEngine::vc58() {
      uint sprite = _vgaCurSpriteId;
      uint file = _vgaCurFileId;
      const byte *vc_ptr_org;
      uint16 tmp;

      _vgaCurFileId = vc_read_next_word();
      _vgaCurSpriteId = vc_read_next_word();

      tmp = TO_BE_16(vc_read_next_word());

      vc_ptr_org = _vcPtr;
      _vcPtr = (byte *)&tmp;
      vc23_setSpritePriority();

      _vcPtr = vc_ptr_org;
      _vgaCurSpriteId = sprite;
      _vgaCurFileId = file;
}

void SimonEngine::vc57_no_op() {
      /* unused */
}

void SimonEngine::vc_kill_sprite(uint file, uint sprite) {
      uint16 old_sprite_id, old_cur_file_id;
      VgaSleepStruct *vfs;
      VgaSprite *vsp;
      VgaTimerEntry *vte;
      const byte *vc_ptr_org;

      old_sprite_id = _vgaCurSpriteId;
      old_cur_file_id = _vgaCurFileId;
      vc_ptr_org = _vcPtr;

      _vgaCurFileId = file;
      _vgaCurSpriteId = sprite;

      vfs = _vgaSleepStructs;
      while (vfs->ident != 0) {
            if (vfs->sprite_id == _vgaCurSpriteId && ((_game & GF_SIMON1) || vfs->cur_vga_file == _vgaCurFileId)) {
                  while (vfs->ident != 0) {
                        memcpy(vfs, vfs + 1, sizeof(VgaSleepStruct));
                        vfs++;
                  }
                  break;
            }
            vfs++;
      }

      vsp = find_cur_sprite();
      if (vsp->id) {
            vc25_halt_sprite();

            vte = _vgaTimerList;
            while (vte->delay != 0) {
                  if (vte->sprite_id == _vgaCurSpriteId && ((_game & GF_SIMON1) || vte->cur_vga_file == _vgaCurFileId)) {
                        delete_vga_timer(vte);
                        break;
                  }
                  vte++;
            }
      }

      _vgaCurFileId = old_cur_file_id;
      _vgaCurSpriteId = old_sprite_id;
      _vcPtr = vc_ptr_org;
}

void SimonEngine::vc60_killSprite() {
      uint file;

      if (_game & GF_SIMON2) {
            file = vc_read_next_word();
      } else {
            file = _vgaCurFileId;
      }
      uint sprite = vc_read_next_word();
      vc_kill_sprite(file, sprite);
}

void SimonEngine::vc61_changeSprite() {
      VgaSprite *vsp = find_cur_sprite();

      vsp->image = vc_read_var_or_word();

      vsp->x += vc_read_next_word();
      vsp->y += vc_read_next_word();
      vsp->flags = 0x24;

      _vgaSpriteChanged++;
}

void SimonEngine::vc62_fastFadeOut() {
      uint i;

      vc29_stopAllSounds();

      if (!_videoVar3) {
            _videoVar3 = true;

            _videoNumPalColors = 256;
            if (_windowNum == 4)
                  _videoNumPalColors = 208;

            memcpy(_videoBuf1, _paletteBackup, _videoNumPalColors * sizeof(uint32));
            for (i = NUM_PALETTE_FADEOUT; i != 0; --i) {
                  palette_fadeout((uint32 *)_videoBuf1, _videoNumPalColors);
                  _system->setPalette(_videoBuf1, 0, _videoNumPalColors);
                  if (_fade)
                        _system->updateScreen();
                  delay(5);
            }

            if (_game & GF_SIMON1) {
                  uint16 params[5];                               /* parameters to vc10_draw */
                  VgaSprite *vsp;
                  VgaPointersEntry *vpe;
                  const byte *vc_ptr_org = _vcPtr;

                  vsp = _vgaSprites;
                  while (vsp->id != 0) {
                        if (vsp->id == 0x80) {
                              byte *old_file_1 = _curVgaFile1;
                              byte *old_file_2 = _curVgaFile2;
                              uint palmode = _windowNum;

                              vpe = &_vgaBufferPointers[vsp->fileId];
                              _curVgaFile1 = vpe->vgaFile1;
                              _curVgaFile2 = vpe->vgaFile2;
                              _windowNum = vsp->windowNum;

                              params[0] = READ_BE_UINT16(&vsp->image);
                              params[1] = READ_BE_UINT16(&vsp->palette);
                              params[2] = READ_BE_UINT16(&vsp->x);
                              params[3] = READ_BE_UINT16(&vsp->y);
                              params[4] = READ_BE_UINT16(&vsp->flags);
                              _vcPtr = (byte *)params;
                              vc10_draw();

                              _windowNum = palmode;
                              _curVgaFile1 = old_file_1;
                              _curVgaFile2 = old_file_2;
                              break;
                        }
                        vsp++;
                  }
                  _vcPtr = vc_ptr_org;
            }

            // Allow one section of Simon the Sorcerer 1 introduction to be displayed
            // in lower half of screen
            if ((_game & GF_SIMON1) && (_subroutine == 2923 || _subroutine == 2926))
                  dx_clear_surfaces(200);
            else
                  dx_clear_surfaces(_windowNum == 4 ? 134 : 200);
      }
      if (_game & GF_SIMON2) {
            if (_nextMusicToPlay != -1)
                  loadMusic(_nextMusicToPlay);
      }

}

void SimonEngine::vc63_fastFadeIn() {
      _paletteColorCount = 208;
      if (_windowNum != 4) {
            _paletteColorCount = 256;
      }
      _videoVar3 = false;
}

void SimonEngine::vc64_skipIfSpeechEnded() {
      // Simon2
      if (!_sound->isVoiceActive() || (_subtitles && _language != 20))
            vc_skip_next_instruction();
}

void SimonEngine::vc65_slowFadeIn() {
      // Simon2
      _paletteColorCount = 624;
      _videoNumPalColors = 208;
      if (_windowNum != 4) {
            _paletteColorCount = 768;
            _videoNumPalColors = 256;
      }
      _paletteColorCount |= 0x8000;
      _videoVar3 = false;
}

void SimonEngine::vc66_skipIfNotEqual() {
      // Simon2
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();

      if (vc_read_var(a) != vc_read_var(b))
            vc_skip_next_instruction();
}

void SimonEngine::vc67_skipIfGE() {
      // Simon2
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();

      if (vc_read_var(a) >= vc_read_var(b))
            vc_skip_next_instruction();
}

void SimonEngine::vc68_skipIfLE() {
      // Simon2
      uint a = vc_read_next_word();
      uint b = vc_read_next_word();

      if (vc_read_var(a) <= vc_read_var(b))
            vc_skip_next_instruction();
}

void SimonEngine::vc69_playTrack() {
      // Simon2
      int16 track = vc_read_next_word();
      int16 loop = vc_read_next_word();

      // Jamieson630:
      // This is a "play track". The original
      // design stored the track to play if one was
      // already in progress, so that the next time a
      // "fill MIDI stream" event occured, the MIDI
      // player would find the change and switch
      // tracks. We use a different architecture that
      // allows for an immediate response here, but
      // we'll simulate the variable changes so other
      // scripts don't get thrown off.
      // NOTE: This opcode looks very similar in function
      // to vc72(), except that vc72() may allow for
      // specifying a non-valid track number (999 or -1)
      // as a means of stopping what music is currently
      // playing.
      midi.setLoop(loop != 0);
      midi.startTrack(track);
}

void SimonEngine::vc70_queueMusic() {
      // Simon2
      uint16 track = vc_read_next_word();
      uint16 loop = vc_read_next_word();

      // Jamieson630:
      // This sets the "on end of track" action.
      // It specifies whether to loop the current
      // track and, if not, whether to switch to
      // a different track upon completion.
      if (track != 0xFFFF && track != 999)
            midi.queueTrack(track, loop != 0);
      else
            midi.setLoop(loop != 0);
}

void SimonEngine::vc71_checkMusicQueue() {
      // Simon2
      // Jamieson630:
      // This command skips the next instruction
      // unless (1) there is a track playing, AND
      // (2) there is a track queued to play after it.
      if (!midi.isPlaying (true))
            vc_skip_next_instruction();
}

void SimonEngine::vc72_play_track_2() {
      // Simon2
      // Jamieson630:
      // This is a "play or stop track". Note that
      // this opcode looks very similar in function
      // to vc69(), except that this opcode may allow
      // for specifying a track of 999 or -1 in order to
      // stop the music. We'll code it that way for now.

      // NOTE: It's possible that when "stopping" a track,
      // we're supposed to just go on to the next queued
      // track, if any. Must find out if there is ANY
      // case where this is used to stop a track in the
      // first place.

      int16 track = vc_read_next_word();
      int16 loop = vc_read_next_word();

      if (track == -1 || track == 999) {
            midi.stop();
      } else {
            midi.setLoop (loop != 0);
            midi.startTrack (track);
      }
}

void SimonEngine::vc73_setMark() {
      // Simon2
      vc_read_next_byte();
      _marks |= 1 << vc_read_next_byte();
}

void SimonEngine::vc74_clearMark() {
      // Simon2
      vc_read_next_byte();
      _marks &= ~(1 << vc_read_next_byte());
}

void SimonEngine::vc75_setScale() {
      // Set scale
      int baseY = vc_read_next_word();
      int scale = vc_read_next_word();
      debug(0, "STUB: vc75_setScale: baseY %d scale %d", baseY, scale);
}

void SimonEngine::vc76_setScaleXOffs() {
      // Scale X related
      int image = vc_read_next_word();
      int xoffs = vc_read_next_word();
      int var = vc_read_next_word();
      debug(0, "STUB: vc76_setScaleXOffs: image %d xoffs %d flag %d", image, xoffs, var);
}

void SimonEngine::vc77_setScaleYOffs() {
      // Scale Y related
      int image = vc_read_next_word();
      int yoffs = vc_read_next_word();
      int var = vc_read_next_word();
      debug(0, "STUB: vc77_setScaleYOffs: image %d yoffs %d flag %d", image, yoffs, var);
}

void SimonEngine::vc78_pathUnk1() {
      // Pathfinder related
      debug(0, "STUB: vc78_pathUnk1");
}

void SimonEngine::vc79_pathUnk2() {
      // Pathfinder related
      debug(0, "STUB: vc79_pathUnk2");
}

void SimonEngine::vc80_setOverlayImage() {
      VgaSprite *vsp = find_cur_sprite();

      vsp->image = vc_read_var_or_word();

      vsp->x += vc_read_next_word();
      vsp->y += vc_read_next_word();
      vsp->flags = 0x10;

      _vgaSpriteChanged++;
}

void SimonEngine::vc81_setRandom() {
      uint var = vc_read_next_word();
      uint value = vc_read_next_word();
      writeVariable(var, _rnd.getRandomNumber(value - 1));
}

void SimonEngine::vc82_pathUnk3() {
      // Set var to path position
      int var = vc_read_next_word();
      debug(0, "STUB: vc82_pathUnk3: var %d", var);
}

void SimonEngine::vc83_playSoundLoop() {
      // Start looping sound effect
      int snd = vc_read_next_word();
      int vol = vc_read_next_word();
      int pan = vc_read_next_word();
      debug(0, "STUB: vc83_playSoundLoop: snd %d vol %d pan %d", snd, vol, pan);
}

void SimonEngine::vc84_stopSoundLoop() {
      // Stop looping sound effect
      debug(0, "STUB: vc84_stopSoundLoop");
}

} // End of namespace Simon

Generated by  Doxygen 1.6.0   Back to index