[Scummvm-cvs-logs] SF.net SVN: scummvm: [24635] scummvm/trunk

sev at users.sourceforge.net sev at users.sourceforge.net
Mon Nov 6 14:21:04 CET 2006


Revision: 24635
          http://svn.sourceforge.net/scummvm/?rev=24635&view=rev
Author:   sev
Date:     2006-11-06 05:19:12 -0800 (Mon, 06 Nov 2006)

Log Message:
-----------
Predictive input for AGI engine. To Do:
- Multitap
- scummvm.ini-based dictionary path
- speedup dictionary loading

Modified Paths:
--------------
    scummvm/trunk/engines/agi/graphics.cpp
    scummvm/trunk/engines/agi/graphics.h
    scummvm/trunk/engines/agi/keyboard.cpp
    scummvm/trunk/engines/agi/module.mk
    scummvm/trunk/engines/agi/text.h

Added Paths:
-----------
    scummvm/trunk/engines/agi/predictive.cpp
    scummvm/trunk/tools/construct-pred-dict.pl
    scummvm/trunk/tools/extract-words-tok.pl

Modified: scummvm/trunk/engines/agi/graphics.cpp
===================================================================
--- scummvm/trunk/engines/agi/graphics.cpp	2006-11-06 12:02:28 UTC (rev 24634)
+++ scummvm/trunk/engines/agi/graphics.cpp	2006-11-06 13:19:12 UTC (rev 24635)
@@ -282,7 +282,7 @@
  * @param a  set if the button has focus
  * @param p  set if the button is pressed
  */
-void draw_button(int x, int y, const char *s, int a, int p) {
+void draw_button(int x, int y, const char *s, int a, int p, int fgcolor, int bgcolor) {
 	int len = strlen(s);
 	int x1, y1, x2, y2;
 
@@ -292,7 +292,7 @@
 	y2 = y + CHAR_LINES + 2;
 
 	while (*s) {
-		put_text_character(0, x + (!!p), y + (!!p), *s++, a ? 15 : 0, a ? 0 : 15);
+		put_text_character(0, x + (!!p), y + (!!p), *s++, a ? fgcolor : bgcolor, a ? bgcolor : fgcolor);
 		x += CHAR_COLS;
 	}
 

Modified: scummvm/trunk/engines/agi/graphics.h
===================================================================
--- scummvm/trunk/engines/agi/graphics.h	2006-11-06 12:02:28 UTC (rev 24634)
+++ scummvm/trunk/engines/agi/graphics.h	2006-11-06 13:19:12 UTC (rev 24635)
@@ -63,7 +63,7 @@
 void clear_screen(int);
 void clear_console_screen(int);
 void draw_box(int, int, int, int, int, int, int);
-void draw_button(int, int, const char *, int, int);
+void draw_button(int, int, const char *, int, int, int fgcolor = 0, int bgcolor = 0);
 int test_button(int, int, const char *);
 void draw_rectangle(int, int, int, int, int);
 void save_block(int, int, int, int, uint8 *);

Modified: scummvm/trunk/engines/agi/keyboard.cpp
===================================================================
--- scummvm/trunk/engines/agi/keyboard.cpp	2006-11-06 12:02:28 UTC (rev 24634)
+++ scummvm/trunk/engines/agi/keyboard.cpp	2006-11-06 13:19:12 UTC (rev 24635)
@@ -170,6 +170,16 @@
 				break;
 			}
 		}
+
+		if (mouse.y >= game.line_user_input * CHAR_LINES &&
+				mouse.y <= (game.line_user_input + 1) * CHAR_LINES) {
+			if (_text->predictiveDialog()) {
+				strcpy((char *)game.input_buffer, _text->_predictiveResult);
+				handle_keys(KEY_ENTER);
+			}
+			return true;
+		}
+
 		if (!opt.agimouse) {
 			/* Handle mouse button events */
 			if (key == BUTTON_LEFT) {
@@ -201,6 +211,18 @@
 	debugC(3, kDebugLevelInput, "handling key: %02x", key);
 
 	switch (key) {
+	case BUTTON_LEFT:
+		if (mouse.y >= stringdata.y * CHAR_LINES &&
+				mouse.y <= (stringdata.y + 1) * CHAR_LINES) {
+			if (_text->predictiveDialog()) {
+				strcpy(game.strings[stringdata.str], _text->_predictiveResult);
+				new_input_mode(INPUT_NORMAL);
+				print_character(stringdata.x + strlen(game.strings[stringdata.str]) + 1,
+								stringdata.y, ' ', game.color_fg, game.color_bg);
+				return;
+			}
+		}
+		break;
 	case KEY_ENTER:
 		debugC(3, kDebugLevelInput, "KEY_ENTER");
 		game.has_prompt = 0;

Modified: scummvm/trunk/engines/agi/module.mk
===================================================================
--- scummvm/trunk/engines/agi/module.mk	2006-11-06 12:02:28 UTC (rev 24634)
+++ scummvm/trunk/engines/agi/module.mk	2006-11-06 13:19:12 UTC (rev 24635)
@@ -23,6 +23,7 @@
 	op_test.o \
 	patches.o \
 	picture.o \
+	predictive.o \
 	savegame.o \
 	sound.o \
 	sprite.o \

Added: scummvm/trunk/engines/agi/predictive.cpp
===================================================================
--- scummvm/trunk/engines/agi/predictive.cpp	                        (rev 0)
+++ scummvm/trunk/engines/agi/predictive.cpp	2006-11-06 13:19:12 UTC (rev 24635)
@@ -0,0 +1,350 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/graphics.h"
+#include "agi/keyboard.h"
+#include "agi/text.h"
+
+#include "common/func.h"
+
+namespace Agi {
+
+#define kModePre 0
+#define kModeNum 1
+#define kModeAbc 2
+
+bool TextMan::predictiveDialog(void) {
+	int key, active = 0;
+	bool rc = false;
+	int x;
+	int y;
+	int bx[17], by[17];
+	String prefix = "";
+	char temp[MAXWORDLEN + 1];
+
+	const char *buttonStr[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" };
+	const char *buttons[] = {
+		"(1)'-.&",  "(2)abc", "(3)def",
+		"(4)ghi",  "(5)jkl", "(6)mno",
+		"(7)pqrs", "(8)tuv", "(9)wxyz",
+		"next",    "add",
+		"<",
+		"Cancel",  "OK", 
+		"Pre", "(0) ", NULL
+	};
+	const int colors[] = {
+		15, 0, 15, 0, 15, 0,
+		15, 0, 15, 0, 15, 0,
+		15, 0, 15, 0, 15, 0,
+		15, 12, 15, 12,
+		15, 0,
+		15, 0, 15, 0,
+		14, 0, 15, 0, 0, 0
+	};
+	const char *modes[] = { "Pre", "123", "Abc" };
+
+	if (!_dict.size())
+		loadDict();
+
+	draw_window(50, 40, 269, 159);
+	draw_rectangle(62, 54, 249, 66, MSG_BOX_TEXT);
+	flush_block(62, 54, 249, 66);
+
+	print_character(3, 11, game.cursor_char, MSG_BOX_COLOUR, MSG_BOX_TEXT);
+
+	bx[15] = 73; // Zero/space
+	by[15] = 120;
+	bx[9] = 120; // next
+	by[9] = 120;
+	bx[10] = 160; // add
+	by[10] = 120;
+	bx[14] = 200; // Mode
+	by[14] = 120;
+	bx[11] = 252; // Backspace
+	by[11] = 57;
+	bx[12] = 180; // Cancel
+	by[12] = 140;
+	bx[13] = 240; // OK
+	by[13] = 140;
+
+	x = 73;
+	y = 75;
+	for (int i = 0; i < 9; i++) {
+		bx[i] = x;
+		by[i] = y;
+		x += 60;
+		if (i % 3 == 2) {
+			y += 15;
+			x = 73;
+		}
+	}
+
+	/* clear key queue */
+	while (keypress()) {
+		get_key();
+	}
+
+	_wordPosition = 0;
+	_currentCode = "";
+	_currentWord = "";
+	_matchedWord = "";
+	_wordNumber = 0;
+	_nextIsActive = _addIsActive = false;
+
+	int mode = kModePre;
+
+	bool needRefresh = true;
+
+	while (42) {
+		if (needRefresh) {
+			for (int i = 0; buttons[i]; i++) {
+				int color1 = colors[i * 2];
+				int color2 = colors[i * 2 + 1];
+
+				if (i == 9 && !_nextIsActive) { // Next
+					color2 = 7;
+				}
+				if (i == 10 && !_addIsActive) { // Add
+					color2 = 7;
+				}
+				if (i == 14) {
+					draw_button(bx[i], by[i], modes[mode], i == active, 0, color1, color2);
+				} else {
+					draw_button(bx[i], by[i], buttons[i], i == active, 0, color1, color2);
+				}
+			}
+
+			if (_currentWord != "") {
+				temp[MAXWORDLEN] = 0;
+
+				strncpy(temp, prefix.c_str(), MAXWORDLEN);
+				strncat(temp, _currentWord.c_str(), MAXWORDLEN);
+
+				for (int i = prefix.size() + _currentCode.size(); i < MAXWORDLEN; i++)
+					temp[i] = ' ';
+
+				print_text(temp, 0, 8, 7, MAXWORDLEN, 15, 0);
+				flush_block(62, 54, 249, 66);
+			}
+		}
+
+		poll_timer();	/* msdos driver -> does nothing */
+		key = do_poll_keyboard();
+		switch (key) {
+		case KEY_ENTER:
+			rc = true;
+			goto press;
+		case KEY_ESCAPE:
+			rc = false;
+			goto getout;
+		case BUTTON_LEFT:
+			for (int i = 0; buttons[i]; i++) {
+				if (test_button(bx[i], by[i], buttons[i])) {
+					needRefresh = true;
+					active = i;
+
+					if (active == 15 && mode != kModeNum) { // Space
+						strncpy(temp, _currentWord.c_str(), _currentCode.size());
+
+						temp[_currentCode.size()] = 0;
+
+						prefix += temp;
+						prefix += " ";
+						_wordPosition = 0;
+						_currentCode = "";
+					} if (active < 9 || active == 11 || active == 15) { // number or backspace
+						if (active == 11) { // backspace
+							if (_currentCode.size()) {
+								_currentCode.deleteLastChar();
+								_wordPosition--;
+							} else {
+								if (prefix.size())
+									prefix.deleteLastChar();
+							}
+						} else if (active == 15) { // zero
+							_currentCode += buttonStr[9];
+							_wordPosition++;
+						} else {
+							_currentCode += buttonStr[active];
+							_wordPosition++;
+						}
+
+						if (mode == kModeNum) {
+							_currentWord = _currentCode;
+						} else if (mode == kModePre) {
+							if (!matchWord() && _currentCode.size()) {
+								_currentCode.deleteLastChar();
+								_wordPosition--;
+								matchWord();
+							}
+						}
+					} else if (active == 9) { // next
+						if (_nextIsActive) {
+							int wordsNumber = (_matchedWord.size() + 1) / _currentCode.size();
+							int start;
+
+							_wordNumber = (_wordNumber + 1) % wordsNumber;
+
+							start = _wordNumber * (_currentCode.size() + 1);
+
+							strncpy(temp, _matchedWord.c_str() + start, _currentCode.size());
+							temp[_matchedWord.size() + 1] = 0;
+
+							_currentWord = temp;
+						}
+					} else if (active == 10) { // add
+						debug(0, "add");
+					} else if (active == 13) { // Ok
+						rc = true;
+						goto press;
+					} else if (active == 14) { // Mode
+						mode++;
+						if (mode > kModeAbc)
+							mode = kModePre;
+					} else {
+						goto press;
+					}
+				}
+			}
+			break;
+		case 0x09:	/* Tab */
+			debugC(3, kDebugLevelText, "Focus change");
+			active++;
+			active %= ARRAYSIZE(buttons) - 1;
+			needRefresh = true;
+			break;
+		}
+		do_update();
+	}
+
+ press:
+	strncpy(_predictiveResult, prefix.c_str(), 40);
+	strncat(_predictiveResult, _currentWord.c_str(), 40);
+	_predictiveResult[prefix.size() + _currentCode.size() + 1] = 0;
+
+ getout:
+	close_window();
+
+	return rc;
+}
+
+static char *ltrim(char *t) {
+        while (isspace(*t))
+                t++;
+        return t;
+}
+
+static char *rtrim(char *t) {
+        int l = strlen(t) - 1;
+        while (l >= 0 && isspace(t[l]))
+                t[l--] = 0;
+        return t;
+}
+
+#define MAXLINELEN 80
+
+void TextMan::loadDict(void) {
+	Common::File in;
+	char buf[MAXLINELEN];
+
+	in.open("pred.txt");
+
+	while (!in.eos()) {
+		if (!in.readLine(buf, MAXLINELEN))
+			break;
+
+		// Skip leading & trailing whitespaces
+		char *t = rtrim(ltrim(buf));
+		char *k = t;
+		int len = 0;
+		char key[30];
+
+		// Skip empty lines
+		if (*t == 0)
+			continue;
+
+		while (!isspace(*t)) {
+			len++;
+			t++;
+		}
+
+		while (isspace(*t))
+			t++;
+
+		strncpy(key, k, len);
+		key[len] = 0;
+
+		_dict[String(key)] = String(t);
+		_dictKeys.push_back(String(key));
+	}
+
+	Common::sort(_dictKeys.begin(), _dictKeys.end());
+
+	debug(0, "Loaded %d keys", _dict.size());
+}
+
+bool TextMan::matchWord(void) {
+	_addIsActive = false;
+
+	if (!_currentCode.size()) {
+		return false;
+	}
+
+	if (_dict.contains(_currentCode)) {
+		_currentWord = _matchedWord = _dict[_currentCode];
+
+		_nextIsActive = ((_matchedWord.size() + 1) / _currentCode.size() > 1);
+		return true;
+	}
+
+	// Else search first partial match
+	for (uint i = 0; i < _dictKeys.size(); i++) {
+		bool matched = true;
+
+		if (_dictKeys[i].size() < _wordPosition)
+			continue;
+
+		for (uint j = 0; j < _dictKeys[i].size() && j < _wordPosition; j++) {
+			if (_currentCode[j] != _dictKeys[i][j]) {
+				matched = false;
+				break;
+			}
+		}
+		if (matched && _dictKeys[i].size() >= _wordPosition) {
+			_currentWord = _matchedWord = _dict[_dictKeys[i]];
+
+			_nextIsActive = ((_matchedWord.size() + 1) / _currentCode.size() > 1);
+			return true;
+		}
+	}
+
+	_currentWord = _matchedWord = "";
+	_nextIsActive = false;
+	_addIsActive = true;
+
+	return false;
+}
+
+
+
+} // End of namespace Agi


Property changes on: scummvm/trunk/engines/agi/predictive.cpp
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Modified: scummvm/trunk/engines/agi/text.h
===================================================================
--- scummvm/trunk/engines/agi/text.h	2006-11-06 12:02:28 UTC (rev 24634)
+++ scummvm/trunk/engines/agi/text.h	2006-11-06 13:19:12 UTC (rev 24635)
@@ -26,9 +26,14 @@
 #define AGI_TEXT_H
 
 #include "agi/agi.h"
+#include "common/hash-str.h"
 
 namespace Agi {
 
+#define MAXWORDLEN 24
+
+typedef Common::String String;
+
 class TextMan {
 public:
 	int message_box(const char *);
@@ -44,6 +49,7 @@
 	void write_prompt(void);
 	void clear_lines(int, int, int);
 	void flush_lines(int, int);
+	bool predictiveDialog(void);
 
 private:
 	void print_status(const char *message, ...);
@@ -51,6 +57,30 @@
 	void blit_textbox(const char *p, int y, int x, int len);
 	void erase_textbox();
 	char *safe_strcat(char *s, const char *t);
+
+	void loadDict(void);
+
+	bool matchWord(void);
+
+private:
+	typedef Common::HashMap<String, String, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> DictMap;
+
+	DictMap _dict;
+	Common::StringList _dictKeys;
+
+	String _currentCode;
+	String _currentWord;
+	String _matchedWord;
+
+	int _wordNumber;
+
+	int _wordPosition;
+
+	bool _nextIsActive;
+	bool _addIsActive;
+
+public:
+	char _predictiveResult[40];
 };
 
 extern TextMan *_text;

Added: scummvm/trunk/tools/construct-pred-dict.pl
===================================================================
--- scummvm/trunk/tools/construct-pred-dict.pl	                        (rev 0)
+++ scummvm/trunk/tools/construct-pred-dict.pl	2006-11-06 13:19:12 UTC (rev 24635)
@@ -0,0 +1,63 @@
+#!perl
+#
+# ScummVM - Scumm Interpreter
+# Copyright (C) 2006 The ScummVM project
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# $URL$
+# $Id$
+#
+
+# This script constructs dictionary for use with predictive input
+#
+# Feed it with list of words. One word per line
+
+%words = ();
+
+while(<>) {
+  chomp;
+
+  s/[\x7f-\xff]//g; # remove unprintable characters
+
+  next if /^.$/;    # skip one character words
+  next if $_ eq ""; # skip empty words
+
+  $words{$_} = 1;
+}
+
+%list = ();
+
+for $_ (sort keys %words) {
+  $word = $_;
+
+  s/['-.&_!]/1/g;
+  s/[abc]/2/g;
+  s/[def]/3/g;
+  s/[ghi]/4/g;
+  s/[jkl]/5/g;
+  s/[mno]/6/g;
+  s/[pqrs]/7/g;
+  s/[tuv]/8/g;
+  s/[wxyz]/9/g;
+
+  $list{$_} .= "$word ";
+}
+
+for $k (sort keys %list) {
+  chop $list{$k};
+
+  print "$k $list{$k}\n";
+}


Property changes on: scummvm/trunk/tools/construct-pred-dict.pl
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native

Added: scummvm/trunk/tools/extract-words-tok.pl
===================================================================
--- scummvm/trunk/tools/extract-words-tok.pl	                        (rev 0)
+++ scummvm/trunk/tools/extract-words-tok.pl	2006-11-06 13:19:12 UTC (rev 24635)
@@ -0,0 +1,73 @@
+#!perl
+#
+# ScummVM - Scumm Interpreter
+# Copyright (C) 2006 The ScummVM project
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# $URL$
+# $Id$
+#
+
+# This script extracts AGI words.tok file
+#
+# It produces one word per line. Multiword verbs get splitted
+#
+# Typical usage:
+#
+# for i in agigames/*/words.tok
+# do
+#   tools/extract-words-tok.pl "$i"
+# done | tools/construct-pred-dict.pl
+#
+
+local $/;
+local $file = <>;
+
+#$off = ord(substr($file, $i * 2, 1)) * 256 + ord(substr($file, $i * 2 + 1, 1));
+#$offn = ord(substr($file, ($i + 1) * 2, 1)) * 256 + ord(substr($file, ($i + 1) * 2 + 1, 1));
+
+$off = 52;
+
+$word = "";
+$mode = 0;
+
+while ($off < length $file) {
+  $c = (ord(substr($file, $off, 1)));
+  if ($mode == 0) {
+    $word = substr $word, 0, $c;
+    $mode = 1;
+    $off++;
+    next;
+  }
+
+  $r = ($c & 0x7f) ^ 0x7f;
+  $word .= chr($r);
+
+  if ($c & 0x80) {
+    for $w (split ' ', $word) {
+      print "$w\n";
+    }
+    $off += 3;
+    $mode = 0;
+    next;
+  }
+
+  $off++;
+}
+
+for $w (split ' ', $word) {
+  print "$w\n";
+}


Property changes on: scummvm/trunk/tools/extract-words-tok.pl
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Date Rev Author URL Id
Name: svn:eol-style
   + native


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list