view scn2k/scn2k_grp.cc @ 66:d112357a0ec1

Fix a bug with savegames introduced with changeset c7bcc0ec2267. Warning: savegames created since c7bcc0ec2267 are probably corrupted, you may have to start the game over. If you chose not to do so, you should replace all occurrences of 'TextWindow' by 'TextImplWindow', and 'Text Window' by 'TextImpl Window' in your save files.
author Thibaut Girka <thib@sitedethib.com>
date Sat, 11 Dec 2010 18:36:20 +0100
parents 4416cfac86ae
children 1fd20d231376
line wrap: on
line source

/*
 * 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.h"
#include "system/file.h"
#include "system/system_config.h"
#include "font/text.h"
#include "window/render.h"

extern XKFont::HorizLayout* DefaultLayout(int text_size);

/*******************************************************************
** GrpObj(implementation)
*/

GrpObj::GrpObj(void) :
	name(""), gan_name(""), pic_parent(0), picture(0), anm(0),
	_posx(0), _posy(0), clip_area(0,0,0,0),
	alpha(255), order(0), surface_num(0), print_moji(""), print_size(0), print_r(-1),print_g(-1),print_b(-1),
	dig_number(0), dig_digit(0),
	zoom(-1), rotate(-1), attr(GrpObj::HIDDEN) {
	int i;
	for (i=0; i<9; i++) {
		posx[i] = posy[i] = 0;
	}
}

GrpObj::~GrpObj() {
	if (picture != NULL) delete picture;
}

int GrpObj::PosX() {
	return _posx;
}

int GrpObj::PosY() {
	return _posy;
}

void GrpObj::SetUpdate(void) {
	attr = Attribute (attr | UPDATE_PICTURE);
	//Update(); //FIXME
}

void GrpObj::SetPos(int index, int x, int y) {
	if (index < 0 || index > 8) {
		fprintf(stderr,"GrpObj::SetPos: Invalid index %d <- %d,%d\n",index,x,y);
		return;
	}
	if (x == posx[index] && y == posy[index]) return;
	attr = Attribute(attr | UPDATE_POS);
	_posx += x-posx[index];
	_posy += y-posy[index];
	posx[index] = x;
	posy[index] = y;
}

void GrpObj::GetPos(int index, int& x, int& y) {
	if (index < 0 || index > 8) {
		fprintf(stderr,"GrpObj::GetPos: Invalid index %d\n",index);
		x = 0; y = 0;
		return;
	}
	x = posx[index];
	y = posy[index];
}

void GrpObj::SetAlpha(int new_alpha) {
	if (alpha == new_alpha) return;
	alpha = new_alpha;
	attr = Attribute(attr | UPDATE_ALPHA);
	return;
}

void GrpObj::SetSurfaceNum(int num) {
	if (num != -1) {
		if (surface_num == num) return;
		surface_num = num;
	}
	attr = Attribute(attr | UPDATE_SNUM);
}

void GrpObj::SetClipArea(int x, int y, int w, int h) {
	Rect new_clip(x,y,x+w,y+h);
	if (clip_area == new_clip) return;
	clip_area = new_clip;
	attr = Attribute(attr | UPDATE_CLIP);
}

PicBase* GrpObj::DeletePic(void) {
	PicBase* p = picture;
	anm = NULL;
	picture = NULL;
	src_pos.clear();
	attr = Attribute(attr & HIDDEN);
	return p;
}

void GrpObj::GetSrcGeom(int& width, int& height) {
	if (src_pos.empty()) {
		width = 0; height = 0;
		if (name.length() == 0) {
			return;
		}
		/* ボタンの位置情報を求める */
		/* g00 ファイルのヘッダ部分に位置情報は入っている */
		string path(name);
		path += ".g00";
		ARCINFO* info = FileSearcher::GetInstance()->Find(FileSearcher::PDT, path.c_str(), "g00");
		if (info == NULL) { // ファイルが見つからない
			fprintf(stderr, "GrpObj::GetSrcGeom : Cannot find file %s\n", path.c_str());
			return;
		}
		const char* data = info->Read();
		if (data != NULL && *data == 2) { // 画像ファイル内にボタン情報が存在する
			int srclen = read_little_endian_int(data+5);
			int i;
			for (i=0; i<srclen; i++) {
				int x1 = read_little_endian_int(data+9+i*24+0);
				int y1 = read_little_endian_int(data+9+i*24+4);
				int x2 = read_little_endian_int(data+9+i*24+8);
				int y2 = read_little_endian_int(data+9+i*24+12);
				src_pos.push_back(Rect(x1, y1, x2+1, y2+1));
				if (width < src_pos.back().width()) width = src_pos.back().width();
				if (height < src_pos.back().height()) height = src_pos.back().height();
			}
		} else { // 画像ファイルから大きさ取得
			width = read_little_endian_short(data+1);
			height = read_little_endian_short(data+3);
			src_pos.push_back(Rect(0,0,width,height));
		}
		delete info;
	}
	int sn = surface_num;
	if (sn < 0 || sn > src_pos.size()) sn = 0;
	width = src_pos[sn].width();
	height = src_pos[sn].height();
}

void GrpObj::Update(void) {
	if (attr & UPDATE_PICTURE) {
		UpdateSurface();
		attr = Attribute( (attr | UPDATE_ALL) & (~UPDATE_PICTURE));
	}
	if (picture == NULL) return;
	if (attr & UPDATE_POS) {
		if (zoom != -1) {
			int w=0, h=0;
			GetSrcGeom(w,h);
			picture->Move(_posx-w/2, _posy-h/2);
		} else {
			picture->Move(_posx, _posy);
		}
	}
	if (attr & UPDATE_ALPHA) {
		if (alpha <= 0) {
			picture->SetSurfaceAlpha(0, Rect(0,0));
			picture->hide();
		} else if (alpha >= ALPHA_MAX) {
			picture->SetSurfaceAlpha(0, Rect(0,0));
			if (attr & HIDDEN) picture->hide();
			else picture->show();
		} else {
			picture->SetSurfaceAlpha(&alpha, Rect(0,0,1,1));
			if (attr & HIDDEN) picture->hide();
			else picture->show();
		}
	}
	if ( (attr & UPDATE_SNUM) && (!src_pos.empty())) {
		if (surface_num < 0 || surface_num >= src_pos.size()) surface_num = 0;
		picture->SetSurfacePos(src_pos[surface_num].lx, src_pos[surface_num].ty);
	}
	if (attr & UPDATE_CLIP) {
		picture->SetClipArea(clip_area);
	}
	attr = Attribute(attr & (~UPDATE_ALL));
	if (attr & ANM_PLAYSTART) {
		if (anm != NULL) {
			anm->Play();
			attr = Attribute(attr | ANM_PLAYING);
		}
		attr = Attribute(attr & (~ANM_PLAYSTART));
	}
}

void GrpObj::CreateSurface(PicContainer* parent) {
	if (picture != NULL) {
		PicBase* p = DeletePic();
		delete p;
	}
	src_pos.clear();
	// picture を作成
	pic_parent = parent;
	picture = parent->create_leaf(Rect(_posx,_posy,_posx+1,_posy+1), 0);
	picture->hide();
	UpdateSurface();
}
  
void GrpObj::UpdateSurface(void) {
	if (pic_parent == NULL || picture == NULL) return;
	int width = 0, height = 0;
	if (gtype == FILE || gtype == GAN) {
		if (name.length() == 0) return;
		// ファイル名が存在する場合、ファイルを読み込み
		GetSrcGeom(width, height);
		if (width <= 0 || height <= 0) return;
		// surface の設定
		if (surface_num == 0 && ( (zoom > 0 && zoom != 256) || rotate > 0)) {
			ZoomRotate();
		} else {
			// 普通に surface を設定
			string path(name);
			path += ".g00";
			picture->SetSurface(path.c_str(), 0, 0);
			picture->SetSurfaceRect(Rect(0,0,width,height));
		}
		if (attr & BLIT_ADD)
			picture->SetSurfaceAttribute(PicBase::BLIT_ADD);
	} else if (gtype == MOJI) { // テキスト描画
		if (print_moji.length() == 0) return;
		UpdateMoji();
	} else if (gtype == DIGIT) { // 数値を画像表示
		UpdateDigit();
	}
}

void GrpObj::ZoomRotate(void) {
	picture->SetSurface( (Surface*)0, 0, 0);

	// 回転、縮小拡大は座標原点が画像の中心になる
	string path(name);
	path += ".g00";
	Surface* surface_orig = pic_parent->Root().NewSurface(path.c_str());
	if (surface_orig == NULL) return;

	Surface* zoom_surface = pic_parent->Root().RotZoomSurface(surface_orig, double(zoom)/256.0, rotate);
	Rect zoom_r (*zoom_surface);
	picture->SetSurface(zoom_surface, 0, 0);
	picture->SetSurfaceFreeFlag();
	//picture->Move(PosX() + - zoom_r.width()/2, PosY() + - zoom_r.height()/2);
// 中心座標がわからん・・・
	picture->Move(320 - zoom_r.width()/2, 240 - zoom_r.height()/2);
	picture->SetSurfaceRect(Rect(0, 0, zoom_r.width(), zoom_r.height()));

	pic_parent->Root().DeleteSurface(surface_orig);
}

void GrpObj::UpdateMoji(void) { // 文字の大きさ、色などを変更
	if (print_moji.length() == 0 || print_size <= 2) return;
	if (pic_parent == 0) return;
	/* テキストの大きさを得る */
	int r, g, b;
	if (print_r == -1 || print_g == -1 || print_b == -1) {// 色設定なし
		r = g = b = 0;  // とりあえず黒(clannad のSave/Loadメニュー用)
	} else {
		r = print_r;
		g = print_g;
		b = print_b;
	}
	TextStream ts = TextStream::ParseMoji(print_moji.c_str(), r, g, b, print_size);
	TextGlyphStream gs;
	vector<int> lh;
	// とりあえず drawable width は充分に大きく(2048)取る
	DefaultLayout(print_size-2)->Layout(ts, gs, lh, 2048); // print_size そのままだと弱干大きすぎるので -2
	int width = gs.width();
	int height = gs.height();
	Surface* surface = pic_parent->Root().NewSurface(width, height, ALPHA_MASK);
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);
	DSurfaceRenderText(gs.begin(), gs.end(), Rect(0, 0, width, height), surface, Rect(0,0));
	picture->SetSurface(surface, 0, 0);
	picture->SetSurfaceRect(Rect(0,0,width,height));
	picture->SetSurfaceFreeFlag();
}

void GrpObj::UpdateDigit(void) {
	// 画像表示の数値文字列を表示する
	if (name.length() == 0) return;
	// ファイル名が存在する場合、ファイルを読み込み
	string path(name);
	path += ".g00";
	Surface* surface_orig = pic_parent->Root().NewSurface(path.c_str());
	if (surface_orig == NULL) return;

	int width, height;
	int i;
	GetSrcGeom(width, height);
	if (width <= 0 || height <= 0) return;
	if (src_pos.size() < 14) {
		// 必要な数の object がない
		// 表示できない分の空の rect を追加しておく
		for (i=src_pos.size(); i<14; i++)
			src_pos.push_back(Rect(0,0,0,0));
		pic_parent->Root().DeleteSurface(surface_orig);
		return;
	}
	// 桁数の計算
	char num_str[20];
	if (dig_number < 0) sprintf(num_str, "%d", -dig_number);
	else sprintf(num_str, "%d", dig_number);
	int sign_count = 0;
	int space_count = 0;
	int total_count;
	int dig_count = strlen(num_str);
	if (dig_number < 0 && (attr&DIG_SIGN) == 0) dig_count++;
	if (dig_count < dig_digit) space_count = dig_digit - dig_count;
	if (attr & DIG_SIGN) sign_count = 1;
	total_count = dig_count + space_count + sign_count;

	Surface* surface = pic_parent->Root().NewSurface(width*total_count, height, ALPHA_MASK);
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0, 0);

	/* surface にコピーする */
	int cur_x = 0;
	if ( (attr & DIG_PACK) && !(attr & DIG_ZERO)) { // 始めに空白を挿入
		cur_x += space_count * width;
	}
	int plus = 10, minus = 11, plusminus = 12;
	if (dig_number < 0) {
		DSurfaceMove(surface, src_pos[minus], surface, Rect(cur_x,0));
		cur_x += width;
	} else if (attr & DIG_SIGN) {
		if (dig_number == 0)
			DSurfaceMove(surface, src_pos[plusminus], surface, Rect(cur_x,0));
		else
			DSurfaceMove(surface, src_pos[plus], surface, Rect(cur_x,0));
		cur_x += width;
	}
	if (attr & DIG_ZERO) { // ゼロ・パディング
		for (i=0; i<space_count; i++) {
			DSurfaceMove(surface, src_pos[0], surface, Rect(cur_x, 0));
			cur_x += width;;
		}
	} else if (!(attr & DIG_PACK)) { // PACK オプションなし
		cur_x += space_count * width;
	}
	for (i=0; num_str[i] != 0; i++) {
		DSurfaceMove(surface_orig, src_pos[num_str[i]-'0'], surface, Rect(cur_x, 0));
		cur_x += width;
	}
	
	/* picture に設定 */
	picture->SetSurface(surface, 0, 0);
	picture->SetSurfaceRect(Rect(0,0,width*total_count,height));
	picture->SetSurfaceFreeFlag();

	pic_parent->Root().DeleteSurface(surface_orig);
}

void GrpObj::CreateGan(Event::Container& event, int event_number) {
	if (picture == NULL) {
		fprintf(stderr,"GrpObj::CreateGan() is called before Create()\n");
		return;
	}
	if (anm != NULL) {
		anm->Abort();
		delete anm;
		anm = NULL;
	}
	if (gan_name.empty()) return;
	/* アニーメション情報 (.GAN ファイル)を求める */
	string path(gan_name);
	path += ".gan";
	ARCINFO* info = FileSearcher::GetInstance()->Find(FileSearcher::GAN, path.c_str(), "gan");
	if (info == NULL) {
		fprintf(stderr,"GrpObj::CreateGan: Cannot Find 'GAN' file %s\n", path.c_str());
		return;
	}
	const char* data = info->Read();
	if (read_little_endian_int(data) != 10000 || read_little_endian_int(data+4) != 10000) {
		fprintf(stderr,"GrpObj::CreateGan: Invalid'GAN' file %s\n", path.c_str());
		delete info;
		return;
	}

	attr = Attribute(attr | UPDATE_POS);

	const char* buf = data + 16;
	buf += strlen(buf) + 1; // 画像ファイル名が入っている
	buf += 4; // 定数 20000
	int pics = read_little_endian_int(buf); buf += 4; // 複数のアニメーション情報が入っている場合、情報数 
	// 以下、pics 回繰り返し
	// アニメーションを行う実体を作成
	AnmAlphaMove* wid = new AnmAlphaMove(event, picture);

	if (event_number && event_number < pics) { // 複数のアニメーション情報がある場合、先の情報を読み飛ばす */
		int i; for (i=0; i<event_number; i++) {
			buf += 4; // 定数 30000
			int ptns = read_little_endian_int(buf); buf += 4;
			buf += ptns*52;
		}
	}
	buf += 4; // 定数 30000
	int ptns = read_little_endian_int(buf); buf += 4;
	int total_time = 0;
	int i;
	for (i=0; i<ptns; i++) {
		int p = read_little_endian_int(buf+i*52+0*8+4);
		int x = read_little_endian_int(buf+i*52+1*8+4);
		int y = read_little_endian_int(buf+i*52+2*8+4);
		int t = read_little_endian_int(buf+i*52+3*8+4);
		int a = read_little_endian_int(buf+i*52+4*8+4);
		x += PosX();
		y += PosY();
		if (p == -1) { a = 0; p = 0; } // p == -1 ならなにも表示しない
		if (p >= src_pos.size()) {
			fprintf(stderr,"Reading GAN file %s (G00 %s) : not enough pictures in .G00 file\n", path.c_str(), name.c_str());
			a = 0; p = 0;
		}
		total_time += t;
		wid->ptns.push_back(AnmAlphaMove::Ptn(Rect(x,y), src_pos[p], a, total_time));
	}
	wid->SetPtn(); // パターン登録終了
	attr = Attribute(attr | ANM_PLAYSTART);
	anm = wid;
}

void GrpObj::CreateGanSpecial(Event::Container& event, int event_number, int time) {
	if (picture == NULL) {
		fprintf(stderr,"GrpObj::CreateGan() is called before Create()\n");
		return;
	}
	if (anm != NULL) {
		anm->Abort();
		delete anm;
		anm = NULL;
	}

	// アニメーションを行う実体を作成
	AnmAlphaMove* wid = new AnmAlphaMove(event, picture);

	int i;
	switch(event_number) {
		case 0: // pattern を 0 から最後まで変化させる
			for (i=0; i<src_pos.size(); i++) {
				wid->ptns.push_back(AnmAlphaMove::Ptn(Rect(PosX(), PosY()), src_pos[i], 255, time*i));
			}
			wid->SetPtn(); // パターン登録終了
			anm = wid;
			attr = Attribute(attr | ANM_PLAYSTART);
			break;
		default:
			break;
	}
}

void GrpObj::SetZoomRotate(int new_zoom, int new_rotate) {
	if (zoom == new_zoom && rotate == new_rotate) return;
	if ( zoom == -1 || new_zoom == -1) {
		attr = Attribute(attr | UPDATE_POS); // centering する
	}
	zoom = new_zoom;
	if (new_rotate != -1) rotate = new_rotate;
	if (zoom < 0) zoom = 256;
	if (rotate < 0) rotate = 0;
	else if (rotate > 360) rotate %= 360;

	attr = Attribute(attr | UPDATE_PICTURE);
}

void GrpObj::Refresh(GrpObj& parent_obj) {
	//if (&parent_obj != this) printf("Toto\n"); //FIXME

	GrpObjMap::iterator it;

	for (it = children_obj.begin(); it != children_obj.end(); it++)
		it->second.Refresh(parent_obj);

	if (picture == NULL) return;
	if (alpha == 0 || (attr & GrpObj::HIDDEN) || (parent_obj.attr & GrpObj::HIDDEN)) {
		if (attr & GrpObj::ANM_PLAYING) {
			attr = GrpObj::Attribute(attr & ~(GrpObj::ANM_PLAYING));
			if (anm != NULL) anm->Abort();
		}
		picture->hide();
	} else {
		Update();
		picture->show();
	}
}

void GrpObj::_debug_Dump(int id, int indent)
{
	const char* repr;

	if (indent == 0)
		repr = "obj %04d(%p): name %10s  pos %d,%d alpha %d (%d/%d/%d)\n";
	else
		repr = "  * obj %04d(%p): name %10s  pos %d,%d alpha %d (%d/%d/%d)\n";

	if (picture != NULL) {
		if (!name.empty())
			fprintf(stderr, repr,
				id, this, name.c_str(), PosX(), PosY(), alpha, attr&GrpObj::HIDDEN ? 1 : 0, 0,
				picture->IsHidden());
		else if (!print_moji.empty())
			fprintf(stderr, repr,
				id, this, print_moji.c_str(), PosX(), PosY(), alpha, attr&GrpObj::HIDDEN ? 1 : 0,
				0, picture->IsHidden());
		else
			fprintf(stderr, repr,
				id, this, "<EMPTY>", PosX(), PosY(), alpha, attr&GrpObj::HIDDEN ? 1 : 0, 0,
				picture->IsHidden());
	}

	GrpObjMap::iterator it;
	for (it = children_obj.begin(); it != children_obj.end(); it++)
		it->second._debug_Dump(it->first, indent+1);
}

/******************************************************************
**
**	class ScnGrp*
*/
/* Princess Bride: 背景画の一部のみ移動、の実装 */

ScnGrpMove::ScnGrpMove(Event::Container& container, PicBase* _pic, PicRoot& _root, Surface* _dest, const Rect& _dest_r, Surface* _src, const Rect& _from, const Rect& _to, int total_time) :
	WidAnmTime(container, _pic, total_time),
	dest(_dest), src(_src), root(_root),dest_r(_dest_r), from(_from), to(_to) {
	int dx = to.lx - from.lx;
	int dy = to.ty - from.ty;
	if (dx < 0) dx = -dx;
	if (dy < 0) dy = -dy;
	if (dx < dy) dx = dy;
	if (dx == 0) dx = 1;
	SetAllCount(dx);
}

void ScnGrpMove::Exec(int count) {
	Rect r(0,0,dest_r.width(),dest_r.height());
	int dx = to.lx - from.lx;
	int dy = to.ty - from.ty;
	int x = dx*count/all_count + from.lx;
	int y = dy*count/all_count + from.ty;
	r.rmove(x, y);
	root.BlitSurface(src, r, dest, dest_r);
	iterator it;
	for (it=pic.begin(); it!=pic.end(); it++)
		(*it)->SetSurface(dest, 0, 0);
}

void ScnGrpAnm::CalcTotal(void) {
	/* total time を計算 */
	if (empty()) return;
	int tm = 0;
	vector<ScnGrpAnmAtom>::iterator it;
	for (it=begin(); it != end(); it++) tm += it->time;
	total_time = tm;
	SetAllCount(tm);
}

void ScnGrpAnm::Exec(int count) {
	int tm = 0; vector<ScnGrpAnmAtom>::iterator it;
	for (it=begin(); it != end(); it++) {
		tm += it->time;
		if (count < tm) break;
	}
	if (it == end()) it--;
	owner.LoadSurface(it->name.c_str(), 0);
}


/*****************************************************
*
*  Grp
*
*/

#include "music2/music.h"

Grp::Grp(Event::Container& _event, PicContainer& _parent, const Flags& f, set<int>& _cgm_data):
	event(_event),
	flags(f), 
	parent(_parent),
	status(NORMAL),
	skip_mode(SKIP_NO),
	cgm_data(_cgm_data)
{
	int i;
	for (i=0; i<MAXPDT; i++) {
		ssurface[i] = 0;
		dsurface[i] = 0;
	}

	music = MuSys::GetInstance();
	config = AyuSysConfig::GetInstance();

	screen = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
	screen_front = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
	surface = parent.Root().NewSurface(parent.Width(), parent.Height(), NO_MASK);
	surface_update = parent.Root().NewSurface(parent.Width(), parent.Height(), NO_MASK);
	DSurfaceFill(surface, Rect(*surface), 0, 0, 0);
	DSurfaceFill(surface_update, Rect(*surface), 0, 0, 0);
	screen->SetSurface(surface, 0, 0);
	screen->show();
	screen_front->hide();
	screen_front->ZMove(screen);

	LoadCgm();

	RegisterCommand(1, 30, 0, "stackClear", (CmdImpl) &Grp::impl_stackClear);
	RegisterCommand(1, 33, 70, "grpBuffer", (CmdImpl) &Grp::impl_grpBuffer);
	RegisterCommand(1, 33, 73, "grpOpenBG", (CmdImpl) &Grp::impl_grpOpen);
	RegisterCommand(1, 33, 75, "grpMulti", (CmdImpl) &Grp::impl_grpMulti); //FIXME: or not...
	RegisterCommand(1, 33, 76, "grpOpen", (CmdImpl) &Grp::impl_grpOpen);
	RegisterCommand(1, 33, 32, "shake", (CmdImpl) &Grp::impl_shake);
	RegisterCommand(1, 33, 100, "grpCopy", (CmdImpl) &Grp::impl_grpCopy);
	RegisterCommand(1, 33, 1201, "recFill", (CmdImpl) &Grp::impl_recFill);
	RegisterCommand(1, 33, 1100, "recCopy", (CmdImpl) &Grp::impl_recCopy);
	RegisterCommand(1, 33, 1101, "recMaskCopy", NULL); //TODO: Same thing as recCopy, but using source's alpha
	RegisterCommand(1, 33, 1600, "recAdd", (CmdImpl) &Grp::impl_recAdd);
	RegisterCommand(1, 33, 406, "grpPan", (CmdImpl) &Grp::impl_grpPan);

	RegisterCommand(1, 34, 3120, "snmBgScroll", (CmdImpl) &Grp::impl_snmBgScroll);
	RegisterCommand(1, 34, 3100, "snmBgPlay", (CmdImpl) &Grp::impl_snmPlay);
	RegisterCommand(1, 34, 2100, "snmPlay", (CmdImpl) &Grp::impl_snmPlay);
	RegisterCommand(1, 34, 2101, "snmPlayEx", (CmdImpl) &Grp::impl_snmPlay);

	RegisterCommand(1, 4, 1500, "cgGetTotal", (CmdImpl) &Grp::impl_cgGet);
	RegisterCommand(1, 4, 1501, "cgGetViewed", (CmdImpl) &Grp::impl_cgGet);
	RegisterCommand(1, 4, 1502, "cgGetViewedPcnt", (CmdImpl) &Grp::impl_cgGet);
	RegisterCommand(1, 4, 1503, "cgGetFlag", (CmdImpl) &Grp::impl_cgStatus);
	RegisterCommand(1, 4, 1504, "cgStatus", (CmdImpl) &Grp::impl_cgStatus);

	RegisterCommand(1, 4, 0x6a4, "CreateInput", NULL);
	RegisterCommand(1, 4, 0x6ae, "SetInput", NULL);

	RegisterCommand(1, 61, 10, "objClear", (CmdImpl) &Grp::impl_objClear);
	RegisterCommand(1, 61, 11, "objDelete", (CmdImpl) &Grp::impl_objClear);

	RegisterCommand(1, 71, 1000, "objOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 71, 1003, "objOfFileGan", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 71, 1100, "objOfArea", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 71, 1200, "objOfText", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 71, 1300, "objDriftOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 71, 1400, "objOfDigits", (CmdImpl) &Grp::impl_createObj);

	RegisterCommand(2, 71, 1000, "subObjOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(2, 71, 1003, "subObjOfGan", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(2, 71, 1100, "subObjOfArea", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(2, 71, 1200, "subObjOfText", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(2, 71, 1300, "subObjDriftOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(2, 71, 1400, "subObjOfDigits", (CmdImpl) &Grp::impl_createObj);

	//I suppose it's the same thing as createObj*, but I didn't see it in action. For now, mark it unhandled.
	RegisterCommand(1, 72, 1000, "objBgOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 72, 1003, "objBgOfFileGan", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 72, 1100, "objBgOfArea", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 72, 1200, "objBgOfText", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 72, 1300, "objBgDriftOfFile", (CmdImpl) &Grp::impl_createObj);
	RegisterCommand(1, 72, 1400, "objBgOfDigits", (CmdImpl) &Grp::impl_createObj);

	RegisterCommand(2, 72, 1000, "subObjBgOfFile", NULL);//FIXME
	RegisterCommand(2, 72, 1003, "subObjBgOfGan", NULL);//FIXME
	RegisterCommand(2, 72, 1100, "subObjBgOfArea", NULL);//FIXME
	RegisterCommand(2, 72, 1200, "subObjBgOfText", NULL);//FIXME
	RegisterCommand(2, 72, 1300, "subObjBgDriftOfFile", NULL);//FIXME
	RegisterCommand(2, 72, 1400, "subObjBgOfDigits", NULL);//FIXME;

	RegisterCommand(1, 73, 0, "ganStop?", NULL); //That's what xclannad says, but I'm not sure...
	RegisterCommand(1, 73, 1000, "ganStop", (CmdImpl) &Grp::impl_gan); //That's what rldev says
	RegisterCommand(1, 73, 3, "ganIsPlaying", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 2003, "objPlay", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 1001, "ganLoop", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 1003, "ganPlay", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 1005, "ganPlayOnce", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 3001, "ganLoop2", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 3003, "ganPlay2", (CmdImpl) &Grp::impl_gan);
	RegisterCommand(1, 73, 3005, "ganPlayOnce2", (CmdImpl) &Grp::impl_gan);

	RegisterCommand(2, 73, 0, "ganSubStop?", NULL); //FIXME
	RegisterCommand(2, 73, 1000, "ganSubStop", NULL); //FIXME
	RegisterCommand(2, 73, 3, "ganSubIsPlaying", NULL); //FIXME
	RegisterCommand(2, 73, 2003, "objSubPlay", NULL); //FIXME
	RegisterCommand(2, 73, 1001, "ganSubLoop", NULL); //FIXME
	RegisterCommand(2, 73, 1003, "ganSubPlay", NULL); //FIXME
	RegisterCommand(2, 73, 1005, "ganSubPlayOnce", NULL); //FIXME
	RegisterCommand(2, 73, 3001, "ganSubLoop2", (CmdImpl) &Grp::impl_gan); //FIXME
	RegisterCommand(2, 73, 3003, "ganSubPlay2", NULL); //FIXME
	RegisterCommand(2, 73, 3005, "ganSubPlayOnce2", NULL); //FIXME


	RegisterCommand(1, 81, 1000, "objMove", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 82, 1000, "objBgMove", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 81, 1001, "objLeft", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 82, 1001, "objBgLeft", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 81, 1002, "objTop", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 82, 1002, "objBgTop", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 81, 1003, "objAlpha", (CmdImpl) &Grp::impl_objAlpha);
	RegisterCommand(1, 82, 1003, "objBgAlpha", (CmdImpl) &Grp::impl_objAlpha);
	RegisterCommand(1, 81, 1004, "objShow", (CmdImpl) &Grp::impl_objShow);
	RegisterCommand(1, 82, 1004, "objBgShow", (CmdImpl) &Grp::impl_objShow);
	RegisterCommand(1, 81, 1005, "objDispArea", NULL);
	RegisterCommand(1, 82, 1005, "objBgDispArea", NULL);
	RegisterCommand(1, 81, 1006, "objAdjust", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 82, 1006, "objBgAdjust", NULL); //FIXME: (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(1, 81, 1007, "objAdjustX", NULL);
	RegisterCommand(1, 82, 1007, "objBgAdjustX", NULL);
	RegisterCommand(1, 81, 1008, "objAdjustY", NULL);
	RegisterCommand(1, 82, 1008, "objBgAdjustY", NULL);
	RegisterCommand(1, 81, 2006, "objAdjust2?", NULL); //FIXME: (CmdImpl) &Grp::impl_objSetPos); I don't know if it is usefull or properly implemented
	RegisterCommand(1, 82, 2006, "objBgAdjust2?", NULL); //FIXME: (CmdImpl) &Grp::impl_objSetPos); See above
	RegisterCommand(1, 81, 1016, "objColour", NULL); //FIXME: (CmdImpl) &Grp::impl_objColour);
	RegisterCommand(1, 82, 1016, "objBgColour", NULL); //FIXME: (CmdImpl) &Grp::impl_objColour);
	RegisterCommand(1, 81, 1017, "objColR", NULL);
	RegisterCommand(1, 82, 1017, "objBgColR", NULL);
	RegisterCommand(1, 81, 1018, "objColG", NULL);
	RegisterCommand(1, 82, 1018, "objBgColG", NULL);
	RegisterCommand(1, 81, 1019, "objColB", NULL);
	RegisterCommand(1, 82, 1019, "objBgColB", NULL);
	RegisterCommand(1, 81, 1020, "objColLevel", NULL);
	RegisterCommand(1, 82, 1020, "objBgColLevel", NULL);
	RegisterCommand(1, 81, 1021, "objComposite", (CmdImpl) &Grp::impl_objComposite); //FIXME: May be broken
	RegisterCommand(1, 82, 1021, "objBgComposite", (CmdImpl) &Grp::impl_objComposite);
	RegisterCommand(1, 81, 1024, "objSetText", (CmdImpl) &Grp::impl_objSetText);
	RegisterCommand(1, 82, 1024, "objBgSetText", (CmdImpl) &Grp::impl_objSetText);
	RegisterCommand(1, 81, 1025, "objTextOpts", (CmdImpl) &Grp::impl_objTextOpts); //FIXME: Incomplete
	RegisterCommand(1, 82, 1025, "objBgTextOpts", (CmdImpl) &Grp::impl_objTextOpts);
	RegisterCommand(1, 81, 1032, "objOrder", (CmdImpl) &Grp::impl_objOrder);
	RegisterCommand(1, 82, 1032, "objBgOrder", (CmdImpl) &Grp::impl_objOrder);
	RegisterCommand(1, 81, 1034, "objDispRect", (CmdImpl) &Grp::impl_objDispArea);
	RegisterCommand(1, 82, 1034, "objBgDispRect", (CmdImpl) &Grp::impl_objDispArea);
	RegisterCommand(1, 81, 1037, "objSetDigits", (CmdImpl) &Grp::impl_objSetDigits);
	RegisterCommand(1, 82, 1037, "objBgSetDigits", (CmdImpl) &Grp::impl_objSetDigits);
	RegisterCommand(1, 81, 1038, "objNumOpts", (CmdImpl) &Grp::impl_objNumOpts);
	RegisterCommand(1, 82, 1038, "objBgNumOpts", (CmdImpl) &Grp::impl_objNumOpts);
	RegisterCommand(1, 81, 1039, "objPattNo", (CmdImpl) &Grp::impl_objPattNo);
	RegisterCommand(1, 82, 1039, "objBgPattNo", (CmdImpl) &Grp::impl_objPattNo);
	RegisterCommand(1, 81, 1046, "objScale", (CmdImpl) &Grp::impl_objScale); //FIXME: Broken behaviour
	RegisterCommand(1, 82, 1046, "objBgScale", (CmdImpl) &Grp::impl_objScale);
	RegisterCommand(1, 81, 1047, "objWidth", NULL);
	RegisterCommand(1, 82, 1047, "objBgWidth", NULL);
	RegisterCommand(1, 81, 1049, "objRotate", (CmdImpl) &Grp::impl_objRotate);
	RegisterCommand(1, 82, 1049, "objBgRotate", (CmdImpl) &Grp::impl_objRotate);

	RegisterCommand(2, 81, 1000, "childObjMove", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(2, 82, 1000, "childObjBgMove", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(2, 81, 1001, "childObjLeft", NULL);
	RegisterCommand(2, 82, 1001, "childObjBgLeft", NULL);
	RegisterCommand(2, 81, 1002, "childObjTop", NULL);
	RegisterCommand(2, 82, 1002, "childObjBgTop", NULL);
	RegisterCommand(2, 81, 1003, "childObjAlpha", (CmdImpl) &Grp::impl_objAlpha);
	RegisterCommand(2, 82, 1003, "childObjBgAlpha", (CmdImpl) &Grp::impl_objAlpha);
	RegisterCommand(2, 81, 1004, "childObjShow", (CmdImpl) &Grp::impl_objShow);
	RegisterCommand(2, 82, 1004, "childObjBgShow", (CmdImpl) &Grp::impl_objShow);
	RegisterCommand(2, 81, 1005, "childObjDispArea", NULL);
	RegisterCommand(2, 82, 1005, "childObjBgDispArea", NULL);
	RegisterCommand(2, 81, 1006, "childObjAdjust", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(2, 82, 1006, "childObjBgAdjust", (CmdImpl) &Grp::impl_objSetPos);
	RegisterCommand(2, 81, 1007, "childObjAdjustX", NULL);
	RegisterCommand(2, 82, 1007, "childObjBgAdjustX", NULL);
	RegisterCommand(2, 81, 1008, "childObjAdjustY", NULL);
	RegisterCommand(2, 82, 1008, "childObjBgAdjustY", NULL);
	RegisterCommand(2, 81, 2006, "childObjAdjust2?", NULL);
	RegisterCommand(2, 82, 2006, "childObjBgAdjust2?", NULL);
	RegisterCommand(2, 81, 1016, "childObjColour", NULL);
	RegisterCommand(2, 82, 1016, "childObjBgColour", NULL);
	RegisterCommand(2, 81, 1017, "childObjColR", NULL);
	RegisterCommand(2, 82, 1017, "childObjBgColR", NULL);
	RegisterCommand(2, 81, 1018, "childObjColG", NULL);
	RegisterCommand(2, 82, 1018, "childObjBgColG", NULL);
	RegisterCommand(2, 81, 1019, "childObjColB", NULL);
	RegisterCommand(2, 82, 1019, "childObjBgColB", NULL);
	RegisterCommand(2, 81, 1020, "childObjColLevel", NULL);
	RegisterCommand(2, 82, 1020, "childObjBgColLevel", NULL);
	RegisterCommand(2, 81, 1021, "childObjComposite", NULL);
	RegisterCommand(2, 82, 1021, "childObjBgComposite", NULL);
	RegisterCommand(2, 81, 1024, "childObjSetText", (CmdImpl) &Grp::impl_objSetText);
	RegisterCommand(2, 82, 1024, "childObjBgSetText", (CmdImpl) &Grp::impl_objSetText);
	RegisterCommand(2, 81, 1025, "childObjTextOpts", (CmdImpl) &Grp::impl_objTextOpts);
	RegisterCommand(2, 82, 1025, "childObjBgTextOpts", (CmdImpl) &Grp::impl_objTextOpts);
	RegisterCommand(2, 81, 1032, "childObjOrder", NULL);
	RegisterCommand(2, 82, 1032, "childObjBgOrder", NULL);
	RegisterCommand(2, 81, 1034, "childObjDispRect", NULL);
	RegisterCommand(2, 82, 1034, "childObjBgDispRect", NULL);
	RegisterCommand(2, 81, 1037, "childObjSetDigits", (CmdImpl) &Grp::impl_objSetDigits);
	RegisterCommand(2, 82, 1037, "childObjBgSetDigits", (CmdImpl) &Grp::impl_objSetDigits);
	RegisterCommand(2, 81, 1038, "childObjNumOpts", (CmdImpl) &Grp::impl_objNumOpts);
	RegisterCommand(2, 82, 1038, "childObjBgNumOpts", (CmdImpl) &Grp::impl_objNumOpts);
	RegisterCommand(2, 81, 1039, "childObjPattNo", (CmdImpl) &Grp::impl_objPattNo);
	RegisterCommand(2, 82, 1039, "childObjBgPattNo", (CmdImpl) &Grp::impl_objPattNo);
	RegisterCommand(2, 81, 1046, "childObjScale", (CmdImpl) &Grp::impl_objScale);
	RegisterCommand(2, 82, 1046, "childObjBgScale", (CmdImpl) &Grp::impl_objScale);
	RegisterCommand(2, 81, 1047, "childObjWidth", NULL);
	RegisterCommand(2, 82, 1047, "childObjBgWidth", NULL);
	RegisterCommand(2, 81, 1049, "childObjRotate", NULL);
	RegisterCommand(2, 82, 1049, "childObjBgRotate", NULL);

	RegisterCommand(1, 84, 1000, "objGetPos", (CmdImpl) &Grp::impl_objPosDims);
	RegisterCommand(1, 84, 1100, "objGetDims", (CmdImpl) &Grp::impl_objPosDims);

	RegisterCommand(2, 84, 1000, "childObjGetPos", (CmdImpl) &Grp::impl_objPosDims);
	RegisterCommand(2, 84, 1100, "childObjGetDims", (CmdImpl) &Grp::impl_objPosDims);

	RegisterCommand(1, 31, 0, "refresh", (CmdImpl) &Grp::impl_refresh);

	RegisterCommand(1, 20, 0, "bgmLoop", (CmdImpl) &Grp::impl_bgmLoop);
	RegisterCommand(1, 20, 1, "bgmPlayEx", (CmdImpl) &Grp::impl_bgmLoop); //FIXME: wait
	RegisterCommand(1, 20, 2, "bgmPlay", (CmdImpl) &Grp::impl_bgmLoop);
	RegisterCommand(1, 20, 5, "bgmStop", (CmdImpl) &Grp::impl_bgmStop);
	RegisterCommand(1, 20, 105, "bgmFadeOut", (CmdImpl) &Grp::impl_bgmStop);

	RegisterCommand(1, 21, 0, "wavPlay", (CmdImpl) &Grp::impl_playWav);
	RegisterCommand(1, 21, 1, "wavPlayEx", (CmdImpl) &Grp::impl_playWav);
	RegisterCommand(1, 21, 2, "wavLoop", (CmdImpl) &Grp::impl_playWav);
	RegisterCommand(1, 21, 3, "wavWait", NULL);
	RegisterCommand(1, 21, 4, "wavPlaying", NULL);
	RegisterCommand(1, 21, 5, "wavStop", (CmdImpl) &Grp::impl_stopWav);
	RegisterCommand(1, 21, 105, "wavFadeout", (CmdImpl) &Grp::impl_stopWav);

	RegisterCommand(1, 22, 0, "sePlay", (CmdImpl) &Grp::impl_playSE);

	RegisterCommand(1, 4, 2230, "SetBgmVolMod", (CmdImpl) &Grp::impl_SetVolMod);
	RegisterCommand(1, 4, 2231, "SetKoeVolMod", (CmdImpl) &Grp::impl_SetVolMod);
	RegisterCommand(1, 4, 2232, "SetPCMVolMod", (CmdImpl) &Grp::impl_SetVolMod);
	RegisterCommand(1, 4, 2233, "SetSeVolMod", (CmdImpl) &Grp::impl_SetVolMod);
	RegisterCommand(1, 4, 2330, "BgmVolMod", (CmdImpl) &Grp::impl_GetVolMod);
	RegisterCommand(1, 4, 2331, "KoeVolMod", (CmdImpl) &Grp::impl_GetVolMod);
	RegisterCommand(1, 4, 2332, "PCMVolMod", (CmdImpl) &Grp::impl_GetVolMod);
	RegisterCommand(1, 4, 2333, "SeVolMod", (CmdImpl) &Grp::impl_GetVolMod);

	RegisterCommand(1, 23, 0, "koePlay", (CmdImpl) &Grp::impl_koePlay);
	RegisterCommand(1, 23, 1, "koePlayEx", (CmdImpl) &Grp::impl_koePlay); //FIXME
	RegisterCommand(1, 23, 7, "koePlayExC", (CmdImpl) &Grp::impl_koePlay); //FIXME
	RegisterCommand(1, 23, 8, "koeDoPlay", (CmdImpl) &Grp::impl_koePlay); //FIXME
	RegisterCommand(1, 23, 9, "koeDoPlayEx", (CmdImpl) &Grp::impl_koePlay); //FIXME
	RegisterCommand(1, 23, 10, "koeDoPlayExC", (CmdImpl) &Grp::impl_koePlay); //FIXME

	RegisterCommand(1, 26, 1, "movPlayEx", (CmdImpl) &Grp::impl_movPlay);
	RegisterCommand(1, 26, 20, "movPlayExC", (CmdImpl) &Grp::impl_movPlay);

	RegisterCommand(1, 61, 14, "objSwap?", NULL);
	RegisterCommand(1, 62, 14, "objSwap?", NULL);

	RegisterCommand(1, 4, 1211, "EnableSyscom", NULL);
	RegisterCommand(1, 4, 1212, "HideSyscom", NULL);
	RegisterCommand(1, 4, 1213, "DisableSyscom", NULL);

	anm1 = NULL;
	anm2 = NULL;
}

Grp::~Grp() {
	map<int,GrpObj>::iterator it;
	for (it=grpobj.begin(); it!=grpobj.end(); it++) {
		PicBase* p = it->second.DeletePic();
		delete p;
	}

	delete screen;
	delete screen_front;
	parent.Root().DeleteSurface(surface);
	parent.Root().DeleteSurface(surface_update);
	int i;
	for (i=0; i<MAXPDT; i++) {
		if (ssurface[i]) parent.Root().DeleteSurface(ssurface[i]);
		if (dsurface[i]) parent.Root().DeleteSurface(dsurface[i]);
	}
}

Surface* Grp::Dsurface(int pdt) {
	if (pdt == 0) return surface;
	if (dsurface[pdt] == 0) { // とりあえず画面の大きさということにする
		if (pdt == WORKPDT)
			dsurface[pdt] = parent.Root().NewSurface(parent.Width(), parent.Height(), ALPHA_MASK);
		else
			dsurface[pdt] = parent.Root().NewSurface(parent.Width(), parent.Height(), NO_MASK);
	}
	if (ssurface[pdt]) { // ssurface が存在すれば、dsurface にコピーして返す
		DSurfaceMove(ssurface[pdt], Rect(*ssurface[pdt]), dsurface[pdt], Rect(0,0));
		parent.Root().DeleteSurface(ssurface[pdt]);
		ssurface[pdt] = 0;
	}
	return dsurface[pdt];
}

GrpObj* Grp::GetGraphicObj(int grp, bool fg) {
	if (fg)
		return &grpobj[grp];
	else
		return &bs_obj[grp];
}

GrpObj* Grp::GetGraphicObj(int grp, int index, bool fg) {
	GrpObj* g = GetGraphicObj(grp, fg);
	return &g->children_obj[index];
}

GrpObj* Grp::GetGraphicObjVarMode(Cmd& cmd, int &base_arg, bool fg) {
	GrpObj* g;
	if (cmd.cmd1 == 2) {
		g = GetGraphicObj(cmd.args[base_arg].value, cmd.args[base_arg+1].value, fg);
		base_arg += 1;
	}
	else
		g = GetGraphicObj(cmd.args[base_arg].value, fg);
	return g;
}

#include <SDL.h>
Surface* Grp::Ssurface(int pdt) {
	if (pdt == 0) return surface;
	if (ssurface[pdt]) {
		return ssurface[pdt];
	}
	return Dsurface(pdt);
}

void Grp::LoadSurface(const char* str, int pdt) {
	string s = str;
	if (cgm_info.find(s) != cgm_info.end()) {
		cgm_data.insert(cgm_info[s]);
	}
	Surface* bg = parent.Root().NewSurface(s.c_str());
	if (bg == NULL) {
		s += ".g00";
		bg = parent.Root().NewSurface(s.c_str());
	}
	if (bg != NULL) {
		if (ssurface[pdt]) parent.Root().DeleteSurface(ssurface[pdt]);
		ssurface[pdt] = bg;
		if (pdt == 0) {
			/* とりあえず Princess Bride のアニメーション効果専用 */
			Rect r(*ssurface[0]);
			Rect dr(*surface);
			int x = (dr.width()-r.width())/2;
			int y = (dr.height()-r.height())/2;
			DSurfaceMove(ssurface[0], r, surface, Rect(x,y));
			parent.Root().DeleteSurface(ssurface[0]);
			ssurface[0] = NULL;
			screen->SetSurface(surface, 0, 0);
		}
	} else {
		if (str[0] != 0)
			fprintf(stderr,"Cannot find surface %d <- '%s'\n",pdt,str);
	}
}

void Grp::InitSel(void) {
	int i;
	int args[16];
	char key[10];
	for (i=0; i<999; i++) {
		sprintf(key, "#SEL.%03d",i);
		if (config->GetParam(key, 15, &args[0], &args[1],
			&args[2], &args[3], &args[4], &args[5], &args[6], &args[7],
			&args[8], &args[9], &args[10], &args[11], &args[12], &args[13],
			&args[14])) {

			sprintf(key, "#SELR.%03d", i);
			if (config->GetParam(key, 16, &args[0], &args[1],
				&args[2], &args[3], &args[4], &args[5], &args[6], &args[7],
				&args[8], &args[9], &args[10], &args[11], &args[12], &args[13],
				&args[14], &args[15]))  continue;
		}
		SEL& s = anmtype[i];
		s.from = Rect(args[0], args[1], args[2]+1, args[3]+1);
		s.to = Rect(args[4], args[5]);
		s.time = args[6];
		s.sel_no = args[7];
		int j;
		for (j=0; j<8; j++) s.args[j] = args[8+j];
	}
}

void Grp::SetSkipMode(SkipMode _mode) {
	if ( (skip_mode & SKIP_IN_MENU) && (_mode & SKIP_IN_MENU) == 0) {
		RefreshObj();
	} else if ( (skip_mode & SKIP_IN_MENU) == 0 && (_mode & SKIP_IN_MENU) ) {
	}
	skip_mode = _mode;
}

void Grp::SetObjChanged(int num) {
	changed_obj.insert(num);
}

void Grp::RefreshObj(void) {
	if (!deleted_pic.empty()) {
		vector<PicBase*>::iterator it;
		for (it=deleted_pic.begin(); it!=deleted_pic.end(); it++) {
			if (*it) delete *it;
		}
		deleted_pic.clear();
	}
	if (!changed_obj.empty()) {
		set<int>::iterator it;
		for (it=changed_obj.begin(); it != changed_obj.end(); it++) {
			if (grpobj.find(*it) == grpobj.end()) continue;
			GrpObj& obj = grpobj[*it];
			obj.Refresh(obj);
		}
		changed_obj.clear();
	}
	if (reserved_load_surface0.length() != 0) {
		LoadSurface(reserved_load_surface0.c_str(), 0);
		reserved_load_surface0 = "";
	}
	screen->ReBlit();
}


#include <SDL.h>
void Grp::StartAnm(int type) {
	SEL sel;

	if (anmtype.find(type) == anmtype.end()) {
		if (anmtype.find(0) == anmtype.end()) {
			sel.sel_no = 1;
			sel.from = Rect(*surface);
			sel.to = Rect(0,0);
			sel.time = 0;
		} else {
			sel = anmtype[0];
		}
	} else {
		sel = anmtype[type];
	}
	if (anm1 != NULL) {
		fprintf(stderr,"Warning: StartAnm() called before anm1 finished\n");
		anm1->Abort();
		delete anm1;
		anm1 = NULL;
	}
	map<int,GrpObj>::iterator it;
	// 現在表示中のobjectを消去
	deleted_pic.push_back(screen);
	for (it=grpobj.begin(); it!=grpobj.end(); it++) {
		if (! (it->second.attr & GrpObj::WIPEON)) { // 画像切り替え時に object 削除
			deleted_pic.push_back(it->second.DeletePic());
		} else {
			GrpObj& new_obj = bs_obj[it->first];
			if (new_obj.name.empty()) { // 新しい object が存在しなければ内容を引き継ぐ
				new_obj = it->second;
				it->second.DeletePic();
			} else {
				new_obj.attr = GrpObj::Attribute(new_obj.attr | GrpObj::WIPEON);
				deleted_pic.push_back(it->second.DeletePic());
			}
		}
	}
	grpobj.clear(); // 全オブジェクト削除

	// 全画像オブジェクトの前にscreen 移動
	// 新しい screen_front を作成しておく
	screen = screen_front;
	screen->hide();
	screen->SetSurface(surface_update, 0, 0);
	parent.Root().BlitSurface(Dsurface(1), Rect(*surface_update), surface_update, Rect(0,0));

	screen_front = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
	screen_front->hide();
	screen_front->ZMove(screen);
	
	// 新しい object へ更新、surface_update へ新しい object を表示
	// (object 作成時は picture は hide されている)
	for (it=bs_obj.begin(); it!=bs_obj.end(); it++) {
		grpobj[it->first] = it->second;
		it->second.DeletePic();
		CreateObj(it->first);//FIXME: Adapt to groups
		GrpObj& g = grpobj[it->first];
		if (g.picture) {
			g.Update();
			if (g.alpha == 0 || (g.attr & GrpObj::HIDDEN)) ;
			else g.picture->SimpleBlit(surface_update);
			g.picture->hide();
		}
	}
	bs_obj.clear();
	// 画像効果開始
	switch(sel.sel_no) {
		default:
		case 0: case 50: // 0 と 50 の違いが良くわからない
			if (skip_mode & SKIP_GRP_NOEFFEC)
				anm1 = new WidAnmAlpha(event, screen, ALPHA_MAX, ALPHA_MAX, 0);
			else if (skip_mode & SKIP_GRP_FAST)
				anm1 = new WidAnmAlpha(event, screen, 0, ALPHA_MAX, sel.time/4);
			else
				anm1 = new WidAnmAlpha(event, screen, 0, ALPHA_MAX, sel.time);
			break;
	}
	if (anm1) anm1->Play();
	if (skip_mode & SKIP_GRP_NOEFFEC) AbortAnm();
}

void Grp::StartShake(int total, const int* pattern) {
	if (anm2) {
		fprintf(stderr,"Warning: StartShake() called before another animation finished\n");
		anm2->Abort();
		delete anm2;
		anm2 = NULL;
	}
	if (skip_mode & SKIP_GRP_NOEFFEC) return;
	AnmAlphaMove* new_anm = new AnmAlphaMove(event, &parent); // shake screen では元画面の座標を揺らす
	int i;
	int tm = 0;
	for (i=0; i<total; i+=3) {
		int x = pattern[i];
		int y = pattern[i+1];
		new_anm->ptns.push_back(AnmAlphaMove::Ptn(Rect(x,y), Rect(0,0), 255, tm));
		tm += pattern[i+2];
	}
	new_anm->ptns.push_back(AnmAlphaMove::Ptn(Rect(0,0), Rect(0,0), 255, tm));
	new_anm->SetPtn(); // パターン登録終了
	new_anm->Play();
	anm2 = new_anm;
}

void Grp::AbortAnm(void) {
	if (anm1 == NULL) return;
	anm1->Abort();
	delete anm1;
	anm1 = NULL;
	/* 画像効果終了 */
	/* 古い画面への画像効果があれば消去 */
	if (anm2 && anm2->pic[0] != screen) {
		anm2->Abort();
		delete anm2;
		anm2 = NULL;
	}
	/* pdt1 -> pdt0 へコピー */
	DSurfaceMove(dsurface[1], Rect(*dsurface[1]), surface, Rect(0,0));
	screen->SetSurface(surface, 0, 0);
	// 画像効果開始時に存在したobjectを消去
	// 新しい object 表示
	RefreshObj();
	return;
}

void Grp::LoadSurface(const char* str) {
	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
	LoadSurface(str, 1);
	bg_name = str;
}

void Grp::LoadSurface(void) {
	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
	LoadSurface(bg_name.c_str(), 1);
}

void Grp::AddSurface(const char* str) {
	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
	LoadSurface(bg_name.c_str());

	string s = str;
	Surface* front = parent.Root().NewSurface(s.c_str());
	if (front == NULL) {
		s += ".g00";
		front = parent.Root().NewSurface(s.c_str());
	}
	if (front != NULL) {
		parent.Root().BlitSurface(front, Rect(*front), Dsurface(1), Rect(0,0));
		parent.Root().DeleteSurface(front);
	} else {
		fprintf(stderr,"Cannot find surface %s\n",str);
	}
}

void Grp::CreateObj(int index) {
	GrpObjMap::iterator cur = grpobj.find(index);
	if (cur == grpobj.end()) return;
	GrpObj& g = grpobj[index];
	g.CreateSurface(&parent);
	g.order = index;
	if (g.picture == NULL) return; // エラー:surface が存在しない
	g.picture->hide();
	SetObjChanged(index);
	ZMoveObj(index);
}

void Grp::CreateSubObj(int grp_index, int index) {
	GrpObjMap::iterator cur = grpobj.find(grp_index);
	if (cur == grpobj.end()) return;
	GrpObj* g = &grpobj[grp_index];
	cur = g->children_obj.find(index);
	if (cur == g->children_obj.end()) return;
	g = &g->children_obj[index];
	g->CreateSurface(&parent);
	g->order = index;
	if (g->picture == NULL) return; // エラー:surface が存在しない
	g->picture->hide();
	//TODO
	SetObjChanged(grp_index);
	/*ZMoveObj(index);*/
}

void Grp::ZMoveObj(int index) {
	GrpObjMap::iterator cur = grpobj.find(index);
	if (cur == grpobj.end()) return;
	GrpObj& g = grpobj[index];
	if (g.picture == NULL) return;
	// 自分より前に object があれば、その前に表示
	// そうでなければ screen の前に表示
	GrpObjMap::iterator cur_backobj = grpobj.end();
	GrpObjMap::iterator it;
	for (it = grpobj.begin(); it != grpobj.end(); it++) {
		if (it == cur) continue;
		if (it->second.picture == NULL) continue;
		if (it->second.order < g.order) {
			if (cur_backobj == grpobj.end()) {
				cur_backobj = it;
			} else if (cur_backobj->second.order < it->second.order) {
				cur_backobj = it;
			}
		}
	}
	if (cur_backobj == grpobj.end()) {
		g.picture->ZMove(screen);
	} else {
		g.picture->ZMove(cur_backobj->second.picture);
	}
}

void Grp::SwapObj(int index1, int index2) {
	// デフォルト値から order が変更されていた場合のみ、order は保存される
	// まずは両方のobjectをswap
	if (grpobj.find(index1) == grpobj.end()) {
		if (grpobj.find(index2) == grpobj.end()) return; // どちらの object も存在しない
		grpobj[index1] = grpobj[index2];
		if (grpobj[index1].order == index2)
			grpobj[index1].order = index1;
		grpobj[index2].DeletePic();
		grpobj.erase(index2);
		ZMoveObj(index1);
		return;
	} else if (grpobj.find(index2) == grpobj.end()) { // index2 が存在しない場合
		grpobj[index2] = grpobj[index1];
		if (grpobj[index2].order == index1)
			grpobj[index2].order = index2;
		grpobj[index1].DeletePic();
		grpobj.erase(index1);
		ZMoveObj(index2);
		return;
	} else {
		GrpObj obj = grpobj[index1];
		grpobj[index1] = grpobj[index2];
		grpobj[index2].DeletePic();
		if (grpobj[index1].order == index2)
			grpobj[index1].order = index1;
		ZMoveObj(index1);
		grpobj[index2] = obj;
		if (grpobj[index2].order == index1)
			grpobj[index2].order = index2;
		ZMoveObj(index2);
		obj.DeletePic();
	}
}

bool Grp::Pressed(int x, int y, void* pointer) { // マウスクリックでキャンセル
	Grp* g = (Grp*)pointer;
	if (g->status == WAIT_MOVIE)
		g->music->StopMovie();
	if (g->status == WAIT_ANM)
		g->AbortAnm();
	if (g->status == WAIT_SHAKE && g->anm2 != NULL) {
		g->anm2->Abort();
		delete g->anm2;
		g->anm2 = NULL;
	}
	return false; // event deleted
}

/* mode.cgm の decode 用 */
static unsigned char decode_char[256] = {
	0x8b, 0xe5, 0x5d, 0xc3, 0xa1, 0xe0, 0x30, 0x44, 
	0x00, 0x85, 0xc0, 0x74, 0x09, 0x5f, 0x5e, 0x33, 
	0xc0, 0x5b, 0x8b, 0xe5, 0x5d, 0xc3, 0x8b, 0x45, 
	0x0c, 0x85, 0xc0, 0x75, 0x14, 0x8b, 0x55, 0xec, 
	0x83, 0xc2, 0x20, 0x52, 0x6a, 0x00, 0xe8, 0xf5, 
	0x28, 0x01, 0x00, 0x83, 0xc4, 0x08, 0x89, 0x45, 
	0x0c, 0x8b, 0x45, 0xe4, 0x6a, 0x00, 0x6a, 0x00, 
	0x50, 0x53, 0xff, 0x15, 0x34, 0xb1, 0x43, 0x00, 
	0x8b, 0x45, 0x10, 0x85, 0xc0, 0x74, 0x05, 0x8b, 
	0x4d, 0xec, 0x89, 0x08, 0x8a, 0x45, 0xf0, 0x84, 
	0xc0, 0x75, 0x78, 0xa1, 0xe0, 0x30, 0x44, 0x00, 
	0x8b, 0x7d, 0xe8, 0x8b, 0x75, 0x0c, 0x85, 0xc0, 
	0x75, 0x44, 0x8b, 0x1d, 0xd0, 0xb0, 0x43, 0x00, 
	0x85, 0xff, 0x76, 0x37, 0x81, 0xff, 0x00, 0x00, 
	0x04, 0x00, 0x6a, 0x00, 0x76, 0x43, 0x8b, 0x45, 
	0xf8, 0x8d, 0x55, 0xfc, 0x52, 0x68, 0x00, 0x00, 
	0x04, 0x00, 0x56, 0x50, 0xff, 0x15, 0x2c, 0xb1, 
	0x43, 0x00, 0x6a, 0x05, 0xff, 0xd3, 0xa1, 0xe0, 
	0x30, 0x44, 0x00, 0x81, 0xef, 0x00, 0x00, 0x04, 
	0x00, 0x81, 0xc6, 0x00, 0x00, 0x04, 0x00, 0x85, 
	0xc0, 0x74, 0xc5, 0x8b, 0x5d, 0xf8, 0x53, 0xe8, 
	0xf4, 0xfb, 0xff, 0xff, 0x8b, 0x45, 0x0c, 0x83, 
	0xc4, 0x04, 0x5f, 0x5e, 0x5b, 0x8b, 0xe5, 0x5d, 
	0xc3, 0x8b, 0x55, 0xf8, 0x8d, 0x4d, 0xfc, 0x51, 
	0x57, 0x56, 0x52, 0xff, 0x15, 0x2c, 0xb1, 0x43, 
	0x00, 0xeb, 0xd8, 0x8b, 0x45, 0xe8, 0x83, 0xc0, 
	0x20, 0x50, 0x6a, 0x00, 0xe8, 0x47, 0x28, 0x01, 
	0x00, 0x8b, 0x7d, 0xe8, 0x89, 0x45, 0xf4, 0x8b, 
	0xf0, 0xa1, 0xe0, 0x30, 0x44, 0x00, 0x83, 0xc4, 
	0x08, 0x85, 0xc0, 0x75, 0x56, 0x8b, 0x1d, 0xd0, 
	0xb0, 0x43, 0x00, 0x85, 0xff, 0x76, 0x49, 0x81, 
	0xff, 0x00, 0x00, 0x04, 0x00, 0x6a, 0x00, 0x76
};

void Grp::LoadCgm() {
	/* cgm ファイル読み込み */
	const char* fname = config->GetParaStr("#CGTABLE_FILE");
	if (fname == NULL) return;
	ARCINFO* info = FileSearcher::GetInstance()->Find(FileSearcher::ALL, fname, "");
	if (info == NULL) return;
	char* data = info->CopyRead();
	int sz = info->Size();
	delete info;

	
	if ( strncmp(data, "CGTABLE", 7) != 0) {
		delete[] data;
		return;
	}
	cgm_size = read_little_endian_int(data+0x10);

	int i, j;
	// xor 解除
	for (i=0; i < sz-0x20; i++) {
		data[i+0x20]^=decode_char[i&0xff];
	}
	// 展開
	int dest_size = cgm_size * 36;
	char* dest = new char[dest_size+1024];
	char* src = data + 0x28;
	char* dest_orig = dest;
	ARCINFO::Extract2k(dest,src,dest+dest_size,data+sz);
	dest = dest_orig;
	for (i=0; i<cgm_size; i++) {
		char* s = dest + i * 36;
		int n = read_little_endian_int(dest + i * 36 + 32);
		cgm_info[s] = n;
	}
	delete[] data;
	delete[] dest_orig;
}

/*****************************************************
*
*   Grp :: Save, Load : セーブファイル処理
*
*/
void Grp::Save(std::string& str) {
}

void Grp::Load(const char* str) {
	status = NORMAL;
	if (anm1 != NULL) {
		AbortAnm();
	}
	if (anm2 != NULL) {
		anm2->Abort();
		delete anm2;
		anm2 = NULL;
	}
	map<int,GrpObj>::iterator it;
	for (it=grpobj.begin(); it!=grpobj.end(); it++) {
		PicBase* p = it->second.DeletePic();
		delete p;
	}
	grpobj.clear();
	
	bg_name = "";
	music->StopCDROM(100);
}

void Grp::SaveSys(string& save) {
	char buf[1024];
	save = "\n[Graphics]\n";
	save += "CGM_CG=";

	set<int>::iterator it;
	for (it=cgm_data.begin(); it != cgm_data.end(); it++) {
		sprintf(buf,"%d,",*it);
		save += buf;
	}
	save += "\n";
}

void Grp::LoadSys(const char* save) {
	cgm_data.clear();
	save = strstr(save, "\n[Graphics]\n");

	if (save) {
		save += strlen("\n[Graphics]\n");
		do {
			if (save[0] == '[') break; // next section
			if (strncmp(save, "CGM_CG=",7) == 0) {
				save += 7;
				while(isdigit(*save)) {
					int n = atoi(save);
					cgm_data.insert(n);
					save = strchr(save, ',');
					if (save) save++;
				}
			}
			save = strchr(save, '\n');
			if (save) save++;
		} while (save);
	}
	return;
}


/*****************************************************
*
*   Grp :: Wait , Exec : コマンド実行部
*
*/
static vector<int> drawn_images;
static int draw_n = 0;
extern bool grpdump_req;
bool Grp::Wait(unsigned int current_time, Cmd& cmd) {
	if (grpdump_req) {
		grpdump_req = 0;
		GrpObjMap::iterator it;
		fprintf(stderr,"front %p(%d) / %p(%d)\n",screen,screen->IsHidden(),screen_front,screen_front->IsHidden());
		for (it=grpobj.begin(); it != grpobj.end(); it++) {
			GrpObj& obj = it->second;
			obj._debug_Dump(it->first, 0);
		}
		std::list<PicBase*>::iterator it2;
		for (it2=parent.children.begin(); it2!=parent.children.end();it2++) {
			fprintf(stderr,"%p(%d)\n",*it2,(*it2)->IsHidden());
		}
		RefreshObj();

	}
#if 0
	if (event.presscount(MOUSE_UP)) {
        	std::list<PicBase*>::iterator lit;
		draw_n++; int i=0;
		for (lit=parent.children.end(); lit!=parent.children.begin(); ) {
			lit--;
			(*lit)->hide();
			i++;
			if (i >= draw_n) break;
		}
		if (drawn_images.empty()) {
			map<int, GrpObj>::iterator it;
			for (it=grpobj.begin(); it!=grpobj.end(); it++) {
				if (it->second.picture) {
					drawn_images.push_back(it->first);
					PicBase* p = it->second.DeletePic();
					delete p;
				}
			}
		} else {
			vector<int>::iterator it;
			for (it=drawn_images.begin(); it!=drawn_images.end(); it++) {
				CreateObj(*it);
			}
			drawn_images.clear();
		}
	}
#endif
	if (status == WAIT_ANM) {
		if (anm1 != NULL) {
			if (!anm1->IsEnd()) return true;
			//FIXME: Handle animation loops
			AbortAnm();
		}
	} else if (status == WAIT_SHAKE) {
		if (anm2 != NULL) {
			if (!anm2->IsEnd()) return true;
			delete anm2;
			anm2 = NULL;
		}
		status = NORMAL;
	} else if (status == WAIT_SE) {
		if (music->IsStopSE()) status = NORMAL;
		return true;
	} else if (status == WAIT_MOVIE) {
		if (music->IsStopMovie()) {
			music->StopMovie();
			status = NORMAL;
			screen->ReBlit();
		}
		return true;
	}
	if (anm2 != NULL) {
		if (anm2->IsEnd()) {
			delete anm2;
			anm2 = NULL;
		}
	}
	return false;
}

void Grp::DeleteObjPic(int num) { // object の surface のみ削除
	if (grpobj.find(num) == grpobj.end()) return;
	deleted_pic.push_back(grpobj[num].DeletePic());
}

void Grp::DeleteSubObjPic(int num_grp, int num) {
	if (grpobj.find(num_grp) == grpobj.end()) return;
	if (grpobj[num_grp].children_obj.find(num) == grpobj[num_grp].children_obj.end()) return;
	deleted_pic.push_back(grpobj[num_grp].children_obj[num].DeletePic());
}

void Grp::DeleteObj(int num) {
	if (grpobj.find(num) == grpobj.end()) return;
	deleted_pic.push_back(grpobj[num].DeletePic());
	GrpObjMap::iterator it;
	for (it = grpobj[num].children_obj.begin(); it != grpobj[num].children_obj.end(); it++) {
		deleted_pic.push_back(it->second.DeletePic());
	}
	grpobj.erase(num);
}

void Grp::DeleteSubObj(int num_grp, int num) {
	if (grpobj.find(num_grp) == grpobj.end()) return;
	if (grpobj[num_grp].children_obj.find(num) == grpobj[num_grp].children_obj.end()) return;
	deleted_pic.push_back(grpobj[num_grp].children_obj[num].DeletePic());
	grpobj[num_grp].children_obj.erase(num);
}

void Grp::Exec(Cmd& cmd) {
	if (cmd.cmd_type == CMD_TEXTEND) {
		music->StopKoe(500); // テキスト終了で声を止める
		cmd.clear();
		return;
	}
	if (cmd.cmd_type == CMD_WAITFRAMEUPDATE) {
		// wait する場合は RefreshObj() しておく
		RefreshObj();
	}
	if (cmd.cmd_type != CMD_OTHER) return;

	CommandHandler::Exec(cmd);

	//TODO: ???
	if (cmd.cmd1 == 1 && cmd.cmd2 == 60 && cmd.cmd3 == 0) { // ??? : KANOGI : 画像オブジェクトの削除?
		DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
		GrpObj& g = grpobj[cmd.args[0].value];
		g.attr = GrpObj::Attribute(g.attr | GrpObj::HIDDEN);
		cmd.clear();
	}

	// Refresh changed objects...
	//FIXME: should may be go away?
	//Seems it'll work only for objects in the foreground
	if ( (cmd.cmd1 == 1 || cmd.cmd1 == 2) && cmd.cmd2 == 81) {
		GrpObj* g;
		if (cmd.cmd1 == 2)
			g = GetGraphicObj(cmd.args[0].value, cmd.args[1].value);
		else
			g = GetGraphicObj(cmd.args[0].value);
		if (g->attr & GrpObj::UPDATE_ALL)
			SetObjChanged(cmd.args[0].value);
	}
}