Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
	Common::String name = s->_segMan->getString(argv[0]);
	Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
	bool result;

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

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

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

	debugC(kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
	if (result)
		return NULL_REG;
	return make_reg(0, 2); // DOS - file not found error code
}
Ejemplo n.º 4
0
/**
 * 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());
	}
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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);
				}
		}
	}
}
Ejemplo n.º 8
0
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();
}
Ejemplo n.º 9
0
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;
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
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;
}
Ejemplo n.º 13
0
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);
}
Ejemplo n.º 14
0
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);
}
Ejemplo n.º 15
0
Common::String Resource::translateFileName(const Common::String filename) {
	Common::String upperFilename = filename;
	upperFilename.toUppercase();
	Common::String fileNameStrFinal;

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

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

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

		upperFilename.deleteChar(0);
	}

	fileNameStrFinal += upperFilename;

	return fileNameStrFinal;
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
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;
}
Ejemplo n.º 19
0
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();
}
Ejemplo n.º 20
0
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;
}
Ejemplo n.º 21
0
bool Debugger::Cmd_PlayText(int argc, const char **argv) {
	if (argc != 2) {
		debugPrintf("Usage: %s <text name>\n", argv[0]);
		return true;
	} else {
		Common::String resName = argv[1];
		if (resName.hasPrefix("@"))
			resName.deleteChar(0);

		Common::File f;
		if (f.exists(resName) || f.exists(resName + ".txr")) {
			TextView::execute(_vm, resName);
			return false;
		} else {
			debugPrintf("Could not find resource file\n");
			return true;
		}
	}
}
Ejemplo n.º 22
0
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
}
Ejemplo n.º 23
0
/**
 * 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();
}
Ejemplo n.º 24
0
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);
}
Ejemplo n.º 25
0
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)
	);
}
Ejemplo n.º 26
0
void Subtitles::loadSubtitles() {
	File f("special.bin");

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

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

	while (f.pos() < f.size())
		_lines.push_back(f.readString());
	f.close();
}
Ejemplo n.º 27
0
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);
}
Ejemplo n.º 28
0
reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
	Common::String name = s->_segMan->getString(argv[0]);

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

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

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

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

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

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

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

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

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

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

	return file_open(s, name, mode, unwrapFilename);
}
Ejemplo n.º 29
0
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;
}
Ejemplo n.º 30
0
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;
}