bool FilesPageHandler::listDirectory(Common::String path, Common::String &content, const Common::String &itemTemplate) { if (path == "" || path == "/") { if (ConfMan.hasKey("rootpath", "cloud")) addItem(content, itemTemplate, IT_DIRECTORY, "/root/", _("File system root")); addItem(content, itemTemplate, IT_DIRECTORY, "/saves/", _("Saved games")); return true; } if (HandlerUtils::hasForbiddenCombinations(path)) return false; Common::String prefixToRemove = "", prefixToAdd = ""; if (!transformPath(path, prefixToRemove, prefixToAdd)) return false; Common::FSNode node = Common::FSNode(path); if (path == "/") node = node.getParent(); // absolute root if (!HandlerUtils::permittedPath(node.getPath())) return false; if (!node.isDirectory()) return false; // list directory Common::FSList _nodeContent; if (!node.getChildren(_nodeContent, Common::FSNode::kListAll, false)) // do not show hidden files _nodeContent.clear(); else Common::sort(_nodeContent.begin(), _nodeContent.end()); // add parent directory link { Common::String filePath = path; if (filePath.hasPrefix(prefixToRemove)) filePath.erase(0, prefixToRemove.size()); if (filePath == "" || filePath == "/" || filePath == "\\") filePath = "/"; else filePath = parentPath(prefixToAdd + filePath); addItem(content, itemTemplate, IT_PARENT_DIRECTORY, filePath, _("Parent directory")); } // fill the content for (Common::FSList::iterator i = _nodeContent.begin(); i != _nodeContent.end(); ++i) { Common::String name = i->getDisplayName(); if (i->isDirectory()) name += "/"; Common::String filePath = i->getPath(); if (filePath.hasPrefix(prefixToRemove)) filePath.erase(0, prefixToRemove.size()); filePath = prefixToAdd + filePath; addItem(content, itemTemplate, detectType(i->isDirectory(), name), filePath, name); } return true; }
bool PlayAnimationCommandParser::parse(const Common::String &line, ScriptParseContext &parseCtx, Command *&command) { if (line.size() < 11 || (!line.hasPrefix("FLB ") && !line.hasPrefix("FLX "))) return false; const int fromFrame = atoi(line.c_str() + 4); const int toFrame = atoi(line.c_str() + 8); command = new PlayAnimationCommand(fromFrame, toFrame); return true; }
reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) { Common::String name = s->_segMan->getString(argv[0]); Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager(); bool result; // SQ4 floppy prepends /\ to the filenames if (name.hasPrefix("/\\")) { name.deleteChar(0); name.deleteChar(0); } // Special case for SQ4 floppy: This game has hardcoded names for all of // its savegames, and they are all named "sq4sg.xxx", where xxx is the // slot. We just take the slot number here, and delete the appropriate // save game. if (name.hasPrefix("sq4sg.")) { // Special handling for SQ4... get the slot number and construct the // save game name. int slotNum = atoi(name.c_str() + name.size() - 3); Common::Array<SavegameDesc> saves; listSavegames(saves); int savedir_nr = saves[slotNum].id; name = g_sci->getSavegameName(savedir_nr); result = saveFileMan->removeSavefile(name); } else if (getSciVersion() >= SCI_VERSION_2) { // The file name may be already wrapped, so check both cases result = saveFileMan->removeSavefile(name); if (!result) { const Common::String wrappedName = g_sci->wrapFilename(name); result = saveFileMan->removeSavefile(wrappedName); } #ifdef ENABLE_SCI32 if (name == PHANTASMAGORIA_SAVEGAME_INDEX) { delete s->_virtualIndexFile; s->_virtualIndexFile = 0; } #endif } else { const Common::String wrappedName = g_sci->wrapFilename(name); result = saveFileMan->removeSavefile(wrappedName); } debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str()); if (result) return NULL_REG; return make_reg(0, 2); // DOS - file not found error code }
/** * Handles the sciAudio calls in fanmade games. * sciAudio is an external .NET library for playing MP3 files in fanmade games. * It runs in the background, and obtains sound commands from the * currently running game via text files (called "conductor files"). * For further info, check: http://sciprogramming.com/community/index.php?topic=634.0 */ void AudioPlayer::handleFanmadeSciAudio(reg_t sciAudioObject, SegManager *segMan) { // TODO: This is a bare bones implementation. Only the play/playx and stop commands // are handled for now - the other commands haven't been observed in any fanmade game // yet. All the volume related and fading functionality is currently missing. Kernel *kernel = g_sci->getKernel(); reg_t commandReg = readSelector(segMan, sciAudioObject, kernel->findSelector("command")); Common::String command = segMan->getString(commandReg); if (command == "play" || command == "playx") { #ifdef USE_MAD reg_t fileNameReg = readSelector(segMan, sciAudioObject, kernel->findSelector("fileName")); Common::String fileName = segMan->getString(fileNameReg); int16 loopCount = (int16)readSelectorValue(segMan, sciAudioObject, kernel->findSelector("loopCount")); // When loopCount is -1, we treat it as infinite looping, else no looping is done. // This is observed by game scripts, which can set loopCount to all sorts of random values. // Adjust loopCount for ScummVM's LoopingAudioStream semantics loopCount = (loopCount == -1) ? 0 : 1; // Determine sound type Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType; if (fileName.hasPrefix("music")) soundType = Audio::Mixer::kMusicSoundType; else if (fileName.hasPrefix("speech")) soundType = Audio::Mixer::kSpeechSoundType; Common::File *sciAudio = new Common::File(); // Replace backwards slashes for (uint i = 0; i < fileName.size(); i++) { if (fileName[i] == '\\') fileName.setChar('/', i); } sciAudio->open("sciAudio/" + fileName); Audio::SeekableAudioStream *audioStream = Audio::makeMP3Stream(sciAudio, DisposeAfterUse::YES); // We only support one audio handle _mixer->playStream(soundType, &_audioHandle, Audio::makeLoopingAudioStream((Audio::RewindableAudioStream *)audioStream, loopCount)); #endif } else if (command == "stop") { _mixer->stopHandle(_audioHandle); } else { warning("Unhandled sciAudio command: %s", command.c_str()); } }
Common::String ComposerEngine::mangleFilename(Common::String filename) { while (filename.size() && (filename[0] == '~' || filename[0] == ':' || filename[0] == '\\')) filename = filename.c_str() + 1; uint slashesToStrip = _directoriesToStrip; if (filename.hasPrefix("..")) slashesToStrip = 1; while (slashesToStrip--) { for (uint i = 0; i < filename.size(); i++) { if (filename[i] != '\\' && filename[i] != ':') continue; filename = filename.c_str() + i + 1; break; } } Common::String outFilename; for (uint i = 0; i < filename.size(); i++) { if (filename[i] == '\\' || filename[i] == ':') outFilename += '/'; else outFilename += filename[i]; } return outFilename; }
Common::String SciEngine::unwrapFilename(const Common::String &name) const { Common::String prefix = getFilePrefix() + "-"; if (name.hasPrefix(prefix.c_str())) return Common::String(name.c_str() + prefix.size()); else return name; }
void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound, bool recursion = false) { for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { // The required game data files can be located in the game directory, or in // a subdirectory called "clusters". In the latter case, we don't want to // detect the game in that subdirectory, as this will detect the game twice // when mass add is searching inside a directory. In this case, the first // result (the game directory) will be correct, but the second result (the // clusters subdirectory) will be wrong, as the optional speech, music and // video data files will be ignored. Note that this fix will skip the game // data files if the user has placed them inside a "clusters" subdirectory, // or if he/she points ScummVM directly to the "clusters" directory of the // game CD. Fixes bug #3049346. Common::String directory = file->getParent().getName(); directory.toLowercase(); if (directory.hasPrefix("clusters") && directory.size() <= 9 && !recursion) continue; const char *fileName = file->getName().c_str(); for (int cnt = 0; cnt < NUM_FILES_TO_CHECK; cnt++) if (scumm_stricmp(fileName, g_filesToCheck[cnt]) == 0) filesFound[cnt] = true; } else { for (int cnt = 0; cnt < ARRAYSIZE(g_dirNames); cnt++) if (scumm_stricmp(file->getName().c_str(), g_dirNames[cnt]) == 0) { Common::FSList fslist2; if (file->getChildren(fslist2, Common::FSNode::kListFilesOnly)) Sword1CheckDirectory(fslist2, filesFound, true); } } } }
POSIXFilesystemNode::POSIXFilesystemNode(const Common::String &p) { assert(p.size() > 0); // Expand "~/" to the value of the HOME env variable if (p.hasPrefix("~/")) { const char *home = getenv("HOME"); if (home != NULL && strlen(home) < MAXPATHLEN) { _path = home; // Skip over the tilda. We know that p contains at least // two chars, so this is safe: _path += p.c_str() + 1; } } else _path = p; // Normalize the path (that is, remove unneeded slashes etc.) _path = Common::normalizePath(_path, '/'); _displayName = Common::lastPathComponent(_path, '/'); setFlags(); }
bool BaseImage::loadFile(const Common::String &filename) { _filename = filename; _filename.toLowercase(); if (filename.hasPrefix("savegame:") || _filename.hasSuffix(".bmp")) { _decoder = new Image::BitmapDecoder(); } else if (_filename.hasSuffix(".png")) { _decoder = new Image::PNGDecoder(); } else if (_filename.hasSuffix(".tga")) { _decoder = new Image::TGADecoder(); } else if (_filename.hasSuffix(".jpg")) { _decoder = new Image::JPEGDecoder(); } else { error("BaseImage::loadFile : Unsupported fileformat %s", filename.c_str()); } _filename = filename; Common::SeekableReadStream *file = _fileManager->openFile(filename.c_str()); if (!file) { return false; } _decoder->loadStream(*file); _surface = _decoder->getSurface(); _palette = _decoder->getPalette(); _fileManager->closeFile(file); return true; }
Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) { if (!_useEnglishSpeech || fileName.size() < 9 || !fileName.hasPrefix("/speech/")) return fileName; // Always keep German speech as a fallback in case the English speech pack is not present. // However this means we cannot play with German text and English voice. if (fileName.hasPrefix("/speech/de")) return fileName; Common::String newFileName = "/speech/en"; int fileIdx = 9; while (fileIdx < fileName.size() && fileName[fileIdx] != '/') ++fileIdx; if (fileIdx < fileName.size()) newFileName += fileName.c_str() + fileIdx; return newFileName; }
WindowsFilesystemNode::WindowsFilesystemNode(const Common::String &p, const bool currentDir) { if (currentDir) { char path[MAX_PATH]; GetCurrentDirectory(MAX_PATH, path); _path = path; } else { assert(p.size() > 0); #ifndef _WIN32_WCE _path = p; #else // Check whether p is a relative path if (p.hasPrefix("\\") || p.hasPrefix("/")) { _path = p; } else { // Add current directory as a prefix to the path // (Windows CE has no concept for relative paths) char path[MAX_PATH]; GetCurrentDirectory(MAX_PATH, path); _path = path; _path += '\\'; _path += p; } #endif } _displayName = lastPathComponent(_path, '\\'); // Check whether it is a directory, and whether the file actually exists DWORD fileAttribs = GetFileAttributes(toUnicode(_path.c_str())); if (fileAttribs == INVALID_FILE_ATTRIBUTES) { _isDirectory = false; _isValid = false; } else { _isDirectory = ((fileAttribs & FILE_ATTRIBUTE_DIRECTORY) != 0); _isValid = true; // Add a trailing slash, if necessary. if (_isDirectory && _path.lastChar() != '\\') { _path += '\\'; } } _isPseudoRoot = false; }
RenderedImage::RenderedImage(const Common::String &filename, bool &result) : _data(0), _width(0), _height(0), _isTransparent(true) { result = false; PackageManager *pPackage = Kernel::getInstance()->getPackage(); assert(pPackage); _backSurface = Kernel::getInstance()->getGfx()->getSurface(); // Load file byte *pFileData; uint fileSize; bool isPNG = true; if (filename.hasPrefix("/saves")) { pFileData = readSavegameThumbnail(filename, fileSize, isPNG); } else { pFileData = pPackage->getFile(filename, &fileSize); } if (!pFileData) { error("File \"%s\" could not be loaded.", filename.c_str()); return; } // Uncompress the image int pitch; if (isPNG) result = ImgLoader::decodePNGImage(pFileData, fileSize, _data, _width, _height, pitch); else result = ImgLoader::decodeThumbnailImage(pFileData, fileSize, _data, _width, _height, pitch); if (!result) { error("Could not decode image."); delete[] pFileData; return; } // Cleanup FileData delete[] pFileData; _doCleanup = true; #if defined(SCUMM_LITTLE_ENDIAN) // Makes sense for LE only at the moment checkForTransparency(); #endif return; }
bool ThemeEval::getWidgetData(const Common::String &widget, int16 &x, int16 &y, uint16 &w, uint16 &h) { Common::StringTokenizer tokenizer(widget, "."); if (widget.hasPrefix("Dialog.")) tokenizer.nextToken(); Common::String dialogName = "Dialog." + tokenizer.nextToken(); Common::String widgetName = tokenizer.nextToken(); if (!_layouts.contains(dialogName)) return false; return _layouts[dialogName]->getWidgetData(widgetName, x, y, w, h); }
Graphics::TextAlign ThemeEval::getWidgetTextHAlign(const Common::String &widget) { Common::StringTokenizer tokenizer(widget, "."); if (widget.hasPrefix("Dialog.")) tokenizer.nextToken(); Common::String dialogName = "Dialog." + tokenizer.nextToken(); Common::String widgetName = tokenizer.nextToken(); if (!_layouts.contains(dialogName)) return Graphics::kTextAlignInvalid; return _layouts[dialogName]->getWidgetTextHAlign(widgetName); }
Common::String Resource::translateFileName(const Common::String filename) { Common::String upperFilename = filename; upperFilename.toUppercase(); Common::String fileNameStrFinal; if (upperFilename.hasPrefix("P:") || upperFilename.hasPrefix("F:")) { if (_vm->_isHiRes) fileNameStrFinal = "SPICT/"; else fileNameStrFinal = "PICT/"; if (_vm->getPlatform() == Common::kPlatformAmiga) { if (upperFilename.hasPrefix("P:")) { fileNameStrFinal = "PICT/"; } else { fileNameStrFinal = "LABFONTS/"; upperFilename += "T"; // all the Amiga fonts have a ".FONT" suffix } } } else if (upperFilename.hasPrefix("LAB:")) { // Look inside the game folder } else if (upperFilename.hasPrefix("MUSIC:")) { fileNameStrFinal = "MUSIC/"; } if (upperFilename.contains(':')) { while (upperFilename[0] != ':') { upperFilename.deleteChar(0); } upperFilename.deleteChar(0); } fileNameStrFinal += upperFilename; return fileNameStrFinal; }
bool StaticBitmap::unpersist(InputPersistenceBlock &reader) { bool result = true; result &= Bitmap::unpersist(reader); Common::String resourceFilename; reader.readString(resourceFilename); // We may not have saves, and we actually do not need to // restore them. So do not even try to load them. if (!resourceFilename.hasPrefix("/saves")) result &= initBitmapResource(resourceFilename); result &= RenderObject::unpersistChildren(reader); return reader.isGood() && result; }
bool ChangeSceneCommandParser::parse(const Common::String &line, ScriptParseContext &, Command *&command) { if (!line.hasPrefix("CHANGE ")) { return false; } uint8 sceneId = 0; uint8 objectId = 0; ChangeCommand::ChangeRegister reg; ChangeCommand::ChangeOperation op; ChangeCommandValue val; if (!parseValueString(line.c_str() + 7, false, sceneId, objectId, reg, op, val)) { return false; } command = new ChangeSceneCommand(sceneId, objectId, reg, op, val); return true; }
int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) { Common::String normalizedFilter = normalizePath(filter, _currentDirectory); int num = 0; if (path.size() > 0) warning("STUB: PackageManager::doSearch(<%s>, <%s>, %d)", filter.c_str(), path.c_str(), typeFilter); // Loop through checking each archive Common::List<ArchiveEntry *>::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { Common::ArchiveMemberList memberList; if (!normalizedFilter.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } // Construct relative path Common::String resFilter(&normalizedFilter.c_str()[(*i)->_mountPath.size()]); if ((*i)->archive->listMatchingMembers(memberList, resFilter) == 0) continue; // Create a list of the matching names for (Common::ArchiveMemberList::iterator it = memberList.begin(); it != memberList.end(); ++it) { if (((typeFilter & PackageManager::FT_DIRECTORY) && (*it)->getName().hasSuffix("/")) || ((typeFilter & PackageManager::FT_FILE) && !(*it)->getName().hasSuffix("/"))) { // Do not add duplicate files bool found = false; for (Common::ArchiveMemberList::iterator it1 = list.begin(); it1 != list.end(); ++it1) { if ((*it1)->getName() == (*it)->getName()) { found = true; break; } } if (!found) list.push_back(*it); num++; } } } return num; }
POSIXFilesystemNode::POSIXFilesystemNode(const Common::String &p) { assert(p.size() > 0); // Expand "~/" to the value of the HOME env variable if (p.hasPrefix("~/")) { const char *home = getenv("HOME"); if (home != NULL && strlen(home) < MAXPATHLEN) { _path = home; // Skip over the tilda. We know that p contains at least // two chars, so this is safe: _path += p.c_str() + 1; } } else { _path = p; } #ifdef __OS2__ // On OS/2, 'X:/' is a root of drive X, so we should not remove that last // slash. if (!(_path.size() == 3 && _path.hasSuffix(":/"))) #endif // Normalize the path (that is, remove unneeded slashes etc.) _path = Common::normalizePath(_path, '/'); _displayName = Common::lastPathComponent(_path, '/'); // TODO: should we turn relative paths into absolute ones? // Pro: Ensures the "getParent" works correctly even for relative dirs. // Contra: The user may wish to use (and keep!) relative paths in his // config file, and converting relative to absolute paths may hurt him... // // An alternative approach would be to change getParent() to work correctly // if "_path" is the empty string. #if 0 if (!_path.hasPrefix("/")) { char buf[MAXPATHLEN+1]; getcwd(buf, MAXPATHLEN); strcat(buf, "/"); _path = buf + _path; } #endif // TODO: Should we enforce that the path is absolute at this point? //assert(_path.hasPrefix("/")); setFlags(); }
GBAMPFileSystemNode::GBAMPFileSystemNode(const Common::String& path) { // consolePrintf("'%s'",path.c_str()); int lastSlash = 3; for (int r = 0; r < (int) path.size() - 1; r++) { if ((path[r] == '\\') || (path[r] == '/')) { lastSlash = r; } } if (path == "mp:/") { // This is the root directory _isDirectory = true; _isValid = false; // Old code returned false here, but I'm not sure why } else { char check[128]; memset(check, 0, 128); if (path.size() > 4 && path.hasPrefix("mp:/")) { // Files which start with mp:/ // Clear the filename to 128 bytes, because a libfat bug occasionally tries to read in this area. strcpy(check, path.c_str() + 3); } else { // Clear the filename to 128 bytes, because a libfat bug occationally tries to read in this area. strcpy(check, path.c_str()); } // Remove terminating slash - FileExists fails without this if (check[strlen(check) - 1] == '/') { check[strlen(check) - 1] = 0; } int fileOrDir = FAT_FileExists(check); _isDirectory = fileOrDir == FT_DIR; _isValid = fileOrDir == FT_FILE; } // consolePrintf("Path: %s \n", check); _displayName = Common::String(path.c_str() + lastSlash + 1); _path = path; }
bool Debugger::Cmd_PlayText(int argc, const char **argv) { if (argc != 2) { debugPrintf("Usage: %s <text name>\n", argv[0]); return true; } else { Common::String resName = argv[1]; if (resName.hasPrefix("@")) resName.deleteChar(0); Common::File f; if (f.exists(resName) || f.exists(resName + ".txr")) { TextView::execute(_vm, resName); return false; } else { debugPrintf("Could not find resource file\n"); return true; } } }
void MessageState::outputString(reg_t buf, const Common::String &str) { #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_STRING) { SciString *sciString = _segMan->lookupString(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, str.c_str()[i]); sciString->setValue(str.size(), 0); } else if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_ARRAY) { // Happens in the intro of LSL6, we are asked to write the string // into an array SciArray<reg_t> *sciString = _segMan->lookupArray(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, make_reg(0, str.c_str()[i])); sciString->setValue(str.size(), NULL_REG); } } else { #endif SegmentRef buffer_r = _segMan->dereference(buf); if ((unsigned)buffer_r.maxSize >= str.size() + 1) { _segMan->strcpy(buf, str.c_str()); } else { // LSL6 sets an exit text here, but the buffer size allocated // is too small. Don't display a warning in this case, as we // don't use the exit text anyway - bug report #3035533 if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) { // LSL6 buggy exit text, don't show warning } else { warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str()); } // Set buffer to empty string if possible if (buffer_r.maxSize > 0) _segMan->strcpy(buf, ""); } #ifdef ENABLE_SCI32 } #endif }
/** * Scans through the archive list for a specified file */ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) { // Loop through checking each archive Common::List<ArchiveEntry *>::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { if (!fileName.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } // Look into the archive for the desired file Common::Archive *archiveFolder = (*i)->archive; // Construct relative path Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]); if (archiveFolder->hasFile(resPath)) { return archiveFolder->getMember(resPath); } } return Common::ArchiveMemberPtr(); }
DSFileSystemNode::DSFileSystemNode(const Common::String& path) { // consolePrintf("--%s ",path.c_str()); int lastSlash = 3; for (int r = 0; r < (int) path.size() - 1; r++) { if (path[r] == '\\') { lastSlash = r; } } _displayName = Common::String(path.c_str() + lastSlash + 1); _path = path; // _isValid = true; // _isDirectory = false; const char *pathStr = path.c_str(); if (path.hasPrefix("ds:/")) { pathStr += 4; } if (*pathStr == '\0') { _isValid = true; _isDirectory = true; return; } _zipFile->setAllFilesVisible(true); if (_zipFile->findFile(pathStr)) { _isValid = true; _isDirectory = _zipFile->isDirectory(); } else { _isValid = false; _isDirectory = false; } _zipFile->setAllFilesVisible(false); // consolePrintf("%s - Found: %d, Dir: %d\n", pathStr, _isValid, _isDirectory); }
void FolderDownloadRequest::downloadNextFile() { do { if (_pendingFiles.empty()) { sendCommand(GUI::kDownloadEndedCmd, 0); finishDownload(_failedFiles); return; } _currentFile = _pendingFiles.back(); _pendingFiles.pop_back(); } while (_currentFile.isDirectory()); // directories are actually removed earlier, in the directoryListedCallback() sendCommand(GUI::kDownloadProgressCmd, (int)(getProgress() * 100)); Common::String remotePath = _currentFile.path(); Common::String localPath = remotePath; if (_remoteDirectoryPath == "" || remotePath.hasPrefix(_remoteDirectoryPath)) { localPath.erase(0, _remoteDirectoryPath.size()); if (_remoteDirectoryPath != "" && (_remoteDirectoryPath.lastChar() != '/' && _remoteDirectoryPath.lastChar() != '\\')) localPath.erase(0, 1); } else { warning("FolderDownloadRequest: Can't process the following paths:"); warning("remote directory: %s", _remoteDirectoryPath.c_str()); warning("remote file under that directory: %s", remotePath.c_str()); } if (_localDirectoryPath != "") { if (_localDirectoryPath.lastChar() == '/' || _localDirectoryPath.lastChar() == '\\') localPath = _localDirectoryPath + localPath; else localPath = _localDirectoryPath + "/" + localPath; } debug(9, "FolderDownloadRequest: %s -> %s", remotePath.c_str(), localPath.c_str()); _workingRequest = _storage->downloadById( _currentFile.id(), localPath, new Common::Callback<FolderDownloadRequest, Storage::BoolResponse>(this, &FolderDownloadRequest::fileDownloadedCallback), new Common::Callback<FolderDownloadRequest, Networking::ErrorResponse>(this, &FolderDownloadRequest::fileDownloadedErrorCallback) ); }
void Subtitles::loadSubtitles() { File f("special.bin"); if (!g_vm->_files->_ccNum) { // The first subtitle line contains all the text for the Clouds intro. Since ScummVM allows // both voice and subtitles at the same time, unlike the original, we need to split up the // first subtitle into separate lines to allow them to better interleave with the voice Common::String line = f.readString(); for (;;) { const char *lineSep = strstr(line.c_str(), " "); if (!lineSep) break; _lines.push_back(Common::String(line.c_str(), lineSep)); line = Common::String(lineSep + 3); while (line.hasPrefix(" ")) line.deleteChar(0); } } while (f.pos() < f.size()) _lines.push_back(f.readString()); f.close(); }
BdfFont *BdfFont::loadFont(Common::SeekableReadStream &stream) { BdfFontData font; memset(&font, 0, sizeof(font)); font.ascent = -1; font.defaultCharacter = -1; // We only load the first 256 characters font.numCharacters = 256; byte **bitmaps = new byte *[font.numCharacters]; memset(bitmaps, 0, sizeof(byte *) * font.numCharacters); byte *advances = new byte[font.numCharacters]; BdfBoundingBox *boxes = new BdfBoundingBox[font.numCharacters]; int descent = -1; Common::String line; while (true) { line = stream.readLine(); if (stream.err() || stream.eos()) { warning("BdfFont::loadFont: Premature end of file"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } // Only parse and handle declarations we actually need if (line.hasPrefix("FONTBOUNDINGBOX ")) { int width, height, xOffset, yOffset; if (sscanf(line.c_str(), "FONTBOUNDINGBOX %d %d %d %d", &width, &height, &xOffset, &yOffset) != 4) { warning("BdfFont::loadFont: Invalid FONTBOUNDINGBOX"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } font.defaultBox.width = width; font.defaultBox.height = height; font.defaultBox.xOffset = xOffset; font.defaultBox.yOffset = yOffset; } else if (line.hasPrefix("FONT_ASCENT ")) { if (sscanf(line.c_str(), "FONT_ASCENT %d", &font.ascent) != 1) { warning("BdfFont::loadFont: Invalid FONT_ASCENT"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } } else if (line.hasPrefix("FONT_DESCENT ")) { if (sscanf(line.c_str(), "FONT_DESCENT %d", &descent) != 1) { warning("BdfFont::loadFont: Invalid FONT_DESCENT"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } } else if (line.hasPrefix("DEFAULT_CHAR ")) { if (sscanf(line.c_str(), "DEFAULT_CHAR %d", &font.defaultCharacter) != 1) { warning("BdfFont::loadFont: Invalid DEFAULT_CHAR"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } } else if (line.hasPrefix("STARTCHAR ")) { BdfBoundingBox box = font.defaultBox; int encoding = -1; int advance = -1; byte *bitmap = loadCharacter(stream, encoding, advance, box); // Ignore all characters above 255. if (encoding < -1 || encoding >= font.numCharacters) { delete[] bitmap; encoding = -1; } // Calculate the max advance if (encoding != -1 && advance > font.maxAdvance) font.maxAdvance = advance; if (!bitmap && encoding != -1) { warning("BdfFont::loadFont: Character %d invalid", encoding); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } if (encoding != -1) { bitmaps[encoding] = bitmap; advances[encoding] = advance; boxes[encoding] = box; } } else if (line == "ENDFONT") { break; } } if (font.ascent < 0 || descent < 0) { warning("BdfFont::loadFont: Invalid ascent or descent"); freeBitmaps(bitmaps, font.numCharacters); delete[] bitmaps; delete[] advances; delete[] boxes; return 0; } font.height = font.ascent + descent; font.bitmaps = bitmaps; font.advances = advances; font.boxes = boxes; int firstCharacter = font.numCharacters; int lastCharacter = -1; bool hasFixedBBox = true; bool hasFixedAdvance = true; for (int i = 0; i < font.numCharacters; ++i) { if (!font.bitmaps[i]) continue; if (i < firstCharacter) firstCharacter = i; if (i > lastCharacter) lastCharacter = i; if (font.advances[i] != font.maxAdvance) hasFixedAdvance = false; const BdfBoundingBox &bbox = font.boxes[i]; if (bbox.width != font.defaultBox.width || bbox.height != font.defaultBox.height || bbox.xOffset != font.defaultBox.xOffset || bbox.yOffset != font.defaultBox.yOffset) hasFixedBBox = false; } if (lastCharacter == -1) { warning("BdfFont::loadFont: No glyphs found"); delete[] font.bitmaps; delete[] font.advances; delete[] font.boxes; return 0; } // Free the advance table, in case all glyphs use the same advance if (hasFixedAdvance) { delete[] font.advances; font.advances = 0; } // Free the box table, in case all glyphs use the same box if (hasFixedBBox) { delete[] font.boxes; font.boxes = 0; } // Adapt for the fact that we never use encoding 0. if (font.defaultCharacter < firstCharacter || font.defaultCharacter > lastCharacter) font.defaultCharacter = -1; font.firstCharacter = firstCharacter; const int charsAvailable = lastCharacter - firstCharacter + 1; // Try to compact the tables if (charsAvailable < font.numCharacters) { byte **newBitmaps = new byte *[charsAvailable]; boxes = 0; advances = 0; if (!hasFixedBBox) boxes = new BdfBoundingBox[charsAvailable]; if (!hasFixedAdvance) advances = new byte[charsAvailable]; for (int i = 0; i < charsAvailable; ++i) { const int encoding = i + firstCharacter; if (font.bitmaps[encoding]) { newBitmaps[i] = bitmaps[encoding]; if (!hasFixedBBox) boxes[i] = font.boxes[encoding]; if (!hasFixedAdvance) advances[i] = font.advances[encoding]; } else { newBitmaps[i] = 0; } } delete[] font.bitmaps; font.bitmaps = newBitmaps; delete[] font.advances; font.advances = advances; delete[] font.boxes; font.boxes = boxes; font.numCharacters = charsAvailable; } return new BdfFont(font, DisposeAfterUse::YES); }
reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) { Common::String name = s->_segMan->getString(argv[0]); // SCI32 can call K_FILEIO_OPEN with only one argument. It seems to // just be checking if it exists. int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16(); bool unwrapFilename = true; // SQ4 floppy prepends /\ to the filenames if (name.hasPrefix("/\\")) { name.deleteChar(0); name.deleteChar(0); } // SQ4 floppy attempts to update the savegame index file sq4sg.dir when // deleting saved games. We don't use an index file for saving or loading, // so just stop the game from modifying the file here in order to avoid // having it saved in the ScummVM save directory. if (name == "sq4sg.dir") { debugC(kDebugLevelFile, "Not opening unused file sq4sg.dir"); return SIGNAL_REG; } if (name.empty()) { // Happens many times during KQ1 (e.g. when typing something) debugC(kDebugLevelFile, "Attempted to open a file with an empty filename"); return SIGNAL_REG; } debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode); #ifdef ENABLE_SCI32 if (name == PHANTASMAGORIA_SAVEGAME_INDEX) { if (s->_virtualIndexFile) { return make_reg(0, VIRTUALFILE_HANDLE); } else { Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH); Common::String wrappedName = g_sci->wrapFilename(englishName); if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) { s->_virtualIndexFile = new VirtualIndexFile(wrappedName); return make_reg(0, VIRTUALFILE_HANDLE); } } } // Shivers is trying to store savegame descriptions and current spots in // separate .SG files, which are hardcoded in the scripts. // Essentially, there is a normal save file, created by the executable // and an extra hardcoded save file, created by the game scripts, probably // because they didn't want to modify the save/load code to add the extra // information. // Each slot in the book then has two strings, the save description and a // description of the current spot that the player is at. Currently, the // spot strings are always empty (probably related to the unimplemented // kString subop 14, which gets called right before this call). // For now, we don't allow the creation of these files, which means that // all the spot descriptions next to each slot description will be empty // (they are empty anyway). Until a viable solution is found to handle these // extra files and until the spot description strings are initialized // correctly, we resort to virtual files in order to make the load screen // useable. Without this code it is unusable, as the extra information is // always saved to 0.SG for some reason, but on restore the correct file is // used. Perhaps the virtual ID is not taken into account when saving. // // Future TODO: maintain spot descriptions and show them too, ideally without // having to return to this logic of extra hardcoded files. if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) { if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) { // Game scripts are trying to create a file with the save // description, stop them here debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str()); return SIGNAL_REG; } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) { // Create a virtual file containing the save game description // and slot number, as the game scripts expect. int slotNumber; sscanf(name.c_str(), "%d.SG", &slotNumber); Common::Array<SavegameDesc> saves; listSavegames(saves); int savegameNr = findSavegame(saves, slotNumber - SAVEGAMEID_OFFICIALRANGE_START); if (!s->_virtualIndexFile) { // Make the virtual file buffer big enough to avoid having it grow dynamically. // 50 bytes should be more than enough. s->_virtualIndexFile = new VirtualIndexFile(50); } s->_virtualIndexFile->seek(0, SEEK_SET); s->_virtualIndexFile->write(saves[savegameNr].name, strlen(saves[savegameNr].name)); s->_virtualIndexFile->write("\0", 1); s->_virtualIndexFile->write("\0", 1); // Spot description (empty) s->_virtualIndexFile->seek(0, SEEK_SET); return make_reg(0, VIRTUALFILE_HANDLE); } } #endif // QFG import rooms get a virtual filelisting instead of an actual one if (g_sci->inQfGImportRoom()) { // We need to find out what the user actually selected, "savedHeroes" is // already destroyed when we get here. That's why we need to remember // selection via kDrawControl. name = s->_dirseeker.getVirtualFilename(s->_chosenQfGImportItem); unwrapFilename = false; } return file_open(s, name, mode, unwrapFilename); }
GameList detectGamesImpl(const Common::FSList &fslist, bool recursion = false) { GameList detectedGames; const Sword2::GameSettings *g; Common::FSList::const_iterator file; bool isFullVersion = isFullGame(fslist); for (g = Sword2::sword2_settings; g->gameid; ++g) { // Iterate over all files in the given directory for (file = fslist.begin(); file != fslist.end(); ++file) { if (!file->isDirectory()) { // The required game data files can be located in the game directory, or in // a subdirectory called "clusters". In the latter case, we don't want to // detect the game in that subdirectory, as this will detect the game twice // when mass add is searching inside a directory. In this case, the first // result (the game directory) will be correct, but the second result (the // clusters subdirectory) will be wrong, as the optional speech, music and // video data files will be ignored. Note that this fix will skip the game // data files if the user has placed them inside a "clusters" subdirectory, // or if he/she points ScummVM directly to the "clusters" directory of the // game CD. Fixes bug #3049336. Common::String directory = file->getParent().getName(); directory.toLowercase(); if (directory.hasPrefix("clusters") && directory.size() <= 9 && !recursion) continue; if (file->getName().equalsIgnoreCase(g->detectname)) { // Make sure that the sword2 demo is not mixed up with the // full version, since they use the same filename for detection if ((g->features == Sword2::GF_DEMO && isFullVersion) || (g->features == 0 && !isFullVersion)) continue; // Match found, add to list of candidates, then abort inner loop. detectedGames.push_back(GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, GUIO2(GUIO_NOMIDI, GUIO_NOASPECT))); break; } } } } if (detectedGames.empty()) { // Nothing found -- try to recurse into the 'clusters' subdirectory, // present e.g. if the user copied the data straight from CD. for (file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) { if (file->getName().equalsIgnoreCase("clusters")) { Common::FSList recList; if (file->getChildren(recList, Common::FSNode::kListAll)) { GameList recGames(detectGamesImpl(recList, true)); if (!recGames.empty()) { detectedGames.push_back(recGames); break; } } } } } } return detectedGames; }
Common::Error GroovieEngine::run() { _script = new Script(this, _gameDescription->version); // Initialize the graphics switch (_gameDescription->version) { case kGroovieV2: // Request the mode with the highest precision available initGraphics(640, 480, true, NULL); // Save the enabled mode as it can be both an RGB mode or CLUT8 _pixelFormat = _system->getScreenFormat(); _mode8bit = (_pixelFormat == Graphics::PixelFormat::createFormatCLUT8()); break; case kGroovieT7G: initGraphics(640, 480, true); _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); break; } // Create debugger. It requires GFX to be initialized _debugger = new Debugger(this); _script->setDebugger(_debugger); // Create the graphics manager _graphicsMan = new GraphicsMan(this); // Create the resource and cursor managers and the video player // Prepare the font too switch (_gameDescription->version) { case kGroovieT7G: if (getPlatform() == Common::kPlatformMacintosh) { _macResFork = new Common::MacResManager(); if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName)) error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName); // The Macintosh release used system fonts. We use GUI fonts. _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); } else { Common::File fontfile; if (!fontfile.open("sphinx.fnt")) { error("Couldn't open sphinx.fnt"); return Common::kNoGameDataFoundError; } else if (!_sphinxFont.load(fontfile)) { error("Error loading sphinx.fnt"); return Common::kUnknownError; } fontfile.close(); _font = &_sphinxFont; } _resMan = new ResMan_t7g(_macResFork); _grvCursorMan = new GrvCursorMan_t7g(_system, _macResFork); _videoPlayer = new VDXPlayer(this); break; case kGroovieV2: _resMan = new ResMan_v2(); _grvCursorMan = new GrvCursorMan_v2(_system); _videoPlayer = new ROQPlayer(this); break; } // Create the music player if (getPlatform() == Common::kPlatformMacintosh) _musicPlayer = new MusicPlayerMac(this); else _musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample"); // Load volume levels syncSoundSettings(); // Get the name of the main script Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName; if (_gameDescription->version == kGroovieT7G) { // Run The 7th Guest's demo if requested if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode")) filename = "demo.grv"; else if (getPlatform() == Common::kPlatformMacintosh) filename = "script.grv"; // Stored inside the executable's resource fork } else if (_gameDescription->version == kGroovieV2) { // Open the disk index Common::File disk; if (!disk.open(filename)) { error("Couldn't open %s", filename.c_str()); return Common::kNoGameDataFoundError; } // Search the entry bool found = false; int index = 0; while (!found && !disk.eos()) { Common::String line = disk.readLine(); if (line.hasPrefix("title: ")) { // A new entry index++; } else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) { // It's the boot of the entry we're looking for, // get the script filename filename = line.c_str() + 6; found = true; } } // Couldn't find the entry if (!found) { error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str()); return Common::kUnknownError; } } // Check the script file extension if (!filename.hasSuffix(".grv")) { error("%s isn't a valid script filename", filename.c_str()); return Common::kUnknownError; } // Load the script if (!_script->loadScript(filename)) { error("Couldn't load the script file %s", filename.c_str()); return Common::kUnknownError; } // Should I load a saved game? if (ConfMan.hasKey("save_slot")) { // Get the requested slot int slot = ConfMan.getInt("save_slot"); _script->directGameLoad(slot); } // Check that the game files and the audio tracks aren't together run from // the same cd checkCD(); // Game timer counter uint16 tmr = 0; // Initialize the CD int cd_num = ConfMan.getInt("cdrom"); if (cd_num >= 0) _system->getAudioCDManager()->openCD(cd_num); while (!shouldQuit()) { // Give the debugger a chance to act _debugger->onFrame(); // Handle input Common::Event ev; while (_eventMan->pollEvent(ev)) { switch (ev.type) { case Common::EVENT_KEYDOWN: // CTRL-D: Attach the debugger if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) _debugger->attach(); // Send the event to the scripts _script->setKbdChar(ev.kbd.ascii); // Continue the script execution to handle the key _waitingForInput = false; break; case Common::EVENT_MAINMENU: // Closing the GMM case Common::EVENT_MOUSEMOVE: // Continue the script execution, the mouse pointer // may fall inside a different hotspot now _waitingForInput = false; break; case Common::EVENT_LBUTTONDOWN: // Send the event to the scripts _script->setMouseClick(1); // Continue the script execution to handle // the click _waitingForInput = false; break; case Common::EVENT_RBUTTONDOWN: // Send the event to the scripts (to skip the video) _script->setMouseClick(2); break; case Common::EVENT_QUIT: quitGame(); break; default: break; } } // The event loop may have triggered the quit status. In this case, // stop the execution. if (shouldQuit()) { continue; } if (_waitingForInput) { // Still waiting for input, just update the mouse, game timer and then wait a bit more _grvCursorMan->animate(); _system->updateScreen(); tmr++; // Wait a little bit between increments. While mouse is moving, this triggers // only negligably slower. if (tmr > 4) { _script->timerTick(); tmr = 0; } _system->delayMillis(50); } else if (_graphicsMan->isFading()) { // We're waiting for a fading to end, let the CPU rest // for a while and continue _system->delayMillis(30); } else { // Everything's fine, execute another script step _script->step(); } // Update the screen if required _graphicsMan->update(); } return Common::kNoError; }