ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

massadd.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/metaengine.h"
00024 #include "common/algorithm.h"
00025 #include "common/config-manager.h"
00026 #include "common/debug.h"
00027 #include "common/system.h"
00028 #include "common/taskbar.h"
00029 #include "common/translation.h"
00030 
00031 #include "gui/massadd.h"
00032 
00033 #ifndef DISABLE_MASS_ADD
00034 namespace GUI {
00035 
00036 /*
00037 TODO:
00038 - Themify this dialog
00039 - Add a ListWidget showing all the games we are going to add, and update it live
00040 - Add a 'busy' mouse cursor (animated?) which indicates to the user that
00041   something is in progress, and show this cursor while we scan
00042 */
00043 
00044 enum {
00045     // Upper bound (im milliseconds) we want to spend in handleTickle.
00046     // Setting this low makes the GUI more responsive but also slows
00047     // down the scanning.
00048     kMaxScanTime = 50
00049 };
00050 
00051 enum {
00052     kOkCmd = 'OK  ',
00053     kCancelCmd = 'CNCL'
00054 };
00055 
00056 
00057 
00058 MassAddDialog::MassAddDialog(const Common::FSNode &startDir)
00059     : Dialog("MassAdd"),
00060     _dirsScanned(0),
00061     _oldGamesCount(0),
00062     _dirTotal(0),
00063     _okButton(0),
00064     _dirProgressText(0),
00065     _gameProgressText(0) {
00066 
00067     StringArray l;
00068 
00069     // The dir we start our scan at
00070     _scanStack.push(startDir);
00071 
00072     // Removed for now... Why would you put a title on mass add dialog called "Mass Add Dialog"?
00073     // new StaticTextWidget(this, "massadddialog_caption", "Mass Add Dialog");
00074 
00075     _dirProgressText = new StaticTextWidget(this, "MassAdd.DirProgressText",
00076                                            _("... progress ..."));
00077 
00078     _gameProgressText = new StaticTextWidget(this, "MassAdd.GameProgressText",
00079                                              _("... progress ..."));
00080 
00081     _dirProgressText->setAlign(Graphics::kTextAlignCenter);
00082     _gameProgressText->setAlign(Graphics::kTextAlignCenter);
00083 
00084     _list = new ListWidget(this, "MassAdd.GameList");
00085     _list->setEditable(false);
00086     _list->setNumberingMode(kListNumberingOff);
00087     _list->setList(l);
00088 
00089     _okButton = new ButtonWidget(this, "MassAdd.Ok", _("OK"), 0, kOkCmd, Common::ASCII_RETURN);
00090     _okButton->setEnabled(false);
00091 
00092     new ButtonWidget(this, "MassAdd.Cancel", _("Cancel"), 0, kCancelCmd, Common::ASCII_ESCAPE);
00093 
00094     // Build a map from all configured game paths to the targets using them
00095     const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
00096     Common::ConfigManager::DomainMap::const_iterator iter;
00097     for (iter = domains.begin(); iter != domains.end(); ++iter) {
00098 
00099 #ifdef __DS__
00100         // DS port uses an extra section called 'ds'.  This prevents the section from being
00101         // detected as a game.
00102         if (iter->_key == "ds") {
00103             continue;
00104         }
00105 #endif
00106 
00107         Common::String path(iter->_value.getVal("path"));
00108         // Remove trailing slash, so that "/foo" and "/foo/" match.
00109         // This works around a bug in the POSIX FS code (and others?)
00110         // where paths are not normalized (so FSNodes refering to identical
00111         // FS objects may return different values in path()).
00112         while (path != "/" && path.lastChar() == '/')
00113             path.deleteLastChar();
00114         if (!path.empty())
00115             _pathToTargets[path].push_back(iter->_key);
00116     }
00117 }
00118 
00119 struct GameTargetLess {
00120     bool operator()(const DetectedGame &x, const DetectedGame &y) const {
00121         return x.preferredTarget.compareToIgnoreCase(y.preferredTarget) < 0;
00122     }
00123 };
00124 
00125 struct GameDescLess {
00126     bool operator()(const DetectedGame &x, const DetectedGame &y) const {
00127         return x.description.compareToIgnoreCase(y.description) < 0;
00128     }
00129 };
00130 
00131 
00132 void MassAddDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00133 #if defined(USE_TASKBAR)
00134     // Remove progress bar and count from taskbar
00135     g_system->getTaskbarManager()->setProgressState(Common::TaskbarManager::kTaskbarNoProgress);
00136     g_system->getTaskbarManager()->setCount(0);
00137 #endif
00138 
00139     // FIXME: It's a really bad thing that we use two arbitrary constants
00140     if (cmd == kOkCmd) {
00141         // Sort the detected games. This is not strictly necessary, but nice for
00142         // people who want to edit their config file by hand after a mass add.
00143         Common::sort(_games.begin(), _games.end(), GameTargetLess());
00144         // Add all the detected games to the config
00145         for (DetectedGames::iterator iter = _games.begin(); iter != _games.end(); ++iter) {
00146             debug(1, "  Added gameid '%s', desc '%s'\n",
00147                 iter->gameId.c_str(),
00148                 iter->description.c_str());
00149             iter->gameId = EngineMan.createTargetForGame(*iter);
00150         }
00151 
00152         // Write everything to disk
00153         ConfMan.flushToDisk();
00154 
00155         // And scroll to first detected game
00156         if (!_games.empty()) {
00157             Common::sort(_games.begin(), _games.end(), GameDescLess());
00158             ConfMan.set("temp_selection", _games.front().gameId);
00159         }
00160 
00161         close();
00162     } else if (cmd == kCancelCmd) {
00163         // User cancelled, so we don't do anything and just leave.
00164         _games.clear();
00165         close();
00166     } else {
00167         Dialog::handleCommand(sender, cmd, data);
00168     }
00169 }
00170 
00171 void MassAddDialog::handleTickle() {
00172     if (_scanStack.empty())
00173         return; // We have finished scanning
00174 
00175     uint32 t = g_system->getMillis();
00176 
00177     // Perform a breadth-first scan of the filesystem.
00178     while (!_scanStack.empty() && (g_system->getMillis() - t) < kMaxScanTime) {
00179         Common::FSNode dir = _scanStack.pop();
00180 
00181         Common::FSList files;
00182         if (!dir.getChildren(files, Common::FSNode::kListAll)) {
00183             continue;
00184         }
00185 
00186         // Run the detector on the dir
00187         DetectionResults detectionResults = EngineMan.detectGames(files);
00188 
00189         if (detectionResults.foundUnknownGames()) {
00190             Common::String report = detectionResults.generateUnknownGameReport(false, 80);
00191             g_system->logMessage(LogMessageType::kInfo, report.c_str());
00192         }
00193 
00194         // Just add all detected games / game variants. If we get more than one,
00195         // that either means the directory contains multiple games, or the detector
00196         // could not fully determine which game variant it was seeing. In either
00197         // case, let the user choose which entries he wants to keep.
00198         //
00199         // However, we only add games which are not already in the config file.
00200         DetectedGames candidates = detectionResults.listRecognizedGames();
00201         for (DetectedGames::const_iterator cand = candidates.begin(); cand != candidates.end(); ++cand) {
00202             const DetectedGame &result = *cand;
00203 
00204             Common::String path = dir.getPath();
00205 
00206             // Remove trailing slashes
00207             while (path != "/" && path.lastChar() == '/')
00208                 path.deleteLastChar();
00209 
00210             // Check for existing config entries for this path/gameid/lang/platform combination
00211             if (_pathToTargets.contains(path)) {
00212                 Common::String resultPlatformCode = Common::getPlatformCode(result.platform);
00213                 Common::String resultLanguageCode = Common::getLanguageCode(result.language);
00214 
00215                 bool duplicate = false;
00216                 const StringArray &targets = _pathToTargets[path];
00217                 for (StringArray::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) {
00218                     // If the gameid, platform and language match -> skip it
00219                     Common::ConfigManager::Domain *dom = ConfMan.getDomain(*iter);
00220                     assert(dom);
00221 
00222                     if ((*dom)["gameid"] == result.gameId &&
00223                         (*dom)["platform"] == resultPlatformCode &&
00224                         (*dom)["language"] == resultLanguageCode) {
00225                         duplicate = true;
00226                         break;
00227                     }
00228                 }
00229                 if (duplicate) {
00230                     _oldGamesCount++;
00231                     continue;   // Skip duplicates
00232                 }
00233             }
00234             _games.push_back(result);
00235 
00236             _list->append(result.description);
00237         }
00238 
00239 
00240         // Recurse into all subdirs
00241         for (Common::FSList::const_iterator file = files.begin(); file != files.end(); ++file) {
00242             if (file->isDirectory()) {
00243                 _scanStack.push(*file);
00244 
00245                 _dirTotal++;
00246             }
00247         }
00248 
00249         _dirsScanned++;
00250 
00251 #if defined(USE_TASKBAR)
00252         g_system->getTaskbarManager()->setProgressValue(_dirsScanned, _dirTotal);
00253         g_system->getTaskbarManager()->setCount(_games.size());
00254 #endif
00255     }
00256 
00257 
00258     // Update the dialog
00259     Common::String buf;
00260 
00261     if (_scanStack.empty()) {
00262         // Enable the OK button
00263         _okButton->setEnabled(true);
00264 
00265         buf = _("Scan complete!");
00266         _dirProgressText->setLabel(buf);
00267 
00268         buf = Common::String::format(_("Discovered %d new games, ignored %d previously added games."), _games.size(), _oldGamesCount);
00269         _gameProgressText->setLabel(buf);
00270 
00271     } else {
00272         buf = Common::String::format(_("Scanned %d directories ..."), _dirsScanned);
00273         _dirProgressText->setLabel(buf);
00274 
00275         buf = Common::String::format(_("Discovered %d new games, ignored %d previously added games ..."), _games.size(), _oldGamesCount);
00276         _gameProgressText->setLabel(buf);
00277     }
00278 
00279     if (_games.size() > 0) {
00280         _list->scrollToEnd();
00281     }
00282 
00283     drawDialog(kDrawLayerForeground);
00284 }
00285 
00286 
00287 } // End of namespace GUI
00288 
00289 #endif // DISABLE_MASS_ADD


Generated on Sat Mar 16 2019 05:01:45 for ResidualVM by doxygen 1.7.1
curved edge   curved edge