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(); }
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; }
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)); }
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 }
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; } } }
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(); }
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(); }
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); }
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(); } */ } }
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; }
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; }
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(); }
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); }
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(); } }
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; }
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; }
/** * 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(); }
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; }
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 }
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; }