Пример #1
0
        /// <summary>
        /// Subscribes to a given system if not already subscribed and kicks
        /// off <see cref="GamesUpdated"/>.
        /// </summary>
        /// <param name="system">System to kick-off and subscribe</param>
        private void AddSystem(PinballXSystem system)
        {
            // skip if already subscribed
            if (_systemDisposables.ContainsKey(system))
            {
                return;
            }

            // kick off
            if (system.Enabled)
            {
                // current games
                system.Games.Keys.ToList().ForEach(databaseFile => {
                    _gamesUpdated.OnNext(new Tuple <PinballXSystem, string, List <PinballXGame> >(system, databaseFile, system.Games[databaseFile]));
                });

                // current mappings
                _mappingsUpdated.OnNext(new Tuple <PinballXSystem, List <Mapping> >(system, system.Mappings.ToList()));
            }

            // relay future changes
            var systemDisponsable = new CompositeDisposable {
                system.GamesUpdated.Subscribe(x => _gamesUpdated.OnNext(new Tuple <PinballXSystem, string, List <PinballXGame> >(system, x.Item1, x.Item2))),
                system.MappingsUpdated.Subscribe(mappings => _mappingsUpdated.OnNext(new Tuple <PinballXSystem, List <Mapping> >(system, mappings)))
            };

            _systemDisposables.Add(system, systemDisponsable);
        }
Пример #2
0
        public SystemMapping UnmarshallMappings(string databaseFile, PinballXSystem system)
        {
            if (!_file.Exists(databaseFile))
            {
                return(new SystemMapping());
            }

            _logger.Info("Reading mappings from {0}...", databaseFile);
            try {
                using (var sr = new StreamReader(databaseFile))
                    using (JsonReader reader = new JsonTextReader(sr)) {
                        try {
                            var systemMapping = _serializer.Deserialize <SystemMapping>(reader);
                            reader.Close();
                            return(systemMapping);
                        } catch (Exception e) {
                            _logger.Error(e, "Error parsing vpdb.json, deleting and ignoring.");
                            _crashManager.Report(e, "json");
                            reader.Close();
                            File.Delete(databaseFile);
                            return(new SystemMapping());
                        }
                    }
            } catch (Exception e) {
                _logger.Error(e, "Error reading vpdb.json, ignoring.");
                _crashManager.Report(e, "json");
                return(new SystemMapping());
            }
        }
Пример #3
0
 /// <summary>
 /// Moves a downloaded file to the table folder of the platform.
 /// </summary>
 /// <param name="job">Job of downloaded file</param>
 /// <param name="system">System of the downloaded file</param>
 private void MoveDownloadedFile(Job job, PinballXSystem system)
 {
     // move downloaded file to table folder
     if (job.FilePath != null && File.Exists(job.FilePath))
     {
         try {
             var dest = job.GetFileDestination(system);
             if (dest != null && !File.Exists(dest))
             {
                 _logger.Info("Moving downloaded file from \"{0}\" to \"{1}\"...", job.FilePath, dest);
                 File.Move(job.FilePath, dest);
             }
             else
             {
                 // todo see how to handle, probably name it differently.
                 _logger.Warn("File \"{0}\" already exists at destination!", dest);
             }
         } catch (Exception e) {
             _logger.Error(e, "Error moving downloaded file.");
             _crashManager.Report(e, "fs");
         }
     }
     else
     {
         _logger.Error("Downloaded file \"{0}\" does not exist.", job.FilePath);
     }
 }
Пример #4
0
        /// <summary>
        /// Enables XML database and table folder watching for the given system.
        /// </summary>
        /// <param name="system">System to initialize</param>
        private void InitSystem(PinballXSystem system)
        {
            // xml database watching (triggered on change of system's `Enabled`)
            system.Initialize();

            // folder watching: submit all systems to IFileSystemWatcher to figure out what to do
            system.WhenAnyValue(s => s.Enabled, s => s.TablePath).Subscribe(_ => _watcher.WatchTables(Systems));
        }
Пример #5
0
        public SystemItemViewModel(GamesViewModel parent, PinballXSystem system)
        {
            _parent       = parent;
            System        = system;
            ToggleDetails = ReactiveCommand.Create(() => { IsExpanded = !IsExpanded; });

            this.WhenAnyValue(vm => vm.IsExpanded).Select(expanded => expanded ? 180d : 0d).ToProperty(this, vm => vm.ExpanderRotation, out _expanderRotation);
        }
Пример #6
0
        /// <summary>
        /// Unsubscribes from a system and announce to <see cref="GamesUpdated"/>.
        /// </summary>
        /// <param name="system">System to unsubscribe from</param>
        private void RemoveSystem(PinballXSystem system)
        {
            _logger.Info("Removing system \"{0}\".", system.Name);

            // announce system removal
            system.Games.Keys.ToList().ForEach(databaseFile => _gamesUpdated.OnNext(new Tuple <PinballXSystem, string, List <PinballXGame> >(system, databaseFile, new List <PinballXGame>())));

            // unsubscribe
            _systemDisposables[system]?.Dispose();
            _systemDisposables.Remove(system);
        }
Пример #7
0
 public void OnDatabaseFileFilterChanged(PinballXSystem system, string fileName, bool isChecked)
 {
     if (isChecked)
     {
         _databaseFileFilter[system].Remove(fileName);
     }
     else
     {
         _databaseFileFilter[system].Add(fileName);
     }
     RefreshGameVisibility();
 }
Пример #8
0
 public void OnExecutableFilterChanged(PinballXSystem system, string fileName, bool isChecked)
 {
     fileName = fileName == PinballXSystem.DefaultExecutableLabel ? "" : fileName;
     if (isChecked)
     {
         _executableFilter[system].Remove(fileName);
     }
     else
     {
         _executableFilter[system].Add(fileName);
     }
     RefreshGameVisibility();
 }
Пример #9
0
        /// <summary>
        /// Returns the absolute path of where the file should moved to after
        /// downloading.
        /// </summary>
        /// <param name="system">System of the table file belonging to the download</param>
        /// <returns>Absolute path to local download location</returns>
        public string GetFileDestination([NotNull] PinballXSystem system)
        {
            switch (FileType)
            {
            case FileType.TableFile:
                return(Path.Combine(system.TablePath, File.Name));

            case FileType.TableScript:
                var scriptsFolder = Path.Combine(Path.GetDirectoryName(system.TablePath), "Scripts");
                return(Path.Combine(Directory.Exists(scriptsFolder) ? scriptsFolder : system.TablePath, File.Name));

            case FileType.TableAuxiliary:

                return(Path.Combine(system.TablePath, File.Name));

            case FileType.TableMusic:
                var musicFolder = Path.Combine(Path.GetDirectoryName(system.TablePath), "Music");
                if (!Directory.Exists(musicFolder))
                {
                    Directory.CreateDirectory(musicFolder);
                }
                return(Path.Combine(musicFolder, File.Name));

            case FileType.TableImage:
                // todo handle desktop media (goes to "Table Images Desktop" folder)
                return(Path.Combine(system.MediaPath, MediaTableImages, Release.Game.DisplayName + Path.GetExtension(FilePath)));

            case FileType.TableVideo:
                // todo handle desktop media (goes to "Table Videos Desktop" folder)
                return(Path.Combine(system.MediaPath, MediaTableVideos, Release.Game.DisplayName + Path.GetExtension(FilePath)));

            case FileType.BackglassImage:
                return(Path.Combine(system.MediaPath, MediaBackglassImages, Release.Game.DisplayName + Path.GetExtension(FilePath)));

            case FileType.WheelImage:
                return(Path.Combine(system.MediaPath, MediaWheelImages, Release.Game.DisplayName + Path.GetExtension(FilePath)));

            case FileType.Rom:                     // todo
            default:
                throw new ArgumentOutOfRangeException();
            }
        }
Пример #10
0
        /// <summary>
        /// Merges a list of mappings of a given system into Global Games.
        /// </summary>
        ///
        /// <remarks>
        /// The provided mappings are exhaustive, i.e. exiting games not in the
        /// provided list for the given system are to be removed.
        /// </remarks>
        ///
        /// <param name="system">System of updated mappings</param>
        /// <param name="mappings">Mappings</param>
        private void MergeMappings(PinballXSystem system, IEnumerable <Mapping> mappings)
        {
            lock (AggregatedGames) {
                // if Global Games are empty, don't bother identifiying and add them all
                if (AggregatedGames.IsEmpty)
                {
                    _threadManager.MainDispatcher.Invoke(() => {
                        using (AggregatedGames.SuppressChangeNotifications()) {
                            AggregatedGames.AddRange(mappings.Select(m => new AggregatedGame(m)));
                        }
                    });
                    _logger.Info("Added {0} games from mappings.", mappings.Count());
                    return;
                }

                // otherwise, retrieve the system's games from Global Games.
                var selectedGames = AggregatedGames.Where(g =>
                                                          g.HasSystem &&
                                                          ReferenceEquals(g.System, system)
                                                          ).ToList();

                var remainingGames = new HashSet <AggregatedGame>(selectedGames);
                var gamesToAdd     = new List <AggregatedGame>();
                var updated        = 0;
                var removed        = 0;
                var cleared        = 0;

                // update games
                foreach (var mapping in mappings)
                {
                    // todo could also use an index
                    var game = AggregatedGames.FirstOrDefault(g => g.FileId == mapping.Id);

                    // no match, add
                    if (game == null)
                    {
                        gamesToAdd.Add(new AggregatedGame(mapping));

                        // match and not equal, so update.
                    }
                    else if (!game.EqualsMapping(mapping))
                    {
                        _threadManager.MainDispatcher.Invoke(() => game.Update(mapping));
                        remainingGames.Remove(game);
                        updated++;

                        // match but equal, ignore.
                    }
                    else
                    {
                        remainingGames.Remove(game);
                    }
                }

                // add games
                if (gamesToAdd.Count > 0)
                {
                    _threadManager.MainDispatcher.Invoke(() => AggregatedGames.AddRange(gamesToAdd));
                }

                // remove or clear games
                _threadManager.MainDispatcher.Invoke(() => {
                    foreach (var game in remainingGames)
                    {
                        if (!game.HasLocalFile && !game.HasXmlGame)
                        {
                            AggregatedGames.Remove(game);
                            removed++;
                        }
                        else
                        {
                            cleared++;
                            game.ClearMapping();
                        }
                    }
                });

                // done!
                _logger.Info("Added {0} games, removed {1} ({2}), updated {3} from mappings.", gamesToAdd.Count, removed, cleared, updated);
            }
        }
Пример #11
0
        /// <summary>
        /// Merges changed games from PinballX into Global Games.
        /// </summary>
        ///
        /// <remarks>
        /// The provided games list is exhaustive, meaning existing games of
        /// the given system and database file are to be removed. Provide an
        /// empty list to remove all of them.
        ///
        /// This is triggered on <see cref="PinballXSystem.GamesUpdated"/>. The
        /// received list is then merged into <see cref="AggregatedGames"/>,
        /// while only data that has changed is updated.
        /// </remarks>
        ///
        /// <param name="system">System of the updated games</param>
        /// <param name="databaseFile">Database file of the updated games</param>
        /// <param name="xmlGames">Parsed games</param>
        public void MergeXmlGames(PinballXSystem system, string databaseFile, List <PinballXGame> xmlGames)
        {
            lock (AggregatedGames) {
                // if Global Games are empty, don't bother identifiying and add them all
                if (AggregatedGames.IsEmpty)
                {
                    _threadManager.MainDispatcher.Invoke(() => {
                        using (AggregatedGames.SuppressChangeNotifications()) {
                            AggregatedGames.AddRange(xmlGames.Select(g => new AggregatedGame(g)));
                        }
                    });
                    _logger.Info("Added {0} games from PinballX database.", xmlGames.Count());
                    return;
                }

                // otherwise, retrieve the system's games from Global Games.
                var selectedGames = AggregatedGames.Where(g =>
                                                          g.XmlGame != null &&
                                                          ReferenceEquals(g.XmlGame.System, system) &&
                                                          g.XmlGame.DatabaseFile == databaseFile
                                                          ).ToList();

                var remainingGames = new HashSet <AggregatedGame>(selectedGames);
                var gamesToAdd     = new List <AggregatedGame>();
                var updated        = 0;
                var removed        = 0;
                var cleared        = 0;

                // update games
                foreach (var newGame in xmlGames)
                {
                    // todo could also use an index
                    var fileId  = Path.Combine(newGame.System.TablePath, Path.GetFileName(newGame.FileName));
                    var oldGame = AggregatedGames.FirstOrDefault(g => g.FileId == fileId);

                    // no match, add
                    if (oldGame == null)
                    {
                        gamesToAdd.Add(new AggregatedGame(newGame));

                        // match and not equal, so update.
                    }
                    else if (!oldGame.EqualsXmlGame(newGame))
                    {
                        _threadManager.MainDispatcher.Invoke(() => oldGame.Update(newGame));
                        remainingGames.Remove(oldGame);
                        updated++;

                        // match but equal, ignore.
                    }
                    else
                    {
                        remainingGames.Remove(oldGame);
                    }
                }

                // add games
                if (gamesToAdd.Count > 0)
                {
                    _threadManager.MainDispatcher.Invoke(() => AggregatedGames.AddRange(gamesToAdd));
                }

                // remove or clear games
                _threadManager.MainDispatcher.Invoke(() => {
                    foreach (var game in remainingGames)
                    {
                        if (!game.HasLocalFile && !game.HasMapping)
                        {
                            AggregatedGames.Remove(game);
                            removed++;
                        }
                        else
                        {
                            cleared++;
                            game.ClearXmlGame();
                        }
                    }
                });

                // done!
                _logger.Info("Added {0} games, removed {1} ({2}), updated {3} from PinballX database.", gamesToAdd.Count, removed, cleared, updated);
            }
        }
Пример #12
0
 /// <summary>
 /// Constructor when linking game to VPDB
 /// </summary>
 /// <param name="game">Game to link to</param>
 /// <param name="system">System to link to</param>
 /// <param name="release">VPDB release</param>
 /// <param name="fileId">File ID of VPDB release</param>
 public Mapping(AggregatedGame game, PinballXSystem system, VpdbRelease release, string fileId) : this(game, system)
 {
     ReleaseId = release.Id;
     FileId    = fileId;
 }
Пример #13
0
 /// <summary>
 /// Constructor with given game
 /// </summary>
 /// <param name="game">Game to which the mapping is linked to</param>
 /// <param name="system">System to which the game is linked to</param>
 public Mapping(AggregatedGame game, PinballXSystem system)
 {
     System   = system;
     FileName = Path.GetFileName(game.FilePath);
 }