view scn2k/scn2k_grpimpl.cc @ 60:e16e13d8cd68

Replaced SATURATE -> ADD, implemented objComposite, corrected minor things
author Thibaut GIRKA <thib@sitedethib.com>
date Fri, 18 Dec 2009 20:41:38 +0100
parents c7bcc0ec2267
children bdd8a5ff8f46
line wrap: on
line source

/*
 * 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) {
	//TODO: Handle forms 0 and 1
	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) { // add 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/ add %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) {
			// add 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_ADD);
			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;
		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::BLIT_ADD);
		cmd.clear();
	} else if (cmd.args[base_arg + 1].value == 0) {
		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::BLIT_ADD));
		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();
	}
}