diff scn2k/scn2k_grpimpl.cc @ 56:c7bcc0ec2267

* replaced Grp and Text classes by the TextImpl and GrpImpl ones * splitted scn2k.h into smaller header files * moved some definitions from scn2k_*.cc to the header files * moved opcode implementation to scn2k_*impl.cc
author thib
date Thu, 30 Apr 2009 19:05:09 +0000
parents
children e16e13d8cd68
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/scn2k/scn2k_grpimpl.cc
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2009 Thibaut GIRKA
+ * Copyright (c) 2004-2006  Kazunori "jagarl" Ueno
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "scn2k_grp.h"
+#include "system/system_config.h"
+#include "music2/music.h"
+#include "window/render.h"
+
+void Grp::impl_stackClear (Cmd& cmd) {
+	cmd.cmd_type = CMD_SAVECMDGRP_START;
+}
+
+void Grp::impl_grpBuffer (Cmd& cmd) {
+	const char* name = cmd.Str(cmd.args[0]);
+	int pdt = cmd.args[1].value;
+	eprintf("load surface %s pdt %d\n",name, pdt);
+	if (pdt == 0)
+		reserved_load_surface0 = name; // 画像読み込みは 01-1f:0000 まで待つ
+	else if (pdt == 1)
+		LoadSurface(name); // 背景絵読み込み?
+	else
+		LoadSurface(name, pdt);
+	cmd.cmd_type = CMD_SAVECMDGRP;
+}
+
+void Grp::impl_grpMulti(Cmd& cmd) {
+	int pos = cmd.args[0].value;
+	const char* name = cmd.Str(cmd.args[1]);
+	int sel = cmd.args[2].value;
+	eprintf("set foreground %s sel %d pos %d\n",name, sel, pos);
+	AddSurface(name);
+	StartAnm(sel);
+	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+	status = WAIT_ANM;
+	cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
+}
+
+void Grp::impl_grpOpen(Cmd& cmd) {
+	const char* name = cmd.Str(cmd.args[0]);
+	int sel = cmd.args[1].value;
+
+	if (name[0] == '?')
+		LoadSurface();
+	else if(cmd.cmd3 == 73)
+		LoadSurface(name, 1);
+	else
+		LoadSurface(name);
+
+	StartAnm(sel);
+	status = WAIT_ANM;
+	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+
+	if (name[0] == '?')
+		cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
+	else
+		cmd.cmd_type = CMD_SAVECMDGRP_START;
+}
+
+void Grp::impl_shake(Cmd& cmd) {
+	// shake screen
+	char key[11];
+	sprintf(key, "#SHAKE.%03d", cmd.args[0].value);
+	if (config->SearchParam(key) != 2) {
+		fprintf(stderr,"Cannot find shake pattern %d; use default pattern\n",cmd.args[0].value);
+		strcpy(key, "#SHAKE.000"); // default key
+	}
+	int num;
+	const int* pattern;
+	pattern = config->GetParamArray(key, num);
+	if (pattern) {
+		StartShake(num, pattern);
+		status = WAIT_SHAKE;
+	}
+	cmd.clear();
+}
+
+void Grp::impl_grpCopy(Cmd& cmd) {
+	if (cmd.cmd4 == 2) { // copy (KANOGI)
+		int sx = cmd.args[0].value;
+		int sy = cmd.args[1].value;
+		int w = cmd.args[2].value - sx;
+		int h = cmd.args[3].value - sy;
+		Rect rect(sx, sy, sx+w, sy+h);
+		int src = cmd.args[4].value;
+		int dx = cmd.args[5].value;
+		int dy = cmd.args[6].value;
+		int dest = cmd.args[7].value;
+		unsigned char alpha;
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		printf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
+		if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.clear();
+	}
+}
+
+void Grp::impl_recFill(Cmd& cmd) {
+	int x = cmd.args[0].value;
+	int y = cmd.args[1].value;
+	int w = cmd.args[2].value;
+	int h = cmd.args[3].value;
+	Rect rect(x, y, x+w, y+w);
+	int pdt = cmd.args[4].value;
+	int r = cmd.args[5].value;
+	int g = cmd.args[6].value;
+	int b = cmd.args[7].value;
+
+	if (cmd.cmd4 == 2) {
+		eprintf("clear %d:(%d,%d) size (%d,%d) r %d g %d b %d\n",pdt,x,y,w,h,r,g,b);
+		DSurfaceFill(Dsurface(pdt), rect, r, g, b);
+		// if (pdt == 0) screen->ReBlit(rect);
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+	else if (cmd.cmd4 == 3) { // alpha つきfill
+		int a = cmd.args[8].value;
+		eprintf("alpha-clear %d:(%d,%d) size (%d,%d) r %d g %d b %d a %d\n",pdt,x,y,w,h,r,g,b,a);
+		if (a <= 0) ;
+		else if (a >= 255) DSurfaceFill(Dsurface(pdt), rect, r, g, b);
+		else {
+			DSurfaceFill(Dsurface(WORKPDT), rect, r, g, b, a);
+			parent.Root().BlitSurface(Dsurface(WORKPDT), rect, Dsurface(pdt), rect);
+		}
+		// if (pdt == 0) screen->ReBlit(rect);
+		cmd.clear();
+	}
+}
+
+void Grp::impl_recCopy(Cmd& cmd) {
+	int sx = cmd.args[0].value;
+	int sy = cmd.args[1].value;
+	int w = cmd.args[2].value;
+	int h = cmd.args[3].value;
+	Rect rect(sx, sy, sx + w, sy + h);
+	int src = cmd.args[4].value;
+	int dx = cmd.args[5].value;
+	int dy = cmd.args[6].value;
+	int dest = cmd.args[7].value;
+
+	if (cmd.cmd4 == 2) {
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
+		//DSurfaceMove(Ssurface(src), Rect(sx,sy,sx+w,sy+h), Dsurface(dest), Rect(dx,dy));
+		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+
+	else if (cmd.cmd4 == 3) { // alpha つきcopy
+		unsigned char alpha;
+		if (cmd.args[8].value < 0) alpha = 0;
+		else if (cmd.args[8].value > 255) alpha = 255;
+		else alpha = cmd.args[8].value;
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		if (alpha != 0)
+			parent.Root().BlitSurface(Ssurface(src), rect, &alpha, Rect(0,0,1,1), Dsurface(dest), Rect(dx,dy), 0);
+		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.clear();
+	}
+}
+
+void Grp::impl_recAdd(Cmd& cmd) {
+	if (cmd.cmd4 == 3) { // saturate mode で alpha 付き copy
+		int sx = cmd.args[0].value;
+		int sy = cmd.args[1].value;
+		int w = cmd.args[2].value;
+		int h = cmd.args[3].value;
+		Rect rect(sx, sy, sx+w, sy+h);
+		int src = cmd.args[4].value;
+		int dx = cmd.args[5].value;
+		int dy = cmd.args[6].value;
+		int dest = cmd.args[7].value;
+		unsigned char alpha;
+		if (cmd.args[8].value < 0) alpha = 0;
+		else if (cmd.args[8].value > 255) alpha = 255;
+		else alpha = cmd.args[8].value;
+		eprintf("copy surface w/ saturate %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		if (alpha != 0) {
+			// saturate mode : screen (picture) を一時的に作成
+			PicBase* screen_tmp = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
+			screen_tmp->SetSurface(Ssurface(src), 0, 0, PicBase::BLIT_SATURATE);
+			screen_tmp->SetSurfaceRect(rect);
+			screen_tmp->Move(dx, dy);
+			screen_tmp->SetSurfaceAlpha(&alpha, Rect(0,0,1,1));
+			screen_tmp->SimpleBlit(Dsurface(dest));
+			delete screen_tmp;
+		}
+		cmd.clear();
+	}
+}
+
+void Grp::impl_grpPan(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		Rect r_from(cmd.args[0].value, cmd.args[1].value);
+		Rect r_to(cmd.args[2].value, cmd.args[3].value);
+		int src_pdt = cmd.args[4].value;
+		Rect r(cmd.args[5].value,cmd.args[6].value,cmd.args[7].value+1,cmd.args[8].value+1);
+		int tm = cmd.args[9].value;
+		fprintf(stderr,"??? cmd time %d\n",tm);
+		// anm1 = new ScnGrpMove(event, screen, parent.Root(), surface, r, Ssurface(2), r_from, r_to, tm);
+		// status = WAIT_ANM;
+	}
+}
+
+void Grp::impl_snmBgScroll(Cmd& cmd) {
+	if (cmd.cmd4 == 0) { // スクロールする画像効果(Princess Bride)
+		if (anm2 != NULL) {
+			anm2->Abort();
+			delete anm2;
+			anm2 = NULL;
+		}
+		PicBase* pic; Surface* s;
+		Rect r(cmd.args[1].value, cmd.args[2].value, cmd.args[3].value+1, cmd.args[4].value+1);
+		const char* name = cmd.Str(cmd.args[5]);
+		Rect sr_start(cmd.args[6].value,cmd.args[7].value);
+		Rect sr_end(cmd.args[8].value,cmd.args[9].value);
+		int tm = cmd.args[10].value;
+		LoadSurface(name, 2); /* PDT2 に読み込み、と決め打ち */
+
+		anm2 = new ScnGrpMove(event, screen, parent.Root(), Dsurface(1), r, Ssurface(2), sr_start, sr_end, tm);
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+}
+
+void Grp::impl_snmPlay(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		// カードが落ちるアニメーション
+		int i;
+		ScnGrpAnm* new_anm = new ScnGrpAnm(event, screen, *this);
+		if (cmd.cmd3 == 0x834 || cmd.cmd3 == 0x835) {
+			AbortAnm();
+			anm1 = new_anm;
+			if (cmd.cmd3 == 0x835) {
+				status = WAIT_ANM;
+				event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+			}
+		} else {
+ 			anm2 = new_anm;
+		}
+		for (i=0; i<cmd.argc; i++) {
+			const char* name = cmd.Str(cmd.args[i*3+1]);
+			int tm = cmd.args[i*3+2].value;
+			new_anm->push_back(ScnGrpAnmAtom(name,tm));
+		}
+		new_anm->CalcTotal();
+		cmd.clear();
+	}
+}
+
+void Grp::impl_cgGet(Cmd& cmd) {
+	if (cmd.cmd3 == 0x5dc) // Total number of CG
+		cmd.SetSysvar(cgm_size);
+
+	if (cmd.cmd3 == 0x5dd) // Number of CG viewed
+		cmd.SetSysvar(cgm_data.size());
+
+	if (cmd.cmd3 == 0x5de) // Percentage of CG viewed
+		cmd.SetSysvar(cgm_data.size() * 100 / cgm_size);
+}
+
+void Grp::impl_cgStatus(Cmd& cmd) {
+	string s = cmd.Str(cmd.args[0]);
+	if (cgm_info.find(s) == cgm_info.end()) {
+		fprintf(stderr,"cmd 01-04:05e0 : cannot find cgm-info of '%s'\n",s.c_str());
+		cmd.SetSysvar(-1);
+	}
+	else {
+		int n = cgm_info[s];
+		if (cmd.cmd3 == 1503) cmd.SetSysvar(n);
+		else {
+			if (cgm_data.find(n) == cgm_data.end()) cmd.SetSysvar(0);
+			else cmd.SetSysvar(1);
+		}
+	}
+}
+
+void Grp::impl_objClear(Cmd& cmd) { //FIXME: may be broken (doesn't reflect what Haeleth says)
+	if (cmd.cmd1 == 1)
+		DeleteObj(cmd.args[0].value);
+	if (cmd.cmd1 == 2)
+		DeleteSubObj(cmd.args[0].value, cmd.args[1].value);
+	cmd.clear();
+}
+
+void Grp::impl_createObj(Cmd& cmd) {
+	/**************:
+	0x47 : オブジェクト内容の設定
+			1100: G00 file
+			1003: GAN file
+			1100: rect
+			1200: string
+			1300: weather effects
+			1400: number
+	*/
+	int base_argc = 0;
+
+	if (cmd.cmd1 == 1) { // 1: group object
+		DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
+		if (cmd.cmd2 == 71)
+			DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
+	}
+	else { // 2: single object in group
+		DeleteSubObjPic(cmd.args[0].value, cmd.args[1].value); // 旧ファイル名のsurfaceを削除
+		if (cmd.cmd2 == 71)
+			DeleteSubObjPic(cmd.args[0].value, cmd.args[1].value); // 旧ファイル名のsurfaceを削除
+	}
+
+	GrpObj* g = (cmd.cmd2 == 71) ? &grpobj[cmd.args[0].value] : &bs_obj[cmd.args[0].value];
+	if (cmd.cmd1 == 2) // 2: single object in a group
+		g = &g->children_obj[cmd.args[1].value];
+
+	if (cmd.cmd1 == 2)
+		base_argc = 1;
+
+	if (cmd.cmd3 == 1000) { /* ファイル名設定 */
+		g->gtype = GrpObj::FILE; //FIXME: Strange thing in the main menu; that happens with objComposite
+		string name = cmd.Str(cmd.args[base_argc + 1]);
+		if (name.find('?') != -1) {//TODO
+			//Used for shading, with DAT/tcdata.tcc or other filename provided by #TONECURVE_FILENAME
+			printf("Warning: the part after the '?' was removed: '%s'\n", name.c_str());
+			name.erase(name.find('?')); // '?' 以降の意味がわからない
+		}
+		g->name = name;
+	} else if (cmd.cmd3 == 1003) { /* ファイル名設定(GAN含む) */
+		g->gtype = GrpObj::GAN;
+		if (cmd.Str(cmd.args[base_argc + 1]) == string("???"))
+			g->name = cmd.Str(cmd.args[base_argc + 2]);
+		else
+			g->name = cmd.Str(cmd.args[base_argc + 1]);
+		g->gan_name = cmd.Str(cmd.args[base_argc + 2]);
+
+		if (cmd.cmd4 >= 1 && cmd.args[base_argc + 3].value == 0)
+			g->attr =  GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
+		else
+			g->attr =  GrpObj::Attribute(g->attr & ~(GrpObj::HIDDEN));
+
+		if (cmd.argc >= base_argc + 5)
+			g->SetPos(1, cmd.args[base_argc + 4].value, -cmd.args[base_argc + 5].value);
+
+		if (g->name.find('?') != -1) {
+			g->name.erase(g->name.find('?'));
+			g->gan_name = cmd.Str(cmd.args[base_argc + 2]);
+		}
+	} else if (cmd.cmd3 == 1200) { // 画像を文字列として指定
+		g->gtype = GrpObj::MOJI;
+		g->print_moji = cmd.Str(cmd.args[base_argc + 1]);
+		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN)); // 常に表示がデフォルト?
+		cmd.clear();
+	} else if (cmd.cmd3 == 1400) { // 数値を画像として表示
+		g->gtype = GrpObj::DIGIT;
+		g->name = cmd.Str(cmd.args[base_argc + 1]);
+	}
+
+	CreateObj(cmd.args[0].value);
+	if (cmd.cmd1 == 2)
+		CreateSubObj(cmd.args[0].value, cmd.args[1].value);
+
+	if (cmd.cmd3 == 1000 || cmd.cmd3 == 1003 || cmd.cmd3 == 1200 || cmd.cmd3 == 1400) {
+		// FILE, GAN, MOJI, DIGIT ならば座標等の設定を行う
+		if (cmd.cmd4 >= 1) {
+			if (cmd.args[2+base_argc].value == 0) {
+				g->attr = GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
+			} else {
+				g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN));
+			}
+			SetObjChanged(cmd.args[0].value);
+		}
+		if (cmd.cmd4 >= 2) { // 座標等も設定
+			g->SetPos(0, cmd.args[3+base_argc].value, cmd.args[4+base_argc].value);
+		}
+		if ( (cmd.cmd3 == 1000 || cmd.cmd3 == 1003) && cmd.cmd4 >= 3) { // pattern 番号も設定
+			g->SetSurfaceNum(cmd.args[5+base_argc].value);
+			base_argc++; // 1000 (FILE) / 1003 (GAN) の場合のみこのオプションは存在する
+		}
+		cmd.clear();
+	} else {
+		fprintf(stderr,"CreateObj : cmd.cmd3 = %04x ; not supported!\n",cmd.cmd3);
+	}
+}
+
+void Grp::impl_gan(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg);
+
+	if (cmd.cmd3 == 3) { // ganIsPlaying
+		if (g->anm == NULL || g->anm->IsEnd())
+			cmd.SetSysvar(0);
+		else
+			cmd.SetSysvar(1);
+	}
+	else if (cmd.cmd3 == 1000) { // ganStop
+		if (g->anm == NULL || g->anm->IsEnd())
+			g->SetSurfaceNum(cmd.args[1].value);
+		else {
+			g->anm->Abort();
+			g->SetSurfaceNum(cmd.args[1].value);
+		}
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+	else if (cmd.cmd3 == 2003) { // objPlay
+		g->CreateGanSpecial(event, 0, cmd.args[1].value);
+		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+	else if (cmd.cmd3 == 3001 || cmd.cmd3 == 3003 || cmd.cmd3 == 3005 ||
+			 cmd.cmd3 == 1001 || cmd.cmd3 == 1003 || cmd.cmd3 == 1005) { // ganPlay*
+		g->CreateGan(event, cmd.args[1].value);
+		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+}
+
+void Grp::impl_objSetPos(Cmd& cmd) {
+	//obj or objBg
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	int index, x, y;
+	if (cmd.cmd3 == 1006 || cmd.cmd3 == 2006) { //objAdjust
+		index = cmd.args[1+base_arg].value + 1;
+		x = cmd.args[2+base_arg].value;
+		y = cmd.args[3+base_arg].value;
+	}
+	else {
+		index = 0;
+		if (cmd.cmd3 == 1000) {
+			x = cmd.args[1+base_arg].value;
+			y = cmd.args[2+base_arg].value;
+		}
+		else {
+			g->GetPos(index, x, y);
+			if (cmd.cmd3 == 1001)
+				x = cmd.args[1+base_arg].value;
+			else
+				y = cmd.args[1+base_arg].value;
+		}
+	}
+
+	g->SetPos(index, x, y);
+	cmd.clear();
+}
+
+void Grp::impl_objAlpha(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->SetAlpha(cmd.args[base_arg + 1].value);
+	cmd.clear();
+}
+
+void Grp::impl_objShow(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	if (cmd.args[base_arg + 1].value)
+		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN));
+	else
+		g->attr = GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
+
+	g->attr = GrpObj::Attribute(g->attr | GrpObj::UPDATE_VISIBLE);
+		// グループ単位で次の RefreshObj で表示・消去
+	if (cmd.cmd2 == 0x51) //not Bg
+		SetObjChanged(cmd.args[0].value);
+	cmd.clear();
+}
+
+void Grp::impl_objColour(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->print_r = cmd.args[base_arg+1].value;
+	g->print_g = cmd.args[base_arg+2].value;
+	g->print_b = cmd.args[base_arg+3].value;
+	g->SetUpdate();
+	cmd.clear();
+}
+
+void Grp::impl_objComposite(Cmd& cmd) {//FIXME
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	if (cmd.args[base_arg + 1].value == 1) {
+		g->attr = GrpObj::Attribute(g->attr | GrpObj::SATURATE);
+		cmd.clear();
+	} else if (cmd.args[base_arg + 1].value == 0) {
+		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::SATURATE));
+		cmd.clear();
+	}
+	g->SetUpdate();
+}
+
+void Grp::impl_objSetText(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->print_moji = cmd.Str(cmd.args[base_arg + 1]);
+	g->SetUpdate();
+	cmd.clear();
+}
+
+void Grp::impl_objTextOpts(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	// 画像を文字列として設定:文字の大きさなど
+	g->print_size = cmd.args[base_arg + 1].value;
+	/* 前景色を得る */
+	int cr, cg, cb;
+	char key[17];
+	sprintf(key, "#COLOR_TABLE.%03d", cmd.args[base_arg + 5].value);
+	if (config->GetParam(key, 3, &cr, &cg, &cb)) { // color not found
+		cr = cg = cb = 0;
+	}
+	g->print_r = cr;
+	g->print_g = cg;
+	g->print_b = cb;
+	g->SetUpdate();
+	cmd.clear();
+}
+
+void Grp::impl_objOrder(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	int order = cmd.args[base_arg + 1].value;
+	g->order = order;
+	ZMoveObj(cmd.args[0].value);
+	cmd.clear();
+}
+
+void Grp::impl_objDispArea(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	// オブジェクトのどの部分を画面に表示するか(クリップ領域)の設定
+	int rx, ry, w, h;
+	if (cmd.args.size() == base_arg + 5) {
+		int rx = cmd.args[base_arg + 1].value;
+		int ry = cmd.args[base_arg + 2].value;
+		int w = cmd.args[base_arg + 3].value;
+		int h = cmd.args[base_arg + 4].value;
+		if (cmd.cmd3 == 1005) {
+			w -= rx;
+			h -= ry;
+		}
+	}
+	else {
+		rx = ry = 0;
+		w = screen->Width();
+		h = screen->Height();
+	}
+	g->SetClipArea(rx, ry, w, h); //TODO: case when cmd.args.size() == 1
+	cmd.clear();
+}
+
+void Grp::impl_objSetDigits(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->dig_number = cmd.args[base_arg + 1].value;
+	g->SetUpdate();
+	cmd.clear();
+}
+
+void Grp::impl_objNumOpts(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->dig_digit = cmd.args[base_arg + 1].value;
+	int attr = g->attr;
+	attr &= ~(GrpObj::DIG_ZERO | GrpObj::DIG_SIGN | GrpObj::DIG_PACK);
+	if (cmd.args[base_arg + 2].value) attr |= GrpObj::DIG_ZERO;
+	if (cmd.args[base_arg + 3].value) attr |= GrpObj::DIG_SIGN;
+	if (cmd.args[base_arg + 4].value) attr |= GrpObj::DIG_PACK;
+	g->attr = GrpObj::Attribute(attr);
+	g->SetUpdate();
+	cmd.clear();
+}
+
+void Grp::impl_objPattNo(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->SetSurfaceNum(cmd.args[base_arg + 1].value);
+	cmd.clear();
+}
+
+void Grp::impl_objScale(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	int zoom = (cmd.args[base_arg + 1].value + cmd.args[base_arg + 2].value)/2; //FIXME: eurk
+	zoom = zoom*256/100;
+	g->SetZoomRotate(zoom, -1);
+	cmd.clear();
+}
+
+void Grp::impl_objRotate(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	int angle = cmd.args[base_arg + 1].value;
+	angle /= 10;
+	if (angle < 0) {
+		angle %= 360;
+		angle += 360;
+	}
+	angle %= 360;
+	g->SetZoomRotate(-1, angle);
+	cmd.clear();
+}
+
+void Grp::impl_objPosDims(Cmd& cmd) {
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, true);
+
+	VarInfo arg1 = cmd.args[base_arg + 1];
+	VarInfo arg2 = cmd.args[base_arg + 2];
+
+	int val1, val2;
+
+	if (cmd.cmd3 == 1000)
+		g->GetPos(0, val1, val2);
+	else if (cmd.cmd3 == 1100)
+		g->GetSrcGeom(val1, val2);
+
+	cmd.SetFlagvar(arg1, val1);
+	cmd.SetFlagvar(arg2, val2);
+}
+
+void Grp::impl_refresh(Cmd& cmd) {
+	// 本来は grpstack clear らしい
+	RefreshObj();
+	// Princess Bride の中途 Staff roll
+	// このタイミングで描画するのが都合がいいので、
+	//シナリオループを抜けて描画を起動
+	cmd.cmd_type = CMD_WAITFRAMEUPDATE;
+}
+
+void Grp::impl_bgmLoop(Cmd& cmd) {
+	if (cmd.cmd4 == 0 || cmd.cmd4 == 2) {
+		int count = 8000;
+		if (cmd.cmd3 == 2)
+			count = 0; //bgmPlay, play once
+		music->PlayCDROM((char*)cmd.Str(cmd.args[0]), count);
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+}
+
+void Grp::impl_bgmStop(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		if (cmd.cmd3 == 5)
+			music->StopCDROM(0);
+		else if (cmd.cmd3 == 105)
+			music->StopCDROM(cmd.args[0].value);
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+}
+
+void Grp::impl_playWav(Cmd& cmd) {
+	if (cmd.cmd3 == 2) {
+		music->PlaySE(cmd.Str(cmd.args[0]), 1); //loop
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+	else {
+		music->PlaySE(cmd.Str(cmd.args[0]));
+		cmd.clear();
+	}
+	if (cmd.cmd3 == 1)
+		status = WAIT_SE;
+}
+
+void Grp::impl_playSE(Cmd& cmd) {
+	music->PlaySE(cmd.args[0].value);
+	cmd.clear();
+}
+
+void Grp::impl_stopWav(Cmd& cmd) {
+	if (cmd.cmd3 == 5)
+		music->StopSE();
+	else if (cmd.cmd3 == 105)
+		music->StopSE(cmd.args[0].value);
+
+	cmd.cmd_type = CMD_SAVECMD_ONCE;
+}
+
+void Grp::impl_SetVolMod(Cmd& cmd) {
+	music->volmod[cmd.cmd3-0x8b6] = cmd.args[0].value;
+	config->SetParam("#VOLMOD", 4, music->volmod[0], music->volmod[1], music->volmod[2], music->volmod[3]);
+	cmd.clear();
+}
+
+void Grp::impl_GetVolMod(Cmd& cmd) {
+	cmd.SetSysvar(music->volmod[cmd.cmd3-0x91a]);
+}
+
+void Grp::impl_koePlay(Cmd& cmd) {
+	eprintf("play koe %d",cmd.args[0].value);
+	if (cmd.cmd4 == 1) {
+		eprintf(", para? %d",cmd.args[1].value);
+	}
+	eprintf("\n");
+	char buf[1024]; sprintf(buf, "%d",cmd.args[0].value);
+	if ( !(skip_mode & SKIP_TEXT)) music->PlayKoe(buf);
+	cmd.clear();
+}
+
+/*It may be useful... or not.
+void Grp::impl_objSwap(Cmd& cmd) {
+	if (cmd.cmd1 == 1 && cmd.args.size() == 2) {
+		SwapObj(cmd.args[0].value, cmd.args[1].value);
+	}
+	cmd.clear();
+}*/
+
+void Grp::impl_movPlay(Cmd& cmd) {
+	if ( cmd.cmd4 == 0) {
+		const char* str = cmd.Str(cmd.args[0]);
+		int x = cmd.args[1].value;
+		int y = cmd.args[2].value;
+		int x2 = cmd.args[3].value;
+		int y2 = cmd.args[4].value;
+		eprintf("play movie ; name %s pos %d,%d - %d,%d\n",str,x,y,x2,y2);
+		music->PlayMovie(str, x, y, x2, y2,1);
+		status = WAIT_MOVIE;
+		event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+		cmd.clear();
+	}
+}