changeset 54:d7cde171a1de

* scn2k_grp.cc now handles commands in a cleanier way \o/ * some cleaning
author thib
date Mon, 20 Apr 2009 16:18:55 +0000
parents ddbcbd000206
children f1a27ee7e03c
files scn2k/Makefile.am scn2k/command_handler.cc scn2k/scn2k.h scn2k/scn2k_cmd.cc scn2k/scn2k_grp.cc system/system_config.cc window/system.cc
diffstat 7 files changed, 1119 insertions(+), 858 deletions(-) [+]
line wrap: on
line diff
--- a/scn2k/Makefile.am
+++ b/scn2k/Makefile.am
@@ -1,5 +1,6 @@
 noinst_LIBRARIES = libscn2k.a
 libscn2k_a_SOURCES = scn2k_cmd.cc scn2k_text.cc scn2k_grp.cc scn2k_impl.cc \
+                     command_handler.cc \
                      scn2k.h scn2k_impl.h
 
 EXTRA_PROGRAMS = scn2kdump
new file mode 100644
--- /dev/null
+++ b/scn2k/command_handler.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009 Thibaut GIRKA <thib@sitedehib.com>
+ * 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 <map>
+#include <stdlib.h>
+
+#include "scn2k.h"
+
+#define CMDMAP_DEBUG 1
+
+void CommandHandler::RegisterCommand(int cmd1, int cmd2, int cmd3, const char* descr, CmdImpl func)
+{
+	if (descr == NULL && func == NULL)
+		return;
+	SimpleCmd cmd(cmd1, cmd2, cmd3);
+	CommandInfo info;
+	info.function = func;
+	info.descr = descr;
+
+#ifdef CMDMAP_DEBUG
+	CommandMap::iterator it;
+	it = command_map.find(cmd);
+	if (it != command_map.end()) {
+		printf("ERROR: Command '%s' already registered as '%s'!\n", descr, it->second.descr);
+		abort();
+	}
+#endif
+
+	command_map[cmd] = info;
+}
+
+bool CommandHandler::Exec(Cmd &cmd) {
+	CommandMap::iterator it;
+
+	it = command_map.find(cmd);
+	if (it == command_map.end()) {
+		//printf("Unrecognized opcode %d %d %d\n", cmd.cmd1, cmd.cmd2, cmd.cmd3); //Debug
+		return false;
+	}
+	else {
+		CmdImpl func = it->second.function;
+		if (func == NULL) printf("Function %s not implemented yet\n", it->second.descr);
+		else (this->*func)(cmd);
+		return true;
+	}
+}
+
--- a/scn2k/scn2k.h
+++ b/scn2k/scn2k.h
@@ -82,6 +82,8 @@ struct VarInfo {
 	VarInfo(int n) { type = TYPE_VAL; value = n;}
 	VarInfo(const VarInfo& i) { type = i.type; number = i.number; value = i.value;}
 };
+
+
 class Flags {
 /* flag:
 **  type 0-5 : ローカル整数、各2000個
@@ -147,10 +149,22 @@ struct CmdSimplified { // Cmd 保存用
 	void copy(const CmdSimplified& from, char*& args_buffer);
 };
 
-class Cmd {
+class SimpleCmd {
+	public:
+		SimpleCmd(int a, int b, int c);
+		SimpleCmd();
+
+		bool operator==(const SimpleCmd& cmd) const;
+		bool operator<(const SimpleCmd& cmd) const;
+
+	public:
+		int cmd1, cmd2, cmd3;
+};
+
+class Cmd : public SimpleCmd{
 	public:
 		Cmdtype cmd_type;
-		int cmd1, cmd2, cmd3, cmd4;
+		int cmd4;
 		int argc;
 		int pos, scn;
 		const char* rawdata;
@@ -179,6 +193,7 @@ class Cmd {
 	public:
 		const char* Str(const VarInfo& info) const;
 		int AddStr(char* s);
+
 	private:
 		char strheap[STRHEAP_SIZE];
 		int strend;
@@ -186,6 +201,7 @@ class Cmd {
 		void ResetString(void) {
 			strend = 0;
 		}
+
 	public:
 		void GetCmd(Flags& f, const char*& d);
 		void SetSysvar(int n, int v);
@@ -200,6 +216,21 @@ class Cmd {
 		void write(CmdSimplified& cmd, char*& args_buffer) const;
 };
 
+class CommandHandler {
+	public:
+		typedef void (CommandHandler::*CmdImpl)(Cmd& cmd);
+		typedef struct {
+			const char* descr;
+			CmdImpl function;
+		} CommandInfo;
+		void RegisterCommand(int cmd1, int cmd2, int cmd3, const char* descr, CmdImpl func);
+		bool Exec(Cmd& cmd);
+
+	private:
+		typedef std::map<SimpleCmd, CommandInfo> CommandMap;
+		CommandMap command_map;
+};
+
 enum SkipMode {
 	SKIP_NO=0, SKIP_TEXT=1, SKIP_GRP_FAST=16, SKIP_GRP_NOEFFEC=32,
 	SKIP_GRP_NODRAW=64, SKIPEND_TEXT=256, SKIPEND_KEY=512, SKIP_IN_MENU=1024
--- a/scn2k/scn2k_cmd.cc
+++ b/scn2k/scn2k_cmd.cc
@@ -616,6 +616,40 @@ bool Flags::Exec(Cmd& cmd) {
 }
 
 /*********************************************************************
+**   SimpleCmd
+*/
+
+SimpleCmd::SimpleCmd(int a, int b, int c)
+{
+	cmd1 = a;
+	cmd2 = b;
+	cmd3 = c;
+}
+
+SimpleCmd::SimpleCmd(void)
+{
+	cmd1 = cmd2 = cmd3 = 0;
+}
+
+bool SimpleCmd::operator<(const SimpleCmd& cmd) const
+{
+	if (cmd1 < cmd.cmd1) return true;
+	else if (cmd1 > cmd.cmd1) return false;
+
+	if (cmd2 < cmd.cmd2) return true;
+	else if (cmd2 > cmd.cmd2) return false;
+
+	if (cmd3 < cmd.cmd3) return true;
+	else return false;
+}
+
+bool SimpleCmd::operator==(const SimpleCmd& cmd) const
+{
+	return (cmd1 == cmd.cmd1 && cmd2 == cmd.cmd2 && cmd3 == cmd.cmd3);
+}
+
+
+/*********************************************************************
 **   Cmd
 */
 
--- a/scn2k/scn2k_grp.cc
+++ b/scn2k/scn2k_grp.cc
@@ -134,10 +134,95 @@ struct GrpObjMap : std::map<int, GrpObj>
 	}
 };
 
-class GrpImpl {
+class GrpImpl : public CommandHandler {
 #define MAXPDT 256
 #define WORKPDT 255
 	private:
+		void CreateObj(int number);
+		void ZMoveObj(int number);
+		void SetObjChanged(int number);
+		void SwapObj(int a1, int a2);
+		void DeleteObjPic(int num);// object の surface のみ削除
+		void DeleteObj(int num);
+		void RefreshObj(void);
+
+		Surface* Dsurface(int pdt);
+		Surface* Ssurface(int pdt);
+
+		// cgmode 用画像処理関連
+		void LoadCgm(void);
+
+	public:
+		GrpImpl(Event::Container& _event, PicContainer& _parent, const Flags& _flag, set<int>& _cgm_data);
+		~GrpImpl();
+		bool Wait(unsigned int current_time, Cmd& cmd);
+		void Exec(Cmd& cmd);
+		void InitSel(void);
+		void Save(std::string& str);
+		void Load(const char* str);
+		void SaveSys(std::string& str);
+		void LoadSys(const char* str);
+		void SetSkipMode(SkipMode _mode);
+		void LoadSurface(const char* str, int pdt);
+
+	private:
+		void LoadSurface(const char* str);
+		void LoadSurface(void);
+		void AddSurface(const char* str);
+
+		void StartAnm(int type);
+		void StartShake(int total, const int* pattern);
+		void AbortAnm(void);
+		static bool Pressed(int x, int y, void* pointer);
+
+		// Opcode handling
+		void impl_stackClear(Cmd& cmd);
+		void impl_grpBuffer(Cmd& cmd);
+		void impl_grpMulti(Cmd &cmd);
+		void impl_grpOpen(Cmd &cmd);
+		void impl_shake(Cmd &cmd);
+		void impl_grpCopy(Cmd &cmd);
+		void impl_recFill(Cmd &cmd);
+		void impl_recCopy(Cmd &cmd);
+		void impl_recAdd(Cmd &cmd);
+		void impl_grpPan(Cmd &cmd);
+		void impl_snmPlay(Cmd &cmd);
+		void impl_snmBgScroll(Cmd &cmd);
+		void impl_cgGet(Cmd &cmd);
+		void impl_cgStatus(Cmd &cmd);
+		void impl_objClear(Cmd &cmd);
+		void impl_createObj(Cmd &cmd);
+		void impl_gan(Cmd &cmd);
+		void impl_objSetPos(Cmd &cmd);
+		void impl_objAlpha(Cmd &cmd);
+		void impl_objShow(Cmd &cmd);
+		void impl_objColour(Cmd &cmd);
+		void impl_objComposite(Cmd &cmd);
+		void impl_objSetText(Cmd &cmd);
+		void impl_objTextOpts(Cmd &cmd);
+		void impl_objOrder(Cmd &cmd);
+		void impl_objDispArea(Cmd &cmd);
+		void impl_objSetDigits(Cmd &cmd);
+		void impl_objNumOpts(Cmd &cmd);
+		void impl_objPattNo(Cmd &cmd);
+		void impl_objScale(Cmd &cmd);
+		void impl_objRotate(Cmd &cmd);
+		void impl_objPosDims(Cmd &cmd);
+		void impl_refresh(Cmd &cmd);
+		void impl_bgmLoop(Cmd &cmd);
+		void impl_bgmStop(Cmd &cmd);
+		void impl_playWav(Cmd &cmd);
+		void impl_playSE(Cmd &cmd);
+		void impl_stopWav(Cmd &cmd);
+		void impl_SetVolMod(Cmd &cmd);
+		void impl_GetVolMod(Cmd &cmd);
+		void impl_koePlay(Cmd &cmd);
+		void impl_movPlay(Cmd &cmd);
+
+	public:
+		AyuSysConfig *config;
+
+	private:
 		Event::Container& event;
 		const Flags& flags;
 		PicBase* screen;
@@ -158,56 +243,16 @@ class GrpImpl {
 		std::map<int, SEL> anmtype;
 		GrpObjMap grpobj;
 		GrpObjMap bs_obj;
-		void CreateObj(int number);
-		void ZMoveObj(int number);
-		void SetObjChanged(int number);
-		void SetObjChangedGroup(int number);
-		void SwapObj(int a1, int a2);
-		void DeleteObjPic(int num);// object の surface のみ削除
-		void DeleteObj(int num);
-		void DeleteObjRange(int num_b, int num_e);
+
+		std::map<std::string, int> cgm_info;
+		set<int>& cgm_data;
+		int cgm_size;
+
+		class MuSys *music;
 
 		std::set<int> changed_obj;
 		string reserved_load_surface0;
 		vector<PicBase*> deleted_pic;
-		void RefreshObj(void);
-
-		Surface* Dsurface(int pdt);
-		Surface* Ssurface(int pdt);
-
-		// cgmode 用画像処理関連
-		void LoadCgm(void);
-		std::map<std::string, int> cgm_info;
-		set<int>& cgm_data;
-		int cgm_size;
-		
-		class MuSys *music;
-
-	public:
-		AyuSysConfig *config;
-		void LoadSurface(const char* str, int pdt);
-
-	private:
-		void LoadSurface(const char* str);
-		void LoadSurface(void);
-		void AddSurface(const char* str);
-
-		void StartAnm(int type);
-		void StartShake(int total, const int* pattern);
-		void AbortAnm(void);
-		static bool Pressed(int x, int y, void* pointer);
-
-	public:
-		GrpImpl(Event::Container& _event, PicContainer& _parent, const Flags& _flag, set<int>& _cgm_data);
-		~GrpImpl();
-		bool Wait(unsigned int current_time, Cmd& cmd);
-		void Exec(Cmd& cmd);
-		void InitSel(void);
-		void Save(std::string& str);
-		void Load(const char* str);
-		void SaveSys(std::string& str);
-		void LoadSys(const char* str);
-		void SetSkipMode(SkipMode _mode);
 };
 /*******************************************************************
 ** GrpObj(implementation)
@@ -218,7 +263,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(0) {
+	zoom(-1), rotate(-1), attr(GrpObj::HIDDEN), parent_pimpl(NULL) {
 	int i;
 	for (i=0; i<9; i++) {
 		posx[i] = posy[i] = 0;
@@ -227,7 +272,7 @@ GrpObj::GrpObj(void) :
 
 GrpObj::~GrpObj() {
 	if (picture) delete picture;
-	if (parent_pimpl == 0) {
+	if (parent_pimpl == NULL) {
 		fprintf(stderr,"\n**************\nFATAL : UNINITIALIZED GrpObj IS FOUND!!! \n**************\n");
 	}
 }
@@ -245,7 +290,7 @@ void GrpObj::SetUpdate(void) {
 	//Update(); //FIXME
 }
 
-void GrpObj::SetPos(int index, int x,int y) {
+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;
@@ -382,7 +427,7 @@ void GrpObj::Update(void) {
 	}
 	attr = Attribute(attr & (~UPDATE_ALL));
 	if (attr & ANM_PLAYSTART) {
-		if (anm) {
+		if (anm != NULL) {
 			anm->Play();
 			attr = Attribute(attr | ANM_PLAYING);
 		}
@@ -558,9 +603,10 @@ void GrpObj::CreateGan(Event::Container&
 		fprintf(stderr,"GrpObj::CreateGan() is called before Create()\n");
 		return;
 	}
-	if (anm) {
+	if (anm != NULL) {
 		anm->Abort();
 		delete anm;
+		anm = NULL;
 	}
 	if (gan_name.empty()) return;
 	/* アニーメション情報 (.GAN ファイル)を求める */
@@ -626,9 +672,10 @@ void GrpObj::CreateGanSpecial(Event::Con
 		fprintf(stderr,"GrpObj::CreateGan() is called before Create()\n");
 		return;
 	}
-	if (anm) {
+	if (anm != NULL) {
 		anm->Abort();
 		delete anm;
+		anm = NULL;
 	}
 
 	// アニメーションを行う実体を作成
@@ -778,6 +825,159 @@ GrpImpl::GrpImpl(Event::Container& _even
 
 	LoadCgm();
 
+	RegisterCommand(1, 30, 0, "stackClear", (CmdImpl) &GrpImpl::impl_stackClear);
+	RegisterCommand(1, 33, 70, "grpBuffer", (CmdImpl) &GrpImpl::impl_grpBuffer);
+	RegisterCommand(1, 33, 73, "grpOpenBG", (CmdImpl) &GrpImpl::impl_grpOpen);
+	RegisterCommand(1, 33, 75, "grpMulti", (CmdImpl) &GrpImpl::impl_grpMulti); //FIXME: or not...
+	RegisterCommand(1, 33, 76, "grpOpen", (CmdImpl) &GrpImpl::impl_grpOpen);
+	RegisterCommand(1, 33, 32, "shake", (CmdImpl) &GrpImpl::impl_shake);
+	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, 1600, "recAdd", (CmdImpl) &GrpImpl::impl_recAdd);
+	RegisterCommand(1, 33, 406, "grpPan", (CmdImpl) &GrpImpl::impl_grpPan);
+
+	RegisterCommand(1, 34, 3120, "snmBgScroll", (CmdImpl) &GrpImpl::impl_snmBgScroll);
+	RegisterCommand(1, 34, 3100, "snmBgPlay", (CmdImpl) &GrpImpl::impl_snmPlay);
+	RegisterCommand(1, 34, 2100, "snmPlay", (CmdImpl) &GrpImpl::impl_snmPlay);
+	RegisterCommand(1, 34, 2101, "snmPlayEx", (CmdImpl) &GrpImpl::impl_snmPlay);
+
+	RegisterCommand(1, 4, 1500, "cgGetTotal", (CmdImpl) &GrpImpl::impl_cgGet);
+	RegisterCommand(1, 4, 1501, "cgGetViewed", (CmdImpl) &GrpImpl::impl_cgGet);
+	RegisterCommand(1, 4, 1502, "cgGetViewedPcnt", (CmdImpl) &GrpImpl::impl_cgGet);
+	RegisterCommand(1, 4, 1503, "cgGetFlag", (CmdImpl) &GrpImpl::impl_cgStatus);
+	RegisterCommand(1, 4, 1504, "cgStatus", (CmdImpl) &GrpImpl::impl_cgStatus);
+
+	RegisterCommand(1, 4, 0x6a4, "CreateInput", NULL);
+	RegisterCommand(1, 4, 0x6ae, "SetInput", NULL);
+
+	RegisterCommand(1, 61, 10, "objClear", (CmdImpl) &GrpImpl::impl_objClear);
+	RegisterCommand(1, 61, 11, "objDelete", (CmdImpl) &GrpImpl::impl_objClear);
+
+	RegisterCommand(1, 71, 1000, "createObjG00", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 71, 1003, "createObjGAN", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 71, 1100, "createObjRect", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 71, 1200, "createObjText", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 71, 1300, "createObjWeaver", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 71, 1400, "createObjDigit", (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);
+	RegisterCommand(1, 72, 1100, "createBgObjRect", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 72, 1200, "createBgObjText", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 72, 1300, "createBgObjWeaver", (CmdImpl) &GrpImpl::impl_createObj);
+	RegisterCommand(1, 72, 1400, "createBgObjDigit", (CmdImpl) &GrpImpl::impl_createObj);
+
+	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);
+	RegisterCommand(1, 73, 2003, "objPlay", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 1001, "ganLoop", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 1003, "ganPlay", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 1005, "ganPlayOnce", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 3001, "ganLoop2", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 3003, "ganPlay2", (CmdImpl) &GrpImpl::impl_gan);
+	RegisterCommand(1, 73, 3005, "ganPlayOnce2", (CmdImpl) &GrpImpl::impl_gan);
+
+	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);
+	RegisterCommand(1, 82, 1006, "objBgAdjust", NULL); //FIXME: (CmdImpl) &GrpImpl::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) &GrpImpl::impl_objSetPos); I don't know if it is usefull or properly implemented
+	RegisterCommand(1, 82, 2006, "objBgAdjust2?", NULL); //FIXME: (CmdImpl) &GrpImpl::impl_objSetPos); See above
+	RegisterCommand(1, 81, 1016, "objColour", NULL); //FIXME: (CmdImpl) &GrpImpl::impl_objColour);
+	RegisterCommand(1, 82, 1016, "objBgColour", NULL); //FIXME: (CmdImpl) &GrpImpl::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) &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);
+	RegisterCommand(1, 81, 1025, "objTextOpts", (CmdImpl) &GrpImpl::impl_objTextOpts); //FIXME: Incomplete
+	RegisterCommand(1, 82, 1025, "objBgTextOpts", (CmdImpl) &GrpImpl::impl_objTextOpts);
+	RegisterCommand(1, 81, 1032, "objOrder", (CmdImpl) &GrpImpl::impl_objOrder);
+	RegisterCommand(1, 82, 1032, "objBgOrder", (CmdImpl) &GrpImpl::impl_objOrder);
+	RegisterCommand(1, 81, 1034, "objDispRect", (CmdImpl) &GrpImpl::impl_objDispArea);
+	RegisterCommand(1, 82, 1034, "objBgDispRect", (CmdImpl) &GrpImpl::impl_objDispArea);
+	RegisterCommand(1, 81, 1037, "objSetDigits", (CmdImpl) &GrpImpl::impl_objSetDigits);
+	RegisterCommand(1, 82, 1037, "objBgSetDigits", (CmdImpl) &GrpImpl::impl_objSetDigits);
+	RegisterCommand(1, 81, 1038, "objNumOpts", (CmdImpl) &GrpImpl::impl_objNumOpts);
+	RegisterCommand(1, 82, 1038, "objBgNumOpts", (CmdImpl) &GrpImpl::impl_objNumOpts);
+	RegisterCommand(1, 81, 1039, "objPattNo", (CmdImpl) &GrpImpl::impl_objPattNo);
+	RegisterCommand(1, 82, 1039, "objBgPattNo", (CmdImpl) &GrpImpl::impl_objPattNo);
+	RegisterCommand(1, 81, 1046, "objScale", (CmdImpl) &GrpImpl::impl_objScale); //FIXME: Broken behaviour
+	RegisterCommand(1, 82, 1046, "objBgScale", (CmdImpl) &GrpImpl::impl_objScale);
+	RegisterCommand(1, 81, 1047, "objWidth", NULL);
+	RegisterCommand(1, 82, 1047, "objBgWidth", NULL);
+	RegisterCommand(1, 81, 1049, "objRotate", (CmdImpl) &GrpImpl::impl_objRotate);
+	RegisterCommand(1, 82, 1049, "objBgRotate", (CmdImpl) &GrpImpl::impl_objRotate);
+
+	RegisterCommand(1, 84, 1000, "objGetPos", (CmdImpl) &GrpImpl::impl_objPosDims);
+	RegisterCommand(1, 84, 1100, "objGetDims", (CmdImpl) &GrpImpl::impl_objPosDims);
+
+	RegisterCommand(1, 31, 0, "refresh", (CmdImpl) &GrpImpl::impl_refresh);
+
+	RegisterCommand(1, 20, 0, "bgmLoop", (CmdImpl) &GrpImpl::impl_bgmLoop);
+	RegisterCommand(1, 20, 1, "bgmPlayEx", (CmdImpl) &GrpImpl::impl_bgmLoop); //FIXME: wait
+	RegisterCommand(1, 20, 2, "bgmPlay", (CmdImpl) &GrpImpl::impl_bgmLoop);
+	RegisterCommand(1, 20, 5, "bgmStop", (CmdImpl) &GrpImpl::impl_bgmStop);
+	RegisterCommand(1, 20, 105, "bgmFadeOut", (CmdImpl) &GrpImpl::impl_bgmStop);
+
+	RegisterCommand(1, 21, 0, "wavPlay", (CmdImpl) &GrpImpl::impl_playWav);
+	RegisterCommand(1, 21, 1, "wavPlayEx", (CmdImpl) &GrpImpl::impl_playWav);
+	RegisterCommand(1, 21, 2, "wavLoop", (CmdImpl) &GrpImpl::impl_playWav);
+	RegisterCommand(1, 21, 3, "wavWait", NULL);
+	RegisterCommand(1, 21, 4, "wavPlaying", NULL);
+	RegisterCommand(1, 21, 5, "wavStop", (CmdImpl) &GrpImpl::impl_stopWav);
+	RegisterCommand(1, 21, 105, "wavFadeout", (CmdImpl) &GrpImpl::impl_stopWav);
+
+	RegisterCommand(1, 22, 0, "sePlay", (CmdImpl) &GrpImpl::impl_playSE);
+
+	RegisterCommand(1, 4, 2230, "SetBgmVolMod", (CmdImpl) &GrpImpl::impl_SetVolMod);
+	RegisterCommand(1, 4, 2231, "SetKoeVolMod", (CmdImpl) &GrpImpl::impl_SetVolMod);
+	RegisterCommand(1, 4, 2232, "SetPCMVolMod", (CmdImpl) &GrpImpl::impl_SetVolMod);
+	RegisterCommand(1, 4, 2233, "SetSeVolMod", (CmdImpl) &GrpImpl::impl_SetVolMod);
+	RegisterCommand(1, 4, 2330, "BgmVolMod", (CmdImpl) &GrpImpl::impl_GetVolMod);
+	RegisterCommand(1, 4, 2331, "KoeVolMod", (CmdImpl) &GrpImpl::impl_GetVolMod);
+	RegisterCommand(1, 4, 2332, "PCMVolMod", (CmdImpl) &GrpImpl::impl_GetVolMod);
+	RegisterCommand(1, 4, 2333, "SeVolMod", (CmdImpl) &GrpImpl::impl_GetVolMod);
+
+	RegisterCommand(1, 23, 0, "koePlay", (CmdImpl) &GrpImpl::impl_koePlay);
+	RegisterCommand(1, 23, 1, "koePlayEx", (CmdImpl) &GrpImpl::impl_koePlay); //FIXME
+	RegisterCommand(1, 23, 7, "koePlayExC", (CmdImpl) &GrpImpl::impl_koePlay); //FIXME
+	RegisterCommand(1, 23, 8, "koeDoPlay", (CmdImpl) &GrpImpl::impl_koePlay); //FIXME
+	RegisterCommand(1, 23, 9, "koeDoPlayEx", (CmdImpl) &GrpImpl::impl_koePlay); //FIXME
+	RegisterCommand(1, 23, 10, "koeDoPlayExC", (CmdImpl) &GrpImpl::impl_koePlay); //FIXME
+
+	RegisterCommand(1, 26, 1, "movPlayEx", (CmdImpl) &GrpImpl::impl_movPlay);
+	RegisterCommand(1, 26, 20, "movPlayExC", (CmdImpl) &GrpImpl::impl_movPlay);
+
+	RegisterCommand(1, 61, 14, "objSwap?", NULL);
+	RegisterCommand(1, 62, 14, "objSwap?", NULL);
+
 	anm1 = NULL;
 	anm2 = NULL;
 }
@@ -836,7 +1036,7 @@ void GrpImpl::LoadSurface(const char* st
 		s += ".g00";
 		bg = parent.Root().NewSurface(s.c_str());
 	}
-	if (bg) {
+	if (bg != NULL) {
 		if (ssurface[pdt]) parent.Root().DeleteSurface(ssurface[pdt]);
 		ssurface[pdt] = bg;
 		if (pdt == 0) {
@@ -859,7 +1059,7 @@ void GrpImpl::LoadSurface(const char* st
 void GrpImpl::InitSel(void) {
 	int i;
 	int args[16];
-	char key[1024];
+	char key[10];
 	for (i=0; i<999; i++) {
 		sprintf(key, "#SEL.%03d",i);
 		if (config->GetParam(key, 15, &args[0], &args[1],
@@ -878,7 +1078,8 @@ void GrpImpl::InitSel(void) {
 		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];
+		int j;
+		for (j=0; j<8; j++) s.args[j] = args[8+j];
 	}
 }
 
@@ -894,19 +1095,6 @@ void GrpImpl::SetObjChanged(int num) {
 	changed_obj.insert(num);
 }
 
-void GrpImpl::SetObjChangedGroup(int num) {
-	if (num % 1000 != 0) {
-		SetObjChanged(num);
-		return;
-	}
-	std::map<int, GrpObj>::iterator begin,end,it;
-	begin = grpobj.lower_bound(num);
-	end = grpobj.lower_bound(num+1000);
-	for (it=begin;it!=end;it++) {
-		changed_obj.insert(it->first);
-	}
-}
-
 void GrpImpl::RefreshObj(void) {
 	if (!deleted_pic.empty()) {
 		vector<PicBase*>::iterator it;
@@ -920,9 +1108,8 @@ 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];
-			GrpObj& parent_obj = grpobj[ ((*it)/1000) * 1000];
-			if (obj.picture == 0) continue;
-			if (obj.alpha == 0 || (obj.attr & GrpObj::HIDDEN) || (parent_obj.attr & GrpObj::HIDDEN_GROUP) ) {
+			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();
@@ -1002,10 +1189,9 @@ void GrpImpl::StartAnm(int type) {
 		it->second.DeletePic();
 		CreateObj(it->first);
 		GrpObj& g = grpobj[it->first];
-		GrpObj& parent_obj = grpobj[ (it->first/1000) * 1000];
 		if (g.picture) {
 			g.Update();
-			if (g.alpha == 0 || (g.attr & GrpObj::HIDDEN) || (parent_obj.attr & GrpObj::HIDDEN_GROUP) ) ;
+			if (g.alpha == 0 || (g.attr & GrpObj::HIDDEN)) ;
 			else g.picture->SimpleBlit(surface_update);
 			g.picture->hide();
 		}
@@ -1072,18 +1258,18 @@ void GrpImpl::AbortAnm(void) {
 }
 
 void GrpImpl::LoadSurface(const char* str) {
-	if (anm1) AbortAnm(); // 前の描画が終わってなければ強制終了
+	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
 	LoadSurface(str, 1);
 	bg_name = str;
 }
 
 void GrpImpl::LoadSurface(void) {
-	if (anm1) AbortAnm(); // 前の描画が終わってなければ強制終了
+	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
 	LoadSurface(bg_name.c_str(), 1);
 }
 
 void GrpImpl::AddSurface(const char* str) {
-	if (anm1) AbortAnm(); // 前の描画が終わってなければ強制終了
+	if (anm1 != NULL) AbortAnm(); // 前の描画が終わってなければ強制終了
 	LoadSurface(bg_name.c_str());
 
 	string s = str;
@@ -1344,20 +1530,19 @@ bool GrpImpl::Wait(unsigned int current_
 		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;
-			GrpObj& parent_obj = grpobj[ ((it->first)/1000) * 1000];
 			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,parent_obj.attr&GrpObj::HIDDEN_GROUP ? 1 : 0,obj.picture->IsHidden());
+					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,parent_obj.attr&GrpObj::HIDDEN_GROUP ? 1 : 0,obj.picture->IsHidden());
+					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,parent_obj.attr&GrpObj::HIDDEN_GROUP ? 1 : 0,obj.picture->IsHidden());
+					obj.PosX(),obj.PosY(),obj.alpha,obj.attr&GrpObj::HIDDEN ? 1 : 0, 0, obj.picture->IsHidden());
 				}
 			}
 		}
@@ -1399,6 +1584,7 @@ bool GrpImpl::Wait(unsigned int current_
 	if (status == WAIT_ANM) {
 		if (anm1 != NULL) {
 			if (!anm1->IsEnd()) return true;
+			//FIXME: Handle animation loops
 			AbortAnm();
 		}
 	} else if (status == WAIT_SHAKE) {
@@ -1439,14 +1625,705 @@ void GrpImpl::DeleteObj(int num) {
 	grpobj.erase(num);
 }
 
-void GrpImpl::DeleteObjRange(int num_first, int num_end) {
-	std::map<int, GrpObj>::iterator begin,end,it;
-	begin = grpobj.lower_bound(num_first);
-	end = grpobj.lower_bound(num_end);
-	for (it=begin;it!=end;it++) {
-		deleted_pic.push_back(it->second.DeletePic());
+void GrpImpl::impl_stackClear (Cmd& cmd) {
+	cmd.cmd_type = CMD_SAVECMDGRP_START;
+}
+
+void GrpImpl::impl_grpBuffer (Cmd& cmd) {
+	const char* name = cmd.Str(cmd.args[0]);
+	int pdt = cmd.args[1].value;
+	eprintf("load surface %s pdt %d\n",name, pdt);
+	if (pdt == 0)
+		reserved_load_surface0 = name; // 画像読み込みは 01-1f:0000 まで待つ
+	else if (pdt == 1)
+		LoadSurface(name); // 背景絵読み込み?
+	else
+		LoadSurface(name, pdt);
+	cmd.cmd_type = CMD_SAVECMDGRP;
+}
+
+void GrpImpl::impl_grpMulti(Cmd& cmd) {
+	int pos = cmd.args[0].value;
+	const char* name = cmd.Str(cmd.args[1]);
+	int sel = cmd.args[2].value;
+	eprintf("set foreground %s sel %d pos %d\n",name, sel, pos);
+	AddSurface(name);
+	StartAnm(sel);
+	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+	status = WAIT_ANM;
+	cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
+}
+
+void GrpImpl::impl_grpOpen(Cmd& cmd) {
+	const char* name = cmd.Str(cmd.args[0]);
+	int sel = cmd.args[1].value;
+
+	if (name[0] == '?')
+		LoadSurface();
+	else if(cmd.cmd3 == 73)
+		LoadSurface(name, 1);
+	else
+		LoadSurface(name);
+
+	StartAnm(sel);
+	status = WAIT_ANM;
+	event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+
+	if (name[0] == '?')
+		cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
+	else
+		cmd.cmd_type = CMD_SAVECMDGRP_START;
+}
+
+void GrpImpl::impl_shake(Cmd& cmd) {
+	// shake screen
+	char key[11];
+	sprintf(key, "#SHAKE.%03d", cmd.args[0].value);
+	if (config->SearchParam(key) != 2) {
+		fprintf(stderr,"Cannot find shake pattern %d; use default pattern\n",cmd.args[0].value);
+		strcpy(key, "#SHAKE.000"); // default key
+	}
+	int num;
+	const int* pattern;
+	pattern = config->GetParamArray(key, num);
+	if (pattern) {
+		StartShake(num, pattern);
+		status = WAIT_SHAKE;
+	}
+	cmd.clear();
+}
+
+void GrpImpl::impl_grpCopy(Cmd& cmd) {
+	if (cmd.cmd4 == 2) { // copy (KANOGI)
+		int sx = cmd.args[0].value;
+		int sy = cmd.args[1].value;
+		int w = cmd.args[2].value - sx;
+		int h = cmd.args[3].value - sy;
+		Rect rect(sx, sy, sx+w, sy+h);
+		int src = cmd.args[4].value;
+		int dx = cmd.args[5].value;
+		int dy = cmd.args[6].value;
+		int dest = cmd.args[7].value;
+		unsigned char alpha;
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		printf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
+		if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.clear();
+	}
+}
+
+void GrpImpl::impl_recFill(Cmd& cmd) {
+	int x = cmd.args[0].value;
+	int y = cmd.args[1].value;
+	int w = cmd.args[2].value;
+	int h = cmd.args[3].value;
+	Rect rect(x, y, x+w, y+w);
+	int pdt = cmd.args[4].value;
+	int r = cmd.args[5].value;
+	int g = cmd.args[6].value;
+	int b = cmd.args[7].value;
+
+	if (cmd.cmd4 == 2) {
+		eprintf("clear %d:(%d,%d) size (%d,%d) r %d g %d b %d\n",pdt,x,y,w,h,r,g,b);
+		DSurfaceFill(Dsurface(pdt), rect, r, g, b);
+		// if (pdt == 0) screen->ReBlit(rect);
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+	else if (cmd.cmd4 == 3) { // alpha つきfill
+		int a = cmd.args[8].value;
+		eprintf("alpha-clear %d:(%d,%d) size (%d,%d) r %d g %d b %d a %d\n",pdt,x,y,w,h,r,g,b,a);
+		if (a <= 0) ;
+		else if (a >= 255) DSurfaceFill(Dsurface(pdt), rect, r, g, b);
+		else {
+			DSurfaceFill(Dsurface(WORKPDT), rect, r, g, b, a);
+			parent.Root().BlitSurface(Dsurface(WORKPDT), rect, Dsurface(pdt), rect);
+		}
+		// if (pdt == 0) screen->ReBlit(rect);
+		cmd.clear();
+	}
+}
+
+void GrpImpl::impl_recCopy(Cmd& cmd) {
+	int sx = cmd.args[0].value;
+	int sy = cmd.args[1].value;
+	int w = cmd.args[2].value;
+	int h = cmd.args[3].value;
+	Rect rect(sx, sy, sx + w, sy + h);
+	int src = cmd.args[4].value;
+	int dx = cmd.args[5].value;
+	int dy = cmd.args[6].value;
+	int dest = cmd.args[7].value;
+
+	if (cmd.cmd4 == 2) {
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
+		//DSurfaceMove(Ssurface(src), Rect(sx,sy,sx+w,sy+h), Dsurface(dest), Rect(dx,dy));
+		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+
+	else if (cmd.cmd4 == 3) { // alpha つきcopy
+		unsigned char alpha;
+		if (cmd.args[8].value < 0) alpha = 0;
+		else if (cmd.args[8].value > 255) alpha = 255;
+		else alpha = cmd.args[8].value;
+		eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		if (alpha != 0)
+			parent.Root().BlitSurface(Ssurface(src), rect, &alpha, Rect(0,0,1,1), Dsurface(dest), Rect(dx,dy), 0);
+		// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
+		cmd.clear();
+	}
+}
+
+void GrpImpl::impl_recAdd(Cmd& cmd) {
+	if (cmd.cmd4 == 3) { // saturate mode で alpha 付き copy
+		int sx = cmd.args[0].value;
+		int sy = cmd.args[1].value;
+		int w = cmd.args[2].value;
+		int h = cmd.args[3].value;
+		Rect rect(sx, sy, sx+w, sy+h);
+		int src = cmd.args[4].value;
+		int dx = cmd.args[5].value;
+		int dy = cmd.args[6].value;
+		int dest = cmd.args[7].value;
+		unsigned char alpha;
+		if (cmd.args[8].value < 0) alpha = 0;
+		else if (cmd.args[8].value > 255) alpha = 255;
+		else alpha = cmd.args[8].value;
+		eprintf("copy surface w/ saturate %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
+		if (src == dest) {
+			DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
+			src = WORKPDT;
+		}
+		if (alpha != 0) {
+			// saturate mode : screen (picture) を一時的に作成
+			PicBase* screen_tmp = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
+			screen_tmp->SetSurface(Ssurface(src), 0, 0, PicBase::BLIT_SATURATE);
+			screen_tmp->SetSurfaceRect(rect);
+			screen_tmp->Move(dx, dy);
+			screen_tmp->SetSurfaceAlpha(&alpha, Rect(0,0,1,1));
+			screen_tmp->SimpleBlit(Dsurface(dest));
+			delete screen_tmp;
+		}
+		cmd.clear();
+	}
+}
+
+void GrpImpl::impl_grpPan(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		Rect r_from(cmd.args[0].value, cmd.args[1].value);
+		Rect r_to(cmd.args[2].value, cmd.args[3].value);
+		int src_pdt = cmd.args[4].value;
+		Rect r(cmd.args[5].value,cmd.args[6].value,cmd.args[7].value+1,cmd.args[8].value+1);
+		int tm = cmd.args[9].value;
+		fprintf(stderr,"??? cmd time %d\n",tm);
+		// anm1 = new ScnGrpMove(event, screen, parent.Root(), surface, r, Ssurface(2), r_from, r_to, tm);
+		// status = WAIT_ANM;
+	}
+}
+
+void GrpImpl::impl_snmBgScroll(Cmd& cmd) {
+	if (cmd.cmd4 == 0) { // スクロールする画像効果(Princess Bride)
+		if (anm2 != NULL) {
+			anm2->Abort();
+			delete anm2;
+			anm2 = NULL;
+		}
+		PicBase* pic; Surface* s;
+		Rect r(cmd.args[1].value, cmd.args[2].value, cmd.args[3].value+1, cmd.args[4].value+1);
+		const char* name = cmd.Str(cmd.args[5]);
+		Rect sr_start(cmd.args[6].value,cmd.args[7].value);
+		Rect sr_end(cmd.args[8].value,cmd.args[9].value);
+		int tm = cmd.args[10].value;
+		LoadSurface(name, 2); /* PDT2 に読み込み、と決め打ち */
+
+		anm2 = new ScnGrpMove(event, screen, parent.Root(), Dsurface(1), r, Ssurface(2), sr_start, sr_end, tm);
+		cmd.cmd_type = CMD_SAVECMDGRP;
+	}
+}
+
+void GrpImpl::impl_snmPlay(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		// カードが落ちるアニメーション
+		int i;
+		ScnGrpAnm* new_anm = new ScnGrpAnm(event, screen, *this);
+		if (cmd.cmd3 == 0x834 || cmd.cmd3 == 0x835) {
+			AbortAnm();
+			anm1 = new_anm;
+			if (cmd.cmd3 == 0x835) {
+				status = WAIT_ANM;
+				event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+			}
+		} else {
+ 			anm2 = new_anm;
+		}
+		for (i=0; i<cmd.argc; i++) {
+			const char* name = cmd.Str(cmd.args[i*3+1]);
+			int tm = cmd.args[i*3+2].value;
+			new_anm->push_back(ScnGrpAnmAtom(name,tm));
+		}
+		new_anm->CalcTotal();
+		cmd.clear();
+	}
+}
+
+void GrpImpl::impl_cgGet(Cmd& cmd) {
+	if (cmd.cmd3 == 0x5dc) // Total number of CG
+		cmd.SetSysvar(cgm_size);
+
+	if (cmd.cmd3 == 0x5dd) // Number of CG viewed
+		cmd.SetSysvar(cgm_data.size());
+
+	if (cmd.cmd3 == 0x5de) // Percentage of CG viewed
+		cmd.SetSysvar(cgm_data.size() * 100 / cgm_size);
+}
+
+void GrpImpl::impl_cgStatus(Cmd& cmd) {
+	string s = cmd.Str(cmd.args[0]);
+	if (cgm_info.find(s) == cgm_info.end()) {
+		fprintf(stderr,"cmd 01-04:05e0 : cannot find cgm-info of '%s'\n",s.c_str());
+		cmd.SetSysvar(-1);
+	}
+	else {
+		int n = cgm_info[s];
+		if (cmd.cmd3 == 1503) cmd.SetSysvar(n);
+		else {
+			if (cgm_data.find(n) == cgm_data.end()) cmd.SetSysvar(0);
+			else cmd.SetSysvar(1);
+		}
+	}
+}
+
+void GrpImpl::impl_objClear(Cmd& cmd) { //FIXME: may be broken (doesn't reflect what Haeleth says)
+	DeleteObj(cmd.args[0].value);
+	cmd.clear();
+}
+
+void GrpImpl::impl_createObj(Cmd& cmd) {
+	/**************:
+	0x47 : オブジェクト内容の設定
+			1100: G00 file
+			1003: GAN file
+			1100: rect
+			1200: string
+			1300: weather effects
+			1400: number
+	*/
+	int base_argc = 0;
+	DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
+	if (cmd.cmd2 == 71)
+		DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
+
+	GrpObj& g = (cmd.cmd2 == 71) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+
+	if (cmd.cmd3 == 1000) { /* ファイル名設定 */
+		g.gtype = GrpObj::FILE; //FIXME: Strange thing in the main menu
+		string name = cmd.Str(cmd.args[1]);
+		if (name.find('?') != -1) {
+			name.erase(name.find('?')); // '?' 以降の意味がわからない
+		}
+		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]);
+		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));
+
+		if (cmd.argc >= 5)
+			g.SetPos(1, cmd.args[4].value, -cmd.args[5].value);
+
+		if (g.name.find('?') != -1) {
+			g.name.erase(g.name.find('?'));
+			g.gan_name = cmd.Str(cmd.args[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)); // 常に表示がデフォルト?
+		cmd.clear();
+	} else if (cmd.cmd3 == 1400) { // 数値を画像として表示
+		g.gtype = GrpObj::DIGIT;
+		g.name = cmd.Str(cmd.args[1]);
 	}
-	grpobj.erase(begin, end);
+	CreateObj(cmd.args[0].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);
+			} 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));
+			}
+			if (cmd.cmd1 == 1)
+				SetObjChanged(cmd.args[0].value);
+		}
+		if (cmd.cmd4 >= 2) { // 座標等も設定
+			g.SetPos(0, cmd.args[3+base_argc].value, cmd.args[4+base_argc].value);
+		}
+		if ( (cmd.cmd3 == 1000 || cmd.cmd3 == 1003) && cmd.cmd4 >= 3) { // pattern 番号も設定
+			g.SetSurfaceNum(cmd.args[5+base_argc].value);
+			base_argc++; // 1000 (FILE) / 1003 (GAN) の場合のみこのオプションは存在する
+		}
+		cmd.clear();
+	} else {
+		fprintf(stderr,"CreateObj : cmd.cmd3 = %04x ; not supported!\n",cmd.cmd3);
+	}
+}
+
+void GrpImpl::impl_gan(Cmd& cmd) {
+	GrpObj& g = grpobj[cmd.args[0].value];
+
+	if (cmd.cmd3 == 3) { // ganIsPlaying
+		if (g.anm == NULL || g.anm->IsEnd())
+			cmd.SetSysvar(0);
+		else
+			cmd.SetSysvar(1);
+	}
+	else if (cmd.cmd3 == 1000) { // ganStop
+		if (g.anm == NULL || g.anm->IsEnd())
+			g.SetSurfaceNum(cmd.args[1].value);
+		else {
+			g.anm->Abort();
+			g.SetSurfaceNum(cmd.args[1].value);
+		}
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+	else if (cmd.cmd3 == 2003) { // objPlay
+		g.CreateGanSpecial(event, 0, cmd.args[1].value);
+		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+	else if (cmd.cmd3 == 3001 || cmd.cmd3 == 3003 || cmd.cmd3 == 3005 ||
+			 cmd.cmd3 == 1001 || cmd.cmd3 == 1003 || cmd.cmd3 == 1005) { // ganPlay*
+		g.CreateGan(event, cmd.args[1].value);
+		// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
+		SetObjChanged(cmd.args[0].value);
+		cmd.clear();
+	}
+}
+
+void 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 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;
+	}
+	else {
+		index = 0;
+		if (cmd.cmd3 == 1000) {
+			x = cmd.args[1].value;
+			y = cmd.args[2].value;
+		}
+		else {
+			g.GetPos(index, x, y);
+			if (cmd.cmd3 == 1001)
+				x = cmd.args[1].value;
+			else
+				y = cmd.args[1].value;
+		}
+	}
+
+	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);
+	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);
+		// グループ単位で次の RefreshObj で表示・消去
+	if (cmd.cmd2 == 0x51) //not Bg
+		SetObjChanged(cmd.args[0].value);
+	cmd.clear();
+}
+
+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 */
+	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);
+		cmd.clear();
+	} else if (cmd.args[1].value == 0) {
+		g.attr = GrpObj::Attribute(g.attr & (~GrpObj::SATURATE));
+		cmd.clear();
+	}
+	g.SetUpdate();
+}
+
+void GrpImpl::impl_objSetText(Cmd& cmd) {
+	GrpObj& g = (cmd.cmd2 == 0x51) ? grpobj[cmd.args[0].value] : bs_obj[cmd.args[0].value];
+
+	g.print_moji = cmd.Str(cmd.args[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];
+	// 画像を文字列として設定:文字の大きさなど
+	g.print_size = cmd.args[1].value;
+	/* 前景色を得る */
+	int cr, cg, cb;
+	char key[17];
+	sprintf(key, "#COLOR_TABLE.%03d", cmd.args[5].value);
+	if (config->GetParam(key, 3, &cr, &cg, &cb)) { // color not found
+		cr = cg = cb = 0;
+	}
+	g.print_r = cr;
+	g.print_g = cg;
+	g.print_b = cb;
+	g.SetUpdate();
+	cmd.clear();
+}
+
+void 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;
+	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 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.cmd3 == 1005) {
+			w -= rx;
+			h -= ry;
+		}
+	}
+	else {
+		rx = ry = 0;
+		w = screen->Width();
+		h = screen->Height();
+	}
+	g.SetClipArea(rx, ry, w, h); //TODO: case when cmd.args.size() == 1
+	cmd.clear();
+}
+
+void 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();
+	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;
+	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();
+	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);
+	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
+	zoom = zoom*256/100;
+	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;
+	angle /= 10;
+	if (angle < 0) {
+		angle %= 360;
+		angle += 360;
+	}
+	angle %= 360;
+	g.SetZoomRotate(-1, angle);
+	cmd.clear();
+}
+
+void GrpImpl::impl_objPosDims(Cmd& cmd) {
+	GrpObj& obj = grpobj[cmd.args[0].value];
+
+	VarInfo arg1 = cmd.args[1];
+	VarInfo arg2 = cmd.args[2];
+
+	int val1, val2;
+
+	if (cmd.cmd3 == 1000)
+		obj.GetPos(0, val1, val2);
+	else if (cmd.cmd3 == 1100)
+		obj.GetSrcGeom(val1, val2);
+
+	cmd.SetFlagvar(arg1, val1);
+	cmd.SetFlagvar(arg2, val2);
+}
+
+void GrpImpl::impl_refresh(Cmd& cmd) {
+	// 本来は grpstack clear らしい
+	RefreshObj();
+	// Princess Bride の中途 Staff roll
+	// このタイミングで描画するのが都合がいいので、
+	//シナリオループを抜けて描画を起動
+	cmd.cmd_type = CMD_WAITFRAMEUPDATE;
+}
+
+void GrpImpl::impl_bgmLoop(Cmd& cmd) {
+	if (cmd.cmd4 == 0 || cmd.cmd4 == 2) {
+		int count = 8000;
+		if (cmd.cmd3 == 2)
+			count = 0; //bgmPlay, play once
+		music->PlayCDROM((char*)cmd.Str(cmd.args[0]), count);
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+}
+
+void GrpImpl::impl_bgmStop(Cmd& cmd) {
+	if (cmd.cmd4 == 0) {
+		if (cmd.cmd3 == 5)
+			music->StopCDROM(0);
+		else if (cmd.cmd3 == 105)
+			music->StopCDROM(cmd.args[0].value);
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+}
+
+void GrpImpl::impl_playWav(Cmd& cmd) {
+	if (cmd.cmd3 == 2) {
+		music->PlaySE(cmd.Str(cmd.args[0]), 1); //loop
+		cmd.cmd_type = CMD_SAVECMD_ONCE;
+	}
+	else {
+		music->PlaySE(cmd.Str(cmd.args[0]));
+		cmd.clear();
+	}
+	if (cmd.cmd3 == 1)
+		status = WAIT_SE;
+}
+
+void GrpImpl::impl_playSE(Cmd& cmd) {
+	music->PlaySE(cmd.args[0].value);
+	cmd.clear();
+}
+
+void GrpImpl::impl_stopWav(Cmd& cmd) {
+	if (cmd.cmd3 == 5)
+		music->StopSE();
+	else if (cmd.cmd3 == 105)
+		music->StopSE(cmd.args[0].value);
+
+	cmd.cmd_type = CMD_SAVECMD_ONCE;
+}
+
+void GrpImpl::impl_SetVolMod(Cmd& cmd) {
+	music->volmod[cmd.cmd3-0x8b6] = cmd.args[0].value;
+	config->SetParam("#VOLMOD", 4, music->volmod[0], music->volmod[1], music->volmod[2], music->volmod[3]);
+	cmd.clear();
+}
+
+void GrpImpl::impl_GetVolMod(Cmd& cmd) {
+	cmd.SetSysvar(music->volmod[cmd.cmd3-0x91a]);
+}
+
+void GrpImpl::impl_koePlay(Cmd& cmd) {
+	eprintf("play koe %d",cmd.args[0].value);
+	if (cmd.cmd4 == 1) {
+		eprintf(", para? %d",cmd.args[1].value);
+	}
+	eprintf("\n");
+	char buf[1024]; sprintf(buf, "%d",cmd.args[0].value);
+	if ( !(skip_mode & SKIP_TEXT)) music->PlayKoe(buf);
+	cmd.clear();
+}
+
+/*It may be useful... or not.
+void GrpImpl::impl_objSwap(Cmd& cmd) {
+	if (cmd.cmd1 == 1 && cmd.args.size() == 2) {
+		SwapObj(cmd.args[0].value, cmd.args[1].value);
+	}
+	cmd.clear();
+}*/
+
+void GrpImpl::impl_movPlay(Cmd& cmd) {
+	if ( cmd.cmd4 == 0) {
+		const char* str = cmd.Str(cmd.args[0]);
+		int x = cmd.args[1].value;
+		int y = cmd.args[2].value;
+		int x2 = cmd.args[3].value;
+		int y2 = cmd.args[4].value;
+		eprintf("play movie ; name %s pos %d,%d - %d,%d\n",str,x,y,x2,y2);
+		music->PlayMovie(str, x, y, x2, y2,1);
+		status = WAIT_MOVIE;
+		event.RegisterGlobalPressFunc(&Pressed, (void*)this);
+		cmd.clear();
+	}
 }
 
 void GrpImpl::Exec(Cmd& cmd) {
@@ -1460,778 +2337,24 @@ void GrpImpl::Exec(Cmd& cmd) {
 		RefreshObj();
 	}
 	if (cmd.cmd_type != CMD_OTHER) return;
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x1e && cmd.cmd3 == 0) {
-		cmd.cmd_type = CMD_SAVECMDGRP_START; // grp stack clear
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x21) {
-		if (cmd.cmd3 == 0x46) {
-			const char* name = cmd.Str(cmd.args[0]);
-			int pdt = cmd.args[1].value;
-			eprintf("load surface %s pdt %d\n",name, pdt);
-			if (pdt == 0)
-				reserved_load_surface0 = name; // 画像読み込みは 01-1f:0000 まで待つ
-			else if (pdt == 1)
-				LoadSurface(name); // 背景絵読み込み?
-			else
-				LoadSurface(name, pdt);
-			cmd.cmd_type = CMD_SAVECMDGRP;
-		} else if (cmd.cmd3 == 0x49) {
-			const char* name = cmd.Str(cmd.args[0]);
-			int sel = cmd.args[1].value;
-			eprintf("set background %s sel %d\n",name, sel);
-			if (name[0] == '?') {
-				LoadSurface();
-			} else {
-				LoadSurface(name);
-			}
-			StartAnm(sel);
-			status = WAIT_ANM;
-			event.RegisterGlobalPressFunc(&Pressed, (void*)this);
-			if (name[0] == '?')
-				cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
-			else
-				cmd.cmd_type = CMD_SAVECMDGRP_START;
-		} else if (cmd.cmd3 == 0x4b) {
-			int pos = cmd.args[0].value;
-			const char* name = cmd.Str(cmd.args[1]);
-			int sel = cmd.args[2].value;
-			eprintf("set foreground %s sel %d pos %d\n",name, sel, pos);
-			AddSurface(name);
-			StartAnm(sel);
-			event.RegisterGlobalPressFunc(&Pressed, (void*)this);
-			status = WAIT_ANM;
-			cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
-		} else if (cmd.cmd3 == 0x4c) {
-			/* 0x46 との違いがわからない */
-			/* とりあえず bg として登録しないでみる */
-			/* 735 / 19438 : unsupported command; 0x23 - cmd 01-21:004c:00[ 2] 
-        		** "?",0
-			*/
-			/* arg1 = "?" arg2 = 0 */
-			const char* name = cmd.Str(cmd.args[0]);
-			int sel = cmd.args[1].value;
-			if (name[0] == '?') {
-				LoadSurface();
-			} else {
-				LoadSurface(name, 1);
-			}
-			StartAnm(sel);
-			status = WAIT_ANM;
-			event.RegisterGlobalPressFunc(&Pressed, (void*)this);
-			if (name[0] == '?')
-				cmd.cmd_type = CMD_SAVECMDGRP_ONCE;
-			else
-				cmd.cmd_type = CMD_SAVECMDGRP_START;
-		} else if (cmd.cmd3 == 0x20) {
-			// shake screen
-			char key[1024];
-			sprintf(key, "#SHAKE.%03d", cmd.args[0].value);
-			if (config->SearchParam(key) != 2) {
-				fprintf(stderr,"Cannot find shake pattern %d; use default pattern\n",cmd.args[0].value);
-				strcpy(key, "#SHAKE.000"); // default key
-			}
-			int num; const int* pattern;
-			pattern = config->GetParamArray(key, num);
-			if (pattern) {
-				StartShake(num, pattern);
-				status = WAIT_SHAKE;
-			}
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x64 && cmd.cmd4 == 2) { // copy (KANOGI)
-			int sx = cmd.args[0].value;
-			int sy = cmd.args[1].value;
-			int w = cmd.args[2].value - sx;
-			int h = cmd.args[3].value - sy;
-			Rect rect(sx, sy, sx+w, sy+h);
-			int src = cmd.args[4].value;
-			int dx = cmd.args[5].value;
-			int dy = cmd.args[6].value;
-			int dest = cmd.args[7].value;
-			unsigned char alpha;
-			eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
-			printf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
-			if (src == dest) {
-				DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
-				src = WORKPDT;
-			}
-			parent.Root().BlitSurface(Ssurface(src), rect, Dsurface(dest), Rect(dx,dy));
-			if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x4b1 && cmd.cmd4 == 2) {
-			int x = cmd.args[0].value;
-			int y = cmd.args[1].value;
-			int w = cmd.args[2].value;
-			int h = cmd.args[3].value;
-			Rect rect(x,y,x+w,y+w);
-			int pdt = cmd.args[4].value;
-			int r = cmd.args[5].value;
-			int g = cmd.args[6].value;
-			int b = cmd.args[7].value;
-			eprintf("clear %d:(%d,%d) size (%d,%d) r %d g %d b %d\n",pdt,x,y,w,h,r,g,b);
-			DSurfaceFill(Dsurface(pdt), rect, r, g, b, 0xff);
-			// if (pdt == 0) screen->ReBlit(rect);
-			cmd.cmd_type = CMD_SAVECMDGRP;
-		} else if (cmd.cmd3 == 0x4b1 && cmd.cmd4 == 3) { // alpha つきfill
-			int x = cmd.args[0].value;
-			int y = cmd.args[1].value;
-			int w = cmd.args[2].value;
-			int h = cmd.args[3].value;
-			Rect rect(x,y,x+w,y+h);
-			int pdt = cmd.args[4].value;
-			int r = cmd.args[5].value;
-			int g = cmd.args[6].value;
-			int b = cmd.args[7].value;
-			int a = cmd.args[8].value;
-			eprintf("alpha-clear %d:(%d,%d) size (%d,%d) r %d g %d b %d a %d\n",pdt,x,y,w,h,r,g,b,a);
-			if (a <= 0) ;
-			else if (a >= 255) DSurfaceFill(Dsurface(pdt), rect, r, g, b);
-			else {
-				DSurfaceFill(Dsurface(WORKPDT), rect, r, g, b, a);
-				parent.Root().BlitSurface(Dsurface(WORKPDT), rect, Dsurface(pdt), rect);
-			}
-			// if (pdt == 0) screen->ReBlit(rect);
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x44c && cmd.cmd4 == 2) {
-			int sx = cmd.args[0].value;
-			int sy = cmd.args[1].value;
-			int w = cmd.args[2].value;
-			int h = cmd.args[3].value;
-			int src = cmd.args[4].value;
-			int dx = cmd.args[5].value;
-			int dy = cmd.args[6].value;
-			int dest = cmd.args[7].value;
-			eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
-			parent.Root().BlitSurface(Ssurface(src), Rect(sx,sy,sx+w,sy+h), Dsurface(dest), Rect(dx,dy));
-			//DSurfaceMove(Ssurface(src), Rect(sx,sy,sx+w,sy+h), Dsurface(dest), Rect(dx,dy));
-			// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
-			cmd.cmd_type = CMD_SAVECMDGRP;
-		} else if (cmd.cmd3 == 0x44c && cmd.cmd4 == 3) { // alpha つきcopy
-			int sx = cmd.args[0].value;
-			int sy = cmd.args[1].value;
-			int w = cmd.args[2].value;
-			int h = cmd.args[3].value;
-			Rect rect(sx, sy, sx+w, sy+h);
-			int src = cmd.args[4].value;
-			int dx = cmd.args[5].value;
-			int dy = cmd.args[6].value;
-			int dest = cmd.args[7].value;
-			unsigned char alpha;
-			if (cmd.args[8].value < 0) alpha = 0;
-			else if (cmd.args[8].value > 255) alpha = 255;
-			else alpha = cmd.args[8].value;
-			eprintf("copy surface %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
-			if (src == dest) {
-				DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
-				src = WORKPDT;
-			}
-			if (alpha != 0)
-				parent.Root().BlitSurface(Ssurface(src), rect, &alpha, Rect(0,0,1,1), Dsurface(dest), Rect(dx,dy), 0);
-			// if (dest == 0) screen->ReBlit(Rect(dx,dy,dx+w,dy+h));
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x640 && cmd.cmd4 == 3) { // saturate mode で alpha 付き copy
-			int sx = cmd.args[0].value;
-			int sy = cmd.args[1].value;
-			int w = cmd.args[2].value;
-			int h = cmd.args[3].value;
-			Rect rect(sx, sy, sx+w, sy+h);
-			int src = cmd.args[4].value;
-			int dx = cmd.args[5].value;
-			int dy = cmd.args[6].value;
-			int dest = cmd.args[7].value;
-			unsigned char alpha;
-			if (cmd.args[8].value < 0) alpha = 0;
-			else if (cmd.args[8].value > 255) alpha = 255;
-			else alpha = cmd.args[8].value;
-			eprintf("copy surface w/ saturate %d:(%d,%d) size(%d,%d) -> %d:(%d,%d)\n",src,sx,sy,w,h,dest,dx,dy);
-			if (src == dest) {
-				DSurfaceMove(Ssurface(src), rect, Dsurface(WORKPDT), rect);
-				src = WORKPDT;
-			}
-			if (alpha != 0) {
-				// saturate mode : screen (picture) を一時的に作成
-				PicBase* screen_tmp = parent.create_leaf(Rect(0, 0, parent.Width(), parent.Height()), 0);
-				screen_tmp->SetSurface(Ssurface(src), 0, 0, PicBase::BLIT_SATURATE);
-				screen_tmp->SetSurfaceRect(rect);
-				screen_tmp->Move(dx, dy);
-				screen_tmp->SetSurfaceAlpha(&alpha, Rect(0,0,1,1));
-				screen_tmp->SimpleBlit(Dsurface(dest));
-				delete screen_tmp;
-			}
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x196 && cmd.cmd4 == 0) {
-			Rect r_from(cmd.args[0].value, cmd.args[1].value);
-			Rect r_to(cmd.args[2].value, cmd.args[3].value);
-			int src_pdt = cmd.args[4].value;
-			Rect r(cmd.args[5].value,cmd.args[6].value,cmd.args[7].value+1,cmd.args[8].value+1);
-			int tm = cmd.args[9].value;
-			fprintf(stderr,"??? cmd time %d\n",tm);
-			// anm1 = new ScnGrpMove(event, screen, parent.Root(), surface, r, Ssurface(2), r_from, r_to, tm);
-			// status = WAIT_ANM;
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x22) {
-		if (cmd.cmd3 == 0x0c30 && cmd.cmd4 == 0) { // スクロールする画像効果(Princess Bride)
-			if (anm2) {
-				anm2->Abort();
-				delete anm2;
-			}
-			PicBase* pic; Surface* s;
-			Rect r(cmd.args[1].value, cmd.args[2].value, cmd.args[3].value+1, cmd.args[4].value+1);
-			const char* name = cmd.Str(cmd.args[5]);
-			Rect sr_start(cmd.args[6].value,cmd.args[7].value);
-			Rect sr_end(cmd.args[8].value,cmd.args[9].value);
-			int tm = cmd.args[10].value;
-			LoadSurface(name, 2); /* PDT2 に読み込み、と決め打ち */
-			
-			anm2 = new ScnGrpMove(event, screen, parent.Root(), Dsurface(1), r, Ssurface(2), sr_start, sr_end, tm);
-			cmd.cmd_type = CMD_SAVECMDGRP;
-		}
-		if ( (cmd.cmd3 == 0xc1c && cmd.cmd4 == 0) || (cmd.cmd3 == 0x835 && cmd.cmd4 == 0) ) {
-			// カードが落ちるアニメーション
-			int i;
-			ScnGrpAnm* new_anm = new ScnGrpAnm(event, screen, *this);
-			if (cmd.cmd3 == 0x835) {
-				AbortAnm();
-				anm1 = new_anm;
-				status = WAIT_ANM;
-				event.RegisterGlobalPressFunc(&Pressed, (void*)this);
-			} else {
- 				anm2 = new_anm;
-			}
-			for (i=0; i<cmd.argc; i++) {
-				const char* name = cmd.Str(cmd.args[i*3+1]);
-				int tm = cmd.args[i*3+2].value;
-				new_anm->push_back(ScnGrpAnmAtom(name,tm));
-			}
-			new_anm->CalcTotal();
-			cmd.clear();
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 4) {
-		if (cmd.cmd3 == 0x5dc) // Total number of CG
-			cmd.SetSysvar(cgm_size);
-		if (cmd.cmd3 == 0x5dd) // Number of CG viewed
-			cmd.SetSysvar(cgm_data.size());
-		if (cmd.cmd3 == 0x5de) // Percentage of CG viewed
-			cmd.SetSysvar(cgm_data.size() * 100 / cgm_size);
+
+	CommandHandler::Exec(cmd);
 
-		if (cmd.cmd3 == 0xd8 || cmd.cmd3 == 0xd3 || cmd.cmd3 == 0xd2 || cmd.cmd3 == 0xd7) {
-			cmd.clear(); // いつも 0xd8 / 0xd7 と組で出てくる
-		}
-
-		// CG Status
-		if (cmd.cmd3 == 0x5e0) { // 画像既視フラグを得る
-			string s = cmd.Str(cmd.args[0]);
-			if (cgm_info.find(s) == cgm_info.end()) {
-				fprintf(stderr,"cmd 01-04:05e0 : cannot find cgm-info of '%s'\n",s.c_str());
-				return;
-			}
-			int n = cgm_info[s];
-			if (cgm_data.find(n) == cgm_data.end()) cmd.SetSysvar(0);
-			else cmd.SetSysvar(1);
-		}
-
-		if (cmd.cmd3 == 0x6a4) { // CreateInput (index, x, y, width, height, fontsize, br, bg, bb, fr, fg, fb)
-			int index = cmd.args[0].value;
-			int x = cmd.args[1].value;
-			int y = cmd.args[2].value;
-			//TODO
-		}
-		if (cmd.cmd3 == 0x6ae) { // SetInput (index, text)
-			//TODO
-		}
-	}
-#if 1
-	/* object 操作 */
-	if ( (cmd.cmd1 == 1 || cmd.cmd1 == 2)  && (cmd.cmd2 == 0x3d || cmd.cmd2 == 0x3e) && (cmd.cmd3 == 0x0a || cmd.cmd3 == 0x0b || cmd.cmd3 == 0x0e)) { // clear object
-		if (cmd.cmd3 == 0x0a || cmd.cmd3 == 0x0b) {
-			if (cmd.cmd1 == 2 && cmd.args.size() == 2) {
-				int num = cmd.args[0].value*1000 + cmd.args[1].value + 500;
-				DeleteObj(num);
-			} else if (cmd.args.size() == 1) { // group ごと消去
-				int num_first = cmd.args[0].value * 1000;
-				int num_end = num_first+1000;
-				DeleteObjRange(num_first, num_end);
-			}
-		} else { // 0x0e
-			if (cmd.cmd1 == 1 && cmd.args.size() == 2) {
-				SwapObj(cmd.args[0].value * 1000, cmd.args[1].value * 1000);
-			} else if (cmd.cmd1 == 2 && cmd.args.size() == 3) {
-				int v1 = cmd.args[0].value*1000 + cmd.args[1].value + 500;
-				int v2 = cmd.args[0].value*1000 + cmd.args[2].value + 500;
-				SwapObj(v1, v2);
-			}
-		}
-		cmd.clear();
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x3c && cmd.cmd3 == 0x01) { // ??? : CLANNAD
-		cmd.clear();
-	}
+	//TODO: ???
 	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x3c && cmd.cmd3 == 0) { // ??? : KANOGI : 画像オブジェクトの削除?
-		DeleteObjPic(cmd.args[0].value * 1000); // 旧ファイル名のsurfaceを削除
-		GrpObj& g = grpobj[cmd.args[0].value * 1000];
+		DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
+		GrpObj& g = grpobj[cmd.args[0].value];
 		g.attr = GrpObj::Attribute(g.attr | GrpObj::HIDDEN);
 		cmd.clear();
 	}
-	if ( (cmd.cmd1 == 1 || cmd.cmd1 == 2) && (cmd.cmd2 == 0x47 || cmd.cmd2 == 0x48|| cmd.cmd2 == 0x49 || cmd.cmd2 == 0x51 || cmd.cmd2 == 0x52 || cmd.cmd2 == 0x54)) {
-		if (cmd.cmd1 == 1) {
-			if (cmd.args.size() >= 1) cmd.args[0].value *= 1000; // とりあえず 1000倍しておく
-		} else { // cmd.cmd2 == 2
-			// オブジェクト番号を指定するコマンド引数が一つ増えているのを消去
-                        vector<VarInfo> args = cmd.args;
-			cmd.args.clear();
-			if (args.size() >= 2) {
-				cmd.args.push_back(args[0].value*1000 + args[1].value + 500);
-				cmd.args.insert(cmd.args.end(), args.begin()+2, args.end());
-			}
-		}
-		if (cmd.cmd2 == 0x47) {
-			/**************:
-				0x47 : オブジェクト内容の設定
-					0x3e8: G00 ファイル
-					0x3eb: GAN ファイル
-					0x44c: 矩形領域
-					0x4b0: 文字列
-					0x514: 天候効果
-					0x578: 数字の画像表示
-			*/
-			int base_argc = 0;
-			DeleteObjPic(cmd.args[0].value); // 旧ファイル名のsurfaceを削除
-			GrpObj& g = grpobj[cmd.args[0].value];
-			if (cmd.cmd3 == 0x3e8) { /* ファイル名設定 */
-				g.gtype = GrpObj::FILE;
-				string name = cmd.Str(cmd.args[1]);
-				if (name.find('?') != -1) {
-					name.erase(name.find('?')); // '?' 以降の意味がわからない
-				}
-				g.name = name;
-			} else if (cmd.cmd3 == 0x3eb) { /* ファイル名設定(GAN含む) */
-				g.gtype = GrpObj::GAN;
-				if (cmd.Str(cmd.args[1]) == string("???"))
-					g.name = cmd.Str(cmd.args[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));
-				
-				if (cmd.argc >= 5)
-				    g.SetPos(1, cmd.args[4].value, -cmd.args[5].value);
-				
-				if (g.name.find('?') != -1) {
-			        g.name.erase(g.name.find('?'));
-				    g.gan_name = cmd.Str(cmd.args[2]);
-		        }
-			} else if (cmd.cmd3 == 0x4b0) { // 画像を文字列として指定
-				g.gtype = GrpObj::MOJI;
-				g.print_moji = cmd.Str(cmd.args[1]);
-				g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN)); // 常に表示がデフォルト?
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x578) { // 数値を画像として表示
-				g.gtype = GrpObj::DIGIT;
-				g.name = cmd.Str(cmd.args[1]);
-			}
-			CreateObj(cmd.args[0].value);
-			if (cmd.cmd3 == 0x3e8 || cmd.cmd3 == 0x3eb || cmd.cmd3 == 0x4b0 || cmd.cmd3 == 0x578) {
-				// 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);
-					} 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));
-					}
-					if (cmd.cmd1 == 1)
-						SetObjChangedGroup(cmd.args[0].value);
-				}
-				if (cmd.cmd4 >= 2) { // 座標等も設定
-					g.SetPos(0,cmd.args[3+base_argc].value, cmd.args[4+base_argc].value);
-				}
-				if ( (cmd.cmd3 == 0x3e8 || cmd.cmd3 == 0x3eb) && cmd.cmd4 >= 3) { // pattern 番号も設定
-					g.SetSurfaceNum(cmd.args[5+base_argc].value);
-					base_argc++; // 0x3e8 (FILE) / 0x3eb (GAN) の場合のみこのオプションは存在する
-				}
-				cmd.clear();
-			} else {
-				fprintf(stderr,"CreateObj : cmd.cmd3 = %04x ; not supported!\n",cmd.cmd3);
-			}
-		} else if (cmd.cmd2 == 0x48) {
-			// 画面切り替え後の object (back screen object) 設定
-			if (cmd.cmd3 == 0x3e8) {
-				// cmd.cmd4 == 0 : args = 2, CLANNAD : cg mode
-				// cmd.cmd4 == 1 : args = 3, CLANNAD :  春原回想?のところで画面が黒くなってしまうので、とりあえず。
-				// cmd.cmd4 == 2 : args = 5, KANOGI : Fore Graphics
-				// cmd.cmd4 == 3 : args = 6, KANOGI : CG mode
-				GrpObj& g = bs_obj[cmd.args[0].value];
-				string name = cmd.Str(cmd.args[1]);
-				if (name.find('?') != -1) {
-					name.erase(name.find('?')); // '?' 以降の意味がわからない
-				}
-				g.gtype = GrpObj::FILE;
-				g.name = name;
-				if (cmd.cmd4 >= 1 && cmd.args[2].value == 0)
-					g.attr =  GrpObj::Attribute(g.attr | GrpObj::HIDDEN);
-				else
-					g.attr =  GrpObj::Attribute(g.attr & ~(GrpObj::HIDDEN));
-				if (cmd.cmd4 >= 2)
-					g.SetPos(0,cmd.args[3].value, cmd.args[4].value);
-				if (cmd.cmd4 >= 3)
-					g.SetSurfaceNum(cmd.args[5].value);
-				if (cmd.cmd4 <= 3)
-					cmd.cmd_type = CMD_SAVECMDGRP;
-			}
-		} else if (cmd.cmd2 == 0x49) {
-			if (cmd.cmd3 == 0) {  // アニメーションを強制終了
-				GrpObj& g = grpobj[cmd.args[0].value];
-				if (g.anm == NULL || g.anm->IsEnd()) ;
-				else g.anm->Abort();
-			} else if (cmd.cmd3 == 3) { // アニメーション中か?
-				GrpObj& g = grpobj[cmd.args[0].value];
-				if (g.anm == NULL || g.anm->IsEnd()) {
-					cmd.SetSysvar(0);
-				} else {
-					cmd.SetSysvar(1);
-				}
-			} else if (cmd.cmd3 == 1000) {
-				// アニメーションを途中で停止した状態にする
-				GrpObj& g = grpobj[cmd.args[0].value];
-				if (g.anm == NULL || g.anm->IsEnd()) {
-					// fprintf(stderr,"AnimPause : no animation in %d (%d)\n",cmd.args[0].value, cmd.args[1].value);
-					g.SetSurfaceNum(cmd.args[1].value);
-				} else {
-					g.anm->Abort();
-					g.SetSurfaceNum(cmd.args[1].value);
-				}
-				SetObjChanged(cmd.args[0].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x7d3) { // surface を増加させる画像効果
-				GrpObj& g = grpobj[cmd.args[0].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 == 0xbbd || cmd.cmd3 == 0xbbb || cmd.cmd3 == 0xbb9) { // アニメーション開始
-				GrpObj& g = grpobj[cmd.args[0].value];
-				g.CreateGan(event, cmd.args[1].value);
-				// g.attr = GrpObj::Attribute(g.attr & (~GrpObj::HIDDEN));
-				SetObjChanged(cmd.args[0].value);
-				cmd.clear();
-			}
-		} else if (cmd.cmd2 == 0x51 || cmd.cmd2 == 0x52) {
-			GrpObj& g = (cmd.cmd2 == 0x51) ?
-				grpobj[cmd.args[0].value] :
-				bs_obj[cmd.args[0].value];
-			if (cmd.cmd3 == 0x3e8) { /* 座標設定 */
-				g.SetPos(0,cmd.args[1].value, cmd.args[2].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3e9 || cmd.cmd3 == 0x3ea) { /* x / y 座標のみ設定 */
-				int x0, y0;
-				g.GetPos(0, x0, y0);
-				if (cmd.cmd3 == 0x3e9)
-					g.SetPos(0,cmd.args[1].value, y0);
-				else
-					g.SetPos(0,x0, cmd.args[1].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3eb) { /* alpha */
-				g.SetAlpha(cmd.args[1].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3ec) { /* visible flag */
-				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);
- 				// グループ単位で次の RefreshObj で表示・消去
-				if (cmd.cmd2 == 0x51 && cmd.cmd1 == 1)
-					SetObjChangedGroup(cmd.args[0].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3ee || cmd.cmd3 == 0x7d6) { /* 座標設定その2? */
-					/* 0x7d6 : 画像側の基準座標を args[4,5] に入れているのかもしれない */
-				int index = cmd.args[1].value;
-				int x = cmd.args[2].value;
-				int y = cmd.args[3].value;
-				g.SetPos(index+1, x, y);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3f8) { // 画像を文字列として設定:色の設定
-				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 */
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x3fd) { // centering mode などを設定?
-				if (cmd.args[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));
-					cmd.clear();
-				}
-				g.SetUpdate();
-/* cmd3 == 0x41c : ゲームの進行とともに
-        args:10,105
-        args:10,133
-        args:10,144
-        args:10,144
- と変化
-
-  cmd3 == 0x418 :
-	args: 10, 400 -> 100
-	と、alpha の増加とともに変化
-*/
-/*
-487 / 8047 : unsupported command; 0x23 - cmd 01-51:0419:00[ 2] 
-        81,-40,
-	第二引数の 1/10 がオブジェクトの回転角
-*/
-			} else if (cmd.cmd3 == 0x400) { // 画像を文字列として指定
-				g.print_moji = cmd.Str(cmd.args[1]);
-				g.SetUpdate();
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x401) { // 画像を文字列として設定:文字の大きさなど
-				/* args: size, xspace, yspace, vertical, color, shadow */
-				/*
-        			args:17, 0,0,-1,  0,-1 DT in Tomoyo
-        			args:17, 0,0,-1,200,-1 Save/Load in Tomoyo
-        			args:20, 0,0, 0,255,-1 "──ありがとう…。" (勝平Ed付近)
-        			args:16,-1,0,99,255,-1 "やあ、久しぶり──…。" (同上,Save/Load Menu)
-        			args:26,-1,0,99,  0,255 Kuma in CLANNAD
-				*/
-				g.print_size = cmd.args[1].value;
-				/* 前景色を得る */
-				int cr,cg,cb; char key[1024];
-				sprintf(key, "#COLOR_TABLE.%03d", cmd.args[5].value);
-				if (config->GetParam(key, 3, &cr, &cg, &cb)) { // color not found
-					cr = cg = cb = 0;
-				}
-				g.print_r = cr;
-				g.print_g = cg;
-				g.print_b = cb;
-				g.SetUpdate();
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x408) { // set order
-				int order = cmd.args[1].value;
-				if (cmd.cmd1 == 1) order *= 1000; // order も 1000 倍する必要がある?
-				g.order = order;
-				ZMoveObj(cmd.args[0].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x40a && cmd.cmd4 == 1) { // set surface geometry?
-				// オブジェクトのどの部分を画面に表示するか(クリップ領域)の設定
-				int rx = cmd.args[1].value;
-				int ry = cmd.args[2].value;
-				int x2 = cmd.args[3].value;
-				int y2 = cmd.args[4].value;
-				g.SetClipArea(rx, ry, x2, y2); //TODO: case when cmd.args.size() == 1
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x40d) { // set digit number
-				g.dig_number = cmd.args[1].value;
-				g.SetUpdate();
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x40e) { // set digit option
-				g.dig_digit = cmd.args[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();
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x40f) { /* set surface number */
-				g.SetSurfaceNum(cmd.args[1].value);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x416) { // オブジェクトの拡大率設定
-				int zoom = (cmd.args[1].value + cmd.args[2].value)/2; // x,y 別に設定できるらしい
-				zoom = zoom*256/100;
-				g.SetZoomRotate(zoom, -1);
-				cmd.clear();
-			} else if (cmd.cmd3 == 0x419) { // オブジェクトの回転設定
-				int angle = cmd.args[1].value;
-				angle /= 10;
-				if (angle < 0) {
-					angle %= 360;
-					angle += 360;
-				}
-				angle %= 360;
-				g.SetZoomRotate(-1, angle);
-				cmd.clear();
-			}
-			if (cmd.cmd2 == 0x51 && (g.attr & GrpObj::UPDATE_ALL)) {
-				SetObjChanged(cmd.args[0].value);
-			}
-		} else if (cmd.cmd2 == 0x54) {
-			/* 座標取得 */
-			if (cmd.cmd3 == 0x3e8) {
-				GrpObj& obj = grpobj[cmd.args[0].value];
-				VarInfo arg1 = cmd.args[1];
-				VarInfo arg2 = cmd.args[2];
-				int x0, y0;
-				obj.GetPos(0,x0, y0);
-				cmd.SetFlagvar(arg1, x0);
-				cmd.SetFlagvar(arg2, y0);
-			} else if (cmd.cmd3 == 0x44c) {
-				int w, h;
-				GrpObj& obj = grpobj[cmd.args[0].value];
-				obj.GetSrcGeom(w, h);
-				VarInfo arg1 = cmd.args[1];
-				VarInfo arg2 = cmd.args[2];
-				cmd.SetFlagvar(arg1, w);
-				cmd.SetFlagvar(arg2, h);
-			}
-		}
-		// セーブ用にコマンドを元に戻す
-		if (cmd.args.size() != 0 && (cmd.cmd_type == CMD_SAVECMDGRP || cmd.cmd_type == CMD_SAVECMDGRP_ONCE)) {
-			if (cmd.cmd1 == 1) cmd.args[0].value /= 1000;
-			else if (cmd.cmd1 == 2) {
-				vector<VarInfo> args = cmd.args;
-				int value = args[0].value;
-				cmd.args.clear();
-				args[0].value = value / 1000;
-				cmd.args.push_back(args[0]);
-				args[0].value = value % 1000 - 500;
-				cmd.args.push_back(args[0]);
-				cmd.args.insert(cmd.args.end(), args.begin()+1, args.end());
-			}
+	//TODO: ???
+	if ( (cmd.cmd1 == 1 || cmd.cmd1 == 2) && cmd.cmd2 == 0x51) {
+		GrpObj& g = grpobj[cmd.args[0].value];
+		if (g.attr & GrpObj::UPDATE_ALL) {
+			SetObjChanged(cmd.args[0].value);
 		}
 	}
-#endif
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x1f) { // 本来は grpstack clear らしい
-		RefreshObj();
-		// Princess Bride の中途 Staff roll
-		// このタイミングで描画するのが都合がいいので、
-		//シナリオループを抜けて描画を起動
-		cmd.cmd_type = CMD_WAITFRAMEUPDATE;
-	}
-
-/* XXX : GiGiGi */
-/* 122 :  0x23 - cmd 01-04:0924:00[ 0] : V<sys> にBGM再生モードを返す (0/1)
-** 256 :  0x23 - cmd 01-04:091a:00[ 0] : V<sys> にBGM音量を返す (0-255?)
-** 278 :  0x23 - cmd 01-04:0926:00[ 0] : V<sys> にEff再生モードを返す (0/1)
-** 412 :  0x23 - cmd 01-04:091c:00[ 0] : V<sys> にEff音量を返す (0-255?)
-** 434 :  0x23 - cmd 01-04:0927:00[ 0] : V<sys> にSE 再生モードを返す (0/1)
-** 568 :  0x23 - cmd 01-04:091d:00[ 0] : V<sys> にSE 音量を返す (0-255?)
-
-** 122 :  0x23 - cmd 01-04:08c0:00[ 0] : V<sys> にBGM再生モードを設定 (0/1)
-** 256 :  0x23 - cmd 01-04:08b6:00[ 0] : V<sys> にBGM音量を設定 (0-255?)
-** 278 :  0x23 - cmd 01-04:08c2:00[ 0] : V<sys> にEff再生モードを設定 (0/1)
-** 412 :  0x23 - cmd 01-04:08b8:00[ 0] : V<sys> にEff音量を設定 (0-255?)
-** 434 :  0x23 - cmd 01-04:08c3:00[ 0] : V<sys> にSE 再生モードを設定 (0/1)
-** 568 :  0x23 - cmd 01-04:08b9:00[ 0] : V<sys> にSE 音量を設定 (0-255?)
-*/
-	// 本来は音楽関連のコマンド
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x14) {
-		if (cmd.cmd3 == 0 && cmd.cmd4 == 0) {
-			eprintf("play bgm %s\n",cmd.Str(cmd.args[0]));
-			music->PlayCDROM( (char*)cmd.Str(cmd.args[0]), 10000);
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		}
-		if (cmd.cmd3 == 2 && (cmd.cmd4 == 2 || cmd.cmd4 == 0) ) { /* ??? : ことみシナリオラストの音楽再生 */
-			eprintf("play bgm %s\n",cmd.Str(cmd.args[0]));
-			music->PlayCDROM( (char*)cmd.Str(cmd.args[0]), 1);
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		}
-		if (cmd.cmd3 == 0 && cmd.cmd4 == 2) {
-			eprintf("fade bgm %d? and play bgm %s; %d\n",cmd.args[1].value, cmd.Str(cmd.args[0]), cmd.args[2].value);
-			// music->PlayCDROM( (char*)cmd.Str(cmd.args[0]), 10000, cmd.args[2].value);
-			music->PlayCDROM( (char*)cmd.Str(cmd.args[0]), 10000);
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		}
-		if ( (cmd.cmd3 == 5 || cmd.cmd3 == 0x69) && cmd.cmd4 == 0) {
-			if (cmd.cmd3 == 5) {
-				music->StopCDROM(0);
-				eprintf("stop bgm\n");
-			} else {
-				music->StopCDROM(cmd.args[0].value);
-				eprintf("fade bgm %d\n",cmd.args[0].value);
-			}
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x15) {
-		if ((cmd.cmd3 == 2) || (cmd.cmd3 == 0 && (cmd.cmd4 == 1 || cmd.cmd4 == 2)) || (cmd.cmd3 == 0 && cmd.cmd4 == 0)) {
-			eprintf("play SE %s\n",cmd.Str(cmd.args[0]));
-			if (cmd.cmd3 == 2) {
-				music->PlaySE(cmd.Str(cmd.args[0]),1);
-				cmd.cmd_type = CMD_SAVECMD_ONCE;
-			} else {
-				music->PlaySE(cmd.Str(cmd.args[0]));
-				cmd.clear();
-			}
-		} else if (cmd.cmd3 == 5) {
-			eprintf("Stop SE\n");
-			music->StopSE();
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		} else if (cmd.cmd3 == 0x69) {
-			eprintf("Stop SE with fade %d\n",cmd.args[0].value);
-			music->StopSE(cmd.args[0].value);
-			cmd.cmd_type = CMD_SAVECMD_ONCE;
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 4 && (cmd.cmd3 == 0x4bb || cmd.cmd3 == 0x4bc) ) {
-		// 音楽を待ってみる(絶対に違うが)。本来、04-803 に対応してなにかの終わりをwaitするっぽい(風子/智代Ed付近)
-		// EnableSyscom らしいが、よくわからない (rldev)
-//		if (!music->IsStopSE()) status = WAIT_SE;
-		cmd.clear();
-	}
-
-		// 音楽モードで音量を上げるためのコマンド (SetBgmVolume)
-		// とりあえず未実装
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 4 && cmd.cmd3 >= 0x8b6 && cmd.cmd3 <= 0x8b9) { // Set{Bgm,Koe,PCM,Se}VolMod
-		music->volmod[cmd.cmd3-0x8b6] = cmd.args[0].value;
-		config->SetParam("#VOLMOD", 4, music->volmod[0], music->volmod[1], music->volmod[2], music->volmod[3]);
-		cmd.clear();
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 4 && cmd.cmd3 >= 0x91a && cmd.cmd3 <= 0x91d) { // Get{Bgm,Koe,PCM,Se}VolMod
-		cmd.SetSysvar(music->volmod[cmd.cmd3-0x91a]);
-	}
-
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x16) {
-		if (cmd.cmd3 == 0 && cmd.cmd4 == 0) {
-			eprintf("play SE %d\n",cmd.args[0].value);
-			music->PlaySE(cmd.args[0].value);
-			cmd.clear();
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x17) {
-		if (cmd.cmd3 == 0) {
-			eprintf("play koe %d",cmd.args[0].value);
-			if (cmd.cmd4 == 1) {
-				eprintf(", para? %d",cmd.args[1].value);
-			}
-			eprintf("\n");
-			char buf[1024]; sprintf(buf, "%d",cmd.args[0].value);
-			if ( !(skip_mode & SKIP_TEXT)) music->PlayKoe(buf);
-			cmd.clear();
-		}
-	}
-	if (cmd.cmd1 == 1 && cmd.cmd2 == 0x1a) {
-		if ( (cmd.cmd3 == 0x14 || cmd.cmd3 == 1) && cmd.cmd4 == 0) {
-			const char* str = cmd.Str(cmd.args[0]);
-			int x = cmd.args[1].value;
-			int y = cmd.args[2].value;
-			int x2 = cmd.args[3].value;
-			int y2 = cmd.args[4].value;
-			eprintf("play movie ; name %s pos %d,%d - %d,%d\n",str,x,y,x2,y2);
-			music->PlayMovie(str, x, y, x2, y2,1);
-			status = WAIT_MOVIE;
-			event.RegisterGlobalPressFunc(&Pressed, (void*)this);
-			cmd.clear();
-		}
-	}
-	return;
 }
 
 
--- a/system/system_config.cc
+++ b/system/system_config.cc
@@ -1275,6 +1275,7 @@ const char* TrackName::SETrack(int n) {
 	if (n < 0 || n >= se_deal) return NULL;
 	return se_track[n];
 }
+
 void TrackName::AddSE(int n, char* file) {
 	if (se_deal <= n) ExpandSE(n);
 	if (se_track[n]) delete[] se_track[n];
--- a/window/system.cc
+++ b/window/system.cc
@@ -96,12 +96,12 @@ namespace System {
 	}
 
 	void Main::SetCursor(Surface* s, const Rect& r) {
-		if (instance == 0) return;
+		if (instance == NULL) return;
 		if (instance->cursor) delete instance->cursor;
-		if (s == 0) { // カーソル消去
-			instance->cursor = 0;
+		if (s == NULL) { // カーソル消去
+			instance->cursor = NULL;
 		} else if (s == DEFAULT_MOUSECURSOR) {
-			instance->cursor = 0;
+			instance->cursor = NULL;
 			SDL_ShowCursor(SDL_ENABLE);
 		} else {
 			instance->cursor = new WidMouseCursor(instance->event, instance->root.root, s, r.lx, r.ty, r.width(), r.height());