Exemple #1
0
bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
	const char *const EXTENSIONS[3] = { ".ulx", ".blb", ".gblorb" };

	// Loop through the files of the folder
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		// Check for a recognised filename
		if (file->isDirectory())
			continue;
		Common::String filename = file->getName();
		bool hasExt = false;
		for (int idx = 0; idx < 3 && !hasExt; ++idx)
			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
		if (!hasExt)
			continue;

		// Open up the file and calculate the md5
		Common::File gameFile;
		if (!gameFile.open(*file))
			continue;
		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
		size_t filesize = gameFile.size();
		gameFile.close();

		// Check for known games
		const GlulxeGameDescription *p = GLULXE_GAMES;
		while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize))
			++p;

		DetectedGame gd;
		if (!p->_gameId) {
			if (filename.hasSuffixIgnoreCase(".blb"))
				continue;

			if (gDebugLevel > 0) {
				// Print an entry suitable for putting into the detection_tables.h, using the
				// name of the parent folder the game is in as the presumed game Id
				Common::String folderName = file->getParent().getName();
				if (folderName.hasSuffix("\\"))
					folderName.deleteLastChar();
				Common::String fname = filename;
				const char *dot = strchr(fname.c_str(), '.');
				if (dot)
					fname = Common::String(fname.c_str(), dot);

				debug("ENTRY0(\"%s\", \"%s\", %u),", fname.c_str(), md5.c_str(), (uint)filesize);
			}
			const PlainGameDescriptor &desc = GLULXE_GAME_LIST[0];
			gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown);
		} else {
			PlainGameDescriptor gameDesc = findGame(p->_gameId);
			gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
			gd.setGUIOptions(GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
		}

		gd.addExtraEntry("filename", filename);
		gameList.push_back(gd);
	}

	return !gameList.empty();
}
Exemple #2
0
static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map, int depth, const char **globs) {
	if (depth <= 0)
		return;

	if (fslist.empty())
		return;

	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (!file->isDirectory()) {
			DetectorDesc d;
			d.node = *file;
			d.md5Entry = 0;
			fileMD5Map[file->getName()] = d;
		} else {
			if (!globs)
				continue;

			bool matched = false;
			for (const char **glob = globs; *glob; glob++)
				if (file->getName().matchString(*glob, true)) {
					matched = true;
					break;
				}
					
			if (!matched)
				continue;

			Common::FSList files;

			if (file->getChildren(files, Common::FSNode::kListAll)) {
				composeFileHashMap(files, fileMD5Map, depth - 1, globs);
			}
		}
	}
}
Exemple #3
0
GameList QueenMetaEngine::detectGames(const Common::FSList &fslist) const {
	GameList detectedGames;

	// Iterate over all files in the given directory
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (file->isDirectory()) {
			continue;
		}
		if (file->getName().equalsIgnoreCase("queen.1") || file->getName().equalsIgnoreCase("queen.1c")) {
			Common::File dataFile;
			if (!dataFile.open(*file)) {
				continue;
			}
			Queen::DetectedGameVersion version;
			if (Queen::Resource::detectVersion(&version, &dataFile)) {
				GameDescriptor dg(queenGameDescriptor.gameid, queenGameDescriptor.description, version.language, version.platform);
				if (version.features & Queen::GF_DEMO) {
					dg.updateDesc("Demo");
					dg.setGUIOptions(Common::GUIO_NOSPEECH);
				} else if (version.features & Queen::GF_INTERVIEW) {
					dg.updateDesc("Interview");
					dg.setGUIOptions(Common::GUIO_NOSPEECH);
				} else if (version.features & Queen::GF_FLOPPY) {
					dg.updateDesc("Floppy");
					dg.setGUIOptions(Common::GUIO_NOSPEECH);
				} else if (version.features & Queen::GF_TALKIE) {
					dg.updateDesc("Talkie");
				}
				detectedGames.push_back(dg);
				break;
			}
		}
	}
	return detectedGames;
}
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
	ADGameDescList matches;
	GameList detectedGames;
	FileMap allFiles;

	if (fslist.empty())
		return detectedGames;

	// Compose a hashmap of all files in fslist.
	composeFileHashMap(allFiles, fslist, (_maxScanDepth == 0 ? 1 : _maxScanDepth));

	// Run the detector on this
	matches = detectGame(fslist.begin()->getParent(), allFiles, Common::UNK_LANG, Common::kPlatformUnknown, "");

	if (matches.empty()) {
		// Use fallback detector if there were no matches by other means
		const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist);
		if (fallbackDesc != 0) {
			GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds));
			updateGameDescriptor(desc, fallbackDesc);
			detectedGames.push_back(desc);
		}
	} else {
		// Otherwise use the found matches
		cleanupPirated(matches);
		for (uint i = 0; i < matches.size(); i++) {
			GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds));
			updateGameDescriptor(desc, matches[i]);
			detectedGames.push_back(desc);
		}
	}

	return detectedGames;
}
Exemple #5
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);
				}
		}
	}
}
Exemple #6
0
// Search for a node with the given "name", inside fslist. Ignores case
// when performing the matching. The first match is returned, so if you
// search for "resource" and two nodes "RESOURE and "resource" are present,
// the first match is used.
static bool searchFSNode(const Common::FSList &fslist, const Common::String &name, Common::FSNode &result) {
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (!scumm_stricmp(file->getName().c_str(), name.c_str())) {
			result = *file;
			return true;
		}
	}
	return false;
}
bool BaseFileManager::registerPackages(const Common::FSList &fslist) {
	for (Common::FSList::const_iterator it = fslist.begin(); it != fslist.end(); ++it) {
		debugC(kWintermuteDebugFileAccess, "Adding %s", (*it).getName().c_str());
		if ((*it).getName().contains(".dcp")) {
			if (registerPackage((*it))) {
				addPath(PATH_PACKAGE, (*it));
			}
		}
	}
	return true;
}
Exemple #8
0
bool isFullGame(const Common::FSList &fslist) {
	Common::FSList::const_iterator file;

	// We distinguish between the two versions by the presense of paris.clu
	for (file = fslist.begin(); file != fslist.end(); ++file) {
		if (!file->isDirectory()) {
			if (file->getName().equalsIgnoreCase("paris.clu"))
				return true;
		}
	}

	return false;
}
Exemple #9
0
 virtual const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const {
     for (Common::FSList::const_iterator d = fslist.begin(); d != fslist.end(); ++d) {
         Common::FSList audiofslist;
         if (d->isDirectory() && d->getName().equalsIgnoreCase("audio") && d->getChildren(audiofslist, Common::FSNode::kListFilesOnly)) {
             for (Common::FSList::const_iterator f = audiofslist.begin(); f != audiofslist.end(); ++f) {
                 if (!f->isDirectory() && f->getName().equalsIgnoreCase("demorolc.raw")) {
                     return &tuckerDemoGameDescription;
                 }
             }
         }
     }
     return 0;
 }
Exemple #10
0
GameList SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
	GameList detectedGames;
	bool hasSkyDsk = false;
	bool hasSkyDnr = false;
	int dinnerTableEntries = -1;
	int dataDiskSize = -1;

	// Iterate over all files in the given directory
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (!file->isDirectory()) {
			const char *fileName = file->getName().c_str();

			if (0 == scumm_stricmp("sky.dsk", fileName)) {
				Common::File dataDisk;
				if (dataDisk.open(*file)) {
					hasSkyDsk = true;
					dataDiskSize = dataDisk.size();
				}
			}

			if (0 == scumm_stricmp("sky.dnr", fileName)) {
				Common::File dinner;
				if (dinner.open(*file)) {
					hasSkyDnr = true;
					dinnerTableEntries = dinner.readUint32LE();
				}
			}
		}
	}

	if (hasSkyDsk && hasSkyDnr) {
		// Match found, add to list of candidates, then abort inner loop.
		// The game detector uses US English by default. We want British
		// English to match the recorded voices better.
		GameDescriptor dg("sky", skySetting.gameid, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown);
		const SkyVersion *sv = skyVersions;
		while (sv->dinnerTableEntries) {
			if (dinnerTableEntries == sv->dinnerTableEntries &&
				(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
				dg.updateDesc(Common::String::format("v0.0%d %s", sv->version, sv->extraDesc).c_str());
				dg.setGUIOptions(sv->guioptions);
				break;
			}
			++sv;
		}
		detectedGames.push_back(dg);
	}

	return detectedGames;
}
Exemple #11
0
bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
	// Loop through the files of the folder
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		// Check for a recognised filename
		Common::String filename = file->getName();
		if (file->isDirectory() || !(filename.hasSuffixIgnoreCase(".gam")
				|| filename.hasSuffixIgnoreCase(".blorb")))
			continue;

		// Open up the file and calculate the md5
		Common::File gameFile;
		if (!gameFile.open(*file))
			continue;
		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
		size_t filesize = gameFile.size();
		gameFile.close();

		// Check for known games
		const TADSGameDescription *p = TADS_GAMES;
		while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
			++p;

		DetectedGame gd;
		if (!p->_gameId) {
			if (!filename.hasSuffixIgnoreCase(".gam"))
				continue;

			if (gDebugLevel > 0) {
				// Print an entry suitable for putting into the detection_tables.h, using the
				Common::String fname = filename;
				const char *dot = strchr(fname.c_str(), '.');
				if (dot)
					fname = Common::String(fname.c_str(), dot);

				debug("ENTRY0(\"%s\", \"%s\", %u),", fname.c_str(), md5.c_str(), (uint)filesize);
			}
			const TADSDescriptor &desc = TADS_GAME_LIST[0];
			gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown);
		}
		else {
			PlainGameDescriptor gameDesc = findGame(p->_gameId);
			gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
		}

		gd.addExtraEntry("filename", filename);
		gameList.push_back(gd);
	}

	return !gameList.empty();
}
Exemple #12
0
GameList Sword2MetaEngine::detectGames(const Common::FSList &fslist) const {
	GameList detectedGames;
	const Sword2::GameSettings *g;
	Common::FSList::const_iterator file;

	// TODO: It would be nice if we had code here which distinguishes
	// between the 'sword2' and 'sword2demo' targets. The current code
	// can't do that since they use the same detectname.

	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()) {
				const char *fileName = file->getName().c_str();

				if (0 == scumm_stricmp(g->detectname, fileName)) {
					// Match found, add to list of candidates, then abort inner loop.
					detectedGames.push_back(GameDescriptor(g->gameid, g->description, Common::UNK_LANG, Common::kPlatformUnknown, Common::GUIO_NOMIDI));
					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()) {
				const char *fileName = file->getName().c_str();

				if (0 == scumm_stricmp("clusters", fileName)) {
					Common::FSList recList;
					if (file->getChildren(recList, Common::FSNode::kListAll)) {
						GameList recGames(detectGames(recList));
						if (!recGames.empty()) {
							detectedGames.push_back(recGames);
							break;
						}
					}
				}
			}
		}
	}


	return detectedGames;
}
Exemple #13
0
void Sword1CheckDirectory(const Common::FSList &fslist, bool *filesFound) {
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (!file->isDirectory()) {
			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);
				}
		}
	}
}
Exemple #14
0
void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
	// Check that path exists and is usable.
	checkPath(Common::FSNode(savePathName));

#ifdef USE_LIBCURL
	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
	if (!files.empty()) updateSavefilesList(files); //makes this cache invalid
	else _lockedFiles = files;
#endif

	if (_cachedDirectory == savePathName) {
		return;
	}

	_saveFileCache.clear();
	_cachedDirectory.clear();

	if (getError().getCode() != Common::kNoError) {
		warning("DefaultSaveFileManager::assureCached: Can not cache path '%s': '%s'", savePathName.c_str(), getErrorDesc().c_str());
		return;
	}

	// FSNode can cache its members, thus create it after checkPath to reflect
	// actual file system state.
	const Common::FSNode savePath(savePathName);

	Common::FSList children;
	if (!savePath.getChildren(children, Common::FSNode::kListFilesOnly)) {
		return;
	}

	// Build the savefile name cache.
	for (Common::FSList::const_iterator file = children.begin(), end = children.end(); file != end; ++file) {
		if (_saveFileCache.contains(file->getName())) {
			warning("DefaultSaveFileManager::assureCached: Name clash when building cache, ignoring file '%s'", file->getName().c_str());
		} else {
			_saveFileCache[file->getName()] = *file;
		}
	}

	// Only now store that we cached 'savePathName' to indicate we successfully
	// cached the directory.
	_cachedDirectory = savePathName;
}
Exemple #15
0
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);

}
Exemple #16
0
bool BaseFileManager::registerPackages() {
	debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Scanning packages");

	// Register without using SearchMan, as otherwise the FSNode-based lookup in openPackage will fail
	// and that has to be like that to support the detection-scheme.
	Common::FSList files;
	for (Common::FSList::iterator it = _packagePaths.begin(); it != _packagePaths.end(); ++it) {
		debugC(kWintermuteDebugFileAccess, "Should register folder: %s %s", (*it).getPath().c_str(), (*it).getName().c_str());
		if (!(*it).getChildren(files, Common::FSNode::kListFilesOnly)) {
			warning("getChildren() failed for path: %s", (*it).getDisplayName().c_str());
		}
		for (Common::FSList::iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
			if (!fileIt->getName().hasSuffix(".dcp")) {
				continue;
			}
			// Avoid registering all the language files
			// TODO: Select based on the gameDesc.
			if (_language != Common::UNK_LANG && fileIt->getParent().getName() == "language") {
				Common::String parentName = fileIt->getParent().getName();
				Common::String dcpName = fileIt->getName();
				if (_language == Common::EN_ANY && fileIt->getName() != "english.dcp") {
					continue;
				} else if (_language == Common::CZ_CZE && fileIt->getName() != "czech.dcp") {
					continue;
				} else if (_language == Common::IT_ITA && fileIt->getName() != "italian.dcp") {
					continue;
				} else if (_language == Common::PL_POL && fileIt->getName() != "polish.dcp") {
					continue;
				} else if (_language == Common::RU_RUS && fileIt->getName() != "russian.dcp") {
					continue;
				}
			}
			debugC(kWintermuteDebugFileAccess, "Registering %s %s", (*fileIt).getPath().c_str(), (*fileIt).getName().c_str());
			registerPackage((*fileIt));
		}
	}

//	debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "  Registered %d files in %d package(s)", _files.size(), _packages.size());

	return STATUS_OK;
}
static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char * const *directoryGlobs) {
    if (depth <= 0)
        return;

    if (fslist.empty())
        return;

    // First we compose a hashmap of all files in fslist.
    // Includes nifty stuff like removing trailing dots and ignoring case.
    for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
        if (file->isDirectory()) {
            Common::FSList files;

            if (!directoryGlobs)
                continue;

            bool matched = false;
            for (const char * const *glob = directoryGlobs; *glob; glob++)
                if (file->getName().matchString(*glob, true)) {
                    matched = true;
                    break;
                }

            if (!matched)
                continue;

            if (!file->getChildren(files, Common::FSNode::kListAll))
                continue;

            composeFileHashMap(files, allFiles, depth - 1, directoryGlobs);
        }

        Common::String tstr = file->getName();

        // Strip any trailing dot
        if (tstr.lastChar() == '.')
            tstr.deleteLastChar();

        allFiles[tstr] = *file;	// Record the presence of this file
    }
}
Exemple #18
0
void AdvancedMetaEngine::composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName) const {
	if (depth <= 0)
		return;

	if (fslist.empty())
		return;

	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		Common::String tstr = (_matchFullPaths && !parentName.empty() ? parentName + "/" : "") + file->getName();

		if (file->isDirectory()) {
			Common::FSList files;

			if (!_directoryGlobs)
				continue;

			bool matched = false;
			for (const char * const *glob = _directoryGlobs; *glob; glob++)
				if (file->getName().matchString(*glob, true)) {
					matched = true;
					break;
				}

			if (!matched)
				continue;

			if (!file->getChildren(files, Common::FSNode::kListAll))
				continue;

			composeFileHashMap(allFiles, files, depth - 1, tstr);
		}

		// Strip any trailing dot
		if (tstr.lastChar() == '.')
			tstr.deleteLastChar();

		allFiles[tstr] = *file;	// Record the presence of this file
	}
}
Exemple #19
0
Common::HashMap<Common::String, Score *> DirectorEngine::loadMMMNames(Common::String folder) {
	Common::FSNode directory(folder);
	Common::FSList movies;

	Common::HashMap<Common::String, Score *> nameMap;
	directory.getChildren(movies, Common::FSNode::kListFilesOnly);

	if (!movies.empty()) {
		for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) {
			if (i->getName() == _sharedMMM) {
				loadSharedCastsFrom(i->getPath());
				continue;
			}

			RIFFArchive *arc = new RIFFArchive();
			arc->openFile(i->getPath());
			Score *sc = new Score(this);
			nameMap[sc->getMacName()] = sc;
		}
	}

	return nameMap;
}
Exemple #20
0
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);
}
Exemple #21
0
Common::HashMap<Common::String, Score *> *DirectorEngine::scanMovies(const Common::String &folder) {
    Common::FSNode directory(folder);
    Common::FSList movies;
    const char *sharedMMMname;

    if (getPlatform() == Common::kPlatformWindows)
        sharedMMMname = "SHARDCST.MMM";
    else
        sharedMMMname = "Shared Cast*";


    Common::HashMap<Common::String, Score *> *nameMap = new Common::HashMap<Common::String, Score *>();
    if (!directory.getChildren(movies, Common::FSNode::kListFilesOnly))
        return nameMap;

    if (!movies.empty()) {
        for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) {
            debugC(2, kDebugLoading, "File: %s", i->getName().c_str());

            if (Common::matchString(i->getName().c_str(), sharedMMMname, true)) {
                _sharedCastFile = i->getName();
                continue;
            }

            Archive *arc = createArchive();

            warning("name: %s", i->getName().c_str());
            arc->openFile(i->getName());
            Score *sc = new Score(this, arc);
            nameMap->setVal(sc->getMacName(), sc);

            debugC(2, kDebugLoading, "Movie name: \"%s\"", sc->getMacName().c_str());
        }
    }

    return nameMap;
}
Exemple #22
0
ADDetectedGame CGEMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
	ADDetectedGame game = detectGameFilebased(allFiles, fslist, CGE::fileBasedFallback);

	if (!game.desc)
		return ADDetectedGame();

	SearchMan.addDirectory("CGEMetaEngine::fallbackDetect", fslist.begin()->getParent());
	ResourceManager *resman;
	resman = new ResourceManager();
	bool sayFileFound = resman->exist("CGE.SAY");
	delete resman;

	SearchMan.remove("CGEMetaEngine::fallbackDetect");

	if (!sayFileFound)
		return ADDetectedGame();

	return game;
}
Exemple #23
0
void JNI::getPluginDirectories(Common::FSList &dirs) {
	JNIEnv *env = JNI::getEnv();

	jobjectArray array =
		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getPluginDirectories);

	if (env->ExceptionCheck()) {
		LOGE("Error finding plugin directories");

		env->ExceptionDescribe();
		env->ExceptionClear();

		return;
	}

	jsize size = env->GetArrayLength(array);
	for (jsize i = 0; i < size; ++i) {
		jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);

		if (path_obj == 0)
			continue;

		const char *path = env->GetStringUTFChars(path_obj, 0);

		if (path == 0) {
			LOGE("Error getting string characters from plugin directory");

			env->ExceptionClear();
			env->DeleteLocalRef(path_obj);

			continue;
		}

		dirs.push_back(Common::FSNode(path));

		env->ReleaseStringUTFChars(path_obj, path);
		env->DeleteLocalRef(path_obj);
	}
}
Exemple #24
0
bool Sword25Engine::loadPackages() {
	PackageManager *packageManagerPtr = Kernel::getInstance()->getPackage();
	assert(packageManagerPtr);

	// Load the main package
	if (!packageManagerPtr->loadPackage("data.b25c", "/"))
		return false;

	// Get the contents of the main program directory and sort them alphabetically
	Common::FSNode dir(ConfMan.get("path"));
	Common::FSList files;
	if (!dir.isDirectory() || !dir.getChildren(files, Common::FSNode::kListAll)) {
		warning("Game data path does not exist or is not a directory");
		return false;
	}

	Common::sort(files.begin(), files.end());

	// Identify all patch packages
	// The filename of patch packages must have the form patch??.b25c, with the question marks
	// are placeholders for numbers.
	// Since the filenames have been sorted, patches are mounted with low numbers first, through
	// to ones with high numbers. This is important, because newly mount packages overwrite
	// existing files in the virtual file system, if they include files with the same name.
	for (Common::FSList::const_iterator it = files.begin(); it != files.end(); ++it) {
		if (it->getName().matchString("patch???.b25c", true))
			if (!packageManagerPtr->loadPackage(it->getName(), "/"))
				return false;
	}

	// Identify and mount all language packages
	// The filename of the packages have the form lang_*.b25c (eg. lang_de.b25c)
	for (Common::FSList::const_iterator it = files.begin(); it != files.end(); ++it) {
		if (it->getName().matchString("lang_*.b25c", true))
			if (!packageManagerPtr->loadPackage(it->getName(), "/"))
				return false;
	}

	return true;
}
Exemple #25
0
Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
	assert(engine);

	const ADGameDescription *agdDesc = 0;
	Common::Language language = Common::UNK_LANG;
	Common::Platform platform = Common::kPlatformUnknown;
	Common::String extra;

	if (ConfMan.hasKey("language"))
		language = Common::parseLanguage(ConfMan.get("language"));
	if (ConfMan.hasKey("platform"))
		platform = Common::parsePlatform(ConfMan.get("platform"));
	if (_flags & kADFlagUseExtraAsHint) {
		if (ConfMan.hasKey("extra"))
			extra = ConfMan.get("extra");
	}

	Common::String gameid = ConfMan.get("gameid");

	Common::String path;
	if (ConfMan.hasKey("path")) {
		path = ConfMan.get("path");
	} else {
		path = ".";

		// This situation may happen only when game was
		// launched from a command line with wrong target and
		// no path was provided.
		//
		// A dummy entry will get created and will keep game path
		// We mark this entry, so it will not be added to the
		// config file.
		//
		// Fixes bug #1544799
		ConfMan.setBool("autoadded", true);

		warning("No path was provided. Assuming the data files are in the current directory");
	}
	Common::FSNode dir(path);
	Common::FSList files;
	if (!dir.isDirectory() || !dir.getChildren(files, Common::FSNode::kListAll, true)) {
		warning("Game data path does not exist or is not a directory (%s)", path.c_str());
		return Common::kNoGameDataFoundError;
	}

	if (files.empty())
		return Common::kNoGameDataFoundError;

	// Compose a hashmap of all files in fslist.
	FileMap allFiles;
	composeFileHashMap(allFiles, files, (_maxScanDepth == 0 ? 1 : _maxScanDepth));

	// Run the detector on this
	ADGameDescList matches = detectGame(files.begin()->getParent(), allFiles, language, platform, extra);

	if (cleanupPirated(matches))
		return Common::kNoGameDataFoundError;

	if (_singleId == NULL) {
		// Find the first match with correct gameid.
		for (uint i = 0; i < matches.size(); i++) {
			if (matches[i]->gameId == gameid) {
				agdDesc = matches[i];
				break;
			}
		}
	} else if (matches.size() > 0) {
		agdDesc = matches[0];
	}

	if (agdDesc == 0) {
		// Use fallback detector if there were no matches by other means
		agdDesc = fallbackDetect(allFiles, files);
		if (agdDesc != 0) {
			// Seems we found a fallback match. But first perform a basic
			// sanity check: the gameid must match.
			if (_singleId == NULL && agdDesc->gameId != gameid)
				agdDesc = 0;
		}
	}

	if (agdDesc == 0)
		return Common::kNoGameDataFoundError;

	// If the GUI options were updated, we catch this here and update them in the users config
	// file transparently.
	Common::String lang = getGameGUIOptionsDescriptionLanguage(agdDesc->language);
	if (agdDesc->flags & ADGF_ADDENGLISH)
		lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY);

	Common::updateGameGUIOptions(agdDesc->guiOptions + _guiOptions, lang);

	GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameIds);

	bool showTestingWarning = false;

#ifdef RELEASE_BUILD
	showTestingWarning = true;
#endif

	if (((gameDescriptor.getSupportLevel() == kUnstableGame
			|| (gameDescriptor.getSupportLevel() == kTestingGame
					&& showTestingWarning)))
			&& !Engine::warnUserAboutUnsupportedGame())
		return Common::kUserCanceled;

	debug(2, "Running %s", gameDescriptor.description().c_str());
	initSubSystems(agdDesc);
	if (!createInstance(syst, engine, agdDesc))
		return Common::kNoGameDataFoundError;
	else
		return Common::kNoError;
}
Exemple #26
0
bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
	const char *const EXTENSIONS[] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8",
		".dat", ".zip", nullptr };

	// Loop through the files of the folder
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		// Check for a recognised filename
		if (file->isDirectory())
			continue;
		Common::String filename = file->getName();
		bool hasExt = Blorb::hasBlorbExt(filename), isBlorb = false;
		for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
			hasExt = filename.hasSuffixIgnoreCase(*ext);
		if (!hasExt)
			continue;

		// Open up the file and calculate the md5, and get the serial
		Common::File gameFile;
		if (!gameFile.open(*file))
			continue;
		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
		size_t filesize = gameFile.size();
		char serial[9] = "";
		bool emptyBlorb = false;
		gameFile.seek(0);
		isBlorb = Blorb::isBlorb(gameFile, ID_ZCOD);

		if (!isBlorb) {
			if (Blorb::hasBlorbExt(filename)) {
				gameFile.close();
				continue;
			}
			gameFile.seek(18);
			strcpy(&serial[0], "\"");
			gameFile.read(&serial[1], 6);
			strcpy(&serial[7], "\"");
		} else {
			Blorb b(*file, INTERPRETER_FROTZ);
			Common::SeekableReadStream *f = b.createReadStreamForMember("game");
			emptyBlorb = f == nullptr;

			if (!emptyBlorb) {
				f->seek(18);
				strcpy(&serial[0], "\"");
				f->read(&serial[1], 6);
				strcpy(&serial[7], "\"");
				delete f;
			}
		}
		gameFile.close();

		// Check for known games. Note that there has been some variation in exact filesizes
		// for Infocom games due to padding at the end of files. So we match on md5s for the
		// first 5Kb, and only worry about filesize for more recent Blorb based Zcode games
		const FrotzGameDescription *p = FROTZ_GAMES;
		while (p->_gameId && p->_md5 && (md5 != p->_md5 ||
				(filesize != p->_filesize && isBlorb)))
			++p;

		DetectedGame gd;
		if (!p->_gameId) {
			// Generic .dat/.zip files don't get reported as matches unless they have a known md5
			if (filename.hasSuffixIgnoreCase(".dat") || filename.hasSuffixIgnoreCase(".zip") || emptyBlorb)
				continue;

			if (gDebugLevel > 0) {
				// Print an entry suitable for putting into the detection_tables.h, using the
				// name of the parent folder the game is in as the presumed game Id
				Common::String folderName = file->getParent().getName();
				if (folderName.hasSuffix("\\"))
					folderName.deleteLastChar();
				Common::String fname = filename;
				const char *dot = strchr(fname.c_str(), '.');
				if (dot)
					fname = Common::String(fname.c_str(), dot);

				debug("ENTRY0(\"%s\", %s, \"%s\", %u),",
					fname.c_str(), strlen(serial) ? serial : "nullptr", md5.c_str(), (uint)filesize);
			}
			const PlainGameDescriptor &desc = ZCODE_GAME_LIST[0];
			gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown);
		} else {
			GameDescriptor gameDesc = findGame(p->_gameId);
			gd = DetectedGame(p->_gameId, gameDesc._description, p->_language, Common::kPlatformUnknown, p->_extra);
			gd.setGUIOptions(p->_guiOptions);
		}

		gd.addExtraEntry("filename", filename);
		gameList.push_back(gd);
	}

	return !gameList.empty();
}
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;
}
Exemple #28
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();
}
Exemple #29
0
/**
 * Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation
 * where the files haven't been renamed (i.e. don't have the '1' just before the extension)
 */
const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &fslist) const {
	Common::String extra;
	FileMap allFiles;
	SizeMD5Map filesSizeMD5;

	const ADGameFileDescription *fileDesc;
	const Tinsel::TinselGameDescription *g;

	if (fslist.empty())
		return NULL;

	// TODO: The following code is essentially a slightly modified copy of the
	// complete code of function detectGame() in engines/advancedDetector.cpp.
	// That quite some hefty and undesirable code duplication. Its only purpose
	// seems to be to treat filenames of the form "foo1.ext" as "foo.ext".
	// It would be nice to avoid this code duplication.

	// First we compose a hashmap of all files in fslist.
	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
		if (file->isDirectory()) {
			if (!scumm_stricmp(file->getName().c_str(), "dw2")) {
				// Probably Discworld 2 subfolder on CD, so add it's contents as well
				Common::FSList files;
				if (file->getChildren(files, Common::FSNode::kListAll)) {
					Common::FSList::const_iterator file2;
					for (file2 = files.begin(); file2 != files.end(); ++file2) {
						if (file2->isDirectory())
							continue;

						Common::String fname = file2->getName();
						allFiles[fname] = *file2;
					}
				}
			}
			continue;
		}

		Common::String tstr = file->getName();

		allFiles[tstr] = *file;	// Record the presence of this file
	}

	// Check which files are included in some dw2 ADGameDescription *and* present
	// in fslist without a '1' suffix character. Compute MD5s and file sizes for these files.
	for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
		if (strcmp(g->desc.gameid, "dw2") != 0)
			continue;

		for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
			// Get the next filename, stripping off any '1' suffix character
			char tempFilename[50];
			strcpy(tempFilename, fileDesc->fileName);
			char *pOne = strchr(tempFilename, '1');
			if (pOne) {
				do {
					*pOne = *(pOne + 1);
					pOne++;
				} while (*pOne);
			}

			Common::String fname(tempFilename);
			if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
				SizeMD5 tmp;
				Common::File testFile;

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

				filesSizeMD5[fname] = tmp;
			}
		}
	}

	ADGameDescList matched;
	int maxFilesMatched = 0;

	// MD5 based matching
	for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) {
		if (strcmp(g->desc.gameid, "dw2") != 0)
			continue;

		bool fileMissing = false;

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

		bool allFilesPresent = true;

		// Try to match all files for this game
		for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
			// Get the next filename, stripping off any '1' suffix character
			char tempFilename[50];
			strcpy(tempFilename, fileDesc->fileName);
			char *pOne = strchr(tempFilename, '1');
			if (pOne) {
				do {
					*pOne = *(pOne + 1);
					pOne++;
				} while (*pOne);
			}

			Common::String tstr(tempFilename);

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

			if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
				fileMissing = true;
				break;
			}

			if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) {
				fileMissing = true;
				break;
			}
		}

		if (!fileMissing) {
			// Count the number of matching files. Then, only keep those
			// entries which match a maximal amount of files.
			int curFilesMatched = 0;
			for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
				curFilesMatched++;

			if (curFilesMatched > maxFilesMatched) {
				maxFilesMatched = curFilesMatched;

				matched.clear();	// Remove any prior, lower ranked matches.
				matched.push_back((const ADGameDescription *)g);
			} else if (curFilesMatched == maxFilesMatched) {
				matched.push_back((const ADGameDescription *)g);
			}
		}
	}

	// We didn't find a match
	if (matched.empty())
		return NULL;

	return *matched.begin();
}
Exemple #30
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;
}