Common::String getTextFromAnyVar(const Variable &from) { switch (from.varType) { case SVT_STRING: return from.varData.theString; case SVT_FASTARRAY: { Common::String builder = "FAST:"; Common::String builder2 = ""; Common::String grabText = ""; for (int i = 0; i < from.varData.fastArray->size; i++) { builder2 = builder + " "; grabText = getTextFromAnyVar(from.varData.fastArray->fastVariables[i]); builder.clear(); builder = builder2 + grabText; } return builder; } case SVT_STACK: { Common::String builder = "ARRAY:"; Common::String builder2 = ""; Common::String grabText = ""; VariableStack *stacky = from.varData.theStack->first; while (stacky) { builder2 = builder + " "; grabText = getTextFromAnyVar(stacky->thisVar); builder.clear(); builder = builder2 + grabText; stacky = stacky->next; } return builder; } case SVT_INT: { Common::String buff = Common::String::format("%i", from.varData.intValue); return buff; } case SVT_FILE: { return resourceNameFromNum(from.varData.intValue); } case SVT_OBJTYPE: { ObjectType *thisType = g_sludge->_objMan->findObjectType(from.varData.intValue); if (thisType) return thisType->screenName; break; } default: break; } return typeName[from.varType]; }
void AboutDialog::addLine(const char *str) { if (*str == 0) { _lines.push_back(""); } else { Common::String format(str, 2); str += 2; static Common::String asciiStr; if (format[0] == 'A') { bool useAscii = false; #ifdef USE_TRANSLATION // We could use TransMan.getCurrentCharset() but rather than compare strings // it is easier to use TransMan.getCharsetMapping() (non null in case of non // ISO-8859-1 mapping) useAscii = (TransMan.getCharsetMapping() != NULL); #endif if (useAscii) asciiStr = str; return; } StringArray wrappedLines; if (!asciiStr.empty()) { g_gui.getFont().wordWrapText(asciiStr, _w - 2 * _xOff, wrappedLines); asciiStr.clear(); } else g_gui.getFont().wordWrapText(str, _w - 2 * _xOff, wrappedLines); for (StringArray::const_iterator i = wrappedLines.begin(); i != wrappedLines.end(); ++i) { _lines.push_back(format + *i); } } }
void MacText::splitString(Common::String &str) { const char *s = str.c_str(); Common::String tmp; bool prevCR = false; while (*s) { if (*s == '\n' && prevCR) { // trean \r\n as one prevCR = false; s++; continue; } if (*s == '\r') prevCR = true; if (*s == '\r' || *s == '\n') { _textMaxWidth = MAX(_font->wordWrapText(tmp, _maxWidth, _text), _textMaxWidth); tmp.clear(); s++; continue; } tmp += *s; s++; } if (tmp.size()) _textMaxWidth = MAX(_font->wordWrapText(tmp, _maxWidth, _text), _textMaxWidth); }
void add(Common::String &line, int &w) { if (actualMaxLineWidth < w) actualMaxLineWidth = w; lines.push_back(line); line.clear(); w = 0; }
bool SystemUI::askForSaveGameDescription(int16 slotId, Common::String &newDescription) { // Let user enter new description bool previousEditState = _text->inputGetEditStatus(); byte previousEditCursor = _text->inputGetCursorChar(); _text->drawMessageBox(_textSaveGameEnterDescription, 0, 31, true); _text->inputEditOn(); _text->charPos_Push(); _text->charAttrib_Push(); _text->charPos_SetInsideWindow(3, 0); _text->charAttrib_Set(15, 0); _text->clearBlockInsideWindow(3, 0, 31, 0); // input line is supposed to be black _text->inputSetCursorChar('_'); // figure out the current description of the slot _text->stringSet(""); for (uint16 slotNr = 0; slotNr < _savedGameArray.size(); slotNr++) { if (_savedGameArray[slotNr].slotId == slotId) { // found slotId if (_savedGameArray[slotNr].isValid) { // and also valid, so use its description _text->stringSet(_savedGameArray[slotNr].description); } } } _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING); _text->stringEdit(30); // only allow up to 30 characters _text->charAttrib_Pop(); _text->charPos_Pop(); _text->inputSetCursorChar(previousEditCursor); if (!previousEditState) { _text->inputEditOff(); } _text->closeWindow(); if (!_text->stringWasEntered()) { // User cancelled? exit now return false; } // Now verify that the user really wants to do this if (!askForSavedGameVerification(_textSaveGameVerify, _textSaveGameVerifyButton1, _textSaveGameVerifyButton2, (char *)_text->_inputString, slotId)) { return false; } newDescription.clear(); newDescription += (char *)_text->_inputString; return true; }
int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const Common::String &caption, const Common::String &button) { GUI::SaveLoadChooser slc(caption, button, saveMode); selectedName.clear(); int idx = slc.runModalWithCurrentTarget(); if (idx >= 0) { selectedName = slc.getResultString(); } return idx; }
bool Debugger_EoB::cmd_saveOriginal(int argc, const char **argv) { if (!_vm->_runFlag) { DebugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n"); return true; } Common::String dir = ConfMan.get("savepath"); if (dir == "None") dir.clear(); Common::FSNode nd(dir); if (!nd.isDirectory()) return false; if (_vm->game() == GI_EOB1) { if (argc == 1) { if (_vm->saveAsOriginalSaveFile()) { Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA.SAV")); if (nf.isReadable()) DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str()); else DebugPrintf("Failure.\n"); } else { DebugPrintf("Failure.\n"); } } else { DebugPrintf("Syntax: save_original\n (Saves game in original file format to a file which can be used with the orginal game executable.)\n\n"); } return true; } else if (argc == 2) { int slot = atoi(argv[1]); if (slot < 0 || slot > 5) { DebugPrintf("Slot must be between (including) 0 and 5.\n"); } else if (_vm->saveAsOriginalSaveFile(slot)) { Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA%d.SAV", slot)); if (nf.isReadable()) DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str()); else DebugPrintf("Failure.\n"); } else { DebugPrintf("Failure.\n"); } return true; } DebugPrintf("Syntax: save_original <slot>\n (Saves game in original file format to a file which can be used with the orginal game executable.\n A save slot between 0 and 5 must be specified.)\n\n"); return true; }
bool Font::getLine(Common::String &s, int maxWidth, Common::String &line, int &width) { assert(maxWidth > 0); width = 0; const char *src = s.c_str(); char c; while ((c = *src) != '\0') { if (c == '\r') { // End of line, so return calculated line line = Common::String(s.c_str(), src); s = Common::String(src + 1); return false; } ++src; width += charWidth(c); if (width < maxWidth) continue; // Reached maximum allowed size // If this was the last character of the string, let it go if (*src == '\0') { line = Common::String(s.c_str(), src); s.clear(); return true; } // Work backwards to find space at the start of the current word // as a point to split the line on while (src >= s.c_str() && *src != ' ') { width -= charWidth(*src); --src; } if (src < s.c_str()) error("Could not fit line"); // Split the line around the space line = Common::String(s.c_str(), src); s = Common::String(src + 1); return false; } // Return entire string line = s; s = Common::String(); return true; }
// returns true if something has been completed // completion has to be delete[]-ed then bool Debugger::tabComplete(const char *input, Common::String &completion) const { // very basic tab completion // for now it just supports command completions // adding completions of command parameters would be nice (but hard) :-) // maybe also give a list of possible command completions? // (but this will require changes to console) if (strchr(input, ' ')) return false; // already finished the first word const uint inputlen = strlen(input); completion.clear(); CommandsMap::const_iterator i, e = _cmds.end(); for (i = _cmds.begin(); i != e; ++i) { if (i->_key.hasPrefix(input)) { uint commandlen = i->_key.size(); if (commandlen == inputlen) { // perfect match, so no tab completion possible return false; } if (commandlen > inputlen) { // possible match // no previous match if (completion.empty()) { completion = i->_key.c_str() + inputlen; } else { // take common prefix of previous match and this command for (uint j = 0; j < completion.size(); j++) { if (inputlen + j >= i->_key.size() || completion[j] != i->_key[inputlen + j]) { completion = Common::String(completion.begin(), completion.begin() + j); // If there is no unambiguous completion, abort if (completion.empty()) return false; break; } } } } } } if (completion.empty()) return false; return true; }
void ResourceSerializer::syncAsString32(Common::String &string) { if (isLoading()) { string.clear(); uint32 length = _loadStream->readUint32LE(); for (uint i = 0; i < length; i++) { char c = _loadStream->readByte(); string += c; } _bytesSynced += 4 + length; } else { _saveStream->writeUint32LE(string.size()); _saveStream->writeString(string); _bytesSynced += 4 + string.size(); } }
virtual void calc(const char *text, uint16 maxwidth) { setup(); _lineWidth = 0; _line.clear(); _lines = 0; Common::StringTokenizer tokenizer(text, " "); Common::String token; Common::String blank(" "); uint16 blankWidth = _font->getStringWidth(" "); uint16 tokenWidth = 0; while (!tokenizer.empty()) { token = tokenizer.nextToken(); token = expand(token); if (token == "/") { tokenWidth = 0; action(); textNewLine(); } else { // todo: expand '%' tokenWidth = _font->getStringWidth(token.c_str()); if (_lineWidth == 0) { textAccum(token, tokenWidth); } else { if (_lineWidth + blankWidth + tokenWidth <= maxwidth) { textAccum(blank, blankWidth); textAccum(token, tokenWidth); } else { action(); textNewLine(); textAccum(token, tokenWidth); } } } } end(); }
int SaveLoad::selectSaveFile(Common::String &selectedName, bool saveMode, const Common::String &caption, const Common::String &button) { GUI::SaveLoadChooser slc(caption, button); slc.setSaveMode(saveMode); selectedName.clear(); Common::String gameId = ConfMan.get("gameid"); const EnginePlugin *plugin = 0; EngineMan.findGame(gameId, &plugin); int idx = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); if (idx >= 0) { selectedName = slc.getResultString(); slc.close(); } return idx; }
Common::Archive *ResLoaderInsMalcolm::load(Common::ArchiveMemberPtr memberFile, Common::SeekableReadStream &stream) const { Common::List<Common::String> filenames; Common::ScopedPtr<PlainArchive> result(new PlainArchive(memberFile)); if (!result) return 0; // thanks to eriktorbjorn for this code (a bit modified though) stream.seek(3, SEEK_SET); // first file is the index table uint32 size = stream.readUint32LE(); Common::String temp; for (uint32 i = 0; i < size; ++i) { byte c = stream.readByte(); if (c == '\\') { temp.clear(); } else if (c == 0x0D) { // line endings are CRLF c = stream.readByte(); assert(c == 0x0A); ++i; filenames.push_back(temp); } else { temp += (char)c; } } stream.seek(3, SEEK_SET); for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) { const uint32 fileSize = stream.readUint32LE(); const uint32 fileOffset = stream.pos(); result->addFileEntry(*file, PlainArchive::Entry(fileOffset, fileSize)); stream.seek(fileSize, SEEK_CUR); } return result.release(); }
bool SystemUI::askForCommand(Common::String &commandText) { // Let user enter the command (this was originally only available for Hercules rendering, we allow it everywhere) bool previousEditState = _text->inputGetEditStatus(); byte previousEditCursor = _text->inputGetCursorChar(); _text->drawMessageBox(_textEnterCommand, 0, 36, true); _text->inputEditOn(); _text->charPos_Push(); _text->charAttrib_Push(); _text->charPos_SetInsideWindow(2, 0); _text->charAttrib_Set(15, 0); _text->clearBlockInsideWindow(2, 0, 36, 0); // input line is supposed to be black _text->inputSetCursorChar('_'); _text->stringSet(commandText.c_str()); // Set current command text (may be a command recall) _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING); _text->stringEdit(35); // only allow up to 35 characters _text->charAttrib_Pop(); _text->charPos_Pop(); _text->inputSetCursorChar(previousEditCursor); if (!previousEditState) { _text->inputEditOff(); } _text->closeWindow(); if (!_text->stringWasEntered()) { // User cancelled? exit now return false; } commandText.clear(); commandText += (char *)_text->_inputString; return true; }
void AnimVideo::printData() { Anim::printData(); debug("smackerFile: %s", _smackerFile.c_str()); debug("size: x %d, y %d", _width, _height); Common::String description; for (uint32 i = 0; i < _positions.size(); i++) { description += Common::String::format("(x %d, y %d) ", _positions[i].x, _positions[i].y); } debug("positions: %s", description.c_str()); description.clear(); for (uint32 i = 0; i < _sizes.size(); i++) { description += Common::String::format("(l %d, t %d, r %d, b %d) ", _sizes[i].left, _sizes[i].top, _sizes[i].right, _sizes[i].bottom); } debug("sizes: %s", description.c_str()); debug("frameRateOverride): %d", _frameRateOverride); debug("preload: %d", _preload); debug("loop: %d", _loop); }
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 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 }
POSIXSaveFileManager::POSIXSaveFileManager() { // Register default savepath. #if defined(SAMSUNGTV) ConfMan.registerDefault("savepath", "/mtd_wiselink/scummvm savegames"); #else Common::String savePath; #if defined(MACOSX) const char *home = getenv("HOME"); if (home && *home && strlen(home) < MAXPATHLEN) { savePath = home; savePath += "/Documents/ScummVM Savegames"; ConfMan.registerDefault("savepath", savePath); } #else const char *envVar; // Previously we placed our default savepath in HOME. If the directory // still exists, we will use it for backwards compatability. envVar = getenv("HOME"); if (envVar && *envVar) { savePath = envVar; savePath += "/.scummvm"; struct stat sb; if (stat(savePath.c_str(), &sb) != 0 || !S_ISDIR(sb.st_mode)) { savePath.clear(); } } if (savePath.empty()) { Common::String prefix; // On POSIX systems we follow the XDG Base Directory Specification for // where to store files. The version we based our code upon can be found // over here: http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html envVar = getenv("XDG_DATA_HOME"); if (!envVar || !*envVar) { envVar = getenv("HOME"); if (envVar && *envVar) { prefix = envVar; savePath = ".local/share/"; } } else { prefix = envVar; } // Our default save path is '$XDG_DATA_HOME/scummvm/saves' savePath += "scummvm/saves"; if (!Posix::assureDirectoryExists(savePath, prefix.c_str())) { savePath.clear(); } else { savePath = prefix + '/' + savePath; } } if (!savePath.empty() && savePath.size() < MAXPATHLEN) { ConfMan.registerDefault("savepath", savePath); } #endif // The user can override the savepath with the SCUMMVM_SAVEPATH // environment variable. This is weaker than a --savepath on the // command line, but overrides the default savepath. // // To ensure that the command line option (if given) has precedence, // we only set the value in the transient domain if it is not // yet present there. if (!ConfMan.hasKey("savepath", Common::ConfigManager::kTransientDomain)) { const char *dir = getenv("SCUMMVM_SAVEPATH"); if (dir && *dir && strlen(dir) < MAXPATHLEN) { Common::FSNode saveDir(dir); if (!saveDir.exists()) { warning("Ignoring non-existent SCUMMVM_SAVEPATH '%s'", dir); } else if (!saveDir.isWritable()) { warning("Ignoring non-writable SCUMMVM_SAVEPATH '%s'", dir); } else { ConfMan.set("savepath", dir, Common::ConfigManager::kTransientDomain); } } } #endif }
void textNewLine() { _lines++; _lineWidth = 0; _line.clear(); }
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(); } */ } }
Common::Error GagEngine::StateScript() { Common::Error status(Common::kNoError); Common::File file; if(file.open(_script, *_archive)) { // simple script parsing: skim through, find section and execute commands ParserState parse_state(PS_SECTION_SEARCH); Common::String buffer; Common::String command_name; bool done(false); while(!file.eos()) { // add space for multiline command support Common::String line = file.readLine() + ' '; if(file.err()) { status = Common::Error(Common::kReadingFailed, _script + ", readLine()"); break; } bool skip_line(false); for(Common::String::const_iterator it = line.begin(); it != line.end(); ++it) { switch(parse_state) { case PS_SECTION_SEARCH: // section if(*it == '[') { buffer.clear(); parse_state = PS_SECTION_NAME; } break; case PS_SECTION_NAME: // section end if(*it == ']') { #ifdef DEBUG_SKIM_SCRIPT parse_state = PS_SECTION_BODY; #else if(buffer == _section) parse_state = PS_SECTION_BODY; else if(buffer == _END_SECTION) { status = Common::Error(Common::kUnknownError, "[" + _section + "] script section not found"); skip_line = true; done = true; } else parse_state = PS_SECTION_SEARCH; #endif } else buffer += *it; break; case PS_SECTION_BODY: // section if(*it == '[') { #ifdef DEBUG_SKIM_SCRIPT buffer.clear(); parse_state = PS_SECTION_NAME; #else skip_line = true; done = true; #endif } // comment else if(*it == '*') { skip_line = true; } //NOTE: invalid syntax else if(*it == '-' || *it == '/' || (*it >= 'A' && *it <= 'Z')) { #ifndef DEBUG_SKIM_SCRIPT warning("invalid script syntax [file: %s, section: %s, line: \"%s\"], skipped", _script.c_str(), _section.c_str(), line.c_str()); #endif skip_line = true; } // command name else if((*it >= 'a' && *it <= 'z')) { buffer.clear(); buffer += *it; parse_state = PS_COMMAND_NAME; } break; case PS_COMMAND_NAME: // command value if(*it == '=') { command_name = buffer; buffer.clear(); parse_state = PS_COMMAND_VALUE; } else buffer += *it; break; case PS_COMMAND_VALUE: // command value end if(*it == ';') { command_name.trim(); buffer.trim(); #ifndef DEBUG_SKIM_SCRIPT debug("%s=%s;", command_name.c_str(), buffer.c_str()); #endif Common::HashMap<Common::String, Common::Error (GagEngine::*)(const Common::String &)>::const_iterator f = _commandCallbacks.find(command_name); if(f != _commandCallbacks.end()) { status = (this->*f->_value)(buffer); if(status.getCode() != Common::kNoError) { skip_line = true; done = true; } } parse_state = PS_SECTION_BODY; } else buffer += *it; break; } if(skip_line) break; } if(done) break; } } else { status = Common::Error(Common::kReadingFailed, _script + ", open()"); } return status; }
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 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; }
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 AgiEngine::predictiveDialog() { int key = 0, active = -1, lastactive = 0; bool rc = false; uint8 x; int y; int bx[17], by[17]; Common::String prefix; char temp[MAXWORDLEN + 1], repeatcount[MAXWORDLEN]; AgiBlock tmpwindow; bool navigationwithkeys = false; bool processkey; const char *buttonStr[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }; const char *buttons[] = { "(1)'-.&", "(2)abc", "(3)def", "(4)ghi", "(5)jkl", "(6)mno", "(7)pqrs", "(8)tuv", "(9)wxyz", "(#)next", "add", "<", "Cancel", "OK", "Pre", "(0) ", NULL }; const int colors[] = { 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 12, 15, 12, 15, 0, 15, 0, 15, 0, 14, 0, 15, 0, 0, 0 }; const char *modes[] = { "(*)Pre", "(*)123", "(*)Abc" }; // FIXME: Move this to a more appropriate place. if (!_predictiveDictText) { loadDict(); if (!_predictiveDictText) return false; } _predictiveDictActLine = NULL; uint8 numMatchingWords = 0; _predictiveDialogRunning = true; _system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, true); memset(repeatcount, 0, sizeof(repeatcount)); // show the predictive dialog. // if another window is already in display, save its state into tmpwindow memset(&tmpwindow, 0, sizeof(tmpwindow)); tmpwindow.active = false; if (_game.window.active) memcpy(&tmpwindow, &(_game.window), sizeof(AgiBlock)); drawWindow(50, 40, 269, 159); _gfx->drawRectangle(62, 54, 249, 66, MSG_BOX_TEXT); _gfx->flushBlock(62, 54, 249, 66); bx[15] = 73; // Zero/space by[15] = 120; bx[9] = 110; // next by[9] = 120; bx[10] = 172; // add by[10] = 120; bx[14] = 200; // Mode by[14] = 120; bx[11] = 252; // Backspace by[11] = 57; bx[12] = 180; // Cancel by[12] = 140; bx[13] = 240; // OK by[13] = 140; x = 73; y = 75; for (int i = 0; i < 9; i++) { bx[i] = x; by[i] = y; x += 60; if (i % 3 == 2) { y += 15; x = 73; } } clearKeyQueue(); prefix.clear(); _currentCode.clear(); _currentWord.clear(); _wordNumber = 0; int mode = kModePre; bool needRefresh = true; for (;;) { if (needRefresh) { for (int i = 0; buttons[i]; i++) { int color1 = colors[i * 2]; int color2 = colors[i * 2 + 1]; if (i == 9 && !((mode != kModeAbc && _predictiveDictActLine && numMatchingWords > 1) || (mode == kModeAbc && _currentWord.size() && _currentWord.lastChar() != ' '))) { // Next color2 = 7; } // needs fixing, or remove it! bool _addIsActive = false; // FIXME: word adding is not implemented if (i == 10 && !_addIsActive) { // Add color2 = 7; } if (i == 14) { _gfx->drawDefaultStyleButton(bx[i], by[i], modes[mode], i == active, 0, color1, color2); } else { _gfx->drawDefaultStyleButton(bx[i], by[i], buttons[i], i == active, 0, color1, color2); } } Common::strlcpy(temp, prefix.c_str(), sizeof(temp)); Common::strlcat(temp, _currentWord.c_str(), sizeof(temp)); for (int i = prefix.size() + _currentCode.size(); i < MAXWORDLEN; i++) temp[i] = ' '; temp[MAXWORDLEN] = 0; printText(temp, 0, 8, 7, MAXWORDLEN, 15, 0); _gfx->flushBlock(62, 54, 249, 66); if (active >= 0 && !navigationwithkeys) { // provide visual feedback only when not navigating with the arrows // so that the user can see the active button. active = -1; needRefresh = true; } else needRefresh = false; _gfx->doUpdate(); } pollTimer(); key = doPollKeyboard(); processkey = false; switch (key) { case KEY_ENTER: if (navigationwithkeys) { // when the user has utilized arrow key navigation, // interpret enter as 'click' on the active button active = lastactive; } else { // else it is a shortcut for 'Ok' active = 13; } processkey = true; break; case KEY_ESCAPE: rc = false; goto getout; case BUTTON_LEFT: navigationwithkeys = false; for (int i = 0; buttons[i]; i++) { if (_gfx->testButton(bx[i], by[i], buttons[i])) { active = i; processkey = true; break; } } break; case KEY_BACKSPACE: active = 11; processkey = true; break; case '#': active = 9; processkey = true; break; case '*': active = 14; processkey = true; break; case 0x09: // Tab navigationwithkeys = true; debugC(3, kDebugLevelText, "Focus change"); lastactive = active = lastactive + 1; active %= ARRAYSIZE(buttons) - 1; needRefresh = true; break; case KEY_LEFT: navigationwithkeys = true; if (lastactive == 0 || lastactive == 3 || lastactive == 6) active = lastactive + 2; else if (lastactive == 9) active = 15; else if (lastactive == 11) active = 11; else if (lastactive == 12) active = 13; else if (lastactive == 14) active = 10; else active = lastactive - 1; lastactive = active; needRefresh = true; break; case KEY_RIGHT: navigationwithkeys = true; if (lastactive == 2 || lastactive == 5 || lastactive == 8) active = lastactive - 2; else if (lastactive == 10) active = 14; else if (lastactive == 11) active = 11; else if (lastactive == 13) active = 12; else if (lastactive == 15) active = 9; else active = lastactive + 1; lastactive = active; needRefresh = true; break; case KEY_UP: navigationwithkeys = true; if (lastactive <= 2) active = 11; else if (lastactive == 9 || lastactive == 10) active = lastactive - 2; else if (lastactive == 11) active = 13; else if (lastactive == 14) active = 8; else if (lastactive == 15) active = 6; else active = lastactive - 3; lastactive = active; needRefresh = true; break; case KEY_DOWN: navigationwithkeys = true; if (lastactive == 6) active = 15; else if (lastactive == 7 || lastactive == 8) active = lastactive + 2; else if (lastactive == 11) active = 0; else if (lastactive == 12 || lastactive == 13) active = 11; else if (lastactive == 14 || lastactive == 15) active = lastactive - 2; else active = lastactive + 3; lastactive = active; needRefresh = true; break; default: // handle numeric buttons if (key >= '1' && key <= '9') { active = key - '1'; processkey = true; } else if (key == '0') { active = 15; processkey = true; } break; } if (processkey) { if (active >= 0) { needRefresh = true; lastactive = active; if (active == 15 && mode != kModeNum) { // Space // bring MRU word at the top of the list when changing words if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0) bringWordtoTop(_predictiveDictActLine, _wordNumber); strncpy(temp, _currentWord.c_str(), _currentCode.size()); temp[_currentCode.size()] = 0; prefix += temp; prefix += " "; _currentCode.clear(); _currentWord.clear(); numMatchingWords = 0; memset(repeatcount, 0, sizeof(repeatcount)); } else if (active < 9 || active == 11 || active == 15) { // number or backspace if (active == 11) { // backspace if (_currentCode.size()) { repeatcount[_currentCode.size() - 1] = 0; _currentCode.deleteLastChar(); } else { if (prefix.size()) prefix.deleteLastChar(); } } else if (prefix.size() + _currentCode.size() < MAXWORDLEN - 1) { // don't overflow the dialog line if (active == 15) { // zero _currentCode += buttonStr[9]; } else { _currentCode += buttonStr[active]; } } switch (mode) { case kModeNum: _currentWord = _currentCode; break; case kModePre: if (!matchWord() && _currentCode.size()) { _currentCode.deleteLastChar(); matchWord(); } numMatchingWords = countWordsInString(_predictiveDictActLine); break; case kModeAbc: for (x = 0; x < _currentCode.size(); x++) if (_currentCode[x] >= '1') temp[x] = buttons[_currentCode[x] - '1'][3 + repeatcount[x]]; temp[_currentCode.size()] = 0; _currentWord = temp; } } else if (active == 9) { // next if (mode == kModePre) { if (_predictiveDictActLine && numMatchingWords > 1) { _wordNumber = (_wordNumber + 1) % numMatchingWords; char tmp[MAXLINELEN]; strncpy(tmp, _predictiveDictActLine, MAXLINELEN); tmp[MAXLINELEN - 1] = 0; char *tok = strtok(tmp, " "); for (uint8 i = 0; i <= _wordNumber; i++) tok = strtok(NULL, " "); _currentWord = Common::String(tok, _currentCode.size()); } } else if (mode == kModeAbc){ x = _currentCode.size(); if (x) { if (_currentCode.lastChar() == '1' || _currentCode.lastChar() == '7' || _currentCode.lastChar() == '9') repeatcount[x - 1] = (repeatcount[x - 1] + 1) % 4; else repeatcount[x - 1] = (repeatcount[x - 1] + 1) % 3; if (_currentCode.lastChar() >= '1') _currentWord.setChar(buttons[_currentCode[x - 1] - '1'][3 + repeatcount[x - 1]], x-1); } } } else if (active == 10) { // add debug(0, "add"); } else if (active == 13) { // Ok // bring MRU word at the top of the list when ok'ed out of the dialog if (mode == kModePre && _predictiveDictActLine && numMatchingWords > 1 && _wordNumber != 0) bringWordtoTop(_predictiveDictActLine, _wordNumber); rc = true; goto press; } else if (active == 14) { // Mode mode++; if (mode > kModeAbc) mode = kModePre; // truncate current input at mode change strncpy(temp, _currentWord.c_str(), _currentCode.size()); temp[_currentCode.size()] = 0; prefix += temp; _currentCode.clear(); _currentWord.clear(); memset(repeatcount, 0, sizeof(repeatcount)); } else { goto press; } } } } press: Common::strlcpy(_predictiveResult, prefix.c_str(), sizeof(_predictiveResult)); Common::strlcat(_predictiveResult, _currentWord.c_str(), sizeof(_predictiveResult)); getout: // if another window was shown, bring it up again if (!tmpwindow.active) closeWindow(); else { _gfx->restoreBlock(_game.window.x1, _game.window.y1, _game.window.x2, _game.window.y2, _game.window.buffer); free(_game.window.buffer); memcpy(&(_game.window), &tmpwindow, sizeof(AgiBlock)); _gfx->doUpdate(); } _system->setFeatureState(OSystem::kFeatureDisableKeyFiltering, false); _predictiveDialogRunning = false; return rc; }
void Lingo::func_mci(Common::String &s) { Common::String params[5]; MCITokenType command = kMCITokenNone; s.trim(); s.toLowercase(); MCITokenType state = kMCITokenNone; Common::String token; const char *ptr = s.c_str(); int respos = -1; while (*ptr) { while (*ptr && *ptr == ' ') ptr++; token.clear(); while (*ptr && *ptr != ' ') token += *ptr++; switch (state) { case kMCITokenNone: { MCIToken *f = MCITokens; while (f->token) { if (command == f->command && token == f->token) break; f++; } if (command == kMCITokenNone) { // We caught command command = f->flag; // Switching to processing this command parameters } else if (f->flag == kMCITokenNone) { // Unmatched token, storing as filename if (!params[0].empty()) warning("Duplicate filename in MCI command: %s -> %s", params[0].c_str(), token.c_str()); params[0] = token; } else { // This is normal parameter, storing next token to designated position if (f->pos > 0) { // This is normal parameter state = f->flag; respos = f->pos; } else { // This is boolean params[-f->pos] = "true"; state = kMCITokenNone; } } break; } default: params[respos] = token; state = kMCITokenNone; break; } } switch (command) { case kMCITokenOpen: { warning("MCI open file: %s, type: %s, alias: %s buffer: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str()); Common::File *file = new Common::File(); if (!file->open(params[0])) { warning("Failed to open %s", params[0].c_str()); delete file; return; } if (params[1] == "waveaudio") { Audio::AudioStream *sound = Audio::makeWAVStream(file, DisposeAfterUse::YES); _audioAliases[params[2]] = sound; } else { warning("Unhandled audio type %s", params[2].c_str()); } } break; case kMCITokenPlay: { warning("MCI play file: %s, from: %s, to: %s, repeat: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str()); if (!_audioAliases.contains(params[0])) { warning("Unknown alias %s", params[0].c_str()); return; } uint32 from = strtol(params[1].c_str(), 0, 10); uint32 to = strtol(params[2].c_str(), 0, 10); _vm->getSoundManager()->playMCI(*_audioAliases[params[0]], from, to); } break; default: warning("Unhandled MCI command: %s", s.c_str()); } }
void MacText::splitString(Common::String &str) { const char *s = str.c_str(); Common::String tmp; bool prevCR = false; if (_textLines.empty()) { _textLines.resize(1); _textLines[0].chunks.push_back(_defaultFormatting); } int curLine = _textLines.size() - 1; int curChunk = _textLines[curLine].chunks.size() - 1; bool nextChunk = false; MacFontRun previousFormatting; while (*s) { #if DEBUG for (uint i = 0; i < _textLines.size(); i++) { debugN(7, "%2d ", i); for (uint j = 0; j < _textLines[i].chunks.size(); j++) debugN(7, "[%d] \"%s\"", _textLines[i].chunks[j].fontId, _textLines[i].chunks[j].text.c_str()); debug(7, " --> %c %d, '%s'", (*s > 0x20 ? *s : ' '), (byte)*s, tmp.c_str()); } #endif if (*s == '\001') { s++; if (*s == '\001') { // Copy it verbatim } else { if (*s++ != '\015') error("MacText: formatting error"); uint16 fontId = *s++; fontId = (fontId << 8) | *s++; byte textSlant = *s++; byte unk3f = *s++; uint16 fontSize = *s++; fontSize = (fontSize << 8) | *s++; uint16 palinfo1 = *s++; palinfo1 = (palinfo1 << 8) | *s++; uint16 palinfo2 = *s++; palinfo2 = (palinfo2 << 8) | *s++; uint16 palinfo3 = *s++; palinfo3 = (palinfo3 << 8) | *s++; debug(8, "******** splitString: fontId: %d, textSlant: %d, unk3: %d, fontSize: %d, p0: %x p1: %x p2: %x", fontId, textSlant, unk3f, fontSize, palinfo1, palinfo2, palinfo3); previousFormatting = _currentFormatting; _currentFormatting.setValues(_wm, fontId, textSlant, unk3f, fontSize, palinfo1, palinfo2, palinfo3); if (curLine == 0 && curChunk == 0 && tmp.empty()) previousFormatting = _currentFormatting; nextChunk = true; } } else if (*s == '\n' && prevCR) { // trean \r\n as one prevCR = false; s++; continue; } else if (*s == '\r') { prevCR = true; } if (*s == '\r' || *s == '\n' || nextChunk) { Common::Array<Common::String> text; if (!nextChunk) previousFormatting = _currentFormatting; int w = getLineWidth(curLine, true); previousFormatting.getFont()->wordWrapText(tmp, _maxWidth, text, w); tmp.clear(); if (text.size()) { for (uint i = 0; i < text.size(); i++) { _textLines[curLine].chunks[curChunk].text = text[i]; if ((text.size() > 1 || !nextChunk) && !(i == text.size() - 1 && nextChunk)) { curLine++; _textLines.resize(curLine + 1); _textLines[curLine].chunks.push_back(previousFormatting); curChunk = 0; } } if (nextChunk) { curChunk++; _textLines[curLine].chunks.push_back(_currentFormatting); } else { _textLines[curLine].chunks[0] = _currentFormatting; } } else { if (nextChunk) { // No text, replacing formatting _textLines[curLine].chunks[curChunk] = _currentFormatting; } else { // Otherwise it is an empty line curLine++; _textLines.resize(curLine + 1); _textLines[curLine].chunks.push_back(previousFormatting); curChunk = 0; } } if (!nextChunk) // Don't skip next character s++; nextChunk = false; continue; } tmp += *s; s++; } if (tmp.size()) { Common::Array<Common::String> text; int w = getLineWidth(curLine, true); _currentFormatting.getFont()->wordWrapText(tmp, _maxWidth, text, w); _textLines[curLine].chunks[curChunk].text = text[0]; if (text.size() > 1) { for (uint i = 1; i < text.size(); i++) { curLine++; _textLines.resize(curLine + 1); _textLines[curLine].chunks.push_back(_currentFormatting); _textLines[curLine].chunks[0].text = text[i]; } } } }
void Dialog::show(Scene *scene, uint16 addr, uint16 animation1, uint16 animation2, byte color1, byte color2, byte slot1, byte slot2) { debug(0, "Dialog::show(%04x, %u:%u, %u:%u)", addr, slot1, animation1, slot2, animation2); Resources *res = Resources::instance(); int n = 0; Common::String message; byte color = color1; if (animation1 != 0) { SceneEvent e(SceneEvent::kPlayAnimation); e.animation = animation1; e.slot = 0xc0 | slot1; //looped, paused scene->push(e); } if (animation2 != 0) { SceneEvent e(SceneEvent::kPlayAnimation); e.animation = animation2; e.slot = 0xc0 | slot2; //looped, paused scene->push(e); } while (n < 4) { byte c = res->eseg.get_byte(addr++); //debug(0, "%02x: %c", c, c > 0x20? c: '.'); switch (c) { case 0: ++n; switch (n) { case 1: //debug(0, "new line\n"); if (!message.empty()) message += '\n'; break; case 2: //debug(0, "displaymessage %s", message.c_str()); if (color == color2) { //pause animation in other slot { SceneEvent e(SceneEvent::kPauseAnimation); e.slot = 0x80 | slot1; scene->push(e); } { SceneEvent e(SceneEvent::kPlayAnimation); e.animation = animation2; e.slot = 0x80 | slot2; scene->push(e); } } else if (color == color1) { //pause animation in other slot { SceneEvent e(SceneEvent::kPauseAnimation); e.slot = 0x80 | slot2; scene->push(e); } { SceneEvent e(SceneEvent::kPlayAnimation); e.animation = animation1; e.slot = 0x80 | slot1; scene->push(e); } } { message.trim(); if (message.empty()) break; SceneEvent e(SceneEvent::kMessage); e.message = message; e.color = color; if (color == color1) e.slot = slot1; if (color == color2) e.slot = slot2; scene->push(e); message.clear(); } break; case 3: color = color == color1 ? color2 : color1; //debug(0, "changing color to %02x", color); break; } break; case 0xff: { //fixme : wait for the next cycle of the animation } break; default: message += c; n = 0; } } SceneEvent e(SceneEvent::kClearAnimations); scene->push(e); }
void Scripts::cmdTexChoice() { // MM is defining 2 times the last range in the original. static const int BTN_RANGES_v1[BTN_COUNT][2] = { { 0, 60 }, { 64, 124 }, { 129, 192 }, { 194, 227 }, { 233, 292 }, { 297, 319 } }; static const int BTN_RANGES_v2[BTN_COUNT][2] = { { 0, 76 }, { 77, 154 }, { 155, 232 }, { 233, 276 }, { 0, 0 }, { 277, 319 } }; _vm->_oldRects.clear(); _choiceStart = _data->pos() - 1; _vm->_fonts._charSet._lo = 1; _vm->_fonts._charSet._hi = 8; _vm->_fonts._charFor._hi = 255; if (_vm->getGameID() == GType_MartianMemorandum) { _vm->_fonts._charFor._lo = 247; _vm->_screen->_maxChars = 23; } else { _vm->_fonts._charFor._lo = 55; _vm->_screen->_maxChars = 20; } _vm->_screen->_printOrg = _texsOrg; _vm->_screen->_printStart = _texsOrg; _vm->_bubbleBox->clearBubbles(); _vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 1"); byte v; Common::String tmpStr = ""; while ((v = _data->readByte()) != 0) tmpStr += (char)v; _vm->_bubbleBox->calcBubble(tmpStr); _vm->_bubbleBox->printBubble(tmpStr); Common::Array<Common::Rect> responseCoords; responseCoords.push_back(_vm->_bubbleBox->_bounds); _vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11); findNull(); tmpStr.clear(); while ((v = _data->readByte()) != 0) tmpStr += (char)v; if (tmpStr.size() != 0) { _vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 2"); _vm->_bubbleBox->calcBubble(tmpStr); _vm->_bubbleBox->printBubble(tmpStr); responseCoords.push_back(_vm->_bubbleBox->_bounds); _vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11); } findNull(); bool choice3Fl = false; tmpStr.clear(); while ((v = _data->readByte()) != 0) tmpStr += (char)v; if (tmpStr.size() != 0) { _vm->_bubbleBox->_bubbleDisplStr = Common::String("RESPONSE 3"); _vm->_bubbleBox->calcBubble(tmpStr); _vm->_bubbleBox->printBubble(tmpStr); responseCoords.push_back(_vm->_bubbleBox->_bounds); _vm->_screen->_printOrg.y = _vm->_bubbleBox->_bounds.bottom + ((_vm->getGameID() == GType_MartianMemorandum) ? 20 : 11); } findNull(); int choice = -1; do { _vm->_events->pollEvents(); if (_vm->shouldQuit()) return; charLoop(); _vm->_bubbleBox->_bubbleDisplStr = _vm->_bubbleBox->_bubbleTitle; if (_vm->_events->_leftButton) { if (_vm->_events->_mouseRow >= ((_vm->getGameID() == GType_MartianMemorandum) ? 23 : 22)) { _vm->_events->debounceLeft(); int x = _vm->_events->_mousePos.x; for (int i = 0; i < BTN_COUNT; i++) { if (((_vm->getGameID() == GType_MartianMemorandum) && (x >= BTN_RANGES_v1[i][0]) && (x < BTN_RANGES_v1[i][1])) || ((_vm->getGameID() == GType_Amazon) && (x >= BTN_RANGES_v2[i][0]) && (x < BTN_RANGES_v2[i][1]))) { choice = i; break; } } } else { _vm->_events->debounceLeft(); choice = _vm->_events->checkMouseBox1(responseCoords); } } } while ((choice == -1) || ((choice == 2) && choice3Fl)); _choice = choice + 1; _vm->_bubbleBox->clearBubbles(); }
void TextRenderer::drawTextWithWordWrapping(const Common::String &text, Graphics::Surface &dest) { Common::Array<TextSurface> textSurfaces; Common::Array<uint> lineWidths; Common::Array<TextJustification> lineJustifications; // Create the initial text state TextStyleState currentState; // Create an empty font and bind it to the state StyledTTFont font(_engine); currentState.updateFontWithTextState(font); Common::String currentSentence; // Not a true 'grammatical' sentence. Rather, it's just a collection of words Common::String currentWord; int sentenceWidth = 0; int wordWidth = 0; int lineWidth = 0; int lineHeight = font.getFontHeight(); uint currentLineNumber = 0u; uint numSpaces = 0u; int spaceWidth = 0; // The pixel offset to the currentSentence Common::Point sentencePixelOffset; uint i = 0u; uint stringlen = text.size(); while (i < stringlen) { if (text[i] == '<') { // Flush the currentWord to the currentSentence currentSentence += currentWord; sentenceWidth += wordWidth; // Reset the word variables currentWord.clear(); wordWidth = 0; // Parse the style tag uint startTextPosition = i; while (i < stringlen && text[i] != '>') { ++i; } uint endTextPosition = i; uint32 textColor = currentState.getTextColor(_engine); uint stateChanges = 0u; if ((endTextPosition - startTextPosition - 1) > 0) { stateChanges = currentState.parseStyle(Common::String(text.c_str() + startTextPosition + 1), endTextPosition - startTextPosition - 1); } if (stateChanges & (TEXT_CHANGE_FONT_TYPE | TEXT_CHANGE_FONT_STYLE)) { // Use the last state to render out the current sentence // Styles apply to the text 'after' them if (!currentSentence.empty()) { textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); lineWidth += sentenceWidth; sentencePixelOffset.x += sentenceWidth; // Reset the sentence variables currentSentence.clear(); sentenceWidth = 0; } // Update the current state with the style information currentState.updateFontWithTextState(font); lineHeight = MAX(lineHeight, font.getFontHeight()); spaceWidth = font.getCharWidth(' '); } if (stateChanges & TEXT_CHANGE_NEWLINE) { // If the current sentence has content, render it out if (!currentSentence.empty()) { textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); } // Set line width lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); currentSentence.clear(); sentenceWidth = 0; // Update the offsets sentencePixelOffset.x = 0u; sentencePixelOffset.y += lineHeight; // Reset the line variables lineHeight = font.getFontHeight(); lineWidth = 0; ++currentLineNumber; lineJustifications.push_back(currentState._justification); } if (stateChanges & TEXT_CHANGE_HAS_STATE_BOX) { Common::String temp = Common::String::format("%d", _engine->getScriptManager()->getStateValue(currentState._statebox)); wordWidth += font.getStringWidth(temp); // If the word causes the line to overflow, render the sentence and start a new line if (lineWidth + sentenceWidth + wordWidth > dest.w) { textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, textColor), sentencePixelOffset, currentLineNumber)); // Set line width lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); currentSentence.clear(); sentenceWidth = 0; // Update the offsets sentencePixelOffset.x = 0u; sentencePixelOffset.y += lineHeight; // Reset the line variables lineHeight = font.getFontHeight(); lineWidth = 0; ++currentLineNumber; lineJustifications.push_back(currentState._justification); } } } else { currentWord += text[i]; wordWidth += font.getCharWidth(text[i]); if (text[i] == ' ') { // When we hit the first space, flush the current word to the sentence if (!currentWord.empty()) { currentSentence += currentWord; sentenceWidth += wordWidth; currentWord.clear(); wordWidth = 0; } // We track the number of spaces so we can disregard their width in lineWidth calculations ++numSpaces; } else { // If the word causes the line to overflow, render the sentence and start a new line if (lineWidth + sentenceWidth + wordWidth > dest.w) { // Only render out content if (!currentSentence.empty()) { textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } // Set line width lineWidths.push_back(lineWidth + sentenceWidth - (numSpaces * spaceWidth)); currentSentence.clear(); sentenceWidth = 0; // Update the offsets sentencePixelOffset.x = 0u; sentencePixelOffset.y += lineHeight; // Reset the line variables lineHeight = font.getFontHeight(); lineWidth = 0; ++currentLineNumber; lineJustifications.push_back(currentState._justification); } numSpaces = 0u; } } i++; } // Render out any remaining words/sentences if (!currentWord.empty() || !currentSentence.empty()) { currentSentence += currentWord; sentenceWidth += wordWidth; textSurfaces.push_back(TextSurface(font.renderSolidText(currentSentence, currentState.getTextColor(_engine)), sentencePixelOffset, currentLineNumber)); } lineWidths.push_back(lineWidth + sentenceWidth); lineJustifications.push_back(currentState._justification); for (Common::Array<TextSurface>::iterator iter = textSurfaces.begin(); iter != textSurfaces.end(); ++iter) { Common::Rect empty; if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_LEFT) { _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_CENTER) { _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, ((dest.w - lineWidths[iter->_lineNumber]) / 2) + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); } else if (lineJustifications[iter->_lineNumber] == TEXT_JUSTIFY_RIGHT) { _engine->getRenderManager()->blitSurfaceToSurface(*iter->_surface, empty, dest, dest.w - lineWidths[iter->_lineNumber] + iter->_surfaceOffset.x, iter->_surfaceOffset.y, 0); } // Release memory iter->_surface->free(); delete iter->_surface; } }