Common::String Menu::prepareSaveNameForDisplay(const Common::String &name) { Common::String display = name; display.toUppercase(); if (display.hasSuffix(".M3S")) { display.deleteLastChar(); display.deleteLastChar(); display.deleteLastChar(); display.deleteLastChar(); } while (display.size() > 17) display.deleteLastChar(); return display; }
void SavesSyncRequest::start() { //cleanup _ignoreCallback = true; if (_workingRequest) _workingRequest->finish(); _currentDownloadingFile = StorageFile(); _currentUploadingFile = ""; _filesToDownload.clear(); _filesToUpload.clear(); _localFilesTimestamps.clear(); _totalFilesToHandle = 0; _ignoreCallback = false; //load timestamps _localFilesTimestamps = DefaultSaveFileManager::loadTimestamps(); //list saves directory Common::String dir = _storage->savesDirectoryPath(); if (dir.lastChar() == '/') dir.deleteLastChar(); _workingRequest = _storage->listDirectory( dir, new Common::Callback<SavesSyncRequest, Storage::ListDirectoryResponse>(this, &SavesSyncRequest::directoryListedCallback), new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryListedErrorCallback) ); if (!_workingRequest) finishError(Networking::ErrorResponse(this)); }
void Menu::handleInput(const Common::KeyState &e) { uint16 node = _vm->_state->getLocationNode(); uint16 room = _vm->_state->getLocationRoom(); uint16 item = _vm->_state->getMenuSaveLoadSelectedItem(); if (room != 901 || node != 300 || item != 7) return; Common::String display = prepareSaveNameForDisplay(_saveName); if (e.keycode == Common::KEYCODE_BACKSPACE || e.keycode == Common::KEYCODE_DELETE) { display.deleteLastChar(); _saveName = display; return; } else if (e.keycode == Common::KEYCODE_RETURN || e.keycode == Common::KEYCODE_KP_ENTER) { saveMenuSave(); return; } if (((e.ascii >= 'a' && e.ascii <= 'z') || (e.ascii >= 'A' && e.ascii <= 'Z') || (e.ascii >= '0' && e.ascii <= '9') || e.ascii == ' ') && (display.size() < 17)) { display += e.ascii; display.toUppercase(); _saveName = display; } }
void bringWordtoTop(char *str, int wordnum) { // This function reorders the words on the given pred.dic line // by moving the word at position 'wordnum' to the front (that is, right behind // right behind the numerical code word at the start of the line). Common::Array<Common::String> words; char buf[MAXLINELEN]; if (!str) return; strncpy(buf, str, MAXLINELEN); buf[MAXLINELEN - 1] = 0; char *word = strtok(buf, " "); if (!word) { debug("Invalid dictionary line"); return; } words.push_back(word); while ((word = strtok(NULL, " ")) != NULL) words.push_back(word); words.insert_at(1, words.remove_at(wordnum + 1)); Common::String tmp; for (uint8 i = 0; i < words.size(); i++) tmp += words[i] + " "; tmp.deleteLastChar(); memcpy(str, tmp.c_str(), strlen(str)); }
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(); }
bool LabelCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) { if (line.lastChar() != ':') { return false; } Common::String label = line; label.deleteLastChar(); LabelCommand *labelCmd = new LabelCommand(label); if (!parseCtx._labels.contains(label)) { parseCtx._labels[label] = labelCmd; } else { warning("Label '%s' already exists", label.c_str()); } if (parseCtx._pendingGotos.contains(label)) { GotoCommands &gotos = parseCtx._pendingGotos[label]; for (GotoCommands::const_iterator it = gotos.begin(); it != gotos.end(); ++it) { (*it)->setLabelCommand(labelCmd); } gotos.clear(); } command = labelCmd; return true; }
void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) { _workingRequest = nullptr; if (_ignoreCallback) return; bool irrecoverable = error.interrupted || error.failed; if (error.failed) { Common::JSONValue *value = Common::JSON::parse(error.response.c_str()); if (value) { if (value->isObject()) { Common::JSONObject object = value->asObject(); //Dropbox-related error: if (object.contains("error_summary") && object.getVal("error_summary")->isString()) { Common::String summary = object.getVal("error_summary")->asString(); if (summary.contains("not_found")) { irrecoverable = false; } } //OneDrive-related error: if (object.contains("error") && object.getVal("error")->isObject()) { Common::JSONObject errorNode = object.getVal("error")->asObject(); if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) { Common::String code = errorNode.getVal("code")->asString(); if (code == "itemNotFound") { irrecoverable = false; } } } } delete value; } //Google Drive and Box-related ScummVM-based error if (error.response.contains("subdirectory not found")) { irrecoverable = false; //base "/ScummVM/" folder not found } else if (error.response.contains("no such file found in its parent directory")) { irrecoverable = false; //"Saves" folder within "/ScummVM/" not found } } if (irrecoverable) { finishError(error); return; } //we're lucky - user just lacks his "/cloud/" folder - let's create one Common::String dir = _storage->savesDirectoryPath(); if (dir.lastChar() == '/') dir.deleteLastChar(); debug(9, "SavesSyncRequest: creating %s", dir.c_str()); _workingRequest = _storage->createDirectory( dir, new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback), new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback) ); if (!_workingRequest) finishError(Networking::ErrorResponse(this)); }
bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) { const char *const EXTENSIONS[3] = { ".ulx", ".blb", ".gblorb" }; // Loop through the files of the folder for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { // Check for a recognised filename if (file->isDirectory()) continue; Common::String filename = file->getName(); bool hasExt = false; for (int idx = 0; idx < 3 && !hasExt; ++idx) hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]); if (!hasExt) continue; // Open up the file and calculate the md5 Common::File gameFile; if (!gameFile.open(*file)) continue; Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000); size_t filesize = gameFile.size(); gameFile.close(); // Check for known games const GlulxeGameDescription *p = GLULXE_GAMES; while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize)) ++p; DetectedGame gd; if (!p->_gameId) { if (filename.hasSuffixIgnoreCase(".blb")) continue; if (gDebugLevel > 0) { // Print an entry suitable for putting into the detection_tables.h, using the // name of the parent folder the game is in as the presumed game Id Common::String folderName = file->getParent().getName(); if (folderName.hasSuffix("\\")) folderName.deleteLastChar(); Common::String fname = filename; const char *dot = strchr(fname.c_str(), '.'); if (dot) fname = Common::String(fname.c_str(), dot); debug("ENTRY0(\"%s\", \"%s\", %u),", fname.c_str(), md5.c_str(), (uint)filesize); } const PlainGameDescriptor &desc = GLULXE_GAME_LIST[0]; gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown); } else { PlainGameDescriptor gameDesc = findGame(p->_gameId); gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra); gd.setGUIOptions(GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)); } gd.addExtraEntry("filename", filename); gameList.push_back(gd); } return !gameList.empty(); }
void MacFontManager::loadFontsBDF() { Common::Archive *dat; dat = Common::makeZipArchive("classicmacfonts.dat"); if (!dat) { warning("Could not find classicmacfonts.dat. Falling back to built-in fonts"); _builtInFonts = true; return; } Common::ArchiveMemberList list; dat->listMembers(list); for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) { Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName()); Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream); delete stream; Common::String fontName; MacFont *macfont; if (font->getFamilyName() && *font->getFamilyName()) { fontName = Common::String::format("%s-%s-%d", font->getFamilyName(), font->getFontSlant(), font->getFontSize()); macfont = new MacFont(_fontNames.getVal(font->getFamilyName(), kMacFontNonStandard), font->getFontSize(), parseFontSlant(font->getFontSlant())); } else { // Get it from the file name fontName = (*it)->getName(); // Trim the .bdf extension for (int i = fontName.size() - 1; i >= 0; --i) { if (fontName[i] == '.') { while ((uint)i < fontName.size()) { fontName.deleteLastChar(); } break; } } macfont = new MacFont(kMacFontNonStandard); macfont->setName(fontName); } FontMan.assignFontToName(fontName, font); //macfont->setFont(font); _fontRegistry.setVal(fontName, macfont); debug(2, " %s", fontName.c_str()); } _builtInFonts = false; delete dat; }
Common::String ScummEngine_v60he::convertFilePath(const byte *src) { debug(2, "convertFilePath in: '%s'", (const char *)src); int srcSize = resStrLen(src); int start = 0; if (srcSize > 2) { if (src[0] == ':') { // Game Data Path (Macintosh) // The default game data path is set to ':' by ScummVM start = 1; } else if (src[0] == '.' && src[1] == '\\') { // Game Data Path (Windows) // The default game data path is set to '.\\' by ScummVM start = 2; } else if (src[0] == '*' && src[1] == '\\') { // Save Game Path (Windows HE72 - HE100) // The default save game path is set to '*\\' by ScummVM start = 2; } else if (src[0] == '*' && src[1] == ':') { // Save Game Path (Macintosh HE72 - HE100) // The default save game path is set to '*:' by ScummVM start = 2; } else if (src[0] == 'c' && src[1] == ':') { // Save Game Path (HE60 - HE71) // The default save path is game path (DOS) or 'c:\\hegames\\' (Windows) for (start = srcSize; start != 0; start--) if (src[start - 1] == '\\') break; } else if (src[0] == 'u' && src[1] == 's') { // Save Game Path (Moonbase Commander) // The default save path is 'user\\' start = 5; } } Common::String dst; for (int i = start; i < srcSize; i++) { // Convert path separators if (src[i] == '\\' || src[i] == ':') dst += '/'; else dst += src[i]; } // Sanity check if (dst.lastChar() == '/') dst.deleteLastChar(); debug(2, "convertFilePath out: '%s'", dst.c_str()); return dst; }
void OneDriveListDirectoryRequest::listNextDirectory() { if (_directoriesQueue.empty()) { finishListing(_files); return; } _currentDirectory = _directoriesQueue.back(); _directoriesQueue.pop_back(); if (_currentDirectory != "" && _currentDirectory.lastChar() != '/' && _currentDirectory.lastChar() != '\\') _currentDirectory += '/'; Common::String dir = _currentDirectory; dir.deleteLastChar(); Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, ConnMan.urlEncode(dir).c_str()); makeRequest(url); }
bool Debugger::cmd_lua_do(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Usage: lua_do <lua command>\n"); return true; } Common::String cmd; for (int i = 1; i < argc; ++i) { cmd += argv[i]; cmd += " "; } cmd.deleteLastChar(); DebugPrintf("Executing command: <%s>\n", cmd.c_str()); cmd = Common::String::format("__temp_fn__ = function()\n%s\nend\nstart_script(__temp_fn__)", cmd.c_str()); g_grim->debugLua(cmd); return true; }
SaveStateList BuriedMetaEngine::listSaves(const char *target) const { // The original had no pattern, so the user must rename theirs // Note that we ignore the target because saves are compatible between // all versions Common::StringArray fileNames = Buried::BuriedEngine::listSaveFiles(); SaveStateList saveList; for (uint32 i = 0; i < fileNames.size(); i++) { // Isolate the description from the file name Common::String desc = fileNames[i].c_str() + 7; for (int j = 0; j < 4; j++) desc.deleteLastChar(); saveList.push_back(SaveStateDescriptor(i, desc)); } return saveList; }
void RemoteBrowserDialog::goUp() { if (_rememberedNodeContents.contains(_node.path())) _rememberedNodeContents.erase(_node.path()); Common::String path = _node.path(); if (path.size() && (path.lastChar() == '/' || path.lastChar() == '\\')) path.deleteLastChar(); if (path.empty()) { _rememberedNodeContents.erase(""); } else { for (int i = path.size() - 1; i >= 0; --i) if (i == 0 || path[i] == '/' || path[i] == '\\') { path.erase(i); break; } } listDirectory(Cloud::StorageFile(path, 0, 0, true)); }
Common::String ThemeEngine::getThemeId(const Common::String &filename) { // If no filename has been given we will initialize the builtin theme if (filename.empty()) return "builtin"; Common::FSNode node(filename); if (!node.exists()) return "builtin"; if (node.getName().matchString("*.zip", true)) { Common::String id = node.getName(); for (int i = 0; i < 4; ++i) id.deleteLastChar(); return id; } else { return node.getName(); } }
static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char * const *directoryGlobs) { if (depth <= 0) return; if (fslist.empty()) return; // First we compose a hashmap of all files in fslist. // Includes nifty stuff like removing trailing dots and ignoring case. for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) { Common::FSList files; if (!directoryGlobs) continue; bool matched = false; for (const char * const *glob = directoryGlobs; *glob; glob++) if (file->getName().matchString(*glob, true)) { matched = true; break; } if (!matched) continue; if (!file->getChildren(files, Common::FSNode::kListAll)) continue; composeFileHashMap(files, allFiles, depth - 1, directoryGlobs); } Common::String tstr = file->getName(); // Strip any trailing dot if (tstr.lastChar() == '.') tstr.deleteLastChar(); allFiles[tstr] = *file; // Record the presence of this file } }
Common::Error GlkEngine::run() { // Open up the game file Common::String filename = getFilename(); if (!Common::File::exists(filename)) return Common::kNoGameDataFoundError; if (Blorb::isBlorb(filename)) { // Blorb archive _blorb = new Blorb(filename, getInterpreterType()); SearchMan.add("blorb", _blorb, 99, false); if (!_gameFile.open("game", *_blorb)) return Common::kNoGameDataFoundError; } else { // Check for a secondary blorb file with the same filename Common::String baseName = filename; while (baseName.contains('.')) baseName.deleteLastChar(); if (Common::File::exists(baseName + ".blorb")) { _blorb = new Blorb(baseName + ".blorb", getInterpreterType()); SearchMan.add("blorb", _blorb, 99, false); } else if (Common::File::exists(baseName + ".blb")) { _blorb = new Blorb(baseName + ".blb", getInterpreterType()); SearchMan.add("blorb", _blorb, 99, false); } // Open up the game file if (!_gameFile.open(filename)) return Common::kNoGameDataFoundError; } // Perform initialization initialize(); // Play the game runGame(); return Common::kNoError; }
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 AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const { if (depth <= 0) return; if (fslist.empty()) return; for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { Common::String tstr = (_matchFullPaths && !parentName.empty() ? parentName + "/" : "") + file->getName(); if (file->isDirectory()) { Common::FSList files; if (!_directoryGlobs) continue; bool matched = false; for (const char * const *glob = _directoryGlobs; *glob; glob++) if (file->getName().matchString(*glob, true)) { matched = true; break; } if (!matched) continue; if (!file->getChildren(files, Common::FSNode::kListAll)) continue; composeFileHashMap(allFiles, files, depth - 1, tstr); } // Strip any trailing dot if (tstr.lastChar() == '.') tstr.deleteLastChar(); allFiles[tstr] = *file; // Record the presence of this file } }
bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) { const char *const EXTENSIONS[] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".dat", ".zip", nullptr }; // Loop through the files of the folder for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { // Check for a recognised filename if (file->isDirectory()) continue; Common::String filename = file->getName(); bool hasExt = Blorb::hasBlorbExt(filename), isBlorb = false; for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext) hasExt = filename.hasSuffixIgnoreCase(*ext); if (!hasExt) continue; // Open up the file and calculate the md5, and get the serial Common::File gameFile; if (!gameFile.open(*file)) continue; Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000); size_t filesize = gameFile.size(); char serial[9] = ""; bool emptyBlorb = false; gameFile.seek(0); isBlorb = Blorb::isBlorb(gameFile, ID_ZCOD); if (!isBlorb) { if (Blorb::hasBlorbExt(filename)) { gameFile.close(); continue; } gameFile.seek(18); strcpy(&serial[0], "\""); gameFile.read(&serial[1], 6); strcpy(&serial[7], "\""); } else { Blorb b(*file, INTERPRETER_FROTZ); Common::SeekableReadStream *f = b.createReadStreamForMember("game"); emptyBlorb = f == nullptr; if (!emptyBlorb) { f->seek(18); strcpy(&serial[0], "\""); f->read(&serial[1], 6); strcpy(&serial[7], "\""); delete f; } } gameFile.close(); // Check for known games. Note that there has been some variation in exact filesizes // for Infocom games due to padding at the end of files. So we match on md5s for the // first 5Kb, and only worry about filesize for more recent Blorb based Zcode games const FrotzGameDescription *p = FROTZ_GAMES; while (p->_gameId && p->_md5 && (md5 != p->_md5 || (filesize != p->_filesize && isBlorb))) ++p; DetectedGame gd; if (!p->_gameId) { // Generic .dat/.zip files don't get reported as matches unless they have a known md5 if (filename.hasSuffixIgnoreCase(".dat") || filename.hasSuffixIgnoreCase(".zip") || emptyBlorb) continue; if (gDebugLevel > 0) { // Print an entry suitable for putting into the detection_tables.h, using the // name of the parent folder the game is in as the presumed game Id Common::String folderName = file->getParent().getName(); if (folderName.hasSuffix("\\")) folderName.deleteLastChar(); Common::String fname = filename; const char *dot = strchr(fname.c_str(), '.'); if (dot) fname = Common::String(fname.c_str(), dot); debug("ENTRY0(\"%s\", %s, \"%s\", %u),", fname.c_str(), strlen(serial) ? serial : "nullptr", md5.c_str(), (uint)filesize); } const PlainGameDescriptor &desc = ZCODE_GAME_LIST[0]; gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown); } else { GameDescriptor gameDesc = findGame(p->_gameId); gd = DetectedGame(p->_gameId, gameDesc._description, p->_language, Common::kPlatformUnknown, p->_extra); gd.setGUIOptions(p->_guiOptions); } gd.addExtraEntry("filename", filename); gameList.push_back(gd); } return !gameList.empty(); }
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 MassAddDialog::handleTickle() { if (_scanStack.empty()) return; // We have finished scanning uint32 t = g_system->getMillis(); // Perform a breadth-first scan of the filesystem. while (!_scanStack.empty() && (g_system->getMillis() - t) < kMaxScanTime) { Common::FSNode dir = _scanStack.pop(); Common::FSList files; if (!dir.getChildren(files, Common::FSNode::kListAll)) { continue; } // Run the detector on the dir GameList candidates(EngineMan.detectGames(files)); // Just add all detected games / game variants. If we get more than one, // that either means the directory contains multiple games, or the detector // could not fully determine which game variant it was seeing. In either // case, let the user choose which entries he wants to keep. // // However, we only add games which are not already in the config file. for (GameList::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) { GameDescriptor result = *cand; Common::String path = dir.getPath(); // Remove trailing slashes while (path != "/" && path.lastChar() == '/') path.deleteLastChar(); // Check for existing config entries for this path/gameid/lang/platform combination if (_pathToTargets.contains(path)) { bool duplicate = false; const StringArray &targets = _pathToTargets[path]; for (StringArray::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) { // If the gameid, platform and language match -> skip it Common::ConfigManager::Domain *dom = ConfMan.getDomain(*iter); assert(dom); if ((*dom)["gameid"] == result["gameid"] && (*dom)["platform"] == result["platform"] && (*dom)["language"] == result["language"]) { duplicate = true; break; } } if (duplicate) { _oldGamesCount++; break; // Skip duplicates } } result["path"] = path; _games.push_back(result); _list->append(result.description()); } // Recurse into all subdirs for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) { if (file->isDirectory()) { _scanStack.push(*file); } } _dirsScanned++; } // Update the dialog Common::String buf; if (_scanStack.empty()) { // Enable the OK button _okButton->setEnabled(true); buf = _("Scan complete!"); _dirProgressText->setLabel(buf); buf = Common::String::format(_("Discovered %d new games, ignored %d previously added games."), _games.size(), _oldGamesCount); _gameProgressText->setLabel(buf); } else { buf = Common::String::format(_("Scanned %d directories ..."), _dirsScanned); _dirProgressText->setLabel(buf); buf = Common::String::format(_("Discovered %d new games, ignored %d previously added games ..."), _games.size(), _oldGamesCount); _gameProgressText->setLabel(buf); } if (_games.size() > 0) { _list->scrollToEnd(); } drawDialog(); }
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(); } }
Common::String Font::handleEllipsis(const Common::String &input, int w) const { Common::String s = input; int width = getStringWidth(s); if (width > w && s.hasSuffix("...")) { // String is too wide. Check whether it ends in an ellipsis // ("..."). If so, remove that and try again! s.deleteLastChar(); s.deleteLastChar(); s.deleteLastChar(); width = getStringWidth(s); } if (width > w) { Common::String str; // String is too wide. So we shorten it "intelligently" by // replacing parts of the string by an ellipsis. There are // three possibilities for this: replace the start, the end, or // the middle of the string. What is best really depends on the // context; but unless we want to make this configurable, // replacing the middle seems to be a good compromise. const int ellipsisWidth = getStringWidth("..."); // SLOW algorithm to remove enough of the middle. But it is good enough // for now. const int halfWidth = (w - ellipsisWidth) / 2; int w2 = 0; Common::String::unsigned_type last = 0; uint i = 0; for (; i < s.size(); ++i) { const Common::String::unsigned_type cur = s[i]; int charWidth = getCharWidth(cur) + getKerningOffset(last, cur); if (w2 + charWidth > halfWidth) break; last = cur; w2 += charWidth; str += cur; } // At this point we know that the first 'i' chars are together 'w2' // pixels wide. We took the first i-1, and add "..." to them. str += "..."; last = '.'; // The original string is width wide. Of those we already skipped past // w2 pixels, which means (width - w2) remain. // The new str is (w2+ellipsisWidth) wide, so we can accommodate about // (w - (w2+ellipsisWidth)) more pixels. // Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) = // (width + ellipsisWidth - w) int skip = width + ellipsisWidth - w; for (; i < s.size() && skip > 0; ++i) { const Common::String::unsigned_type cur = s[i]; skip -= getCharWidth(cur) + getKerningOffset(last, cur); last = cur; } // Append the remaining chars, if any for (; i < s.size(); ++i) { str += s[i]; } return str; } else { return s; } }
/** * Load Sprite Bank */ int AnimationManager::loadSpriteBank(int idx, const Common::String &filename) { int result = 0; Bank[idx]._loadedFl = true; Bank[idx]._filename = filename; byte *fileDataPtr = _vm->_fileIO->loadFile(filename); Bank[idx]._fileHeader = 0; if (fileDataPtr[1] == 'L' && fileDataPtr[2] == 'E') Bank[idx]._fileHeader = 1; else if (fileDataPtr[1] == 'O' && fileDataPtr[2] == 'R') Bank[idx]._fileHeader = 2; if (!Bank[idx]._fileHeader) { _vm->_globals->freeMemory(fileDataPtr); Bank[idx]._loadedFl = false; result = -1; } Bank[idx]._data = fileDataPtr; int objectDataIdx = 0; for(objectDataIdx = 0; objectDataIdx <= 249; objectDataIdx++) { int width = _vm->_objectsMan->getWidth(fileDataPtr, objectDataIdx); int height = _vm->_objectsMan->getHeight(fileDataPtr, objectDataIdx); if (!width && !height) break; } if (objectDataIdx > 249) { _vm->_globals->freeMemory(fileDataPtr); Bank[idx]._loadedFl = false; result = -2; } Bank[idx]._objDataIdx = objectDataIdx; Common::String ofsFilename = Bank[idx]._filename; char ch; do { ch = ofsFilename.lastChar(); ofsFilename.deleteLastChar(); } while (ch != '.'); ofsFilename += ".OFS"; Common::File f; if (f.exists(ofsFilename)) { byte *ofsData = _vm->_fileIO->loadFile(ofsFilename); byte *curOfsData = ofsData; for (int objIdx = 0; objIdx < Bank[idx]._objDataIdx; ++objIdx, curOfsData += 8) { int x1 = READ_LE_INT16(curOfsData); int y1 = READ_LE_INT16(curOfsData + 2); int x2 = READ_LE_INT16(curOfsData + 4); int y2 = READ_LE_INT16(curOfsData + 6); _vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x1, y1, 0); if (Bank[idx]._fileHeader == 2) _vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x2, y2, 1); } _vm->_globals->freeMemory(ofsData); result = 0; } return result; }
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 Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { assert(dst != 0); const int leftX = x, rightX = x + w; uint i; Common::String s = sOld; int width = getStringWidth(s); Common::String str; if (useEllipsis && width > w && s.hasSuffix("...")) { // String is too wide. Check whether it ends in an ellipsis // ("..."). If so, remove that and try again! s.deleteLastChar(); s.deleteLastChar(); s.deleteLastChar(); width = getStringWidth(s); } if (useEllipsis && width > w) { // String is too wide. So we shorten it "intelligently" by // replacing parts of the string by an ellipsis. There are // three possibilities for this: replace the start, the end, or // the middle of the string. What is best really depends on the // context; but unless we want to make this configurable, // replacing the middle seems to be a good compromise. const int ellipsisWidth = getStringWidth("..."); // SLOW algorithm to remove enough of the middle. But it is good enough // for now. const int halfWidth = (w - ellipsisWidth) / 2; int w2 = 0; uint last = 0; for (i = 0; i < s.size(); ++i) { const uint cur = s[i]; int charWidth = getCharWidth(cur) + getKerningOffset(last, cur); if (w2 + charWidth > halfWidth) break; last = cur; w2 += charWidth; str += cur; } // At this point we know that the first 'i' chars are together 'w2' // pixels wide. We took the first i-1, and add "..." to them. str += "..."; last = '.'; // The original string is width wide. Of those we already skipped past // w2 pixels, which means (width - w2) remain. // The new str is (w2+ellipsisWidth) wide, so we can accommodate about // (w - (w2+ellipsisWidth)) more pixels. // Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) = // (width + ellipsisWidth - w) int skip = width + ellipsisWidth - w; for (; i < s.size() && skip > 0; ++i) { const uint cur = s[i]; skip -= getCharWidth(cur) + getKerningOffset(last, cur); last = cur; } // Append the remaining chars, if any for (; i < s.size(); ++i) { str += s[i]; } width = getStringWidth(str); } else { str = s; } if (align == kTextAlignCenter) x = x + (w - width)/2; else if (align == kTextAlignRight) x = x + w - width; x += deltax; uint last = 0; for (i = 0; i < str.size(); ++i) { const uint cur = str[i]; x += getKerningOffset(last, cur); last = cur; w = getCharWidth(cur); if (x+w > rightX) break; if (x >= leftX) drawChar(dst, str[i], x, y, color); x += w; } }
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; }
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; }