changeset 55:f1a27ee7e03c

* started the same changes on scn2k_text.cc * handle opcodes childObj*. In fact, it was handled (in a strange way, but it worked) before the previous changeset
author thib
date Wed, 22 Apr 2009 15:01:42 +0000
parents d7cde171a1de
children c7bcc0ec2267
files scn2k/command_handler.cc scn2k/scn2k.h scn2k/scn2k_grp.cc scn2k/scn2k_text.cc
diffstat 4 files changed, 557 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- a/scn2k/command_handler.cc
+++ b/scn2k/command_handler.cc
@@ -53,6 +53,30 @@ void CommandHandler::RegisterCommand(int
 	command_map[cmd] = info;
 }
 
+void CommandHandler::PrintCmd(Cmd& cmd)
+{
+	CommandMap::iterator it;
+	it = command_map.find(cmd);
+
+	fprintf(stderr, "%d - %d : ", cmd.scn, cmd.pos);
+	if (it == command_map.end())
+		fprintf(stderr, "%02d:%02d:%04d", cmd.cmd1, cmd.cmd2, cmd.cmd3);
+	else
+		fprintf(stderr, "%s", it->second.descr);
+	fprintf(stderr, "[%d] (", cmd.cmd4);
+	int i;
+	for (i=0; i<cmd.args.size(); i++) {
+		VarInfo info = cmd.args[i];
+		if (info.type == TYPE_STR || info.type == TYPE_VARSTR)
+			fprintf(stderr, "\"%s\"", cmd.Str(info));
+		else
+			fprintf(stderr, "%d", info.value);
+		if (i < cmd.args.size()-1)
+			fprintf(stderr, ", ");
+	}
+	fprintf(stderr,");\n");
+}
+
 bool CommandHandler::Exec(Cmd &cmd) {
 	CommandMap::iterator it;
 
--- a/scn2k/scn2k.h
+++ b/scn2k/scn2k.h
@@ -225,6 +225,7 @@ class CommandHandler {
 		} CommandInfo;
 		void RegisterCommand(int cmd1, int cmd2, int cmd3, const char* descr, CmdImpl func);
 		bool Exec(Cmd& cmd);
+		void PrintCmd(Cmd& cmd);
 
 	private:
 		typedef std::map<SimpleCmd, CommandInfo> CommandMap;
--- a/scn2k/scn2k_grp.cc
+++ b/scn2k/scn2k_grp.cc
@@ -53,6 +53,9 @@ struct SEL {
 	SEL() : from(0,0), to(0,0) {}
 };
 
+struct GrpObj;
+typedef std::map<int, GrpObj> GrpObjMap;
+
 struct GrpObj {
 	string name;
 	string gan_name;
@@ -66,6 +69,8 @@ struct GrpObj {
 	int order;
 	int surface_num;
 
+	GrpObjMap children_obj;
+
 	string print_moji;
 	int print_size, print_r, print_b, print_g;
 
@@ -77,16 +82,14 @@ struct GrpObj {
 
 	vector<Rect> src_pos;
 	enum GrpType { FILLRECT = 1, FILE = 2, GAN = 3, MOJI = 4, DIGIT = 5} gtype;
-	enum Attribute { NONE=0, WIPEON=1, SATURATE=2, HIDDEN=4, HIDDEN_GROUP=8,
+	enum Attribute { NONE=0, WIPEON=1, SATURATE=2, HIDDEN=4,
 		UPDATE_PICTURE = 16, UPDATE_POS = 32, UPDATE_ALPHA = 64, UPDATE_SNUM = 128, UPDATE_CLIP = 256, UPDATE_VISIBLE = 512,
-		UPDATE_ALL = (16|32|64|128|256|512),
+		UPDATE_ALL = (UPDATE_PICTURE | UPDATE_POS | UPDATE_ALPHA | UPDATE_SNUM | UPDATE_CLIP | UPDATE_VISIBLE),
 		ANM_PLAYSTART = 0x8000, ANM_PLAYING = 0x10000,
 		DIG_ZERO = 0x10000*2, DIG_SIGN = 0x10000*4, DIG_PACK=0x10000*8,DIG_SPACE=0x10000*16
 		};
 	Attribute attr;
 
-	GrpImpl* parent_pimpl;
-
 	GrpObj(void);
 	~GrpObj(void);
 
@@ -105,6 +108,8 @@ struct GrpObj {
 	void UpdateDigit(void);
 	void UpdateSurface(void);
 	void ZoomRotate(void);
+	void Refresh(GrpObj& parent_obj);
+	void _debug_Dump(int, int);
 	void Update(void);
 	void CreateSurface(PicContainer* parent);
 	void CreateGan(Event::Container& event, int event_number);
@@ -116,34 +121,19 @@ struct GrpObj {
 ** GrpObj(interface)
 */
 
-
-struct GrpObjMap : std::map<int, GrpObj> {
-	typedef pair<const int, GrpObj> value_type;
-	class GrpImpl* parent;
-	GrpObj& operator[](const int& k) {
-		iterator it = lower_bound(k);
-		if (it == end() || it->first != k) {
-			GrpObj obj;
-			obj.parent_pimpl = parent;
-			it = insert(it, value_type(k, obj));
-		}
-		return it->second;
-	}
-	GrpObjMap(class GrpImpl* p) {
-		parent = p;
-	}
-};
-
 class GrpImpl : public CommandHandler {
 #define MAXPDT 256
 #define WORKPDT 255
 	private:
 		void CreateObj(int number);
+		void CreateSubObj(int grp_num, int number);
 		void ZMoveObj(int number);
 		void SetObjChanged(int number);
 		void SwapObj(int a1, int a2);
 		void DeleteObjPic(int num);// object の surface のみ削除
+		void DeleteSubObjPic(int grp_num, int num);
 		void DeleteObj(int num);
+		void DeleteSubObj(int grp_num, int num);
 		void RefreshObj(void);
 
 		Surface* Dsurface(int pdt);
@@ -175,6 +165,10 @@ class GrpImpl : public CommandHandler {
 		void AbortAnm(void);
 		static bool Pressed(int x, int y, void* pointer);
 
+		GrpObj* GetGraphicObj(int grp, bool fg=true);
+		GrpObj* GetGraphicObj(int grp, int index, bool fg=true);
+		GrpObj* GetGraphicObjVarMode(Cmd& cmd, int &base_arg, bool fg=true);
+
 		// Opcode handling
 		void impl_stackClear(Cmd& cmd);
 		void impl_grpBuffer(Cmd& cmd);
@@ -263,7 +257,7 @@ GrpObj::GrpObj(void) :
 	_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), parent_pimpl(NULL) {
+	zoom(-1), rotate(-1), attr(GrpObj::HIDDEN) {
 	int i;
 	for (i=0; i<9; i++) {
 		posx[i] = posy[i] = 0;
@@ -271,10 +265,7 @@ GrpObj::GrpObj(void) :
 }
 
 GrpObj::~GrpObj() {
-	if (picture) delete picture;
-	if (parent_pimpl == NULL) {
-		fprintf(stderr,"\n**************\nFATAL : UNINITIALIZED GrpObj IS FOUND!!! \n**************\n");
-	}
+	if (picture != NULL) delete picture;
 }
 
 int GrpObj::PosX() {
@@ -311,7 +302,6 @@ void GrpObj::GetPos(int index, int& x, i
 	}
 	x = posx[index];
 	y = posy[index];
-	return;
 }
 
 void GrpObj::SetAlpha(int new_alpha) {
@@ -327,7 +317,6 @@ void GrpObj::SetSurfaceNum(int num) {
 		surface_num = num;
 	}
 	attr = Attribute(attr | UPDATE_SNUM);
-	return;
 }
 
 void GrpObj::SetClipArea(int x, int y, int w, int h) {
@@ -335,15 +324,14 @@ void GrpObj::SetClipArea(int x, int y, i
 	if (clip_area == new_clip) return;
 	clip_area = new_clip;
 	attr = Attribute(attr | UPDATE_CLIP);
-	return;
 }
 
 PicBase* GrpObj::DeletePic(void) {
 	PicBase* p = picture;
-	anm = 0;
-	picture = 0;
+	anm = NULL;
+	picture = NULL;
 	src_pos.clear();
-	attr = Attribute(attr & (HIDDEN | HIDDEN_GROUP));
+	attr = Attribute(attr & HIDDEN);
 	return p;
 }
 
@@ -386,7 +374,6 @@ void GrpObj::GetSrcGeom(int& width, int&
 	if (sn < 0 || sn > src_pos.size()) sn = 0;
 	width = src_pos[sn].width();
 	height = src_pos[sn].height();
-	return;
 }
 
 void GrpObj::Update(void) {
@@ -394,7 +381,7 @@ void GrpObj::Update(void) {
 		UpdateSurface();
 		attr = Attribute( (attr | UPDATE_ALL) & (~UPDATE_PICTURE));
 	}
-	if (picture == 0) return;
+	if (picture == NULL) return;
 	if (attr & UPDATE_POS) {
 		if ( (attr & SATURATE) || zoom != -1) {
 			int w=0, h=0;
@@ -709,6 +696,57 @@ void GrpObj::SetZoomRotate(int new_zoom,
 
 	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*
@@ -799,9 +837,7 @@ GrpImpl::GrpImpl(Event::Container& _even
 	parent(_parent),
 	status(NORMAL),
 	skip_mode(SKIP_NO),
-	cgm_data(_cgm_data),
-	grpobj(this),
-	bs_obj(this)
+	cgm_data(_cgm_data)
 {
 	int i;
 	for (i=0; i<MAXPDT; i++) {
@@ -834,6 +870,7 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 33, 100, "grpCopy", (CmdImpl) &GrpImpl::impl_grpCopy);
 	RegisterCommand(1, 33, 1201, "recFill", (CmdImpl) &GrpImpl::impl_recFill);
 	RegisterCommand(1, 33, 1100, "recCopy", (CmdImpl) &GrpImpl::impl_recCopy);
+	RegisterCommand(1, 33, 1101, "recMaskCopy", NULL); //FIXME
 	RegisterCommand(1, 33, 1600, "recAdd", (CmdImpl) &GrpImpl::impl_recAdd);
 	RegisterCommand(1, 33, 406, "grpPan", (CmdImpl) &GrpImpl::impl_grpPan);
 
@@ -861,6 +898,13 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 71, 1300, "createObjWeaver", (CmdImpl) &GrpImpl::impl_createObj);
 	RegisterCommand(1, 71, 1400, "createObjDigit", (CmdImpl) &GrpImpl::impl_createObj);
 
+	RegisterCommand(2, 71, 1000, "createSubObjG00", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(2, 71, 1003, "createSubObjGAN", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(2, 71, 1100, "createSubObjRect", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(2, 71, 1200, "createSubObjText", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(2, 71, 1300, "createSubObjWeaver", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(2, 71, 1400, "createSubObjDigit", (CmdImpl) &GrpImpl::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, "createBgObjG00", (CmdImpl) &GrpImpl::impl_createObj);
 	RegisterCommand(1, 72, 1003, "createBgObjGAN", (CmdImpl) &GrpImpl::impl_createObj);
@@ -869,6 +913,14 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 72, 1300, "createBgObjWeaver", (CmdImpl) &GrpImpl::impl_createObj);
 	RegisterCommand(1, 72, 1400, "createBgObjDigit", (CmdImpl) &GrpImpl::impl_createObj);
 
+	RegisterCommand(2, 72, 1000, "createBgSubObjG00", NULL);//FIXME
+	RegisterCommand(2, 72, 1003, "createBgSubObjGAN", NULL);//FIXME
+	RegisterCommand(2, 72, 1100, "createBgSubObjRect", NULL);//FIXME
+	RegisterCommand(2, 72, 1200, "createBgSubObjText", NULL);//FIXME
+	RegisterCommand(2, 72, 1300, "createBgSubObjWeaver", NULL);//FIXME
+	RegisterCommand(2, 72, 1400, "createBgSubObjDigit", NULL);//FIXME
+
+
 	RegisterCommand(1, 73, 0, "ganStop?", NULL); //That's what xclannad says, but I'm not sure...
 	RegisterCommand(1, 73, 1000, "ganStop", (CmdImpl) &GrpImpl::impl_gan); //That's what rldev says
 	RegisterCommand(1, 73, 3, "ganIsPlaying", (CmdImpl) &GrpImpl::impl_gan);
@@ -880,18 +932,28 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 73, 3003, "ganPlay2", (CmdImpl) &GrpImpl::impl_gan);
 	RegisterCommand(1, 73, 3005, "ganPlayOnce2", (CmdImpl) &GrpImpl::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) &GrpImpl::impl_gan); //FIXME
+	RegisterCommand(2, 73, 3003, "ganSubPlay2", NULL); //FIXME
+	RegisterCommand(2, 73, 3005, "ganSubPlayOnce2", NULL); //FIXME
+
+
 	RegisterCommand(1, 81, 1000, "objMove", (CmdImpl) &GrpImpl::impl_objSetPos);
 	RegisterCommand(1, 82, 1000, "objBgMove", (CmdImpl) &GrpImpl::impl_objSetPos);
 	RegisterCommand(1, 81, 1001, "objLeft", (CmdImpl) &GrpImpl::impl_objSetPos);
 	RegisterCommand(1, 82, 1001, "objBgLeft", (CmdImpl) &GrpImpl::impl_objSetPos);
 	RegisterCommand(1, 81, 1002, "objTop", (CmdImpl) &GrpImpl::impl_objSetPos);
 	RegisterCommand(1, 82, 1002, "objBgTop", (CmdImpl) &GrpImpl::impl_objSetPos);
-
 	RegisterCommand(1, 81, 1003, "objAlpha", (CmdImpl) &GrpImpl::impl_objAlpha);
 	RegisterCommand(1, 82, 1003, "objBgAlpha", (CmdImpl) &GrpImpl::impl_objAlpha);
 	RegisterCommand(1, 81, 1004, "objShow", (CmdImpl) &GrpImpl::impl_objShow);
 	RegisterCommand(1, 82, 1004, "objBgShow", (CmdImpl) &GrpImpl::impl_objShow);
-
 	RegisterCommand(1, 81, 1005, "objDispArea", NULL);
 	RegisterCommand(1, 82, 1005, "objBgDispArea", NULL);
 	RegisterCommand(1, 81, 1006, "objAdjust", (CmdImpl) &GrpImpl::impl_objSetPos);
@@ -912,7 +974,7 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 82, 1019, "objBgColB", NULL);
 	RegisterCommand(1, 81, 1020, "objColLevel", NULL);
 	RegisterCommand(1, 82, 1020, "objBgColLevel", NULL);
-	RegisterCommand(1, 81, 1021, "objComposite", (CmdImpl) &GrpImpl::impl_objComposite); //FIXME: May be broken
+	RegisterCommand(1, 81, 1021, "objComposite", NULL);//(CmdImpl) &GrpImpl::impl_objComposite); //FIXME: May be broken
 	RegisterCommand(1, 82, 1021, "objBgComposite", (CmdImpl) &GrpImpl::impl_objComposite);
 	RegisterCommand(1, 81, 1024, "objSetText", (CmdImpl) &GrpImpl::impl_objSetText);
 	RegisterCommand(1, 82, 1024, "objBgSetText", (CmdImpl) &GrpImpl::impl_objSetText);
@@ -935,9 +997,65 @@ GrpImpl::GrpImpl(Event::Container& _even
 	RegisterCommand(1, 81, 1049, "objRotate", (CmdImpl) &GrpImpl::impl_objRotate);
 	RegisterCommand(1, 82, 1049, "objBgRotate", (CmdImpl) &GrpImpl::impl_objRotate);
 
+	RegisterCommand(2, 81, 1000, "childObjMove", (CmdImpl) &GrpImpl::impl_objSetPos);
+	RegisterCommand(2, 82, 1000, "childObjBgMove", (CmdImpl) &GrpImpl::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) &GrpImpl::impl_objAlpha);
+	RegisterCommand(2, 82, 1003, "childObjBgAlpha", (CmdImpl) &GrpImpl::impl_objAlpha);
+	RegisterCommand(2, 81, 1004, "childObjShow", (CmdImpl) &GrpImpl::impl_objShow);
+	RegisterCommand(2, 82, 1004, "childObjBgShow", (CmdImpl) &GrpImpl::impl_objShow);
+	RegisterCommand(2, 81, 1005, "childObjDispArea", NULL);
+	RegisterCommand(2, 82, 1005, "childObjBgDispArea", NULL);
+	RegisterCommand(2, 81, 1006, "childObjAdjust", (CmdImpl) &GrpImpl::impl_objSetPos);
+	RegisterCommand(2, 82, 1006, "childObjBgAdjust", (CmdImpl) &GrpImpl::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) &GrpImpl::impl_objSetText);
+	RegisterCommand(2, 82, 1024, "childObjBgSetText", (CmdImpl) &GrpImpl::impl_objSetText);
+	RegisterCommand(2, 81, 1025, "childObjTextOpts", (CmdImpl) &GrpImpl::impl_objTextOpts);
+	RegisterCommand(2, 82, 1025, "childObjBgTextOpts", (CmdImpl) &GrpImpl::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) &GrpImpl::impl_objSetDigits);
+	RegisterCommand(2, 82, 1037, "childObjBgSetDigits", (CmdImpl) &GrpImpl::impl_objSetDigits);
+	RegisterCommand(2, 81, 1038, "childObjNumOpts", (CmdImpl) &GrpImpl::impl_objNumOpts);
+	RegisterCommand(2, 82, 1038, "childObjBgNumOpts", (CmdImpl) &GrpImpl::impl_objNumOpts);
+	RegisterCommand(2, 81, 1039, "childObjPattNo", (CmdImpl) &GrpImpl::impl_objPattNo);
+	RegisterCommand(2, 82, 1039, "childObjBgPattNo", (CmdImpl) &GrpImpl::impl_objPattNo);
+	RegisterCommand(2, 81, 1046, "childObjScale", (CmdImpl) &GrpImpl::impl_objScale);
+	RegisterCommand(2, 82, 1046, "childObjBgScale", (CmdImpl) &GrpImpl::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) &GrpImpl::impl_objPosDims);
 	RegisterCommand(1, 84, 1100, "objGetDims", (CmdImpl) &GrpImpl::impl_objPosDims);
 
+	RegisterCommand(2, 84, 1000, "childObjGetPos", (CmdImpl) &GrpImpl::impl_objPosDims);
+	RegisterCommand(2, 84, 1100, "childObjGetDims", (CmdImpl) &GrpImpl::impl_objPosDims);
+
 	RegisterCommand(1, 31, 0, "refresh", (CmdImpl) &GrpImpl::impl_refresh);
 
 	RegisterCommand(1, 20, 0, "bgmLoop", (CmdImpl) &GrpImpl::impl_bgmLoop);
@@ -978,6 +1096,10 @@ GrpImpl::GrpImpl(Event::Container& _even
 	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;
 }
@@ -1017,6 +1139,29 @@ Surface* GrpImpl::Dsurface(int pdt) {
 	return dsurface[pdt];
 }
 
+GrpObj* GrpImpl::GetGraphicObj(int grp, bool fg) {
+	if (fg)
+		return &grpobj[grp];
+	else
+		return &bs_obj[grp];
+}
+
+GrpObj* GrpImpl::GetGraphicObj(int grp, int index, bool fg) {
+	GrpObj* g = GetGraphicObj(grp, fg);
+	return &g->children_obj[index];
+}
+
+GrpObj* GrpImpl::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* GrpImpl::Ssurface(int pdt) {
 	if (pdt == 0) return surface;
@@ -1108,17 +1253,7 @@ void GrpImpl::RefreshObj(void) {
 		for (it=changed_obj.begin(); it != changed_obj.end(); it++) {
 			if (grpobj.find(*it) == grpobj.end()) continue;
 			GrpObj& obj = grpobj[*it];
-			if (obj.picture == NULL) continue;
-			if (obj.alpha == 0 || (obj.attr & GrpObj::HIDDEN)) {
-				if (obj.attr & GrpObj::ANM_PLAYING) {
-					obj.attr = GrpObj::Attribute(obj.attr & ~(GrpObj::ANM_PLAYING));
-					if (obj.anm) obj.anm->Abort();
-				}
-				obj.picture->hide();
-			} else {
-				obj.Update();
-				obj.picture->show();
-			}
+			obj.Refresh(obj);
 		}
 		changed_obj.clear();
 	}
@@ -1187,7 +1322,7 @@ void GrpImpl::StartAnm(int type) {
 	for (it=bs_obj.begin(); it!=bs_obj.end(); it++) {
 		grpobj[it->first] = it->second;
 		it->second.DeletePic();
-		CreateObj(it->first);
+		CreateObj(it->first);//FIXME: Adapt to groups
 		GrpObj& g = grpobj[it->first];
 		if (g.picture) {
 			g.Update();
@@ -1287,7 +1422,7 @@ void GrpImpl::AddSurface(const char* str
 }
 
 void GrpImpl::CreateObj(int index) {
-	std::map<int, GrpObj>::iterator cur = grpobj.find(index);
+	GrpObjMap::iterator cur = grpobj.find(index);
 	if (cur == grpobj.end()) return;
 	GrpObj& g = grpobj[index];
 	g.CreateSurface(&parent);
@@ -1298,15 +1433,31 @@ void GrpImpl::CreateObj(int index) {
 	ZMoveObj(index);
 }
 
+void GrpImpl::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 GrpImpl::ZMoveObj(int index) {
-	std::map<int, GrpObj>::iterator cur = grpobj.find(index);
+	GrpObjMap::iterator cur = grpobj.find(index);
 	if (cur == grpobj.end()) return;
 	GrpObj& g = grpobj[index];
 	if (g.picture == NULL) return;
 	// 自分より前に object があれば、その前に表示
 	// そうでなければ screen の前に表示
-	std::map<int, GrpObj>::iterator cur_backobj = grpobj.end();
-	std::map<int, GrpObj>::iterator it;
+	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;
@@ -1526,25 +1677,11 @@ extern bool grpdump_req;
 bool GrpImpl::Wait(unsigned int current_time, Cmd& cmd) {
 	if (grpdump_req) {
 		grpdump_req = 0;
-		std::map<int,GrpObj>::iterator it;
+		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;
-			if (obj.picture) {
-				if (!obj.name.empty()) {
-					fprintf(stderr,"obj %06d(%p): name %10s  pos %d,%d alpha %d (%d/%d/%d)\n",
-					it->first,obj.picture,obj.name.c_str(),
-					obj.PosX(),obj.PosY(),obj.alpha,obj.attr&GrpObj::HIDDEN ? 1 : 0, 0, obj.picture->IsHidden());
-				} else if (!obj.print_moji.empty()) {
-					fprintf(stderr,"obj %06d(%p): name %10s  pos %d,%d alpha %d (%d/%d/%d)\n",
-					it->first,obj.picture,obj.print_moji.c_str(),
-					obj.PosX(),obj.PosY(),obj.alpha,obj.attr&GrpObj::HIDDEN ? 1 : 0, 0, obj.picture->IsHidden());
-				} else {
-					fprintf(stderr,"obj %06d(%p): name %10s  pos %d,%d alpha %d (%d/%d/%d)\n",
-					it->first,obj.picture,"<EMPTY>",
-					obj.PosX(),obj.PosY(),obj.alpha,obj.attr&GrpObj::HIDDEN ? 1 : 0, 0, obj.picture->IsHidden());
-				}
-			}
+			obj._debug_Dump(it->first, 0);
 		}
 		std::list<PicBase*>::iterator it2;
 		for (it2=parent.children.begin(); it2!=parent.children.end();it2++) {
@@ -1619,12 +1756,29 @@ void GrpImpl::DeleteObjPic(int num) { // object の surface のみ削除
 	deleted_pic.push_back(grpobj[num].DeletePic());
 }
 
+void GrpImpl::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 GrpImpl::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 GrpImpl::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 GrpImpl::impl_stackClear (Cmd& cmd) {
 	cmd.cmd_type = CMD_SAVECMDGRP_START;
 }
@@ -1904,7 +2058,10 @@ void GrpImpl::impl_cgStatus(Cmd& cmd) {
 }
 
 void GrpImpl::impl_objClear(Cmd& cmd) { //FIXME: may be broken (doesn't reflect what Haeleth says)
-	DeleteObj(cmd.args[0].value);
+	if (cmd.cmd1 == 1)
+		DeleteObj(cmd.args[0].value);
+	if (cmd.cmd1 == 2)
+		DeleteSubObj(cmd.args[0].value, cmd.args[1].value);
 	cmd.clear();
 }
 
@@ -1919,70 +2076,83 @@ void GrpImpl::impl_createObj(Cmd& cmd) {
 			1400: number
 	*/
 	int base_argc = 0;
-	DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
-	if (cmd.cmd2 == 71)
+
+	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];
+	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
-		string name = cmd.Str(cmd.args[1]);
+		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) {
+			//Maybe it's for shading or something like that?
+			printf("Warning: the part after the '?' was removed: '%s'\n", name.c_str());
 			name.erase(name.find('?')); // '?' 以降の意味がわからない
 		}
-		g.name = name;
+		g->name = name;
 	} else if (cmd.cmd3 == 1003) { /* ファイル名設定(GAN含む) */
-		g.gtype = GrpObj::GAN;
-		if (cmd.Str(cmd.args[1]) == string("???"))
-			g.name = cmd.Str(cmd.args[2]);
+		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[1]);
-		g.gan_name = cmd.Str(cmd.args[2]);
-			if (cmd.cmd4 >= 1 && cmd.args[3].value == 0)
-			g.attr =  GrpObj::Attribute(g.attr | GrpObj::HIDDEN);
-		else
-			g.attr =  GrpObj::Attribute(g.attr & ~(GrpObj::HIDDEN));
+			g->name = cmd.Str(cmd.args[base_argc + 1]);
+		g->gan_name = cmd.Str(cmd.args[base_argc + 2]);
 
-		if (cmd.argc >= 5)
-			g.SetPos(1, cmd.args[4].value, -cmd.args[5].value);
+		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 (g.name.find('?') != -1) {
-			g.name.erase(g.name.find('?'));
-			g.gan_name = cmd.Str(cmd.args[2]);
+		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[1]);
-		g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN)); // 常に表示がデフォルト?
+		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[1]);
+		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) {
-				if (cmd.cmd1 == 1)
-					g.attr = GrpObj::Attribute(g.attr | GrpObj::HIDDEN | GrpObj::HIDDEN_GROUP);
-				else
-					g.attr = GrpObj::Attribute(g.attr | GrpObj::HIDDEN);
+				g->attr = GrpObj::Attribute(g->attr | GrpObj::HIDDEN);
 			} else {
-				if (cmd.cmd1 == 1)
-					g.attr = GrpObj::Attribute(g.attr & (~(GrpObj::HIDDEN | GrpObj::HIDDEN_GROUP)));
-				else
-					g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
+				g->attr = GrpObj::Attribute(g->attr & (~GrpObj::HIDDEN));
 			}
-			if (cmd.cmd1 == 1)
-				SetObjChanged(cmd.args[0].value);
+			SetObjChanged(cmd.args[0].value);
 		}
 		if (cmd.cmd4 >= 2) { // 座標等も設定
-			g.SetPos(0, cmd.args[3+base_argc].value, cmd.args[4+base_argc].value);
+			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);
+			g->SetSurfaceNum(cmd.args[5+base_argc].value);
 			base_argc++; // 1000 (FILE) / 1003 (GAN) の場合のみこのオプションは存在する
 		}
 		cmd.clear();
@@ -1992,33 +2162,34 @@ void GrpImpl::impl_createObj(Cmd& cmd) {
 }
 
 void GrpImpl::impl_gan(Cmd& cmd) {
-	GrpObj& g = grpobj[cmd.args[0].value];
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg);
 
 	if (cmd.cmd3 == 3) { // ganIsPlaying
-		if (g.anm == NULL || g.anm->IsEnd())
+		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);
+		if (g->anm == NULL || g->anm->IsEnd())
+			g->SetSurfaceNum(cmd.args[1].value);
 		else {
-			g.anm->Abort();
-			g.SetSurfaceNum(cmd.args[1].value);
+			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->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->CreateGan(event, cmd.args[1].value);
 		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
 		SetObjChanged(cmd.args[0].value);
 		cmd.clear();
@@ -2027,53 +2198,52 @@ void GrpImpl::impl_gan(Cmd& cmd) {
 
 void GrpImpl::impl_objSetPos(Cmd& cmd) {
 	//obj or objBg
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+	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].value + 1;
-		x = cmd.args[2].value;
-		y = cmd.args[3].value;
+		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].value;
-			y = cmd.args[2].value;
+			x = cmd.args[1+base_arg].value;
+			y = cmd.args[2+base_arg].value;
 		}
 		else {
-			g.GetPos(index, x, y);
+			g->GetPos(index, x, y);
 			if (cmd.cmd3 == 1001)
-				x = cmd.args[1].value;
+				x = cmd.args[1+base_arg].value;
 			else
-				y = cmd.args[1].value;
+				y = cmd.args[1+base_arg].value;
 		}
 	}
 
-	g.SetPos(index, x, y);
+	g->SetPos(index, x, y);
 	cmd.clear();
 }
 
 void GrpImpl::impl_objAlpha(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	g.SetAlpha(cmd.args[1].value);
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->SetAlpha(cmd.args[base_arg + 1].value);
 	cmd.clear();
 }
 
 void GrpImpl::impl_objShow(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	if (cmd.cmd1 == 1) {
-		if (cmd.args[1].value)
-			g.attr = GrpObj::Attribute(g.attr & (~(GrpObj::HIDDEN | GrpObj::HIDDEN_GROUP)));
-		else
-			g.attr = GrpObj::Attribute(g.attr | GrpObj::HIDDEN | GrpObj::HIDDEN_GROUP);
-	} else {
-		if (cmd.args[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);
+	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);
@@ -2081,73 +2251,80 @@ void GrpImpl::impl_objShow(Cmd& cmd) {
 }
 
 void GrpImpl::impl_objColour(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	g.print_r = cmd.args[1].value;
-	g.print_g = cmd.args[2].value;
-	g.print_b = cmd.args[3].value;
-	g.SetUpdate();
-	// grpobj[cmd.args[0].value].print_a = cmd.args[4].value;
-	/* args:229,18,minus-1,0,99,255,-1 */
-	/* args:102,26,minus-1,0,99,0,255 */
+	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 GrpImpl::impl_objComposite(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	// centering mode などを設定?
-	if (cmd.args[1].value == 1) {
-		g.attr = GrpObj::Attribute(g.attr | GrpObj::SATURATE);
+void GrpImpl::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[1].value == 0) {
-		g.attr = GrpObj::Attribute(g.attr & (~GrpObj::SATURATE));
+	} else if (cmd.args[base_arg + 1].value == 0) {
+		g->attr = GrpObj::Attribute(g->attr & (~GrpObj::SATURATE));
 		cmd.clear();
 	}
-	g.SetUpdate();
+	g->SetUpdate();
 }
 
 void GrpImpl::impl_objSetText(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
 
-	g.print_moji = cmd.Str(cmd.args[1]);
-	g.SetUpdate();
+	g->print_moji = cmd.Str(cmd.args[base_arg + 1]);
+	g->SetUpdate();
 	cmd.clear();
 }
 
 void GrpImpl::impl_objTextOpts(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
 	// 画像を文字列として設定:文字の大きさなど
-	g.print_size = cmd.args[1].value;
+	g->print_size = cmd.args[base_arg + 1].value;
 	/* 前景色を得る */
 	int cr, cg, cb;
 	char key[17];
-	sprintf(key, "#COLOR_TABLE.%03d", cmd.args[5].value);
+	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();
+	g->print_r = cr;
+	g->print_g = cg;
+	g->print_b = cb;
+	g->SetUpdate();
 	cmd.clear();
 }
 
 void GrpImpl::impl_objOrder(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	int order = cmd.args[1].value;
-	g.order = order;
+	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 GrpImpl::impl_objDispArea(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
 	// オブジェクトのどの部分を画面に表示するか(クリップ領域)の設定
 	int rx, ry, w, h;
-	if (cmd.args.size() == 5) {
-		int rx = cmd.args[1].value;
-		int ry = cmd.args[2].value;
-		int w = cmd.args[3].value;
-		int h = cmd.args[4].value;
+	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;
@@ -2158,69 +2335,80 @@ void GrpImpl::impl_objDispArea(Cmd& cmd)
 		w = screen->Width();
 		h = screen->Height();
 	}
-	g.SetClipArea(rx, ry, w, h); //TODO: case when cmd.args.size() == 1
+	g->SetClipArea(rx, ry, w, h); //TODO: case when cmd.args.size() == 1
 	cmd.clear();
 }
 
 void GrpImpl::impl_objSetDigits(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	g.dig_number = cmd.args[1].value;
-	g.SetUpdate();
+	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 GrpImpl::impl_objNumOpts(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	g.dig_digit = cmd.args[1].value;
-	int attr = g.attr;
+	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[2].value) attr |= GrpObj::DIG_ZERO;
-	if (cmd.args[3].value) attr |= GrpObj::DIG_SIGN;
-	if (cmd.args[4].value) attr |= GrpObj::DIG_PACK;
-	g.attr = GrpObj::Attribute(attr);
-	g.SetUpdate();
+	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 GrpImpl::impl_objPattNo(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	g.SetSurfaceNum(cmd.args[1].value);
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, (cmd.cmd2 == 0x51));
+
+	g->SetSurfaceNum(cmd.args[base_arg + 1].value);
 	cmd.clear();
 }
 
 void GrpImpl::impl_objScale(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	int zoom = (cmd.args[1].value + cmd.args[2].value)/2; //FIXME: eurk
+	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);
+	g->SetZoomRotate(zoom, -1);
 	cmd.clear();
 }
 
 void GrpImpl::impl_objRotate(Cmd& cmd) {
-	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
-	int angle = cmd.args[1].value;
+	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);
+	g->SetZoomRotate(-1, angle);
 	cmd.clear();
 }
 
 void GrpImpl::impl_objPosDims(Cmd& cmd) {
-	GrpObj& obj = grpobj[cmd.args[0].value];
+	int base_arg = 0;
+	GrpObj* g = GetGraphicObjVarMode(cmd, base_arg, true);
 
-	VarInfo arg1 = cmd.args[1];
-	VarInfo arg2 = cmd.args[2];
+	VarInfo arg1 = cmd.args[base_arg + 1];
+	VarInfo arg2 = cmd.args[base_arg + 2];
 
 	int val1, val2;
 
 	if (cmd.cmd3 == 1000)
-		obj.GetPos(0, val1, val2);
+		g->GetPos(0, val1, val2);
 	else if (cmd.cmd3 == 1100)
-		obj.GetSrcGeom(val1, val2);
+		g->GetSrcGeom(val1, val2);
 
 	cmd.SetFlagvar(arg1, val1);
 	cmd.SetFlagvar(arg2, val2);
@@ -2350,10 +2538,20 @@ void GrpImpl::Exec(Cmd& cmd) {
 
 	//TODO: ???
 	if ( (cmd.cmd1 == 1 || cmd.cmd1 == 2) && cmd.cmd2 == 0x51) {
-		GrpObj& g = grpobj[cmd.args[0].value];
-		if (g.attr & GrpObj::UPDATE_ALL) {
+		/*GrpObj& g = grpobj[cmd.args[0].value];
+		int attr;
+		GrpObjMap::iterator it;
+		for (it = g.children_obj.begin(); it != g.children_obj.end(); it++)
+			attr |= it->second.attr;
+		if (attr & GrpObj::UPDATE_ALL)
+			SetObjChanged(cmd.args[0].value);*/
+		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);
-		}
 	}
 }
 
--- a/scn2k/scn2k_text.cc
+++ b/scn2k/scn2k_text.cc
@@ -163,7 +163,39 @@ struct TextWindow {
 	}
 };
 
-class TextImpl {
+class TextImpl : public CommandHandler {
+	public:
+		TextImpl(Event::Container& _event, PicContainer& _parent, vector<BacklogItem>& parent_backlog, BacklogItem& parent_backlog_item);
+		~TextImpl();
+		void InitWindow(void);
+		void SetWindowColor(int r, int g, int b, int a, bool is_transparent);
+		void SetTextSpeed(int new_speed);
+		void SetTextWait(int new_wait);
+		void CreateSelect(Cmd& cmd);
+		void Exec(Cmd& cmd);
+		bool Wait(unsigned int current_time, Cmd& cmd);
+		void hide(void);
+		void show(void) { show(text_window_number); }
+		void show(int num);
+		void DrawBacklog(BacklogItem& item, Cmd& cmd);
+		void Save(std::string& str, bool select_save);
+		void Load(const char* str);
+		void SetSkipMode(SkipMode _mode);
+		void CreateSelBG(void);
+
+		void AddText(const char* str);
+
+		static void PressFuncSkip(void* pointer, WidButton* from);
+		static void PressFuncLoad(void* pointer, WidButton* from);
+		static void PressFuncSave(void* pointer, WidButton* from);
+		static void PressFuncBacklog(void* pointer, WidButton* from);
+		static void PressFuncBacklogFwd(void* pointer, WidButton* from);
+
+	private:
+		static void PressFuncButton(void* pointer, WidButton* from);
+		static bool PressFunc(int x, int y, void* pointer);
+		void SetCursor(int num);
+
 	public:
 		TextWindow* text;
 		typedef enum {NORMAL=0, WAIT_TEXT=1, WAIT=2,
@@ -174,9 +206,13 @@ class TextImpl {
 			CLEARSCR_MASK = 1024, STATSAVE_MASK = 2048, CLEARSCR_WAIT_MASK=(1<<12),
 			SKIPEND_MASK = (1<<13), BACKLOG_MASK=(1<<14), BACKLOG_MASK_FWD=(1<<15),
 			BACKLOG_MASK_KOE=(1<<16), BACKLOG_WAIT_MASK=(1<<17),
-			ALLMASK = (CLEARSCR_MASK | WAIT_EXTRN_MASK | SAVEMASK | LOADMASK | SKIPMASK | BACKLOG_MASK | BACKLOG_MASK_FWD | BACKLOG_MASK_KOE | BACKLOG_WAIT_MASK | STATSAVE_MASK | CLEARSCR_WAIT_MASK | SKIPEND_MASK)
+			ALLMASK = (CLEARSCR_MASK | WAIT_EXTRN_MASK | SAVEMASK |
+						LOADMASK | SKIPMASK | BACKLOG_MASK | BACKLOG_MASK_FWD |
+						BACKLOG_MASK_KOE | BACKLOG_WAIT_MASK | STATSAVE_MASK |
+						CLEARSCR_WAIT_MASK | SKIPEND_MASK)
 		} Status;
 		Status status, status_saved, status_mask;
+
 	private:
 		std::string ruby_text;
 		bool ruby_text_flag;
@@ -202,50 +238,26 @@ class TextImpl {
 		BacklogItem cur_backlog_item;
 		BacklogItem drawn_backlog_item;
 
-	public:
-		PicContainer& parent;
-		Event::Container& event;
-		AyuSysConfig *config;
-	private:
 		TextWindow* widgets[32];
 		WidTimeCursor* kcursor;
 		Surface* sel_bg1;
 		Surface* sel_bg2;
 		Rect sel_bg_rect;
 
-		void SetCursor(int num);
 		VarInfo wait_savedvar[2];
 
-	public:
-		void AddText(const char* str);
+		AyuSysConfig *config;
 
-		static void PressFuncSkip(void* pointer, WidButton* from);
-		static void PressFuncLoad(void* pointer, WidButton* from);
-		static void PressFuncSave(void* pointer, WidButton* from);
-		static void PressFuncBacklog(void* pointer, WidButton* from);
-		static void PressFuncBacklogFwd(void* pointer, WidButton* from);
-	private:
-		static void PressFuncButton(void* pointer, WidButton* from);
-		static bool PressFunc(int x, int y, void* pointer);
+		Event::Container& event;
+		PicContainer& parent;
 
-	public:
-		TextImpl(Event::Container& _event, PicContainer& _parent, vector<BacklogItem>& parent_backlog, BacklogItem& parent_backlog_item);
-		~TextImpl();
-		void InitWindow(void);
-		void SetWindowColor(int r, int g, int b, int a, bool is_transparent);
-		void SetTextSpeed(int new_speed);
-		void SetTextWait(int new_wait);
-		void CreateSelect(Cmd& cmd);
-		void Exec(Cmd& cmd);
-		bool Wait(unsigned int current_time, Cmd& cmd);
-		void hide(void);
-		void show(void) { show(text_window_number); }
-		void show(int num);
-		void DrawBacklog(BacklogItem& item, Cmd& cmd);
-		void Save(std::string& str, bool select_save);
-		void Load(const char* str);
-		void SetSkipMode(SkipMode _mode);
-		void CreateSelBG(void);
+		//Opcode handling
+		void impl_txtClear(Cmd& cmd);
+		void impl_logKoe(Cmd& cmd);
+		void impl_pause(Cmd& cmd);
+		void impl_br(Cmd& cmd);
+		void impl_FaceOpen(Cmd& cmd);
+		void impl_FaceClear(Cmd& cmd);
 };
 
 /**************************************************************::
@@ -264,6 +276,20 @@ TextImpl::TextImpl(Event::Container& _ev
 	}
 	text_stream.kanji_type = TextStream::sjis;
 	event.RegisterGlobalPressFunc(&PressFunc, (void*)this);
+
+	RegisterCommand(1, 33, 73, "grpOpenBg", (CmdImpl) &TextImpl::impl_txtClear);
+	RegisterCommand(1, 33, 75, "grpMulti", (CmdImpl) &TextImpl::impl_txtClear);
+	RegisterCommand(1, 33, 76, "grpOpen", (CmdImpl) &TextImpl::impl_txtClear);
+
+	RegisterCommand(1, 23, 0, "koePlay", (CmdImpl) &TextImpl::impl_logKoe);
+	RegisterCommand(1, 23, 8, "koeDoPlay", (CmdImpl) &TextImpl::impl_logKoe);
+
+	RegisterCommand(0, 3, 151, "msgHide", (CmdImpl) &TextImpl::impl_txtClear);
+	RegisterCommand(0, 3, 17, "pause", (CmdImpl) &TextImpl::impl_pause);
+	RegisterCommand(0, 3, 3, "par", (CmdImpl) &TextImpl::impl_br); //FIXME
+	RegisterCommand(0, 3, 201, "br", (CmdImpl) &TextImpl::impl_br);
+	RegisterCommand(0, 3, 1000, "FaceOpen", (CmdImpl) &TextImpl::impl_FaceOpen);
+	RegisterCommand(0, 3, 1001, "FaceClear", (CmdImpl) &TextImpl::impl_FaceClear);
 }
 
 TextImpl::~TextImpl() {
@@ -864,6 +890,66 @@ void TextImpl::AddText(const char* str_o
 	text_stream.Add(str_top);
 }
 
+void TextImpl::impl_txtClear(Cmd& cmd) {
+	if (text != NULL) {
+		text->ResetFace();
+		if (cmd.cmd2 == 3 && cmd.cmd3 == 151)
+			text->wid->Clear();
+	}
+	cur_backlog_item.face = "";
+	if (cmd.cmd2 == 3 && cmd.cmd3 == 151)
+		text_stream.Clear();
+	hide();
+}
+
+void TextImpl::impl_logKoe(Cmd& cmd) {
+	// PlayKoe ; 声出力コマンドをチェックする */
+	cur_backlog_item.koe = cmd.args[0].value;
+}
+
+void TextImpl::impl_pause(Cmd& cmd) {
+	if (text != NULL) {
+		eprintf("start\n");
+		text->StartText(text_stream);
+		if (skip_mode & SKIP_TEXT) text->wid->Flush();
+		else if (kcursor) kcursor->show();
+		status = WAIT_TEXT;
+		text_parsing = false;
+	}
+	backlog_item = cur_backlog_item;
+	if (cur_backlog_item.scn == 0 && cur_backlog_item.pos == -1) backlog_item.text = text_stream;
+	cur_backlog_item.Clear();
+
+	cmd.clear();
+	cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
+}
+
+void TextImpl::impl_br(Cmd& cmd) {
+	text_stream.AddReturn();
+	cur_backlog_item.DeleteTextPos();
+	cmd.clear();
+}
+
+void TextImpl::impl_FaceOpen(Cmd& cmd) {
+	if (text == NULL)
+		show();
+	string s = cmd.Str(cmd.args[0]);
+	s += ".g00";
+	if (text != NULL)
+		text->ShowFace(s.c_str());
+	cur_backlog_item.face = s;
+	cmd.cmd_type = CMD_SAVECMD_ONCE;
+}
+
+void TextImpl::impl_FaceClear(Cmd& cmd) {
+	if (text == NULL)
+		show();
+	if (text)
+		text->ResetFace();
+	cur_backlog_item.face = "";
+	cmd.cmd_type = CMD_SAVECMD_ONCE;
+}
+
 void TextImpl::Exec(Cmd& cmd) {
 	if (cmd.cmd_type == CMD_TEXT) {
 		if (text == NULL) {
@@ -888,66 +974,13 @@ void TextImpl::Exec(Cmd& cmd) {
 		text_parsing = true; /* テキスト待ち直後のテキスト位置=セーブ位置 */
 		return;
 	}
+
 	if (cmd.cmd_type != CMD_OTHER) return;
-	/* テキストウィンドウを消去するコマンド類をチェックする */
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x21) {
-		if (cmd.cmd3 == 0x49 || cmd.cmd3 == 0x4b || cmd.cmd3 == 0x4c) {
-			if (text) text->ResetFace();
-			cur_backlog_item.face = "";
-			hide();
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x17 && cmd.cmd3 == 0 && cmd.cmd4 == 1) { 
-		// PlayKoe ; 声出力コマンドをチェックする */
-		cur_backlog_item.koe = cmd.args[0].value;
-	}
-	if (cmd.cmd1 == 0 && cmd.cmd2 == 3 && cmd.cmd3 == 0x97) { // いいのかなー
-		
-		if (text) {
-			text->ResetFace();
-			text->wid->Clear();
-		}
-		cur_backlog_item.face = "";
-		text_stream.Clear();
-		hide();
-	}
+
+	CommandHandler::Exec(cmd);
+
 	if (cmd.cmd1 == 0 && cmd.cmd2 == 3) {
-		if (cmd.cmd3 == 0x11) { // テキスト表示、クリック待ち
-			if (text) {
-				eprintf("start\n");
-				text->StartText(text_stream);
-				if (skip_mode & SKIP_TEXT) text->wid->Flush();
-				else if (kcursor) kcursor->show();
-				status = WAIT_TEXT;
-				text_parsing = false;
-			}
-			backlog_item = cur_backlog_item;
-			if (cur_backlog_item.scn == 0 && cur_backlog_item.pos == -1) backlog_item.text = text_stream;
-			cur_backlog_item.Clear();
-
-			cmd.clear();
-			cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
-				// これだと1フレーム1テキストしか表示されなくなるので注意
-		} else if (cmd.cmd3 == 3 || cmd.cmd3 == 0xc9) { // リターン挿入
-			text_stream.AddReturn();
-			cur_backlog_item.DeleteTextPos();
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x3e8 || cmd.cmd3 == 0x3e9) { // 顔グラフィック変更
-			if (text == NULL) {
-				show();
-			}
-			if (cmd.cmd3 == 0x3e8) {
-				string s = cmd.Str(cmd.args[0]);
-				s += ".g00";
-				if (text) text->ShowFace(s.c_str());
-				cur_backlog_item.face = s;
-				cmd.cmd_type = CMD_SAVECMD_ONCE;
-			} else if (cmd.cmd3 == 0x3e9) { // 顔グラフィック消去
-				if (text) text->ResetFace();
-				cur_backlog_item.face = "";
-				cmd.cmd_type = CMD_SAVECMD_ONCE;
-			}
-		} else if (cmd.cmd3 == 0x78) { // ルビ関連
+		if (cmd.cmd3 == 0x78) { // ルビ関連
 			if (text == NULL) {
 				show();
 			}