diff scn2k/scn2k_text.cc @ 56:c7bcc0ec2267

* replaced Grp and Text classes by the TextImpl and GrpImpl ones * splitted scn2k.h into smaller header files * moved some definitions from scn2k_*.cc to the header files * moved opcode implementation to scn2k_*impl.cc
author thib
date Thu, 30 Apr 2009 19:05:09 +0000
parents f1a27ee7e03c
children 6d9146f56ccf
line wrap: on
line diff
--- a/scn2k/scn2k_text.cc
+++ b/scn2k/scn2k_text.cc
@@ -9,7 +9,7 @@ TODO:
 			Start すると文字を描画開始する。クリックで全描画。
 			Flush するとバッファ内の文字をすべて描画する
 			Wait すると全描画後、クリックされるまでカーソルを表示するまで待つ
-		TextImpl 側の状態としては Wait のみを持つ (PREPAREに戻るのを待つ)
+		Text 側の状態としては Wait のみを持つ (PREPAREに戻るのを待つ)
 		ただし、Skip の権利はどっちがもつ?(現状は?)
 
 	GrpObj: NextObj と GrpObj を分離。CreateObj は現状通り、Visible=1 時に行う。
@@ -55,219 +55,24 @@ DONE:
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "window/event.h"
-#include "window/picture.h"
-#include "window/widget.h"
+#include "scn2k_text.h"
+
 #include "system/file.h"
-#include "system/system_config.h"
 #include "scn2k.h"
 
 #include <string>
 using namespace std;
 
-// kanji conv : デバッグ表示用
-void kconv(const unsigned char* src, unsigned char* dest);
-void kconv_rev(const unsigned char* src, unsigned char* dest);
-string kconv(const string& s);
-string kconv_rev(const string& s);
-// render.cc
-void DSurfaceFillA(Surface* src, const Rect& rect, int r, int g, int b, int a); // テキストウィンドウ背景の設定
-void DSurfaceMove(Surface* src_o, const Rect& srcrect, Surface* dst_o, const Rect& dstrect); // コピー
+#include "window/render.h"
 
 /**************************************************************::
 **
-**	TextImpl(interface)
+**	Text(implementation)
 */
-struct TimerAtom {
-	int from;
-	int to;
-	unsigned int start_time;
-	unsigned int total_time;
-};
-
-struct TextWindow {
-/* @@@ : SetWindowColor での surface 再設定に注意 */
-	WidText* wid;
-	bool name_visible;
-	WidLabel* name;
-	PicContainer* name_container;
-	PicBase* face;
-	PicBase* face_pics[8];
-	TextWindow(PicContainer& parent, Event::Container& event, int window_no, void* callback);
-	~TextWindow() {
-		if (name_container != NULL) {
-			delete name_container;
-			name_container = NULL;
-		}
-		int i;
-		for (i=0; i<8; i++) {
-			if (face_pics[i] != NULL) {
-				delete face_pics[i];
-				face_pics[i] = NULL;
-			}
-		}
-		if (wid != NULL) {
-			delete wid;
-			wid = NULL;
-		}
-	}
-	Rect WakuSize(PicContainer& pic, int waku_no);
-	void MakeWaku(PicContainer& pic, Event::Container& event, int waku_no,int window_no, bool* use_btn, void* callback);
-	void show(void) {
-		wid->show();
-		if (name_container && name_visible) name_container->show();
-		if (face) face->show();
-	}
-	void hide(void) {
-		wid->hide();
-		if (name_container) name_container->hide();
-		if (face) face->hide();
-	}
-	void ShowFace(const char* path) {
-		if (!face) return;
-		face->SetSurface( path, 0,0);
-	}
-	void ResetFace(void) {
-		if (!face) return;
-		face->SetSurface( (Surface*)0, 0,0);
-	}
-	void StartText(const TextStream& _stream) {
-		wid->Clear();
-		wid->stream = _stream;
-		if (name_container) {
-			char namestr[1024];
-			namestr[0] = 0;
-			wid->stream.RemoveName(namestr, 1024);
-			if (namestr[0] == 0) {
-				name_container->hide();
-			} else {
-				if (name) {
-					name_container->show_all();
-					name->SetText(namestr);
-				}
-			}
-		}
-		wid->Start();
-	}
-	void SetName(const char* n) {
-		if (name_container && name) {
-			if (n[0]) {
-				name_container->show();
-				name->SetText(n);
-				name_visible = true;
-			} else {
-				name_container->hide();
-				name_visible = false;
-			}
-		}
-	}
-};
-
-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,
-			WAIT_CLICK=3, WAIT_ABORT=4, WAIT_CLICK_MOUSEPOS = 5,
-			WAIT_CLICK_MOUSEPOSEND_L = 6, WAIT_CLICK_MOUSEPOSEND_R = 7,
-			WAIT_SELECT_INBOX = 10, WAIT_SELECT_OUTBOX=11, WAIT_SELECT_VALUE = 12,
-			WAIT_EXTRN_MASK = 64, SAVEMASK = 128, LOADMASK = 256, SKIPMASK = 512,
-			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)
-		} Status;
-		Status status, status_saved, status_mask;
-
-	private:
-		std::string ruby_text;
-		bool ruby_text_flag;
-		unsigned int wait_time;
-		unsigned int old_time;
-		unsigned int base_time;
-		int text_window_number;
-		bool text_parsing;
-		TextStream text_stream;
-		SkipMode skip_mode;
-		int save_selectcount;
-
-		std::map<int, TimerAtom> timer_var;
-		std::vector<WidTextButton*> selects;
-		std::vector<int> sel_backlog_pos;
-		string replace_name[26];
-		string replace_name2[26];
-		PicContainer* sel_widget;
-		PicWidget* backlog_widget;
-
-		vector<BacklogItem>& backlog;
-		BacklogItem& backlog_item;
-		BacklogItem cur_backlog_item;
-		BacklogItem drawn_backlog_item;
-
-		TextWindow* widgets[32];
-		WidTimeCursor* kcursor;
-		Surface* sel_bg1;
-		Surface* sel_bg2;
-		Rect sel_bg_rect;
-
-		VarInfo wait_savedvar[2];
-
-		AyuSysConfig *config;
-
-		Event::Container& event;
-		PicContainer& parent;
-
-		//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);
-};
-
-/**************************************************************::
-**
-**	TextImpl(implementation)
-*/
-TextImpl::TextImpl(Event::Container& _event, PicContainer& _parent, vector<BacklogItem>& parent_backlog, BacklogItem& parent_backlog_item) :
-	text(0),status(TextImpl::NORMAL), status_saved(TextImpl::NORMAL), status_mask(TextImpl::NORMAL), ruby_text_flag(false),
+Text::Text(Event::Container& _event, PicContainer& _parent) :
+	text(0),status(Text::NORMAL), status_saved(Text::NORMAL), status_mask(Text::NORMAL), ruby_text_flag(false),
 	old_time(0), base_time(0), text_window_number(0), text_parsing(false), skip_mode(SKIP_NO), save_selectcount(0), sel_widget(0),
-	backlog_widget(0), backlog(parent_backlog), backlog_item(parent_backlog_item), parent(_parent), event(_event),
+	backlog_widget(0), parent(_parent), event(_event),
 	kcursor(0), sel_bg1(0), sel_bg2(0), sel_bg_rect(0,0,0,0) {
 	config = AyuSysConfig::GetInstance();
 	int i;
@@ -277,35 +82,54 @@ 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, 33, 73, "grpOpenBg", (CmdImpl) &Text::impl_txtClear);
+	RegisterCommand(1, 33, 75, "grpMulti", (CmdImpl) &Text::impl_txtClear);
+	RegisterCommand(1, 33, 76, "grpOpen", (CmdImpl) &Text::impl_txtClear);
 
-	RegisterCommand(1, 23, 0, "koePlay", (CmdImpl) &TextImpl::impl_logKoe);
-	RegisterCommand(1, 23, 8, "koeDoPlay", (CmdImpl) &TextImpl::impl_logKoe);
+	RegisterCommand(1, 23, 0, "koePlay", (CmdImpl) &Text::impl_logKoe);
+	RegisterCommand(1, 23, 8, "koeDoPlay", (CmdImpl) &Text::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);
+	RegisterCommand(0, 3, 151, "msgHide", (CmdImpl) &Text::impl_txtClear);
+	RegisterCommand(0, 3, 17, "pause", (CmdImpl) &Text::impl_pause);
+	RegisterCommand(0, 3, 3, "par", (CmdImpl) &Text::impl_br); //FIXME
+	RegisterCommand(0, 3, 201, "br", (CmdImpl) &Text::impl_br);
+	RegisterCommand(0, 3, 1000, "FaceOpen", (CmdImpl) &Text::impl_FaceOpen);
+	RegisterCommand(0, 3, 1001, "FaceClear", (CmdImpl) &Text::impl_FaceClear);
+	RegisterCommand(0, 3, 120, "__doruby", (CmdImpl) &Text::impl_doRuby); //FIXME: I don't know how it works
+	RegisterCommand(0, 3, 102, "TextWindow", (CmdImpl) &Text::impl_TextWindow);
+	RegisterCommand(0, 3, 103, "FastText", NULL);//FIXME: (CmdImpl) &Text::impl_FastText);
+	RegisterCommand(0, 3, 104, "NormalText", NULL);
+	RegisterCommand(0, 3, 152, "msgClear", (CmdImpl) &Text::impl_msgClear);
+
+	RegisterCommand(0, 2, 1, "select", (CmdImpl) &Text::impl_createSelect);
+	RegisterCommand(0, 2, 3, "select2?", (CmdImpl) &Text::impl_createSelect); //What difference with select?
+
+	RegisterCommand(0, 4, 1000, "ShowBackground", (CmdImpl) &Text::impl_ShowBackground);
+	RegisterCommand(0, 4, 1100, "SetSkipMode", (CmdImpl) &Text::impl_SetSkipMode);
+	RegisterCommand(1, 4, 100, "wait", (CmdImpl) &Text::impl_Wait);
+	RegisterCommand(1, 4, 111, "time", (CmdImpl) &Text::impl_Wait);
+	RegisterCommand(1, 4, 121, "timeEx", (CmdImpl) &Text::impl_Wait);
 }
 
-TextImpl::~TextImpl() {
-	if (sel_widget) delete sel_widget;
+Text::~Text() {
+	if (sel_widget != NULL)
+		delete sel_widget;
 	int i;
 	for (i=0; i<32; i++) {
-		if (widgets[i]) delete widgets[i];
+		if (widgets[i] != NULL)
+			delete widgets[i];
 	}
-	if (backlog_widget) delete backlog_widget;
-	if (sel_bg1) parent.Root().DeleteSurface(sel_bg1);
-	if (sel_bg2) parent.Root().DeleteSurface(sel_bg2);
+	if (backlog_widget != NULL)
+		delete backlog_widget;
+	if (sel_bg1 != NULL)
+		parent.Root().DeleteSurface(sel_bg1);
+	if (sel_bg2 != NULL)
+		parent.Root().DeleteSurface(sel_bg2);
 	event.DeleteGlobalPressFunc(&PressFunc, (void*)this);
 }
 
-bool TextImpl::PressFunc(int x, int y, void* pointer) {
-	TextImpl* t = (TextImpl*)pointer;
+bool Text::PressFunc(int x, int y, void* pointer) {
+	Text* t = (Text*)pointer;
 	if (t->status == WAIT_CLICK) {
 		t->status = WAIT_ABORT;
 	} else if (t->status == WAIT_CLICK_MOUSEPOS) {
@@ -328,8 +152,9 @@ bool TextImpl::PressFunc(int x, int y, v
 	}
 	return true; // event not deleted
 }
-void TextImpl::PressFuncButton(void* pointer, WidButton* from) {
-	TextImpl* t = (TextImpl*)pointer;
+
+void Text::PressFuncButton(void* pointer, WidButton* from) {
+	Text* t = (Text*)pointer;
 	if (t->status != WAIT_SELECT_INBOX && t->status != WAIT_SELECT_OUTBOX) return;
 	vector<WidTextButton*>::iterator it;
 	int sel = 0;
@@ -337,14 +162,13 @@ void TextImpl::PressFuncButton(void* poi
 		if (from == *it) break;
 	}
 	if (it == t->selects.end()) {
-		fprintf(stderr,"TextImpl::PressFuncButton: Cannot find select widget\n");
+		fprintf(stderr,"Text::PressFuncButton: Cannot find select widget\n");
 		return;
 	}
 	t->status = Status(WAIT_SELECT_VALUE + sel);
-	return;
 }
 
-void TextImpl::SetSkipMode(SkipMode _mode) {
+void Text::SetSkipMode(SkipMode _mode) {
 	if ( (skip_mode & SKIP_IN_MENU) && (_mode & SKIP_IN_MENU) == 0) {
 		if (status_mask & BACKLOG_WAIT_MASK) { // backlog mode から復帰
 			status_mask = Status(status_mask & (~(BACKLOG_MASK|BACKLOG_MASK_FWD|BACKLOG_MASK_KOE|BACKLOG_WAIT_MASK)));
@@ -375,67 +199,50 @@ void TextImpl::SetSkipMode(SkipMode _mod
 	skip_mode = _mode;
 }
 
-/* hash_map が欲しい……*/
-#include <map>
-#include <list>
-struct SaveFaceHash { // バックログセーブ時の顔画像管理を行う
-	map<string, int> facetonum;
-	typedef pair<string,int> Node;
-	typedef list<Node> List;
-	List container;
-	int id_max;
-	static int size_max;
-	SaveFaceHash() : id_max(0) {
-	}
-	void NewNode(string face, int face_id) {
-		facetonum[face] = face_id;
-		container.push_front(Node(face, face_id));
-		if (container.size() > size_max) {
-			Node remove = container.back();
-			container.pop_back();
-			facetonum.erase(remove.first);
+void Text::InitWindow(void) {
+	int i;
+	int w;
+	std::string str;
+
+	for (w=0; w<32; w++) {
+		widgets[w] = new TextWindow(parent, event, w, (void*)this);
+		if (widgets[w]->wid == 0) {
+			delete widgets[w];
+			widgets[w] = NULL;
 		}
 	}
-	int Add(string face) {
-		int id; int ret = -1;
-		int i; List::iterator it;
-		if (face.empty()) return -1;
-		if (facetonum.find(face) == facetonum.end()) {
-			id = ++id_max;
-			NewNode(face, id);
-			ret = -1;
-		} else {
-			id = facetonum[face];
-			for (i=0, it=container.begin(); it != container.end(); i++, it++) {
-				if (it->second == id) {
-					ret = i;
-					Node n = *it;
-					container.erase(it);
-					container.push_front(n);
-					break;
-				}
-			}
-		}
-		return ret;
+	SetCursor(0);
+	for (i=0; i<26; i++) {
+		char buf[1024];
+		sprintf(buf, "#NAME.%c", i+'A');
+		const char* s = config->GetParaStr(buf);
+		if (s != NULL) replace_name[i] = s;
 	}
-	string Get(int num) {
-		if (num < 0) return "";
-		List::iterator it = container.begin();
-		for (; it != container.end(); it++) {
-			if (num == 0) return it->first;
-			num--;
-		}
-		return "";
-	}
-};
+	// replace_name2 : 初期設定
+	// 渚、秋生、渚 (CLANNAD)
+	char name_nagisa[3] = {'\x8f', '\x8d', '\0'};
+	char name_akio[5] = {'\x8f', '\x48', '\x90', '\xb6', '\0'};
+	replace_name2[0] = name_nagisa;
+	replace_name2[1] = name_akio;
+	replace_name2[2] = name_nagisa;
+	text = NULL;
+	/* テキスト速度の設定 */
+	int speed, mod, wait, auto_mod;
+	config->GetParam("#INIT_MESSAGE_SPEED", 1, &speed);
+	config->GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &mod);
+	config->GetParam("#MESSAGE_KEY_WAIT_USE", 1, &auto_mod);
+	config->GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &wait);
+	if (mod) speed = -1;
+	if (!auto_mod) wait = -1;
+	SetTextSpeed(speed);
+	SetTextWait(wait);
+}
 
-int SaveFaceHash::size_max = 20;
-
-void TextImpl::Save(string& str, bool rollback_save) {
+void Text::Save(string& str, bool rollback_save) {
 	char buf[1024];
 	str = "\n";
-	str += "[TextImpl Window]\n";
-	sprintf(buf, "TextImplWindow=%d\n",text_window_number);
+	str += "[Text Window]\n";
+	sprintf(buf, "TextWindow=%d\n",text_window_number);
 	str += buf;
 	if (rollback_save) {
 		++save_selectcount;
@@ -458,53 +265,55 @@ void TextImpl::Save(string& str, bool ro
 	if (!rollback_save) {
 		SaveFaceHash face_log;
 		do {
-		int cur_scn = -1; int cur_pos = -1;
-		sprintf(buf, "Backlog.%d=",++cnt);
-		str += buf;
-		for (; it != backlog.end(); it++) {
-			buf[0] = 0; int buflen = 0;
-			if (it->scn == -1) continue;
-			if (it->pos == -1 && it->scn != 0) continue;
+			int cur_scn = -1; int cur_pos = -1;
+			sprintf(buf, "Backlog.%d=",++cnt);
+			str += buf;
+			for (; it != backlog.end(); it++) {
+				buf[0] = 0;
+				int buflen = 0;
+				if (it->scn == -1)
+					continue;
+				if (it->pos == -1 && it->scn != 0)
+					continue;
 
-			buf[buflen++] = ';';
-			if (it->scn == 0 && it->pos == -1) {
-				buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\".", it->text.Save().c_str());
-			} else {
-				if (cur_scn != -1 && cur_scn != it->scn) break; // scn change
-				if (cur_pos != -1 && cur_pos/5000 != it->pos/5000) break; // pos exceeded
-				if (!it->text.container.empty()) {
-					buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\"", it->text.Save().c_str());
+				buf[buflen++] = ';';
+				if (it->scn == 0 && it->pos == -1)
+					buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\".", it->text.Save().c_str());
+				else {
+					if (cur_scn != -1 && cur_scn != it->scn) break; // scn change
+					if (cur_pos != -1 && cur_pos/5000 != it->pos/5000) break; // pos exceeded
+					if (!it->text.container.empty()) {
+						buflen += snprintf(buf+buflen, 1000-buflen, "\"%s\"", it->text.Save().c_str());
+					}
+					if (cur_scn == -1) { // scene change
+						buflen += snprintf(buf+buflen, 1000-buflen, ":%d:%d",it->scn,it->pos);
+						cur_scn = it->scn;
+					}
+					else
+						buflen += snprintf(buf+buflen, 1000-buflen, "%d",it->pos);
+					cur_pos = it->pos;
 				}
-				if (cur_scn == -1) { // scene change
-					buflen += snprintf(buf+buflen, 1000-buflen, ":%d:%d",it->scn,it->pos);
-					cur_scn = it->scn;
-				} else {
-					buflen += snprintf(buf+buflen, 1000-buflen, "%d",it->pos);
+				if (it->koe != -1)
+					buflen += snprintf(buf+buflen, 1000-buflen, ",%d",it->koe);
+				if (!it->face.empty()) {
+					if (it->koe == -1) buf[buflen++] = ',';
+					int face_num = face_log.Add(it->face);
+					if (face_num >= 0 && face_num < 20)
+						buflen += snprintf(buf+buflen, 1000-buflen, ",%c", 'A'+face_num);
+					else
+						buflen += snprintf(buf+buflen, 1000-buflen, ",\"%s\"", it->face.c_str());
 				}
-				cur_pos = it->pos;
+				buf[buflen++] = '\0';
+				if (buflen >= 1000) { // 万が一、バックログ1アイテムの大きさが 1000byte を越えるとき
+					fprintf(stderr,"Fatal : Cannot save backlog crrectly; Please send bug report to the author.\n");
+				} else str += buf;
 			}
-			if (it->koe != -1)
-				buflen += snprintf(buf+buflen, 1000-buflen, ",%d",it->koe);
-			if (!it->face.empty()) {
-				if (it->koe == -1) buf[buflen++] = ',';
-				int face_num = face_log.Add(it->face);
-				if (face_num >= 0 && face_num < 20)
-					buflen += snprintf(buf+buflen, 1000-buflen, ",%c", 'A'+face_num);
-				else
-					buflen += snprintf(buf+buflen, 1000-buflen, ",\"%s\"", it->face.c_str());
-			}
-			buf[buflen++] = '\0';
-			if (buflen >= 1000) { // 万が一、バックログ1アイテムの大きさが 1000byte を越えるとき
-				fprintf(stderr,"Fatal : Cannot save backlog crrectly; Please send bug report to the author.\n");
-			} else str += buf;
-		}
-		str += "\n";
-	} while(it != backlog.end());
+			str += "\n";
+		} while(it != backlog.end());
 	}
-	return;
 }
 
-void TextImpl::Load(const char* str) {
+void Text::Load(const char* str) {
 	if (text) text->wid->Clear();
 	hide();
 	text_window_number = 0;
@@ -530,11 +339,11 @@ void TextImpl::Load(const char* str) {
 	cur_backlog_item.Clear();
 	drawn_backlog_item.Clear();
 
-	str = strstr(str, "\n[TextImpl Window]\n");
+	str = strstr(str, "\n[Text Window]\n");
 
 	if (str) {
 		SaveFaceHash face_log;
-		str += strlen("\n[TextImpl Window]\n");
+		str += strlen("\n[Text Window]\n");
 		const char* strend = str;
 		do {
 			str = strend;
@@ -544,7 +353,7 @@ void TextImpl::Load(const char* str) {
 			else strend++;
 
 			if (str[0] == '[') break; // next section
-			if (strncmp(str, "TextImplWindow=",15) == 0) {
+			if (strncmp(str, "TextWindow=",15) == 0) {
 				str += 15;
 				sscanf(str, "%d", &text_window_number);
 			} else if (strncmp(str, "SaveSelectCount=",16) == 0) {
@@ -632,12 +441,12 @@ void TextImpl::Load(const char* str) {
 	// backlog.clear();
 }
 
-void TextImpl::hide(void) {
+void Text::hide(void) {
 	if (text) text->hide();
 	if (kcursor) kcursor->hide();
 	text = NULL;
 }
-void TextImpl::show(int num) {
+void Text::show(int num) {
 	if (num != text_window_number) {
 		hide();
 		if (num >= 0 && num < 32 && widgets[num] != 0) {
@@ -660,7 +469,7 @@ void TextImpl::show(int num) {
 	}
 }
 
-void TextImpl::DrawBacklog(BacklogItem& item, Cmd& cmd) {
+void Text::DrawBacklog(BacklogItem& item, Cmd& cmd) {
 	show();
 	text->wid->deactivate();
 	status_mask = Status(status_mask | BACKLOG_WAIT_MASK);
@@ -681,7 +490,7 @@ void TextImpl::DrawBacklog(BacklogItem& 
 	if (kcursor) kcursor->hide();
 }
 
-void TextImpl::CreateSelBG(void) {
+void Text::CreateSelBG(void) {
 	if (sel_bg1 != NULL || sel_bg2 != NULL) return;
 
 	const char* btnfile1 = config->GetParaStr("#SELBTN.000.NAME");
@@ -704,8 +513,8 @@ void TextImpl::CreateSelBG(void) {
 	if (sel_bg2) sel_bg_rect.join(Rect(*sel_bg2));
 }
 
-void TextImpl::CreateSelect(Cmd& cmd) {
-	char key[1024];
+void Text::CreateSelect(Cmd& cmd) {
+	char key[23];
 	sprintf(key, "#WINDOW.%03d.SELCOM_USE",text_window_number);
 	int sel_type = 0;
 	if (cmd.cmd3 == 1) config->GetParam(key, 1, &sel_type);
@@ -825,7 +634,7 @@ External_select:
 	}
 }
 
-void TextImpl::AddText(const char* str_o) {
+void Text::AddText(const char* str_o) {
 	char str[10001];
 	if (text == NULL) return;
 	/* まず、replace string を変換 */
@@ -890,67 +699,7 @@ 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) {
+void Text::Exec(Cmd& cmd) {
 	if (cmd.cmd_type == CMD_TEXT) {
 		if (text == NULL) {
 			show();
@@ -979,95 +728,9 @@ void TextImpl::Exec(Cmd& cmd) {
 
 	CommandHandler::Exec(cmd);
 
-	if (cmd.cmd1 == 0 && cmd.cmd2 == 3) {
-		if (cmd.cmd3 == 0x78) { // ルビ関連
-			if (text == NULL) {
-				show();
-			}
-			if (cmd.cmd4 == 1) {
-				ruby_text_flag = true;
-				eprintf("SetRubyTextImpl.");
-				cmd.clear();
-			} else if (cmd.cmd4 == 0) {
-				if (ruby_text.length() == 0) { // ルビを振るテキストがない
-					eprintf("Cannot find ruby text.\n");
-					return;
-				}
-				if (cmd.args.size() != 1) return;
-				char debug1[1024], debug2[1024];
-				kconv( (unsigned char*)ruby_text.c_str(), (unsigned char*)debug1);
-				kconv( (unsigned char*)cmd.Str(cmd.args[0]), (unsigned char*)debug2);
-				eprintf("SetRuby. %s, %s",debug1, debug2);
-				text_stream.AddRuby(ruby_text.c_str(), cmd.Str(cmd.args[0]));
-				cur_backlog_item.DeleteTextPos();
-				cmd.clear();
-			}
-		} else if (cmd.cmd3 == 0x66) { // テキストウィンドウの形
-			if (cmd.cmd4 == 0) {
-				eprintf("set text window <- %d\n",cmd.args[0].value);
-				if (text) show(cmd.args[0].value);
-				else text_window_number = cmd.args[0].value;
-			} else if (cmd.cmd4 == 1) { // default value
-				eprintf("set text window <- default\n");
-				if (text) show(0);
-				else text_window_number = 0;
-			}
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x67) { // テキストウィンドウ表示?
-			show();
-// 表示の際はテキストをクリアしない?
-//			if (text) text->wid->Clear();
-//			text_stream.Clear();
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x68) { // テキスト表示?
-			// 全テキスト表示
-			if (text) {
-				text->StartText(text_stream);
-				text->wid->Flush();
-			}
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x98) { // テキストウィンドウクリア?
-			show();
-			if (text) text->wid->Clear();
-			text_stream.Clear();
-			cmd.clear();
-		}
-	} else if (cmd.cmd1 == 0 && cmd.cmd2 == 2 && (cmd.cmd3 == 1 || cmd.cmd3 == 3) && cmd.cmd4 == 0) {
-		// 選択肢
-		CreateSelect(cmd);
-		//FIXME: Check if it's really clean
-		if (text_parsing) {
-			show();
-			text->StartText(text_stream);
-			if (skip_mode & SKIP_TEXT) text->wid->Flush();
-			else if (kcursor) kcursor->hide();
-			text_parsing = false;
-			text_stream.Clear();
-		}
-		cmd.cmd_type = CMD_ROLLBACKPOINT; /* 選択肢はセーブ位置 / シナリオ巻き戻し位置 */
-		// cmd.clear();
-	} else if (cmd.cmd1 == 0 && cmd.cmd2 == 4) {
-		if (cmd.cmd3 == 0x44c) { // テキストスキップ開始
-			status_mask = Status(SKIPMASK | status_mask);
-			cmd.clear();
-		} else if (cmd.cmd3 == 0x3e8) { // ウィンドウ消去
-			status_mask = Status(CLEARSCR_MASK | status_mask);
-			cmd.clear();
-		}
-	} else if (cmd.cmd1 == 1 && cmd.cmd2 == 0x04) {
+	if (cmd.cmd1 == 1 && cmd.cmd2 == 4) {
 		/* ウェイト関連命令 */
-		if (cmd.cmd3 == 0x64 || cmd.cmd3 == 0x6f || cmd.cmd3 == 0x79) {
-			eprintf("wait %dmsec\n",cmd.args[0].value);
-			if (cmd.cmd3 == 0x64 && text) {
-				/* 0x64 だと文字描画中の待ちに使うことがある */
-				text->StartText(text_stream);
-				text->wid->Flush();
-			}
-			if (cmd.cmd3 == 0x6f || cmd.cmd3 == 0x79) wait_time = base_time + cmd.args[0].value;
-			else wait_time = old_time + cmd.args[0].value;
-			status = WAIT;
-			cmd.cmd_type = CMD_WAITFRAMEUPDATE; // 画像描画に戻る(skip時にテキストが描画されやすくするため)
-		} else if (cmd.cmd3 == 0x65 || cmd.cmd3 == 0x70) {
+		if (cmd.cmd3 == 0x65 || cmd.cmd3 == 0x70) {
 			eprintf("wait %dmsec(click stop)\n",cmd.args[0].value);
 			if (cmd.cmd3 == 0x70) wait_time = base_time + cmd.args[0].value;
 			else wait_time = old_time + cmd.args[0].value;
@@ -1374,7 +1037,7 @@ else fprintf(stderr,"AUTO %d,%d <- wait 
 }
 
 extern int print_blit;
-bool TextImpl::Wait(unsigned int current_time, Cmd& cmd) {
+bool Text::Wait(unsigned int current_time, Cmd& cmd) {
 	if (current_time != Event::Time::NEVER_WAKE) old_time = current_time;
 /*
 if (event.presscount(MOUSE_UP)) {
@@ -1560,40 +1223,40 @@ print_blit^=1;
 
 void clearbtn_press(void* pointer, WidButton* button) {
 	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::CLEARSCR_MASK);
-	return;
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::CLEARSCR_MASK);
 }
-void TextImpl::PressFuncSkip(void* pointer, WidButton* from) {
+
+void Text::PressFuncSkip(void* pointer, WidButton* from) {
 	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::SKIPMASK);
-	return;
-}
-void TextImpl::PressFuncLoad(void* pointer, WidButton* from) {
-	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::LOADMASK);
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::SKIPMASK);
 	return;
 }
-void TextImpl::PressFuncSave(void* pointer, WidButton* from) {
+void Text::PressFuncLoad(void* pointer, WidButton* from) {
 	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::SAVEMASK);
-	return;
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::LOADMASK);
 }
-void TextImpl::PressFuncBacklog(void* pointer, WidButton* from) {
+
+void Text::PressFuncSave(void* pointer, WidButton* from) {
 	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::BACKLOG_MASK);
-	return;
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::SAVEMASK);
 }
-void TextImpl::PressFuncBacklogFwd(void* pointer, WidButton* from) {
+
+void Text::PressFuncBacklog(void* pointer, WidButton* from) {
 	if (pointer == NULL) return;
-	TextImpl* t = (TextImpl*)pointer;
-	t->status_mask = TextImpl::Status(t->status_mask | TextImpl::BACKLOG_MASK_FWD);
-	return;
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::BACKLOG_MASK);
 }
+
+void Text::PressFuncBacklogFwd(void* pointer, WidButton* from) {
+	if (pointer == NULL) return;
+	Text* t = (Text*)pointer;
+	t->status_mask = Text::Status(t->status_mask | Text::BACKLOG_MASK_FWD);
+}
+
 void movebtn_drag(int from_x, int from_y, int x, int y, void* pointer, WidButton* button) {
 	if (pointer == NULL) return;
 	fprintf(stderr,"drag.\n");
@@ -1619,14 +1282,14 @@ static int btnpos[BTNCNT] = { // g00 ファイル内のボタン情報の位置
 };
 
 static WidButton::PressFunc btnpress[BTNCNT] = {
-	0, clearbtn_press, &TextImpl::PressFuncSkip,0,&TextImpl::PressFuncBacklogFwd,&TextImpl::PressFuncBacklog,&TextImpl::PressFuncBacklogFwd,&TextImpl::PressFuncSave,&TextImpl::PressFuncLoad,0
+	0, clearbtn_press, &Text::PressFuncSkip,0,&Text::PressFuncBacklogFwd,&Text::PressFuncBacklog,&Text::PressFuncBacklogFwd,&Text::PressFuncSave,&Text::PressFuncLoad,0
 };
 
 static WidButton::DragFunc btndrag[BTNCNT] = {
 	movebtn_drag, 0,0,0,0, 0,0,0,0, 0
 };
 
-void TextImpl::SetTextSpeed(int speed) {
+void Text::SetTextSpeed(int speed) {
 	// 100 : 10char / sec
 	// 10 : 100char / sec
 	// text widget:
@@ -1638,13 +1301,13 @@ void TextImpl::SetTextSpeed(int speed) {
 		if (widgets[i]) widgets[i]->wid->SetSpeed(speed);
 }
 
-void TextImpl::SetTextWait(int wait) {
+void Text::SetTextWait(int wait) {
 	int i;
 	for (i=0; i<32; i++)
 		if (widgets[i]) widgets[i]->wid->SetWait(wait);
 }
 
-void TextImpl::SetWindowColor(int r, int g, int b, int a, bool is_transparent) {
+void Text::SetWindowColor(int r, int g, int b, int a, bool is_transparent) {
 	char key[1024];
 	int w;
 
@@ -1668,7 +1331,7 @@ void TextImpl::SetWindowColor(int r, int
 	}
 }
 
-void TextImpl::SetCursor(int cursor_no) {
+void Text::SetCursor(int cursor_no) {
 	char key[1024];
 	sprintf(key, "#CURSOR.%03d.NAME", cursor_no);
 	string path = config->GetParaStr(key);
@@ -1773,59 +1436,6 @@ string kconv_rev(const string& s) {
 
 /**************************************************************::
 **
-**	Text
-*/
-Text::Text(Event::Container& _event, PicContainer& _parent) {
-	pimpl = new TextImpl(_event, _parent, backlog, backlog_item);
-}
-
-Text::~Text() {
-	delete pimpl;
-	pimpl = NULL;
-}
-
-void Text::InitWindow(void) {
-	pimpl->InitWindow();
-}
-
-void Text::Exec(Cmd& cmd) {
-	pimpl->Exec(cmd);
-}
-
-bool Text::Wait(unsigned int current_time, Cmd& cmd) {
-	return pimpl->Wait(current_time, cmd);
-}
-
-void Text::SetSkipMode(SkipMode mode) {
-	pimpl->SetSkipMode(mode);
-}
-
-void Text::Save(std::string& str, bool select_save) {
-	pimpl->Save(str, select_save);
-}
-
-void Text::Load(const char* str) {
-	pimpl->Load(str);
-}
-
-void Text::hide(void) {
-	pimpl->hide();
-}
-
-void Text::show(void) {
-	pimpl->show();
-}
-
-void Text::show(int num) {
-	pimpl->show(num);
-}
-
-void Text::DrawBacklog(BacklogItem& item, Cmd& cmd) {
-	pimpl->DrawBacklog(item, cmd);
-}
-
-/**************************************************************::
-**
 **	BacklogItem
 */
 
@@ -1872,6 +1482,12 @@ void BacklogItem::SetSavepos(int p) {
 	pos = p;
 }
 
+
+/**************************************************************::
+**
+**	TextWindow
+*/
+
 Rect TextWindow::WakuSize(PicContainer& pic, int waku_no) {
 	char key[1024];
 	sprintf(key, "#WAKU.%03d.000.NAME", waku_no);
@@ -2075,42 +1691,144 @@ TextWindow::TextWindow(PicContainer& par
 	MakeWaku(*wid->PicNode(), event,waku_no, win_no, use_btn, callback);
 }
 
-void TextImpl::InitWindow(void) {
+TextWindow::~TextWindow()
+{
+	if (name_container != NULL) {
+		delete name_container;
+		name_container = NULL;
+	}
 	int i;
-	int w;
-	std::string str;
+	for (i=0; i<8; i++) {
+		if (face_pics[i] != NULL) {
+			delete face_pics[i];
+			face_pics[i] = NULL;
+		}
+	}
+	if (wid != NULL) {
+		delete wid;
+		wid = NULL;
+	}
+}
+
+void TextWindow::show(void)
+{
+	wid->show();
+	if (name_container != NULL && name_visible)
+		name_container->show();
+	if (face != NULL)
+		face->show();
+}
 
-	for (w=0; w<32; w++) {
-		widgets[w] = new TextWindow(parent, event, w, (void*)this);
-		if (widgets[w]->wid == 0) {
-			delete widgets[w];
-			widgets[w] = NULL;
+void TextWindow::hide(void)
+{
+	wid->hide();
+	if (name_container != NULL)
+		name_container->hide();
+	if (face != NULL)
+		face->hide();
+}
+
+void TextWindow::ShowFace(const char* path)
+{
+	if (face == NULL)
+		return;
+	face->SetSurface(path, 0, 0);
+}
+
+void TextWindow::ResetFace(void) {
+	if (face == NULL)
+		return;
+	face->SetSurface((Surface*) NULL, 0, 0);
+}
+
+void TextWindow::StartText(const TextStream& _stream)
+{
+	wid->Clear();
+	wid->stream = _stream;
+	if (name_container != NULL) {
+		char namestr[1024];
+		namestr[0] = 0;
+		wid->stream.RemoveName(namestr, 1024);
+		if (namestr[0] == 0) {
+			name_container->hide();
+		}
+		else {
+			if (name != NULL) {
+				name_container->show_all();
+				name->SetText(namestr);
+			}
 		}
 	}
-	SetCursor(0);
-	for (i=0; i<26; i++) {
-		char buf[1024];
-		sprintf(buf, "#NAME.%c", i+'A');
-		const char* s = config->GetParaStr(buf);
-		if (s != NULL) replace_name[i] = s;
+	wid->Start();
+}
+
+void TextWindow::SetName(const char* n)
+{
+	if (name_container != NULL && name != NULL) {
+		if (n[0]) {
+			name_container->show();
+			name->SetText(n);
+			name_visible = true;
+		}
+		else {
+			name_container->hide();
+			name_visible = false;
+		}
+	}
+}
+
+/**************************************************************::
+**
+** SaveFaceHash
+*/
+
+void SaveFaceHash::NewNode(string face, int face_id)
+{
+	facetonum[face] = face_id;
+	container.push_front(Node(face, face_id));
+	if (container.size() > size_max) {
+		Node remove = container.back();
+		container.pop_back();
+		facetonum.erase(remove.first);
 	}
-	// replace_name2 : 初期設定
-	// 渚、秋生、渚 (CLANNAD)
-	char name_nagisa[3] = {'\x8f', '\x8d', '\0'};
-	char name_akio[5] = {'\x8f', '\x48', '\x90', '\xb6', '\0'};
-	replace_name2[0] = name_nagisa;
-	replace_name2[1] = name_akio;
-	replace_name2[2] = name_nagisa;
-	text = NULL;
-	/* テキスト速度の設定 */
-	int speed, mod, wait, auto_mod;
-	config->GetParam("#INIT_MESSAGE_SPEED", 1, &speed);
-	config->GetParam("#INIT_MESSAGE_SPEED_MOD", 1, &mod);
-	config->GetParam("#MESSAGE_KEY_WAIT_USE", 1, &auto_mod);
-	config->GetParam("#MESSAGE_KEY_WAIT_TIME", 1, &wait);
-	if (mod) speed = -1;
-	if (!auto_mod) wait = -1;
-	SetTextSpeed(speed);
-	SetTextWait(wait);
-	return;
 }
+
+int SaveFaceHash::Add(string face)
+{
+	int id;
+	int ret = -1;
+	int i;
+	List::iterator it;
+	if (face.empty()) return -1;
+	if (facetonum.find(face) == facetonum.end()) {
+		id = ++id_max;
+		NewNode(face, id);
+		ret = -1;
+	}
+	else {
+		id = facetonum[face];
+		for (i=0, it=container.begin(); it != container.end(); i++, it++) {
+			if (it->second == id) {
+				ret = i;
+				Node n = *it;
+				container.erase(it);
+				container.push_front(n);
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+string SaveFaceHash::Get(int num) {
+	if (num < 0) return "";
+	List::iterator it = container.begin();
+	for (; it != container.end(); it++) {
+		if (num == 0) return it->first;
+		num--;
+	}
+	return "";
+}
+
+int SaveFaceHash::size_max = 20;
+