[Scummvm-cvs-logs] SF.net SVN: scummvm:[33509] scummvm/trunk/engines/parallaction

peres001 at users.sourceforge.net peres001 at users.sourceforge.net
Sat Aug 2 04:24:37 CEST 2008


Revision: 33509
          http://scummvm.svn.sourceforge.net/scummvm/?rev=33509&view=rev
Author:   peres001
Date:     2008-08-02 02:24:36 +0000 (Sat, 02 Aug 2008)

Log Message:
-----------
* Added a preprocessor to deal with the crappy location scripts in BRA.
* Added some comments on how the parser and related code should be changed to make things smoother.

Modified Paths:
--------------
    scummvm/trunk/engines/parallaction/balloons.cpp
    scummvm/trunk/engines/parallaction/parser.cpp
    scummvm/trunk/engines/parallaction/parser.h
    scummvm/trunk/engines/parallaction/parser_br.cpp

Modified: scummvm/trunk/engines/parallaction/balloons.cpp
===================================================================
--- scummvm/trunk/engines/parallaction/balloons.cpp	2008-08-01 21:51:30 UTC (rev 33508)
+++ scummvm/trunk/engines/parallaction/balloons.cpp	2008-08-02 02:24:36 UTC (rev 33509)
@@ -245,6 +245,8 @@
 	_numBalloons = 0;
 }
 
+// TODO: get rid of parseNextToken from here. Use the
+// StringTokenizer instead.
 void BalloonManager_ns::drawWrappedText(Font *font, Graphics::Surface* surf, char *text, byte color, int16 wrapwidth) {
 
 	uint16 lines = 0;
@@ -302,6 +304,8 @@
 
 }
 
+// TODO: get rid of parseNextToken from here. Use the
+// StringTokenizer instead.
 void BalloonManager_ns::getStringExtent(Font *font, char *text, uint16 maxwidth, int16* width, int16* height) {
 
 	uint16 lines = 0;

Modified: scummvm/trunk/engines/parallaction/parser.cpp
===================================================================
--- scummvm/trunk/engines/parallaction/parser.cpp	2008-08-01 21:51:30 UTC (rev 33508)
+++ scummvm/trunk/engines/parallaction/parser.cpp	2008-08-02 02:24:36 UTC (rev 33509)
@@ -28,6 +28,7 @@
 
 namespace Parallaction {
 
+int				_numTokens;
 char			_tokens[20][MAX_TOKEN_LEN];
 
 Script::Script(Common::ReadStream *input, bool disposeSource) : _input(input), _disposeSource(disposeSource), _line(0) {}
@@ -68,6 +69,8 @@
 	for (uint16 i = 0; i < 20; i++)
 		_tokens[i][0] = '\0';
 
+	_numTokens = 0;
+
 	return;
 
 }
@@ -157,6 +160,8 @@
 		i++;
 	}
 
+	_numTokens = i;
+
 	return i;
 }
 
@@ -243,4 +248,184 @@
 }
 
 
+#define BLOCK_BASE	100
+
+class StatementDef {
+protected:
+	Common::String makeLineFromTokens() {
+		Common::String space(" ");
+		Common::String newLine("\n");
+		Common::String text;
+		for (int i = 0; i < _numTokens; i++)
+			text += (Common::String(_tokens[i]) + space);
+		text.deleteLastChar();
+		text += newLine;
+		return text;
+	}
+
+public:
+	uint score;
+	const char*	name;
+
+
+	StatementDef(uint score, const char *name) : score(score), name(name) { }
+
+	virtual Common::String makeLine(Script &script) = 0;;
+
+};
+
+
+class SimpleStatementDef : public StatementDef {
+
+public:
+	SimpleStatementDef(uint score, const char *name) : StatementDef(score, name) { }
+
+	Common::String makeLine(Script &script) {
+		return makeLineFromTokens();
+	}
+
+};
+
+
+
+class BlockStatementDef : public StatementDef {
+
+	const char*	ending1;
+	const char*	ending2;
+
+public:
+	BlockStatementDef(uint score, const char *name, const char *ending1, const char *ending2 = 0) : StatementDef(score, name), ending1(ending1),
+		ending2(ending2) { }
+
+	Common::String makeLine(Script &script) {
+		Common::String text = makeLineFromTokens();
+		bool end;
+		do {
+			script.readLineToken(true);
+			text += makeLineFromTokens();
+			end = !scumm_stricmp(ending1, _tokens[0]) || (ending2 && !scumm_stricmp(ending2, _tokens[0]));
+		} while (!end);
+		return text;
+	}
+
+};
+
+class CommentStatementDef : public StatementDef {
+
+	Common::String parseComment(Script &script) {
+		Common::String result;
+		char buf[129];
+
+		do {
+			script.readLine(buf, 128);
+			buf[strlen(buf)-1] = '\0';
+			if (!scumm_stricmp(buf, "endtext"))
+				break;
+			result += Common::String(buf) + "\n";
+		} while (true);
+		result += "endtext\n";
+		return result;
+	}
+
+public:
+	CommentStatementDef(uint score, const char *name) : StatementDef(score, name) { }
+
+	Common::String makeLine(Script &script) {
+		Common::String text = makeLineFromTokens();
+		text += parseComment(script);
+		return text;
+	}
+
+};
+
+
+
+
+PreProcessor::PreProcessor() {
+	_defs.push_back(new SimpleStatementDef(1, "disk" ));
+	_defs.push_back(new SimpleStatementDef(2, "location" ));
+	_defs.push_back(new SimpleStatementDef(3, "localflags" ));
+	_defs.push_back(new SimpleStatementDef(4, "flags" ));
+	_defs.push_back(new SimpleStatementDef(5, "zeta" ));
+	_defs.push_back(new SimpleStatementDef(6, "music" ));
+	_defs.push_back(new SimpleStatementDef(7, "sound" ));
+	_defs.push_back(new SimpleStatementDef(8, "mask" ));
+	_defs.push_back(new SimpleStatementDef(9, "path" ));
+	_defs.push_back(new SimpleStatementDef(10, "character" ));
+	_defs.push_back(new CommentStatementDef(11, "comment" ));
+	_defs.push_back(new CommentStatementDef(12, "endcomment" ));
+	_defs.push_back(new BlockStatementDef(13,  "ifchar", "endif" ));
+	_defs.push_back(new BlockStatementDef(BLOCK_BASE, "zone", "endanimation", "endzone" ));
+	_defs.push_back(new BlockStatementDef(BLOCK_BASE, "animation", "endanimation", "endzone" ));
+	_defs.push_back(new BlockStatementDef(1000, "commands", "endcommands" ));
+	_defs.push_back(new BlockStatementDef(1001, "acommands", "endcommands" ));
+	_defs.push_back(new BlockStatementDef(1002, "escape", "endcommands" ));
+	_defs.push_back(new SimpleStatementDef(2000, "endlocation"));
+}
+
+PreProcessor::~PreProcessor() {
+	DefList::iterator it = _defs.begin();
+	for (; it != _defs.end(); it++) {
+		delete *it;
+	}
+}
+
+StatementDef* PreProcessor::findDef(const char* name) {
+	DefList::iterator it = _defs.begin();
+	for (; it != _defs.end(); it++) {
+		if (!scumm_stricmp((*it)->name, name)) {
+			return *it;
+		}
+	}
+	return 0;
+}
+
+
+
+uint PreProcessor::getDefScore(StatementDef* def) {
+	if (def->score == BLOCK_BASE) {
+		_numZones++;
+		return (_numZones + BLOCK_BASE);
+	}
+	return def->score;
+}
+
+
+void PreProcessor::preprocessScript(Script &script, StatementList &list) {
+	_numZones = 0;
+	Common::String text;
+	do {
+		script.readLineToken(false);
+		if (_numTokens == 0)
+			break;
+
+		StatementDef *def = findDef(_tokens[0]);
+		assert(def);
+
+		text = def->makeLine(script);
+		int score = getDefScore(def);
+		list.push_back(StatementListNode(score, def->name, text));
+	} while (true);
+	Common::sort(list.begin(), list.end());
+}
+
+
+
+
+void testPreprocessing(Parallaction *vm, const char *filename) {
+	Script *script = vm->_disk->loadLocation(filename);
+	StatementList list;
+	PreProcessor pp;
+	pp.preprocessScript(*script, list);
+	delete script;
+	Common::DumpFile dump;
+	dump.open(filename);
+	StatementList::iterator it = list.begin();
+	for ( ; it != list.end(); it++) {
+		dump.write((*it)._text.c_str(), (*it)._text.size());
+	}
+	dump.close();
+}
+
+
 } // namespace Parallaction

Modified: scummvm/trunk/engines/parallaction/parser.h
===================================================================
--- scummvm/trunk/engines/parallaction/parser.h	2008-08-01 21:51:30 UTC (rev 33508)
+++ scummvm/trunk/engines/parallaction/parser.h	2008-08-02 02:24:36 UTC (rev 33509)
@@ -35,6 +35,7 @@
 char   *parseNextToken(char *s, char *tok, uint16 count, const char *brk, bool ignoreQuotes = false);
 
 #define MAX_TOKEN_LEN	50
+extern int  _numTokens;
 extern char _tokens[][MAX_TOKEN_LEN];
 
 class Script {
@@ -63,6 +64,7 @@
 typedef Common::Array<const Opcode*>	OpcodeSet;
 
 
+
 class Parser {
 
 public:
@@ -95,6 +97,7 @@
 class Parallaction_ns;
 class Parallaction_br;
 
+
 class LocationParser_ns {
 
 protected:
@@ -240,6 +243,23 @@
 
 };
 
+/*
+	TODO: adapt the parser to effectively use the
+	statement list provided by preprocessor as its
+	input, instead of relying on the current Script
+	class.
+
+	This would need a major rewrite of the parsing
+	system!
+
+	parseNextToken could then be sealed into the
+	PreProcessor class forever, together with the
+	_tokens[] and _numTokens stuff, now dangling as
+	global objects.
+
+	NS balloons code should be dealt with before,
+	though.
+*/
 class LocationParser_br : public LocationParser_ns {
 
 protected:
@@ -402,6 +422,117 @@
 
 };
 
+
+/*
+	This simple stream is temporarily needed to hook the
+	preprocessor output to the parser. It will go away
+	when the parser is rewritten to fully exploit the
+	statement list provided by the preprocessor.
+*/
+
+class ReadStringStream : public Common::ReadStream {
+
+	char *_text;
+	uint32	_pos;
+	uint32	_size;
+
+public:
+	ReadStringStream(const Common::String &text) {
+		_text = new char[text.size() + 1];
+		strcpy(_text, text.c_str());
+		_size = text.size();
+		_pos = 0;
+	}
+
+	~ReadStringStream() {
+		delete []_text;
+	}
+
+	uint32 read(void *buffer, uint32 size) {
+		if (_pos + size > _size) {
+			size = _size - _pos;
+		}
+		memcpy(buffer, _text + _pos, size);
+		_pos += size;
+		return size;
+	}
+
+	bool eos() const {
+		return _pos == _size;
+	}
+
+};
+
+
+/*
+	Demented as it may sound, the success of a parsing operation in the
+	original BRA depends on what has been parsed before. The game features
+	an innovative chaos system that involves the parser and the very game
+	engine, in order to inflict the user an unforgettable game experience.
+
+	Ok, now for the serious stuff.
+
+	The PreProcessor implemented here fixes the location scripts before
+	they are fed to the parser. It tries to do so by a preliminary scan
+	of the text file, during which a score is assigned to each statement
+	(more on this later). When the whole file has been analyzed, the
+	statements are sorted according to their score, to create a parsable
+	sequence.
+
+	For parsing, the statements in location scripts can be conveniently
+	divided into 3 groups:
+
+	* location definitions
+	* element definitions
+	* start-up commands
+
+	Since the parsing of element definitions requires location parameters
+	to be set, location definitions should be encountered first in the
+	script. Start-up commands in turn may reference elements, so they can
+	be parsed last. The first goal is to make sure the parser gets these
+	three sets in this order.
+
+	Location definitions must also be presented in a certain sequence,
+	because resource files are not fully self-describing. In short, some
+	critical game data in contained in certain files, that must obviously
+	be read before any other can be analyzed. This is the second goal.
+
+	TODO: some words about actual implementation.
+*/
+
+class StatementDef;
+
+struct StatementListNode {
+	int	_score;
+	Common::String	_name;
+	Common::String	_text;
+
+	StatementListNode(int score, const Common::String &name, const Common::String &text) : _score(score), _name(name), _text(text) { }
+
+	bool operator<(const StatementListNode& node) const {
+		return _score < node._score;
+	}
+};
+typedef Common::List<StatementListNode> StatementList;
+
+
+class PreProcessor {
+	typedef Common::List<StatementDef*> DefList;
+
+	int _numZones;
+	DefList _defs;
+
+	StatementDef* findDef(const char* name);
+	uint getDefScore(StatementDef*);
+
+public:
+	PreProcessor();
+	~PreProcessor();
+	void preprocessScript(Script &script, StatementList &list);
+};
+
+
+
 } // namespace Parallaction
 
 #endif

Modified: scummvm/trunk/engines/parallaction/parser_br.cpp
===================================================================
--- scummvm/trunk/engines/parallaction/parser_br.cpp	2008-08-01 21:51:30 UTC (rev 33508)
+++ scummvm/trunk/engines/parallaction/parser_br.cpp	2008-08-02 02:24:36 UTC (rev 33509)
@@ -25,6 +25,8 @@
 
 
 #include "parallaction/parallaction.h"
+#include "parallaction/preprocessor.h"
+
 #include "parallaction/sound.h"
 
 namespace Parallaction {
@@ -104,6 +106,7 @@
 #define INST_ENDIF		30
 #define INST_STOP		31
 
+
 const char *_zoneTypeNamesRes_br[] = {
 	"examine",
 	"door",
@@ -1168,8 +1171,27 @@
 	INSTRUCTION_PARSER(endscript);
 }
 
+
+/*
+	Ancillary routine to support hooking preprocessor and
+	parser.
+*/
+Common::ReadStream *getStream(StatementList &list) {
+	Common::String text;
+	StatementList::iterator it = list.begin();
+	for ( ; it != list.end(); it++) {
+		text += (*it)._text;
+	}
+	return new ReadStringStream(text);
+}
+
 void LocationParser_br::parse(Script *script) {
 
+	PreProcessor pp;
+	StatementList list;
+	pp.preprocessScript(*script, list);
+	Script *script2 = new Script(getStream(list), true);
+
 	ctxt.numZones = 0;
 	ctxt.bgName = 0;
 	ctxt.maskName = 0;
@@ -1177,7 +1199,7 @@
 	ctxt.characterName = 0;
 	ctxt.info = new BackgroundInfo;
 
-	LocationParser_ns::parse(script);
+	LocationParser_ns::parse(script2);
 
 	_vm->_disk->loadScenery(*ctxt.info, ctxt.bgName, ctxt.maskName, ctxt.pathName);
 	_vm->_gfx->setBackground(kBackgroundLocation, ctxt.info);
@@ -1193,6 +1215,7 @@
 	free(ctxt.pathName);
 	free(ctxt.characterName);
 
+	delete script2;
 }
 
 } // namespace Parallaction


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