Beispiel #1
0
        public IPinballXManager RenameGame(string oldFileName, AggregatedGame game)
        {
            var xmlPath     = Path.Combine(game.System.DatabasePath, game.XmlGame.DatabaseFile);
            var newFilename = Path.GetFileNameWithoutExtension(game.FileName);

            if (_settingsManager.Settings.ReformatXml)
            {
                // read xml
                var menu = _marshallManager.UnmarshallXml(xmlPath);

                // get game
                var xmlGame = menu.Games.FirstOrDefault(g => g.FileName == oldFileName);
                if (xmlGame == null)
                {
                    throw new InvalidOperationException($"Cannot find game with name {oldFileName} in {xmlPath}.");
                }

                // update game
                xmlGame.FileName = newFilename;

                // save xml
                _marshallManager.MarshallXml(menu, xmlPath);
            }
            else
            {
                var xml = _file.ReadAllText(xmlPath);
                xml = xml.Replace($"name=\"{oldFileName}\"", $"name=\"{newFilename}\"");
                _file.WriteAllText(xmlPath, xml);

                _logger.Info("Replaced name \"{0}\" with \"{1}\" in {2}.", oldFileName, newFilename, xmlPath);
            }
            return(this);
        }
Beispiel #2
0
        public void MapGame(AggregatedGame game, VpdbRelease release, string fileId)
        {
            // update in case we didn't catch the last version.
            _vpdbManager.GetRelease(release.Id).Subscribe(updatedRelease => {
                _logger.Info("Mapping {0} to {1} ({2})", game, release, fileId);

                GetOrCreateMapping(game).Map(release, fileId);
            }, exception => _vpdbClient.HandleApiError(exception, "retrieving release details during linking"));
        }
Beispiel #3
0
        public Message LogReleaseLinked(AggregatedGame game, VpdbRelease release, string fileId)
        {
            var msg = new Message(MessageType.ReleaseLinked, MessageLevel.Info, new Dictionary <string, string> {
                { DataGameName, game.FileId },
                { DataRelease, release.Id },
                { DataFile, fileId }
            });

            return(Log(msg));
        }
Beispiel #4
0
        public void UnHideGame(AggregatedGame game)
        {
            _logger.Info("Unhiding game {0}", game.FileId);

            if (!game.HasMapping)
            {
                _logger.Error("Cannot unhide game without mapping.");
                return;
            }
            game.Mapping.IsHidden = false;
        }
Beispiel #5
0
        /*
         * public IDownloadManager DownloadRelease(string releaseId, string currentFileId = null)
         * {
         *      // ignore if already launched
         *      if (_currentlyDownloading.Contains(releaseId)) {
         *              _logger.Warn("Skipping already queued release {0}.", releaseId);
         *              return this;
         *      }
         *      _currentlyDownloading.Add(releaseId);
         *
         *      // retrieve release details
         *      _logger.Info("Retrieving details for release {0} before downloading..", releaseId);
         *      _vpdbManager.GetRelease(releaseId).ObserveOn(Scheduler.Default).Subscribe(release => {
         *
         *              // match file based on settings
         *              var file = FindLatestFile(release, currentFileId);
         *
         *              // check if match
         *              if (file == null) {
         *                      _logger.Info("Nothing matched current flavor configuration, skipping.");
         *                      _currentlyDownloading.Remove(releaseId);
         *                      return;
         *              }
         *
         *              // download
         *              DownloadRelease(release, file);
         *
         *      }, exception => _vpdbManager.HandleApiError(exception, "retrieving release details during download"));
         *
         *      return this;
         * }*/

        public IDownloadManager DownloadRelease(AggregatedGame game)
        {
            var release   = game.MappedRelease;
            var tableFile = game.MappedTableFile;

            // also fetch game data for media & co
            _vpdbManager.GetGame(release.Game.Id, true).Subscribe(g => {
                var version = _databaseManager.GetVersion(release.Id, tableFile.Reference.Id);
                _logger.Info($"Checking what to download for {g.DisplayName} - {release.Name} v{version?.Name} ({tableFile.Reference.Id})");

                var gameName  = release.Game.DisplayName;
                var system    = _pinballXManager.FindSystem(tableFile);
                var platform  = tableFile.Compatibility[0].Platform;
                var fileTypes = new List <FileType>();

                // check if backglass image needs to be downloaded
                var backglassImagePath = Path.Combine(system.MediaPath, Job.MediaBackglassImages);
                if (!FileBaseExists(backglassImagePath, gameName))
                {
                    fileTypes.Add(FileType.BackglassImage);
                    _jobManager.AddJob(new Job(release, g.Backglass, FileType.BackglassImage, platform));
                }

                // check if wheel image needs to be downloaded
                var wheelImagePath = Path.Combine(system.MediaPath, Job.MediaWheelImages);
                if (!FileBaseExists(wheelImagePath, gameName))
                {
                    fileTypes.Add(FileType.WheelImage);
                    _jobManager.AddJob(new Job(release, g.Logo, FileType.WheelImage, platform));
                }

                // queue table shot
                var tableImage = Path.Combine(system.MediaPath, Job.MediaTableImages);
                if (!FileBaseExists(tableImage, gameName))
                {
                    fileTypes.Add(FileType.TableImage);
                    _jobManager.AddJob(new Job(release, tableFile.PlayfieldImage, FileType.TableImage, platform));
                }

                // todo check for ROM to be downloaded, path at HKEY_CURRENT_USER\SOFTWARE\Freeware\Visual PinMame\globals
                // todo also queue all remaining non-table files of the release.

                // queue for download
                var job = new Job(release, tableFile, FileType.TableFile, platform);
                fileTypes.Add(FileType.TableFile);
                _logger.Info("Found {0} file type(s) to download: {1}", fileTypes.Count, string.Join(", ", fileTypes));
                _jobManager.AddJob(job);
                game.SetJob(job);
                _logger.Info("Created new job for {0} - {1} v{2} ({3}): {4}", job.Release.Game.DisplayName, job.Release.Name, job.Version.Name, job.File.Id, tableFile.ToString());

                _currentlyDownloading.Remove(release.Id);
            }, exception => _vpdbManager.HandleApiError(exception, "retrieving game details during download"));
            return(this);
        }
Beispiel #6
0
        private Mapping GetOrCreateMapping(AggregatedGame game)
        {
            if (game.HasMapping)
            {
                return(game.Mapping);
            }
            var system  = FindSystem(game);
            var mapping = game.Mapping ?? new Mapping(game, system);

            game.Update(mapping);
            system.Mappings.Add(mapping);
            return(mapping);
        }
Beispiel #7
0
        public GameResultItemViewModel(AggregatedGame game, VpdbRelease release, VpdbVersion version, VpdbTableFile tableFile, ICommand closeCommand)
        {
            Game      = game;
            Version   = version;
            Release   = release;
            TableFile = tableFile;

            SelectResult = ReactiveCommand.Create(() => {
                GameManager.MapGame(game, release, tableFile.Reference.Id);
                //MessageManager.LogReleaseLinked(game, release, tableFile.Reference.Id);

                closeCommand.Execute(null);
            });
        }
Beispiel #8
0
        public void HideGame(AggregatedGame game)
        {
            _logger.Info("Hiding game {0}", game.FileId);

            if (game.FilePath == null)
            {
                _logger.Error("Cannot hide game without local file.");
                return;
            }

            var mapping = GetOrCreateMapping(game);

            mapping.IsHidden = true;
        }
Beispiel #9
0
        private PinballXSystem FindSystem(AggregatedGame game)
        {
            if (game.HasSystem)
            {
                return(game.System);
            }
            var tablePath = PathHelper.NormalizePath(Path.GetDirectoryName(game.FilePath));
            var system    = _pinballXManager.Systems.FirstOrDefault(s => s.TablePath == tablePath);

            if (system == null)
            {
                throw new Exception($"Got game at {game.FilePath} but no systems that match path ({string.Join(", ", _pinballXManager.Systems.Select(s => s.TablePath))}).");
            }
            return(system);
        }
Beispiel #10
0
        public void AddGame(AggregatedGame game)
        {
            if (game.HasXmlGame)
            {
                throw new InvalidOperationException("Game already in XML database.");
            }
            if (game.MappedRelease == null || game.MappedTableFile == null)
            {
                throw new InvalidOperationException("Cannot add game without release mapping.");
            }
            var xmlGame = new PinballXGame {
                FileName     = Path.GetFileNameWithoutExtension(game.FileDisplayName),
                Description  = $"{game.MappedRelease.Game.Title} ({game.MappedRelease.Game.Manufacturer} {game.MappedRelease.Game.Year})",
                Manufacturer = game.MappedRelease.Game.Manufacturer,
                Year         = game.MappedRelease.Game.Year.ToString(),
                System       = game.System
            };

            _pinballXManager.AddGame(xmlGame);
        }
Beispiel #11
0
        /// <summary>
        /// Returns true if a given game should be displayed or false otherwise.
        ///
        /// Visibility is determined in function of platform filters and enabled
        /// platforms.
        /// </summary>
        /// <param name="game">Game</param>
        /// <returns>True if visible, false otherwise</returns>
        private bool IsGameVisible(AggregatedGame game)
        {
            if (game.System != null)
            {
                if (_systemFilter.Contains(game.System.Name))
                {
                    return(false);
                }
                if (game.HasXmlGame && _databaseFileFilter[game.System].Contains(game.XmlGame.DatabaseFile))
                {
                    return(false);
                }
                if (game.HasXmlGame && _executableFilter[game.System].Contains(game.XmlGame.AlternateExe ?? ""))
                {
                    return(false);
                }
            }
            if (StatusFilter == DataStatus.FilesNotInDatabase && game.HasXmlGame)
            {
                return(false);
            }
            if (StatusFilter == DataStatus.GamesNotOnDisk && game.HasLocalFile)
            {
                return(false);
            }
            if (StatusFilter == DataStatus.UnmappedFiles && game.HasMapping)
            {
                return(false);
            }

            if (!ShowHidden && (game.HasMapping && game.Mapping.IsHidden))
            {
                return(false);
            }
            if (!ShowDisabled && game.HasXmlGame && game.XmlGame.Enabled != null && !"true".Equals(game.XmlGame.Enabled, StringComparison.InvariantCultureIgnoreCase))
            {
                return(false);
            }

            return(true);
        }
Beispiel #12
0
        /// <summary>
        /// Applies table script changes from a previous version to an updated version.
        /// </summary>
        /// <param name="game">Game where to apply changes to</param>
        /// <param name="baseFileName">File name of the previous version</param>
        /// <param name="fileToPatchId">File ID of the updated version</param>
        private void PatchGame(AggregatedGame game, string baseFileName, string fileToPatchId)
        {
            // todo create log message when something goes wrong.

            /*
             * var baseFileId = game.PreviousFileId;
             * _logger.Info("Patching file {0} with changes from file {1}", fileToPatchId, baseFileId);
             *
             * // get table scripts for original files
             * var baseFile = _databaseManager.GetTableFile(game.ReleaseId, baseFileId).Reference;
             * var fileToPatch = _databaseManager.GetTableFile(game.ReleaseId, fileToPatchId).Reference;
             * var originalBaseScript = (string)baseFile?.Metadata["table_script"];
             * var originalScriptToPatch = (string)fileToPatch?.Metadata["table_script"];
             * if (originalBaseScript == null || originalScriptToPatch == null) {
             *      _logger.Warn("Got no script for file {0}, aborting.", originalBaseScript == null ? baseFileId : fileToPatchId);
             *      return;
             * }
             *
             * // get script from local (potentially modified) table file
             * var localBaseFilePath = Path.Combine(game.Platform.TablePath, baseFileName);
             * var localBaseScript = _visualPinballManager.GetTableScript(localBaseFilePath);
             * if (localBaseScript == null) {
             *      _logger.Warn("Error reading table script from {0}.", localBaseFilePath);
             *      return;
             * }
             *
             * if (localBaseScript == originalBaseScript) {
             *      _logger.Info("No changes between old local and remote version, so nothing to patch. We're done patching.");
             *      return;
             * }
             * _logger.Info("Script changes between old local and old remote table detected, so let's merge those changes!");
             *
             * // sanity check: compare extracted script from vpdb with our own
             * var localFilePathToPatch = Path.Combine(game.Platform.TablePath, game.Filename);
             * var localScriptToPatch = _visualPinballManager.GetTableScript(localFilePathToPatch);
             * if (localScriptToPatch != originalScriptToPatch) {
             *      _logger.Error("Script in metadata ({0} bytes) is not identical to what we've extracted from the download ({1} bytes).", originalScriptToPatch.Length, localScriptToPatch.Length);
             *      return;
             * }
             *
             * // we need line arrays for the merge tool
             * var originalBaseScriptLines = originalBaseScript.Split('\n');
             * var originalScriptToPatchLines = originalScriptToPatch.Split('\n');
             * var localBaseScriptLines = localBaseScript.Split('\n');
             *
             * // do the three-way merge
             * var result = Diff.Diff3Merge(localBaseScriptLines, originalBaseScriptLines, originalScriptToPatchLines, true);
             * var patchedScriptLines = new List<string>();
             * var failed = 0;
             * var succeeded = 0;
             * foreach (var okBlock in result.Select(block => block as Diff.MergeOkResultBlock)) {
             *      if (okBlock != null) {
             *              succeeded++;
             *              patchedScriptLines.AddRange(okBlock.ContentLines);
             *      } else {
             *              failed++;
             *      }
             * }
             * if (failed > 0) {
             *      _logger.Warn("Merge failed ({0} block(s) ok, {1} block(s) conflicted. Needs manual resolving", succeeded, failed);
             *      return;
             * }
             * var patchedScript = string.Join("\n", patchedScriptLines);
             * _logger.Info("Successfully merged changes - {0} block(s) applied.", succeeded);
             *
             * // save script to table
             * try {
             *      _visualPinballManager.SetTableScript(localFilePathToPatch, patchedScript);
             * } catch (Exception e) {
             *      _logger.Error(e, "Error writing patched script back to table file.");
             *      return;
             * }
             * game.PatchedTableScript = patchedScript;
             *
             * _logger.Info("Successfully wrote back script to table file.");*/
        }
Beispiel #13
0
 public IGameManager Sync(AggregatedGame game)
 {
     _downloadManager.DownloadRelease(game);
     return(this);
 }
Beispiel #14
0
 public void DownloadGame(AggregatedGame game)
 {
     _downloadManager.DownloadRelease(game);
 }
Beispiel #15
0
        public GameItemViewModel(AggregatedGame game, IDependencyResolver resolver)
        {
            Game = game;

            _logger          = resolver.GetService <ILogger>();
            _vpdbClient      = resolver.GetService <IVpdbClient>();
            _gameManager     = resolver.GetService <IGameManager>();
            _pinballXManager = resolver.GetService <IPinballXManager>();
            _messageManager  = resolver.GetService <IMessageManager>();
            var threadManager = resolver.GetService <IThreadManager>();

            game.WhenAnyValue(g => g.IsDownloading).ToProperty(this, vm => vm.IsDownloading, out _isDownloading);
            game.WhenAnyValue(g => g.IsQueued).ToProperty(this, vm => vm.IsQueued, out _isQueued);

            // release identify
            IdentifyRelease = ReactiveCommand.CreateFromObservable(() => _vpdbClient.Api.GetReleasesBySize(Game.FileSize, MatchThreshold).SubscribeOn(threadManager.WorkerScheduler));
            IdentifyRelease.Select(releases => releases
                                   .Select(release => new { release, release.Versions })
                                   .SelectMany(x => x.Versions.Select(version => new { x.release, version, version.Files }))
                                   .SelectMany(x => x.Files.Select(file => new GameResultItemViewModel(game, x.release, x.version, file, CloseResults)))
                                   ).Subscribe(x => {
                var releases   = x as GameResultItemViewModel[] ?? x.ToArray();
                var numMatches = 0;
                _logger.Info("Found {0} releases for game to identify.", releases.Length);
                GameResultItemViewModel match = null;
                foreach (var vm in releases)
                {
                    if (game.FileName == vm.TableFile.Reference.Name && game.FileSize == vm.TableFile.Reference.Bytes)
                    {
                        numMatches++;
                        match = vm;
                    }
                }
                _logger.Info("Found {0} identical match(es).", numMatches);

                // if file name and file size are identical, directly match.
                if (numMatches == 1 && match != null)
                {
                    _logger.Info("File name and size are equal to local release, linking.");
                    _gameManager.MapGame(match.Game, match.Release, match.TableFile.Reference.Id);
                    //_messageManager.LogReleaseLinked(match.Game, match.Release, match.TableFile.Reference.Id);
                }
                else
                {
                    _logger.Info("View model updated with identified releases.");
                    IdentifiedReleases = releases;
                    ShowResults        = true;
                }
            }, exception => _vpdbClient.HandleApiError(exception, "identifying a game by file size"));

            //var canSync = this.WhenAnyValue(x => x.Game.IsSynced, x => x.Game.HasRelease, (isSynced, hasRelease) => isSynced && hasRelease);
            //var canSync = this.WhenAnyValue(x => x.Game.Mapping.IsSynced, x => x.Game.MappedRelease, (isSynced, rls) => isSynced && rls != null);
            //SyncToggled = ReactiveCommand.Create(() => { _gameManager.Sync(Game); }, canSync);

            // handle errors
            IdentifyRelease.ThrownExceptions.Subscribe(e => { _logger.Error(e, "Error matching game."); });

            // result switch
            IdentifyRelease.Select(r => r.Count > 0).Subscribe(hasResults => { HasResults = hasResults; });

            // add to db button
            AddGame = ReactiveCommand.Create(() => _gameManager.AddGame(Game));

            // remove from db button
            RemoveGame = ReactiveCommand.Create(() => _pinballXManager.RemoveGame(Game.XmlGame));

            // close button
            CloseResults = ReactiveCommand.Create(() => { ShowResults = false; });

            // hide button
            HideGame = ReactiveCommand.Create(() => _gameManager.HideGame(Game));

            // unhide button
            UnHideGame = ReactiveCommand.Create(() => _gameManager.UnHideGame(Game));

            // download button
            DownloadMissing = ReactiveCommand.Create(() => _gameManager.DownloadGame(Game));

            // spinner
            IdentifyRelease.IsExecuting.ToProperty(this, vm => vm.IsExecuting, out _isExecuting);

            // global buttons hide
            this.WhenAnyValue(
                vm => vm.ShowResults,
                vm => vm.IsExecuting,
                vm => vm.IsDownloading,
                vm => vm.IsQueued,
                (showResults, isExecuting, isDownloading, isQueued) => !showResults && !isExecuting && !isDownloading && !isQueued
                ).ToProperty(this, vm => vm.ShowButtons, out _showButtons);

            // identify button visibility
            this.WhenAny(
                vm => vm.Game.HasLocalFile,
                vm => vm.Game.MappedTableFile,
                vm => vm.ShowButtons,
                (hasLocalFile, mappedFile, showButtons) => hasLocalFile.Value && mappedFile.Value == null && showButtons.Value
                ).ToProperty(this, vm => vm.ShowIdentifyButton, out _showIdentifyButton);

            // hide button visibility
            this.WhenAny(
                vm => vm.Game.Mapping,
                vm => vm.Game.HasLocalFile,
                vm => vm.Game.HasMappedRelease,
                vm => vm.Game.HasXmlGame,
                vm => vm.ShowButtons,
                (mapping, hasLocalFile, hasMappedRelease, hasXmlGame, showButtons) => (mapping.Value == null || !mapping.Value.IsHidden) && hasLocalFile.Value && !hasMappedRelease.Value && !hasXmlGame.Value && showButtons.Value
                ).ToProperty(this, vm => vm.ShowHideButton, out _showHideButton);

            // unhide button visibility
            this.WhenAny(
                vm => vm.Game.Mapping,
                vm => vm.ShowButtons,
                (mapping, showButtons) => mapping.Value != null && mapping.Value.IsHidden && showButtons.Value
                ).ToProperty(this, vm => vm.ShowUnHideButton, out _showUnHideButton);

            // add to db button visibility
            this.WhenAny(
                vm => vm.Game.HasLocalFile,
                vm => vm.Game.HasXmlGame,
                vm => vm.Game.MappedTableFile,
                vm => vm.ShowButtons,
                (hasLocalFile, hasXmlGame, mappedFile, showButtons) => hasLocalFile.Value && !hasXmlGame.Value && mappedFile.Value != null && showButtons.Value
                ).ToProperty(this, vm => vm.ShowAddToDbButton, out _showAddToDbButton);

            // remove from db button visibility
            this.WhenAny(
                vm => vm.Game.HasLocalFile,
                vm => vm.Game.HasXmlGame,
                vm => vm.ShowButtons,
                (hasLocalFile, hasXmlGame, showButtons) => !hasLocalFile.Value && hasXmlGame.Value && showButtons.Value
                ).ToProperty(this, vm => vm.ShowRemoveFromDbButton, out _showRemoveFromDbButton);

            // download button visibility
            this.WhenAny(
                vm => vm.Game.HasLocalFile,
                vm => vm.Game.MappedTableFile,
                vm => vm.ShowButtons,
                (hasLocalFile, mappedFile, showButtons) => !hasLocalFile.Value && mappedFile.Value != null && showButtons.Value
                ).ToProperty(this, vm => vm.ShowDownloadMissingButton, out _showDownloadMissingButton);

            // download progress
            this.WhenAnyValue(vm => vm.IsDownloading).Subscribe(isDownloading => {
                if (isDownloading)
                {
                    Game.MappedJob.WhenAnyValue(j => j.TransferPercent)
                    .Sample(TimeSpan.FromMilliseconds(300))
                    .Where(x => !Game.MappedJob.IsFinished)
                    .Subscribe(progress => {
                        // on main thread
                        System.Windows.Application.Current.Dispatcher.Invoke(() => {
                            DownloadPercent = progress;
                        });
                    });
                }
            });
        }