Exemple #1
0
bool ThemeEngine::themeConfigParseHeader(Common::String header, Common::String &themeName) {
	// Check that header is not corrupted
	if ((byte)header[0] > 127) {
		warning("Corrupted theme header found");
		return false;
	}

	header.trim();

	if (header.empty())
		return false;

	if (header[0] != '[' || header.lastChar() != ']')
		return false;

	header.deleteChar(0);
	header.deleteLastChar();

	Common::StringTokenizer tok(header, ":");

	if (tok.nextToken() != SCUMMVM_THEME_VERSION_STR)
		return false;

	themeName = tok.nextToken();
	Common::String author = tok.nextToken();

	return tok.empty();
}
Exemple #2
0
Common::String Resource::translateFileName(const Common::String filename) {
	Common::String upperFilename = filename;
	upperFilename.toUppercase();
	Common::String fileNameStrFinal;

	if (upperFilename.hasPrefix("P:") || upperFilename.hasPrefix("F:")) {
		if (_vm->_isHiRes)
			fileNameStrFinal = "SPICT/";
		else
			fileNameStrFinal = "PICT/";

		if (_vm->getPlatform() == Common::kPlatformAmiga) {
			if (upperFilename.hasPrefix("P:")) {
				fileNameStrFinal = "PICT/";
			} else {
				fileNameStrFinal = "LABFONTS/";
				upperFilename += "T";	// all the Amiga fonts have a ".FONT" suffix
			}
		}
	} else if (upperFilename.hasPrefix("LAB:")) {
		// Look inside the game folder
	} else if (upperFilename.hasPrefix("MUSIC:")) {
		fileNameStrFinal = "MUSIC/";
	}

	if (upperFilename.contains(':')) {
		while (upperFilename[0] != ':') {
			upperFilename.deleteChar(0);
		}

		upperFilename.deleteChar(0);
	}

	fileNameStrFinal += upperFilename;

	return fileNameStrFinal;
}
Exemple #3
0
void ResourceHandler::handle(Client &client) {
	Common::String filename = client.path();
	filename.deleteChar(0);

	// if archive hidden file is requested, ignore
	if (filename.size() && filename[0] == '.')
		return;

	// if file not found, don't set handler either
	Common::SeekableReadStream *file = HandlerUtils::getArchiveFile(filename);
	if (file == nullptr)
		return;

	LocalWebserver::setClientGetHandler(client, file, 200, determineMimeType(filename));
}
Exemple #4
0
reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
	Common::String name = s->_segMan->getString(argv[0]);
	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
	bool result;

	// SQ4 floppy prepends /\ to the filenames
	if (name.hasPrefix("/\\")) {
		name.deleteChar(0);
		name.deleteChar(0);
	}

	// Special case for SQ4 floppy: This game has hardcoded names for all of
	// its savegames, and they are all named "sq4sg.xxx", where xxx is the
	// slot. We just take the slot number here, and delete the appropriate
	// save game.
	if (name.hasPrefix("sq4sg.")) {
		// Special handling for SQ4... get the slot number and construct the
		// save game name.
		int slotNum = atoi(name.c_str() + name.size() - 3);
		Common::Array<SavegameDesc> saves;
		listSavegames(saves);
		int savedir_nr = saves[slotNum].id;
		name = g_sci->getSavegameName(savedir_nr);
		result = saveFileMan->removeSavefile(name);
	} else if (getSciVersion() >= SCI_VERSION_2) {
		// The file name may be already wrapped, so check both cases
		result = saveFileMan->removeSavefile(name);
		if (!result) {
			const Common::String wrappedName = g_sci->wrapFilename(name);
			result = saveFileMan->removeSavefile(wrappedName);
		}

#ifdef ENABLE_SCI32
		if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
			delete s->_virtualIndexFile;
			s->_virtualIndexFile = 0;
		}
#endif
	} else {
		const Common::String wrappedName = g_sci->wrapFilename(name);
		result = saveFileMan->removeSavefile(wrappedName);
	}

	debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
	if (result)
		return NULL_REG;
	return make_reg(0, 2); // DOS - file not found error code
}
Exemple #5
0
bool Debugger::Cmd_PlayText(int argc, const char **argv) {
	if (argc != 2) {
		debugPrintf("Usage: %s <text name>\n", argv[0]);
		return true;
	} else {
		Common::String resName = argv[1];
		if (resName.hasPrefix("@"))
			resName.deleteChar(0);

		Common::File f;
		if (f.exists(resName) || f.exists(resName + ".txr")) {
			TextView::execute(_vm, resName);
			return false;
		} else {
			debugPrintf("Could not find resource file\n");
			return true;
		}
	}
}
Exemple #6
0
bool ThemeEngine::themeConfigParseHeader(Common::String header, Common::String &themeName) {
	header.trim();

	if (header.empty())
		return false;

	if (header[0] != '[' || header.lastChar() != ']')
		return false;

	header.deleteChar(0);
	header.deleteLastChar();

	Common::StringTokenizer tok(header, ":");

	if (tok.nextToken() != RESIDUAL_THEME_VERSION_STR)
		return false;

	themeName = tok.nextToken();
	Common::String author = tok.nextToken();

	return tok.empty();
}
Exemple #7
0
void Subtitles::loadSubtitles() {
	File f("special.bin");

	if (!g_vm->_files->_ccNum) {
		// The first subtitle line contains all the text for the Clouds intro. Since ScummVM allows
		// both voice and subtitles at the same time, unlike the original, we need to split up the
		// first subtitle into separate lines to allow them to better interleave with the voice
		Common::String line = f.readString();
		for (;;) {
			const char *lineSep = strstr(line.c_str(), "   ");
			if (!lineSep)
				break;

			_lines.push_back(Common::String(line.c_str(), lineSep));
			line = Common::String(lineSep + 3);
			while (line.hasPrefix(" "))
				line.deleteChar(0);
		}
	}

	while (f.pos() < f.size())
		_lines.push_back(f.readString());
	f.close();
}
Exemple #8
0
reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
	Common::String name = s->_segMan->getString(argv[0]);

	// SCI32 can call K_FILEIO_OPEN with only one argument. It seems to
	// just be checking if it exists.
	int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16();
	bool unwrapFilename = true;

	// SQ4 floppy prepends /\ to the filenames
	if (name.hasPrefix("/\\")) {
		name.deleteChar(0);
		name.deleteChar(0);
	}

	// SQ4 floppy attempts to update the savegame index file sq4sg.dir when
	// deleting saved games. We don't use an index file for saving or loading,
	// so just stop the game from modifying the file here in order to avoid
	// having it saved in the ScummVM save directory.
	if (name == "sq4sg.dir") {
		debugC(kDebugLevelFile, "Not opening unused file sq4sg.dir");
		return SIGNAL_REG;
	}

	if (name.empty()) {
		// Happens many times during KQ1 (e.g. when typing something)
		debugC(kDebugLevelFile, "Attempted to open a file with an empty filename");
		return SIGNAL_REG;
	}
	debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);

#ifdef ENABLE_SCI32
	if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
		if (s->_virtualIndexFile) {
			return make_reg(0, VIRTUALFILE_HANDLE);
		} else {
			Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH);
			Common::String wrappedName = g_sci->wrapFilename(englishName);
			if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) {
				s->_virtualIndexFile = new VirtualIndexFile(wrappedName);
				return make_reg(0, VIRTUALFILE_HANDLE);
			}
		}
	}

	// Shivers is trying to store savegame descriptions and current spots in
	// separate .SG files, which are hardcoded in the scripts.
	// Essentially, there is a normal save file, created by the executable
	// and an extra hardcoded save file, created by the game scripts, probably
	// because they didn't want to modify the save/load code to add the extra
	// information.
	// Each slot in the book then has two strings, the save description and a
	// description of the current spot that the player is at. Currently, the
	// spot strings are always empty (probably related to the unimplemented
	// kString subop 14, which gets called right before this call).
	// For now, we don't allow the creation of these files, which means that
	// all the spot descriptions next to each slot description will be empty
	// (they are empty anyway). Until a viable solution is found to handle these
	// extra files and until the spot description strings are initialized
	// correctly, we resort to virtual files in order to make the load screen
	// useable. Without this code it is unusable, as the extra information is
	// always saved to 0.SG for some reason, but on restore the correct file is
	// used. Perhaps the virtual ID is not taken into account when saving.
	//
	// Future TODO: maintain spot descriptions and show them too, ideally without
	// having to return to this logic of extra hardcoded files.
	if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) {
		if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
			// Game scripts are trying to create a file with the save
			// description, stop them here
			debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
			return SIGNAL_REG;
		} else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
			// Create a virtual file containing the save game description
			// and slot number, as the game scripts expect.
			int slotNumber;
			sscanf(name.c_str(), "%d.SG", &slotNumber);

			Common::Array<SavegameDesc> saves;
			listSavegames(saves);
			int savegameNr = findSavegame(saves, slotNumber - SAVEGAMEID_OFFICIALRANGE_START);

			if (!s->_virtualIndexFile) {
				// Make the virtual file buffer big enough to avoid having it grow dynamically.
				// 50 bytes should be more than enough.
				s->_virtualIndexFile = new VirtualIndexFile(50);
			}

			s->_virtualIndexFile->seek(0, SEEK_SET);
			s->_virtualIndexFile->write(saves[savegameNr].name, strlen(saves[savegameNr].name));
			s->_virtualIndexFile->write("\0", 1);
			s->_virtualIndexFile->write("\0", 1);	// Spot description (empty)
			s->_virtualIndexFile->seek(0, SEEK_SET);
			return make_reg(0, VIRTUALFILE_HANDLE);
		}
	}
#endif

	// QFG import rooms get a virtual filelisting instead of an actual one
	if (g_sci->inQfGImportRoom()) {
		// We need to find out what the user actually selected, "savedHeroes" is
		// already destroyed when we get here. That's why we need to remember
		// selection via kDrawControl.
		name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem);
		unwrapFilename = false;
	}

	return file_open(s, name, mode, unwrapFilename);
}
Exemple #9
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 #10
0
Stxt::Stxt(Common::SeekableSubReadStreamEndian &textStream) {
	// TODO: Side effects on textStream make this a little hard to understand in context?
	uint32 unk1 = textStream.readUint32();
	uint32 strLen = textStream.readUint32();
	uint32 dataLen = textStream.readUint32();
	Common::String text;

	for (uint32 i = 0; i < strLen; i++) {
		byte ch = textStream.readByte();
		if (ch == 0x0d) {
			ch = '\n';
		}
		text += ch;
	}
	debugC(3, kDebugText, "Stxt init: unk1: %d strLen: %d dataLen: %d textlen: %u", unk1, strLen, dataLen, text.size());
	if (strLen < 200)
		debugC(3, kDebugText, "text: '%s'", text.c_str());

	uint16 formattingCount = textStream.readUint16();
	uint32 prevPos = 0;

	while (formattingCount) {
		uint32 formatStartOffset = textStream.readUint32();
		uint16 unk1f = textStream.readUint16();
		uint16 unk2f = textStream.readUint16();

		_fontId = textStream.readUint16();
		_textSlant = textStream.readByte();
		byte unk3f = textStream.readByte();
		_fontSize = textStream.readUint16();

		_palinfo1 = textStream.readUint16();
		_palinfo2 = textStream.readUint16();
		_palinfo3 = textStream.readUint16();

		debugC(3, kDebugText, "Stxt init: formattingCount: %u, formatStartOffset: %d, unk1: %d unk2: %d, fontId: %d, textSlant: %d",
			   formattingCount, formatStartOffset, unk1f, unk2f, _fontId, _textSlant);

		debugC(3, kDebugText, "        unk3: %d, fontSize: %d, p0: %x p1: %x p2: %x", unk3f, _fontSize, _palinfo1, _palinfo2, _palinfo3);

		assert(prevPos <= formatStartOffset);  // If this is triggered, we have to implement sorting

		while (prevPos != formatStartOffset) {
			char f = text.firstChar();
			_ftext += text.firstChar();
			text.deleteChar(0);

			if (f == '\001')    // Insert two \001s as a replacement
				_ftext += '\001';

			prevPos++;

			debugCN(4, kDebugText, "%c", f);
		}

		debugCN(4, kDebugText, "*");

		_ftext += Common::String::format("\001\015%c%c%c%c%c%c%c%c%c%c%c%c",
										 (_fontId >> 8) & 0xff, _fontId & 0xff,
										 _textSlant & 0xff, unk3f & 0xff,
										 (_fontSize >> 8) & 0xff, _fontSize & 0xff,
										 (_palinfo1 >> 8) & 0xff, _palinfo1 & 0xff,
										 (_palinfo2 >> 8) & 0xff, _palinfo2 & 0xff,
										 (_palinfo3 >> 8) & 0xff, _palinfo3 & 0xff);

		formattingCount--;
	}

	debugC(4, kDebugText, "%s", text.c_str());
	_ftext += text;
}
Exemple #11
0
void TextObject::createBitmap() {
	if (_created)
		destroyBitmap();

	Common::String msg = parseMsgText(_textID, NULL);
	Common::String message;
	const char *c = msg.c_str();

	// remove spaces (NULL_TEXT) from the end of the string,
	// while this helps make the string unique it screws up
	// text justification
	for (int i = (int)msg.size() - 1; c[i] == TEXT_NULL; i--)
		msg.deleteLastChar();

	// remove char of id 13 from the end of the string,
	for (int i = (int)msg.size() - 1; c[i] == 13; i--)
		msg.deleteLastChar();

	// 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;
	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
	_numberLines = 1;
	int lineWidth = 0;
	int maxLineWidth = 0;
	for (int i = 0; i < (int)msg.size(); i++) {
		lineWidth += MAX(_font->getCharWidth(msg[i]), _font->getCharDataWidth(msg[i]));
		if (lineWidth > maxWidth) {
			if (message.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 += '-';
 			}
			message += '\n';
			_numberLines++;

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

			continue; // don't add the space back
		}

		if (lineWidth > maxLineWidth)
			maxLineWidth = lineWidth;

		message += 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;
		}
	}

	_textObjectHandle = (GfxBase::TextObjectHandle **)malloc(sizeof(long) * _numberLines);
	_bitmapWidthPtr = new int[_numberLines];

	for (int j = 0; j < _numberLines; j++) {
		int nextLinePos, cutLen;
		const char *pos = strchr(message.c_str(), '\n');
		if (pos) {
			nextLinePos = pos - message.c_str();
			cutLen = nextLinePos + 1;
		} else {
			nextLinePos = message.size();
			cutLen = nextLinePos;
		}
		Common::String currentLine(message.c_str(), message.c_str() + nextLinePos);

		_bitmapWidthPtr[j] = 0;
		for (unsigned int i = 0; i < currentLine.size(); ++i) {
			_bitmapWidthPtr[j] += MAX(_font->getCharWidth(currentLine[i]), _font->getCharDataWidth(currentLine[i]));
		}

		_textBitmap = new uint8[_font->getHeight() * (_bitmapWidthPtr[j] + 1)];
		memset(_textBitmap, 0, _font->getHeight() * (_bitmapWidthPtr[j] + 1));

		// Fill bitmap
		int startOffset = 0;
		for (unsigned int d = 0; d < currentLine.size(); d++) {
			int ch = currentLine[d];
			int8 startingLine = _font->getCharStartingLine(ch) + _font->getBaseOffsetY();
			int32 charDataWidth = _font->getCharDataWidth(ch);
			int32 charWidth = _font->getCharWidth(ch);
			int8 startingCol = _font->getCharStartingCol(ch);
			for (int line = 0; line < _font->getCharDataHeight(ch); line++) {
				int offset = startOffset + ((_bitmapWidthPtr[j] + 1) * (line + startingLine));
				for (int r = 0; r < charDataWidth; r++) {
					const byte pixel = *(_font->getCharData(ch) + r + (charDataWidth * line));
					byte *dst = _textBitmap + offset + startingCol + r;
					if (*dst == 0 && pixel != 0)
						_textBitmap[offset + startingCol + r] = pixel;
				}
				if (line + startingLine >= _font->getHeight())
					break;
			}
			startOffset += charWidth;
		}

		_textObjectHandle[j] = g_driver->createTextBitmap(_textBitmap, _bitmapWidthPtr[j] + 1, _font->getHeight(), _fgColor);
		delete[] _textBitmap;
		for (int count = 0; count < cutLen; count++)
			message.deleteChar(0);
	}
	_created = true;
}
Exemple #12
0
void EndCredits::show() {
	_vm->_mouse->disable();
	_vm->_mixer->stopAll();
	_vm->_ambientSounds->removeAllNonLoopingSounds(true);
	_vm->_ambientSounds->removeAllLoopingSounds(4);
	_vm->_audioSpeech->stopSpeech();

	_vm->_music->play(_vm->_gameInfo->getMusicTrack(17), 100, 0, 2, -1, 0, 3);

	Font *fontBig = new Font(_vm);
	fontBig->open("TAHOMA24.FON", 640, 480, -1, 0, 0);
	fontBig->setSpacing(1, 0);

	Font *fontSmall = new Font(_vm);
	fontSmall->open("TAHOMA18.FON", 640, 480, -1, 0, 0);
	fontSmall->setSpacing(1, 0);

	TextResource *textResource = new TextResource(_vm);
	textResource->open("ENDCRED");

	int textCount = textResource->getCount();
	int *textPositions = (int *)malloc(textCount * sizeof(int));
	int y = 452;
	bool small = false;

	for (int i = 0; i < textCount; i++) {
		Common::String s = textResource->getText(i);
		if (s.hasPrefix("^")) {
			if (!small) {
				y += 28;
			}
			small = false;
		} else {
			if (small) {
				y += 24;
			} else {
				y += 28;
			}
			small = true;
		}
		if (s.hasPrefix("^")) {
			textPositions[i] = y;
		} else {
			textPositions[i] = y + 2;
		}
	}

	_vm->_vqaIsPlaying = true;
	_vm->_vqaStopIsRequested = false;

	double position = 0.0;
	uint32 timeLast = _vm->getTotalPlayTime(); // Original game is using system timer

	while (!_vm->_vqaStopIsRequested && !_vm->shouldQuit()) {
		if (position >= textPositions[textCount - 1]) {
			break;
		}

		//soundSystem::tick(SoundSystem);
		_vm->handleEvents();

		if (!_vm->_gameIsRunning) {
			timeLast = _vm->getTotalPlayTime(); // Original game is using system timer

			continue;
		}

		uint32 timeNow = _vm->getTotalPlayTime(); // Original game is using system timer
		position += (double)(timeNow - timeLast) * 0.05f;
		timeLast = timeNow;

		_vm->_surfaceFront.fillRect(Common::Rect(640, 480), 0);

		for (int i = 0; i < textCount; i++) {
			Common::String s = textResource->getText(i);
			Font *font;
			int height;

			if (s.hasPrefix("^")) {
				font = fontBig;
				height = 28;
				s.deleteChar(0);
			} else {
				font = fontSmall;
				height = 24;
			}

			y = textPositions[i] - (int)position;

			if (y < 452 && y + height > 28) {
				int x;

				if (font == fontBig) {
					x = 280;
				} else {
					x = 270 - font->getTextWidth(s);
				}

				font->draw(s, _vm->_surfaceFront, x, y);
			}
		}

		_vm->_surfaceFront.fillRect(Common::Rect(0, 0, 640, 28), 0);
		_vm->_surfaceFront.fillRect(Common::Rect(0, 452, 640, 480), 0);

		_vm->blitToScreen(_vm->_surfaceFront);

		_vm->_system->delayMillis(10);
	}

	_vm->_vqaIsPlaying = false;
	_vm->_vqaStopIsRequested = false;

	free(textPositions);
	delete textResource;

	delete fontSmall;
	delete fontBig;

	_vm->_music->stop(0);
	_vm->_mouse->enable();
}
Exemple #13
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 #14
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 #15
0
bool WidgetFiles::getFilename() {
	Events &events = *_vm->_events;
	TattooScene &scene = *(TattooScene *)_vm->_scene;
	Screen &screen = *_vm->_screen;
	Talk &talk = *_vm->_talk;
	int index = 0;
	int done = 0;
	bool blinkFlag = false;
	int blinkCountdown = 0;
	int cursorColor = 192;
	byte color, textColor;
	bool insert = true;

	assert(_selector != -1);
	Common::Point pt(_surface.stringWidth("00.") + _surface.widestChar() + 5,
		_surface.fontHeight() + 14 + (_selector - _savegameIndex) * (_surface.fontHeight() + 1));

	Common::String numStr = Common::String::format("%d.", _selector + 1);
	_surface.writeString(numStr, Common::Point(_surface.widestChar(), pt.y), COMMAND_HIGHLIGHTED);

	Common::String filename = _savegames[_selector];

	if (isSlotEmpty(_selector)) {
		index = 0;
		_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.right - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
		filename = "";
	} else {
		index = filename.size();
		_surface.writeString(filename, pt, COMMAND_HIGHLIGHTED);
		pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
	}

	do {
		scene.doBgAnim();

		if (talk._talkToAbort)
			return false;

		char currentChar = (index == (int)filename.size()) ? ' ' : filename[index];
		Common::String charString = Common::String::format("%c", currentChar);
		int width = screen.charWidth(currentChar);

		// Wait for keypress
		while (!events.kbHit()) {
			events.pollEventsAndWait();
			events.setButtonState();

			scene.doBgAnim();

			if (talk._talkToAbort)
				return false;

			if (--blinkCountdown <= 0) {
				blinkCountdown = 3;
				blinkFlag = !blinkFlag;
				if (blinkFlag) {
					textColor = 236;
					color = cursorColor;
				} else {
					textColor = COMMAND_HIGHLIGHTED;
					color = TRANSPARENCY;
				}

				_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), color);
				if (currentChar != ' ')
					_surface.writeString(charString, pt, textColor);
			}
			if (_vm->shouldQuit())
				return false;
		}

		Common::KeyState keyState = events.getKey();
		if (keyState.keycode == Common::KEYCODE_BACKSPACE && index > 0) {
			pt.x -= _surface.charWidth(filename[index - 1]);
			--index;

			if (insert) {
				filename.deleteChar(index);
			} else {
				filename.setChar(' ', index);
			}

			_surface.fillRect(Common::Rect(pt.x, pt.y, _surface.width() - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
			_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);

		} else if ((keyState.keycode == Common::KEYCODE_LEFT && index > 0)
				|| (keyState.keycode == Common::KEYCODE_RIGHT && index < 49 && pt.x < (_bounds.right - BUTTON_SIZE - 20))
				|| (keyState.keycode == Common::KEYCODE_HOME && index > 0)
				|| (keyState.keycode == Common::KEYCODE_END)) {
			_surface.fillRect(Common::Rect(pt.x, pt.y, pt.x + width, pt.y + _surface.fontHeight()), TRANSPARENCY);
			if (currentChar)
				_surface.writeString(charString, pt, COMMAND_HIGHLIGHTED);

			switch (keyState.keycode) {
			case Common::KEYCODE_LEFT:
				pt.x -= _surface.charWidth(filename[index - 1]);
				--index;
				break;

			case Common::KEYCODE_RIGHT:
				pt.x += _surface.charWidth(filename[index]);
				++index;
				break;

			case Common::KEYCODE_HOME:
				pt.x = _surface.stringWidth("00.") + _surface.widestChar() + 5;
				index = 0;
				break;

			case Common::KEYCODE_END:
				pt.x = _surface.stringWidth("00.") + _surface.stringWidth(filename) + _surface.widestChar() + 5;
				index = filename.size();

				while (filename[index - 1] == ' ' && index > 0) {
					pt.x -= _surface.charWidth(filename[index - 1]);
					--index;
				}
				break;

			default:
				break;
			}
		} else if (keyState.keycode == Common::KEYCODE_INSERT) {
			insert = !insert;
			if (insert)
				cursorColor = 192;
			else
				cursorColor = 200;

		} else if (keyState.keycode == Common::KEYCODE_DELETE) {
			filename.deleteChar(index);

			_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.right - BUTTON_SIZE - 9, pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
			_surface.writeString(filename + index, pt, COMMAND_HIGHLIGHTED);

		} else  if (keyState.keycode == Common::KEYCODE_RETURN) {
			done = 1;

		} else if (keyState.keycode == Common::KEYCODE_ESCAPE) {
			_selector = -1;
			render(RENDER_NAMES_AND_SCROLLBAR);
			done = -1;
		}

		if ((keyState.ascii >= ' ') && (keyState.ascii <= 'z') && (index < 50)) {
			if (pt.x + _surface.charWidth(keyState.ascii) < _surface.w - BUTTON_SIZE - 20) {
				if (insert)
					filename.insertChar(keyState.ascii, index);
				else
					filename.setChar(keyState.ascii, index);

				_surface.fillRect(Common::Rect(pt.x, pt.y, _bounds.width() - BUTTON_SIZE - 9,
					pt.y + _surface.fontHeight() - 1), TRANSPARENCY);
				_surface.writeString(filename.c_str() + index, pt, COMMAND_HIGHLIGHTED);
				pt.x += _surface.charWidth(keyState.ascii);
				++index;
			}
		}
	} while (!done && !_vm->shouldQuit());

	scene.doBgAnim();

	if (talk._talkToAbort)
		return false;

	if (done == 1)
		_savegames[_selector] = filename;

	return done == 1;
}
Exemple #16
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 #17
0
/**
 * Loads and draws the chosen page of the help.
 * @remarks Originally called 'getme'
 */
void Help::switchPage(byte which) {
	// Help icons are 80x20.

	_highlightWas = 177; // Forget where the highlight was.

	Common::File file;

	if (!file.open("help.avd"))
		error("AVALANCHE: Help: File not found: help.avd");

	file.seek(which * 2);
	uint16 offset = file.readUint16LE();
	file.seek(offset);

	Common::String title = getLine(file);

	_vm->_graphics->drawFilledRectangle(Common::Rect(0, 0, 640, 200), kColorBlue);
	_vm->_graphics->drawFilledRectangle(Common::Rect(8, 40, 450, 200), kColorWhite);

	byte index = file.readByte();
	_vm->_graphics->helpDrawButton(-177, index);

	// Plot the title:
	_vm->_graphics->drawNormalText(title, _vm->_font, 8, 629 - 8 * title.size(), 26, kColorBlack);
	_vm->_graphics->drawNormalText(title, _vm->_font, 8, 630 - 8 * title.size(), 25, kColorCyan);

	_vm->_graphics->helpDrawBigText("help!", 549, 1, kColorBlack);
	_vm->_graphics->helpDrawBigText("help!", 550, 0, kColorCyan);

	byte y = 0;
	do {
		Common::String line = getLine(file);
		if (!line.empty()) {
			if (line.compareTo(Common::String('!')) == 0)  // End of the help text is signalled with a '!'.
				break;
			if (line[0] == '\\') {
				line.deleteChar(0);
				_vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorRed);
			}
			else
				_vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorBlack);
		}
		y++;
	} while (true);

	// We are now at the end of the text. Next we must read the icons:
	y = 0;
	_buttonNum = 0;
	while (!file.eos()) {
		int trigger = file.readByte();

		if (trigger == 177)
			break;
		switch (trigger) {
		case 254: // Escape
			trigger = 27;
			break;
		case 214: // PageUp
			trigger = 280;
			break;
		case 216: // PageDown
			trigger = 281;
			break;
		default: // A - Z
			// The characters are stored in the file in uppercase, but we need the lowercase versions for KeyCode:
			trigger = tolower(trigger);
			break;
		}

		_buttons[y]._trigger = Common::KeyCode(trigger);
		index = file.readByte();
		if (_buttons[y]._trigger != Common::KEYCODE_INVALID)
			_vm->_graphics->helpDrawButton(13 + (y + 1) * 27, index);
		_buttons[y]._whither = file.readByte(); // This is the position to jump to.

		Common::String text = "";
		switch (_buttons[y]._trigger) {
		case Common::KEYCODE_ESCAPE:
			text = Common::String("Esc");
			break;
		case Common::KEYCODE_PAGEUP:
			text = Common::String(24);
			break;
		case Common::KEYCODE_PAGEDOWN:
			text = Common::String(25);
			break;
		default:
			text = Common::String(toupper(_buttons[y]._trigger));
			break;
		}

		_vm->_graphics->helpDrawBigText(text, 589 - (text.size() * 8), 18 + (y + 1) * 27, kColorBlack);
		_vm->_graphics->helpDrawBigText(text, 590 - (text.size() * 8), 17 + (y + 1) * 27, kColorCyan);

		y++;
		_buttonNum++;
	}

	_vm->_graphics->refreshScreen();

	file.close();
}
Exemple #18
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 #19
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 #20
0
bool WintermuteEngine::getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption) {
	bool retVal = false;
	caption = name = "(invalid)";
	Common::SeekableReadStream *stream = NULL;
	// Quick-fix, instead of possibly breaking the persistence-system, let's just roll with it
	BaseFileManager *fileMan = new BaseFileManager(Common::UNK_LANG);
	fileMan->registerPackages(fslist);
	stream = fileMan->openFile("startup.settings", false, false);

	// The process is as follows: Check the "GAME=" tag in startup.settings, to decide where the
	// game-settings are (usually "default.game"), then look into the game-settings to find
	// the NAME = and CAPTION = tags, to use them to generate a gameid and extras-field

	Common::String settingsGameFile = "default.game";
	// If the stream-open failed, lets at least attempt to open the default game file afterwards
	// so, we don't call it a failure yet.
	if (stream) {
		while (!stream->eos() && !stream->err()) {
			Common::String line = stream->readLine();
			line.trim(); // Get rid of indentation
			// Expect "SETTINGS {" or comment, or empty line
			if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) {
				continue;
			} else {
				// We are looking for "GAME ="
				Common::StringTokenizer token(line, "=");
				Common::String key = token.nextToken();
				Common::String value = token.nextToken();
				if (value.size() == 0) {
					continue;
				}
				if (value[0] == '\"') {
					value.deleteChar(0);
				} else {
					continue;
				}
				if (value.lastChar() == '\"') {
					value.deleteLastChar();
				}
				if (key == "GAME") {
					settingsGameFile = value;
					break;
				}
			}
		}
	}

	delete stream;
	stream = fileMan->openFile(settingsGameFile, false, false);
	if (stream) {
		// We do some manual parsing here, as the engine needs gfx to be initalized to do that.
		while (!stream->eos() && !stream->err()) {
			Common::String line = stream->readLine();
			line.trim(); // Get rid of indentation
			// Expect "GAME {" or comment, or empty line
			if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) {
				continue;
			} else {
				Common::StringTokenizer token(line, "=");
				Common::String key = token.nextToken();
				Common::String value = token.nextToken();
				if (value.size() == 0) {
					continue;
				}
				if (value[0] == '\"') {
					value.deleteChar(0);
				} else {
					continue;    // not a string
				}
				if (value.lastChar() == '\"') {
					value.deleteLastChar();
				}
				if (key == "NAME") {
					retVal = true;
					name = value;
				} else if (key == "CAPTION") {
					retVal = true;
					caption = value;
				}
			}
		}
		delete stream;
	}
	delete fileMan;
	BaseEngine::destroy();
	return retVal;
}