コード例 #1
0
ファイル: default-saves.cpp プロジェクト: 0xf1sh/scummvm
void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) {
	clearError();
	if (!dir.exists()) {
		setError(Common::kPathDoesNotExist, "The savepath '"+dir.getPath()+"' does not exist");
	} else if (!dir.isDirectory()) {
		setError(Common::kPathNotDirectory, "The savepath '"+dir.getPath()+"' is not a directory");
	}
}
コード例 #2
0
ファイル: debugger.cpp プロジェクト: MaddTheSane/scummvm
bool Debugger_EoB::cmd_saveOriginal(int argc, const char **argv) {
	if (!_vm->_runFlag) {
		DebugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n");
		return true;
	}

	Common::String dir = ConfMan.get("savepath");
	if (dir == "None")
		dir.clear();

	Common::FSNode nd(dir);
	if (!nd.isDirectory())
		return false;

	if (_vm->game() == GI_EOB1) {
		if (argc == 1) {
			if (_vm->saveAsOriginalSaveFile()) {
				Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA.SAV"));
				if (nf.isReadable())
					DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str());
				else
					DebugPrintf("Failure.\n");
			} else {
				DebugPrintf("Failure.\n");
			}
		} else {
			DebugPrintf("Syntax:   save_original\n          (Saves game in original file format to a file which can be used with the orginal game executable.)\n\n");
		}
		return true;

	} else if (argc == 2) {
		int slot = atoi(argv[1]);
		if (slot < 0 || slot > 5) {
			DebugPrintf("Slot must be between (including) 0 and 5.\n");
		} else if (_vm->saveAsOriginalSaveFile(slot)) {
			Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA%d.SAV", slot));
			if (nf.isReadable())
				DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str());
			else
				DebugPrintf("Failure.\n");
		} else {
			DebugPrintf("Failure.\n");
		}
		return true;
	}

	DebugPrintf("Syntax:   save_original <slot>\n          (Saves game in original file format to a file which can be used with the orginal game executable.\n          A save slot between 0 and 5 must be specified.)\n\n");
	return true;
}
コード例 #3
0
ファイル: savefilemgr.cpp プロジェクト: 33d/scummvm
bool Ps2SaveFileManager::removeSavefile(const Common::String &filename) {
	Common::FSNode savePath(ConfMan.get("savepath")); // TODO: is this fast?
	Common::FSNode file;

	if (!savePath.exists() || !savePath.isDirectory())
		return false;

	if (_getDev(savePath) == MC_DEV) {
	// if (strncmp(savePath.getPath().c_str(), "mc0:", 4) == 0) {
		char path[32], temp[32];
		strcpy(temp, filename.c_str());

		// mcSplit(temp, game, ext);
		char *game = strdup(strtok(temp, "."));
		char *ext = strdup(strtok(NULL, "*"));
		sprintf(path, "mc0:ScummVM/%s", game); // per game path
		mcCheck(path);
		sprintf(path, "mc0:ScummVM/%s/%s.sav", game, ext);
		file = Common::FSNode(path);
		free(game);
		free(ext);
	} else {
		file = savePath.getChild(filename);
	}

	if (!file.exists() || file.isDirectory())
		return false;

	fio.remove(file.getPath().c_str());

	return true;
}
コード例 #4
0
ファイル: system.cpp プロジェクト: SinSiXX/scummvm
bool TizenSaveFileManager::removeSavefile(const Common::String &filename) {
	Common::String savePathName = getSavePath();

	checkPath(Common::FSNode(savePathName));
	if (getError().getCode() != Common::kNoError) {
		return false;
	}

	// recreate FSNode since checkPath may have changed/created the directory
	Common::FSNode savePath(savePathName);
	Common::FSNode file = savePath.getChild(filename);

	String unicodeFileName;
	StringUtil::Utf8ToString(file.getPath().c_str(), unicodeFileName);

	switch (Tizen::Io::File::Remove(unicodeFileName)) {
	case E_SUCCESS:
		return true;

	case E_ILLEGAL_ACCESS:
		setError(Common::kWritePermissionDenied, "Search or write permission denied: " +
					file.getName());
		break;

	default:
		setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() +
					"' does not exist or path is invalid");
		break;
	}

	return false;
}
コード例 #5
0
ファイル: advancedDetector.cpp プロジェクト: Botje/residualvm
void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps, const ADGameIdList &matchedGameIds) const {
	Common::String report = Common::String::format(
			_("The game in '%s' seems to be an unknown %s engine game "
			  "variant.\n\nPlease report the following data to the ResidualVM "
			  "team at %s along with the name of the game you tried to add and "
			  "its version, language, etc.:"),
			path.getPath().c_str(), getName(), "https://github.com/residualvm/residualvm/issues");

	if (matchedGameIds.size()) {
		report += "\n\n";
		report += _("Matched game IDs:");
		report += " ";

		for (ADGameIdList::const_iterator gameId = matchedGameIds.begin(); gameId != matchedGameIds.end(); ++gameId) {
			if (gameId != matchedGameIds.begin()) {
				report += ", ";
			}
			report += *gameId;
		}
	}

	report += "\n\n";

	report.wordWrap(80);

	for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file)
		report += Common::String::format("  {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);

	report += "\n";

	g_system->logMessage(LogMessageType::kInfo, report.c_str());
}
コード例 #6
0
ファイル: default-saves.cpp プロジェクト: 0xf1sh/scummvm
bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) {
	Common::String savePathName = getSavePath();
	checkPath(Common::FSNode(savePathName));
	if (getError().getCode() != Common::kNoError)
		return false;

	// recreate FSNode since checkPath may have changed/created the directory
	Common::FSNode savePath(savePathName);

	Common::FSNode file = savePath.getChild(filename);

	// FIXME: remove does not exist on all systems. If your port fails to
	// compile because of this, please let us know (scummvm-devel or Fingolfin).
	// There is a nicely portable workaround, too: Make this method overloadable.
	if (remove(file.getPath().c_str()) != 0) {
#ifndef _WIN32_WCE
		if (errno == EACCES)
			setError(Common::kWritePermissionDenied, "Search or write permission denied: "+file.getName());

		if (errno == ENOENT)
			setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid");
#endif
		return false;
	} else {
		return true;
	}
}
コード例 #7
0
ファイル: filespagehandler.cpp プロジェクト: 86400/scummvm
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;
}
コード例 #8
0
ファイル: ThemeEngine.cpp プロジェクト: megaboy/scummvm
void ThemeEngine::listUsableThemes(const Common::FSNode &node, Common::List<ThemeDescriptor> &list, int depth) {
	if (!node.exists() || !node.isReadable() || !node.isDirectory())
		return;

	ThemeDescriptor td;

	// Check whether we point to a valid theme directory.
	if (themeConfigUsable(node, td.name)) {
		td.filename = node.getPath();
		td.id = node.getName();

		list.push_back(td);

		// A theme directory should never contain any other themes
		// thus we just return to the caller here.
		return;
	}

	Common::FSList fileList;
	// Check all files. We need this to find all themes inside ZIP archives.
	if (!node.getChildren(fileList, Common::FSNode::kListFilesOnly))
		return;

	for (Common::FSList::iterator i = fileList.begin(); i != fileList.end(); ++i) {
		// We will only process zip files for now
		if (!i->getPath().matchString("*.zip", true))
			continue;

		td.name.clear();
		if (themeConfigUsable(*i, td.name)) {
			td.filename = i->getPath();
			td.id = i->getName();

			// If the name of the node object also contains
			// the ".zip" suffix, we will strip it.
			if (td.id.matchString("*.zip", true)) {
				for (int j = 0; j < 4; ++j)
					td.id.deleteLastChar();
			}

			list.push_back(td);
		}
	}

	fileList.clear();

	// Check if we exceeded the given recursion depth
	if (depth - 1 == -1)
		return;

	// As next step we will search all subdirectories
	if (!node.getChildren(fileList, Common::FSNode::kListDirectoriesOnly))
		return;

	for (Common::FSList::iterator i = fileList.begin(); i != fileList.end(); ++i)
		listUsableThemes(*i, list, depth == -1 ? - 1 : depth - 1);
}
コード例 #9
0
Common::String PersistenceService::getSavegameDirectory() {
	Common::FSNode node(FileSystemUtil::getUserdataDirectory());
	Common::FSNode childNode = node.getChild(SAVEGAME_DIRECTORY);

	// Try and return the path using the savegame subfolder. But if doesn't exist, fall back on the data directory
	if (childNode.exists())
		return childNode.getPath();

	return node.getPath();
}
コード例 #10
0
bool WagFileParser::parse(const Common::FSNode &node) {
	WagProperty property; // Temporary property used for reading
	Common::SeekableReadStream *stream = NULL; // The file stream

	_parsedOk = false; // We haven't parsed the file yet

	stream = node.createReadStream(); // Open the file
	if (stream) { // Check that opening the file was succesful
		if (checkWagVersion(*stream)) { // Check that WinAGI version string is valid
			// It seems we've got a valid *.wag file so let's parse its properties from the start.
			stream->seek(0); // Rewind the stream
			if (!_propList.empty()) _propList.clear(); // Clear out old properties (If any)

			do { // Parse the properties
				if (property.read(*stream)) { // Read the property and check it was read ok
					_propList.push_back(property); // Add read property to properties list
					debug(4, "WagFileParser::parse: Read property with code %d, type %d, number %d, size %d, data \"%s\"",
						property.getCode(), property.getType(), property.getNumber(), property.getSize(), property.getData());
				} else // Reading failed, let's bail out
					break;
			} while (!endOfProperties(*stream)); // Loop until the end of properties

			// File was parsed successfully only if we got to the end of properties
			// and all the properties were read successfully (Also the last).
			_parsedOk = endOfProperties(*stream) && property.readOk();

			if (!_parsedOk) // Error parsing stream
				warning("Error parsing WAG file (%s). WAG file ignored", node.getPath().c_str());
		} else // Invalid WinAGI version string or it couldn't be read
			warning("Invalid WAG file (%s) version or error reading it. WAG file ignored", node.getPath().c_str());
	} else // Couldn't open file
		warning("Couldn't open WAG file (%s). WAG file ignored", node.getPath().c_str());

	delete stream;
	return _parsedOk;
}
コード例 #11
0
static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSizeMD5) {
    // TODO: This message should be cleaned up / made more specific.
    // For example, we should specify at least which engine triggered this.
    //
    // Might also be helpful to display the full path (for when this is used
    // from the mass detector).
    printf("The game in '%s' seems to be unknown.\n", path.getPath().c_str());
    printf("Please, report the following data to the ScummVM team along with name\n");
    printf("of the game you tried to add and its version/language/etc.:\n");

    for (SizeMD5Map::const_iterator file = filesSizeMD5.begin(); file != filesSizeMD5.end(); ++file)
        printf("  {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);

    printf("\n");
}
コード例 #12
0
ファイル: tinsel.cpp プロジェクト: Fyre91/scummvm
TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) :
		Engine(syst), _gameDescription(gameDesc), _random("tinsel") {
	_vm = this;

	_config = new Config(this);

	// Register debug flags
	DebugMan.addDebugChannel(kTinselDebugAnimations, "animations", "Animations debugging");
	DebugMan.addDebugChannel(kTinselDebugActions, "actions", "Actions debugging");
	DebugMan.addDebugChannel(kTinselDebugSound, "sound", "Sound debugging");
	DebugMan.addDebugChannel(kTinselDebugMusic, "music", "Music debugging");

	// Setup mixer
	syncSoundSettings();

	// Add DW2 subfolder to search path in case user is running directly from the CDs
	const Common::FSNode gameDataDir(ConfMan.get("path"));
	SearchMan.addSubDirectoryMatching(gameDataDir, "dw2");

	// Add subfolders needed for psx versions of Discworld 1
	if (TinselV1PSX)
		SearchMan.addDirectory(gameDataDir.getPath(), gameDataDir, 0, 3, true);

	const GameSettings *g;

	const char *gameid = ConfMan.get("gameid").c_str();
	for (g = tinselSettings; g->gameid; ++g)
		if (!scumm_stricmp(g->gameid, gameid))
			_gameId = g->id;

	int cd_num = ConfMan.getInt("cdrom");
	if (cd_num >= 0)
		_system->getAudioCDManager()->openCD(cd_num);

	_midiMusic = new MidiMusicPlayer();
	_pcmMusic = new PCMMusicPlayer();

	_sound = new SoundManager(this);

	_bmv = new BMVPlayer();

	_mousePos.x = 0;
	_mousePos.y = 0;
	_keyHandler = NULL;
	_dosPlayerDir = 0;
}
コード例 #13
0
ファイル: cloud.cpp プロジェクト: 86400/scummvm
TestExitStatus CloudTests::testFolderDownloading() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API downloadFolder() method.\n"
		"In this test we'll try to download remote 'testbed/' directory.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : downloadFolder()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
	Common::String filepath = node.getPath();
	if (CloudMan.downloadFolder(
			getRemoteTestPath(),
			filepath.c_str(),
			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryDownloadedCallback),
			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
		) == nullptr) {
		Testsuite::logPrintf("Warning! No Request is returned!\n");
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to download into 'testbed/downloaded_directory'?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! Directory was not downloaded!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("Directory was downloaded\n");
	return kTestPassed;
}
コード例 #14
0
ファイル: psp-saves.cpp プロジェクト: havlenapetr/Scummvm
void PSPSaveFileManager::checkPath(const Common::FSNode &dir) {
	const char *savePath = dir.getPath().c_str();
	clearError();

	PowerMan.beginCriticalSection();
	
	//check if the save directory exists
	SceUID fd = sceIoDopen(savePath);
	if (fd < 0) {
		//No? then let's create it.
		sceIoMkdir(savePath, 0777);
	} else {
		//it exists, so close it again.
		sceIoDclose(fd);
	}
	
	PowerMan.endCriticalSection();
}
コード例 #15
0
ファイル: cloud.cpp プロジェクト: 86400/scummvm
TestExitStatus CloudTests::testSavesSync() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API syncSaves() method.\n"
		"In this test we'll try to sync your saves.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : syncSaves()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
	Common::String filepath = node.getPath();
	if (CloudMan.syncSaves(
			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&savesSyncedCallback),
			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
		) == nullptr) {
		Testsuite::logPrintf("Warning! No Request is returned!\n");
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to sync saves?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! Saves were not synced!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("Saves were synced successfully\n");
	return kTestPassed;
}
コード例 #16
0
ファイル: default-saves.cpp プロジェクト: peterkohaut/scummvm
bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) {
	// Assure the savefile name cache is up-to-date.
	assureCached(getSavePath());
	if (getError().getCode() != Common::kNoError)
		return false;
	
#ifdef USE_LIBCURL
	// Update file's timestamp
	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
	Common::HashMap<Common::String, uint32>::iterator it = timestamps.find(filename);
	if (it != timestamps.end()) {
		timestamps.erase(it);
		saveTimestamps(timestamps);
	}
#endif

	// Obtain node if exists.
	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
	if (file == _saveFileCache.end()) {
		return false;
	} else {
		const Common::FSNode fileNode = file->_value;
		// Remove from cache, this invalidates the 'file' iterator.
		_saveFileCache.erase(file);
		file = _saveFileCache.end();

		// FIXME: remove does not exist on all systems. If your port fails to
		// compile because of this, please let us know (scummvm-devel).
		// There is a nicely portable workaround, too: Make this method overloadable.
		if (remove(fileNode.getPath().c_str()) != 0) {
#ifndef _WIN32_WCE
			if (errno == EACCES)
				setError(Common::kWritePermissionDenied, "Search or write permission denied: "+fileNode.getName());

			if (errno == ENOENT)
				setError(Common::kPathDoesNotExist, "removeSavefile: '"+fileNode.getName()+"' does not exist or path is invalid");
#endif
			return false;
		} else {
			return true;
		}
	}
}
コード例 #17
0
ファイル: CELauncherDialog.cpp プロジェクト: peres/scummvm
void CELauncherDialog::automaticScanDirectory(const Common::FSNode &node) {
	// First check if we have a recognized game in the current directory
	Common::FSList files;
	node.getChildren(files, Common::FSNode::kListFilesOnly);
	// detect
	GameList candidates(EngineMan.detectGames(files));
	// insert
	if (candidates.size() >= 1) {
		GameDescriptor result = candidates[0];
		result["path"] = node.getPath();
		addGameToConf(result);
	}
	// Then recurse on the subdirectories
	Common::FSList dirs;
	node.getChildren(dirs, Common::FSNode::kListDirectoriesOnly);
	for (Common::FSList::const_iterator currentDir = dirs.begin(); currentDir != dirs.end(); ++currentDir)
		automaticScanDirectory(*currentDir);

}
コード例 #18
0
ファイル: advancedDetector.cpp プロジェクト: OmerMor/scummvm
void AdvancedMetaEngine::reportUnknown(const Common::FSNode &path, const ADFilePropertiesMap &filesProps) const {
	// TODO: This message should be cleaned up / made more specific.
	// For example, we should specify at least which engine triggered this.
	//
	// Might also be helpful to display the full path (for when this is used
	// from the mass detector).
	Common::String report = Common::String::format(
			_("The game in '%s' seems to be unknown.\n"
			  "Please, report the following data to the ScummVM team along with name\n"
			  "of the game you tried to add and its version, language, etc.:"), path.getPath().c_str()) + "\n";
	report += "\n";

	for (ADFilePropertiesMap::const_iterator file = filesProps.begin(); file != filesProps.end(); ++file)
		report += Common::String::format("  {\"%s\", 0, \"%s\", %d},\n", file->_key.c_str(), file->_value.md5.c_str(), file->_value.size);

	report += "\n";

	g_system->logMessage(LogMessageType::kInfo, report.c_str());
}
コード例 #19
0
ファイル: default-saves.cpp プロジェクト: peterkohaut/scummvm
Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String &filename, bool compress) {
	// Assure the savefile name cache is up-to-date.
	const Common::String savePathName = getSavePath();
	assureCached(savePathName);
	if (getError().getCode() != Common::kNoError)
		return nullptr;

	for (Common::StringArray::const_iterator i = _lockedFiles.begin(), end = _lockedFiles.end(); i != end; ++i) {
		if (filename == *i) {
			return nullptr; //file is locked, no saving available
		}
	}

#ifdef USE_LIBCURL
	// Update file's timestamp
	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
	timestamps[filename] = INVALID_TIMESTAMP;
	saveTimestamps(timestamps);
#endif

	// Obtain node.
	SaveFileCache::const_iterator file = _saveFileCache.find(filename);
	Common::FSNode fileNode;

	// If the file did not exist before, we add it to the cache.
	if (file == _saveFileCache.end()) {
		const Common::FSNode savePath(savePathName);
		fileNode = savePath.getChild(filename);
	} else {
		fileNode = file->_value;
	}

	// Open the file for saving.
	Common::WriteStream *const sf = fileNode.createWriteStream();
	Common::OutSaveFile *const result = new Common::OutSaveFile(compress ? Common::wrapCompressedWriteStream(sf) : sf);

	// Add file to cache now that it exists.
	_saveFileCache[filename] = Common::FSNode(fileNode.getPath());

	return result;
}
コード例 #20
0
ファイル: detection.cpp プロジェクト: athrxx/scummvm
// Based on AdvancedMetaEngine::detectGame
ADDetectedGames AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
	// We run the file-based detector first and then add to the returned list
	ADDetectedGames matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra);

	debug(3, "Starting disk image detection in dir '%s'", parent.getPath().c_str());

	FilePropertiesMap filesProps;
	bool gotAnyMatchesWithAllFiles = false;

	for (uint g = 0; gameDiskDescriptions[g].desc.gameId != 0; ++g) {
		ADDetectedGame game(&gameDiskDescriptions[g].desc);

		// Skip games that don't meet the language/platform/extra criteria
		if (language != Common::UNK_LANG && game.desc->language != Common::UNK_LANG) {
			if (game.desc->language != language && !(language == Common::EN_ANY && (game.desc->flags & ADGF_ADDENGLISH)))
				continue;
		}

		if (platform != Common::kPlatformUnknown && game.desc->platform != Common::kPlatformUnknown && game.desc->platform != platform)
			continue;

		if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && game.desc->extra != extra)
			continue;

		bool allFilesPresent = true;

		for (uint f = 0; game.desc->filesDescriptions[f].fileName; ++f) {
			const ADGameFileDescription &fDesc = game.desc->filesDescriptions[f];
			Common::String fileName;
			bool foundDiskImage = false;

			for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) {
				if (diskImageExts[e].platform == game.desc->platform) {
					Common::String testFileName(fDesc.fileName);
					testFileName += diskImageExts[e].extension;

					if (addFileProps(allFiles, testFileName, filesProps)) {
						if (foundDiskImage) {
							warning("Ignoring '%s' (already found '%s')", testFileName.c_str(), fileName.c_str());
							filesProps.erase(testFileName);
						} else {
							foundDiskImage = true;
							fileName = testFileName;
						}
					}
				}
			}

			if (!foundDiskImage) {
				allFilesPresent = false;
				break;
			}

			game.matchedFiles[fileName] = filesProps[fileName];

			if (game.hasUnknownFiles)
				continue;

			if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) {
				debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str());
				game.hasUnknownFiles = true;
				continue;
			}

			if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) {
				debug(3, "Size Mismatch. Skipping");
				game.hasUnknownFiles = true;
				continue;
			}

			debug(3, "Matched file: %s", fileName.c_str());
		}

		if (allFilesPresent && !game.hasUnknownFiles) {
			debug(2, "Found game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g);
			gotAnyMatchesWithAllFiles = true;
			matched.push_back(game);
		} else {
			if (allFilesPresent && !gotAnyMatchesWithAllFiles) {
				if (matched.empty() || strcmp(matched.back().desc->gameId, game.desc->gameId) != 0)
					matched.push_back(game);
			}

			debug(5, "Skipping game: %s (%s/%s) (%d)", game.desc->gameId, getPlatformDescription(game.desc->platform), getLanguageDescription(game.desc->language), g);
		}
	}

	return matched;
}
コード例 #21
0
ファイル: engine.cpp プロジェクト: DrItanium/scummvm
void Engine::initializePath(const Common::FSNode &gamePath) {
	SearchMan.addDirectory(gamePath.getPath(), gamePath, 0, 4);
}
コード例 #22
0
ファイル: cloud.cpp プロジェクト: 86400/scummvm
TestExitStatus CloudTests::testUploading() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API upload() method.\n"
		"In this test we'll try to upload a 'test1/file.txt' file.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : upload()\n");
		return kTestSkipped;
	}

	if (!ConfParams.isGameDataFound()) {
		Testsuite::logPrintf("Info! Couldn't find the game data, so skipping test : upload()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSDirectory *directory = gameRoot.getSubDirectory("test1");
	Common::FSNode node = directory->getFSNode().getChild("file.txt");
	delete directory;

	if (CloudMan.getCurrentStorage()->uploadStreamSupported()) {
		if (CloudMan.getCurrentStorage()->upload(
				Common::String(getRemoteTestPath()) + "/testfile.txt",
				node.createReadStream(),
				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}
	} else {
		Common::String filepath = node.getPath();
		if (CloudMan.getCurrentStorage()->upload(
				Common::String(getRemoteTestPath()) + "/testfile.txt",
				filepath.c_str(),
				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	Common::String info2 = "upload() is finished. Do you want to list '/testbed' directory?";

	if (!Testsuite::handleInteractiveInput(info2, "Yes", "No", kOptionRight)) {
		ConfParams.setCloudTestCallbackCalled(false);

		if (CloudMan.listDirectory(
				getRemoteTestPath(),
				new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}

		if (!waitForCallbackMore()) return kTestSkipped;
		Testsuite::clearScreen();

		if (ConfParams.isCloudTestErrorCallbackCalled()) {
			Testsuite::logPrintf("Error callback was called\n");
			return kTestFailed;
		}
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to upload into 'testbed/testfile.txt' file?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! File was not uploaded!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("File was uploaded\n");
	return kTestPassed;
}
コード例 #23
0
ファイル: engine.cpp プロジェクト: jaeyeonkim/scummvm-kor
void Engine::checkCD() {
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
	// It is a known bug under Windows that games that play CD audio cause
	// ScummVM to crash if the data files are read from the same CD. Check
	// if this appears to be the case and issue a warning.

	// If we can find a compressed audio track, then it should be ok even
	// if it's running from CD.

#ifdef USE_VORBIS
	if (Common::File::exists("track1.ogg") ||
	    Common::File::exists("track01.ogg"))
		return;
#endif
#ifdef USE_FLAC
	if (Common::File::exists("track1.fla") ||
            Common::File::exists("track1.flac") ||
	    Common::File::exists("track01.fla") ||
	    Common::File::exists("track01.flac"))
		return;
#endif
#ifdef USE_MAD
	if (Common::File::exists("track1.mp3") ||
	    Common::File::exists("track01.mp3"))
		return;
#endif

	char buffer[MAXPATHLEN];
	int i;

	const Common::FSNode gameDataDir(ConfMan.get("path"));

	if (gameDataDir.getPath().empty()) {
		// That's it! I give up!
		if (getcwd(buffer, MAXPATHLEN) == NULL)
			return;
	} else
		Common::strlcpy(buffer, gameDataDir.getPath().c_str(), sizeof(buffer));

	for (i = 0; i < MAXPATHLEN - 1; i++) {
		if (buffer[i] == '\\')
			break;
	}

	buffer[i + 1] = 0;

	if (GetDriveType(buffer) == DRIVE_CDROM) {
		GUI::MessageDialog dialog(
			_("You appear to be playing this game directly\n"
			"from the CD. This is known to cause problems,\n"
			"and it is therefore recommended that you copy\n"
			"the data files to your hard disk instead.\n"
			"See the README file for details."), _("OK"));
		dialog.runModal();
	} else {
		// If we reached here, the game has audio tracks,
		// it's not ran from the CD and the tracks have not
		// been ripped.
		GUI::MessageDialog dialog(
			_("This game has audio tracks in its disk. These\n"
			"tracks need to be ripped from the disk using\n"
			"an appropriate CD audio extracting tool in\n"
			"order to listen to the game's music.\n"
			"See the README file for details."), _("OK"));
		dialog.runModal();
	}
#endif
}
コード例 #24
0
ファイル: posix-saves.cpp プロジェクト: jvprat/residual
void POSIXSaveFileManager::checkPath(const Common::FSNode &dir) {
	const Common::String path = dir.getPath();
	clearError();

	struct stat sb;

	// Check whether the dir exists
	if (stat(path.c_str(), &sb) == -1) {
		// The dir does not exist, or stat failed for some other reason.
		// If the problem was that the path pointed to nothing, try
		// to create the dir (ENOENT case).
		switch (errno) {
		case EACCES:
			setError(Common::kWritePermissionDenied, "Search or write permission denied: "+path);
			break;
		case ELOOP:
			setError(Common::kUnknownError, "Too many symbolic links encountered while traversing the path: "+path);
			break;
		case ENAMETOOLONG:
			setError(Common::kUnknownError, "The path name is too long: "+path);
			break;
		case ENOENT:
			if (mkdir(path.c_str(), 0755) != 0) {
				// mkdir could fail for various reasons: The parent dir doesn't exist,
				// or is not writeable, the path could be completly bogus, etc.
				warning("mkdir for '%s' failed", path.c_str());
				perror("mkdir");

				switch (errno) {
				case EACCES:
					setError(Common::kWritePermissionDenied, "Search or write permission denied: "+path);
					break;
				case EMLINK:
					setError(Common::kUnknownError, "The link count of the parent directory would exceed {LINK_MAX}: "+path);
					break;
				case ELOOP:
					setError(Common::kUnknownError, "Too many symbolic links encountered while traversing the path: "+path);
					break;
				case ENAMETOOLONG:
					setError(Common::kUnknownError, "The path name is too long: "+path);
					break;
				case ENOENT:
					setError(Common::kPathDoesNotExist, "A component of the path does not exist, or the path is an empty string: "+path);
					break;
				case ENOTDIR:
					setError(Common::kPathDoesNotExist, "A component of the path prefix is not a directory: "+path);
					break;
				case EROFS:
					setError(Common::kWritePermissionDenied, "The parent directory resides on a read-only file system:"+path);
					break;
				}
			}
			break;
		case ENOTDIR:
			setError(Common::kPathDoesNotExist, "A component of the path prefix is not a directory: "+path);
			break;
		}
	} else {
		// So stat() succeeded. But is the path actually pointing to a directory?
		if (!S_ISDIR(sb.st_mode)) {
			setError(Common::kPathDoesNotExist, "The given savepath is not a directory: "+path);
		}
	}
}
コード例 #25
0
static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &params, Common::Language language, Common::Platform platform, const Common::String &extra) {
    FileMap allFiles;
    SizeMD5Map filesSizeMD5;

    const ADGameFileDescription *fileDesc;
    const ADGameDescription *g;
    const byte *descPtr;

    if (fslist.empty())
        return ADGameDescList();
    Common::FSNode parent = fslist.begin()->getParent();
    debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());

    // First we compose a hashmap of all files in fslist.
    // Includes nifty stuff like removing trailing dots and ignoring case.
    composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs);

    // Check which files are included in some ADGameDescription *and* present
    // in fslist. Compute MD5s and file sizes for these files.
    for (descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize) {
        g = (const ADGameDescription *)descPtr;

        for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
            Common::String fname = fileDesc->fileName;
            SizeMD5 tmp;

            if (filesSizeMD5.contains(fname))
                continue;

            // FIXME/TODO: We don't handle the case that a file is listed as a regular
            // file and as one with resource fork.

            if (g->flags & ADGF_MACRESFORK) {
                Common::MacResManager *macResMan = new Common::MacResManager();

                if (macResMan->open(parent, fname)) {
                    tmp.md5 = macResMan->computeResForkMD5AsString(params.md5Bytes);
                    tmp.size = macResMan->getResForkDataSize();
                    debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str());
                    filesSizeMD5[fname] = tmp;
                }

                delete macResMan;
            } else {
                if (allFiles.contains(fname)) {
                    debug(3, "+ %s", fname.c_str());

                    Common::File testFile;

                    if (testFile.open(allFiles[fname])) {
                        tmp.size = (int32)testFile.size();
                        tmp.md5 = Common::computeStreamMD5AsString(testFile, params.md5Bytes);
                    } else {
                        tmp.size = -1;
                    }

                    debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str());
                    filesSizeMD5[fname] = tmp;
                }
            }
        }
    }

    ADGameDescList matched;
    int maxFilesMatched = 0;
    bool gotAnyMatchesWithAllFiles = false;

    // MD5 based matching
    uint i;
    for (i = 0, descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize, ++i) {
        g = (const ADGameDescription *)descPtr;
        bool fileMissing = false;

        // Do not even bother to look at entries which do not have matching
        // language and platform (if specified).
        if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language
                && !(language == Common::EN_ANY && (g->flags & ADGF_ADDENGLISH))) ||
                (platform != Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown && g->platform != platform)) {
            continue;
        }

        if ((params.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
            continue;

        bool allFilesPresent = true;
        int curFilesMatched = 0;

        // Try to match all files for this game
        for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
            Common::String tstr = fileDesc->fileName;

            if (!filesSizeMD5.contains(tstr)) {
                fileMissing = true;
                allFilesPresent = false;
                break;
            }

            if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
                debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5.c_str());
                fileMissing = true;
                break;
            }

            if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) {
                debug(3, "Size Mismatch. Skipping");
                fileMissing = true;
                break;
            }

            debug(3, "Matched file: %s", tstr.c_str());
            curFilesMatched++;
        }

        // We found at least one entry with all required files present.
        // That means that we got new variant of the game.
        //
        // Without this check we would have erroneous checksum display
        // where only located files will be enlisted.
        //
        // Potentially this could rule out variants where some particular file
        // is really missing, but the developers should better know about such
        // cases.
        if (allFilesPresent)
            gotAnyMatchesWithAllFiles = true;

        if (!fileMissing) {
            debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
                  getPlatformDescription(g->platform), getLanguageDescription(g->language), i);

            if (curFilesMatched > maxFilesMatched) {
                debug(2, " ... new best match, removing all previous candidates");
                maxFilesMatched = curFilesMatched;

                matched.clear();	// Remove any prior, lower ranked matches.
                matched.push_back(g);
            } else if (curFilesMatched == maxFilesMatched) {
                matched.push_back(g);
            } else {
                debug(2, " ... skipped");
            }

        } else {
            debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameid, g->extra,
                  getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
        }
    }

    // We didn't find a match
    if (matched.empty()) {
        if (!filesSizeMD5.empty() && gotAnyMatchesWithAllFiles) {
            reportUnknown(parent, filesSizeMD5);
        }

        // Filename based fallback
        if (params.fileBasedFallback != 0)
            matched = detectGameFilebased(allFiles, params);
    }

    return matched;
}
コード例 #26
0
ファイル: advancedDetector.cpp プロジェクト: Botje/residualvm
ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
	ADFilePropertiesMap filesProps;

	const ADGameFileDescription *fileDesc;
	const ADGameDescription *g;
	const byte *descPtr;

	debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());

	// Check which files are included in some ADGameDescription *and* are present.
	// Compute MD5s and file sizes for these files.
	for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize) {
		g = (const ADGameDescription *)descPtr;

		for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
			Common::String fname = fileDesc->fileName;
			ADFileProperties tmp;

			if (filesProps.contains(fname))
				continue;

			if (getFileProperties(parent, allFiles, *g, fname, tmp)) {
				debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str());
				filesProps[fname] = tmp;
			}
		}
	}

	ADGameDescList matched;
	ADGameIdList matchedGameIds;
	int maxFilesMatched = 0;
	bool gotAnyMatchesWithAllFiles = false;

	// MD5 based matching
	uint i;
	for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize, ++i) {
		g = (const ADGameDescription *)descPtr;
		bool fileMissing = false;

		// Do not even bother to look at entries which do not have matching
		// language and platform (if specified).
		if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language
			 && !(language == Common::EN_ANY && (g->flags & ADGF_ADDENGLISH))) ||
			(platform != Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown && g->platform != platform)) {
			continue;
		}

		if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra)
			continue;

		bool allFilesPresent = true;
		int curFilesMatched = 0;
		bool hashOrSizeMismatch = false;

		// Try to match all files for this game
		for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) {
			Common::String tstr = fileDesc->fileName;

			if (!filesProps.contains(tstr)) {
				fileMissing = true;
				allFilesPresent = false;
				break;
			}

			if (hashOrSizeMismatch)
				continue;

			if (fileDesc->md5 != NULL && fileDesc->md5 != filesProps[tstr].md5) {
				debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesProps[tstr].md5.c_str());
				fileMissing = true;
				hashOrSizeMismatch = true;
				continue;
			}

			if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesProps[tstr].size) {
				debug(3, "Size Mismatch. Skipping");
				fileMissing = true;
				hashOrSizeMismatch = true;
				continue;
			}

			debug(3, "Matched file: %s", tstr.c_str());
			curFilesMatched++;
		}

		// We found at least one entry with all required files present.
		// That means that we got new variant of the game.
		//
		// Without this check we would have erroneous checksum display
		// where only located files will be enlisted.
		//
		// Potentially this could rule out variants where some particular file
		// is really missing, but the developers should better know about such
		// cases.
		if (allFilesPresent) {
			gotAnyMatchesWithAllFiles = true;
			if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), g->gameId) != 0)
				matchedGameIds.push_back(g->gameId);
		}

		if (!fileMissing) {
			debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
			 getPlatformDescription(g->platform), getLanguageDescription(g->language), i);

			if (curFilesMatched > maxFilesMatched) {
				debug(2, " ... new best match, removing all previous candidates");
				maxFilesMatched = curFilesMatched;

				matched.clear();	// Remove any prior, lower ranked matches.
				matched.push_back(g);
			} else if (curFilesMatched == maxFilesMatched) {
				matched.push_back(g);
			} else {
				debug(2, " ... skipped");
			}

		} else {
			debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra,
			 getPlatformDescription(g->platform), getLanguageDescription(g->language), i);
		}
	}

	// We didn't find a match
	if (matched.empty()) {
		if (!filesProps.empty() && gotAnyMatchesWithAllFiles) {
			reportUnknown(parent, filesProps, matchedGameIds);
		}

		// Filename based fallback
	}

	return matched;
}
コード例 #27
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();
}