Exemple #1
0
Common::String getTextFromAnyVar(const Variable &from) {
	switch (from.varType) {
		case SVT_STRING:
			return from.varData.theString;

		case SVT_FASTARRAY: {
			Common::String builder = "FAST:";
			Common::String builder2 = "";
			Common::String grabText = "";

			for (int i = 0; i < from.varData.fastArray->size; i++) {
				builder2 = builder + " ";
				grabText = getTextFromAnyVar(from.varData.fastArray->fastVariables[i]);
				builder.clear();
				builder = builder2 + grabText;
			}
			return builder;
		}

		case SVT_STACK: {
			Common::String builder = "ARRAY:";
			Common::String builder2 = "";
			Common::String grabText = "";

			VariableStack *stacky = from.varData.theStack->first;

			while (stacky) {
				builder2 = builder + " ";
				grabText = getTextFromAnyVar(stacky->thisVar);
				builder.clear();
				builder = builder2 + grabText;
				stacky = stacky->next;
			}
			return builder;
		}

		case SVT_INT: {
			Common::String buff = Common::String::format("%i", from.varData.intValue);
			return buff;
		}

		case SVT_FILE: {
			return resourceNameFromNum(from.varData.intValue);
		}

		case SVT_OBJTYPE: {
			ObjectType *thisType = g_sludge->_objMan->findObjectType(from.varData.intValue);
			if (thisType)
				return thisType->screenName;
			break;
		}

		default:
			break;
	}

	return typeName[from.varType];
}
Exemple #2
0
void AboutDialog::addLine(const char *str) {
	if (*str == 0) {
		_lines.push_back("");
	} else {
		Common::String format(str, 2);
		str += 2;

		static Common::String asciiStr;
		if (format[0] == 'A') {
			bool useAscii = false;
#ifdef USE_TRANSLATION
			// We could use TransMan.getCurrentCharset() but rather than compare strings
			// it is easier to use TransMan.getCharsetMapping() (non null in case of non
			// ISO-8859-1 mapping)
			useAscii = (TransMan.getCharsetMapping() != NULL);
#endif
			if (useAscii)
				asciiStr = str;
			return;
		}
		StringArray wrappedLines;
		if (!asciiStr.empty()) {
			g_gui.getFont().wordWrapText(asciiStr, _w - 2 * _xOff, wrappedLines);
			asciiStr.clear();
		} else
			g_gui.getFont().wordWrapText(str, _w - 2 * _xOff, wrappedLines);

		for (StringArray::const_iterator i = wrappedLines.begin(); i != wrappedLines.end(); ++i) {
			_lines.push_back(format + *i);
		}
	}
}
Exemple #3
0
void MacText::splitString(Common::String &str) {
	const char *s = str.c_str();

	Common::String tmp;
	bool prevCR = false;

	while (*s) {
		if (*s == '\n' && prevCR) {	// trean \r\n as one
			prevCR = false;

			s++;
			continue;
		}

		if (*s == '\r')
			prevCR = true;

		if (*s == '\r' || *s == '\n') {
			_textMaxWidth = MAX(_font->wordWrapText(tmp, _maxWidth, _text), _textMaxWidth);

			tmp.clear();

			s++;
			continue;
		}

		tmp += *s;
		s++;
	}

	if (tmp.size())
		_textMaxWidth = MAX(_font->wordWrapText(tmp, _maxWidth, _text), _textMaxWidth);
}
Exemple #4
0
	void add(Common::String &line, int &w) {
		if (actualMaxLineWidth < w)
			actualMaxLineWidth = w;

		lines.push_back(line);

		line.clear();
		w = 0;
	}
Exemple #5
0
bool SystemUI::askForSaveGameDescription(int16 slotId, Common::String &newDescription) {
	// Let user enter new description
	bool previousEditState = _text->inputGetEditStatus();
	byte previousEditCursor = _text->inputGetCursorChar();

	_text->drawMessageBox(_textSaveGameEnterDescription, 0, 31, true);

	_text->inputEditOn();

	_text->charPos_Push();
	_text->charAttrib_Push();

	_text->charPos_SetInsideWindow(3, 0);
	_text->charAttrib_Set(15, 0);
	_text->clearBlockInsideWindow(3, 0, 31, 0); // input line is supposed to be black
	_text->inputSetCursorChar('_');

	// figure out the current description of the slot
	_text->stringSet("");
	for (uint16 slotNr = 0; slotNr < _savedGameArray.size(); slotNr++) {
		if (_savedGameArray[slotNr].slotId == slotId) {
			// found slotId
			if (_savedGameArray[slotNr].isValid) {
				// and also valid, so use its description
				_text->stringSet(_savedGameArray[slotNr].description);
			}
		}
	}

	_vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING);
	_text->stringEdit(30); // only allow up to 30 characters

	_text->charAttrib_Pop();
	_text->charPos_Pop();
	_text->inputSetCursorChar(previousEditCursor);
	if (!previousEditState) {
		_text->inputEditOff();
	}

	_text->closeWindow();

	if (!_text->stringWasEntered()) {
		// User cancelled? exit now
		return false;
	}

	// Now verify that the user really wants to do this
	if (!askForSavedGameVerification(_textSaveGameVerify, _textSaveGameVerifyButton1, _textSaveGameVerifyButton2, (char *)_text->_inputString, slotId)) {
		return false;
	}

	newDescription.clear();
	newDescription += (char *)_text->_inputString;
	return true;
}
Exemple #6
0
int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const Common::String &caption, const Common::String &button) {
	GUI::SaveLoadChooser slc(caption, button, saveMode);

	selectedName.clear();

	int idx = slc.runModalWithCurrentTarget();
	if (idx >= 0) {
		selectedName = slc.getResultString();
	}

	return idx;
}
Exemple #7
0
bool Debugger_EoB::cmd_saveOriginal(int argc, const char **argv) {
	if (!_vm->_runFlag) {
		DebugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n");
		return true;
	}

	Common::String dir = ConfMan.get("savepath");
	if (dir == "None")
		dir.clear();

	Common::FSNode nd(dir);
	if (!nd.isDirectory())
		return false;

	if (_vm->game() == GI_EOB1) {
		if (argc == 1) {
			if (_vm->saveAsOriginalSaveFile()) {
				Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA.SAV"));
				if (nf.isReadable())
					DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str());
				else
					DebugPrintf("Failure.\n");
			} else {
				DebugPrintf("Failure.\n");
			}
		} else {
			DebugPrintf("Syntax:   save_original\n          (Saves game in original file format to a file which can be used with the orginal game executable.)\n\n");
		}
		return true;

	} else if (argc == 2) {
		int slot = atoi(argv[1]);
		if (slot < 0 || slot > 5) {
			DebugPrintf("Slot must be between (including) 0 and 5.\n");
		} else if (_vm->saveAsOriginalSaveFile(slot)) {
			Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA%d.SAV", slot));
			if (nf.isReadable())
				DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str());
			else
				DebugPrintf("Failure.\n");
		} else {
			DebugPrintf("Failure.\n");
		}
		return true;
	}

	DebugPrintf("Syntax:   save_original <slot>\n          (Saves game in original file format to a file which can be used with the orginal game executable.\n          A save slot between 0 and 5 must be specified.)\n\n");
	return true;
}
Exemple #8
0
bool Font::getLine(Common::String &s, int maxWidth, Common::String &line, int &width) {
	assert(maxWidth > 0);
	width = 0;
	const char *src = s.c_str();
	char c;

	while ((c = *src) != '\0') {
		if (c == '\r') {
			// End of line, so return calculated line
			line = Common::String(s.c_str(), src);
			s = Common::String(src + 1);
			return false;
		}

		++src;
		width += charWidth(c);
		if (width < maxWidth)
			continue;

		// Reached maximum allowed size
		// If this was the last character of the string, let it go
		if (*src == '\0') {
			line = Common::String(s.c_str(), src);
			s.clear();
			return true;
		}

		// Work backwards to find space at the start of the current word
		// as a point to split the line on
		while (src >= s.c_str() && *src != ' ') {
			width -= charWidth(*src);
			--src;
		}
		if (src < s.c_str())
			error("Could not fit line");

		// Split the line around the space
		line = Common::String(s.c_str(), src);
		s = Common::String(src + 1);
		return false;
	}

	// Return entire string
	line = s;
	s = Common::String();
	return true;
}
Exemple #9
0
// returns true if something has been completed
// completion has to be delete[]-ed then
bool Debugger::tabComplete(const char *input, Common::String &completion) const {
	// very basic tab completion
	// for now it just supports command completions

	// adding completions of command parameters would be nice (but hard) :-)
	// maybe also give a list of possible command completions?
	//   (but this will require changes to console)

	if (strchr(input, ' '))
		return false; // already finished the first word

	const uint inputlen = strlen(input);

	completion.clear();

	CommandsMap::const_iterator i, e = _cmds.end();
	for (i = _cmds.begin(); i != e; ++i) {
		if (i->_key.hasPrefix(input)) {
			uint commandlen = i->_key.size();
			if (commandlen == inputlen) { // perfect match, so no tab completion possible
				return false;
			}
			if (commandlen > inputlen) { // possible match
				// no previous match
				if (completion.empty()) {
					completion = i->_key.c_str() + inputlen;
				} else {
					// take common prefix of previous match and this command
					for (uint j = 0; j < completion.size(); j++) {
						if (inputlen + j >= i->_key.size() ||
								completion[j] != i->_key[inputlen + j]) {
							completion = Common::String(completion.begin(), completion.begin() + j);
							// If there is no unambiguous completion, abort
							if (completion.empty())
								return false;
							break;
						}
					}
				}
			}
		}
	}
	if (completion.empty())
		return false;

	return true;
}
void ResourceSerializer::syncAsString32(Common::String &string) {
	if (isLoading()) {
		string.clear();

		uint32 length = _loadStream->readUint32LE();
		for (uint i = 0; i < length; i++) {
			char c = _loadStream->readByte();
			string += c;
		}

		_bytesSynced += 4 + length;
	} else {
		_saveStream->writeUint32LE(string.size());
		_saveStream->writeString(string);
		_bytesSynced += 4 + string.size();
	}
}
Exemple #11
0
	virtual void calc(const char *text, uint16 maxwidth) {
		setup();

		_lineWidth = 0;
		_line.clear();
		_lines = 0;

		Common::StringTokenizer	tokenizer(text, " ");
		Common::String token;
		Common::String blank(" ");

		uint16 blankWidth = _font->getStringWidth(" ");
		uint16 tokenWidth = 0;

		while (!tokenizer.empty()) {
			token = tokenizer.nextToken();
			token = expand(token);

			if (token == "/") {
				tokenWidth = 0;
				action();
				textNewLine();
			} else {
				// todo: expand '%'
				tokenWidth = _font->getStringWidth(token.c_str());

				if (_lineWidth == 0) {
					textAccum(token, tokenWidth);
				} else {
					if (_lineWidth + blankWidth + tokenWidth <= maxwidth) {
						textAccum(blank, blankWidth);
						textAccum(token, tokenWidth);
					} else {
						action();
						textNewLine();
						textAccum(token, tokenWidth);
					}
				}
			}
		}

		end();
	}
Exemple #12
0
int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const Common::String &caption, const Common::String &button) {
	GUI::SaveLoadChooser slc(caption, button);
	slc.setSaveMode(saveMode);

	selectedName.clear();

	Common::String gameId = ConfMan.get("gameid");

	const EnginePlugin *plugin = 0;
	EngineMan.findGame(gameId, &plugin);

	int idx = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
	if (idx >= 0) {
		selectedName = slc.getResultString();
		slc.close();
	}

	return idx;
}
Exemple #13
0
Common::Archive *ResLoaderInsMalcolm::load(Common::ArchiveMemberPtr memberFile, Common::SeekableReadStream &stream) const {
	Common::List<Common::String> filenames;
	Common::ScopedPtr<PlainArchive> result(new PlainArchive(memberFile));
	if (!result)
		return 0;

	// thanks to eriktorbjorn for this code (a bit modified though)
	stream.seek(3, SEEK_SET);

	// first file is the index table
	uint32 size = stream.readUint32LE();
	Common::String temp;

	for (uint32 i = 0; i < size; ++i) {
		byte c = stream.readByte();

		if (c == '\\') {
			temp.clear();
		} else if (c == 0x0D) {
			// line endings are CRLF
			c = stream.readByte();
			assert(c == 0x0A);
			++i;

			filenames.push_back(temp);
		} else {
			temp += (char)c;
		}
	}

	stream.seek(3, SEEK_SET);

	for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) {
		const uint32 fileSize = stream.readUint32LE();
		const uint32 fileOffset = stream.pos();

		result->addFileEntry(*file, PlainArchive::Entry(fileOffset, fileSize));
		stream.seek(fileSize, SEEK_CUR);
	}

	return result.release();
}
Exemple #14
0
bool SystemUI::askForCommand(Common::String &commandText) {
	// Let user enter the command (this was originally only available for Hercules rendering, we allow it everywhere)
	bool previousEditState = _text->inputGetEditStatus();
	byte previousEditCursor = _text->inputGetCursorChar();

	_text->drawMessageBox(_textEnterCommand, 0, 36, true);

	_text->inputEditOn();

	_text->charPos_Push();
	_text->charAttrib_Push();

	_text->charPos_SetInsideWindow(2, 0);
	_text->charAttrib_Set(15, 0);
	_text->clearBlockInsideWindow(2, 0, 36, 0); // input line is supposed to be black
	_text->inputSetCursorChar('_');

	_text->stringSet(commandText.c_str()); // Set current command text (may be a command recall)

	_vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING);
	_text->stringEdit(35); // only allow up to 35 characters

	_text->charAttrib_Pop();
	_text->charPos_Pop();
	_text->inputSetCursorChar(previousEditCursor);
	if (!previousEditState) {
		_text->inputEditOff();
	}

	_text->closeWindow();

	if (!_text->stringWasEntered()) {
		// User cancelled? exit now
		return false;
	}

	commandText.clear();
	commandText += (char *)_text->_inputString;
	return true;
}
Exemple #15
0
void AnimVideo::printData() {
	Anim::printData();

	debug("smackerFile: %s", _smackerFile.c_str());
	debug("size: x %d, y %d", _width, _height);

	Common::String description;
	for (uint32 i = 0; i < _positions.size(); i++) {
		description += Common::String::format("(x %d, y %d) ", _positions[i].x, _positions[i].y);
	}
	debug("positions: %s", description.c_str());

	description.clear();
	for (uint32 i = 0; i < _sizes.size(); i++) {
		description += Common::String::format("(l %d, t %d, r %d, b %d) ",
				_sizes[i].left, _sizes[i].top, _sizes[i].right, _sizes[i].bottom);
	}
	debug("sizes: %s", description.c_str());

	debug("frameRateOverride): %d", _frameRateOverride);
	debug("preload: %d", _preload);
	debug("loop: %d", _loop);
}
Exemple #16
0
void GfxControls16::kernelTexteditChange(reg_t controlObject, reg_t eventObject) {
	uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor));
	uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
	reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
	Common::String text;
	uint16 textSize, eventType, eventKey = 0, modifiers = 0;
	bool textChanged = false;
	bool textAddChar = false;
	Common::Rect rect;

	if (textReference.isNull())
		error("kEditControl called on object that doesnt have a text reference");
	text = _segMan->getString(textReference);

	uint16 oldCursorPos = cursorPos;

	if (!eventObject.isNull()) {
		textSize = text.size();
		eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));

		switch (eventType) {
		case SCI_EVENT_MOUSE_PRESS:
			// TODO: Implement mouse support for cursor change
			break;
		case SCI_EVENT_KEYBOARD:
			eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message));
			modifiers = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers));
			switch (eventKey) {
			case SCI_KEY_BACKSPACE:
				if (cursorPos > 0) {
					cursorPos--; text.deleteChar(cursorPos);
					textChanged = true;
				}
				break;
			case SCI_KEY_DELETE:
				if (cursorPos < textSize) {
					text.deleteChar(cursorPos);
					textChanged = true;
				}
				break;
			case SCI_KEY_HOME: // HOME
				cursorPos = 0; textChanged = true;
				break;
			case SCI_KEY_END: // END
				cursorPos = textSize; textChanged = true;
				break;
			case SCI_KEY_LEFT: // LEFT
				if (cursorPos > 0) {
					cursorPos--; textChanged = true;
				}
				break;
			case SCI_KEY_RIGHT: // RIGHT
				if (cursorPos + 1 <= textSize) {
					cursorPos++; textChanged = true;
				}
				break;
			case 3:	// returned in SCI1 late and newer when Control - C is pressed
				if (modifiers & SCI_KEYMOD_CTRL) {
					// Control-C erases the whole line
					cursorPos = 0; text.clear();
					textChanged = true;
				}
				break;
			default:
				if ((modifiers & SCI_KEYMOD_CTRL) && eventKey == 99) {
					// Control-C in earlier SCI games (SCI0 - SCI1 middle)
					// Control-C erases the whole line
					cursorPos = 0; text.clear();
					textChanged = true;
				} else if (eventKey > 31 && eventKey < 256 && textSize < maxChars) {
					// insert pressed character
					textAddChar = true;
					textChanged = true;
				}
				break;
			}
			break;
		}
	}

	if (g_sci->getVocabulary() && !textChanged && oldCursorPos != cursorPos) {
		assert(!textAddChar);
		textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos);
	}

	if (textChanged) {
		GuiResourceId oldFontId = _text16->GetFontId();
		GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font));
		rect = g_sci->_gfxCompare->getNSRect(controlObject);

		_text16->SetFont(fontId);
		if (textAddChar) {

			const char *textPtr = text.c_str();

			// We check if we are really able to add the new char
			uint16 textWidth = 0;
			while (*textPtr)
				textWidth += _text16->_font->getCharWidth((byte)*textPtr++);
			textWidth += _text16->_font->getCharWidth(eventKey);

			// Does it fit?
			if (textWidth >= rect.width()) {
				_text16->SetFont(oldFontId);
				return;
			}

			text.insertChar(eventKey, cursorPos++);

			// Note: the following checkAltInput call might make the text
			// too wide to fit, but SSCI fails to check that too.
		}
		if (g_sci->getVocabulary())
			g_sci->getVocabulary()->checkAltInput(text, cursorPos);
		texteditCursorErase();
		_paint16->eraseRect(rect);
		_text16->Box(text.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1);
		_paint16->bitsShow(rect);
		texteditCursorDraw(rect, text.c_str(), cursorPos);
		_text16->SetFont(oldFontId);
		// Write back string
		_segMan->strcpy(textReference, text.c_str());
	} else {
		if (g_system->getMillis() >= _texteditBlinkTime) {
			_paint16->invertRect(_texteditCursorRect);
			_paint16->bitsShow(_texteditCursorRect);
			_texteditCursorVisible = !_texteditCursorVisible;
			texteditSetBlinkTime();
		}
	}

	writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos);
}
Exemple #17
0
void GfxControls32::kernelTexteditChange(reg_t controlObject) {
	SciEvent curEvent;
	uint16 maxChars = 40;	//readSelectorValue(_segMan, controlObject, SELECTOR(max));	// TODO
	reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
	GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font)));
	Common::String text;
	uint16 textSize;
	bool textChanged = false;
	bool textAddChar = false;
	Common::Rect rect;

	if (textReference.isNull())
		error("kEditControl called on object that doesn't have a text reference");
	text = _segMan->getString(textReference);

	// TODO: Finish this
	warning("kEditText ('%s')", text.c_str());
	return;

	uint16 cursorPos = 0;
	//uint16 oldCursorPos = cursorPos;
	bool captureEvents = true;
	EventManager* eventMan = g_sci->getEventManager();

	while (captureEvents) {
		curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);

		if (curEvent.type == SCI_EVENT_NONE) {
			eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
		} else {
			textSize = text.size();

			switch (curEvent.type) {
			case SCI_EVENT_MOUSE_PRESS:
				// TODO: Implement mouse support for cursor change
				break;
			case SCI_EVENT_KEYBOARD:
				switch (curEvent.character) {
				case SCI_KEY_BACKSPACE:
					if (cursorPos > 0) {
						cursorPos--; text.deleteChar(cursorPos);
						textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_DELETE:
					if (cursorPos < textSize) {
						text.deleteChar(cursorPos);
						textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_HOME: // HOME
					cursorPos = 0; textChanged = true;
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_END: // END
					cursorPos = textSize; textChanged = true;
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_LEFT: // LEFT
					if (cursorPos > 0) {
						cursorPos--; textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_RIGHT: // RIGHT
					if (cursorPos + 1 <= textSize) {
						cursorPos++; textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case 3:	// returned in SCI1 late and newer when Control - C is pressed
					if (curEvent.modifiers & SCI_KEYMOD_CTRL) {
						// Control-C erases the whole line
						cursorPos = 0; text.clear();
						textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				case SCI_KEY_UP:
				case SCI_KEY_DOWN:
				case SCI_KEY_ENTER:
				case SCI_KEY_ESC:
				case SCI_KEY_TAB:
				case SCI_KEY_SHIFT_TAB:
					captureEvents = false;
					break;
				default:
					if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') {
						// Control-C in earlier SCI games (SCI0 - SCI1 middle)
						// Control-C erases the whole line
						cursorPos = 0; text.clear();
						textChanged = true;
					} else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) {
						// insert pressed character
						textAddChar = true;
						textChanged = true;
					}
					eventMan->getSciEvent(SCI_EVENT_KEYBOARD);	// consume the event
					break;
				}
				break;
			}
		}

		if (textChanged) {
			rect = g_sci->_gfxCompare->getNSRect(controlObject);

			if (textAddChar) {
				const char *textPtr = text.c_str();

				// We check if we are really able to add the new char
				uint16 textWidth = 0;
				while (*textPtr)
					textWidth += font->getCharWidth((byte)*textPtr++);
				textWidth += font->getCharWidth(curEvent.character);

				// Does it fit?
				if (textWidth >= rect.width()) {
					return;
				}

				text.insertChar(curEvent.character, cursorPos++);

				// Note: the following checkAltInput call might make the text
				// too wide to fit, but SSCI fails to check that too.
			}

			reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
			Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
			//texteditCursorErase();	// TODO: Cursor

			// Write back string
			_segMan->strcpy(textReference, text.c_str());
			// Modify the buffer and show it
			warning("kernelTexteditChange");
#if 0
			_text->createTextBitmap(controlObject, 0, 0, hunkId);

			_text->drawTextBitmap(0, 0, nsRect, controlObject);
			//texteditCursorDraw(rect, text.c_str(), cursorPos);	// TODO: Cursor
			g_system->updateScreen();
#endif
		} else {
			// TODO: Cursor
			/*
			if (g_system->getMillis() >= _texteditBlinkTime) {
				_paint16->invertRect(_texteditCursorRect);
				_paint16->bitsShow(_texteditCursorRect);
				_texteditCursorVisible = !_texteditCursorVisible;
				texteditSetBlinkTime();
			}
			*/
		}

		textAddChar = false;
		textChanged = false;
		g_sci->sleep(10);
	}	// while
}
Exemple #18
0
POSIXSaveFileManager::POSIXSaveFileManager() {
	// Register default savepath.
#if defined(SAMSUNGTV)
	ConfMan.registerDefault("savepath", "/mtd_wiselink/scummvm savegames");
#else
	Common::String savePath;

#if defined(MACOSX)
	const char *home = getenv("HOME");
	if (home && *home && strlen(home) < MAXPATHLEN) {
		savePath = home;
		savePath += "/Documents/ScummVM Savegames";

		ConfMan.registerDefault("savepath", savePath);
	}

#else
	const char *envVar;

	// Previously we placed our default savepath in HOME. If the directory
	// still exists, we will use it for backwards compatability.
	envVar = getenv("HOME");
	if (envVar && *envVar) {
		savePath = envVar;
		savePath += "/.scummvm";

		struct stat sb;
		if (stat(savePath.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) {
			savePath.clear();
		}
	}

	if (savePath.empty()) {
		Common::String prefix;

		// On POSIX systems we follow the XDG Base Directory Specification for
		// where to store files. The version we based our code upon can be found
		// over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html
		envVar = getenv("XDG_DATA_HOME");
		if (!envVar || !*envVar) {
			envVar = getenv("HOME");
			if (envVar && *envVar) {
				prefix = envVar;
				savePath = ".local/share/";
			}
		} else {
			prefix = envVar;
		}

		// Our default save path is '$XDG_DATA_HOME/scummvm/saves'
		savePath += "scummvm/saves";

		if (!Posix::assureDirectoryExists(savePath, prefix.c_str())) {
			savePath.clear();
		} else {
			savePath = prefix + '/' + savePath;
		}
	}

	if (!savePath.empty() && savePath.size() < MAXPATHLEN) {
		ConfMan.registerDefault("savepath", savePath);
	}
#endif

	// The user can override the savepath with the SCUMMVM_SAVEPATH
	// environment variable. This is weaker than a --savepath on the
	// command line, but overrides the default savepath.
	//
	// To ensure that the command line option (if given) has precedence,
	// we only set the value in the transient domain if it is not
	// yet present there.
	if (!ConfMan.hasKey("savepath", Common::ConfigManager::kTransientDomain)) {
		const char *dir = getenv("SCUMMVM_SAVEPATH");
		if (dir && *dir && strlen(dir) < MAXPATHLEN) {
			Common::FSNode saveDir(dir);
			if (!saveDir.exists()) {
				warning("Ignoring non-existent SCUMMVM_SAVEPATH '%s'", dir);
			} else if (!saveDir.isWritable()) {
				warning("Ignoring non-writable SCUMMVM_SAVEPATH '%s'", dir);
			} else {
				ConfMan.set("savepath", dir, Common::ConfigManager::kTransientDomain);
			}
		}
	}
#endif
}
Exemple #19
0
	void textNewLine() {
		_lines++;
		_lineWidth = 0;
		_line.clear();
	}
Exemple #20
0
void GfxControls32::kernelTexteditChange(reg_t controlObject) {
	SciEvent curEvent;
	uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max));
	reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text));
	GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font)));
	Common::String text;
	uint16 textSize;
	bool textChanged = false;
	bool textAddChar = false;
	Common::Rect rect;

	if (textReference.isNull())
		error("kEditControl called on object that doesnt have a text reference");
	text = _segMan->getString(textReference);

	// TODO: Finish this, add a loop etc
	warning("kEditText ('%s')", text.c_str());
	return;

	uint16 cursorPos = 0;
	//uint16 oldCursorPos = cursorPos;

	curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD);
	if (curEvent.type != SCI_EVENT_NONE) {
		textSize = text.size();

		switch (curEvent.type) {
		case SCI_EVENT_MOUSE_PRESS:
			// TODO: Implement mouse support for cursor change
			break;
		case SCI_EVENT_KEYBOARD:
			switch (curEvent.data) {
			case SCI_KEY_BACKSPACE:
				if (cursorPos > 0) {
					cursorPos--; text.deleteChar(cursorPos);
					textChanged = true;
				}
				break;
			case SCI_KEY_DELETE:
				if (cursorPos < textSize) {
					text.deleteChar(cursorPos);
					textChanged = true;
				}
				break;
			case SCI_KEY_HOME: // HOME
				cursorPos = 0; textChanged = true;
				break;
			case SCI_KEY_END: // END
				cursorPos = textSize; textChanged = true;
				break;
			case SCI_KEY_LEFT: // LEFT
				if (cursorPos > 0) {
					cursorPos--; textChanged = true;
				}
				break;
			case SCI_KEY_RIGHT: // RIGHT
				if (cursorPos + 1 <= textSize) {
					cursorPos++; textChanged = true;
				}
				break;
			case 3:	// returned in SCI1 late and newer when Control - C is pressed
				if (curEvent.modifiers & SCI_KEYMOD_CTRL) {
					// Control-C erases the whole line
					cursorPos = 0; text.clear();
					textChanged = true;
				}
				break;
			default:
				if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.data == 99) {
					// Control-C in earlier SCI games (SCI0 - SCI1 middle)
					// Control-C erases the whole line
					cursorPos = 0; text.clear();
					textChanged = true;
				} else if (curEvent.data > 31 && curEvent.data < 256 && textSize < maxChars) {
					// insert pressed character
					textAddChar = true;
					textChanged = true;
				}
				break;
			}
			break;
		}
	}

	if (textChanged) {
		rect = g_sci->_gfxCompare->getNSRect(controlObject);

		if (textAddChar) {
			const char *textPtr = text.c_str();

			// We check if we are really able to add the new char
			uint16 textWidth = 0;
			while (*textPtr)
				textWidth += font->getCharWidth((byte)*textPtr++);
			textWidth += font->getCharWidth(curEvent.data);

			// Does it fit?
			if (textWidth >= rect.width()) {
				return;
			}

			text.insertChar(curEvent.data, cursorPos++);

			// Note: the following checkAltInput call might make the text
			// too wide to fit, but SSCI fails to check that too.
		}
		// TODO: Cursor
		/*
		texteditCursorErase();
		_paint16->eraseRect(rect);
		_text16->Box(text.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1);
		_paint16->bitsShow(rect);
		texteditCursorDraw(rect, text.c_str(), cursorPos);
		*/
		// Write back string
		_segMan->strcpy(textReference, text.c_str());
	} else {
		// TODO: Cursor
		/*
		if (g_system->getMillis() >= _texteditBlinkTime) {
			_paint16->invertRect(_texteditCursorRect);
			_paint16->bitsShow(_texteditCursorRect);
			_texteditCursorVisible = !_texteditCursorVisible;
			texteditSetBlinkTime();
		}
		*/
	}
}
Exemple #21
0
Common::Error GagEngine::StateScript()
{
	Common::Error status(Common::kNoError);

	Common::File file;
	if(file.open(_script, *_archive))
	{
		// simple script parsing: skim through, find section and execute commands
		ParserState parse_state(PS_SECTION_SEARCH);
		Common::String buffer;
		Common::String command_name;

		bool done(false);
		while(!file.eos())
		{
			// add space for multiline command support
			Common::String line = file.readLine() + ' ';
			if(file.err())
			{
				status = Common::Error(Common::kReadingFailed, _script + ", readLine()");
				break;
			}

			bool skip_line(false);
			for(Common::String::const_iterator it = line.begin(); it != line.end(); ++it)
			{
				switch(parse_state)
				{
				case PS_SECTION_SEARCH:
					// section
					if(*it == '[')
					{
						buffer.clear();

						parse_state = PS_SECTION_NAME;
					}
					break;

				case PS_SECTION_NAME:
					// section end
					if(*it == ']')
					{
#ifdef DEBUG_SKIM_SCRIPT
						parse_state = PS_SECTION_BODY;
#else
						if(buffer == _section)
							parse_state = PS_SECTION_BODY;
						else if(buffer == _END_SECTION)
						{
							status = Common::Error(Common::kUnknownError, "[" + _section + "] script section not found");
							skip_line = true;
							done = true;
						}
						else
							parse_state = PS_SECTION_SEARCH;
#endif
					}
					else
						buffer += *it;
					break;

				case PS_SECTION_BODY:
					// section
					if(*it == '[')
					{
#ifdef DEBUG_SKIM_SCRIPT
						buffer.clear();
						parse_state = PS_SECTION_NAME;
#else
						skip_line = true;
						done = true;
#endif
					}
					// comment
					else if(*it == '*')
					{
						skip_line = true;
					}
					//NOTE: invalid syntax
					else if(*it == '-' || *it == '/' || (*it >= 'A' && *it <= 'Z'))
					{
#ifndef DEBUG_SKIM_SCRIPT
						warning("invalid script syntax [file: %s, section: %s, line: \"%s\"], skipped", _script.c_str(), _section.c_str(), line.c_str());
#endif
						skip_line = true;
					}
					// command name
					else if((*it >= 'a' && *it <= 'z'))
					{
						buffer.clear();
						buffer += *it;

						parse_state = PS_COMMAND_NAME;
					}
					break;

				case PS_COMMAND_NAME:
					// command value
					if(*it == '=')
					{
						command_name = buffer;
						buffer.clear();

						parse_state = PS_COMMAND_VALUE;
					}
					else
						buffer += *it;
					break;

				case PS_COMMAND_VALUE:
					// command value end
					if(*it == ';')
					{
						command_name.trim();
						buffer.trim();

#ifndef DEBUG_SKIM_SCRIPT
						debug("%s=%s;", command_name.c_str(), buffer.c_str());
#endif

						Common::HashMap<Common::String, Common::Error (GagEngine::*)(const Common::String &)>::const_iterator f = _commandCallbacks.find(command_name);
						if(f != _commandCallbacks.end())
						{
							status = (this->*f->_value)(buffer);
							if(status.getCode() != Common::kNoError)
							{
								skip_line = true;
								done = true;
							}
						}

						parse_state = PS_SECTION_BODY;
					}
					else
						buffer += *it;
					break;
				}

				if(skip_line)
					break;
			}

			if(done)
				break;
		}
	}
	else
	{
		status = Common::Error(Common::kReadingFailed, _script + ", open()");
	}

	return status;
}
Exemple #22
0
int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const {
	WordWrapper wrapper(lines);
	Common::String line;
	Common::String tmpStr;
	int lineWidth = 0;
	int tmpWidth = 0;

	// The rough idea behind this algorithm is as follows:
	// We accumulate characters into the string tmpStr. Whenever a full word
	// has been gathered together this way, we 'commit' it to the line buffer
	// 'line', i.e. we add tmpStr to the end of line, then clear it. Before
	// we do that, we check whether it would cause 'line' to exceed maxWidth;
	// in that case, we first add line to lines, then reset it.
	//
	// If a newline character is read, then we also add line to lines and clear it.
	//
	// Special care has to be taken to account for 'words' that exceed the width
	// of a line. If we encounter such a word, we have to wrap it over multiple
	// lines.

	uint last = 0;
	for (Common::String::const_iterator x = str.begin(); x != str.end(); ++x) {
		const byte c = *x;
		const int w = getCharWidth(c) + getKerningOffset(last, c);
		last = c;
		const bool wouldExceedWidth = (lineWidth + tmpWidth + w > maxWidth);

		// If this char is a whitespace, then it represents a potential
		// 'wrap point' where wrapping could take place. Everything that
		// came before it can now safely be added to the line, as we know
		// that it will not have to be wrapped.
		if (Common::isSpace(c)) {
			line += tmpStr;
			lineWidth += tmpWidth;

			tmpStr.clear();
			tmpWidth = 0;

			// If we encounter a line break (\n), or if the new space would
			// cause the line to overflow: start a new line
			if (c == '\n' || wouldExceedWidth) {
				wrapper.add(line, lineWidth);
				continue;
			}
		}

		// If the max line width would be exceeded by adding this char,
		// insert a line break.
		if (wouldExceedWidth) {
			// Commit what we have so far, *if* we have anything.
			// If line is empty, then we are looking at a word
			// which exceeds the maximum line width.
			if (lineWidth > 0) {
				wrapper.add(line, lineWidth);
				// Trim left side
				while (tmpStr.size() && Common::isSpace(tmpStr[0])) {
					tmpStr.deleteChar(0);
					// This is not very fast, but it is the simplest way to
					// assure we do not mess something up because of kerning.
					tmpWidth = getStringWidth(tmpStr);
				}
			} else {
				wrapper.add(tmpStr, tmpWidth);
			}
		}

		tmpWidth += w;
		tmpStr += c;
	}

	// If some text is left over, add it as the final line
	line += tmpStr;
	lineWidth += tmpWidth;
	if (lineWidth > 0) {
		wrapper.add(line, lineWidth);
	}
	return wrapper.actualMaxLineWidth;
}
Exemple #23
0
void TextObject::setupText() {
	Common::String msg = LuaBase::instance()->parseMsgText(_textID.c_str(), NULL);
	Common::String message;

	// remove spaces (NULL_TEXT) from the end of the string,
	// while this helps make the string unique it screws up
	// text justification
	// remove char of id 13 from the end of the string,
	int pos = msg.size() - 1;
	while (pos >= 0 && (msg[pos] == ' ' || msg[pos] == 13)) {
		msg.deleteLastChar();
		pos = msg.size() - 1;
	}
	delete[] _lines;
	if (msg.size() == 0) {
		_lines = NULL;
		return;
	}

	if (g_grim->getGameType() == GType_MONKEY4) {
		if (_x == 0)
			_x = 320;
		if (_y == 0)
			_y = 240;
	}

	// format the output message to incorporate line wrapping
	// (if necessary) for the text object
	const int SCREEN_WIDTH = _width ? _width : 640;
	const int SCREEN_MARGIN = 75;

	// If the speaker is too close to the edge of the screen we have to make
	// some room for the subtitles.
	if (_isSpeech){
		if (_x < SCREEN_MARGIN) {
			_x = SCREEN_MARGIN;
		} else if (SCREEN_WIDTH - _x < SCREEN_MARGIN) {
			_x = SCREEN_WIDTH - SCREEN_MARGIN;
		}
	}

	// The maximum width for any line of text is determined by the justification
	// mode. Note that there are no left/right margins -- this is consistent
	// with GrimE.
	int maxWidth = 0;
	if (_justify == CENTER) {
		maxWidth = 2 * MIN(_x, SCREEN_WIDTH - _x);
	} else if (_justify == LJUSTIFY) {
		maxWidth = SCREEN_WIDTH - _x;
	} else if (_justify == RJUSTIFY) {
		maxWidth = _x;
	}

	// We break the message to lines not longer than maxWidth
	Common::String currLine;
	_numberLines = 1;
	int lineWidth = 0;
	int maxLineWidth = 0;
	for (uint i = 0; i < msg.size(); i++) {
		lineWidth += MAX(_font->getCharWidth(msg[i]), _font->getCharDataWidth(msg[i]));
		if (lineWidth > maxWidth) {
			bool wordSplit = false;
			if (currLine.contains(' ')) {
				while (msg[i] != ' ' && i > 0) {
					lineWidth -= MAX(_font->getCharWidth(msg[i]), _font->getCharDataWidth(msg[i]));
					message.deleteLastChar();
					--i;
				}
			} else if (msg[i] != ' ') { // if it is a unique word
				int dashWidth = MAX(_font->getCharWidth('-'), _font->getCharDataWidth('-'));
				while (lineWidth + dashWidth > maxWidth) {
					lineWidth -= MAX(_font->getCharWidth(msg[i]), _font->getCharDataWidth(msg[i]));
					message.deleteLastChar();
					--i;
				}
				message += '-';
				wordSplit = true;
 			}
			message += '\n';
			currLine.clear();
			_numberLines++;

			if (lineWidth > maxLineWidth) {
				maxLineWidth = lineWidth;
			}
			lineWidth = 0;

			if (wordSplit) {
				lineWidth += MAX(_font->getCharWidth(msg[i]), _font->getCharDataWidth(msg[i]));
			} else {
				continue; // don't add the space back
			}
		}

		if (lineWidth > maxLineWidth)
			maxLineWidth = lineWidth;

		message += msg[i];
		currLine += msg[i];
	}

	// If the text object is a speech subtitle, the y parameter is the
	// coordinate of the bottom of the text block (instead of the top). It means
	// that every extra line pushes the previous lines up, instead of being
	// printed further down the screen.
	const int SCREEN_TOP_MARGIN = 16;
	if (_isSpeech) {
		_y -= _numberLines * _font->getHeight();
		if (_y < SCREEN_TOP_MARGIN) {
			_y = SCREEN_TOP_MARGIN;
		}
	}

	_lines = new Common::String[_numberLines];

	for (int j = 0; j < _numberLines; j++) {
		int nextLinePos, cutLen;
		const char *breakPos = strchr(message.c_str(), '\n');
		if (breakPos) {
			nextLinePos = breakPos - message.c_str();
			cutLen = nextLinePos + 1;
		} else {
			nextLinePos = message.size();
			cutLen = nextLinePos;
		}
		Common::String currentLine(message.c_str(), message.c_str() + nextLinePos);
		_lines[j] = currentLine;
		int width = _font->getStringLength(currentLine);
		if (width > _maxLineWidth)
			_maxLineWidth = width;
		for (int count = 0; count < cutLen; count++)
			message.deleteChar(0);
	}
	_elapsedTime = 0;
}
Exemple #24
0
void Menu::createCommandsMenu(MenuItem *menu) {
	Common::String string(_gui->_engine->_world->_commandsMenu);

	Common::String item;

	for (uint i = 0; i < string.size(); i++) {
		while(i < string.size() && string[i] != ';') // Read token
			item += string[i++];

		if (item == "(-") {
			menu->subitems.push_back(new MenuSubItem(NULL, 0));
		} else {
			bool enabled = true;
			int style = 0;
			char shortcut = 0;
			const char *shortPtr = strrchr(item.c_str(), '/');
			if (shortPtr != NULL) {
				if (strlen(shortPtr) >= 2) {
					shortcut = shortPtr[1];
					item.deleteChar(shortPtr - item.c_str());
					item.deleteChar(shortPtr - item.c_str());
				} else {
					error("Unexpected shortcut: '%s', item '%s' in menu '%s'", shortPtr, item.c_str(), string.c_str());
				}
			}

			while (item.size() >= 2 && item[item.size() - 2] == '<') {
				char c = item.lastChar();
				if (c == 'B') {
					style |= kFontStyleBold;
				} else if (c == 'I') {
					style |= kFontStyleItalic;
				} else if (c == 'U') {
					style |= kFontStyleUnderline;
				} else if (c == 'O') {
					style |= kFontStyleOutline;
				} else if (c == 'S') {
					style |= kFontStyleShadow;
				} else if (c == 'C') {
					style |= kFontStyleCondensed;
				} else if (c == 'E') {
					style |= kFontStyleExtended;
				}
				item.deleteLastChar();
				item.deleteLastChar();
			}

			Common::String tmpitem(item);
			tmpitem.trim();
			if (tmpitem[0] == '(') {
				enabled = false;

				for (uint j = 0; j < item.size(); j++)
					if (item[j] == '(') {
						item.deleteChar(j);
						break;
					}
			}

			menu->subitems.push_back(new MenuSubItem(item.c_str(), kMenuActionCommand, style, shortcut, enabled));
		}

		item.clear();
	}
}
Exemple #25
0
bool AgiEngine::predictiveDialog() {
	int key = 0, active = -1, lastactive = 0;
	bool rc = false;
	uint8 x;
	int y;
	int bx[17], by[17];
	Common::String prefix;
	char temp[MAXWORDLEN + 1], repeatcount[MAXWORDLEN];
	AgiBlock tmpwindow;
	bool navigationwithkeys = false;
	bool processkey;

	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" };

	// FIXME: Move this to a more appropriate place.
	if (!_predictiveDictText) {
		loadDict();
		if (!_predictiveDictText)
			return false;
	}
	_predictiveDictActLine = NULL;
	uint8 numMatchingWords = 0;

	_predictiveDialogRunning = true;
	_system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, true);

	memset(repeatcount, 0, sizeof(repeatcount));

	// show the predictive dialog.
	// if another window is already in display, save its state into tmpwindow
	memset(&tmpwindow, 0, sizeof(tmpwindow));
	tmpwindow.active = false;
	if (_game.window.active)
		memcpy(&tmpwindow, &(_game.window), sizeof(AgiBlock));
	drawWindow(50, 40, 269, 159);
	_gfx->drawRectangle(62, 54, 249, 66, MSG_BOX_TEXT);
	_gfx->flushBlock(62, 54, 249, 66);

	bx[15] = 73; // Zero/space
	by[15] = 120;
	bx[9] = 110; // next
	by[9] = 120;
	bx[10] = 172; // 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;
		}
	}

	clearKeyQueue();

	prefix.clear();
	_currentCode.clear();
	_currentWord.clear();
	_wordNumber = 0;

	int mode = kModePre;

	bool needRefresh = true;

	for (;;) {
		if (needRefresh) {
			for (int i = 0; buttons[i]; i++) {
				int color1 = colors[i * 2];
				int color2 = colors[i * 2 + 1];

				if (i == 9 && !((mode != kModeAbc && _predictiveDictActLine && numMatchingWords > 1)
							|| (mode == kModeAbc && _currentWord.size() && _currentWord.lastChar() != ' '))) { // Next
					color2 = 7;
				}

				// needs fixing, or remove it!
				bool _addIsActive = false; // FIXME: word adding is not implemented
				if (i == 10 && !_addIsActive) { // Add
					color2 = 7;
				}
				if (i == 14) {
					_gfx->drawDefaultStyleButton(bx[i], by[i], modes[mode], i == active, 0, color1, color2);
				} else {
					_gfx->drawDefaultStyleButton(bx[i], by[i], buttons[i], i == active, 0, color1, color2);
				}
			}

			Common::strlcpy(temp, prefix.c_str(), sizeof(temp));
			Common::strlcat(temp, _currentWord.c_str(), sizeof(temp));

			for (int i = prefix.size() + _currentCode.size(); i < MAXWORDLEN; i++)
				temp[i] = ' ';
			temp[MAXWORDLEN] = 0;

			printText(temp, 0, 8, 7, MAXWORDLEN, 15, 0);
			_gfx->flushBlock(62, 54, 249, 66);

			if (active >= 0 && !navigationwithkeys) {
				// provide visual feedback only when not navigating with the arrows
				// so that the user can see the active button.
				active = -1;
				needRefresh = true;
			} else
				needRefresh = false;

			_gfx->doUpdate();
		}

		pollTimer();
		key = doPollKeyboard();
		processkey = false;
		switch (key) {
		case KEY_ENTER:
			if (navigationwithkeys) {
				// when the user has utilized arrow key navigation,
				// interpret enter as 'click' on the active button
				active = lastactive;
			} else {
				// else it is a shortcut for 'Ok'
				active = 13;
			}
			processkey = true;
			break;
		case KEY_ESCAPE:
			rc = false;
			goto getout;
		case BUTTON_LEFT:
			navigationwithkeys = false;
			for (int i = 0; buttons[i]; i++) {
				if (_gfx->testButton(bx[i], by[i], buttons[i])) {
					active = i;
					processkey = true;
					break;
				}
			}
			break;
		case KEY_BACKSPACE:
			active = 11;
			processkey = true;
			break;
		case '#':
			active = 9;
			processkey = true;
			break;
		case '*':
			active = 14;
			processkey = true;
			break;
		case 0x09:	// Tab
			navigationwithkeys = true;
			debugC(3, kDebugLevelText, "Focus change");
			lastactive = active = lastactive + 1;
			active %= ARRAYSIZE(buttons) - 1;
			needRefresh = true;
			break;
		case KEY_LEFT:
			navigationwithkeys = true;
			if (lastactive == 0 || lastactive == 3 || lastactive == 6)
				active = lastactive + 2;
			else if (lastactive == 9)
				active = 15;
			else if (lastactive == 11)
				active = 11;
			else if (lastactive == 12)
				active = 13;
			else if (lastactive == 14)
				active = 10;
			else
				active = lastactive - 1;
			lastactive = active;
			needRefresh = true;
			break;
		case KEY_RIGHT:
			navigationwithkeys = true;
			if (lastactive == 2 || lastactive == 5 || lastactive == 8)
				active = lastactive - 2;
			else if (lastactive == 10)
				active = 14;
			else if (lastactive == 11)
				active = 11;
			else if (lastactive == 13)
				active = 12;
			else if (lastactive == 15)
				active = 9;
			else
				active = lastactive + 1;
			lastactive = active;
			needRefresh = true;
			break;
		case KEY_UP:
			navigationwithkeys = true;
			if (lastactive <= 2)
				active = 11;
			else if (lastactive == 9 || lastactive == 10)
				active = lastactive - 2;
			else if (lastactive == 11)
				active = 13;
			else if (lastactive == 14)
				active = 8;
			else if (lastactive == 15)
				active = 6;
			else
				active = lastactive - 3;
			lastactive = active;
			needRefresh = true;
			break;
		case KEY_DOWN:
			navigationwithkeys = true;
			if (lastactive == 6)
				active = 15;
			else if (lastactive == 7 || lastactive == 8)
				active = lastactive + 2;
			else if (lastactive == 11)
				active = 0;
			else if (lastactive == 12 || lastactive == 13)
				active = 11;
			else if (lastactive == 14 || lastactive == 15)
				active = lastactive - 2;
			else
				active = lastactive + 3;
			lastactive = active;
			needRefresh = true;
			break;
		default:
			// handle numeric buttons
			if (key >= '1' && key <= '9') {
				active = key - '1';
				processkey = true;
			} else if (key == '0') {
				active = 15;
				processkey = true;
			}
			break;
		}

		if (processkey) {
			if (active >= 0) {
				needRefresh = true;
				lastactive = active;
				if (active == 15 && mode != kModeNum) { // Space
					// bring MRU word at the top of the list when changing words
					if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0)
						bringWordtoTop(_predictiveDictActLine, _wordNumber);
					strncpy(temp, _currentWord.c_str(), _currentCode.size());
					temp[_currentCode.size()] = 0;
					prefix += temp;
					prefix += " ";
					_currentCode.clear();
					_currentWord.clear();
					numMatchingWords = 0;
					memset(repeatcount, 0, sizeof(repeatcount));
				} else if (active < 9 || active == 11 || active == 15) { // number or backspace
					if (active == 11) { // backspace
						if (_currentCode.size()) {
							repeatcount[_currentCode.size() - 1] = 0;
							_currentCode.deleteLastChar();
						} else {
							if (prefix.size())
								prefix.deleteLastChar();
						}
					} else if (prefix.size() + _currentCode.size() < MAXWORDLEN - 1) { // don't overflow the dialog line
						if (active == 15) { // zero
							_currentCode += buttonStr[9];
						} else {
							_currentCode += buttonStr[active];
						}
					}

					switch (mode) {
					case kModeNum:
						_currentWord = _currentCode;
						break;
					case kModePre:
						if (!matchWord() && _currentCode.size()) {
							_currentCode.deleteLastChar();
							matchWord();
						}
						numMatchingWords = countWordsInString(_predictiveDictActLine);
						break;
					case kModeAbc:
						for (x = 0; x < _currentCode.size(); x++)
							if (_currentCode[x] >= '1')
								temp[x] = buttons[_currentCode[x] - '1'][3 + repeatcount[x]];
						temp[_currentCode.size()] = 0;
						_currentWord = temp;
					}
				} else if (active == 9) { // next
					if (mode == kModePre) {
						if (_predictiveDictActLine && numMatchingWords > 1) {
							_wordNumber = (_wordNumber + 1) % numMatchingWords;
							char tmp[MAXLINELEN];
							strncpy(tmp, _predictiveDictActLine, MAXLINELEN);
							tmp[MAXLINELEN - 1] = 0;
							char *tok = strtok(tmp, " ");
							for (uint8 i = 0; i <= _wordNumber; i++)
								tok = strtok(NULL, " ");
							_currentWord = Common::String(tok, _currentCode.size());
						}
					} else if (mode == kModeAbc){
						x = _currentCode.size();
						if (x) {
							if (_currentCode.lastChar() == '1' || _currentCode.lastChar() == '7' || _currentCode.lastChar() == '9')
								repeatcount[x - 1] = (repeatcount[x - 1] + 1) % 4;
							else
								repeatcount[x - 1] = (repeatcount[x - 1] + 1) % 3;
							if (_currentCode.lastChar() >= '1')
								_currentWord.setChar(buttons[_currentCode[x - 1] - '1'][3 + repeatcount[x - 1]], x-1);
						}
					}
				} else if (active == 10) { // add
					debug(0, "add");
				} else if (active == 13) { // Ok
					// bring MRU word at the top of the list when ok'ed out of the dialog
					if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0)
						bringWordtoTop(_predictiveDictActLine, _wordNumber);
					rc = true;
					goto press;
				} else if (active == 14) { // Mode
					mode++;
					if (mode > kModeAbc)
						mode = kModePre;

					// truncate current input at mode change
					strncpy(temp, _currentWord.c_str(), _currentCode.size());
					temp[_currentCode.size()] = 0;
					prefix += temp;
					_currentCode.clear();
					_currentWord.clear();
					memset(repeatcount, 0, sizeof(repeatcount));
				} else {
					goto press;
				}
			}
		}
	}

 press:
	Common::strlcpy(_predictiveResult, prefix.c_str(), sizeof(_predictiveResult));
	Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult));

 getout:
	// if another window was shown, bring it up again
	if (!tmpwindow.active)
		closeWindow();
	else {
		_gfx->restoreBlock(_game.window.x1, _game.window.y1,
				_game.window.x2, _game.window.y2, _game.window.buffer);

		free(_game.window.buffer);
		memcpy(&(_game.window), &tmpwindow, sizeof(AgiBlock));
		_gfx->doUpdate();
	}

	_system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, false);
	_predictiveDialogRunning = false;

	return rc;
}
Exemple #26
0
void Lingo::func_mci(Common::String &s) {
	Common::String params[5];
	MCITokenType command = kMCITokenNone;

	s.trim();
	s.toLowercase();

	MCITokenType state = kMCITokenNone;
	Common::String token;
	const char *ptr = s.c_str();
	int respos = -1;

	while (*ptr) {
		while (*ptr && *ptr == ' ')
			ptr++;

		token.clear();

		while (*ptr && *ptr != ' ')
			token += *ptr++;

		switch (state) {
		case kMCITokenNone:
			{
				MCIToken *f = MCITokens;

				while (f->token) {
					if (command == f->command && token == f->token)
						break;

					f++;
				}

				if (command == kMCITokenNone) { // We caught command
					command = f->flag; // Switching to processing this command parameters
				} else if (f->flag == kMCITokenNone) { // Unmatched token, storing as filename
					if (!params[0].empty())
						warning("Duplicate filename in MCI command: %s -> %s", params[0].c_str(), token.c_str());
					params[0] = token;
				} else { // This is normal parameter, storing next token to designated position
					if (f->pos > 0) { // This is normal parameter
						state = f->flag;
						respos = f->pos;
					} else { // This is boolean
						params[-f->pos] = "true";
						state = kMCITokenNone;
					}
				}
				break;
			}
		default:
			params[respos] = token;
			state = kMCITokenNone;
			break;
		}
	}

	switch (command) {
	case kMCITokenOpen:
		{
			warning("MCI open file: %s, type: %s, alias: %s buffer: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str());

			Common::File *file = new Common::File();

			if (!file->open(params[0])) {
				warning("Failed to open %s", params[0].c_str());
				delete file;
				return;
			}

			if (params[1] == "waveaudio") {
				Audio::AudioStream *sound = Audio::makeWAVStream(file, DisposeAfterUse::YES);
				_audioAliases[params[2]] = sound;
			} else {
				warning("Unhandled audio type %s", params[2].c_str());
			}
		}
		break;
	case kMCITokenPlay:
		{
			warning("MCI play file: %s, from: %s, to: %s, repeat: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str());

			if (!_audioAliases.contains(params[0])) {
				warning("Unknown alias %s", params[0].c_str());
				return;
			}

			uint32 from = strtol(params[1].c_str(), 0, 10);
			uint32 to = strtol(params[2].c_str(), 0, 10);

			_vm->getSoundManager()->playMCI(*_audioAliases[params[0]], from, to);
		}
		break;
	default:
		warning("Unhandled MCI command: %s", s.c_str());
	}
}
Exemple #27
0
void MacText::splitString(Common::String &str) {
	const char *s = str.c_str();

	Common::String tmp;
	bool prevCR = false;

	if (_textLines.empty()) {
		_textLines.resize(1);
		_textLines[0].chunks.push_back(_defaultFormatting);
	}

	int curLine = _textLines.size() - 1;
	int curChunk = _textLines[curLine].chunks.size() - 1;
	bool nextChunk = false;
	MacFontRun previousFormatting;

	while (*s) {
#if DEBUG
		for (uint i = 0; i < _textLines.size(); i++) {
			debugN(7, "%2d ", i);

			for (uint j = 0; j < _textLines[i].chunks.size(); j++)
				debugN(7, "[%d] \"%s\"", _textLines[i].chunks[j].fontId, _textLines[i].chunks[j].text.c_str());

			debug(7, " --> %c %d, '%s'", (*s > 0x20 ? *s : ' '), (byte)*s, tmp.c_str());
		}
#endif

		if (*s == '\001') {
			s++;
			if (*s == '\001') {
				// Copy it verbatim
			} else {
				if (*s++ != '\015')
					error("MacText: formatting error");

				uint16 fontId = *s++; fontId = (fontId << 8) | *s++;
				byte textSlant = *s++;
				byte unk3f = *s++;
				uint16 fontSize = *s++; fontSize = (fontSize << 8) | *s++;
				uint16 palinfo1 = *s++; palinfo1 = (palinfo1 << 8) | *s++;
				uint16 palinfo2 = *s++; palinfo2 = (palinfo2 << 8) | *s++;
				uint16 palinfo3 = *s++; palinfo3 = (palinfo3 << 8) | *s++;

				debug(8, "******** splitString: fontId: %d, textSlant: %d, unk3: %d, fontSize: %d, p0: %x p1: %x p2: %x",
						fontId, textSlant, unk3f, fontSize, palinfo1, palinfo2, palinfo3);

				previousFormatting = _currentFormatting;
				_currentFormatting.setValues(_wm, fontId, textSlant, unk3f, fontSize, palinfo1, palinfo2, palinfo3);

				if (curLine == 0 && curChunk == 0 && tmp.empty())
					previousFormatting = _currentFormatting;

				nextChunk = true;
			}
		} else if (*s == '\n' && prevCR) {	// trean \r\n as one
			prevCR = false;

			s++;
			continue;
		} else if (*s == '\r') {
			prevCR = true;
		}

		if (*s == '\r' || *s == '\n' || nextChunk) {
			Common::Array<Common::String> text;

			if (!nextChunk)
				previousFormatting = _currentFormatting;

			int w = getLineWidth(curLine, true);

			previousFormatting.getFont()->wordWrapText(tmp, _maxWidth, text, w);
			tmp.clear();

			if (text.size()) {
				for (uint i = 0; i < text.size(); i++) {
					_textLines[curLine].chunks[curChunk].text = text[i];

					if ((text.size() > 1 || !nextChunk) && !(i == text.size() - 1 && nextChunk)) {
						curLine++;
						_textLines.resize(curLine + 1);
						_textLines[curLine].chunks.push_back(previousFormatting);
						curChunk = 0;
					}
				}

				if (nextChunk) {
					curChunk++;

					_textLines[curLine].chunks.push_back(_currentFormatting);
				} else {
					_textLines[curLine].chunks[0] = _currentFormatting;
				}
			} else {
				if (nextChunk) { // No text, replacing formatting
					_textLines[curLine].chunks[curChunk] = _currentFormatting;
				} else { // Otherwise it is an empty line
					curLine++;
					_textLines.resize(curLine + 1);
					_textLines[curLine].chunks.push_back(previousFormatting);
					curChunk = 0;
				}
			}

			if (!nextChunk) // Don't skip next character
				s++;

			nextChunk = false;
			continue;
		}

		tmp += *s;
		s++;
	}

	if (tmp.size()) {
		Common::Array<Common::String> text;
		int w = getLineWidth(curLine, true);

		_currentFormatting.getFont()->wordWrapText(tmp, _maxWidth, text, w);

		_textLines[curLine].chunks[curChunk].text = text[0];

		if (text.size() > 1) {
			for (uint i = 1; i < text.size(); i++) {
				curLine++;
				_textLines.resize(curLine + 1);
				_textLines[curLine].chunks.push_back(_currentFormatting);
				_textLines[curLine].chunks[0].text = text[i];
			}
		}
	}
}
Exemple #28
0
void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) {
	debug(0, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2);
	Resources *res = Resources::instance();
	int n = 0;
	Common::String message;
	byte color = color1;

	if (animation1 != 0) {
		SceneEvent e(SceneEvent::kPlayAnimation);
		e.animation = animation1;
		e.slot = 0xc0 | slot1; //looped, paused
		scene->push(e);
	}

	if (animation2 != 0) {
		SceneEvent e(SceneEvent::kPlayAnimation);
		e.animation = animation2;
		e.slot = 0xc0 | slot2; //looped, paused
		scene->push(e);
	}

	while (n < 4) {
		byte c = res->eseg.get_byte(addr++);
		//debug(0, "%02x: %c", c, c > 0x20? c: '.');

		switch (c) {
		case 0:
			++n;
			switch (n) {
			case 1:
				//debug(0, "new line\n");
				if (!message.empty())
					message += '\n';
				break;
			case 2:
				//debug(0, "displaymessage %s", message.c_str());
				if (color == color2) {
					//pause animation in other slot
					{
						SceneEvent e(SceneEvent::kPauseAnimation);
						e.slot = 0x80 | slot1;
						scene->push(e);
					}
					{
						SceneEvent e(SceneEvent::kPlayAnimation);
						e.animation = animation2;
						e.slot = 0x80 | slot2;
						scene->push(e);
					}
				} else if (color == color1) {
					//pause animation in other slot
					{
						SceneEvent e(SceneEvent::kPauseAnimation);
						e.slot = 0x80 | slot2;
						scene->push(e);
					}
					{
						SceneEvent e(SceneEvent::kPlayAnimation);
						e.animation = animation1;
						e.slot = 0x80 | slot1;
						scene->push(e);
					}
				}

				{
					message.trim();
					if (message.empty())
						break;

					SceneEvent e(SceneEvent::kMessage);
					e.message = message;
					e.color = color;
					if (color == color1)
						e.slot = slot1;
					if (color == color2)
						e.slot = slot2;
					scene->push(e);
					message.clear();
				}
				break;

			case 3:
				color = color == color1 ? color2 : color1;
				//debug(0, "changing color to %02x", color);
				break;
			}
			break;

		case 0xff: {
			//fixme : wait for the next cycle of the animation
		}
		break;

		default:
			message += c;
			n = 0;
		}
	}

	SceneEvent e(SceneEvent::kClearAnimations);
	scene->push(e);
}
Exemple #29
0
void Scripts::cmdTexChoice() {
	// MM is defining 2 times the last range in the original.
	static const int BTN_RANGES_v1[BTN_COUNT][2] = {
		{ 0, 60 }, { 64, 124 }, { 129, 192 }, { 194, 227 }, { 233, 292 }, { 297, 319 }
	};

	static const int BTN_RANGES_v2[BTN_COUNT][2] = {
		{ 0, 76 }, { 77, 154 }, { 155, 232 }, { 233, 276 }, { 0, 0 }, { 277, 319 }
	};


	_vm->_oldRects.clear();
	_choiceStart = _data->pos() - 1;
	_vm->_fonts._charSet._lo = 1;
	_vm->_fonts._charSet._hi = 8;
	_vm->_fonts._charFor._hi = 255;

	if (_vm->getGameID() == GType_MartianMemorandum) {
		_vm->_fonts._charFor._lo = 247;
		_vm->_screen->_maxChars = 23;
	} else {
		_vm->_fonts._charFor._lo = 55;
		_vm->_screen->_maxChars = 20;
	}

	_vm->_screen->_printOrg = _texsOrg;
	_vm->_screen->_printStart = _texsOrg;

	_vm->_bubbleBox->clearBubbles();
	_vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 1");

	byte v;
	Common::String tmpStr = "";
	while ((v = _data->readByte()) != 0)
		tmpStr += (char)v;

	_vm->_bubbleBox->calcBubble(tmpStr);
	_vm->_bubbleBox->printBubble(tmpStr);

	Common::Array<Common::Rect> responseCoords;
	responseCoords.push_back(_vm->_bubbleBox->_bounds);
	_vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11);

	findNull();

	tmpStr.clear();
	while ((v = _data->readByte()) != 0)
		tmpStr += (char)v;

	if (tmpStr.size() != 0) {
		_vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 2");
		_vm->_bubbleBox->calcBubble(tmpStr);
		_vm->_bubbleBox->printBubble(tmpStr);
		responseCoords.push_back(_vm->_bubbleBox->_bounds);
		_vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11);
	}

	findNull();

	bool choice3Fl = false;
	tmpStr.clear();
	while ((v = _data->readByte()) != 0)
		tmpStr += (char)v;

	if (tmpStr.size() != 0) {
		_vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 3");
		_vm->_bubbleBox->calcBubble(tmpStr);
		_vm->_bubbleBox->printBubble(tmpStr);
		responseCoords.push_back(_vm->_bubbleBox->_bounds);
		_vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11);
	}

	findNull();

	int choice = -1;
	do {
		_vm->_events->pollEvents();
		if (_vm->shouldQuit())
			return;

		charLoop();

		_vm->_bubbleBox->_bubbleDisplStr = _vm->_bubbleBox->_bubbleTitle;
		if (_vm->_events->_leftButton) {
			if (_vm->_events->_mouseRow >= ((_vm->getGameID() == GType_MartianMemorandum) ? 23 : 22)) {
				_vm->_events->debounceLeft();
				int x = _vm->_events->_mousePos.x;
				for (int i = 0; i < BTN_COUNT; i++) {
					if (((_vm->getGameID() == GType_MartianMemorandum) && (x >= BTN_RANGES_v1[i][0]) && (x < BTN_RANGES_v1[i][1]))
					||  ((_vm->getGameID() == GType_Amazon) && (x >= BTN_RANGES_v2[i][0]) && (x < BTN_RANGES_v2[i][1]))) {

						choice = i;
						break;
					}
				}
			} else {
				_vm->_events->debounceLeft();
				choice = _vm->_events->checkMouseBox1(responseCoords);
			}
		}
	} while ((choice == -1) || ((choice == 2) && choice3Fl));

	_choice = choice + 1;
	_vm->_bubbleBox->clearBubbles();
}
Exemple #30
0
void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest) {
	Common::Array<TextSurface> textSurfaces;
	Common::Array<uint> lineWidths;
	Common::Array<TextJustification> lineJustifications;

	// Create the initial text state
	TextStyleState currentState;

	// Create an empty font and bind it to the state
	StyledTTFont font(_engine);
	currentState.updateFontWithTextState(font);

	Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words
	Common::String currentWord;
	int sentenceWidth = 0;
	int wordWidth = 0;
	int lineWidth = 0;
	int lineHeight = font.getFontHeight();

	uint currentLineNumber = 0u;

	uint numSpaces = 0u;
	int spaceWidth = 0;

	// The pixel offset to the currentSentence
	Common::Point sentencePixelOffset;

	uint i = 0u;
	uint stringlen = text.size();

	while (i < stringlen) {
		if (text[i] == '<') {
			// Flush the currentWord to the currentSentence
			currentSentence += currentWord;
			sentenceWidth += wordWidth;

			// Reset the word variables
			currentWord.clear();
			wordWidth = 0;

			// Parse the style tag
			uint startTextPosition = i;
			while (i < stringlen && text[i] != '>') {
				++i;
			}
			uint endTextPosition = i;

			uint32 textColor = currentState.getTextColor(_engine);

			uint stateChanges = 0u;
			if ((endTextPosition - startTextPosition - 1) > 0) {
				stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1);
			}

			if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) {
				// Use the last state to render out the current sentence
				// Styles apply to the text 'after' them
				if (!currentSentence.empty()) {
					textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));

					lineWidth += sentenceWidth;
					sentencePixelOffset.x += sentenceWidth;

					// Reset the sentence variables
					currentSentence.clear();
					sentenceWidth = 0;
				}

				// Update the current state with the style information
				currentState.updateFontWithTextState(font);

				lineHeight = MAX(lineHeight, font.getFontHeight());
				spaceWidth = font.getCharWidth(' ');
			}
			if (stateChanges & TEXT_CHANGE_NEWLINE) {
				// If the current sentence has content, render it out
				if (!currentSentence.empty()) {
					textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));
				}

				// Set line width
				lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));

				currentSentence.clear();
				sentenceWidth = 0;

				// Update the offsets
				sentencePixelOffset.x = 0u;
				sentencePixelOffset.y += lineHeight;

				// Reset the line variables
				lineHeight = font.getFontHeight();
				lineWidth = 0;
				++currentLineNumber;
				lineJustifications.push_back(currentState._justification);
			}
			if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) {
				Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox));
				wordWidth += font.getStringWidth(temp);

				// If the word causes the line to overflow, render the sentence and start a new line
				if (lineWidth + sentenceWidth + wordWidth > dest.w) {
					textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber));

					// Set line width
					lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));

					currentSentence.clear();
					sentenceWidth = 0;

					// Update the offsets
					sentencePixelOffset.x = 0u;
					sentencePixelOffset.y += lineHeight;

					// Reset the line variables
					lineHeight = font.getFontHeight();
					lineWidth = 0;
					++currentLineNumber;
					lineJustifications.push_back(currentState._justification);
				}
			}
		} else {
			currentWord += text[i];
			wordWidth += font.getCharWidth(text[i]);

			if (text[i] == ' ') {
				// When we hit the first space, flush the current word to the sentence
				if (!currentWord.empty()) {
					currentSentence += currentWord;
					sentenceWidth += wordWidth;

					currentWord.clear();
					wordWidth = 0;
				}

				// We track the number of spaces so we can disregard their width in lineWidth calculations
				++numSpaces;
			} else {
				// If the word causes the line to overflow, render the sentence and start a new line
				if (lineWidth + sentenceWidth + wordWidth > dest.w) {
					// Only render out content
					if (!currentSentence.empty()) {
						textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
					}

					// Set line width
					lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth));

					currentSentence.clear();
					sentenceWidth = 0;

					// Update the offsets
					sentencePixelOffset.x = 0u;
					sentencePixelOffset.y += lineHeight;

					// Reset the line variables
					lineHeight = font.getFontHeight();
					lineWidth = 0;
					++currentLineNumber;
					lineJustifications.push_back(currentState._justification);
				}

				numSpaces = 0u;
			}
		}

		i++;
	}

	// Render out any remaining words/sentences
	if (!currentWord.empty() || !currentSentence.empty()) {
		currentSentence += currentWord;
		sentenceWidth += wordWidth;

		textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber));
	}

	lineWidths.push_back(lineWidth + sentenceWidth);
	lineJustifications.push_back(currentState._justification);

	for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) {
		Common::Rect empty;

		if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_LEFT) {
			_engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
		} else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_CENTER) {
			_engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, ((dest.w - lineWidths[iter->_lineNumber]) / 2) + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
		} else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_RIGHT) {
			_engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, dest.w - lineWidths[iter->_lineNumber]  + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0);
		}

		// Release memory
		iter->_surface->free();
		delete iter->_surface;
	}
}