/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001/2002 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/simon/simonvga.cpp,v 1.2 2002/07/07 19:06:48 fingolfin Exp $
 *
 */

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

typedef void (SimonState::*VgaOpcodeProc) ();
static const uint16 vc_get_out_of_code = 0;

// Opcode tables
static const VgaOpcodeProc vga_opcode_table[] = {
	NULL,
	&SimonState::vc_1,
	&SimonState::vc_2,
	&SimonState::vc_3,
	&SimonState::vc_4,
	&SimonState::vc_5,
	&SimonState::vc_6_maybe_skip_3_inv,
	&SimonState::vc_7_maybe_skip_3,
	&SimonState::vc_8_maybe_skip_2,
	&SimonState::vc_9_maybe_skip,
	&SimonState::vc_10,
	&SimonState::vc_11_clear_pathfind_array,
	&SimonState::vc_12_sleep_variable,
	&SimonState::vc_13_offset_x,
	&SimonState::vc_14_offset_y,
	&SimonState::vc_15_start_funkystruct_by_id,
	&SimonState::vc_16_setup_funkystruct,
	&SimonState::vc_17_set_pathfind_item,
	&SimonState::vc_18_jump_rel,
	&SimonState::vc_19,
	&SimonState::vc_20,
	&SimonState::vc_21,
	&SimonState::vc_22,
	&SimonState::vc_23_set_pri,
	&SimonState::vc_24_set_image_xy,
	&SimonState::vc_25_del_sprite_and_get_out,
	&SimonState::vc_26,
	&SimonState::vc_27_reset,
	&SimonState::vc_28,
	&SimonState::vc_29_stop_all_sounds,
	&SimonState::vc_30_set_base_delay,
	&SimonState::vc_31_set_palette_mode,
	&SimonState::vc_32_copy_var,
	&SimonState::vc_33,
	&SimonState::vc_34,
	&SimonState::vc_35,
	&SimonState::vc_36,
	&SimonState::vc_37_sprite_unk3_add,
	&SimonState::vc_38_skip_if_var_zero,
	&SimonState::vc_39_set_var,
	&SimonState::vc_40_var_add,
	&SimonState::vc_41_var_sub,
	&SimonState::vc_42_delay_if_not_eq,
	&SimonState::vc_43_skip_if_bit_clear,
	&SimonState::vc_44_skip_if_bit_set,
	&SimonState::vc_45_set_x,
	&SimonState::vc_46_set_y,
	&SimonState::vc_47_add_var_f,
	&SimonState::vc_48,
	&SimonState::vc_49_set_bit,
	&SimonState::vc_50_clear_bit,
	&SimonState::vc_51_clear_hitarea_bit_0x40,
	&SimonState::vc_52,
	&SimonState::vc_53_no_op,
	&SimonState::vc_54_no_op,
	&SimonState::vc_55_offset_hit_area,
	&SimonState::vc_56_no_op,
	&SimonState::vc_57_no_op,
	&SimonState::vc_58,
	&SimonState::vc_59,
	&SimonState::vc_60,
	&SimonState::vc_61_sprite_change,
	&SimonState::vc_62,
	&SimonState::vc_63,
	&SimonState::vc_64,
	&SimonState::vc_65,
	&SimonState::vc_66,
	&SimonState::vc_67,
	&SimonState::vc_68,
	&SimonState::vc_69,
	&SimonState::vc_70,
	&SimonState::vc_71,
	&SimonState::vc_72,
	&SimonState::vc_73,
	&SimonState::vc_74,
};

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

#ifdef DUMP_CONTINOUS_VGASCRIPT
		if ((void *)_vc_ptr != (void *)&vc_get_out_of_code) {
//      if (_vga_cur_sprite_id==62 && _vga_cur_file_id==68 ||
//          _vga_cur_sprite_id==1 && _vga_cur_file_id==2) {
			fprintf(_dump_file, "%.5d %.5X: %5d %4d ", _vga_tick_counter, _vc_ptr - _cur_vga_file_1,
							_vga_cur_sprite_id, _vga_cur_file_id);
			dump_video_script(_vc_ptr, true);
//      }
		}
#endif

		if (!(_game & GAME_SIMON2)) {
			opcode = READ_BE_UINT16_UNALIGNED(_vc_ptr);
			_vc_ptr += 2;
		} else {
			opcode = *_vc_ptr++;
		}

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

		if (opcode == 0)
			return;

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

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

uint SimonState::vc_read_next_word()
{
	uint a = READ_BE_UINT16_UNALIGNED(_vc_ptr);
	_vc_ptr += 2;
	return a;
}

uint SimonState::vc_read_next_byte()
{
	return *_vc_ptr++;
}


void SimonState::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
	};

	if (_game & GAME_SIMON2) {
		uint opcode = vc_read_next_byte();
		_vc_ptr += opcode_param_len_simon2[opcode];
	} else {
		uint opcode = vc_read_next_word();
		_vc_ptr += opcode_param_len_simon1[opcode];
	}

#ifdef DUMP_CONTINOUS_VGASCRIPT
	fprintf(_dump_file, "; skipped\n");
#endif
}

void SimonState::o_read_vgares_23()
{																// Simon1 Only
	if (_vga_res_328_loaded == true) {
		_vga_res_328_loaded = false;
		_lock_word |= 0x4000;
		read_vga_from_datfile_1(23);
		_lock_word &= ~0x4000;
	}
}

void SimonState::o_read_vgares_328()
{																// Simon1 Only
	if (_vga_res_328_loaded == false) {
		_vga_res_328_loaded = true;
		_lock_word |= 0x4000;
		read_vga_from_datfile_1(328);
		_lock_word &= ~0x4000;
	}
}


// VGA Script commands
void SimonState::vc_1()
{
	/* dummy opcode */
	_vc_ptr += 6;
}

void SimonState::vc_2()
{
	VgaPointersEntry *vpe;
	uint num;
	uint res;
	byte *old_file_1, *old_file_2;
	byte *b, *bb, *vc_ptr_org;

	num = vc_read_var_or_word();

	old_file_1 = _cur_vga_file_1;
	old_file_2 = _cur_vga_file_2;

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

		_cur_vga_file_1 = vpe->vgaFile1;
		_cur_vga_file_2 = vpe->vgaFile2;
		if (vpe->vgaFile1 != NULL)
			break;
		if (_vga_cur_file_2 != res)
			_video_var_7 = _vga_cur_file_2;

		ensureVgaResLoaded(res);
		_video_var_7 = 0xFFFF;
	}


	bb = _cur_vga_file_1;
	b = bb + READ_BE_UINT16_UNALIGNED(&((VgaFile1Header *) bb)->hdr2_start);
	b = bb + READ_BE_UINT16_UNALIGNED(&((VgaFile1Header2 *) b)->unk2_offs);

	while (READ_BE_UINT16_UNALIGNED(&((VgaFile1Struct0x8 *) b)->id) != num)
		b += sizeof(VgaFile1Struct0x8);

	vc_ptr_org = _vc_ptr;

	_vc_ptr = _cur_vga_file_1 + READ_BE_UINT16_UNALIGNED(&((VgaFile1Struct0x8 *) b)->script_offs);


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

	_cur_vga_file_1 = old_file_1;
	_cur_vga_file_2 = old_file_2;

	_vc_ptr = vc_ptr_org;
}

void SimonState::vc_3()
{
	uint16 a, b, c, d, e, f;
	uint16 res;
	VgaSprite *vsp;
	VgaPointersEntry *vpe;
	byte *p, *pp;

	a = vc_read_next_word();			/* 0 */

	if (_game & GAME_SIMON2) {
		f = vc_read_next_word();		/* 0 */
		b = vc_read_next_word();		/* 2 */
	} else {
		b = vc_read_next_word();		/* 2 */
		f = b / 100;
	}

	c = vc_read_next_word();			/* 4 */
	d = vc_read_next_word();			/* 6 */
	e = vc_read_next_word();			/* 8 */

	/* 2nd param ignored with simon1 */
	if (has_vgastruct_with_id(b, f))
		return;

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

	vsp->base_color = e;
	vsp->unk6 = a;
	vsp->unk5 = 0;
	vsp->unk4 = 0;
	vsp->image = 0;
	vsp->x = c;
	vsp->y = d;
	vsp->id = b;
	vsp->unk7 = res = f;

	for (;;) {
		vpe = &_vga_buffer_pointers[res];
		_cur_vga_file_1 = vpe->vgaFile1;

		if (vpe->vgaFile1 != NULL)
			break;
		if (res != _vga_cur_file_2)
			_video_var_7 = res;

		ensureVgaResLoaded(res);
		_video_var_7 = 0xFFFF;
	}

	pp = _cur_vga_file_1;
	p = pp + READ_BE_UINT16_UNALIGNED(&((VgaFile1Header *) pp)->hdr2_start);
	p = pp + READ_BE_UINT16_UNALIGNED(&((VgaFile1Header2 *) p)->id_table);

	while (READ_BE_UINT16_UNALIGNED(&((VgaFile1Struct0x6 *) p)->id) != b)
		p += sizeof(VgaFile1Struct0x6);

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

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

//  dump_vga_script(_cur_vga_file_1 + READ_BE_UINT16_UNALIGNED(&((VgaFile1Struct0x6*)p)->script_offs), res, b);

	add_vga_timer(gss->VGA_DELAY_BASE,
								_cur_vga_file_1 + READ_BE_UINT16_UNALIGNED(&((VgaFile1Struct0x6 *) p)->script_offs),
								b, res);
}

void SimonState::vc_4()
{
	/* dummy opcode */
	_vc_ptr += 6;
}

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

void SimonState::vc_6_maybe_skip_3_inv()
{
	if (!vc_maybe_skip_proc_3(vc_read_next_word()))
		vc_skip_next_instruction();
}

void SimonState::vc_7_maybe_skip_3()
{
	if (vc_maybe_skip_proc_3(vc_read_next_word()))
		vc_skip_next_instruction();
}

void SimonState::vc_8_maybe_skip_2()
{
	uint a = vc_read_next_word();
	uint b = vc_read_next_word();
	if (!vc_maybe_skip_proc_2(a, b))
		vc_skip_next_instruction();
}

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

struct VC10_state {
	int image;
	uint16 e;
	int x, y;

	byte base_color;

	uint draw_width, draw_height;
	uint x_skip, y_skip;

	byte *surf2_addr;
	uint surf2_pitch;

	byte *surf_addr;
	uint surf_pitch;

	byte dl, dh;

	byte *depack_src;
	int8 depack_cont;

	byte depack_dest[200];
};

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

	if (a != -0x80)
		goto start_here;

	for (;;) {
		a = *src++;
	start_here:;
		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);
		}
	}

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

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

byte *SimonState::vc_10_depack_swap(byte *src, uint w, uint h)
{
	w <<= 3;

	{
		byte *dst_org = _video_buf_1 + 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 = _video_buf_1 + 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 _video_buf_1;

}

byte *vc_10_no_depack_swap(byte *src)
{
	error("vc_10_no_depack_swap unimpl");
}

/* 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 SimonState::vc_10_helper_8(byte *dst, byte *src)
{
	const uint pitch = _dx_surface_pitch;
	int8 reps = (int8)0x80;
	byte color;
	byte *dst_org = dst;
	uint h = _vga_var5, 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 = _vga_var5;
				}
			} 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 = _vga_var5;
				}
			} while (++reps != 0);
		}
	}
}

void SimonState::vc_10()
{
	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;

//  if (_vga_cur_sprite_id != 802)
//    return;

	state.base_color = (_vc_ptr[1] << 4);
	_vc_ptr += 2;
	state.x = (int16)vc_read_next_word();
	if (_game & GAME_SIMON2) {
		state.x -= _x_scroll;
	}
	state.y = (int16)vc_read_next_word();

	if (!(_game & GAME_SIMON2)) {
		state.e = vc_read_next_word();
	} else {
		state.e = vc_read_next_byte();
	}

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

	p2 = _cur_vga_file_2 + state.image * 8;
	state.depack_src = _cur_vga_file_2 + READ_BE_UINT32_UNALIGNED(&*(uint32 *)p2);

	width = READ_BE_UINT16_UNALIGNED(p2 + 6) >> 4;
	height = p2[5];
	flags = p2[4];

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

#ifdef DUMP_DRAWN_BITMAPS
	dump_single_bitmap(_vga_cur_file_id, state.image, state.depack_src, width * 16, height,
										 state.base_color);
#endif

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

	if (_game & GAME_SIMON2 && width >= 21) {
		byte *src, *dst;
		uint w;

		_vga_var1 = width * 2 - 40;
		_vga_var7 = state.depack_src;
		_vga_var5 = height;
		if (_variableArray[34] == -1)
			state.x = _variableArray[502 / 2];

		_x_scroll = state.x;

		vc_write_var(0xfb, _x_scroll);

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

		w = 40;
		do {
			vc_10_helper_8(dst, src + READ_BE_UINT32_UNALIGNED(&*(uint32 *)src));
			dst += 8;
			src += 4;
		} while (--w);

		dx_unlock_attached();


		return;
	}

	if (state.e & 0x10)
		state.depack_src = vc_10_depack_swap(state.depack_src, width, height);
	else if (state.e & 1)
		state.depack_src = vc_10_no_depack_swap(state.depack_src);


	vlut = &_video_windows[_video_palette_mode * 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 = _dx_surface_pitch;

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

	{
		uint offs = ((vlut[0] - _video_windows[16]) * 2 + state.x) * 8;
		uint 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.e & 0x20) {
		byte *mask, *src, *dst;
		byte h;
		uint w;

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

		vc_10_skip_cols(&state);

		/* XXX: implement transparency */

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

			h = state.draw_height;
			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);

		/* vc_10_helper_5 */
	} else if (_lock_word & 0x20 && state.base_color == 0 || state.base_color == 0xC0) {
		byte *src, *dst;
		uint h, i;

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

			state.draw_width *= 2;

			if (state.e & 2) {
				/* no transparency */
				h = state.draw_height;
				do {
					memcpy(dst, src, state.draw_width);
					dst += 320;
					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 += 320;
					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.e & 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 += 320;
				} 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 += 320;
				} while (--state.draw_height);
			}
		}
		/* vc_10_helper_4 */
	} else {
		if (_game & GAME_SIMON2 && state.e & 0x4 && _bit_array[10] & 0x800) {
			state.surf_addr = state.surf2_addr;
			state.surf_pitch = state.surf2_pitch;
			warning("vc_10: (state.e&0x4)");
		}

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

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

			vc_10_skip_cols(&state);

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

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

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

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

			src = state.depack_src + (width * state.y_skip) * 8;
			dst = state.surf_addr;
			state.x_skip <<= 2;
			if (state.e & 2) {
				do {
					for (count = 0; count != state.draw_width; count++) {
						dst[count * 2] = (src[count + state.x_skip] >> 4) | state.base_color;
						dst[count * 2 + 1] = (src[count + state.x_skip] & 15) | state.base_color;
					}
					dst += 320;
					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.base_color;
						color = (src[count + state.x_skip] & 15);
						if (color)
							dst[count * 2 + 1] = color | state.base_color;
					}
					dst += 320;
					src += width * 8;
				} while (--state.draw_height);

			}

			/* vc_10_helper_7 */
		}
	}

	dx_unlock_2();
	dx_unlock_attached();

}

void SimonState::vc_11_clear_pathfind_array()
{
	memset(&_pathfind_array, 0, sizeof(_pathfind_array));
}

void SimonState::vc_12_sleep_variable()
{
	uint num;

	if (!(_game & GAME_SIMON2)) {
		num = vc_read_var_or_word();
	} else {
		num = vc_read_next_byte() * _vga_base_delay;
	}

	add_vga_timer(num + gss->VGA_DELAY_BASE, _vc_ptr, _vga_cur_sprite_id, _vga_cur_file_id);
	_vc_ptr = (byte *)&vc_get_out_of_code;
}

void SimonState::vc_13_offset_x()
{
	VgaSprite *vsp = find_cur_sprite();
	int16 a = vc_read_next_word();
	vsp->x += a;
	_vga_sprite_changed++;
}

void SimonState::vc_14_offset_y()
{
	VgaSprite *vsp = find_cur_sprite();
	int16 a = vc_read_next_word();
	vsp->y += a;
	_vga_sprite_changed++;
}

/* wakeup_id */
void SimonState::vc_15_start_funkystruct_by_id()
{
	VgaSleepStruct *vfs = _vga_sleep_structs, *vfs_tmp;
	uint16 id = vc_read_next_word();
	while (vfs->ident != 0) {
		if (vfs->ident == id) {
			add_vga_timer(gss->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 == _vga_wait_for)
		_vga_wait_for = 0;
}


/* sleep_on_id */
void SimonState::vc_16_setup_funkystruct()
{
	VgaSleepStruct *vfs = _vga_sleep_structs;
	while (vfs->ident)
		vfs++;

	vfs->ident = vc_read_next_word();
	vfs->code_ptr = _vc_ptr;
	vfs->sprite_id = _vga_cur_sprite_id;
	vfs->cur_vga_file = _vga_cur_file_id;

	_vc_ptr = (byte *)&vc_get_out_of_code;
}

void SimonState::vc_17_set_pathfind_item()
{
	uint a = vc_read_next_word();
	_pathfind_array[a - 1] = (uint16 *)_vc_ptr;
	while (READ_BE_UINT16_UNALIGNED(_vc_ptr) != 999)
		_vc_ptr += 4;
	_vc_ptr += 2;
}

void SimonState::vc_18_jump_rel()
{
	int16 offs = vc_read_next_word();
	_vc_ptr += offs;
}

/* chain to script? */
void SimonState::vc_19()
{
	/* XXX: not implemented */
	error("vc_19: chain to script not implemented");
}


/* helper routines */

/* write unaligned 16-bit */
static void write_16_le(void *p, uint16 a)
{
	((byte *)p)[0] = (byte)(a);
	((byte *)p)[1] = (byte)(a >> 8);
}

/* read unaligned 16-bit */
static uint16 read_16_le(void *p)
{
	return ((byte *)p)[0] | (((byte *)p)[1] << 8);
}

/* FIXME: unaligned access */
void SimonState::vc_20()
{
	uint16 a = vc_read_next_word();
	write_16_le(_vc_ptr, a);
	_vc_ptr += 2;
}

/* FIXME: unaligned access */
void SimonState::vc_21()
{
	if (!(_game & GAME_SIMON2)) {
		int16 a = vc_read_next_word();
		byte *tmp = _vc_ptr + a;
		uint16 val = read_16_le(tmp + 4);

		if (val != 0) {
			write_16_le(tmp + 4, val - 1);
			_vc_ptr = tmp + 6;
		}
	} else {
		int16 a = vc_read_next_word();
		byte *tmp = _vc_ptr + a;
		uint16 val = read_16_le(tmp + 3);

		if (val != 0) {
			write_16_le(tmp + 3, val - 1);
			_vc_ptr = tmp + 5;
		}
	}
}

void SimonState::vc_22()
{
	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 = _cur_vga_file_1 + 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);

	_video_var_9 = 2;
	_vga_sprite_changed++;
}

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

	if (vsp->id == 0) {
		warning("tried to set pri for unknown id %d", _vga_cur_sprite_id);
		return;
	}

	memcpy(&bak, vsp, sizeof(bak));
	bak.unk5 = pri;
	bak.unk6 |= 0x8000;

	vus2 = vsp;

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

void SimonState::vc_24_set_image_xy()
{
	VgaSprite *vsp = find_cur_sprite();
	vsp->image = vc_read_var_or_word();

	if (vsp->id == 0) {
		warning("Trying to set XY of nonexistent sprite '%d'", _vga_cur_sprite_id);
	}

	vsp->x += (int16)vc_read_next_word();
	vsp->y += (int16)vc_read_next_word();
	if (!(_game & GAME_SIMON2)) {
		vsp->unk4 = vc_read_next_word();
	} else {
		vsp->unk4 = vc_read_next_byte();
	}

	_vga_sprite_changed++;
}

void SimonState::vc_25_del_sprite_and_get_out()
{
	VgaSprite *vsp = find_cur_sprite();
	while (vsp->id != 0) {
		memcpy(vsp, vsp + 1, sizeof(VgaSprite));
		vsp++;
	}
	_vc_ptr = (byte *)&vc_get_out_of_code;
	_vga_sprite_changed++;
}

void SimonState::vc_26()
{
	uint16 *as = &_video_windows[vc_read_next_word() * 4];
	as[0] = vc_read_next_word();
	as[1] = vc_read_next_word();
	as[2] = vc_read_next_word();
	as[3] = vc_read_next_word();
}

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

	_lock_word |= 8;

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

	vsp = _vga_sprites;
	while (vsp->id) {
		if (vsp->id == 128) {
			memcpy(&bak, vsp, sizeof(VgaSprite));
		}
		vsp->id = 0;
		vsp++;
	}

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

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


	vte = _vga_timer_list;
	while (vte->delay) {
		if (vte->sprite_id != 0x80) {
			vte2 = vte;
			while (vte2->delay) {
				memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry));
				vte2++;
			}
		} else {
			vte++;
		}
	}

	vc_write_var(0xFE, 0);

	_lock_word &= ~8;
}


void SimonState::vc_27_reset_simon2()
{
	_lock_word |= 8;

	{
		VgaSprite *vsp = _vga_sprites;
		while (vsp->id) {
			vsp->id = 0;
			vsp++;
		}
	}

	{
		VgaSleepStruct *vfs = _vga_sleep_structs;
		while (vfs->ident) {
			vfs->ident = 0;
			vfs++;
		}
	}

	{
		VgaTimerEntry *vte = _vga_timer_list;
		while (vte->delay) {
			VgaTimerEntry *vte2 = vte;
			while (vte2->delay) {
				memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry));
				vte2++;
			}
		}
	}

	vc_write_var(0xFE, 0);

	_lock_word &= ~8;
}

void SimonState::vc_27_reset()
{
	if (!(_game & GAME_SIMON2))
		vc_27_reset_simon1();
	else
		vc_27_reset_simon2();
}

void SimonState::vc_28()
{
	/* dummy opcode */
	_vc_ptr += 8;
}

void SimonState::vc_29_stop_all_sounds()
{
	_mixer->stop_all();
}

void SimonState::vc_30_set_base_delay()
{
	_vga_base_delay = vc_read_next_word();
}

void SimonState::vc_31_set_palette_mode()
{
	_video_palette_mode = vc_read_next_word();
}

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

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

void SimonState::vc_32_copy_var()
{
	uint16 a = vc_read_var(vc_read_next_word());
	vc_write_var(vc_read_next_word(), a);
}

void SimonState::vc_33()
{
	if (_lock_counter != 0) {
		_lock_counter = 1;
		unlock();
	}
}

void SimonState::vc_34()
{
	lock();
	_lock_counter = 200;
	_left_button_down = 0;
}

void SimonState::vc_35()
{
	/* not used? */
	_vc_ptr += 4;
	_vga_sprite_changed++;
}

void SimonState::vc_36()
{
	uint vga_res = vc_read_next_word();
	uint mode = vc_read_next_word();

	if (!(_game & GAME_SIMON2)) {
		if (mode == 16) {
			_copy_partial_mode = 2;
		} else {
			set_video_mode_internal(mode, vga_res);
		}
	} else {
		set_video_mode_internal(mode, vga_res);
	}
}

void SimonState::vc_37_sprite_unk3_add()
{
	VgaSprite *vsp = find_cur_sprite();
	vsp->y += vc_read_var(vc_read_next_word());
	_vga_sprite_changed++;
}

void SimonState::vc_38_skip_if_var_zero()
{
	uint var = vc_read_next_word();
	if (vc_read_var(var) == 0)
		vc_skip_next_instruction();
}

void SimonState::vc_39_set_var()
{
	uint var = vc_read_next_word();
	int16 value = vc_read_next_word();
	vc_write_var(var, value);
}

void SimonState::vc_40_var_add()
{
	uint var = vc_read_next_word();
	int16 value = vc_read_var(var) + vc_read_next_word();

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

		if (_vga_var2 != 0) {
			if (_vga_var2 >= 0)
				goto no_scroll;
			_vga_var2 = 0;
		} else {
			if (_vga_var3 != 0)
				goto no_scroll;
		}

		if (value - _x_scroll >= 30) {
			_vga_var2 = 20;
			tmp = _vga_var1 - _x_scroll;
			if (tmp < 20)
				_vga_var2 = tmp;
			add_vga_timer(10, NULL, 0, 0);	/* special timer */
		}
	}
no_scroll:;

	vc_write_var(var, value);
}

void SimonState::vc_41_var_sub()
{
	uint var = vc_read_next_word();
	int16 value = vc_read_var(var) - vc_read_next_word();

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

		if (_vga_var2 != 0) {
			if (_vga_var2 < 0)
				goto no_scroll;
			_vga_var2 = 0;
		} else {
			if (_vga_var3 != 0)
				goto no_scroll;
		}

		if ((uint16)(value - _x_scroll) < 11) {
			_vga_var2 = -20;
			tmp = _vga_var1 - _x_scroll;
			if (_x_scroll < 20)
				_vga_var2 = -_x_scroll;
			add_vga_timer(10, NULL, 0, 0);	/* special timer */
		}
	}
no_scroll:;

	vc_write_var(var, value);
}

void SimonState::vc_42_delay_if_not_eq()
{
	uint val = vc_read_var(vc_read_next_word());
	if (val == vc_read_next_word()) {

		add_vga_timer(_vga_base_delay + 1, _vc_ptr - 4, _vga_cur_sprite_id, _vga_cur_file_id);
		_vc_ptr = (byte *)&vc_get_out_of_code;
	}
}

void SimonState::vc_43_skip_if_bit_clear()
{
	if (!vc_get_bit(vc_read_next_word())) {
		vc_skip_next_instruction();
	}
}

void SimonState::vc_44_skip_if_bit_set()
{
	if (vc_get_bit(vc_read_next_word())) {
		vc_skip_next_instruction();
	}
}

void SimonState::vc_45_set_x()
{
	VgaSprite *vsp = find_cur_sprite();
	vsp->x = vc_read_var(vc_read_next_word());
	_vga_sprite_changed++;
}

void SimonState::vc_46_set_y()
{
	VgaSprite *vsp = find_cur_sprite();
	vsp->y = vc_read_var(vc_read_next_word());
	_vga_sprite_changed++;
}

void SimonState::vc_47_add_var_f()
{
	uint var = vc_read_next_word();
	vc_write_var(var, vc_read_var(var) + vc_read_var(vc_read_next_word()));
}

void SimonState::vc_48()
{
	uint a = (uint16)_variableArray[12];
	uint b = (uint16)_variableArray[13];
	int c = _variableArray[14];
	uint16 *p = _pathfind_array[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 = READ_BE_UINT16_UNALIGNED(p);
		p += step;
		y1 = READ_BE_UINT16_UNALIGNED(p) - y2;

//    assert(READ_BE_UINT16_UNALIGNED(&p[1]) != 999);

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

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

}

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

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

void SimonState::vc_49_set_bit()
{
	vc_set_bit_to(vc_read_next_word(), true);
}

void SimonState::vc_50_clear_bit()
{
	vc_set_bit_to(vc_read_next_word(), false);
}

void SimonState::vc_51_clear_hitarea_bit_0x40()
{
	clear_hitarea_bit_0x40(vc_read_next_word());
}

void SimonState::vc_52()
{
	uint16 a = vc_read_next_word();

	if (!(_game & GAME_SIMON2)) {
		playSound(a);
	} else {
		if (a >= 0x8000) {
			a = -a;
			warning("vc_52(%d): unimpl");
		} else {
			playSound(a);
		}
	}
}

void SimonState::vc_53_no_op()
{
	/* no op */
}

void SimonState::vc_54_no_op()
{
	/* no op */
}

void SimonState::vc_55_offset_hit_area()
{
	HitArea *ha = _hit_areas;
	uint count = ARRAYSIZE(_hit_areas);
	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;
	}

	_need_hitarea_recalc++;
}

void SimonState::vc_56_no_op()
{
	/* No-Op in simon1 */
	if (_game & GAME_SIMON2) {
		uint num = vc_read_var_or_word() * _vga_base_delay;

#ifdef DUMP_CONTINOUS_VGASCRIPT
		fprintf(_dump_file, "; sleep_ex = %d\n", num + gss->VGA_DELAY_BASE);
#endif

		add_vga_timer(num + gss->VGA_DELAY_BASE, _vc_ptr, _vga_cur_sprite_id, _vga_cur_file_id);
		_vc_ptr = (byte *)&vc_get_out_of_code;
	}
}

void SimonState::vc_59()
{
	if (_game & GAME_SIMON2) {
		uint file = vc_read_next_word();
		uint start = vc_read_next_word();
		uint end = vc_read_next_word() + 1;

		do {
			vc_kill_thread(file, start);
		} while (++start != end);
	} else {
		if (vc_59_helper())
			vc_skip_next_instruction();
	}
}

void SimonState::vc_58()
{
	uint sprite = _vga_cur_sprite_id;
	uint file = _vga_cur_file_id;
	byte *vc_ptr;
	uint16 tmp;

	_vga_cur_file_id = vc_read_next_word();
	_vga_cur_sprite_id = vc_read_next_word();

	tmp = TO_BE_16(vc_read_next_word());

	vc_ptr = _vc_ptr;
	_vc_ptr = (byte *)&tmp;
	vc_23_set_pri();

	_vc_ptr = vc_ptr;
	_vga_cur_sprite_id = sprite;
	_vga_cur_file_id = file;
}

void SimonState::vc_57_no_op()
{
	/* no op */

}

void SimonState::vc_kill_thread(uint file, uint sprite)
{
	uint16 old_sprite_id, old_cur_file_id;
	VgaSleepStruct *vfs;
	VgaSprite *vsp;
	VgaTimerEntry *vte;
	byte *vc_org;

	old_sprite_id = _vga_cur_sprite_id;
	old_cur_file_id = _vga_cur_file_id;
	vc_org = _vc_ptr;

	_vga_cur_file_id = file;
	_vga_cur_sprite_id = sprite;

	vfs = _vga_sleep_structs;
	while (vfs->ident != 0) {
		if (vfs->sprite_id == _vga_cur_sprite_id
				&& (vfs->cur_vga_file == _vga_cur_file_id || !(_game & GAME_SIMON2))
			) {
			while (vfs->ident != 0) {
				memcpy(vfs, vfs + 1, sizeof(VgaSleepStruct));
				vfs++;
			}
			break;
		}
		vfs++;
	}

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

		vte = _vga_timer_list;
		while (vte->delay != 0) {
			if (vte->sprite_id == _vga_cur_sprite_id
					&& (vte->cur_vga_file == _vga_cur_file_id || !(_game & GAME_SIMON2))
				) {
				delete_vga_timer(vte);
				break;
			}
			vte++;
		}
	}

	_vga_cur_file_id = old_cur_file_id;
	_vga_cur_sprite_id = old_sprite_id;
	_vc_ptr = vc_org;
}


/* kill thread */
void SimonState::vc_60()
{
	uint file;

	if (_game & GAME_SIMON2) {
		file = vc_read_next_word();
	} else {
		file = _vga_cur_file_id;
	}
	uint sprite = vc_read_next_word();
	vc_kill_thread(file, sprite);
}

void SimonState::vc_61_sprite_change()
{
	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->unk4 = 36;

	_vga_sprite_changed++;
}

void SimonState::vc_62()
{
	uint i;
	byte *vc_ptr_org = _vc_ptr;


	vc_29_stop_all_sounds();

//  if (!_video_var_3) {
	_video_var_3 = true;
	_video_num_pal_colors = 256;
	if (_video_palette_mode == 4)
		_video_num_pal_colors = 208;
//  }

	memcpy(_video_buf_1, _palette_backup, _video_num_pal_colors * sizeof(uint32));
	for (i = NUM_PALETTE_FADEOUT; i != 0; --i) {
		palette_fadeout((uint32 *)_video_buf_1, _video_num_pal_colors);
		_system->set_palette(_video_buf_1, 0, _video_num_pal_colors);
		_system->update_screen();
		delay(5);
	}

	if (!(_game & GAME_SIMON2)) {
		uint16 params[5];						/* parameters to vc_10 */
		VgaSprite *vsp;
		VgaPointersEntry *vpe;

		vsp = _vga_sprites;
		while (vsp->id != 0) {
			if (vsp->id == 128) {
				byte *f1 = _cur_vga_file_1;
				byte *f2 = _cur_vga_file_2;
				uint palmode = _video_palette_mode;

				vpe = &_vga_buffer_pointers[vsp->unk7];
				_cur_vga_file_1 = vpe->vgaFile1;
				_cur_vga_file_2 = vpe->vgaFile2;
				_video_palette_mode = vsp->unk6;

				params[0] = READ_BE_UINT16_UNALIGNED(&vsp->image);
				params[1] = READ_BE_UINT16_UNALIGNED(&vsp->base_color);
				params[2] = READ_BE_UINT16_UNALIGNED(&vsp->x);
				params[3] = READ_BE_UINT16_UNALIGNED(&vsp->y);
				params[4] = READ_BE_UINT16_UNALIGNED(&vsp->unk4);
				_vc_ptr = (byte *)params;
				vc_10();

				_video_palette_mode = palmode;
				_cur_vga_file_1 = f1;
				_cur_vga_file_2 = f2;
				break;
			}
			vsp++;
		}
	}

	dx_clear_surfaces(_video_palette_mode == 4 ? 134 : 200);

	_vc_ptr = vc_ptr_org;
}

void SimonState::vc_63()
{
	_palette_color_count = 208;
	if (_video_palette_mode != 4) {
		_palette_color_count = 256;
	}
	_video_var_3 = false;
}

void SimonState::vc_64()
{																// Simon2
	if (vc_59_helper())
		vc_skip_next_instruction();
}

void SimonState::vc_65()
{																// Simon2
	error("vc_65 unimplemented");
}

void SimonState::vc_66()
{																// 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 SimonState::vc_67()
{																// 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 SimonState::vc_68()
{																// 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 SimonState::vc_69()
{																// Simon2
	uint16 a = vc_read_next_word();
	uint16 b = vc_read_next_word();

	warning("vc_69(%d,%d): music stuff?", a, b);
}

void SimonState::vc_70()
{																// Simon2
	uint16 a = vc_read_next_word();
	uint16 b = vc_read_next_word();

	_vc70_var1 = a;
	_vc70_var2 = b;

	warning("vc_70(%d,%d): music stuff?", a, b);
}


void SimonState::vc_71()
{																// Simon2
	if (_vc72_var3 == 0xFFFF && _vc72_var1 == 0xFFFF)
		vc_skip_next_instruction();
}

void SimonState::vc_72()
{																// Simon2
	uint16 a = vc_read_next_word();
	uint16 b = vc_read_next_word();
	if (a != _vc72_var1) {
		_vc72_var2 = b;
		_vc72_var3 = a;
	}

	warning("vc_72(%d,%d): music stuff?", a, b);
}

void SimonState::vc_73()
{																// Simon2
	vc_read_next_byte();
	_op_189_flags |= 1 << vc_read_next_byte();
}

void SimonState::vc_74()
{																// Simon2
	vc_read_next_byte();
	_op_189_flags &= ~(1 << vc_read_next_byte());
}
